Wallet数据结构
Wallet位于:./accounts/accounts.go
?钱包有三个实现:./keystore/keystore.go,./scwallet/wallet.go,./usbwallet/wallet.go
?主要看./keystore/keystore.go
1)导入账户
// KeyStore manages a key storage directory on disk.
type KeyStore struct {
storage keyStore // Storage backend, might be cleartext or encrypted
cache *accountCache // In-memory account cache over the filesystem storage
changes chan struct{} // Channel receiving change notifications from the cache
unlocked map[common.Address]*unlocked // Currently unlocked account (decrypted private keys)
wallets []accounts.Wallet // Wallet wrappers around the individual key files
updateFeed event.Feed // Event feed to notify wallet additions/removals
updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
updating bool // Whether the event notification loop is running
mu sync.RWMutex
importMu sync.Mutex // Import Mutex locks the import to prevent two insertions from racing
}
// NewKeyStore creates a keystore for the given directory.
func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
// 返回给的路径的绝对路径,生成的json文件目录
keydir, _ = filepath.Abs(keydir)
// 构造KeyStore结构体
ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP, false}}
// 创建账户
ks.init(keydir)
return ks
}
进入init方法, 一个钱包wallet有多个账户account
根据传入的文件地址,解析json文件,生成对应的公钥
func (ks *KeyStore) init(keydir string) {
// Lock the mutex since the account cache might call back with events
ks.mu.Lock()
defer ks.mu.Unlock()
// 初始化未锁定账户的map集合
// Initialize the set of unlocked keys and the account cache
ks.unlocked = make(map[common.Address]*unlocked)
// 创建账户缓存
ks.cache, ks.changes = newAccountCache(keydir)
// TODO: In order for this finalizer to work, there must be no references
// to ks. addressCache doesn't keep a reference but unlocked keys do,
// so the finalizer will not trigger until all timed unlocks have expired.
runtime.SetFinalizer(ks, func(m *KeyStore) {
m.cache.close()
})
// 读取文件,生成账户
// Create the initial list of wallets from the cache
accs := ks.cache.accounts()
// 遍历cache中的账户,初始化钱包
ks.wallets = make([]accounts.Wallet, len(accs))
for i := 0; i < len(accs); i++ {
ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks}
}
}
?进入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
}
进入maybeReload
func (ac *accountCache) maybeReload() {
ac.mu.Lock()
// 检测文件监听器是否启动,启动直接返回
if ac.watcher.running {
ac.mu.Unlock()
return // A watcher is running and will keep the cache up-to-date.
}
if ac.throttle == nil {
ac.throttle = time.NewTimer(0)
} else {
select {
case <-ac.throttle.C:
default:
ac.mu.Unlock()
return // The cache was reloaded recently.
}
}
// No watcher running, start it.
// 没有启动监听器,启动
ac.watcher.start()
ac.throttle.Reset(minReloadInterval)
ac.mu.Unlock()
//手动扫描文件
ac.scanAccounts()
}
进入scanAccounts
// 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)
if err != nil {
log.Debug("Failed to reload keystore contents", "err", err)
return err
}
// 全部为空,返回
if creates.Cardinality() == 0 && deletes.Cardinality() == 0 && updates.Cardinality() == 0 {
return nil
}
// Create a helper method to scan the contents of the key files
var (
buf = new(bufio.Reader)
key struct {
Address string `json:"address"`
}
)
// 解析单个文件,生成account
readAccount := func(path string) *accounts.Account {
fd, err := os.Open(path)
if err != nil {
log.Trace("Failed to open keystore file", "path", path, "err", err)
return nil
}
defer fd.Close()
buf.Reset(fd)
// Parse the address.
key.Address = ""
err = json.NewDecoder(buf).Decode(&key)
addr := common.HexToAddress(key.Address)
switch {
case err != nil:
log.Debug("Failed to decode keystore key", "path", path, "err", err)
case addr == common.Address{}:
log.Debug("Failed to decode keystore key", "path", path, "err", "missing or zero address")
default:
return &accounts.Account{
Address: addr,
URL: accounts.URL{Scheme: KeyStoreScheme, Path: path},
}
}
return nil
}
// Process all the file diffs
start := time.Now()
// 遍历新加的文件,生成account,添加到accountCache,并排序
for _, p := range creates.ToSlice() {
if a := readAccount(p.(string)); a != nil {
ac.add(*a)
}
}
// 遍历删除的文件,删除account
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)
}
}
end := time.Now()
select {
case ac.notify <- struct{}{}:
default:
}
log.Trace("Handled keystore changes", "time", end.Sub(start))
return nil
}
?2)创建账户
./cmd/geth/accountcmd.go?
?根据传入的目录地址,和输入的密码,生成账户account
personal.newAccount()
// StoreKey generates a key, encrypts with 'auth' and stores in the given directory
func StoreKey(dir, auth string, scryptN, scryptP int) (accounts.Account, error) {
_, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
return a, err
}
进入storeNewKey
func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) {
// 创建公私钥
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))},
}
// 保存账户,生成文件
if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
// 报错失败,清空密钥
zeroKey(key.PrivateKey)
return nil, a, err
}
return key, a, err
}
进入newKey
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
}
进入newKeyFromECDSA
func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
id, err := uuid.NewRandom()
if err != nil {
panic(fmt.Sprintf("Could not create random uuid: %v", err))
}
key := &Key{
Id: id,
Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
PrivateKey: privateKeyECDSA,
}
return key
}
保存密钥,
func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
// 根据密码加密密钥
keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
if err != nil {
return err
}
// 保存文件
// Write into temporary file
tmpName, err := writeTemporaryKeyFile(filename, keyjson)
if err != nil {
return err
}
// 是否验证
if !ks.skipKeyFileVerification {
// Verify that we can decrypt the file with the given password.
_, err = ks.GetKey(key.Address, tmpName, auth)
if err != nil {
msg := "An error was encountered when saving and verifying the keystore file. \n" +
"This indicates that the keystore is corrupted. \n" +
"The corrupted file is stored at \n%v\n" +
"Please file a ticket at:\n\n" +
"https://github.com/ethereum/go-ethereum/issues." +
"The error was : %s"
//lint:ignore ST1005 This is a message for the user
return fmt.Errorf(msg, tmpName, err)
}
}
return os.Rename(tmpName, filename)
}
钱包有个事件监听功能:
KeyStore监听底层json文件是否被改动,发出WalletArrived、WalletDropped等事件,manager监听事件,更新上层钱包。
主要原因:感觉用的对象不是同一个,manager有自己的wallets,初始化只是获取到最新的钱包账户信息,后续还需要通过事件进行更新。为啥不用同一个?
根据阅读简化的钱包代码:请问饿瑞/以太坊源码学习 - Gitee.com
只有初始化钱包和创建新账户account功能
后续继续简化ethereum并完善
|