以太坊作为全球领先的智能合约平台,其核心在于账户模型的设计与管理,理解以太坊账户的源码实现,对于深入把握以太坊的工作原理、安全机制以及开发安全可靠的DApp至关重要,本文将基于以太坊客户端(以Go客户端geth为例)的源码,对以太坊账户的核心实现与机制进行深入分析。

以太坊账户模型概览

在深入源码之前,我们先简要回顾以太坊的账户模型,以太坊主要有两种账户类型:

  1. 外部账户 (Externally Owned Accounts, EOAs):由用户通过私钥控制,没有关联的代码,发起交易、部署合约等操作通常由EOAs驱动,其标识是地址(Address)。
  2. 合约账户 (Contract Accounts):由智能合约代码控制,拥有状态(State),可以通过接收交易或调用其他合约来改变自身状态,其标识也是地址(由创建者地址和nonce等生成)。

这两种账户共同构成了以太坊的状态基础,所有账户信息都存储在以太坊的MPT(Merkle Patricia Trie)状态数据库中。

账户数据结构:AccountStateObject

在以太坊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{} // 标记哪些存储槽被修改
}

StateObject是账户状态在内存中的“活性”代表,所有对账户状态的修改(如转账、合约调用读写存储)都通过StateObject进行。

账户的创建与管理

账户的创建

账户的加载与更新

在以太坊节点处理交易或查询状态时,需要从数据库中加载账户信息:

  1. 加载:通过账户地址从MPT状态数据库中读取Account结构体数据,然后将其封装成StateObject对象加载到内存中。
  2. 更新:对StateObject的字段(如BalanceNonce、合约的storage)进行修改,每次修改后,StateObject会被标记为dirty
  3. 提交:当需要持久化状态时(如区块打包完成),所有被标记为dirtyStateObject会被重新序列化为Account结构体,并更新到MPT状态数据库中,对于合约账户,其storage的修改也会被组织成一个单独的存储MPT,并更新其Root哈希。

账户状态的核心操作

余额转移

当一笔转账交易被处理时:

  1. 从发送方EOA的StateObject中扣除相应数量的Balance,并增加其Nonce
  2. 从接收方账户(可能是EOA或合约账户)的StateObject中增加相应数量的Balance
  3. 双方的StateObject都会被标记为dirty

合约存储读写

当调用合约并读写其存储时:

  1. 加载合约账户的StateObject
  2. 读取:根据指定的key(存储槽索引),从storage map中获取对应的value
  3. 写入:根据指定的keyvalue,更新storage map,并将该key标记为dirty(存储在storageDirty中)。
  4. 合约账户的StateObject本身也会被标记为dirty

账户代码的管理

合约账户的代码是其核心。StateObject中的Code字段通常是一个Code结构体,包含:

当部署合约或执行合约时,字节码会被设置到StateObjectCode字段中,并更新data.CodeHash,以太坊会利用CodeHash来缓存和验证代码,避免重复存储和加载。

源码中的关键包与函数

分析以太坊账户源码,主要关注以下几个关键包和函数:

总结与展望

通过对以太坊账户源码的剖析,我们可以看到:

1

返回栏目