Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

P2SH-为比特币赋能的脚本

哈哈笑417
162 0 0
P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M# O$ N3 q; A# p1 e( Y% c. X
P2PK
0 K( a7 {; v- c# f. a5 j0 s比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:' l$ |! ]: ?9 a4 h: @/ f4 d
04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG
, Z% L' ~% |- |. B& z' _0x04开头,表示是未压缩的0x04后面紧接这的是64字节是公钥内容OP_CHECKSIG:操作码,用于花费的时执行验证签名
7 r  k% W2 O- b# X0 d那么验证时(即花费这笔输出)堆栈内容为:
! L3 Z: S) g( t1 F- _- L
, z- L  u9 Q0 Y" W8 c5 p花费脚本为:
/ W/ V& G! a1 `连接前向的输出,完整的堆栈:
: B+ [- [9 m$ ~4 m8 U" T  OP_CHECKSIG; S# W7 b/ z. d9 U4 E1 y5 Y4 }
OP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。8 p" K+ m' n, {. R( O$ n3 |8 A
P2PKH
; s0 `, e+ }6 t* i  V后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:
3 _4 t8 f$ I3 B  D9 r) z7 GOP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
1 A: m0 F7 D2 @4 E6 K" Z( P; R5 x3 |OP_DUP: 操作码,复制栈顶元素OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希public_key_hash:公钥的哈希值,20字节OP_EQUALVERIFY:操作码,判断是否相等OP_CHECKSIG:操作码,用于花费的时执行验证签名  v% M: P' w+ z4 m$ Q4 Q
此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:
8 P; @$ J' o( U' J- Z' \- b4 w& x5 [' n$ O0 m7 v5 A
花费脚本为:
/ b4 b: X: g+ e" Q: F  
% f' j3 K( N* }) x+ m+ z9 w8 _5 Y( F连接前向的输出,完整的堆栈:
. @  _- P0 ~- P, h  H  OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
' S, d* `7 U$ _验证运算过程:
8 X/ a. q* \7 V+ H从前往后找OP操作码,首先遇到OP_DUP
2 L+ J8 l) x: e执行后堆栈为:   OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG8 ]0 i: T9 G) d: a- O
执行OP_HASH160$ m- L6 r* q& l; {; F9 J+ s
执行后堆栈为:    OP_EQUALVERIFY OP_CHECKSIG
0 G( Z" z; K$ U5 R- k& S% J0 H执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配
, p0 |3 B. B* P0 h执行后堆栈为:  OP_CHECKSIG3 c, |' z  E7 r0 [, t% q
到这里就与P2PKH一致了
" u* U$ A7 Z. p) T5 l执行OP_CHECKSIG,验证签名。& X! K9 m; E. k
这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?; d5 U& d* z& t( o5 R' [& u. ]
原始多重签名- r& c, R/ J1 \" P0 D
随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:. e7 @2 s- ^) R) j' M+ Y
m {pubkey}...{pubkey} n OP_CHECKMULTISIG, j. }. x) J+ ]
2/2的一个原始多重签名示例:& q4 q! e5 Q) v% g0 P7 m
04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac46 I% F5 G+ |4 \. o" _7 z
0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af, `5 t: V8 ?6 T
2 OP_CHECKMULTISIG% D7 Q( p1 Y  c: m( _( A4 d/ @" d
验证时的堆栈内容为:
& w5 y2 j% @; L1 D2 G5 q: q& q花费脚本为:
) G  L& c1 m# {5 s6 G* D0   
7 f7 v% r: v4 R( N* K3 n+ b+ C连接前向的输出,完整的堆栈:  o3 f6 I# q+ [3 j, n% C
0    M    N
# m* p' q/ T6 v3 W/ YOP_CHECKMULTISIG, o3 I9 _: {2 R$ t- _
OP_CHECKMULTISIG的运行过程稍微复杂一些:! B9 w; z: J# J7 i
弹出最后一个数,就是N,公钥总数。# h  u4 X6 L: x) T
执行后堆栈为:0    M   弹出N个堆栈元素,就是这N把公钥。$ `4 E9 |$ S. W9 }6 W% F
执行后堆栈为:0    M弹出签名数量M,即需要M个签名数量。
; j* Z* h" ]/ T7 l5 h% S! U执行后堆栈为:0   弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。
" N3 x  b+ U- ]* C) K7 E7 f5 G执行后堆栈为:0弹出最后一个元素0。% [- W4 b1 v) _) x
0即OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。
2 v$ z( j! C8 _. L8 Q这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。
0 j1 C" F4 l0 P/ u& }
# Y* d2 @( c$ p. R+ p4 PP2SH. y0 k) h. |6 _, m& c
因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:$ v. w8 U( S; w, N7 H: Q% g; \
OP_HASH160  OP_EQUAL
% _3 [) a: c" z/ W  c/ U其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:
& c+ Z4 l, c0 W8 g' |. q8 vRedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG; v; t% ]; C( t6 g0 R8 |9 n
20-byte-hash-value = RIPEMD-160(RedeemScript)
% I3 M% D4 W* M. [1 }( F( c" a* XRedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。
- ^; Z. F" f" i验证时的堆栈内容为:' y$ A0 c" \4 W9 r6 c. g) C
花费脚本为:+ N" I) w) W, w* d  V0 {
OP_0    9 j; j3 p5 p( [
连接前向的输出,完整的堆栈:7 K) T- @, O, ~6 l# y
OP_0     OP_HASH160  OP_EQUAL
; ^8 m2 ]% s& \/ V6 E. a6 i验证过程分为两大步骤,第一步骤:( ~6 c1 C5 q, K! g1 g% P2 J. R
1.执行OP_HASH160,计算HASH值: OP_HASH1605 Y8 ]7 h, @* {0 [
执行后堆栈为:OP_0     OP_EQUAL5 I% G! d2 {" e- z0 j' |- u6 f
2.执行OP_EQUAL,验证两个哈希值是否相等
5 L2 p! X+ F5 C5 J% k5 g+ d执行后堆栈为:OP_0   / n1 d: S* S9 ^, R% `
第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:
0 j* \+ L4 N; ~OP_0    M    N OP_CHECKMULTISIG9 q7 j) P. v8 o1 x' y+ p% g
当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。
( u$ `' F* B2 \( \# p" c软分叉的关键点
; R9 J9 c$ H. T/ r& C/ w# Z在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。
4 h1 I5 L2 Y: ^& Z/ W当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。
  P8 |- \7 w; W激活; J  E2 J9 J% g# W& `
P2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。
+ M; ?  y) u' B% x6 _为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。
; c+ o- s6 g2 y, J影响深远
5 N$ r4 W+ Z+ ~6 K! A3 eP2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。0 a$ g; O* K; V$ V! M, q5 r
发展状况
标签: P2SH 比特币
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

哈哈笑417 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11