Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

P2SH-为比特币赋能的脚本

哈哈笑417
112 0 0
P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M( \" ]* v' ~) {7 p8 E
P2PK
  y- ?5 z6 c% y& n比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:* g. e4 R' O( k# j
04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG
4 {! E* ?) m( ]: G4 T0x04开头,表示是未压缩的0x04后面紧接这的是64字节是公钥内容OP_CHECKSIG:操作码,用于花费的时执行验证签名
8 F' t! D+ E5 g' E1 {! s那么验证时(即花费这笔输出)堆栈内容为:- R2 q* @0 _( s$ m& S
5 B4 J/ F. K/ {
花费脚本为:  I9 A" G& {8 L
连接前向的输出,完整的堆栈:1 v5 s+ O) @5 t: _  T
  OP_CHECKSIG
+ a" X' a$ L% P  @) G( UOP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。, ~; D7 |- H) G9 ?; c. `
P2PKH: O/ a8 \! d+ m3 F
后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:: ]* E% }7 w! `+ T' y4 A+ d, L8 _
OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
  d& A0 Z0 @$ w3 LOP_DUP: 操作码,复制栈顶元素OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希public_key_hash:公钥的哈希值,20字节OP_EQUALVERIFY:操作码,判断是否相等OP_CHECKSIG:操作码,用于花费的时执行验证签名
  D8 b# ]$ W) F! ]  C此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:
" {/ B7 i7 r6 O" \: [8 K, _# Q# R8 Z* s& N* Y7 B# P
花费脚本为:
3 s  S4 t/ C. C  ]0 F  T6 }2 ?    M! C9 U% ^8 v- _4 B7 L! ]# f1 d
连接前向的输出,完整的堆栈:  t, l! H; @. W" u: \
  OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
: K6 R  T' {, y( d5 V, K验证运算过程:7 r- v  w( F7 [, O, a, z( Q3 c
从前往后找OP操作码,首先遇到OP_DUP
0 p$ _; i- P# @执行后堆栈为:   OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
2 e& y6 i9 e+ A2 z  c3 R# v执行OP_HASH160  }7 d0 y5 h+ c- s5 G
执行后堆栈为:    OP_EQUALVERIFY OP_CHECKSIG
: M% _; }% o8 u, {- c4 G( [执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配
  L: Z3 C( Z1 l1 C执行后堆栈为:  OP_CHECKSIG2 n3 [' Z  I! G; e0 I) j5 x
到这里就与P2PKH一致了
; l! g5 W' j6 ?! Q8 o& R执行OP_CHECKSIG,验证签名。' O$ q1 _, W! A+ w
这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?- Z6 s1 _& \6 T
原始多重签名
+ ~3 n, W6 [/ @) a. |3 Y随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:1 Y# k* |$ Y5 K. y  m, f' y5 W
m {pubkey}...{pubkey} n OP_CHECKMULTISIG
: k% h  w& B# t2/2的一个原始多重签名示例:$ u+ S" O% i) T& ~
04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac42 |% p. V9 G6 h/ k+ y) N
0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af; b' B& U+ Z& k( H
2 OP_CHECKMULTISIG
+ a3 t9 z4 d/ E* v% o验证时的堆栈内容为:( o) k# P' ~0 H5 C& x. u4 D
花费脚本为:+ Z/ F! l- n* W* E  ^+ {( Z  S
0   
1 X! _! e+ Q& W  p1 b连接前向的输出,完整的堆栈:  \1 H4 E* s  {/ p
0    M    N 2 c( a1 s0 ]1 @# R* t1 p& z# n% v* T
OP_CHECKMULTISIG' x' G6 W& A  V1 B
OP_CHECKMULTISIG的运行过程稍微复杂一些:
! D& _6 [+ O. N2 [, f- C" n4 b9 |3 V0 c弹出最后一个数,就是N,公钥总数。
& Y2 c" e3 k  D- ~9 }执行后堆栈为:0    M   弹出N个堆栈元素,就是这N把公钥。: Z; e0 Q! t9 l/ u% B% i
执行后堆栈为:0    M弹出签名数量M,即需要M个签名数量。
. H# R9 t1 A  s执行后堆栈为:0   弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。- G0 G3 J# N! x# l! f' Z$ f/ ]
执行后堆栈为:0弹出最后一个元素0。
$ T7 v& R7 u5 T0 Y7 z# H, y0即OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。
2 V6 v2 |3 Z* p; O这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。
8 o: R  Z0 F% V1 y" F: F
: |4 U3 [" @* g9 G: f1 [P2SH
1 a8 n8 E4 @  N; E, X5 v因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:  F  }) z6 J7 t$ F, ]! k6 X) A
OP_HASH160  OP_EQUAL
! b: E2 _) w+ z/ m其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:
6 ]% q) P/ o2 o9 t1 vRedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG0 ?$ f: R8 R. P2 P7 r: r0 T' U
20-byte-hash-value = RIPEMD-160(RedeemScript)" ^% h* A& D9 U* j
RedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。9 D) ]. U6 g# j' x+ W' {6 q
验证时的堆栈内容为:
/ E/ z6 k7 w' t7 {& }: K+ [花费脚本为:
3 R* w+ u1 Q' o# c4 q: t1 _8 tOP_0   
8 u2 ~. Z* ~5 l7 w连接前向的输出,完整的堆栈:
' H: h% }* `  G% y" J8 |( POP_0     OP_HASH160  OP_EQUAL
, a+ P8 `7 J: s- }# ^4 {验证过程分为两大步骤,第一步骤:7 w+ G: j. z3 ?! P* s9 ^
1.执行OP_HASH160,计算HASH值: OP_HASH160
3 M: n  E" n5 N# }; a& D执行后堆栈为:OP_0     OP_EQUAL
* I* K3 D" F# ?" N9 H( W% Z2.执行OP_EQUAL,验证两个哈希值是否相等4 a! t  X9 c8 v  Y( C# ]5 q7 b0 C
执行后堆栈为:OP_0   
0 I$ C& W5 F# t/ R) \% U1 J第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:
5 f4 d6 x$ N3 _& o% P) Q) zOP_0    M    N OP_CHECKMULTISIG) X, d4 J" A) o# f6 D
当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。
. p. B' Q6 L- ?软分叉的关键点) r' o) o- s' F% S" y9 o
在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。
: p8 B4 j/ ^" B. c" n( b) Q当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。
) {0 _. B' ~5 m; J& p2 |5 C激活
  J  a5 d/ H; H5 [P2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。2 X3 B; C$ Z# V% L1 N9 T
为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。; n! g* r& v$ M4 }0 E1 H
影响深远. u6 S2 x9 O2 x! Q
P2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。3 i0 V! p; W8 l/ g- S
发展状况
标签: P2SH 比特币
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

哈哈笑417 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11