Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

P2SH-为比特币赋能的脚本

哈哈笑417
116 0 0
P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M
: g  ]  ]& w* V' GP2PK
9 H2 N1 p# z; {比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:! `6 i+ a+ h8 S: X  x/ V$ w* h
04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG
0 v; V- L+ R; }) r0x04开头,表示是未压缩的0x04后面紧接这的是64字节是公钥内容OP_CHECKSIG:操作码,用于花费的时执行验证签名
1 Z" h! ^5 B7 F, Y' A( t那么验证时(即花费这笔输出)堆栈内容为:3 a& h& _' y0 W) u5 e1 U

8 u2 [. K  p7 e! Q, }. i花费脚本为:6 G. G2 O3 c/ ?* J
连接前向的输出,完整的堆栈:
6 D& t7 R6 K+ M( p. n/ c, g* Q* ]- {9 P  OP_CHECKSIG
3 d. l* `5 v! c6 g( L6 }) fOP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。* T1 h6 X0 G8 `; C
P2PKH
2 b! K6 @. A4 L9 x9 v2 B7 u后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:
7 I: h* ~8 j4 l' b. i) f# BOP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
' g/ R; I" x1 |4 [! HOP_DUP: 操作码,复制栈顶元素OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希public_key_hash:公钥的哈希值,20字节OP_EQUALVERIFY:操作码,判断是否相等OP_CHECKSIG:操作码,用于花费的时执行验证签名# W# {+ O: Q. Y( L4 T  i
此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:' t; O9 L: Q+ Z- O1 a
% E( }1 m. N: z
花费脚本为:9 D& t; v& F+ A# W( g
  - a, E! D1 ]* W. o
连接前向的输出,完整的堆栈:& _) {2 Q: S. x8 s* x4 X
  OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
/ x; z7 w& L# Z( K3 \) B3 i验证运算过程:/ Y" s3 E2 E3 L( G4 g( {
从前往后找OP操作码,首先遇到OP_DUP( x8 l* Z0 s3 n( J3 C  }. W
执行后堆栈为:   OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG- H  @) i3 r5 g: U: R
执行OP_HASH160
2 k' X6 \! b. \6 u  M" S1 }执行后堆栈为:    OP_EQUALVERIFY OP_CHECKSIG
0 o8 J1 l3 U9 k/ ]3 x) e执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配* Q: K: [5 F; r( A8 \, _; u! k+ |8 q$ `
执行后堆栈为:  OP_CHECKSIG; W% X) x% c" w; ]5 v9 s
到这里就与P2PKH一致了7 U* l% U; c6 ?; I6 S, U9 K7 d
执行OP_CHECKSIG,验证签名。# j! c; V1 {1 Y9 K- q) _6 y6 Z3 z
这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?
+ Z/ T  M1 A/ \! a  a1 {! _原始多重签名
5 y# ]; w' u: g4 ^9 x随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:
3 j( O6 M$ H& ^$ E1 |3 z# E* mm {pubkey}...{pubkey} n OP_CHECKMULTISIG1 ?$ w5 b/ S" _
2/2的一个原始多重签名示例:
- e; N" @" C) r$ ?& R# t04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4
6 i% ]' j0 J/ x. k- y9 c- z2 ]& W0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af
4 t5 A9 w0 G" v) c* @3 t2 OP_CHECKMULTISIG
$ j. a! j  Q$ J2 ^: K( ?3 R6 T" b验证时的堆栈内容为:4 U' c/ I% Y' ?, Y* y0 W9 w
花费脚本为:  h7 u) R) W. z$ V  h
0   
. |, k6 n9 Y" @' O) o# M+ t. p连接前向的输出,完整的堆栈:3 v5 R# r4 N1 |+ y
0    M    N . X9 l/ Z# @2 ]' |
OP_CHECKMULTISIG
9 z. t" u. p% S; t8 }2 ]OP_CHECKMULTISIG的运行过程稍微复杂一些:( c# _) w0 a, C  s3 T
弹出最后一个数,就是N,公钥总数。# F7 z% s1 Y1 c& I3 j8 v$ D# Y
执行后堆栈为:0    M   弹出N个堆栈元素,就是这N把公钥。
$ {: h! r. [$ i8 J+ c  f* g! i执行后堆栈为:0    M弹出签名数量M,即需要M个签名数量。
1 A; ]6 b. A, G, ?7 ~- e, J8 T% O执行后堆栈为:0   弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。- A5 Z, o9 d8 Z
执行后堆栈为:0弹出最后一个元素0。* _' c) [3 b( Z; J
0即OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。
6 E9 E- n  ~/ j9 \$ U5 r$ e这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。
7 b: d# c8 a$ E1 a4 v) g9 t0 M9 d" G1 ~. ]
P2SH- v! ?- @+ \: P6 ^% `) `9 l
因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:- O  f1 E3 {- f  F( h- d
OP_HASH160  OP_EQUAL8 Y1 Z8 n2 ?4 ~4 N: N
其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:
& @& N3 J9 U3 G% z# f" ZRedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG  t1 c: \1 j" b( i, }
20-byte-hash-value = RIPEMD-160(RedeemScript)7 g5 ^  d  s. a. L: Q; t/ ?
RedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。
+ \- s& j3 }6 {. E' q验证时的堆栈内容为:/ s3 x1 s: A4 X
花费脚本为:5 f( c5 R; }& g- y
OP_0   
8 \+ h* V: i" L# l5 u连接前向的输出,完整的堆栈:5 w1 r; B7 q. b3 b
OP_0     OP_HASH160  OP_EQUAL
" j0 t' S- q8 E验证过程分为两大步骤,第一步骤:
! g6 R! \! M$ ]1.执行OP_HASH160,计算HASH值: OP_HASH160% D, `0 r1 S% C$ E$ r8 f! c
执行后堆栈为:OP_0     OP_EQUAL7 X0 r" S, z/ s; L+ n" y
2.执行OP_EQUAL,验证两个哈希值是否相等
5 ^% u: F7 _% {) f# h执行后堆栈为:OP_0     X% U/ U" {, {/ j" j+ r( J. z' C
第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:
" s/ w- u& M% J6 G( {OP_0    M    N OP_CHECKMULTISIG
4 p9 q% y: y; w# c* T4 n6 o3 v当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。
% t1 q) o4 ]0 k/ [- s* ]. O4 B$ T软分叉的关键点
1 R5 k' q* ^' J2 Y! x) Q- B在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。
+ i8 o9 @/ ?& v% v8 Q; }% v7 N当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。' t/ R) k( [9 {1 L$ q
激活0 {' k/ \1 a* f9 t0 _
P2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。
5 [$ E5 D/ C/ d8 a; t$ L为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。+ n: X/ D1 I9 M6 R$ X
影响深远  N& }4 N6 e: Y8 H7 r( }: [
P2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。2 H5 H. U0 m! w, w8 b2 [
发展状况
标签: P2SH 比特币
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

哈哈笑417 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11