Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

P2SH-为比特币赋能的脚本

哈哈笑417
115 0 0
P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M& F" O* Z/ |; K( z
P2PK6 D; K; e) `5 H% k* N, @5 u0 H
比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:
* u1 r! I2 J  j& I04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG3 k1 {! f3 Y4 `5 o
0x04开头,表示是未压缩的0x04后面紧接这的是64字节是公钥内容OP_CHECKSIG:操作码,用于花费的时执行验证签名
) v/ R$ S& j8 \% A  D& M6 y6 S5 F那么验证时(即花费这笔输出)堆栈内容为:( g; {) j' R8 P2 L6 i5 k, W5 m
) @+ t+ _( i8 `8 P% A4 B( R# q3 n" Z
花费脚本为:
  z  g$ D8 v& {: \: C, ^. r% Y连接前向的输出,完整的堆栈:
: k2 [1 ~  \% T, Z4 p  OP_CHECKSIG! W3 }/ K8 b4 u  f* l2 A0 w
OP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。' o7 |! q: h3 B7 b; m( S
P2PKH1 N( W# y5 n% O4 {; M) O0 p+ d5 B
后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:. Y# a: O* e; s) M, c
OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG, S7 y  {+ ^  R0 G
OP_DUP: 操作码,复制栈顶元素OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希public_key_hash:公钥的哈希值,20字节OP_EQUALVERIFY:操作码,判断是否相等OP_CHECKSIG:操作码,用于花费的时执行验证签名$ E% p5 @, P7 [. |1 l) ?5 {  X, I" G
此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:' J% q& E1 N/ }: E+ C) k4 u/ u

) ^/ k$ `3 F# n# O7 w, `0 _; W花费脚本为:2 q6 S& `* t8 q+ {
  
( L  r' e6 }: I  g- J连接前向的输出,完整的堆栈:
% I$ [( T1 T/ H7 r  OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
0 a8 ~8 P0 a, W" Y- n2 b验证运算过程:
! `) J0 ~" Q- P6 B3 e6 Q( D; Z& [# D从前往后找OP操作码,首先遇到OP_DUP6 D$ w  ?  k/ ?0 t! J9 K' K& N
执行后堆栈为:   OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
' f# g, `1 _$ i/ h/ p' D5 b* W执行OP_HASH160
$ }/ W, R1 h6 S9 Y% k( a执行后堆栈为:    OP_EQUALVERIFY OP_CHECKSIG
  V- t2 W3 s9 Y, _, T执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配
) R  y6 g' O) ~# P2 P8 c5 Z4 L/ F执行后堆栈为:  OP_CHECKSIG* p# u3 f9 m& C# F# S0 c5 ~& b
到这里就与P2PKH一致了7 d4 y& x+ u8 D# C
执行OP_CHECKSIG,验证签名。# I5 k5 ]2 c$ x
这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?, {. K2 e! ?7 O- _- k
原始多重签名
/ E) _) M& u3 J1 i随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:
+ S! S: w/ F9 ^m {pubkey}...{pubkey} n OP_CHECKMULTISIG
3 D: S4 \+ _0 F; e. i2/2的一个原始多重签名示例:+ I; Z, ^' I% |
04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4: C" p  c2 M, M6 f! R* N( z
0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af8 u5 o3 F/ Y2 x
2 OP_CHECKMULTISIG* E% s+ H4 z5 R4 G! T8 s( E+ H
验证时的堆栈内容为:8 R2 _" z. |0 i% E: }9 t1 k" y
花费脚本为:& N$ m! {7 v7 e. R
0   
2 Y6 y3 k: ^& B' e1 B1 \( W8 ~; z* j连接前向的输出,完整的堆栈:8 g* M' m' `. D/ c* t
0    M    N 5 V3 Q) j' f# r$ N- m
OP_CHECKMULTISIG$ q% B! t, r& P+ k: l, D
OP_CHECKMULTISIG的运行过程稍微复杂一些:
( R0 }5 h3 Q. n( ^弹出最后一个数,就是N,公钥总数。
5 A1 D6 B/ {1 s2 n5 `+ y执行后堆栈为:0    M   弹出N个堆栈元素,就是这N把公钥。
" W% j- Q0 e% A- B执行后堆栈为:0    M弹出签名数量M,即需要M个签名数量。
1 }& E$ w5 ^, C) Y, T8 s3 p执行后堆栈为:0   弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。
5 j+ f$ V0 J+ J7 z- [执行后堆栈为:0弹出最后一个元素0。( l* [  P* F  P/ W% N& v" h* |
0即OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。
! u$ I0 i6 P8 Y) [4 W& z这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。6 F2 N9 Y+ F- w, n8 ]$ ^
, U  g7 W% W2 J5 F: S" H
P2SH
9 S- p) L5 r' i. V6 X5 T! t因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:* l8 L: M, n' I2 ^' J; V) r
OP_HASH160  OP_EQUAL! t3 x0 r) ^, G, G* A/ R
其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:% V7 a- m- D7 i  q  i$ [- t
RedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG
% R3 E! W/ L6 K1 ]20-byte-hash-value = RIPEMD-160(RedeemScript)! `% x) r, T4 p0 R2 A/ i
RedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。
( v) n3 c% ], u& {验证时的堆栈内容为:
) I/ u: j. w) E2 @) U花费脚本为:
- J2 g4 `) z, b8 e+ {( \OP_0    . d6 D$ X1 @8 {5 K9 F( U
连接前向的输出,完整的堆栈:; D& m7 K* r$ @, y
OP_0     OP_HASH160  OP_EQUAL
' T6 V6 c" ~; u" a验证过程分为两大步骤,第一步骤:9 [& ]) ~0 P! m7 x
1.执行OP_HASH160,计算HASH值: OP_HASH160
/ n' [- w/ E" p: D  N7 F$ ~执行后堆栈为:OP_0     OP_EQUAL: {; Y+ K0 Q. e+ ~
2.执行OP_EQUAL,验证两个哈希值是否相等
" g, l6 }/ v% G& X" e执行后堆栈为:OP_0   3 z0 p1 d4 T# B4 u/ ~5 D
第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:4 r4 r0 V9 K! ~; H/ U
OP_0    M    N OP_CHECKMULTISIG1 `2 V9 D/ P' G2 P2 `+ c
当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。
! q& b: x: H7 F! v' @3 R软分叉的关键点
: ]' z) W) t) z" r& A  B1 ]在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。
( d* E$ k7 S9 w2 L2 P当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。
: Q1 c+ ~4 r' M8 b  O激活
$ C% m* {! _6 N2 Y! P3 r2 PP2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。! c" s% a3 N4 z: P; _
为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。
" Z0 B5 A( `. D影响深远6 w% K" e% R2 g$ c/ d; j
P2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。! ]& |) r- @& S" ?2 V- f, B
发展状况
标签: P2SH 比特币
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

哈哈笑417 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11