BTC笔记-08-脚本
BTC-脚本
一个交易实例
比特币语言仅能操作一个堆栈,因此称为 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。验证分为两步:
- 验证序列化的
redeemScript是否与output script中的哈希值匹配 - 反序列化并执行
redeemScript,验证input script中给出的签名是否正确
redeemScript 的形式:
P2PK形式P2PKH形式多重签名形式
例子:用
P2SH实现P2PKredeemScript: 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
这种方式需要指定 N 和 M 的值
这种方式的输出脚本是比较复杂的,下面使用 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 中

浙公网安备 33010602011771号