-
Notifications
You must be signed in to change notification settings - Fork 26
/
tx.go
596 lines (495 loc) · 16.4 KB
/
tx.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
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
package bt
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"log"
"github.com/libsv/go-bk/crypto"
"github.com/libsv/go-bt/v2/bscript"
)
/*
General format of a Bitcoin transaction (inside a block)
--------------------------------------------------------
Field Description Size
Version no currently 1 4 bytes
In-counter positive integer VI = VarInt 1 - 9 bytes
list of Inputs the first input of the first transaction is also called "coinbase" <in-counter>-many Inputs
(its content was ignored in earlier versions)
Out-counter positive integer VI = VarInt 1 - 9 bytes
list of Outputs the Outputs of the first transaction spend the mined <out-counter>-many Outputs
bitcoins for the block
lock_time if non-zero and sequence numbers are < 0xFFFFFFFF: block height or 4 bytes
timestamp when transaction is final
--------------------------------------------------------
*/
// Tx wraps a bitcoin transaction
//
// DO NOT CHANGE ORDER - Optimised memory via malign
type Tx struct {
Inputs []*Input `json:"inputs"`
Outputs []*Output `json:"outputs"`
Version uint32 `json:"version"`
LockTime uint32 `json:"locktime"`
}
// Txs a collection of *bt.Tx.
type Txs []*Tx
// NewTx creates a new transaction object with default values.
func NewTx() *Tx {
return &Tx{Version: 1, LockTime: 0, Inputs: make([]*Input, 0)}
}
// NewTxFromString takes a toBytesHelper string representation of a bitcoin transaction
// and returns a Tx object.
func NewTxFromString(str string) (*Tx, error) {
bb, err := hex.DecodeString(str)
if err != nil {
return nil, err
}
return NewTxFromBytes(bb)
}
// NewTxFromBytes takes an array of bytes, constructs a Tx and returns it.
// This function assumes that the byte slice contains exactly 1 transaction.
func NewTxFromBytes(b []byte) (*Tx, error) {
tx, used, err := NewTxFromStream(b)
if err != nil {
return nil, err
}
if used != len(b) {
return nil, ErrNLockTimeLength
}
return tx, nil
}
// NewTxFromStream takes an array of bytes and constructs a Tx from it, returning the Tx and the bytes used.
// Despite the name, this is not actually reading a stream in the true sense: it is a byte slice that contains
// many transactions one after another.
func NewTxFromStream(b []byte) (*Tx, int, error) {
tx := Tx{}
bytesRead, err := tx.ReadFrom(bytes.NewReader(b))
return &tx, int(bytesRead), err
}
// ReadFrom reads from the `io.Reader` into the `bt.Tx`.
func (tx *Tx) ReadFrom(r io.Reader) (int64, error) {
*tx = Tx{}
var bytesRead int64
// Define n64 and err here to avoid linter complaining about shadowing variables.
var n64 int64
var err error
version := make([]byte, 4)
n, err := io.ReadFull(r, version)
bytesRead += int64(n)
if err != nil {
return bytesRead, err
}
tx.Version = binary.LittleEndian.Uint32(version)
extended := false
var inputCount VarInt
n64, err = inputCount.ReadFrom(r)
bytesRead += n64
if err != nil {
return bytesRead, err
}
var outputCount VarInt
locktime := make([]byte, 4)
// ----------------------------------------------------------------------------------
// If the inputCount is 0, we may be parsing an incomplete transaction, or we may be
// both of these cases without needing to rewind (peek) the incoming stream of bytes.
// ----------------------------------------------------------------------------------
if inputCount == 0 {
n64, err = outputCount.ReadFrom(r)
bytesRead += n64
if err != nil {
return bytesRead, err
}
if outputCount == 0 {
// Read in lock time
n, err = io.ReadFull(r, locktime)
bytesRead += int64(n)
if err != nil {
return bytesRead, err
}
if binary.BigEndian.Uint32(locktime) != 0xEF {
tx.LockTime = binary.LittleEndian.Uint32(locktime)
return bytesRead, nil
}
extended = true
n64, err = inputCount.ReadFrom(r)
bytesRead += n64
if err != nil {
return bytesRead, err
}
}
}
// ----------------------------------------------------------------------------------
// If we have not returned from the previous block, we will have detected a sane
// transaction and we will know if it is extended format or not.
// We can now proceed with reading the rest of the transaction.
// ----------------------------------------------------------------------------------
// create Inputs
for i := uint64(0); i < uint64(inputCount); i++ {
input := &Input{}
n64, err = input.readFrom(r, extended)
bytesRead += n64
if err != nil {
return bytesRead, err
}
tx.Inputs = append(tx.Inputs, input)
}
if inputCount > 0 || extended {
// Re-read the actual output count...
n64, err = outputCount.ReadFrom(r)
bytesRead += n64
if err != nil {
return bytesRead, err
}
}
for i := uint64(0); i < uint64(outputCount); i++ {
output := new(Output)
n64, err = output.ReadFrom(r)
bytesRead += n64
if err != nil {
return bytesRead, err
}
tx.Outputs = append(tx.Outputs, output)
}
n, err = io.ReadFull(r, locktime)
bytesRead += int64(n)
if err != nil {
return bytesRead, err
}
tx.LockTime = binary.LittleEndian.Uint32(locktime)
return bytesRead, nil
}
// ReadFrom txs from a block in a `bt.Txs`. This assumes a preceding varint detailing
// the total number of txs that the reader will provide.
func (tt *Txs) ReadFrom(r io.Reader) (int64, error) {
var bytesRead int64
var txCount VarInt
n, err := txCount.ReadFrom(r)
bytesRead += n
if err != nil {
return bytesRead, err
}
*tt = make([]*Tx, txCount)
for i := uint64(0); i < uint64(txCount); i++ {
tx := new(Tx)
n, err := tx.ReadFrom(r)
bytesRead += n
if err != nil {
return bytesRead, err
}
(*tt)[i] = tx
}
return bytesRead, nil
}
// HasDataOutputs returns true if the transaction has
// at least one data (OP_RETURN) output in it.
func (tx *Tx) HasDataOutputs() bool {
for _, out := range tx.Outputs {
if out.LockingScript.IsData() {
return true
}
}
return false
}
// InputIdx will return the input at the specified index.
//
// This will consume an overflow error and simply return nil if the input
// isn't found at the index.
func (tx *Tx) InputIdx(i int) *Input {
if i > tx.InputCount()-1 {
return nil
}
return tx.Inputs[i]
}
// OutputIdx will return the output at the specified index.
//
// This will consume an overflow error and simply return nil if the output
// isn't found at the index.
func (tx *Tx) OutputIdx(i int) *Output {
if i > tx.OutputCount()-1 {
return nil
}
return tx.Outputs[i]
}
// IsCoinbase determines if this transaction is a coinbase by
// checking if the tx input is a standard coinbase input.
func (tx *Tx) IsCoinbase() bool {
if len(tx.Inputs) != 1 {
return false
}
cbi := make([]byte, 32)
if !bytes.Equal(tx.Inputs[0].PreviousTxID(), cbi) {
return false
}
if tx.Inputs[0].PreviousTxOutIndex == DefaultSequenceNumber || tx.Inputs[0].SequenceNumber == DefaultSequenceNumber {
return true
}
return false
}
// TxIDBytes returns the transaction ID of the transaction as bytes
// (which is also the transaction hash).
func (tx *Tx) TxIDBytes() []byte {
return ReverseBytes(crypto.Sha256d(tx.Bytes()))
}
// TxID returns the transaction ID of the transaction
// (which is also the transaction hash).
func (tx *Tx) TxID() string {
return hex.EncodeToString(ReverseBytes(crypto.Sha256d(tx.Bytes())))
}
// String encodes the transaction into a hex string.
func (tx *Tx) String() string {
return hex.EncodeToString(tx.Bytes())
}
// IsValidTxID will check that the txid bytes are valid.
//
// A txid should be of 32 bytes length.
func IsValidTxID(txid []byte) bool {
return len(txid) == 32
}
// Bytes encodes the transaction into a byte array.
// See https://chainquery.com/bitcoin-cli/decoderawtransaction
func (tx *Tx) Bytes() []byte {
return tx.toBytesHelper(0, nil, false)
}
// ExtendedBytes outputs the transaction into a byte array in extended format
// (with PreviousTxSatoshis and PreviousTXScript included)
func (tx *Tx) ExtendedBytes() []byte {
return tx.toBytesHelper(0, nil, true)
}
// BytesWithClearedInputs encodes the transaction into a byte array but clears its Inputs first.
// This is used when signing transactions.
func (tx *Tx) BytesWithClearedInputs(index int, lockingScript []byte) []byte {
return tx.toBytesHelper(index, lockingScript, false)
}
// Clone returns a clone of the tx
func (tx *Tx) Clone() *Tx {
// Ignore err as byte slice passed in is created from valid tx
clone, err := NewTxFromBytes(tx.Bytes())
if err != nil {
log.Fatal(err)
}
for i, input := range tx.Inputs {
clone.Inputs[i].PreviousTxSatoshis = input.PreviousTxSatoshis
clone.Inputs[i].PreviousTxScript = input.PreviousTxScript
}
return clone
}
// NodeJSON returns a wrapped *bt.Tx for marshalling/unmarshalling into a node tx format.
//
// Marshalling usage example:
//
// bb, err := json.Marshal(tx.NodeJSON())
//
// Unmarshalling usage example:
//
// tx := bt.NewTx()
// if err := json.Unmarshal(bb, tx.NodeJSON()); err != nil {}
func (tx *Tx) NodeJSON() interface{} {
return &nodeTxWrapper{Tx: tx}
}
// NodeJSON returns a wrapped bt.Txs for marshalling/unmarshalling into a node tx format.
//
// Marshalling usage example:
//
// bb, err := json.Marshal(txs.NodeJSON())
//
// Unmarshalling usage example:
//
// var txs bt.Txs
// if err := json.Unmarshal(bb, txs.NodeJSON()); err != nil {}
func (tt *Txs) NodeJSON() interface{} {
return (*nodeTxsWrapper)(tt)
}
func (tx *Tx) toBytesHelper(index int, lockingScript []byte, extended bool) []byte {
h := make([]byte, 0)
h = append(h, LittleEndianBytes(tx.Version, 4)...)
if extended {
h = append(h, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0xEF}...)
}
h = append(h, VarInt(uint64(len(tx.Inputs))).Bytes()...)
for i, in := range tx.Inputs {
s := in.Bytes(lockingScript != nil)
if i == index && lockingScript != nil {
h = append(h, VarInt(uint64(len(lockingScript))).Bytes()...)
h = append(h, lockingScript...)
} else {
h = append(h, s...)
}
if extended {
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, in.PreviousTxSatoshis)
h = append(h, b...)
if in.PreviousTxScript != nil {
l := uint64(len(*in.PreviousTxScript))
h = append(h, VarInt(l).Bytes()...)
h = append(h, *in.PreviousTxScript...)
} else {
h = append(h, 0x00) // The length of the script is zero
}
}
}
h = append(h, VarInt(uint64(len(tx.Outputs))).Bytes()...)
for _, out := range tx.Outputs {
h = append(h, out.Bytes()...)
}
lt := make([]byte, 4)
binary.LittleEndian.PutUint32(lt, tx.LockTime)
return append(h, lt...)
}
// TxSize contains the size breakdown of a transaction
// including the breakdown of data bytes vs standard bytes.
// This information can be used when calculating fees.
type TxSize struct {
// TotalBytes are the amount of bytes for the entire tx.
TotalBytes uint64
// TotalStdBytes are the amount of bytes for the tx minus the data bytes.
TotalStdBytes uint64
// TotalDataBytes is the size in bytes of the op_return / data outputs.
TotalDataBytes uint64
}
// Size will return the size of tx in bytes.
func (tx *Tx) Size() int {
return len(tx.Bytes())
}
// SizeWithTypes will return the size of tx in bytes
// and include the different data types (std/data/etc.).
func (tx *Tx) SizeWithTypes() *TxSize {
totBytes := tx.Size()
// calculate data outputs
dataLen := 0
for _, d := range tx.Outputs {
if d.LockingScript.IsData() {
dataLen += len(*d.LockingScript)
}
}
return &TxSize{
TotalBytes: uint64(totBytes),
TotalStdBytes: uint64(totBytes - dataLen),
TotalDataBytes: uint64(dataLen),
}
}
// EstimateSize will return the size of tx in bytes and will add 107 bytes
// to the unlocking script of any unsigned inputs (only P2PKH for now) found
// to give a final size estimate of the tx size.
func (tx *Tx) EstimateSize() (int, error) {
tempTx, err := tx.estimatedFinalTx()
if err != nil {
return 0, err
}
return tempTx.Size(), nil
}
// EstimateSizeWithTypes will return the size of tx in bytes, including the
// different data types (std/data/etc.), and will add 107 bytes to the unlocking
// script of any unsigned inputs (only P2PKH for now) found to give a final size
// estimate of the tx size.
func (tx *Tx) EstimateSizeWithTypes() (*TxSize, error) {
tempTx, err := tx.estimatedFinalTx()
if err != nil {
return nil, err
}
return tempTx.SizeWithTypes(), nil
}
func (tx *Tx) estimatedFinalTx() (*Tx, error) {
tempTx := tx.Clone()
for i, in := range tempTx.Inputs {
if in.PreviousTxScript == nil {
return nil, fmt.Errorf("%w at index %d in order to calc expected UnlockingScript", ErrEmptyPreviousTxScript, i)
}
if !(in.PreviousTxScript.IsP2PKH() || in.PreviousTxScript.IsP2PKHInscription()) {
return nil, ErrUnsupportedScript
}
if in.UnlockingScript == nil || len(*in.UnlockingScript) == 0 {
//nolint:lll // insert dummy p2pkh unlocking script (sig + pubkey)
dummyUnlockingScript, _ := hex.DecodeString("4830450221009c13cbcbb16f2cfedc7abf3a4af1c3fe77df1180c0e7eee30d9bcc53ebda39da02207b258005f1bc3cf9dffa06edb358d6db2bcfc87f50516fac8e3f4686fc2a03df412103107feff22788a1fc8357240bf450fd7bca4bd45d5f8bac63818c5a7b67b03876")
in.UnlockingScript = bscript.NewFromBytes(dummyUnlockingScript)
}
}
return tempTx, nil
}
// TxFees is returned when CalculateFee is called and contains
// a breakdown of the fees including the total and the size breakdown of
// the tx in bytes.
type TxFees struct {
// TotalFeePaid is the total amount of fees this tx will pay.
TotalFeePaid uint64
// StdFeePaid is the amount of fee to cover the standard inputs and outputs etc.
StdFeePaid uint64
// DataFeePaid is the amount of fee to cover the op_return data outputs.
DataFeePaid uint64
}
// IsFeePaidEnough will calculate the fees that this transaction is paying
// including the individual fee types (std/data/etc.).
func (tx *Tx) IsFeePaidEnough(fees *FeeQuote) (bool, error) {
expFeesPaid, err := tx.feesPaid(tx.SizeWithTypes(), fees)
if err != nil {
return false, err
}
totalInputSatoshis := tx.TotalInputSatoshis()
totalOutputSatoshis := tx.TotalOutputSatoshis()
if totalInputSatoshis < totalOutputSatoshis {
return false, nil
}
actualFeePaid := totalInputSatoshis - totalOutputSatoshis
return actualFeePaid >= expFeesPaid.TotalFeePaid, nil
}
// EstimateIsFeePaidEnough will calculate the fees that this transaction is paying
// including the individual fee types (std/data/etc.), and will add 107 bytes to the unlocking
// script of any unsigned inputs (only P2PKH for now) found to give a final size
// estimate of the tx size for fee calculation.
func (tx *Tx) EstimateIsFeePaidEnough(fees *FeeQuote) (bool, error) {
tempTx, err := tx.estimatedFinalTx()
if err != nil {
return false, err
}
expFeesPaid, err := tempTx.feesPaid(tempTx.SizeWithTypes(), fees)
if err != nil {
return false, err
}
totalInputSatoshis := tempTx.TotalInputSatoshis()
totalOutputSatoshis := tempTx.TotalOutputSatoshis()
if totalInputSatoshis < totalOutputSatoshis {
return false, nil
}
actualFeePaid := totalInputSatoshis - totalOutputSatoshis
return actualFeePaid >= expFeesPaid.TotalFeePaid, nil
}
// EstimateFeesPaid will estimate how big the tx will be when finalised
// by estimating input unlocking scripts that have not yet been filled
// including the individual fee types (std/data/etc.).
func (tx *Tx) EstimateFeesPaid(fees *FeeQuote) (*TxFees, error) {
size, err := tx.EstimateSizeWithTypes()
if err != nil {
return nil, err
}
return tx.feesPaid(size, fees)
}
func (tx *Tx) feesPaid(size *TxSize, fees *FeeQuote) (*TxFees, error) {
// get fees
stdFee, err := fees.Fee(FeeTypeStandard)
if err != nil {
return nil, err
}
dataFee, err := fees.Fee(FeeTypeData)
if err != nil {
return nil, err
}
txFees := &TxFees{
StdFeePaid: size.TotalStdBytes * uint64(stdFee.MiningFee.Satoshis) / uint64(stdFee.MiningFee.Bytes),
DataFeePaid: size.TotalDataBytes * uint64(dataFee.MiningFee.Satoshis) / uint64(dataFee.MiningFee.Bytes),
}
txFees.TotalFeePaid = txFees.StdFeePaid + txFees.DataFeePaid
return txFees, nil
}
func (tx *Tx) estimateDeficit(fees *FeeQuote) (uint64, error) {
totalInputSatoshis := tx.TotalInputSatoshis()
totalOutputSatoshis := tx.TotalOutputSatoshis()
expFeesPaid, err := tx.EstimateFeesPaid(fees)
if err != nil {
return 0, err
}
if totalInputSatoshis > totalOutputSatoshis+expFeesPaid.TotalFeePaid {
return 0, nil
}
return totalOutputSatoshis + expFeesPaid.TotalFeePaid - totalInputSatoshis, nil
}