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

区块链技术巴比特2017-11-25 06:53:05  阅读 -评论 0

前言

在谈及脚本系统之前,先问一个似乎很简单的问题:比特币的“地址”是什么? 估计大多数人对此不屑一顾——这个问题太简单(弱智)了。比特币地址是一个日常使用非常频繁的东西。几乎在绝大多数交易都会用到比特币地址。对比特币稍有一点儿了解的人都可能会把Base58encode编码和RIPEMD160哈希算法挂在嘴边,懂一点技术的人更是能把比特币地址的计算的每一环节都细说无遗。 实际上,这样的认识只停留在表面上。以这样的理解,恐怕无法真正明白什么是比特币系统,从而严重低估这一创造性发明的价值,甚至有些人会认为”真正有价值的不是比特币,而是区块链”。   在解释”比特币地址”是什么之前,先要铺垫一下关于比特币“交易”的概念。在比特币系统的创立之初并没有“地址”这一说法,那时也照样可以交易。那么交易是如何实现的?

一、交易的实现和比特币地址的引入

比特币的交易实际上是不依赖地址的,至少用的并不是我们所看到的”地址“。比特币系统的交易依赖于脚本。 支付款项时,我们实际上是把支付的数额与接收者的“赎回脚本”绑定到一起。日后,接收者可以用自己的”签名脚本“来确认使用权。每一笔交易的实现所依赖的只是”脚本“。 以下是两种常见脚本的格式: “赎回脚本”:(输出脚本)

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

OP_DUP OP_HASH160 (0×14) [一个20字节的哈希值] OP_EQUALVERIFY OP_CHECKSIG

P2SH(支付到脚本模式,使用多重签名就需要用到这种模式):

OP_HASH160 (0×14)  [一个20字节的哈希值] OP_EQUAL “签名脚本”:(输入脚本)

P2PKH模式:

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

P2SH模式:m-n Multisig

0×00  [字节数]  [签名1] 0×01 …[字节数] [签名m] 0×01 [支付合同脚本的字节数]  [m]  [字节数][公钥1]… [字节数][公钥n][n] OP_CHECKSIGVERIFY   用巴比特的一比交易作为示例:http://blockmeta.com/tx/c8bc7cff08249ea5f9970e15be64259b0135b0b6e37f1f9f088a719508cbd8bc

c8bc7cff08249ea5f9970e15be64259b0135b0b6e37f1f9f088a719508cbd8bc (txid)(Fees: 0.0001 BTC) 5.83294119 BTC

3Ae2TYfyHvwH11pUy6HaK7rBYn9GfGZ3Fk(Input)5.83294119 BTC

17EFZ829NBT2WETLj3wJ5YUfXVaGckuUgs(Input)0.0001 BTC

1BQpsoxUq7N5Hv57QCnzLBbZSHGtqafaFy(Output)0.01 BTC

3Ae2TYfyHvwH11pUy6HaK7rBYn9GfGZ3Fk(Output)5.82294119 BTC

Time: 2015-05-05 15:21:27

Input Scripts(输入脚本——签名脚本)

(第一个输入脚本为P2SH模式,由3Ae2TYfyHvwH11pUy6HaK7rBYn9GfGZ3Fk地址支出)

00 (OP_FALSE,约定为P2SH方式)

48 (第1个签名的字节数)

304502210080075aa29c42f8062f75cf6ab32004944417af974775581719008052c78719710220409fee54c6ddf2ca83e090077e443f95b427a63cc1ad87fca2625951b789d1c2 (第1个签名)

01 (约定所签名的数据为HASH_ALL(tx))

49(第2个签名的字节数)

3046022100b61d8f206d17efd6db32dad106f754f231ee8a16882929b1eb39a58bfd36b39e022100c62cff92dd6fb22b373025fc9b87044cf1b33502acc9de707e5f54d1c8a042a7 (第2个签名)

01 (约定所签名的数据为HASH_ALL(tx))

47 (支付合同脚本的字节数)

52 (OP_2, 表明m-n签名中的m = 2)

21  (第1个公钥的字节数)

0293baf0397588acc1aba056e868fd188dc0eea7554b45370aae862f9d2493a4c1

21  (第2个公钥的字节数)

020ab7517cf22a46b503ee8dcae7f9f109ec4cd19f0ab9d77c89c607554f3d5aa9(第一个公钥)

52(OP_2, 表明m-n签名中的n = 2, 说明这是一个2-2签名)

ae(OP_CHECKSIGVERIFY)

(第二个输入脚本为P2SH模式,由17EFZ829NBT2WETLj3wJ5YUfXVaGckuUgs地址支出)

48 (签名的字节数)

304502203fe5f04a013512a4773414b25edc8c7915473dd5cf87bc73d28e1aaffdb4d14f022100e16156d526d1498f2cf5eb02d53e02f7fd5cf1dfdd25e4b032fdc5c59c9fd27b (签名)

01 (约定所签名的数据为HASH_ALL(tx))

21 (公钥的字节数)

0203635e5c184951e14fcfecc83b15960594f4fceec729e09a4a517b0a03a7f4b9 (公钥)

Output Scripts (输出脚本——赎回脚本)

(第一个输出脚本为P2PKH模式,支付到1BQpsoxUq7N5Hv57QCnzLBbZSHGtqafaFy地址上)

OP_DUP

OP_HASH160

14 (字节数,0×14 = 20L, 网站在显示时省略了这一字节)

7232ca33e0797405a512fa872934cd922c812965 (20字节的哈希值)

OP_EQUALVERIFY

OP_CHECKSIG

(第二个输出脚本为P2PKH模式,找零回3Ae2TYfyHvwH11pUy6HaK7rBYn9GfGZ3Fk地址)

OP_HASH160

14 (字节数,0×14 = 20L, 网站在显示时省略了这一字节)

622854939d571b63df97f47e8302b700ab2932b6 (20字节的哈希值)

OP_EQUAL

为了简化说明,我们先只分析P2PKH模式的交易。如果读者对P2SH感兴趣,可以在下面介绍了脚本原理后,自己分析一下P2SH是如何实现的。 此处暂且先把“交易是如何验证的”这个问题放在一边。从交易数据的格式中可以看出: P2PKH下,付款时需要通过“签名脚本”确认资产的使用权,同时需要知道接收方的“赎回脚本”才能正确付款。 普通用户恐怕无法直接记住并使用这些脚本。那么他们如何才能进行交易?   为了简化日常使用的交易过程,核心开发者们引入了”比特币地址“这一概念。通过一些协议约定,使得各种类型的钱包软件可以用某种通用的方式自动推算出所需的的脚本。 实际应用中,这些脚本是由钱包软件代替用户来实现的。 核心开发者们约定并正式发布的一种协议,规定了某种特定数据格式,以及由该格式的数据演算对应脚本的方法。这是一种独立与比特系系统内核之外的、约定俗成的规则,使得各种不同的钱包软件可以实现用某种通用的方式来收发款。

P2PKH地址的格式约定如下:

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

P2SH地址的格式约定如下:

1. 前导字节(1个字节):prefix = 0×05,表示这是一个P2SH地址; 2. 合同脚本的哈希值(20字节):用hash160算法——RIPEMD160(sha256(合同脚本)),生成一个20字节的数据; 3. 校验码(4个字节):用hash256算法——SHA256(SHA256([0x00] [20字节的哈希值] )),取前4个字节作为校验码; 4. 生成文本格式的地址:用Base58编码由前三步所得到的25个字节的数据。 由于前导字节为0×05,生成的地址首字母为字符“3”。

演算对应的脚本的方式:

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

“赎回脚本”:OP_DUP OP_HASH160 [20字节的哈希值] OP_EQUALVERIFY OP_CHECKSIG “签名脚本”:[签名][签名消息的类型(1个字节)][公钥]

P2SH模式:

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

也就是说,比特币的地址是一种规定了格式和脚本演算方式的约定,使得大家可以用某种通用的方法进行交易。(不使用地址实际上也仍然可以交易)但是,这些脚本到底有什么用?为什么要把交易过程变得这么复杂?

二、脚本的重要性

如果只是为了适应某种特定交易,那么没有必要使用脚本。假设只是实现收发款,那么,在比特币系统创立之初就把”赎回脚本“约定为公钥,把“签名脚本”约定为签名,就可以了。 中心式方式下,完全可以这样做,因为如果日后发现某种方式无法满足新的交易类型,或是发现某种方式存在安全隐患,那么推出一个升级包或补丁,或是另外发行一个新版本就可以解决。 然而,去中心方式下就行不通了。某种协议一旦确定,任何变更都需要提前经过讨论并取得共识,除非问题简单到一目了然,并且只有唯一的解决方法,那么取得共识几乎是无法实现的。以目前区块扩容这样一个简单的问题,即使大家确实知道扩容迫不及待,且知道20M的大小虽不是长久之计,但至少可以暂时缓解危机,但仍要拖到大约1年以后才能知道是否能最终取得共识。   估计比特币系统的设计者是为了使系统有机会处理无法预见到的交易模式,即使几十年或数百年以后,就算不修改协议,这一系统仍不会过时。故此引入了脚本系统以增加适应性。不过,这也增加了系统设计的难度。 脚本解释程序(或流程)必须在一开始就设计得足够简单,这样才能不出现bug,同时还要并近乎完美,能够有足够的灵活度适应各种未知需求。一旦脚本系统确定下来,任何变更都必须经过严格的测试并先达成共识,否则势必引起分叉。   这种设计对开发者能力的要求非常高。早期的开发者们设计的脚本系统并不完善,协议也表述得非常含糊(比如OP_ADD这样的加法指令,没有规定对整数溢出如何处理),为了系统的安全性,还增加了很多与比特币系统本身无关的额外限制(包括脚本字节数限制、指令限制等等,区块大小的限制是另一种额外限制),这使得脚本系统丧失了很大的灵活性。所以说,比特币系统目前仍处于实验期,在系统设计上还没有达到理想状态。(当协议足够完善、各种限制放开后,比特币系统才可以说渡过了实验期。当然,即使渡过了实验期也不表示比特币系统一定会成功,但毫无疑问的是,这一系统至少是一种有足够震撼力的创造)

三、脚本原理

比特币系统的脚本系统设计得非常简洁,稍有编程基础的人很容易理解。与用堆栈(stack)实现一个简单的计算器的方式非常类似。 比特币的脚本系统借助堆栈进行运算。验证交易时,脚本系统依次读取每个指令,并对堆栈进行操作。所有指令结束后,检查堆栈中的残留数据,如果均为TRUE,则交易有效,否则交易无效。

堆栈(stack)是一种实现后进先出(LIFO, Last In First Out)方式的数据结构,使用两种基本操作:压入(push)和弹出(pop),具体实现时通常还会有另一个常用操作:检视(peek)。最上面的元素称为栈顶(top)。 打个比方:往一个有一定高度的箱子里放书(箱子的宽度只能容纳一本书),放入书的动作是push,取出书的动作是pop。最的书一定是最被取的。拿起最上面的一本检视一下再放回原处称为peek。

脚本系统规定,指令(或运算符)均为1个字节,也就是说,最多只能有256种指令。 协议规定了执行每一个指令(OP)时要对堆栈(S)进行什么操作。比如: 假设S中依次push了如下下数据:(a, b, c, d, e) OP_ADD:加法指令。从S中依次pop出两个数据,这两个数据分别为e, d。计算f = e + d. 然后将f 压入S。此时S中的元素为(a, b, c, f) OP_DUP:复制栈顶数据。从S中peek出一个数据,此时数据为f, 复制一份f并压入堆栈。此时S中的元素为(a,b,c,f,f) OP_PUSHDATA1: 压入数据。将紧随OP指令后面的1个字节的数据(比如g)压入堆栈。此时S中的元素为(a,b,c,f,f, g)。   脚本系统协议中的每种指令名称及其用法请参考:https://en.bitcoin.it/wiki/Script 附件中有一个github的链接,附有一个比特币脚本解释器的示例(C语言),没经过严格测试,仅供参考。 下面用一个P2PKH的交易来说明一下脚本系统是如何运作的,数据取自第一节中所引用的交易。 21

输入脚本(签名脚本):48304502203fe5f04a013512a4773414b25edc8c7915473dd5cf87bc73d28e1aaffdb4d14f022100e16156d526d1498f2cf5eb02d53e02f7fd5cf1dfdd25e4b032fdc5c59c9fd27b01210203635e5c184951e14fcfecc83b15960594f4fceec729e09a4a517b0a03a7f4b9

每个数字的含义如下:

PUSHDATA 48 签名 (DER) sequence 30 length 45 integer 02 length 20 X 3fe5f04a013512a4773414b25edc8c7915473dd5cf87bc73d28e1aaffdb4d14f integer 02 length 21 Y 00e16156d526d1498f2cf5eb02d53e02f7fd5cf1dfdd25e4b032fdc5c59c9fd27b SIGHASH_ALL 01 PUSHDATA 41 21 公钥 type 02 X 03635e5c184951e14fcfecc83b15960594f4fceec729e09a4a517b0a03a7f4b9 Y

从区块链上找到该UTXO所对应的输出脚本(赎回脚本)为: (注意:不是这一笔交易中的输出脚本)

OP_DUP OP_HASH160 44524fa542897f46a9a0cccc27ccb91ba822b4b6 OP_EQUALVERIFY OP_CHECKSIG

76 a9  14 44524fa542897f46a9a0cccc27ccb91ba822b4b6 88 ac

OP_DUP 76 OP_HASH160 a9 PUSHDATA 14 公钥哈希值 44524fa542897f46a9a0cccc27ccb91ba822b4b6 OP_EQUALVERIFY 88 OP_CHECKSIG ac

注:指令中,凡是小于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×48 push后面72字节的数据(签名数据sig) ([sig]) 0×21 push后面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_HASH160 pop出一个元素,并进行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

声明:链世界登载此文仅出于分享区块链知识,并不意味着赞同其观点或证实其描述。文章内容仅供参考,不构成投资建议。投资者据此操作,风险自担。此文如侵犯到您的合法权益,请联系我们100@7234.cn

    参与讨论 (0 人参与讨论)

    相关推荐

    区块链投资趋势报告:巨头入场布局行业趋于成熟

    区块链投资趋势报告:巨头入场布局行业趋于成熟

    来自:https://mp.weixin.qq.com/s?__biz=MzI4NzIxOTY1NA==&amp;mid=2650632639&amp;idx=1&amp;sn=e6d1c29731d992a80410aaee82ec3ea6&amp;chksm=f3d8db16c4af520097e4a64a71b1d4743ac326b9f027

    重新发明货币

    重新发明货币

    一、货币的演化过程 先简单回顾一下人类货币的演化过程,大概有以下阶段: a. 1.0版本:自然货币(贝壳、牲口、金银……) 这个阶段,货币基于一般等价物的稀有性或者实用性,货币不可能出现人为操纵的超发。 b. 2.0版本:早期纸币、银票到本位纸币 当贸易量越来越大,实物货币太不方便了,而且大家发现其实并不在意货币本身有什么价值,在意的只是这么多的货币能不能交换到足够的物品,于是纸币这种信用货

    从比特币交易看欧洲央行虚拟货币分类

    从比特币交易看欧洲央行虚拟货币分类

      互联网对传统社会的颠覆从未停止,在其完成对信息流、商流、物流、资金流的初步改造之后,或将以虚拟货币的形式打破现有货币体系   4月18日,在中国极客张沈鹏创办的比特币交易平台(42BTC.com)上,比特币对人民币的平均交易价为576元。当天,该平台完成了100个比特币的交易量。仅仅过去一周,4月25日上午,比特币对人民币的平均交易价已达到906元。据42BTC网站统计:在过去的32个月

    欧洲央行-比特币报告

    3.1 比特币 3.1.1 基本特征          比特币可能是最成功的,也可能是最有争议的虚拟货币方案,由日本程序员中本聪(译者注:事实上,中本聪是不是日本人,甚至是不是单个人无从考证)在2009年设计并实现。该计划基于一个类似于BitTorrent的P2P网络。BitTorrent是互联网上著名的共享文件协议,应用在电影,游戏和音乐领域。比特币在全球层面上运作,可用于各类货币交易(虚

    彻底玩转比特币地址和私匙

    彻底玩转比特币地址和私匙

    比特币地址和私匙是所有比特币初学者面对的一大难题,再加上那一串超长的字符串,让人更是摸不到头脑。 现在编者以问答的形式,带你一步步的揭开比特币地址和私匙的面纱。 还不知道什么是比特币地址和私匙的同学请点这里 问题一、比特币钱包由什么组成? 答 我们知道,比特币地址和私匙组成了比特币钱包,而私匙则决定了比特币地址上比特币的归属。 地址和私匙 问题二、如果只记得私匙我们还能还原比特币地址么? 答

    用GO语言实现比特币算法

    用GO语言实现比特币算法

    本节的这个例子展示一点点高精度数学包math/big、一点点散列包hash、一点点加密包crypto,还有一点点测试包testing的知识。这里不介绍bitcoin协议和算法——尽管它们很有趣,而是试图指出,Go对多种操作系统的支持,是实现这种跨平台应用的理想语言。 位钱(bitcoin)是一种使用加密手段制作的分布式电子货币。它最初于1998年由Wei Dai提出,并由中本聪(Satoshi

    详解比特币的找零机制

    详解比特币的找零机制

    比特币的找零机制一直让人有些迷惑,明明只向一个地址发送了比特币为什么 blockchain 上面的显示的有时是1个地址对多个地址,有时是多个地址对1个地址,有时又显示多个地址对多个地址? 为什么比特币资深用户要提醒大家当比特币钱包交易100次以上时再次交易后要重新备份钱包,恢复以前的钱包备份有可能会遭遇损失? 是的,这一切都是因为比特币的找零(Change)机制。本文参考 Bitcoin的维

    玩转比特币客户端之一:C盘转移和加速下载

    玩转比特币客户端之一:C盘转移和加速下载

    C盘空间不足?交易数据下载速度太慢?别着急,乐享比特币教你轻松玩转比特币官方客户端。 所有新人开始接触比特币时做的第一件事情大多数是安装比特币的官方客户端。 安全起见大家最好直接访问官方发布渠道sourceforge的地址进行下载:http://sourceforge.net/projects/bitcoin/files/Bitcoin/ 该网页列出了各版本的官方比特币客户端,目前

    麦妖榜
    更新日期 2019-06-16
    排名用户贡献值
    1BitettFan23992
    2等待的宿命23809
    3六叶树20309
    4区块大康18606
    5天下无双16192
    6linjm122715948
    7牛市来了15758
    8lizhen00215077
    9让时间淡忘14475
    10冷风大q11188
    返回顶部 ↑