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

handshake-org / hsd / 16056199985

03 Jul 2025 04:51PM UTC coverage: 71.628%. Remained the same
16056199985

Pull #937

github

web-flow
Merge 0ae672838 into f75e5ebc6
Pull Request #937: feat: add GitHub Actions workflow to tweet on release

8214 of 13317 branches covered (61.68%)

Branch coverage included in aggregate %.

26136 of 34639 relevant lines covered (75.45%)

34138.07 hits per line

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

77.92
/lib/blockchain/chain.js
1
/*!
2
 * chain.js - blockchain management 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');
1✔
10
const path = require('path');
1✔
11
const AsyncEmitter = require('bevent');
1✔
12
const Logger = require('blgr');
1✔
13
const {Lock} = require('bmutex');
1✔
14
const LRU = require('blru');
1✔
15
const {BufferMap, BufferSet} = require('buffer-map');
1✔
16
const Network = require('../protocol/network');
1✔
17
const ChainDB = require('./chaindb');
1✔
18
const common = require('./common');
1✔
19
const consensus = require('../protocol/consensus');
1✔
20
const rules = require('../covenants/rules');
1✔
21
const NameState = require('../covenants/namestate');
1✔
22
const util = require('../utils/util');
1✔
23
const ChainEntry = require('./chainentry');
1✔
24
const CoinView = require('../coins/coinview');
1✔
25
const Script = require('../script/script');
1✔
26
const {VerifyError} = require('../protocol/errors');
1✔
27
const {OwnershipProof} = require('../covenants/ownership');
1✔
28
const AirdropProof = require('../primitives/airdropproof');
1✔
29
const {CriticalError} = require('../errors');
1✔
30
const thresholdStates = common.thresholdStates;
1✔
31
const scanActions = common.scanActions;
1✔
32
const {states} = NameState;
1✔
33

34
const {
35
  VERIFY_COVENANTS_HARDENED,
36
  VERIFY_COVENANTS_LOCKUP
37
} = rules.nameFlags;
1✔
38

39
/** @typedef {import('../types').Hash} Hash */
40
/** @typedef {import('../types').LockFlags} LockFlags */
41
/** @typedef {import('@handshake-org/bfilter').BloomFilter} BloomFilter */
42
/** @typedef {import('../primitives/block')} Block */
43
/** @typedef {import('../primitives/tx')} TX */
44
/** @typedef {import('../primitives/txmeta')} TXMeta */
45
/** @typedef {import('../primitives/outpoint')} Outpoint */
46
/** @typedef {import('../primitives/coin')} Coin */
47
/** @typedef {import('../primitives/address')} Address */
48
/** @typedef {import('../coins/coinentry')} CoinEntry */
49

50
/**
51
 * Blockchain
52
 * @alias module:blockchain.Chain
53
 * @property {ChainDB} db
54
 * @property {ChainEntry?} tip
55
 * @property {Number} height
56
 * @property {DeploymentState} state
57
 */
58

59
class Chain extends AsyncEmitter {
60
  /**
61
   * Create a blockchain.
62
   * @constructor
63
   * @param {Object} options
64
   */
65

66
  constructor(options) {
67
    super();
287✔
68

69
    this.opened = false;
287✔
70
    this.options = new ChainOptions(options);
287✔
71

72
    this.network = this.options.network;
285✔
73
    this.logger = this.options.logger.context('chain');
285✔
74
    this.workers = this.options.workers;
285✔
75

76
    this.db = new ChainDB(this.options);
285✔
77

78
    this.locker = new Lock(true, BufferMap);
285✔
79
    this.invalid = new LRU(5000, null, BufferMap);
285✔
80
    this.state = new DeploymentState(this.network.genesis.hash);
285✔
81

82
    this.tip = new ChainEntry();
285✔
83
    this.height = -1;
285✔
84
    this.synced = false;
285✔
85

86
    this.orphanMap = new BufferMap();
285✔
87
    this.orphanPrev = new BufferMap();
285✔
88
  }
89

90
  /**
91
   * Open the chain, wait for the database to load.
92
   * @returns {Promise<void>}
93
   */
94

95
  async open() {
96
    assert(!this.opened, 'Chain is already open.');
319✔
97
    this.opened = true;
319✔
98

99
    this.logger.info('Chain is loading.');
319✔
100

101
    if (this.options.checkpoints)
319✔
102
      this.logger.info('Checkpoints are enabled.');
318✔
103

104
    await this.db.open();
319✔
105

106
    const tip = await this.db.getTip();
295✔
107

108
    assert(tip);
295✔
109

110
    this.tip = tip;
294✔
111
    this.height = tip.height;
294✔
112

113
    this.logger.info('Chain Height: %d', tip.height);
294✔
114

115
    this.logger.memory();
294✔
116

117
    const state = await this.getDeploymentState();
294✔
118

119
    this.setDeploymentState(state);
294✔
120

121
    if (!this.options.spv) {
294✔
122
      const sync = await this.tryCompact();
276✔
123

124
      if (sync)
275✔
125
        await this.syncTree();
270✔
126
    }
127

128
    this.logger.memory();
293✔
129

130
    this.emit('tip', tip);
293✔
131

132
    this.maybeSync();
293✔
133
  }
134

135
  /**
136
   * Close the chain, wait for the database to close.
137
   * @returns {Promise<void>}
138
   */
139

140
  async close() {
141
    assert(this.opened, 'Chain is not open.');
319✔
142
    this.opened = false;
319✔
143
    return this.db.close();
319✔
144
  }
145

146
  /**
147
   * Get compaction heights.
148
   * @returns {Promise<Object>}
149
   */
150

151
  async getCompactionHeights() {
152
    if (this.options.spv)
39!
153
      return null;
×
154

155
    const {keepBlocks} = this.network.block;
39✔
156
    const {compactionHeight} = await this.db.getTreeState();
39✔
157
    const {compactTreeInitInterval} = this.options;
39✔
158
    const compactFrom = compactionHeight + keepBlocks + compactTreeInitInterval;
39✔
159

160
    return {
39✔
161
      compactionHeight,
162
      compactFrom
163
    };
164
  }
165

166
  /**
167
   * Check if we need to compact tree data.
168
   * @returns {Promise<Boolean>} - Should we sync
169
   */
170

171
  async tryCompact() {
172
    if (this.options.spv)
276!
173
      return false;
×
174

175
    if (!this.options.compactTreeOnInit)
276✔
176
      return true;
263✔
177

178
    const {txStart} = this.network;
13✔
179
    const {keepBlocks} = this.network.block;
13✔
180
    const startFrom = txStart + keepBlocks;
13✔
181

182
    if (this.height <= startFrom)
13✔
183
      return true;
6✔
184

185
    const {compactFrom} = await this.getCompactionHeights();
7✔
186

187
    if (compactFrom > this.height) {
7✔
188
      this.logger.debug(
1✔
189
        `Tree will compact when restarted after height ${compactFrom}.`);
190
      return true;
1✔
191
    }
192

193
    // Compact tree calls syncTree so we don't want to rerun it.
194
    await this.compactTree();
6✔
195
    return false;
5✔
196
  }
197

198
  /**
199
   * Sync tree state.
200
   */
201

202
  async syncTree() {
203
    this.logger.info('Synchronizing Tree with block history...');
293✔
204

205
    // Current state of the tree, loaded from chain database and
206
    // injected in chainDB.open(). It should be in the most
207
    // recently-committed state, which should have been at the last
208
    // tree interval. We might also need to recover from a
209
    // failed compactTree() operation. Either way, there might have been
210
    // new blocks added to the chain since then.
211
    const currentRoot = this.db.treeRoot();
293✔
212

213
    // We store commit height for the tree in the tree state.
214
    // commitHeight is the height of the block that committed tree root.
215
    // Note that the block at commitHeight has different tree root.
216
    const treeState = await this.db.getTreeState();
293✔
217
    const {commitHeight} = treeState;
293✔
218

219
    // sanity check
220
    if (commitHeight < this.height) {
293✔
221
      const entry = await this.db.getEntryByHeight(commitHeight + 1);
42✔
222
      assert(entry.treeRoot.equals(treeState.treeRoot));
42✔
223
      assert(entry.treeRoot.equals(currentRoot));
42✔
224
    }
225

226
    // Replay all blocks since the last tree interval to rebuild
227
    // the `txn` which is the in-memory delta between tree interval commitments.
228
    for (let height = commitHeight + 1; height <= this.height; height++) {
293✔
229
      const entry = await this.db.getEntryByHeight(height);
1,718✔
230
      assert(entry);
1,718✔
231

232
      const block = await this.db.getBlock(entry.hash);
1,718✔
233
      assert(block);
1,718✔
234

235
      const state = await this.readDeploymentState(entry);
1,718✔
236
      assert(state);
1,718✔
237

238
      const view = new CoinView();
1,718✔
239

240
      for (const tx of block.txs)
1,718✔
241
        await this.verifyCovenants(tx, view, height, state.nameFlags);
2,159✔
242

243
      // If the chain replay crosses a tree interval, it will commit
244
      // and write to disk in saveNames(), resetting the `txn` like usual.
245
      await this.db.saveNames(view, entry, false);
1,718✔
246
    }
247

248
    this.logger.info('Synchronized Tree Root: %x.', this.db.txn.rootHash());
293✔
249
  }
250

251
  /**
252
   * Perform all necessary contextual verification on a block.
253
   * @private
254
   * @param {Block} block
255
   * @param {ChainEntry} prev
256
   * @param {Number} flags
257
   * @returns {Promise<Array>} - [CoinView, DeploymentState]
258
   */
259

260
  async verifyContext(block, prev, flags) {
261
    // Initial non-contextual verification.
262
    const state = await this.verify(block, prev, flags);
20,561✔
263

264
    // Skip everything if we're in SPV mode.
265
    if (this.options.spv) {
20,540✔
266
      const view = new CoinView();
596✔
267
      return [view, state];
596✔
268
    }
269

270
    // Skip everything if we're using checkpoints.
271
    if (this.isHistorical(prev)) {
19,944✔
272
      const view = await this.updateInputs(block, prev, state);
290✔
273
      return [view, state];
290✔
274
    }
275

276
    // Verify scripts, spend and add coins.
277
    const view = await this.verifyInputs(block, prev, state);
19,654✔
278

279
    return [view, state];
19,617✔
280
  }
281

282
  /**
283
   * Perform all necessary contextual verification
284
   * on a block, without POW check.
285
   * @param {Block} block
286
   * @returns {Promise<Array>} - [CoinView, DeploymentState]
287
   */
288

289
  async verifyBlock(block) {
290
    const unlock = await this.locker.lock();
1✔
291
    try {
1✔
292
      return await this._verifyBlock(block);
1✔
293
    } finally {
294
      unlock();
1✔
295
    }
296
  }
297

298
  /**
299
   * Perform all necessary contextual verification
300
   * on a block, without POW check (no lock).
301
   * @private
302
   * @param {Block} block
303
   * @returns {Promise<Array>} - [CoinView, DeploymentState]
304
   */
305

306
  async _verifyBlock(block) {
307
    const flags = common.DEFAULT_FLAGS & ~common.flags.VERIFY_POW;
1✔
308
    return this.verifyContext(block, this.tip, flags);
1✔
309
  }
310

311
  /**
312
   * Test whether the hash is in the main chain.
313
   * @param {Hash} hash
314
   * @returns {Promise<Boolean>}
315
   */
316

317
  isMainHash(hash) {
318
    return this.db.isMainHash(hash);
124✔
319
  }
320

321
  /**
322
   * Test whether the entry is in the main chain.
323
   * @param {ChainEntry} entry
324
   * @returns {Promise<Boolean>}
325
   */
326

327
  isMainChain(entry) {
328
    return this.db.isMainChain(entry);
655✔
329
  }
330

331
  /**
332
   * Get ancestor by `height`.
333
   * @param {ChainEntry} entry
334
   * @param {Number} height
335
   * @returns {Promise<ChainEntry?>}
336
   */
337

338
  getAncestor(entry, height) {
339
    return this.db.getAncestor(entry, height);
217,633✔
340
  }
341

342
  /**
343
   * Get previous entry.
344
   * @param {ChainEntry} entry
345
   * @returns {Promise<ChainEntry?>}
346
   */
347

348
  getPrevious(entry) {
349
    return this.db.getPrevious(entry);
45,940✔
350
  }
351

352
  /**
353
   * Get previous cached entry.
354
   * @param {ChainEntry} entry
355
   * @returns {ChainEntry?}
356
   */
357

358
  getPrevCache(entry) {
359
    return this.db.getPrevCache(entry);
417,260✔
360
  }
361

362
  /**
363
   * Get next entry.
364
   * @param {ChainEntry} entry
365
   * @returns {Promise<ChainEntry?>}
366
   */
367

368
  getNext(entry) {
369
    return this.db.getNext(entry);
2,557✔
370
  }
371

372
  /**
373
   * Get next entry.
374
   * @param {ChainEntry} entry
375
   * @returns {Promise<ChainEntry?>}
376
   */
377

378
  getNextEntry(entry) {
379
    return this.db.getNextEntry(entry);
×
380
  }
381

382
  /**
383
   * Calculate median time past.
384
   * @param {ChainEntry} prev
385
   * @returns {Promise<Number>}
386
   */
387

388
  async getMedianTime(prev) {
389
    const timespan = consensus.MEDIAN_TIMESPAN;
39,487✔
390
    const median = [];
39,487✔
391

392
    let entry = prev;
39,487✔
393

394
    for (let i = 0; i < timespan && entry; i++) {
39,487✔
395
      median.push(entry.time);
417,260✔
396

397
      const cache = this.getPrevCache(entry);
417,260✔
398

399
      if (cache)
417,260✔
400
        entry = cache;
413,853✔
401
      else
402
        entry = await this.getPrevious(entry);
3,407✔
403
    }
404

405
    median.sort(cmp);
39,487✔
406

407
    return median[median.length >>> 1];
39,487✔
408
  }
409

410
  /**
411
   * Test whether the entry is potentially
412
   * an ancestor of a checkpoint.
413
   * @param {ChainEntry} prev
414
   * @returns {Boolean}
415
   */
416

417
  isHistorical(prev) {
418
    if (this.options.checkpoints) {
40,958✔
419
      if (prev.height + 1 <= this.network.lastCheckpoint)
40,376✔
420
        return true;
580✔
421
    }
422
    return false;
40,378✔
423
  }
424

425
  /**
426
   * Test whether the height is potentially
427
   * an ancestor of a checkpoint.
428
   * @param {Number} height
429
   * @returns {Boolean}
430
   */
431

432
  isHistoricalHeight(height) {
433
    if (this.options.checkpoints) {
19,968✔
434
      if (height <= this.network.lastCheckpoint)
19,933✔
435
        return true;
36✔
436
    }
437
    return false;
19,932✔
438
  }
439

440
  /**
441
   * Contextual verification for a block, including
442
   * version deployments (IsSuperMajority), versionbits,
443
   * coinbase height, finality checks.
444
   * @private
445
   * @param {Block} block
446
   * @param {ChainEntry} prev
447
   * @param {Number} flags
448
   * @returns {Promise<DeploymentState>}
449
   */
450

451
  async verify(block, prev, flags) {
452
    assert(typeof flags === 'number');
21,014✔
453

454
    // Extra sanity check.
455
    if (!block.prevBlock.equals(prev.hash))
21,014!
456
      throw new VerifyError(block, 'invalid', 'bad-prevblk', 0);
×
457

458
    // Verify a checkpoint if there is one.
459
    const hash = block.hash();
21,014✔
460
    if (!this.verifyCheckpoint(prev, hash)) {
21,014!
461
      throw new VerifyError(block,
×
462
        'checkpoint',
463
        'checkpoint mismatch',
464
        100);
465
    }
466

467
    // Skip everything when using checkpoints.
468
    // We can do this safely because every
469
    // block in between each checkpoint was
470
    // validated outside in the header chain.
471
    if (this.isHistorical(prev)) {
21,014✔
472
      // Check merkle root.
473
      if (flags & common.flags.VERIFY_BODY) {
290!
474
        assert(typeof block.createMerkleRoot === 'function');
290✔
475

476
        const root = block.createMerkleRoot();
290✔
477

478
        if (!block.merkleRoot.equals(root)) {
290!
479
          throw new VerifyError(block,
×
480
            'invalid',
481
            'bad-txnmrklroot',
482
            100,
483
            true);
484
        }
485

486
        const witnessRoot = block.createWitnessRoot();
290✔
487

488
        if (!block.witnessRoot.equals(witnessRoot)) {
290!
489
          throw new VerifyError(block,
×
490
            'invalid',
491
            'bad-witnessroot',
492
            100,
493
            true);
494
        }
495

496
        flags &= ~common.flags.VERIFY_BODY;
290✔
497
      }
498
    }
499

500
    // Non-contextual checks.
501
    if (flags & common.flags.VERIFY_BODY) {
21,014✔
502
      const [valid, reason, score] = block.checkBody();
19,780✔
503

504
      if (!valid)
19,780✔
505
        throw new VerifyError(block, 'invalid', reason, score, true);
5✔
506
    }
507

508
    // Check name DoS limits.
509
    const set = new BufferSet();
21,009✔
510

511
    let opens = 0;
21,009✔
512
    let updates = 0;
21,009✔
513
    let renewals = 0;
21,009✔
514

515
    for (let i = 0; i < block.txs.length; i++) {
21,009✔
516
      const tx = block.txs[i];
31,775✔
517

518
      opens += rules.countOpens(tx);
31,775✔
519

520
      if (opens > consensus.MAX_BLOCK_OPENS) {
31,775✔
521
        throw new VerifyError(block,
1✔
522
          'invalid',
523
          'bad-blk-opens',
524
          100);
525
      }
526

527
      updates += rules.countUpdates(tx);
31,774✔
528

529
      if (updates > consensus.MAX_BLOCK_UPDATES) {
31,774✔
530
        throw new VerifyError(block,
1✔
531
          'invalid',
532
          'bad-blk-updates',
533
          100);
534
      }
535

536
      renewals += rules.countRenewals(tx);
31,773✔
537

538
      if (renewals > consensus.MAX_BLOCK_RENEWALS) {
31,773✔
539
        throw new VerifyError(block,
1✔
540
          'invalid',
541
          'bad-blk-renewals',
542
          100);
543
      }
544

545
      // Certain covenants can only be used once per name per block
546
      if (rules.hasNames(tx, set)) {
31,772✔
547
        throw new VerifyError(block,
1✔
548
          'invalid',
549
          'bad-blk-names',
550
          100);
551
      }
552

553
      rules.addNames(tx, set);
31,771✔
554
    }
555

556
    // Ensure the POW is what we expect.
557
    const bits = await this.getTarget(block.time, prev);
21,005✔
558

559
    if (block.bits !== bits) {
21,005✔
560
      this.logger.debug(
1✔
561
        'Bad diffbits: 0x%s != 0x%s',
562
        util.hex32(block.bits),
563
        util.hex32(bits));
564

565
      throw new VerifyError(block,
1✔
566
        'invalid',
567
        'bad-diffbits',
568
        100);
569
    }
570

571
    // Ensure the timestamp is correct.
572
    const mtp = await this.getMedianTime(prev);
21,004✔
573

574
    if (block.time <= mtp) {
21,004✔
575
      throw new VerifyError(block,
3✔
576
        'invalid',
577
        'time-too-old',
578
        0);
579
    }
580

581
    // Check timestamp against adj-time+2hours.
582
    // If this fails we may be able to accept
583
    // the block later.
584
    if (block.time > this.network.now() + 2 * 60 * 60) {
21,001✔
585
      throw new VerifyError(block,
3✔
586
        'invalid',
587
        'time-too-new',
588
        0,
589
        true);
590
    }
591

592
    // Skip all blocks in spv mode once
593
    // we've verified the network target.
594
    if (this.options.spv)
20,998✔
595
      return this.state;
644✔
596

597
    // Calculate height of current block.
598
    const height = prev.height + 1;
20,354✔
599

600
    // Get the new deployment state.
601
    const state = await this.getDeployments(block.time, prev);
20,354✔
602

603
    // Transactions must be finalized with
604
    // regards to nSequence and nLockTime.
605
    for (let i = 1; i < block.txs.length; i++) {
20,354✔
606
      const tx = block.txs[i];
9,662✔
607
      if (!tx.isFinal(height, mtp)) {
9,662✔
608
        throw new VerifyError(block,
1✔
609
          'invalid',
610
          'bad-txns-nonfinal',
611
          10);
612
      }
613
    }
614

615
    // Make sure the height contained
616
    // in the coinbase is correct.
617
    if (block.getCoinbaseHeight() !== height) {
20,353✔
618
      throw new VerifyError(block,
1✔
619
        'invalid',
620
        'bad-cb-height',
621
        100);
622
    }
623

624
    const cb = block.txs[0];
20,352✔
625

626
    for (let i = 1; i < cb.inputs.length; i++) {
20,352✔
627
      const {witness} = cb.inputs[i];
68✔
628

629
      if (witness.items.length !== 1) {
68!
630
        throw new VerifyError(block,
×
631
          'invalid',
632
          'bad-witness-size',
633
          100);
634
      }
635

636
      if (i >= cb.outputs.length) {
68!
637
        throw new VerifyError(block,
×
638
          'invalid',
639
          'bad-output',
640
          100);
641
      }
642

643
      const output = cb.outputs[i];
68✔
644

645
      // Airdrop proof.
646
      if (!output.covenant.isClaim()) {
68✔
647
        // Disable airdrop claims if airstop is activated
648
        if (state.hasAirstop) {
35✔
649
          throw new VerifyError(block,
2✔
650
            'invalid',
651
            'bad-airdrop-disabled',
652
            100);
653
        }
654

655
        let proof;
656
        try {
33✔
657
          proof = AirdropProof.decode(witness.items[0]);
33✔
658
        } catch (e) {
659
          throw new VerifyError(block,
×
660
            'invalid',
661
            'bad-airdrop-format',
662
            100);
663
        }
664

665
        if (!proof.isSane()) {
33!
666
          throw new VerifyError(block,
×
667
            'invalid',
668
            'bad-airdrop-sanity',
669
            100);
670
        }
671

672
        if (prev.height + 1 >= this.network.goosigStop) {
33✔
673
          const key = proof.getKey();
1✔
674

675
          if (!key) {
1!
676
            throw new VerifyError(block,
×
677
              'invalid',
678
              'bad-airdrop-proof',
679
              100);
680
          }
681

682
          if (key.isGoo()) {
1!
683
            throw new VerifyError(block,
1✔
684
              'invalid',
685
              'bad-goosig-disabled',
686
              100);
687
          }
688
        }
689

690
        // Note: GooSig RSA 1024 is possible to
691
        // crack as well, but in order to make
692
        // it safe we would need to include a
693
        // commitment to the key size (bad).
694
        // We may have to just disallow <2048
695
        // bit for mainnet.
696
        if (state.hasHardening()) {
32!
697
          if (proof.isWeak()) {
×
698
            throw new VerifyError(block,
×
699
              'invalid',
700
              'bad-airdrop-sanity',
701
              10);
702
          }
703
        }
704

705
        continue;
32✔
706
      }
707

708
      // DNSSEC ownership proof.
709
      let proof;
710
      try {
33✔
711
        proof = OwnershipProof.decode(witness.items[0]);
33✔
712
      } catch (e) {
713
        throw new VerifyError(block,
×
714
          'invalid',
715
          'bad-dnssec-format',
716
          100);
717
      }
718

719
      // Verify times.
720
      if (!proof.verifyTimes(prev.time)) {
33!
721
        throw new VerifyError(block,
×
722
          'invalid',
723
          'bad-dnssec-times',
724
          10);
725
      }
726
    }
727

728
    return state;
20,349✔
729
  }
730

731
  /**
732
   * Check all deployments on a chain.
733
   * @param {Number} time
734
   * @param {ChainEntry} prev
735
   * @returns {Promise<DeploymentState>}
736
   */
737

738
  async getDeployments(time, prev) {
739
    const deployments = this.network.deployments;
49,269✔
740
    const state = new DeploymentState(prev.hash);
49,269✔
741

742
    // Disable RSA-1024.
743
    if (await this.isActive(prev, deployments.hardening))
49,269✔
744
      state.nameFlags |= rules.nameFlags.VERIFY_COVENANTS_HARDENED;
1✔
745

746
    // Disable ICANN, TOP100 and CUSTOM TLDs from getting auctioned.
747
    if (await this.isActive(prev, deployments.icannlockup))
49,269✔
748
      state.nameFlags |= rules.nameFlags.VERIFY_COVENANTS_LOCKUP;
1,335✔
749

750
    // Disable airdrop claims.
751
    if (await this.isActive(prev, deployments.airstop))
49,269✔
752
      state.hasAirstop = true;
7,539✔
753

754
    return state;
49,269✔
755
  }
756

757
  /**
758
   * Set a new deployment state.
759
   * @param {DeploymentState} state
760
   */
761

762
  setDeploymentState(state) {
763
    if (this.options.checkpoints && this.height < this.network.lastCheckpoint) {
20,816✔
764
      this.state = state;
299✔
765
      return;
299✔
766
    }
767

768
    if (!this.state.hasHardening() && state.hasHardening())
20,517!
769
      this.logger.warning('RSA hardening has been activated.');
×
770

771
    if (this.height === this.network.deflationHeight)
20,517✔
772
      this.logger.warning('Name claim deflation has been activated.');
26✔
773

774
    if (!this.state.hasICANNLockup() && state.hasICANNLockup())
20,517✔
775
      this.logger.warning('ICANN lockup has been activated.');
1✔
776

777
    if (!this.state.hasAirstop && state.hasAirstop)
20,517✔
778
      this.logger.warning('Airdrop claims has been disabled.');
4✔
779

780
    this.state = state;
20,517✔
781
  }
782

783
  /**
784
   * Spend and update inputs (checkpoints only).
785
   * @private
786
   * @param {Block} block
787
   * @param {ChainEntry} prev
788
   * @param {DeploymentState} state
789
   * @returns {Promise<CoinView>}
790
   */
791

792
  async updateInputs(block, prev, state) {
793
    const view = new CoinView();
290✔
794
    const height = prev.height + 1;
290✔
795

796
    assert(block.treeRoot.equals(this.db.treeRoot()));
290✔
797

798
    for (let i = 0; i < block.txs.length; i++) {
290✔
799
      const tx = block.txs[i];
321✔
800

801
      if (i === 0) {
321✔
802
        assert(view.bits.spend(this.db.field, tx));
290✔
803
      } else {
804
        assert(await view.spendInputs(this.db, tx),
31✔
805
          'BUG: Spent inputs in historical data!');
806
      }
807

808
      await this.verifyCovenants(tx, view, height, state.nameFlags);
321✔
809

810
      view.addTX(tx, height);
321✔
811
    }
812

813
    return view;
290✔
814
  }
815

816
  /**
817
   * Check block transactions for all things pertaining
818
   * to inputs. This function is important because it is
819
   * what actually fills the coins into the block. This
820
   * function will check the block reward, the sigops,
821
   * the tx values, and execute and verify the scripts (it
822
   * will attempt to do this on the worker pool). If
823
   * `checkpoints` is enabled, it will skip verification
824
   * for historical data.
825
   * @private
826
   * @see TX#verifyInputs
827
   * @see TX#verify
828
   * @param {Block} block
829
   * @param {ChainEntry} prev
830
   * @param {DeploymentState} state
831
   * @returns {Promise<CoinView>}
832
   */
833

834
  async verifyInputs(block, prev, state) {
835
    const network = this.network;
19,654✔
836
    const view = new CoinView();
19,654✔
837
    const height = prev.height + 1;
19,654✔
838
    const interval = network.halvingInterval;
19,654✔
839

840
    let sigops = 0;
19,654✔
841
    let reward = 0;
19,654✔
842

843
    // Check the name tree root.
844
    if (!block.treeRoot.equals(this.db.treeRoot())) {
19,654!
845
      throw new VerifyError(block,
×
846
        'invalid',
847
        'bad-tree-root',
848
        100);
849
    }
850

851
    // Check all transactions
852
    for (let i = 0; i < block.txs.length; i++) {
19,654✔
853
      const tx = block.txs[i];
29,166✔
854

855
      // Ensure tx is not double spending an output.
856
      if (i === 0) {
29,166✔
857
        if (!view.bits.spend(this.db.field, tx)) {
19,654✔
858
          throw new VerifyError(block,
4✔
859
            'invalid',
860
            'bad-txns-bits-missingorspent',
861
            100);
862
        }
863
      } else {
864
        if (!await view.spendInputs(this.db, tx)) {
9,512✔
865
          throw new VerifyError(block,
2✔
866
            'invalid',
867
            'bad-txns-inputs-missingorspent',
868
            100);
869
        }
870

871
        // Verify sequence locks.
872
        const valid = await this.verifyLocks(prev, tx, view, state.lockFlags);
9,510✔
873

874
        if (!valid) {
9,510✔
875
          throw new VerifyError(block,
2✔
876
            'invalid',
877
            'bad-txns-nonfinal',
878
            100);
879
        }
880
      }
881

882
      // Count sigops.
883
      sigops += tx.getSigops(view);
29,158✔
884

885
      if (sigops > consensus.MAX_BLOCK_SIGOPS) {
29,158✔
886
        throw new VerifyError(block,
1✔
887
          'invalid',
888
          'bad-blk-sigops',
889
          100);
890
      }
891

892
      // Contextual sanity checks.
893
      const [fee, reason, score] = tx.checkInputs(view, height, network);
29,157✔
894

895
      if (fee === -1) {
29,157✔
896
        throw new VerifyError(block,
2✔
897
          'invalid',
898
          reason,
899
          score);
900
      }
901

902
      reward += fee;
29,155✔
903

904
      if (reward > consensus.MAX_MONEY) {
29,155!
905
        throw new VerifyError(block,
×
906
          'invalid',
907
          'bad-cb-amount',
908
          100);
909
      }
910

911
      // Verify covenants.
912
      await this.verifyCovenants(tx, view, height, state.nameFlags);
29,155✔
913

914
      // Add new coins.
915
      view.addTX(tx, height);
29,140✔
916
    }
917

918
    // Make sure the miner isn't trying to conjure more coins.
919
    reward += consensus.getReward(height, interval);
19,628✔
920

921
    if (block.getClaimed() > reward) {
19,628✔
922
      throw new VerifyError(block,
6✔
923
        'invalid',
924
        'bad-cb-amount',
925
        0);
926
    }
927

928
    // Push onto verification queue.
929
    const jobs = [];
19,622✔
930
    for (let i = 0; i < block.txs.length; i++) {
19,622✔
931
      const tx = block.txs[i];
29,030✔
932
      jobs.push(tx.verifyAsync(view, state.flags, this.workers));
29,030✔
933
    }
934

935
    // Verify all txs in parallel.
936
    const results = await Promise.all(jobs);
19,622✔
937

938
    for (const result of results) {
19,622✔
939
      if (!result) {
29,030✔
940
        throw new VerifyError(block,
5✔
941
          'invalid',
942
          'mandatory-script-verify-flag-failed',
943
          100);
944
      }
945
    }
946

947
    return view;
19,617✔
948
  }
949

950
  /**
951
   * Get main chain height for hash.
952
   * @param {Hash} hash
953
   * @returns {Promise<Number>}
954
   */
955

956
  async getMainHeight(hash) {
957
    const entry = await this.db.getEntry(hash);
53✔
958

959
    if (!entry)
53!
960
      return -1;
×
961

962
    // Must be the current chain.
963
    if (!await this.db.isMainChain(entry))
53!
964
      return -1;
×
965

966
    return entry.height;
53✔
967
  }
968

969
  /**
970
   * Verify a renewal.
971
   * @param {Hash} hash
972
   * @param {Number} height
973
   * @returns {Promise<Boolean>}
974
   */
975

976
  async verifyRenewal(hash, height) {
977
    assert(Buffer.isBuffer(hash));
4,099✔
978
    assert((height >>> 0) === height);
4,099✔
979

980
    // Cannot renew yet.
981
    if (height < this.network.names.renewalMaturity)
4,099✔
982
      return true;
94✔
983

984
    // We require renewals to commit to a block
985
    // within the past 6 months, to prove that
986
    // the user still owns the key. This prevents
987
    // people from presigning thousands of years
988
    // worth of renewals. The block must be at
989
    // least 400 blocks back to prevent the
990
    // possibility of a reorg invalidating the
991
    // covenant.
992

993
    const entry = await this.db.getEntry(hash);
4,005✔
994

995
    if (!entry)
4,005!
996
      return false;
×
997

998
    // Must be the current chain.
999
    if (!await this.db.isMainChain(entry))
4,005!
1000
      return false;
×
1001

1002
    // Make sure it's a mature block (unlikely to be reorgd).
1003
    if (entry.height > height - this.network.names.renewalMaturity)
4,005!
1004
      return false;
×
1005

1006
    // Block committed to must be
1007
    // no older than a 6 months.
1008
    if (entry.height < height - this.network.names.renewalPeriod)
4,005!
1009
      return false;
×
1010

1011
    return true;
4,005✔
1012
  }
1013

1014
  /**
1015
   * Verify covenants.
1016
   * @param {TX} tx
1017
   * @param {CoinView} view
1018
   * @param {Number} height
1019
   * @param {Number} nameFlags
1020
   */
1021

1022
  async verifyCovenants(tx, view, height, nameFlags) {
1023
    assert(tx);
33,927✔
1024
    assert(view instanceof CoinView);
33,927✔
1025
    assert((height >>> 0) === height);
33,927✔
1026
    assert(typeof nameFlags === 'number');
33,927✔
1027

1028
    const {types} = rules;
33,927✔
1029
    const network = this.network;
33,927✔
1030

1031
    for (let i = 0; i < tx.outputs.length; i++) {
33,927✔
1032
      const output = tx.outputs[i];
80,914✔
1033
      const {covenant} = output;
80,914✔
1034

1035
      if (!covenant.isName())
80,914✔
1036
        continue;
60,946✔
1037

1038
      // BID and REDEEM covenants to do not update NameState.
1039
      // Therefore, if we are still inside checkpoints we can simply
1040
      // assume these covenants are valid without checking anything,
1041
      // or even getting and decoding the NameState from the tree.
1042
      // We could skip checks for ALL covenant types under checkpoints,
1043
      // but since the other types modify the NameState we still
1044
      // need to get the data, and the checks themselves are cheap.
1045
      if (this.isHistoricalHeight(height)) {
19,968✔
1046
        if (covenant.isBid() || covenant.isRedeem())
36✔
1047
          continue;
13✔
1048
      }
1049

1050
      const nameHash = covenant.getHash(0);
19,955✔
1051
      const start = covenant.getU32(1);
19,955✔
1052
      const ns = await view.getNameState(this.db, nameHash);
19,955✔
1053

1054
      if (ns.isNull()) {
19,955✔
1055
        if (!covenant.isClaim() && !covenant.isOpen())
3,607!
1056
          throw new Error('Database inconsistency.');
×
1057

1058
        const name = covenant.get(2);
3,607✔
1059
        ns.set(name, height);
3,607✔
1060
      }
1061

1062
      // Check for name expiration/revocation.
1063
      // Note that claimed names never expire
1064
      // before the reservation period ends.
1065
      // However, they _can_ be revoked.
1066
      ns.maybeExpire(height, network);
19,955✔
1067

1068
      // Calculate the current state.
1069
      const state = ns.state(height, network);
19,955✔
1070

1071
      // none -> claim
1072
      if (covenant.isClaim()) {
19,955✔
1073
        const flags = covenant.getU8(3);
57✔
1074
        const weak = (flags & 1) !== 0;
57✔
1075

1076
        // Claims can be re-redeemed any time
1077
        // before registration. This is required
1078
        // in order for our emergency soft-forks
1079
        // to truly behave as _soft_ forks. Once
1080
        // re-redeemed, the locktime resets and
1081
        // they re-enter the LOCKED state. Note
1082
        // that a newer claim invalidates the
1083
        // old output by committing to a higher
1084
        // height (will fail with nonlocal).
1085
        const valid = state === states.OPENING
57!
1086
                   || state === states.LOCKED
1087
                   || (state === states.CLOSED && !ns.registered);
1088

1089
        if (!valid) {
57!
1090
          throw new VerifyError(tx,
×
1091
            'invalid',
1092
            'bad-claim-state',
1093
            100);
1094
        }
1095

1096
        // Can only claim reserved names.
1097
        // Once a reserved name is revoked,
1098
        // it is no longer claimable.
1099
        if (ns.expired || !rules.isReserved(nameHash, height, network)) {
57✔
1100
          throw new VerifyError(tx,
4✔
1101
            'invalid',
1102
            'bad-claim-notreserved',
1103
            100);
1104
        }
1105

1106
        // Once the fork is active, we reject
1107
        // any weak algorithms (i.e. RSA-1024).
1108
        // Any future emergency soft-forks should
1109
        // also be included below this check.
1110
        if ((nameFlags & VERIFY_COVENANTS_HARDENED) && weak) {
53!
1111
          throw new VerifyError(tx,
×
1112
            'invalid',
1113
            'bad-claim-algorithm',
1114
            100);
1115
        }
1116

1117
        // Check commitment hash.
1118
        const block = covenant.getHash(4);
53✔
1119
        const claimed = await this.getMainHeight(block);
53✔
1120

1121
        // Implicitly checks for `-1`.
1122
        if (claimed !== covenant.getU32(5)) {
53!
1123
          throw new VerifyError(tx,
×
1124
            'invalid',
1125
            'bad-claim-commit-height',
1126
            100);
1127
        }
1128

1129
        // Implicitly disallows the genesis block.
1130
        if (claimed <= ns.claimed) {
53!
1131
          throw new VerifyError(tx,
×
1132
            'invalid',
1133
            'bad-claim-commit-hash',
1134
            100);
1135
        }
1136

1137
        assert(claimed >= 1);
53✔
1138

1139
        // Handle inflation-fixing soft-fork.
1140
        if (height >= network.deflationHeight) {
53✔
1141
          const {claimFrequency} = network.names;
24✔
1142

1143
          // Require claim height to be 1 on
1144
          // initial claims. This makes some
1145
          // non-contextual verification easier.
1146
          if (ns.owner.isNull()) {
24✔
1147
            if (claimed !== 1) {
17!
1148
              throw new VerifyError(tx,
×
1149
                'invalid',
1150
                'bad-claim-height',
1151
                0);
1152
            }
1153
          }
1154

1155
          // Limit the frequency of re-claims.
1156
          if (!ns.owner.isNull() && height < ns.height + claimFrequency) {
24!
1157
            throw new VerifyError(tx,
×
1158
              'invalid',
1159
              'bad-claim-frequency',
1160
              0);
1161
          }
1162

1163
          // Allow replacement, but require the
1164
          // same fee, which is then miner-burned.
1165
          if (!ns.owner.isNull()) {
24✔
1166
            const coin = await this.getCoin(ns.owner.hash, ns.owner.index);
7✔
1167

1168
            if (!coin || output.value !== coin.value) {
7✔
1169
              throw new VerifyError(tx,
1✔
1170
                'invalid',
1171
                'bad-claim-value',
1172
                0);
1173
            }
1174
          }
1175
        }
1176

1177
        ns.setHeight(height);
52✔
1178
        ns.setRenewal(height);
52✔
1179
        ns.setClaimed(claimed);
52✔
1180
        ns.setValue(0);
52✔
1181
        ns.setOwner(tx.outpoint(i));
52✔
1182
        ns.setHighest(0);
52✔
1183
        ns.setWeak(weak);
52✔
1184

1185
        continue;
52✔
1186
      }
1187

1188
      assert(!tx.isCoinbase());
19,898✔
1189

1190
      // none/redeem/open -> open
1191
      if (covenant.isOpen()) {
19,898✔
1192
        if (state !== states.OPENING) {
3,567!
1193
          throw new VerifyError(tx,
×
1194
            'invalid',
1195
            'bad-open-state',
1196
            100);
1197
        }
1198

1199
        // Only one open transaction can ever exist.
1200
        if (ns.height !== height) {
3,567!
1201
          throw new VerifyError(tx,
×
1202
            'invalid',
1203
            'bad-open-multiple',
1204
            100);
1205
        }
1206

1207
        // Cannot bid on a reserved name.
1208
        if (!ns.expired && rules.isReserved(nameHash, height, network)) {
3,567!
1209
          throw new VerifyError(tx,
×
1210
            'invalid',
1211
            'bad-open-reserved',
1212
            100);
1213
        }
1214

1215
        // Make sure locked up names are not opened if ICANN LOCKUP has
1216
        // activated.
1217
        const isLockUpActive = nameFlags & VERIFY_COVENANTS_LOCKUP;
3,567✔
1218
        if (isLockUpActive && rules.isLockedUp(nameHash, height, network)) {
3,567✔
1219
          throw new VerifyError(tx,
4✔
1220
            'invalid',
1221
            'bad-open-lockedup',
1222
            100);
1223
        }
1224

1225
        // On mainnet, names are released on a
1226
        // weekly basis for the first year.
1227
        if (!rules.hasRollout(nameHash, height, network)) {
3,563!
1228
          throw new VerifyError(tx,
×
1229
            'invalid',
1230
            'bad-open-rollout',
1231
            100);
1232
        }
1233

1234
        continue;
3,563✔
1235
      }
1236

1237
      // none/redeem/open -> bid
1238
      if (covenant.isBid()) {
16,331✔
1239
        if (state !== states.BIDDING) {
4,047✔
1240
          throw new VerifyError(tx,
3✔
1241
            'invalid',
1242
            'bad-bid-state',
1243
            100);
1244
        }
1245

1246
        if (start !== ns.height) {
4,044!
1247
          throw new VerifyError(tx,
×
1248
            'invalid',
1249
            'bad-bid-height',
1250
            100);
1251
        }
1252

1253
        continue;
4,044✔
1254
      }
1255

1256
      assert(i < tx.inputs.length);
12,284✔
1257

1258
      const {prevout} = tx.inputs[i];
12,284✔
1259

1260
      switch (covenant.type) {
12,284!
1261
        // bid -> reveal
1262
        case types.REVEAL: {
1263
          if (start !== ns.height) {
3,934!
1264
            throw new VerifyError(tx,
×
1265
              'invalid',
1266
              'bad-reveal-nonlocal',
1267
              100);
1268
          }
1269

1270
          // Early reveals? No.
1271
          if (state !== states.REVEAL) {
3,934!
1272
            throw new VerifyError(tx,
×
1273
              'invalid',
1274
              'bad-reveal-state',
1275
              100);
1276
          }
1277

1278
          if (ns.owner.isNull() || output.value > ns.highest) {
3,934✔
1279
            ns.setValue(ns.highest);
2,401✔
1280
            ns.setOwner(tx.outpoint(i));
2,401✔
1281
            ns.setHighest(output.value);
2,401✔
1282
          } else if (output.value > ns.value) {
1,533✔
1283
            ns.setValue(output.value);
938✔
1284
          }
1285

1286
          break;
3,934✔
1287
        }
1288

1289
        // reveal -> redeem
1290
        case types.REDEEM: {
1291
          if (start !== ns.height) {
1,640!
1292
            throw new VerifyError(tx,
×
1293
              'invalid',
1294
              'bad-redeem-nonlocal',
1295
              100);
1296
          }
1297

1298
          // Allow participants to get their
1299
          // money out, even in a revoked state.
1300
          if (state < states.CLOSED) {
1,640!
1301
            throw new VerifyError(tx,
×
1302
              'invalid',
1303
              'bad-redeem-state',
1304
              100);
1305
          }
1306

1307
          // Must be the loser in order
1308
          // to redeem the money now.
1309
          if (prevout.equals(ns.owner)) {
1,640!
1310
            throw new VerifyError(tx,
×
1311
              'invalid',
1312
              'bad-redeem-owner',
1313
              100);
1314
          }
1315

1316
          break;
1,640✔
1317
        }
1318

1319
        // claim/reveal -> register
1320
        case types.REGISTER: {
1321
          if (start !== ns.height) {
1,774!
1322
            throw new VerifyError(tx,
×
1323
              'invalid',
1324
              'bad-register-nonlocal',
1325
              100);
1326
          }
1327

1328
          if (state !== states.CLOSED) {
1,774!
1329
            throw new VerifyError(tx,
×
1330
              'invalid',
1331
              'bad-register-state',
1332
              100);
1333
          }
1334

1335
          const data = covenant.get(2);
1,774✔
1336
          const hash = covenant.getHash(3);
1,774✔
1337

1338
          // Verify block hash for renewal.
1339
          if (!await this.verifyRenewal(hash, height)) {
1,774!
1340
            throw new VerifyError(tx,
×
1341
              'invalid',
1342
              'bad-register-renewal',
1343
              100);
1344
          }
1345

1346
          // Must be the winner in
1347
          // order to redeem the name.
1348
          if (!prevout.equals(ns.owner)) {
1,774!
1349
            throw new VerifyError(tx,
×
1350
              'invalid',
1351
              'bad-register-owner',
1352
              100);
1353
          }
1354

1355
          // Must match the second highest bid.
1356
          if (output.value !== ns.value) {
1,774!
1357
            throw new VerifyError(tx,
×
1358
              'invalid',
1359
              'bad-register-value',
1360
              100);
1361
          }
1362

1363
          // For claimed names: if the keys used in
1364
          // the proof were somehow compromised, the
1365
          // name becomes locked until the reservation
1366
          // period ends. Note that this is the same
1367
          // code path that can be used for emergency
1368
          // soft-forks in the case that a large name
1369
          // registrar's keys are compromised.
1370
          if (ns.isClaimable(height, network)) {
1,774✔
1371
            // Soft-fork #1 (RSA hardening).
1372
            if ((nameFlags & VERIFY_COVENANTS_HARDENED) && ns.weak) {
9!
1373
              throw new VerifyError(tx,
×
1374
                'invalid',
1375
                'bad-register-state',
1376
                100);
1377
            }
1378

1379
            // Emergency soft-forks go here.
1380
            // Use only to prevent sky from falling.
1381
            //
1382
            // A vision for an emergency soft-fork:
1383
            //
1384
            // 1. A list of compromised DNSKEYs are collected
1385
            //    out of band.
1386
            // 2. The chain is scanned on first boot in order
1387
            //    to find proofs which are vulnerable. The
1388
            //    relevant names are marked as such.
1389
            //    - Pruned nodes and nodes without witness
1390
            //      data will unfortunately need to re-sync.
1391
            // 3. Any proof published before the flag day
1392
            //    is also marked in this way if it contains
1393
            //    a vulnerable key.
1394
            // 4. At soft-fork activation, the "vulnerable"
1395
            //    check will take place here. This function
1396
            //    should return true for any name that was
1397
            //    redeemed with a vulnerable key.
1398
            //
1399
            // To future generations:
1400
            // PUT THE VULNERABLE KEY CHECK HERE!
1401
          }
1402

1403
          ns.setRegistered(true);
1,774✔
1404
          ns.setOwner(tx.outpoint(i));
1,774✔
1405

1406
          if (data.length > 0)
1,774✔
1407
            ns.setData(data);
1,765✔
1408

1409
          ns.setRenewal(height);
1,774✔
1410

1411
          break;
1,774✔
1412
        }
1413

1414
        // update/renew/register/finalize -> update
1415
        case types.UPDATE: {
1416
          if (start !== ns.height) {
1,623!
1417
            throw new VerifyError(tx,
×
1418
              'invalid',
1419
              'bad-update-nonlocal',
1420
              100);
1421
          }
1422

1423
          if (state !== states.CLOSED) {
1,623!
1424
            throw new VerifyError(tx,
×
1425
              'invalid',
1426
              'bad-update-state',
1427
              100);
1428
          }
1429

1430
          const data = covenant.get(2);
1,623✔
1431

1432
          ns.setOwner(tx.outpoint(i));
1,623✔
1433

1434
          if (data.length > 0)
1,623✔
1435
            ns.setData(data);
1,607✔
1436

1437
          ns.setTransfer(0);
1,623✔
1438

1439
          break;
1,623✔
1440
        }
1441

1442
        // update/renew/register/finalize -> renew
1443
        case types.RENEW: {
1444
          if (start !== ns.height) {
1,450!
1445
            throw new VerifyError(tx,
×
1446
              'invalid',
1447
              'bad-renewal-nonlocal',
1448
              100);
1449
          }
1450

1451
          if (state !== states.CLOSED) {
1,450!
1452
            throw new VerifyError(tx,
×
1453
              'invalid',
1454
              'bad-renewal-state',
1455
              100);
1456
          }
1457

1458
          const hash = covenant.getHash(2);
1,450✔
1459

1460
          if (height < ns.renewal + network.names.treeInterval) {
1,450✔
1461
            throw new VerifyError(tx,
2✔
1462
              'invalid',
1463
              'bad-renewal-premature',
1464
              100);
1465
          }
1466

1467
          if (!await this.verifyRenewal(hash, height)) {
1,448!
1468
            throw new VerifyError(tx,
×
1469
              'invalid',
1470
              'bad-renewal',
1471
              100);
1472
          }
1473

1474
          ns.setOwner(tx.outpoint(i));
1,448✔
1475
          ns.setTransfer(0);
1,448✔
1476
          ns.setRenewal(height);
1,448✔
1477
          ns.setRenewals(ns.renewals + 1);
1,448✔
1478

1479
          break;
1,448✔
1480
        }
1481

1482
        // update/renew/register/finalize -> transfer
1483
        case types.TRANSFER: {
1484
          if (start !== ns.height) {
931!
1485
            throw new VerifyError(tx,
×
1486
              'invalid',
1487
              'bad-transfer-nonlocal',
1488
              100);
1489
          }
1490

1491
          if (state !== states.CLOSED) {
931!
1492
            throw new VerifyError(tx,
×
1493
              'invalid',
1494
              'bad-transfer-state',
1495
              100);
1496
          }
1497

1498
          ns.setOwner(tx.outpoint(i));
931✔
1499

1500
          assert(ns.transfer === 0);
931✔
1501
          ns.setTransfer(height);
931✔
1502

1503
          break;
931✔
1504
        }
1505

1506
        // transfer -> finalize
1507
        case types.FINALIZE: {
1508
          if (start !== ns.height) {
888!
1509
            throw new VerifyError(tx,
×
1510
              'invalid',
1511
              'bad-finalize-nonlocal',
1512
              100);
1513
          }
1514

1515
          if (state !== states.CLOSED) {
888!
1516
            throw new VerifyError(tx,
×
1517
              'invalid',
1518
              'bad-finalize-state',
1519
              100);
1520
          }
1521

1522
          assert(ns.transfer !== 0);
888✔
1523
          assert(network.names.transferLockup >= network.names.treeInterval);
888✔
1524

1525
          if (height < ns.transfer + network.names.transferLockup) {
888✔
1526
            throw new VerifyError(tx,
11✔
1527
              'invalid',
1528
              'bad-finalize-maturity',
1529
              100);
1530
          }
1531

1532
          const flags = covenant.getU8(3);
877✔
1533
          const weak = (flags & 1) !== 0;
877✔
1534
          const claimed = covenant.getU32(4);
877✔
1535
          const renewals = covenant.getU32(5);
877✔
1536
          const hash = covenant.getHash(6);
877✔
1537

1538
          if (weak !== ns.weak
877!
1539
              || claimed !== ns.claimed
1540
              || renewals !== ns.renewals) {
1541
            throw new VerifyError(tx,
×
1542
              'invalid',
1543
              'bad-finalize-statetransfer',
1544
              100);
1545
          }
1546

1547
          if (!await this.verifyRenewal(hash, height)) {
877!
1548
            throw new VerifyError(tx,
×
1549
              'invalid',
1550
              'bad-finalize-renewal',
1551
              100);
1552
          }
1553

1554
          ns.setOwner(tx.outpoint(i));
877✔
1555
          ns.setTransfer(0);
877✔
1556
          ns.setRenewal(height);
877✔
1557
          ns.setRenewals(ns.renewals + 1);
877✔
1558

1559
          break;
877✔
1560
        }
1561

1562
        // register/update/renew/transfer/finalize -> revoke
1563
        case types.REVOKE: {
1564
          if (start !== ns.height) {
44!
1565
            throw new VerifyError(tx,
×
1566
              'invalid',
1567
              'bad-revoke-nonlocal',
1568
              100);
1569
          }
1570

1571
          if (state !== states.CLOSED) {
44!
1572
            throw new VerifyError(tx,
×
1573
              'invalid',
1574
              'bad-revoke-state',
1575
              100);
1576
          }
1577

1578
          assert(ns.revoked === 0);
44✔
1579
          ns.setRevoked(height);
44✔
1580
          ns.setTransfer(0);
44✔
1581
          ns.setData(null);
44✔
1582

1583
          break;
44✔
1584
        }
1585

1586
        default: {
1587
          assert.fail('Invalid covenant type.');
×
1588
          break;
×
1589
        }
1590
      }
1591
    }
1592

1593
    return;
33,902✔
1594
  }
1595

1596
  /**
1597
   * Find the block at which a fork ocurred.
1598
   * @private
1599
   * @param {ChainEntry} fork - The current chain.
1600
   * @param {ChainEntry} longer - The competing chain.
1601
   * @returns {Promise<ChainEntry>}
1602
   */
1603

1604
  async findFork(fork, longer) {
1605
    while (!fork.hash.equals(longer.hash)) {
45✔
1606
      while (longer.height > fork.height) {
542✔
1607
        longer = await this.getPrevious(longer);
542✔
1608
        if (!longer)
542!
1609
          throw new Error('No previous entry for new tip.');
×
1610
      }
1611

1612
      if (fork.hash.equals(longer.hash))
542✔
1613
        return fork;
45✔
1614

1615
      fork = await this.getPrevious(fork);
497✔
1616

1617
      if (!fork)
497!
1618
        throw new Error('No previous entry for old tip.');
×
1619
    }
1620

1621
    return fork;
×
1622
  }
1623

1624
  /**
1625
   * Reorganize the blockchain (connect and disconnect inputs).
1626
   * Called when a competing chain with a higher chainwork
1627
   * is received.
1628
   * @private
1629
   * @param {ChainEntry} competitor - The competing chain's tip.
1630
   * @returns {Promise<ChainEntry>} - Fork block.
1631
   */
1632

1633
  async reorganize(competitor) {
1634
    const tip = this.tip;
41✔
1635
    const fork = await this.findFork(tip, competitor);
41✔
1636

1637
    assert(fork, 'No free space or data corruption.');
41✔
1638

1639
    // Blocks to disconnect.
1640
    const disconnect = [];
41✔
1641
    let entry = tip;
41✔
1642
    while (!entry.hash.equals(fork.hash)) {
41✔
1643
      disconnect.push(entry);
464✔
1644
      entry = await this.getPrevious(entry);
464✔
1645
      assert(entry);
464✔
1646
    }
1647

1648
    // Blocks to connect.
1649
    const connect = [];
41✔
1650
    entry = competitor;
41✔
1651
    while (!entry.hash.equals(fork.hash)) {
41✔
1652
      connect.push(entry);
505✔
1653
      entry = await this.getPrevious(entry);
505✔
1654
      assert(entry);
505✔
1655
    }
1656

1657
    // Disconnect blocks/txs.
1658
    for (let i = 0; i < disconnect.length; i++) {
41✔
1659
      const entry = disconnect[i];
464✔
1660
      await this.disconnect(entry);
464✔
1661
    }
1662

1663
    // Connect blocks/txs.
1664
    // We don't want to connect the new tip here.
1665
    // That will be done outside in setBestChain.
1666
    for (let i = connect.length - 1; i >= 1; i--) {
41✔
1667
      const entry = connect[i];
444✔
1668

1669
      try {
444✔
1670
        await this.reconnect(entry);
444✔
1671
      } catch (err) {
1672
        if (err.type === 'VerifyError') {
3!
1673
          if (!err.malleated) {
3!
1674
            while (i--)
3✔
1675
              this.setInvalid(connect[i].hash);
23✔
1676
          }
1677

1678
          if (this.tip.chainwork.lte(tip.chainwork))
3!
1679
            await this.unreorganize(fork, tip);
3✔
1680
        }
1681

1682
        throw err;
3✔
1683
      }
1684
    }
1685

1686
    this.logger.warning(
38✔
1687
      'Chain reorganization: old=%x(%d) new=%x(%d)',
1688
      tip.hash,
1689
      tip.height,
1690
      competitor.hash,
1691
      competitor.height
1692
    );
1693

1694
    await this.emitAsync('reorganize', tip, competitor, fork);
38✔
1695

1696
    return fork;
38✔
1697
  }
1698

1699
  /**
1700
   * Revert a failed reorganization.
1701
   * @private
1702
   * @param {ChainEntry} fork - The common ancestor.
1703
   * @param {ChainEntry} last - The previous valid tip.
1704
   * @returns {Promise}
1705
   */
1706

1707
  async unreorganize(fork, last) {
1708
    const tip = this.tip;
4✔
1709

1710
    // Blocks to disconnect.
1711
    const disconnect = [];
4✔
1712
    let entry = tip;
4✔
1713
    while (!entry.hash.equals(fork.hash)) {
4✔
1714
      disconnect.push(entry);
10✔
1715
      entry = await this.getPrevious(entry);
10✔
1716
      assert(entry);
10✔
1717
    }
1718

1719
    // Blocks to connect.
1720
    const connect = [];
4✔
1721
    entry = last;
4✔
1722
    while (!entry.hash.equals(fork.hash)) {
4✔
1723
      connect.push(entry);
33✔
1724
      entry = await this.getPrevious(entry);
33✔
1725
      assert(entry);
33✔
1726
    }
1727

1728
    // Disconnect blocks/txs.
1729
    for (let i = 0; i < disconnect.length; i++) {
4✔
1730
      const entry = disconnect[i];
10✔
1731
      await this.disconnect(entry);
10✔
1732
    }
1733

1734
    // Connect blocks/txs.
1735
    for (let i = connect.length - 1; i >= 0; i--) {
4✔
1736
      const entry = connect[i];
33✔
1737
      await this.reconnect(entry);
33✔
1738
    }
1739

1740
    this.logger.warning(
4✔
1741
      'Chain un-reorganization: old=%x(%d) new=%x(%d)',
1742
      tip.hash,
1743
      tip.height,
1744
      last.hash,
1745
      last.height
1746
    );
1747

1748
    // Treat as a reorganize event.
1749
    await this.emitAsync('reorganize', tip, last, fork);
4✔
1750
  }
1751

1752
  /**
1753
   * Reorganize the blockchain for SPV. This
1754
   * will reset the chain to the fork block.
1755
   * @private
1756
   * @param {ChainEntry} competitor - The competing chain's tip.
1757
   * @returns {Promise}
1758
   */
1759

1760
  async reorganizeSPV(competitor) {
1761
    const tip = this.tip;
4✔
1762
    const fork = await this.findFork(tip, competitor);
4✔
1763

1764
    assert(fork, 'No free space or data corruption.');
4✔
1765

1766
    // Buffer disconnected blocks.
1767
    const disconnect = [];
4✔
1768
    let entry = tip;
4✔
1769
    while (!entry.hash.equals(fork.hash)) {
4✔
1770
      disconnect.push(entry);
33✔
1771
      entry = await this.getPrevious(entry);
33✔
1772
      assert(entry);
33✔
1773
    }
1774

1775
    // Reset the main chain back
1776
    // to the fork block, causing
1777
    // us to redownload the blocks
1778
    // on the new main chain.
1779
    await this._reset(fork.hash, false);
4✔
1780

1781
    // Emit disconnection events now that
1782
    // the chain has successfully reset.
1783
    for (const entry of disconnect) {
4✔
1784
      const headers = entry.toHeaders();
33✔
1785
      const view = new CoinView();
33✔
1786
      await this.emitAsync('disconnect', entry, headers, view);
33✔
1787
    }
1788

1789
    this.logger.warning(
4✔
1790
      'SPV reorganization: old=%x(%d) new=%x(%d)',
1791
      tip.hash,
1792
      tip.height,
1793
      competitor.hash,
1794
      competitor.height
1795
    );
1796

1797
    this.logger.warning(
4✔
1798
      'Chain replay from height %d necessary.',
1799
      fork.height);
1800

1801
    return this.emitAsync('reorganize', tip, competitor, fork);
4✔
1802
  }
1803

1804
  /**
1805
   * Disconnect an entry from the chain (updates the tip).
1806
   * @param {ChainEntry} entry
1807
   * @returns {Promise}
1808
   */
1809

1810
  async disconnect(entry) {
1811
    let block = await this.getBlock(entry.hash);
527✔
1812

1813
    if (!block) {
527!
1814
      if (!this.options.spv)
×
1815
        throw new Error('Block not found.');
×
1816
      block = entry.toHeaders();
×
1817
    }
1818

1819
    const prev = await this.getPrevious(entry);
527✔
1820
    const view = await this.db.disconnect(entry, block);
527✔
1821

1822
    assert(prev);
527✔
1823

1824
    this.tip = prev;
527✔
1825
    this.height = prev.height;
527✔
1826

1827
    this.emit('tip', prev);
527✔
1828

1829
    return this.emitAsync('disconnect', entry, block, view);
527✔
1830
  }
1831

1832
  /**
1833
   * Reconnect an entry to the chain (updates the tip).
1834
   * This will do contextual-verification on the block
1835
   * (necessary because we cannot validate the inputs
1836
   * in alternate chains when they come in).
1837
   * @param {ChainEntry} entry
1838
   * @returns {Promise}
1839
   */
1840

1841
  async reconnect(entry) {
1842
    const flags = common.flags.VERIFY_NONE;
477✔
1843

1844
    let block = await this.getBlock(entry.hash);
477✔
1845

1846
    if (!block) {
477!
1847
      if (!this.options.spv)
×
1848
        throw new Error('Block not found.');
×
1849
      block = entry.toHeaders();
×
1850
    }
1851

1852
    const prev = await this.getPrevious(entry);
477✔
1853
    assert(prev);
477✔
1854

1855
    let view, state;
1856
    try {
477✔
1857
      [view, state] = await this.verifyContext(block, prev, flags);
477✔
1858
    } catch (err) {
1859
      if (err.type === 'VerifyError') {
3!
1860
        if (!err.malleated)
3!
1861
          this.setInvalid(entry.hash);
3✔
1862
        this.logger.warning(
3✔
1863
          'Tried to reconnect invalid block: %x (%d).',
1864
          entry.hash, entry.height);
1865
      }
1866
      throw err;
3✔
1867
    }
1868

1869
    await this.db.reconnect(entry, block, view);
474✔
1870

1871
    this.tip = entry;
474✔
1872
    this.height = entry.height;
474✔
1873
    this.setDeploymentState(state);
474✔
1874

1875
    this.emit('tip', entry);
474✔
1876
    this.emit('reconnect', entry, block);
474✔
1877

1878
    if ((entry.height % this.network.names.treeInterval) === 0)
474✔
1879
      this.emit('tree commit', this.db.tree.rootHash(), entry, block);
91✔
1880

1881
    return this.emitAsync('connect', entry, block, view);
474✔
1882
  }
1883

1884
  /**
1885
   * Set the best chain. This is called on every incoming
1886
   * block with greater chainwork than the current tip.
1887
   * @private
1888
   * @param {ChainEntry} entry
1889
   * @param {Block} block
1890
   * @param {ChainEntry} prev
1891
   * @param {Number} flags
1892
   * @returns {Promise}
1893
   */
1894

1895
  async setBestChain(entry, block, prev, flags) {
1896
    const tip = this.tip;
20,090✔
1897

1898
    let fork = null;
20,090✔
1899

1900
    // A higher fork has arrived.
1901
    // Time to reorganize the chain.
1902
    if (!entry.prevBlock.equals(this.tip.hash)) {
20,090✔
1903
      try {
45✔
1904
        // Do as much verification
1905
        // as we can before reorganizing.
1906
        await this.verify(block, prev, flags);
45✔
1907
      } catch (err) {
1908
        if (err.type === 'VerifyError') {
×
1909
          if (!err.malleated)
×
1910
            this.setInvalid(entry.hash);
×
1911

1912
          this.logger.warning(
×
1913
            'Tried to connect invalid block: %x (%d).',
1914
            entry.hash, entry.height);
1915
        }
1916

1917
        throw err;
×
1918
      }
1919

1920
      this.logger.warning('WARNING: Reorganizing chain.');
45✔
1921

1922
      // In spv-mode, we reset the
1923
      // chain and redownload the blocks.
1924
      if (this.options.spv)
45✔
1925
        return this.reorganizeSPV(entry);
4✔
1926

1927
      fork = await this.reorganize(entry);
41✔
1928
    }
1929

1930
    // Warn of unknown versionbits.
1931
    if (entry.hasUnknown(this.network)) {
20,083!
1932
      this.logger.warning(
×
1933
        'Unknown version bits in block %d: %s.',
1934
        entry.height, util.hex32(entry.version));
1935
    }
1936

1937
    // Otherwise, everything is in order.
1938
    // Do "contextual" verification on our block
1939
    // now that we're certain its previous
1940
    // block is in the chain.
1941
    let view, state;
1942
    try {
20,083✔
1943
      [view, state] = await this.verifyContext(block, prev, flags);
20,083✔
1944
    } catch (err) {
1945
      if (err.type === 'VerifyError') {
55!
1946
        if (!err.malleated)
55✔
1947
          this.setInvalid(entry.hash);
47✔
1948

1949
        this.logger.warning(
55✔
1950
          'Tried to connect invalid block: %x (%d).',
1951
          entry.hash, entry.height);
1952

1953
        if (fork && this.tip.chainwork.lte(tip.chainwork))
55✔
1954
          await this.unreorganize(fork, tip);
1✔
1955
      }
1956

1957
      throw err;
55✔
1958
    }
1959

1960
    // Save block and connect inputs.
1961
    try {
20,028✔
1962
      await this.db.save(entry, block, view);
20,028✔
1963
    } catch (e) {
1964
      const error = new CriticalError(e.message);
5✔
1965
      this.emit('abort', error);
5✔
1966
      throw error;
5✔
1967
    }
1968

1969
    // Expose the new state.
1970
    this.tip = entry;
20,023✔
1971
    this.height = entry.height;
20,023✔
1972
    this.setDeploymentState(state);
20,023✔
1973

1974
    this.emit('tip', entry);
20,023✔
1975
    this.emit('block', block, entry);
20,023✔
1976

1977
    if ((entry.height % this.network.names.treeInterval) === 0)
20,023✔
1978
      this.emit('tree commit', this.db.tree.rootHash(), entry, block);
3,956✔
1979

1980
    return this.emitAsync('connect', entry, block, view);
20,023✔
1981
  }
1982

1983
  /**
1984
   * Save block on an alternate chain.
1985
   * @private
1986
   * @param {ChainEntry} entry
1987
   * @param {Block} block
1988
   * @param {ChainEntry} prev
1989
   * @param {Number} flags
1990
   * @returns {Promise}
1991
   */
1992

1993
  async saveAlternate(entry, block, prev, flags) {
1994
    // Do not accept forked chain older than the
1995
    // last checkpoint.
1996
    if (this.options.checkpoints) {
409!
1997
      if (prev.height + 1 < this.network.lastCheckpoint)
409✔
1998
        throw new VerifyError(block,
1✔
1999
          'checkpoint',
2000
          'bad-fork-prior-to-checkpoint',
2001
          100);
2002
    }
2003

2004
    try {
408✔
2005
      // Do as much verification
2006
      // as we can before saving.
2007
      await this.verify(block, prev, flags);
408✔
2008
    } catch (err) {
2009
      if (err.type === 'VerifyError') {
×
2010
        if (!err.malleated)
×
2011
          this.setInvalid(entry.hash);
×
2012
        this.logger.warning(
×
2013
          'Invalid block on alternate chain: %x (%d).',
2014
          entry.hash, entry.height);
2015
      }
2016
      throw err;
×
2017
    }
2018

2019
    // Warn of unknown versionbits.
2020
    if (entry.hasUnknown(this.network)) {
408!
2021
      this.logger.warning(
×
2022
        'Unknown version bits in block %d: %s.',
2023
        entry.height, util.hex32(entry.version));
2024
    }
2025

2026
    try {
408✔
2027
      await this.db.save(entry, block);
408✔
2028
    } catch (e) {
2029
      const error = new CriticalError(e.message);
×
2030
      this.emit('abort', error);
×
2031
      throw error;
×
2032
    }
2033

2034
    this.logger.warning('Heads up: Competing chain at height %d:'
408✔
2035
      + ' tip-height=%d competitor-height=%d'
2036
      + ' tip-hash=%x competitor-hash=%x'
2037
      + ' tip-chainwork=%s competitor-chainwork=%s'
2038
      + ' chainwork-diff=%s',
2039
      entry.height,
2040
      this.tip.height,
2041
      entry.height,
2042
      this.tip.hash,
2043
      entry.hash,
2044
      this.tip.chainwork.toString(),
2045
      entry.chainwork.toString(),
2046
      this.tip.chainwork.sub(entry.chainwork).toString());
2047

2048
    // Emit as a "competitor" block.
2049
    this.emit('competitor', block, entry);
408✔
2050
  }
2051

2052
  /**
2053
   * Reset the chain to the desired block. This
2054
   * is useful for replaying the blockchain download
2055
   * for SPV.
2056
   * @param {Hash|Number} block
2057
   * @returns {Promise}
2058
   */
2059

2060
  async reset(block) {
2061
    const unlock = await this.locker.lock();
26✔
2062
    try {
26✔
2063
      return await this._reset(block, false);
26✔
2064
    } finally {
2065
      unlock();
26✔
2066
    }
2067
  }
2068

2069
  /**
2070
   * Reset the chain to the desired block without a lock.
2071
   * @private
2072
   * @param {Hash|Number} block
2073
   * @param {Boolean} silent - don't emit reset.
2074
   * @returns {Promise}
2075
   */
2076

2077
  async _reset(block, silent = false) {
×
2078
    const tip = await this.db.reset(block);
31✔
2079

2080
    // Reset state.
2081
    this.tip = tip;
25✔
2082
    this.height = tip.height;
25✔
2083
    this.synced = false;
25✔
2084

2085
    const state = await this.getDeploymentState();
25✔
2086

2087
    this.setDeploymentState(state);
25✔
2088

2089
    this.emit('tip', tip);
25✔
2090

2091
    if (!silent)
25!
2092
      await this.emitAsync('reset', tip);
25✔
2093

2094
    // Reset the orphan map completely. There may
2095
    // have been some orphans on a forked chain we
2096
    // no longer need.
2097
    this.purgeOrphans();
25✔
2098

2099
    this.maybeSync();
25✔
2100
  }
2101

2102
  /**
2103
   * Reset the chain to a height or hash. Useful for replaying
2104
   * the blockchain download for SPV.
2105
   * @param {Hash|Number} block - hash/height
2106
   * @returns {Promise}
2107
   */
2108

2109
  async replay(block) {
2110
    const unlock = await this.locker.lock();
×
2111
    try {
×
2112
      return await this._replay(block, true);
×
2113
    } finally {
2114
      unlock();
×
2115
    }
2116
  }
2117

2118
  /**
2119
   * Reset the chain without a lock.
2120
   * @private
2121
   * @param {Hash|Number} block - hash/height
2122
   * @param {Boolean} silent
2123
   * @returns {Promise}
2124
   */
2125

2126
  async _replay(block, silent) {
2127
    const entry = await this.getEntry(block);
1✔
2128

2129
    if (!entry)
1!
2130
      throw new Error('Block not found.');
×
2131

2132
    if (!await this.isMainChain(entry))
1!
2133
      throw new Error('Cannot reset on alternate chain.');
×
2134

2135
    if (entry.isGenesis()) {
1!
2136
      await this._reset(entry.hash, silent);
×
2137
      return;
×
2138
    }
2139

2140
    await this._reset(entry.prevBlock, silent);
1✔
2141
  }
2142

2143
  /**
2144
   * Invalidate block.
2145
   * @param {Hash} hash
2146
   * @returns {Promise}
2147
   */
2148

2149
  async invalidate(hash) {
2150
    const unlock = await this.locker.lock();
1✔
2151
    try {
1✔
2152
      return await this._invalidate(hash);
1✔
2153
    } finally {
2154
      unlock();
1✔
2155
    }
2156
  }
2157

2158
  /**
2159
   * Invalidate block (no lock).
2160
   * @param {Hash} hash
2161
   * @returns {Promise}
2162
   */
2163

2164
  async _invalidate(hash) {
2165
    await this._replay(hash, false);
1✔
2166
    this.setInvalid(hash);
1✔
2167
  }
2168

2169
  /**
2170
   * Retroactively prune the database.
2171
   * @returns {Promise<Boolean>}
2172
   */
2173

2174
  async prune() {
2175
    const unlock = await this.locker.lock();
1✔
2176
    try {
1✔
2177
      return await this.db.prune();
1✔
2178
    } finally {
2179
      unlock();
1✔
2180
    }
2181
  }
2182

2183
  /**
2184
   * Compact the Urkel Tree.
2185
   * Removes all historical state and all data not
2186
   * linked directly to the provided root node hash.
2187
   * @returns {Promise}
2188
   */
2189

2190
  async compactTree() {
2191
    if (this.options.spv)
23!
2192
      return;
×
2193

2194
    if (this.height < this.network.block.keepBlocks)
23✔
2195
      throw new Error('Chain is too short to compact tree.');
2✔
2196

2197
    const unlock = await this.locker.lock();
21✔
2198
    this.logger.info('Compacting Urkel Tree...');
21✔
2199

2200
    // To support chain reorgs of limited depth we compact the tree
2201
    // to some commitment point in recent history, then rebuild it from there
2202
    // back up to the current chain tip. In order to support pruning nodes,
2203
    // all blocks above this depth must be available on disk.
2204
    // This actually further reduces the ability for a pruning node to recover
2205
    // from a deep reorg. On mainnet, `keepBlocks` is 288. A normal pruning
2206
    // node can recover from a reorg up to that depth. Compacting the tree
2207
    // potentially reduces that depth to 288 - 36 = 252. A reorg deeper than
2208
    // that will result in a `MissingNodeError` thrown by Urkel inside
2209
    // chain.saveNames() as it tries to restore a deleted state.
2210

2211
    // Oldest block available to a pruning node.
2212
    const oldestBlock = this.height - this.network.block.keepBlocks;
21✔
2213

2214
    const {treeInterval} = this.network.names;
21✔
2215

2216
    // Distance from that block to the start of the oldest tree interval.
2217
    const toNextInterval = (treeInterval - (oldestBlock % treeInterval))
21✔
2218
      % treeInterval;
2219

2220
    // Get the oldest Urkel Tree root state a pruning node can recover from.
2221
    const oldestTreeIntervalStart = oldestBlock + toNextInterval + 1;
21✔
2222
    const entry = await this.db.getEntryByHeight(oldestTreeIntervalStart);
21✔
2223

2224
    try {
21✔
2225
      // TODO: For RPC calls, If compaction fails while compacting
2226
      // and we never hit syncTree, we need to shut down the node
2227
      // so on restart chain can recover.
2228
      // Error can also happen in syncTree, but that means the DB
2229
      // is done for. (because restart would just retry syncTree.)
2230
      // It's fine on open, open throwing would just stop the node.
2231

2232
      // Rewind Urkel Tree and delete all historical state.
2233
      this.emit('tree compact start', entry.treeRoot, entry);
21✔
2234
      await this.db.compactTree(entry);
21✔
2235
      await this.syncTree();
20✔
2236
      this.emit('tree compact end', entry.treeRoot, entry);
20✔
2237
    } catch(e) {
2238
      const error = new CriticalError(e.message);
1✔
2239
      this.emit('abort', error);
1✔
2240
      throw error;
1✔
2241
    } finally {
2242
      unlock();
21✔
2243
    }
2244
  }
2245

2246
  /**
2247
   * Reconstruct the Urkel Tree.
2248
   * @returns {Promise}
2249
   */
2250

2251
  async reconstructTree() {
2252
    if (this.options.spv)
4!
2253
      return;
×
2254

2255
    if (this.options.prune)
4✔
2256
      throw new Error('Cannot reconstruct tree in pruned mode.');
1✔
2257

2258
    const unlock = await this.locker.lock();
3✔
2259

2260
    const treeState = await this.db.getTreeState();
3✔
2261

2262
    if (treeState.compactionHeight === 0)
3!
2263
      throw new Error('Nothing to reconstruct.');
×
2264

2265
    // Compact all the way to the first block and
2266
    // let the syncTree do its job.
2267
    const entry = await this.db.getEntryByHeight(1);
3✔
2268

2269
    try {
3✔
2270
      this.emit('tree reconstruct start');
3✔
2271
      await this.db.compactTree(entry);
3✔
2272
      await this.syncTree();
3✔
2273
      this.emit('tree reconstruct end');
3✔
2274
    } finally {
2275
      unlock();
3✔
2276
    }
2277
  }
2278

2279
  /**
2280
   * Scan the blockchain for transactions containing specified address hashes.
2281
   * @param {Hash|Number} start - Block hash or height to start at.
2282
   * @param {BloomFilter} filter - Bloomfilter containing tx and address hashes.
2283
   * @param {Function} iter - Iterator.
2284
   * @returns {Promise<void>}
2285
   */
2286

2287
  async scan(start, filter, iter) {
2288
    const unlock = await this.locker.lock();
4✔
2289
    try {
4✔
2290
      return await this.db.scan(start, filter, iter);
4✔
2291
    } finally {
2292
      unlock();
4✔
2293
    }
2294
  }
2295

2296
  /** @typedef {import('./common').ScanAction} ScanAction */
2297

2298
  /**
2299
   * @callback ScanInteractiveIterCB
2300
   * @param {ChainEntry} entry
2301
   * @param {TX[]} txs
2302
   * @returns {Promise<ScanAction>}
2303
   */
2304

2305
  /**
2306
   * Interactive scan the blockchain for transactions containing specified
2307
   * address hashes. Allows repeat and abort.
2308
   * @param {Hash|Number} start - Block hash or height to start at.
2309
   * @param {BloomFilter} filter - Starting bloom filter containing tx,
2310
   * address and name hashes.
2311
   * @param {ScanInteractiveIterCB} iter - Iterator.
2312
   * @param {Boolean} [fullLock=false]
2313
   * @returns {Promise<void>}
2314
   */
2315

2316
  async scanInteractive(start, filter, iter, fullLock = false) {
×
2317
    if (fullLock) {
401✔
2318
      const unlock = await this.locker.lock();
343✔
2319
      try {
343✔
2320
        // We lock the whole chain, no longer lock per block scan.
2321
        return await this._scanInteractive(start, filter, iter, false);
343✔
2322
      } catch (e) {
2323
        this.logger.debug('Scan(interactive) errored. Error: %s', e.message);
3✔
2324
        throw e;
3✔
2325
      } finally {
2326
        unlock();
343✔
2327
      }
2328
    }
2329

2330
    return this._scanInteractive(start, filter, iter, true);
58✔
2331
  }
2332

2333
  /**
2334
   * Interactive scan the blockchain for transactions containing specified
2335
   * address hashes. Allows repeat and abort.
2336
   * @param {Hash|Number} start - Block hash or height to start at.
2337
   * @param {BloomFilter} filter - Starting bloom filter containing tx,
2338
   * address and name hashes.
2339
   * @param {ScanInteractiveIterCB} iter - Iterator.
2340
   * @param {Boolean} [lockPerScan=true] - if we should lock per block scan.
2341
   * @returns {Promise<void>}
2342
   */
2343

2344
  async _scanInteractive(start, filter, iter, lockPerScan = true) {
×
2345
    if (start == null)
401!
2346
      start = this.network.genesis.hash;
×
2347

2348
    if (typeof start === 'number')
401✔
2349
      this.logger.info('Scanning(interactive) from height %d.', start);
62✔
2350
    else
2351
      this.logger.info('Scanning(interactive) from block %x.', start);
339✔
2352

2353
    let hash = start;
401✔
2354

2355
    while (hash != null) {
401✔
2356
      let unlock;
2357

2358
      if (lockPerScan)
2,722✔
2359
        unlock = await this.locker.lock();
361✔
2360

2361
      try {
2,722✔
2362
        const {entry, txs} = await this.db.scanBlock(hash, filter);
2,722✔
2363

2364
        const action = await iter(entry, txs);
2,720✔
2365

2366
        if (!action || typeof action !== 'object')
2,718!
2367
          throw new Error('Did not get proper action');
×
2368

2369
        switch (action.type) {
2,718!
2370
          case scanActions.REPEAT: {
2371
            break;
150✔
2372
          }
2373
          case scanActions.REPEAT_SET: {
2374
            // try again with updated filter.
2375
            filter = action.filter;
54✔
2376
            break;
54✔
2377
          }
2378
          case scanActions.REPEAT_ADD: {
2379
            if (!filter)
9!
2380
              throw new Error('No filter set.');
×
2381

2382
            for (const chunk of action.chunks)
9✔
2383
              filter.add(chunk);
9✔
2384
            break;
9✔
2385
          }
2386
          case scanActions.NEXT: {
2387
            const next = await this.getNext(entry);
2,464✔
2388
            hash = next && next.hash;
2,464✔
2389
            break;
2,464✔
2390
          }
2391
          case scanActions.ABORT: {
2392
            this.logger.info('Scan(interactive) aborted at %x (%d).',
41✔
2393
              entry.hash, entry.height);
2394
            throw new Error('scan request aborted.');
41✔
2395
          }
2396
          default:
2397
            this.logger.debug('Scan(interactive) aborting. Unknown action: %d',
×
2398
              action.type);
2399
            throw new Error('Unknown action.');
×
2400
        }
2401
      } catch (e) {
2402
        this.logger.debug('Scan(interactive) errored. Error: %s', e.message);
45✔
2403
        throw e;
45✔
2404
      } finally {
2405
        if (lockPerScan)
2,722✔
2406
          unlock();
361✔
2407
      }
2408
    }
2409
  }
2410

2411
  /**
2412
   * Add a block to the chain, perform all necessary verification.
2413
   * @param {Block} block
2414
   * @param {Number?} flags
2415
   * @param {Number?} id
2416
   * @returns {Promise<ChainEntry?>}
2417
   */
2418

2419
  async add(block, flags, id) {
2420
    const hash = block.hash();
20,500✔
2421
    const unlock = await this.locker.lock(hash);
20,500✔
2422
    try {
20,500✔
2423
      return await this._add(block, flags, id);
20,500✔
2424
    } finally {
2425
      unlock();
20,500✔
2426
    }
2427
  }
2428

2429
  /**
2430
   * Add a block to the chain without a lock.
2431
   * @private
2432
   * @param {Block} block
2433
   * @param {Number?} flags
2434
   * @param {Number?} id
2435
   * @returns {Promise<ChainEntry?>}
2436
   */
2437

2438
  async _add(block, flags, id) {
2439
    const hash = block.hash();
20,501✔
2440

2441
    if (flags == null)
20,501✔
2442
      flags = common.DEFAULT_FLAGS;
19,294✔
2443

2444
    if (id == null)
20,501✔
2445
      id = -1;
19,621✔
2446

2447
    // Special case for genesis block.
2448
    if (hash.equals(this.network.genesis.hash)) {
20,501!
2449
      this.logger.debug('Saw genesis block: %x.', block.hash());
×
2450
      throw new VerifyError(block, 'duplicate', 'duplicate', 0);
×
2451
    }
2452

2453
    // Do we already have this block in the queue?
2454
    if (this.hasPending(hash)) {
20,501!
2455
      this.logger.debug('Already have pending block: %x.', block.hash());
×
2456
      throw new VerifyError(block, 'duplicate', 'duplicate', 0);
×
2457
    }
2458

2459
    // If the block is already known to be
2460
    // an orphan, ignore it.
2461
    if (this.hasOrphan(hash)) {
20,501!
2462
      this.logger.debug('Already have orphan block: %x.', block.hash());
×
2463
      throw new VerifyError(block, 'duplicate', 'duplicate', 0);
×
2464
    }
2465

2466
    // Do not revalidate known invalid blocks.
2467
    if (this.hasInvalid(block)) {
20,501!
2468
      this.logger.debug('Invalid ancestors for block: %x.', block.hash());
×
2469
      throw new VerifyError(block, 'duplicate', 'duplicate', 100);
×
2470
    }
2471

2472
    // Check the POW before doing anything.
2473
    if (flags & common.flags.VERIFY_POW) {
20,501✔
2474
      if (!block.verifyPOW())
19,385!
2475
        throw new VerifyError(block, 'invalid', 'high-hash', 50);
×
2476
    }
2477

2478
    // Do we already have this block?
2479
    if (await this.hasEntry(hash)) {
20,501!
2480
      this.logger.debug('Already have block: %x.', block.hash());
×
2481
      throw new VerifyError(block, 'duplicate', 'duplicate', 0);
×
2482
    }
2483

2484
    // Find the previous block entry.
2485
    const prev = await this.getEntry(block.prevBlock);
20,501✔
2486

2487
    // If previous block wasn't ever seen,
2488
    // add it current to orphans and return.
2489
    if (!prev) {
20,501✔
2490
      this.storeOrphan(block, flags, id);
28✔
2491
      return null;
28✔
2492
    }
2493

2494
    // Connect the block.
2495
    const entry = await this.connect(prev, block, flags);
20,473✔
2496

2497
    // Handle any orphans.
2498
    if (this.hasNextOrphan(hash))
20,407✔
2499
      await this.handleOrphans(entry);
10✔
2500

2501
    return entry;
20,407✔
2502
  }
2503

2504
  /**
2505
   * Connect block to chain.
2506
   * @private
2507
   * @param {ChainEntry} prev
2508
   * @param {Block} block
2509
   * @param {Number} flags
2510
   * @returns {Promise<ChainEntry?>}
2511
   */
2512

2513
  async connect(prev, block, flags) {
2514
    const start = util.bench();
20,501✔
2515

2516
    // Sanity check.
2517
    assert(block.prevBlock.equals(prev.hash));
20,501✔
2518

2519
    // Explanation: we try to keep as much data
2520
    // off the javascript heap as possible. Blocks
2521
    // in the future may be 8mb or 20mb, who knows.
2522
    // In fullnode-mode we store the blocks in
2523
    // "compact" form (the headers plus the raw
2524
    // Buffer object) until they're ready to be
2525
    // fully validated here. They are deserialized,
2526
    // validated, and connected. Hopefully the
2527
    // deserialized blocks get cleaned up by the
2528
    // GC quickly.
2529
    if (block.isMemory()) {
20,501✔
2530
      try {
91✔
2531
        block = block.toBlock();
91✔
2532
      } catch (e) {
2533
        this.logger.error(e);
×
2534
        throw new VerifyError(block,
×
2535
          'malformed',
2536
          'error parsing message',
2537
          10,
2538
          true);
2539
      }
2540
    }
2541

2542
    // Transactions are not allowed in any block before
2543
    // a certain amount of chainwork has been accumulated.
2544
    // This is a non-malleated, permanently invalid block
2545
    // and whoever sent it should be banned.
2546
    if (prev.height + 1 < this.network.txStart) {
20,501✔
2547
      let invalid = false;
6✔
2548

2549
      // No transactions allowed besides coinbase
2550
      if (block.txs.length > 1)
6✔
2551
        invalid = true;
1✔
2552

2553
      if (!this.options.spv) {
6!
2554
        // No claims or airdrops allowed in coinbase yet.
2555
        const cb = block.txs[0];
6✔
2556
        if (cb.outputs.length > 1)
6✔
2557
          invalid = true;
1✔
2558

2559
        // Sanity check
2560
        if (!cb.outputs[0].covenant.isNone())
6!
2561
          invalid = true;
×
2562
      }
2563

2564
      if (invalid) {
6✔
2565
        this.setInvalid(block.hash());
2✔
2566
        throw new VerifyError(block,
2✔
2567
          'invalid',
2568
          'no-tx-allowed-yet',
2569
          100,
2570
          false);
2571
      }
2572
    }
2573

2574
    // Create a new chain entry.
2575
    const entry = ChainEntry.fromBlock(block, prev);
20,499✔
2576

2577
    // The block is on a alternate chain if the
2578
    // chainwork is less than or equal to
2579
    // our tip's. Add the block but do _not_
2580
    // connect the inputs.
2581
    if (entry.chainwork.lte(this.tip.chainwork)) {
20,499✔
2582
      // Save block to an alternate chain.
2583
      await this.saveAlternate(entry, block, prev, flags);
409✔
2584
    } else {
2585
      // Attempt to add block to the chain index.
2586
      await this.setBestChain(entry, block, prev, flags);
20,090✔
2587
    }
2588

2589
    // Keep track of stats.
2590
    this.logStatus(start, block, entry);
20,435✔
2591

2592
    // Check sync state.
2593
    this.maybeSync();
20,435✔
2594

2595
    return entry;
20,435✔
2596
  }
2597

2598
  /**
2599
   * Handle orphans.
2600
   * @private
2601
   * @param {ChainEntry} entry
2602
   * @returns {Promise}
2603
   */
2604

2605
  async handleOrphans(entry) {
2606
    let orphan = this.resolveOrphan(entry.hash);
10✔
2607

2608
    while (orphan) {
10✔
2609
      const {block, flags, id} = orphan;
28✔
2610

2611
      try {
28✔
2612
        entry = await this.connect(entry, block, flags);
28✔
2613
      } catch (err) {
2614
        if (err.type === 'VerifyError') {
×
2615
          this.logger.warning(
×
2616
            'Could not resolve orphan block %x: %s.',
2617
            block.hash(), err.message);
2618

2619
          this.emit('bad orphan', err, id);
×
2620

2621
          break;
×
2622
        }
2623
        throw err;
×
2624
      }
2625

2626
      this.logger.debug(
28✔
2627
        'Orphan block was resolved: %x (%d).',
2628
        block.hash(), entry.height);
2629

2630
      this.emit('resolved', block, entry);
28✔
2631

2632
      orphan = this.resolveOrphan(entry.hash);
28✔
2633
    }
2634
  }
2635

2636
  /**
2637
   * Test whether the chain has reached its slow height.
2638
   * @private
2639
   * @returns {Boolean}
2640
   */
2641

2642
  isSlow() {
2643
    if (this.options.spv)
20,435✔
2644
      return false;
644✔
2645

2646
    if (this.synced)
19,791!
2647
      return true;
19,791✔
2648

2649
    if (this.height === 1 || this.height % 20 === 0)
×
2650
      return true;
×
2651

2652
    if (this.height >= this.network.block.slowHeight)
×
2653
      return true;
×
2654

2655
    return false;
×
2656
  }
2657

2658
  /**
2659
   * Calculate the time difference from
2660
   * start time and log block.
2661
   * @private
2662
   * @param {Array} start
2663
   * @param {Block} block
2664
   * @param {ChainEntry} entry
2665
   */
2666

2667
  logStatus(start, block, entry) {
2668
    if (!this.isSlow())
20,435✔
2669
      return;
644✔
2670

2671
    // Report memory for debugging.
2672
    this.logger.memory();
19,791✔
2673

2674
    const elapsed = util.bench(start);
19,791✔
2675

2676
    this.logger.info(
19,791✔
2677
      'Block %x (%d) added to chain (size=%d txs=%d time=%d).',
2678
      entry.hash,
2679
      entry.height,
2680
      block.getSize(),
2681
      block.txs.length,
2682
      elapsed);
2683
  }
2684

2685
  /**
2686
   * Verify a block hash and height against the checkpoints.
2687
   * @private
2688
   * @param {ChainEntry} prev
2689
   * @param {Hash} hash
2690
   * @returns {Boolean}
2691
   */
2692

2693
  verifyCheckpoint(prev, hash) {
2694
    if (!this.options.checkpoints)
21,014✔
2695
      return true;
291✔
2696

2697
    const height = prev.height + 1;
20,723✔
2698
    const checkpoint = this.network.checkpointMap[height];
20,723✔
2699

2700
    if (!checkpoint)
20,723✔
2701
      return true;
20,721✔
2702

2703
    if (hash.equals(checkpoint)) {
2!
2704
      this.logger.debug('Hit checkpoint block %x (%d).', hash, height);
2✔
2705
      this.emit('checkpoint', hash, height);
2✔
2706
      return true;
2✔
2707
    }
2708

2709
    // Someone is either mining on top of
2710
    // an old block for no reason, or the
2711
    // consensus protocol is broken and
2712
    // there was a 20k+ block reorg.
2713
    this.logger.warning(
×
2714
      'Checkpoint mismatch at height %d: expected=%x received=%x',
2715
      height,
2716
      checkpoint,
2717
      hash
2718
    );
2719

2720
    this.purgeOrphans();
×
2721

2722
    return false;
×
2723
  }
2724

2725
  /**
2726
   * Store an orphan.
2727
   * @private
2728
   * @param {Block} block
2729
   * @param {Number?} flags
2730
   * @param {Number?} id
2731
   */
2732

2733
  storeOrphan(block, flags, id) {
2734
    const height = block.getCoinbaseHeight();
28✔
2735
    const orphan = this.orphanPrev.get(block.prevBlock);
28✔
2736

2737
    // The orphan chain forked.
2738
    if (orphan) {
28!
2739
      assert(!orphan.block.hash().equals(block.hash()));
×
2740
      assert(orphan.block.prevBlock.equals(block.prevBlock));
×
2741

2742
      this.logger.warning(
×
2743
        'Removing forked orphan block: %x (%d).',
2744
        orphan.block.hash(), height);
2745

2746
      this.removeOrphan(orphan);
×
2747
    }
2748

2749
    this.limitOrphans();
28✔
2750
    this.addOrphan(new Orphan(block, flags, id));
28✔
2751

2752
    this.logger.debug(
28✔
2753
      'Storing orphan block: %x (%d).',
2754
      block.hash(), height);
2755

2756
    this.emit('orphan', block);
28✔
2757
  }
2758

2759
  /**
2760
   * Add an orphan.
2761
   * @private
2762
   * @param {Orphan} orphan
2763
   * @returns {Orphan}
2764
   */
2765

2766
  addOrphan(orphan) {
2767
    const block = orphan.block;
28✔
2768
    const hash = block.hash();
28✔
2769

2770
    assert(!this.orphanMap.has(hash));
28✔
2771
    assert(!this.orphanPrev.has(block.prevBlock));
28✔
2772
    assert(this.orphanMap.size >= 0);
28✔
2773

2774
    this.orphanMap.set(hash, orphan);
28✔
2775
    this.orphanPrev.set(block.prevBlock, orphan);
28✔
2776

2777
    return orphan;
28✔
2778
  }
2779

2780
  /**
2781
   * Remove an orphan.
2782
   * @private
2783
   * @param {Orphan} orphan
2784
   * @returns {Orphan}
2785
   */
2786

2787
  removeOrphan(orphan) {
2788
    const block = orphan.block;
28✔
2789
    const hash = block.hash();
28✔
2790

2791
    assert(this.orphanMap.has(hash));
28✔
2792
    assert(this.orphanPrev.has(block.prevBlock));
28✔
2793
    assert(this.orphanMap.size > 0);
28✔
2794

2795
    this.orphanMap.delete(hash);
28✔
2796
    this.orphanPrev.delete(block.prevBlock);
28✔
2797

2798
    return orphan;
28✔
2799
  }
2800

2801
  /**
2802
   * Test whether a hash would resolve the next orphan.
2803
   * @private
2804
   * @param {Hash} hash - Previous block hash.
2805
   * @returns {Boolean}
2806
   */
2807

2808
  hasNextOrphan(hash) {
2809
    return this.orphanPrev.has(hash);
20,407✔
2810
  }
2811

2812
  /**
2813
   * Resolve an orphan.
2814
   * @private
2815
   * @param {Hash} hash - Previous block hash.
2816
   * @returns {Orphan}
2817
   */
2818

2819
  resolveOrphan(hash) {
2820
    const orphan = this.orphanPrev.get(hash);
38✔
2821

2822
    if (!orphan)
38✔
2823
      return null;
10✔
2824

2825
    return this.removeOrphan(orphan);
28✔
2826
  }
2827

2828
  /**
2829
   * Purge any waiting orphans.
2830
   */
2831

2832
  purgeOrphans() {
2833
    const count = this.orphanMap.size;
25✔
2834

2835
    if (count === 0)
25!
2836
      return;
25✔
2837

2838
    this.orphanMap.clear();
×
2839
    this.orphanPrev.clear();
×
2840

2841
    this.logger.debug('Purged %d orphans.', count);
×
2842
  }
2843

2844
  /**
2845
   * Prune orphans, only keep the orphan with the highest
2846
   * coinbase height (likely to be the peer's tip).
2847
   */
2848

2849
  limitOrphans() {
2850
    const now = util.now();
28✔
2851

2852
    let oldest = null;
28✔
2853
    for (const orphan of this.orphanMap.values()) {
28✔
2854
      if (now < orphan.time + 60 * 60) {
60!
2855
        if (!oldest || orphan.time < oldest.time)
60✔
2856
          oldest = orphan;
18✔
2857
        continue;
60✔
2858
      }
2859

2860
      this.removeOrphan(orphan);
×
2861
    }
2862

2863
    if (this.orphanMap.size < this.options.maxOrphans)
28!
2864
      return;
28✔
2865

2866
    if (!oldest)
×
2867
      return;
×
2868

2869
    this.removeOrphan(oldest);
×
2870
  }
2871

2872
  /**
2873
   * Test whether an invalid block hash has been seen.
2874
   * @private
2875
   * @param {Block} block
2876
   * @returns {Boolean}
2877
   */
2878

2879
  hasInvalid(block) {
2880
    const hash = block.hash();
20,504✔
2881

2882
    if (this.invalid.has(hash))
20,504✔
2883
      return true;
3✔
2884

2885
    if (this.invalid.has(block.prevBlock)) {
20,501!
2886
      this.setInvalid(hash);
×
2887
      return true;
×
2888
    }
2889

2890
    return false;
20,501✔
2891
  }
2892

2893
  /**
2894
   * Mark a block as invalid.
2895
   * @private
2896
   * @param {Hash} hash
2897
   */
2898

2899
  setInvalid(hash) {
2900
    this.invalid.set(hash, true);
76✔
2901
  }
2902

2903
  /**
2904
   * Forget an invalid block hash.
2905
   * @private
2906
   * @param {Hash} hash
2907
   */
2908

2909
  removeInvalid(hash) {
2910
    this.invalid.remove(hash);
1✔
2911
  }
2912

2913
  /**
2914
   * Test the chain to see if it contains
2915
   * a block, or has recently seen a block.
2916
   * @param {Hash} hash
2917
   * @returns {Promise} - Returns Boolean.
2918
   */
2919

2920
  async has(hash) {
2921
    if (this.hasOrphan(hash))
927!
2922
      return true;
×
2923

2924
    if (this.locker.has(hash))
927!
2925
      return true;
×
2926

2927
    if (this.invalid.has(hash))
927!
2928
      return true;
×
2929

2930
    return this.hasEntry(hash);
927✔
2931
  }
2932

2933
  /**
2934
   * Find the corresponding block entry by hash or height.
2935
   * @param {Hash|Number} hash
2936
   * @returns {Promise<ChainEntry?>}
2937
   */
2938

2939
  getEntry(hash) {
2940
    return this.db.getEntry(hash);
22,622✔
2941
  }
2942

2943
  /**
2944
   * Retrieve a chain entry by height.
2945
   * @param {Number} height
2946
   * @returns {Promise<ChainEntry?>}
2947
   */
2948

2949
  getEntryByHeight(height) {
2950
    return this.db.getEntryByHeight(height);
101✔
2951
  }
2952

2953
  /**
2954
   * Retrieve a chain entry by hash.
2955
   * @param {Hash} hash
2956
   * @returns {Promise<ChainEntry?>}
2957
   */
2958

2959
  getEntryByHash(hash) {
2960
    return this.db.getEntryByHash(hash);
×
2961
  }
2962

2963
  /**
2964
   * Get the hash of a block by height. Note that this
2965
   * will only return hashes in the main chain.
2966
   * @param {Number} height
2967
   * @returns {Promise<Hash>}
2968
   */
2969

2970
  getHash(height) {
2971
    return this.db.getHash(height);
1,849✔
2972
  }
2973

2974
  /**
2975
   * Get the height of a block by hash.
2976
   * @param {Hash} hash
2977
   * @returns {Promise<Number>}
2978
   */
2979

2980
  getHeight(hash) {
2981
    return this.db.getHeight(hash);
481✔
2982
  }
2983

2984
  /**
2985
   * Test the chain to see if it contains a block.
2986
   * @param {Hash} hash
2987
   * @returns {Promise<Boolean>}
2988
   */
2989

2990
  hasEntry(hash) {
2991
    return this.db.hasEntry(hash);
21,428✔
2992
  }
2993

2994
  /**
2995
   * Get the _next_ block hash (does not work by height).
2996
   * @param {Hash} hash
2997
   * @returns {Promise<Hash>}
2998
   */
2999

3000
  getNextHash(hash) {
3001
    return this.db.getNextHash(hash);
571✔
3002
  }
3003

3004
  /**
3005
   * Check whether coins are still unspent.
3006
   * @param {TX} tx
3007
   * @returns {Promise<Boolean>}
3008
   */
3009

3010
  hasCoins(tx) {
3011
    return this.db.hasCoins(tx);
2,278✔
3012
  }
3013

3014
  /**
3015
   * Get all tip hashes.
3016
   * @returns {Promise<Hash[]>}
3017
   */
3018

3019
  getTips() {
3020
    return this.db.getTips();
×
3021
  }
3022

3023
  /**
3024
   * Get range of hashes.
3025
   * @param {Number} [start=-1]
3026
   * @param {Number} [end=-1]
3027
   * @returns {Promise<Hash[]>}
3028
   */
3029

3030
  getHashes(start = -1, end = -1) {
×
3031
    return this.db.getHashes(start, end);
1✔
3032
  }
3033

3034
  /**
3035
   * Get range of entries.
3036
   * @param {Number} [start=-1]
3037
   * @param {Number} [end=-1]
3038
   * @returns {Promise<ChainEntry[]>}
3039
   */
3040

3041
  getEntries(start = -1, end = -1) {
×
3042
    return this.db.getEntries(start, end);
88✔
3043
  }
3044

3045
  /**
3046
   * Get a coin (unspents only).
3047
   * @param {Outpoint} prevout
3048
   * @returns {Promise<CoinEntry?>}
3049
   */
3050

3051
  readCoin(prevout) {
3052
    return this.db.readCoin(prevout);
3,885✔
3053
  }
3054

3055
  /**
3056
   * Get a coin (unspents only).
3057
   * @param {Hash} hash
3058
   * @param {Number} index
3059
   * @returns {Promise<Coin?>}
3060
   */
3061

3062
  getCoin(hash, index) {
3063
    return this.db.getCoin(hash, index);
25✔
3064
  }
3065

3066
  /**
3067
   * Retrieve a block from the database (not filled with coins).
3068
   * @param {Hash} hash
3069
   * @returns {Promise<Block?>}
3070
   */
3071

3072
  getBlock(hash) {
3073
    return this.db.getBlock(hash);
9,527✔
3074
  }
3075

3076
  /**
3077
   * Retrieve a block from the database (not filled with coins).
3078
   * @param {Hash|Number} hashHeight
3079
   * @returns {Promise<Buffer|null>}
3080
   */
3081

3082
  getRawBlock(hashHeight) {
3083
    return this.db.getRawBlock(hashHeight);
93✔
3084
  }
3085

3086
  /**
3087
   * Get a historical block coin viewpoint.
3088
   * @param {Block} block
3089
   * @returns {Promise<CoinView>}
3090
   */
3091

3092
  getBlockView(block) {
3093
    return this.db.getBlockView(block);
1✔
3094
  }
3095

3096
  /**
3097
   * Get a transaction with metadata.
3098
   * @param {Hash} hash
3099
   * @returns {Promise<TXMeta>}
3100
   */
3101

3102
  getMeta(hash) {
3103
    return this.db.getMeta(hash);
2✔
3104
  }
3105

3106
  /**
3107
   * Retrieve a transaction.
3108
   * @param {Hash} hash
3109
   * @returns {Promise<TX>}
3110
   */
3111

3112
  getTX(hash) {
3113
    return this.db.getTX(hash);
×
3114
  }
3115

3116
  /**
3117
   * @param {Hash} hash
3118
   * @returns {Promise<Boolean>}
3119
   */
3120

3121
  hasTX(hash) {
3122
    return this.db.hasTX(hash);
×
3123
  }
3124

3125
  /**
3126
   * Get all coins pertinent to an address.
3127
   * @param {Address[]} addrs
3128
   * @returns {Promise<Coin[]>}
3129
   */
3130

3131
  getCoinsByAddress(addrs) {
3132
    return this.db.getCoinsByAddress(addrs);
1✔
3133
  }
3134

3135
  /**
3136
   * Get all transaction hashes to an address.
3137
   * @param {Address[]} addrs
3138
   * @returns {Promise<Hash[]>}
3139
   */
3140

3141
  getHashesByAddress(addrs) {
3142
    return this.db.getHashesByAddress(addrs);
×
3143
  }
3144

3145
  /**
3146
   * Get all transactions pertinent to an address.
3147
   * @param {Address[]} addrs
3148
   * @returns {Promise<TX[]>}
3149
   */
3150

3151
  getTXByAddress(addrs) {
3152
    return this.db.getTXByAddress(addrs);
×
3153
  }
3154

3155
  /**
3156
   * Get all transactions pertinent to an address.
3157
   * @param {Address[]} addrs
3158
   * @returns {Promise<TXMeta[]>}
3159
   */
3160

3161
  getMetaByAddress(addrs) {
3162
    return this.db.getMetaByAddress(addrs);
×
3163
  }
3164

3165
  /**
3166
   * Get an orphan block.
3167
   * @param {Hash} hash
3168
   * @returns {Block?}
3169
   */
3170

3171
  getOrphan(hash) {
3172
    return this.orphanMap.get(hash) || null;
×
3173
  }
3174

3175
  /**
3176
   * Test the chain to see if it contains an orphan.
3177
   * @param {Hash} hash
3178
   * @returns {Boolean}
3179
   */
3180

3181
  hasOrphan(hash) {
3182
    return this.orphanMap.has(hash);
22,356✔
3183
  }
3184

3185
  /**
3186
   * Test the chain to see if it contains a pending block in its queue.
3187
   * @param {Hash} hash
3188
   * @returns {Boolean} - Returns Boolean.
3189
   */
3190

3191
  hasPending(hash) {
3192
    return this.locker.pending(hash);
20,501✔
3193
  }
3194

3195
  /**
3196
   * Get coin viewpoint.
3197
   * @param {TX} tx
3198
   * @returns {Promise<CoinView>}
3199
   */
3200

3201
  getCoinView(tx) {
3202
    return this.db.getCoinView(tx);
×
3203
  }
3204

3205
  /**
3206
   * Get coin viewpoint (spent).
3207
   * @param {TX} tx
3208
   * @returns {Promise<CoinView>}
3209
   */
3210

3211
  async getSpentView(tx) {
3212
    const unlock = await this.locker.lock();
×
3213
    try {
×
3214
      return await this.db.getSpentView(tx);
×
3215
    } finally {
3216
      unlock();
×
3217
    }
3218
  }
3219

3220
  /**
3221
   * Test the chain to see if it is synced.
3222
   * @returns {Boolean}
3223
   */
3224

3225
  isFull() {
3226
    return this.synced;
×
3227
  }
3228

3229
  /**
3230
   * Potentially emit a `full` event.
3231
   * @private
3232
   */
3233

3234
  maybeSync() {
3235
    if (this.synced)
20,753✔
3236
      return;
20,459✔
3237

3238
    if (this.options.checkpoints) {
294✔
3239
      if (this.height < this.network.lastCheckpoint)
293✔
3240
        return;
11✔
3241
    }
3242

3243
    if (!this.hasChainwork())
283!
3244
      return;
×
3245

3246
    if (this.tip.time < this.network.now() - this.network.block.maxTipAge)
283!
3247
      return;
×
3248

3249
    this.synced = true;
283✔
3250
    this.emit('full');
283✔
3251
  }
3252

3253
  /**
3254
   * Test the chain to see if it has the
3255
   * minimum required chainwork for the
3256
   * network.
3257
   * @returns {Boolean}
3258
   */
3259

3260
  hasChainwork() {
3261
    return this.tip.chainwork.gte(this.network.pow.chainwork);
283✔
3262
  }
3263

3264
  /**
3265
   * Get the fill percentage.
3266
   * @returns {Number} percent - Ranges from 0.0 to 1.0.
3267
   */
3268

3269
  getProgress() {
3270
    const start = this.network.genesis.time;
170✔
3271
    const current = this.tip.time - start;
170✔
3272
    const end = this.network.now() - start - 40 * 60;
170✔
3273
    return Math.min(1, current / end);
170✔
3274
  }
3275

3276
  /**
3277
   * Calculate chain locator (an array of hashes).
3278
   * @param {Hash?} start - Height or hash to treat as the tip.
3279
   * The current tip will be used if not present. Note that this can be a
3280
   * non-existent hash, which is useful for headers-first locators.
3281
   * @returns {Promise<Hash[]>}
3282
   */
3283

3284
  async getLocator(start) {
3285
    const unlock = await this.locker.lock();
205✔
3286
    try {
205✔
3287
      return await this._getLocator(start);
205✔
3288
    } finally {
3289
      unlock();
205✔
3290
    }
3291
  }
3292

3293
  /**
3294
   * Calculate chain locator without a lock.
3295
   * @private
3296
   * @param {Hash?} start
3297
   * @returns {Promise<Hash[]>}
3298
   */
3299

3300
  async _getLocator(start) {
3301
    if (start == null)
205✔
3302
      start = this.tip.hash;
202✔
3303

3304
    assert(Buffer.isBuffer(start));
205✔
3305

3306
    let entry = await this.getEntry(start);
205✔
3307

3308
    const hashes = [];
205✔
3309

3310
    if (!entry) {
205!
3311
      entry = this.tip;
×
3312
      hashes.push(start);
×
3313
    }
3314

3315
    let main = await this.isMainChain(entry);
205✔
3316
    let hash = entry.hash;
205✔
3317
    let height = entry.height;
205✔
3318
    let step = 1;
205✔
3319

3320
    hashes.push(hash);
205✔
3321

3322
    while (height > 0) {
205✔
3323
      height -= step;
1,264✔
3324

3325
      if (height < 0)
1,264✔
3326
        height = 0;
58✔
3327

3328
      if (hashes.length > 10)
1,264✔
3329
        step *= 2;
389✔
3330

3331
      if (main) {
1,264!
3332
        // If we're on the main chain, we can
3333
        // do a fast lookup of the hash.
3334
        hash = await this.getHash(height);
1,264✔
3335
        assert(hash);
1,260✔
3336
      } else {
3337
        const ancestor = await this.getAncestor(entry, height);
×
3338
        assert(ancestor);
×
3339
        main = await this.isMainChain(ancestor);
×
3340
        hash = ancestor.hash;
×
3341
      }
3342

3343
      hashes.push(hash);
1,260✔
3344
    }
3345

3346
    return hashes;
201✔
3347
  }
3348

3349
  /**
3350
   * Calculate the orphan root of the hash (if it is an orphan).
3351
   * @param {Hash} hash
3352
   * @returns {Hash}
3353
   */
3354

3355
  getOrphanRoot(hash) {
3356
    let root = null;
19✔
3357

3358
    assert(hash);
19✔
3359

3360
    for (;;) {
19✔
3361
      const orphan = this.orphanMap.get(hash);
53✔
3362

3363
      if (!orphan)
53✔
3364
        break;
19✔
3365

3366
      root = hash;
34✔
3367
      hash = orphan.block.prevBlock;
34✔
3368
    }
3369

3370
    return root;
19✔
3371
  }
3372

3373
  /**
3374
   * Calculate the time difference (in seconds)
3375
   * between two blocks by examining chainworks.
3376
   * @param {ChainEntry} to
3377
   * @param {ChainEntry} from
3378
   * @returns {Number}
3379
   */
3380

3381
  getProofTime(to, from) {
3382
    const pow = this.network.pow;
×
3383
    let sign, work;
3384

3385
    if (to.chainwork.gt(from.chainwork)) {
×
3386
      work = to.chainwork.sub(from.chainwork);
×
3387
      sign = 1;
×
3388
    } else {
3389
      work = from.chainwork.sub(to.chainwork);
×
3390
      sign = -1;
×
3391
    }
3392

3393
    work = work.imuln(pow.targetSpacing);
×
3394
    work = work.div(this.tip.getProof());
×
3395

3396
    if (work.bitLength() > 53)
×
3397
      return sign * Number.MAX_SAFE_INTEGER;
×
3398

3399
    return sign * work.toNumber();
×
3400
  }
3401

3402
  /**
3403
   * Calculate the next target based on the chain tip.
3404
   * @returns {Promise} - returns Number
3405
   * (target is in compact/mantissa form).
3406
   */
3407

3408
  async getCurrentTarget() {
3409
    return this.getTarget(this.network.now(), this.tip);
×
3410
  }
3411

3412
  /**
3413
   * Get median block by timestamp.
3414
   * @param {ChainEntry} prev
3415
   * @returns {Promise<ChainEntry>}
3416
   */
3417

3418
  async getSuitableBlock(prev) {
3419
    assert(prev);
×
3420

3421
    let z = prev;
×
3422
    let y = await this.getPrevious(z);
×
3423
    let x = await this.getPrevious(y);
×
3424

3425
    assert(x);
×
3426

3427
    if (x.time > z.time)
×
3428
      [x, z] = [z, x];
×
3429

3430
    if (x.time > y.time)
×
3431
      [x, y] = [y, x];
×
3432

3433
    if (y.time > z.time)
×
3434
      [y, z] = [z, y];
×
3435

3436
    return y;
×
3437
  }
3438

3439
  /**
3440
   * Calculate the next target.
3441
   * @param {Number} time - Next block timestamp.
3442
   * @param {ChainEntry} prev - Previous entry.
3443
   * @returns {Promise<Number>} - returns Number
3444
   * (target is in compact/mantissa form).
3445
   */
3446

3447
  async getTarget(time, prev) {
3448
    const pow = this.network.pow;
38,696✔
3449

3450
    // Genesis
3451
    if (!prev) {
38,696!
3452
      assert(time === this.network.genesis.time);
×
3453
      return pow.bits;
×
3454
    }
3455

3456
    // Do not retarget
3457
    if (pow.noRetargeting)
38,696!
3458
      return pow.bits;
38,696✔
3459

3460
    // Special behavior for testnet:
3461
    if (pow.targetReset) {
×
3462
      if (time > prev.time + pow.targetSpacing * 2)
×
3463
        return pow.bits;
×
3464
    }
3465

3466
    assert(pow.blocksPerDay === 144);
×
3467
    assert(pow.targetWindow === 144);
×
3468

3469
    if (prev.height < pow.blocksPerDay + 2) {
×
3470
      assert(prev.bits === pow.bits);
×
3471
      return pow.bits;
×
3472
    }
3473

3474
    const last = await this.getSuitableBlock(prev);
×
3475

3476
    const height = prev.height - pow.blocksPerDay;
×
3477
    assert(height >= 0);
×
3478

3479
    const ancestor = await this.getAncestor(prev, height);
×
3480
    const first = await this.getSuitableBlock(ancestor);
×
3481

3482
    return this.retarget(first, last);
×
3483
  }
3484

3485
  /**
3486
   * Calculate the next target.
3487
   * @param {ChainEntry} first - Suitable block from 1 day prior.
3488
   * @param {ChainEntry} last - Last suitable block.
3489
   * @returns {Number} target - Target in compact/mantissa form.
3490
   */
3491

3492
  retarget(first, last) {
3493
    assert(last.height > first.height);
×
3494

3495
    const pow = this.network.pow;
×
3496
    const maxChainwork = ChainEntry.MAX_CHAINWORK;
×
3497
    const minActual = pow.blocksPerDay / 4;
×
3498
    const maxActual = pow.blocksPerDay * 4;
×
3499

3500
    assert(minActual === 36); // 72 on BCH
×
3501
    assert(maxActual === 576); // 288 on BCH
×
3502

3503
    assert(minActual * pow.targetSpacing === pow.minActual);
×
3504
    assert(maxActual * pow.targetSpacing === pow.maxActual);
×
3505

3506
    const work = last.chainwork.sub(first.chainwork);
×
3507

3508
    work.imuln(pow.targetSpacing);
×
3509

3510
    let actualTimespan = last.time - first.time;
×
3511

3512
    if (actualTimespan < minActual * pow.targetSpacing)
×
3513
      actualTimespan = minActual * pow.targetSpacing;
×
3514

3515
    if (actualTimespan > maxActual * pow.targetSpacing)
×
3516
      actualTimespan = maxActual * pow.targetSpacing;
×
3517

3518
    work.idivn(actualTimespan);
×
3519

3520
    if (work.isZero())
×
3521
      return pow.bits;
×
3522

3523
    const target = maxChainwork.div(work).isubn(1);
×
3524

3525
    if (target.gt(pow.limit))
×
3526
      return pow.bits;
×
3527

3528
    const cmpct = consensus.toCompact(target);
×
3529

3530
    this.logger.debug('Retargetting to: %s (0x%s).',
×
3531
      consensus.fromCompact(cmpct).toString('hex', 64),
3532
      util.hex32(cmpct));
3533

3534
    return cmpct;
×
3535
  }
3536

3537
  /**
3538
   * Find a locator. Analagous to bitcoind's `FindForkInGlobalIndex()`.
3539
   * @param {Hash[]} locator - Hashes.
3540
   * @returns {Promise<Hash>} (the hash of the latest known block).
3541
   */
3542

3543
  async findLocator(locator) {
3544
    for (const hash of locator) {
56✔
3545
      if (await this.isMainHash(hash))
121✔
3546
        return hash;
56✔
3547
    }
3548

3549
    return this.network.genesis.hash;
×
3550
  }
3551

3552
  /**
3553
   * Check whether a versionbits deployment is active (BIP9: versionbits).
3554
   * @example
3555
   * await chain.isActive(tip, deployments.segwit);
3556
   * @see https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki
3557
   * @param {ChainEntry} prev - Previous chain entry.
3558
   * @param {Object} deployment - Deployment.
3559
   * @returns {Promise<Boolean>}
3560
   */
3561

3562
  async isActive(prev, deployment) {
3563
    const state = await this.getState(prev, deployment);
147,807✔
3564
    return state === thresholdStates.ACTIVE;
147,807✔
3565
  }
3566

3567
  /**
3568
   * Get chain entry state for a deployment (BIP9: versionbits).
3569
   * @example
3570
   * await chain.getState(tip, deployments.segwit);
3571
   * @see https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki
3572
   * @param {ChainEntry} prev - Previous chain entry.
3573
   * @param {Object} deployment - Deployment.
3574
   * @returns {Promise<Number>}
3575
   */
3576

3577
  async getState(prev, deployment) {
3578
    const bit = deployment.bit;
218,409✔
3579

3580
    let window = this.network.minerWindow;
218,409✔
3581
    let threshold = this.network.activationThreshold;
218,409✔
3582

3583
    if (deployment.threshold !== -1)
218,409!
3584
      threshold = deployment.threshold;
×
3585

3586
    if (deployment.window !== -1)
218,409!
3587
      window = deployment.window;
×
3588

3589
    if (((prev.height + 1) % window) !== 0) {
218,409✔
3590
      const height = prev.height - ((prev.height + 1) % window);
217,260✔
3591

3592
      prev = await this.getAncestor(prev, height);
217,260✔
3593

3594
      if (!prev)
217,260✔
3595
        return thresholdStates.DEFINED;
108,834✔
3596

3597
      assert(prev.height === height);
108,426✔
3598
      assert(((prev.height + 1) % window) === 0);
108,426✔
3599
    }
3600

3601
    let entry = prev;
109,575✔
3602
    let state = thresholdStates.DEFINED;
109,575✔
3603

3604
    const compute = [];
109,575✔
3605

3606
    while (entry) {
109,575✔
3607
      const cached = this.db.stateCache.get(bit, entry);
109,814✔
3608

3609
      if (cached !== -1) {
109,814✔
3610
        state = cached;
109,443✔
3611
        break;
109,443✔
3612
      }
3613

3614
      const time = await this.getMedianTime(entry);
371✔
3615

3616
      if (time < deployment.startTime) {
371!
3617
        state = thresholdStates.DEFINED;
×
3618
        this.db.stateCache.set(bit, entry, state);
×
3619
        break;
×
3620
      }
3621

3622
      compute.push(entry);
371✔
3623

3624
      const height = entry.height - window;
371✔
3625

3626
      entry = await this.getAncestor(entry, height);
371✔
3627
    }
3628

3629
    while (compute.length) {
109,575✔
3630
      const entry = compute.pop();
371✔
3631

3632
      switch (state) {
371!
3633
        case thresholdStates.DEFINED: {
3634
          const time = await this.getMedianTime(entry);
132✔
3635

3636
          if (time >= deployment.timeout) {
132✔
3637
            state = thresholdStates.FAILED;
65✔
3638
            break;
65✔
3639
          }
3640

3641
          if (time >= deployment.startTime) {
67!
3642
            state = thresholdStates.STARTED;
67✔
3643
            break;
67✔
3644
          }
3645

3646
          break;
×
3647
        }
3648
        case thresholdStates.STARTED: {
3649
          const time = await this.getMedianTime(entry);
63✔
3650

3651
          if (time >= deployment.timeout) {
63✔
3652
            state = thresholdStates.FAILED;
1✔
3653
            break;
1✔
3654
          }
3655

3656
          let block = entry;
62✔
3657
          let count = 0;
62✔
3658

3659
          for (let i = 0; i < window; i++) {
62✔
3660
            if (block.hasBit(bit))
7,920✔
3661
              count += 1;
3,454✔
3662

3663
            if (count >= threshold) {
7,920✔
3664
              state = thresholdStates.LOCKED_IN;
29✔
3665
              break;
29✔
3666
            }
3667

3668
            block = await this.getPrevious(block);
7,891✔
3669
            assert(block);
7,891✔
3670
          }
3671

3672
          break;
62✔
3673
        }
3674
        case thresholdStates.LOCKED_IN: {
3675
          state = thresholdStates.ACTIVE;
12✔
3676
          break;
12✔
3677
        }
3678
        case thresholdStates.FAILED:
3679
        case thresholdStates.ACTIVE: {
3680
          break;
164✔
3681
        }
3682
        default: {
3683
          assert(false, 'Bad state.');
×
3684
          break;
×
3685
        }
3686
      }
3687

3688
      this.db.stateCache.set(bit, entry, state);
371✔
3689
    }
3690

3691
    return state;
109,575✔
3692
  }
3693

3694
  /**
3695
   * Get signalling statistics for BIP9/versionbits soft fork
3696
   * @param {ChainEntry} prev - Previous chain entry.
3697
   * @param {Object} deployment - Deployment.
3698
   * @returns {Promise<Object>}
3699
   */
3700

3701
  async getBIP9Stats(prev, deployment) {
3702
    const state = await this.getState(prev, deployment);
269✔
3703
    if (state !== thresholdStates.STARTED)
269✔
3704
      throw new Error(`Deployment "${deployment.name}" not in STARTED state.`);
1✔
3705

3706
    const bit = deployment.bit;
268✔
3707
    let window = this.network.minerWindow;
268✔
3708
    let threshold = this.network.activationThreshold;
268✔
3709

3710
    // Deployments like `segsignal` (BIP91) have custom window & threshold
3711
    if (deployment.window !== -1)
268!
3712
      window = deployment.window;
×
3713

3714
    if (deployment.threshold !== -1)
268!
3715
      threshold = deployment.threshold;
×
3716

3717
    let count = 0;
268✔
3718
    let block = prev;
268✔
3719

3720
    while((block.height + 1) % window !== 0) {
268✔
3721
      if (block.hasBit(bit))
29,513✔
3722
        count++;
8,312✔
3723

3724
      block = await this.getPrevious(block);
29,513✔
3725
      if(!block)
29,513!
3726
        break;
×
3727
    }
3728

3729
    return {
268✔
3730
      period: window,
3731
      threshold: threshold,
3732
      elapsed: (prev.height + 1) % window,
3733
      count: count,
3734
      possible: (window - threshold) >= ((prev.height + 1) % window) - count
3735
    };
3736
  }
3737

3738
  /**
3739
   * Compute the version for a new block (BIP9: versionbits).
3740
   * @see https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki
3741
   * @param {ChainEntry} prev - Previous chain entry (usually the tip).
3742
   * @returns {Promise<Number>}
3743
   */
3744

3745
  async computeBlockVersion(prev) {
3746
    let version = 0;
17,452✔
3747

3748
    for (const deployment of this.network.deploys) {
17,452✔
3749
      const state = await this.getState(prev, deployment);
69,808✔
3750

3751
      if (state === thresholdStates.LOCKED_IN
69,808✔
3752
          || state === thresholdStates.STARTED) {
3753
        version |= 1 << deployment.bit;
12,412✔
3754
      }
3755
    }
3756

3757
    version >>>= 0;
17,452✔
3758

3759
    return version;
17,452✔
3760
  }
3761

3762
  /**
3763
   * Get the current deployment state of the chain. Called on load.
3764
   * @private
3765
   * @returns {Promise<DeploymentState>}
3766
   */
3767

3768
  async getDeploymentState() {
3769
    return this.readDeploymentState(this.tip);
319✔
3770
  }
3771

3772
  /**
3773
   * Get deployment state.
3774
   * @private
3775
   * @param {ChainEntry} tip
3776
   * @returns {Promise<DeploymentState>}
3777
   */
3778

3779
  async readDeploymentState(tip) {
3780
    const prev = await this.getPrevious(tip);
2,037✔
3781

3782
    if (!prev) {
2,037✔
3783
      assert(tip.isGenesis());
246✔
3784
      return new DeploymentState(this.network.genesis.hash);
246✔
3785
    }
3786

3787
    if (this.options.spv)
1,791✔
3788
      return new DeploymentState(this.network.genesis.hash);
14✔
3789

3790
    return this.getDeployments(tip.time, prev);
1,777✔
3791
  }
3792

3793
  /**
3794
   * Get the next deployment state of the chain.
3795
   * @returns {Promise<DeploymentState>}
3796
   */
3797

3798
  async getNextState() {
3799
    if (this.options.spv)
9,684✔
3800
      return this.state;
2✔
3801

3802
    return this.getDeployments(this.network.now(), this.tip);
9,682✔
3803
  }
3804

3805
  /**
3806
   * Check transaction finality, taking into account MEDIAN_TIME_PAST
3807
   * if it is present in the lock flags.
3808
   * @param {ChainEntry} prev - Previous chain entry.
3809
   * @param {TX} tx
3810
   * @param {LockFlags} flags
3811
   * @returns {Promise} - Returns Boolean.
3812
   */
3813

3814
  async verifyFinal(prev, tx, flags) {
3815
    const height = prev.height + 1;
2,279✔
3816

3817
    // We can skip MTP if the locktime is height.
3818
    if (!(tx.locktime & consensus.LOCKTIME_FLAG))
2,279!
3819
      return tx.isFinal(height, -1);
2,279✔
3820

3821
    const time = await this.getMedianTime(prev);
×
3822

3823
    return tx.isFinal(height, time);
×
3824
  }
3825

3826
  /**
3827
   * Get the necessary minimum time and height sequence locks for a transaction.
3828
   * @param {ChainEntry} prev
3829
   * @param {TX} tx
3830
   * @param {CoinView} view
3831
   * @param {LockFlags} flags
3832
   * @returns {Promise}
3833
   */
3834

3835
  async getLocks(prev, tx, view, flags) {
3836
    const GRANULARITY = consensus.SEQUENCE_GRANULARITY;
11,798✔
3837
    const DISABLE_FLAG = consensus.SEQUENCE_DISABLE_FLAG;
11,798✔
3838
    const TYPE_FLAG = consensus.SEQUENCE_TYPE_FLAG;
11,798✔
3839
    const MASK = consensus.SEQUENCE_MASK;
11,798✔
3840

3841
    if (tx.isCoinbase())
11,798!
3842
      return [-1, -1];
×
3843

3844
    let minHeight = -1;
11,798✔
3845
    let minTime = -1;
11,798✔
3846

3847
    for (const {prevout, sequence} of tx.inputs) {
11,798✔
3848
      if (sequence & DISABLE_FLAG)
26,320✔
3849
        continue;
26,311✔
3850

3851
      let height = view.getHeight(prevout);
9✔
3852

3853
      if (height === -1)
9✔
3854
        height = this.height + 1;
1✔
3855

3856
      if (!(sequence & TYPE_FLAG)) {
9✔
3857
        height += (sequence & MASK) - 1;
7✔
3858
        minHeight = Math.max(minHeight, height);
7✔
3859
        continue;
7✔
3860
      }
3861

3862
      height = Math.max(height - 1, 0);
2✔
3863

3864
      const entry = await this.getAncestor(prev, height);
2✔
3865
      assert(entry, 'Database is corrupt.');
2✔
3866

3867
      let time = await this.getMedianTime(entry);
2✔
3868
      time += ((sequence & MASK) << GRANULARITY) - 1;
2✔
3869
      minTime = Math.max(minTime, time);
2✔
3870
    }
3871

3872
    return [minHeight, minTime];
11,798✔
3873
  }
3874

3875
  /**
3876
   * Verify sequence locks.
3877
   * @param {ChainEntry} prev
3878
   * @param {TX} tx
3879
   * @param {CoinView} view
3880
   * @param {LockFlags} flags
3881
   * @returns {Promise} - Returns Boolean.
3882
   */
3883

3884
  async verifyLocks(prev, tx, view, flags) {
3885
    const [height, time] = await this.getLocks(prev, tx, view, flags);
11,798✔
3886

3887
    if (height !== -1) {
11,798✔
3888
      if (height >= prev.height + 1)
7✔
3889
        return false;
2✔
3890
    }
3891

3892
    if (time !== -1) {
11,796✔
3893
      const mtp = await this.getMedianTime(prev);
2✔
3894

3895
      if (time >= mtp)
2✔
3896
        return false;
1✔
3897
    }
3898

3899
    return true;
11,795✔
3900
  }
3901

3902
  /**
3903
   * Get safe tree root.
3904
   * @returns {Promise<Hash>}
3905
   */
3906

3907
  async getSafeRoot() {
3908
    // The tree is committed on an interval.
3909
    // Mainnet is 36 blocks, meaning at height 36,
3910
    // the name set of the past 36 blocks are
3911
    // inserted into the tree. The commitment for
3912
    // that insertion actually appears in a block
3913
    // header one block later (height 37). We
3914
    // want the the root _before_ the current one
3915
    // so we can calculate that with:
3916
    //   chain_height - (chain_height % interval)
3917
    const interval = this.network.names.treeInterval;
57✔
3918

3919
    let mod = this.height % interval;
57✔
3920

3921
    // If there's enough proof-of-work
3922
    // on top of the most recent root,
3923
    // it should be safe to use it.
3924
    if (mod >= 12)
57!
3925
      mod = 0;
×
3926

3927
    const height = this.height - mod;
57✔
3928
    const entry = await this.getEntryByHeight(height);
57✔
3929
    assert(entry);
57✔
3930

3931
    return entry.treeRoot;
57✔
3932
  }
3933
}
3934

3935
/**
3936
 * ChainOptions
3937
 * @alias module:blockchain.ChainOptions
3938
 */
3939

3940
class ChainOptions {
3941
  /**
3942
   * Create chain options.
3943
   * @constructor
3944
   * @param {Object} options
3945
   */
3946

3947
  constructor(options) {
3948
    this.network = Network.primary;
287✔
3949
    this.logger = Logger.global;
287✔
3950
    this.blocks = null;
287✔
3951
    this.workers = null;
287✔
3952

3953
    this.prefix = null;
287✔
3954
    this.location = null;
287✔
3955
    this.treeLocation = null;
287✔
3956
    this.memory = true;
287✔
3957
    this.maxFiles = 64;
287✔
3958
    this.cacheSize = 32 << 20;
287✔
3959
    this.compression = true;
287✔
3960

3961
    this.spv = false;
287✔
3962
    this.prune = false;
287✔
3963
    this.indexTX = false;
287✔
3964
    this.indexAddress = false;
287✔
3965

3966
    this.entryCache = 5000;
287✔
3967
    this.maxOrphans = 20;
287✔
3968
    this.checkpoints = true;
287✔
3969
    this.chainMigrate = -1;
287✔
3970
    this.compactTreeOnInit = false;
287✔
3971
    this.compactTreeInitInterval = 10000;
287✔
3972

3973
    if (options)
287!
3974
      this.fromOptions(options);
287✔
3975
  }
3976

3977
  /**
3978
   * Inject properties from object.
3979
   * @private
3980
   * @param {Object} options
3981
   * @returns {ChainOptions}
3982
   */
3983

3984
  fromOptions(options) {
3985
    if (!options.spv) {
287✔
3986
      assert(typeof options.blocks === 'object', 'Chain requires BlockStore.');
263✔
3987
      this.blocks = options.blocks;
263✔
3988
    }
3989

3990
    if (options.network != null)
287✔
3991
      this.network = Network.get(options.network);
286✔
3992

3993
    if (options.logger != null) {
287✔
3994
      assert(typeof options.logger === 'object');
205✔
3995
      this.logger = options.logger;
205✔
3996
    }
3997

3998
    if (options.workers != null) {
287✔
3999
      assert(typeof options.workers === 'object');
226✔
4000
      this.workers = options.workers;
226✔
4001
    }
4002

4003
    if (options.spv != null) {
287✔
4004
      assert(typeof options.spv === 'boolean');
31✔
4005
      this.spv = options.spv;
31✔
4006
    }
4007

4008
    if (options.prefix != null) {
287✔
4009
      assert(typeof options.prefix === 'string');
245✔
4010
      this.prefix = options.prefix;
245✔
4011
      this.location = this.spv
245✔
4012
        ? path.join(this.prefix, 'spvchain')
4013
        : path.join(this.prefix, 'chain');
4014
      this.treePrefix = path.join(this.prefix, 'tree');
245✔
4015
    }
4016

4017
    if (options.location != null) {
287!
4018
      assert(typeof options.location === 'string');
×
4019
      this.location = options.location;
×
4020
    }
4021

4022
    if (options.treePrefix != null) {
287!
4023
      assert(typeof options.treePrefix === 'string');
×
4024
      this.treePrefix = options.treePrefix;
×
4025
    }
4026

4027
    if (options.memory != null) {
287✔
4028
      assert(typeof options.memory === 'boolean');
280✔
4029
      this.memory = options.memory;
280✔
4030
    }
4031

4032
    if (options.maxFiles != null) {
287!
4033
      assert((options.maxFiles >>> 0) === options.maxFiles);
×
4034
      this.maxFiles = options.maxFiles;
×
4035
    }
4036

4037
    if (options.cacheSize != null) {
287!
4038
      assert(Number.isSafeInteger(options.cacheSize));
×
4039
      assert(options.cacheSize >= 0);
×
4040
      this.cacheSize = options.cacheSize;
×
4041
    }
4042

4043
    if (options.compression != null) {
287!
4044
      assert(typeof options.compression === 'boolean');
×
4045
      this.compression = options.compression;
×
4046
    }
4047

4048
    if (options.prune != null) {
287✔
4049
      assert(typeof options.prune === 'boolean');
16✔
4050
      assert(!options.prune || !options.spv, 'Can not prune in spv mode.');
16✔
4051
      this.prune = options.prune;
15✔
4052
    }
4053

4054
    if (options.indexTX != null) {
286✔
4055
      assert(typeof options.indexTX === 'boolean');
3✔
4056
      this.indexTX = options.indexTX;
3✔
4057
    }
4058

4059
    if (options.indexAddress != null) {
286✔
4060
      assert(typeof options.indexAddress === 'boolean');
2✔
4061
      this.indexAddress = options.indexAddress;
2✔
4062
    }
4063

4064
    if (options.entryCache != null) {
286!
4065
      assert((options.entryCache >>> 0) === options.entryCache);
×
4066
      this.entryCache = options.entryCache;
×
4067
    }
4068

4069
    if (options.maxOrphans != null) {
286!
4070
      assert((options.maxOrphans >>> 0) === options.maxOrphans);
×
4071
      this.maxOrphans = options.maxOrphans;
×
4072
    }
4073

4074
    if (options.checkpoints != null) {
286✔
4075
      assert(typeof options.checkpoints === 'boolean');
2✔
4076
      this.checkpoints = options.checkpoints;
2✔
4077
    }
4078

4079
    if (options.chainMigrate != null) {
286!
4080
      assert(typeof options.chainMigrate === 'number');
×
4081
      this.chainMigrate = options.chainMigrate;
×
4082
    }
4083

4084
    if (options.compactTreeOnInit != null) {
286✔
4085
      assert(typeof options.compactTreeOnInit === 'boolean');
15✔
4086
      this.compactTreeOnInit = options.compactTreeOnInit;
15✔
4087
    }
4088

4089
    if (options.compactTreeInitInterval != null) {
286✔
4090
      const {keepBlocks} = this.network.block;
15✔
4091
      assert(typeof options.compactTreeInitInterval === 'number');
15✔
4092
      assert(options.compactTreeInitInterval >= keepBlocks,
15✔
4093
        `compaction interval must not be smaller than ${keepBlocks}.`);
4094
      this.compactTreeInitInterval = options.compactTreeInitInterval;
14✔
4095
    }
4096

4097
    if (this.spv || this.memory)
285✔
4098
      this.treePrefix = null;
198✔
4099

4100
    return this;
285✔
4101
  }
4102

4103
  /**
4104
   * Instantiate chain options from object.
4105
   * @param {Object} options
4106
   * @returns {ChainOptions}
4107
   */
4108

4109
  static fromOptions(options) {
4110
    return new ChainOptions().fromOptions(options);
×
4111
  }
4112
}
4113

4114
/**
4115
 * Deployment State
4116
 * @alias module:blockchain.DeploymentState
4117
 * @property {VerifyFlags} flags
4118
 * @property {LockFlags} lockFlags
4119
 */
4120

4121
class DeploymentState {
4122
  /**
4123
   * Create a deployment state.
4124
   * @param {Hash} tip
4125
   * @constructor
4126
   */
4127

4128
  constructor(tip) {
4129
    this.tip = tip;
49,814✔
4130
    this.flags = Script.flags.MANDATORY_VERIFY_FLAGS;
49,814✔
4131
    this.lockFlags = common.MANDATORY_LOCKTIME_FLAGS;
49,814✔
4132
    this.nameFlags = rules.MANDATORY_VERIFY_COVENANT_FLAGS;
49,814✔
4133
    this.hasAirstop = false;
49,814✔
4134
  }
4135

4136
  hasHardening() {
4137
    return (this.nameFlags & VERIFY_COVENANTS_HARDENED) !== 0;
50,687✔
4138
  }
4139

4140
  hasICANNLockup() {
4141
    return (this.nameFlags & VERIFY_COVENANTS_LOCKUP) !== 0;
40,635✔
4142
  }
4143
}
4144

4145
/**
4146
 * Orphan
4147
 * @ignore
4148
 */
4149

4150
class Orphan {
4151
  /**
4152
   * Create an orphan.
4153
   * @constructor
4154
   */
4155

4156
  constructor(block, flags, id) {
4157
    this.block = block;
28✔
4158
    this.flags = flags;
28✔
4159
    this.id = id;
28✔
4160
    this.time = util.now();
28✔
4161
  }
4162
}
4163

4164
/*
4165
 * Helpers
4166
 */
4167

4168
function cmp(a, b) {
4169
  return a - b;
969,377✔
4170
}
4171

4172
/*
4173
 * Expose
4174
 */
4175

4176
Chain.ChainOptions = ChainOptions;
1✔
4177

4178
module.exports = Chain;
1✔
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

© 2025 Coveralls, Inc