5月28日 04:06

什么是以太坊账户模型?EOA和合约账户有什么区别?

以太坊用账户模型追踪状态,每个账户有唯一地址和关联状态。账户分两种:EOA(外部拥有账户)合约账户

EOA 由私钥控制,用户通过钱包管理,能主动发起交易、转账 ETH、调用合约,但不能存储代码。合约账户由部署的智能合约代码控制,不能主动发起交易,只能被 EOA 或其他合约调用后执行逻辑,可以存数据、跑代码。

核心区别看三个维度:

  • 控制权:EOA 看私钥,合约账户看代码
  • 主动性:EOA 能发起交易,合约账户只能被动响应
  • 代码能力:EOA 没有代码,合约账户有字节码和存储

每个账户都包含四个字段:nonce(EOA 是交易序号,合约账户是创建序号)、balance(ETH 余额)、storageRoot(Merkle Patricia Trie 根哈希,验证存储完整性)、codeHash(EOA 是空字符串哈希,合约账户是字节码哈希)。

状态管理上,每笔交易触发一次状态转换:验签 → 扣 Gas → 执行逻辑 → 更新状态 → 生成新状态根。重放攻击通过 nonce 防止,这也是账户模型比 UTXO 模型更需要 nonce 的原因。

EIP-4337(账户抽象)正在模糊两者的界限,让合约账户也能像 EOA 一样发起交易,改善用户体验。

追问

EOA 和合约账户的地址是怎么生成的?

EOA 地址 = 私钥 → 公钥 → keccak256 哈希取后 20 字节。合约地址 = keccak256(创建者地址 + nonce) 计算。两者生成方式完全不同,但都是 20 字节长度,从地址本身无法区分类型。

合约账户能不能自己发起一笔交易?

不能。合约账户的所有操作都必须由外部调用触发。这是以太坊的安全设计——如果合约能自主行动,整个系统的确定性就无法保证。账户抽象(ERC-4337)通过引入 UserOperation 和 Bundler 机制绕过了这个限制。

账户模型和比特币的 UTXO 模型各有什么优劣?

维度账户模型UTXO 模型
智能合约天然支持不支持
重放防护需要 nonce天然防止
并行处理困难容易
隐私性较弱较强
状态管理简单直观较复杂

账户模型为了图灵完备牺牲了并行性和隐私,UTXO 为了简洁牺牲了可编程性。

怎么在合约里判断一个地址是 EOA 还是合约?

extcodesize 操作码:address(addr).code.length > 0 就是合约。但有个坑——合约在 constructor 执行期间 code.length 为 0,此时看起来像 EOA。安全做法是用 OpenZeppelin 的 Address.isContract(),或者更好的方案是不依赖这个判断做权限控制。

写段代码

solidity
// 判断地址类型(注意 constructor 陷阱) function isContract(address addr) public view returns (bool) { uint256 size; assembly { size := extcodesize(addr) } return size > 0; } // 安全的 EOA 地址生成 // 私钥 → 公钥 → keccak256 → 取后20字节 // address = 0x + keccak256(pubKey)[12:]
标签:以太坊