Blockchain tutorial Ethereum Source analysis core-state Source analysis, Core/state package mainly for the Ethereum state Trie provides a layer of cache layers (cache)
Database provides an abstraction of the trie tree, providing caching of the trie tree and the length of the contract code.
Journal mainly provides the operation log, as well as the operation rollback function.
State_object is an abstraction of the account object that provides some of the functionality of the accounts.
Statedb mainly provides some of the functions of State trie.
Database.go
Database.go provides an abstraction of a database.
Data
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 a account. Opentrie opened the master account of the trie tree//Openstoragetrie opened an account of storage trie opentrie (root common. Hash) (Trie, error) Openstoragetrie (Addrhash, Root common. Hash) (Trie, error)//Accessing Contract Code://Access Contract Code Contractcode (Addrhash, Codehash Common. Hash) ([]byte, error)//access to the size of the contract. This method may be called frequently. Because there is a cache. Contractcodesize (Addrhash, Codehash Common. Hash) (int, error)//Copytrie returns an independent copy of the given trie. Copytrie returns a separate copy Copytrie (Trie) trie}//Newdatabase creates a backing store for state of the specified Trie. 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 Tree Cache Codesizecache *lru. Cache//Contract code size caching}
Opentrie, look for it from within the cache. If a copy of the trie that returns the cache is found, a tree is rebuilt to return.
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 and Contractcodesize, Contractcodesize has a cache.
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}
The structure of the Cachedtrie and the commit method, when commit, calls the Pushtrie method to cache the previous trie tree.
// 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
The journal represents the Operation log and provides the corresponding rollback functionality for the logs of various operations. You can do some transaction-type operations based on this log.
The type definition, which defines the Journalentry interface, provides the undo functionality. Journal is the list of journalentry.
type journalEntry interface { undo(*StateDB)}type journal []journalEntry
A variety of different log types and the Undo method.
Createobjectchange struct {//create the log of the object. The Undo method is to delete the created object from the Statedb. Account *common. Address}func (Ch createobjectchange) undo (s *statedb) {delete (s.stateobjects, *ch.account) Delete (s.stateobjectsdir Ty, *ch.account)}//for StateObject modification, the Undo method is to change the value to the original object. Resetobjectchange struct {prev *stateobject}func (ch resetobjectchange) undo (S *statedb) {s.setstateobject (Ch.prev )}//the suicide change. Suicide should be to delete the account, but if there is no commit, the object has not been deleted from 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.suici ded = Ch.prev obj.setbalance (ch.prevbalance)}}//changes to individual accounts.balancechange struct {Accou NT *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). Setbalanc E (ch.prev)}func (ch noncechange) undo (S *statedb) {s.getstateobject (*ch.account). Setnonce (Ch.prev)}func (Ch CodeChang e) 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)}//I understand is the DAO event refund processing refundchange struct {prev *big. Int}func (Ch refundchange) undo (S *statedb) {s.refund = ch.prev}//increased the modification of the log addlogchange struct {txhash common. Hash}func (Ch addlogchange) undo (S *statedb) {logs: = S.logs[ch.txhash] If len (logs) = = 1 {Delete (s.logs, C H.txhash)} else {S.logs[ch.txhash] = Logs[:len (logs)-1]} s.logsize--}//this is to increase the VM to see the SHA3 of the original byte[], increase s HA3 Hash---byte[] the corresponding relationship 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 represents the Ethereum account being modified.
Data
Type Storage Map[common. Hash]common. hash//StateObject represents an Ethereum accounts which is being modified.//StateObject represents the Ethereum account being modified. The usage pattern is as follows://first you need to obtain a state object.//account values can be accessed and Modifi Ed through the object.//Finally, call Committrie to write the modified storage trie into a database. The usage pattern is as follows: first you need to get a state _object. Account values can be accessed and modified through objects. Finally, call Committrie to write the modified storage trie to the database. Type stateobject struct {address common. Address Addrhash Common. Hash//Hash of Ethereum address the hash value of the account Ethereum ID//This is the actual Ethereum account information db *statedb//state database DB error. State objects is used by the consensus core and VM which is//unable to deal with Database-level errors. Any error This occurs//during a database read is memoized here and would eventually be returned/by Statedb.commi T.//Database error. The stateobject will be used by the core of the consensus algorithm and the VM, which cannot handle database-level errors inside the code. Any errors that occur during database reads are stored here and will eventually be returned by Statedb.commit. Dberr Error//Write caches. Write cache trie trie//Storage trie, which becomes non-nil on first access user's storage trie, became non-empty code code//contract on initial access Bytecode, which gets set when the code is loaded contract code is set Cachedstorage Storage//Storage entry cache to avoid du Plicate reads user storage object cache, used to avoid repeated reads Dirtystorage Storage//Storage entries that need to being flushed to disk user storage objects that need to be brushed into the disks Cache flags. Cache flag//When a object is marked suicided It'll be delete from the trie//during the "update" phase of the St Ate transition. When an object is marked as a suicide, it is removed from the tree during the update phase of the state transition. dirtycode BOOL//True if the code was updated is set to True suicided BOOL touched bool deleted bool Ondirt If the codes are updated y func (addr common. Address)//Callback method to mark a state object newly dirty will be invoked the first time it is set to drity. }//account is the Ethereum consensus representation of accounts.//these objects be stored in the main accounts trie.// The account that the Ethereum consensus represents. These objects are stored in main account trie. Type Account Struct {Nonce UInt64 Balance *big. Int Root Common. Hash//Merkle root of the storage trie Codehash []byte}
constructor function
// 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, }}
The RLP encoding method will only encode the account object.
// EncodeRLP implements rlp.Encoder.func (c *stateObject) EncodeRLP(w io.Writer) error { return rlp.Encode(w, c.data)}
Some functions that change state.
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}
Treatment of storage
Gettrie Return account 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 Storag e.//GetState Returns the value of the account storage, which is of type hash. Note that only hash values can be stored in account storage? If the cache exists in the cache to find, otherwise from the database inside query. It is then stored inside the cache. 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 I S 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.//toward Account Storeage A value that is set to the type of key value is a hash type. 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.ondir Ty (self. Address ()) Self.ondirty = nil}}
Commit commit
Committrie the storage trie of the object to dwb.//this updates the trie root.//step, first open, then modify, and then commit or rollback the func (self *stat Eobject) Committrie (db Database, dBW trie. Databasewriter) Error {Self.updatetrie (db)///Updatetrie write the modified cache to Trie tree if self.dberr! = Nil {return self.db ERR} root, err: = Self.trie.CommitTo (DBW) If Err = = Nil {self.data.Root = root} return err}//upd Atetrie 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//set the account root as the current Trie tree's heel. Func (self *stateobject) updateroot (db Database) {Self.updatetrie (db) Self.data.Root = Self.trie.Hash ()}
For some additional features, Deepcopy provides a deep copy of the 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}
Not to be continued ... Thank you for your continued attention Brother Lian Blockchain tutorial share
Blockchain tutorial Ethereum Source Analysis Core-state Source Analysis (a)