// Account is the Ethereum consensus representation of accounts. // These objects are stored in the main account trie. // Account是账户的数据,不包含账户地址 // 账户需要使用地址来表示,地址在stateObject中 type Account struct { // 每执行1次交易,Nonce+1 Nonce uint64 Balance *big.Int // 该账户的状态,即trie的根 Root common.Hash // merkle root of the storage trie // 合约账户专属,合约代码编译后的Hash值 CodeHash []byte }
以上是账户的数据,那如何表示一个账户呢? 使用账户地址表示账户,它记录在stateObject中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// stateObject represents an Ethereum account which is being modified. // // The usage pattern is as follows: // First you need to obtain a state object. // Account values can be accessed and modified through the object. // Finally, call CommitTrie to write the modified storage trie into a database. // 地址、账户、账户哈希、数据库 type stateObject struct { // 账户信息 address common.Address addrHash common.Hash // hash of ethereum address of the account data Account
type stateObject struct { // 账户信息 address common.Address addrHash common.Hash // hash of ethereum address of the account data Account // 所属于的stateDB db *StateDB
// DB error. // State objects are used by the consensus core and VM which are // unable to deal with database-level errors. Any error that occurs // during a database read is memoized http://lessisbetter.site/2018/06/22/ethereum-code-statedb/ and will eventually be returned // by StateDB.Commit. // VM不处理db层的错误,先记录下来,最后返回,只能保存1个错误,保存存的第一个错误 dbErr error
// Write caches. // 使用trie组织stateObj的数据 trie Trie // storage trie, which becomes non-nil on first access // 合约代码 code Code // contract bytecode, which gets set when code is loaded
// 存缓存,避免重复从数据库读 cachedStorage Storage // Storage entry cache to avoid duplicate reads // 需要写到磁盘的缓存 dirtyStorage Storage // Storage entries that need to be flushed to disk
// Cache flags. // When an object is marked suicided it will be delete from the trie // during the "update" phase of the state transition. dirtyCode bool// true if the code was updated // 标记suicided,代表这个对象要从trie删除,在update阶段 suicided bool deleted bool }
// SetState updates a value in account storage. // 设置一个新的kv:保存过去的kv,然后设置新的。 func(self *stateObject)SetState(db Database, key, value common.Hash) { self.db.journal.append(storageChange{ account: &self.address, key: key, prevalue: self.GetState(db, key), }) self.setState(key, value) }
// 先加入缓存和dirty func(self *stateObject)setState(key, value common.Hash) { self.cachedStorage[key] = value self.dirtyStorage[key] = value }
// updateTrie writes cached storage modifications into the object's storage trie. // 把标记为dirty的kv写入、删除、更新到存储trie、 func(self *stateObject)updateTrie(db Database)Trie { tr := self.getTrie(db) for key, value := range self.dirtyStorage { delete(self.dirtyStorage, key) // 空value代表删除kv if (value == common.Hash{}) { self.setError(tr.TryDelete(key[:])) continue } // Encoding []byte cannot fail, ok to ignore the error. v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) self.setError(tr.TryUpdate(key[:], v)) } return tr }
// UpdateRoot sets the trie root to the current root hash of // 更新root:更新trie,然后获取新的root。Finalize使用 func(self *stateObject)updateRoot(db Database) { self.updateTrie(db) self.data.Root = self.trie.Hash() }
// StateDBs within the ethereum protocol are used to store anything // within the merkle trie. StateDBs take care of caching and storing // nested states. It's the general query interface to retrieve: // * Contracts // * Accounts // 在merkle树种保存任何数据,形式是kv type StateDB struct { // 存储本Trie的数据库 db Database // 存储所有的stateObject trie Trie
// This map holds 'live' objects, which will get modified while processing a state transition. // 最近使用过的数据对象,他们的账户地址为key stateObjects map[common.Address]*stateObject // 修改过的账户对象 stateObjectsDirty map[common.Address]struct{}
// DB error. // State objects are used by the consensus core and VM which are // unable to deal with database-level errors. Any error that occurs // during a database read is memoized http://lessisbetter.site/2018/06/22/ethereum-code-statedb/ and will eventually be returned // by StateDB.Commit. dbErr error
// The refund counter, also used by state transitioning. refund uint64
thash, bhash common.Hash txIndex int logs map[common.Hash][]*types.Log logSize uint
preimages map[common.Hash][]byte
// Journal of state modifications. This is the backbone of // Snapshot and RevertToSnapshot. // 快照和回滚的主要参数 // 存放每一步修改了啥 journal *journal // 快照id和journal的长度组成revision,可以回滚 validRevisions []revision // 下一个可用的快照id nextRevisionId int
// CreateAccount explicitly creates a state object. If a state object with the address // already exists the balance is carried over to the new account. // // CreateAccount is called during the EVM CREATE operation. The situation might arise that // a contract does the following: // // 1. sends funds to sha(account ++ (nonce + 1)) // 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) // // Carrying over the balance ensures that Ether doesn't disappear. // 创建一个新的空账户,如果存在该地址的旧账户,则把旧地址中的余额,放到新账户中 func(self *StateDB)CreateAccount(addr common.Address) { new, prev := self.createObject(addr) if prev != nil { new.setBalance(prev.data.Balance) } }
// createObject creates a new state object. If there is an existing account with // the given address, it is overwritten and returned as the second return value. // 创建一个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 }
// Retrieve a state object given by the address. Returns nil if not found. // stateDB中使用trie保存addr到stateObject的映射,stateObject中保存key到value的映射 // 先从stateObjects中读取,否则从Trie读取Account,然后创建stateObject,存到stateObjects func(self *StateDB)getStateObject(addr common.Address)(stateObject *stateObject) { // Prefer 'live' objects. if obj := self.stateObjects[addr]; obj != nil { if obj.deleted { returnnil } return obj }
// Load the object from the database. enc, err := self.trie.TryGet(addr[:]) iflen(enc) == 0 { self.setError(err) returnnil } // trie中实际实际保存的是Account var data Account if err := rlp.DecodeBytes(enc, &data); err != nil { log.Error("Failed to decode state object", "addr", addr, "err", err) returnnil } // Insert into the live set. obj := newObject(self, addr, data) self.setStateObject(obj) return obj }
// Retrieve a state object or create a new state object if nil. // 获取stateObject,不存在则创建 func(self *StateDB)GetOrNewStateObject(addr common.Address) *stateObject { stateObject := self.getStateObject(addr) if stateObject == nil || stateObject.deleted { stateObject, _ = self.createObject(addr) } return stateObject }
// Finalise finalises the state by removing the self destructed objects // and clears the journal as well as the refunds. // 最终化数据库,遍历的日志中标记为dirty的账户,删除部分自杀、或空的数据,然后把数据写入存储trie,然后更新root,但每个对象都没有commit func(s *StateDB)Finalise(deleteEmptyObjects bool) { // 只处理journal中标记为dirty的对象,不处理stateObjectsDirty中的对象 for addr := range s.journal.dirties { stateObject, exist := s.stateObjects[addr] if !exist { // ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 // That tx goes out of gas, and although the notion of 'touched' does not exist there, the // touch-event will still be recorded in the journal. Since ripeMD is a special snowflake, // it will persist in the journal even though the journal is reverted. In this special circumstance, // it may exist in `s.journal.dirties` but not in `s.stateObjects`. // Thus, we can safely ignore it http://lessisbetter.site/2018/06/22/ethereum-code-statedb/ continue }
if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { s.deleteStateObject(stateObject) } else { // 把对象数据写入到storage trie,并获取新的root stateObject.updateRoot(s.db) s.updateStateObject(stateObject) } // 加入到stateObjectsDirty s.stateObjectsDirty[addr] = struct{}{} } // Invalidate journal because reverting across transactions is not allowed. // 清空journal,没法再回滚了 s.clearJournalAndRefund() }
// Commit writes the state to the underlying in-memory trie database. // 把数据写入trie数据库,与Finalize不同,这里处理的是Dirty的对象 func(s *StateDB)Commit(deleteEmptyObjects bool)(root common.Hash, err error) { // 清空journal无法再回滚 defer s.clearJournalAndRefund()
// 把journal中dirties的对象,加入到stateObjectsDirty for addr := range s.journal.dirties { s.stateObjectsDirty[addr] = struct{}{} } // Commit objects to the trie. // 遍历所有活动/修改过的对象 for addr, stateObject := range s.stateObjects { _, isDirty := s.stateObjectsDirty[addr] switch { case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()): // If the object has been removed, don't bother syncing it // and just mark it for deletion in the trie. s.deleteStateObject(stateObject) case isDirty: // Write any contract code associated with the state object // 把修改过的合约代码写到数据库,这个用法高级,直接把数据库拿过来,插进去 // 注意:这里写入的DB是stateDB的数据库,因为stateObject的Trie只保存Account信息 if stateObject.code != nil && stateObject.dirtyCode { s.db.TrieDB().Insert(common.BytesToHash(stateObject.CodeHash()), stateObject.code) stateObject.dirtyCode = false } // Write any storage changes in the state object to its storage trie. // 对象提交:把任何改变的存储数据写到数据库 if err := stateObject.CommitTrie(s.db); err != nil { return common.Hash{}, err } // Update the object in the main account trie. // 把修改后的对象,编码后写入到stateDB的trie中 s.updateStateObject(stateObject) } delete(s.stateObjectsDirty, addr) } // Write trie changes. // stateDB的提交 root, err = s.trie.Commit(func(leaf []byte, parent common.Hash)error { var account Account if err := rlp.DecodeBytes(leaf, &account); err != nil { returnnil } // 如果叶子节点的trie不空,则trie关联到父节点 if account.Root != emptyState { // reference的功能还没搞懂 s.db.TrieDB().Reference(account.Root, parent) } // 如果叶子节点的code不空(合约账户),则把code关联到父节点 code := common.BytesToHash(account.CodeHash) if code != emptyCode { s.db.TrieDB().Reference(code, parent) } returnnil }) log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads()) return root, err }
// Snapshot returns an identifier for the current revision of the state. // 快照只是一个id,把id和日志的长度关联起来,存到Revisions中 // EVM在执行在运行一个交易时,在修改state之前,创建快照,出现错误,则回滚 func(self *StateDB)Snapshot()int { id := self.nextRevisionId self.nextRevisionId++ self.validRevisions = append(self.validRevisions, revision{id, self.journal.length()}) return id }
// RevertToSnapshot reverts all state changes made since the given revision. // 回滚到指定vision/快照 func(self *StateDB)RevertToSnapshot(revid int) { // Find the snapshot in the stack of valid snapshots. 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
// Replay the journal to undo changes and remove invalidated snapshots // 反操作后续的操作,达到回滚的目的 self.journal.revert(self, snapshot) self.validRevisions = self.validRevisions[:idx] }