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
}
我的星球.jpg
网友评论