Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

handshake-org / hsd / 3558259368

27 Nov 2022 - 12:49 coverage: 67.74% (+0.003%) from 67.737%
3558259368

Pull #780

github

GitHub
Merge 13e0a194f into 9cf8cb838
Pull Request #780: txdb lockBalances assertion errors MUST DIE ☠️

7273 of 12613 branches covered (57.66%)

Branch coverage included in aggregate %.

31 of 33 new or added lines in 3 files covered. (93.94%)

7 existing lines in 1 file now uncovered.

23250 of 32446 relevant lines covered (71.66%)

31478.79 hits per line

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

85.11
/lib/wallet/txdb.js
1
/*!
2
 * txdb.js - persistent transaction pool
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 bio = require('bufio');
1×
11
const {BufferSet} = require('buffer-map');
1×
12
const util = require('../utils/util');
1×
13
const Amount = require('../ui/amount');
1×
14
const CoinView = require('../coins/coinview');
1×
15
const Coin = require('../primitives/coin');
1×
16
const Outpoint = require('../primitives/outpoint');
1×
17
const records = require('./records');
1×
18
const layout = require('./layout').txdb;
1×
19
const consensus = require('../protocol/consensus');
1×
20
const policy = require('../protocol/policy');
1×
21
const rules = require('../covenants/rules');
1×
22
const NameState = require('../covenants/namestate');
1×
23
const NameUndo = require('../covenants/undo');
1×
24
const {TXRecord} = records;
1×
25
const {types} = rules;
1×
26

27
/*
28
 * Constants
29
 */
30

31
const EMPTY = Buffer.alloc(0);
1×
32

33
/**
34
 * TXDB
35
 * @alias module:wallet.TXDB
36
 */
37

38
class TXDB {
39
  /**
40
   * Create a TXDB.
41
   * @constructor
42
   * @param {WalletDB} wdb
43
   */
44

45
  constructor(wdb, wid) {
46
    this.wdb = wdb;
213×
47
    this.db = wdb.db;
213×
48
    this.logger = wdb.logger;
213×
49

50
    this.wid = wid || 0;
213×
51
    this.bucket = null;
213×
52
    this.wallet = null;
213×
53
    this.locked = new BufferSet();
213×
54
  }
55

56
  /**
57
   * Open TXDB.
58
   * @returns {Promise}
59
   */
60

61
  async open(wallet) {
62
    const prefix = layout.prefix.encode(wallet.wid);
192×
63

64
    this.wid = wallet.wid;
192×
65
    this.bucket = this.db.bucket(prefix);
192×
66
    this.wallet = wallet;
192×
67
  }
68

69
  /**
70
   * Emit transaction event.
71
   * @private
72
   * @param {String} event
73
   * @param {Object} data
74
   * @param {Details} details
75
   */
76

77
  emit(event, data, details) {
78
    this.wdb.emit(event, this.wallet, data, details);
7,721×
79
    this.wallet.emit(event, data, details);
7,721×
80
  }
81

82
  /**
83
   * Get wallet path for output.
84
   * @param {Output} output
85
   * @returns {Promise} - Returns {@link Path}.
86
   */
87

88
  getPath(output) {
89
    const hash = output.getHash();
54,433×
90

91
    if (!hash)
Branches [[1, 0]] missed. 54,433×
92
      return null;
!
93

94
    return this.wdb.getPath(this.wid, hash);
54,433×
95
  }
96

97
  /**
98
   * Test whether path exists for output.
99
   * @param {Output} output
100
   * @returns {Promise} - Returns Boolean.
101
   */
102

103
  hasPath(output) {
104
    const hash = output.getHash();
!
105

106
    if (!hash)
Branches [[2, 0], [2, 1]] missed. !
107
      return false;
!
108

109
    return this.wdb.hasPath(this.wid, hash);
!
110
  }
111

112
  /**
113
   * Save credit.
114
   * @param {Credit} credit
115
   * @param {Path} path
116
   */
117

118
  async saveCredit(b, credit, path) {
119
    const {coin} = credit;
32,200×
120

121
    b.put(layout.c.encode(coin.hash, coin.index), credit.encode());
32,200×
122
    b.put(layout.C.encode(path.account, coin.hash, coin.index), null);
32,200×
123

124
    return this.addOutpointMap(b, coin.hash, coin.index);
32,200×
125
  }
126

127
  /**
128
   * Remove credit.
129
   * @param {Credit} credit
130
   * @param {Path} path
131
   */
132

133
  async removeCredit(b, credit, path) {
134
    const {coin} = credit;
8,194×
135

136
    b.del(layout.c.encode(coin.hash, coin.index));
8,194×
137
    b.del(layout.C.encode(path.account, coin.hash, coin.index));
8,194×
138

139
    return this.removeOutpointMap(b, coin.hash, coin.index);
8,194×
140
  }
141

142
  /**
143
   * Spend credit.
144
   * @param {Credit} credit
145
   * @param {TX} tx
146
   * @param {Number} index
147
   */
148

149
  spendCredit(b, credit, tx, index) {
150
    const prevout = tx.inputs[index].prevout;
8,077×
151
    const spender = Outpoint.fromTX(tx, index);
8,077×
152
    b.put(layout.s.encode(prevout.hash, prevout.index), spender.encode());
8,077×
153
    b.put(layout.d.encode(spender.hash, spender.index), credit.coin.encode());
8,077×
154
  }
155

156
  /**
157
   * Unspend credit.
158
   * @param {TX} tx
159
   * @param {Number} index
160
   */
161

162
  unspendCredit(b, tx, index) {
163
    const prevout = tx.inputs[index].prevout;
63×
164
    const spender = Outpoint.fromTX(tx, index);
63×
165
    b.del(layout.s.encode(prevout.hash, prevout.index));
63×
166
    b.del(layout.d.encode(spender.hash, spender.index));
63×
167
  }
168

169
  /**
170
   * Write input record.
171
   * @param {TX} tx
172
   * @param {Number} index
173
   */
174

175
  async writeInput(b, tx, index) {
176
    const prevout = tx.inputs[index].prevout;
470×
177
    const spender = Outpoint.fromTX(tx, index);
470×
178
    b.put(layout.s.encode(prevout.hash, prevout.index), spender.encode());
470×
179
    return this.addOutpointMap(b, prevout.hash, prevout.index);
470×
180
  }
181

182
  /**
183
   * Remove input record.
184
   * @param {TX} tx
185
   * @param {Number} index
186
   */
187

188
  async removeInput(b, tx, index) {
189
    const prevout = tx.inputs[index].prevout;
177×
190
    b.del(layout.s.encode(prevout.hash, prevout.index));
177×
191
    return this.removeOutpointMap(b, prevout.hash, prevout.index);
177×
192
  }
193

194
  /**
195
   * Update wallet balance.
196
   * @param {BalanceDelta} state
197
   */
198

199
  async updateBalance(b, state) {
200
    const balance = await this.getWalletBalance();
3,858×
201
    state.applyTo(balance);
3,858×
202
    b.put(layout.R.encode(), balance.encode());
3,858×
203
    return balance;
3,858×
204
  }
205

206
  /**
207
   * Update account balance.
208
   * @param {Number} acct
209
   * @param {Balance} delta
210
   */
211

212
  async updateAccountBalance(b, acct, delta) {
213
    const balance = await this.getAccountBalance(acct);
3,904×
214
    delta.applyTo(balance);
3,904×
215
    b.put(layout.r.encode(acct), balance.encode());
3,904×
216
    return balance;
3,904×
217
  }
218

219
  /**
220
   * Test a whether a coin has been spent.
221
   * @param {Hash} hash
222
   * @param {Number} index
223
   * @returns {Promise} - Returns Boolean.
224
   */
225

226
  async getSpent(hash, index) {
227
    const data = await this.bucket.get(layout.s.encode(hash, index));
9,286×
228

229
    if (!data)
9,286×
230
      return null;
8,964×
231

232
    return Outpoint.decode(data);
322×
233
  }
234

235
  /**
236
   * Test a whether a coin has been spent.
237
   * @param {Hash} hash
238
   * @param {Number} index
239
   * @returns {Promise} - Returns Boolean.
240
   */
241

242
  isSpent(hash, index) {
243
    return this.bucket.has(layout.s.encode(hash, index));
!
244
  }
245

246
  /**
247
   * Append to global map.
248
   * @param {Number} height
249
   * @returns {Promise}
250
   */
251

252
  addBlockMap(b, height) {
253
    return this.wdb.addBlockMap(b.root(), height, this.wid);
2,973×
254
  }
255

256
  /**
257
   * Remove from global map.
258
   * @param {Number} height
259
   * @returns {Promise}
260
   */
261

262
  removeBlockMap(b, height) {
263
    return this.wdb.removeBlockMap(b.root(), height, this.wid);
487×
264
  }
265

266
  /**
267
   * Append to global map.
268
   * @param {Hash} hash
269
   * @returns {Promise}
270
   */
271

272
  addTXMap(b, hash) {
273
    return this.wdb.addTXMap(b.root(), hash, this.wid);
1,039×
274
  }
275

276
  /**
277
   * Remove from global map.
278
   * @param {Hash} hash
279
   * @returns {Promise}
280
   */
281

282
  removeTXMap(b, hash) {
283
    return this.wdb.removeTXMap(b.root(), hash, this.wid);
889×
284
  }
285

286
  /**
287
   * Append to global map.
288
   * @param {Hash} hash
289
   * @param {Number} index
290
   * @returns {Promise}
291
   */
292

293
  addOutpointMap(b, hash, index) {
294
    return this.wdb.addOutpointMap(b.root(), hash, index, this.wid);
32,670×
295
  }
296

297
  /**
298
   * Remove from global map.
299
   * @param {Hash} hash
300
   * @param {Number} index
301
   * @returns {Promise}
302
   */
303

304
  removeOutpointMap(b, hash, index) {
305
    return this.wdb.removeOutpointMap(b.root(), hash, index, this.wid);
8,371×
306
  }
307

308
  /**
309
   * Append to global map.
310
   * @param {Hash} hash
311
   * @param {Number} index
312
   * @returns {Promise}
313
   */
314

315
  addNameMap(b, nameHash) {
316
    return this.wdb.addNameMap(b.root(), nameHash, this.wid);
934×
317
  }
318

319
  /**
320
   * Remove from global map.
321
   * @param {Hash} hash
322
   * @param {Number} index
323
   * @returns {Promise}
324
   */
325

326
  removeNameMap(b, nameHash) {
327
    return this.wdb.removeNameMap(b.root(), nameHash, this.wid);
!
328
  }
329

330
  /**
331
   * List block records.
332
   * @returns {Promise}
333
   */
334

335
  getBlocks() {
336
    return this.bucket.keys({
!
337
      gte: layout.b.min(),
338
      lte: layout.b.max(),
339
      parse: key => layout.b.decode(key)[0]
!
340
    });
341
  }
342

343
  /**
344
   * Get block record.
345
   * @param {Number} height
346
   * @returns {Promise}
347
   */
348

349
  async getBlock(height) {
350
    const data = await this.bucket.get(layout.b.encode(height));
366×
351

352
    if (!data)
Branches [[4, 0]] missed. 366×
353
      return null;
!
354

355
    return BlockRecord.decode(data);
366×
356
  }
357

358
  /**
359
   * Append to the global block record.
360
   * @param {Hash} hash
361
   * @param {BlockMeta} block
362
   * @returns {Promise}
363
   */
364

365
  async addBlock(b, hash, block) {
366
    const key = layout.b.encode(block.height);
2,973×
367
    const data = await this.bucket.get(key);
2,973×
368

369
    if (!data) {
2,973×
370
      const blk = BlockRecord.fromMeta(block);
2,540×
371
      blk.add(hash);
2,540×
372
      b.put(key, blk.encode());
2,540×
373
      return;
2,540×
374
    }
375

376
    const raw = Buffer.allocUnsafe(data.length + 32);
433×
377
    data.copy(raw, 0);
433×
378

379
    const size = raw.readUInt32LE(40, true);
433×
380
    raw.writeUInt32LE(size + 1, 40, true);
433×
381
    hash.copy(raw, data.length);
433×
382

383
    b.put(key, raw);
433×
384
  }
385

386
  /**
387
   * Remove from the global block record.
388
   * @param {Hash} hash
389
   * @param {Number} height
390
   * @returns {Promise}
391
   */
392

393
  async removeBlock(b, hash, height) {
394
    const key = layout.b.encode(height);
487×
395
    const data = await this.bucket.get(key);
487×
396

397
    if (!data)
Branches [[6, 0]] missed. 487×
398
      return;
!
399

400
    const size = data.readUInt32LE(40, true);
487×
401

402
    assert(size > 0);
487×
403
    assert(data.slice(-32).equals(hash));
487×
404

405
    if (size === 1) {
487×
406
      b.del(key);
366×
407
      return;
366×
408
    }
409

410
    const raw = data.slice(0, -32);
121×
411
    raw.writeUInt32LE(size - 1, 40, true);
121×
412

413
    b.put(key, raw);
121×
414
  }
415

416
  /**
417
   * Remove from the global block record.
418
   * @param {Hash} hash
419
   * @param {Number} height
420
   * @returns {Promise}
421
   */
422

423
  async spliceBlock(b, hash, height) {
424
    const block = await this.getBlock(height);
!
425

426
    if (!block)
Branches [[8, 0], [8, 1]] missed. !
427
      return;
!
428

429
    if (!block.remove(hash))
Branches [[9, 0], [9, 1]] missed. !
430
      return;
!
431

432
    if (block.hashes.size === 0) {
Branches [[10, 0], [10, 1]] missed. !
433
      b.del(layout.b.encode(height));
!
434
      return;
!
435
    }
436

437
    b.put(layout.b.encode(height), block.encode());
!
438
  }
439

440
  /**
441
   * Test whether we have a name.
442
   * @param {Buffer} nameHash
443
   * @returns {Boolean}
444
   */
445

446
  async hasNameState(nameHash) {
447
    return this.bucket.has(layout.A.encode(nameHash));
8×
448
  }
449

450
  /**
451
   * Get a name state if present.
452
   * @param {Buffer} nameHash
453
   * @returns {NameState}
454
   */
455

456
  async getNameState(nameHash) {
457
    const raw = await this.bucket.get(layout.A.encode(nameHash));
135,693×
458

459
    if (!raw)
135,693×
460
      return null;
2,186×
461

462
    const ns = NameState.decode(raw);
133,507×
463
    ns.nameHash = nameHash;
133,507×
464

465
    return ns;
133,507×
466
  }
467

468
  /**
469
   * Get all names.
470
   * @returns {NameState[]}
471
   */
472

473
  async getNames() {
474
    const iter = this.bucket.iterator({
67×
475
      gte: layout.A.min(),
476
      lte: layout.A.max(),
477
      values: true
478
    });
479

480
    const names = [];
67×
481

482
    await iter.each((key, raw) => {
67×
483
      const [nameHash] = layout.A.decode(key);
41,734×
484
      const ns = NameState.decode(raw);
41,734×
485
      ns.nameHash = nameHash;
41,734×
486
      names.push(ns);
41,734×
487
    });
488

489
    return names;
67×
490
  }
491

492
  /**
493
   * Test whether we have a bid.
494
   * @param {Buffer} nameHash
495
   * @param {Outpoint} outpoint
496
   * @returns {Boolean}
497
   */
498

499
  async hasBid(nameHash, outpoint) {
500
    const {hash, index} = outpoint;
!
501
    return this.bucket.has(layout.i.encode(nameHash, hash, index));
!
502
  }
503

504
  /**
505
   * Get a bid if present.
506
   * @param {Buffer} nameHash
507
   * @param {Outpoint} outpoint
508
   * @returns {BlindBid}
509
   */
510

511
  async getBid(nameHash, outpoint) {
UNCOV
512
    const {hash, index} = outpoint;
!
UNCOV
513
    const raw = await this.bucket.get(layout.i.encode(nameHash, hash, index));
!
514

UNCOV
515
    if (!raw)
Branches [[12, 0], [12, 1]] missed. !
516
      return null;
!
517

UNCOV
518
    const bb = BlindBid.decode(raw);
!
UNCOV
519
    bb.nameHash = nameHash;
!
UNCOV
520
    bb.prevout = outpoint;
!
521

UNCOV
522
    return bb;
!
523
  }
524

525
  /**
526
   * Write a bid.
527
   * @param {Object} b
528
   * @param {Buffer} nameHash
529
   * @param {Outpoint} outpoint
530
   * @param {Object} options
531
   */
532

533
  putBid(b, nameHash, outpoint, options) {
534
    const {hash, index} = outpoint;
2,599×
535
    const bb = new BlindBid();
2,599×
536
    bb.nameHash = nameHash;
2,599×
537
    bb.name = options.name;
2,599×
538
    bb.lockup = options.lockup;
2,599×
539
    bb.blind = options.blind;
2,599×
540
    bb.own = options.own;
2,599×
541
    b.put(layout.i.encode(nameHash, hash, index), bb.encode());
2,599×
542
  }
543

544
  /**
545
   * Delete a bid.
546
   * @param {Object} b
547
   * @param {Buffer} nameHash
548
   * @param {Outpoint} outpoint
549
   */
550

551
  removeBid(b, nameHash, outpoint) {
552
    const {hash, index} = outpoint;
57×
553
    b.del(layout.i.encode(nameHash, hash, index));
57×
554
  }
555

556
  /**
557
   * Get all bids for name.
558
   * @param {Buffer} nameHash
559
   * @returns {BlindBid[]}
560
   */
561

562
  async getBids(nameHash) {
563
    const iter = this.bucket.iterator({
134×
564
      gte: nameHash ? layout.i.min(nameHash) : layout.i.min(),
565
      lte: nameHash ? layout.i.max(nameHash) : layout.i.max(),
566
      values: true
567
    });
568

569
    const bids = [];
134×
570

571
    await iter.each(async (key, raw) => {
134×
572
      const [nameHash, hash, index] = layout.i.decode(key);
64,303×
573
      const bb = BlindBid.decode(raw);
64,303×
574

575
      bb.nameHash = nameHash;
64,303×
576
      bb.prevout = new Outpoint(hash, index);
64,303×
577

578
      const bv = await this.getBlind(bb.blind);
64,303×
579

580
      if (bv)
64,303×
581
        bb.value = bv.value;
64,251×
582

583
      bids.push(bb);
64,303×
584
    });
585

586
    return bids;
134×
587
  }
588

589
  /**
590
   * Remove all bids for name.
591
   * @param {Buffer} nameHash
592
   */
593

594
  async removeBids(b, nameHash) {
595
    const iter = this.bucket.iterator({
!
596
      gte: layout.i.min(nameHash),
597
      lte: layout.i.max(nameHash)
598
    });
599

600
    await iter.each(k => b.del(k));
!
601
  }
602

603
  /**
604
   * Test whether we have a reveal.
605
   * @param {Buffer} nameHash
606
   * @param {Outpoint} outpoint
607
   * @returns {Boolean}
608
   */
609

610
  async hasReveal(nameHash, outpoint) {
611
    const {hash, index} = outpoint;
!
612
    return this.bucket.has(layout.B.encode(nameHash, hash, index));
!
613
  }
614

615
  /**
616
   * Get a reveal if present.
617
   * @param {Buffer} nameHash
618
   * @param {Outpoint} outpoint
619
   * @returns {BidReveal}
620
   */
621

622
  async getReveal(nameHash, outpoint) {
623
    const {hash, index} = outpoint;
!
624
    const raw = await this.bucket.get(layout.B.encode(nameHash, hash, index));
!
625

626
    if (!raw)
Branches [[16, 0], [16, 1]] missed. !
627
      return null;
!
628

629
    const brv = BidReveal.decode(raw);
!
630
    brv.nameHash = nameHash;
!
631
    brv.prevout = outpoint;
!
632

633
    return brv;
!
634
  }
635

636
  /**
637
   * Write a reveal.
638
   * @param {Object} b
639
   * @param {Buffer} nameHash
640
   * @param {Outpoint} outpoint
641
   * @param {Object} options
642
   */
643

644
  putReveal(b, nameHash, outpoint, options) {
645
    const {hash, index} = outpoint;
2,516×
646
    const brv = new BidReveal();
2,516×
647
    brv.nameHash = nameHash;
2,516×
648
    brv.name = options.name;
2,516×
649
    brv.value = options.value;
2,516×
650
    brv.height = options.height;
2,516×
651
    brv.own = options.own;
2,516×
652
    b.put(layout.B.encode(nameHash, hash, index), brv.encode());
2,516×
653
  }
654

655
  /**
656
   * Delete a reveal.
657
   * @param {Object} b
658
   * @param {Buffer} nameHash
659
   * @param {Outpoint} outpoint
660
   */
661

662
  removeReveal(b, nameHash, outpoint) {
663
    const {hash, index} = outpoint;
26×
664
    b.del(layout.B.encode(nameHash, hash, index));
26×
665
  }
666

667
  /**
668
   * Get all reveals by name.
669
   * @param {Buffer} nameHash
670
   * @returns {BidReveal[]}
671
   */
672

673
  async getReveals(nameHash) {
674
    const iter = this.bucket.iterator({
72×
675
      gte: nameHash ? layout.B.min(nameHash) : layout.B.min(),
676
      lte: nameHash ? layout.B.max(nameHash) : layout.B.max(),
677
      values: true
678
    });
679

680
    const reveals = [];
72×
681

682
    await iter.each(async (key, raw) => {
72×
683
      const [nameHash, hash, index] = layout.B.decode(key);
57,047×
684
      const brv = BidReveal.decode(raw);
57,047×
685
      brv.nameHash = nameHash;
57,047×
686
      brv.prevout = new Outpoint(hash, index);
57,047×
687
      reveals.push(brv);
57,047×
688
    });
689

690
    return reveals;
72×
691
  }
692

693
  /**
694
   * Remove all reveals by name.
695
   * @param {Object} b
696
   * @param {Buffer} nameHash
697
   */
698

699
  async removeReveals(b, nameHash) {
700
    const iter = this.bucket.iterator({
!
701
      gte: layout.B.min(nameHash),
702
      lte: layout.B.max(nameHash)
703
    });
704

705
    await iter.each(k => b.del(k));
!
706
  }
707

708
  /**
709
   * Test whether a blind value is present.
710
   * @param {Buffer} blind - Blind hash.
711
   * @returns {Boolean}
712
   */
713

714
  async hasBlind(blind) {
715
    return this.bucket.has(layout.v.encode(blind));
!
716
  }
717

718
  /**
719
   * Get a blind value if present.
720
   * @param {Buffer} blind - Blind hash.
721
   * @returns {BlindValue}
722
   */
723

724
  async getBlind(blind) {
725
    const raw = await this.bucket.get(layout.v.encode(blind));
68,132×
726

727
    if (!raw)
68,132×
728
      return null;
52×
729

730
    return BlindValue.decode(raw);
68,080×
731
  }
732

733
  /**
734
   * Write a blind value.
735
   * @param {Object} b
736
   * @param {Buffer} blind
737
   * @param {Object} options
738
   */
739

740
  putBlind(b, blind, options) {
741
    const {value, nonce} = options;
5,133×
742
    const bv = new BlindValue();
5,133×
743
    bv.value = value;
5,133×
744
    bv.nonce = nonce;
5,133×
745
    b.put(layout.v.encode(blind), bv.encode());
5,133×
746
  }
747

748
  /**
749
   * Save blind value.
750
   * @param {Buffer} blind
751
   * @param {Object} options
752
   */
753

754
  async saveBlind(blind, options) {
755
    const b = this.bucket.batch();
2,666×
756
    this.putBlind(b, blind, options);
2,666×
757
    await b.write();
2,666×
758
  }
759

760
  /**
761
   * Delete a blind value.
762
   * @param {Object} b
763
   * @param {Buffer} blind
764
   */
765

766
  removeBlind(b, blind) {
767
    b.del(layout.v.encode(blind));
!
768
  }
769

770
  /**
771
   * Add transaction without a batch.
772
   * @private
773
   * @param {TX} tx
774
   * @returns {Promise}
775
   */
776

777
  async add(tx, block) {
778
    const hash = tx.hash();
4,354×
779
    const existing = await this.getTX(hash);
4,354×
780

781
    assert(!tx.mutable, 'Cannot add mutable TX to wallet.');
4,354×
782

783
    if (existing) {
4,354×
784
      // Existing tx is already confirmed. Ignore.
785
      if (existing.height !== -1)
1,418×
786
        return null;
215×
787

788
      // The incoming tx won't confirm the
789
      // existing one anyway. Ignore.
790
      if (!block)
1,203×
791
        return null;
360×
792

793
      // Confirm transaction.
794
      return this.confirm(existing, block);
843×
795
    }
796

797
    const wtx = TXRecord.fromTX(tx, block);
2,936×
798

799
    if (!block) {
2,936×
800
      // Potentially remove double-spenders.
801
      // Only remove if they're not confirmed.
802
      if (!await this.removeConflicts(tx, true))
Branches [[24, 0]] missed. 790×
803
        return null;
!
804
      if (await this.isDoubleOpen(tx))
Branches [[25, 0]] missed. 790×
805
        return null;
!
806
    } else {
807
      // Potentially remove double-spenders.
808
      await this.removeConflicts(tx, false);
2,146×
809
      await this.removeDoubleOpen(tx);
2,146×
810
    }
811

812
    // Finally we can do a regular insertion.
813
    return this.insert(wtx, block);
2,936×
814
  }
815

816
  /**
817
   * Test whether the transaction
818
   * has a duplicate open.
819
   * @param {TX}
820
   * @returns {Boolean}
821
   */
822

823
  async isDoubleOpen(tx) {
824
    for (const {covenant} of tx.outputs) {
1,985×
825
      if (!covenant.isOpen())
97,863×
826
        continue;
11,027×
827

828
      const nameHash = covenant.getHash(0);
86,836×
829
      const key = layout.o.encode(nameHash);
86,836×
830
      const hash = await this.bucket.get(key);
86,836×
831

832
      // Allow a double open if previous auction period has expired
833
      // this is not a complete check for name availability or status!
834
      if (hash) {
86,836×
835
        const names = this.wdb.network.names;
6×
836
        const period = names.biddingPeriod + names.revealPeriod;
6×
837
        const oldTX = await this.getTX(hash);
6×
838
        if (oldTX.height !== -1 && oldTX.height + period < this.wdb.height)
6×
839
          return false;
4×
840

841
        return true;
2×
842
      }
843
    }
844

845
    return false;
1,979×
846
  }
847

848
  /**
849
   * Remove duplicate opens.
850
   * @private
851
   * @param {TX} tx
852
   */
853

854
  async removeDoubleOpen(tx) {
855
    for (const {covenant} of tx.outputs) {
2,146×
856
      if (!covenant.isOpen())
2,502×
857
        continue;
2,477×
858

859
      const nameHash = covenant.getHash(0);
25×
860
      const key = layout.o.encode(nameHash);
25×
861
      const hash = await this.bucket.get(key);
25×
862

863
      if (!hash)
Branches [[31, 1]] missed. 25×
864
        continue;
25×
865

866
      await this.remove(hash);
!
867
    }
868
  }
869

870
  /**
871
   * Index open covenants.
872
   * @private
873
   * @param {Batch} b
874
   * @param {TX} tx
875
   */
876

877
  indexOpens(b, tx) {
878
    for (const {covenant} of tx.outputs) {
625×
879
      if (!covenant.isOpen())
11,563×
880
        continue;
10,690×
881

882
      const nameHash = covenant.getHash(0);
873×
883
      const key = layout.o.encode(nameHash);
873×
884

885
      b.put(key, tx.hash());
873×
886
    }
887
  }
888

889
  /**
890
   * Unindex open covenants.
891
   * @private
892
   * @param {Batch} b
893
   * @param {TX} tx
894
   */
895

896
  unindexOpens(b, tx) {
897
    for (const {covenant} of tx.outputs) {
41×
898
      if (!covenant.isOpen())
Branches [[33, 1]] missed. 82×
899
        continue;
82×
900

901
      const nameHash = covenant.getHash(0);
!
902
      const key = layout.o.encode(nameHash);
!
903

904
      b.del(key);
!
905
    }
906
  }
907

908
  /**
909
   * Insert transaction.
910
   * @private
911
   * @param {TXRecord} wtx
912
   * @param {BlockMeta} block
913
   * @returns {Promise}
914
   */
915

916
  async insert(wtx, block) {
917
    const b = this.bucket.batch();
2,936×
918
    const {tx, hash} = wtx;
2,936×
919
    const height = block ? block.height : -1;
2,936×
920
    const details = new Details(wtx, block);
2,936×
921
    const state = new BalanceDelta();
2,936×
922
    const view = new CoinView();
2,936×
923

924
    let own = false;
2,936×
925

926
    if (!tx.isCoinbase()) {
2,936×
927
      // We need to potentially spend some coins here.
928
      for (let i = 0; i < tx.inputs.length; i++) {
1,133×
929
        const input = tx.inputs[i];
8,890×
930
        const {hash, index} = input.prevout;
8,890×
931
        const credit = await this.getCredit(hash, index);
8,890×
932

933
        if (!credit) {
8,890×
934
          // Watch all inputs for incoming txs.
935
          // This allows us to check for double spends.
936
          if (!block)
821×
937
            await this.writeInput(b, tx, i);
461×
938
          continue;
821×
939
        }
940

941
        const coin = credit.coin;
8,069×
942
        const path = await this.getPath(coin);
8,069×
943
        assert(path);
8,069×
944

945
        // Build the tx details object
946
        // as we go, for speed.
947
        details.setInput(i, path, coin);
8,069×
948

949
        // Write an undo coin for the credit
950
        // and add it to the stxo set.
951
        this.spendCredit(b, credit, tx, i);
8,069×
952

953
        // Unconfirmed balance should always
954
        // be updated as it reflects the on-chain
955
        // balance _and_ mempool balance assuming
956
        // everything in the mempool were to confirm.
957
        state.tx(path, 1);
8,069×
958
        state.coin(path, -1);
8,069×
959
        state.unconfirmed(path, -coin.value);
8,069×
960

961
        // If the first time we see a TX is in a block
962
        // (i.e. during a rescan) update the "unconfirmed" unlocked balance
963
        // before updating the "confirmed" locked balance.
964
        if (height !== -1)
8,069×
965
          this.unlockBalances(state, coin, path, -1);
134×
966

967
        this.unlockBalances(state, coin, path, height);
8,069×
968

969
        if (!block) {
8,069×
970
          // If the tx is not mined, we do not
971
          // disconnect the coin, we simply mark
972
          // a `spent` flag on the credit. This
973
          // effectively prevents the mempool
974
          // from altering our utxo state
975
          // permanently. It also makes it
976
          // possible to compare the on-chain
977
          // state vs. the mempool state.
978
          credit.spent = true;
7,935×
979
          await this.saveCredit(b, credit, path);
7,935×
980
        } else {
981
          // If the tx is mined, we can safely
982
          // remove the coin being spent. This
983
          // coin will be indexed as an undo
984
          // coin so it can be reconnected
985
          // later during a reorg.
986
          state.confirmed(path, -coin.value);
134×
987
          await this.removeCredit(b, credit, path);
134×
988

989
          view.addCoin(coin);
134×
990
        }
991

992
        own = true;
8,069×
993
      }
994
    }
995

996
    // Potentially add coins to the utxo set.
997
    for (let i = 0; i < tx.outputs.length; i++) {
2,936×
998
      const output = tx.outputs[i];
14,396×
999
      const path = await this.getPath(output);
14,396×
1000

1001
      if (!path)
14,396×
1002
        continue;
1,829×
1003

1004
      // If the first time we see a TX is in a block
1005
      // (i.e. during a rescan) update the "unconfirmed" locked balance
1006
      // before updating the "confirmed" locked balance.
1007
      if (height !== -1)
12,567×
1008
        this.lockBalances(state, output, path, -1);
2,014×
1009

1010
      this.lockBalances(state, output, path, height);
12,567×
1011

1012
      details.setOutput(i, path);
12,567×
1013

1014
      const credit = Credit.fromTX(tx, i, height);
12,567×
1015
      credit.own = own;
12,567×
1016

1017
      state.tx(path, 1);
12,567×
1018
      state.coin(path, 1);
12,567×
1019
      state.unconfirmed(path, output.value);
12,567×
1020

1021
      if (block)
12,567×
1022
        state.confirmed(path, output.value);
2,014×
1023

1024
      await this.saveCredit(b, credit, path);
12,567×
1025
    }
1026

1027
    // Handle names.
1028
    if (block && !await this.bucket.has(layout.U.encode(hash))) {
2,936×
1029
      const updated = await this.connectNames(b, tx, view, height);
2,143×
1030

1031
      if (updated && !state.updated()) {
2,143×
1032
        // Always save namestate transitions,
1033
        // even if they don't affect wallet balance
1034
        await this.addBlockMap(b, height);
195×
1035
        await this.addBlock(b, tx.hash(), block);
195×
1036
        await b.write();
195×
1037
      }
1038
    }
1039

1040
    // If this didn't update any coins,
1041
    // it's not our transaction.
1042
    if (!state.updated())
2,936×
1043
      return null;
381×
1044

1045
    // Index open covenants.
1046
    if (!block)
2,555×
1047
      this.indexOpens(b, tx);
625×
1048

1049
    // Save and index the transaction record.
1050
    b.put(layout.t.encode(hash), wtx.encode());
2,555×
1051
    b.put(layout.m.encode(wtx.mtime, hash), null);
2,555×
1052

1053
    if (!block)
2,555×
1054
      b.put(layout.p.encode(hash), null);
625×
1055
    else
1056
      b.put(layout.h.encode(height, hash), null);
1,930×
1057

1058
    // Do some secondary indexing for account-based
1059
    // queries. This saves us a lot of time for
1060
    // queries later.
1061
    for (const [acct, delta] of state.accounts) {
2,555×
1062
      await this.updateAccountBalance(b, acct, delta);
2,573×
1063

1064
      b.put(layout.T.encode(acct, hash), null);
2,573×
1065
      b.put(layout.M.encode(acct, wtx.mtime, hash), null);
2,573×
1066

1067
      if (!block)
2,573×
1068
        b.put(layout.P.encode(acct, hash), null);
642×
1069
      else
1070
        b.put(layout.H.encode(acct, height, hash), null);
1,931×
1071
    }
1072

1073
    // Update block records.
1074
    if (block) {
2,555×
1075
      await this.addBlockMap(b, height);
1,930×
1076
      await this.addBlock(b, tx.hash(), block);
1,930×
1077
    } else {
1078
      await this.addTXMap(b, hash);
625×
1079
    }
1080

1081
    // Commit the new state.
1082
    const balance = await this.updateBalance(b, state);
2,555×
1083

1084
    await b.write();
2,555×
1085

1086
    // This transaction may unlock some
1087
    // coins now that we've seen it.
1088
    this.unlockTX(tx);
2,555×
1089

1090
    // Emit events for potential local and
1091
    // websocket listeners. Note that these
1092
    // will only be emitted if the batch is
1093
    // successfully written to disk.
1094
    this.emit('tx', tx, details);
2,555×
1095
    this.emit('balance', balance);
2,555×
1096

1097
    return details;
2,555×
1098
  }
1099

1100
  /**
1101
   * Attempt to confirm a transaction.
1102
   * @private
1103
   * @param {TXRecord} wtx
1104
   * @param {BlockMeta} block
1105
   * @returns {Promise}
1106
   */
1107

1108
  async confirm(wtx, block) {
1109
    const b = this.bucket.batch();
848×
1110
    const {tx, hash} = wtx;
848×
1111
    const height = block.height;
848×
1112
    const details = new Details(wtx, block);
848×
1113
    const state = new BalanceDelta();
848×
1114
    const view = new CoinView();
848×
1115
    let own = false;
848×
1116

1117
    wtx.setBlock(block);
848×
1118

1119
    if (!tx.isCoinbase()) {
848×
1120
      const credits = await this.getSpentCredits(tx);
644×
1121

1122
      // Potentially spend coins. Now that the tx
1123
      // is mined, we can actually _remove_ coins
1124
      // from the utxo state.
1125
      for (let i = 0; i < tx.inputs.length; i++) {
644×
1126
        const input = tx.inputs[i];
8,145×
1127
        const {hash, index} = input.prevout;
8,145×
1128

1129
        let resolved = false;
8,145×
1130

1131
        // There may be new credits available
1132
        // that we haven't seen yet.
1133
        if (!credits[i]) {
8,145×
1134
          await this.removeInput(b, tx, i);
170×
1135

1136
          const credit = await this.getCredit(hash, index);
170×
1137

1138
          if (!credit)
170×
1139
            continue;
162×
1140

1141
          // Add a spend record and undo coin
1142
          // for the coin we now know is ours.
1143
          // We don't need to remove the coin
1144
          // since it was never added in the
1145
          // first place.
1146
          this.spendCredit(b, credit, tx, i);
8×
1147

1148
          credits[i] = credit;
8×
1149
          resolved = true;
8×
1150
        }
1151

1152
        const credit = credits[i];
7,983×
1153
        const coin = credit.coin;
7,983×
1154

1155
        assert(coin.height !== -1);
7,983×
1156

1157
        const path = await this.getPath(coin);
7,983×
1158
        assert(path);
7,983×
1159
        own = true;
7,983×
1160

1161
        this.unlockBalances(state, coin, path, height);
7,983×
1162

1163
        details.setInput(i, path, coin);
7,983×
1164

1165
        if (resolved) {
7,983×
1166
          state.coin(path, -1);
8×
1167
          state.unconfirmed(path, -coin.value);
8×
1168
        }
1169

1170
        // We can now safely remove the credit
1171
        // entirely, now that we know it's also
1172
        // been removed on-chain.
1173
        state.confirmed(path, -coin.value);
7,983×
1174

1175
        await this.removeCredit(b, credit, path);
7,983×
1176

1177
        view.addCoin(coin);
7,983×
1178
      }
1179
    }
1180

1181
    // Update credit heights, including undo coins.
1182
    for (let i = 0; i < tx.outputs.length; i++) {
848×
1183
      const output = tx.outputs[i];
11,782×
1184
      const path = await this.getPath(output);
11,782×
1185

1186
      if (!path)
11,782×
1187
        continue;
935×
1188

1189
      this.lockBalances(state, output, path, height);
10,847×
1190

1191
      details.setOutput(i, path);
10,847×
1192

1193
      let credit = await this.getCredit(hash, i);
10,847×
1194

1195
      if (!credit) {
10,847×
1196
        // This credit didn't belong to us the first time we
1197
        // saw the transaction (before confirmation or rescan).
1198
        // Create new credit for database.
1199
        credit = Credit.fromTX(tx, i, height);
2×
1200

1201
        // If this tx spent any of our own coins, we "own" this output,
1202
        // meaning if it becomes unconfirmed, we can still confidently spend it.
1203
        credit.own = own;
2×
1204

1205
        // Add coin to "unconfirmed" balance (which includes confirmed coins)
1206
        state.coin(path, 1);
2×
1207
        state.unconfirmed(path, credit.coin.value);
2×
1208
      }
1209

1210
      // Credits spent in the mempool add an
1211
      // undo coin for ease. If this credit is
1212
      // spent in the mempool, we need to
1213
      // update the undo coin's height.
1214
      if (credit.spent)
10,847×
1215
        await this.updateSpentCoin(b, tx, i, height);
164×
1216

1217
      // Update coin height and confirmed
1218
      // balance. Save once again.
1219
      state.confirmed(path, output.value);
10,847×
1220
      credit.coin.height = height;
10,847×
1221

1222
      await this.saveCredit(b, credit, path);
10,847×
1223
    }
1224

1225
    // Handle names.
1226
    await this.connectNames(b, tx, view, height);
848×
1227

1228
    // Save the new serialized transaction as
1229
    // the block-related properties have been
1230
    // updated. Also reindex for height.
1231
    b.put(layout.t.encode(hash), wtx.encode());
848×
1232
    b.del(layout.p.encode(hash));
848×
1233
    b.put(layout.h.encode(height, hash), null);
848×
1234

1235
    // Secondary indexing also needs to change.
1236
    for (const [acct, delta] of state.accounts) {
848×
1237
      await this.updateAccountBalance(b, acct, delta);
866×
1238
      b.del(layout.P.encode(acct, hash));
866×
1239
      b.put(layout.H.encode(acct, height, hash), null);
866×
1240
    }
1241

1242
    await this.removeTXMap(b, hash);
848×
1243
    await this.addBlockMap(b, height);
848×
1244
    await this.addBlock(b, tx.hash(), block);
848×
1245

1246
    // Commit the new state. The balance has updated.
1247
    const balance = await this.updateBalance(b, state);
848×
1248

1249
    await b.write();
848×
1250

1251
    this.unlockTX(tx);
848×
1252

1253
    this.emit('confirmed', tx, details);
848×
1254
    this.emit('balance', balance);
848×
1255

1256
    return details;
848×
1257
  }
1258

1259
  /**
1260
   * Recursively remove a transaction
1261
   * from the database.
1262
   * @param {Hash} hash
1263
   * @returns {Promise}
1264
   */
1265

1266
  async remove(hash) {
1267
    const wtx = await this.getTX(hash);
34×
1268

1269
    if (!wtx)
Branches [[59, 0]] missed. 34×
1270
      return null;
!
1271

1272
    return this.removeRecursive(wtx);
34×
1273
  }
1274

1275
  /**
1276
   * Remove a transaction from the
1277
   * database. Disconnect inputs.
1278
   * @private
1279
   * @param {TXRecord} wtx
1280
   * @returns {Promise}
1281
   */
1282

1283
  async erase(wtx, block) {
1284
    const b = this.bucket.batch();
41×
1285
    const {tx, hash} = wtx;
41×
1286
    const height = block ? block.height : -1;
Branches [[60, 0]] missed. 41×
1287
    const details = new Details(wtx, block);
41×
1288
    const state = new BalanceDelta();
41×
1289

1290
    if (!tx.isCoinbase()) {
Branches [[61, 1]] missed. 41×
1291
      // We need to undo every part of the
1292
      // state this transaction ever touched.
1293
      // Start by getting the undo coins.
1294
      const credits = await this.getSpentCredits(tx);
41×
1295

1296
      for (let i = 0; i < tx.inputs.length; i++) {
41×
1297
        const credit = credits[i];
70×
1298

1299
        if (!credit) {
70×
1300
          if (!block)
Branches [[63, 1]] missed. 7×
1301
            await this.removeInput(b, tx, i);
7×
1302
          continue;
7×
1303
        }
1304

1305
        const coin = credit.coin;
63×
1306
        const path = await this.getPath(coin);
63×
1307
        assert(path);
63×
1308

1309
        details.setInput(i, path, coin);
63×
1310

1311
        // Recalculate the balance, remove
1312
        // from stxo set, remove the undo
1313
        // coin, and resave the credit.
1314
        state.tx(path, -1);
63×
1315
        state.coin(path, 1);
63×
1316
        state.unconfirmed(path, coin.value);
63×
1317

1318
        this.lockBalances(state, coin, path, height);
63×
1319

1320
        if (block)
Branches [[64, 0]] missed. 63×
1321
          state.confirmed(path, coin.value);
!
1322

1323
        this.unspendCredit(b, tx, i);
63×
1324

1325
        credit.spent = false;
63×
1326
        await this.saveCredit(b, credit, path);
63×
1327
      }
1328
    }
1329

1330
    // We need to remove all credits
1331
    // this transaction created.
1332
    for (let i = 0; i < tx.outputs.length; i++) {
41×
1333
      const output = tx.outputs[i];
82×
1334
      const path = await this.getPath(output);
82×
1335

1336
      if (!path)
82×
1337
        continue;
5×
1338

1339
      this.unlockBalances(state, output, path, height);
77×
1340

1341
      details.setOutput(i, path);
77×
1342

1343
      const credit = Credit.fromTX(tx, i, height);
77×
1344

1345
      state.tx(path, -1);
77×
1346
      state.coin(path, -1);
77×
1347
      state.unconfirmed(path, -output.value);
77×
1348

1349
      if (block)
Branches [[66, 0]] missed. 77×
1350
        state.confirmed(path, -output.value);
!
1351

1352
      await this.removeCredit(b, credit, path);
77×
1353
    }
1354

1355
    // Undo name state.
1356
    await this.undoNameState(b, tx);
41×
1357

1358
    if (!block)
Branches [[67, 1]] missed. 41×
1359
      this.unindexOpens(b, tx);
41×
1360

1361
    // Remove the transaction data
1362
    // itself as well as unindex.
1363
    b.del(layout.t.encode(hash));
41×
1364
    b.del(layout.m.encode(wtx.mtime, hash));
41×
1365

1366
    if (!block)
Branches [[68, 1]] missed. 41×
1367
      b.del(layout.p.encode(hash));
41×
1368
    else
1369
      b.del(layout.h.encode(height, hash));
!
1370

1371
    // Remove all secondary indexing.
1372
    for (const [acct, delta] of state.accounts) {
41×
1373
      await this.updateAccountBalance(b, acct, delta);
45×
1374

1375
      b.del(layout.T.encode(acct, hash));
45×
1376
      b.del(layout.M.encode(acct, wtx.mtime, hash));
45×
1377

1378
      if (!block)
Branches [[69, 1]] missed. 45×
1379
        b.del(layout.P.encode(acct, hash));
45×
1380
      else
1381
        b.del(layout.H.encode(acct, height, hash));
!
1382
    }
1383

1384
    // Update block records.
1385
    if (block) {
Branches [[70, 0]] missed. 41×
1386
      await this.removeBlockMap(b, height);
!
1387
      await this.spliceBlock(b, hash, height);
!
1388
    } else {
1389
      await this.removeTXMap(b, hash);
41×
1390
    }
1391

1392
    // Update the transaction counter
1393
    // and commit new state due to
1394
    // balance change.
1395
    const balance = await this.updateBalance(b, state);
41×
1396

1397
    await b.write();
41×
1398

1399
    this.emit('remove tx', tx, details);
41×
1400
    this.emit('balance', balance);
41×
1401

1402
    return details;
41×
1403
  }
1404

1405
  /**
1406
   * Remove a transaction and recursively
1407
   * remove all of its spenders.
1408
   * @private
1409
   * @param {TXRecord} wtx
1410
   * @returns {Promise}
1411
   */
1412

1413
  async removeRecursive(wtx) {
1414
    const {tx, hash} = wtx;
42×
1415

1416
    if (!await this.hasTX(hash))
42×
1417
      return null;
1×
1418

1419
    for (let i = 0; i < tx.outputs.length; i++) {
41×
1420
      const spent = await this.getSpent(hash, i);
82×
1421

1422
      if (!spent)
82×
1423
        continue;
79×
1424

1425
      // Remove all of the spender's spenders first.
1426
      const stx = await this.getTX(spent.hash);
3×
1427

1428
      assert(stx);
3×
1429

1430
      await this.removeRecursive(stx);
3×
1431
    }
1432

1433
    // Remove the spender.
1434
    return this.erase(wtx, wtx.getBlock());
41×
1435
  }
1436

1437
  /**
1438
   * Revert a block.
1439
   * @param {Number} height
1440
   * @returns {Promise}
1441
   */
1442

1443
  async revert(height) {
1444
    const block = await this.getBlock(height);
366×
1445

1446
    if (!block)
Branches [[73, 0]] missed. 366×
1447
      return 0;
!
1448

1449
    const hashes = block.toArray();
366×
1450

1451
    for (let i = hashes.length - 1; i >= 0; i--) {
366×
1452
      const hash = hashes[i];
487×
1453
      await this.unconfirm(hash, height);
487×
1454
    }
1455

1456
    return hashes.length;
366×
1457
  }
1458

1459
  /**
1460
   * Unconfirm a transaction without a batch.
1461
   * @private
1462
   * @param {Hash} hash
1463
   * @param {Number} height
1464
   * @returns {Promise}
1465
   */
1466

1467
  async unconfirm(hash, height) {
1468
    const wtx = await this.getTX(hash);
487×
1469

1470
    if (!wtx) {
487×
1471
      this.logger.spam(
73×
1472
        'Reverting namestate without transaction: %x',
1473
        hash
1474
      );
1475

1476
      const b = this.bucket.batch();
73×
1477

1478
      if (await this.applyNameUndo(b, hash)) {
Branches [[75, 1]] missed. 73×
1479
        await this.removeBlockMap(b, height);
73×
1480
        await this.removeBlock(b, hash, height);
73×
1481

1482
        return b.write();
73×
1483
      }
1484

1485
      return null;
!
1486
    }
1487

1488
    if (wtx.height === -1)
Branches [[76, 0]] missed. 414×
1489
      return null;
!
1490

1491
    return this.disconnect(wtx, wtx.getBlock());
414×
1492
  }
1493

1494
  /**
1495
   * Unconfirm a transaction. Necessary after a reorg.
1496
   * @param {TXRecord} wtx
1497
   * @returns {Promise}
1498
   */
1499

1500
  async disconnect(wtx, block) {
1501
    const b = this.bucket.batch();
414×
1502
    const {tx, hash, height} = wtx;
414×
1503
    const details = new Details(wtx, block);
414×
1504
    const state = new BalanceDelta();
414×
1505

1506
    assert(block);
414×
1507

1508
    wtx.unsetBlock();
414×
1509

1510
    if (!tx.isCoinbase()) {
414×
1511
      // We need to reconnect the coins. Start
1512
      // by getting all of the undo coins we know
1513
      // about.
1514
      const credits = await this.getSpentCredits(tx);
175×
1515

1516
      for (let i = 0; i < tx.inputs.length; i++) {
175×
1517
        const credit = credits[i];
219×
1518

1519
        if (!credit) {
219×
1520
          await this.writeInput(b, tx, i);
9×
1521
          continue;
9×
1522
        }
1523

1524
        const coin = credit.coin;
210×
1525

1526
        assert(coin.height !== -1);
210×
1527

1528
        const path = await this.getPath(coin);
210×
1529
        assert(path);
210×
1530

1531
        this.lockBalances(state, coin, path, height);
210×
1532

1533
        details.setInput(i, path, coin);
210×
1534

1535
        state.confirmed(path, coin.value);
210×
1536

1537
        // Resave the credit and mark it
1538
        // as spent in the mempool instead.
1539
        credit.spent = true;
210×
1540
        await this.saveCredit(b, credit, path);
210×
1541
      }
1542
    }
1543

1544
    // We need to remove heights on
1545
    // the credits and undo coins.
1546
    for (let i = 0; i < tx.outputs.length; i++) {
414×
1547
      const output = tx.outputs[i];
589×
1548
      const path = await this.getPath(output);
589×
1549

1550
      if (!path)
589×
1551
        continue;
11×
1552

1553
      this.unlockBalances(state, output, path, height);
578×
1554

1555
      const credit = await this.getCredit(hash, i);
578×
1556

1557
      // Potentially update undo coin height.
1558
      if (!credit) {
Branches [[80, 0]] missed. 578×
1559
        await this.updateSpentCoin(b, tx, i, height);
!
1560
        continue;
!
1561
      }
1562

1563
      if (credit.spent)
578×
1564
        await this.updateSpentCoin(b, tx, i, height);
150×
1565

1566
      details.setOutput(i, path);
578×
1567

1568
      // Update coin height and confirmed
1569
      // balance. Save once again.
1570
      credit.coin.height = -1;
578×
1571

1572
      state.confirmed(path, -output.value);
578×
1573

1574
      await this.saveCredit(b, credit, path);
578×
1575
    }
1576

1577
    // Undo name state.
1578
    await this.undoNameState(b, tx);
414×
1579

1580
    await this.addTXMap(b, hash);
414×
1581
    await this.removeBlockMap(b, height);
414×
1582
    await this.removeBlock(b, tx.hash(), height);
414×
1583

1584
    // We need to update the now-removed
1585
    // block properties and reindex due
1586
    // to the height change.
1587
    b.put(layout.t.encode(hash), wtx.encode());
414×
1588
    b.put(layout.p.encode(hash), null);
414×
1589
    b.del(layout.h.encode(height, hash));
414×
1590

1591
    // Secondary indexing also needs to change.
1592
    for (const [acct, delta] of state.accounts) {
414×
1593
      await this.updateAccountBalance(b, acct, delta);
420×
1594
      b.put(layout.P.encode(acct, hash), null);
420×
1595
      b.del(layout.H.encode(acct, height, hash));
420×
1596
    }
1597

1598
    // Commit state due to unconfirmed
1599
    // vs. confirmed balance change.
1600
    const balance = await this.updateBalance(b, state);
414×
1601

1602
    await b.write();
414×
1603

1604
    this.emit('unconfirmed', tx, details);
414×
1605
    this.emit('balance', balance);
414×
1606

1607
    return details;
414×
1608
  }
1609

1610
  /**
1611
   * Remove spenders that have not been confirmed. We do this in the
1612
   * odd case of stuck transactions or when a coin is double-spent
1613
   * by a newer transaction. All previously-spending transactions
1614
   * of that coin that are _not_ confirmed will be removed from
1615
   * the database.
1616
   * @private
1617
   * @param {Hash} hash
1618
   * @param {TX} ref - Reference tx, the tx that double-spent.
1619
   * @returns {Promise} - Returns Boolean.
1620
   */
1621

1622
  async removeConflict(wtx) {
1623
    const tx = wtx.tx;
5×
1624

1625
    this.logger.warning('Handling conflicting tx: %x.', tx.hash());
5×
1626

1627
    const details = await this.removeRecursive(wtx);
5×
1628

1629
    this.logger.warning('Removed conflict: %x.', tx.hash());
5×
1630

1631
    // Emit the _removed_ transaction.
1632
    this.emit('conflict', tx, details);
5×
1633

1634
    return details;
5×
1635
  }
1636

1637
  /**
1638
   * Retrieve coins for own inputs, remove
1639
   * double spenders, and verify inputs.
1640
   * @private
1641
   * @param {TX} tx
1642
   * @returns {Promise}
1643
   */
1644

1645
  async removeConflicts(tx, conf) {
1646
    if (tx.isCoinbase())
2,936×
1647
      return true;
1,803×
1648

1649
    const txid = tx.hash();
1,133×
1650
    const spends = [];
1,133×
1651

1652
    // Gather all spent records first.
1653
    for (const {prevout} of tx.inputs) {
1,133×
1654
      const {hash, index} = prevout;
8,890×
1655

1656
      // Is it already spent?
1657
      const spent = await this.getSpent(hash, index);
8,890×
1658

1659
      if (!spent)
8,890×
1660
        continue;
8,885×
1661

1662
      // Did _we_ spend it?
1663
      if (spent.hash.equals(txid))
Branches [[84, 0]] missed. 5×
1664
        continue;
!
1665

1666
      const spender = await this.getTX(spent.hash);
5×
1667
      assert(spender);
5×
1668

1669
      if (conf && spender.height !== -1)
Branches [[85, 0]] missed. 5×
1670
        return false;
!
1671

1672
      spends.push(spender);
5×
1673
    }
1674

1675
    // Once we know we're not going to
1676
    // screw things up, remove the double
1677
    // spenders.
1678
    for (const spender of spends) {
1,133×
1679
      // Remove the double spender.
1680
      await this.removeConflict(spender);
5×
1681
    }
1682

1683
    return true;
1,133×
1684
  }
1685

1686
  /**
1687
   * Lock balances according to covenant.
1688
   * Inserting or confirming: TX outputs.
1689
   * Removing or undoing: Coins spent by the wallet in tx inputs.
1690
   * @param {State} state
1691
   * @param {Coin|Output} coin
1692
   * @param {Path} path
1693
   * @param {Number} height
1694
   */
1695

1696
  lockBalances(state, coin, path, height) {
1697
    const {value, covenant} = coin;
25,701×
1698

1699
    switch (covenant.type) {
25,701×
1700
      case types.CLAIM:    // output is locked until REGISTER
1701
      case types.BID:      // output is locked until REVEAL
1702
      case types.REVEAL:   // output is locked until REDEEM
1703
      case types.REGISTER: // output is now locked or "burned"
1704
      case types.UPDATE:   // output has been locked since REGISTER
1705
      case types.RENEW:
1706
      case types.TRANSFER:
1707
      case types.FINALIZE:
1708
      case types.REVOKE: {
1709
        if (height === -1)
15,031×
1710
          state.ulocked(path, value);
7,496×
1711
        else
1712
          state.clocked(path, value);
7,535×
1713
        break;
15,031×
1714
      }
1715

1716
      case types.REDEEM:   // noop: already unlocked by the REVEAL in the input
1717
        break;
3,180×
1718
    }
1719
  }
1720

1721
  /**
1722
   * Unlock balances according to covenants.
1723
   * Inserting or confirming: Coins spent by the wallet in TX inputs.
1724
   * Removing or undoing: TX outputs.
1725
   * @param {State} state
1726
   * @param {Coin|Output} coin
1727
   * @param {Path} path
1728
   * @param {Number} height
1729
   */
1730

1731
  unlockBalances(state, coin, path, height) {
1732
    const {value, covenant} = coin;
16,841×
1733

1734
    switch (covenant.type) {
16,841×
1735
      case types.CLAIM:    // output is locked until REGISTER
1736
      case types.BID:      // output is locked until REVEAL
1737
      case types.REVEAL:   // output is locked until REDEEM
1738
      case types.REGISTER: // output is now locked or "burned"
1739
      case types.UPDATE:   // output has been locked since REGISTER
1740
      case types.RENEW:
1741
      case types.TRANSFER:
1742
      case types.FINALIZE:
1743
      case types.REVOKE: {
1744
        if (height === -1)
14,845×
1745
          state.ulocked(path, -value);
7,404×
1746
        else
1747
          state.clocked(path, -value);
7,441×
1748
        break;
14,845×
1749
      }
1750

1751
      case types.REDEEM:   // noop: already unlocked by the REVEAL in the input
1752
        break;
9×
1753
    }
1754
  }
1755

1756
  /**
1757
   * Handle incoming covenant.
1758
   * @param {Object} b
1759
   * @param {TX} tx
1760
   * @param {Number} i
1761
   * @param {Path} path
1762
   * @param {Number} height
1763
   * @returns {Object} out
1764
   * @returns {Boolean} out.updated
1765
   * @returns {Boolean} out.index
1766
   */
1767

1768
  async connectNames(b, tx, view, height) {
1769
    const hash = tx.hash();
2,991×
1770
    const network = this.wdb.network;
2,991×
1771

1772
    assert(height !== -1);
2,991×
1773

1774
    // If namestate has been updated we need to write to DB
1775
    let updated = false;
2,991×
1776

1777
    for (let i = 0; i < tx.outputs.length; i++) {
2,991×
1778
      const output = tx.outputs[i];
14,278×
1779
      const {covenant} = output;
14,278×
1780

1781
      if (!covenant.isName())
14,278×
1782
        continue;
3,247×
1783

1784
      const path = await this.getPath(output);
11,031×
1785
      const nameHash = covenant.getHash(0);
11,031×
1786
      const outpoint = tx.outpoint(i);
11,031×
1787
      const ns = await view.getNameState(this, nameHash);
11,031×
1788

1789
      if (!ns.isNull())
11,031×
1790
        ns.maybeExpire(height, network);
10,074×
1791

1792
      switch (covenant.type) {
11,031×
1793
        case types.CLAIM: {
1794
          if (!path)
5×
1795
            break;
1×
1796

1797
          const name = covenant.get(2);
4×
1798
          const flags = covenant.getU8(3);
4×
1799
          const claimed = covenant.getU32(5);
4×
1800

1801
          if (ns.isNull()) {
4×
1802
            await this.addNameMap(b, nameHash);
2×
1803
            ns.set(name, height);
2×
1804
          }
1805

1806
          ns.setHeight(height);
4×
1807
          ns.setRenewal(height);
4×
1808
          ns.setClaimed(claimed);
4×
1809
          ns.setValue(0);
4×
1810
          ns.setOwner(outpoint);
4×
1811
          ns.setHighest(0);
4×
1812
          ns.setWeak((flags & 1) !== 0);
4×
1813

1814
          updated = true;
4×
1815

1816
          break;
4×
1817
        }
1818

1819
        case types.OPEN: {
1820
          if (!path) {
922×
1821
            // Are we "watching" this name?
1822
            const map = await this.wdb.getNameMap(nameHash);
14×
1823
            if (!map || !map.wids.has(this.wid))
Branches [[97, 0]] missed. 14×
1824
              break;
!
1825

1826
            const name = covenant.get(2);
14×
1827
            ns.set(name, height);
14×
1828
            updated = true;
14×
1829
            break;
14×
1830
          }
1831

1832
          if (ns.isNull()) {
908×
1833
            const name = covenant.get(2);
906×
1834

1835
            await this.addNameMap(b, nameHash);
906×
1836

1837
            ns.set(name, height);
906×
1838

1839
            updated = true;
906×
1840
          }
1841

1842
          break;
908×
1843
        }
1844

1845
        case types.BID: {
1846
          const start = covenant.getU32(1);
2,605×
1847
          const name = covenant.get(2);
2,605×
1848
          const blind = covenant.getHash(3);
2,605×
1849
          const lockup = output.value;
2,605×
1850

1851
          if (!path) {
2,605×
1852
            if (ns.isNull())
74×
1853
              break;
6×
1854

1855
            this.putBid(b, nameHash, outpoint, {
68×
1856
              name,
1857
              lockup,
1858
              blind,
1859
              own: false
1860
            });
1861

1862
            updated = true;
68×
1863

1864
            break;
68×
1865
          }
1866

1867
          if (ns.isNull())
2,531×
1868
            await this.addNameMap(b, nameHash);
20×
1869

1870
          ns.set(name, start);
2,531×
1871

1872
          this.putBid(b, nameHash, outpoint, {
2,531×
1873
            name,
1874
            lockup,
1875
            blind,
1876
            own: true
1877
          });
1878

1879
          updated = true;
2,531×
1880

1881
          break;
2,531×
1882
        }
1883

1884
        case types.REVEAL: {
1885
          if (ns.isNull())
2,517×
1886
            break;
1×
1887

1888
          if (ns.owner.isNull() || output.value > ns.highest) {
2,516×
1889
            ns.setValue(ns.highest);
925×
1890
            ns.setOwner(outpoint);
925×
1891
            ns.setHighest(output.value);
925×
1892
          } else if (output.value > ns.value) {
1,591×
1893
            ns.setValue(output.value);
850×
1894
          }
1895

1896
          if (!path) {
2,516×
1897
            this.putReveal(b, nameHash, outpoint, {
49×
1898
              name: ns.name,
1899
              value: output.value,
1900
              height: height,
1901
              own: false
1902
            });
1903
            updated = true;
49×
1904
            break;
49×
1905
          }
1906

1907
          const {prevout} = tx.inputs[i];
2,467×
1908
          const coin = view.getOutput(prevout);
2,467×
1909
          const uc = coin.covenant;
2,467×
1910
          const blind = uc.getHash(3);
2,467×
1911
          const nonce = covenant.getHash(2);
2,467×
1912

1913
          this.putBlind(b, blind, {
2,467×
1914
            value: output.value,
1915
            nonce: nonce
1916
          });
1917

1918
          this.putReveal(b, nameHash, outpoint, {
2,467×
1919
            name: ns.name,
1920
            value: output.value,
1921
            height: height,
1922
            own: true
1923
          });
1924

1925
          updated = true;
2,467×
1926

1927
          break;
2,467×
1928
        }
1929

1930
        case types.REDEEM: {
1931
          break;
1,600×
1932
        }
1933

1934
        case types.REGISTER: {
1935
          if (ns.isNull())
Branches [[108, 0]] missed. 879×
1936
            break;
!
1937

1938
          const data = covenant.get(2);
879×
1939

1940
          ns.setRegistered(true);
879×
1941
          ns.setOwner(outpoint);
879×
1942

1943
          if (data.length > 0)
Branches [[109, 1]] missed. 879×
1944
            ns.setData(data);
879×
1945

1946
          ns.setRenewal(height);
879×
1947

1948
          updated = true;
879×
1949

1950
          break;
879×
1951
        }
1952

1953
        case types.UPDATE: {
1954
          if (ns.isNull())
Branches [[110, 0]] missed. 9×
1955
            break;
!
1956

1957
          const data = covenant.get(2);
9×
1958

1959
          ns.setOwner(outpoint);
9×
1960

1961
          if (data.length > 0)
9×
1962
            ns.setData(data);
5×
1963

1964
          ns.setTransfer(0);
9×
1965

1966
          updated = true;
9×
1967

1968
          break;
9×
1969
        }
1970

1971
        case types.RENEW: {
1972
          if (ns.isNull())
808×
1973
            break;
1×
1974

1975
          ns.setOwner(outpoint);
807×
1976
          ns.setTransfer(0);
807×
1977
          ns.setRenewal(height);
807×
1978
          ns.setRenewals(ns.renewals + 1);
807×
1979

1980
          updated = true;
807×
1981

1982
          break;
807×
1983
        }
1984

1985
        case types.TRANSFER: {
1986
          if (ns.isNull())
Branches [[113, 0]] missed. 841×
1987
            break;
!
1988

1989
          ns.setOwner(outpoint);
841×
1990

1991
          assert(ns.transfer === 0);
841×
1992
          ns.setTransfer(height);
841×
1993

1994
          updated = true;
841×
1995

1996
          break;
841×
1997
        }
1998

1999
        case types.FINALIZE: {
2000
          if (ns.isNull()) {
830×
2001
            if (!path)
Branches [[115, 0]] missed. 6×
2002
              break;
!
2003

2004
            await this.addNameMap(b, nameHash);
6×
2005

2006
            const start = covenant.getU32(1);
6×
2007
            const name = covenant.get(2);
6×
2008
            const flags = covenant.getU8(3);
6×
2009
            const weak = (flags & 1) !== 0;
6×
2010
            const claimed = covenant.getU32(4);
6×
2011
            const renewals = covenant.getU32(5);
6×
2012

2013
            ns.set(name, start);
6×
2014
            ns.setRegistered(true);
6×
2015
            ns.setValue(output.value);
6×
2016
            ns.setWeak(weak);
6×
2017
            ns.setClaimed(claimed);
6×
2018
            ns.setRenewals(renewals);
6×
2019

2020
            // Cannot get data or highest.
2021
            ns.setHighest(output.value);
6×
2022
          } else {
2023
            assert(ns.transfer !== 0);
824×
2024
            ns.setTransfer(0);
824×
2025
          }
2026

2027
          ns.setOwner(tx.outpoint(i));
830×
2028
          ns.setRenewal(height);
830×
2029
          ns.setRenewals(ns.renewals + 1);
830×
2030

2031
          updated = true;
830×
2032

2033
          break;
830×
2034
        }
2035

2036
        case types.REVOKE: {
2037
          if (ns.isNull())
Branches [[116, 0]] missed. 15×
2038
            break;
!
2039

2040
          assert(ns.revoked === 0);
15×
2041
          ns.setRevoked(height);
15×
2042
          ns.setTransfer(0);
15×
2043
          ns.setData(null);
15×
2044

2045
          updated = true;
15×
2046

2047
          break;
15×
2048
        }
2049
      }
2050
    }
2051

2052
    for (const ns of view.names.values()) {
2,991×
2053
      const {nameHash} = ns;
7,150×
2054

2055
      if (ns.isNull()) {
7,150×
2056
        b.del(layout.A.encode(nameHash));
9×
2057
        continue;
9×
2058
      }
2059

2060
      b.put(layout.A.encode(nameHash), ns.encode());
7,141×
2061
    }
2062

2063
    if (updated) {
2,991×
2064
      const undo = view.toNameUndo();
713×
2065

2066
      // Always write the undo record as it
2067
      // serves as a marker to determine whether
2068
      // we have a processed a transaction's
2069
      // covenants before. This function is
2070
      // probably better served by its own
2071
      // special record, but that would require
2072
      // a data migration.
2073
      b.put(layout.U.encode(hash), undo.encode());
713×
2074
    }
2075

2076
    return updated;
2,991×
2077
  }
2078

2079
  /**
2080
   * Handle reorg'd covenant.
2081
   * @param {Object} b
2082
   * @param {TX} tx
2083
   * @param {Number} i
2084
   * @param {Path} path
2085
   * @param {Number} height
2086
   */
2087

2088
  async undoNameState(b, tx) {
2089
    const hash = tx.hash();
455×
2090

2091
    for (let i = 0; i < tx.outputs.length; i++) {
455×
2092
      const output = tx.outputs[i];
671×
2093
      const {covenant} = output;
671×
2094

2095
      if (!covenant.isName())
671×
2096
        continue;
515×
2097

2098
      switch (covenant.type) {
156×
2099
        case types.BID: {
2100
          const nameHash = covenant.getHash(0);
57×
2101
          this.removeBid(b, nameHash, tx.outpoint(i));
57×
2102
          break;
57×
2103
        }
2104
        case types.REVEAL: {
2105
          const nameHash = covenant.getHash(0);
26×
2106
          this.removeReveal(b, nameHash, tx.outpoint(i));
26×
2107
          break;
26×
2108
        }
2109
      }
2110
    }
2111

2112
    return this.applyNameUndo(b, hash);
455×
2113
  }
2114

2115
  /**
2116
   * Apply namestate undo data by hash without transaction.
2117
   * Should only be called directly to undo namestate transitions
2118
   * that do not affect wallet balance like a TRANSFER for a name
2119
   * that is in the nameMap but does not involve wallet addresses.
2120
   * @param {Object} b
2121
   * @param {Hash} hash
2122
   */
2123

2124
  async applyNameUndo(b, hash) {
2125
    const raw = await this.bucket.get(layout.U.encode(hash));
528×
2126

2127
    if (!raw)
528×
2128
      return false;
345×
2129

2130
    const undo = NameUndo.decode(raw);
183×
2131
    const view = new CoinView();
183×
2132

2133
    for (const [nameHash, delta] of undo.names) {
183×
2134
      const ns = await view.getNameState(this, nameHash);
115×
2135

2136
      ns.applyState(delta);
115×
2137

2138
      if (ns.isNull()) {
115×
2139
        // Even though we are removing the namestate from the database,
2140
        // we are going to keep the name in the wallet's "watch list"
2141
        // by NOT calling removeNameMap(). This will preserve names
2142
        // added by rpc importname. The side effect will be potential db bloat,
2143
        // but only in the edge case where an auction we are interested in
2144
        // is re-org'ed completely out of existence. In that case,
2145
        // we will still track the name even though the OPEN has vanished.
2146
        b.del(layout.A.encode(nameHash));
37×
2147
      } else {
2148
        b.put(layout.A.encode(nameHash), ns.encode());
78×
2149
      }
2150
    }
2151

2152
    b.del(layout.U.encode(hash));
183×
2153

2154
    return true;
183×
2155
  }
2156

2157
  /**
2158
   * Lock all coins in a transaction.
2159
   * @param {TX} tx
2160
   */
2161

2162
  lockTX(tx) {
2163
    if (tx.isCoinbase())
Branches [[123, 0], [123, 1]] missed. !
2164
      return;
!
2165

2166
    for (const input of tx.inputs)
!
2167
      this.lockCoin(input.prevout);
!
2168
  }
2169

2170
  /**
2171
   * Unlock all coins in a transaction.
2172
   * @param {TX} tx
2173
   */
2174

2175
  unlockTX(tx) {
2176
    if (tx.isCoinbase())
3,403×
2177
      return;
2,007×
2178

2179
    for (const input of tx.inputs)
1,396×
2180
      this.unlockCoin(input.prevout);
16,440×
2181
  }
2182

2183
  /**
2184
   * Lock a single coin.
2185
   * @param {Coin|Outpoint} coin
2186
   */
2187

2188
  lockCoin(coin) {
2189
    const key = coin.toKey();
!
2190
    this.locked.add(key);
!
2191
  }
2192

2193
  /**
2194
   * Unlock a single coin.
2195
   * @param {Coin|Outpoint} coin
2196
   */
2197

2198
  unlockCoin(coin) {
2199
    const key = coin.toKey();
16,440×
2200
    return this.locked.delete(key);
16,440×
2201
  }
2202

2203
  /**
2204
   * Unlock all locked coins.
2205
   */
2206

2207
  unlockCoins() {
2208
    for (const coin of this.getLocked())
!
2209
      this.unlockCoin(coin);
!
2210
  }
2211

2212
  /**
2213
   * Test locked status of a single coin.
2214
   * @param {Coin|Outpoint} coin
2215
   */
2216

2217
  isLocked(coin) {
2218
    const key = coin.toKey();
170,547×
2219
    return this.locked.has(key);
170,547×
2220
  }
2221

2222
  /**
2223
   * Filter array of coins or outpoints
2224
   * for only unlocked ones.
2225
   * @param {Coin[]|Outpoint[]}
2226
   * @returns {Array}
2227
   */
2228

2229
  filterLocked(coins) {
2230
    const out = [];
587×
2231

2232
    for (const coin of coins) {
587×
2233
      if (!this.isLocked(coin))
Branches [[125, 1]] missed. 170,521×
2234
        out.push(coin);
170,521×
2235
    }
2236

2237
    return out;
587×
2238
  }
2239

2240
  /**
2241
   * Return an array of all locked outpoints.
2242
   * @returns {Outpoint[]}
2243
   */
2244

2245
  getLocked() {
2246
    const outpoints = [];
!
2247

2248
    for (const key of this.locked.keys())
!
2249
      outpoints.push(Outpoint.fromKey(key));
!
2250

2251
    return outpoints;
!
2252
  }
2253

2254
  /**
2255
   * Get hashes of all transactions in the database.
2256
   * @param {Number} acct
2257
   * @returns {Promise} - Returns {@link Hash}[].
2258
   */
2259

2260
  getAccountHistoryHashes(acct) {
2261
    assert(typeof acct === 'number');
!
2262
    return this.bucket.keys({
!
2263
      gte: layout.T.min(acct),
2264
      lte: layout.T.max(acct),
2265
      parse: (key) => {
2266
        const [, hash] = layout.T.decode(key);
!
2267
        return hash;
!
2268
      }
2269
    });
2270
  }
2271

2272
  /**
2273
   * Test whether an account owns a coin.
2274
   * @param {Number} acct
2275
   * @param {Hash} hash
2276
   * @param {Index} number
2277
   * @returns {Promise} - Returns Boolean.
2278
   */
2279

2280
  hasCoinByAccount(acct, hash, index) {
2281
    assert(typeof acct === 'number');
3,513×
2282

2283
    return this.bucket.has(layout.C.encode(acct, hash, index));
3,513×
2284
  }
2285

2286
  /**
2287
   * Get hashes of all transactions in the database.
2288
   * @param {Number} acct
2289
   * @returns {Promise} - Returns {@link Hash}[].
2290
   */
2291

2292
  getHistoryHashes(acct) {
2293
    assert(typeof acct === 'number');
!
2294

2295
    if (acct !== -1)
Branches [[126, 0], [126, 1]] missed. !
2296
      return this.getAccountHistoryHashes(acct);
!
2297

2298
    return this.bucket.keys({
!
2299
      gte: layout.t.min(),
2300
      lte: layout.t.max(),
2301
      parse: key => layout.t.decode(key)[0]
!
2302
    });
2303
  }
2304

2305
  /**
2306
   * Get hashes of all unconfirmed transactions in the database.
2307
   * @param {Number} acct
2308
   * @returns {Promise} - Returns {@link Hash}[].
2309
   */
2310

2311
  getAccountPendingHashes(acct) {
2312
    assert(typeof acct === 'number');
!
2313
    return this.bucket.keys({
!
2314
      gte: layout.P.min(acct),
2315
      lte: layout.P.max(acct),
2316
      parse: (key) => {
2317
        const [, hash] = layout.P.decode(key);
!
2318
        return hash;
!
2319
      }
2320
    });
2321
  }
2322

2323
  /**
2324
   * Get hashes of all unconfirmed transactions in the database.
2325
   * @param {Number} acct
2326
   * @returns {Promise} - Returns {@link Hash}[].
2327
   */
2328

2329
  getPendingHashes(acct) {
2330
    assert(typeof acct === 'number');
3×
2331

2332
    if (acct !== -1)
Branches [[127, 0]] missed. 3×
2333
      return this.getAccountPendingHashes(acct);
!
2334

2335
    return this.bucket.keys({
3×
2336
      gte: layout.p.min(),
2337
      lte: layout.p.max(),
2338
      parse: key => layout.p.decode(key)[0]
50×
2339
    });
2340
  }
2341

2342
  /**
2343
   * Test whether the database has a pending transaction.
2344
   * @param {Hash} hash
2345
   * @returns {Promise} - Returns Boolean.
2346
   */
2347

2348
  async hasPending(hash) {
2349
    return this.bucket.has(layout.p.encode(hash));
9,426×
2350
  }
2351

2352
  /**
2353
   * Get all coin hashes in the database.
2354
   * @param {Number} acct
2355
   * @returns {Promise} - Returns {@link Hash}[].
2356
   */
2357

2358
  getAccountOutpoints(acct) {
2359
    assert(typeof acct === 'number');
116×
2360
    return this.bucket.keys({
116×
2361
      gte: layout.C.min(acct),
2362
      lte: layout.C.max(acct),
2363
      parse: (key) => {
2364
        const [, hash, index] = layout.C.decode(key);
2,589×
2365
        return new Outpoint(hash, index);
2,589×
2366
      }
2367
    });
2368
  }
2369

2370
  /**
2371
   * Get all coin hashes in the database.
2372
   * @param {Number} acct
2373
   * @returns {Promise} - Returns {@link Hash}[].
2374
   */
2375

2376
  getOutpoints(acct) {
2377
    assert(typeof acct === 'number');
116×
2378

2379
    if (acct !== -1)
Branches [[128, 1]] missed. 116×
2380
      return this.getAccountOutpoints(acct);
116×
2381

2382
    return this.bucket.keys({
!
2383
      gte: layout.c.min(),
2384
      lte: layout.c.max(),
2385
      parse: (key) => {
2386
        const [hash, index] = layout.c.decode(key);
!
2387
        return new Outpoint(hash, index);
!
2388
      }
2389
    });
2390
  }
2391

2392
  /**
2393
   * Get TX hashes by height range.
2394
   * @param {Number} acct
2395
   * @param {Object} options
2396
   * @param {Number} options.start - Start height.
2397
   * @param {Number} options.end - End height.
2398
   * @param {Number?} options.limit - Max number of records.
2399
   * @param {Boolean?} options.reverse - Reverse order.
2400
   * @returns {Promise} - Returns {@link Hash}[].
2401
   */
2402

2403
  getAccountHeightRangeHashes(acct, options) {
2404
    assert(typeof acct === 'number');
!
2405

2406
    const start = options.start || 0;
Branches [[129, 0], [129, 1]] missed. !
2407
    const end = options.end || 0xffffffff;
Branches [[130, 0], [130, 1]] missed. !
2408

2409
    return this.bucket.keys({
!
2410
      gte: layout.H.min(acct, start),
2411
      lte: layout.H.max(acct, end),
2412
      limit: options.limit,
2413
      reverse: options.reverse,
2414
      parse: (key) => {
2415
        const [,, hash] = layout.H.decode(key);
!
2416
        return hash;
!
2417
      }
2418
    });
2419
  }
2420

2421
  /**
2422
   * Get TX hashes by height range.
2423
   * @param {Number} acct
2424
   * @param {Object} options
2425
   * @param {Number} options.start - Start height.
2426
   * @param {Number} options.end - End height.
2427
   * @param {Number?} options.limit - Max number of records.
2428
   * @param {Boolean?} options.reverse - Reverse order.
2429
   * @returns {Promise} - Returns {@link Hash}[].
2430
   */
2431

2432
  getHeightRangeHashes(acct, options) {
2433
    assert(typeof acct === 'number');
!
2434

2435
    if (acct !== -1)
Branches [[131, 0], [131, 1]] missed. !
2436
      return this.getAccountHeightRangeHashes(acct, options);
!
2437

2438
    const start = options.start || 0;
Branches [[132, 0], [132, 1]] missed. !
2439
    const end = options.end || 0xffffffff;
Branches [[133, 0], [133, 1]] missed. !
2440

2441
    return this.bucket.keys({
!
2442
      gte: layout.h.min(start),
2443
      lte: layout.h.max(end),
2444
      limit: options.limit,
2445
      reverse: options.reverse,
2446
      parse: (key) => {
2447
        const [, hash] = layout.h.decode(key);
!
2448
        return hash;
!
2449
      }
2450
    });
2451
  }
2452

2453
  /**
2454
   * Get TX hashes by height.
2455
   * @param {Number} height
2456
   * @returns {Promise} - Returns {@link Hash}[].
2457
   */
2458

2459
  getHeightHashes(height) {
2460
    return this.getHeightRangeHashes({ start: height, end: height });
!
2461
  }
2462

2463
  /**
2464
   * Get TX hashes by timestamp range.
2465
   * @param {Number} acct
2466
   * @param {Object} options
2467
   * @param {Number} options.start - Start height.
2468
   * @param {Number} options.end - End height.
2469
   * @param {Number?} options.limit - Max number of records.
2470
   * @param {Boolean?} options.reverse - Reverse order.
2471
   * @returns {Promise} - Returns {@link Hash}[].
2472
   */
2473

2474
  getAccountRangeHashes(acct, options) {
2475
    assert(typeof acct === 'number');
22×
2476

2477
    const start = options.start || 0;
22×
2478
    const end = options.end || 0xffffffff;
22×
2479

2480
    return this.bucket.keys({
22×
2481
      gte: layout.M.min(acct, start),
2482
      lte: layout.M.max(acct, end),
2483
      limit: options.limit,
2484
      reverse: options.reverse,
2485
      parse: (key) => {
2486
        const [,, hash] = layout.M.decode(key);
76×
2487
        return hash;
76×
2488
      }
2489
    });
2490
  }
2491

2492
  /**
2493
   * Get TX hashes by timestamp range.
2494
   * @param {Number} acct
2495
   * @param {Object} options
2496
   * @param {Number} options.start - Start height.
2497
   * @param {Number} options.end - End height.
2498
   * @param {Number?} options.limit - Max number of records.
2499
   * @param {Boolean?} options.reverse - Reverse order.
2500
   * @returns {Promise} - Returns {@link Hash}[].
2501
   */
2502

2503
  getRangeHashes(acct, options) {
2504
    assert(typeof acct === 'number');
23×
2505

2506
    if (acct !== -1)
23×
2507
      return this.getAccountRangeHashes(acct, options);
22×
2508

2509
    const start = options.start || 0;
Branches [[137, 1]] missed. 1×
2510
    const end = options.end || 0xffffffff;
1×
2511

2512
    return this.bucket.keys({
1×
2513
      gte: layout.m.min(start),
2514
      lte: layout.m.max(end),
2515
      limit: options.limit,
2516
      reverse: options.reverse,
2517
      parse: (key) => {
2518
        const [, hash] = layout.m.decode(key);
2×
2519
        return hash;
2×
2520
      }
2521
    });
2522
  }
2523

2524
  /**
2525
   * Get transactions by timestamp range.
2526
   * @param {Number} acct
2527
   * @param {Object} options
2528
   * @param {Number} options.start - Start time.
2529
   * @param {Number} options.end - End time.
2530
   * @param {Number?} options.limit - Max number of records.
2531
   * @param {Boolean?} options.reverse - Reverse order.
2532
   * @returns {Promise} - Returns {@link TX}[].
2533
   */
2534

2535
  async getRange(acct, options) {
2536
    const hashes = await this.getRangeHashes(acct, options);
23×
2537
    const txs = [];
23×
2538

2539
    for (const hash of hashes) {
23×
2540
      const tx = await this.getTX(hash);
78×
2541
      assert(tx);
78×
2542
      txs.push(tx);
78×
2543
    }
2544

2545
    return txs;
23×
2546
  }
2547

2548
  /**
2549
   * Get last N transactions.
2550
   * @param {Number} acct
2551
   * @param {Number} limit - Max number of transactions.
2552
   * @returns {Promise} - Returns {@link TX}[].
2553
   */
2554

2555
  getLast(acct, limit) {
2556
    return this.getRange(acct, {
!
2557
      start: 0,
2558
      end: 0xffffffff,
2559
      reverse: true,
2560
      limit: limit || 10
Branches [[139, 0], [139, 1]] missed.
2561
    });
2562
  }
2563

2564
  /**
2565
   * Get all transactions.
2566
   * @param {Number} acct
2567
   * @returns {Promise} - Returns {@link TX}[].
2568
   */
2569

2570
  getHistory(acct) {
2571
    assert(typeof acct === 'number');
16×
2572

2573
    // Slow case
2574
    if (acct !== -1)
Branches [[140, 0]] missed. 16×
2575
      return this.getAccountHistory(acct);
!
2576

2577
    // Fast case
2578
    return this.bucket.values({
16×
2579
      gte: layout.t.min(),
2580
      lte: layout.t.max(),
2581
      parse: data => TXRecord.decode(data)
495×
2582
    });
2583
  }
2584

2585
  /**
2586
   * Get all acct transactions.
2587
   * @param {Number} acct
2588
   * @returns {Promise} - Returns {@link TX}[].
2589
   */
2590

2591
  async getAccountHistory(acct) {
2592
    const hashes = await this.getHistoryHashes(acct);
!
2593
    const txs = [];
!
2594

2595
    for (const hash of hashes) {
!
2596
      const tx = await this.getTX(hash);
!
2597
      assert(tx);
!
2598
      txs.push(tx);
!
2599
    }
2600

2601
    return txs;
!
2602
  }
2603

2604
  /**
2605
   * Get unconfirmed transactions.
2606
   * @param {Number} acct
2607
   * @returns {Promise} - Returns {@link TX}[].
2608
   */
2609

2610
  async getPending(acct) {
2611
    const hashes = await this.getPendingHashes(acct);
3×
2612
    const txs = [];
3×
2613

2614
    for (const hash of hashes) {
3×
2615
      const tx = await this.getTX(hash);
50×
2616
      assert(tx);
50×
2617
      txs.push(tx);
50×
2618
    }
2619

2620
    return txs;
3×
2621
  }
2622

2623
  /**
2624
   * Get coins.
2625
   * @param {Number} acct
2626
   * @returns {Promise} - Returns {@link Coin}[].
2627
   */
2628

2629
  getCredits(acct) {
2630
    assert(typeof acct === 'number');
596×
2631

2632
    // Slow case
2633
    if (acct !== -1)
596×
2634
      return this.getAccountCredits(acct);
116×
2635

2636
    // Fast case
2637
    return this.bucket.range({
480×
2638
      gte: layout.c.min(),
2639
      lte: layout.c.max(),
2640
      parse: (key, value) => {
2641
        const [hash, index] = layout.c.decode(key);
175,767×
2642
        const credit = Credit.decode(value);
175,767×
2643
        credit.coin.hash = hash;
175,767×
2644
        credit.coin.index = index;
175,767×
2645
        return credit;
175,767×
2646
      }
2647
    });
2648
  }
2649

2650
  /**
2651
   * Get coins by account.
2652
   * @param {Number} acct
2653
   * @returns {Promise} - Returns {@link Coin}[].
2654
   */
2655

2656
  async getAccountCredits(acct) {
2657
    const outpoints = await this.getOutpoints(acct);
116×
2658
    const credits = [];
116×
2659

2660
    for (const {hash, index} of outpoints) {
116×
2661
      const credit = await this.getCredit(hash, index);
2,589×
2662
      assert(credit);
2,589×
2663
      credits.push(credit);
2,589×
2664
    }
2665

2666
    return credits;
116×
2667
  }
2668

2669
  /**
2670
   * Fill a transaction with coins (all historical coins).
2671
   * @param {TX} tx
2672
   * @returns {Promise} - Returns {@link TX}.
2673
   */
2674

2675
  async getSpentCredits(tx) {
2676
    if (tx.isCoinbase())
Branches [[142, 0]] missed. 879×
2677
      return [];
!
2678

2679
    const hash = tx.hash();
879×
2680
    const credits = [];
879×
2681

2682
    for (let i = 0; i < tx.inputs.length; i++)
879×
2683
      credits.push(null);
8,467×
2684

2685
    await this.bucket.range({
879×
2686
      gte: layout.d.min(hash),
2687
      lte: layout.d.max(hash),
2688
      parse: (key, value) => {
2689
        const [, index] = layout.d.decode(key);
8,276×
2690
        const coin = Coin.decode(value);
8,276×
2691
        const input = tx.inputs[index];
8,276×
2692
        assert(input);
8,276×
2693
        coin.hash = input.prevout.hash;
8,276×
2694
        coin.index = input.prevout.index;
8,276×
2695
        credits[index] = new Credit(coin);
8,276×
2696
      }
2697
    });
2698

2699
    return credits;
879×
2700
  }
2701

2702
  /**
2703
   * Get coins.
2704
   * @param {Number} acct
2705
   * @returns {Promise} - Returns {@link Coin}[].
2706
   */
2707

2708
  async getCoins(acct) {
2709
    const credits = await this.getCredits(acct);
592×
2710
    const coins = [];
592×
2711

2712
    for (const credit of credits) {
592×
2713
      if (credit.spent)
178,328×
2714
        continue;
7,023×
2715

2716
      coins.push(credit.coin);
171,305×
2717
    }
2718

2719
    return coins;
592×
2720
  }
2721

2722
  /**
2723
   * Get coins by account.
2724
   * @param {Number} acct
2725
   * @returns {Promise} - Returns {@link Coin}[].
2726
   */
2727

2728
  async getAccountCoins(acct) {
2729
    const credits = await this.getAccountCredits(acct);
!
2730
    const coins = [];
!
2731

2732
    for (const credit of credits) {
!
2733
      if (credit.spent)
Branches [[144, 0], [144, 1]] missed. !
2734
        continue;
!
2735

2736
      coins.push(credit.coin);
!
2737
    }
2738

2739
    return coins;
!
2740
  }
2741

2742
  /**
2743
   * Get historical coins for a transaction.
2744
   * @param {TX} tx
2745
   * @returns {Promise} - Returns {@link TX}.
2746
   */
2747

2748
  async getSpentCoins(tx) {
2749
    if (tx.isCoinbase())
146×
2750
      return [];
127×
2751

2752
    const credits = await this.getSpentCredits(tx);
19×
2753
    const coins = [];
19×
2754

2755
    for (const credit of credits) {
19×
2756
      if (!credit) {
33×
2757
        coins.push(null);
5×
2758
        continue;
5×
2759
      }
2760

2761
      coins.push(credit.coin);
28×
2762
    }
2763

2764
    return coins;
19×
2765
  }
2766

2767
  /**
2768
   * Get a coin viewpoint.
2769
   * @param {TX} tx
2770
   * @returns {Promise} - Returns {@link CoinView}.
2771
   */
2772

2773
  async getCoinView(tx) {
2774
    const view = new CoinView();
!
2775

2776
    if (tx.isCoinbase())
Branches [[147, 0], [147, 1]] missed. !
2777
      return view;
!
2778

2779
    for (const {prevout} of tx.inputs) {
!
2780
      const {hash, index} = prevout;
!
2781
      const coin = await this.getCoin(hash, index);
!
2782

2783
      if (!coin)
Branches [[148, 0], [148, 1]] missed. !
2784
        continue;
!
2785

2786
      view.addCoin(coin);
!
2787
    }
2788

2789
    return view;
!
2790
  }
2791

2792
  /**
2793
   * Get historical coin viewpoint.
2794
   * @param {TX} tx
2795
   * @returns {Promise} - Returns {@link CoinView}.
2796
   */
2797

2798
  async getSpentView(tx) {
2799
    const view = new CoinView();
!
2800

2801
    if (tx.isCoinbase())
Branches [[149, 0], [149, 1]] missed. !
2802
      return view;
!
2803

2804
    const coins = await this.getSpentCoins(tx);
!
2805

2806
    for (const coin of coins) {
!
2807
      if (!coin)
Branches [[150, 0], [150, 1]] missed. !
2808
        continue;
!
2809

2810
      view.addCoin(coin);
!
2811
    }
2812

2813
    return view;
!
2814
  }
2815

2816
  /**
2817
   * Get transaction.
2818
   * @param {Hash} hash
2819
   * @returns {Promise} - Returns {@link TX}.
2820
   */
2821

2822
  async getTX(hash) {
2823
    const raw = await this.bucket.get(layout.t.encode(hash));
6,512×
2824

2825
    if (!raw)
6,512×
2826
      return null;
3,017×
2827

2828
    return TXRecord.decode(raw);
3,495×
2829
  }
2830

2831
  /**
2832
   * Get transaction details.
2833
   * @param {Hash} hash
2834
   * @returns {Promise} - Returns {@link TXDetails}.
2835
   */
2836

2837
  async getDetails(hash) {
2838
    const wtx = await this.getTX(hash);
3×
2839

2840
    if (!wtx)
Branches [[152, 0]] missed. 3×
2841
      return null;
!
2842

2843
    return this.toDetails(wtx);
3×
2844
  }
2845

2846
  /**
2847
   * Convert transaction to transaction details.
2848
   * @param {TXRecord[]} wtxs
2849
   * @returns {Promise}
2850
   */
2851

2852
  async toDetails(wtxs) {
2853
    const out = [];
8×
2854

2855
    if (!Array.isArray(wtxs))
8×
2856
      return this._toDetails(wtxs);
4×
2857

2858
    for (const wtx of wtxs) {
4×
2859
      const details = await this._toDetails(wtx);
142×
2860

2861
      if (!details)
Branches [[154, 0]] missed. 142×
2862
        continue;
!
2863

2864
      out.push(details);
142×
2865
    }
2866

2867
    return out;
4×
2868
  }
2869

2870
  /**
2871
   * Convert transaction to transaction details.
2872
   * @private
2873
   * @param {TXRecord} wtx
2874
   * @returns {Promise}
2875
   */
2876

2877
  async _toDetails(wtx) {
2878
    const tx = wtx.tx;
146×
2879
    const block = wtx.getBlock();
146×
2880
    const details = new Details(wtx, block);
146×
2881
    const coins = await this.getSpentCoins(tx);
146×
2882

2883
    for (let i = 0; i < tx.inputs.length; i++) {
146×
2884
      const coin = coins[i];
160×
2885

2886
      let path = null;
160×
2887

2888
      if (coin)
160×
2889
        path = await this.getPath(coin);
28×
2890

2891
      details.setInput(i, path, coin);
160×
2892
    }
2893

2894
    for (let i = 0; i < tx.outputs.length; i++) {
146×
2895
      const output = tx.outputs[i];
200×
2896
      const path = await this.getPath(output);
200×
2897
      details.setOutput(i, path);
200×
2898
    }
2899

2900
    return details;
146×
2901
  }
2902

2903
  /**
2904
   * Test whether the database has a transaction.
2905
   * @param {Hash} hash
2906
   * @returns {Promise} - Returns Boolean.
2907
   */
2908

2909
  hasTX(hash) {
2910
    return this.bucket.has(layout.t.encode(hash));
43×
2911
  }
2912

2913
  /**
2914
   * Get coin.
2915
   * @param {Hash} hash
2916
   * @param {Number} index
2917
   * @returns {Promise} - Returns {@link Coin}.
2918
   */
2919

2920
  async getCoin(hash, index) {
2921
    const credit = await this.getCredit(hash, index);
4,512×
2922

2923
    if (!credit)
4,512×
2924
      return null;
25×
2925

2926
    return credit.coin;
4,487×
2927
  }
2928

2929
  /**
2930
   * Get coin.
2931
   * @param {Hash} hash
2932
   * @param {Number} index
2933
   * @returns {Promise} - Returns {@link Coin}.
2934
   */
2935

2936
  async getCredit(hash, index) {
2937
    const data = await this.bucket.get(layout.c.encode(hash, index));
79,925×
2938

2939
    if (!data)
79,925×
2940
      return null;
36,655×
2941

2942
    const credit = Credit.decode(data);
43,270×
2943
    credit.coin.hash = hash;
43,270×
2944
    credit.coin.index = index;
43,270×
2945

2946
    return credit;
43,270×
2947
  }
2948

2949
  /**
2950
   * Get spender coin.
2951
   * @param {Outpoint} spent
2952
   * @param {Outpoint} prevout
2953
   * @returns {Promise} - Returns {@link Coin}.
2954
   */
2955

2956
  async getSpentCoin(spent, prevout) {
2957
    const key = layout.d.encode(spent.hash, spent.index);
314×
2958
    const data = await this.bucket.get(key);
314×
2959

2960
    if (!data)
Branches [[158, 0]] missed. 314×
2961
      return null;
!
2962

2963
    const coin = Coin.decode(data);
314×
2964
    coin.hash = prevout.hash;
314×
2965
    coin.index = prevout.index;
314×
2966

2967
    return coin;
314×
2968
  }
2969

2970
  /**
2971
   * Test whether the database has a spent coin.
2972
   * @param {Outpoint} spent
2973
   * @returns {Promise} - Returns {@link Coin}.
2974
   */
2975

2976
  hasSpentCoin(spent) {
2977
    return this.bucket.has(layout.d.encode(spent.hash, spent.index));
!
2978
  }
2979

2980
  /**
2981
   * Update spent coin height in storage.
2982
   * @param {TX} tx - Sending transaction.
2983
   * @param {Number} index
2984
   * @param {Number} height
2985
   * @returns {Promise}
2986
   */
2987

2988
  async updateSpentCoin(b, tx, index, height) {
2989
    const prevout = Outpoint.fromTX(tx, index);
314×
2990
    const spent = await this.getSpent(prevout.hash, prevout.index);
314×
2991

2992
    if (!spent)
Branches [[159, 0]] missed. 314×
2993
      return;
!
2994

2995
    const coin = await this.getSpentCoin(spent, prevout);
314×
2996

2997
    if (!coin)
Branches [[160, 0]] missed. 314×
2998
      return;
!
2999

3000
    coin.height = height;
314×
3001

3002
    b.put(layout.d.encode(spent.hash, spent.index), coin.encode());
314×
3003
  }
3004

3005
  /**
3006
   * Test whether the database has a transaction.
3007
   * @param {Hash} hash
3008
   * @returns {Promise} - Returns Boolean.
3009
   */
3010

3011
  async hasCoin(hash, index) {
3012
    return this.bucket.has(layout.c.encode(hash, index));
!
3013
  }
3014

3015
  /**
3016
   * Calculate balance.
3017
   * @param {Number?} account
3018
   * @returns {Promise} - Returns {@link Balance}.
3019
   */
3020

3021
  async getBalance(acct) {
3022
    assert(typeof acct === 'number');
266×
3023

3024
    if (acct !== -1)
266×
3025
      return this.getAccountBalance(acct);
120×
3026

3027
    return this.getWalletBalance();
146×
3028
  }
3029

3030
  /**
3031
   * Calculate balance.
3032
   * @returns {Promise} - Returns {@link Balance}.
3033
   */
3034

3035
  async getWalletBalance() {
3036
    const data = await this.bucket.get(layout.R.encode());
4,004×
3037

3038
    if (!data)
4,004×
3039
      return new Balance();
117×
3040

3041
    return Balance.decode(data);
3,887×
3042
  }
3043

3044
  /**
3045
   * Calculate balance by account.
3046
   * @param {Number} acct
3047
   * @returns {Promise} - Returns {@link Balance}.
3048
   */
3049

3050
  async getAccountBalance(acct) {
3051
    const data = await this.bucket.get(layout.r.encode(acct));
4,024×
3052

3053
    if (!data)
4,024×
3054
      return new Balance(acct);
119×
3055

3056
    const balance = Balance.decode(data);
3,905×
3057
    balance.account = acct;
3,905×
3058
    return balance;
3,905×
3059
  }
3060

3061
  /**
3062
   * Zap pending transactions older than `age`.
3063
   * @param {Number} acct
3064
   * @param {Number} age - Age delta.
3065
   * @returns {Promise}
3066
   */
3067

3068
  async zap(acct, age) {
3069
    assert((age >>> 0) === age);
19×
3070

3071
    const now = util.now();
19×
3072

3073
    const txs = await this.getRange(acct, {
19×
3074
      start: 0,
3075
      end: now - age
3076
    });
3077

3078
    const hashes = [];
19×
3079

3080
    for (const wtx of txs) {
19×
3081
      if (wtx.height !== -1)
70×
3082
        continue;
53×
3083

3084
      assert(now - wtx.mtime >= age);
17×
3085

3086
      this.logger.debug('Zapping TX: %x (%d)',
17×
3087
        wtx.tx.hash(), this.wid);
3088

3089
      await this.remove(wtx.hash);
17×
3090

3091
      hashes.push(wtx.hash);
17×
3092
    }
3093

3094
    return hashes;
19×
3095
  }
3096

3097
  /**
3098
   * Abandon transaction.
3099
   * @param {Hash} hash
3100
   * @returns {Promise}
3101
   */
3102

3103
  async abandon(hash) {
3104
    const result = await this.bucket.has(layout.p.encode(hash));
17×
3105

3106
    if (!result)
Branches [[165, 0]] missed. 17×
3107
      throw new Error('TX not eligible.');
!
3108

3109
    return this.remove(hash);
17×
3110
  }
3111
}
3112

3113
/**
3114
 * Balance
3115
 * @alias module:wallet.Balance
3116
 */
3117

3118
class Balance extends bio.Struct {
3119
  /**
3120
   * Create a balance.
3121
   * @constructor
3122
   * @param {Number} account
3123
   */
3124

3125
  constructor(acct = -1) {
3126
    super();
16,171×
3127

3128
    assert(typeof acct === 'number');
16,171×
3129

3130
    this.account = acct;
16,171×
3131
    this.tx = 0;
16,171×
3132
    this.coin = 0;
16,171×
3133
    this.unconfirmed = 0;
16,171×
3134
    this.confirmed = 0;
16,171×
3135
    this.ulocked = 0;
16,171×
3136
    this.clocked = 0;
16,171×
3137
  }
3138

3139
  /**
3140
   * Apply delta.
3141
   * @param {Balance} balance
3142
   */
3143

3144
  applyTo(balance) {
3145
    balance.tx += this.tx;
7,762×
3146
    balance.coin += this.coin;
7,762×
3147
    balance.unconfirmed += this.unconfirmed;
7,762×
3148
    balance.confirmed += this.confirmed;
7,762×
3149
    balance.ulocked += this.ulocked;
7,762×
3150
    balance.clocked += this.clocked;
7,762×
3151

3152
    assert(balance.tx >= 0);
7,762×
3153
    assert(balance.coin >= 0);
7,762×
3154
    assert(balance.unconfirmed >= 0);
7,762×
3155
    assert(balance.confirmed >= 0);
7,762×
3156
    assert(balance.ulocked >= 0);
7,762×
3157
    assert(balance.clocked >= 0);
7,762×
3158
  }
3159

3160
  /**
3161
   * Calculate size.
3162
   * @returns {Number}
3163
   */
3164

3165
  getSize() {
3166
    return 48;
7,762×
3167
  }
3168

3169
  /**
3170
   * Serialize balance.
3171
   * @returns {Buffer}
3172
   */
3173

3174
  write(bw) {
3175
    bw.writeU64(this.tx);
7,762×
3176
    bw.writeU64(this.coin);
7,762×
3177
    bw.writeU64(this.unconfirmed);
7,762×
3178
    bw.writeU64(this.confirmed);
7,762×
3179
    bw.writeU64(this.ulocked);
7,762×
3180
    bw.writeU64(this.clocked);
7,762×
3181
    return bw;
7,762×
3182
  }
3183

3184
  /**
3185
   * Inject properties from serialized data.
3186
   * @private
3187
   * @param {Buffer} data
3188
   * @returns {TXDBState}
3189
   */
3190

3191
  read(br) {
3192
    this.tx = br.readU64();
7,792×
3193
    this.coin = br.readU64();
7,792×
3194
    this.unconfirmed = br.readU64();
7,792×
3195
    this.confirmed = br.readU64();
7,792×
3196
    this.ulocked = br.readU64();
7,792×
3197
    this.clocked = br.readU64();
7,792×
3198
    return this;
7,792×
3199
  }
3200

3201
  /**
3202
   * Convert balance to a more json-friendly object.
3203
   * @param {Boolean?} minimal
3204
   * @returns {Object}
3205
   */
3206

3207
  getJSON(minimal) {
3208
    return {
166×
3209
      account: !minimal ? this.account : undefined,
Branches [[167, 1]] missed.
3210
      tx: this.tx,
3211
      coin: this.coin,
3212
      unconfirmed: this.unconfirmed,
3213
      confirmed: this.confirmed,
3214
      lockedUnconfirmed: this.ulocked,
3215
      lockedConfirmed: this.clocked
3216
    };
3217
  }
3218

3219
  /**
3220
   * Inspect balance.
3221
   * @param {String}
3222
   */
3223

3224
  format() {
3225
    return '<Balance'
!
3226
      + ` tx=${this.tx}`
3227
      + ` coin=${this.coin}`
3228
      + ` unconfirmed=${Amount.coin(this.unconfirmed)}`
3229
      + ` confirmed=${Amount.coin(this.confirmed)}`
3230
      + ` lockedUnconfirmed=${Amount.coin(this.ulocked)}`
3231
      + ` lockedConfirmed=${Amount.coin(this.clocked)}`
3232
      + '>';
3233
  }
3234
}
3235

3236
/**
3237
 * Balance Delta
3238
 * @ignore
3239
 */
3240

3241
class BalanceDelta {
3242
  /**
3243
   * Create a balance delta.
3244
   * @constructor
3245
   */
3246

3247
  constructor() {
3248
    this.wallet = new Balance();
4,239×
3249
    this.accounts = new Map();
4,239×
3250
  }
3251

3252
  updated() {
3253
    return this.wallet.tx !== 0;
3,198×
3254
  }
3255

3256
  applyTo(balance) {
3257
    this.wallet.applyTo(balance);
3,858×
3258
  }
3259

3260
  get(path) {
3261
    if (!this.accounts.has(path.account))
113,990×
3262
      this.accounts.set(path.account, new Balance());
3,904×
3263

3264
    return this.accounts.get(path.account);
113,990×
3265
  }
3266

3267
  tx(path, value) {
3268
    const account = this.get(path);
20,776×
3269
    account.tx = value;
20,776×
3270
    this.wallet.tx = value;
20,776×
3271
  }
3272

3273
  coin(path, value) {
3274
    const account = this.get(path);
20,786×
3275
    account.coin += value;
20,786×
3276
    this.wallet.coin += value;
20,786×
3277
  }
3278

3279
  unconfirmed(path, value) {
3280
    const account = this.get(path);
20,786×
3281
    account.unconfirmed += value;
20,786×
3282
    this.wallet.unconfirmed += value;
20,786×
3283
  }
3284

3285
  confirmed(path, value) {
3286
    const account = this.get(path);
21,766×
3287
    account.confirmed += value;
21,766×
3288
    this.wallet.confirmed += value;
21,766×
3289
  }
3290

3291
  ulocked(path, value) {
3292
    const account = this.get(path);
14,900×
3293
    account.ulocked += value;
14,900×
3294
    this.wallet.ulocked += value;
14,900×
3295
  }
3296

3297
  clocked(path, value) {
3298
    const account = this.get(path);
14,976×
3299
    account.clocked += value;
14,976×
3300
    this.wallet.clocked += value;
14,976×
3301
  }
3302
}
3303

3304
/**
3305
 * Credit (wrapped coin)
3306
 * @alias module:wallet.Credit
3307
 * @property {Coin} coin
3308
 * @property {Boolean} spent
3309
 */
3310

3311
class Credit extends bio.Struct {
3312
  /**
3313
   * Create a credit.
3314
   * @constructor
3315
   * @param {Coin} coin
3316
   * @param {Boolean?} spent
3317
   */
3318

3319
  constructor(coin, spent) {
3320
    super();
239,959×
3321
    this.coin = coin || new Coin();
239,959×
3322
    this.spent = spent || false;
239,959×
3323
    this.own = false;
239,959×
3324
  }
3325

3326
  /**
3327
   * Inject properties from serialized data.
3328
   * @private
3329
   * @param {Buffer} data
3330
   */
3331

3332
  read(br) {
3333
    this.coin.read(br);
219,037×
3334
    this.spent = br.readU8() === 1;
219,037×
3335
    this.own = br.readU8() === 1;
219,037×
3336
    return this;
219,037×
3337
  }
3338

3339
  /**
3340
   * Get serialization size.
3341
   * @returns {Number}
3342
   */
3343

3344
  getSize() {
3345
    return this.coin.getSize() + 2;
32,200×
3346
  }
3347

3348
  /**
3349
   * Serialize credit.
3350
   * @returns {Buffer}
3351
   */
3352

3353
  write(bw) {
3354
    this.coin.write(bw);
32,200×
3355
    bw.writeU8(this.spent ? 1 : 0);
32,200×
3356
    bw.writeU8(this.own ? 1 : 0);
32,200×
3357
    return bw;
32,200×
3358
  }
3359

3360
  /**
3361
   * Inject properties from tx object.
3362
   * @private
3363
   * @param {TX} tx
3364
   * @param {Number} index
3365
   * @returns {Credit}
3366
   */
3367

3368
  fromTX(tx, index, height) {
3369
    this.coin.fromTX(tx, index, height);
12,646×
3370
    this.spent = false;
12,646×
3371
    this.own = false;
12,646×
3372
    return this;
12,646×
3373
  }
3374

3375
  /**
3376
   * Instantiate credit from transaction.
3377
   * @param {TX} tx
3378
   * @param {Number} index
3379
   * @returns {Credit}
3380
   */
3381

3382
  static fromTX(tx, index, height) {
3383
    return new this().fromTX(tx, index, height);
12,646×
3384
  }
3385
}
3386

3387
/**
3388
 * Transaction Details
3389
 * @alias module:wallet.Details
3390
 */
3391

3392
class Details {
3393
  /**
3394
   * Create transaction details.
3395
   * @constructor
3396
   * @param {TXRecord} wtx
3397
   * @param {BlockMeta} block
3398
   */
3399

3400
  constructor(wtx, block) {
3401
    this.hash = wtx.hash;
4,385×
3402
    this.tx = wtx.tx;
4,385×
3403
    this.mtime = wtx.mtime;
4,385×
3404
    this.size = this.tx.getSize();
4,385×
3405
    this.vsize = this.tx.getVirtualSize();
4,385×
3406

3407
    this.block = null;
4,385×
3408
    this.height = -1;
4,385×
3409
    this.time = 0;
4,385×
3410

3411
    if (block) {
4,385×
3412
      this.block = block.hash;
3,546×
3413
      this.height = block.height;
3,546×
3414
      this.time = block.time;
3,546×
3415
    }
3416

3417
    this.inputs = [];
4,385×
3418
    this.outputs = [];
4,385×
3419

3420
    this.init();
4,385×
3421
  }
3422

3423
  /**
3424
   * Initialize transaction details.
3425
   * @private
3426
   */
3427

3428
  init() {
3429
    for (const input of this.tx.inputs) {
4,385×
3430
      const member = new DetailsMember();
19,735×
3431
      member.address = input.getAddress();
19,735×
3432
      this.inputs.push(member);
19,735×
3433
    }
3434

3435
    for (const output of this.tx.outputs) {
4,385×
3436
      const member = new DetailsMember();
27,049×
3437
      member.value = output.value;
27,049×
3438
      member.address = output.getAddress();
27,049×
3439
      member.covenant = output.covenant;
27,049×
3440
      this.outputs.push(member);
27,049×
3441
    }
3442
  }
3443

3444
  /**
3445
   * Add necessary info to input member.
3446
   * @param {Number} i
3447
   * @param {Path} path
3448
   * @param {Coin} coin
3449
   */
3450

3451
  setInput(i, path, coin) {
3452
    const member = this.inputs[i];
16,485×
3453

3454
    if (coin) {
16,485×
3455
      member.value = coin.value;
16,353×
3456
      member.address = coin.getAddress();
16,353×
3457
    }
3458

3459
    if (path)
16,485×
3460
      member.path = path;
16,353×
3461
  }
3462

3463
  /**
3464
   * Add necessary info to output member.
3465
   * @param {Number} i
3466
   * @param {Path} path
3467
   */
3468

3469
  setOutput(i, path) {
3470
    const member = this.outputs[i];
24,269×
3471

3472
    if (path)
24,269×
3473
      member.path = path;
24,257×
3474
  }
3475

3476
  /**
3477
   * Calculate confirmations.
3478
   * @returns {Number}
3479
   */
3480

3481
  getDepth(height) {
3482
    if (this.height === -1)
121×
3483
      return 0;
8×
3484

3485
    if (height == null)
Branches [[178, 0]] missed. 113×
3486
      return 0;
!
3487

3488
    const depth = height - this.height;
113×
3489

3490
    if (depth < 0)
Branches [[179, 0]] missed. 113×
3491
      return 0;
!
3492

3493
    return depth + 1;
113×
3494
  }
3495

3496
  /**
3497
   * Calculate fee. Only works if wallet
3498
   * owns all inputs. Returns 0 otherwise.
3499
   * @returns {Amount}
3500
   */
3501

3502
  getFee() {
3503
    let inputValue = 0;
121×
3504
    let outputValue = 0;
121×
3505

3506
    for (const input of this.inputs) {
121×
3507
      if (!input.path)
121×
3508
        return 0;
114×
3509

3510
      inputValue += input.value;
7×
3511
    }
3512

3513
    for (const output of this.outputs)
7×
3514
      outputValue += output.value;
14×
3515

3516
    return inputValue - outputValue;
7×
3517
  }
3518

3519
  /**
3520
   * Calculate fee rate. Only works if wallet
3521
   * owns all inputs. Returns 0 otherwise.
3522
   * @param {Amount} fee
3523
   * @returns {Rate}
3524
   */
3525

3526
  getRate(fee) {
3527
    return policy.getRate(this.vsize, fee);
121×
3528
  }
3529

3530
  /**
3531
   * Convert details to a more json-friendly object.
3532
   * @returns {Object}
3533
   */
3534

3535
  getJSON(network, height) {
3536
    const fee = this.getFee();
121×
3537
    const rate = this.getRate(fee);
121×
3538

3539
    return {
121×
3540
      hash: this.hash.toString('hex'),
3541
      height: this.height,
3542
      block: this.block ? this.block.toString('hex') : null,
3543
      time: this.time,
3544
      mtime: this.mtime,
3545
      date: util.date(this.time),
3546
      mdate: util.date(this.mtime),
3547
      size: this.size,
3548
      virtualSize: this.vsize,
3549
      fee: fee,
3550
      rate: rate,
3551
      confirmations: this.getDepth(height),
3552
      inputs: this.inputs.map((input) => {
3553
        return input.getJSON(network);
121×
3554
      }),
3555
      outputs: this.outputs.map((output) => {
3556
        return output.getJSON(network);
133×
3557
      }),
3558
      tx: this.tx.toHex()
3559
    };
3560
  }
3561

3562
  /**
3563
   * Convert details to a more json-friendly object.
3564
   * @returns {Object}
3565
   */
3566

3567
  toJSON() {
3568
    return this.getJSON();
1×
3569
  }
3570
}
3571

3572
/**
3573
 * Transaction Details Member
3574
 * @property {Number} value
3575
 * @property {Address} address
3576
 * @property {Path} path
3577
 */
3578

3579
class DetailsMember {
3580
  /**
3581
   * Create details member.
3582
   * @constructor
3583
   */
3584

3585
  constructor() {
3586
    this.value = 0;
46,784×
3587
    this.address = null;
46,784×
3588
    this.covenant = null;
46,784×
3589
    this.path = null;
46,784×
3590
  }
3591

3592
  /**
3593
   * Convert the member to a more json-friendly object.
3594
   * @returns {Object}
3595
   */
3596

3597
  toJSON() {
3598
    return this.getJSON();
!
3599
  }
3600

3601
  /**
3602
   * Convert the member to a more json-friendly object.
3603
   * @param {Network} network
3604
   * @returns {Object}
3605
   */
3606

3607
  getJSON(network) {
3608
    return {
254×
3609
      value: this.value,
3610
      address: this.address
3611
        ? this.address.toString(network)
3612
        : null,
3613
      covenant: this.covenant
3614
        ? this.covenant.toJSON()
3615
        : undefined,
3616
      path: this.path
3617
        ? this.path.toJSON()
3618
        : null
3619
    };
3620
  }
3621
}
3622

3623
/**
3624
 * Block Record
3625
 * @alias module:wallet.BlockRecord
3626
 */
3627

3628
class BlockRecord extends bio.Struct {
3629
  /**
3630
   * Create a block record.
3631
   * @constructor
3632
   * @param {Hash} hash
3633
   * @param {Number} height
3634
   * @param {Number} time
3635
   */
3636

3637
  constructor(hash, height, time) {
3638
    super();
2,906×
3639
    this.hash = hash || consensus.ZERO_HASH;
2,906×
3640
    this.height = height != null ? height : -1;
Branches [[186, 0]] missed. 2,906×
3641
    this.time = time || 0;
2,906×
3642
    this.hashes = new BufferSet();
2,906×
3643
  }
3644

3645
  /**
3646
   * Add transaction to block record.
3647
   * @param {Hash} hash
3648
   * @returns {Boolean}
3649
   */
3650

3651
  add(hash) {
3652
    if (this.hashes.has(hash))
Branches [[188, 0]] missed. 2,540×
3653
      return false;
!
3654

3655
    this.hashes.add(hash);
2,540×
3656

3657
    return true;
2,540×
3658
  }
3659

3660
  /**
3661
   * Remove transaction from block record.
3662
   * @param {Hash} hash
3663
   * @returns {Boolean}
3664
   */
3665

3666
  remove(hash) {
3667
    return this.hashes.delete(hash);
!
3668
  }
3669

3670
  /**
3671
   * Instantiate wallet block from serialized tip data.
3672
   * @private
3673
   * @param {Buffer} data
3674
   */
3675

3676
  read(br) {
3677
    this.hash = br.readHash();
366×
3678
    this.height = br.readU32();
366×
3679
    this.time = br.readU32();
366×
3680

3681
    const count = br.readU32();
366×
3682

3683
    for (let i = 0; i < count; i++) {
366×
3684
      const hash = br.readHash();
487×
3685
      this.hashes.add(hash);
487×
3686
    }
3687

3688
    return this;
366×
3689
  }
3690

3691
  /**
3692
   * Get serialization size.
3693
   * @returns {Number}
3694
   */
3695

3696
  getSize() {
3697
    return 44 + this.hashes.size * 32;
2,540×
3698
  }
3699

3700
  /**
3701
   * Serialize the wallet block as a tip (hash and height).
3702
   * @returns {Buffer}
3703
   */
3704

3705
  write(bw) {
3706
    bw.writeHash(this.hash);
2,540×
3707
    bw.writeU32(this.height);
2,540×
3708
    bw.writeU32(this.time);
2,540×
3709

3710
    bw.writeU32(this.hashes.size);
2,540×
3711

3712
    for (const hash of this.hashes)
2,540×
3713
      bw.writeHash(hash);
2,540×
3714

3715
    return bw;
2,540×
3716
  }
3717

3718
  /**
3719
   * Convert hashes set to an array.
3720
   * @returns {Hash[]}
3721
   */
3722

3723
  toArray() {
3724
    return this.hashes.toArray();
366×
3725
  }
3726

3727
  /**
3728
   * Convert the block to a more json-friendly object.
3729
   * @returns {Object}
3730
   */
3731

3732
  getJSON() {
3733
    return {
!
3734
      hash: this.hash.toString('hex'),
3735
      height: this.height,
3736
      time: this.time,
3737
      hashes: this.toArray().map(h => h.toString('hex'))
!
3738
    };
3739
  }
3740

3741
  /**
3742
   * Instantiate wallet block from block meta.
3743
   * @private
3744
   * @param {BlockMeta} block
3745
   */
3746

3747
  fromMeta(block) {
3748
    this.hash = block.hash;
2,540×
3749
    this.height = block.height;
2,540×
3750
    this.time = block.time;
2,540×
3751
    return this;
2,540×
3752
  }
3753

3754
  /**
3755
   * Instantiate wallet block from block meta.
3756
   * @param {BlockMeta} block
3757
   * @returns {BlockRecord}
3758
   */
3759

3760
  static fromMeta(block) {
3761
    return new this().fromMeta(block);
2,540×
3762
  }
3763
}
3764

3765
/**
3766
 * Blind Bid
3767
 */
3768

3769
class BlindBid extends bio.Struct {
3770
  constructor() {
3771
    super();
66,902×
3772
    this.name = EMPTY;
66,902×
3773
    this.nameHash = consensus.ZERO_HASH;
66,902×
3774
    this.prevout = new Outpoint();
66,902×
3775
    this.value = -1;
66,902×
3776
    this.lockup = 0;
66,902×
3777
    this.blind = consensus.ZERO_HASH;
66,902×
3778
    this.own = false;
66,902×
3779
  }
3780

3781
  getSize() {
3782
    return 1 + this.name.length + 41;
2,599×
3783
  }
3784

3785
  write(bw) {
3786
    bw.writeU8(this.name.length);
2,599×
3787
    bw.writeBytes(this.name);
2,599×
3788
    bw.writeU64(this.lockup);
2,599×
3789
    bw.writeBytes(this.blind);
2,599×
3790
    bw.writeU8(this.own ? 1 : 0);
2,599×
3791
    return bw;
2,599×
3792
  }
3793

3794
  read(br) {
3795
    this.name = br.readBytes(br.readU8());
64,303×
3796
    this.lockup = br.readU64();
64,303×
3797
    this.blind = br.readBytes(32);
64,303×
3798
    this.own = br.readU8() === 1;
64,303×
3799
    return this;
64,303×
3800
  }
3801

3802
  getJSON() {
3803
    return {
17×
3804
      name: this.name.toString('ascii'),
3805
      nameHash: this.nameHash.toString('hex'),
3806
      prevout: this.prevout.toJSON(),
3807
      value: this.value === -1 ? undefined : this.value,
3808
      lockup: this.lockup,
3809
      blind: this.blind.toString('hex'),
3810
      own: this.own
3811
    };
3812
  }
3813
}
3814

3815
/**
3816
 * Blind Value
3817
 */
3818

3819
class BlindValue extends bio.Struct {
3820
  constructor() {
3821
    super();
73,213×
3822
    this.value = 0;
73,213×
3823
    this.nonce = consensus.ZERO_HASH;
73,213×
3824
  }
3825

3826
  getSize() {
3827
    return 40;
5,133×
3828
  }
3829

3830
  write(bw) {
3831
    bw.writeU64(this.value);
5,133×
3832
    bw.writeBytes(this.nonce);
5,133×
3833
    return bw;
5,133×
3834
  }
3835

3836
  read(br) {
3837
    this.value = br.readU64();
68,080×
3838
    this.nonce = br.readBytes(32);
68,080×
3839
    return this;
68,080×
3840
  }
3841

3842
  getJSON() {
3843
    return {
!
3844
      value: this.value,
3845
      nonce: this.nonce.toString('hex')
3846
    };
3847
  }
3848
}
3849

3850
/**
3851
 * Bid Reveal
3852
 */
3853

3854
class BidReveal extends bio.Struct {
3855
  constructor() {
3856
    super();
59,563×
3857
    this.name = EMPTY;
59,563×
3858
    this.nameHash = consensus.ZERO_HASH;
59,563×
3859
    this.prevout = new Outpoint();
59,563×
3860
    this.value = 0;
59,563×
3861
    this.height = -1;
59,563×
3862
    this.own = false;
59,563×
3863
  }
3864

3865
  getSize() {
3866
    return 1 + this.name.length + 13;
2,516×
3867
  }
3868

3869
  write(bw) {
3870
    let height = this.height;
2,516×
3871

3872
    if (height === -1)
Branches [[191, 0]] missed. 2,516×
3873
      height = 0xffffffff;
!
3874

3875
    bw.writeU8(this.name.length);
2,516×
3876
    bw.writeBytes(this.name);
2,516×
3877
    bw.writeU64(this.value);
2,516×
3878
    bw.writeU32(height);
2,516×
3879
    bw.writeU8(this.own ? 1 : 0);
2,516×
3880

3881
    return bw;
2,516×
3882
  }
3883

3884
  read(br) {
3885
    this.name = br.readBytes(br.readU8());
57,047×
3886
    this.value = br.readU64();
57,047×
3887
    this.height = br.readU32();
57,047×
3888
    this.own = br.readU8() === 1;
57,047×
3889

3890
    if (this.height === 0xffffffff)
Branches [[193, 0]] missed. 57,047×
3891
      this.height = -1;
!
3892

3893
    return this;
57,047×
3894
  }
3895

3896
  getJSON() {
3897
    return {
9×
3898
      name: this.name.toString('ascii'),
3899
      nameHash: this.nameHash.toString('hex'),
3900
      prevout: this.prevout.toJSON(),
3901
      value: this.value,
3902
      height: this.height,
3903
      own: this.own
3904
    };
3905
  }
3906
}
3907

3908
/*
3909
 * Expose
3910
 */
3911

3912
module.exports = TXDB;
1×
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2023 Coveralls, Inc