Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

比特币中对交易进行签名的详细过程

V刘晨曦
89 0 0
交易的结构
6 B" Q& p. S& T我们先来看看在比特币中,一个交易的结构是什么样的?
5 w& d, P  }# V; ^' {9 M* ttype MsgTx struct {3 P% e9 U( J( [
    Version int32. K* ?7 B' n. _* s, A
    TxIn []*TxIn
, Z2 F0 S- ^2 z9 @, p    TxOut []*TxOut
  R/ g- A4 g  Z! k! T0 K    LockTime uint32
- O# B2 K2 n! S' N$ L! T}% ]0 ^. k8 S; O! r/ @+ Z. w. ?
type TxOut struct {1 O3 d) z; U1 S7 T- e; y; `8 V4 b
    Value int64
1 l& N' n1 G. o: L( u    PkScript []byte
% {$ v0 w8 h/ r1 r: D7 j6 ~}
4 w" c" M: s' t: otype TxIn struct {+ V/ _7 A) g: l) R/ i
    PreviousOutPoint OutPoint: X% e8 n* m" g
    SignatureScript []byte& ~8 e3 K3 J6 h) ?6 l4 N1 F
    Sequence uint32
5 l" _2 X% i) O2 W, y6 ?! b  N9 |9 n}
- @$ h% X% M1 G/ |! Ptype OutPoint struct {: H8 s+ b8 O4 T- A( z0 Z
    Hash chainhash.Hash6 W3 O/ i2 m1 r* R0 S" b0 d' o
    Index uint32
2 L# ?# |4 M4 U2 C8 W& y}
2 J3 [* Y3 D" x5 W+ e我们可以看到,一个交易(MsgTx)是由多个Input和多个Output组成的,而在Input中是由指向UTXO的OutPoint,解锁脚本SignatureScript和序列Sequence组成。" V5 c6 q3 M1 S4 f: g
UTXO我们可以认为是一个KeyValue的大表,在该表中,交易的Hash和该交易中Output所在的位置索引Index就构成了UTXO的Key,而Value就是比特币Amount、锁定脚本等信息,所以在UTXO数据库中,我们通过OutPoint能够很快的找到对应的Amount和锁定脚本。3 a. \4 q3 q! ~, }3 c4 p
在比特币中,要做一笔交易分为三个步骤:& i8 K; ]" w6 U3 H! s' `7 e5 ]

' A5 A3 D$ \7 f- z[*]构建原始交易RawTransaction,该交易包含了输入指向的OutPoint,也包含了完整的Output,但是没有签名,也就是没有设置SignatureScript的内容。
( m9 S2 o7 R7 A# ~3 }) S) n[*]用私钥对签名构建的RawTransaction进行签名,并将签名构建成完整的解锁脚本,填入对应的Input的SignatureScript字段中。
( d4 I8 V0 B! ?5 [; x8 [. l[*]将签名后的Transaction发送到P2P网络中。! M: d* s- p( g5 _# N2 J; ]
" y' N+ _  S) `+ ~  A
构建原始交易RawTransaction  a1 c- T- m( ~5 T/ I) S- m
现在假设我有一个地址mx3KrUjRzzqYTcsyyvWBiHBncLrrTPXnkV(这是一个测试网地址),该地址收到了两笔转账,一笔0.4BTC(https://testnet.blockchain.info/tx-index/239152566/1 ),另一笔1.1BTC(https://testnet.blockchain.info/tx-index/239157459/1 ),这两笔收入都是在其交易Output的第二条,也就是Index=1(Index从0开始算)。现在我们想要做一笔1.2BTC的转账,然后给一定的手续费后,找零到原地址,所以我们会构建一笔交易,该交易有2Input和2Output。- |: R  j) o0 J6 f
以下是我用Go基于btcd写的示例代码,这里我们就构建好了一个RawTransaction。
) \) ~. |0 ~% Y9 P4 S. kfunc buildRawTx() *wire.MsgTx {
. \8 N: r. q+ m6 v//https://testnet.blockchain.info/tx/f0d9d482eb122535e32a3ae92809dd87839e63410d5fd52816fc9fc6215018cc?show_adv=true, j6 Y+ T( }8 @( r
tx := wire.NewMsgTx(wire.TxVersion)
# n/ x/ V( Z: a3 f2 a: {2 L5 f//https://testnet.blockchain.info/tx-index/239152566/1 0.4BTC6 ?8 C& a' \* a2 a9 p: P
utxoHash, _ := chainhash.NewHashFromStr("1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52")
4 ]% n5 J# L; Lpoint := wire.OutPoint{Hash: *utxoHash, Index: 1}+ b4 O% T) C' ^9 ?/ U8 J" d
//构建第一个Input,指向一个0.4BTC的UTXO,第二个参数是解锁脚本,现在是nil7 }0 X- b9 D1 C) k
    tx.AddTxIn(wire.NewTxIn(&point, nil, nil))+ X; C0 E4 |/ s- L! V) M
//https://testnet.blockchain.info/tx-index/239157459/1 1.1BTC  S: l0 h2 j/ t
utxoHash2, _ := chainhash.NewHashFromStr("24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66")
4 T0 r& t' S/ s0 fpoint2 := wire.OutPoint{Hash: *utxoHash2, Index: 1}/ v) F/ P0 l/ \: _  d' @/ f
//构建第二个Input,指向一个1.1BTC的UTXO,第二个参数是解锁脚本,现在是nil8 R. o0 o  R6 D2 V6 ]
    tx.AddTxIn(wire.NewTxIn(&point2, nil, nil))4 A% [3 u0 w, R: D7 W0 s
//找零的地址(这里是16进制形式,变成Base58格式就是mx3KrUjRzzqYTcsyyvWBiHBncLrrTPXnkV)
/ p# j  N( Q1 j7 W" u. XpubKeyHash, _ := hex.DecodeString("b5407cec767317d41442aab35bad2712626e17ca")
" D: s3 F# M1 |8 s/ t8 G' ^lock, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).
3 n3 k- _  Y0 P. A9 R8 i! xAddData(pubKeyHash).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).
0 C* M) a7 i6 N5 W$ E: zScript()
- b/ T8 w1 U  [+ n' r//构建第一个Output,是找零0.2991024 BTC  O5 e2 V8 n$ F) z, Y
    tx.AddTxOut(wire.NewTxOut(29910240, lock))( S6 Y: D9 w2 c# p$ }; `( Y
//支付给了某个地址,仍然是16进制形式,Base58形式是:mxqnGTekzKqnMqNFHKYi8FhV99WcvQGhfH。
7 {, Z5 Z! J* E- t/ VpubKeyHash2, _ := hex.DecodeString("be09abcbfda1f2c26899f062979ab0708731235a")
' i# O2 F& g6 M* i- y1 D/ l/ U4 alock2, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).
( h  K( K  G7 S: [AddData(pubKeyHash2).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).
7 S! F, a/ ]0 F9 f0 FScript()+ f9 k8 z4 j. O5 W" s7 k* w
//构建第二个Output,支付1.2 BTC出去
$ c+ u* o9 f" c0 `) _( o    tx.AddTxOut(wire.NewTxOut(120000000, lock2))
8 {  x! c  ~: Z, p  E% C. Q3 ireturn tx
/ ~% b6 r0 A: l/ L2 `}
: R% f3 J% x; o4 x# T. U交易的签名过程
! U! |( X1 k9 _  i4 m/ F/ w( @5 g现在我们知道私钥,需要对该交易进行签名,因为有2个Input,所以我们要签名2次,每个签名的原理是一样的,我就以第一个Input为例来说明吧。
/ d/ |) t+ Z/ G# m2 D4 J  F在比特币中,对一笔交易的签名流程是这样的:. \1 c, ^. z: l3 I( G, q
1.查找该笔交易对应的UTXO4 v# [* X, F& s5 B- j5 K/ d1 m2 W, l" o
2.获得该UTXO对应的锁定脚本" x; p) Q! m3 j
3.复制该交易对象,并在复制副本中将该Input的解锁脚本字段的值设置为对应的锁定脚本
$ X/ ]4 Q. B. l" I9 m+ k+ [4.清除其他Input的解锁脚本字段
3 n- r. D. `# c) q: {6 ?5 L; ~5.对这个改造后的交易对象计算Hash( M# q3 G. M0 a! W) l; G# C
6.使用私钥对Hash进行签名。
2 {  Y  @/ K, A5 q用表格的形式可以更容易表达:# Q9 s. ~7 Y% S  M* l
这是原始未签名的交易RawTransaction,主要是第二列和第三列:. Y# l/ D- Z8 J, k

. J) W" V& F: j" M: VUTXO        Input        Output
( U% A% B9 t( ~, B- h$ ^, i4 pTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,$ h  m& {/ N* \- ^4 G) h+ x& _
OutIndex:1,
- ~( V$ N/ k8 Q" o* p( A9 hAmount:0.4BTC,PkScript:6 ?7 Y6 V# ?  c  ~4 C, Y
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
  C/ [, b; C1 W  D4 w9 U/ R* i% qPreviousOutPoint={) C' l3 |9 ~3 z
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,2 A% y2 D9 z8 P) i' D! |  ^
OutIndex:1}  V3 `$ u* `& Q8 n
SignatureScript =NULL,Sequence =0xFFFFFFFF7 C; N$ F' j5 ^9 \+ ~" y$ \
Value=29910240* U% e* B; T$ t1 Q
PkScript=  P8 W, g! D3 _( _/ b1 z
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG1 ~# r1 Y5 O# r
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,8 n/ {4 T" J2 j" S+ q, E* Q8 c! [
OutIndex:1,( b- `( m7 Q1 Y: A/ H7 f. u
Amount:1.1BTC,PkScript:8 z/ u* j  q2 R! B5 F7 K3 I; m. k) K9 P
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG- g! M3 j& Y3 o/ k! l( |0 r
PreviousOutPoint={  `2 Y/ x# h: i$ b6 d
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
$ [3 N6 @" Q, m' [OutIndex:1}
2 z8 J9 Z. I2 T- D! U3 R9 O; _SignatureScript =NULL,Sequence =0xFFFFFFFF: X- I6 s: O8 r- U' o. U$ J
Value=120000000
8 e6 C: e, V7 M: E, ~0 zPkScript=0 |: ^1 ?0 r1 C; _
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG
' F0 m5 _6 k: u& ]% J" F  N# f接下来我们要对第一个Input签名,于是我们需要将交易复制一个副本,并改为:
3 Q* S* m: [! I* U; IInput        Output
! P  |( p( G. ]7 y+ V8 J! sPreviousOutPoint={; P  L% i  r0 O- H6 O) [$ `) Z6 w3 A  M8 W
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,% n+ \6 B6 W: |3 D7 w( a
OutIndex:1}
& f, Q- l3 _. {. |) CSignatureScript =
8 J& @4 J  Z; @8 tOP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
1 P9 K( `: b' b* [,Sequence =0xFFFFFFFF, P/ [) v1 J. i5 J* ]( _* r; Y
Value=29910240
5 s1 U! |" t7 [PkScript=
+ o6 N$ K  ?8 k) i$ WOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
; P* [; T# a! Q- fPreviousOutPoint={3 z, H+ P! p, ?" s8 X: E2 z/ r
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
+ d+ U4 I" M. d; {OutIndex:1}0 U; R$ S0 e% P2 U
SignatureScript =NULL,Sequence =0xFFFFFFFF' w; x' T( s( g2 i# p+ d
Value=120000000
% _+ d2 ~1 ~" ^9 @3 {1 ?% q8 APkScript=
# V3 Y6 A$ g% H! C& C) d+ Y: z+ ]OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG" m# X, M/ C8 N( s6 |7 f4 Z
接下来对这个交易计算Hash,然后进行签名。得到签名结果:3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501 而我们知道这个地址的公钥是:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb
1 p' ^! \7 E3 l1 \0 k. v所以签完名后,我们的交易变成:% U  N- ]* X( L2 [# p- h$ A& N
Input        Output! f0 A( r3 l2 t4 l6 n+ V1 U9 w
PreviousOutPoint={' _) Z) N* x  z
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,% P# [0 l# r5 J2 p/ E. }& Z" T
OutIndex:1}9 F' k0 l* |8 I: Z& T1 A
SignatureScript =
  I' `8 u1 l) @  ], e& nPUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd2+ b; m! r$ [4 s
2ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]' y) I) o& u( x/ h1 R' M* Y6 B
,Sequence =0xFFFFFFFF
% x3 v6 p' w6 s( ?8 T  oValue=29910240# s: z) e0 v& O! C& b2 r
PkScript=  f) c5 s$ N' ?* P
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG0 Z6 y, W6 W$ d( ?3 f! ~) G
PreviousOutPoint={3 p6 k/ T: a( e1 G/ Q# r) O
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,3 |, x/ M2 F" v6 S% c
OutIndex:1}
. m) h- L8 h1 f3 x$ hSignatureScript =NULL,Sequence =0xFFFFFFFF
  h$ p. m; e' D  u9 hValue=120000000
. i4 Y9 ~: q1 j* A3 IPkScript=
2 Z$ K9 G* i! ZOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG5 C/ c. E5 G/ w5 C2 H
这才只是完成了第一个Input的签名,接下来我们再对第二个Input进行签名,同样的道理,我们需要制造一个交易的副本,然后把第一个Input的SignatureScript清空,然后给第二个Input的SignatureScript赋值:
$ H$ t. a, x- bInput        Output
9 i; C# c) P7 `! OPreviousOutPoint={& E4 m. a3 Y/ f5 k
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
6 `) h5 Z: {5 R5 @, F6 J2 ~( YOutIndex:1}
7 {7 Z- X2 Z- k  t, ]SignatureScript =NULL,Sequence =0xFFFFFFFF$ s! }. X5 R+ j9 b! ?3 y8 h) [
Value=29910240/ b+ S' K4 g# R$ I: ]/ z6 e
PkScript=' _# M# m0 P9 y
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
  V! \9 ~+ Z( X- p( H" I6 qPreviousOutPoint={: [4 T* S' l6 t0 T  \2 |: a
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,0 x: v: J% |; n0 D/ `
OutIndex:1}  v) F! M9 r( c
SignatureScript =OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
/ i4 E# p, X$ b4 }1 u,Sequence =0xFFFFFFFF
# z) U: K1 l$ X$ k: I0 h2 |) w1 x6 b% qValue=120000000
( s& `) z- ~+ s  T: dPkScript=
& ^. M) L1 S  ZOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG
9 l2 w9 `4 w! H* F3 D# H) ?4 G3 c显然这个副本与第一个签名时的数据是不一样的,所以签名结果也不一样,最终签名结果为:30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501,当然我们因为是同一个地址,所以公钥是一样的:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb
% d) m2 X$ o* T: y* @: D" o我们把这个签名和公钥再放回原始交易中,就变成我们需要的完整签名的交易:. J& o5 I( G& N3 d, M. r
Input        Output
* L% S6 m+ y/ d3 B% @1 tPreviousOutPoint={
/ k* j! g- y# ?& r& r$ `3 `TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
) e5 {, N! y, r" g" j9 C$ ?OutIndex:1}4 F" x" W  h; j+ S* f/ H
SignatureScript =PUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]  f1 y3 o! s7 D
,Sequence =0xFFFFFFFF
6 O. ~! G6 [& Z; v: z! YValue=29910240
' ^- [' L; B! J: L! C. N  @8 qPkScript=' j% F4 A0 N4 K6 P5 u# ~  |6 L) t, e
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG6 N) p! o/ a. D
PreviousOutPoint={9 ]; S8 u6 g* {+ w  p5 |4 q0 u
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
" I4 P. Y: q* i# e# jOutIndex:1}
3 T/ b% I) y( y3 e- P/ q5 mSignatureScript =PUSHDATA(71)[30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]. p" a, X- N6 g7 x6 {
,Sequence =0xFFFFFFFF
6 D$ U4 C: x6 u  P" M这就是一个真实完整的交易了,接下来就可以通过P2P网络发送该交易,并最终被矿工打包确认。5 ?( h: v' b- Y6 Q6 R
总结* {  _, u3 D& T2 T$ B: r& F  H
实际上在比特币的源码中比我上面说的还要复杂一些,还涉及到这个hash是对整个交易进行SigHashAll还是SigHashSingle或者SigHashNone,这些都是很特殊的情况,一般的比特币钱包也不支持,具体可以参加精通比特币书中的介绍:6.5.3签名哈希类型( SIGHASH)' O/ H+ ^1 z3 a4 e8 q
普通来说,我们要对一笔交易进行签名或者验签,就是把当前Input中的解锁脚本替换成锁定脚本,而其他Input的解锁脚本情况,然后计算Hash和签名!* D. R$ `* f7 u# h( k
其实我还是有点不明白,为什么比特币中不直接对没有任何解锁脚本的RawTransaction进行签名呢?而是非要加上锁定脚本来签名?不知道这里面有什么更深的考虑。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14