美文网首页区块链技术探索
以太坊源码分析 账号

以太坊源码分析 账号

作者: hasika | 来源:发表于2018-10-15 16:11 被阅读4次

    AccountManager创建

    p2p节点创建时会创建accountmanager

    node.New

    / New creates a new P2P node, ready for protocol registration.
    func New(conf *Config) (*Node, error) {
       // Ensure that the AccountManager method works before the node has started.
       // We rely on this in cmd/geth.
       am, ephemeralKeystore, err := makeAccountManager(conf)
    ...
    }
    

    config.makeAccountManager

    1. 初始化backends数组,数组中调用了newKeyStore

    2. 利用backends 创建accounts.NewManager

      func makeAccountManager(conf Config) (accounts.Manager, string, error) {
      scryptN, scryptP, keydir, err := conf.AccountConfig()
      var ephemeral string
      // Assemble the account manager and supported backends
      //初始化backends,其中调用了newKeyStore
      backends := []accounts.Backend{
      keystore.NewKeyStore(keydir, scryptN, scryptP),
      }

      return accounts.NewManager(backends...), ephemeral, nil
      

      }

    keystore.NewKeyStore

    1. 初始化了一个KeyStore,调用init方法

    keystore.init

    1. 调用ks.cache.accounts方法遍历硬盘上keystore目录下的所有账号

    2. 根据账号初始化keystoreWallet,并缓存到ks.wallets数组中

      // NewKeyStore creates a keystore for the given directory.
      func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
      keydir, _ = filepath.Abs(keydir)
      ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP}}
      ks.init(keydir)
      return ks
      }

      func (ks *KeyStore) init(keydir string) {

        // Initialize the set of unlocked keys and the account cache
        ks.unlocked = make(map[common.Address]*unlocked)
        ks.cache, ks.changes = newAccountCache(keydir)
    
        // Create the initial list of wallets from the cache
        //取缓存中的account列表,这个方法会遍历keystore目录,将所有的账号都遍历出来
        accs := ks.cache.accounts()
        ks.wallets = make([]accounts.Wallet, len(accs))
        for i := 0; i < len(accs); i++ {
            ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks}
        }
    }
    

    account_cache.accounts

    func (ac *accountCache) accounts() []accounts.Account {
        //加载账号列表
       ac.maybeReload()
       ac.mu.Lock()
       defer ac.mu.Unlock()
       cpy := make([]accounts.Account, len(ac.all))
       copy(cpy, ac.all)
       return cpy
    }
    
    func (ac *accountCache) maybeReload() {
        ...
        
        ac.scanAccounts()
    }
    

    account_cache.scanAccounts

    遍历目录,生成account并赋值

    // scanAccounts checks if any changes have occurred on the filesystem, and
    // updates the account cache accordingly
    func (ac *accountCache) scanAccounts() error {
       // Scan the entire folder metadata for file changes
        //遍历目录,将新创建的,新删除的,更新的账号目录遍历出来
       creates, deletes, updates, err := ac.fileC.scan(ac.keydir)
       
        //将账号添加到ac中
       for _, p := range creates.ToSlice() {
          if a := readAccount(p.(string)); a != nil {
             ac.add(*a)
          }
       }
       for _, p := range deletes.ToSlice() {
          ac.deleteByFile(p.(string))
       }
       for _, p := range updates.ToSlice() {
          path := p.(string)
          ac.deleteByFile(path)
          if a := readAccount(path); a != nil {
             ac.add(*a)
          }
       }
    }
    

    回到makeAccountManager的最后NewManager方法

    1. 将所有backend中的wallet合并

    2. 注册事件

    3. 根据事件更新wallet列表

      // NewManager creates a generic account manager to sign transaction via various
      // supported backends.
      func NewManager(backends ...Backend) Manager {
      // Retrieve the initial list of wallets from the backends and sort by URL
      var wallets []Wallet
      //将所有backend中的wallet合并
      for _, backend := range backends {
      wallets = merge(wallets, backend.Wallets()...)
      }
      // Subscribe to wallet notifications from all backends
      updates := make(chan WalletEvent, 4
      len(backends))

      subs := make([]event.Subscription, len(backends))
      for i, backend := range backends {
          //注册事件
         subs[i] = backend.Subscribe(updates)
      }
      // Assemble the account manager and return
      am := &Manager{
         backends: make(map[reflect.Type][]Backend),
         updaters: subs,
         updates:  updates,
         wallets:  wallets,
         quit:     make(chan chan error),
      }
      for _, backend := range backends {
         kind := reflect.TypeOf(backend)
         am.backends[kind] = append(am.backends[kind], backend)
      }
      //开启事件监听
      go am.update()
      
      return am
      

      }

    manger.update

    // update is the wallet event loop listening for notifications from the backends
    // and updating the cache of wallets.
    func (am *Manager) update() {
    
       // Loop until termination
       for {
          select {
          case event := <-am.updates:
             // Wallet event arrived, update local cache
             am.lock.Lock()
             switch event.Kind {
                 //当新填wallet时,合并
             case WalletArrived:
                am.wallets = merge(am.wallets, event.Wallet)
                 //上删除wallet时,删除
             case WalletDropped:
                am.wallets = drop(am.wallets, event.Wallet)
             }
       }
    }
    

    创建账号

    accountcmd.accountCreate()入口函数

    • password := getPassPhrase 拿到用户输入的密码
    • address, err := keystore.StoreKey(keydir, password, scryptN, scryptP) 生成公私钥及对应的keystore文件
      • key.storeNewKey

        • newKey 生成key,key中包含地址,公钥,私钥
          • privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand) 生成公私钥
          • newKeyFromECDSA(privateKeyECDSA) 根据公私钥生成key
        • a := accounts.Account 根据key生成账号
        • ks.StoreKey(a.URL.Path, key, auth) 用输入的密码加密keystore并写入文件系统中

        func accountCreate(ctx *cli.Context) error {
        //获取输入的密码
        password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
        //使用keystore 创建keystore文件及公私钥
        address, err := keystore.StoreKey(keydir, password, scryptN, scryptP)

        return nil
        }

        // StoreKey generates a key, encrypts with 'auth' and stores in the given directory
        func StoreKey(dir, auth string, scryptN, scryptP int) (common.Address, error) {
        _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP}, rand.Reader, auth)
        return a.Address, err
        }

        func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) {
        //生成key,key中包含地址,公钥,私钥
        key, err := newKey(rand)
        if err != nil {
        return nil, accounts.Account{}, err
        }
        //生成账号
        a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}}
        //保存key
        if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
        zeroKey(key.PrivateKey)
        return nil, a, err
        }
        return key, a, err
        }

        func newKey(rand io.Reader) (*Key, error) {
        //生成公钥,私钥
        privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand)
        if err != nil {
        return nil, err
        }
        //生成key,主要是生成地址
        return newKeyFromECDSA(privateKeyECDSA), nil
        }

        func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
        id := uuid.NewRandom()
        key := &Key{
        Id: id,
        Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
        PrivateKey: privateKeyECDSA,
        }
        return key
        }

        func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
        //加密key
        keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
        if err != nil {
        return err
        }
        //将加密的数据,写入到文件中
        return writeKeyFile(filename, keyjson)
        }

    解锁账号

    accountcmd.unlockAccount

    // tries unlocking the specified account a few times.
    func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) {
       //根据keystore和address,获得对应的account
       account, err := utils.MakeAddress(ks, address)
          //获取密码
          password := getPassPhrase(prompt, false, i, passwords)
          //解锁
          err = ks.Unlock(account, password)
          if err == nil {
             log.Info("Unlocked account", "address", account.Address.Hex())
             return account, password
          }
    }
    

    utils.MakeAddress

    // MakeAddress converts an account specified directly as a hex encoded string or
    // a key index in the key store to an internal account representation.
    func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error) {
       // Otherwise try to interpret the account as a keystore index
       index, err := strconv.Atoi(account)
     
       accs := ks.Accounts()
       if len(accs) <= index {
          return accounts.Account{}, fmt.Errorf("index %d higher than number of accounts %d", index, len(accs))
       }
        //根据下标,返回缓存的账号信息
       return accs[index], nil
    }
    

    ks.Unlock(account, password)

    ks.TimedUnlock

    调用解密函数,解密keystore文件,获得秘钥,构建账号对象返回

    func (ks *KeyStore) TimedUnlock(a accounts.Account, passphrase string, timeout time.Duration) error {
       //解锁key
       a, key, err := ks.getDecryptedKey(a, passphrase)
        //生成unlocked对象
       u = &unlocked{Key: key}
       //将unlocked对象保存在队列中
       ks.unlocked[a.Address] = u
       return nil
    }
    

    更新账号

    更新账号很简单,先用老密码解密keystore文件,然后用新密码在加密keystore并保存在文件系统中

    // accountUpdate transitions an account from a previous format to the current
    // one, also providing the possibility to change the pass-phrase.
    func accountUpdate(ctx *cli.Context) error {
       if len(ctx.Args()) == 0 {
          utils.Fatalf("No accounts specified to update")
       }
       stack, _ := makeConfigNode(ctx)
       ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
    
       for _, addr := range ctx.Args() {
          account, oldPassword := unlockAccount(ctx, ks, addr, 0, nil)
          newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
           //关键方法,更新
          if err := ks.Update(account, oldPassword, newPassword); err != nil {
             utils.Fatalf("Could not update the account: %v", err)
          }
       }
       return nil
    }
    
    // Update changes the passphrase of an existing account.
    func (ks *KeyStore) Update(a accounts.Account, passphrase, newPassphrase string) error {
        //解密
       a, key, err := ks.getDecryptedKey(a, passphrase)
       if err != nil {
          return err
       }
        //加密
       return ks.storage.StoreKey(a.URL.Path, key, newPassphrase)
    }
    

    导入钱包

    accountcmd.importWallet

    1. 读取参数中的文件信息

    2. 使用KeyStoreType类型的backend解密文件内容

    3. 生成账号信息

      func importWallet(ctx cli.Context) error {
      keyfile := ctx.Args().First()
      //读取参数中的文件信息
      keyJSON, err := ioutil.ReadFile(keyfile)
      // 获取对应的密码
      passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
      //获得对应类型的backend
      ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(
      keystore.KeyStore)
      //解密文件获得账号信息
      acct, err := ks.ImportPreSaleKey(keyJSON, passphrase)
      return nil
      }

    ks.ImportPreSaleKey

    // ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
    // a key file in the key directory. The key file is encrypted with the same passphrase.
    func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (accounts.Account, error) {
        //解密账号信息
       a, _, err := importPreSaleKey(ks.storage, keyJSON, passphrase)
       if err != nil {
          return a, err
       }
        //缓存账号
       ks.cache.add(a)
        //刷新钱包
       ks.refreshWallets()
       return a, nil
    }
    

    prosale.importPreSaleKey

    // creates a Key and stores that in the given KeyStore by decrypting a presale key JSON
    func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accounts.Account, *Key, error) {
        //解密数据
       key, err := decryptPreSaleKey(keyJSON, password)
       if err != nil {
          return accounts.Account{}, nil, err
       }
       key.Id = uuid.NewRandom()
        // 生成账号信息
       a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: keyStore.JoinPath(keyFileName(key.Address))}}
       //保存keystore
       err = keyStore.StoreKey(a.URL.Path, key, password)
       return a, key, err
    }
    

    https://t.zsxq.com/iiMvfea

    我的星球.jpg

    相关文章

      网友评论

        本文标题:以太坊源码分析 账号

        本文链接:https://www.haomeiwen.com/subject/dkgfzftx.html