比特币脚本
1.比特币交易的内容
1.1 比特币交易实例
一个比特币的交易有元数据和输入输出组成,元数据指的是这个交易的基本结构信息:交易大小,版本号,区块信息等,不包括输入输出等杂项
1.1.1交易的宏观信息(Bitcoin Transaction Metadata)
这里的txid就是这个交易的哈希值,也可以理解为交易ID
1.1.2交易的输入输出结构
1.1.2.1 交易的输入(与UTXO相关联,内部包含比特币脚本)
每个输入(vin)包含以下字段:
交易的输入结构vin是个数组,因为一个交易可以有多个输入,每个输入都要说明这个输入花的币是来自之前哪个交易的输出,
txid:之前那个交易的哈希值
vout:表示是这个交易(之前那个交易c0cb...c57b)里的第0个输出
scriptSig(inputScript):输入脚本
"vin": [
{
"txid": "c0cb...c57b", // 上一个交易的哈希值
"vout": 0, // 表示是这个交易(之前那个交易c0cb...c57b)里的第0个输出
"scriptSig(输入脚本,这里不展开,后面单独讲脚本细讲)": {},
]
1.1.2.2 交易的输出(与UTXO相关联,内部包含比特币脚本)
交易的输出vout同样是一个数组,具体内容如下
value :输出的金额,单位比特币(也可以用一聪)这里的0.22684000比特币=22684000聪
n:序号,下标从0开始,表示这是这个交易里的第几个输出,
scriptPubKey:输出脚本,后面详细解释
2.比特币脚本的形式以及执行过程
比特币脚本是一种基于栈的脚本语言(stack base language ),通过入栈出栈操作验证交易数据的合法性。比特币使用的脚本语言唯一能访问的内存空间就是一个堆栈
2.1 比特币脚本的使用流程
比特币脚本是如何来验证这个交易的合法性:
案例说明:这里A给B转账后,一段时间,B给C也转了账,这里B->C交易交易的输入就来自A->B交易的输出。
把B转给C这个交易的输入脚本+A转给B的输出脚本拼接在一起来执行的,并且是前面这个交易的输出脚本放在后面,后面的输入脚本放在前面
早期的比特币的实现过程中,这两个脚本是直接拼接在一起执行,后来出于安全的考虑,这两个脚本改为分别执行,首先执行输入脚本,如果没有出错,再执行输出脚本,如果没有出错,最后栈顶的结果为非零值,也就是true,那么验证通过,这个交易就是合法的,如果过程中出现任何错误,那么就是非法的。
如果一个交易有多个输入,那么每个输入脚本都要和对应交易的输出脚本匹配之后来进行验证,全都
通过,这个交易才是合法的,
2.2 比特币脚本的形式以及脚本内部执行流程
汇编解释:PUSHDATA:入栈、
为了提高比特币网络的安全性,比特币脚本一直在进化中,主要是分为了以下四类
2.2.1 P2PK(Pay to public Key)
P2PK是比特币网络最原始最简单的脚本,主要是分了如下步骤
输入脚本以及输出脚本包含的内容:
输出脚本里直接给出收款人的公钥(最简单的输出脚本的形式)
输入脚本里存储收款人的私钥对输入脚本所在整个交易的签名
执行流程
输入脚本执行执行
1.PUSHDATA(SIG): 把输入脚本里提供的签名PublicKey压入栈
输出脚本执行执行
2.PUSHDATA(PUBKEY): 将输出脚本里提供的公钥Sig压入栈
3.CHECKSIG: 将刚压入的签名PubKey和公钥Sig弹出来,用公钥检查这个签名是否正确,如果正确,返回true,说明验证通过,否则不通过
脚本实例:
2.2.2P2PKH(Pay to public Key Hash)(最常用)
与第一种形式的区别:输出脚本里没有给出收款人的公钥,给出的是收款人公钥的哈希,公钥是输入脚本里给出的,输入脚本既要给出签名,也要给出公钥
输入脚本执行:
1.PUSHDATA(SIG): 签名sig压入栈
2.PUSHDATA(PUBKEY): 公钥pubkey压入栈
输出脚本执行:
3.DUP: 将栈顶的元素pubkey复制一遍
4.HASH160: 弹出栈顶元素pubkey弹出来,取哈希得到publicKeyHash1再压入栈,
5.PUSHDATA(publicKeyHash2): 将输出脚本里的公钥的哈希压入栈
这个时候栈顶有两个哈希值,
栈顶第一个是输入脚本里存的
第二个输入脚本里给出的公钥现场HASH160计算的
6.EQUALVERIFY: 弹出栈顶的两个元素,比较刚在得出的两个pubkeyhash是否相等:为了防止有人茂名顶替,用自己的公钥冒充收款人的公钥
如果这两个哈希值是相等的,就从栈顶消失了。
7.CHECKSIG: 通过栈顶的pubkey和sig检查签名是否正确,如果正确,栈顶就为true,通过脚本
脚本实例
2.2.3 P2SH(Pay to Script Hash) 采用BIP16的方案,最复杂的形式
输出脚本给出的不是收款人公钥的哈希,而是收款人一个脚本(redeemScript赎回脚本)的哈希,将来花这个钱的时候,输入脚本要给出赎回脚本的具体内容,同时还要给出能让赎回脚本正确运行的签名
验证的时候分为两步
1.验证序列化的redeemScript是否与输出脚本中的哈希值匹配?
2.反序列化并执行redeemScript,检查赎回脚本是否能执行通过。
1.第一阶段的验证:
输入脚本执行(所有脚本省略了OP_,例OP_PUSHDATA)
1.PUSHDATA(SIG) :输入脚本的签名入栈
2.PUSHDATA(serialized redeemSrcipt)输入脚本的赎回脚本入栈
输出脚本执行
3.HASH160:对输入脚本的赎脚本内容取哈希得到redeemScript1
4.PUSHDATA(redeemScriptHash):输出脚本里给出的赎回脚本哈希值redeemScript2入栈
5.EQUAL:比较这两个哈希值是否相等
两个赎回脚本的哈希值相等,第一阶段通过,进入二阶段:执行赎回脚本
2.第二阶段的验证(执行赎回脚本):
6.把输入脚本里的序列化的赎回脚本serialized redeemSrcipt进行反序列化,执行赎回脚本,看看赎回脚本能不能通过,通过后验证签名
这里的赎回脚本同理与比特币脚本,可以是P2PK形式也可以是P2PKH形式又或者多重签名的形式,不同的交易,可能有不同的赎回脚本。
验证通过,二阶段完成
具体案例说明:用P2SH实现P2PK的功能
这里的输入脚本就是给出签名,再给出序列化的赎回脚本
这里的赎回脚本的内容就是给出公钥然后用checkSig检查签名
输出脚本是用来验证输入脚本里给出的赎回脚本是否正确(取哈希,看是否一致)
P2SH常见的应用场景:对多重签名的支持
2.2.4 Proof of Burn(销毁证明)格式的脚本(特殊)
这个脚本是证明销毁比特币的一种方法,换取唯一标识的机会
应用场景1:有些小的币种,要求销毁一定数量的比特币,才能得到这个币种,小币种:altCoin(alternative coin)
应用场景2:往区块链里写入一些需要永久保存的内容,例如digitail commitment(数字承诺),例如证明知识产权的,证明已经在某个时间点已经知道某个知识。