账户模型 vs UTXO:以太坊换了一种记账方式
比特币用 UTXO 记账——销毁旧币、创造新币;以太坊用账户模型记账——每个地址一条全局状态(余额 + nonce),转账就是状态改写。本文以同一笔转账的两种视角对照两种模型,讲清 nonce 为什么存在、两种模型各自的取舍,并为理解 EVM 铺路。
引言:Bitcoin 之后,换一种记账方式
阶段 1 我们把比特币拆了个透:交易是 UTXO 的销毁与创造,共识是算力竞争,网络由全节点与矿工分工运转。最后留了一个尾巴——比特币是一台「记账机器」,它擅长证明「这笔钱是谁的」,却不擅长执行「复杂的规则」。
Ethereum 要做的是另一件事:把「规则」本身变成代码,让它自动执行。而要执行规则,先得回答一个更基础的问题——钱该怎么记?
以太坊没有沿用 UTXO,而是换了一种更接近银行直觉的记账方式:账户模型 。每个地址对应一条全局状态,转账不再销毁旧币、创造新币,而是直接改写状态数值。
这个选择不是随手为之。它决定了以太坊能做什么、防什么、牺牲什么——也正是从这一篇开始,我们要从「价值转移」切换到「去中心化计算」的视角。
什么是账户模型
在账户模型下,以太坊的全网状态不是一堆零散的「未花费硬币」,而是一张巨大的表:每个地址对应一条状态记录。
一条账户状态包含:
- 余额(balance):这个地址持有多少 ETH
- nonce:这个地址已经发起过多少笔交易(计数器)
- 代码与存储(仅合约账户有):部署在地址上的程序和它的数据
用一张 ASCII 示意图画出以太坊的全局状态:
以太坊全局状态(World State)—— 一张全网共享的大表
┌────────────────────────┬─────────┬───────┬──────────────┐
│ 地址 │ 余额 │ nonce │ 代码 / 存储 │
├────────────────────────┼─────────┼───────┼──────────────┤
│ 0xAlice... │ 1.2 ETH │ 5 │ —(普通账户) │
│ 0xBob... │ 0.4 ETH │ 2 │ —(普通账户) │
│ 0xContract...(合约) │ 3.0 ETH │ 1 │ 字节码 + 槽位 │
│ ...千万级条目... │ │ │ │
└────────────────────────┴─────────┴───────┴──────────────┘
转账 = 找到两行记录,把余额一减一加,发送方 nonce 加一
以太坊的账户分两类:
- 普通账户(EOA,外部所有账户):由私钥控制,可以主动发起交易。就是你在钱包里创建的那种地址。
- 合约账户:由代码控制,不能自己「醒来」发起交易,只能在被调用时执行逻辑。它也有余额和 nonce,额外的「代码与存储」就放在这里。
同一笔转账的两种视角
沿用阶段 1 的例子:Alice 要给 Bob 转 0.3 个币,她的账户里有 0.5 个币。
UTXO 视角(比特币)——销毁 + 创造:
交易前:Alice 持有 UTXO #001(0.5 BTC)
交易输入: UTXO #001(0.5 BTC)→ 被消耗,永久移除
交易输出: 新 UTXO #002:0.3 BTC → Bob
新 UTXO #003:0.199 BTC → Alice(找零)
差额 0.001 BTC → 矿工费(隐式差额)
账户模型视角(以太坊)——状态改写:
交易前全局状态:
Alice: 余额 0.5 ETH, nonce 5
Bob: 余额 0.2 ETH, nonce 2
交易:Alice → Bob 转 0.3 ETH,附带手续费(Gas 费)
交易后全局状态:
Alice: 余额 0.2 ETH − 手续费, nonce 6 ← 扣款 + 计数器加一
Bob: 余额 0.5 ETH, nonce 2 ← 只动余额
两个直观差异:
- 没有找零。账户模型下不需要「凑面额」——余额是一个可任意拆分的数,直接做减法。
- 多了一个 nonce 的变化。UTXO 交易里 Alice 的「钱包状态」变化是硬币集合的替换;账户模型里除了余额,还必须把 Alice 的计数器从 5 推到 6。这个不起眼的 +1,是账户模型防攻击的关键——下一节展开。
Nonce:账户模型的防双花担当
UTXO 为什么不需要 nonce
比特币防双花的逻辑是结构性的:每个 UTXO 只能被花费一次。同一枚「硬币」如果出现在两笔交易里,全网节点验证时会直接拒绝第二笔——不存在「同一个余额被扣两次」的问题,因为根本没有「余额」这个共享数字。
账户模型的重放困境
账户模型把余额变成了一个共享的、可改写的数字,这带来了一个新攻击面:重放攻击(replay attack)。
设想没有 Nonce 的世界:
无 nonce 的假想世界:
1. Alice 签名一笔交易:「给 Bob 转 0.3 ETH」并广播
2. Bob 收到钱——但他把这笔签名交易原样再广播一次
3. 节点验证:签名有效、余额足够 → 再次执行
4. Alice 又被扣了 0.3 ETH …… 可以无限重放,直到余额扣光
同一笔签名交易,攻击者不需要私钥就可以「替你」重复发起。
nonce 的解法
以太坊的解法:每个账户有一个计数器,每笔交易必须声明自己占据的序号,且必须等于账户当前 nonce。
有 nonce 的真实世界:
Alice 账户当前 nonce = 5
交易 A(nonce=5):给 Bob 转 0.3 ETH → 执行,账户 nonce 变为 6
交易 A 重放(nonce=5):再次广播 → 拒绝:期望 nonce=6,过期交易
交易 B(nonce=6):合法的下一笔 → 可执行
一笔交易无论被重放多少次,只有第一次能对上序号——之后的重放全是「过期交易」,直接丢弃。双花/重放在账户模型下不是被「结构」挡住的,而是被「顺序」挡住的:所有账户状态变更必须按 nonce 严格排序执行。
对比表:五个维度看两种模型
沿用 UTXO 模型:比特币的会计学 已有的维度(记账单位、双花防护、隐私),并按本系列的进展扩展两个新维度(并行性、合约适配性):
| 对比维度 | UTXO 模型(比特币) | 账户模型(以太坊) |
|---|---|---|
| 记账单位 | 一组可指认的「硬币」(UTXO 集合),交易 = 销毁 + 创造 | 每地址一条全局状态(余额 + nonce),交易 = 状态改写 |
| 双花防护 | 结构性:每个 UTXO 只能被花费一次,重复引用直接无效 | 顺序性:nonce 严格递增,重放交易因序号过期被拒 |
| 隐私 | 较好——可生成大量一次性地址收款,但链上分析在收窄边界 | 较弱——账户余额与交易历史完全公开,地址复用会累积画像 |
| 并行性 | 验证阶段可并行(不同 UTXO 互不干扰),打包时同一 UTXO 不可重复引用 | 全局状态按 nonce 排序执行,天然串行,并行化要靠协议层设计补 |
| 合约适配性 | Script 只有简单锁定逻辑,难承载复杂状态 | 全局共享状态天然是 智能合约 读写的「数据库」 |
最后两行是本篇新增的重点:并行性上 UTXO 有结构优势,合约适配性上账户模型是压倒性的——这不是谁更「先进」的问题,而是两种设计目标的不同取舍。
为什么以太坊必须选账户模型
回到以太坊的目标:让规则变成代码、自动执行。
一份智能合约的本质是什么?是一段需要读写共享状态的程序。它要回答「Alice 的余额够不够」「这个代币的归属是谁」「上一轮投票的结果是多少」——这些问题都指向一个统一的全局状态。
如果底层是 UTXO:每份「状态」都散落在无数硬币里,合约想读「某个用户的余额」,得先把他所有相关的 UTXO 拼起来——每次合约调用都变成一次跨「硬币」的复杂拼图。图灵完备的程序需要一个可以直接寻址读写的状态表,而不是一堆硬币。
账户模型把状态集中成一张表,正好是 EVM 这台状态机需要的「内存布局」:
比特币:账本 = 硬币的流转历史(状态藏在交易里)
以太坊:账本 = 全局状态表(历史只是状态改动的记录)
全局状态表 ──读/写──▶ 智能合约(部署在合约账户里)
▲
└── 每个区块 = 对状态表的一批有序改动
总结:状态有了,谁来执行?
这一篇我们完成了从比特币到以太坊的第一次视角切换:
- 比特币用 UTXO 记账:钱是具体的硬币,转账是销毁与创造,防双花靠「一币一花」的结构
- 以太坊用账户模型记账:钱是全局状态表里的数字,转账是状态改写,防重放靠 nonce 的严格排序
- 两种模型没有绝对优劣:UTXO 换来并行验证与更好的隐私边界,账户模型换来智能合约需要的全局共享状态
但新的问题立刻出现了:全网节点都持有这张状态表,一笔交易说「我要把状态从 X 改成 Y」——凭什么是这个结果?谁来执行这段逻辑,又如何保证一千个节点执行出一模一样的答案?
状态只是数据,数据自己不会变。改写状态需要一台所有节点都在运行的、确定性的执行机器——EVM(以太坊虚拟机)。它是下一篇文章的主角。
相关文章:
- UTXO 模型:比特币的会计学 — 本文的镜像对照:UTXO 侧的完整机制
- 比特币白皮书精读 — 一切的原始蓝图
- 比特币网络实际运行 — 阶段 1 收尾:节点、矿工与交易的日常
- 工作量证明 — 比特币侧的共识规则,对照阶段 2 将展开的 PoS