Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

P2SH-为比特币赋能的脚本

哈哈笑417
282 0 0
P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M
2 _: v6 [; L  F( n1 mP2PK
5 ~, X. ]5 Z9 E6 h3 d: n0 C5 q比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:- D) j6 D, m  D3 ^
04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG4 \2 O/ [& A8 ?' s0 \' W
0x04开头,表示是未压缩的0x04后面紧接这的是64字节是公钥内容OP_CHECKSIG:操作码,用于花费的时执行验证签名
* {; I* G) E) M0 S那么验证时(即花费这笔输出)堆栈内容为:* F' d0 `4 {, `2 p5 N, r

0 I! {2 T9 F* m花费脚本为:
) H$ r3 E1 A+ y连接前向的输出,完整的堆栈:" O3 X$ N. |7 R6 z* }% |! Y
  OP_CHECKSIG! J5 p5 b5 Y/ K6 i
OP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。
3 D9 {, V. c' o! c, @) k& rP2PKH
" m% \7 t+ u# L9 f$ A# U后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:. A/ @' {# ^) b$ M8 D; |6 M
OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
5 W+ a0 U- b- d* TOP_DUP: 操作码,复制栈顶元素OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希public_key_hash:公钥的哈希值,20字节OP_EQUALVERIFY:操作码,判断是否相等OP_CHECKSIG:操作码,用于花费的时执行验证签名
. P) {5 o0 ]" G9 W( i此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:
+ b" s& R7 k/ u
0 x% I, N7 r- ?+ A& {1 d5 p花费脚本为:% l! Z9 e9 d+ q: y4 i, K, G
  
0 M' e; Z, K+ I+ r: _连接前向的输出,完整的堆栈:% k, z4 K; K, f( t3 j0 s
  OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
5 D. O; H- B# x) u/ D$ f6 t1 [验证运算过程:& R* ~0 l' _* p- X' A
从前往后找OP操作码,首先遇到OP_DUP
  ^4 ?( Y9 }8 c4 \# c8 q, i4 O6 g8 D执行后堆栈为:   OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG; C- P2 l9 B4 s  X% x
执行OP_HASH160
' F$ E, a$ C/ V. w2 ?执行后堆栈为:    OP_EQUALVERIFY OP_CHECKSIG
7 L7 ?0 j5 Q, {执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配7 h6 V- y" M6 {2 C7 Y4 |5 A
执行后堆栈为:  OP_CHECKSIG
% B# ^1 ^$ V0 S+ @1 w2 I/ n7 Y- L到这里就与P2PKH一致了
( ?; X: E# L/ w- z1 C" X0 h1 W执行OP_CHECKSIG,验证签名。
2 z' m5 n) j# n! R这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?
+ ?& o+ t' x5 S- \; B9 o; P原始多重签名
# [" Z# t2 o$ m随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:0 Q3 h( S2 v7 D$ [$ w
m {pubkey}...{pubkey} n OP_CHECKMULTISIG
, f$ j$ J0 j5 T+ o% r: h6 z+ R/ r2/2的一个原始多重签名示例:+ Z, D6 N( y$ h% k  z* |* f
04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac48 V4 B' x$ H1 d
0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af
3 S9 v) F4 ?, X( ~/ @( p6 X& y) \2 OP_CHECKMULTISIG
  W( @* S* A/ b7 ?5 p- B验证时的堆栈内容为:
+ P4 P' X& i) C' H( Z' Y% K花费脚本为:* I$ W' M# a8 [8 b- ~
0   0 ^8 a3 B. W2 Z4 {. z& I, |" h
连接前向的输出,完整的堆栈:3 H) `. A1 ~& y) V, B  L8 f- `5 K: H
0    M    N 4 ~, O( l; K) a, u& g
OP_CHECKMULTISIG
3 U4 F' |3 A8 ?/ {! l6 R: c$ I% @OP_CHECKMULTISIG的运行过程稍微复杂一些:5 J4 z% j9 {+ B% m# K4 _- R
弹出最后一个数,就是N,公钥总数。
  S# d  Q/ K3 C8 d执行后堆栈为:0    M   弹出N个堆栈元素,就是这N把公钥。  ^- A: `% I$ R: }
执行后堆栈为:0    M弹出签名数量M,即需要M个签名数量。& X& y( ~  |7 }! [9 q
执行后堆栈为:0   弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。( m& |1 u. H. U
执行后堆栈为:0弹出最后一个元素0。* q9 @( |6 t% Z/ K5 N+ l: K
0即OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。
: w5 D9 K9 k/ H9 I5 `. O这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。+ K* W( ~$ A: N: l0 P$ c# |; [' E
" z$ {2 C+ V% @' L6 x; `
P2SH7 b  @  x5 x8 \2 t! O0 L4 E
因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:
: s  Z  u# X' \' e( AOP_HASH160  OP_EQUAL
8 O) i) K3 K+ y6 `) [4 `其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:" ^7 W' M$ f8 F
RedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG9 h/ S( p$ l3 C7 V% }* L3 c
20-byte-hash-value = RIPEMD-160(RedeemScript)1 _3 ~2 Z. g# w" U0 v/ m3 @+ I
RedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。6 i& A$ e5 D) }. ^7 v# T
验证时的堆栈内容为:4 L4 p6 L* g  g! y7 ^
花费脚本为:7 g3 M3 `4 m- W( D, }, E
OP_0   
) n1 D+ Q: p! z1 c6 r连接前向的输出,完整的堆栈:
! V2 x/ \4 v* eOP_0     OP_HASH160  OP_EQUAL$ W  m5 N$ O# o" _
验证过程分为两大步骤,第一步骤:
; W" W+ W% B- M1.执行OP_HASH160,计算HASH值: OP_HASH160
/ h1 k- x9 Y3 y+ `1 Z4 D执行后堆栈为:OP_0     OP_EQUAL
( o" }  Y  e% p+ x$ \2.执行OP_EQUAL,验证两个哈希值是否相等1 n1 \6 s$ \) U
执行后堆栈为:OP_0   " L4 v2 l& e/ K8 p
第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:( N$ n) Q' r7 H! W  G+ r8 h" J' I
OP_0    M    N OP_CHECKMULTISIG
( K0 n) E! @8 B4 C7 d! g7 Z当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。+ A" ~/ R3 U+ E0 O3 g
软分叉的关键点
- U* E* K( i4 w) z$ g+ e在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。1 r6 S# f' U* q0 a( g9 q
当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。
) N; f7 g9 g. E6 A; D  }! h激活
: I) d  E: t2 O" a7 q& q3 YP2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。
+ Z' |6 @& S" U$ u. J4 h4 K为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。
, r* Y5 G8 ?& p2 F; g0 q! Q影响深远. o1 \2 j2 t6 `& Y1 Y& s! k
P2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。' V- T+ ^& n9 z+ u: Y1 @
发展状况
标签: P2SH 比特币
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

哈哈笑417 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11