BTC笔记-08-脚本

BTC-脚本

B站视频链接


一个交易实例

实例

比特币语言仅能操作一个堆栈,因此称为 stack based language 基于栈的语言


交易结构

交易的宏观信息

"result": {
    "txid": "921a..dd24",
    "hash": "921a..dd24",
    "version": 1,
    "size": 226,
    "locktime": 0,      // 设定交易的生效时间,0 -> 立即生效
    "vin": [...],
    "vout": [...],
    "blockhash": "0000000000000000002c510d...5c0b",
    "confirmations": 219722,
    "time": 1530846727,
    "blocktime": 1530846727
}

交易的输入

"vin": [{
    "txid": "c0cb...c57b",      // 币来源的交易的哈希值
    "vout": 0,                  // 表示是来源交易输出中的第几个
    "scriptSig": {              // input script
        "asm": "3045...0018",   // assembly,表示 OP_CODE
        "hex": "4830...0018"    // 脚本的十六进制表示
    },
}]

交易的输出

"vout": [{
    "value": 0.22684000,        // BTC 数量
    "n": 0,                     // 序号
    "scriptPubKey": {           // output script
        "asm": "DUP HASH160 2683...d743 EQUALVERIFY CHECKSIG",
        "hex": "76a9...88ac",
        "reqSigs": 1,           // 需要多少个签名才能兑现
        "type": "pubkeyhash",   // 输出类型
        "addresses": [          // 输出地址
            "19z8...QvSr",
        ]
    }
}, {
    "value": 0.53756644,
    "n": 1,
    "scriptPubKey": {
        "asm": "DUP HASH160 da7d...2cd2 EQUALVERIFY CHECKSIG",
        "hex": "76a9...88ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
            "1LvG...NYhX",
        ]
    }
}]

交易示意图

验证交易需要将交易的输入脚本与所声明的币来源的输出脚本拼接执行,若不出错则交易合法(安全起见,实际上输入脚本与输出脚本是分开执行的)


输入、输出脚本的几种形式

P2PK (Pay to Public Key)

input script:
    PUSHDATA (Sig)

output script:
    PUSHDATA (PubKey)
    CHECKSIG

输出脚本里直接给出收款人的公钥

输入脚本里直接给出签名,这个签名是用私钥对输入脚本所在的整个交易的签名

CHECKSIG 会弹出栈顶的两个元素,并用公钥进行检查。如果合法,将 TRUE 压入栈顶,否则运行失败


P2PKH (Pay to Public Key Hash)

input script:
    PUSHDATA (Sig)
    PUSHDATA (PubKey)

output script:
    DUP
    HASH160
    PUSHDATA (PubKeyHash)
    EQUALVERIFY
    CHECKSIG

与上面的区别是,输出脚本中没有直接给出收款人地址的公钥,而是使用公钥的哈希。公钥是在输入脚本里给出的

这种方式也是最常使用的

DUP 会将栈顶元素复制一份再压入栈

HASH160 会将栈顶元素弹出,取哈希值,再压入栈

EQUALVERIFY 会比较栈顶的两个元素是否相等


P2SH (Pay to Script Hash)

采用 \(\text{BIP 16}\) 的方案

input script:
    ...
    PUSHDATA (Sig)
    ...
    PUSHDATA (serialized redeemScript)

output script:
    HASH160
    PUSHDATA (redeemScriptHash)
    EQUAL

这里的输出脚本没有使用收款人地址的哈希,而是一段脚本的哈希,称为 redeemScript 赎回脚本

input script 要给出一些签名及一段序列化的 redeemScript。验证分为两步:

  1. 验证序列化的 redeemScript 是否与 output script 中的哈希值匹配
  2. 反序列化并执行 redeemScript,验证 input script 中给出的签名是否正确

redeemScript 的形式:

  • P2PK 形式
  • P2PKH 形式
  • 多重签名 形式

例子:用 P2SH 实现 P2PK

redeemScript:
    PUSHDATA (PubKey)
    CHECKSIG

input script:
    PUSHDATA (Sig)
    PUSHDATA (serialized redeemScript)

output script:
    HASH160
    PUSHDATA (redeemScriptHash)
    EQUAL

第一阶段的验证:拼接输入与输出脚本并执行

第二阶段的验证:反序列化赎回脚本,执行

这种方式可以很好的支持多重签名


Proof of Burn

output script:
    RETURN
    ...

这种形式的 output 被称为:Provably Unspendable / Prunable Outputs

假如有一个交易的 input 指向这个 output,不论 input 里的脚本如何设计,执行到 RETURN 命令之后都会直接返回 false,不会执行 RETURN 后面的其他指令,所以这个 output 无法再被花出去

这种交易可以作为销毁比特币的证明,也可以向区块链中写入一些内容以保存


多重签名

最早的多重签名目前已不再使用

input script:
    ❌                  # 随便压入什么东西,以应对 CHECKMULTISIG 的 bug
    PUSHDATA (Sig_1)
    PUSHDATA (Sig_2)
    ...
    PUSHDATA (Sig_M)

output script:
    M
    PUSHDATA (pubkey_1)
    PUSHDATA (pubkey_2)
    ...
    PUSHDATA (pubkey_N)
    N
    CHECKMULTISIG

这种方式需要指定 NM 的值

这种方式的输出脚本是比较复杂的,下面使用 P2SH 实现

redeemScript:
    M
    PUSHDATA (pubkey_1)
    PUSHDATA (pubkey_2)
    ...
    PUSHDATA (pubkey_N)
    N
    CHECKMULTISIG

input script:
    ❌
    PUSHDATA (Sig_1)
    PUSHDATA (Sig_2)
    ...
    PUSHDATA (Sig_M)
    PUSHDATA (seialized RedeemScript)

output script:
    HASH160
    PUSHDATA (RedeemScriptHash)
    EQUAL

输出的复杂被转移到了 redeemScript

posted @ 2022-08-29 12:05  buzzing  阅读(83)  评论(0)    收藏  举报