IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 区块链 -> 以太坊源码阅读【Wallet(钱包)】 -> 正文阅读

[区块链]以太坊源码阅读【Wallet(钱包)】

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并完善

  区块链 最新文章
盘点具备盈利潜力的几大加密板块,以及潜在
阅读笔记|让区块空间成为商品,打造Web3云
区块链1.0-比特币的数据结构
Team Finance被黑分析|黑客自建Token“瞒天
区块链≠绿色?波卡或成 Web3“生态环保”标
期货从入门到高深之手动交易系列D1课
以太坊基础---区块验证
进入以太坊合并的五个数字
经典同态加密算法Paillier解读 - 原理、实现
IPFS/Filecoin学习知识科普(四)
上一篇文章           查看所有文章
加:2021-12-10 11:06:57  更:2021-12-10 11:07:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:21:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码