標籤:ext dual tco 沒有 eval The was div amp
區塊鏈教程以太坊源碼分析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.// 帳戶是以太坊共識表示的帳戶。 這些Object Storage Service在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 Triefunc (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源碼分析(一)