美文网首页区块链教程
兄弟连区块链教程以太坊源码分析core-state源码分析(二)

兄弟连区块链教程以太坊源码分析core-state源码分析(二)

作者: ab6973df9221 | 来源:发表于2018-10-22 17:14 被阅读0次

      兄弟连区块链教程以太坊源码分析core-state源码分析,2018年下半年,区块链行业正逐渐褪去发展之初的浮躁、回归理性,表面上看相关人才需求与身价似乎正在回落。但事实上,正是初期泡沫的渐退,让人们更多的关注点放在了区块链真正的技术之上。

    ## statedb.go

    stateDB用来存储以太坊中关于merkle trie的所有内容。 StateDB负责缓存和存储嵌套状态。 这是检索合约和账户的一般查询界面:

    数据结构

        type StateDB struct {

    db Database // 后端的数据库

    trie Trie    // trie树 main account trie

            // This map holds 'live' objects, which will get modified while processing a state transition.

    // 下面的Map用来存储当前活动的对象,这些对象在状态转换的时候会被修改。

    // stateObjects 用来缓存对象

    // stateObjectsDirty用来缓存被修改过的对象。

            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 here and will eventually be returned

            // by StateDB.Commit.

            dbErr error

            // The refund counter, also used by state transitioning.

    // refund计数器。 暂时还不清楚功能。

            refund *big.Int

    thash, bhash common.Hash //当前的transaction hash 和block hash

    txIndex int         // 当前的交易的index

    logs map[common.Hash][]*types.Log // 日志 key是交易的hash值

            logSize uint

    preimages map[common.Hash][]byte // EVM计算的 SHA3->byte[]的映射关系

            // Journal of state modifications. This is the backbone of

            // Snapshot and RevertToSnapshot.

    // 状态修改日志。 这是Snapshot和RevertToSnapshot的支柱。

            journal journal

            validRevisions []revision

            nextRevisionId int

            lock sync.Mutex

        }

    构造函数

    // 一般的用法 statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))

        // Create a new state from a given trie

        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{}),

                refund: new(big.Int),

                logs: make(map[common.Hash][]*types.Log),

                preimages: make(map[common.Hash][]byte),

            }, nil

        }

    ### 对于Log的处理

    state提供了Log的处理,这比较意外,因为Log实际上是存储在区块链中的,并没有存储在state trie中, state提供Log的处理, 使用了基于下面的几个函数。 奇怪的是暂时没看到如何删除logs里面的信息,如果不删除的话,应该会越积累越多。 TODO logs 删除

    Prepare函数,在交易执行开始被执行。

    AddLog函数,在交易执行过程中被VM执行。添加日志。同时把日志和交易关联起来,添加部分交易的信息。

    GetLogs函数,交易完成取走。

        // Prepare sets the current transaction hash and index and block hash which is

        // used when the EVM emits new state logs.

        func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {

            self.thash = thash

            self.bhash = bhash

            self.txIndex = ti

        }

        func (self *StateDB) AddLog(log *types.Log) {

            self.journal = append(self.journal, 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++

        }

        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

        }

    ### stateObject处理

    getStateObject,首先从缓存里面获取,如果没有就从trie树里面获取,并加载到缓存。

        // Retrieve a state object given my the address. Returns nil if not found.

        func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {

            // Prefer 'live' objects.

            if obj := self.stateObjects[addr]; obj != nil {

                if obj.deleted {

                    return nil

                }

                return obj

            }

            // Load the object from the database.

            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

            }

            // Insert into the live set.

            obj := newObject(self, addr, data, self.MarkStateObjectDirty)

            self.setStateObject(obj)

            return obj

        }

    MarkStateObjectDirty, 设置一个stateObject为Dirty。 直接往stateObjectDirty对应的地址插入一个空结构体。

        // MarkStateObjectDirty adds the specified object to the dirty map to avoid costly

        // state object cache iteration to find a handful of modified ones.

        func (self *StateDB) MarkStateObjectDirty(addr common.Address) {

            self.stateObjectsDirty[addr] = struct{}{}

        }

    ### 快照和回滚功能

    Snapshot可以创建一个快照, 然后通过  RevertToSnapshot可以回滚到哪个状态,这个功能是通过journal来做到的。 每一步的修改都会往journal里面添加一个undo日志。 如果需要回滚只需要执行undo日志就行了。

        // Snapshot returns an identifier for the current revision of the state.

        func (self *StateDB) Snapshot() int {

            id := self.nextRevisionId

            self.nextRevisionId++

            self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)})

            return id

        }

        // RevertToSnapshot reverts all state changes made since the given revision.

        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.

            for i := len(self.journal) - 1; i >= snapshot; i-- {

                self.journal[i].undo(self)

            }

            self.journal = self.journal[:snapshot]

            // Remove invalidated snapshots from the stack.

            self.validRevisions = self.validRevisions[:idx]

        }

    ### 获取中间状态的 root hash值

    IntermediateRoot 用来计算当前的state trie的root的hash值。这个方法会在交易执行的过程中被调用。会被存入 transaction receipt

    Finalise方法会调用update方法把存放在cache层的修改写入到trie数据库里面。 但是这个时候还没有写入底层的数据库。 还没有调用commit,数据还在内存里面,还没有落地成文件。

        // Finalise finalises the state by removing the self destructed objects

        // and clears the journal as well as the refunds.

        func (s *StateDB) Finalise(deleteEmptyObjects bool) {

            for addr := range s.stateObjectsDirty {

                stateObject := s.stateObjects[addr]

                if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) {

                    s.deleteStateObject(stateObject)

                } else {

                    stateObject.updateRoot(s.db)

                    s.updateStateObject(stateObject)

                }

            }

            // Invalidate journal because reverting across transactions is not allowed.

            s.clearJournalAndRefund()

        }

        // IntermediateRoot computes the current root hash of the state trie.

        // It is called in between transactions to get the root hash that

        // goes into transaction receipts.

        func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {

            s.Finalise(deleteEmptyObjects)

            return s.trie.Hash()

        }

    ### commit方法

    CommitTo用来提交更改。

        // CommitTo writes the state to the given database.

        func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) {

            defer s.clearJournalAndRefund()

            // 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

                    if stateObject.code != nil && stateObject.dirtyCode {

                        if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil {

                            return common.Hash{}, err

                        }

                        stateObject.dirtyCode = false

                    }

                    // Write any storage changes in the state object to its storage trie.

                    if err := stateObject.CommitTrie(s.db, dbw); err != nil {

                        return common.Hash{}, err

                    }

                    // Update the object in the main account trie.

                    s.updateStateObject(stateObject)

                }

                delete(s.stateObjectsDirty, addr)

            }

            // Write trie changes.

            root, err = s.trie.CommitTo(dbw)

            log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())

            return root, err

        }

    ### 总结

    state包提供了用户和合约的状态管理的功能。 管理了状态和合约的各种状态转换。 cache, trie, 数据库。 日志和回滚功能。

    相关文章

      网友评论

        本文标题:兄弟连区块链教程以太坊源码分析core-state源码分析(二)

        本文链接:https://www.haomeiwen.com/subject/rcwgzftx.html