兄弟连区块链教程以太坊源码分析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
}
未完待续…感谢继续关注兄弟连区块链教程分享
有疑问加站长微信联系(非本文作者)