深入理解比特币(三) 交易

"交易"是比特币系统中的核心部分, 所谓交易, 是指资产从一个所有者转移到另一个所有者的过程.本篇文章主要包含以下内容:

  • 熟悉比特币交易的数据结构
  • 理解比特币钱包余额和 UTXO 模型
  • 了解比特币编程语言
  • 学习利用 P2SH(Pay-to-Script Hash) 构造复杂交易

数据结构

一笔比特币交易由如下几个组成部分:

  • Version
  • Inputs
  • Outputs
  • Locktime

serialized-transaction

Version(版本号)

和大多数版本号的作用一样, 比特币交易的版本号是为了标识处理该笔交易所需要的支持的特性. 比特币系统也是一个持续迭代的软件, 有时我们需要为交易设置不同的版本号以便交易中的新特性能被更好地支持. 目前比特币系统中大多数交易的版本号为 2. 版本号位于序列化交易数据最开始的位置, 占 4 个字节.

Inputs(输入) 和 Outputs(输出)

在比特币的设计里, 比特币钱包(私钥)所拥有的余额不是以一个单独数字的形式存在的. 比特币世界中的余额与现实世界中现金的模式类似, 在一次交易中:

  • 每一个输入就像是我们此前从别人那里收到的钱
  • 每一个输出就像是我们这次要交给其他人的钱
  • 每笔交易可以包含多笔输入(一次使用之前收到多张现金)和多笔输出(一次交给其他人多张现金)
  • 通过一次交易我们可以改变每张现金的面额(多个输入合并为一个输出或者一个输入分为多个输出), 不过输入的总金额必须大于(或等于)输出的金额, 输入减输出的差额将作为本次交易的手续费

Locktime(锁定时间)

通过设置交易的锁定时间我们可以构造一笔延迟生效的交易. 当 Locktime 被设置为 600000 代表直到区块高度达到 600001 后, 该笔交易方可进入区块链中. 另外, 当 Locktime 大于 500000000 时则不应该为理解为区块高度了, 而是时间戳, 表示达到该时间后, 该笔交易方可进入区块链中.

structure
https://blockstream.info/tx/5c165cd6a4c614c4b0d3f15cc8b7463d32409d5c259a67f980dd8277dd042e17

上图展示的就是一笔普通的比特币交易. 通过图片我们可以看出该笔交易由两个输入和三个输出构成, 每个输入和输出又有各自的数据结构和一些看起来类似汇编语言的代码. 看到这里, 也许你对什么是输入和输出以及其中包含的信息还不能完全理解, 这些疑问我们将在本文后面的内容里一一解答, 相信读完本文后你将对比特币交易有一个更清晰地认识.

UTXO 模型

UTXO 是 Unspent Transaction Output 的缩写, 它表示此时此刻你在比特币世界里所有还没花掉的"现金", 而这些现金的总和就是你全部的资产. 这些"现金"会随着交易在比特币网络中流动, 网络中的全节点会对这些 UTXO 的流向进行监控, 并根据所属账户对 UTXO 集合建立索引, 以便可以更快地对交易进行验证. 比特币也是通过 UTXO 模型来避免"双花问题"(Double Spending)的发生. 当某笔交易的输入引用的 UTXO 在集合中找不到, 则说明该笔 UTXO 可能已经被其他的交易用掉了. 正如现实世界中, 你不可能在两次交易中使用同一张钱(比特币交易将一张钱变成了另一张钱).

关于"双花问题"我们在后面的文章中还会讲到.

utxo-model

交易脚本

比特币系统中的 UTXO 是公开保存在区块链上的, 那我们怎么知道一笔 UTXO 应该属于谁, 并且确保该笔 UTXO 无法被其他人用掉呢? 这就不得不提到比特币中的脚本语言, 也可以称作比特币的"智能合约".

在比特币的设计里, 每一笔 output 都拥有一个锁定脚本公钥脚本(Locking Script 或 ScriptPubkey). output 上的锁定脚本是交易的发起者为这笔输出设置了一道谜题, 任何能解开这道谜题的人就能使用这笔 UTXO, 交易发起者能实现只允许特定的收款人解开这道谜题从而实现指定需求的转账. 当收款人要使用该笔 UTXO 时, 只需要在新交易相应的输入中提供这道谜题的正确答案, 并且所有人能对该答案的正确性进行验证. 这个答案被称作这道谜题的解锁脚本签名脚本(Unlock Script 或 ScriptSig), 在密码学中这样的答案也叫这个谜题的见证(Witness), 所以解锁脚本有时也被称作见证脚本(Witness Script).

这个神奇特性背后的秘密叫做椭圆曲线密码学, 对椭圆曲线密码学感兴趣的同学可以阅读我翻译的系列文章 椭圆曲线密码学: 入门. 通过椭圆曲线密码算法, 我们可以实现通过私钥生成独特的数字签名并通过公钥验证该签名的正确性. 在这篇文章里, 关于椭圆曲线密码学我们知道这些就够了.

P2PKH(Pay-to-pubkey-hash)是比特币网络中最常见的交易, 锁定脚本和解锁脚本的内容如下图所示. 当验证者对新交易进行验证时, 需要将锁定脚本和解锁脚本合并, 并依次放入栈中执行, 最后栈顶的元素即为脚本的执行结果, 当栈为空或栈顶元素为 0 时则表示执行结果为 Flase, 否则表示执行结果为 True.

p2pkh

对于上图展示的脚本我们可以做如下解读:

  1. 将(新交易发起者的)签名放入栈中
  2. 将(新交易发起者的)公钥放入栈中
  3. 将栈顶元素(公钥)复制一份放入栈中
  4. 弹出栈顶元素(公钥)计算其 HASH160 值并放入栈中
  5. 将(前一笔交易发起者指定的)哈希值放入栈中
  6. 弹出栈顶的两个元素(两个哈希值)并校验两个值相等
  7. 用公钥对签名的正确性进行校验并将结果放入栈中

通过对脚本的分析, 我们可以这样来描述整个交易过程: 前一笔交易的发起人在 UTXO 的锁定脚本中要求, 要花费这笔 UTXO 的人必须提供一个哈希值为特定值的公钥, 并且还能提供出一个能用该公钥完成验证的合法签名. 可见, 能够做到这一切的人只可能是拥有该特定公钥对应的私钥的那个人. 而这里说的公钥哈希其实就是比特币系统中普通收款地址进行 Base58 解码后的结果, 比特币中向特定地址发起转账就是这样实现的.

P2SH

P2SH(Pay-to-Script Hash)是比特币另一类重要的交易. 和 P2PKH 不同, P2SH 的收款地址不是由某个钱包对应的公钥生成, 而是通过一段脚本的哈希经过 Base58 编码而成. 这意味着只要脚本最终的执行结果为 True 交易即可达成, 我们可以通过设计脚本实现更复杂和高级的交易规则.

此外, P2SH 中控制交易规则的脚本并没有包含在 UTXO 的锁定脚本中, 而是通过解锁脚本中的赎回脚本(Redeem Script)提供的.

p2sh

从上图可知, 在验证交易时, 验证者将计算赎回脚本的哈希值并将其与锁定脚本中的哈希值进行比较, 当两个哈希值相等时, 再将赎回脚本中的指令依次压入栈中并执行. 如果最终的执行结果为 True, 则表示验证成功.

Show Comments