Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

P2SH-为比特币赋能的脚本

哈哈笑417
270 0 0
P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M
, H# w3 Q3 [1 y' p  k% |8 T( tP2PK
/ u! X  Q5 O. k' T比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:
, N6 e8 L' n! y04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG4 X; F: ?7 W7 f* Z6 Q
0x04开头,表示是未压缩的0x04后面紧接这的是64字节是公钥内容OP_CHECKSIG:操作码,用于花费的时执行验证签名0 X+ ^  C: {0 Q2 Z( ?7 @; B2 D
那么验证时(即花费这笔输出)堆栈内容为:7 r. i3 N: f3 T: R8 O4 J& c
* V, C# q  m6 e% R! g
花费脚本为:
. Z  G1 N5 H0 J5 P3 v连接前向的输出,完整的堆栈:
  p$ b# E( t" ^+ l6 m2 z  OP_CHECKSIG
5 P* n' \" d% g/ C$ y: qOP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。
! n+ y6 S7 V* ?% x) A1 w& y& mP2PKH# C" H: _7 _* f
后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:
+ t7 p1 W; ]2 ]$ W2 ?4 hOP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
  v5 ]8 c. F) q7 ], C/ o$ `OP_DUP: 操作码,复制栈顶元素OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希public_key_hash:公钥的哈希值,20字节OP_EQUALVERIFY:操作码,判断是否相等OP_CHECKSIG:操作码,用于花费的时执行验证签名5 V& ?* F" @9 o4 O4 c
此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:
1 t" i- o$ C# T+ \. z. {5 @
( N1 S- r: o- h# i花费脚本为:/ ^8 x" w% h- y, i+ J7 \
  
8 L1 l8 T2 I4 N: U( C" q$ S+ g连接前向的输出,完整的堆栈:
, t8 N0 n- R5 z  OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
. Z, _5 b, L  k$ U验证运算过程:
, |# I, k. h, F0 K+ }6 w从前往后找OP操作码,首先遇到OP_DUP- d9 _4 I9 U) i8 w, T0 U! w; l
执行后堆栈为:   OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG& f( V1 m/ [5 y8 E5 X
执行OP_HASH160
+ _8 i8 E$ y- r6 ?* g2 I执行后堆栈为:    OP_EQUALVERIFY OP_CHECKSIG7 i5 F4 l# c4 i; d( i/ S7 }
执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配" V& s1 I6 b) g+ B$ T6 }9 W$ q/ Z
执行后堆栈为:  OP_CHECKSIG, O% G/ q4 t2 `+ e
到这里就与P2PKH一致了
+ I# w  U8 V# n% y# k4 ~) Z3 P执行OP_CHECKSIG,验证签名。
+ f. F2 L0 K$ A8 ^这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?: w  O" U% Z) h+ T' `$ l; ^6 p$ n, l
原始多重签名, S* r4 [/ Z3 ]* u
随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:! j" C3 v; Z1 X! z& C4 {& J: d% H# a
m {pubkey}...{pubkey} n OP_CHECKMULTISIG
9 X  R! c  p6 ?$ F2/2的一个原始多重签名示例:, T; g% H% K3 E
04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4
- l4 p- f, a/ o1 T0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af1 ~% B/ T% v8 ^4 a, J# S0 G
2 OP_CHECKMULTISIG8 q; u- a4 V- w
验证时的堆栈内容为:
: z/ Q: o& W2 y# z* {# \花费脚本为:
$ I/ z" z; v+ a% F4 I  O0   
6 S* e+ h, Q1 K0 f0 E连接前向的输出,完整的堆栈:
; [# J, x) F) X3 Q4 I& f0    M    N - j* h* o  K" G0 d% H. V: \
OP_CHECKMULTISIG) t' q7 j3 h3 D2 j0 T
OP_CHECKMULTISIG的运行过程稍微复杂一些:
, W5 b2 s/ s7 \4 Z" q弹出最后一个数,就是N,公钥总数。
: f% Y2 c  J* R$ e执行后堆栈为:0    M   弹出N个堆栈元素,就是这N把公钥。
" i8 o+ Y  o  J1 A( ~: p/ B; U5 \: I6 o执行后堆栈为:0    M弹出签名数量M,即需要M个签名数量。6 D" g% b* t) ?5 ]; w
执行后堆栈为:0   弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。
- e1 C# |0 h. Y/ R, Y4 d执行后堆栈为:0弹出最后一个元素0。' q0 s3 V! L. l3 k& r
0即OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。- a: y# ?$ {; ~, R3 a2 I
这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。8 P& k0 E6 D# L; B
* _' V; G: s8 t; `4 O
P2SH
5 ?2 {' Z4 m  H1 L% l2 ]5 Z因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:
3 M3 [; L; ~6 cOP_HASH160  OP_EQUAL, t$ g* l" P/ i1 ]8 |. S# w- X
其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:
! ~/ q5 }  D' I& O, fRedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG: O5 x0 \6 G" E. A& p9 v
20-byte-hash-value = RIPEMD-160(RedeemScript); L- n0 j! N# i7 ^4 P
RedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。, B  r; k9 N. a" O% A) R+ H
验证时的堆栈内容为:
" i  y2 _' V/ ]! x+ Q; e花费脚本为:5 L$ R' G% T+ W
OP_0   
! I: C& F) h% e! p# e2 u连接前向的输出,完整的堆栈:: c1 K% C! N2 T' j  b
OP_0     OP_HASH160  OP_EQUAL% A& m$ N) F4 f( k' H) ^2 c
验证过程分为两大步骤,第一步骤:
* ]% r3 Z, [; b# E) t+ Z1.执行OP_HASH160,计算HASH值: OP_HASH160( O5 j& o" }3 K; |' y7 v
执行后堆栈为:OP_0     OP_EQUAL
( I' c1 v$ @6 [4 `) D& t2.执行OP_EQUAL,验证两个哈希值是否相等
0 d1 B! o7 n/ J* q' F0 g: Q, O执行后堆栈为:OP_0   ( I; F( Q0 w! z4 t8 C! O
第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:
5 P& M% P2 V* SOP_0    M    N OP_CHECKMULTISIG
. L- m+ `) O9 S8 r当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。; b: {6 P! u& y! T. x* T$ x& v
软分叉的关键点
1 H6 H8 v' ~* ?' V3 t- G: z. |- A在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。2 p" v- N+ H3 ^' i; t  a; x
当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。) U9 C9 I& L3 d6 `* i8 T
激活
& W) u1 Q" U' YP2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。
( `% d2 y( e9 ^( }/ p5 h$ l  A8 |为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。
" _( u: ^2 D& t+ U- v影响深远/ [& G  p& P  I) D$ A1 [
P2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。
0 s9 e9 p: F0 W( M9 D, `发展状况
标签: P2SH 比特币
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

哈哈笑417 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11