[关闭]
@xishuixixia 2017-01-10T04:33:13.000000Z 字数 3461 阅读 1502

给文强的

未分类


深⼊理解⽐特币交易的脚本

在⽐特币区块链中,每⼀个区块都指向上⼀个区块,这些通过SHA256计算的区块哈希链就是⽐特币账本不可篡改的基础。
在⼀个区块中,⽐特币系统⽤交易(Transaction)来表示⼀笔⽐特币交易。⼀个区块包含⾄少⼀笔交易。这些Transaction的Hash通过Merkle Tree计算出所有交易的Merkle Hash,并被包含⾄区块Hash中,从⽽实现交易的不可修改。
如果我们仔细观察每⼀笔交易,可以发现,除了第⼀笔交易是矿⼯的挖矿所得外,每⼀笔交易都拥有⼀个或多个输⼊(TxIn),以及⼀个或多个输出(TxOut):

第⼀笔矿⼯挖矿的收⼊交易通常被称为Coinbase,它没有输⼊,所以TxIn的Hash总是被标记为00000000...0000
其他的交易,任何⼀个TxIn都会唯⼀追溯到区块链上在本区块之前的某个交易Hash,以及索引:

通过交易Hash和索引(从0开始),即可唯⼀确定⼀个未花费的交易输出——UTXO(Unspent Transaction Output)。这样,每⼀个Tx Input都和之前的某个Tx Output关联了起来。
我们假设在上⼀笔交易中,Bob给Alice⽀付了0.15个BTC。
由于⽐特币并没有账户的概念,这⼀笔交易的输出并没有写上Alice的名字,也没有写上Alice的公钥。
那么,Alice想要花费这0.15个BTC,她应该如何证明⾃⼰拥有这个UTXO,并且,其他⼈⽆法假冒Alice来花费这个UTXO呢?
答案是⽐特币的交易创建的输出其实并⾮⼀个简单的公钥地址,⽽是⼀个脚本。在Bob给Alice⽀付0.15个BTC的这个交易中,Bob创建的输出脚本类似:
OP_DUP OP_HASH160 abcd1234...9876 OP_EQUALVERIFY OP_CHECKSIG
其中,abcd1234...9876是Alice的公钥Hash。整个脚本的意思是,谁能够提供⼀个签名和⼀个公钥,让这个脚本运⾏通过,谁就能花费这笔交易的1.5个BTC。
由于创建签名只能使⽤Alice的私钥,⾮Alice的私钥创建的签名将⽆法通过这个脚本的验证,所以,其他⼈⽆法假冒Alice来花费这笔输出。
⼀旦Alice提供了⼀个签名和⾃⼰的公钥,她实际上已经创建了另⼀笔交易来花费这个输出。
所有⼈都可以验证Alice创建的这个新交易是否有效。如果有效,该交易就会被矿⼯打包进新的区块,从⽽成为区块链上不可更改的⼀部分。
我们以著名的Pizza Transaction为例,来验证⼀个交易是否是有效的。
在交易cca75078...4d79中,唯⼀的TxIn输⼊提供的sigScript是:
8b4830450221009908144ca6539e09512b9295c8
a27050d478fbb96f8addbc3d075544dc41328702
201aa528be2b907d316d2da068dd9eb1e23243d9
7e444d59290d2fddf25269ee0e0141042e930f39
ba62c6534ee98ed20ca98959d34aa9e057cda01c
fd422c6bab3667b76426529382c23f42b9b08d78
32d4fee1d6b437a8526e59667ce9c4e9dcebcabb
该sigScript实际上由两部分构成:
签名:30450221...ee0e01(71字节+1字节签名类型),实际签名是去掉最后⼀个字节01的30450221...ee0e,签名类型是SIGHASH_ALL(0x01)。
公钥:042e930f...cabb(65字节)
为了验证该交易是否有效,我们⾸先要根据TxIn所声明的Previous Output Hash:a1075db5…d48d和索引0找到上⼀笔交易的输出:
https://webbtc.com/tx/a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d

这笔交易输出的脚本是:
1976a91446af3fb481837fadbb421727f9959c2d32a3682988ac
⽐特币的脚本由⼀系列指令和数据构成,每个指令占⽤⼀个字节,数据由数据头部的⻓度决定。上述⼆进制脚本翻译后的⽐特币指令如下:
OP_DUP OP_HASH160 46af3fb481837fadbb421727f9959c2d32a36829 OP_EQUALVERIFY
OP_CHECKSIG
现在,我们有了签名,公钥和脚本:
sig: 30450221...ee0e01
pubkey: 042e930f...cabb
OP_DUP OP_HASH160 46af3fb4…6829 OP_EQUALVERIFY OP_CHECKSIG
就可以运⾏这个脚本来验证交易是否有效。
⽐特币脚本被设计成以栈来运⾏的虚拟机指令,它只有有限的⼏种指令,并且故意被设计成没有循环、条件跳转,所以,⽐特币脚本不是图灵完备的语⾔。
⽐特币脚本的执⾏⾮常简单。我们⾸先要准备⼀个空栈,然后把签名和公钥⼊栈:

紧接着,我们就可以执⾏TxOut的脚本:
OP_DUP OP_HASH160 46af3fb481837fadbb421727f9959c2d32a36829 OP_EQUALVERIFY
OP_CHECKSIG
⾸先执⾏OP_DUP,这条指令把栈顶的元素复制⼀份,所以结果变成:

紧接着执⾏OP_HASH160,它对栈顶元素SHA256/RipeMD160,实际上是计算公钥Hash,所以运⾏结果变成:

接下来的指令实际上是⼀个数据,我们直接把数据⼊栈:

然后,执⾏OP_EQUALVERIFY,这条指令会⽐较栈顶的两个元素是否相等,如果不等,整个脚本就执⾏失败了,如果相等,脚本会继续执⾏,所以运⾏结果变成:

最后,执⾏指令OP_CHECKSIG,这条指令会验证签名。⾸先,我们根据签名类型SIGHASH_ALL(0x01)对整个交易进⾏验证。验证⽅法是:
把当前Transaction的所有TxIn的scriptSig去掉(红⾊部分),并把当前TxIn的scriptSig替换为UTXO的script(蓝⾊部分),调整⻓度字段(绿⾊部分):

最后加上⼩端序4字节的签名类型0x01(灰⾊部分),计算两次SHA256,我们得到:
c2d48f45…2669
现在,使⽤ECDSA算法对签名进⾏验证:
boolean ecdsa_verify_signature(byte[] message, byte[] signature, byte[]pubkey)
根据签名的验证结果,我们即可确认该交易是否有效。
由于引⼊了脚本,我们可以看到,⽐特币实际上通过编程脚本实现了⼀个严格以计算机程序验证为基础的数字货币所有权的转移机制。由于计算机程序的可扩展性,⽐特币⽀付其实并不限定在必须⽀付给某⼀个公钥地址。利⽤脚本,我们可以构造出各种⽀付条件,例如,多重签名验证条件:
2 3 OP_CHECKMULTISIGN
这种提供多个公钥地址,并且需要多个签名验证的多重签名脚本,允许在M个签名种⾄少给出N个签名即可使⽤。上述脚本允许提供3个公钥地址中的任意两个有效签名。
当我们把⽐特币托管在某个第三⽅的在线钱包中时,就可以使⽤多重签名来保证只有⾃⼰和第三⽅钱包共同签名后才可动⽤输出,这样保证了⿊客在攻击了第三⽅钱包后也⽆法花掉⽤户的⽐特币。
通过OP_CHECKLOCKTIMEVERIFY,我们可以指定⼀个交易的锁定时间,在此之前,该交易输出⽆法被花掉。这个指令其实实现了⽀付宝的7天资⾦锁定然后再⽀付给卖家的功能。
还有⼀些交易并没有指定⼀个公钥Hash,例如,这个交易的脚本如下:
OP_HASH2566fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000
OP_EQUAL
它的意思是说,谁能够提供⼀个数据,它的SHA256是6fe28c0a...0000,谁就可以花费这笔交易。
(注:该交易已经被花费了,有⼈找到了符合条件的数据)
从⽐特币的脚本,我们可以看到,基于区块链的数字货币⽀付实际上是数字货币所有权的安全转移。如果我们把⾦融资产或者实物资产以数字化的形式登记在区块链上,通过脚本就可以安全实现各种条件下的所有权转移,这正是智能合约在区块链上的应⽤。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注