Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

P2SH-为比特币赋能的脚本

哈哈笑417
118 0 0
P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M' {3 [& w8 {: P2 E7 E; \6 R
P2PK
# K2 ^7 I) ^' s比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:
8 w+ I, J: z/ W" ?" p3 |! q3 y04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG9 g$ l- T# j: I; P3 R$ R: h# D
0x04开头,表示是未压缩的0x04后面紧接这的是64字节是公钥内容OP_CHECKSIG:操作码,用于花费的时执行验证签名
# |- n' X2 ~# f' t那么验证时(即花费这笔输出)堆栈内容为:8 l" G* d7 H2 I0 z3 m

$ N2 x7 j" n' {/ E6 S花费脚本为:
, r' ?$ r- E, f" {连接前向的输出,完整的堆栈:
4 o) r/ U! o7 M3 k  OP_CHECKSIG
/ e- m! E6 l. j& `, M% d- uOP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。; {" |  ~' |" ^/ F
P2PKH  a1 i* ?" S9 Q8 e! ~, D
后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:
) @, [6 H* s5 G- jOP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG1 F# I& q1 b3 y: N# I; |8 G4 ^
OP_DUP: 操作码,复制栈顶元素OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希public_key_hash:公钥的哈希值,20字节OP_EQUALVERIFY:操作码,判断是否相等OP_CHECKSIG:操作码,用于花费的时执行验证签名
; q" a+ |2 O- A& J4 p( j8 e" A此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:4 p1 x- W) y) H& ~  T

- W+ g, E5 j2 e( c0 U1 W3 l4 [花费脚本为:
+ x- C+ U/ A$ n5 F  
7 w  t' Y& k8 d8 P  X: G连接前向的输出,完整的堆栈:
* a) X; p* F# o1 b4 t( @  OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG3 ^" ~5 T/ ?( |7 U% V4 y/ h
验证运算过程:
9 q6 \' P! j0 N从前往后找OP操作码,首先遇到OP_DUP' V' m! _. X  j: T! e
执行后堆栈为:   OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG! H% ^/ ~( F4 Y5 V! o4 A3 \
执行OP_HASH160& U. [1 k8 k; ]+ ^% X" }7 U! m
执行后堆栈为:    OP_EQUALVERIFY OP_CHECKSIG* P( }. f; J3 D9 r' F0 x+ G
执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配
1 S8 d9 d1 |; L& V+ O- p) X; {4 G执行后堆栈为:  OP_CHECKSIG
# w* D0 ~% \4 W  e# R! R- F8 W6 i到这里就与P2PKH一致了- I7 ]' [! {% p8 A" p# S
执行OP_CHECKSIG,验证签名。
0 m; z9 J3 L. M& P5 ^: i  U这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?" i2 m7 x  n4 l: V% @( \" V, Q
原始多重签名
: v/ n7 ]& q2 d) F随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:
: n# U# K& G5 @. _& D6 U* dm {pubkey}...{pubkey} n OP_CHECKMULTISIG4 `9 H9 [& y/ {  l/ ]
2/2的一个原始多重签名示例:! w3 [; X' d! m" y$ \
04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4- h$ [# M8 O% R
0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af
2 k" L: Z' e0 n" t3 S# F5 x  f1 U8 `2 OP_CHECKMULTISIG
' J$ L( |, o# F' D验证时的堆栈内容为:
( I+ n/ a9 a- O9 i* ]花费脚本为:
0 W0 @2 {: D6 q! s5 J( J0   
4 W' Q; n* o: C  D3 s* W8 {连接前向的输出,完整的堆栈:3 g" Z2 O# x) S% {  X# z
0    M    N
' z7 ^, J( \; T& ^- ]3 a7 o' H5 rOP_CHECKMULTISIG+ d# I6 ^, m/ S( @& r/ B* q* I
OP_CHECKMULTISIG的运行过程稍微复杂一些:
6 q3 X. Z) }) G6 ?0 D$ i4 O" a6 P弹出最后一个数,就是N,公钥总数。
4 v5 Y1 l: A9 A$ M2 n执行后堆栈为:0    M   弹出N个堆栈元素,就是这N把公钥。- L1 Q/ W3 U1 M, L: w3 [1 p
执行后堆栈为:0    M弹出签名数量M,即需要M个签名数量。
& J3 B' r; G6 o% x执行后堆栈为:0   弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。
! @! _9 }2 `9 ]8 }) A执行后堆栈为:0弹出最后一个元素0。8 R6 [* @6 L; }4 V6 l5 R" u$ N
0即OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。, l$ c" m  z7 j/ {9 r: X
这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。( E# H+ `( o) g. k

2 T$ f# l: @; {" q5 _5 F, uP2SH: u/ q- O$ R) x2 p
因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:8 A1 O* {' W4 H! F
OP_HASH160  OP_EQUAL- f9 r: C0 T, b2 _1 I1 P
其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:
  o  o5 K, [8 c0 m0 wRedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG& P6 ^5 @: x4 `% z4 ?3 G* I
20-byte-hash-value = RIPEMD-160(RedeemScript)5 E+ E- O* u" o
RedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。3 R$ S( M, O! j7 g3 x) T
验证时的堆栈内容为:
9 H7 x2 q. F0 y/ V, g8 J2 g- x$ S花费脚本为:
1 _. K9 l* B$ kOP_0   
% |. V2 N# K: W9 t连接前向的输出,完整的堆栈:
, g, m, l4 k2 OOP_0     OP_HASH160  OP_EQUAL. m5 w( S* A, g" e* e/ d/ K
验证过程分为两大步骤,第一步骤:
" k* G1 G$ y& U1.执行OP_HASH160,计算HASH值: OP_HASH1601 L+ g- x1 a6 b  J
执行后堆栈为:OP_0     OP_EQUAL! A9 Z; c) a4 B+ b- L
2.执行OP_EQUAL,验证两个哈希值是否相等  q, ^* l4 O5 j. Z5 l3 p: A2 @1 U
执行后堆栈为:OP_0   ) k' L3 F6 @5 h. a( R/ `
第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:
# C! I" `4 W. {OP_0    M    N OP_CHECKMULTISIG2 a. q3 x* `3 c( }  V$ i2 w
当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。; k* S. a7 G8 D8 T. v# e! w
软分叉的关键点
, s  G0 F8 f& T. q' G3 F& o  S+ b在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。
2 Q" O# P4 @" ^5 A9 K0 B" P! n当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。
; q% j% d. q6 M( S$ y2 S8 `激活
/ j) G# A- Y2 d$ O2 x7 n7 V: fP2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。* h9 Q# N' Q# [" [' Y
为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。4 ^2 Q9 t% d' v: w2 v* n# c1 ~5 @. Q
影响深远
3 [" E0 ?0 |" n/ m- E- [# Q/ RP2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。/ r* C' {: ]8 j2 ]+ x
发展状况
标签: P2SH 比特币
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

哈哈笑417 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11