• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

handshake-org / hsd / 11142522588

02 Oct 2024 10:56AM UTC coverage: 70.04% (+0.007%) from 70.033%
11142522588

push

github

nodech
Merge PR #902 from 'nodech/update-types'

7761 of 12916 branches covered (60.09%)

Branch coverage included in aggregate %.

67 of 92 new or added lines in 25 files covered. (72.83%)

8 existing lines in 5 files now uncovered.

24790 of 33559 relevant lines covered (73.87%)

34019.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.66
/lib/primitives/tx.js
1
/*!
2
 * tx.js - transaction object for hsd
3
 * Copyright (c) 2017-2018, Christopher Jeffrey (MIT License).
4
 * https://github.com/handshake-org/hsd
5
 */
6

7
'use strict';
8

9
const assert = require('bsert');
70✔
10
const bio = require('bufio');
70✔
11
const blake2b = require('bcrypto/lib/blake2b');
70✔
12
const secp256k1 = require('bcrypto/lib/secp256k1');
70✔
13
const {BufferSet} = require('buffer-map');
70✔
14
const util = require('../utils/util');
70✔
15
const Amount = require('../ui/amount');
70✔
16
const Network = require('../protocol/network');
70✔
17
const Script = require('../script/script');
70✔
18
const Input = require('./input');
70✔
19
const Output = require('./output');
70✔
20
const Outpoint = require('./outpoint');
70✔
21
const rules = require('../covenants/rules');
70✔
22
const InvItem = require('./invitem');
70✔
23
const consensus = require('../protocol/consensus');
70✔
24
const policy = require('../protocol/policy');
70✔
25
const ScriptError = require('../script/scripterror');
70✔
26
const {OwnershipProof} = require('../covenants/ownership');
70✔
27
const AirdropProof = require('../primitives/airdropproof');
70✔
28
const {encoding} = bio;
70✔
29
const {hashType} = Script;
70✔
30

31
/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */
32
/** @typedef {import('../types').SighashType} SighashType */
33
/** @typedef {import('../types').Hash} Hash */
34
/** @typedef {import('../types').Amount} AmountValue */
35
/** @typedef {import('../types').Rate} Rate */
36
/** @typedef {import('../types').VerifyFlags} VerifyFlags */
37
/** @typedef {import('../types').HexHash} HexHash */
38
/** @typedef {import('../types').BufioWriter} BufioWriter */
39
/** @typedef {import('../blockchain/chainentry')} ChainEntry */
40
/** @typedef {import('../covenants/ownership').OwnershipProof} OwnershipProof */
41
/** @typedef {import('../workers/workerpool')} WorkerPool */
42
/** @typedef {import('../coins/coinview')} CoinView */
43
/** @typedef {import('./covenant')} Covenant */
44
/** @typedef {import('./coin')} Coin */
45
/** @typedef {import('./address')} Address */
46

47
/**
48
 * TX
49
 * A static transaction object.
50
 * @alias module:primitives.TX
51
 * @property {Number} version
52
 * @property {Input[]} inputs
53
 * @property {Output[]} outputs
54
 * @property {Number} locktime
55
 */
56

57
class TX extends bio.Struct {
58
  /**
59
   * Create a transaction.
60
   * @constructor
61
   * @param {Object?} [options]
62
   */
63

64
  constructor(options) {
65
    super();
147,409✔
66

67
    this.version = 0;
147,409✔
68
    /** @type {Input[]} */
69
    this.inputs = [];
147,409✔
70
    /** @type {Output[]} */
71
    this.outputs = [];
147,409✔
72
    this.locktime = 0;
147,409✔
73

74
    this.mutable = false;
147,409✔
75

76
    /** @type {Hash?} */
77
    this._hash = null;
147,409✔
78
    /** @type {Hash?} */
79
    this._wdhash = null;
147,409✔
80
    /** @type {Hash?} */
81
    this._whash = null;
147,409✔
82

83
    /** @type {Buffer?} */
84
    this._raw = null;
147,409✔
85
    /** @type {Sizes?} */
86
    this._sizes = null;
147,409✔
87

88
    /** @type {Hash?} */
89
    this._hashPrevouts = null;
147,409✔
90
    /** @type {Hash?} */
91
    this._hashSequence = null;
147,409✔
92
    /** @type {Hash?} */
93
    this._hashOutputs = null;
147,409✔
94

95
    if (options)
147,409✔
96
      this.fromOptions(options);
1,787✔
97
  }
98

99
  /**
100
   * Inject properties from options object.
101
   * @param {Object} options
102
   */
103

104
  fromOptions(options) {
105
    assert(options, 'TX data is required.');
1,787✔
106

107
    if (options.version != null) {
1,787✔
108
      assert((options.version >>> 0) === options.version,
1,770✔
109
        'Version must be a uint32.');
110
      this.version = options.version;
1,770✔
111
    }
112

113
    if (options.inputs) {
1,787!
114
      assert(Array.isArray(options.inputs), 'Inputs must be an array.');
1,787✔
115
      for (const input of options.inputs)
1,787✔
116
        this.inputs.push(new Input(input));
1,799✔
117
    }
118

119
    if (options.outputs) {
1,787!
120
      assert(Array.isArray(options.outputs), 'Outputs must be an array.');
1,787✔
121
      for (const output of options.outputs)
1,787✔
122
        this.outputs.push(new Output(output));
1,794✔
123
    }
124

125
    if (options.locktime != null) {
1,787✔
126
      assert((options.locktime >>> 0) === options.locktime,
1,770✔
127
        'Locktime must be a uint32.');
128
      this.locktime = options.locktime;
1,770✔
129
    }
130

131
    return this;
1,787✔
132
  }
133

134
  /**
135
   * Inject properties from tx.
136
   * Used for cloning.
137
   * @param {this} tx
138
   * @returns {this}
139
   */
140

141
  inject(tx) {
142
    this.version = tx.version;
28,454✔
143

144
    for (const input of tx.inputs)
28,454✔
145
      this.inputs.push(input.clone());
57,939✔
146

147
    for (const output of tx.outputs)
28,454✔
148
      this.outputs.push(output.clone());
160,853✔
149

150
    this.locktime = tx.locktime;
28,454✔
151

152
    return this;
28,454✔
153
  }
154

155
  /**
156
   * Clear any cached values.
157
   */
158

159
  refresh() {
160
    this._hash = null;
37,813✔
161
    this._wdhash = null;
37,813✔
162
    this._whash = null;
37,813✔
163

164
    this._raw = null;
37,813✔
165
    this._sizes = null;
37,813✔
166

167
    this._hashPrevouts = null;
37,813✔
168
    this._hashSequence = null;
37,813✔
169
    this._hashOutputs = null;
37,813✔
170

171
    return this;
37,813✔
172
  }
173

174
  /**
175
   * Hash the transaction with the non-witness serialization.
176
   * @returns {Hash}
177
   */
178

179
  hash() {
180
    if (this.mutable)
460,002✔
181
      return this.left();
1,441✔
182

183
    if (!this._hash)
458,561✔
184
      this._hash = this.left();
87,562✔
185

186
    return this._hash;
458,561✔
187
  }
188

189
  /**
190
   * Hash the transaction with the witness
191
   * serialization, return the wtxid (normal
192
   * hash if no witness is present, all zeroes
193
   * if coinbase).
194
   * @returns {Hash} hash
195
   */
196

197
  witnessHash() {
198
    if (this.mutable)
82,543✔
199
      return this.root();
38✔
200

201
    if (!this._whash)
82,505✔
202
      this._whash = this.root();
62✔
203

204
    return this._whash;
82,505✔
205
  }
206

207
  /**
208
   * Calculate the virtual size of the transaction.
209
   * Note that this is cached.
210
   * @returns {Number} vsize
211
   */
212

213
  getVirtualSize() {
214
    const scale = consensus.WITNESS_SCALE_FACTOR;
42,008✔
215
    return (this.getWeight() + scale - 1) / scale | 0;
42,008✔
216
  }
217

218
  /**
219
   * Calculate the virtual size of the transaction
220
   * (weighted against bytes per sigop cost).
221
   * @param {Number} sigops - Sigops cost.
222
   * @returns {Number} vsize
223
   */
224

225
  getSigopsSize(sigops) {
226
    const scale = consensus.WITNESS_SCALE_FACTOR;
1,430✔
227
    const bytes = policy.BYTES_PER_SIGOP;
1,430✔
228
    const weight = Math.max(this.getWeight(), sigops * bytes);
1,430✔
229
    return (weight + scale - 1) / scale | 0;
1,430✔
230
  }
231

232
  /**
233
   * Calculate the weight of the transaction.
234
   * Note that this is cached.
235
   * @returns {Number} weight
236
   */
237

238
  getWeight() {
239
    const sizes = this.getSizes();
84,277✔
240
    return sizes.getWeight();
84,277✔
241
  }
242

243
  /**
244
   * Calculate the real size of the transaction
245
   * with the witness included.
246
   * @returns {Number} size
247
   */
248

249
  getSize() {
250
    const {base, witness} = this.getSizes();
103,325✔
251
    return base + witness;
103,325✔
252
  }
253

254
  /**
255
   * Calculate the size of the transaction
256
   * without the witness.
257
   * with the witness included.
258
   * @returns {Number} size
259
   */
260

261
  getBaseSize() {
262
    const {base} = this.getSizes();
31,851✔
263
    return base;
31,851✔
264
  }
265

266
  /**
267
   * Test whether the transaction has a non-empty witness.
268
   * @returns {Boolean}
269
   */
270

271
  hasWitness() {
272
    for (const {witness} of this.inputs) {
16✔
273
      if (witness.items.length > 0)
16✔
274
        return true;
14✔
275
    }
276

277
    return false;
2✔
278
  }
279

280
  /**
281
   * Get the signature hash of the transaction for signing verifying.
282
   * @param {Number} index - Index of input being signed/verified.
283
   * @param {Script} prev - Previous output script or redeem script
284
   * (in the case of witnesspubkeyhash, this should be the generated
285
   * p2pkh script).
286
   * @param {AmountValue} value - Previous output value.
287
   * @param {SighashType} type - Sighash type.
288
   * @returns {Buffer} Signature hash.
289
   */
290

291
  signatureHash(index, prev, value, type) {
292
    assert(index >= 0 && index < this.inputs.length);
50,737✔
293
    assert(prev instanceof Script);
50,737✔
294
    assert(typeof value === 'number');
50,737✔
295
    assert(typeof type === 'number');
50,737✔
296

297
    let input = this.inputs[index];
50,737✔
298
    let prevouts = consensus.ZERO_HASH;
50,737✔
299
    let sequences = consensus.ZERO_HASH;
50,737✔
300
    let outputs = consensus.ZERO_HASH;
50,737✔
301

302
    // SIGHASH_NOINPUT does not commit to "this input's data", meaning the
303
    // outpoint and sequence of this input (only this input) is malleable.
304
    // However, our implementation neglects to also nullify the `prevouts`
305
    // and `sequences` values as specified in the original SIGHASH_NOINPUT BIP
306
    // https://blockstream.com/eltoo.pdf (appendix A).
307
    // This means that our NOINPUT is not useful by itself, because even though
308
    // it removes this input's outpoint from one part of the signed data, it
309
    // leaves it in place in a different part, so it is still committed to by
310
    // the signature.
311
    // Note that eltoo's NOINPUT and it's successor BIP-118 also make committing
312
    // to the witness script optional (in our code that value is called `prev`).
313
    if (type & hashType.NOINPUT)
50,737✔
314
      input = new Input();
9✔
315

316
    if (!(type & hashType.ANYONECANPAY)) {
50,737✔
317
      if (this._hashPrevouts) {
50,721✔
318
        prevouts = this._hashPrevouts;
13,005✔
319
      } else {
320
        const bw = bio.pool(this.inputs.length * 36);
37,716✔
321

322
        for (const input of this.inputs)
37,716✔
323
          input.prevout.write(bw);
4,576,191✔
324

325
        prevouts = blake2b.digest(bw.render());
37,716✔
326

327
        if (!this.mutable)
37,716✔
328
          this._hashPrevouts = prevouts;
8,777✔
329
      }
330
    }
331

332
    if (!(type & hashType.ANYONECANPAY)
50,737✔
333
        && (type & 0x1f) !== hashType.SINGLE
334
        && (type & 0x1f) !== hashType.SINGLEREVERSE
335
        && (type & 0x1f) !== hashType.NONE) {
336
      if (this._hashSequence) {
50,713✔
337
        sequences = this._hashSequence;
13,005✔
338
      } else {
339
        const bw = bio.pool(this.inputs.length * 4);
37,708✔
340

341
        for (const input of this.inputs)
37,708✔
342
          bw.writeU32(input.sequence);
4,576,183✔
343

344
        sequences = blake2b.digest(bw.render());
37,708✔
345

346
        if (!this.mutable)
37,708✔
347
          this._hashSequence = sequences;
8,777✔
348
      }
349
    }
350

351
    if ((type & 0x1f) !== hashType.SINGLE
50,737✔
352
        && (type & 0x1f) !== hashType.SINGLEREVERSE
353
        && (type & 0x1f) !== hashType.NONE) {
354
      if (this._hashOutputs) {
50,724✔
355
        outputs = this._hashOutputs;
13,005✔
356
      } else {
357
        let size = 0;
37,719✔
358

359
        for (const output of this.outputs)
37,719✔
360
          size += output.getSize();
4,687,469✔
361

362
        const bw = bio.pool(size);
37,719✔
363

364
        for (const output of this.outputs)
37,719✔
365
          output.write(bw);
4,687,469✔
366

367
        outputs = blake2b.digest(bw.render());
37,719✔
368

369
        if (!this.mutable)
37,719✔
370
          this._hashOutputs = outputs;
8,777✔
371
      }
372
    } else if ((type & 0x1f) === hashType.SINGLE) {
13!
373
      if (index < this.outputs.length) {
×
374
        const output = this.outputs[index];
×
375
        outputs = blake2b.digest(output.encode());
×
376
      }
377
    } else if ((type & 0x1f) === hashType.SINGLEREVERSE) {
13!
378
      if (index < this.outputs.length) {
13!
379
        const output = this.outputs[(this.outputs.length - 1) - index];
13✔
380
        outputs = blake2b.digest(output.encode());
13✔
381
      }
382
    }
383

384
    const size = 156 + prev.getVarSize();
50,737✔
385
    const bw = bio.pool(size);
50,737✔
386

387
    bw.writeU32(this.version);
50,737✔
388
    bw.writeBytes(prevouts);
50,737✔
389
    bw.writeBytes(sequences);
50,737✔
390
    bw.writeHash(input.prevout.hash);
50,737✔
391
    bw.writeU32(input.prevout.index);
50,737✔
392
    bw.writeVarBytes(prev.encode());
50,737✔
393
    bw.writeU64(value);
50,737✔
394
    bw.writeU32(input.sequence);
50,737✔
395
    bw.writeBytes(outputs);
50,737✔
396
    bw.writeU32(this.locktime);
50,737✔
397
    bw.writeU32(type);
50,737✔
398

399
    return blake2b.digest(bw.render());
50,737✔
400
  }
401

402
  /**
403
   * Verify signature.
404
   * @param {Number} index
405
   * @param {Script} prev
406
   * @param {AmountValue} value
407
   * @param {Buffer} sig
408
   * @param {Buffer} key
409
   * @returns {Boolean}
410
   */
411

412
  checksig(index, prev, value, sig, key) {
413
    if (sig.length === 0)
×
414
      return false;
×
415

416
    const type = sig[sig.length - 1];
×
417
    const hash = this.signatureHash(index, prev, value, type);
×
418

419
    return secp256k1.verify(hash, sig.slice(0, -1), key);
×
420
  }
421

422
  /**
423
   * Create a signature suitable for inserting into scriptSigs/witnesses.
424
   * @param {Number} index - Index of input being signed.
425
   * @param {Script} prev - Previous output script or redeem script
426
   * (in the case of witnesspubkeyhash, this should be the generated
427
   * p2pkh script).
428
   * @param {AmountValue} value - Previous output value.
429
   * @param {Buffer} key
430
   * @param {SighashType} type
431
   * @returns {Buffer} Signature in DER format.
432
   */
433

434
  signature(index, prev, value, key, type) {
435
    if (type == null)
28,884✔
436
      type = hashType.ALL;
17,248✔
437

438
    const hash = this.signatureHash(index, prev, value, type);
28,884✔
439
    const sig = secp256k1.sign(hash, key);
28,884✔
440
    const bw = bio.write(65);
28,884✔
441

442
    bw.writeBytes(sig);
28,884✔
443
    bw.writeU8(type);
28,884✔
444

445
    return bw.render();
28,884✔
446
  }
447

448
  /**
449
   * Verify all transaction inputs.
450
   * @param {CoinView} view
451
   * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS]
452
   * @throws {ScriptError} on invalid inputs
453
   */
454

455
  check(view, flags) {
456
    if (this.inputs.length === 0)
28,770✔
457
      throw new ScriptError('UNKNOWN_ERROR', 'No inputs.');
1✔
458

459
    if (this.isCoinbase()) {
28,769✔
460
      for (let i = 1; i < this.inputs.length; i++) {
18,698✔
461
        const {witness} = this.inputs[i];
67✔
462

463
        if (witness.items.length !== 1)
67!
464
          throw new ScriptError('UNKNOWN_ERROR', 'Invalid claim proof.');
×
465

466
        if (i >= this.outputs.length)
67!
467
          throw new ScriptError('UNKNOWN_ERROR', 'Invalid claim proof.');
×
468

469
        const output = this.outputs[i];
67✔
470

471
        if (!output.covenant.isClaim()) {
67✔
472
          assert(output.covenant.isNone());
17✔
473

474
          let proof;
475
          try {
17✔
476
            proof = AirdropProof.decode(witness.items[0]);
17✔
477
          } catch (e) {
478
            throw new ScriptError('UNKNOWN_ERROR', 'Invalid airdrop proof.');
×
479
          }
480

481
          if (!proof.isSane())
17!
482
            throw new ScriptError('UNKNOWN_ERROR', 'Non-sane airdrop proof.');
×
483

484
          if (!proof.verify()) {
17✔
485
            throw new ScriptError('UNKNOWN_ERROR',
1✔
486
                                  'Invalid airdrop signature.');
487
          }
488

489
          if (output.value !== proof.getValue() - proof.fee
16!
490
              || output.address.version !== proof.version
491
              || !output.address.hash.equals(proof.address)) {
492
            throw new ScriptError('UNKNOWN_ERROR', 'Invalid airdrop output.');
×
493
          }
494

495
          continue;
16✔
496
        }
497

498
        /** @type {OwnershipProof} */
499
        let proof;
500
        try {
50✔
501
          proof = OwnershipProof.decode(witness.items[0]);
50✔
502
        } catch (e) {
503
          throw new ScriptError('UNKNOWN_ERROR', 'Invalid claim proof.');
×
504
        }
505

506
        if (!proof.isSane())
50!
507
          throw new ScriptError('UNKNOWN_ERROR', 'Non-sane claim proof.');
×
508

509
        if (!proof.verifySignatures())
50✔
510
          throw new ScriptError('UNKNOWN_ERROR', 'Invalid claim signatures.');
2✔
511
      }
512

513
      return;
18,695✔
514
    }
515

516
    for (let i = 0; i < this.inputs.length; i++) {
10,071✔
517
      const {prevout} = this.inputs[i];
23,875✔
518
      const coin = view.getOutput(prevout);
23,875✔
519

520
      if (!coin)
23,875!
521
        throw new ScriptError('UNKNOWN_ERROR', 'No coin available.');
×
522

523
      this.checkInput(i, coin, flags);
23,875✔
524
    }
525
  }
526

527
  /**
528
   * Verify a transaction input.
529
   * @param {Number} index - Index of output being
530
   * verified.
531
   * @param {Coin|Output} coin - Previous output.
532
   * @param {VerifyFlags} [flags=STANDARD_VERIFY_FLAGS]
533
   * @throws {ScriptError} on invalid input
534
   */
535

536
  checkInput(index, coin, flags) {
537
    const input = this.inputs[index];
23,875✔
538

539
    assert(input, 'Input does not exist.');
23,875✔
540
    assert(coin, 'No coin passed.');
23,875✔
541

542
    Script.verify(
23,875✔
543
      input.witness,
544
      coin.address,
545
      this,
546
      index,
547
      coin.value,
548
      flags
549
    );
550
  }
551

552
  /**
553
   * Verify the transaction inputs on the worker pool
554
   * (if workers are enabled).
555
   * @param {CoinView} view
556
   * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS]
557
   * @param {WorkerPool?} [pool]
558
   * @returns {Promise}
559
   */
560

561
  async checkAsync(view, flags, pool) {
562
    if (this.inputs.length === 0)
28,709!
563
      throw new ScriptError('UNKNOWN_ERROR', 'No inputs.');
×
564

565
    if (!pool) {
28,709✔
566
      this.check(view, flags);
4,285✔
567
      return undefined;
4,283✔
568
    }
569

570
    return pool.check(this, view, flags);
24,424✔
571
  }
572

573
  /**
574
   * Verify a transaction input asynchronously.
575
   * @param {Number} index - Index of output being
576
   * verified.
577
   * @param {Coin|Output} coin - Previous output.
578
   * @param {VerifyFlags} [flags=STANDARD_VERIFY_FLAGS]
579
   * @param {WorkerPool?} [pool]
580
   * @returns {Promise}
581
   */
582

583
  async checkInputAsync(index, coin, flags, pool) {
584
    const input = this.inputs[index];
×
585

586
    assert(input, 'Input does not exist.');
×
587
    assert(coin, 'No coin passed.');
×
588

589
    if (!pool) {
×
590
      this.checkInput(index, coin, flags);
×
591
      return undefined;
×
592
    }
593

594
    return pool.checkInput(this, index, coin, flags);
×
595
  }
596

597
  /**
598
   * Verify all transaction inputs.
599
   * @param {CoinView} view
600
   * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS]
601
   * @returns {Boolean} Whether the inputs are valid.
602
   */
603

604
  verify(view, flags) {
605
    try {
6✔
606
      this.check(view, flags);
6✔
607
    } catch (e) {
608
      if (e.type === 'ScriptError')
1!
609
        return false;
1✔
610
      throw e;
×
611
    }
612
    return true;
5✔
613
  }
614

615
  /**
616
   * Verify a transaction input.
617
   * @param {Number} index - Index of output being
618
   * verified.
619
   * @param {Coin|Output} coin - Previous output.
620
   * @param {VerifyFlags} [flags=STANDARD_VERIFY_FLAGS]
621
   * @returns {Boolean} Whether the input is valid.
622
   */
623

624
  verifyInput(index, coin, flags) {
625
    try {
×
626
      this.checkInput(index, coin, flags);
×
627
    } catch (e) {
628
      if (e.type === 'ScriptError')
×
629
        return false;
×
630
      throw e;
×
631
    }
632
    return true;
×
633
  }
634

635
  /**
636
   * Verify the transaction inputs on the worker pool
637
   * (if workers are enabled).
638
   * @param {CoinView} view
639
   * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS]
640
   * @param {WorkerPool?} [pool]
641
   * @returns {Promise}
642
   */
643

644
  async verifyAsync(view, flags, pool) {
645
    try {
28,709✔
646
      await this.checkAsync(view, flags, pool);
28,709✔
647
    } catch (e) {
648
      if (e.type === 'ScriptError')
11!
649
        return false;
11✔
650
      throw e;
×
651
    }
652
    return true;
28,698✔
653
  }
654

655
  /**
656
   * Verify a transaction input asynchronously.
657
   * @param {Number} index - Index of output being
658
   * verified.
659
   * @param {Coin|Output} coin - Previous output.
660
   * @param {VerifyFlags} [flags=STANDARD_VERIFY_FLAGS]
661
   * @param {WorkerPool?} [pool]
662
   * @returns {Promise}
663
   */
664

665
  async verifyInputAsync(index, coin, flags, pool) {
666
    try {
×
NEW
667
      await this.checkInputAsync(index, coin, flags, pool);
×
668
    } catch (e) {
669
      if (e.type === 'ScriptError')
×
670
        return false;
×
671
      throw e;
×
672
    }
673
    return true;
×
674
  }
675

676
  /**
677
   * Test whether the transaction is a coinbase
678
   * by examining the inputs.
679
   * @returns {Boolean}
680
   */
681

682
  isCoinbase() {
683
    return this.inputs.length > 0 && this.inputs[0].prevout.isNull();
561,227✔
684
  }
685

686
  /**
687
   * Calculate the fee for the transaction.
688
   * @param {CoinView} view
689
   * @returns {AmountValue} fee (zero if not all coins are available).
690
   */
691

692
  getFee(view) {
693
    if (!this.hasCoins(view))
34,988✔
694
      return 0;
12,458✔
695

696
    return this.getInputValue(view) - this.getOutputValue();
22,530✔
697
  }
698

699
  /**
700
   * Calculate the total input value.
701
   * @param {CoinView} view
702
   * @returns {AmountValue} value
703
   */
704

705
  getInputValue(view) {
706
    let total = 0;
70,407✔
707

708
    for (const {prevout} of this.inputs) {
70,407✔
709
      const coin = view.getOutput(prevout);
191,866✔
710

711
      if (!coin)
191,866!
712
        return 0;
×
713

714
      total += coin.value;
191,866✔
715
    }
716

717
    return total;
70,407✔
718
  }
719

720
  /**
721
   * Calculate the total output value.
722
   * @returns {AmountValue} value
723
   */
724

725
  getOutputValue() {
726
    let total = 0;
60,486✔
727

728
    for (const output of this.outputs)
60,486✔
729
      total += output.value;
195,387✔
730

731
    return total;
60,486✔
732
  }
733

734
  /**
735
   * Get all input addresses.
736
   * @private
737
   * @param {CoinView} view
738
   * @returns {Array} [addrs, table]
739
   */
740

741
  _getInputAddresses(view) {
742
    const table = new BufferSet();
2,563✔
743
    const addrs = [];
2,563✔
744

745
    if (this.isCoinbase())
2,563✔
746
      return [addrs, table];
12✔
747

748
    for (const input of this.inputs) {
2,551✔
749
      const coin = view ? view.getOutputFor(input) : null;
22,699!
750
      const addr = input.getAddress(coin);
22,699✔
751

752
      if (!addr)
22,699!
753
        continue;
×
754

755
      const hash = addr.getHash();
22,699✔
756

757
      if (!table.has(hash)) {
22,699✔
758
        table.add(hash);
22,456✔
759
        addrs.push(addr);
22,456✔
760
      }
761
    }
762

763
    return [addrs, table];
2,551✔
764
  }
765

766
  /**
767
   * Get all output addresses.
768
   * @private
769
   * @returns {Array} [addrs, table]
770
   */
771

772
  _getOutputAddresses() {
773
    const table = new BufferSet();
25,153✔
774
    const addrs = [];
25,153✔
775

776
    for (const output of this.outputs) {
25,153✔
777
      const addr = output.getAddress();
92,213✔
778

779
      if (!addr)
92,213!
780
        continue;
×
781

782
      const hash = addr.getHash();
92,213✔
783

784
      if (!table.has(hash)) {
92,213✔
785
        table.add(hash);
87,984✔
786
        addrs.push(addr);
87,984✔
787
      }
788
    }
789

790
    return [addrs, table];
25,153✔
791
  }
792

793
  /**
794
   * Get all addresses.
795
   * @private
796
   * @param {CoinView} view
797
   * @returns {Array} [addrs, table]
798
   */
799

800
  _getAddresses(view) {
801
    const [addrs, table] = this._getInputAddresses(view);
14✔
802
    const output = this.getOutputAddresses();
14✔
803

804
    for (const addr of output) {
14✔
805
      const hash = addr.getHash();
14✔
806

807
      if (!table.has(hash)) {
14✔
808
        table.add(hash);
12✔
809
        addrs.push(addr);
12✔
810
      }
811
    }
812

813
    return [addrs, table];
14✔
814
  }
815

816
  /**
817
   * Get all input addresses.
818
   * @param {CoinView|null} view
819
   * @returns {Address[]} addresses
820
   */
821

822
  getInputAddresses(view) {
823
    const [addrs] = this._getInputAddresses(view);
×
824
    return addrs;
×
825
  }
826

827
  /**
828
   * Get all output addresses.
829
   * @returns {Address[]} addresses
830
   */
831

832
  getOutputAddresses() {
833
    const [addrs] = this._getOutputAddresses();
14✔
834
    return addrs;
14✔
835
  }
836

837
  /**
838
   * Get all addresses.
839
   * @param {CoinView|null} view
840
   * @returns {Address[]} addresses
841
   */
842

843
  getAddresses(view) {
844
    const [addrs] = this._getAddresses(view);
×
845
    return addrs;
×
846
  }
847

848
  /**
849
   * Get all input address hashes.
850
   * @param {CoinView|null} view
851
   * @returns {Hash[]} hashes
852
   */
853

854
  getInputHashes(view) {
855
    const [, table] = this._getInputAddresses(view);
2,549✔
856
    return table.toArray();
2,549✔
857
  }
858

859
  /**
860
   * Get all output address hashes.
861
   * @returns {Hash[]} hashes
862
   */
863

864
  getOutputHashes() {
865
    const [, table] = this._getOutputAddresses();
25,139✔
866
    return table.toArray();
25,139✔
867
  }
868

869
  /**
870
   * Get all address hashes.
871
   * @param {CoinView|null} view
872
   * @returns {Hash[]} hashes
873
   */
874

875
  getHashes(view) {
876
    const [, table] = this._getAddresses(view);
14✔
877
    return table.toArray();
14✔
878
  }
879

880
  /**
881
   * Test whether the transaction has
882
   * all coins available.
883
   * @param {CoinView} view
884
   * @returns {Boolean}
885
   */
886

887
  hasCoins(view) {
888
    if (this.inputs.length === 0)
37,560!
889
      return false;
×
890

891
    for (const {prevout} of this.inputs) {
37,560✔
892
      if (!view.hasEntry(prevout))
93,933✔
893
        return false;
12,460✔
894
    }
895

896
    return true;
25,100✔
897
  }
898

899
  /**
900
   * Check finality of transaction by examining
901
   * nLocktime and nSequence values.
902
   * @example
903
   * tx.isFinal(chain.height + 1, network.now());
904
   * @param {Number} height - Height at which to test. This
905
   * is usually the chain height, or the chain height + 1
906
   * when the transaction entered the mempool.
907
   * @param {Number} time - Time at which to test. This is
908
   * usually the chain tip's parent's median time, or the
909
   * time at which the transaction entered the mempool. If
910
   * MEDIAN_TIME_PAST is enabled this will be the median
911
   * time of the chain tip's previous entry's median time.
912
   * @returns {Boolean}
913
   */
914

915
  isFinal(height, time) {
916
    const FLAG = consensus.LOCKTIME_FLAG;
11,759✔
917
    const MASK = consensus.LOCKTIME_MASK;
11,759✔
918
    const MULT = consensus.LOCKTIME_MULT;
11,759✔
919

920
    if (this.locktime === 0)
11,759✔
921
      return true;
11,756✔
922

923
    if (this.locktime & FLAG) {
3!
924
      const locktime = this.locktime & MASK;
×
925

926
      if ((locktime * MULT) < time)
×
927
        return true;
×
928
    } else {
929
      if (this.locktime < height)
3✔
930
        return true;
1✔
931
    }
932

933
    for (const input of this.inputs) {
2✔
934
      if (input.sequence !== 0xffffffff)
2!
935
        return false;
2✔
936
    }
937

938
    return true;
×
939
  }
940

941
  /**
942
   * Verify the absolute locktime of a transaction.
943
   * Called by OP_CHECKLOCKTIMEVERIFY.
944
   * @param {Number} index - Index of input being verified.
945
   * @param {Number} predicate - Locktime to verify against.
946
   * @returns {Boolean}
947
   */
948

949
  verifyLocktime(index, predicate) {
950
    const FLAG = consensus.LOCKTIME_FLAG;
3✔
951
    const MASK = consensus.LOCKTIME_MASK;
3✔
952
    const input = this.inputs[index];
3✔
953

954
    assert(input, 'Input does not exist.');
3✔
955
    assert(predicate >= 0, 'Locktime must be non-negative.');
3✔
956

957
    // Locktimes must be of the same type (blocks or seconds).
958
    if ((this.locktime & FLAG) !== (predicate & FLAG))
3!
959
      return false;
×
960

961
    if ((predicate & MASK) > (this.locktime & MASK))
3!
962
      return false;
3✔
963

964
    if (input.sequence === 0xffffffff)
×
965
      return false;
×
966

967
    return true;
×
968
  }
969

970
  /**
971
   * Verify the relative locktime of an input.
972
   * Called by OP_CHECKSEQUENCEVERIFY.
973
   * @param {Number} index - Index of input being verified.
974
   * @param {Number} predicate - Relative locktime to verify against.
975
   * @returns {Boolean}
976
   */
977

978
  verifySequence(index, predicate) {
979
    const DISABLE_FLAG = consensus.SEQUENCE_DISABLE_FLAG;
8✔
980
    const TYPE_FLAG = consensus.SEQUENCE_TYPE_FLAG;
8✔
981
    const MASK = consensus.SEQUENCE_MASK;
8✔
982
    const input = this.inputs[index];
8✔
983

984
    assert(input, 'Input does not exist.');
8✔
985
    assert(predicate >= 0, 'Locktime must be non-negative.');
8✔
986

987
    // For future softfork capability.
988
    if (predicate & DISABLE_FLAG)
8✔
989
      return true;
1✔
990

991
    // Cannot use the disable flag without
992
    // the predicate also having the disable
993
    // flag (for future softfork capability).
994
    if (input.sequence & DISABLE_FLAG)
7✔
995
      return false;
5✔
996

997
    // Locktimes must be of the same type (blocks or seconds).
998
    if ((input.sequence & TYPE_FLAG) !== (predicate & TYPE_FLAG))
2!
999
      return false;
×
1000

1001
    if ((predicate & MASK) > (input.sequence & MASK))
2!
1002
      return false;
×
1003

1004
    return true;
2✔
1005
  }
1006

1007
  /**
1008
   * Calculate sigops.
1009
   * @param {CoinView} view
1010
   * @returns {Number}
1011
   */
1012

1013
  getSigops(view) {
1014
    if (this.isCoinbase())
42,185✔
1015
      return 0;
18,704✔
1016

1017
    let total = 0;
23,481✔
1018

1019
    for (const input of this.inputs) {
23,481✔
1020
      const coin = view.getOutputFor(input);
61,359✔
1021

1022
      if (!coin)
61,359✔
1023
        continue;
13,858✔
1024

1025
      total += coin.address.getSigops(input.witness);
47,501✔
1026
    }
1027

1028
    return total;
23,481✔
1029
  }
1030

1031
  /**
1032
   * Non-contextual sanity checks for the transaction.
1033
   * Will mostly verify coin and output values.
1034
   * @see CheckTransaction()
1035
   * @returns {Array} [result, reason, score]
1036
   */
1037

1038
  isSane() {
1039
    const [valid] = this.checkSanity();
1,499✔
1040
    return valid;
1,499✔
1041
  }
1042

1043
  /**
1044
   * Non-contextual sanity checks for the transaction.
1045
   * Will mostly verify coin and output values.
1046
   * @see CheckTransaction()
1047
   * @returns {Array} [valid, reason, score]
1048
   */
1049

1050
  checkSanity() {
1051
    if (this.inputs.length === 0)
31,851!
1052
      return [false, 'bad-txns-vin-empty', 100];
×
1053

1054
    if (this.outputs.length === 0)
31,851✔
1055
      return [false, 'bad-txns-vout-empty', 100];
1✔
1056

1057
    if (this.getBaseSize() > consensus.MAX_TX_SIZE)
31,850!
1058
      return [false, 'bad-txns-oversize', 100];
×
1059

1060
    if (this.getWeight() > consensus.MAX_TX_WEIGHT)
31,850!
1061
      return [false, 'bad-txns-overweight', 100];
×
1062

1063
    // Single TX should not exceed consensus block limits.
1064
    if (rules.countOpens(this) > consensus.MAX_BLOCK_OPENS)
31,850✔
1065
      return [false, 'bad-txns-opens', 100];
1✔
1066

1067
    if (rules.countUpdates(this) > consensus.MAX_BLOCK_UPDATES)
31,849✔
1068
      return [false, 'bad-txns-updates', 100];
1✔
1069

1070
    if (rules.countRenewals(this) > consensus.MAX_BLOCK_RENEWALS)
31,848✔
1071
      return [false, 'bad-txns-renewals', 100];
1✔
1072

1073
    let total = 0;
31,847✔
1074

1075
    for (const output of this.outputs) {
31,847✔
1076
      if (output.value < 0)
92,175!
1077
        return [false, 'bad-txns-vout-negative', 100];
×
1078

1079
      if (output.value > consensus.MAX_MONEY)
92,175✔
1080
        return [false, 'bad-txns-vout-toolarge', 100];
4✔
1081

1082
      total += output.value;
92,171✔
1083

1084
      if (total < 0 || total > consensus.MAX_MONEY)
92,171✔
1085
        return [false, 'bad-txns-txouttotal-toolarge', 100];
2✔
1086

1087
      if (!output.address.isValid())
92,169!
1088
        return [false, 'bad-txns-address-size', 100];
×
1089
    }
1090

1091
    if (this.isCoinbase()) {
31,841✔
1092
      if (!this.inputs[0].prevout.isNull())
18,956!
1093
        return [false, 'bad-cb-outpoint', 100];
×
1094

1095
      const size = this.inputs[0].witness.getSize();
18,956✔
1096

1097
      if (size > 1000)
18,956!
1098
        return [false, 'bad-cb-length', 100];
×
1099

1100
      for (let i = 1; i < this.inputs.length; i++) {
18,956✔
1101
        const input = this.inputs[i];
50✔
1102

1103
        if (!input.prevout.isNull())
50!
1104
          return [false, 'bad-cb-outpoint', 100];
×
1105

1106
        if (input.witness.items.length !== 1)
50!
1107
          return [false, 'bad-cb-witness', 100];
×
1108

1109
        const size = input.witness.items[0].length;
50✔
1110

1111
        if (size > 10000)
50!
1112
          return [false, 'bad-cb-length', 100];
×
1113
      }
1114
    } else {
1115
      const prevout = new BufferSet();
12,885✔
1116

1117
      for (const input of this.inputs) {
12,885✔
1118
        const key = input.prevout.toKey();
38,582✔
1119

1120
        if (prevout.has(key))
38,582!
1121
          return [false, 'bad-txns-inputs-duplicate', 100];
×
1122

1123
        prevout.add(key);
38,582✔
1124
      }
1125

1126
      for (const input of this.inputs) {
12,885✔
1127
        if (input.prevout.isNull())
38,582!
1128
          return [false, 'bad-txns-prevout-null', 10];
×
1129
      }
1130
    }
1131

1132
    if (!this.hasSaneCovenants())
31,841!
1133
      return [false, 'bad-txns-covenants', 100];
×
1134

1135
    return [true, 'valid', 0];
31,841✔
1136
  }
1137

1138
  /**
1139
   * Test whether the transaction violates
1140
   * any basic covenants rules.
1141
   * @returns {Boolean}
1142
   */
1143

1144
  hasSaneCovenants() {
1145
    return rules.hasSaneCovenants(this);
31,841✔
1146
  }
1147

1148
  /**
1149
   * Non-contextual checks to determine whether the
1150
   * transaction has all standard output script
1151
   * types and standard input script size with only
1152
   * pushdatas in the code.
1153
   * Will mostly verify coin and output values.
1154
   * @see IsStandardTx()
1155
   * @returns {Array} [valid, reason, score]
1156
   */
1157

1158
  isStandard() {
1159
    const [valid] = this.checkStandard();
×
1160
    return valid;
×
1161
  }
1162

1163
  /**
1164
   * Non-contextual checks to determine whether the
1165
   * transaction has all standard output script
1166
   * types and standard input script size with only
1167
   * pushdatas in the code.
1168
   * Will mostly verify coin and output values.
1169
   * @see IsStandardTx()
1170
   * @returns {Array} [valid, reason, score]
1171
   */
1172

1173
  checkStandard() {
1174
    if (this.version > policy.MAX_TX_VERSION)
×
1175
      return [false, 'version', 0];
×
1176

1177
    if (this.getWeight() > policy.MAX_TX_WEIGHT)
×
1178
      return [false, 'tx-size', 0];
×
1179

1180
    let nulldata = 0;
×
1181

1182
    for (const output of this.outputs) {
×
1183
      if (output.address.isUnknown())
×
1184
        return [false, 'address', 0];
×
1185

1186
      if (output.address.isNulldata()) {
×
1187
        nulldata += 1;
×
1188
        continue;
×
1189
      }
1190

1191
      if (output.covenant.isUnknown())
×
1192
        return [false, 'covenant', 0];
×
1193

1194
      if (output.isDust(policy.MIN_RELAY))
×
1195
        return [false, 'dust', 0];
×
1196
    }
1197

1198
    if (nulldata > 1)
×
1199
      return [false, 'multi-op-return', 0];
×
1200

1201
    return [true, 'valid', 0];
×
1202
  }
1203

1204
  /**
1205
   * Perform contextual checks to verify coin and input
1206
   * script standardness (including the redeem script).
1207
   * @see AreInputsStandard()
1208
   * @param {CoinView} view
1209
   * @returns {Boolean}
1210
   */
1211

1212
  hasStandardInputs(view) {
1213
    if (this.isCoinbase())
1!
1214
      return true;
×
1215

1216
    for (const input of this.inputs) {
1✔
1217
      const witness = input.witness;
1✔
1218
      const coin = view.getOutputFor(input);
1✔
1219

1220
      if (!coin)
1!
1221
        continue;
×
1222

1223
      if (witness.items.length === 0)
1!
1224
        continue;
×
1225

1226
      const addr = coin.address;
1✔
1227

1228
      if (addr.isPubkeyhash()) {
1!
1229
        if (witness.items.length !== 2)
1!
1230
          return false;
×
1231

1232
        if (witness.items[0].length !== 65)
1!
1233
          return false;
×
1234

1235
        if (witness.items[1].length !== 33)
1!
1236
          return false;
×
1237

1238
        continue;
1✔
1239
      }
1240

1241
      if (addr.isScripthash()) {
×
1242
        if (witness.items.length - 1 > policy.MAX_P2WSH_STACK)
×
1243
          return false;
×
1244

1245
        for (let i = 0; i < witness.items.length - 1; i++) {
×
1246
          const item = witness.items[i];
×
1247
          if (item.length > policy.MAX_P2WSH_PUSH)
×
1248
            return false;
×
1249
        }
1250

1251
        const raw = witness.items[witness.items.length - 1];
×
1252

1253
        if (raw.length > policy.MAX_P2WSH_SIZE)
×
1254
          return false;
×
1255

1256
        const redeem = Script.decode(raw);
×
1257

1258
        if (redeem.isPubkey()) {
×
1259
          if (witness.items.length - 1 !== 1)
×
1260
            return false;
×
1261

1262
          if (witness.items[0].length !== 65)
×
1263
            return false;
×
1264

1265
          continue;
×
1266
        }
1267

1268
        if (redeem.isPubkeyhash()) {
×
1269
          if (input.witness.items.length - 1 !== 2)
×
1270
            return false;
×
1271

1272
          if (witness.items[0].length !== 65)
×
1273
            return false;
×
1274

1275
          if (witness.items[1].length !== 33)
×
1276
            return false;
×
1277

1278
          continue;
×
1279
        }
1280

1281
        const [m] = redeem.getMultisig();
×
1282

1283
        if (m !== -1) {
×
1284
          if (witness.items.length - 1 !== m + 1)
×
1285
            return false;
×
1286

1287
          if (witness.items[0].length !== 0)
×
1288
            return false;
×
1289

1290
          for (let i = 1; i < witness.items.length - 1; i++) {
×
1291
            const item = witness.items[i];
×
1292
            if (item.length !== 65)
×
1293
              return false;
×
1294
          }
1295
        }
1296

1297
        continue;
×
1298
      }
1299

1300
      if (witness.items.length > policy.MAX_P2WSH_STACK)
×
1301
        return false;
×
1302

1303
      for (const item of witness.items) {
×
1304
        if (item.length > policy.MAX_P2WSH_PUSH)
×
1305
          return false;
×
1306
      }
1307
    }
1308

1309
    return true;
1✔
1310
  }
1311

1312
  /**
1313
   * Perform contextual checks to verify input, output,
1314
   * and fee values, as well as coinbase spend maturity
1315
   * (coinbases can only be spent 100 blocks or more
1316
   * after they're created). Note that this function is
1317
   * consensus critical.
1318
   * @param {CoinView} view
1319
   * @param {Number} height - Height at which the
1320
   * transaction is being spent. In the mempool this is
1321
   * the chain height plus one at the time it entered the pool.
1322
   * @param {Network} network
1323
   * @returns {Boolean}
1324
   */
1325

1326
  verifyInputs(view, height, network) {
1327
    const [fee] = this.checkInputs(view, height, network);
226✔
1328
    return fee !== -1;
226✔
1329
  }
1330

1331
  /**
1332
   * Perform contextual checks to verify input, output,
1333
   * and fee values, as well as coinbase spend maturity
1334
   * (coinbases can only be spent 100 blocks or more
1335
   * after they're created). Note that this function is
1336
   * consensus critical.
1337
   * @param {CoinView} view
1338
   * @param {Number} height - Height at which the
1339
   * transaction is being spent. In the mempool this is
1340
   * the chain height plus one at the time it entered the pool.
1341
   * @param {Network} network
1342
   * @returns {Array} [fee, reason, score]
1343
   */
1344

1345
  checkInputs(view, height, network) {
1346
    assert(typeof height === 'number');
30,310✔
1347
    assert(network instanceof Network);
30,310✔
1348

1349
    if (this.isCoinbase()) {
30,310✔
1350
      const conjured = this.verifyCovenants(view, height, network);
18,912✔
1351

1352
      if (conjured === -1)
18,912!
1353
        return [-1, 'bad-txns-claims', 100];
×
1354

1355
      return [conjured, 'valid', 0];
18,912✔
1356
    }
1357

1358
    let total = 0;
11,398✔
1359

1360
    for (const {prevout} of this.inputs) {
11,398✔
1361
      const entry = view.getEntry(prevout);
35,685✔
1362

1363
      if (!entry)
35,685!
1364
        return [-1, 'bad-txns-inputs-missingorspent', 0];
×
1365

1366
      if (entry.coinbase) {
35,685✔
1367
        if (height - entry.height < network.coinbaseMaturity)
8,698✔
1368
          return [-1, 'bad-txns-premature-spend-of-coinbase', 0];
2✔
1369
      }
1370

1371
      const coin = view.getOutput(prevout);
35,683✔
1372
      assert(coin);
35,683✔
1373

1374
      if (coin.value < 0 || coin.value > consensus.MAX_MONEY)
35,683✔
1375
        return [-1, 'bad-txns-inputvalues-outofrange', 100];
8✔
1376

1377
      total += coin.value;
35,675✔
1378

1379
      if (total < 0 || total > consensus.MAX_MONEY)
35,675✔
1380
        return [-1, 'bad-txns-inputvalues-outofrange', 100];
2✔
1381
    }
1382

1383
    // Overflows already checked in `isSane()`.
1384
    const value = this.getOutputValue();
11,386✔
1385

1386
    if (total < value)
11,386✔
1387
      return [-1, 'bad-txns-in-belowout', 100];
6✔
1388

1389
    const fee = total - value;
11,380✔
1390

1391
    if (fee < 0)
11,380!
1392
      return [-1, 'bad-txns-fee-negative', 100];
×
1393

1394
    if (fee > consensus.MAX_MONEY)
11,380!
1395
      return [-1, 'bad-txns-fee-outofrange', 100];
×
1396

1397
    const conjured = this.verifyCovenants(view, height, network);
11,380✔
1398

1399
    if (conjured === -1)
11,380!
1400
      return [-1, 'bad-txns-covenants', 100];
×
1401

1402
    assert(conjured === 0);
11,380✔
1403

1404
    return [fee, 'valid', 0];
11,380✔
1405
  }
1406

1407
  /**
1408
   * Test whether the transaction violates
1409
   * any contextual covenants rules.
1410
   * @param {CoinView} view
1411
   * @param {Number} height
1412
   * @param {Network} network
1413
   * @returns {Number}
1414
   */
1415

1416
  verifyCovenants(view, height, network) {
1417
    return rules.verifyCovenants(this, view, height, network);
30,292✔
1418
  }
1419

1420
  /**
1421
   * Calculate the modified size of the transaction. This
1422
   * is used in the mempool for calculating priority.
1423
   * @param {Number?} size - The size to modify. If not present,
1424
   * virtual size will be used.
1425
   * @returns {Number} Modified size.
1426
   */
1427

1428
  getModifiedSize(size) {
1429
    if (size == null)
×
1430
      size = this.getVirtualSize();
×
1431

1432
    for (const input of this.inputs) {
×
1433
      const offset = 45 + Math.min(100, input.witness.getSize());
×
1434
      if (size > offset)
×
1435
        size -= offset;
×
1436
    }
1437

1438
    return size;
×
1439
  }
1440

1441
  /**
1442
   * Calculate the transaction priority.
1443
   * @param {CoinView} view
1444
   * @param {Number} height
1445
   * @param {Number?} [size] - Size to calculate priority
1446
   * based on. If not present, virtual size will be used.
1447
   * @returns {Number}
1448
   */
1449

1450
  getPriority(view, height, size) {
1451
    assert(typeof height === 'number', 'Must pass in height.');
13,673✔
1452

1453
    if (this.isCoinbase())
13,673!
1454
      return 0;
×
1455

1456
    if (size == null)
13,673✔
1457
      size = this.getVirtualSize();
12,243✔
1458

1459
    let sum = 0;
13,673✔
1460

1461
    for (const {prevout} of this.inputs) {
13,673✔
1462
      const coin = view.getOutput(prevout);
28,980✔
1463

1464
      if (!coin)
28,980✔
1465
        continue;
13,858✔
1466

1467
      const coinHeight = view.getHeight(prevout);
15,122✔
1468

1469
      if (coinHeight === -1)
15,122✔
1470
        continue;
2,865✔
1471

1472
      if (coinHeight <= height) {
12,257!
1473
        const age = height - coinHeight;
12,257✔
1474
        sum += coin.value * age;
12,257✔
1475
      }
1476
    }
1477

1478
    return Math.floor(sum / size);
13,673✔
1479
  }
1480

1481
  /**
1482
   * Calculate the transaction's on-chain value.
1483
   * @param {CoinView} view
1484
   * @returns {Number}
1485
   */
1486

1487
  getChainValue(view) {
1488
    if (this.isCoinbase())
1,430!
1489
      return 0;
×
1490

1491
    let value = 0;
1,430✔
1492

1493
    for (const {prevout} of this.inputs) {
1,430✔
1494
      const coin = view.getOutput(prevout);
3,080✔
1495

1496
      if (!coin)
3,080!
1497
        continue;
×
1498

1499
      const height = view.getHeight(prevout);
3,080✔
1500

1501
      if (height === -1)
3,080✔
1502
        continue;
1,256✔
1503

1504
      value += coin.value;
1,824✔
1505
    }
1506

1507
    return value;
1,430✔
1508
  }
1509

1510
  /**
1511
   * Determine whether the transaction is above the
1512
   * free threshold in priority. A transaction which
1513
   * passed this test is most likely relayable
1514
   * without a fee.
1515
   * @param {CoinView} view
1516
   * @param {Number} height - If not present, tx
1517
   * height or network height will be used.
1518
   * @param {Number?} [size] - If not present, modified
1519
   * size will be calculated and used.
1520
   * @returns {Boolean}
1521
   */
1522

1523
  isFree(view, height, size) {
1524
    const priority = this.getPriority(view, height, size);
×
1525
    return priority > policy.FREE_THRESHOLD;
×
1526
  }
1527

1528
  /**
1529
   * Calculate minimum fee in order for the transaction
1530
   * to be relayable (not the constant min relay fee).
1531
   * @param {Number?} [size] - If not present, max size
1532
   * estimation will be calculated and used.
1533
   * @param {Rate?} [rate] - Rate of dollarydoo per kB.
1534
   * @returns {AmountValue} fee
1535
   */
1536

1537
  getMinFee(size, rate) {
1538
    if (size == null)
×
1539
      size = this.getVirtualSize();
×
1540

1541
    return policy.getMinFee(size, rate);
×
1542
  }
1543

1544
  /**
1545
   * Calculate the minimum fee in order for the transaction
1546
   * to be relayable, but _round to the nearest kilobyte
1547
   * when taking into account size.
1548
   * @param {Number?} [size] - If not present, max size
1549
   * estimation will be calculated and used.
1550
   * @param {Rate?} [rate] - Rate of dollarydoo per kB.
1551
   * @returns {AmountValue} fee
1552
   */
1553

1554
  getRoundFee(size, rate) {
1555
    if (size == null)
×
1556
      size = this.getVirtualSize();
×
1557

1558
    return policy.getRoundFee(size, rate);
×
1559
  }
1560

1561
  /**
1562
   * Calculate the transaction's rate based on size
1563
   * and fees. Size will be calculated if not present.
1564
   * @param {CoinView} view
1565
   * @param {Number?} [size]
1566
   * @returns {Rate}
1567
   */
1568

1569
  getRate(view, size) {
1570
    const fee = this.getFee(view);
12,285✔
1571

1572
    if (fee < 0)
12,285!
1573
      return 0;
×
1574

1575
    if (size == null)
12,285!
1576
      size = this.getVirtualSize();
12,285✔
1577

1578
    return policy.getRate(size, fee);
12,285✔
1579
  }
1580

1581
  /**
1582
   * Get all unique outpoint hashes.
1583
   * @returns {Hash[]} Outpoint hashes.
1584
   */
1585

1586
  getPrevout() {
1587
    if (this.isCoinbase())
×
1588
      return [];
×
1589

1590
    const prevout = new BufferSet();
×
1591

1592
    for (const input of this.inputs)
×
1593
      prevout.add(input.prevout.hash);
×
1594

1595
    return prevout.toArray();
×
1596
  }
1597

1598
  /**
1599
   * Test a transaction against a bloom filter.
1600
   * @param {BloomFilter} filter
1601
   * @returns {Boolean}
1602
   */
1603

1604
  test(filter) {
1605
    if (filter.test(this.hash()))
×
1606
      return true;
×
1607

1608
    for (let i = 0; i < this.outputs.length; i++) {
×
1609
      const {address, covenant} = this.outputs[i];
×
1610

1611
      if (filter.test(address.hash) || covenant.test(filter))
×
1612
        return true;
×
1613
    }
1614

1615
    for (const {prevout} of this.inputs) {
×
1616
      if (filter.test(prevout.encode()))
×
1617
        return true;
×
1618
    }
1619

1620
    return false;
×
1621
  }
1622

1623
  /**
1624
   * Test a transaction against a bloom filter.
1625
   * @param {BloomFilter} filter
1626
   * @returns {Boolean}
1627
   */
1628

1629
  testAndMaybeUpdate(filter) {
1630
    let found = false;
5,388✔
1631

1632
    if (filter.test(this.hash()))
5,388✔
1633
      found = true;
126✔
1634

1635
    for (let i = 0; i < this.outputs.length; i++) {
5,388✔
1636
      const {address, covenant} = this.outputs[i];
7,398✔
1637

1638
      if (filter.test(address.hash) || covenant.test(filter)) {
7,398✔
1639
        const prevout = Outpoint.fromTX(this, i);
3,825✔
1640
        filter.add(prevout.encode());
3,825✔
1641
        found = true;
3,825✔
1642
      }
1643
    }
1644

1645
    if (found)
5,388✔
1646
      return found;
2,856✔
1647

1648
    for (const {prevout} of this.inputs) {
2,532✔
1649
      if (filter.test(prevout.encode()))
2,563✔
1650
        return true;
6✔
1651
    }
1652

1653
    return false;
2,526✔
1654
  }
1655

1656
  /**
1657
   * Get little-endian tx hash.
1658
   * @returns {HexHash}
1659
   */
1660

1661
  txid() {
1662
    return this.hash().toString('hex');
12,648✔
1663
  }
1664

1665
  /**
1666
   * Get little-endian wtx hash.
1667
   * @returns {HexHash}
1668
   */
1669

1670
  wtxid() {
1671
    return this.witnessHash().toString('hex');
734✔
1672
  }
1673

1674
  /**
1675
   * Create outpoint from output index.
1676
   * @param {Number} index
1677
   * @returns {Outpoint}
1678
   */
1679

1680
  outpoint(index) {
1681
    return new Outpoint(this.hash(), index);
30,272✔
1682
  }
1683

1684
  /**
1685
   * Get input from index.
1686
   * @param {Number} index
1687
   * @returns {Input|null}
1688
   */
1689

1690
  input(index) {
1691
    if (index >= this.inputs.length)
30,211✔
1692
      return null;
8,839✔
1693
    return this.inputs[index];
21,372✔
1694
  }
1695

1696
  /**
1697
   * Get output from index.
1698
   * @param {Number} index
1699
   * @returns {Output|null}
1700
   */
1701

1702
  output(index) {
1703
    if (index >= this.outputs.length)
35,777✔
1704
      return null;
2,666✔
1705
    return this.outputs[index];
33,111✔
1706
  }
1707

1708
  /**
1709
   * Get covenant from index.
1710
   * @param {Number} index
1711
   * @returns {Covenant|null}
1712
   */
1713

1714
  covenant(index) {
1715
    if (index >= this.outputs.length)
35,709✔
1716
      return null;
2,666✔
1717
    return this.outputs[index].covenant;
33,043✔
1718
  }
1719

1720
  /**
1721
   * Convert the tx to an inv item.
1722
   * @returns {InvItem}
1723
   */
1724

1725
  toInv() {
1726
    return new InvItem(InvItem.types.TX, this.hash());
52✔
1727
  }
1728

1729
  /**
1730
   * Inspect the transaction and return a more
1731
   * user-friendly representation of the data.
1732
   * @param {CoinView?} [view]
1733
   * @param {ChainEntry?} [entry]
1734
   * @param {Number?} [index]
1735
   * @returns {Object}
1736
   */
1737

1738
  format(view, entry, index) {
1739
    let rate = 0;
×
1740
    let fee = 0;
×
1741
    let height = -1;
×
1742
    let block = null;
×
1743
    let time = 0;
×
1744
    let date = null;
×
1745

1746
    if (view) {
×
1747
      fee = this.getFee(view);
×
1748
      rate = this.getRate(view);
×
1749

1750
      // Rate can exceed 53 bits in testing.
1751
      if (!Number.isSafeInteger(rate))
×
1752
        rate = 0;
×
1753
    }
1754

1755
    if (entry) {
×
1756
      height = entry.height;
×
1757
      block = entry.hash.toString('hex');
×
1758
      time = entry.time;
×
1759
      date = util.date(time);
×
1760
    }
1761

1762
    if (index == null)
×
1763
      index = -1;
×
1764

1765
    return {
×
1766
      hash: this.txid(),
1767
      witnessHash: this.wtxid(),
1768
      size: this.getSize(),
1769
      virtualSize: this.getVirtualSize(),
1770
      value: Amount.coin(this.getOutputValue()),
1771
      fee: Amount.coin(fee),
1772
      rate: Amount.coin(rate),
1773
      minFee: Amount.coin(this.getMinFee()),
1774
      height: height,
1775
      block: block,
1776
      time: time,
1777
      date: date,
1778
      index: index,
1779
      version: this.version,
1780
      inputs: this.inputs.map((input) => {
1781
        const coin = view ? view.getOutputFor(input) : null;
×
1782
        return input.format(coin);
×
1783
      }),
1784
      outputs: this.outputs,
1785
      locktime: this.locktime
1786
    };
1787
  }
1788

1789
  /**
1790
   * Convert the transaction to an object suitable
1791
   * for JSON serialization.
1792
   * @param {Network} [network]
1793
   * @param {CoinView} [view]
1794
   * @param {ChainEntry} [entry]
1795
   * @param {Number} [index]
1796
   * @returns {Object}
1797
   */
1798

1799
  getJSON(network, view, entry, index) {
1800
    let rate, fee, height, block, time, date;
1801

1802
    if (view) {
729✔
1803
      fee = this.getFee(view);
38✔
1804
      rate = this.getRate(view);
38✔
1805

1806
      // Rate can exceed 53 bits in testing.
1807
      if (!Number.isSafeInteger(rate))
38!
1808
        rate = 0;
×
1809
    }
1810

1811
    if (entry) {
729!
1812
      height = entry.height;
×
1813
      block = entry.hash.toString('hex');
×
1814
      time = entry.time;
×
1815
      date = util.date(time);
×
1816
    }
1817

1818
    network = Network.get(network);
729✔
1819

1820
    return {
729✔
1821
      hash: this.txid(),
1822
      witnessHash: this.wtxid(),
1823
      fee: fee,
1824
      rate: rate,
1825
      mtime: util.now(),
1826
      height: height,
1827
      block: block,
1828
      time: time,
1829
      date: date,
1830
      index: index,
1831
      version: this.version,
1832
      inputs: this.inputs.map((input) => {
1833
        const coin = view ? view.getCoinFor(input) : null;
880✔
1834
        const path = view ? view.getPathFor(input) : null;
880✔
1835
        return input.getJSON(network, coin, path);
880✔
1836
      }),
1837
      outputs: this.outputs.map((output) => {
1838
        return output.getJSON(network);
1,017✔
1839
      }),
1840
      locktime: this.locktime,
1841
      hex: this.toHex()
1842
    };
1843
  }
1844

1845
  /**
1846
   * Inject properties from a json object.
1847
   * @param {Object} json
1848
   */
1849

1850
  fromJSON(json) {
1851
    assert(json, 'TX data is required.');
24✔
1852
    assert((json.version >>> 0) === json.version, 'Version must be a uint32.');
24✔
1853
    assert(Array.isArray(json.inputs), 'Inputs must be an array.');
24✔
1854
    assert(Array.isArray(json.outputs), 'Outputs must be an array.');
24✔
1855
    assert((json.locktime >>> 0) === json.locktime,
24✔
1856
      'Locktime must be a uint32.');
1857

1858
    this.version = json.version;
24✔
1859

1860
    for (const input of json.inputs)
24✔
1861
      this.inputs.push(Input.fromJSON(input));
30✔
1862

1863
    for (const output of json.outputs)
24✔
1864
      this.outputs.push(Output.fromJSON(output));
55✔
1865

1866
    this.locktime = json.locktime;
24✔
1867

1868
    return this;
24✔
1869
  }
1870

1871
  /**
1872
   * Inject properties from serialized
1873
   * buffer reader (witness serialization).
1874
   * @param {bio.BufferReader} br
1875
   */
1876

1877
  read(br) {
1878
    br.start();
48,741✔
1879

1880
    this.version = br.readU32();
48,741✔
1881

1882
    const inCount = br.readVarint();
48,741✔
1883

1884
    for (let i = 0; i < inCount; i++)
48,741✔
1885
      this.inputs.push(Input.read(br));
88,890✔
1886

1887
    const outCount = br.readVarint();
48,741✔
1888

1889
    for (let i = 0; i < outCount; i++)
48,741✔
1890
      this.outputs.push(Output.read(br));
198,008✔
1891

1892
    this.locktime = br.readU32();
48,739✔
1893

1894
    const start = br.offset;
48,739✔
1895

1896
    for (let i = 0; i < inCount; i++) {
48,739✔
1897
      const input = this.inputs[i];
88,888✔
1898
      input.witness.read(br);
88,888✔
1899
    }
1900

1901
    const witness = br.offset - start;
48,739✔
1902

1903
    if (!this.mutable) {
48,739✔
1904
      const raw = br.endData();
48,596✔
1905
      const base = raw.length - witness;
48,596✔
1906
      this._raw = raw;
48,596✔
1907
      this._sizes = new Sizes(base, witness);
48,596✔
1908
    } else {
1909
      br.end();
143✔
1910
    }
1911

1912
    return this;
48,739✔
1913
  }
1914

1915
  /**
1916
   * Calculate the real size of the transaction
1917
   * with the witness included.
1918
   * @returns {Sizes}
1919
   */
1920

1921
  getSizes() {
1922
    if (this._sizes)
345,141✔
1923
      return this._sizes;
272,945✔
1924

1925
    let base = 0;
72,196✔
1926
    let witness = 0;
72,196✔
1927

1928
    base += 4;
72,196✔
1929
    base += encoding.sizeVarint(this.inputs.length);
72,196✔
1930

1931
    for (const input of this.inputs) {
72,196✔
1932
      base += 40;
1,552,508✔
1933
      witness += input.witness.getVarSize();
1,552,508✔
1934
    }
1935

1936
    base += encoding.sizeVarint(this.outputs.length);
72,196✔
1937

1938
    for (const output of this.outputs)
72,196✔
1939
      base += output.getSize();
1,687,108✔
1940

1941
    base += 4;
72,196✔
1942

1943
    const sizes = new Sizes(base, witness);
72,196✔
1944

1945
    if (!this.mutable)
72,196✔
1946
      this._sizes = sizes;
59,284✔
1947

1948
    return sizes;
72,196✔
1949
  }
1950

1951
  /**
1952
   * Serialize transaction with witness. Calculates the witness
1953
   * size as it is framing (exposed on return value as `witness`).
1954
   * @param {BufioWriter} bw
1955
   * @returns {BufioWriter}
1956
   */
1957

1958
  write(bw) {
1959
    if (this._raw) {
112,076✔
1960
      bw.writeBytes(this._raw);
51,197✔
1961
      return bw;
51,197✔
1962
    }
1963

1964
    bw.writeU32(this.version);
60,879✔
1965

1966
    bw.writeVarint(this.inputs.length);
60,879✔
1967

1968
    for (const input of this.inputs)
60,879✔
1969
      input.write(bw);
82,981✔
1970

1971
    bw.writeVarint(this.outputs.length);
60,879✔
1972

1973
    for (const output of this.outputs)
60,879✔
1974
      output.write(bw);
186,668✔
1975

1976
    bw.writeU32(this.locktime);
60,879✔
1977

1978
    for (const input of this.inputs)
60,879✔
1979
      input.witness.write(bw);
82,981✔
1980

1981
    return bw;
60,879✔
1982
  }
1983

1984
  /**
1985
   * Serialize transaction.
1986
   * @returns {Buffer}
1987
   */
1988

1989
  encode() {
1990
    if (this.mutable)
91,708✔
1991
      return super.encode();
1,528✔
1992

1993
    if (!this._raw)
90,180✔
1994
      this._raw = super.encode();
59,253✔
1995

1996
    return this._raw;
90,180✔
1997
  }
1998

1999
  /**
2000
   * Calculate left hash.
2001
   * @returns {Buffer}
2002
   */
2003

2004
  left() {
2005
    return this.hashes()[0];
89,003✔
2006
  }
2007

2008
  /**
2009
   * Calculate right hash.
2010
   * @returns {Buffer}
2011
   */
2012

2013
  right() {
2014
    return this.hashes()[1];
×
2015
  }
2016

2017
  /**
2018
   * Calculate root hash.
2019
   * @returns {Buffer}
2020
   */
2021

2022
  root() {
2023
    return this.hashes()[2];
100✔
2024
  }
2025

2026
  /**
2027
   * Calculate all three transaction hashes.
2028
   * @private
2029
   * @returns {Buffer[]}
2030
   */
2031

2032
  hashes() {
2033
    if (this._hash && this._wdhash && this._whash)
89,103!
2034
      return [this._hash, this._wdhash, this._whash];
×
2035

2036
    const {base, witness} = this.getSizes();
89,103✔
2037
    const raw = this.encode();
89,103✔
2038

2039
    assert(raw.length === base + witness);
89,103✔
2040

2041
    // Normal data.
2042
    const ndata = raw.slice(0, base);
89,103✔
2043

2044
    // Witness data.
2045
    const wdata = raw.slice(base, base + witness);
89,103✔
2046

2047
    // Left = HASH(normal-data) = normal txid
2048
    const hash = blake2b.digest(ndata);
89,103✔
2049

2050
    // Right = HASH(witness-data)
2051
    const wdhash = blake2b.digest(wdata);
89,103✔
2052

2053
    // WTXID = HASH(normal-txid || witness-data-hash)
2054
    const whash = blake2b.root(hash, wdhash);
89,103✔
2055

2056
    if (!this.mutable) {
89,103✔
2057
      this._hash = hash;
87,624✔
2058
      this._wdhash = wdhash;
87,624✔
2059
      this._whash = whash;
87,624✔
2060
    }
2061

2062
    return [hash, wdhash, whash];
89,103✔
2063
  }
2064

2065
  /**
2066
   * Test whether an object is a TX.
2067
   * @param {Object} obj
2068
   * @returns {Boolean}
2069
   */
2070

2071
  static isTX(obj) {
2072
    return obj instanceof TX;
×
2073
  }
2074
}
2075

2076
/*
2077
 * Helpers
2078
 */
2079

2080
class Sizes {
2081
  constructor(base, witness) {
2082
    this.base = base;
120,792✔
2083
    this.witness = witness;
120,792✔
2084
  }
2085

2086
  getWeight() {
2087
    const total = this.base + this.witness;
90,383✔
2088
    return this.base * (consensus.WITNESS_SCALE_FACTOR - 1) + total;
90,383✔
2089
  }
2090
}
2091

2092
/*
2093
 * Expose
2094
 */
2095

2096
module.exports = TX;
70✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc