工作原理
从分布式系统角度看,比特币网络是一个典型的分布式点对点网络,网络中的矿工通过“挖矿”来完成对交易记录的记账过程,维护网络的正常运行。
区块链网络提供一个公共可见的记账本,该记账本记录网络中所有的交易历史,而并非记录各个账户的余额。该设计可以避免重放攻击,即某个交易被多次重复提交。
比特币中没有账户的概念。因此,每次发生交易,用户需要将交易记录写到比特币网络账本中,等网络确认后即可认为交易完成。
除了挖矿获得奖励的 coinbase 交易只有输出,正常情况下每个交易需要包括若干输入和输出,未经使用(引用)的交易的输出(Unspent Transaction Outputs,UTXO)可以被新的交易引用作为其合法的输入。被使用过的交易的输出(Spent Transaction Outputs,STXO),则无法被引用作为合法输入。
因此,比特币网络中一笔合法的交易,必须是引用某些已存在交易的 UTXO(必须是属于付款方才能合法引用)作为新交易的输入,并生成新的 UTXO(将属于收款方)。
那么,在交易过程中,付款方如何证明自己所引用的 UTXO 合法?比特币中通过“签名脚本”来实现,并且指定“输出脚本”来限制将来能使用新 UTXO 者只能为指定收款方。对每笔交易,付款方需要进行签名确认。并且,对每一笔交易来说,总输入不能小于总输出。总输入相比总输出多余的部分称为交易费用(Transaction Fee),为生成包含该交易区块的矿工所获得。目前规定每笔交易的交易费用不能小于 0.0001 BTC,交易费用越高,越多矿工愿意包含该交易,也就越早被放到网络中。交易费用在奖励矿工的同时,也避免了网络受到大量攻击。
交易中金额的最小单位是“聪”,即一亿分之一(10^-8)比特币。
下图展示了一些简单的示例交易。更一般情况下,交易的输入、输出可以为多方。
交易 | 目的 | 输入 | 输出 | 签名 | 差额 |
---|---|---|---|---|---|
T0 | A 转给 B | 他人向 A 交易的输出 | B 账户可以使用该交易 | A 签 名确认 | 输入减输出,为交易服务费 |
T1 | B 转给 C | T0 的输出 | C 账户可以使用该交易 | B 签名确认 | 输入减输出,为交易服务费 |
... | X 转给 Y | 他人向 X 交易的输出 | Y 账户可以使用该交易 | X 签名确认 | 输入减输出,为交易服务费 |
需要注意,刚放进网络中的交易(深度为 0)并非是实时得到确认的。进入网络中的交易存在被推翻的可能性,一般要再生成几个新的区块后(深度大于 0)才认为该交易被确认。
比特币采用了 UTXO 模型,相对账户模型,UTXO 模型可以更容易实现并行处理和隐私保护,并追踪完整交易路径;但由于需要存储和检索所有交易记录,对节点存储压力较大。
下面分别介绍比特币网络中的重要概念和主要设计思路。
比特币采用了非对称的加密算法,用户自己保留私钥,对自己发出的交易进行签名确认,并公开公钥。
比特币的账户地址其实就是用户公钥经过一系列 Hash(HASH160,或先进行 SHA256,然后进行 RIPEMD160)及编码运算后生成的 160 位(20 字节)的字符串。
一般地,也常常对账户地址串进行 Base58Check 编码,并添加前导字节(表明支持哪种脚本)和 4 字节校验字节,以提高可读性和准确性。
注:账户并非直接是公钥内容,而是 Hash 后的值,避免公钥过早公开后导致被破解出私钥。
交易是完成比特币功能的核心概念,一条交易可能包括如下信息:
- 付款人地址:合法的地址,公钥经过 SHA256 和 RIPEMD160 两次 Hash,得到 160 位 Hash 串;
- 付款人对交易的签字确认:确保交易内容不被篡改;
- 付款人资金的来源交易 ID:从哪个交易的输出作为本次交易的输入;
- 交易的金额:多少钱,跟输入的差额为交易的服务费;
- 收款人地址:合法的地址;
- 时间戳:交易何时能生效。
网络中节点收到交易信息后,将进行如下检查:
- 交易是否已经处理过;
- 交易是否合法。包括地址是否合法、发起交易者是否是输入地址的合法拥有者、是否是 UTXO;
- 交易的输入之和是否大于输出之和。
检查都通过,则将交易标记为合法的未确认交易,并在网络内进行广播。
用户可以从 blockchain.info 网站查看实时的交易信息,一个示例交易的内容如下图所示。

比特币交易的例子
脚本(Script) 是保障交易完成(主要用于检验交易是否合法)的核心机制,当所依附的交易发生时被触发。通过脚本机制而非写死交易过程,比特币网络实现了一定的可扩展性。比特币脚本语言是一种非图灵完备的语言,类似 Forth 语言。
一般每个交易都会包括两个脚本:负责输入的解锁脚本(scriptSig)和负责输出的锁定脚本(scriptPubKey)。