浅析比特币脚本

浅析比特币脚本,第1张

文章目录 前言比特币脚本语言结构脚本执行过程参考文献

前言

在比特币网络中,每个用户的身份是通过公钥来识别的,由此实现了一定程度的用户的匿名性。那么,直觉上,自然而然地我们会认为每一笔交易是通过公钥来指明支付的对象,毕竟现在公钥就等价于用户的身份z号码。然而实际上并非如此,每一笔交易的输出实际上只是指明了一个脚本(script),通过脚本,间接地指明这笔交易会把多少比特币转给谁。那么为什么交易的输出不是直接用公钥来指明比特币的支付对象,而要通过脚本来指明呢?

实际上这是为了保证两点,其一,支付对象的匿名性;其二,所支付的比特币只能被支付对象所使用。举个例子,如下图所示,假如Alice在交易a的输出中,将十个比特币给了Bob,在交易b中,Bob又将这十个比特币转给了Carol,那么Alice在交易a中应该怎么做才能保证这十个比特币在后续的交易中只能被Bob使用,而不是被别人偷走?
Alice应该在交易输出中给出Bob的公钥哈希,并且要求,假如Bob要使用这个十个比特币,那就必须要提供Bob的公钥以及Bob的数字签名,只有当Bob给出的公钥的哈希与Alice给的相等,并且Bob的数字签名有效时,才能使用这十个比特币。为什么Alice不是在交易输出中直接指明Bob的公钥,而是指明Bob的公钥哈希?这是为了实现匿名性,具体原因可以参考这篇博客为何使用地址而不是公钥?。

当Alice在交易输出中使用Bob的公钥哈希时,Alice可以证明这笔支付是给了Bob;而当Bob在后续的交易公开给出公钥和他的数字签名时,可以向大家证明他是Bob,从而解锁Alice所支付的比特币,注意,Bob需要在后续交易中公开公钥,而不是简单地给出他的公钥哈希,这样才能让别人在验证他的数字签名是有效的同时,也证明上一次Alice所支付的地址的确是Bob的公钥哈希。Alice支付的过程就是通过给出输出脚本来实现的,而Bob解锁Alice所支付给他的比特币的过程则是通过输入脚本来实现的。当Bob要解锁Alice支付的比特币时,Bob会给出输入脚本,这个输入脚本会和Alice给出的输出脚本拼接到一起,组成一个完整的脚本,然后执行脚本,如果结果是True,那么就解锁成功,Bob的这次交易就能被成功地添加到区块中,否则这次交易无效,不会被添加到区块中。

比特币脚本语言结构

从上面一节可以看出,Bob要完成一次交易,需要执行一个比特币脚本,该脚本由输入脚本(scriptSig)和输出脚本(scriptPubKey)两部分组成,输出脚本由Alice在之前的交易(交易a)的输出中给出,输入脚本则由Bob在本次交易(交易b)中给出。比特币脚本使用方法主要有以下几种。

P2PK (Pay to Public Key)P2PKH (Pay to Public Key Hash)P2SH (Pay to Script Hash)多重签名Proof of Burn

在本文中介绍的例子将会使用第二种(P2PKH),这种是最常用的方法,有关其它方法的细节,可以参考比特币脚本原理和使用方法一文,这里不再细述。一个P2PKH输出脚本(scriptPubKey)的例子如下所示,在上面交易例子中由Alice在交易a的输出中给出,输出脚本里包含了Bob的公钥哈希(69e02e18…),以及一些 *** 作,后面会提到这些 *** 作。

OP_DUP
OP_HASH160
69e02e18...
OP_EQUALVERIFY
OP_CHECKSIG

一个输入脚本(scriptSig)的例子如下图所示,在上面的交易例子中由Bob在交易b的输入中给出。这里的输入由两部分组成,一是Bob用他自己的私钥对本次交易(即交易b)进行签名所得到的数字签名(sig, signature),另一部分就是Bob的公钥(pubKey, public key)。给出公钥主要有两个目的,一个是证明Alice在交易a中支付的对象的确是Bob,前面提到,Alice在交易a中只是给出了一个公钥哈希,但是并没有明确指明Bob的公钥,那么别人怎么能知道Alice给的这个公钥哈希就是Bob的公钥哈希呢?因此,为了证明这点,Bob需要在交易b公开他的公钥,然后取哈希,一旦得到的哈希与Alice给的相等,那就证明了Alice给的公钥哈希的确是Bob的公钥哈希,换言之,Alice这十个比特币的确是支付给Bob的。另外一个目的就是为了进行数字验签,Bob需要给出公钥,别人才能用他的公钥和数字签名进行验签,才能确认他的数字签名有效,即他的确是Bob。

<sig>
<pubKey>

Bob在进行交易b时,需要证明他是Bob,以解锁交易a输出中给Bob的十个比特币,这个证明的过程是通过执行脚本来实现的。Bob会将Alice在交易a输出中给出的输出脚本拼接到他给的输入脚本后面,组成一个完整的脚本,然后执行该脚本,拼接后Bob所执行的完整的脚本如下所示。

<sig>
<pubKey>
------------------------
OP_DUP
OP_HASH160
69e02e18...
OP_EQUALVERIFY
OP_CHECKSIG
脚本执行过程

比特币脚本所使用的语言是一门基于堆栈的语言,脚本的每一行要么是数据,要么是指令,执行过程从脚本的第一行开始,自上到下。如果是数据的话,就将其压入到堆栈中,如果是指令的话,就执行该指令。

还是以上面的例子来进行说明,当Bob在交易b要解锁Alice在交易a支付给他的十个比特币时,Bob需要执行上面的这个完整的比特币脚本,执行过程如下。

第一步,将Bob在交易b的输入脚本中给出的他对当前交易(即交易b)的数字签名压入堆栈。第二步,将Bob在交易b的输入脚本中给出的他自己的公钥压入堆栈。第三步,执行OP_DUP,该 *** 作将会复制Bob的公钥并添加到栈顶。第四步,执行OP_HASH160,该 *** 作将对当前栈顶的Bob的公钥拷贝进行哈希运算,得到Bob的公钥哈希,并将其替换当前栈顶的Bob的公钥拷贝。第五步,将Alice在输出脚本中给出的Bob的公钥哈希(在上面的例子中是69e02e18…)d入到堆栈中。第六步,执行OP_EQUALVERIFY,判断Alice给出的公钥哈希,和第四步对Bob的公钥进行哈希运算后得到的公钥哈希进行对比,判断两者是否相等,相等则继续往下执行,否则认为本次交易失败。第七步,假如第六步的 *** 作成功,那么将执行这一步。到这一步时,堆栈中只剩下Bob在交易b的输入脚本中给出的他的公钥(),以及他用自己的私钥对交易b进行签名所得到的数字签名()。执行OP_CHECKSIG,该 *** 作将进行数字验签。验签过程大致如下,用Bob给出的公钥()对他给出的他对交易b的数字签名进行解密,得到一个哈希值hash = decrypt(publicKey, signature),然后再对交易b进行哈希运算,得到另一个哈希值hash’,假如hash和hash’相等,那么数字验签成功,即Bob证明了他是Bob,可以开始使用Alice在交易a中给他的十个比特币了,本次交易有效,将被加入到新创建的区块中;否则验签失败,本次交易被丢弃。关于数字验签,可以参考签名算法以及一文彻底搞懂加密、数字签名和数字证书!这两篇文章。

上面的执行过程可以用下图表示,大家可以参考这张图,结合上面的描述来理解整个过程。

参考文献 为何使用地址而不是公钥?比特币脚本原理和使用方法签名算法一文彻底搞懂加密、数字签名和数字证书!深入理解比特币交易的脚本理解比特币脚本Bitcoin and Cryptocurrency Technologies by Arvind Narayanan, Joseph Bonneau etl.

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/1323293.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存