跳转至

8.交易脚本

8.1 核心概念

  1. 脚本语言:

    • 基于栈(Stack) 的语言。所有操作(指令)都是对栈顶数据进行操作(压入、弹出、计算、验证)。
    • 指令通常以 OP_ 开头(如 OP_CHECKSIG)。
    • 不支持循环,是图灵不完备的(出于安全性和确定性考虑)。
    • 密码学操作(哈希、签名验证)是其核心功能。
  2. 脚本执行:

    • 交易的解锁脚本(Input Script / ScriptSig) 和对应 UTXO 的锁定脚本(Output Script / ScriptPubKey) 会被拼接在一起执行。
    • 出于安全考虑,它们确实是分别执行的:先执行解锁脚本(将数据压入栈),再执行锁定脚本(对栈上数据进行验证)。这防止了解锁脚本中的恶意操作直接影响锁定脚本的执行逻辑。
    • 执行结果:栈顶最终结果为 True (非零值,通常是 1) 表示验证成功,允许花费该 UTXO;结果为 False (零) 表示验证失败。
  3. 常见脚本类型:

    • 1. P2PK (Pay to Public Key - 早期/简单):

      • 锁定脚本 (Output): <Public Key> OP_CHECKSIG
      • 解锁脚本 (Input): <Signature>
      • 执行过程:
        1. 输入脚本执行:压入签名 Sig
        2. 输出脚本执行:压入公钥 PubKey,然后执行 OP_CHECKSIG
        3. OP_CHECKSIG 弹出栈顶的两个元素 (SigPubKey),用公钥验证签名是否对该交易有效。验证成功压入 1 (True),失败压入 0 (False)。
      • 笔记中的 sig pubkey checksig 指的就是这种基本模式。
    • 2. P2PKH (Pay to Public Key Hash - 最常用):

      • 锁定脚本 (Output): OP_DUP OP_HASH160 <Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG
      • 解锁脚本 (Input): <Signature> <Public Key>
      • 执行过程 (拼接后):
        1. 压入签名 Sig
        2. 压入公钥 PubKey
        3. OP_DUP: 复制栈顶公钥 (现在栈: Sig, PubKey, PubKey)。
        4. OP_HASH160: 弹出栈顶公钥,计算其 RIPEMD160(SHA256) 哈希值并压入 (现在栈: Sig, PubKey, PubKeyHash_Calculated)。
        5. 压入锁定脚本中提供的公钥哈希 <Public Key Hash> (现在栈: Sig, PubKey, PubKeyHash_Calculated, PubKeyHash_Provided)。
        6. OP_EQUALVERIFY: 弹出栈顶两个元素 (PubKeyHash_CalculatedPubKeyHash_Provided),比较它们是否相等。如果不相等,脚本立即失败终止。 如果相等,继续执行 (现在栈: Sig, PubKey)。
        7. OP_CHECKSIG: 弹出栈顶两个元素 (SigPubKey),验证签名。验证成功压入 1 (True),失败压入 0 (False)。
      • 笔记中流程描述正确,术语 PUSHDATA 是隐含的(<Signature>, <Public Key>, <Public Key Hash> 这些本身就是压入数据的操作)。EQUALVERIFY 是关键的安全步骤。
    • 3. P2SH (Pay to Script Hash - 用于复杂脚本/多重签名):

      • 核心思想: 将复杂脚本(赎回脚本 RedeemScript)的哈希值放在锁定脚本中。花费时提供原始脚本和满足它的参数。
      • 锁定脚本 (Output): OP_HASH160 <RedeemScript Hash> OP_EQUAL (非常简单,只存哈希)
      • 解锁脚本 (Input): <Sig1> [Sig2] ... <RedeemScript> (提供满足 RedeemScript 所需的签名和 RedeemScript 本身)
      • 执行过程 (两阶段验证):
        • 阶段 1 (验证脚本哈希匹配):
          1. 输入脚本执行:依次压入提供的签名等参数,最后压入 RedeemScript
          2. 输出脚本执行:OP_HASH160 (弹出栈顶的 RedeemScript,计算其哈希值并压入)。
          3. 压入锁定脚本中的 <RedeemScript Hash>
          4. OP_EQUAL:比较计算出的哈希值和提供的哈希值是否相等。结果 (10) 压入栈顶。如果此时栈顶是 0,脚本失败。 (OP_EQUAL 只压入结果,不立即终止,但后续步骤依赖它为真)
        • 阶段 2 (验证赎回脚本):
          1. 如果阶段1成功(栈顶是 1),反序列化栈中之前压入的 RedeemScript,将其内容作为新的脚本指令序列执行。此时栈上还留有之前压入的签名等参数。
          2. 执行 RedeemScript。这个脚本通常包含实际的验证逻辑(如 m-of-n 多重签名 OP_CHECKMULTISIG)。
          3. RedeemScript 执行的结果决定最终验证是否通过。
      • 优点:
        • 发送方只需知道一个简单的哈希地址,无需关心复杂的接收条件。
        • 复杂逻辑(如多重签名)从锁定脚本移到了赎回脚本,简化了标准交易。
        • 提供了一定的冗余:多重签名允许多个私钥持有者授权交易(m-of-n),即使个别私钥丢失,只要满足最小签名数 m 即可。
      • OP_CHECKMULTISIG (多重签名验证):
        • 语法:<m> <PubKey1> <PubKey2> ... <PubKeyn> <n> OP_CHECKMULTISIG
        • 执行:验证栈上提供的签名数量是否至少为 m,并且这些签名与提供的公钥列表中的 m 个公钥有效对应(顺序需匹配)。
        • 笔记中的 checkmultisig 指的就是这个操作码。
      • 注意: 当前交易的输入脚本(提供签名和 RedeemScript)是和币来源交易(创建该 UTXO 的交易)的输出脚本(包含哈希承诺)进行拼接验证的。
    • 4. OP_RETURN / Proof of Burn (销毁证明):

      • 锁定脚本 (Output): OP_RETURN <Optional Data>
      • 特点:
        • OP_RETURN 指令执行后会立即标记脚本为无效(返回 False)。
        • 发送到这个地址的比特币永远无法被花费,即被销毁
        • 主要用途:
          • 销毁一定数量的 Bitcoin: 有时用于减少流通量或特定协议要求。
          • 添加数据到区块链 (Digital Commitment): <Optional Data> 部分可以存储少量信息(如哈希值、时间戳、文本),作为存在性证明或锚定其他系统。这是最常见的用途。
          • 创建 Altcoin (替代币): 早期一些 Altcoin 通过销毁 BTC 来分配新币(现已不常见)。
      • 笔记中 return命令之后都会直接报错 描述准确。digital commitment 是其核心应用之一。

8.2 其它

  • Coinbase 交易: 每个区块的第一个交易,是矿工的奖励。这个交易的输入(scriptSig)有特殊规则,可以由矿工自由写入一些数据(通常包括 Extra Nonce 和区块高度),而普通交易的输入是指向之前某个 UTXO 的输出。其输出脚本(锁定脚本)决定了奖励发送给哪个地址。
  • 零知识证明: 比特币脚本本身不直接支持通用的零知识证明(如 zk-SNARKs)。像 OP_CHECKSIG 这样的操作码验证签名时,虽然签名本身不直接暴露私钥,但这是一种标准的公钥密码学验证,不是零知识证明。比特币生态中实现强隐私通常需要额外的协议层(如 CoinJoin)或侧链/二层解决方案(如使用 zk-rollups)。核心脚本语言的设计更注重简洁和安全。

  1. 理解栈操作: 脚本执行如何通过压栈、弹栈、操作码处理数据。
  2. 区分 ScriptSig 和 ScriptPubKey: 它们的作用、拼接方式和分阶段执行的原因(安全)。
  3. 掌握 P2PKH 流程: DUP, HASH160, EQUALVERIFY, CHECKSIG 每一步的目的和栈的变化。
  4. 理解 P2SH 原理: 两阶段验证(哈希匹配 + 赎回脚本执行)、目的(简化复杂脚本)、应用(多重签名)。
  5. 理解多重签名 (OP_CHECKMULTISIG): m-of-n 的含义和优势(冗余)。
  6. 理解 OP_RETURN 作用(创建不可花费输出、存储数据)、结果(脚本总是失败)。
  7. 明确 Coinbase 特殊性: 矿工奖励来源,输入脚本可自定义。
  8. 知晓比特币脚本的局限性: 无循环、图灵不完备、不支持原生零知识证明。