StateDB 用法与结构
BlockChain以太坊是基于账户体系实现的,块通过 parentHash 链在一起,每个块都包含若干交易,每个交易都包含账户 from 和 to(部署合约时除外),全部的账户凑在一起就是组成了 StateDB,每个块的 StateDB 都用一颗叫做 Trie 的树来组织账户信息,具体结构如下图:
StateDB保存账户信息的 StateDB 通常会存储在磁盘上,通过 Block.StateRoot 来进行加载,StateRoot 是树根,也是 leveldb 中的一个 key, 这个根只对应当前块的交易相关的账户信息,value 是这棵树的全部叶子节点,加载的时候会用叶子节点来构建下图中的树型结构
stateObject智能合约的地址也被当作账户管理,当 Account 为一个智能合约时,那么这个 stateObject 也会包含一颗树,用来保存智能合约的最新状态信息,这些信息是每次执行 evm 中 SSTORE 这个指令时的输入信息,key 是合约的变量名,value 是最新值,这棵树的加载过程与上图中的过程完全一致
StateDB 的工作方式
用最少的代码和最简单的例子说明 StateDB 的主要工作原理与过程
- StateDB 的结构定义
type StateDB struct {
db Database
trie Trie
stateObjects map[common.Address]*stateObject
stateObjectsDirty map[common.Address]struct{}
dbErr error
refund uint64
thash, bhash common.Hash
txIndex int
logs map[common.Hash][]*types.Log
logSize uint
preimages map[common.Hash][]byte
journal *journal
// 由 snapshot id 和 journal 长度组成,用于回滚
validRevisions []revision
// 下一个可用的 snapshot id
nextRevisionId int
lock sync.Mutex
}
*StateDB 的关键方法
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int)
func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
func (s *StateDB) Finalise(deleteEmptyObjects bool)
通过上面的三个方法可以演示整个 statedb 的操作流程
当执行AddBalance
方法会设置addr
的stateObject.Account.Balance += amount
,这时addr
对应的stateObject
会被放在StateDB.stateObjects
中缓存起来,
当执行Commit
方法时,会将StateDB.stateObjects
中的数据构建成一颗默克尔树存放在StateDB.trie
中,修改数据时会产生journal
日志,为了方便出错时的回滚,
当执行Finalise
方法时会删除journal
刷新stateObject
的trie.root
(如果 stateObject 存在 trie)
执行到此时树的结构已经确定,最终的树根也已经确定,但是在以太坊中数据此时还没有进入数据库
为了提高效率StateDB对数据做了缓存处理,大部分时间都是放在内存中的,StateDB.db 是 state.Database 接口的实现,定义如下:
// Database wraps access to tries and contract code.
type Database interface {
// blockchain 的 树,root == block.stateRoot
OpenTrie(root common.Hash) (Trie, error)
// account 的 树,addrHash == account.address
OpenStorageTrie(addrHash, root common.Hash) (Trie, error)
......
// TrieDB retrieves the low level trie database used for data storage.
TrieDB() *trie.Database
}
需要使用 TrieDB 这个方法返回的 trie.Database 对象来最终完成 trei 写入 leveldb 的操作,这个方法在 WriteBlockWithState 中会被有条件调用,此处不再深入,谨以此文说明 Block、 StateDB、 Trie 之间的关系
网友评论