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

## 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

原文地址:http://blog.51cto.com/14035944/2307447

时间: 2024-10-05 02:31:35

区块链教程以太坊源码分析core-state源码分析(二)的相关文章

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

兄弟连区块链教程以太坊源码分析core-state-process源码分析(二):关于g0的计算,在黄皮书上由详细的介绍和黄皮书有一定出入的部分在于if contractCreation && homestead {igas.SetUint64(params.TxGasContractCreation) 这是因为 Gtxcreate+Gtransaction = TxGasContractCreation func IntrinsicGas(data []byte, contractCre

{区块链教程}以太坊源码分析fast sync算法二

{区块链教程}以太坊源码分析fast sync算法二:上面的表格应该这样解释:如果我们每隔K个区块头验证一次区块头,在N个区块头之后,伪造的概率小于***者产生SHA3冲突的概率.这也意味着,如果确实发现了伪造,那么最后的N个头部应该被丢弃,因为不够安全.可以从上表中选择任何{N,K}对,为了选择一个看起来好看点的数字,我们选择N = 2048,K = 100.后续可能会根据网络带宽/延迟影响以及可能在一些CPU性能比较受限的设备上运行的情况来进行调整. Using this caveat ho

区块链教程以太坊源码分析core-state-process源码分析

StateTransition 状态转换模型 /* The State Transitioning Model 状态转换模型 A state transition is a change made when a transaction is applied to the current world state 状态转换 是指用当前的world state来执行交易,并改变当前的world state The state transitioning model does all all the n

区块链教程以太源码分析accounts账户管理分析

区块链教程以太源码分析accounts账户管理分析. 数据结构分析 ETH的账户管理定义在accounts/manager.go中,其数据结构为: // Manager is an overarching account manager that can communicate with various // backends for signing transactions. type Manager struct { backends map[reflect.Type][]Backend /

【区块链】以太坊(Ethereum )高级进阶实战视频教程

[区块链]以太坊(Ethereum )高级进阶实战视频教程视频教程地址:http://edu.51cto.com/course/14785.html 课程大纲: 课程概要介绍使用bootnode搭建以太坊私有链web3j介绍及基本使用使用web3j管理账户default block parameter以太坊交易详解ERC20代币介绍使用web3j部署ERC20代币合约账户解锁web3j调用代币合约方法(一)web3j调用代币合约方法(二)web3j调用代币合约方法(三)深入sendTransac

4.区块链平台以太坊从入门到精通之 以太币

1.以太币简介 以太币( ether) 是以太坊中使用的货币的名字.它是用于支付在虚拟机中的运算的费用. 了解就可以 2.获取和发送以太币 有三种方式获取 1.成为一名矿工,通过挖矿来获得以太币的奖励. 2.从交易平台处购买或兑换 3. 图形化界面的以太坊客户端集成了https://shapeshift.io/#/coins 接口. 可以直接在客户端上购买以太币. (需要在主网上才可以) 3.集中的交易兑换市场

区块链教程以太源码分析accounts包简介

accounts包实现了eth客户端的钱包和账户管理.账号的数据结构:typeAccount struct { Address common.Address json:"address" // Ethereum account addressderived from the key URLURL json:"url" // Optional resource locator within a backend }钱包interface,是指包含了一个或多个账户的软件钱

区块链:以太坊基础之安装Geth

1.安装cmake 智能合约需要cmake3.x版本才可以编译 # 下载包 wget https://cmake.org/files/v3.3/cmake-3.3.2.tar.gz # 解压 tar zxvf cmake-3.3.2.tar.gz cd cmake-3.3.2 # 安装 ./configure make make install # 编辑环境变量配置文件 vim /etc/profile # 在末尾加上 export PATH=/usr/cmake/cmake-3.3.2/bin

区块链:以太坊基础之搭建私链

1.新建genesis.json {  "config": {    "chainId": 666,    "homesteadBlock": 0,    "eip150Block": 0,    "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",    "eip