签名回答“谁授权的”,UTXO 回答“这笔钱花过没有”,两者让单台机器学会查账。
从零到一发明比特币
Alice 想通过互联网给 Bob 付一笔钱,但两个人都不想经过银行——不希望任何一家机构拥有冻结账户、修改记录或者随意增发货币的权力。于是他们拉上了爱抬杠的 Carol 和懂点密码学的 Dave,四个人打算自己设计一种没有中央管理者的数字现金。
如果你写过后端,可以把他们要造的东西想成一个没有主库、没有 DBA、没有管理员的分布式数据库——而且还要在部分节点随时可能作恶的前提下,让所有副本对“谁有多少钱”达成一致。这篇文章沿着四个人踩坑的顺序,把比特币重新“发明”一遍:每引入一种机制,都先看它解决了前面哪个麻烦,又暴露了什么更深的问题。
从一枚会被复制的数字金币出发,沿着 Alice 向 Bob 付款的故事,逐步推演数字签名、UTXO、点对点网络、区块、工作量证明、分叉与激励机制是怎样一环扣一环组成比特币的。
- 双重支付为什么是第一个难题
- 为什么比特币里根本没有“余额”这个字段
- 工作量证明到底在证明什么
- 一个全节点的硬盘里装着什么
- 作弊为什么“不是不行,是不值得”
每一层机制都只解决一个问题,同时把一个更深的问题交给下一层。
把交易装进有顺序的区块,用父哈希串成链,让篡改可以被发现。
让改写历史必须付出真实成本,同时让身份数量不再等于话语权。
累计工作量让临时分歧收敛,全节点验证限制矿工能写入什么。
区块补贴和手续费让“诚实记账”成为最划算的策略。
第一夜:一枚会分身的金币
Alice 的第一个方案朴素得可爱。她建了一个文件,取名 coin.dat,里面写着“这是一枚数字金币”,然后把它发给了 Bob。
Bob 的电脑确实收到了文件。问题是,Alice 的电脑里还留着原件。当天晚上她顺手又复制了一份发给 Carol——现在 Bob 和 Carol 手里的文件一模一样,两个人都以为自己得到了那枚金币。

纸币有天然的排他性:Alice 把一张纸币交给 Bob,自己手里就没有了。数字信息完全相反——复制一个文件几乎零成本,副本和原件之间没有任何区别。这就是数字现金要面对的第一个、也是贯穿始终的难题:双重支付(double spending)。同一笔钱,凭什么不能花两次?
Carol 抬杠道:“先别说花两次。你怎么证明这笔钱是你发的?我现在就可以伪造一条‘Alice 付给 Carol 一百块’的消息广播出去。”
她说得对。在解决“钱有没有花过”之前,得先解决一个更基础的问题:怎么证明一笔付款确实出自付款人本人。
签名:让“这是我说的”无法抵赖
Dave 搬出了密码学的第一件工具:非对称密钥对。每个人生成一对钥匙——私钥自己秘密保管,公钥随便公开。用私钥可以对一段内容生成签名,任何人拿公钥都能验证这个签名,却反推不出私钥。
于是转账消息变成了这样:Alice 写明“把我的哪笔钱、付给谁、付多少”,用私钥对整条消息签名,然后连消息带签名一起广播。任何节点都能用 Alice 的公钥检查两件事:这条消息确实是 Alice 授权的,而且内容一个字都没被改过。Carol 若把收款人改成自己,原签名立刻对不上;她想重新签一份,又没有 Alice 的私钥。
比特币里的“地址”,本质上就是公钥(或公钥哈希)的一种编码。所谓“拥有比特币”,其实就是掌握着某个私钥——私钥丢了,币就永远锁死在链上,谁也拿不出来。签名背后的数学,之前在《Ed25519 数字签名原理》里展开讲过,比特币用的是同一类思路,只是曲线换成了 secp256k1。
但 Carol 很快找到了新的破绽:“签名防得住我伪造你,防不住你自己作恶。你完全可以亲手签两笔交易——同一笔钱,一笔付给 Bob,一笔付给我。两份签名都是真的。”
问题从“身份授权”转移到了“货币状态”:系统需要一种办法明确表示哪些钱还能用、哪些钱已经被花掉了。
UTXO:把“余额”这个字段扔掉
写后端的人,直觉里的转账大概是这样一张表:
accounts(address, balance)
-- 转账 = UPDATE accounts SET balance = balance - 10 WHERE ...四个人最初也想这么干,但 Carol 提出了一个别扭却深刻的替代方案:根本不要余额表。账本里只存一笔笔交易,把每次收到的钱看成一张有唯一来源、只能整张使用的“兑换券”。付款时必须指明自己正在花哪几张旧券,旧券一旦花出就永久作废,同时由这笔新交易铸造出新券。
这种“尚未被花掉的交易输出”,就是 UTXO(Unspent Transaction Output,未花费交易输出)。它像极了现金:你钱包里没有一个叫“总额”的数字,只有一叠面额不同的钞票,“余额”是你把它们加起来的结果。而且钞票不可分割——你不能从一张 100 元上撕下 30 元来花,必须整张交出去,再拿回找零。
假设 Alice 以前收到过两笔钱,钱包界面显示余额 1.3 BTC,但协议底层的真实状态是两个 UTXO:一个 0.7 BTC,一个 0.6 BTC。她要给 Bob 付 1 BTC 时,钱包会把两张旧券整个消耗掉,铸造出新的:
注意那 0.001 BTC 的手续费:它不是一个显式字段,而是“输入总额减去输出总额”自然留下的差额,谁把这笔交易打包进账本,谁就有权领走它。这个设计简洁,但也埋着一个著名的坑——如果钱包忘了生成找零输出,差额会全部变成手续费白送给矿工,历史上真出过这样的事故。
每个 UTXO 上还挂着一段锁定脚本,规定“满足什么条件才能花它”。最常见的条件就是“拿出与这个地址对应的私钥签名”,但脚本是可编程的,同样的机制能表达多重签名(N 个人凑够 M 个签名)、时间锁(某个高度之后才能花)——这套脚本系统就是后来“智能合约”的雏形。
顺带一提:另一条路
以太坊走的是相反的路线——账户模型,每个地址真的有一个可变的 balance 字段,转账就是一减一增。账户模型对开发者直观、容易写有状态的合约;UTXO 模型的好处是互不引用的交易天然可以并行验证、每次找零换新地址对隐私更友好、验证逻辑没有全局可变状态。两条路各有取舍,比特币选了后者。
有了 UTXO,一台机器已经能独立查账了:收到新交易,只要检查它引用的旧输出是否还在自己维护的“未花费集合”里。节点也确实是这么做的——它们维护一个专门的数据结构叫 UTXO 集合,相当于从完整交易历史推导出来的一份物化视图,验证新交易时直接查这个集合,是一次键查找的事,不必回放整条历史。
单台机器会查账了。但别忘了,这个系统里有成千上万台机器,各自保存一份账本副本。真正的麻烦,现在才开始。
两个收银台的难题
Dave 讲了一个日常故事。一家商场发行了一张编号唯一、只能用一次的电子代金券。商场有两个收银台,各自保存一份“尚未使用的代金券清单”,但两台机器暂时无法实时同步。
顾客先在收银台 A 出示这张券。A 查本地清单:没用过,收下,标记为已使用。几乎同一时刻,顾客又在收银台 B 出示同一张券。A 的记录还没传过来,B 查到的同样是“尚未使用”,于是也收下了。
两台收银机都认真执行了检查,却得到了冲突的结果。原因在于:一套确定性的规则,只有在处理顺序相同时才会产生相同的状态,而网络无法保证所有机器以相同顺序收到消息。
回到比特币。Alice 签署两笔交易:交易 B 把她那个 1 BTC 的 UTXO 付给 Bob,交易 C 把同一个 UTXO 付给 Carol。节点 A 先收到交易 B,验证通过,删掉旧 UTXO;稍后收到交易 C,发现旧 UTXO 已不存在,拒绝。节点 D 的遭遇正好相反。两个节点都诚实、都严格执行了相同的规则,一个认定钱归 Bob,一个认定钱归 Carol。
也不能简单规定“先到的交易有效”——网络里不存在一个所有人都能观察到的统一时钟,节点 A 眼里 B 先到,节点 D 眼里 C 先到,谁都没撒谎。
问题升级了
UTXO 解决的是“在交易顺序已经确定的前提下,怎么判断一笔钱能不能花”。它没有、也不可能自己创造出一个全网统一的顺序。真正需要达成一致的东西,已经从“单笔交易是否有效”扩大成了哪一组交易、以什么顺序,构成大家共同承认的历史。
区块,正是在这里获得了出场的理由。
区块:把零散流水订成有页码的账册
Dave 的提议是:别再让交易一笔一笔地各自为政,把一段时间内的交易装进同一个区块,并让每个新区块的头部记录上一个区块的哈希。区块一个咬着一个,交易历史就成了一条有先后顺序的链:
一个区块的物理结构其实相当紧凑:一个固定 80 字节的区块头,加上交易列表。99% 以上的体积都在交易列表上,而共识需要的所有关键信息都压缩在那 80 字节里:
| 区块头字段 | 大小 | 作用 |
|---|---|---|
| 版本号 | 4 字节 | 区块格式,也用于传递规则升级信号 |
| 父区块哈希 | 32 字节 | 指向前一个区块,把区块连成链 |
| Merkle Root | 32 字节 | 本区块全部交易的密码学指纹 |
| 时间戳 | 4 字节 | 矿工填写的近似时间 |
nBits | 4 字节 | 当前工作量证明目标的紧凑表示 |
nonce | 4 字节 | 留给矿工反复修改的自由变量 |
区块里可能装着几千笔交易,怎么用 32 字节锁定它们全部?做法是把所有交易的哈希两两配对再哈希,层层向上收敛成一个树根——Merkle 树:
任何一笔交易被改动,它到树根的整条路径上的哈希都会跟着变,Merkle Root 变了,区块头也就变了。这个结构还有一个漂亮的副产品:想证明“交易 A 确实在这个区块里”,不需要出示全部交易,只要给出 H(B) 和 H(CD),验证者自己就能重建出树根来比对——N 笔交易只需要 log₂N 个哈希。手机上的轻钱包就是靠这个验证交易的。
现在,篡改可以被发现了:谁若改动区块 101 里的一笔交易,区块 101 的哈希立刻变化,区块 102 存的父哈希就对不上,后面整条链全部断开。
但 Carol 又抬杠了:“发现篡改有什么用?重新计算哈希又不要钱。我改完 101,把 102、103 的父哈希也顺手重算一遍,几毫秒的事。我甚至可以从创世块开始,从头伪造一整条链广播出去——你凭什么说你那条是正版?”
她再次说到了点子上。哈希链只能让篡改留下痕迹,拦不住有人整条重写。要让历史真正难以改写,必须让“制造历史”这件事本身变得昂贵。
工作量证明:给每一页历史灌上铅
四个人定下了一条新规矩:一个区块只有在区块头的哈希小于当前目标值时才算有效——通俗地说,哈希值前面得有足够多个零。
哈希函数的输出无法预测,没有任何捷径能直接构造出合格的区块头。矿工唯一能做的,就是不停修改那 4 字节的 nonce,重算哈希,看运气。像在买一种用算力付款的彩票:试错千万亿次才可能中一次,但别人拿到中奖号码后,只需算一次哈希就能验证真伪。“求解极难、验证极易”,这就是工作量证明(Proof of Work)。

4 字节的 nonce 只有约 43 亿种取值,现代矿机不到一秒就能穷尽,所以矿工还会同时改动 coinbase 交易里的附加数据(俗称 extraNonce)和时间戳,让 Merkle Root 不断变化,换来一片片全新的搜索空间。全网每经过 2016 个区块(约两周)会根据实际出块速度重算一次难度,把平均出块时间校准回十分钟左右——算力涨,目标值就压得更低。
现在再看 Carol 的攻击:她改完区块 101 后,101 原有的工作量证明立刻失效,她必须为 101 重新挖矿,然后是 102、103……与此同时,诚实的矿工还在链的末端不断加高。她不是在重算几个哈希,而是在和全网的电力赛跑。
工作量证明还顺手堵上了另一个漏洞。开放网络里,创建身份是免费的——如果采用“一节点一票”,攻击者伪造几万个空壳节点就能操纵投票,这叫女巫攻击(Sybil attack)。而在工作量证明下,出块能力取决于算力、电力和设备投入,多造身份不会凭空多出工作量。话语权第一次和“真实世界的成本”绑在了一起。
分叉:当网络里短暂出现两本账
即使人人诚实,意外仍会发生:Carol 的矿机和 Dave 的矿机可能几乎同时各挖出一个有效区块,都指向区块 100。靠近 Carol 的节点先看到她的 C101,靠近 Dave 的节点先看到 D101——链暂时裂成了两条,而且两条都完全合规。

规矩是这样的:每个节点沿着自己先看到的链尖继续工作,同时保留另一条分支。假如下一个幸运矿工在 C101 后面接出了 C102,C 分支的累计工作量就反超了。原先站在 D 分支上的节点会撤销 D101 带来的状态变化,改为依次应用 C101 和 C102——这个切换过程叫链重组(reorg)。D101 里的交易并不会凭空消失:大多数会回到待打包队列等待重新入账,只有和 C 分支冲突(花同一个 UTXO)的那些会失效。
常见的说法是“最长链获胜”,更准确的说法是:节点先排除违反规则的链,再在剩下的有效候选里选累计工作量最大的那条。
这也解释了转账为什么要等“确认”。交易被打包进区块只是第 1 个确认,之后每多出一个区块,就多一层压在它上面的工作量。想把这笔交易从历史里抽出来,就得重做它所在区块及其后所有区块的工作,还得追上仍在增长的公开链。所以:
- 0 确认:交易还在等待打包,网络里可能存在与它冲突的版本;
- 1 个确认:进入了当前有效链,但浅层自然分叉仍可能波及它——如果 Alice 故意签了两笔冲突交易分别进了两条分支,Bob 此刻发货就可能吃亏;
- 6 个确认:交易所和商家常用的经验阈值,改写它需要追赶的工作量已经大到不现实——注意这只是习俗,不是协议规定。
比特币提供的不是“写入即永恒”的绝对最终性,而是随确认数逐渐增强的概率最终性。分叉本身是常态而不是事故;真正值得警惕的是有人长期控制多数算力,也就是常说的 51% 攻击。但即使那样,能做的事也比想象中少:
| 多数算力能做到 | 多数算力做不到 |
|---|---|
| 重组近期的交易历史 | 伪造 Bob 的数字签名 |
| 撤销自己刚付出去的钱 | 花别人的 UTXO |
| 延迟或审查部分交易 | 让节点接受超额发行 |
为什么做不到右边那一列?因为除了矿工,这个网络里还站着另一群人。
全节点:Don't trust, verify
矿工完成工作量证明后,把区块广播出去。但工作量只能证明矿工烧了电,不能证明区块里的交易全都合法。每个收到区块的全节点,都会独立地把全部规则重新检查一遍:工作量达不达标、父区块对不对、每笔交易的签名是否有效、引用的 UTXO 是否存在且未被花、coinbase 有没有超额领取奖励……任何一条不满足,整个区块直接丢弃,无论它背后烧掉了多少电。
如果 Carol 在自己的区块里凭空给自己发一亿枚币,或者塞进一笔没有 Bob 签名却花 Bob 的钱的交易,那么就算她的工作量证明完美无缺,正常的全节点也会把区块整个拒收。算力再大,也只能决定“合法交易以什么顺序入账”,决定不了“什么算合法”。矿工提出下一页账,全节点裁决这一页能不能写进账本——两种角色互相制衡,缺一不可。这就是比特币社区那句口号的字面意思:Don't trust, verify(别信任,自己验证)。
打开一个全节点的数据目录,你会发现它存的东西恰好对应三个时间维度:
- 区块数据(
blocks/*.dat)——从创世块至今的完整账本原文,几百 GB 并持续增长,这是“过去发生过什么”; - UTXO 集合(
chainstate/,一个 LevelDB)——从历史推导出的当前可花费输出快照,几 GB,这是“现在是什么状态”; - 内存池(mempool,在内存里)——已广播、已通过校验、还没被打包的交易,这是“接下来可能发生什么”。
节点之间没有广播中心,消息靠 gossip(流言)协议一传十、十传百。传一笔新交易并不是直接把数据砸过去,而是“先通告、按需索取”三步走:A 先告诉 B“我有个新东西,哈希是 X”(inv),B 没见过才回“给我”(getdata),A 这才发送完整数据。B 验证通过后,再向自己的邻居发 inv——如此接力,一笔交易几秒内就能扩散到全网,还不浪费带宽传重复数据。
新节点第一次上线时账本是空的,要经历初始区块下载:先花几分钟拉取全部区块头(每个只有 80 字节)确定哪条链工作量最大,再并行下载几百 GB 的完整区块、逐笔验证数亿条历史交易、边验边构建自己的 UTXO 集合——普通机器要跑上几小时到几天。跑完之后它不欠任何人一句“信我”,因为每一条规则它都亲手执行过一遍。
当然,不是每台设备都跑得动全节点。手机钱包大多是轻节点:只下载区块头,想确认某笔交易时,向全节点索要一份 Merkle 证明(就是前面说的 log₂N 个哈希)。省资源,代价是要部分信任别人提供的数据。完整节点是“自己验证真相”的一方,轻节点是“问别人真相”的一方——全网的抗审查能力,靠的正是散布在世界各地、数以万计的前者。
激励:让诚实成为最划算的策略
还剩最后一个问题,也是 Bob 一直没想通的问题:“矿机很贵,电费很贵,验证和存储也要成本。这里没有公司发工资——凭什么有人愿意干这些活?”
答案写在每个区块的第一笔交易里。这笔交易叫 coinbase 交易,它很特殊:没有真实输入(输入位置是一段全零的占位数据,因为这笔钱是协议凭空发行的),输出则归矿工所有,金额等于两部分之和:
协议规定的区块补贴 + 本区块全部交易的手续费 = 矿工的合法收入区块补贴承担着货币发行的功能:从最初的每块 50 BTC 起,每 210,000 个区块(约四年)减半一次,如今已减到 3.125 BTC,最终趋近于零。这个几何级数的总和收敛于约 2100 万枚——比特币总量上限不是谁宣布的,而是发行曲线算出来的。补贴耗尽之后,矿工的收入将完全来自手续费。
矿工没法自说自话多领:每个全节点都会重算当前区块允许的最高奖励,超额领取的区块会被整个拒收。coinbase 的产出还有100 个区块的成熟期才能花费——防止矿工刚挖到就把奖励花出去,随后区块在分叉竞争中落败,拖垮一串下游交易。
顺带一提,coinbase 的占位输入里有一段矿工可以随意填写的自由字段。中本聪在 2009 年 1 月 3 日的创世区块里,往这里写了一句当天泰晤士报的头版标题:
The Times 03/Jan/2009 Chancellor on brink of second bailout for banks (财政大臣站在第二轮银行救助的边缘)
既是时间戳的自证,也像是这个系统对旧金融体系的一句注脚。
把激励这块拼图放进去,整个系统忽然就“活”了:想赚钱?买矿机、烧电、诚实打包合法交易、领取补贴和手续费——这是一条清晰的获利路径。想作弊?你的违规区块会被全节点拒收,电白烧了;你想重写历史,就要和全网算力赛跑,成本高得离谱,而真的得手一次,比特币的信用崩塌,你囤的币和矿机一起归零。密码学保证真实,哈希链保证可追查,工作量证明保证改写昂贵,经济激励则让诚实成为最优策略——作弊在技术上不是不行,是不值得。这套“激励相容”的设计,才是中本聪真正的巧思。
回到最开始的那笔付款
现在,Alice 向 Bob 支付 1 BTC 的完整旅程可以串起来了:
- 钱包挑出 Alice 名下的 UTXO,构造付款输出和找零输出,用私钥签名;
- 交易沿着 gossip 网络扩散,每个节点独立验证签名和 UTXO 状态,放进内存池;
- 某个矿工把它选进候选区块,撞出合格的工作量证明,广播全网;
- 每个全节点重新验证整个区块,接受后更新自己的 UTXO 集合——Bob 的钱包看到第 1 个确认;
- 后续区块不断在上方堆叠工作量,六个确认之后,Bob 放心发货。
一路上四个人踩过的坑,和最终的解法,正好一一对应:
| 遇到的问题 | 采用的办法 |
|---|---|
| 数字文件可以无限复制 | 把钱表示成只能整体消费一次的 UTXO |
| 别人可以冒充 Alice | 私钥签名,公钥验证 |
| 各节点收到消息的顺序不同 | 把交易组织进有先后顺序的区块链 |
| 哈希链重算起来太便宜 | 工作量证明抬高改写历史的成本 |
| 虚假身份可以无限创建 | 让话语权取决于算力而非节点数量 |
| 两个矿工可能同时出块 | 采用累计工作量最大的有效链 |
| 浅层分叉可能撤销交易 | 用确认数衡量改写难度 |
| 矿工可能打包违规交易 | 全节点独立执行全部共识规则 |
| 没人愿意白白烧电记账 | 区块补贴与手续费 |
在没有共同管理者、参与者互不信任、消息到达顺序也各不相同的网络里,让每个人依据相同规则独立验证,再用累计工作量在有效历史之间做选择——分散在世界各地的账本副本,最终收敛到同一份最难被改写的交易历史。这就是比特币。
- 数字签名回答“谁授权的”,UTXO 回答“这笔钱花过没有”;两者让单台机器能独立查账,但管不了多台机器的处理顺序——这才是区块存在的理由。
- 哈希链只能让篡改被发现,工作量证明才让篡改变得昂贵;它同时把话语权和真实成本绑定,堵死了女巫攻击。
- 矿工决定交易顺序,全节点决定什么合法;算力再大也伪造不了签名、造不出超额发行。确认数衡量的是改写历史需要追赶多少工作量。
- 补贴减半曲线决定了 2100 万的总量上限;激励设计让诚实挖矿比作弊更划算——“作弊不是不行,是不值得”。
延伸阅读
版权所有
版权归属:Shuo Liu
