Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
137 0 0
交易的结构
/ B% P( E2 k2 ~8 Z我们先来看看在比特币中,一个交易的结构是什么样的?
2 ?1 L$ y2 r" C' V- etype MsgTx struct {! w, t3 b9 E% a2 \
    Version int32! }3 |# ]. Y/ h. |4 }
    TxIn []*TxIn4 M) B5 `& a" j6 h4 G
    TxOut []*TxOut
9 `# {$ G8 ~5 }; j  x& h    LockTime uint321 Y4 M, |: i4 h% d, {* |7 t
}
6 L) F4 L# S9 |5 v9 D( R& m; w5 z) btype TxOut struct {" U( V# w  l% l9 B" O% R
    Value int64
$ G9 _, T0 i  W( b8 L    PkScript []byte+ _2 w4 [; K( b0 T
}% @$ _+ s9 n: C. e
type TxIn struct {3 s* f" B* B2 G6 B4 v; P
    PreviousOutPoint OutPoint/ l, E  ^8 B# y+ |/ W
    SignatureScript []byte; O/ Q& Y) k* f- h" e
    Sequence uint32
, T  o8 @) \& m5 ]& \" V; R# K}5 j3 n$ e0 }% I6 j- V$ \4 M
type OutPoint struct {% q5 O( }4 h. `. N" f% U% D( ]
    Hash chainhash.Hash4 U# d/ g6 n7 O
    Index uint32
5 W9 L5 e( l& F1 A, b}# q# H- t2 G( }' l
我们可以看到,一个交易(MsgTx)是由多个Input和多个Output组成的,而在Input中是由指向UTXO的OutPoint,解锁脚本SignatureScript和序列Sequence组成。: H, N  A* l6 P! m5 W
UTXO我们可以认为是一个KeyValue的大表,在该表中,交易的Hash和该交易中Output所在的位置索引Index就构成了UTXO的Key,而Value就是比特币Amount、锁定脚本等信息,所以在UTXO数据库中,我们通过OutPoint能够很快的找到对应的Amount和锁定脚本。
' L  U) H0 E% G) j1 }$ o2 o3 x在比特币中,要做一笔交易分为三个步骤:
9 v! z8 V2 g' U1 j4 W( Y2 U1 f# c: k' c" L
[*]构建原始交易RawTransaction,该交易包含了输入指向的OutPoint,也包含了完整的Output,但是没有签名,也就是没有设置SignatureScript的内容。
: D. Q. n  U$ P( u[*]用私钥对签名构建的RawTransaction进行签名,并将签名构建成完整的解锁脚本,填入对应的Input的SignatureScript字段中。- ~; B- B* `6 R5 G1 V
[*]将签名后的Transaction发送到P2P网络中。
! q4 N5 [" ~2 ]& g& `$ e1 _# p' b; e& F/ P, j! ~# S
构建原始交易RawTransaction8 S+ A  c, f, p% U, b
现在假设我有一个地址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。. E4 u' v8 C; M5 z6 y! t
以下是我用Go基于btcd写的示例代码,这里我们就构建好了一个RawTransaction。
9 D3 n" q! ]- j! |func buildRawTx() *wire.MsgTx {' g: u& d5 n" `9 e' o+ c& @% T( }
//https://testnet.blockchain.info/tx/f0d9d482eb122535e32a3ae92809dd87839e63410d5fd52816fc9fc6215018cc?show_adv=true
3 E; Z# @5 L: \tx := wire.NewMsgTx(wire.TxVersion)/ y& Q: |  @' x# h. y
//https://testnet.blockchain.info/tx-index/239152566/1 0.4BTC
, F# Y. g$ t5 |  o2 x# H$ O- outxoHash, _ := chainhash.NewHashFromStr("1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52"). w; ^0 q- d1 u( |: q% q
point := wire.OutPoint{Hash: *utxoHash, Index: 1}
; o% m# H, a! v3 `//构建第一个Input,指向一个0.4BTC的UTXO,第二个参数是解锁脚本,现在是nil
8 l% s2 _. m% z9 C0 Q* x; D    tx.AddTxIn(wire.NewTxIn(&point, nil, nil))
$ ^' v) `- t7 s3 u/ S! d& X//https://testnet.blockchain.info/tx-index/239157459/1 1.1BTC# r1 q) K2 l5 D+ i
utxoHash2, _ := chainhash.NewHashFromStr("24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66")2 E. P; j2 R. L$ I7 R
point2 := wire.OutPoint{Hash: *utxoHash2, Index: 1}5 H4 W0 @; G# \0 c2 r9 s. D5 s
//构建第二个Input,指向一个1.1BTC的UTXO,第二个参数是解锁脚本,现在是nil3 m( w6 z- f* v% D
    tx.AddTxIn(wire.NewTxIn(&point2, nil, nil))- [" P5 g# A4 k  ~& q2 H* M( o0 [
//找零的地址(这里是16进制形式,变成Base58格式就是mx3KrUjRzzqYTcsyyvWBiHBncLrrTPXnkV)
, P1 F& ]% k  w& H) \) DpubKeyHash, _ := hex.DecodeString("b5407cec767317d41442aab35bad2712626e17ca")4 {( r3 Y& m3 n$ j' k8 t! d. E
lock, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).  _& \, |% F7 b# [5 C, o1 |2 x% j
AddData(pubKeyHash).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).+ N- e, Y- r* n
Script()
- ^( w+ m5 ?- r  T$ S//构建第一个Output,是找零0.2991024 BTC' _3 y0 v- E1 A7 ~) n+ I! j, t
    tx.AddTxOut(wire.NewTxOut(29910240, lock))' O' [$ {2 Y3 m# J$ k% ~) p/ L
//支付给了某个地址,仍然是16进制形式,Base58形式是:mxqnGTekzKqnMqNFHKYi8FhV99WcvQGhfH。5 @" U+ ?5 w2 s3 q  N! I
pubKeyHash2, _ := hex.DecodeString("be09abcbfda1f2c26899f062979ab0708731235a")# c6 w8 D& r  o; i% b! G: v
lock2, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).
5 b7 R/ p7 [+ d# s/ W( l6 E: O' j6 PAddData(pubKeyHash2).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).
) b2 e6 O( M: S4 ?Script()  H% c: J8 S, {; R$ Z
//构建第二个Output,支付1.2 BTC出去
% ?" j4 W6 f1 g. b4 m. o7 o    tx.AddTxOut(wire.NewTxOut(120000000, lock2)): k9 x, t2 T- n7 z& i( S
return tx) ]+ k4 S4 w0 \
}
7 g; y& |& n" j3 _3 j8 ^5 R交易的签名过程
' p2 c0 q+ M# v' C1 h5 q现在我们知道私钥,需要对该交易进行签名,因为有2个Input,所以我们要签名2次,每个签名的原理是一样的,我就以第一个Input为例来说明吧。
: i) B& F" o# ~" X; V& G4 @在比特币中,对一笔交易的签名流程是这样的:
3 x2 u; R8 s9 w7 x1.查找该笔交易对应的UTXO1 e9 |8 ]" O. z, n
2.获得该UTXO对应的锁定脚本) _" q& A6 h$ W/ w& x3 L  N
3.复制该交易对象,并在复制副本中将该Input的解锁脚本字段的值设置为对应的锁定脚本/ G& v5 U* w  U
4.清除其他Input的解锁脚本字段
* I2 E3 m3 N" m( P" p5.对这个改造后的交易对象计算Hash
" F  q" D! ?8 q5 R( v# x- E6.使用私钥对Hash进行签名。
( |$ T! F7 @" K用表格的形式可以更容易表达:& o. Y; X0 N0 ~3 a% v
这是原始未签名的交易RawTransaction,主要是第二列和第三列:
. H& _. H. P9 H+ U4 f1 ]7 R
8 B  l  e8 g* X' B5 s3 sUTXO        Input        Output' {+ [8 u/ P! b9 D% n5 s( K
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
, J  L' {- U3 r6 F" ?OutIndex:1,
9 w& U' o: D! I4 r/ p# @Amount:0.4BTC,PkScript:, M5 N7 G  M( h) W$ F9 a
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
, l# V0 n" P  a4 HPreviousOutPoint={
/ y7 J: n3 x) t+ l' h$ kTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
' J0 p4 k# K& N% yOutIndex:1}
& y+ G( r7 W- e; H7 SSignatureScript =NULL,Sequence =0xFFFFFFFF$ L/ Y2 X8 u. w: T' `( Z: R
Value=29910240
5 K: a# t. g" v' G, ]% JPkScript=9 b7 T- ]+ M2 \, U2 g
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
! c$ e# r3 C* N. i' C9 G- ~TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,4 @5 o# H, Z1 _$ T1 H2 F
OutIndex:1,& J/ w8 ^4 z& t% Y  D
Amount:1.1BTC,PkScript:) C2 `6 I8 t3 c
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
$ g$ F. [4 ]2 z3 x. OPreviousOutPoint={
$ h- c& N  ]$ }& W( F% sTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66," C- y$ R! m$ U6 o; f' S( @
OutIndex:1}! s4 h5 D( e# P6 m7 n1 h
SignatureScript =NULL,Sequence =0xFFFFFFFF# Q( l) ]/ @8 w8 S
Value=120000000
& ]" q2 d( D; {- @; VPkScript=
# H9 X/ j4 H. a% @2 E' t" WOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG$ a3 F( F- u& U+ Q( }2 q( S) t
接下来我们要对第一个Input签名,于是我们需要将交易复制一个副本,并改为:
$ ~' E! h5 {& Z' B7 [# HInput        Output- O" w. X& a0 w1 a! B+ F
PreviousOutPoint={% z4 |: `* L! ^7 v' D6 u
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,' D" T$ F0 b+ |. R7 e
OutIndex:1}
% h4 ^8 [& h: s4 R1 H9 vSignatureScript =5 b  n2 U- c$ h4 W9 S& h% s' @% a
OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
6 J; A- T0 X, O- ?4 E3 \" t' K,Sequence =0xFFFFFFFF8 a2 c0 P) Z+ Q. L4 V1 y
Value=29910240
1 n& q; j) t9 ePkScript=4 M6 D5 |7 ~/ w* w' ]
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG" V; M, K3 ]) s9 J5 W! S# T
PreviousOutPoint={5 I% H: O) g; K7 _' y4 O0 J( X
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
- }. ~5 w$ I% P) N% zOutIndex:1}
( T, T- A9 |7 {  T4 _SignatureScript =NULL,Sequence =0xFFFFFFFF: v/ k) }1 _. p6 Z8 ?: F5 k' t  l
Value=1200000002 `, C$ K/ D, k8 A' [
PkScript=3 U. ?6 p/ i+ g! Q6 k
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG1 ^$ \6 N& g) ]+ C3 E5 u* _) I
接下来对这个交易计算Hash,然后进行签名。得到签名结果:3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501 而我们知道这个地址的公钥是:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb. O& |& C: m) s- m. A
所以签完名后,我们的交易变成:
# w! J, ^( W( M- G& O, R; tInput        Output
$ i5 ~/ G- K& wPreviousOutPoint={; K6 t$ @: @4 q0 ]
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,& _8 n! x, b: R( K3 H2 T
OutIndex:1}
8 y7 Z  }% f9 Y% K$ O' HSignatureScript =& r5 h' U. d5 G
PUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd20 k8 ]0 Z+ M1 ?
2ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]1 P. G9 V* e+ J  Q3 M2 s9 ~
,Sequence =0xFFFFFFFF  g/ ~; b  _6 n
Value=299102404 J6 s+ ^! k/ k
PkScript=4 c% A' L1 H9 w
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG" b" d' F' ~$ x* U/ w
PreviousOutPoint={
4 N1 i2 z7 ^& ^/ k1 ATxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
# m& u/ W2 N8 p) _$ {- C; i# KOutIndex:1}5 P! L; W. ?/ Z% p
SignatureScript =NULL,Sequence =0xFFFFFFFF
2 k) W2 |1 l3 MValue=120000000  b/ j4 s! W$ R, `
PkScript=' j. X7 X( }& U- Z4 [
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG
9 l, O/ y* M+ g$ T这才只是完成了第一个Input的签名,接下来我们再对第二个Input进行签名,同样的道理,我们需要制造一个交易的副本,然后把第一个Input的SignatureScript清空,然后给第二个Input的SignatureScript赋值:
0 R. D/ ]% r, M( }Input        Output( u1 c: E0 W$ f4 M# w/ L* @  S0 Y
PreviousOutPoint={  D0 E8 _8 h8 B) D) Z; @
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
2 E9 h  n' t1 J* D3 @OutIndex:1}
( A' A% T2 [. A  B2 s' ?SignatureScript =NULL,Sequence =0xFFFFFFFF* M# c$ L0 Y( B7 O
Value=29910240
+ M6 G% U6 `; k; H1 fPkScript=
. h0 }# ]4 f, o/ pOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG* {# U- n, t" q# Y3 G  f" r2 o
PreviousOutPoint={
) N* q4 L$ i0 U: }% WTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,& Y; q/ e( N$ O  e+ i- a
OutIndex:1}
3 s8 g$ Q$ ^; y- FSignatureScript =OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
0 E1 ~2 H* r: {,Sequence =0xFFFFFFFF
" y* ?1 y, e$ ^3 @Value=120000000: N7 p" s+ z* ?8 R  X& i
PkScript=
: F3 b" C: g* w- u7 Z6 g& \+ ]( qOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG( N# v1 |2 P& }. {2 o0 W8 l
显然这个副本与第一个签名时的数据是不一样的,所以签名结果也不一样,最终签名结果为:30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501,当然我们因为是同一个地址,所以公钥是一样的:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb0 ?5 q; @8 k, E4 ~& o* P
我们把这个签名和公钥再放回原始交易中,就变成我们需要的完整签名的交易:
& A# l- k& d( {Input        Output
5 T1 v4 A* w4 SPreviousOutPoint={/ O% Z% j; j* l6 q* s
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,. }- H/ G$ Y& Y# G. m3 k8 w
OutIndex:1}
2 S* g  X* _2 w" ASignatureScript =PUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]1 Y  }' \( T0 m6 d( M
,Sequence =0xFFFFFFFF
7 o, T9 ^; J: i5 BValue=29910240! x( \* r+ A/ B+ Z* f$ v! G- n3 ^" y& S
PkScript=$ n2 t/ a2 V4 U5 ]5 n7 `
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
& {% X5 I0 y. RPreviousOutPoint={
% S* K9 P! x+ ?9 X8 J& ^TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
$ p; }5 F6 Q' c/ g: b5 U( ]( Y$ w  qOutIndex:1}
$ Z! t  B8 b& `0 KSignatureScript =PUSHDATA(71)[30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]% V9 Q, C6 A' g# _9 c
,Sequence =0xFFFFFFFF
5 W6 q# g" J3 U; C& A9 _0 C$ o这就是一个真实完整的交易了,接下来就可以通过P2P网络发送该交易,并最终被矿工打包确认。
. ]6 O" _7 D. }7 s/ Q总结
! B. Q; ]4 A  X8 u+ H/ n8 o# o实际上在比特币的源码中比我上面说的还要复杂一些,还涉及到这个hash是对整个交易进行SigHashAll还是SigHashSingle或者SigHashNone,这些都是很特殊的情况,一般的比特币钱包也不支持,具体可以参加精通比特币书中的介绍:6.5.3签名哈希类型( SIGHASH)
8 V" S& R  `1 ?- M/ U1 a普通来说,我们要对一笔交易进行签名或者验签,就是把当前Input中的解锁脚本替换成锁定脚本,而其他Input的解锁脚本情况,然后计算Hash和签名!
% o( a/ a! r1 Q. [+ t其实我还是有点不明白,为什么比特币中不直接对没有任何解锁脚本的RawTransaction进行签名呢?而是非要加上锁定脚本来签名?不知道这里面有什么更深的考虑。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14