ITPub博客

首页 > 区块链 > 区块链 > 区块链教程以太坊源码分析core-state源码分析(二)

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

原创 区块链 作者:兄弟连区块链入门教程 时间:2018-10-22 16:31:43 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包提供了用户和合约的状态管理的功能。 管理了状态和合约的各种状态转换。 cach



来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/31557831/viewspace-2217109/,如需转载,请注明出处,否则将追究法律责任。

全部评论

注册时间:2018-10-11

  • 博文量
    79
  • 访问量
    46536