Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

P2SH-为比特币赋能的脚本

哈哈笑417
119 0 0
P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M6 n: Z: Q8 k: N/ M
P2PK6 b4 k/ G& _$ \8 L& h9 w
比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:
+ u* M3 c4 w% V) H04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG
8 X6 h- z0 |; b% p- P5 `0x04开头,表示是未压缩的0x04后面紧接这的是64字节是公钥内容OP_CHECKSIG:操作码,用于花费的时执行验证签名9 }8 t# a. `6 \6 J* w0 P& U
那么验证时(即花费这笔输出)堆栈内容为:
7 J. S# \. N; E4 i$ E" m  m$ l5 `2 k0 l
- y6 j- c: ?  x% G- F& d6 u: F& ~花费脚本为:
. E% d6 t# c' K' \1 {* {& y连接前向的输出,完整的堆栈:
4 h, j# {$ Y/ z* |, }" Y. ]  OP_CHECKSIG. c1 }& A( j+ ?7 J* w) T
OP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。1 J. N$ E6 a; L/ d4 ^. p, G
P2PKH
& s$ Y# A0 P( `后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:3 k& i' M8 |' e& B& }
OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
, G2 m5 [. h  k9 l" d% ROP_DUP: 操作码,复制栈顶元素OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希public_key_hash:公钥的哈希值,20字节OP_EQUALVERIFY:操作码,判断是否相等OP_CHECKSIG:操作码,用于花费的时执行验证签名
( e+ y* b  V& n3 `; I+ K此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:7 R: b& A' [' T' o+ L- h

- F  g7 T" M5 Z9 m7 t% o花费脚本为:
6 ]5 N7 q! }6 A7 p( c2 Y  3 h0 G" N' s* I* z
连接前向的输出,完整的堆栈:
1 U; h0 u+ ?( A, p  OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG& _# M8 C$ w, l, e! {* p
验证运算过程:. d6 D5 j9 B; Y9 d; ]/ [
从前往后找OP操作码,首先遇到OP_DUP
( E3 H3 p  y, \9 g/ f& I执行后堆栈为:   OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
: ~* {; c' [3 \: b- P0 s9 M执行OP_HASH160
% F7 x( R# c+ E9 i0 S, L执行后堆栈为:    OP_EQUALVERIFY OP_CHECKSIG
% R, a' o/ Q8 Z/ [7 V执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配6 g3 r' w- Q9 y
执行后堆栈为:  OP_CHECKSIG2 j0 `! ?* B0 ^" @$ O
到这里就与P2PKH一致了7 [- R+ N6 _( J
执行OP_CHECKSIG,验证签名。* l/ B5 i7 _$ j- b$ a0 c
这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?
% u* w: {' M8 B; z& m$ B- M原始多重签名2 x% k1 w6 v4 [  \2 C. d
随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:
2 ~0 F! X9 i9 M5 V% `8 g% wm {pubkey}...{pubkey} n OP_CHECKMULTISIG$ ^# }/ h/ z5 q7 R7 p; [
2/2的一个原始多重签名示例:
- }5 S1 ~$ J: v1 g. c6 z04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4! K9 j) a: i1 q$ p9 K' Z
0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af. w. A' N4 Q  L. j1 B% K2 s% m
2 OP_CHECKMULTISIG2 J% c- e7 x1 {# h. m! a7 Q0 X
验证时的堆栈内容为:- Y  l; w$ ]/ l$ T$ r4 V
花费脚本为:
* d+ N2 P5 f' y& o# F0     S6 P8 O* J' a9 F
连接前向的输出,完整的堆栈:  \7 P! ~$ q: H7 J- w  @# @$ w
0    M    N
' p$ K& D9 |& H, Z* h4 q, TOP_CHECKMULTISIG! {: z) Q9 P# X6 J' X# P& H/ Z
OP_CHECKMULTISIG的运行过程稍微复杂一些:
2 n+ B$ _5 K) O$ [弹出最后一个数,就是N,公钥总数。
: W) b: q% Q( r/ g; M2 h执行后堆栈为:0    M   弹出N个堆栈元素,就是这N把公钥。
: _9 s; o/ V2 _. o执行后堆栈为:0    M弹出签名数量M,即需要M个签名数量。; a" ?* c3 K8 G$ {
执行后堆栈为:0   弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。  g3 i( O- g5 s7 J8 P, U, ]
执行后堆栈为:0弹出最后一个元素0。
, }, `1 i, j! m" k" ~2 G0即OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。
6 Q3 |/ h! e& G2 L! z( }6 B这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。" ?* ^! F9 u* |3 e3 m2 ?
; w; W$ P0 P& T5 z1 c2 D
P2SH
" s9 f$ d1 g, e& _, o3 V因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:8 e5 Q+ L: ]: ^! n
OP_HASH160  OP_EQUAL
$ E3 |" |* I# _* A- p! m9 U  o其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:
; z/ o) U3 N0 W/ U1 @5 V( JRedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG
2 z4 I( r2 i7 m8 K1 N20-byte-hash-value = RIPEMD-160(RedeemScript)9 V" m  i1 T8 V6 D5 A
RedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。6 W' f9 J7 ^, V  `4 f/ N8 F: L
验证时的堆栈内容为:5 \8 L. B" Y; x, G: u0 A8 C: H
花费脚本为:6 P* b  @+ V9 H( A* p. V7 H
OP_0   
6 d* V: z& N8 E连接前向的输出,完整的堆栈:1 i1 s) w3 j" ^# c0 _
OP_0     OP_HASH160  OP_EQUAL) F* d. Q; L+ J/ l/ N' S
验证过程分为两大步骤,第一步骤:
2 w( C5 p1 b( o1.执行OP_HASH160,计算HASH值: OP_HASH1605 i: a0 T, M- J8 f2 R
执行后堆栈为:OP_0     OP_EQUAL8 }* k; E7 d2 E& `# n( v
2.执行OP_EQUAL,验证两个哈希值是否相等( J. ]3 l: [9 B  K" C9 X
执行后堆栈为:OP_0   : s0 P0 g# t4 s* ~- L
第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:
, T; B% T* }4 G& }- i2 s; K) e7 i: jOP_0    M    N OP_CHECKMULTISIG
: n) C) U/ @  d7 ~5 g当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。
. ?* A4 o' I9 t* l5 h  L软分叉的关键点
- F- [( V' [! `4 [( L在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。
* p. I# {. c7 p$ k+ W/ K当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。& V5 V- \  l$ V  d
激活+ r4 t( i4 d2 r
P2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。8 e% |/ J2 o5 X3 j: n' x  l
为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。% g  z8 r- x, O& v( l3 C
影响深远0 W6 b3 W( T: |2 F. y( s/ i  G+ d
P2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。
- U# s$ N- P+ f" h7 A发展状况
标签: P2SH 比特币
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

哈哈笑417 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11