比特币系统的脚本(Script)—交易生成和验证的原理(第一部分)(初稿)

当前位置:首页 > 币圈百科 > 比特币系统的脚本(Script)—交易生成和验证的原理(第一部分)(初稿)

比特币系统的脚本(Script)—交易生成和验证的原理(第一部分)(初稿)

2023-01-07币圈百科465

前言

在讲脚本系统之前,先问一个看似简单的问题:比特币的“地址”是什么?估计大部分人对此不屑一顾。——这个问题太简单了(弱智)。比特币地址是日常生活中使用非常频繁的东西。几乎所有交易都使用比特币地址。蓑衣网小编2022对比特币稍有了解的人可能会说Base58encode代码和RIPEMD160哈希算法,对技术稍有了解的人可以详细讲解比特币地址计算的每一步。其实这种认识只停留在表面。有了这样的认识,我恐怕就无法真正理解比特币系统是什么,从而严重低估了这项创造性发明的价值。甚至有人认为“真正有价值的不是比特币,而是区块链”。在解释什么是“比特币地址”之前,首先要为比特币“交易”这个概念做个铺垫。比特币系统创建之初,并没有“地址”这个东西。当时还是可以交易的。那么交易是如何实现的呢?

一、交易实现及比特币地址介绍

比特币交易其实是独立于地址的,至少不是我们看到的“地址”。比特币系统的交易依赖于脚本。在进行支付时,我们实际上将支付的金额与接收方的“赎回脚本”绑定。将来,接收者可以使用自己的“签名脚本”来确认使用权。每一笔交易的实现,都只靠“剧本”。 以下是两种常见脚本的格式:“赎回脚本”:(输出脚本)

P2PKH(支付到公钥地址模式):

OP _ DUP OP _ hash 160(014)[一个20字节的哈希值]OP _ equal verify OP _ check SIG

P2SH(支付到脚本模式,支付到脚本模式?[一个20字节的哈希值] OP_EQUAL“签名脚本”:(输入脚本)

P2PKH模式:

[签名的字节数][签名]0x 01[公钥的字节数][公钥]

P2SH模式:m-n multisig

0x00?[字节数]?[签名1]001…[字节数][签名m]001[支付合同脚本字节数]?[m]?[字节数][公钥1] …?[字节数][公钥n] [n n][n] OP_CHECKSIGVERIFY?以的一对一交易为例:http://block meta . com/TX/c 8 BC 7 CFF 08249 e 5 f 9970 e 15 be 64259 b 0135 b 0 b 6 e 37 f 9 f 088 a 719508 CBD 8 BC[X]c 蓑衣网小编2022 8 BC 7 CFF 08249 ea 5 f 9970 e 15 be 64259 b 0135 b 0 b 6 e 37 f 19 f 088 CBD 8 BC(txt(约定签名数据为HASH_ALL(tx))47(支付合同脚本中的字节数)52 (OP_2,表示m-n签名中M=2)21?(第一个公钥的字节数)0293 BAF 0397588 a cc1ab 056 e 868 FD 188 DC 0 EEA 7554 b 45370 aae 862 f 9d 2493 a 4c 121?(第二公钥的字节数)020 ab 7517 cf 22 a 46t 503 ee 8 dcae 7f 9 f 109 C4 CD 19 f 0 ab 9d 77 c 89 c 607554 F3 D5 aa 9(第一公钥)52(OP_2,表示m-n签名中n=2,说明这是2-2签名)ae(OP_CHECKSIGVERIFY)

(第二个输入脚本是P2SH模式, 付款人17 efz 829 NBT 2 wetlj 3 wj 5 yufxvagkuugs地址)

48(签名字节数)

04502203 Fe 5 f 04 a 013512 a 4773414 b 25 ed c8 c 7915473d D5CF 87 BC 73d 28 e 1 AAFF DB 4d 14 f 02100 e 16156d 526d 1498 f 2 cf 5 EB 02d 53 e 02 f 7 FD 5 cf 1 ddf DD 255 网站显示时省略此字节)7232 ca 33 e 0797405 a 512 fa 872934 CD 922 c 812965(20字节的哈希值)OP_EQUALVERIFYOP_CHECKSIG(第二个输出脚本在P2PKH模式下,取回3 AE 2 tyfyhvwh 11 puy 6 hak 7 Rb yn 9 gfz 3 蓑衣网小编2022 fk的地址)OP_HASH16014(字节数,014=20L,网站在(20字节的哈希值)OP_EQUAL

为了简化解释,我们先只分析P2PKH模式下的事务。如果读者对P2SH感兴趣,可以在下面介绍完脚本原理后,自己分析一下P2SH是如何实现的。在这里,我们先抛开“交易是如何验证的”这个问题。从交易数据的格式可以看出:在P2PKH下,进行支付时需要通过“签名脚本”来确认资产的使用权,并且需要知道接收方的“赎回脚本”才能进行正确的支付。普通用户可能无法直接记住和使用这些脚本。那么他们如何达成交易呢?为了简化日常交易流程,核心开发者引入了“比特币地址”的概念。 通过一些协议,各种钱包软件可以通用的方式自动计算出需要的脚本。实际上,这些脚本是由钱包软件而不是用户实现的。由核心开发人员同意并正式发布的协议,它规定了特定的数据格式以及从这种格式的数据中计算相应脚本的方法。这是一个独立于bit系统内核的既定规则,使得不同的钱包软件能够通用的收发钱。

P2PKH地址的格式约定如下:

1。前导字节(1字节):前缀=000,表示这是一个P2PKH地址;2.公钥的哈希值(20字节):使用hash160算法——RIPEMD160(sha256(公钥))将公钥转换成一个20字节的数据;3.校验码(4字节):使用hash256算法——SHA256(SHA256([0x00] [20字节哈希值]),取前4个字节作为校验码;4.以文本格式生成地址:使用Base58对前三步获得的25字节数据进行编码。因为前导字节是000,所以生成的地址的第一个字母是字符“1”。

P2SH地址的格式约定如下:

1。前导字节(1字节):前缀=005,表示这是一个P2SH地址;2.契约脚本的哈希值(20字节):使用hash160算法——RIPEMD160(sha256(契约脚本))生成一个20字节的数据;3.校验码(4字节):使用hash256算法——SHA256(SHA256([0x00] [20字节哈希值]),取前4个字节作为校验码;4.以文本格式生成地址:使用Base58对前三步获得的25字节数据进行编码。因为前导字节是005,所以生成的地址的首字母是字符“3”。

对应脚本的计算方式:

根据前导字节判断地址模式,自动生成对应格式的脚本:P2PKH模式:

“赎回脚本”:OP _ dupop _ hash 160[20字节的哈希值] OP_EQUALVERIFY OP_CHECKSIG“签名脚本”:[签名][签名消息的类型(1字节)][公钥]

[x“赎回脚本”:OP_HASH160 [20字节的20哈希值] OP_EQUAL“签名脚本”:[签名1…签名m-n多签合同脚本(当n 16),m[公钥1 …公钥n] n

比特币系统的脚本(Script)—交易生成和验证的原理(第一部分)(初稿)

(不使用地址实际上还是可以交易的。 )但是,这些脚本有什么用呢?为什么要把交易过程搞得这么复杂?

二。脚本的重要性

如果只是为了某个特定的交易,那么就没有必要使用脚本。假设只实现支付和收款,那么,在比特币系统建立之初,约定“赎回脚本”为公钥,约定“签名脚本”为签名。在集中模式下,你完全可以做到这一点,因为如果你发现未来某个模式无法满足新的交易类型,或者你发现某个模式存在安全隐患,那么你可以通过推出升级包或补丁,或者发布新版本来解决。然而,它在分散模式下不起作用。一旦达成协议,任何改变都需要事先讨论和同意。除非问题简单明了,一目了然,解决方案只有一个,否则几乎不可能达成共识。以目前区块扩容这么简单的问题,即使大家都知道扩容不能等,20M的规模也不是长久之计,但至少可以暂时缓解危机,但最终能否达成共识还需要一年左右的时间。估计比特币系统的设计者是为了让系统有机会应对不可预见的交易模式。即使经过几十年、几百年,即使不修改协议,这个系统也不会过时。因此,脚本系统被引入以增加适应性。但是,这也增加了系统设计的难度。 脚本解释器(或过程)在开始时必须设计得足够简单,这样就不会有错误。同时,它应该近乎完美,足够灵活,以适应各种未知的需求。剧本体系一旦确定,任何改动都要经过严格的测试,先达成共识,否则必然会产生分歧。这种设计对开发人员的能力要求很高。早期开发者设计的脚本系统并不完善,协议非常模糊(比如OP_ADD之类的加法指令并没有规定如何处理整数溢出)。为了系统的安全性,增加了很多与比特币系统本身无关的附加限制(包括脚本字节、指令等的限制。而块大小的限制是另一个附加限制),这使得脚本系统失去了很多灵活性。所以比特币系统还处于实验期,系统设计还没有达到理想状态。(当协议足够完善,各种限制放开后,比特币系统可以说已经过了实验期。当然,即使实验期结束了,也不代表比特币系统一定会成功,但毫无疑问,这个系统至少是一种有足够震撼力的创造)

III。脚本原理

比特币系统的脚本系统设计的非常简洁,稍微有点编程基础的人都很容易理解。这与用堆栈实现简单计算器的方式非常相似。比特币的脚本系统在stack的帮助下运行。在验证事务时,脚本系统依次读取每条指令并操作堆栈。所有指令完成后,检查堆栈中的剩余数据。如果都为真,则交易有效,否则,交易无效。

stack是实现LIFO的数据结构,后进先出)。它使用两个基本操作:push和pop,通常还有另一个常见操作:peek。顶部的元素称为堆栈的顶部。比如:把一本书放在一定高度的箱子里(箱子的宽度只能装一本书)。放书的动作是push,拿出书的动作是pop。最后放进去的书必须先拿出来。拿起最上面的书,检查一下,然后放回原处,这叫做偷看。

脚本系统规定所有指令(或运算符)都是1字节,也就是最多只能有256种指令。该协议规定了在执行每个指令(OP)时如何处理堆栈。举个例子,假设下面的数据是S中的push:(a,b,c,d,e) OP_ADD:加法指令。从s中依次弹出e和d两条数据。计算f=e d .然后将f压入s .此时s中的元素为(a,b,c,f) OP_DUP:复制栈顶的数据。从S中窥视出一个数据,此时数据为F,复制一份F压入栈中。此时s中的元素为(a,b,c,f,f) OP_PUSHDATA1: push data。将紧接着OP指令的1字节数据(如G)压入堆栈。s中的元素是(a,b,c,f,f,g)。请参考脚本协议中各指令的名称和用法:https://en.bitcoin.it/wiki/Script,的附件中有一个github的链接,附带一个比特币脚本解释器(C语言)的例子,没有经过严格测试,仅供参考。下面的P2PKH事务用于说明脚本系统是如何工作的。数据取自第一部分引用的交易。1

输入脚本(签名脚本):48304502203 Fe 04 a 013512 a 4773414 b 25 ed 8 c 7915473 D5 cf 87 BC 73d 28 E1 AAF db 4d 14 f 02100 e 16156d 526d 1498 F2 cf 5 EB 02d 5302 f 7 FD 5 cf1dfdd25e4b032fdc5c59c9fd27b01210203635e5c184951e14fcfecc83b15960594f4fceec729e09a4a517b0a03a7f4b9

每个数字的含义如下:

PUSHDATA48签名 (DER)sequence30length45integer02length20X3fe5f04a013512a4773414b25edc8c7915473dd5cf87bc73d28e1aaffdb4d14finteger02length21Y00e16156d526d1498f2cf5eb02d53e02f7fd5cf1dfdd25e4b032fdc5c59c9fd27bSIGHASH_ALL01PUSHDATA 4121公钥type02X03635e5c184951e14fcfecc83b15960594f4fceec729e09a4a517b0a03a7f4b9Y从区块链上找到该UTXO所对应的输出脚本(赎回脚本)为: (注意:不是这一笔交易中的输出脚本)OP_DUP OP_HASH160 44524fa542897f46a9a0cccc27ccb91ba822b4b6 OP_EQUALVERIFY OP_CHECKSIG76 a9 ?14?44524fa542897f46a9a0cccc27ccb91ba822b4b6?88 acOP_DUP76OP_HASH160a9PUSHDATA14公钥哈希值44524fa542897f46a9a0cccc27ccb91ba822b4b6OP_EQUALVERIFY88OP_CHECKSIGac注:指令中,凡是小于0x4b的数字均为PUSHDATA指令,这个数字同时代表打算压入堆栈的字节数。验证一笔交易时,首先要读取输入脚本中的内容。题外话:在早期脚本协议的约定中,输入脚本中只允许OP_PUSHDATA指令,这给后期推行P2SH方式制造了很大的麻烦,2012年的时候不得不冒着硬分叉的风险,在取得多数共识后强行引入了新协议,这样我们才有机会使用多重签名或智能合约这种模式。输入脚本(签名脚本):script = 48 30 45 02 20 3f e5 f0 4a 01 35 12 a4 77 34 14 b2 5e dc 8c 79 15 47 3d d5 cf 87 bc 73 d2 8e 1a af fd b4 d1 4f 02 21 00 e1 61 56 d5 26 d1 49 8f 2c f5 eb 02 d5 3e 02 f7 fd 5c f1 df dd 25 e4 b0 32 fd c5 c5 9c 9f d2 7b 01 21 02 03 63 5e 5c 18 49 51 e1 4f cf ec c8 3b 15 96 05 94 f4 fc ee c7 29 e0 9a 4a 51 7b 0a 03 a7 f4 b9流程:令p = script;op = *p; (op = 0×48),这是PUSHDATA指令,p++, 将后面的72个字节数据压入堆栈S, p +=72.op = *p; ?此时,op = 0×21,这也是PUSHDATA指令,p++, 将后面的33个字节数据压入堆栈S, p +=33.此时p已移动至脚本末尾,输入脚本读取完毕,堆栈指令操作()0×48push后面72字节的数据(签名数据sig)([sig])0×21push后面33字节的数据(公钥数据pubkey)([sig], [pubkey])

接下来读取输出脚本(赎回脚本): script =?76 a9 14 72 32 ca 33 e0 79 74 05 a5 12 fa 87 29 34 cd 92 2c 81 29 65 88 ac

流程:令p = script;op = *p; (op = 0×76),这是OP_DUP指令,复制一份栈顶元素至堆栈S。p++;op = *p; ?(op = 0xa9),这也是OP_HASH160指令,从S中pop出一个数据,进行hash160运算,将运算结果压回S。p++;op = *p; (op = 0×14),这是PUSHDATA指令,p++, 将后面的20个字节数据压入堆栈S, p +=20;op = *p; ?(op = 0×88),这也是OP_EQUALVERIFY指令,从S中依次pop出两个数据数据,比较这两个数据,如果不同,在交易验证失败。否则,p++;op = *p; ?(op = 0xac),这也是OP_CHECKSIG指令,从S中依次pop出两个数据数据,根据[sig]数据末尾附加的类型(0×01)对原始交易数据进行哈系运算,M=hash256(tx_raw),检查ECDSA_checksig(M, [sig], [pubkey])的返回值,若结果为1,则将OP_TRUE压入堆栈,否则压入OP_FALSE.此时p已移动至脚本末尾,全部脚本解析完毕。检查堆栈中的元素,若为TRUE,则交易有效,否则交易无效。(接上表)堆栈指令操作([sig], [pubkey])OP_DUP复制栈顶元素[pubkey]至堆栈S([sig,[pubkey], [pubkey])OP_HASH160pop出一个元素,并进行hash160运算,将结果push回S([sig,[pubkey], [hash160_pubkey])?0×14?push后面20字节的数据?([sig,[pubkey], [hash160_pubkey], [hash160])?OP_EQUALVERIFY?pop出2个元素,比较大小。若不相等,则标记交易为无效??([sig,[pubkey])?OP_CHECKSIG?pop出两个元素,验证签名,如成功,则压入TRUE;否则压入FALSE?(TRUE)

交易验证成功,

四、常见的交易脚本

五、自定义脚本

附录1:代码示例 (未完待续) ? chehw 2015.5.12

BTC addr:1CHEp8QzFtfvwXrreoeA6wmKc7cudWD3kv ? Email: htc.chehw@gmail.com

比特币系统的脚本(Script)—交易生成和验证的原理(第一部分)(初稿) | 分享给朋友: