forked from btcsuite/btcwire
/
msgtx.go
393 lines (335 loc) · 9.21 KB
/
msgtx.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
// Copyright (c) 2013 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package btcwire
import (
"bytes"
"io"
)
// MaxTxInSequenceNum is the maximum sequence number the sequence field
// of a transaction input can be.
const MaxTxInSequenceNum uint32 = 0xffffffff
// Outpoint defines a bitcoin data type that is used to track previous
// transaction outputs.
type OutPoint struct {
Hash ShaHash
Index uint32
}
// NewOutPoint returns a new bitcoin transaction outpoint point with the
// provided hash and index.
func NewOutPoint(hash *ShaHash, index uint32) *OutPoint {
return &OutPoint{
Hash: *hash,
Index: index,
}
}
// TxIn defines a bitcoin transaction input.
type TxIn struct {
PreviousOutpoint OutPoint
SignatureScript []byte
Sequence uint32
}
// NewTxIn returns a new bitcoin transaction input with the provided
// previous outpoint point and signature script with a default sequence of
// MaxTxInSequenceNum.
func NewTxIn(prevOut *OutPoint, signatureScript []byte) *TxIn {
return &TxIn{
PreviousOutpoint: *prevOut,
SignatureScript: signatureScript,
Sequence: MaxTxInSequenceNum,
}
}
// TxOut defines a bitcoin transaction output.
type TxOut struct {
Value int64
PkScript []byte
}
// NewTxOut returns a new bitcoin transaction output with the provided
// transaction value and public key script.
func NewTxOut(value int64, pkScript []byte) *TxOut {
return &TxOut{
Value: value,
PkScript: pkScript,
}
}
// MsgTx implements the Message interface and represents a bitcoin tx message.
// It is used to deliver transaction information in response to a getdata
// message (MsgGetData) for a given transaction.
//
// Use the AddTxIn and AddTxOut functions to build up the list of transaction
// inputs and outputs.
type MsgTx struct {
Version uint32
TxIn []*TxIn
TxOut []*TxOut
LockTime uint32
}
// AddTxIn adds a transaction input to the message.
func (msg *MsgTx) AddTxIn(ti *TxIn) {
msg.TxIn = append(msg.TxIn, ti)
}
// AddTxOut adds a transaction output to the message.
func (msg *MsgTx) AddTxOut(to *TxOut) {
msg.TxOut = append(msg.TxOut, to)
}
// TxSha generates the ShaHash name for the transaction.
func (tx *MsgTx) TxSha(pver uint32) (ShaHash, error) {
// Encode the transaction and calculate double sha256 on the result.
// Ignore the error returns since the only way the encode could fail
// is being out of memory or due to nil pointers, both of which would
// cause a run-time panic. Also, SetBytes can't fail here due to the
// fact DoubleSha256 always returns a []byte of the right size
// regardless of input.
var buf bytes.Buffer
var sha ShaHash
_ = tx.BtcEncode(&buf, pver)
_ = sha.SetBytes(DoubleSha256(buf.Bytes()))
// Even though this function can't currently fail, it still returns
// a potential error to help future proof the API should a failure
// become possible.
return sha, nil
}
// Copy creates a deep copy of a transaction so that the original does not get
// modified when the copy is manipulated.
func (tx *MsgTx) Copy() *MsgTx {
// Create new tx and start by copying primitive values.
newTx := MsgTx{
Version: tx.Version,
LockTime: tx.LockTime,
}
// Deep copy the old TxIn data.
for _, oldTxIn := range tx.TxIn {
// Deep copy the old previous outpoint.
oldOutPoint := oldTxIn.PreviousOutpoint
newOutPoint := OutPoint{}
newOutPoint.Hash.SetBytes(oldOutPoint.Hash[:])
newOutPoint.Index = oldOutPoint.Index
// Deep copy the old signature script.
var newScript []byte
oldScript := oldTxIn.SignatureScript
oldScriptLen := len(oldScript)
if oldScriptLen > 0 {
newScript = make([]byte, oldScriptLen, oldScriptLen)
copy(newScript, oldScript[:oldScriptLen])
}
// Create new txIn with the deep copied data and append it to
// new Tx.
newTxIn := TxIn{
PreviousOutpoint: newOutPoint,
SignatureScript: newScript,
Sequence: oldTxIn.Sequence,
}
newTx.TxIn = append(newTx.TxIn, &newTxIn)
}
// Deep copy the old TxOut data.
for _, oldTxOut := range tx.TxOut {
// Deep copy the old PkScript
var newScript []byte
oldScript := oldTxOut.PkScript
oldScriptLen := len(oldScript)
if oldScriptLen > 0 {
newScript = make([]byte, oldScriptLen, oldScriptLen)
copy(newScript, oldScript[:oldScriptLen])
}
// Create new txOut with the deep copied data and append it to
// new Tx.
newTxOut := TxOut{
Value: oldTxOut.Value,
PkScript: newScript,
}
newTx.TxOut = append(newTx.TxOut, &newTxOut)
}
return &newTx
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32) error {
err := readElement(r, &msg.Version)
if err != nil {
return err
}
count, err := readVarInt(r, pver)
if err != nil {
return err
}
for i := uint64(0); i < count; i++ {
ti := TxIn{}
err = readTxIn(r, pver, msg.Version, &ti)
if err != nil {
return err
}
msg.TxIn = append(msg.TxIn, &ti)
}
count, err = readVarInt(r, pver)
if err != nil {
return err
}
for i := uint64(0); i < count; i++ {
to := TxOut{}
err = readTxOut(r, pver, msg.Version, &to)
if err != nil {
return err
}
msg.TxOut = append(msg.TxOut, &to)
}
err = readElement(r, &msg.LockTime)
if err != nil {
return err
}
return nil
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32) error {
err := writeElement(w, msg.Version)
if err != nil {
return err
}
count := uint64(len(msg.TxIn))
err = writeVarInt(w, pver, count)
if err != nil {
return err
}
for _, ti := range msg.TxIn {
err = writeTxIn(w, pver, msg.Version, ti)
if err != nil {
return err
}
}
count = uint64(len(msg.TxOut))
err = writeVarInt(w, pver, count)
if err != nil {
return err
}
for _, to := range msg.TxOut {
err = writeTxOut(w, pver, to)
if err != nil {
return err
}
}
err = writeElement(w, msg.LockTime)
if err != nil {
return err
}
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgTx) Command() string {
return cmdTx
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 {
return maxMessagePayload
}
// NewMsgTx returns a new bitcoin tx message that conforms to the Message
// interface. The return instance has a default version of TxVersion and there
// are no transaction inputs or outputs. Also, the lock time is set to zero
// to indicate the transaction is valid immediately as opposed to some time in
// future.
func NewMsgTx() *MsgTx {
return &MsgTx{Version: TxVersion}
}
// readOutPoint reads the next sequence of bytes from r as an OutPoint.
func readOutPoint(r io.Reader, pver uint32, version uint32, op *OutPoint) error {
err := readElements(r, &op.Hash, &op.Index)
if err != nil {
return err
}
return nil
}
// writeOutPoint encodes op to the bitcoin protocol encoding for an OutPoint
// to w.
func writeOutPoint(w io.Writer, pver uint32, version uint32, op *OutPoint) error {
err := writeElements(w, op.Hash, op.Index)
if err != nil {
return err
}
return nil
}
// readTxIn reads the next sequence of bytes from r as a transaction input
// (TxIn).
func readTxIn(r io.Reader, pver uint32, version uint32, ti *TxIn) error {
op := OutPoint{}
err := readOutPoint(r, pver, version, &op)
if err != nil {
return err
}
ti.PreviousOutpoint = op
count, err := readVarInt(r, pver)
if err != nil {
return err
}
b := make([]byte, count)
err = readElement(r, b)
if err != nil {
return err
}
ti.SignatureScript = b
err = readElement(r, &ti.Sequence)
if err != nil {
return err
}
return nil
}
// writeTxIn encodes ti to the bitcoin protocol encoding for a transaction
// input (TxIn) to w.
func writeTxIn(w io.Writer, pver uint32, version uint32, ti *TxIn) error {
err := writeOutPoint(w, pver, version, &ti.PreviousOutpoint)
if err != nil {
return err
}
slen := uint64(len(ti.SignatureScript))
err = writeVarInt(w, pver, slen)
if err != nil {
return err
}
b := []byte(ti.SignatureScript)
_, err = w.Write(b)
if err != nil {
return err
}
err = writeElement(w, &ti.Sequence)
if err != nil {
return err
}
return nil
}
// readTxOut reads the next sequence of bytes from r as a transaction output
// (TxOut).
func readTxOut(r io.Reader, pver uint32, version uint32, to *TxOut) error {
err := readElement(r, &to.Value)
if err != nil {
return err
}
slen, err := readVarInt(r, pver)
if err != nil {
return err
}
b := make([]byte, slen)
err = readElement(r, b)
if err != nil {
return err
}
to.PkScript = b
return nil
}
// writeTxOut encodes to into the bitcoin protocol encoding for a transaction
// output (TxOut) to w.
func writeTxOut(w io.Writer, pver uint32, to *TxOut) error {
err := writeElement(w, to.Value)
if err != nil {
return err
}
pkLen := uint64(len(to.PkScript))
err = writeVarInt(w, pver, pkLen)
if err != nil {
return err
}
err = writeElement(w, to.PkScript)
if err != nil {
return err
}
return nil
}