0%

BTC八---比特币脚本

比特币脚本

比特币脚本,十分简单,不像c++,java,有堆栈的概念,比特币脚本是基于栈的脚本语言

下面来看一个具体的交易:

实例

size:交易的大小

locktime:用来设定交易的生效时间,0表示立即生效,绝大多数情况下,这个locktime都是0

vin:输入部分

vout:输出部分

time:交易产生的时间

blocktime:区块产生的时间

交易的输入是一个数组,一个交易可以有多个输入,每个输入都要说明输入花的币都是来自之前那个交易的输出

txid:输出这个币的交易的哈希值,

vout:表示是当前交易里的第几个输出,相当于id

scriptSig:输入脚本,因为最简单的输入脚本就是给一个sianature就行了,证明你有权利花这个钱

同样是一个数组,

value:表示输出的金额(BTC),有时候也用Satoshi表示(1Satoshi=0.00000001 BTC)

n:表示这个当前交易的第几个输出,相当于id

scriptPubKey:输出脚本,因为输出脚本最简单的形式就是给出一个public key,所以叫做scriptPubKey

asm:输出脚本的内容,里面包含一系列的操作

reqSigs:表示这个输出需要多少个签名才能兑现

type:输出类型

address:输出地址

脚本执行

如果一个交易有多个输入,那么每个输入脚本都要和对应的输出脚本匹配之后再进行验证,如果全都验证通过,这个交易才是合法的

比特币脚本的几种形式

一、ee

这个签名是用私钥,对整个交易的签名,注意:input script实在我要花这个钱的时候的输出,所以签名是收款人的私钥的签名。(而不是付款人的私钥的签名)

①把输入脚本的签名压入栈

②把输出脚本的收款人的公钥压入栈

③执行CHECKSIG,用PubKey(那么这个)检查Sig是否正确,如果为true,则合法

二、

下面这个hash是你要花这个钱的时候,在输入脚本里给出的公钥

P2PK是最常用的脚本形式!

三、

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

验证分为两步:

①验证输入脚本里给出的redeemScript(赎回脚本)是不是跟输出脚本里给出的哈希值匹配,如果不匹配的话,那么说明给出的赎回脚本是不对的,就类似于P2PKH给出的公钥不对一样,那么验证就失败了;

如果赎回脚本是正确的,那么第二步还要把赎回脚本的内容当作操作指令来执行一遍,看看最后能不能顺利执行,如果两部验证都通过了,那么这个交易才是合法的。

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

赎回脚本的内容就是给出公钥之后,用CHECKSIG检查签名

下面这个输出脚本是用来验证输入脚本给出的赎回脚本是否正确

验证过程:

①②

至此,第一个阶段的验证就到这里结束了,现在进入第二阶段的验证。

第二个阶段首先要把输入脚本里提供的序列化的赎回脚本进行反序列化(序列化才能去哈希,我是这样理解的,如果有什么不对的地方,欢迎再下方评论给我留言!),反序列化的操作是每个结点自己要完成的,然后执行这个被反序列化之后的赎回脚本(反序列化之后就可以执行了,相当于解压)

将PubKey压入栈,之后CHECKSIG

那么这种P2SH的方式有什么好处呢?为什么要搞这么复杂?为什么要把这一部分功能嵌入到赎回脚本里面?

确实,对这个简单的例子来讲,确实有点复杂了,P2SH在最初版本的比特币中是没有的,后来通过软分叉的形式加进去的,它的一个常见的应用场景就是对多重签名的支持。一个输出,可能需要多个签名,才能把钱取出来。

比如某个公司的账户,可能要求五个合伙人中,任意三个人的签名,才能把账上的钱取走,为私钥的泄露提供了一些安全的保护。同时也为私钥的丢失,提供了冗余。

输出脚本给出的M,对应需要多少人的私钥才能匹配,CHECKMULTISIG操作对应匹配操作

输入脚本中第一行的×:比特币中CHECKMULTISIG的实现有一个bug(执行的时候会从堆栈上多弹出一个元素),这个bug现在已经没有办法改了,去中心化的系统,升级软件带来的代价是很大的,要改的话需要硬分叉,所以解决方案是,往栈上多压入一个没用的元素。

另外输入脚本的M个签名的顺序要对应N个公钥的顺序

一次压入栈,看看栈中是否有两个对应的签名,如果有的话,验证通过

注意,这个过程并没有使用P2SH(早期的多重签名只是使用CHECKMULTISTG的方式来实现)

如果是在网上购物的话,用户再转账的时候就需要传给电商公司对应的N个公钥和M的值,这个N个公钥的值和M会在电商网站上公布出来,用户可以相应地填入(每个电商网站可能都不太一样),这样就给用户交易带来了一些不方便的地方(因为这些复杂性都暴露给用户了)。

那么怎么办呢?

就需要用到P2SH,它的本质是把复杂度从输出脚本转移到了输入脚本

复杂度被转移到了redeemScript(赎回脚本由输入脚本提供),输出脚本只需要给出赎回脚本的哈希就可以了,用户只需要知道赎回脚本的哈希值

执行阶段一、

执行阶段二、

把赎回脚本展开后执行

第二阶段的验证和前面直接使用CHECKMULTISIG的情况是类似的

Proof of Burn

比较特殊,输出脚本的开头是RETURN操作,后面可以跟任意的内容,RETURN这个操作的内容是无条件的返回错误,所以包含这个操作的脚本永远不可能通过验证,执行到RETURN这个语句就结束了,后面的内容根本没有机会执行

为什么要设置这样的输出脚本?

这里的钱不是会永远花不出去?这个脚本是证明销毁比特币的一种方法,为什么要销毁?

这个一般是两种应用场景:

①一些小的币种,要求销毁一定数量的比特币才能够得到这个币种,有时候我们管这种小币种叫做AltCoin(Alternative Coin),有的小币种,可能要求你销毁一个比特币,可以得到1000个这样的小币,也就是说,你需要用Proof of Burn这种方法证明你付出了一定的代价,才能够得到这种小币种。

②往区块链里写入一些内容,我们说区块链是个不可篡改的账本,那么有人就利用这种特性,往里面添加一些需要永久保存的内容,你要证明在某个时间知道某些事情,比如说涉及到知识产权保护的,把某项知识产权的内容去哈希之后,把哈希值放在RETURN语句的后面,这个语句后面的内容反正是永远不会被执行的,写什么都没关系,不会占用太大的空间,也不会泄露知识产权的内容,将来如果出现了知识产权的纠纷,那么你再把这个记录的知识产权公布出去,证明你在某个时间点已经知道了某个知识了。

注意:Proof of Burn这种方法所有节点都可以使用,而CoinBase 里的 coinbase transaction域只有获得记账权的节点才能使用,任何用户都可以销毁一点比特币,换取向这个区块链里写入内容的机会。甚至有点交易都没有销毁比特币,只是消耗了交易费。

有一点需要注意,图片中的交易为了简单起见,都没有写OP前缀,比如OP_CHECKSIG

总结

比特币系统中使用的脚本语言是非常简单的(比特币的脚本语言不支持循环),甚至连专门的名字都没有。虽然在某些方法功能有限,但是在另外一些方面功能却很强大,就是和密码学相关的功能是很强大的,比如CHECKMULITSIG用一条语句就可以完成。