Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

P2SH-为比特币赋能的脚本

哈哈笑417
128 0 0
P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M
; n" Y! Y0 b0 G$ K7 D5 v* `) uP2PK
- G- R0 o) V1 _( U. M+ \5 ~+ g0 O比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:
: V7 a/ ^, w+ J1 D$ K9 |- C04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG4 w6 F$ C0 O5 o1 W, M" G' B  _- K
0x04开头,表示是未压缩的0x04后面紧接这的是64字节是公钥内容OP_CHECKSIG:操作码,用于花费的时执行验证签名  y# G6 w: i7 N( p6 v8 Y& b1 O. W
那么验证时(即花费这笔输出)堆栈内容为:
& J9 y! J9 P/ I, p4 K' |4 A$ S! `$ [. N7 z& w
花费脚本为:
! B( E/ N+ A. [7 S+ k  h连接前向的输出,完整的堆栈:
  K+ c# k) b3 O8 ~: Q# ]  OP_CHECKSIG; o3 d. t3 c1 }8 t9 o4 k
OP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。
6 N. k: ?2 S: \* NP2PKH
( `8 h4 x% U' k; Y5 m后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:5 @6 t& I" l; l4 a
OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
& S' v" j5 |2 A. P+ ZOP_DUP: 操作码,复制栈顶元素OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希public_key_hash:公钥的哈希值,20字节OP_EQUALVERIFY:操作码,判断是否相等OP_CHECKSIG:操作码,用于花费的时执行验证签名
( T  k7 W' N! f3 {此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:
3 n( `- ~# _% g1 C6 V" I, T
1 H' r, G* A* Z2 a9 M! w( b3 [. B$ P花费脚本为:
+ t0 _( P; }* o1 |  
& [2 J3 w* ?/ o5 `8 d" n连接前向的输出,完整的堆栈:- f2 q" Q* p& o1 ]
  OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
: R4 ~. L4 t# u验证运算过程:' O  @) {& }9 s9 ^
从前往后找OP操作码,首先遇到OP_DUP
. S5 x- T' S- K7 g2 @; \执行后堆栈为:   OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG* |. r0 `5 q' u
执行OP_HASH160# I+ B+ I3 \8 ?; m9 g
执行后堆栈为:    OP_EQUALVERIFY OP_CHECKSIG& C! ]- X0 `5 i
执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配6 H1 f( ]2 X* i! b
执行后堆栈为:  OP_CHECKSIG
, u/ Z& a1 Z$ k% p4 k- y$ ^9 O到这里就与P2PKH一致了
# K9 o. l' E( i! f% S& J5 D. B执行OP_CHECKSIG,验证签名。) P7 g# j2 v; S3 P! |7 `
这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?; J' z* d  ~7 `6 d7 j
原始多重签名
+ X; e0 n; F/ Y' k随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:* Q& D& m" U+ c, J+ w- Y/ k
m {pubkey}...{pubkey} n OP_CHECKMULTISIG1 d: C; x4 c  M) q, E
2/2的一个原始多重签名示例:
8 J8 y7 g/ e- \- H04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4
' N5 B! m1 O7 X+ ]  }8 H0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af( @" Q, M6 l7 P8 B9 Q6 |6 K
2 OP_CHECKMULTISIG
+ V9 @* Z* P& y+ t; A验证时的堆栈内容为:
6 i+ k( p: G! O花费脚本为:# G1 A$ B8 o. R
0   / b: r  S' I6 m3 ~% m
连接前向的输出,完整的堆栈:
' W; _9 L, X5 G; j8 o# }7 i5 W0    M    N
' a  c5 C# G  T( V5 A  [1 L0 v+ SOP_CHECKMULTISIG9 Y; W2 ~, P" b4 q% y0 w, o
OP_CHECKMULTISIG的运行过程稍微复杂一些:
) _$ {9 I. V' G8 Q8 W弹出最后一个数,就是N,公钥总数。4 M* j8 V+ R- ]2 l
执行后堆栈为:0    M   弹出N个堆栈元素,就是这N把公钥。9 j/ k4 b8 i+ F
执行后堆栈为:0    M弹出签名数量M,即需要M个签名数量。5 k% ?* r; B0 Q  H0 f8 S
执行后堆栈为:0   弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。9 n* ]: ]. `; n4 Q, m7 k
执行后堆栈为:0弹出最后一个元素0。& B( o4 Z/ Q7 H, t
0即OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。
  E' n; l/ a6 x, i" m这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。
. o) I# y. k% R, V! d* ]! N0 L
# Z1 m* {3 w) @7 UP2SH
- E( g1 @0 [, V; n; a( \% _6 U因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:9 r/ r# z7 ~+ f: O# ~
OP_HASH160  OP_EQUAL/ ?  ~% a* k9 {6 `6 B. W7 X" u
其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:
8 Y; j' H2 J3 l# E. CRedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG1 E, ]' C7 T, w" p1 a# v  G' q
20-byte-hash-value = RIPEMD-160(RedeemScript)
( M) j& n! s! F" N* r5 kRedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。  E* p( b& Q6 v* V
验证时的堆栈内容为:
7 M: U8 R3 z; s花费脚本为:7 c; u* a3 e$ Y; c$ k
OP_0   
* P; ~4 M" m" y" \! I# w连接前向的输出,完整的堆栈:3 N- E5 ?$ O- {& Q0 u8 c
OP_0     OP_HASH160  OP_EQUAL
7 ]; m* N# \# R9 u. R, o验证过程分为两大步骤,第一步骤:
1 g4 X7 V* r- o" R; f, e. D  U1.执行OP_HASH160,计算HASH值: OP_HASH160
. G( v) `; v" d# O) ]( x执行后堆栈为:OP_0     OP_EQUAL
7 g4 \& A/ d' r5 d, }7 I# a/ |2.执行OP_EQUAL,验证两个哈希值是否相等9 v2 e/ ^" H  o. D* i
执行后堆栈为:OP_0   0 j3 @/ V0 ^  r" V# B  f" ?
第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:
# Z8 I0 h# K; u! v& _OP_0    M    N OP_CHECKMULTISIG
8 ~& O6 H  o3 Z. \) G4 N当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。: v) s( e, x; t
软分叉的关键点9 x) Q: d9 Z1 `# l7 ], T6 J
在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。  {6 ?" |. w8 p9 a$ o! h! y
当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。4 N6 N' A. Z! Y; v! p6 n
激活: P3 {% _% |! y6 e) Y
P2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。$ g! u' L! }& L' t
为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。
0 _+ X: m2 c4 T% N+ T5 a影响深远
) |8 p, }2 y( L# A0 A: YP2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。" Y+ E/ G  |# ?5 u/ e: I$ T/ R- i
发展状况
标签: P2SH 比特币
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

哈哈笑417 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11