# ETH源码学习（2）GetBalance

By [point](https://paragraph.com/@point) · 2022-05-12

---

从这开始读，因为最简单。还能对重要的东西有所理解。如果是单节点debug，重启链之后余额会归零，所以最少要两个节点，同步区块就没这个问题了，具体原因还未知。

声明：eth版本：Geth/v1.10.7-unstable/darwin-arm64/go1.17.5，我对比了网上的一些文章，这块代码有改动，主要以下改变

1.  从BlockChain的snap中读account
    
2.  几乎放弃从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()
    }
    

创建statedb

    func (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和传统项目的思路差不多，一层层读缓存，最终读数据库，然后再保存缓存，返回前端

1.  getBalance要先获取最新区块的root（stateRoot）来换stateDB
    
2.  然后从stateDB的stateObject中获取
    
3.  没有的话好像从snap中获取
    
    1.  先读缓存
        
    2.  再读levelDB
        
    3.  命中的话缓存至快照
        
4.  没有的话再从trie中获取
    
5.  命中则缓存至stateObject
    
6.  返回前端
    

从上面了解到stateDB这个东西很重要，所有的操作都是围绕他来完成的。stateDB又是靠BlockChain里的数据来创建的

1.  最新区块的state root
    
2.  snaps
    
3.  stateCache
    

这样就有了个大致的影响，然后通过后面的内容，更加深入的了解eth的架构

---

*Originally published on [point](https://paragraph.com/@point/eth-2-getbalance)*
