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

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

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

      兄弟连区块链教程以太坊源码分析core-state源码分析,core/state 包主要为以太坊的state trie提供了一层缓存层(cache)

    - database主要提供了trie树的抽象,提供trie树的缓存和合约代码长度的缓存。

    - journal主要提供了操作日志,以及操作回滚的功能。

    - state_object是account对象的抽象,提供了账户的一些功能。

    - statedb主要是提供了state trie的部分功能。

    ## database.go

    database.go 提供了一个数据库的抽象。

    数据结构

        // Database wraps access to tries and contract code.

        type Database interface {

            // Accessing tries:

            // OpenTrie opens the main account trie.

            // OpenStorageTrie opens the storage trie of an account.

    // OpenTrie 打开了主账号的trie树

    // OpenStorageTrie 打开了一个账号的storage trie

            OpenTrie(root common.Hash) (Trie, error)

            OpenStorageTrie(addrHash, root common.Hash) (Trie, error)

            // Accessing contract code:

    // 访问合约代码

            ContractCode(addrHash, codeHash common.Hash) ([]byte, error)

    // 访问合约的大小。 这个方法可能经常被调用。因为有缓存。

            ContractCodeSize(addrHash, codeHash common.Hash) (int, error)

            // CopyTrie returns an independent copy of the given trie.

    // CopyTrie 返回了一个指定trie的独立的copy

            CopyTrie(Trie) Trie

        }

        // NewDatabase creates a backing store for state. The returned database is safe for

        // concurrent use and retains cached trie nodes in memory.

        func NewDatabase(db ethdb.Database) Database {

            csc, _ := lru.New(codeSizeCacheSize)

            return &cachingDB{db: db, codeSizeCache: csc}

        }

        type cachingDB struct {

            db ethdb.Database

            mu sync.Mutex

    pastTries []*trie.SecureTrie //trie树的缓存

    codeSizeCache *lru.Cache         //合约代码大小的缓存

        }

    OpenTrie,从缓存里面查找。如果找到了返回缓存的trie的copy, 否则重新构建一颗树返回。

        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

        }

        func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {

            return trie.NewSecure(root, db.db, 0)

        }

    ContractCode 和 ContractCodeSize, ContractCodeSize有缓存。

        func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {

            code, err := db.db.Get(codeHash[:])

            if err == nil {

                db.codeSizeCache.Add(codeHash, len(code))

            }

            return code, err

        }

        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)

            if err == nil {

                db.codeSizeCache.Add(codeHash, len(code))

            }

            return len(code), err

        }

    cachedTrie的结构和commit方法,commit的时候会调用pushTrie方法把之前的Trie树缓存起来。

        // cachedTrie inserts its trie into a cachingDB on commit.

        type cachedTrie struct {

            *trie.SecureTrie

            db *cachingDB

        }

        func (m cachedTrie) CommitTo(dbw trie.DatabaseWriter) (common.Hash, error) {

            root, err := m.SecureTrie.CommitTo(dbw)

            if err == nil {

                m.db.pushTrie(m.SecureTrie)

            }

            return root, err

        }

        func (db *cachingDB) pushTrie(t *trie.SecureTrie) {

            db.mu.Lock()

            defer db.mu.Unlock()

            if len(db.pastTries) >= maxPastTries {

                copy(db.pastTries, db.pastTries[1:])

                db.pastTries[len(db.pastTries)-1] = t

            } else {

                db.pastTries = append(db.pastTries, t)

            }

        }

    ## journal.go

    journal代表了操作日志, 并针对各种操作的日志提供了对应的回滚功能。 可以基于这个日志来做一些事务类型的操作。

    类型定义,定义了journalEntry这个接口,提供了undo的功能。 journal 就是journalEntry的列表。

        type journalEntry interface {

            undo(*StateDB)

        }

        type journal []journalEntry

    各种不同的日志类型以及undo方法。

    createObjectChange struct { //创建对象的日志。 undo方法就是从StateDB中删除创建的对象。

            account *common.Address

        }

        func (ch createObjectChange) undo(s *StateDB) {

            delete(s.stateObjects, *ch.account)

            delete(s.stateObjectsDirty, *ch.account)

        }

    // 对于stateObject的修改, undo方法就是把值改为原来的对象。

        resetObjectChange struct {

            prev *stateObject

        }

        func (ch resetObjectChange) undo(s *StateDB) {

            s.setStateObject(ch.prev)

        }

    // 自杀的更改。自杀应该是删除账号,但是如果没有commit的化,对象还没有从stateDB删除。

        suicideChange struct {

            account *common.Address

            prev bool // whether account had already suicided

            prevbalance *big.Int

        }

        func (ch suicideChange) undo(s *StateDB) {

            obj := s.getStateObject(*ch.account)

            if obj != nil {

                obj.suicided = ch.prev

                obj.setBalance(ch.prevbalance)

            }

        }

        // Changes to individual accounts.

        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

        }

        func (ch balanceChange) undo(s *StateDB) {

            s.getStateObject(*ch.account).setBalance(ch.prev)

        }

        func (ch nonceChange) undo(s *StateDB) {

            s.getStateObject(*ch.account).setNonce(ch.prev)

        }

        func (ch codeChange) undo(s *StateDB) {

            s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)

        }

        func (ch storageChange) undo(s *StateDB) {

            s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)

        }

    // 我理解是DAO事件的退款处理

        refundChange struct {

            prev *big.Int

        }

        func (ch refundChange) undo(s *StateDB) {

            s.refund = ch.prev

        }

    // 增加了日志的修改

        addLogChange struct {

            txhash common.Hash

        }

        func (ch addLogChange) undo(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--

        }

    // 这个是增加 VM看到的 SHA3的 原始byte[], 增加SHA3 hash -> byte[] 的对应关系

        addPreimageChange struct {

            hash common.Hash

        }

        func (ch addPreimageChange) undo(s *StateDB) {

            delete(s.preimages, ch.hash)

        }

        touchChange struct {

            account *common.Address

            prev bool

            prevDirty bool

        }

        var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")

        func (ch touchChange) undo(s *StateDB) {

            if !ch.prev && *ch.account != ripemd {

                s.getStateObject(*ch.account).touched = ch.prev

                if !ch.prevDirty {

                    delete(s.stateObjectsDirty, *ch.account)

                }

            }

        }

    ## state_object.go

    stateObject表示正在修改的以太坊帐户。

    数据结构

        type Storage map[common.Hash]common.Hash

        // stateObject represents an Ethereum account which is being modified.

    // stateObject表示正在修改的以太坊帐户。

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

    使用模式如下:

    首先你需要获得一个state_object。

    帐户值可以通过对象访问和修改。

    最后,调用CommitTrie将修改后的存储trie写入数据库。

        type stateObject struct {

            address common.Address

    addrHash common.Hash // hash of ethereum address of the account 以太坊账号地址的hash值

    data Account // 这个是实际的以太坊账号的信息

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

            // by StateDB.Commit.

            //

    数据库错误。

    stateObject会被共识算法的核心和VM使用,在这些代码内部无法处理数据库级别的错误。

    在数据库读取期间发生的任何错误都会在这里被存储,最终将由StateDB.Commit返回。

            dbErr error

    // Write caches. 写缓存

    trie Trie // storage trie, which becomes non-nil on first access 用户的存储trie ,在第一次访问的时候变得非空

    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. Cache 标志

            // 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 如果代码被更新,会设置为true

            suicided bool

            touched bool

            deleted bool

    onDirty func(addr common.Address) // Callback method to mark a state object newly dirty 第一次被设置为drity的时候会被调用。

        }

        // Account is the Ethereum consensus representation of accounts.

        // These objects are stored in the main account trie.

    // 帐户是以太坊共识表示的帐户。 这些对象存储在main account trie。

        type Account struct {

            Nonce uint64

            Balance *big.Int

            Root common.Hash // merkle root of the storage trie

            CodeHash []byte

        }

    构造函数

        // newObject creates a state object.

        func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *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,

                cachedStorage: make(Storage),

                dirtyStorage: make(Storage),

                onDirty: onDirty,

            }

        }

    RLP的编码方式,只会编码 Account对象。

        // EncodeRLP implements rlp.Encoder.

        func (c *stateObject) EncodeRLP(w io.Writer) error {

            return rlp.Encode(w, c.data)

        }

    一些状态改变的函数。

        func (self *stateObject) markSuicided() {

            self.suicided = true

            if self.onDirty != nil {

                self.onDirty(self.Address())

                self.onDirty = nil

            }

        }

        func (c *stateObject) touch() {

            c.db.journal = append(c.db.journal, touchChange{

                account: &c.address,

                prev: c.touched,

                prevDirty: c.onDirty == nil,

            })

            if c.onDirty != nil {

                c.onDirty(c.Address())

                c.onDirty = nil

            }

            c.touched = true

        }

    Storage的处理

    // getTrie返回账户的Storage Trie

        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 returns a value in account storage.

    // GetState 返回account storage 的一个值,这个值的类型是Hash类型。

    // 说明account storage里面只能存储hash值?

    // 如果缓存里面存在就从缓存里查找,否则从数据库里面查询。然后存储到缓存里面。

        func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {

            value, exists := self.cachedStorage[key]

            if exists {

                return value

            }

            // Load from DB in case it is missing.

            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)

            }

            if (value != common.Hash{}) {

                self.cachedStorage[key] = value

            }

            return value

        }

        // SetState updates a value in account storage.

    // 往 account storeage 里面设置一个值 key value 的类型都是Hash类型。

        func (self *stateObject) SetState(db Database, key, value common.Hash) {

            self.db.journal = append(self.db.journal, storageChange{

                account: &self.address,

                key: key,

                prevalue: self.GetState(db, key),

            })

            self.setState(key, value)

        }

        func (self *stateObject) setState(key, value common.Hash) {

            self.cachedStorage[key] = value

            self.dirtyStorage[key] = value

            if self.onDirty != nil {

                self.onDirty(self.Address())

                self.onDirty = nil

            }

        }

    提交 Commit

        // CommitTrie the storage trie of the object to dwb.

        // This updates the trie root.

    // 步骤,首先打开,然后修改,然后提交或者回滚

        func (self *stateObject) CommitTrie(db Database, dbw trie.DatabaseWriter) error {

    self.updateTrie(db) // updateTrie把修改过的缓存写入Trie树

            if self.dbErr != nil {

                return self.dbErr

            }

            root, err := self.trie.CommitTo(dbw)

            if err == nil {

                self.data.Root = root

            }

            return err

        }

        // updateTrie writes cached storage modifications into the object's storage trie.

        func (self *stateObject) updateTrie(db Database) Trie {

            tr := self.getTrie(db)

            for key, value := range self.dirtyStorage {

                delete(self.dirtyStorage, key)

                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树的跟。

        func (self *stateObject) updateRoot(db Database) {

            self.updateTrie(db)

            self.data.Root = self.trie.Hash()

        }

    额外的一些功能 ,deepCopy提供了state_object的深拷贝。

        func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject {

            stateObject := newObject(db, self.address, self.data, onDirty)

            if self.trie != nil {

                stateObject.trie = db.db.CopyTrie(self.trie)

            }

            stateObject.code = self.code

            stateObject.dirtyStorage = self.dirtyStorage.Copy()

            stateObject.cachedStorage = self.dirtyStorage.Copy()

            stateObject.suicided = self.suicided

            stateObject.dirtyCode = self.dirtyCode

            stateObject.deleted = self.deleted

            return stateObject

        }

    未完待续......感谢继续关注兄弟连区块链教程分享

    相关文章

      网友评论

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

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