Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
92 0 0
交易的结构
8 u! f" n  K7 ?) u  y( e我们先来看看在比特币中,一个交易的结构是什么样的?
$ a) y: ]  x3 N, P5 @+ v" _type MsgTx struct {8 |: m7 A; {1 F1 T
    Version int32
( b5 m/ `1 k% a1 @0 m* R    TxIn []*TxIn
" I' W# A& l$ B$ L% }9 G5 A    TxOut []*TxOut4 V$ ]( r$ q5 w' j  t  i' [: u4 i
    LockTime uint32" |, c, e4 L' }4 u1 d
}
- F4 }2 W5 W+ b5 V4 C# Etype TxOut struct {
+ E5 {& f" [  i$ ~9 G    Value int64
" `; M$ L* V- V% [2 v. m/ Z0 H    PkScript []byte
* b) x4 b5 S! X9 ], r3 a7 s( I}
0 N' R* N3 R; {5 f8 _0 Ltype TxIn struct {
$ Y( H5 l  j- _. W    PreviousOutPoint OutPoint0 H  K" l1 ?3 R8 V: v; }  ]
    SignatureScript []byte
# M! j; H0 ]. p# A7 c    Sequence uint32& k5 l0 Z' w2 N5 m0 a6 v
}, o& b+ a4 j, k( a! ?1 A. f
type OutPoint struct {8 r8 ^! p( D: d1 i( K* M/ N
    Hash chainhash.Hash
9 U& C8 k; l- _: f, n% a/ _    Index uint32
% U9 h- f, T2 z  _' y}! s7 ^, o# R! A3 g0 a; [# c3 ^
我们可以看到,一个交易(MsgTx)是由多个Input和多个Output组成的,而在Input中是由指向UTXO的OutPoint,解锁脚本SignatureScript和序列Sequence组成。# q) u  P9 I' B
UTXO我们可以认为是一个KeyValue的大表,在该表中,交易的Hash和该交易中Output所在的位置索引Index就构成了UTXO的Key,而Value就是比特币Amount、锁定脚本等信息,所以在UTXO数据库中,我们通过OutPoint能够很快的找到对应的Amount和锁定脚本。' C& |9 K% H" e3 u. `6 A
在比特币中,要做一笔交易分为三个步骤:
+ Y- B; y( x( O" W) Z. s" `* _4 f  a
[*]构建原始交易RawTransaction,该交易包含了输入指向的OutPoint,也包含了完整的Output,但是没有签名,也就是没有设置SignatureScript的内容。
+ N) [7 K- S  N4 A[*]用私钥对签名构建的RawTransaction进行签名,并将签名构建成完整的解锁脚本,填入对应的Input的SignatureScript字段中。$ W7 |# N3 j5 |
[*]将签名后的Transaction发送到P2P网络中。
. ~9 M" {% E( X" l; x+ |/ o
8 h& @* N0 J5 o! @/ ^( D& D8 h+ t构建原始交易RawTransaction
% Y: r% v4 Q1 H8 V* X现在假设我有一个地址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 E4 L) _9 `, X4 ^1 o  g! R+ k$ I
以下是我用Go基于btcd写的示例代码,这里我们就构建好了一个RawTransaction。  f- V# {1 E5 _7 g& j( ?
func buildRawTx() *wire.MsgTx {
/ x& F" K- m1 a& I//https://testnet.blockchain.info/tx/f0d9d482eb122535e32a3ae92809dd87839e63410d5fd52816fc9fc6215018cc?show_adv=true
: ?4 N9 ^3 [# u8 P- Qtx := wire.NewMsgTx(wire.TxVersion)
8 O( z. ?0 W9 l$ m" \//https://testnet.blockchain.info/tx-index/239152566/1 0.4BTC+ `& J+ n- W6 M& ]) `
utxoHash, _ := chainhash.NewHashFromStr("1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52")
" O1 g& ^0 B+ wpoint := wire.OutPoint{Hash: *utxoHash, Index: 1}
, J# a8 y* j2 F3 V9 E//构建第一个Input,指向一个0.4BTC的UTXO,第二个参数是解锁脚本,现在是nil5 |: W. m: V, Y+ r* c
    tx.AddTxIn(wire.NewTxIn(&point, nil, nil))
/ w/ U0 A) t( N+ j4 \) u//https://testnet.blockchain.info/tx-index/239157459/1 1.1BTC
% g/ t6 Q/ \. _utxoHash2, _ := chainhash.NewHashFromStr("24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66")
9 S( C9 v; Y* Y- P. q3 spoint2 := wire.OutPoint{Hash: *utxoHash2, Index: 1}
6 S/ S" D! U& U0 I9 z/ G' M7 F//构建第二个Input,指向一个1.1BTC的UTXO,第二个参数是解锁脚本,现在是nil
+ r. h, A4 Y- |) w+ k" q    tx.AddTxIn(wire.NewTxIn(&point2, nil, nil))4 [1 G. ^* `+ j5 G, b+ ^; }
//找零的地址(这里是16进制形式,变成Base58格式就是mx3KrUjRzzqYTcsyyvWBiHBncLrrTPXnkV)  s( @1 S1 w9 l% |/ [9 u
pubKeyHash, _ := hex.DecodeString("b5407cec767317d41442aab35bad2712626e17ca")
( k) G3 Z( `" X. ^8 Block, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).5 R9 P  i9 Z# k) d- f
AddData(pubKeyHash).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).+ ]- j- ^5 I3 K+ W$ p+ i& w' }! R0 B
Script()
  X6 a( A2 m' U//构建第一个Output,是找零0.2991024 BTC
+ w' t( M1 s. a/ z8 d6 K7 Z* X+ r    tx.AddTxOut(wire.NewTxOut(29910240, lock))
0 ^( \0 h& n. E//支付给了某个地址,仍然是16进制形式,Base58形式是:mxqnGTekzKqnMqNFHKYi8FhV99WcvQGhfH。
4 y" [6 R& J# |& O, Y7 ypubKeyHash2, _ := hex.DecodeString("be09abcbfda1f2c26899f062979ab0708731235a")
; z1 T% V- c, L: q; _lock2, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).% s( `1 P% @7 O% a
AddData(pubKeyHash2).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).8 X, K4 y, {# A
Script()* j3 E# g* o/ `! j9 J
//构建第二个Output,支付1.2 BTC出去- h4 Y' T- t/ p0 P6 A, K
    tx.AddTxOut(wire.NewTxOut(120000000, lock2))5 c4 ~& v1 q$ @/ Z
return tx
) y$ A. X# W: C' n( H}
( ^% r2 k1 T# G" R交易的签名过程' \& o9 J" l) S/ T; o
现在我们知道私钥,需要对该交易进行签名,因为有2个Input,所以我们要签名2次,每个签名的原理是一样的,我就以第一个Input为例来说明吧。- x/ N# c0 a* I
在比特币中,对一笔交易的签名流程是这样的:
% L' l& Y) g3 {7 |  V1.查找该笔交易对应的UTXO0 R$ j( D) [7 Y5 I' t3 Z
2.获得该UTXO对应的锁定脚本( |* L9 U$ v) d7 Y! f9 g+ [) x
3.复制该交易对象,并在复制副本中将该Input的解锁脚本字段的值设置为对应的锁定脚本
+ U* i* o$ _" Z. I4 B# U# H5 K4.清除其他Input的解锁脚本字段
+ m+ u7 d8 l, r, g2 g5.对这个改造后的交易对象计算Hash
, k5 J) t4 t/ V# a! o6.使用私钥对Hash进行签名。
" o4 d( n: i, L' c7 Z( Y' u用表格的形式可以更容易表达:
; I  O1 P8 t2 N, J0 [/ L这是原始未签名的交易RawTransaction,主要是第二列和第三列:; G. V; H. g9 q: R0 F
5 {5 m3 T, y8 s/ i/ H9 i
UTXO        Input        Output
- D* e. G$ F2 kTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,  @1 e: E* Y* A1 K6 l* i
OutIndex:1,' h. P1 x$ i& [) q* J: L! l8 s9 U
Amount:0.4BTC,PkScript:
- ~" w5 l4 y( k8 BOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
( j+ ?' s& {+ ZPreviousOutPoint={; k* _$ e/ @* q- O3 E$ u$ ]
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,/ B1 s! W# n, O9 i' K
OutIndex:1}/ x- c0 @8 n* h1 F) M3 f0 N/ K- ^4 q, s
SignatureScript =NULL,Sequence =0xFFFFFFFF
& D6 s3 t4 H' kValue=29910240
$ m: r) S( {+ g& M5 P8 s9 YPkScript=4 Z& v5 n5 V$ ]
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
7 y; a! B* x5 \  B9 G& ~* M0 F9 I+ z' ETxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,5 T4 m2 O+ t0 @0 |$ {
OutIndex:1,
$ O1 }2 J7 W' a4 |3 O- jAmount:1.1BTC,PkScript:7 ~1 p: M: }3 j' T! u2 _& s
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG) ^% [+ N& A% z/ o* d( O
PreviousOutPoint={
+ ^/ d2 Y, u- y8 yTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
' R$ e) P, e' U1 E& P. E. COutIndex:1}% G) }& z0 x% {- u3 i3 @
SignatureScript =NULL,Sequence =0xFFFFFFFF
! H" d/ Y0 f. V- oValue=120000000
2 O; R) y: F7 p; }0 c) W1 f# Q. pPkScript=
! h) H) {9 f8 `OP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG
6 {/ W0 X6 u* e! ]- B接下来我们要对第一个Input签名,于是我们需要将交易复制一个副本,并改为:4 O9 v* b% u5 L
Input        Output- I8 ]: p1 H9 p2 r- z
PreviousOutPoint={& v9 o2 b( U. P1 Q
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,6 @5 ?, S" M# _& A4 z5 R& q1 f
OutIndex:1}; u- Z6 J+ e* x
SignatureScript =
" T6 l, j3 G& @OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
% u" f9 }& @( M; }& Z,Sequence =0xFFFFFFFF1 L* T: b" w% }0 o
Value=29910240
  A0 g) ~& \9 r# JPkScript=1 ^$ {3 D! k- i% G5 ~2 P3 M" q1 O' S
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
& M$ R( }3 }( H* F5 F% m' bPreviousOutPoint={8 i! m  b; T2 O, [% u4 _
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
3 Q  C- l2 p5 f4 x$ b2 x% bOutIndex:1}
# _. Q1 I. z7 lSignatureScript =NULL,Sequence =0xFFFFFFFF5 M& D, ^' B; P' U7 j% [2 _9 S
Value=120000000
& F/ A& p/ U, {3 ]/ L& J8 cPkScript=
; [/ \/ t: c  l; o- D7 xOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG& ?. y* n2 N) }' r7 j0 }3 }
接下来对这个交易计算Hash,然后进行签名。得到签名结果:3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501 而我们知道这个地址的公钥是:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb
- f& f4 c1 d$ t所以签完名后,我们的交易变成:* n. I/ V  h( S; P
Input        Output
4 u4 t$ y4 _/ j! zPreviousOutPoint={/ A$ Q# {% ?" i! h6 p% t% x& E: [
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,8 d- L# D# E7 W) o  p; U$ S
OutIndex:1}2 \9 s: ]9 A1 {- v
SignatureScript =" J3 I" L% ?& p1 e: D
PUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd2
8 z4 U3 [  B1 q8 P% E, {3 R/ I$ X2ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]6 n# J$ t1 e3 q) k3 F
,Sequence =0xFFFFFFFF
/ z' A  _+ h) l  }( Z' J; \7 a( ]Value=29910240( A4 Y5 ^9 M- x/ p, x$ K+ M, U
PkScript=
- q- b7 g1 T  }5 D: G1 x9 N% ]OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG5 i2 ]! Y$ r" |6 i# ]# ~
PreviousOutPoint={
2 `  g+ H/ R: J, ~) y/ JTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,( ]+ o6 w! h) Z3 N0 k% G
OutIndex:1}
' o+ r- I2 A* \) jSignatureScript =NULL,Sequence =0xFFFFFFFF& Z) X+ H$ a& b+ `) ]! c2 `7 F
Value=120000000; w$ D2 w" _1 c, i( l
PkScript=
5 c4 U9 C/ V( N3 q% ~  dOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG- t8 L& G& d' k" K2 P9 N
这才只是完成了第一个Input的签名,接下来我们再对第二个Input进行签名,同样的道理,我们需要制造一个交易的副本,然后把第一个Input的SignatureScript清空,然后给第二个Input的SignatureScript赋值:. v" Z" Z8 a. X
Input        Output
) Q+ O, J" U8 T5 n" b0 {/ q- zPreviousOutPoint={& v& v  X% V( n- t+ r
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
4 K) c" R, j* J. d) ROutIndex:1}
$ H$ H3 r* w% I5 {) JSignatureScript =NULL,Sequence =0xFFFFFFFF
2 [4 z( A. \2 v5 x$ j- bValue=29910240
& ], R, l# e' aPkScript=5 S1 _& g" E" s2 l* T' U8 Q
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG+ P7 T% n, Z% _
PreviousOutPoint={6 L" G- @7 C! M5 ]' S% }1 J$ G
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,( A$ X$ K$ x, A7 S0 O  Z3 C
OutIndex:1}
% ?6 V( W  g7 Y5 g5 l. iSignatureScript =OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
: n$ u1 k1 Q9 N,Sequence =0xFFFFFFFF6 s% y* i+ K( q  Q. c/ }7 B' v
Value=120000000, H* ]$ u& A3 ], s& d6 J
PkScript=
) s/ P  D; |. o6 P. z" F2 i  ~% F' c+ uOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG7 P7 \  @1 G( B$ c1 {9 s: p
显然这个副本与第一个签名时的数据是不一样的,所以签名结果也不一样,最终签名结果为:30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501,当然我们因为是同一个地址,所以公钥是一样的:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb
; U$ ]  R0 ~5 Z6 a% q我们把这个签名和公钥再放回原始交易中,就变成我们需要的完整签名的交易:
0 `. e8 C0 S" W$ c. v" J) i4 uInput        Output
0 N2 z7 h9 ?) B/ J% @PreviousOutPoint={
: `, T0 A5 z1 kTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,2 [, b, d) S3 |& O5 C- O$ K
OutIndex:1}" \0 l, {7 @$ [) R3 Z  v1 @# C
SignatureScript =PUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]
# Y- p2 {5 S5 O, N7 q' {,Sequence =0xFFFFFFFF
* [' U! h% S6 `/ G. `( p! hValue=29910240
+ R* K$ y. M: P8 H% |1 v8 F, w, CPkScript=; @( h6 g) y/ D  P) G- F
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
" b2 W; l! G7 m3 S; N8 m( T9 N- hPreviousOutPoint={
/ W/ f( I9 h" K" r2 V' cTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
3 l& ]/ |( A: S3 n' gOutIndex:1}9 h! I4 I& w, e1 {: o: p- k8 j& I
SignatureScript =PUSHDATA(71)[30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]" v  o" {; z( [' F  h2 o7 `/ p
,Sequence =0xFFFFFFFF
9 I" O# y$ Z; |  ~# t9 l! Q这就是一个真实完整的交易了,接下来就可以通过P2P网络发送该交易,并最终被矿工打包确认。
. ^, I- ^) A) R" b) B. R- s  k总结- S9 J8 Z' m3 w) X& v+ s. A3 L2 M
实际上在比特币的源码中比我上面说的还要复杂一些,还涉及到这个hash是对整个交易进行SigHashAll还是SigHashSingle或者SigHashNone,这些都是很特殊的情况,一般的比特币钱包也不支持,具体可以参加精通比特币书中的介绍:6.5.3签名哈希类型( SIGHASH)
- \2 m" d) C, ^8 O9 i% |$ X普通来说,我们要对一笔交易进行签名或者验签,就是把当前Input中的解锁脚本替换成锁定脚本,而其他Input的解锁脚本情况,然后计算Hash和签名!
' X+ K: l0 r7 \其实我还是有点不明白,为什么比特币中不直接对没有任何解锁脚本的RawTransaction进行签名呢?而是非要加上锁定脚本来签名?不知道这里面有什么更深的考虑。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14