Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

P2SH-为比特币赋能的脚本

哈哈笑417
263 0 0
P2SH即Pay to Script Key Hash,最常见的应用是多重签名,N把公钥,M人签名时才能花费这笔交易(M2 D. @+ y3 ?9 W! a, T
P2PK4 d8 J$ R6 V" f2 z6 K
比特币早期中本聪时期是P2PK(pay to public key)这种输出形式的,最早是直接放入一把公钥进去了,还是未压缩的,感受一下:6 [+ i+ I" g# M. [! O
04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f OP_CHECKSIG
# w( z3 I) B! x0x04开头,表示是未压缩的0x04后面紧接这的是64字节是公钥内容OP_CHECKSIG:操作码,用于花费的时执行验证签名
# ]2 Q0 W/ _6 h8 ?4 D+ O那么验证时(即花费这笔输出)堆栈内容为:( q; w, V/ t* P* a  |

7 C1 Q3 M8 @9 U花费脚本为:3 v/ o9 j' o& g$ _. T: |
连接前向的输出,完整的堆栈:: T! C/ ^2 N8 ?1 F3 ]/ `; l5 x0 I
  OP_CHECKSIG& N/ C1 C- Z& W" k* @
OP_CHECKSIG执行签名检查,并返回检查结果,true则通过签名检查,可以花费。
, ~8 T5 z8 d# A6 X8 f3 L: j1 MP2PKH
+ t+ C, z* i& D& r) H4 h9 g后来发现公钥其实用哈希就可以了,没必要放出公钥内容,被称为P2PKH(pay to public key hash)。于是输出脚本演化为:
: U) P& E# L- a4 N: Y4 K5 F% LOP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG% J6 z; m2 ^, u, }( m
OP_DUP: 操作码,复制栈顶元素OP_HASH160:操作码,计算栈顶元素Hash,即计算公钥的哈希public_key_hash:公钥的哈希值,20字节OP_EQUALVERIFY:操作码,判断是否相等OP_CHECKSIG:操作码,用于花费的时执行验证签名) f/ Z& z) O* F1 v3 F
此类型输出,就是最常见的1开头的地址输出。那么验证时的堆栈内容为:
1 B+ F0 P& g4 U( ?6 V6 C% g4 W1 k% O
花费脚本为:
6 Y+ }% k. y- c) A" r+ `  6 z- s8 [" ?- o5 @* C4 m! W
连接前向的输出,完整的堆栈:
3 x) U5 M. [/ Z- {; p  OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG9 [  T! C) U  c/ h7 O
验证运算过程:
3 d# D2 U. m8 c, U& D) B, [从前往后找OP操作码,首先遇到OP_DUP
+ I/ D# i9 N+ D$ B7 M0 J9 f执行后堆栈为:   OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
6 l3 m/ b! }1 m; ], N  z! i执行OP_HASH160
1 j) d  Z1 Y7 w( K执行后堆栈为:    OP_EQUALVERIFY OP_CHECKSIG! K$ c! o0 `( d: Q4 B
执行OP_EQUALVERIFY,即验证该哈希是否与公钥匹配
( \5 N: z1 I5 }$ C+ \) m, }执行后堆栈为:  OP_CHECKSIG
: z2 f" ^6 z  {7 |/ [4 ]9 o; U* L; L到这里就与P2PKH一致了" i; Z2 l5 Z9 S# @. K. m8 a$ O
执行OP_CHECKSIG,验证签名。
4 Q2 O9 j& r3 `# Q2 w) f, ?' O3 }这个过程汪海波写过文章详细的阐述过:理解比特币脚本。p2pk改进为p2pkh后,输出长度缩小了一半多,同时隐私方面迈出了一小步:别人转给你的币在你未花费之前,别人是不知道你的公钥具体内容的。是不是很赞?
! h0 Q( M, B9 M5 V7 f原始多重签名
: G* L! G& ]+ _  G6 P$ Z0 ~随着社区快速发展,人们发现需要多重签名,很快就出现了多重签名的输出格式,Gavin在BIP11里描述了这种输出:
- C/ E; A$ F% c" {1 om {pubkey}...{pubkey} n OP_CHECKMULTISIG
3 U+ V' `7 T$ s/ R/ d" i! I  F2/2的一个原始多重签名示例:( z: C' t/ ?3 ]/ [, v1 \
04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4# E9 e7 o, t. |8 p, _9 P& j& s4 s
0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af
, |* s& _  N& B2 G8 x. x2 OP_CHECKMULTISIG5 o& c: N* x) y% b7 h
验证时的堆栈内容为:6 \3 N3 n6 y& r% ]4 o6 |4 a
花费脚本为:5 p1 ?; M  K% X9 h
0   
/ ]# L0 z9 g- k, J* r; z, K; S* M连接前向的输出,完整的堆栈:
: L' c' A, `9 r0    M    N 2 ^% R. w# {2 O' G# p
OP_CHECKMULTISIG& U. t. o# x# e+ G  ?7 B
OP_CHECKMULTISIG的运行过程稍微复杂一些:
- J0 r0 z1 F. Q# X& j9 `弹出最后一个数,就是N,公钥总数。) }) D! X  J# L* R; N) [
执行后堆栈为:0    M   弹出N个堆栈元素,就是这N把公钥。# m1 ^% v0 z0 O: x2 Z  ]4 u
执行后堆栈为:0    M弹出签名数量M,即需要M个签名数量。
* B, `1 Z# }0 x& m* E( ?, K/ g) v$ Z执行后堆栈为:0   弹出M个堆栈元素,即需要M个签名。同时对M个签名进行验证。
" ?: u: x$ [" F执行后堆栈为:0弹出最后一个元素0。
5 c. O. T1 P+ a# _0即OP_0,为啥多这么一个奇怪的元素呢,因为早期实现OP_CHECKMULTISIG时,存在一个BUG,导致必须多放入一个元素到堆栈里。为了保持兼容性,则不得不放入OP_0,否则就是造成硬分叉。! t: u% L7 j% U6 P
这种类型的输出存在时间很短,大部分人几乎不知道它的的存在,如果你哪天看见某个交易的一个输出里冒出好几个地址,那就是这种古老原始的多重签名。早期主要应用于2/3的担保交易中。
  k# L, S: [  X; u! c; B5 \
3 w0 P2 A0 \/ I6 C5 c& tP2SH
* a+ l  H1 ^1 B& s$ {因为实在是又丑又笨,Gavin很快捣鼓出一个改进版本BIP16:
2 N% h. P/ l- y/ l3 R- cOP_HASH160  OP_EQUAL  w. j, \; O  ~0 x
其实不用把公钥放在输出里了,放入其HASH值即可,与早期P2PK进化为P2PKH一样,将这些公钥连接在一起并计算出其HASH160的值:
6 L3 r2 [/ s7 A* D3 E* BRedeemScript = OP_nRequired | PUBKEY_1 | ... | PUBKEY_N | N | OP_CHECKMULTISIG
. x, M; j1 \4 `+ c/ j% q20-byte-hash-value = RIPEMD-160(RedeemScript)
6 `. v( F& b# c( b% o' fRedeemScript就是把参与的公钥以及m/n的设置值等连接在一起的内容,RedeemScript其实就是前面提到的“原始多重签名”的输出,哈希后产生的这20个字节刚好可以转为普通地址显示,就是现在最常见的3开头的p2sh多重签名地址。N最大为16把公钥。# w+ g- R* @6 e" p0 F8 z" N
验证时的堆栈内容为:
, B1 l  f9 k. }1 Z' Z花费脚本为:3 r; y% [$ n, E, {( J3 G7 s: g5 b
OP_0    5 Q$ X# X/ Z" g7 s) H. j
连接前向的输出,完整的堆栈:( W9 w6 e4 }2 \" K0 z' K: q
OP_0     OP_HASH160  OP_EQUAL
% i' c% l" x  _6 L3 A# m* x" W2 j, u验证过程分为两大步骤,第一步骤:9 s5 L) i0 x' z( [
1.执行OP_HASH160,计算HASH值: OP_HASH160: U( ~- \7 s( ?( a/ k
执行后堆栈为:OP_0     OP_EQUAL; `9 g: ^; {" s" }( p; \1 N
2.执行OP_EQUAL,验证两个哈希值是否相等
1 o& q& p$ v) Y' R  }$ B执行后堆栈为:OP_0   7 Q+ w1 S5 r# n) T1 v9 W6 E
第二步骤,将展开花费脚本中的RedeemScript展开得到子脚本,就得到与“原始多重签名”一致的堆栈数据格式,并执行类似的验证过程:# W4 p2 n+ c9 ?# T& ]
OP_0    M    N OP_CHECKMULTISIG, _) Y% |( i$ l* E
当然,RedeemScript除了放多重签名脚本外,还可以放其他任何脚本,多重签名仅仅是一种应用而已,p2sh提供了无限可能性。( O& k; D* W$ q
软分叉的关键点
% y2 I# H) l5 _2 a. S# N在第一步骤中,redeemScript是作为一个整体数据进栈的,而在第二步里,redeemScript会按照脚本进行解析得到N个栈元素,然后再依次进栈进行验证。这个过程是非常巧妙的,在第一步里,仅验证了脚本的哈希值是否一致,并没有验证签名。真正的签名信息是在第二步骤里进行验证的。因为这点,所以可以软分叉实施P2SH,老节点仅执行第一步骤,新节点执行两个步骤。
  c: u7 Z; U* ^6 P* c1 a/ g当花费过一次后,redeemScript其实就公开了,那么对于老节点来说,任何人都可以用这个公开的redeemScript花掉相同地址的币(验证哈希值)。这就是被称为任何人可以花费(Anyone can spend)的原因。不过,特性激活后,新版全节点(出块节点必然是新版)会强制执行第二步验证,永远都不会被其他人偷花。% b* ^9 h" v4 d& \! }
激活  _! a$ I8 [; R
P2SH的激活,其实算是UASF或者是GASF(Gavin Actived Soft Fork),那时也没有规范的软分叉升级方案,如BIP9。升级代码就直接发布了,并设立了激活信号日2012年04月01日(测试网是2月15日)。支持的用户直接升级代码,不支持的用户不升级代码。
4 v" R2 o! ?1 U0 d2 d% m: _  U为了防止潜在网络分叉,矿工在coinbase交易里打标识/P2SH/来标明支持P2SH。但这个仅供人为观察,并不是代码层面的。
7 L- P  a8 a( x6 B' F$ P. E  ^影响深远
  a& {5 G" i5 @. zP2SH是一个高度灵活的脚本方案,意义重大,影响深远,简直像打开了宝藏一样。其为后面的SegWit,MAST都铺平了道路。. g8 M$ `  ^2 n, w/ _8 \
发展状况
标签: P2SH 比特币
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

哈哈笑417 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11