五、比特币脚本

962 阅读7分钟

比特币脚本

1.比特币交易的内容

1.1 比特币交易实例

一个比特币的交易有元数据输入输出组成,元数据指的是这个交易的基本结构信息:交易大小,版本号,区块信息等,不包括输入输出等杂项

1.1.1交易的宏观信息(Bitcoin Transaction Metadata)

这里的txid就是这个交易的哈希值,也可以理解为交易ID image.png image.png

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(输入脚本,这里不展开,后面单独讲脚本细讲)": {},
    ]

image.png

1.1.2.2 交易的输出(与UTXO相关联,内部包含比特币脚本)

交易的输出vout同样是一个数组,具体内容如下

value :输出的金额,单位比特币(也可以用一聪)这里的0.22684000比特币=22684000聪

n:序号,下标从0开始,表示这是这个交易里的第几个输出,

scriptPubKey:输出脚本,后面详细解释

image.png

2.比特币脚本的形式以及执行过程

比特币脚本是一种基于栈的脚本语言(stack base language ),通过入栈出栈操作验证交易数据的合法性。比特币使用的脚本语言唯一能访问的内存空间就是一个堆栈

2.1 比特币脚本的使用流程

比特币脚本是如何来验证这个交易的合法性:

案例说明:这里A给B转账后,一段时间,B给C也转了账,这里B->C交易交易的输入就来自A->B交易的输出。

image.png

把B转给C这个交易的输入脚本+A转给B的输出脚本拼接在一起来执行的,并且是前面这个交易的输出脚本放在后面,后面的输入脚本放在前面

早期的比特币的实现过程中,这两个脚本是直接拼接在一起执行,后来出于安全的考虑,这两个脚本改为分别执行,首先执行输入脚本,如果没有出错,再执行输出脚本,如果没有出错,最后栈顶的结果为非零值,也就是true,那么验证通过,这个交易就是合法的,如果过程中出现任何错误,那么就是非法的。

如果一个交易有多个输入,那么每个输入脚本都要和对应交易的输出脚本匹配之后来进行验证,全都

通过,这个交易才是合法的,

2.2 比特币脚本的形式以及脚本内部执行流程

汇编解释:PUSHDATA:入栈、

为了提高比特币网络的安全性,比特币脚本一直在进化中,主要是分为了以下四类

2.2.1 P2PK(Pay to public Key)

P2PK是比特币网络最原始最简单的脚本,主要是分了如下步骤

输入脚本以及输出脚本包含的内容:

输出脚本里直接给出收款人的公钥(最简单的输出脚本的形式)

输入脚本里存储收款人的私钥对输入脚本所在整个交易的签名

image.png

执行流程

输入脚本执行执行


1.PUSHDATA(SIG): 把输入脚本里提供的签名PublicKey压入栈

输出脚本执行执行


2.PUSHDATA(PUBKEY): 将输出脚本里提供的公钥Sig压入栈

3.CHECKSIG: 将刚压入的签名PubKey公钥Sig弹出来,用公钥检查这个签名是否正确,如果正确,返回true,说明验证通过,否则不通过

image.png

脚本实例

image.png

2.2.2P2PKH(Pay to public Key Hash)(最常用)

与第一种形式的区别:输出脚本里没有给出收款人的公钥,给出的是收款人公钥的哈希,公钥是输入脚本里给出的,输入脚本既要给出签名,也要给出公钥

image.png

输入脚本执行:

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,通过脚本

image.png

脚本实例

image.png

2.2.3 P2SH(Pay to Script Hash) 采用BIP16的方案,最复杂的形式

输出脚本给出的不是收款人公钥的哈希,而是收款人一个脚本(redeemScript赎回脚本)的哈希,将来花这个钱的时候,输入脚本要给出赎回脚本的具体内容,同时还要给出能让赎回脚本正确运行的签名

验证的时候分为两步

1.验证序列化的redeemScript是否与输出脚本中的哈希值匹配?

2.反序列化并执行redeemScript,检查赎回脚本是否能执行通过。

image.png

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形式又或者多重签名的形式,不同的交易,可能有不同的赎回脚本

验证通过,二阶段完成


image.png

image.png

具体案例说明:用P2SH实现P2PK的功能

image.png 这里的输入脚本就是给出签名,再给出序列化的赎回脚本

这里的赎回脚本的内容就是给出公钥然后用checkSig检查签名

输出脚本是用来验证输入脚本里给出的赎回脚本是否正确(取哈希,看是否一致)

image.png

P2SH常见的应用场景:对多重签名的支持

image.png

image.png

image.png

image.png

image.png

2.2.4 Proof of Burn(销毁证明)格式的脚本(特殊)

image.png

这个脚本是证明销毁比特币的一种方法,换取唯一标识的机会

应用场景1:有些小的币种,要求销毁一定数量的比特币,才能得到这个币种,小币种:altCoin(alternative coin)

image.png

应用场景2:往区块链里写入一些需要永久保存的内容,例如digitail commitment(数字承诺),例如证明知识产权的,证明已经在某个时间点已经知道某个知识。

image.png