Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
90 0 0
交易的结构3 J4 `7 |6 S& U( ]
我们先来看看在比特币中,一个交易的结构是什么样的?+ d% y  a$ M& J: L1 A, ^
type MsgTx struct {
, K% P. w/ N  R+ T) h: \. Z    Version int32
/ X% q: q. d2 l5 j    TxIn []*TxIn/ N8 A+ H& {& g+ f# Y
    TxOut []*TxOut9 P8 P' c4 _! R$ A+ \- U  \
    LockTime uint32
, @" d2 _0 Z4 {}
& l. e1 p: C$ |# `% W" ?type TxOut struct {- U* @  m( Y9 Q3 C
    Value int64
. \1 S$ A9 Q4 K7 X$ L* _    PkScript []byte( s$ }( }4 @4 c  E# N7 t: t5 A1 D
}
) ]% S) H2 P3 Y6 _" Q3 h4 m5 etype TxIn struct {
" u; c# a, M; O0 g: Q    PreviousOutPoint OutPoint
% a, ^; J# O/ C( h) T# h9 E    SignatureScript []byte
) ]* ?# S' T% B' g* \: f) i8 f    Sequence uint32
, r! e+ S3 a+ L( m# {2 H) N}3 L, q& \  h: V7 O
type OutPoint struct {. g) b4 o; u  w
    Hash chainhash.Hash
3 x' e# K( i. a& m) _  s. |' T    Index uint32
$ b; b2 x$ R2 M/ L$ Z* ?}" E0 q1 E, ^* m. f* B: N5 [
我们可以看到,一个交易(MsgTx)是由多个Input和多个Output组成的,而在Input中是由指向UTXO的OutPoint,解锁脚本SignatureScript和序列Sequence组成。
9 o# v6 ?; R# h' H/ e4 M5 }" _UTXO我们可以认为是一个KeyValue的大表,在该表中,交易的Hash和该交易中Output所在的位置索引Index就构成了UTXO的Key,而Value就是比特币Amount、锁定脚本等信息,所以在UTXO数据库中,我们通过OutPoint能够很快的找到对应的Amount和锁定脚本。/ ]$ c$ \" v# t+ Y  c
在比特币中,要做一笔交易分为三个步骤:7 M1 [+ e/ P8 J4 Q0 M
5 d2 @; o( G, h! c6 L5 d% o
[*]构建原始交易RawTransaction,该交易包含了输入指向的OutPoint,也包含了完整的Output,但是没有签名,也就是没有设置SignatureScript的内容。/ S- T! B- j% M
[*]用私钥对签名构建的RawTransaction进行签名,并将签名构建成完整的解锁脚本,填入对应的Input的SignatureScript字段中。
1 I! J1 Y4 t  A1 ?[*]将签名后的Transaction发送到P2P网络中。+ y, Q/ j/ [  L3 d# o1 S1 s, T
+ K1 r- u( K0 t# I$ B
构建原始交易RawTransaction
( L3 j: u2 O0 l: _/ J( 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。6 q2 ^, s0 }4 }. }4 k, v
以下是我用Go基于btcd写的示例代码,这里我们就构建好了一个RawTransaction。. ~0 r0 W$ F- V
func buildRawTx() *wire.MsgTx {
9 r. Q, y/ W+ V9 d: b//https://testnet.blockchain.info/tx/f0d9d482eb122535e32a3ae92809dd87839e63410d5fd52816fc9fc6215018cc?show_adv=true
5 I! ?* U# ?: Vtx := wire.NewMsgTx(wire.TxVersion). h! ]" v8 r6 s+ b0 H9 T
//https://testnet.blockchain.info/tx-index/239152566/1 0.4BTC: o; n" O' v) m1 G. z6 e
utxoHash, _ := chainhash.NewHashFromStr("1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52")5 Q1 b/ i# w5 w/ z% v
point := wire.OutPoint{Hash: *utxoHash, Index: 1}
) t4 D% ]( H* i( _//构建第一个Input,指向一个0.4BTC的UTXO,第二个参数是解锁脚本,现在是nil
$ x6 W- g' j1 Y, O2 ~    tx.AddTxIn(wire.NewTxIn(&point, nil, nil))/ g' L0 S: O# Z" U& p
//https://testnet.blockchain.info/tx-index/239157459/1 1.1BTC' u, Z7 {8 y* y3 k- W; I/ s. h
utxoHash2, _ := chainhash.NewHashFromStr("24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66")5 l2 w2 n; q/ E6 S$ O
point2 := wire.OutPoint{Hash: *utxoHash2, Index: 1}
. x. b9 P1 ~/ x- F6 [' Z  i3 y//构建第二个Input,指向一个1.1BTC的UTXO,第二个参数是解锁脚本,现在是nil5 P& t3 ?. z( J- o' D' g
    tx.AddTxIn(wire.NewTxIn(&point2, nil, nil))
: Q9 [5 b9 _+ H, E, r//找零的地址(这里是16进制形式,变成Base58格式就是mx3KrUjRzzqYTcsyyvWBiHBncLrrTPXnkV): u" _8 \2 f9 @% @
pubKeyHash, _ := hex.DecodeString("b5407cec767317d41442aab35bad2712626e17ca")
0 t9 T; P" Y6 k9 P8 l" Clock, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).
7 J0 O  l/ t; D' ]AddData(pubKeyHash).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).+ ^6 r3 y6 d( w7 ]5 k0 R; J- {! u
Script()( G2 U1 J# b" Z3 B% j& t
//构建第一个Output,是找零0.2991024 BTC
; s* ^! |# P, d/ g% |% P8 d! ~- F7 l    tx.AddTxOut(wire.NewTxOut(29910240, lock))
( d2 X+ N/ |: C& X: \9 A//支付给了某个地址,仍然是16进制形式,Base58形式是:mxqnGTekzKqnMqNFHKYi8FhV99WcvQGhfH。3 A3 q  O; [# R' U3 N* I
pubKeyHash2, _ := hex.DecodeString("be09abcbfda1f2c26899f062979ab0708731235a")! k5 j2 f9 @) m5 J6 A, G
lock2, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).1 O( I/ U! \+ L* \" j0 y6 e
AddData(pubKeyHash2).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).
4 Y. K+ z& n4 ~* \5 NScript()$ |( V8 q! G9 E9 O8 |% x7 y1 X
//构建第二个Output,支付1.2 BTC出去: E. J0 t1 B7 ]: Z2 e2 \% L& y
    tx.AddTxOut(wire.NewTxOut(120000000, lock2)): {) w! S7 i- \5 d+ R: D
return tx
- m- f! |; a% H8 i, S" S}( p: ], X8 E7 X! \+ f% ~
交易的签名过程; y* s. L; m0 l+ Q
现在我们知道私钥,需要对该交易进行签名,因为有2个Input,所以我们要签名2次,每个签名的原理是一样的,我就以第一个Input为例来说明吧。
+ l3 a% n5 z; n/ x' v* Z在比特币中,对一笔交易的签名流程是这样的:' C  h; ]  i, x- C& p6 M4 N1 ?' [. b
1.查找该笔交易对应的UTXO! S3 D6 G  R- Z. y( s  g, z& S
2.获得该UTXO对应的锁定脚本7 _2 |9 P8 ^, v& `* A0 {
3.复制该交易对象,并在复制副本中将该Input的解锁脚本字段的值设置为对应的锁定脚本. T9 r2 P! ~3 c5 S2 i
4.清除其他Input的解锁脚本字段
* i2 K0 C* O- y' [: G, j5.对这个改造后的交易对象计算Hash* Y- u( V$ s) o. K7 x
6.使用私钥对Hash进行签名。; ^# F( R+ p# ~* n1 P
用表格的形式可以更容易表达:
0 ?; G( N0 Z0 U8 Q) }9 ^$ N这是原始未签名的交易RawTransaction,主要是第二列和第三列:/ ?. \$ r1 z8 m

+ W" S' Y; j) OUTXO        Input        Output9 T/ z$ B6 q% ^9 a! u/ [8 i, B& ?# k
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,3 N7 v, `+ X& A) N
OutIndex:1,
+ ^. @) h/ C3 Z4 J6 c* y; a2 qAmount:0.4BTC,PkScript:
6 u7 C2 U. r7 `* wOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG  A4 q$ k; v5 Y" T6 V
PreviousOutPoint={+ s! F5 ]1 i7 d
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,& {5 t6 u7 U5 h0 A( t; N: [
OutIndex:1}4 g% o( M* b" P; R$ f+ Z) r0 x6 d
SignatureScript =NULL,Sequence =0xFFFFFFFF
/ }! e2 G# I4 w) @Value=299102404 \7 l8 r( w& {4 t0 `% v$ ]( S+ `
PkScript=: _8 q" E( p4 W# z
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG, e6 J0 I. E! E9 w  X- @! o
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,0 q& D' J7 z5 d$ h' O4 K# ]
OutIndex:1,
: L0 u  @7 @2 A7 yAmount:1.1BTC,PkScript:. O5 A% x1 M' e3 M
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG  U- a$ T8 _6 g
PreviousOutPoint={4 l4 U* T3 c" w6 u; c; t
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
1 x) A/ P: h1 K; U5 ], c4 {OutIndex:1}
5 c( c& W/ g4 F  b1 _* `SignatureScript =NULL,Sequence =0xFFFFFFFF5 ]9 N  c: _+ @% e
Value=120000000
3 Q& O# R2 g! h* `. B- jPkScript=
. }* [( p! c! y/ P* WOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG
& L* w( q+ q+ [, @" t接下来我们要对第一个Input签名,于是我们需要将交易复制一个副本,并改为:+ H$ @# n5 d& v; ?1 H* [9 n
Input        Output
2 E- V* O$ D0 a4 y2 J8 N9 t* sPreviousOutPoint={
4 \1 c+ |" v" [TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,7 @8 q9 H/ a6 q# K* Y9 s0 a$ v
OutIndex:1}) h5 E* [/ D. Z4 L
SignatureScript =7 w2 E! A9 \: w3 H6 y4 k
OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
2 W! N8 I* W$ p5 `,Sequence =0xFFFFFFFF9 p$ Z+ R( k0 b& M9 K: S0 y6 a4 a4 K/ c& ?
Value=29910240" C( }0 y+ j1 f
PkScript=& C# k- J1 A( c# {. h5 C" w
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG# Q6 G$ Y; m& Y3 M$ ~
PreviousOutPoint={
% ]5 `1 d6 P. X& k5 JTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
3 \1 U. F8 W, J, U/ WOutIndex:1}3 t. W. s3 u7 C6 @" g  A; L
SignatureScript =NULL,Sequence =0xFFFFFFFF
, F$ f( N: s$ O* M( xValue=120000000
: n/ z/ s4 P- O/ mPkScript=
6 q2 a) t3 l0 MOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG
. }6 a' ]# @0 Q2 `接下来对这个交易计算Hash,然后进行签名。得到签名结果:3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501 而我们知道这个地址的公钥是:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb$ n. w, L  W1 [; e0 ?
所以签完名后,我们的交易变成:
7 Y% a1 C. h. ]6 `4 b! q: F/ rInput        Output
4 U- t; S1 H' Y% X( w4 k+ V& x0 aPreviousOutPoint={
+ A! s; Z4 _+ K) y0 i$ gTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
3 b1 r1 y3 D# fOutIndex:1}. w  r% ^6 e% r6 m) ?. I$ t
SignatureScript =
0 h1 K+ T- j) g' ZPUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd2$ l& m$ N+ _, W- k2 y. a
2ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]
# N8 ~: k& s' ~' E,Sequence =0xFFFFFFFF3 J: ~; ?* O& P6 T# r; h. s
Value=29910240
# ^; N" F5 m8 z, }8 E# G& ^PkScript=
; [) ?- m' x2 Q' JOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
/ ^3 t2 V/ w) X- W$ U  a# a. M4 QPreviousOutPoint={
' {# v; R! V. b" ^6 BTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66," M" J* @/ A, n- j
OutIndex:1}. v- g: w3 v" J$ N! D2 a3 p
SignatureScript =NULL,Sequence =0xFFFFFFFF( t; J" w) p9 w, d! `
Value=120000000
  I$ |% c2 B( l% GPkScript=2 q4 B* I- V/ h5 f! p2 Y* i6 P+ }
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG: u5 a( t4 d$ o+ r- i* k! m
这才只是完成了第一个Input的签名,接下来我们再对第二个Input进行签名,同样的道理,我们需要制造一个交易的副本,然后把第一个Input的SignatureScript清空,然后给第二个Input的SignatureScript赋值:5 s* K" ?( r( W+ T, E& s
Input        Output" ?, h( x; {5 ^. d, a2 H4 Y; p
PreviousOutPoint={
' f/ c: ?+ @! a5 |6 \7 T* q0 @TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,' X" c4 n$ P5 L& |
OutIndex:1}8 L% u, }' K/ f; b1 v2 Z$ s
SignatureScript =NULL,Sequence =0xFFFFFFFF$ s# ^+ C( ~5 ]
Value=29910240
$ x5 [0 O# J7 ^; |: {5 WPkScript=7 o9 J$ V1 j! {$ T8 H  h' i
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
3 g7 w- N& O) {, s# H; l5 {$ XPreviousOutPoint={
# Y  p, [8 G& b* A# ?& P; xTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,- |% s) H7 W# b$ ^
OutIndex:1}
# G6 v* \/ m7 n; e5 ~+ [SignatureScript =OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
* j" ~$ @8 [, d6 E- Z,Sequence =0xFFFFFFFF5 g9 `! B' I7 j/ F, g! H3 c$ T' R
Value=120000000
. l$ Y  G; x4 r9 W' I# C' APkScript=1 G& {- B! N  X8 v! N3 [+ _+ [$ X
OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG0 n4 n6 l. g* ], e; C
显然这个副本与第一个签名时的数据是不一样的,所以签名结果也不一样,最终签名结果为:30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501,当然我们因为是同一个地址,所以公钥是一样的:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb4 h$ A2 B+ }/ V; a, |
我们把这个签名和公钥再放回原始交易中,就变成我们需要的完整签名的交易:
' g# x' u2 ~0 C: S' LInput        Output. K9 _5 B2 `% W2 B9 K5 H
PreviousOutPoint={
1 j9 q$ v! }/ VTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,# H) R1 h& V" r
OutIndex:1}
% T) `3 m3 F/ b2 YSignatureScript =PUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]
" a+ d! U2 V* x1 Q+ {* o' B,Sequence =0xFFFFFFFF
7 H6 u8 ]6 I' U/ [- o! Z& C! MValue=29910240
3 u; k$ B( E' p/ ~! WPkScript=6 @- I% R' l4 ?% `: n! Z
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
( X# k# F4 i2 j0 p2 ePreviousOutPoint={0 H' H2 o" N# L# D' W; m) r3 }4 h
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
8 H( R! R1 q3 `& vOutIndex:1}
8 q/ c  _3 h; @1 N- E, zSignatureScript =PUSHDATA(71)[30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]
' t2 h* w0 J4 ~+ H, _" A- |,Sequence =0xFFFFFFFF# Z! a" U# g/ V% w8 n- e
这就是一个真实完整的交易了,接下来就可以通过P2P网络发送该交易,并最终被矿工打包确认。
, G0 W5 k2 r& @# N! M总结& A; |+ j- {  L; t  s$ {: o: R
实际上在比特币的源码中比我上面说的还要复杂一些,还涉及到这个hash是对整个交易进行SigHashAll还是SigHashSingle或者SigHashNone,这些都是很特殊的情况,一般的比特币钱包也不支持,具体可以参加精通比特币书中的介绍:6.5.3签名哈希类型( SIGHASH)- v+ G# d* v/ s" `2 Q
普通来说,我们要对一笔交易进行签名或者验签,就是把当前Input中的解锁脚本替换成锁定脚本,而其他Input的解锁脚本情况,然后计算Hash和签名!
* F8 p* w# b& G5 j. K其实我还是有点不明白,为什么比特币中不直接对没有任何解锁脚本的RawTransaction进行签名呢?而是非要加上锁定脚本来签名?不知道这里面有什么更深的考虑。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14