# ETH源码学习(2)GetBalance **Published by:** [point](https://paragraph.com/@point/) **Published on:** 2022-05-12 **URL:** https://paragraph.com/@point/eth-2-getbalance ## Content 从这开始读,因为最简单。还能对重要的东西有所理解。如果是单节点debug,重启链之后余额会归零,所以最少要两个节点,同步区块就没这个问题了,具体原因还未知。 声明:eth版本:Geth/v1.10.7-unstable/darwin-arm64/go1.17.5,我对比了网上的一些文章,这块代码有改动,主要以下改变从BlockChain的snap中读account几乎放弃从trie中获取account,判断极为严苛,我想不到有什么情况能进入,应该是放弃了这层分析入口//命令 eth.getBalance('0x0d7dd6dbabee2ec9b325aa7aa8b42d75068e8597') //入口 func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { //获取statedb state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err } //获取余额 return (*hexutil.Big)(state.GetBalance(address)), state.Error() } 创建statedbfunc (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { //根据最新的hader state root创建statedb if blockNr, ok := blockNrOrHash.Number(); ok { return b.StateAndHeaderByNumber(ctx, blockNr) } if hash, ok := blockNrOrHash.Hash(); ok { header, err := b.HeaderByHash(ctx, hash) if err != nil { return nil, nil, err } if header == nil { return nil, nil, errors.New("header for hash not found") } if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash { return nil, nil, errors.New("hash is not currently canonical") } stateDb, err := b.eth.BlockChain().StateAt(header.Root) return stateDb, header, err } return nil, nil, errors.New("invalid arguments; neither block nor hash specified") } func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { // Pending state is only known by the miner //有Pending的区块,从这里面获取statedb if number == rpc.PendingBlockNumber { block, state := b.eth.miner.Pending() return state, block.Header(), nil } // Otherwise resolve the block number and return its state header, err := b.HeaderByNumber(ctx, number) if err != nil { return nil, nil, err } if header == nil { return nil, nil, errors.New("header not found") } //创建新的statedb stateDb, err := b.eth.BlockChain().StateAt(header.Root) return stateDb, header, err } //主要做了两件事,创建trie和snap func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) { fmt.Println("root=====", root.String()) tr, err := db.OpenTrie(root) if err != nil { return nil, err } sdb := &StateDB{ db: db, trie: tr, originalRoot: root, snaps: snaps, stateObjects: make(map[common.Address]*stateObject), stateObjectsPending: make(map[common.Address]struct{}), stateObjectsDirty: make(map[common.Address]struct{}), logs: make(map[common.Hash][]*types.Log), preimages: make(map[common.Hash][]byte), journal: newJournal(), accessList: newAccessList(), hasher: crypto.NewKeccakState(), } if sdb.snaps != nil { if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap != nil { sdb.snapDestructs = make(map[common.Hash]struct{}) sdb.snapAccounts = make(map[common.Hash][]byte) sdb.snapStorage = make(map[common.Hash]map[common.Hash][]byte) } } return sdb, nil } statedb获取完毕,获取balance回到最开始从GetBalance进入func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { fmt.Println("addr=====", addr.String()) // Prefer live objects if any is available //从stateobject中获取 if obj := s.stateObjects[addr]; obj != nil { return obj } // If no live objects are available, attempt to use snapshots var ( data *Account err error ) //从缓存中获取 if s.snap != nil { if metrics.EnabledExpensive { defer func(start time.Time) { s.SnapshotAccountReads += time.Since(start) }(time.Now()) } var acc *snapshot.Account if acc, err = s.snap.Account(crypto.HashData(s.hasher, addr.Bytes())); err == nil { if acc == nil { return nil } data = &Account{ Nonce: acc.Nonce, Balance: acc.Balance, CodeHash: acc.CodeHash, Root: common.BytesToHash(acc.Root), } if len(data.CodeHash) == 0 { data.CodeHash = emptyCodeHash } if data.Root == (common.Hash{}) { data.Root = emptyRoot } } } // If snapshot unavailable or reading from it failed, load from the database //从trie中获取 if s.snap == nil || err != nil { if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now()) } enc, err := s.trie.TryGet(addr.Bytes()) if err != nil { s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %v", addr.Bytes(), err)) return nil } if len(enc) == 0 { return nil } data = new(Account) if err := rlp.DecodeBytes(enc, data); err != nil { log.Error("Failed to decode state object", "addr", addr, "err", err) return nil } } // Insert into the live set obj := newObject(s, addr, *data) s.setStateObject(obj) return obj } func (dl *diskLayer) AccountRLP(hash common.Hash) ([]byte, error) { dl.lock.RLock() defer dl.lock.RUnlock() // If the layer was flattened into, consider it invalid (any live reference to // the original should be marked as unusable). if dl.stale { return nil, ErrSnapshotStale } // If the layer is being generated, ensure the requested hash has already been // covered by the generator. if dl.genMarker != nil && bytes.Compare(hash[:], dl.genMarker) > 0 { return nil, ErrNotCoveredYet } // If we're in the disk layer, all diff layers missed snapshotDirtyAccountMissMeter.Mark(1) // Try to retrieve the account from the memory cache //从缓存中读 if blob, found := dl.cache.HasGet(nil, hash[:]); found { snapshotCleanAccountHitMeter.Mark(1) snapshotCleanAccountReadMeter.Mark(int64(len(blob))) return blob, nil } // Cache doesn't contain account, pull from disk and cache for later //从硬盘中读 blob := rawdb.ReadAccountSnapshot(dl.diskdb, hash) dl.cache.Set(hash[:], blob) snapshotCleanAccountMissMeter.Mark(1) if n := len(blob); n > 0 { snapshotCleanAccountWriteMeter.Mark(int64(n)) } else { snapshotCleanAccountInexMeter.Mark(1) } return blob, nil } 总结get balance和传统项目的思路差不多,一层层读缓存,最终读数据库,然后再保存缓存,返回前端getBalance要先获取最新区块的root(stateRoot)来换stateDB然后从stateDB的stateObject中获取没有的话好像从snap中获取先读缓存再读levelDB命中的话缓存至快照没有的话再从trie中获取命中则缓存至stateObject返回前端从上面了解到stateDB这个东西很重要,所有的操作都是围绕他来完成的。stateDB又是靠BlockChain里的数据来创建的最新区块的state rootsnapsstateCache这样就有了个大致的影响,然后通过后面的内容,更加深入的了解eth的架构 ## Publication Information - [point](https://paragraph.com/@point/): Publication homepage - [All Posts](https://paragraph.com/@point/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@point): Subscribe to updates