Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

P2SH-为比特币赋能的脚本

哈哈笑417
161 0 0
P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M% s- [( M/ n1 U$ n1 ?- ^
P2PK6 {; J# q% _2 v0 u! G. |7 p
比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:, e( J: Z/ _7 S
04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG
( V  z6 P0 _& X! a& o$ H& ~0x04开头,表示是未压缩的0x04后面紧接这的是64字节是公钥内容OP_CHECKSIG:操作码,用于花费的时执行验证签名' ]" a* b9 X4 Y$ ^
那么验证时(即花费这笔输出)堆栈内容为:
6 J) O9 C: B; N9 N: B9 D) {. m5 X5 |
花费脚本为:/ f$ U: Y5 }  G# Q1 Y6 C" `
连接前向的输出,完整的堆栈:
9 `- ?- S+ B0 s  OP_CHECKSIG2 N7 P) X% C' b2 C# G. r* W5 T
OP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。5 `/ ]) {: R: H: z' W
P2PKH/ O7 s( S2 m* B/ T0 C& E/ C
后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:
5 b  r& V  z1 m$ j  OOP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG6 d4 M8 K  _- B, A/ }8 I
OP_DUP: 操作码,复制栈顶元素OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希public_key_hash:公钥的哈希值,20字节OP_EQUALVERIFY:操作码,判断是否相等OP_CHECKSIG:操作码,用于花费的时执行验证签名/ V' ^: k; F+ d! y' s9 D5 q( `
此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:5 S: n) A0 \8 u/ f5 H/ x
+ H" y6 N$ }* e( R
花费脚本为:
" W: i+ ~" @7 s  
( t$ f1 r# C( ~  }连接前向的输出,完整的堆栈:
  t/ d) b7 ^. W# L' ]: }  OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
9 b! b: O+ }# r0 O验证运算过程:2 x+ \/ }& L& e: R
从前往后找OP操作码,首先遇到OP_DUP: H- u# U6 `  i; A0 j( E& u7 D
执行后堆栈为:   OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG) y/ y2 z! X* c
执行OP_HASH160& b9 b; {# H1 @8 c1 W, O
执行后堆栈为:    OP_EQUALVERIFY OP_CHECKSIG# U: i( a% D' O8 j. ]( G% u! Z: S7 p
执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配: ^4 l# i3 k* j& C" K: k
执行后堆栈为:  OP_CHECKSIG8 J9 ?$ y8 Y4 }/ ]; t
到这里就与P2PKH一致了: y8 |' D% N: @( m# G1 C
执行OP_CHECKSIG,验证签名。4 ^& W: T) o6 a7 n
这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?
( F# w' u8 ]4 R  p原始多重签名
- y, b, e& N5 d3 T3 n; N5 S7 G随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:
9 a* m  p6 L5 a* A! Zm {pubkey}...{pubkey} n OP_CHECKMULTISIG9 ^, ^( q8 m$ a0 T$ D% b& r
2/2的一个原始多重签名示例:% S: J$ n5 U# ~5 U/ s
04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4) V4 R7 x" L. Z/ X% b
0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af. i3 m) x1 e  r; J/ d
2 OP_CHECKMULTISIG; B1 _; }1 w; D. R
验证时的堆栈内容为:. l" I- ]$ n- k7 N) o
花费脚本为:7 o1 W- f. R( m' q, p9 `* n
0   
* E. T! [) j! d" m  i' f连接前向的输出,完整的堆栈:
- r# x" f& T. @; J$ b  }0    M    N
$ y8 I( A3 P5 I* L* @; sOP_CHECKMULTISIG' E8 _$ Y4 P2 G: j/ c
OP_CHECKMULTISIG的运行过程稍微复杂一些:6 g7 w9 {3 L0 q9 V/ K% f# \
弹出最后一个数,就是N,公钥总数。
! m: m( M0 ]/ o( N$ \( g执行后堆栈为:0    M   弹出N个堆栈元素,就是这N把公钥。7 w: [, Z3 K' x  y! L
执行后堆栈为:0    M弹出签名数量M,即需要M个签名数量。) a) Y9 p. p( N! i
执行后堆栈为:0   弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。3 S: M/ T2 S0 M5 O. G, l! [6 x
执行后堆栈为:0弹出最后一个元素0。
# h9 f  s' q) ~9 o' l' I( k0即OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。# ~  }, ^. a6 C% W
这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。
4 a% h6 p4 x% p! i& V2 f" H( y9 V4 U1 R7 T+ i$ [0 N# h
P2SH
5 k/ @+ m! G; p5 c7 N3 U因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:
4 ^7 [2 U) z0 U7 B5 |OP_HASH160  OP_EQUAL/ N  \6 x! C6 `1 {0 ^2 P
其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:, z/ D  k7 a( R( V/ V" U
RedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG
% d2 N5 X$ I1 v* r( h: ?* B- I. i, \20-byte-hash-value = RIPEMD-160(RedeemScript)( O- A& C- E1 K% L
RedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。
0 q7 y3 F4 l/ |9 R: C6 r8 V1 {& {- m验证时的堆栈内容为:9 p% h8 M: [( w8 _6 d
花费脚本为:
  G& P' ?5 }" oOP_0    ( Z6 T& c  }+ K
连接前向的输出,完整的堆栈:
3 m+ N  F, j- l$ g$ tOP_0     OP_HASH160  OP_EQUAL9 h3 J, n. o, i1 S
验证过程分为两大步骤,第一步骤:: ^: \- o& n2 x7 k1 _. i& b
1.执行OP_HASH160,计算HASH值: OP_HASH160. X$ F# W" B7 A* Z. ^
执行后堆栈为:OP_0     OP_EQUAL! F* P* d2 `/ r& {  c+ \
2.执行OP_EQUAL,验证两个哈希值是否相等
" r2 b( j& l  t  k执行后堆栈为:OP_0   
3 W" u! Q4 i5 K5 m( H$ f: P- L第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:
5 F. N- B+ S8 v) X0 yOP_0    M    N OP_CHECKMULTISIG
9 B4 i1 T  n" v$ d* W当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。
. R* r6 j6 S( x  I  U% `软分叉的关键点9 G4 @1 }& S! ]6 q7 T2 |
在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。& ]% v6 F% b$ c
当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。9 I# @; D5 j  j2 _/ J* A
激活
% ~$ E$ T  w/ b4 f$ L9 r' @  wP2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。
( Y& W$ [; R) }为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。/ P8 i- F) v. _; W' H8 o
影响深远
2 h0 U0 f: r* X$ xP2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。, }5 o+ a+ r3 a. f. i
发展状况
标签: P2SH 比特币
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

哈哈笑417 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11