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
初始化backends數組,數組中調用了newKeyStore
-
利用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
- 初始化了一個KeyStore,調用init方法
keystore.init
調用ks.cache.accounts方法遍歷硬盤上keystore目錄下的所有賬號
-
根據賬號初始化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方法
將所有backend中的wallet合并
注冊事件
-
根據事件更新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, 4len(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)
} - newKey 生成key,key中包含地址,公鑰,私鑰
-
解鎖賬號
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
讀取參數中的文件信息
使用KeyStoreType類型的backend解密文件內容
-
生成賬號信息
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
}