Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
120 0 0
交易的结构
7 O* M' {" J' d: q我们先来看看在比特币中,一个交易的结构是什么样的?; d. c# d" F2 \% l( R
type MsgTx struct {+ l; W. b8 R4 X
    Version int32
8 F2 B) f% z2 ~2 y: Q: K    TxIn []*TxIn5 E0 m( ]+ {$ x6 [9 z6 o6 K9 H
    TxOut []*TxOut
, I3 t$ C  L* W' E    LockTime uint32
3 V" Y: I  C) l8 r! O' j}
) c& D; C- Z1 D% D5 o3 n4 d  L" V; X! jtype TxOut struct {2 A& T* c% b4 G5 m4 @2 \9 Q
    Value int64
2 k; U" J) R0 X& f    PkScript []byte2 R( M* f7 g) x& g! X& S
}
( N- A2 e& A# U" U; `+ f9 wtype TxIn struct {
1 V1 e. L! d+ [: K" ]7 X2 Y/ K    PreviousOutPoint OutPoint
9 Y6 N/ b: A3 T1 n( X, ]( g- Q    SignatureScript []byte
# }% s1 E% B& `: z8 ]2 f# f6 I2 e    Sequence uint32! i- ^4 V8 x) N! W1 F, w% b3 F
}8 t4 N( V- k& K, y
type OutPoint struct {
  B* C! P# @) s; _; H6 d    Hash chainhash.Hash
$ H& o+ a! m( c4 \0 i    Index uint320 ~; p" K0 O3 V
}6 m! S* A: w  ~3 Z  e
我们可以看到,一个交易(MsgTx)是由多个Input和多个Output组成的,而在Input中是由指向UTXO的OutPoint,解锁脚本SignatureScript和序列Sequence组成。
& K" Q0 t, ]3 U$ H' c3 P# I5 zUTXO我们可以认为是一个KeyValue的大表,在该表中,交易的Hash和该交易中Output所在的位置索引Index就构成了UTXO的Key,而Value就是比特币Amount、锁定脚本等信息,所以在UTXO数据库中,我们通过OutPoint能够很快的找到对应的Amount和锁定脚本。2 ?/ J6 F- O3 L9 C5 Q# q
在比特币中,要做一笔交易分为三个步骤:- i3 k" e0 M* O8 @! G

0 r: ^2 B1 m2 U6 Y" x3 H+ X[*]构建原始交易RawTransaction,该交易包含了输入指向的OutPoint,也包含了完整的Output,但是没有签名,也就是没有设置SignatureScript的内容。
: h7 b1 A0 E3 ]1 |- J4 b0 V[*]用私钥对签名构建的RawTransaction进行签名,并将签名构建成完整的解锁脚本,填入对应的Input的SignatureScript字段中。
* X5 x( j# ]4 {4 o# |- c+ [[*]将签名后的Transaction发送到P2P网络中。5 S' J9 I; W0 K+ v$ z$ c0 r

9 ]% P+ C# F0 `) D& x构建原始交易RawTransaction6 O! l& \  W3 {% I+ 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。; A5 z" ]& ]. J) Z4 _
以下是我用Go基于btcd写的示例代码,这里我们就构建好了一个RawTransaction。& ^, ]4 ?; E- z$ i) U
func buildRawTx() *wire.MsgTx {, X$ t. J9 E7 `& X. J
//https://testnet.blockchain.info/tx/f0d9d482eb122535e32a3ae92809dd87839e63410d5fd52816fc9fc6215018cc?show_adv=true" I( F- X; R  ~& M' Z: a- x
tx := wire.NewMsgTx(wire.TxVersion)
. d; d) d# H# N5 B( B+ H+ v//https://testnet.blockchain.info/tx-index/239152566/1 0.4BTC
" h6 p6 W, K; f$ eutxoHash, _ := chainhash.NewHashFromStr("1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52")
, }3 N8 T6 h, U6 v, g$ k+ q: Upoint := wire.OutPoint{Hash: *utxoHash, Index: 1}
0 e( L% _, N5 S' i" I, {//构建第一个Input,指向一个0.4BTC的UTXO,第二个参数是解锁脚本,现在是nil
9 C2 k  l+ ~5 V- F2 O! a% G    tx.AddTxIn(wire.NewTxIn(&point, nil, nil))
& m- y$ R2 J% x//https://testnet.blockchain.info/tx-index/239157459/1 1.1BTC
  E1 g6 h- U" B9 R4 a" ?: _8 \" CutxoHash2, _ := chainhash.NewHashFromStr("24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66")
- D5 Y: }) t# q* P/ |# H3 B  Kpoint2 := wire.OutPoint{Hash: *utxoHash2, Index: 1}
3 \( K0 [9 K: \9 s5 S//构建第二个Input,指向一个1.1BTC的UTXO,第二个参数是解锁脚本,现在是nil9 g+ @: ]2 f8 v  S, B0 _
    tx.AddTxIn(wire.NewTxIn(&point2, nil, nil))
( w' Z7 M' z3 ]( i2 b8 z//找零的地址(这里是16进制形式,变成Base58格式就是mx3KrUjRzzqYTcsyyvWBiHBncLrrTPXnkV)
; B  m7 @0 h( z  l! @* apubKeyHash, _ := hex.DecodeString("b5407cec767317d41442aab35bad2712626e17ca")
3 y; [6 e: |& W- }- Plock, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).
* Z1 A" g7 `  z' bAddData(pubKeyHash).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).9 k1 f9 b* I! {
Script()
7 [  e+ D1 |; L5 i2 h9 D( [! i' o5 \//构建第一个Output,是找零0.2991024 BTC
8 Z8 N, @! |! S& R8 Y% K    tx.AddTxOut(wire.NewTxOut(29910240, lock))
3 L. p0 L' e  U- @) g2 R//支付给了某个地址,仍然是16进制形式,Base58形式是:mxqnGTekzKqnMqNFHKYi8FhV99WcvQGhfH。
& @" {" I7 P# f5 m- V0 K6 qpubKeyHash2, _ := hex.DecodeString("be09abcbfda1f2c26899f062979ab0708731235a")
, T- Z5 t/ _: }, l+ h+ Ilock2, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).
, Q6 x# i( G8 |! D6 rAddData(pubKeyHash2).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG)./ i  l1 \9 S+ d
Script()" G% T" A! u0 P& r: ?- N' d, W
//构建第二个Output,支付1.2 BTC出去
* [( ^% y1 u! B' r    tx.AddTxOut(wire.NewTxOut(120000000, lock2))9 D  H, h6 D: [; f
return tx# i" q4 B  O: M" h
}9 R0 s7 ?2 ?. ]6 g7 }' v) f
交易的签名过程
0 U5 p0 X+ l' N现在我们知道私钥,需要对该交易进行签名,因为有2个Input,所以我们要签名2次,每个签名的原理是一样的,我就以第一个Input为例来说明吧。
) @2 ~4 S0 r$ ^( x7 K9 h' G在比特币中,对一笔交易的签名流程是这样的:
+ }! {9 J% H9 H' \% @( z- g1 j1.查找该笔交易对应的UTXO+ D) R/ N! x. N' l+ n+ \& {/ O
2.获得该UTXO对应的锁定脚本1 r6 i: T- f" f6 L) _' t. Y4 V3 y
3.复制该交易对象,并在复制副本中将该Input的解锁脚本字段的值设置为对应的锁定脚本9 _& k6 g2 Q0 K$ X2 {. P1 j  H
4.清除其他Input的解锁脚本字段) z, E" ~+ Y, G$ J* q
5.对这个改造后的交易对象计算Hash
. i# ~! k3 g2 s6.使用私钥对Hash进行签名。  n; @% }! l- a5 @
用表格的形式可以更容易表达:
* r, _' O; ]$ [' {2 v5 U这是原始未签名的交易RawTransaction,主要是第二列和第三列:
% x6 }4 ?" g: @8 \1 X# ^1 V2 ^
# q3 J# }5 b  K( J0 xUTXO        Input        Output5 b$ M+ d# k4 h  ?) z
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,8 k; q5 e4 U. J; N  `* e$ Y* Q5 A5 Q
OutIndex:1,2 \+ @2 M! }0 T3 c
Amount:0.4BTC,PkScript:
# L& q" r& ]& t' T6 L: o" F4 UOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG% [7 {* `3 [5 z& w& D
PreviousOutPoint={$ d6 i7 e: A8 n& a+ n; g' N  s; o
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
* S) \9 _* D4 C8 q8 MOutIndex:1}$ U* i" B% N' V4 v  A
SignatureScript =NULL,Sequence =0xFFFFFFFF* P; l% K, z  C1 G. v& q' M
Value=29910240- [- ?' k- G* S, _; V
PkScript=
/ I" n- p4 `" o* o- yOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
0 x7 f5 f% f" G. b( a7 rTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
2 A" g8 E3 n! u6 jOutIndex:1,' m# K6 b6 H$ p, z; J
Amount:1.1BTC,PkScript:) v9 i8 N* V& f+ \. s2 C
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG- v3 N( r# ?( }0 a) _
PreviousOutPoint={
$ L: m# h* J  i0 c( {TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,( h; O3 B5 @6 ~" r5 F
OutIndex:1}
" m2 `0 y2 P$ a; @: }' {SignatureScript =NULL,Sequence =0xFFFFFFFF
' t9 @8 @# f9 k  R! O* C% w  QValue=120000000
4 f; w$ F/ m/ c8 S8 cPkScript=
1 i5 D4 B8 _. z) c; R. D2 j' w+ jOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG
/ t7 E+ `  J8 f: i5 d  B' @接下来我们要对第一个Input签名,于是我们需要将交易复制一个副本,并改为:8 |/ M4 ~  f, g4 i
Input        Output
. f- T- b$ c2 ~PreviousOutPoint={
* a; z1 `, C- K7 D: ITxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,- N! G$ X$ S0 d* F6 H, o+ W
OutIndex:1}
; Y0 V6 T; b% q# PSignatureScript =  D/ L1 ~1 C) ]2 P, N2 D
OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG4 q. c( p0 h1 N  Z' i
,Sequence =0xFFFFFFFF8 W9 X3 }9 a! \. ^6 n5 B
Value=29910240
# J( X6 @/ e& o0 n  KPkScript=* M% J  A0 O' n/ }3 v  D* ]
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
2 y* V* N9 e* o7 W  a% KPreviousOutPoint={
- d- Q+ M4 d4 {: I. c% J# @8 iTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,; i1 p3 Y' m% |  k* F' b8 J) _
OutIndex:1}! c/ f9 D) g- a1 Y# y
SignatureScript =NULL,Sequence =0xFFFFFFFF9 J1 u% _0 }% B1 U) ]. g/ L
Value=120000000
! _7 d7 ?7 m6 _2 nPkScript=  i4 \. e4 t8 p
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG- c( B, G9 M9 H  e
接下来对这个交易计算Hash,然后进行签名。得到签名结果:3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501 而我们知道这个地址的公钥是:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb
, h; m  @( o8 Z0 K+ U) _, j1 P7 K所以签完名后,我们的交易变成:$ H  F$ E& y* b7 T7 T
Input        Output' n3 s! b. E1 j+ ?$ W9 t+ ~  V6 E
PreviousOutPoint={
& B8 V, d* a/ r3 c6 cTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
/ ^" p( H$ F0 t+ nOutIndex:1}$ B2 b7 r  i/ ]3 E9 q
SignatureScript =
; S' c: r- B" M, v& k; T0 yPUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd2
3 q3 g+ w6 h; ]' H. O$ h% {% S- d2ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]& x4 |8 I% n) X! `2 d- W9 T, P
,Sequence =0xFFFFFFFF
8 f; s( H0 L. x- W+ R9 t5 `Value=29910240
7 w& _9 ~# p8 M2 w2 }9 rPkScript=
% y1 Y7 r5 }2 ~2 rOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG! t5 t. Y8 Z- b- y6 `- ~
PreviousOutPoint={. a2 `- [5 k  [" C
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
, G9 I4 ?+ j1 b" bOutIndex:1}  ^( M; n+ d- q, K) l
SignatureScript =NULL,Sequence =0xFFFFFFFF8 s  P+ B# n" o% W
Value=120000000' T# a0 v/ W* u2 x/ n' ^+ S
PkScript=( K) v/ K& M. N# c' z$ n. y* y! Y5 z  l
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG/ @7 y1 x( g9 ]- ?1 K1 K! [5 D8 ^
这才只是完成了第一个Input的签名,接下来我们再对第二个Input进行签名,同样的道理,我们需要制造一个交易的副本,然后把第一个Input的SignatureScript清空,然后给第二个Input的SignatureScript赋值:6 [8 [: y4 c5 ~
Input        Output
. z2 c" r( y+ ?: o6 I; ZPreviousOutPoint={' d- I* J% R: S2 x
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,  @0 @2 }" o& m- t
OutIndex:1}0 o+ t0 g& v9 m0 X0 @! ]3 N- A
SignatureScript =NULL,Sequence =0xFFFFFFFF/ ]0 V! X' e: w' B
Value=29910240' D0 }5 g4 N! ]$ |) W% s/ R$ ^
PkScript=
" J( A4 T2 n( P/ N& cOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
, \/ }: K6 E9 {3 p4 l6 jPreviousOutPoint={$ [7 t. Y7 A! L( L& q0 c7 O2 ^
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,; k9 ]5 G. D; y/ `1 E4 c( W! [! v
OutIndex:1}
+ i9 n( n& f& ?1 {: y2 GSignatureScript =OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG; E) h' J! k' _
,Sequence =0xFFFFFFFF
) f! S  u) c6 w7 cValue=120000000
  U, y$ l% Y. Q! X( n% Y# f& YPkScript=
8 G3 h+ C4 _+ u, Y  }7 MOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG/ d2 ~( w. Q# ]- P6 Y
显然这个副本与第一个签名时的数据是不一样的,所以签名结果也不一样,最终签名结果为:30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501,当然我们因为是同一个地址,所以公钥是一样的:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb$ r4 W* ?1 |+ z7 W
我们把这个签名和公钥再放回原始交易中,就变成我们需要的完整签名的交易:
, ~  c3 b7 ^. a1 i9 SInput        Output( ^. R& @* K; p% g
PreviousOutPoint={
$ e8 k" d, H+ U/ E. U$ k: F, GTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
. b" V! l* Z2 [4 |OutIndex:1}
- g6 w: }# ]6 r$ V' jSignatureScript =PUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]3 S3 U2 Y% E; M3 o& \3 {
,Sequence =0xFFFFFFFF
5 i9 I. U) W3 F7 P& d( `/ ?6 bValue=29910240
  x$ y$ ^) V6 L5 {2 A; N' K* pPkScript=
6 S0 |% p$ l! TOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG6 ^+ x5 K* l  @
PreviousOutPoint={4 y, m2 d& n+ @6 Q+ ^/ `# _
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
! T* F" {/ G9 ~: F( LOutIndex:1}& k. i; }# h- V- E
SignatureScript =PUSHDATA(71)[30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]
3 `% ]5 Y8 l$ a# Y9 \/ X,Sequence =0xFFFFFFFF
; k& P3 L- q. \& T9 Q这就是一个真实完整的交易了,接下来就可以通过P2P网络发送该交易,并最终被矿工打包确认。
9 F. ^/ C% V( {/ O9 F5 O" q, ~总结2 n- m" L, ?7 w* p. d5 w
实际上在比特币的源码中比我上面说的还要复杂一些,还涉及到这个hash是对整个交易进行SigHashAll还是SigHashSingle或者SigHashNone,这些都是很特殊的情况,一般的比特币钱包也不支持,具体可以参加精通比特币书中的介绍:6.5.3签名哈希类型( SIGHASH)- |3 k. `) ]4 w% a0 h% y% {
普通来说,我们要对一笔交易进行签名或者验签,就是把当前Input中的解锁脚本替换成锁定脚本,而其他Input的解锁脚本情况,然后计算Hash和签名!* _3 w) X7 [3 V* d" V$ H
其实我还是有点不明白,为什么比特币中不直接对没有任何解锁脚本的RawTransaction进行签名呢?而是非要加上锁定脚本来签名?不知道这里面有什么更深的考虑。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14