区块链入门 ③ - 交易

时间:2023-01-09 22:12:57

交易

概述

比特币交易本质上包含交易参与者价值转移的相关信息数据结构。比特币区块链是一本全球复式记账总账簿,每笔交易都是在比特币区块链上的一个公开记录。

本章中使用术语“钱包”时,我们指的是构建交易的软件,而不仅仅是包含密钥的数据库。

前置知识

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)。

具体流程如下:

  1. 签名算法首先生成一个临时密钥对。临时密钥对基于随机数 k,用作临时私钥。如果使用相同的值 k 在不同的消息(交易)上生成两个签名,那么任何人都可以计算出签名私钥!!!
  2. 利用 k,我们生成相应的临时公钥 P(以 P=k*G 计算,与派生比特币公钥相同)。
  3. 数字签名的R值则是临时公钥 P 的 x 轴坐标。
  4. 数字签名的S值:
    $$
    S = k^{-1}(Hash(m) + dA * R) mod n
    $$

其中:

  • k是临时私钥
  • R是临时公钥的 x 坐标
  • dA是签名私钥
  • m是交易数据(或其部分)
  • n是椭圆曲线的阶
  • 验证签名

验证使用数据(交易或其部分的哈希值),签名者的公钥和签名(R 和 S 值)来计算值 P,该值是椭圆曲线上的一个点,如果计算出的点Px坐标等于R,则签名有效。

$$
P = S^{-1} Hash(m)G + S^{-1}R*Qa
$$

其中:

  • RS是签名值

  • 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):解锁脚本是“解决”或满足输出上锁定脚本设定的花费条件并允许花费输出的脚本。解锁脚本是交易输入的一部分,其包含散列值对应的公钥和由相应私钥创建的数字签名。

区块链入门 ③ - 交易

每个比特币验证节点将通过一起执行锁定和解锁脚本来验证交易。

  1. 每个输入都包含一个解锁脚本,并引用先前存在的 UTXO。
  2. 验证软件将复制解锁脚本,检索输入引用的 UTXO,并从该 UTXO 复制锁定脚本。
  3. 然后按顺序执行解锁和锁定脚本。如果解锁脚本满足锁定脚本条件,则输入有效。所有输入都是独立验证的,作为交易整体验证的一部分。

例子:

  • 锁定脚本
OP_DUP OP_HASH160 <Cafe Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG
  • 解锁脚本
<Cafe Signature> <Cafe Public Key>
  • 组合验证脚本
<Cafe Signature> <Cafe Public Key> OP_DUP OP_HASH160 <Cafe Public Key Hash> 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 标志的方式:

  1. 根据使用的 SIGHASH 标志,生成交易的副本并截断其中的某些字段(设置为零长度并清空)。
  2. 序列化交易副本,将 SIGHASH 标志被添加到序列化交易的结尾。
  3. 将序列化交易副本 +SIGHASH 标志散列,得到的散列值即是被签名的“消息”。
  4. 因为在散列化前,SIGHASH 作为最后一步被包含在内,签名也会对 SIGHASH 类型进行签署,因此不能被更改(例如,不能被矿工修改)。

应用:

  • ALL|ANYONECANPAY:这种标记可以用来做“众筹”交易,众筹发起人可以用单笔输出来构建一个交易,这笔输出将“目标”金额付给众筹发起人。这样的交易显然是无效的,因为它没有输入。但是现在其他人可以通过添加自己的输入作为捐赠来修改它们,他们用 ALL|ANYONECANPAY 签署自己的输入,直到收集到足够的输入以达到输出的目标金额前,交易无效,每一笔捐款都是一种“承诺”,在募集够整个目标金额之前,筹款不能收回。
  • NONE:该结构可用于创建特定金额的“不记名支票”或“空白支票”。它对输入进行承诺,但允许输出锁定脚本被更改。任何人都可以将自己的比特币地址写入输出锁定脚本并兑换交易。
  • NONE|ANYONECANPAY:这种构造可以用来建造一个“吸尘器”。在钱包中拥有微小 UTXO 的用户无法花费这些费用,因为手续费超过了这些微小 UTXO 的价值。借助这种类型的签名,微小 UTXO 可以捐赠给任何愿意募集的人并由他们随时花费。

交易费

交易费是指输入和输出之间的差值。从所有输入中扣掉所有输出之后的剩余的金额是矿工收取的交易费。

  • 交易的数据结构本身没有交易费这个字段。
  • 交易费是以千字节为单位计算的,而不是比特币交易的金额,这意味着如果把大量小型 UTXO 作为输入可能需要支付更高的交易费。
  • 交易费作为矿工打包(挖矿)一笔交易到下一个区块中的一种激励;同时作为一种抑制因素,通过对每一笔交易收取小额费用来防止对系统的滥用。成功挖到某区块的矿工将得到该区内包含的交易费,并将该区块添加至区块链中。
  • 大多数情况下,交易费影响处理优先级,这意味着有足够费用的交易费更可能被打包进下一个挖出的区块中;任何创建交易的比特币服务,包括钱包、交易所、零售应用等,都必须实现交易费动态计算功能。