Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
93 0 0
交易的结构, {% ?5 i  u% c( Y. G5 p+ h; c
我们先来看看在比特币中,一个交易的结构是什么样的?
  W$ O! M2 u& F( a! z- ^- Otype MsgTx struct {
! h! T8 }( J- l1 t. x5 R  ]7 G( ?  U    Version int326 ~% N2 A/ T& @
    TxIn []*TxIn
6 l, s' ]' r8 q% Q2 R    TxOut []*TxOut
7 r* K) ?: E. `    LockTime uint32
; X( Z; d' ~$ ^$ m* @}5 f) e/ c$ C- _( }9 w& P2 k5 }
type TxOut struct {( h: C6 x( \9 A: u7 O, s2 [0 b
    Value int64/ U9 [; D- w* S+ B' s
    PkScript []byte' H+ }: e8 P( i0 J; a8 r
}+ Q$ h0 F& l' g" h* _
type TxIn struct {; r8 K0 P8 D/ g  [" i5 h/ d
    PreviousOutPoint OutPoint
! g1 @4 G9 J5 K% I8 y3 f  F    SignatureScript []byte% P/ R; j- R& a/ @7 {6 d" B6 D7 x+ y
    Sequence uint32
, u( w4 s$ v6 F# I}
9 O, u5 T7 o2 ]! t5 Xtype OutPoint struct {9 M$ y' b  W4 C8 b
    Hash chainhash.Hash
5 S; S) M* V& z5 X  a: q& ?    Index uint322 f& v5 N# p: r4 N/ S! X
}$ l# d2 A1 `5 H. s4 w
我们可以看到,一个交易(MsgTx)是由多个Input和多个Output组成的,而在Input中是由指向UTXO的OutPoint,解锁脚本SignatureScript和序列Sequence组成。
, Z0 x9 u* J, S( zUTXO我们可以认为是一个KeyValue的大表,在该表中,交易的Hash和该交易中Output所在的位置索引Index就构成了UTXO的Key,而Value就是比特币Amount、锁定脚本等信息,所以在UTXO数据库中,我们通过OutPoint能够很快的找到对应的Amount和锁定脚本。
4 A, X4 }* W' `" Q5 `/ H! h在比特币中,要做一笔交易分为三个步骤:
+ f4 ?# A! ?1 O' i( ~" \0 {- h5 Q& m- T
[*]构建原始交易RawTransaction,该交易包含了输入指向的OutPoint,也包含了完整的Output,但是没有签名,也就是没有设置SignatureScript的内容。
5 x, n) B( e9 j! Z[*]用私钥对签名构建的RawTransaction进行签名,并将签名构建成完整的解锁脚本,填入对应的Input的SignatureScript字段中。$ y6 p) j$ L: r5 e# u% s
[*]将签名后的Transaction发送到P2P网络中。
: b9 ]: G( q6 h3 S0 f$ W; |
# v0 a6 S+ a- t) a( T# D构建原始交易RawTransaction
' N. l! ~6 r/ H7 O2 [$ G现在假设我有一个地址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。
2 Y+ T" U6 @& c0 W+ y8 y以下是我用Go基于btcd写的示例代码,这里我们就构建好了一个RawTransaction。
6 J6 R/ L$ [/ y9 Q7 b$ tfunc buildRawTx() *wire.MsgTx {; B( {9 k% {3 z8 R% y
//https://testnet.blockchain.info/tx/f0d9d482eb122535e32a3ae92809dd87839e63410d5fd52816fc9fc6215018cc?show_adv=true
$ z# Z- k1 f. {) d( H: d6 \7 qtx := wire.NewMsgTx(wire.TxVersion)
4 h  W3 |  P: G# Y+ ~8 e% C//https://testnet.blockchain.info/tx-index/239152566/1 0.4BTC& |' y* \# P) @  l8 L
utxoHash, _ := chainhash.NewHashFromStr("1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52")2 y7 G) R/ q$ B+ t- N4 `) C( l
point := wire.OutPoint{Hash: *utxoHash, Index: 1}
5 a% i2 O2 g: L" q2 [2 J//构建第一个Input,指向一个0.4BTC的UTXO,第二个参数是解锁脚本,现在是nil
% p: U" Q1 e& m    tx.AddTxIn(wire.NewTxIn(&point, nil, nil))
2 x; G, T# E- [' k2 |  h/ M//https://testnet.blockchain.info/tx-index/239157459/1 1.1BTC
0 ]% g2 \6 N- s1 U7 [. `2 qutxoHash2, _ := chainhash.NewHashFromStr("24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66")9 I* ?! N7 i7 A& E. t0 s7 I
point2 := wire.OutPoint{Hash: *utxoHash2, Index: 1}6 v' U# c1 \" ^  ]% Z* j# P
//构建第二个Input,指向一个1.1BTC的UTXO,第二个参数是解锁脚本,现在是nil: E  |7 }- n8 t8 D3 t
    tx.AddTxIn(wire.NewTxIn(&point2, nil, nil))
3 T  V7 d- k2 K! m4 v1 x- M: u//找零的地址(这里是16进制形式,变成Base58格式就是mx3KrUjRzzqYTcsyyvWBiHBncLrrTPXnkV)" r4 T( A& H6 p0 @" Z) D
pubKeyHash, _ := hex.DecodeString("b5407cec767317d41442aab35bad2712626e17ca"). A2 `! t( @+ j* X5 M* |
lock, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).) O, g9 p1 [9 ]9 \( B6 r
AddData(pubKeyHash).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).
! a( S: x; t' y; B. zScript()& t$ v5 h# t+ n' U% Y( K: o
//构建第一个Output,是找零0.2991024 BTC4 \& W- ~+ j6 m, }0 Z
    tx.AddTxOut(wire.NewTxOut(29910240, lock))
9 l: P4 P+ i8 r  L; X& R0 c' F8 v//支付给了某个地址,仍然是16进制形式,Base58形式是:mxqnGTekzKqnMqNFHKYi8FhV99WcvQGhfH。
8 Y4 x1 [5 B0 i2 Z$ C* R9 _7 m) p: `  }* JpubKeyHash2, _ := hex.DecodeString("be09abcbfda1f2c26899f062979ab0708731235a")& R' O, u; o, D  V/ D1 k5 N
lock2, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).! a( M' n+ r: \+ {$ Y2 }' q
AddData(pubKeyHash2).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).3 y  G" r) d7 m/ I! O
Script()2 x; e6 Q2 C8 U( z1 n/ r* M# e
//构建第二个Output,支付1.2 BTC出去
: b  z; T% D: D* t; R1 g+ r. ]    tx.AddTxOut(wire.NewTxOut(120000000, lock2))
9 u+ s' G) [& A  ~8 treturn tx
$ E8 o  _) D% P7 K. G: P}
1 A) t) k( k6 c+ V! P+ T6 w' N% @交易的签名过程
4 }2 U6 q7 V' `, P  |现在我们知道私钥,需要对该交易进行签名,因为有2个Input,所以我们要签名2次,每个签名的原理是一样的,我就以第一个Input为例来说明吧。# k$ a" F# j* y; l5 t
在比特币中,对一笔交易的签名流程是这样的:6 m  }* f4 y. o2 M, l
1.查找该笔交易对应的UTXO. A7 l& ^! z" F, v
2.获得该UTXO对应的锁定脚本5 D4 k* U: r& k, }  w6 c$ y/ u
3.复制该交易对象,并在复制副本中将该Input的解锁脚本字段的值设置为对应的锁定脚本. G+ g7 C3 g0 E1 @' r
4.清除其他Input的解锁脚本字段+ L' L1 y! [# j6 }1 N9 d1 W  O
5.对这个改造后的交易对象计算Hash3 L! j9 d% {! h# T+ I0 T: n
6.使用私钥对Hash进行签名。. \, \- l+ \  q; s( v
用表格的形式可以更容易表达:# m. Y7 {+ _/ S/ c8 b
这是原始未签名的交易RawTransaction,主要是第二列和第三列:6 ~/ Y& X' }6 q1 D' x3 E7 R+ n
8 t/ U! {. ?4 W: Q
UTXO        Input        Output
0 e/ G$ C% ^6 q5 g6 ATxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
' ^0 |" n& Z6 e# aOutIndex:1,' w0 l* Z3 h) @+ f
Amount:0.4BTC,PkScript:2 s* P4 g( \8 Y, X& {. b
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
* i# P$ y( |& V1 G4 d) CPreviousOutPoint={
% S) R* w/ K" t" X  r5 [" i& u1 TTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
6 [5 @" w! G; L, x" _) d1 y6 lOutIndex:1}
5 A6 P. K+ C9 \" h0 x" _SignatureScript =NULL,Sequence =0xFFFFFFFF
9 x- ~- }; K" }% }Value=29910240
' Q$ t1 O1 S3 Y) L! G& u( `PkScript=9 h: @" w$ p6 b3 _1 _, y+ E
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
/ ~8 |, q7 m# I/ a9 q  `& hTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,5 Q4 R4 C" g6 e6 y% @9 `
OutIndex:1,2 O2 T. s3 [8 ^+ I
Amount:1.1BTC,PkScript:
. g: v+ G9 y( JOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
! Y, L' q1 L  gPreviousOutPoint={9 W6 L5 [* \+ h$ n. f, K
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66," q' _4 [. r3 x
OutIndex:1}* ]4 [: H, |- t/ s7 M  L3 l
SignatureScript =NULL,Sequence =0xFFFFFFFF4 k+ }5 \, K3 o/ Q, q- }9 o3 w$ a
Value=120000000. K+ s+ l/ m( d0 T5 n; ?- ?( p5 x
PkScript=, s7 ~1 x% L. y) v
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG
! e4 \% i( |9 V; J5 o- S接下来我们要对第一个Input签名,于是我们需要将交易复制一个副本,并改为:( o& H4 N! q4 M
Input        Output
2 ^; i0 k1 ~  f& WPreviousOutPoint={4 {$ f, k* K7 f
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
  D' S" @8 q$ ^, tOutIndex:1}- c1 q8 V( e! n/ v% a7 Y4 u
SignatureScript =
% o+ Z# g4 B0 y* T, ~OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG* l( S  P) i7 Y; |1 a. M4 a
,Sequence =0xFFFFFFFF
! _- w1 i. U# r4 Q% j7 s3 o; Z$ tValue=299102403 [1 Y; m0 I# P# w2 n0 r& x
PkScript=$ K  n5 }3 l/ c' t
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG0 _( |0 ^0 y  w
PreviousOutPoint={, r% n/ B  K( P( t/ ?1 @* c
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
! k  T& f& ~. ]0 J; x- B7 G3 x/ {OutIndex:1}0 J5 x6 R; s- n$ u
SignatureScript =NULL,Sequence =0xFFFFFFFF2 h8 L3 q7 Y3 H1 z# Z( h
Value=120000000
; _/ ], D% _! f: a1 ZPkScript=6 f  z; i, L  J- V- _) K7 ?
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG7 i0 I1 C5 D2 w
接下来对这个交易计算Hash,然后进行签名。得到签名结果:3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501 而我们知道这个地址的公钥是:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb# s3 s5 L" _- n) B% [9 W( Q- c
所以签完名后,我们的交易变成:
6 K* N9 y- \  kInput        Output7 T6 \; |) Q7 t; U: `
PreviousOutPoint={
* p/ {9 F" \# DTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
: k5 K7 @; H" `0 j' ]2 z% dOutIndex:1}5 ^1 @; l* P$ ~
SignatureScript =
2 X7 K' y; L# I) l: R- NPUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd2
; p# d" [) M1 _$ A% ~! p) I" u9 V; b2ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]
5 b. q& t1 |6 F. I0 I5 ^5 s,Sequence =0xFFFFFFFF
4 a( {( L* E8 M7 E# T4 \3 MValue=29910240# C& }9 d  m8 {
PkScript=
4 x! \3 o/ r! S! gOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
3 b( V0 C7 H, R: n4 O' e: r; ZPreviousOutPoint={/ |0 p# `" Y8 G; F, J" q
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
3 o, q1 D3 H9 |* jOutIndex:1}
: S1 d6 A6 a$ \0 [SignatureScript =NULL,Sequence =0xFFFFFFFF
7 K& k7 _* J  i. _9 m- |Value=120000000' v. T# J/ {9 f% X" K8 C2 K6 l( W
PkScript=" ]  b& C0 ^7 q/ i% _8 ]; |# i. Z$ }
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG$ v! j6 Z2 S: V
这才只是完成了第一个Input的签名,接下来我们再对第二个Input进行签名,同样的道理,我们需要制造一个交易的副本,然后把第一个Input的SignatureScript清空,然后给第二个Input的SignatureScript赋值:" R5 K+ \' L# a; H
Input        Output0 _) g$ @, e/ O, _6 A/ O2 J7 s  l
PreviousOutPoint={
/ G. d" P) L- b, LTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,4 h+ t6 ]& G$ Z8 q& E$ _7 {
OutIndex:1}
/ g5 T6 a& R0 f7 Z4 h: O* `: RSignatureScript =NULL,Sequence =0xFFFFFFFF/ A5 K' w8 d9 {8 }/ [
Value=29910240+ R3 M* A  }( O0 o  _3 m" V& A
PkScript=' v+ |2 T2 w; `& \* o) d/ k8 M# u
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
0 {* X$ K) o  k, L2 i' P! R. [9 sPreviousOutPoint={
8 [7 ?6 W- y' d8 b6 c8 w- ITxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
* {# K1 R0 S& IOutIndex:1}
. H4 S' Z2 P" J' z$ T* @3 W3 YSignatureScript =OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
* ?/ L( p# R/ F& H5 w,Sequence =0xFFFFFFFF
' D% \( D! R, L+ L6 fValue=120000000
; d' d( o* r/ f, [7 wPkScript=. A% v  s( n( J" }
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG+ O! I0 ]& e8 f; X
显然这个副本与第一个签名时的数据是不一样的,所以签名结果也不一样,最终签名结果为:30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501,当然我们因为是同一个地址,所以公钥是一样的:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb
! f: N0 g# A& n2 Q我们把这个签名和公钥再放回原始交易中,就变成我们需要的完整签名的交易:9 e3 F$ A' B# R' L! n
Input        Output
. V2 ]  y, T7 j1 fPreviousOutPoint={
/ ]: J. W1 Q$ ~' E4 MTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,* ~. D* ~0 j- ]6 F( A
OutIndex:1}
9 H! e: ?; |; t  lSignatureScript =PUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]3 {1 Y0 |+ _$ F9 ^5 i$ _% ~( l5 b! o
,Sequence =0xFFFFFFFF- r* b$ t% D7 X! j- R6 _7 U5 A" f
Value=29910240
  Q, t7 ~8 n- ^  C3 ]PkScript=
5 |- }, T8 y$ c$ \) g% hOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
0 p$ ^$ R( B' ]: H. C; U* LPreviousOutPoint={' B. d+ M# W* ~2 F4 {
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
5 \, D4 G( L* \* T# u( q6 z2 EOutIndex:1}
. k: y. d1 {: m3 ]4 a0 j4 hSignatureScript =PUSHDATA(71)[30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]
8 P# j( x0 B( l) S( b' @% \,Sequence =0xFFFFFFFF/ `/ n" Q7 j1 H' C( P8 Q" c
这就是一个真实完整的交易了,接下来就可以通过P2P网络发送该交易,并最终被矿工打包确认。
# p+ U1 O+ v8 B6 m5 B7 i9 g4 b& s总结+ J0 f( G7 S1 i3 q# U6 h# }
实际上在比特币的源码中比我上面说的还要复杂一些,还涉及到这个hash是对整个交易进行SigHashAll还是SigHashSingle或者SigHashNone,这些都是很特殊的情况,一般的比特币钱包也不支持,具体可以参加精通比特币书中的介绍:6.5.3签名哈希类型( SIGHASH)1 M3 k  V+ ~  m! ^5 s" N1 q/ C
普通来说,我们要对一笔交易进行签名或者验签,就是把当前Input中的解锁脚本替换成锁定脚本,而其他Input的解锁脚本情况,然后计算Hash和签名!
2 H: c, G3 T- E0 v其实我还是有点不明白,为什么比特币中不直接对没有任何解锁脚本的RawTransaction进行签名呢?而是非要加上锁定脚本来签名?不知道这里面有什么更深的考虑。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14