区块链入门 ③ - 交易
交易
概述
比特币交易本质上包含交易参与者价值转移的相关信息数据结构。比特币区块链是一本全球复式记账总账簿,每笔交易都是在比特币区块链上的一个公开记录。
本章中使用术语“钱包”时,我们指的是构建交易的软件,而不仅仅是包含密钥的数据库。
前置知识
UTXO
UTXO,即“未花费的交易输出”(unspent transaction outputs)。所有 UTXO 的集合被称为 UTXO 集。UTXO 集大小在新的 UTXO 增加时而增长,并在 UTXO 被消耗时而缩小。每一个交易都代表 UTXO 集中的变化(状态转换)。
- UTXO 的最小单位是八位小数的“聪”。
- UTXO 是面值为“聪”的离散且不可分割的价值单位,一个 UTXO 只能在一次交易中作为一个整体被消耗。如果一个 UTXO 面值大于一笔交易所需,它仍然必须作为整体全部使用,但同时会在交易中生成零头。比特币应用可以使用一些策略来满足付款需求:组合若干小额 UTXO,并算出准确的找零;或者使用一个比交易额大的 UTXO 然后进行找零。
- 交易会消耗先前记录的未使用的 UTXO,并创建可供未来交易使用的新的 UTXO。通过这种方式,大量的比特币在消费和创建 UTXO 的交易链中在所有者之间进行转移。
- “币基交易”(Coinbase Transaction):每个区块中的第一笔交易,这笔交易由“获胜”矿工设置,创建全新比特币并支付给该矿工作为采矿奖励。币基交易并不消费 UTXO,相反,它有一种称为“coinbase”的特殊类型的输入。
余额
用户的比特币“余额”是指用户钱包中可用的 UTXO总和,它们可能分散在数百个交易和区块中。比特币钱包通过扫描区块链并汇聚所有属于该钱包控制的私钥的 UTXO 来计算该用户的余额。
比特币脚本语言
比特币的脚本语言是一种基于栈的语言。脚本语言通过从左到右处理每个项目来执行脚本。
- 数字(数据常量)被推到栈上。操作码(Operators)从栈中推送或弹出一个或多个参数,对其执行操作,并可能将结果推送到栈上。例如,操作码 OP_ADD 将从栈中弹出两个项目,添加它们,并将结果的总和推送到栈上。
- 条件操作码(Conditional operators)对一个条件进行计算,产生一个 TRUE 或 FALSE 的布尔结果(boolean result)。例如,OP_EQUAL 从栈中弹出两个项目,如果它们相等,则推送为 TRUE(由数字 1 表示),否则推送为 FALSE(由数字 0 表示)。
比特币数字签名
数字签名是一种由两部分组成的数学方案:第一部分是使用私钥对数据创建签名的算法;第二部分是允许在给定数据和公钥时验证签名合法性的算法。比特币中使用的数字签名算法是椭圆曲线数字签名算法(Elliptic Curve Digital Signature Algorithm,ECDSA)
- 创建签名
在比特币的 ECDSA 算法的实现中,被签名的“消息”是交易,或更确切地说是交易中特定数据子集的哈希值。签名密钥是用户的私钥,结果就是签名,签名 Sig 由两个值组成,称为 R 和 S:Sig = (R, S)。
具体流程如下:
- 签名算法首先生成一个临时密钥对。临时密钥对基于随机数 k,用作临时私钥。如果使用相同的值 k 在不同的消息(交易)上生成两个签名,那么任何人都可以计算出签名私钥!!!
- 利用 k,我们生成相应的临时公钥 P(以 P=k*G 计算,与派生比特币公钥相同)。
- 数字签名的R值则是临时公钥 P 的 x 轴坐标。
数字签名的S值:
$$ S = k^{-1}(Hash(m) + dA * R) mod n $$
其中:
- k是临时私钥
- R是临时公钥的 x 坐标
- dA是签名私钥
- m是交易数据(或其部分)
- n是椭圆曲线的阶
- 验证签名
验证使用数据(交易或其部分的哈希值),签名者的公钥和签名(R 和 S 值)来计算值 P,该值是椭圆曲线上的一个点,如果计算出的点P的x坐标等于R,则签名有效。
$$ P = S^{-1} Hash(m)*G + S^{-1}*R*Qa $$
其中:
- R和S是签名值
- Qa是签名者的公钥
- m是交易数据(或其部分)
- G是椭圆曲线生成点
数字签名的用途:
- 身份认证性 : 签名证明私钥的所有者,即资金所有者,已经授权支出这些资金。
- 不可否认性 : 授权证明是不可否认的(不可否认性)。
- 完整性 : 签名证明交易(或交易的特定部分)在签名之后没有也不能被任何人修改。
交易输出
每一笔比特币交易都会创造输出,并被比特币账簿(区块链)记录下来。除特例之外,几乎所有的输出都能创造 UTXO。
交易输出包含两部分:
- "value":一定量的比特币,面值为“聪”(satoshis)。
- "scriptPubKey":作为花费输出所需条件的加密难题(cryptographic puzzle),也被称为锁定脚本(locking script)、见证脚本(witness script),或脚本公钥(scriptPubKey)。
{
...
"vout": [
{
"value": 0.01500000,
"scriptPubKey": "OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 OP_EQUALVERIFY OP_CHECKSIG"
},
{
"value": 0.08450000,
"scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG"
}
]
}
交易输入
交易输入将 UTXO(通过被引用)标记为将被消费,并通过解锁脚本提供所有权证明。
交易输入包含四部分:
- "txid":一个交易 ID,引用包含正在使用的 UTXO 的交易(不是 UTXO)。
- "vout":一个输出索引,用于标识来自该交易中的第几个 UTXO被引用(一个交易可能存在多个输出,第一个为零)。
- "scriptSig":一个解锁脚本,钱包构建它是为了满足 UTXO 中设置的消费条件,满足放置在 UTXO 上的支付条件,解锁支出。
- "scriptSig":一个序列号,与时间锁有关。
{
...
"vin": [
{
"txid": "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
"vout": 0,
"scriptSig": "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL]0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",
"sequence": 4294967295
}
]
}
- 注意:交易输入中仅包含了“txid”,需要首先从区块链中检索引用的 UTXO,来获取该 UTXO 的面值和锁定条件。
交易脚本
比特币网络处理的大多数交易花费的是由“付款至公钥散列”(Pay-to-Public-Key-Hash,P2PKH)脚本锁定的输出。
相应的锁定脚本和解锁脚本如下:
- 锁定脚本(scriptPubKey):锁定脚本是一个放置在输出上的花费条件:它指定了今后花费这笔输出必须要满足的条件。其包含一个公钥散列(hash)值,即我们常说的比特币地址。
- 解锁脚本(scriptSig):解锁脚本是“解决”或满足输出上锁定脚本设定的花费条件并允许花费输出的脚本。解锁脚本是交易输入的一部分,其包含散列值对应的公钥和由相应私钥创建的数字签名。
每个比特币验证节点将通过一起执行锁定和解锁脚本来验证交易。
- 每个输入都包含一个解锁脚本,并引用先前存在的 UTXO。
- 验证软件将复制解锁脚本,检索输入引用的 UTXO,并从该 UTXO 复制锁定脚本。
- 然后按顺序执行解锁和锁定脚本。如果解锁脚本满足锁定脚本条件,则输入有效。所有输入都是独立验证的,作为交易整体验证的一部分。
例子:
- 锁定脚本
OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG
- 解锁脚本
- 组合验证脚本
OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG
- 验证流程
签名散列类型
比特币签名有一种方法,用于通过使用 SIGHASH 标志来指示交易数据的某一部分包含在由私钥签名的散列中。
每个输入可能在其解锁脚本中包含一个签名。因此,包含多个输入的交易可以拥有具有不同 SIGHASH 标志的多个签名,这些标志在每个输入中承诺交易的不同部分。
有三个 SIGHASH 标志:ALL,NONE 和 SINGLE。
SIGHASH flag | Value | Description |
---|---|---|
ALL | 0x01 | 签名应用到所有输出输入 |
NONE | 0x02 | 签名只应用到所有输入,不包括任何输出 |
SINGLE | 0x03 | 签名应用到所有输入和与签名输入具有相同索引号的那个输出 |
另外还有一个修饰符标志 SIGHASH_ANYONECANPAY,它可以与前面的每个标志组合使用。
当设置 ANYONECANPAY 时,只有当前输入被签名,其余的(及其序列号)保持开放以进行修改。 ANYONECANPAY 的值为 0x80,并通过按位 OR 运算,得到如下表所示组合标志:
SIGHASH flag | Value | Description |
---|---|---|
ALL | ANYONECANPAY | 0x81 |
NONE | ANYONECANPAY | 0x82 |
SINGLE | ANYONECANPAY | 0x83 |
应用 sighash 标志的方式:
- 根据使用的 SIGHASH 标志,生成交易的副本并截断其中的某些字段(设置为零长度并清空)。
- 序列化交易副本,将 SIGHASH 标志被添加到序列化交易的结尾。
- 将序列化交易副本 +SIGHASH 标志散列,得到的散列值即是被签名的“消息”。
- 因为在散列化前,SIGHASH 作为最后一步被包含在内,签名也会对 SIGHASH 类型进行签署,因此不能被更改(例如,不能被矿工修改)。
应用:
- ALL|ANYONECANPAY:这种标记可以用来做“众筹”交易,众筹发起人可以用单笔输出来构建一个交易,这笔输出将“目标”金额付给众筹发起人。这样的交易显然是无效的,因为它没有输入。但是现在其他人可以通过添加自己的输入作为捐赠来修改它们,他们用 ALL|ANYONECANPAY 签署自己的输入,直到收集到足够的输入以达到输出的目标金额前,交易无效,每一笔捐款都是一种“承诺”,在募集够整个目标金额之前,筹款不能收回。
- NONE:该结构可用于创建特定金额的“不记名支票”或“空白支票”。它对输入进行承诺,但允许输出锁定脚本被更改。任何人都可以将自己的比特币地址写入输出锁定脚本并兑换交易。
- NONE|ANYONECANPAY:这种构造可以用来建造一个“吸尘器”。在钱包中拥有微小 UTXO 的用户无法花费这些费用,因为手续费超过了这些微小 UTXO 的价值。借助这种类型的签名,微小 UTXO 可以捐赠给任何愿意募集的人并由他们随时花费。
交易费
交易费是指输入和输出之间的差值。从所有输入中扣掉所有输出之后的剩余的金额是矿工收取的交易费。
- 交易的数据结构本身没有交易费这个字段。
- 交易费是以千字节为单位计算的,而不是比特币交易的金额,这意味着如果把大量小型 UTXO 作为输入可能需要支付更高的交易费。
- 交易费作为矿工打包(挖矿)一笔交易到下一个区块中的一种激励;同时作为一种抑制因素,通过对每一笔交易收取小额费用来防止对系统的滥用。成功挖到某区块的矿工将得到该区内包含的交易费,并将该区块添加至区块链中。
- 大多数情况下,交易费影响处理优先级,这意味着有足够费用的交易费更可能被打包进下一个挖出的区块中;任何创建交易的比特币服务,包括钱包、交易所、零售应用等,都必须实现交易费动态计算功能。