Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
164 0 0
交易的结构+ V3 m) U9 i. q6 i- k" m. O' E& J
我们先来看看在比特币中,一个交易的结构是什么样的?" ?& i9 }) h) s
type MsgTx struct {
! h: Q" i$ s( r6 D- L" v    Version int325 Q$ d. \2 k. x9 Q# l
    TxIn []*TxIn  @. o# X# x1 G1 E, n1 _
    TxOut []*TxOut
+ ~1 c. x& U) W& @, l4 ]: j    LockTime uint32& a5 c8 Y2 Z, s' ?. r2 h
}
4 h( M! I: d% Ptype TxOut struct {* W0 `" P$ f) Q/ U/ J9 @: L4 k, z0 E5 o
    Value int64
2 B3 p! c1 w: ]  z6 i    PkScript []byte
# t# |2 [' @  \! W* U+ |+ U' u}# |% d+ Z, U& {$ z4 Q3 G& B
type TxIn struct {
: S  `$ h6 N' ~1 r    PreviousOutPoint OutPoint
  u. |1 s0 h- `" ~+ Z    SignatureScript []byte) ~% d* G& h, n( L( x
    Sequence uint328 a3 ~  M* [4 ?4 z- Z' a- H- _
}5 D" h0 c) T  N! J+ e! X
type OutPoint struct {5 p% W/ m6 j' C; `
    Hash chainhash.Hash
1 I3 X0 D; Y& |, F" v# l5 I    Index uint32, z7 ^. m( E. \9 x
}: t! J& d: Y) U4 Z/ D
我们可以看到,一个交易(MsgTx)是由多个Input和多个Output组成的,而在Input中是由指向UTXO的OutPoint,解锁脚本SignatureScript和序列Sequence组成。
+ r, W6 d8 t! nUTXO我们可以认为是一个KeyValue的大表,在该表中,交易的Hash和该交易中Output所在的位置索引Index就构成了UTXO的Key,而Value就是比特币Amount、锁定脚本等信息,所以在UTXO数据库中,我们通过OutPoint能够很快的找到对应的Amount和锁定脚本。
' }( w* C: z, V  i* ~在比特币中,要做一笔交易分为三个步骤:0 E: `. M. n6 ?( r; k, Q3 T2 ~1 `& I

: o5 R5 V  \4 H& D- T  D# Z7 x$ S[*]构建原始交易RawTransaction,该交易包含了输入指向的OutPoint,也包含了完整的Output,但是没有签名,也就是没有设置SignatureScript的内容。" s0 u- v8 T% T( A) Z  [! D
[*]用私钥对签名构建的RawTransaction进行签名,并将签名构建成完整的解锁脚本,填入对应的Input的SignatureScript字段中。
, Z" |: e1 C: Y$ L. ^6 ~[*]将签名后的Transaction发送到P2P网络中。
% H# v$ A5 ^  b; s% d) M+ k' l6 Q$ ]5 t4 e
构建原始交易RawTransaction9 T5 P! @& d1 `; S
现在假设我有一个地址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。8 R3 r: ^2 M% \. Y1 [3 A1 W
以下是我用Go基于btcd写的示例代码,这里我们就构建好了一个RawTransaction。2 w: Y$ N3 Y8 T# i9 H) M$ f  Q
func buildRawTx() *wire.MsgTx {+ o  I: R4 X, U
//https://testnet.blockchain.info/tx/f0d9d482eb122535e32a3ae92809dd87839e63410d5fd52816fc9fc6215018cc?show_adv=true
% d8 b$ I6 I2 btx := wire.NewMsgTx(wire.TxVersion)
! g! n1 H$ t  `: g//https://testnet.blockchain.info/tx-index/239152566/1 0.4BTC( X. N# `2 r8 G4 N( G
utxoHash, _ := chainhash.NewHashFromStr("1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52")7 g4 s5 g. ^& I% a7 R
point := wire.OutPoint{Hash: *utxoHash, Index: 1}
  C. q- H' Z9 F2 b5 ~& u//构建第一个Input,指向一个0.4BTC的UTXO,第二个参数是解锁脚本,现在是nil( G! Q3 X$ r$ o. z
    tx.AddTxIn(wire.NewTxIn(&point, nil, nil))( G* E! H% T8 `2 w
//https://testnet.blockchain.info/tx-index/239157459/1 1.1BTC
/ p# I0 l6 M* X2 CutxoHash2, _ := chainhash.NewHashFromStr("24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66")  Y9 \3 f: l/ Z# B' _( g# I
point2 := wire.OutPoint{Hash: *utxoHash2, Index: 1}/ L/ A% x9 d& r' X* v" _: |0 p! U! |
//构建第二个Input,指向一个1.1BTC的UTXO,第二个参数是解锁脚本,现在是nil9 f6 g- ~$ ?, B3 h2 Y- U
    tx.AddTxIn(wire.NewTxIn(&point2, nil, nil))6 E0 R+ C; H0 }& F% P
//找零的地址(这里是16进制形式,变成Base58格式就是mx3KrUjRzzqYTcsyyvWBiHBncLrrTPXnkV)+ H( e: k( |' K
pubKeyHash, _ := hex.DecodeString("b5407cec767317d41442aab35bad2712626e17ca")
3 o3 G+ E: O" [1 Y" }+ Rlock, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).
' G) {: t# W- `3 |2 yAddData(pubKeyHash).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).
. s  S" r: ^4 L# q6 M, L7 n1 AScript()
4 F# |* h' J7 i( C, Q//构建第一个Output,是找零0.2991024 BTC7 |  u6 t! x; l. H  g- Y) q
    tx.AddTxOut(wire.NewTxOut(29910240, lock))
0 _& k; |  y: J+ z$ Q( d//支付给了某个地址,仍然是16进制形式,Base58形式是:mxqnGTekzKqnMqNFHKYi8FhV99WcvQGhfH。
0 u! N" W$ V- l% CpubKeyHash2, _ := hex.DecodeString("be09abcbfda1f2c26899f062979ab0708731235a")) P- l3 _$ m4 b! r0 g
lock2, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).5 q% ]. k' J* s' B3 z  U
AddData(pubKeyHash2).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).+ ?% `3 o' k) x7 R3 o6 I
Script()
1 N4 V; h/ |8 Z3 k- _//构建第二个Output,支付1.2 BTC出去
3 p' x: S% l) U, e9 N+ C    tx.AddTxOut(wire.NewTxOut(120000000, lock2))
; z0 S3 ^0 t- b4 ?9 Wreturn tx
7 T' u. F7 c6 ]1 n1 E}6 u6 i  G+ g$ K8 F8 p
交易的签名过程
$ G( m% H/ c8 h* e2 _' o现在我们知道私钥,需要对该交易进行签名,因为有2个Input,所以我们要签名2次,每个签名的原理是一样的,我就以第一个Input为例来说明吧。
$ t) o; A8 v% Z! l3 m1 T, m在比特币中,对一笔交易的签名流程是这样的:
3 N4 Q  a/ r6 k8 `$ m7 Q1.查找该笔交易对应的UTXO
! O; X$ q8 g  B8 ^2.获得该UTXO对应的锁定脚本
* D& C+ g! _1 `, f* A+ d. a' v3.复制该交易对象,并在复制副本中将该Input的解锁脚本字段的值设置为对应的锁定脚本
+ C! m0 z3 P5 G+ a! E# q+ P" c4.清除其他Input的解锁脚本字段
! `+ ?* N& @2 m, v0 p3 y3 q+ Q5.对这个改造后的交易对象计算Hash
7 i$ B% t: q$ f6.使用私钥对Hash进行签名。
" r% Q( n' C2 T用表格的形式可以更容易表达:5 L. t3 C. s% M# x
这是原始未签名的交易RawTransaction,主要是第二列和第三列:
- }% U, W) B* m! }* J; u+ h* ~& ]1 w" Q1 f& u. g
UTXO        Input        Output0 Y) {0 `7 _7 d$ R% j) B
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
, o0 y( s+ _6 F7 r$ w7 a* wOutIndex:1,
7 n2 a6 n3 s8 Q8 E& u" rAmount:0.4BTC,PkScript:' o5 P. H; y) C, C0 \4 n" u( q
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
4 V0 O3 h( L6 IPreviousOutPoint={
1 l9 v% q) D9 o; h1 k7 @TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,. u( z! }: [; I/ T
OutIndex:1}' [7 o! c" Z' g3 v+ B
SignatureScript =NULL,Sequence =0xFFFFFFFF8 b# ^4 n; b  A" W
Value=29910240
' Y# Z% Y1 x# A; ^( pPkScript=( n' t9 g  R9 H: l5 {' d1 ^: c
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG6 ?0 _0 y2 H5 Z+ a: C* a
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,$ ~* t9 e( [1 ]4 k( i* B4 a2 Y
OutIndex:1,, a5 J, K5 T. V- a: @$ S
Amount:1.1BTC,PkScript:/ P4 l# @9 X) n) K4 h$ j
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
6 k; j' F) B& r  V$ _8 VPreviousOutPoint={% O- h/ S2 ^+ f( w- w: i1 M8 Y
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,0 Z+ @+ a9 Z" z, z3 e4 Z4 h- r
OutIndex:1}
- k- Q- l0 j! g1 BSignatureScript =NULL,Sequence =0xFFFFFFFF& V1 H5 v  o7 U0 @0 I
Value=120000000
. s9 f9 v* g8 k' v6 I/ Z% QPkScript=" k) n4 _/ i! V
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG; z/ b0 C2 {' h0 m
接下来我们要对第一个Input签名,于是我们需要将交易复制一个副本,并改为:
! I2 V6 Y3 Y4 v% g$ Y1 HInput        Output. ~! d) X4 `$ e6 F+ B
PreviousOutPoint={: N% `% r# j6 X- e( \1 Q
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
2 \& t# U2 z0 @OutIndex:1}
9 w6 C3 v9 c+ h9 ^9 C7 l( N2 QSignatureScript =
( v, w( [4 F1 X' J* O$ f* ]OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG' j' Y0 c, n6 p& j2 O% @5 N) v1 f
,Sequence =0xFFFFFFFF0 _% o! h4 T1 L: ]$ C
Value=29910240
+ t5 c+ O2 e% r: L; s9 {PkScript=
2 ^0 H1 K3 k2 q4 t) I4 [OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
& y5 z# m) x( W7 A. O' rPreviousOutPoint={
) [" U1 F( F) m: U6 BTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
& X1 g: c; `( q! d0 F& c( b2 }OutIndex:1}
: i  z; S" W* N9 F& h# b! ESignatureScript =NULL,Sequence =0xFFFFFFFF
; m" W9 R5 J0 ?5 A0 oValue=120000000
: B8 [6 u  w, a( Z/ T7 ^( I. EPkScript=2 N/ S% E4 F0 a0 W6 w8 B! A9 T
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG
: N; N% a) @* q5 a' U" g接下来对这个交易计算Hash,然后进行签名。得到签名结果:3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501 而我们知道这个地址的公钥是:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb
! \  k7 \- j: f0 a$ }7 M& u& v所以签完名后,我们的交易变成:
1 l; g4 |5 {$ `% N7 cInput        Output
9 _6 z: c' R1 s4 wPreviousOutPoint={2 n" D- V0 }/ y$ {! W7 i
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
# |3 f5 Q7 K) `- x$ U  c$ E9 o! u' w2 S5 ?OutIndex:1}
2 x$ t; }2 s. Q7 d( L0 VSignatureScript =* Y0 m7 X. ^$ h! q
PUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd2
9 k' }" e% H$ c# F# A2ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]/ ~  I  Q- O5 z4 \5 m$ K
,Sequence =0xFFFFFFFF& X. j- P  m& z$ C: @# W9 f6 I
Value=29910240
" Q3 H7 Y; A0 K( h+ Z8 T8 s7 GPkScript=- y: O7 A, q, S% N7 y1 h
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG; `! N4 Z; A# E* m, G: y' K- @
PreviousOutPoint={
2 |) k$ A0 S% J# N2 e) oTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,6 u/ [" W; T( M- Q0 a! E4 A( H+ a
OutIndex:1}+ u4 s- B! j) Q( Z2 X
SignatureScript =NULL,Sequence =0xFFFFFFFF; g& I( c, f3 X; _( B2 ~5 f
Value=1200000004 ^5 [5 F/ z7 W* s  n% m
PkScript=0 L4 b* e" N0 Y, c. y$ }
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG+ _" V+ f# ?( I( c5 W6 {2 F! o4 V$ D
这才只是完成了第一个Input的签名,接下来我们再对第二个Input进行签名,同样的道理,我们需要制造一个交易的副本,然后把第一个Input的SignatureScript清空,然后给第二个Input的SignatureScript赋值:7 E9 `7 i% R. B* m: c9 G+ Z0 `
Input        Output$ Z" L2 X  O  R9 Y$ ]
PreviousOutPoint={3 G( i' S- R& g9 R+ ^# j9 J8 B
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,. C: w$ ^1 ], w+ q* C, v& P
OutIndex:1}
% r' k( B; ]# O5 B8 g2 ~: RSignatureScript =NULL,Sequence =0xFFFFFFFF- p# g4 ~4 m0 F: r% E- G
Value=29910240
0 u2 u6 }( E, z6 m/ f; S4 j; @8 q! DPkScript=
9 M% J+ N: j! v: w6 g; [/ \7 SOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
) h: m% Y5 I. h5 C& s, h* H# Q" ^, }PreviousOutPoint={9 p2 u! i( L( x% ~& p: V" z7 G5 g
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,$ Q4 a( a4 ?# r( Y" l$ }% r8 G
OutIndex:1}+ c4 O( @& q% N
SignatureScript =OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
1 v7 `- L( u9 w% R4 E3 u) r  v( l,Sequence =0xFFFFFFFF
* Y8 z6 b# ^- `5 _Value=120000000
7 J8 D. |' i2 U: qPkScript=
( W1 j* T3 d+ @+ f7 K5 p5 OOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG
. U1 U  M2 Y' V, w显然这个副本与第一个签名时的数据是不一样的,所以签名结果也不一样,最终签名结果为:30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501,当然我们因为是同一个地址,所以公钥是一样的:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb
7 q) H) H* |2 e& O1 D& n  I& s我们把这个签名和公钥再放回原始交易中,就变成我们需要的完整签名的交易:
7 j1 U. n4 K" c* u9 {Input        Output  H% K2 V( C! t( z8 \
PreviousOutPoint={
1 a0 G0 l' f8 `TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,( ?! a1 X' r# L; c2 i8 e, D
OutIndex:1}) G+ @; M( [1 G$ L0 o3 Y, W
SignatureScript =PUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]8 ^& Q1 J( `) c1 K* ^: D
,Sequence =0xFFFFFFFF
0 k6 V0 K2 P5 h0 uValue=29910240" r" T' v5 I+ q4 S# l% s7 B
PkScript=, G6 `$ z9 j, E; n; K9 [
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG9 Y4 q9 C* a  m7 b* r
PreviousOutPoint={* y2 ~  l) i! P, S  M
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,' _7 N8 e! \! M! G6 L
OutIndex:1}
9 e* b/ d; p& {5 W* V3 gSignatureScript =PUSHDATA(71)[30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]
5 v9 y, @1 b8 W" t% S,Sequence =0xFFFFFFFF2 ]) ^" G7 y4 k6 C& o0 ]
这就是一个真实完整的交易了,接下来就可以通过P2P网络发送该交易,并最终被矿工打包确认。
  U, e4 [8 [7 V9 q1 Q总结* U) k& c& g8 }) _
实际上在比特币的源码中比我上面说的还要复杂一些,还涉及到这个hash是对整个交易进行SigHashAll还是SigHashSingle或者SigHashNone,这些都是很特殊的情况,一般的比特币钱包也不支持,具体可以参加精通比特币书中的介绍:6.5.3签名哈希类型( SIGHASH)
, e% S3 W$ q  F3 @. \7 @, G' q普通来说,我们要对一笔交易进行签名或者验签,就是把当前Input中的解锁脚本替换成锁定脚本,而其他Input的解锁脚本情况,然后计算Hash和签名!
  y0 ]: v2 S0 \+ n+ r其实我还是有点不明白,为什么比特币中不直接对没有任何解锁脚本的RawTransaction进行签名呢?而是非要加上锁定脚本来签名?不知道这里面有什么更深的考虑。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14