以太坊作为全球领先的智能合约平台,其账户模型是整个区块链系统的基石,理解账户的源码实现,对于深入把握以太坊的工作原理、安全机制以及开发安全、高效的DApp至关重要,本文将从以太坊账户的核心概念入手,逐步深入其Go语言(以太坊客户端Geth的主要实现语言)源码,剖析外部账户(EOA)和合约账户的内部结构、创建流程、状态管理及其在以太坊生态系统中的关键作用。

以太坊账户模型概览

在以太坊中,账户是状态的基本单位,分为两种类型:

  1. 外部账户(Externally Owned Account, EOA):由私钥控制,没有关联代码,用户通过私钥签名交易来发起操作,如转账、部署合约等,其标识是地址。
  2. 合约账户(Contract Account):由智能合约代码控制,合约账户的地址由创建它的交易(通常是另一个账户部署合约)决定,它可以存储数据,并在接收到交易或消息时自动执行其代码。

这两种账户共享同一个地址空间,即以太坊上的每一个地址都对应一个账户,要么是EOA,要么是合约账户,账户状态存储在以太坊的状态数据库(MPT,Merkle Patricia Trie)中。

账户的核心数据结构(源码视角)

在以太坊Geth的core/types包中,Account结构体定义了账户的基本信息,它主要反映的是账户的状态快照,而非完整的账户对象(完整的账户对象在状态树中)。

// core/types/account.go
type Account struct {
    Nonce    uint64
    Balance  *big.Int
    Root     common.Hash // 仅合约账户有,指向存储树的根
    CodeHash common.Hash
}

这个结构体包含了账户的关键状态信息:

  • Nonce
    • EOA:该账户发起的交易序号,用于防止重放攻击,确保每笔交易都是唯一的。
    • 合约账户:该账户创建的合约数量(即其创建的交易序号)。
  • Balance:账户持有的以太币余额,以Wei为单位(1 ETH = 10^18 Wei)。
  • Root仅合约账户有效,指向一个MPT(Merkle Patricia Trie)的根哈希,该MPT存储了合约账户的存储数据(Storage),每个合约账户都有自己的独立存储空间。
  • CodeHash仅合约账户有效,指向合约代码的哈希值,以太坊中,合约代码本身是存储在状态数据库的一个独立区域,通过CodeHash来索引,EOA的CodeHash为空。

状态对象(StateObject)的源码实现

在Geth的状态管理模块(core/state)中,StateObject结构体代表了内存中的一个账户对象,它包含了Account结构体的信息,并提供了修改账户状态的方法。

// core/state/state_object.go
type StateObject struct {
    address  common.Address
    data     Account
    db       Database
    dbErr    error
    // 其他字段,如标记是否被修改、是否被删除等
    dirty    bool
    deleted  bool
    onCommit func(addr common.Address) // 提交时的回调函数
    // 对于合约账户,可能还会缓存存储等
    // ...
}

随机配图