Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

P2SH-为比特币赋能的脚本

哈哈笑417
78 0 0
P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M
, N/ a, d& O7 A" @7 @, hP2PK
7 O# Z" l- F' {& C2 N/ O* E* V; X比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:
, S" D2 c% [& R04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG$ {* U, H! l" P  S) ], T
0x04开头,表示是未压缩的0x04后面紧接这的是64字节是公钥内容OP_CHECKSIG:操作码,用于花费的时执行验证签名: R$ |4 p1 s: ?; E, d7 p
那么验证时(即花费这笔输出)堆栈内容为:- C' |# G2 v$ U- ]2 e* L
* j+ A+ F/ ^0 r5 ?4 j8 h$ X
花费脚本为:2 r2 W( c0 o" b$ G
连接前向的输出,完整的堆栈:
0 M7 N1 n; _: [: Q6 y3 f  OP_CHECKSIG
/ b! N' }  Z% ?' }5 wOP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。
7 U/ M4 P* {+ c/ S5 g1 ^P2PKH
- r3 t( S- e4 A6 q& ~! h) n" F! u后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:
; q! U2 W5 z& ]1 o6 TOP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG5 [- q: |: t3 J
OP_DUP: 操作码,复制栈顶元素OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希public_key_hash:公钥的哈希值,20字节OP_EQUALVERIFY:操作码,判断是否相等OP_CHECKSIG:操作码,用于花费的时执行验证签名
7 m/ q+ h8 b- L( P: G' T  m此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:
4 x- W" h9 p. I$ ]; b* L7 V. m6 y& I, L! r
花费脚本为:7 Y% j8 {: @4 S$ D. p; @
  
3 i$ y5 d) ]; ^5 k. p+ T) A连接前向的输出,完整的堆栈:
3 B5 J+ L' f7 [' C- Y$ k% n! D: I6 l- e  OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
( Y/ p5 z/ o1 m. k9 {. O; e! K2 R验证运算过程:# ]! o# H2 P' d3 H3 X
从前往后找OP操作码,首先遇到OP_DUP
# S) O7 @- z9 j! A, E% P' \执行后堆栈为:   OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG2 X- x  V5 j/ D% X8 |2 a2 C! y* B
执行OP_HASH160% j/ i& B( L$ ?; K2 `
执行后堆栈为:    OP_EQUALVERIFY OP_CHECKSIG+ P2 U5 \& o9 z+ P
执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配" ^: j: @9 ~4 |' W
执行后堆栈为:  OP_CHECKSIG
% j' o, p3 h; m# k& k到这里就与P2PKH一致了8 Q# Z( F8 F; E% a/ G2 A, ^) c$ C
执行OP_CHECKSIG,验证签名。  S7 ^5 p4 e2 {# F$ o
这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?( `$ c0 T) K+ c9 {
原始多重签名5 p5 v3 ?2 r( k) a- k8 O1 F
随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:
4 y$ g4 T6 r  G$ xm {pubkey}...{pubkey} n OP_CHECKMULTISIG
9 G2 D6 X" ?0 i" ~9 d/ H3 Y2/2的一个原始多重签名示例:& O% k4 t( {; u5 S2 D) ?
04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4% M+ M5 t2 J3 _% Q: _( J
0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af
% D# J; C! L2 t2 OP_CHECKMULTISIG
& B+ G- S7 v0 W9 I' n5 D; s验证时的堆栈内容为:) o1 Y, ?, K7 c3 t% p
花费脚本为:8 Y5 C1 E$ c: o& z
0   + c4 N1 s3 j" l4 W5 R3 ?' s
连接前向的输出,完整的堆栈:8 D5 Y3 R6 L3 v8 a
0    M    N
( h6 e3 i4 F" R! a8 U0 f. g% {! q+ C% XOP_CHECKMULTISIG) i1 D; i. p+ y' ]
OP_CHECKMULTISIG的运行过程稍微复杂一些:
! l) C6 l! m% F弹出最后一个数,就是N,公钥总数。
1 v* R; A, @, D+ K0 P执行后堆栈为:0    M   弹出N个堆栈元素,就是这N把公钥。$ t' @6 K6 v: d& c
执行后堆栈为:0    M弹出签名数量M,即需要M个签名数量。1 W- t, j) H: l# ]: o  T1 ]) g
执行后堆栈为:0   弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。
8 _3 g( Q2 G$ S2 s- ^' [3 P执行后堆栈为:0弹出最后一个元素0。- x6 V- Y. `3 W) N
0即OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。% ^+ \9 j4 i/ n* d- m
这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。
; L/ d  w# t* d" }% U+ h# W$ `* G2 `& K
P2SH- m( ?( l& U; ^$ `; W+ J6 a
因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:" F0 O5 k# I+ M" ?+ l0 e
OP_HASH160  OP_EQUAL4 Z+ v/ I- K6 }1 [- w( K1 T# f
其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:1 t/ c5 m! B1 ~7 [7 z
RedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG
1 [' G- X2 i1 C: |7 O/ |, \6 C  ?20-byte-hash-value = RIPEMD-160(RedeemScript)
+ B$ U' ^' y& W& mRedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。0 o6 Y* s) p) \4 @8 x
验证时的堆栈内容为:
' \( K; t2 l8 D$ s5 I0 `2 ?花费脚本为:
) O  v; M( E- ?, I8 C8 ROP_0    ( Y& E) ~: E2 u" ]" q" R
连接前向的输出,完整的堆栈:$ E4 J+ q4 t' F$ _
OP_0     OP_HASH160  OP_EQUAL4 u  U. g* ]9 G- T9 J' n% o2 a' ^/ |) f
验证过程分为两大步骤,第一步骤:
* P0 ?) A' a) t. ?0 A& ?5 G- H1.执行OP_HASH160,计算HASH值: OP_HASH160( A$ y# h7 L- e1 M' M' u0 K! \) h# V4 X
执行后堆栈为:OP_0     OP_EQUAL* U# H/ Z* |8 m. j5 ^: x
2.执行OP_EQUAL,验证两个哈希值是否相等8 e( f0 [: z' p2 {/ e/ b# e$ j0 p
执行后堆栈为:OP_0   
' P! I7 O( M" f1 g+ m. e6 r第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:
5 I' A' w% A' v3 e7 e* D  iOP_0    M    N OP_CHECKMULTISIG
7 f' z6 Y) B, |* o* K; P% M6 g当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。
7 o% x- i  i( a' F7 d软分叉的关键点: e1 \% r3 R& [; t2 r9 J" h+ e
在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。0 D. H; n" a- w7 \8 J4 H( @6 ~$ |
当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。
. }& L4 B/ ~6 u% }8 }* \激活
$ }8 ?5 }! M; r$ dP2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。  Y; J8 F* p! D- H0 D8 R( t8 P2 P
为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。
2 P+ f9 C9 F6 k  Z  h* ~影响深远
2 r3 `7 U4 q8 K9 yP2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。
. y' C4 A/ M' ~( I发展状况
标签: P2SH 比特币
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

哈哈笑417 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11