Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

V刘晨曦
97 0 0
交易的结构- U, _" ~& J6 K: ?$ Q! k' F' w
我们先来看看在比特币中,一个交易的结构是什么样的?( g+ F" d. N4 v
type MsgTx struct {+ C: o$ {; Y! S5 |& ~- \
    Version int32
! ~% S  o( W$ g& l    TxIn []*TxIn+ s- Z$ \. f# c9 C/ d- J0 `* z
    TxOut []*TxOut: n: j0 `& f$ W% {, x/ A8 i4 }
    LockTime uint328 R8 y. W7 e, r( ~# T0 k: L
}
6 v$ h) m) C8 D+ T7 ptype TxOut struct {8 S) l# U3 p2 K) [7 `
    Value int64+ ?; o- @1 \1 G: B* z
    PkScript []byte
, Q) `# B% I# T/ W& V}
" E2 I# u8 O- F/ J* b. _type TxIn struct {
+ `) a0 J- ?, L  Y# g    PreviousOutPoint OutPoint" v9 i( q% R% c7 B2 d! g+ A9 S
    SignatureScript []byte
+ d3 F9 d- ^1 E4 U3 s* A    Sequence uint32. u( \' A% M7 u. ~4 ?3 E1 x
}
, u& R5 R* Z) I' B7 Ttype OutPoint struct {
! t0 h: V' {! \7 B! ~    Hash chainhash.Hash& n9 t7 `3 K6 D3 j. n1 K
    Index uint32
$ V% V' p1 I) ]% m- c! D  p# e: f. O}
# d6 t9 @7 d, X; J) w* ?* a! G我们可以看到,一个交易(MsgTx)是由多个Input和多个Output组成的,而在Input中是由指向UTXO的OutPoint,解锁脚本SignatureScript和序列Sequence组成。
' e  z2 I* U$ F- oUTXO我们可以认为是一个KeyValue的大表,在该表中,交易的Hash和该交易中Output所在的位置索引Index就构成了UTXO的Key,而Value就是比特币Amount、锁定脚本等信息,所以在UTXO数据库中,我们通过OutPoint能够很快的找到对应的Amount和锁定脚本。& e6 o* b2 u1 H  D* P8 h6 F0 ]
在比特币中,要做一笔交易分为三个步骤:
3 n( _6 M$ S/ \& {7 @3 L! P7 W" }3 ~+ P
[*]构建原始交易RawTransaction,该交易包含了输入指向的OutPoint,也包含了完整的Output,但是没有签名,也就是没有设置SignatureScript的内容。6 }4 h, {3 j% q6 R
[*]用私钥对签名构建的RawTransaction进行签名,并将签名构建成完整的解锁脚本,填入对应的Input的SignatureScript字段中。/ [( D8 l- G, n/ n- V
[*]将签名后的Transaction发送到P2P网络中。
6 \3 Z, M+ R! S  C: k7 H1 g# S" R6 N
构建原始交易RawTransaction
# Y1 ^0 y0 \- 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。7 z( B" w" j) E' \
以下是我用Go基于btcd写的示例代码,这里我们就构建好了一个RawTransaction。
) `- l4 {9 C% K) Y; g2 U  W& ^. C  l, Hfunc buildRawTx() *wire.MsgTx {
8 O- e8 S( H& R8 ^//https://testnet.blockchain.info/tx/f0d9d482eb122535e32a3ae92809dd87839e63410d5fd52816fc9fc6215018cc?show_adv=true
- i4 j  a, o) xtx := wire.NewMsgTx(wire.TxVersion)) t9 n9 L% ^% W' P! }
//https://testnet.blockchain.info/tx-index/239152566/1 0.4BTC
) Z5 ?% D) B- o# s+ Y9 [" }! q2 v" }utxoHash, _ := chainhash.NewHashFromStr("1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52")* u. g- B5 V1 }( G
point := wire.OutPoint{Hash: *utxoHash, Index: 1}, {/ w, a' F- Z+ G) o
//构建第一个Input,指向一个0.4BTC的UTXO,第二个参数是解锁脚本,现在是nil
$ l4 z$ b& m! c- o# E    tx.AddTxIn(wire.NewTxIn(&point, nil, nil))9 Q5 E" Q' H+ A  H8 v
//https://testnet.blockchain.info/tx-index/239157459/1 1.1BTC
( b% i2 `' x$ D% u1 U1 [+ W9 YutxoHash2, _ := chainhash.NewHashFromStr("24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66")
5 F4 B( Y$ k7 ]: G% f* Y: upoint2 := wire.OutPoint{Hash: *utxoHash2, Index: 1}, _% i' {: j- [; u8 t! q
//构建第二个Input,指向一个1.1BTC的UTXO,第二个参数是解锁脚本,现在是nil$ l% g4 A+ w/ y7 n9 |8 a+ n- y4 h
    tx.AddTxIn(wire.NewTxIn(&point2, nil, nil))) [9 Y  W9 u. F+ `9 C: Q
//找零的地址(这里是16进制形式,变成Base58格式就是mx3KrUjRzzqYTcsyyvWBiHBncLrrTPXnkV)
6 ?8 M  k" w( npubKeyHash, _ := hex.DecodeString("b5407cec767317d41442aab35bad2712626e17ca")/ w' z+ J0 i" k9 d' d( B  D
lock, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).
8 q' e2 E! f: W& _/ _3 q  P0 rAddData(pubKeyHash).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).
: v1 A! S' F/ p. bScript()/ O( o0 \; T) {
//构建第一个Output,是找零0.2991024 BTC
8 y1 D. o5 H, M; J/ Q1 ^    tx.AddTxOut(wire.NewTxOut(29910240, lock))- s+ b1 F4 P# w" O/ \; q7 W
//支付给了某个地址,仍然是16进制形式,Base58形式是:mxqnGTekzKqnMqNFHKYi8FhV99WcvQGhfH。7 i+ L' C6 l9 Z
pubKeyHash2, _ := hex.DecodeString("be09abcbfda1f2c26899f062979ab0708731235a")3 ~- _" G2 Q( X( B& i( q8 V
lock2, _ := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).AddOp(txscript.OP_HASH160).
* y1 }; [- ?+ C6 G0 yAddData(pubKeyHash2).AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).; j% a8 ~! z8 _8 Q8 {( r
Script()
5 g" e: E/ ^5 _* P//构建第二个Output,支付1.2 BTC出去
( ]0 Z, x  A6 B8 ^9 n) K6 w    tx.AddTxOut(wire.NewTxOut(120000000, lock2))
) Y6 v6 |' ?; h6 X% sreturn tx
- Z7 x7 D0 R: {}
4 t8 p$ t5 _2 F0 [$ f  r- V交易的签名过程: d3 I8 j( e1 e4 r6 U" q
现在我们知道私钥,需要对该交易进行签名,因为有2个Input,所以我们要签名2次,每个签名的原理是一样的,我就以第一个Input为例来说明吧。/ a( I6 ?; N2 c( y( K" m' I
在比特币中,对一笔交易的签名流程是这样的:
' V: ]; }! e- S7 c1.查找该笔交易对应的UTXO
$ U# k5 A5 S9 R2 X2.获得该UTXO对应的锁定脚本2 _2 o: |5 E  T2 s1 `( A
3.复制该交易对象,并在复制副本中将该Input的解锁脚本字段的值设置为对应的锁定脚本
! J! V4 b. n+ L5 A4.清除其他Input的解锁脚本字段7 W- u! H2 h! }# x% R" n1 D  D. K1 w2 U
5.对这个改造后的交易对象计算Hash8 L* ^1 v& Z% z9 n
6.使用私钥对Hash进行签名。
- d0 ~( Y5 i9 f* T4 {- F  \  s& W用表格的形式可以更容易表达:
( I! g; @' j8 X6 M5 b3 Q, |这是原始未签名的交易RawTransaction,主要是第二列和第三列:
0 e" G4 u2 p4 Y3 z. ^! E3 p% S; y5 ~6 Z! e% g2 \8 N- a
UTXO        Input        Output
+ Q7 B! z# ^. ^. p) c% {% CTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,3 M) f! D: d# i2 C7 i" A5 p$ E
OutIndex:1,
8 A2 \% {5 k) a( [! H: RAmount:0.4BTC,PkScript:
" ~7 C' `- x8 I% Q0 K1 VOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
- a" O8 [# V2 D5 J4 ~PreviousOutPoint={: B9 m* r7 ]6 r0 K- w/ d3 F
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,, Z" M' F, x9 X# W
OutIndex:1}
) p: ^7 L/ V& l/ A1 y+ K* y4 SSignatureScript =NULL,Sequence =0xFFFFFFFF+ R0 A6 p5 j! ?
Value=29910240. O1 C( T' G( _3 c- O7 V9 y
PkScript=8 _( u7 u' M% g
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG6 u- j: `6 J5 `/ y4 x: a; u' z+ \
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
: f, v( x$ R5 s) p. I, MOutIndex:1,
; F- V$ b  h/ \Amount:1.1BTC,PkScript:; ^% y. m( I& V2 {; v
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
9 O$ x0 D/ [0 e+ Y% @PreviousOutPoint={5 b" L, ]) [0 H* R' O
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
1 I- E& g* m1 g7 \OutIndex:1}
" f$ j: j0 q( M1 @6 XSignatureScript =NULL,Sequence =0xFFFFFFFF/ e+ x( s! M4 T4 H0 K+ b. H/ s; |  e
Value=120000000
  o; C4 i! M( \9 e" z: G4 O+ h+ LPkScript=
$ u% O/ s/ _3 d8 T: x4 aOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG6 }1 o! l% }, P# R" B' w1 G
接下来我们要对第一个Input签名,于是我们需要将交易复制一个副本,并改为:( x% a8 a5 \: }8 @
Input        Output6 B! e- J: \; f" `! P3 ^
PreviousOutPoint={
3 Q7 F3 B: T% N6 y4 R! I1 U6 FTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,7 I/ q2 i4 c' i$ [6 N7 `. f
OutIndex:1}2 \0 v0 D" L6 W5 n) X
SignatureScript =
% b  h8 N9 X2 _8 COP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
: Y/ }$ M4 q0 @( y,Sequence =0xFFFFFFFF, P) y) i5 ~( H: z' f
Value=29910240
% ~1 b3 \% S7 w& t, f# e- sPkScript=
! E! [. _4 e. n( e$ d3 ROP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG2 F6 f1 q) f; v! s, q
PreviousOutPoint={3 @3 ~) X: z: m
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,# y* N! n+ f1 j  E! O, Y9 D
OutIndex:1}
$ |" r& V! c7 ?SignatureScript =NULL,Sequence =0xFFFFFFFF" z) @' h# v7 C- ^7 ?+ r2 G2 k
Value=1200000008 m9 c- i# [  @# |" P
PkScript=
9 q* l+ D4 Y# f' U" I0 q5 GOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG# S$ m5 h! p8 S) l5 O) T% {- m
接下来对这个交易计算Hash,然后进行签名。得到签名结果:3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501 而我们知道这个地址的公钥是:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb
  k3 b: {# D+ E0 \* ?3 Q. j+ W所以签完名后,我们的交易变成:- q$ R, V, m3 L7 j
Input        Output
! D$ A  O# U' L5 X: z, uPreviousOutPoint={6 g5 l3 u9 A  }3 Q* u0 S
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,
; Y) B0 Z) }6 t7 P  ^( n2 `3 TOutIndex:1}. _" m! E( ~, I
SignatureScript =6 j: B$ V0 n$ ^# j
PUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd2! ^- f' |, T: f
2ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]$ E- R0 K3 Z8 l) q" q: O
,Sequence =0xFFFFFFFF
5 ^5 c3 O' F( y8 P4 ~, ZValue=29910240
& A2 l2 h# }) Y6 k, dPkScript=
* o* s" b, r0 p+ N5 i  T, B- COP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
8 b7 }5 @& \0 I; X9 E/ n9 M7 yPreviousOutPoint={! b$ }! w. [. W# z
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
$ N7 d) w8 R  w" ~6 k5 [- y9 \' mOutIndex:1}- N! m- T1 S# j& n6 ]
SignatureScript =NULL,Sequence =0xFFFFFFFF# Y, h, _# X1 |+ E1 O# K( j
Value=120000000
4 u5 K1 w- K9 b# L0 HPkScript=
. f3 l1 W1 E& ?  gOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG' B# r3 @( B7 Y1 p  T# ?
这才只是完成了第一个Input的签名,接下来我们再对第二个Input进行签名,同样的道理,我们需要制造一个交易的副本,然后把第一个Input的SignatureScript清空,然后给第二个Input的SignatureScript赋值:
; j( v% t+ |  X3 b4 ^Input        Output& a! y2 p+ h: N' P( R
PreviousOutPoint={4 S$ u) _9 ]' C6 x
TxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,. B1 \/ m( ~* k( n# s% [0 q; Z. H
OutIndex:1}. O: j5 o# h( I2 d& x. q7 s
SignatureScript =NULL,Sequence =0xFFFFFFFF
) l( b, T( N% G  w: kValue=29910240
3 V' q: X( J- U; C: hPkScript=+ l$ N  \! W& u3 n# X8 q
OP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
3 D4 K, ~8 l, ?' Q$ e" }# ^3 x( fPreviousOutPoint={* t% W$ `7 O7 n$ o* w' |
TxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,
- Z$ `6 \/ i: g( e% G6 COutIndex:1}
0 ~( c5 ]6 [% M# vSignatureScript =OP_DUP OP_HASH160 PUSHDATA(20) b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG
! c" N2 w% w, ^- ~,Sequence =0xFFFFFFFF' {9 Z5 W: g- _
Value=120000000, Z8 t* g$ @6 ^. K* m) r5 d4 D
PkScript=
5 x* T- ?3 ^& dOP_DUP OP_HASH160 PUSHDATA(20)be09abcbfda1f2c26899f062979ab0708731235a OP_EQUALVERIFY OP_CHECKSIG
, }; g) @4 Q, R9 j( o! Y/ y8 h0 r& W显然这个副本与第一个签名时的数据是不一样的,所以签名结果也不一样,最终签名结果为:30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501,当然我们因为是同一个地址,所以公钥是一样的:038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb
; L! T4 E' C9 v我们把这个签名和公钥再放回原始交易中,就变成我们需要的完整签名的交易:4 X2 ~3 [3 Y- |9 D! m3 m
Input        Output$ M- d* y2 E. r
PreviousOutPoint={
$ ^$ Z# ~+ b* ?) Y$ S4 ~3 BTxHash:1dda832890f85288fec616ef1f4113c0c86b7bf36b560ea244fd8a6ed12ada52,! ?* z5 E4 M+ x  F- Q
OutIndex:1}
4 z0 D5 G9 }& x4 P8 nSignatureScript =PUSHDATA(72)[3045022100c435eb458b295381d6e1f489b8683d1b10ecad0a7691949a4ae7ffee74bd22ae022031e47b9ebed5b90f6d51cd05e6f53bdc59f5d6d754aff14a88a6e8659b5fdad501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]2 i( F# }) A( D7 g8 R1 W  W
,Sequence =0xFFFFFFFF# Q8 E9 s' {4 ?4 j# \- K1 u
Value=29910240
# I* q, R( G8 I/ j/ j& w/ }# {4 YPkScript=
6 E9 [7 T) W0 }' N* u! d3 M7 DOP_DUP OP_HASH160 PUSHDATA(20)b5407cec767317d41442aab35bad2712626e17ca OP_EQUALVERIFY OP_CHECKSIG, A- Z( p1 ^; j8 E" a6 k
PreviousOutPoint={
9 M5 \1 _7 g8 w' `: m5 LTxHash:24f284aed2b9dbc19f0d435b1fe1ee3b3ddc763f28ca28bad798d22b6bea0c66,, @4 [5 |% R3 ]/ q: b$ o" a* y3 F
OutIndex:1}& P2 P! p5 U8 P# g) M7 u
SignatureScript =PUSHDATA(71)[30440220196bce75f0a25ac8afa7218aefc86cba3924845450f3d311c89e9c2a3438a99c0220230bed598a610be971ca49690f4b42ac2acfa80c09d4cbabd278b03c824af14501] PUSHDATA(33)[038cc8c907b29a58b00f8c2590303bfc93c69d773b9da204337678865ee0cafadb]
1 Z, u7 J4 @2 l- R( m: _& v,Sequence =0xFFFFFFFF
9 D' M5 P! e, j这就是一个真实完整的交易了,接下来就可以通过P2P网络发送该交易,并最终被矿工打包确认。6 _, w$ v6 c% w5 P. I
总结, u2 Y; V5 T, U2 l( e
实际上在比特币的源码中比我上面说的还要复杂一些,还涉及到这个hash是对整个交易进行SigHashAll还是SigHashSingle或者SigHashNone,这些都是很特殊的情况,一般的比特币钱包也不支持,具体可以参加精通比特币书中的介绍:6.5.3签名哈希类型( SIGHASH)& [9 f9 {5 M" p% b1 Z0 \, z- i3 O
普通来说,我们要对一笔交易进行签名或者验签,就是把当前Input中的解锁脚本替换成锁定脚本,而其他Input的解锁脚本情况,然后计算Hash和签名!6 c, ^& ~! q. f5 @- K( P
其实我还是有点不明白,为什么比特币中不直接对没有任何解锁脚本的RawTransaction进行签名呢?而是非要加上锁定脚本来签名?不知道这里面有什么更深的考虑。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

V刘晨曦 初中生
  • 粉丝

    0

  • 关注

    3

  • 主题

    14