go-ethereum中core-state源码学习
core里的state包提供了账户状态的管理,包括状态转换以及回滚等操作。
datebase.go
首先提供了数据库的抽象,定义了一个接口:
type Database interface {
OpenTrie(root common.Hash) (Trie, error)
OpenStorageTrie(addrHash, root common.Hash) (Trie, error)
CopyTrie(Trie) Trie
ContractCode(addrHash, codeHash common.Hash) ([]byte, error)
ContractCodeSize(addrHash, codeHash common.Hash) (int, error)
TrieDB() *trie.Database
}
OpenTrie是打开主账号的树,OpenStorageTrie是打开某个账号的数,CopyTrie就是复制,ContractCode是访问合约代码,ContractCodeSize获取合约大小,TrieDB访问底层数据库
new
提供了两个创建该数据库的方法,NewDatabase和NewDatabaseWithCache就是一个带缓冲一个不带缓冲
func NewDatabase(db ethdb.Database) Database {
return NewDatabaseWithCache(db, 0)
}
func NewDatabaseWithCache(db ethdb.Database, cache int) Database {
csc, _ := lru.New(codeSizeCacheSize)
return &cachingDB{
db:trie.NewDatabaseWithCache(db, cache),
codeSizeCache: csc,
}
}
构建了一个cachingDB对象,其中有一个实际的数据库用的是trie的NewDatabaseWithCache方法创建的
// go-ethereum\trie\database.go
func NewDatabaseWithCache(diskdb ethdb.Database, cache int) *Database {
var cleans *bigcache.BigCache
if cache > 0 {
cleans, _ = bigcache.NewBigCache(bigcache.Config{
Shards: 1024,
LifeWindow: time.Hour,
MaxEntriesInWindow: cache * 1024,
MaxEntrySize: 512,
HardMaxCacheSize: cache,
})
}
return &Database{
diskdb: diskdb,
cleans: cleans,
dirties: map[common.Hash]*cachedNode{{}: {}},
preimages: make(map[common.Hash][]byte),
}
}
OpenTrie
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
db.mu.Lock()
defer db.mu.Unlock()
for i := len(db.pastTries) - 1; i >= 0; i-- {
if db.pastTries[i].Hash() == root {
return cachedTrie{db.pastTries[i].Copy(), db}, nil
}
}
tr, err := trie.NewSecure(root, db.db, MaxTrieCacheGen)
if err != nil {
return nil, err
}
return cachedTrie{tr, db}, nil
}
前面说过这个方法是打开主账户的树,这里首先从pastTries中查找,有的话用一个cachedTrie对象包装后返回,没有的话调用NewSecure区创建一个,创建的是一个SecureTrie类型的数,前面我们分析trie源码时听到过,SecureTrie就是trie的包装。前面没有详细分析SecureTrie源码,这里来看一下:
func NewSecure(root common.Hash, db *Database, cachelimit uint16) (*SecureTrie, error) {
if db == nil {
panic("trie.NewSecure called without a database")
}
trie, err := New(root, db)
if err != nil {
return nil, err
}
trie.SetCacheLimit(cachelimit)
return &SecureTrie{trie: *trie}, nil
}
创建很简单,就是用New方法创建一个普通的树,然后构造出一个SecureTrie对象。OpenTrie方法最后返回的是一个cachedTrie对象,里面包装了SecureTrie和cachingDB。
OpenStorageTrie
这个很简单直接创建一个SecureTrie
func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
return trie.NewSecure(root, db.db, 0)
}
ContractCode & ContractCodeSize
func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
code, err := db.db.Node(codeHash)
if err == nil {
db.codeSizeCache.Add(codeHash, len(code))
}
return code, err
}
Node实际上就根据hash从数据库中取值,取值完成后我们会将其放入前面创建的缓存中,这是一个lru缓存,便于下次取。
func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
return cached.(int), nil
}
code, err := db.ContractCode(addrHash, codeHash)
return len(code), err
}
也是一个先取后计算长度的过程
Commit
Commit就是将树缓存起来
func (m cachedTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) {
root, err := m.SecureTrie.Commit(onleaf)
if err == nil {
m.db.pushTrie(m.SecureTrie)
}
return root, err
}
首先调用了SecureTrie的Commit方法,检查源码实际上调用了其包装的trie的commit方法,之前分析过trie源码,Commit是序列化方法,是将一颗树存储到数据库中,返回的是树根的hash。在cachedTrie的Commit方法中,如果没有错首先将原来的树缓存到pastTries中,然后返回提交的结果。
journal.go
先看数据结构
type journal struct {
entries []journalEntry // Current changes tracked by the journal
dirties map[common.Address]int // Dirty accounts and the number of changes
}
包含了一个journalEntry数组和一个map,journalEntry相当于一条日志,dirties记录了某个账户改动次数。journalEntry是一个接口,如下定义
type journalEntry interface {
revert(*StateDB)
dirtied() *common.Address
}
revert是用于撤销操作的,dirtied返回对应的账户地址。对应接口定义了大量的journalEntry类型对象
type (
createObjectChange struct {
account *common.Address
}
resetObjectChange struct {
prev *stateObject
}
suicideChange struct {
account *common.Address
prev bool // whether account had already suicided
prevbalance *big.Int
}
balanceChange struct {
account *common.Address
prev *big.Int
}
nonceChange struct {
account *common.Address
prev uint64
}
storageChange struct {
account *common.Address
key, prevalue common.Hash
}
codeChange struct {
account *common.Address
prevcode, prevhash []byte
}
refundChange struct {
prev uint64
}
addLogChange struct {
txhash common.Hash
}
addPreimageChange struct {
hash common.Hash
}
touchChange struct {
account *common.Address
prev bool
prevDirty bool
}
)
每种struct都对应了不同的事件日志。当然种都有revert和dirtied方法我们后面具体遇到了在分析。
state_object.go
stateObject代表了正在修改的以太坊账户,数据结构如下
type stateObject struct {
address common.Address
addrHash common.Hash
data Account
db *StateDB
dbErr error
trie Trie
code Code
originStorage Storage
dirtyStorage Storage
dirtyCode bool // true if the code was updated
suicided bool
deleted bool
}
根据源码注释,要想修改账户,需要首先获取stateObject,然后通过这个对象访问修改,最后提交进行持久化存储。
new
func newObject(db *StateDB, address common.Address, data Account) *stateObject {
if data.Balance == nil {
data.Balance = new(big.Int)
}
if data.CodeHash == nil {
data.CodeHash = emptyCodeHash
}
return &stateObject{
db: db,
address: address,
addrHash: crypto.Keccak256Hash(address[:]),
data: data,
originStorage: make(Storage),
dirtyStorage: make(Storage),
}
}
需要传入地址和账户的对象才能构造一个stateObject对象。
SetState
SetState是更新账户存储中的某个值
func (self *stateObject) SetState(db Database, key, value common.Hash) {
prev := self.GetState(db, key)
if prev == value {
return
}
self.db.journal.append(storageChange{
account: &self.address,
key: key,
prevalue: prev,
})
self.setState(key, value)
}
func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
value, dirty := self.dirtyStorage[key]
if dirty {
return value
}
return self.GetCommittedState(db, key)
}
func (self *stateObject) GetCommittedState(db Database, key common.Hash) common.Hash {
value, cached := self.originStorage[key]
if cached {
return value
}
enc, err := self.getTrie(db).TryGet(key[:])
if err != nil {
self.setError(err)
return common.Hash{}
}
if len(enc) > 0 {
_, content, _, err := rlp.Split(enc)
if err != nil {
self.setError(err)
}
value.SetBytes(content)
}
self.originStorage[key] = value
return value
}
func (c *stateObject) getTrie(db Database) Trie {
if c.trie == nil {
var err error
c.trie, err = db.OpenStorageTrie(c.addrHash, c.data.Root)
if err != nil {
c.trie, _ = db.OpenStorageTrie(c.addrHash, common.Hash{})
c.setError(fmt.Errorf("can't create storage trie: %v", err))
}
}
return c.trie
}
首先用了GetState方法去取旧的值,GetState方法中先访问dirtyStorage,这也是一个缓存机制,取不到的话调用GetCommittedState,GetCommittedState也是先访问originStorage这个缓存,originStorage与dirtyStorage的区别是,originStorage是已经持久化过的,dirtyStorage而是未持久化的。还找不到的话就需要访问本地数据库了,调用getTrie获取树。GetCommittedState方法如果是从数据库取值的话就向originStorage从缓存一份。
回到SetState中,如果旧值和新值一样,就直接返回,否则新建一个storageChange类型的journalEntry放入journal的entries中,最后调用setState方法,setState主要是向dirtyStorage中缓存。我们顺便看一下storageChange
storageChange struct {
account *common.Address
key, prevalue common.Hash
}
func (ch storageChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
}
func (ch storageChange) dirtied() *common.Address {
return ch.account
}
storageChange主要存储了状态改变前后的新旧值和对应地址。所以revert方法就是取对应状态恢复到旧值
SetBalance
func (self *stateObject) SetBalance(amount *big.Int) {
self.db.journal.append(balanceChange{
account: &self.address,
prev: new(big.Int).Set(self.data.Balance),
})
self.setBalance(amount)
}
func (self *stateObject) setBalance(amount *big.Int) {
self.data.Balance = amount
}
就是修改账户的余额,给日志添加了balanceChange,还有两个加减方法
func (c *stateObject) AddBalance(amount *big.Int) {
if amount.Sign() == 0 {
if c.empty() {
c.touch()
}
return
}
c.SetBalance(new(big.Int).Add(c.Balance(), amount))
}
func (c *stateObject) SubBalance(amount *big.Int) {
if amount.Sign() == 0 {
return
}
c.SetBalance(new(big.Int).Sub(c.Balance(), amount))
}
都要首先判断加减值是否为0,对于加的情况,如果值为0而且账户也为空(即nonce为0,余额为0,,账户代码也为空)则调用touch方法,touch方法个日志添加了touchChange操作。
下面我们先看一下balanceChange。touchChange没有实际操作
balanceChange struct {
account *common.Address
prev *big.Int
}
func (ch balanceChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setBalance(ch.prev)
}
func (ch balanceChange) dirtied() *common.Address {
return ch.account
}
也很简单,就是存储了先前的值,然后回滚时设置余额为之前的值。
SetCode
func (self *stateObject) SetCode(codeHash common.Hash, code []byte) {
prevcode := self.Code(self.db.db)
self.db.journal.append(codeChange{
account: &self.address,
prevhash: self.CodeHash(),
prevcode: prevcode,
})
self.setCode(codeHash, code)
}
func (self *stateObject) Code(db Database) []byte {
if self.code != nil {
return self.code
}
if bytes.Equal(self.CodeHash(), emptyCodeHash) {
return nil
}
code, err := db.ContractCode(self.addrHash, common.BytesToHash(self.CodeHash()))
if err != nil {
self.setError(fmt.Errorf("can't load code hash %x: %v", self.CodeHash(), err))
}
self.code = code
return code
}
func (self *stateObject) setCode(codeHash common.Hash, code []byte) {
self.code = code
self.data.CodeHash = codeHash[:]
self.dirtyCode = true
}
首先取之前的code,取得方法还是先从缓存取,取不到再去数据库取,有两集缓存,一级是stateObject的code字段,每次set都会更新,;另一级是Database的ContractCode取的时候会坚持codeSizeCache缓存,都没有再从数据库取。设置code时,也给日志添加了codeChange。
codeChange struct {
account *common.Address
prevcode, prevhash []byte
}
func (ch codeChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
}
func (ch codeChange) dirtied() *common.Address {
return ch.account
}
和storageChange逻辑类似
SetNonce
func (self *stateObject) SetNonce(nonce uint64) {
self.db.journal.append(nonceChange{
account: &self.address,
prev: self.data.Nonce,
})
self.setNonce(nonce)
}
func (self *stateObject) setNonce(nonce uint64) {
self.data.Nonce = nonce
}
nonceChange struct {
account *common.Address
prev uint64
}
func (ch nonceChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setNonce(ch.prev)
}
func (ch nonceChange) dirtied() *common.Address {
return ch.account
}
和balance一样的逻辑,不在多说
CommitTrie
刚才的set方法都只是改变了stateObject的值,没有实际的持久化,要持久化需要这个方法
func (self *stateObject) CommitTrie(db Database) error {
self.updateTrie(db)
if self.dbErr != nil {
return self.dbErr
}
root, err := self.trie.Commit(nil)
if err == nil {
self.data.Root = root
}
return err
}
func (self *stateObject) updateTrie(db Database) Trie {
tr := self.getTrie(db)
for key, value := range self.dirtyStorage {
delete(self.dirtyStorage, key)
if value == self.originStorage[key] {
continue
}
self.originStorage[key] = value
if (value == common.Hash{}) {
self.setError(tr.TryDelete(key[:]))
continue
}
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
self.setError(tr.TryUpdate(key[:], v))
}
return tr
}
首先调用updateTrie更新树,updateTrie中现获取了树,随后遍历了dirtyStorage,前面说过这是未持久化的值,取出的每个值先和originStorage中的比较,一样的就不操作,随后对于空值记录一个错误,最后先rlp编码,最后调用TryUpdate更新,TryUpdate是SecureTrie中的方法:
func (t *SecureTrie) TryUpdate(key, value []byte) error {
hk := t.hashKey(key)
err := t.trie.TryUpdate(hk, value)
if err != nil {
return err
}
t.getSecKeyCache()[string(hk)] = common.CopyBytes(key)
return nil
}
还是调用trie的TryUpdate方法,TryUpdate方法之前分析trie源码时没有将,不过也很简单
func (t *Trie) TryUpdate(key, value []byte) error {
k := keybytesToHex(key)
if len(value) != 0 {
_, n, err := t.insert(t.root, nil, k, valueNode(value))
if err != nil {
return err
}
t.root = n
} else {
_, n, err := t.delete(t.root, nil, k)
if err != nil {
return err
}
t.root = n
}
return nil
}
首先对于key长度不为0的话就进行插入来更新,否则执行删除逻辑。回到stateobject中,在更新完毕后,如果没错,就调用commit方法持久化存储。
statedb.go
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
validRevisions []revision
nextRevisionId int
}
基本包含了前面分析的三种结构体。
new
func New(root common.Hash, db Database) (*StateDB, error) {
tr, err := db.OpenTrie(root)
if err != nil {
return nil, err
}
return &StateDB{
db: db,
trie: tr,
stateObjects: make(map[common.Address]*stateObject),
stateObjectsDirty: make(map[common.Address]struct{}),
logs: make(map[common.Hash][]*types.Log),
preimages: make(map[common.Hash][]byte),
journal: newJournal(),
}, nil
}
首先调用了OpenTrie打开主账户的树,然后构造了StateDB对象。
AddLog
这个log并不是程序log,它代表合约的log事件
func (self *StateDB) AddLog(log *types.Log) {
self.journal.append(addLogChange{txhash: self.thash})
log.TxHash = self.thash
log.BlockHash = self.bhash
log.TxIndex = uint(self.txIndex)
log.Index = self.logSize
self.logs[self.thash] = append(self.logs[self.thash], log)
self.logSize++
}
首先补全log信息,然后按交易hash放到logs中,关于thash和bhash是在Prepare中设置的
func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {
self.thash = thash
self.bhash = bhash
self.txIndex = ti
}
在AddLog开始的时候还向journal添加了addLogChange
addLogChange struct {
txhash common.Hash
}
func (ch addLogChange) revert(s *StateDB) {
logs := s.logs[ch.txhash]
if len(logs) == 1 {
delete(s.logs, ch.txhash)
} else {
s.logs[ch.txhash] = logs[:len(logs)-1]
}
s.logSize--
}
func (ch addLogChange) dirtied() *common.Address {
return nil
}
addLogChange只有一个字段,它的回滚就是将最后的log删除。此外关于log还有两个方法,都是取的
func (self *StateDB) GetLogs(hash common.Hash) []*types.Log {
return self.logs[hash]
}
func (self *StateDB) Logs() []*types.Log {
var logs []*types.Log
for _, lgs := range self.logs {
logs = append(logs, lgs...)
}
return logs
}
GetXxx & SetXxx
有一系列get和set方法,也就体现了statedb作为对外开放接口的作用
func (self *StateDB) GetBalance(addr common.Address) *big.Int {
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.Balance()
}
return common.Big0
}
func (self *StateDB) GetNonce(addr common.Address) uint64 {
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.Nonce()
}
return 0
}
func (self *StateDB) GetCode(addr common.Address) []byte {
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.Code(self.db)
}
return nil
}
func (self *StateDB) GetCodeSize(addr common.Address) int {
stateObject := self.getStateObject(addr)
if stateObject == nil {
return 0
}
if stateObject.code != nil {
return len(stateObject.code)
}
size, err := self.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
if err != nil {
self.setError(err)
}
return size
}
func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
stateObject := self.getStateObject(addr)
if stateObject == nil {
return common.Hash{}
}
return common.BytesToHash(stateObject.CodeHash())
}
func (self *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.GetState(self.db, hash)
}
return common.Hash{}
}
func (self *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.GetCommittedState(self.db, hash)
}
return common.Hash{}
}
func (self *StateDB) StorageTrie(addr common.Address) Trie {
stateObject := self.getStateObject(addr)
if stateObject == nil {
return nil
}
cpy := stateObject.deepCopy(self)
return cpy.updateTrie(self.db)
}
func (self *StateDB) HasSuicided(addr common.Address) bool {
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.suicided
}
return false
}
set类方法如下
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.AddBalance(amount)
}
}
func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SubBalance(amount)
}
}
func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetBalance(amount)
}
}
func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetNonce(nonce)
}
}
func (self *StateDB) SetCode(addr common.Address, code []byte) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetCode(crypto.Keccak256Hash(code), code)
}
}
func (self *StateDB) SetState(addr common.Address, key, value common.Hash) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetState(self.db, key, value)
}
}
func (self *StateDB) Suicide(addr common.Address) bool {
stateObject := self.getStateObject(addr)
if stateObject == nil {
return false
}
self.journal.append(suicideChange{
account: &addr,
prev: stateObject.suicided,
prevbalance: new(big.Int).Set(stateObject.Balance()),
})
stateObject.markSuicided()
stateObject.data.Balance = new(big.Int)
return true
}
基本上都是通过getStateObject先获取stateObject,前面说过stateObject代表正在修改的以太坊账户,通过他也就可以访问各项内容
操作StateObject
get方法:
func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
if obj := self.stateObjects[addr]; obj != nil {
if obj.deleted {
return nil
}
return obj
}
enc, err := self.trie.TryGet(addr[:])
if len(enc) == 0 {
self.setError(err)
return nil
}
var data Account
if err := rlp.DecodeBytes(enc, &data); err != nil {
log.Error("Failed to decode state object", "addr", addr, "err", err)
return nil
}
obj := newObject(self, addr, data)
self.setStateObject(obj)
return obj
}
func (self *StateDB) deleteStateObject(stateObject *stateObject) {
stateObject.deleted = true
addr := stateObject.Address()
self.setError(self.trie.TryDelete(addr[:]))
}
func (self *StateDB) updateStateObject(stateObject *stateObject) {
addr := stateObject.Address()
data, err := rlp.EncodeToBytes(stateObject)
if err != nil {
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
}
self.setError(self.trie.TryUpdate(addr[:], data))
}
func (self *StateDB) setStateObject(object *stateObject) {
self.stateObjects[object.Address()] = object
}
func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
stateObject := self.getStateObject(addr)
if stateObject == nil || stateObject.deleted {
stateObject, _ = self.createObject(addr)
}
return stateObject
}
func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
prev = self.getStateObject(addr)
newobj = newObject(self, addr, Account{})
newobj.setNonce(0) // sets the object to dirty
if prev == nil {
self.journal.append(createObjectChange{account: &addr})
} else {
self.journal.append(resetObjectChange{prev: prev})
}
self.setStateObject(newobj)
return newobj, prev
}
首先尝试stateObjects回去,没有的话用newObject构造一个,同时存入setStateObject中。
快照
Snapshot可以创建一个快照,然后通过RevertToSnapshot可以回滚到哪个状态,这个功能是通过journal来做到的。
func (self *StateDB) Snapshot() int {
id := self.nextRevisionId
self.nextRevisionId++
self.validRevisions = append(self.validRevisions, revision{id, self.journal.length()})
return id
}
func (self *StateDB) RevertToSnapshot(revid int) {
idx := sort.Search(len(self.validRevisions), func(i int) bool {
return self.validRevisions[i].id >= revid
})
if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid {
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
}
snapshot := self.validRevisions[idx].journalIndex
self.journal.revert(self, snapshot)
self.validRevisions = self.validRevisions[:idx]
}
func (j *journal) revert(statedb *StateDB, snapshot int) {
for i := len(j.entries) - 1; i >= snapshot; i-- {
j.entries[i].revert(statedb)
if addr := j.entries[i].dirtied(); addr != nil {
if j.dirties[*addr]--; j.dirties[*addr] == 0 {
delete(j.dirties, *addr)
}
}
}
j.entries = j.entries[:snapshot]
}
可见创建快照是记录了RevisionId和journal中日志数量。恢复则是确定该位置的journal中日志数量snapshot,然后调用revert,将日志集合中snapshot之后的事件全部执行回滚。
commit
commit用于提交更改
func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
defer s.clearJournalAndRefund()
for addr := range s.journal.dirties {
s.stateObjectsDirty[addr] = struct{}{}
}
for addr, stateObject := range s.stateObjects {
_, isDirty := s.stateObjectsDirty[addr]
switch {
case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()):
s.deleteStateObject(stateObject)
case isDirty:
if stateObject.code != nil && stateObject.dirtyCode {
s.db.TrieDB().InsertBlob(common.BytesToHash(stateObject.CodeHash()), stateObject.code)
stateObject.dirtyCode = false
}
if err := stateObject.CommitTrie(s.db); err != nil {
return common.Hash{}, err
}
s.updateStateObject(stateObject)
}
delete(s.stateObjectsDirty, addr)
}
root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error {
var account Account
if err := rlp.DecodeBytes(leaf, &account); err != nil {
return nil
}
if account.Root != emptyState {
s.db.TrieDB().Reference(account.Root, parent)
}
code := common.BytesToHash(account.CodeHash)
if code != emptyCode {
s.db.TrieDB().Reference(code, parent)
}
return nil
})
log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
return root, err
}
func (s *StateDB) clearJournalAndRefund() {
s.journal = newJournal()
s.validRevisions = s.validRevisions[:0]
s.refund = 0
}
首先遍历了journal.dirties,他存储着每个地址操作的次数,然后在stateObjectsDirty进行记录,表示相应地址有改动。随后遍历stateObjects,他保存着各个地址的stateObject。
如果某个地址有过操作,首先处理code,如果code有更新则调用InsertBlob进行插入。随后调用stateObject的CommitTrie提交这个签名分析过,最终结果是将树写入数据库,之后更新了StateObject。注意前面只是提交各个stateObject的树,回顾stateObject源码,stateObject成员中的树是通过OpenStorageTrie获取的,其中的root参数是通过StateDB的树种获取的,而StateDB成员中的树是通过OpenTrie获取的。stateObject只是代表一个个以太坊账户,所以还要对StateDB的树进行提交。
题图来自unsplash:https://unsplash.com/photos/1Z2niiBPg5A