以太坊源碼分析 賬號

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

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容