以太坊作为全球领先的智能合约平台,其核心在于账户模型的设计与管理,理解以太坊账户的源码实现,对于深入把握以太坊的工作原理、安全机制以及开发安全可靠的DApp至关重要,本文将基于以太坊客户端(以Go客户端geth为例)的源码,对以太坊账户的核心实现与机制进行深入分析。
在深入源码之前,我们先简要回顾以太坊的账户模型,以太坊主要有两种账户类型:
这两种账户共同构成了以太坊的状态基础,所有账户信息都存储在以太坊的MPT(Merkle Patricia Trie)状态数据库中。
Account 与 StateObject在以太坊Go客户端(如geth)的core/state包中,账户的核心数据结构是Account
StateObject。
core/types.Account 结构体这个结构体定义了账户在状态 trie 中存储的基本数据格式,它是一个序列化的结构:
// Account represents an account in the state database.
type Account struct {
Nonce uint64
Balance *big.Int
Root common.Hash // Merkle root of the storage trie
CodeHash common.Hash
}
这个Account结构体是状态数据库中存储的“信息。
core/state.StateObject 结构体StateObject则是内存中账户对象的表示,它包含了Account结构体的信息,并提供了更多的方法和功能,用于修改和查询账户状态。
// StateObject represents an Ethereum account object in the state trie.
type StateObject struct {
address common.Address
data Account
db Database
dbErr error
dirty bool // 是否已被修改
deleted bool // 是否已被删除
onDirty func(addr common.Address) // 当状态改变时的回调函数
// 以下字段用于合约账户
code Code
storage map[common.Hash]common.Hash // 合约的存储槽
storageDirty map[common.Hash]struct{} // 标记哪些存储槽被修改
}
Account结构体,包含账户的基本信息。Code类型,包含字节码和哈希)。StateObject是账户状态在内存中的“活性”代表,所有对账户状态的修改(如转账、合约调用读写存储)都通过StateObject进行。
Nonce从0开始,Balance初始为0(或转入一定数量),Root和CodeHash为空。StateObject,设置Nonce为1(因为创建了一个合约),Balance通常为0(除非在构造函数中转入),Root初始化为空(对应一个空的存储trie),CodeHash为空字符串的哈希。CodeHash为字节码的哈希。StateObject标记为dirty,等待写入状态数据库。在以太坊节点处理交易或查询状态时,需要从数据库中加载账户信息:
Account结构体数据,然后将其封装成StateObject对象加载到内存中。StateObject的字段(如Balance、Nonce、合约的storage)进行修改,每次修改后,StateObject会被标记为dirty。dirty的StateObject会被重新序列化为Account结构体,并更新到MPT状态数据库中,对于合约账户,其storage的修改也会被组织成一个单独的存储MPT,并更新其Root哈希。当一笔转账交易被处理时:
StateObject中扣除相应数量的Balance,并增加其Nonce。StateObject中增加相应数量的Balance。StateObject都会被标记为dirty。当调用合约并读写其存储时:
StateObject。key(存储槽索引),从storage map中获取对应的value。key和value,更新storage map,并将该key标记为dirty(存储在storageDirty中)。StateObject本身也会被标记为dirty。合约账户的代码是其核心。StateObject中的Code字段通常是一个Code结构体,包含:
codeHash:代码的哈希,用于快速验证。code:实际的字节码(可能为nil,如果未加载或通过codehash可以获取)。当部署合约或执行合约时,字节码会被设置到StateObject的Code字段中,并更新data.CodeHash,以太坊会利用CodeHash来缓存和验证代码,避免重复存储和加载。
分析以太坊账户源码,主要关注以下几个关键包和函数:
core/state:定义了Account、StateObject等核心结构体,以及账户状态的加载、更新、提交逻辑。(*StateDB).GetStateObject(addr common.Address) *StateObject:根据地址获取账户对象。(*StateDB).CreateAccount(addr common.Address):创建新账户(通常用于合约创建)。(*StateObject).AddBalance(amount *big.Int) / (*StateObject).SubBalance(amount *big.Int):修改余额。(*StateObject).SetState(key, value common.Hash) / (*StateObject).GetState(key common.Hash) common.Hash:读写合约存储。(*StateDB).Commit():将所有状态变更提交到数据库。core/types:定义了交易(Transaction)、区块(Block)等类型,其中交易中包含了发送方地址、接收方地址、Nonce、金额等信息,这些信息直接关联到账户操作。ethdb:定义了数据库接口,如Database,StateDB通过它与底层存储(如LevelDB)交互。通过对以太坊账户源码的剖析,我们可以看到:
1
返回栏目