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

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

7
'use strict';
8

9
const assert = require('bsert');
58×
10
const bio = require('bufio');
58×
11
const util = require('../utils/util');
58×
12
const rules = require('../covenants/rules');
58×
13
const consensus = require('../protocol/consensus');
58×
14
const {encoding} = bio;
58×
15
const {types, typesByVal} = rules;
58×
16

17
/**
18
 * Covenant
19
 * @alias module:primitives.Covenant
20
 * @property {Number} type
21
 * @property {Buffer[]} items
22
 * @property {Number} length
23
 */
24

25
class Covenant extends bio.Struct {
26
  /**
27
   * Create a covenant.
28
   * @constructor
29
   */
30

31
  constructor(type, items) {
32
    super();
998,825×
33

34
    this.type = types.NONE;
998,825×
35
    this.items = [];
998,825×
36

37
    if (type != null)
998,825×
38
      this.fromOptions(type, items);
21×
39
  }
40

41
  /**
42
   * Inject properties from options object.
43
   * @private
44
   * @param {Object} options
45
   */
46

47
  fromOptions(type, items) {
48
    if (type && typeof type === 'object') {
39×
49
      items = type.items;
19×
50
      type = type.type;
19×
51
    }
52

53
    if (Array.isArray(type))
Branches [[3, 0]] missed. 39×
54
      return this.fromArray(type);
!
55

56
    if (type != null) {
Branches [[4, 1]] missed. 39×
57
      assert((type & 0xff) === type);
39×
58
      this.type = type;
39×
59
      if (items)
Branches [[5, 1]] missed. 39×
60
        return this.fromArray(items);
39×
61
      return this;
!
62
    }
63

64
    return this;
!
65
  }
66

67
  /**
68
   * Get an item.
69
   * @param {Number} index
70
   * @returns {Buffer}
71
   */
72

73
  get(index) {
74
    if (index < 0)
Branches [[6, 0]] missed. 356,294×
75
      index += this.items.length;
!
76

77
    assert((index >>> 0) === index);
356,294×
78
    assert(index < this.items.length);
356,294×
79

80
    return this.items[index];
356,294×
81
  }
82

83
  /**
84
   * Set an item.
85
   * @param {Number} index
86
   * @param {Buffer} item
87
   * @returns {Buffer}
88
   */
89

90
  set(index, item) {
91
    if (index < 0)
Branches [[7, 0], [7, 1]] missed. !
92
      index += this.items.length;
!
93

94
    assert((index >>> 0) === index);
!
95
    assert(index <= this.items.length);
!
96
    assert(Buffer.isBuffer(item));
!
97

98
    this.items[index] = item;
!
99
    return this;
!
100
  }
101

102
  /**
103
   * Push an item.
104
   * @param {Buffer} item
105
   */
106

107
  push(item) {
108
    assert(Buffer.isBuffer(item));
72,591×
109
    this.items.push(item);
72,591×
110
    return this;
72,591×
111
  }
112

113
  /**
114
   * Get a uint8.
115
   * @param {Number} index
116
   * @returns {Number}
117
   */
118

119
  getU8(index) {
120
    const item = this.get(index);
5,025×
121
    assert(item.length === 1);
5,025×
122
    return item[0];
5,025×
123
  }
124

125
  /**
126
   * Push a uint8.
127
   * @param {Number} num
128
   */
129

130
  pushU8(num) {
131
    assert((num & 0xff) === num);
2,280×
132
    const item = Buffer.allocUnsafe(1);
2,280×
133
    item[0] = num;
2,280×
134
    this.push(item);
2,280×
135
    return this;
2,280×
136
  }
137

138
  /**
139
   * Get a uint32.
140
   * @param {Number} index
141
   * @returns {Number}
142
   */
143

144
  getU32(index) {
145
    const item = this.get(index);
64,279×
146
    assert(item.length === 4);
64,279×
147
    return bio.readU32(item, 0);
64,279×
148
  }
149

150
  /**
151
   * Push a uint32.
152
   * @param {Number} num
153
   */
154

155
  pushU32(num) {
156
    assert((num >>> 0) === num);
23,136×
157
    const item = Buffer.allocUnsafe(4);
23,136×
158
    bio.writeU32(item, num, 0);
23,136×
159
    this.push(item);
23,136×
160
    return this;
23,136×
161
  }
162

163
  /**
164
   * Get a hash.
165
   * @param {Number} index
166
   * @returns {Buffer}
167
   */
168

169
  getHash(index) {
170
    const item = this.get(index);
270,916×
171
    assert(item.length === 32);
270,916×
172
    return item;
270,916×
173
  }
174

175
  /**
176
   * Push a hash.
177
   * @param {Buffer} hash
178
   */
179

180
  pushHash(hash) {
181
    assert(Buffer.isBuffer(hash));
34,210×
182
    assert(hash.length === 32);
34,210×
183
    this.push(hash);
34,210×
184
    return this;
34,210×
185
  }
186

187
  /**
188
   * Get a string.
189
   * @param {Number} index
190
   * @returns {String}
191
   */
192

193
  getString(index) {
194
    const item = this.get(index);
!
195
    assert(item.length >= 1 && item.length <= 63);
Branches [[8, 0], [8, 1]] missed. !
196
    return item.toString('binary');
!
197
  }
198

199
  /**
200
   * Push a string.
201
   * @param {String} str
202
   */
203

204
  pushString(str) {
205
    assert(typeof str === 'string');
9×
206
    assert(str.length >= 1 && str.length <= 63);
9×
207
    this.push(Buffer.from(str, 'binary'));
9×
208
    return this;
9×
209
  }
210

211
  /**
212
   * Test whether the covenant is known.
213
   * @returns {Boolean}
214
   */
215

216
  isKnown() {
217
    return this.type <= types.REVOKE;
!
218
  }
219

220
  /**
221
   * Test whether the covenant is unknown.
222
   * @returns {Boolean}
223
   */
224

225
  isUnknown() {
226
    return this.type > types.REVOKE;
!
227
  }
228

229
  /**
230
   * Test whether the covenant is a payment.
231
   * @returns {Boolean}
232
   */
233

234
  isNone() {
235
    return this.type === types.NONE;
87×
236
  }
237

238
  /**
239
   * Test whether the covenant is a claim.
240
   * @returns {Boolean}
241
   */
242

243
  isClaim() {
244
    return this.type === types.CLAIM;
113,227×
245
  }
246

247
  /**
248
   * Test whether the covenant is an open.
249
   * @returns {Boolean}
250
   */
251

252
  isOpen() {
253
    return this.type === types.OPEN;
1,273,833×
254
  }
255

256
  /**
257
   * Test whether the covenant is a bid.
258
   * @returns {Boolean}
259
   */
260

261
  isBid() {
262
    return this.type === types.BID;
13,759×
263
  }
264

265
  /**
266
   * Test whether the covenant is a reveal.
267
   * @returns {Boolean}
268
   */
269

270
  isReveal() {
271
    return this.type === types.REVEAL;
13,469×
272
  }
273

274
  /**
275
   * Test whether the covenant is a redeem.
276
   * @returns {Boolean}
277
   */
278

279
  isRedeem() {
280
    return this.type === types.REDEEM;
25×
281
  }
282

283
  /**
284
   * Test whether the covenant is a register.
285
   * @returns {Boolean}
286
   */
287

288
  isRegister() {
289
    return this.type === types.REGISTER;
98,286×
290
  }
291

292
  /**
293
   * Test whether the covenant is an update.
294
   * @returns {Boolean}
295
   */
296

297
  isUpdate() {
298
    return this.type === types.UPDATE;
1,507×
299
  }
300

301
  /**
302
   * Test whether the covenant is a renewal.
303
   * @returns {Boolean}
304
   */
305

306
  isRenew() {
307
    return this.type === types.RENEW;
1,414×
308
  }
309

310
  /**
311
   * Test whether the covenant is a transfer.
312
   * @returns {Boolean}
313
   */
314

315
  isTransfer() {
316
    return this.type === types.TRANSFER;
31×
317
  }
318

319
  /**
320
   * Test whether the covenant is a finalize.
321
   * @returns {Boolean}
322
   */
323

324
  isFinalize() {
325
    return this.type === types.FINALIZE;
5×
326
  }
327

328
  /**
329
   * Test whether the covenant is a revocation.
330
   * @returns {Boolean}
331
   */
332

333
  isRevoke() {
334
    return this.type === types.REVOKE;
!
335
  }
336

337
  /**
338
   * Test whether the covenant is name-related.
339
   * @returns {Boolean}
340
   */
341

342
  isName() {
343
    if (this.type < types.CLAIM)
314,787×
344
      return false;
199,960×
345

346
    if (this.type > types.REVOKE)
Branches [[11, 0]] missed. 114,827×
347
      return false;
!
348

349
    return true;
114,827×
350
  }
351

352
  /**
353
   * Test whether a covenant type should be
354
   * considered subject to the dust policy rule.
355
   * @returns {Boolean}
356
   */
357

358
  isDustworthy() {
359
    switch (this.type) {
18,491×
360
      case types.NONE:
361
      case types.BID:
362
        return true;
10,308×
363
      default:
364
        return this.type > types.REVOKE;
8,183×
365
    }
366
  }
367

368
  /**
369
   * Test whether a coin should be considered
370
   * unspendable in the coin selector.
371
   * @returns {Boolean}
372
   */
373

374
  isNonspendable() {
375
    switch (this.type) {
8,543×
376
      case types.NONE:
377
      case types.OPEN:
378
      case types.REDEEM:
379
        return false;
8,524×
380
      default:
381
        return true;
19×
382
    }
383
  }
384

385
  /**
386
   * Test whether a covenant should be considered "linked".
387
   * @returns {Boolean}
388
   */
389

390
  isLinked() {
391
    return this.type >= types.REVEAL && this.type <= types.REVOKE;
20,245×
392
  }
393

394
  /**
395
   * Convert covenant to an array of buffers.
396
   * @returns {Buffer[]}
397
   */
398

399
  toArray() {
400
    return this.items.slice();
!
401
  }
402

403
  /**
404
   * Inject properties from an array of buffers.
405
   * @private
406
   * @param {Buffer[]} items
407
   */
408

409
  fromArray(items) {
410
    assert(Array.isArray(items));
39×
411
    this.items = items;
39×
412
    return this;
39×
413
  }
414

415
  /**
416
   * Test whether the covenant is unspendable.
417
   * @returns {Boolean}
418
   */
419

420
  isUnspendable() {
421
    return this.type === types.REVOKE;
182,076×
422
  }
423

424
  /**
425
   * Convert the covenant to a string.
426
   * @returns {String}
427
   */
428

429
  toString() {
430
    return this.encode().toString('hex', 1);
!
431
  }
432

433
  /**
434
   * Inject properties from covenant.
435
   * Used for cloning.
436
   * @private
437
   * @param {Covenant} covenant
438
   * @returns {Covenant}
439
   */
440

441
  inject(covenant) {
442
    assert(covenant instanceof this.constructor);
149,199×
443
    this.type = covenant.type;
149,199×
444
    this.items = covenant.items.slice();
149,199×
445
    return this;
149,199×
446
  }
447

448
  /**
449
   * Test the covenant against a bloom filter.
450
   * @param {Bloom} filter
451
   * @returns {Boolean}
452
   */
453

454
  test(filter) {
455
    for (const item of this.items) {
1,598×
456
      if (item.length === 0)
168×
457
        continue;
1×
458

459
      if (filter.test(item))
167×
460
        return true;
51×
461
    }
462

463
    return false;
1,547×
464
  }
465

466
  /**
467
   * Find a data element in a covenant.
468
   * @param {Buffer} data - Data element to match against.
469
   * @returns {Number} Index (`-1` if not present).
470
   */
471

472
  indexOf(data) {
473
    for (let i = 0; i < this.items.length; i++) {
!
474
      const item = this.items[i];
!
475
      if (item.equals(data))
Branches [[17, 0], [17, 1]] missed. !
476
        return i;
!
477
    }
478
    return -1;
!
479
  }
480

481
  /**
482
   * Calculate size of the covenant
483
   * excluding the varint size bytes.
484
   * @returns {Number}
485
   */
486

487
  getSize() {
488
    let size = 0;
6,464,315×
489

490
    for (const item of this.items)
6,464,315×
491
      size += encoding.sizeVarBytes(item);
16,785,643×
492

493
    return size;
6,464,315×
494
  }
495

496
  /**
497
   * Calculate size of the covenant
498
   * including the varint size bytes.
499
   * @returns {Number}
500
   */
501

502
  getVarSize() {
503
    return 1 + encoding.sizeVarint(this.items.length) + this.getSize();
6,464,315×
504
  }
505

506
  /**
507
   * Write covenant to a buffer writer.
508
   * @param {BufferWriter} bw
509
   */
510

511
  write(bw) {
512
    bw.writeU8(this.type);
4,950,601×
513
    bw.writeVarint(this.items.length);
4,950,601×
514

515
    for (const item of this.items)
4,950,601×
516
      bw.writeVarBytes(item);
12,701,350×
517

518
    return bw;
4,950,601×
519
  }
520

521
  /**
522
   * Encode covenant.
523
   * @returns {Buffer}
524
   */
525

526
  encode() {
527
    const bw = bio.write(this.getVarSize());
!
528
    this.write(bw);
!
529
    return bw.render();
!
530
  }
531

532
  /**
533
   * Convert covenant to a hex string.
534
   * @returns {String}
535
   */
536

537
  getJSON() {
538
    const items = [];
784×
539

540
    for (const item of this.items)
784×
541
      items.push(item.toString('hex'));
844×
542

543
    return {
784×
544
      type: this.type,
545
      action: typesByVal[this.type],
546
      items
547
    };
548
  }
549

550
  /**
551
   * Inject properties from json object.
552
   * @private
553
   * @param {String} json
554
   */
555

556
  fromJSON(json) {
557
    assert(json && typeof json === 'object', 'Covenant must be an object.');
92×
558
    assert((json.type & 0xff) === json.type);
92×
559
    assert(Array.isArray(json.items));
92×
560

561
    this.type = json.type;
92×
562

563
    for (const str of json.items) {
92×
564
      const item = util.parseHex(str, -1);
83×
565
      this.items.push(item);
83×
566
    }
567

568
    return this;
92×
569
  }
570

571
  /**
572
   * Inject properties from buffer reader.
573
   * @private
574
   * @param {BufferReader} br
575
   */
576

577
  read(br) {
578
    this.type = br.readU8();
449,794×
579

580
    const count = br.readVarint();
449,794×
581

582
    if (count > consensus.MAX_SCRIPT_STACK)
Branches [[19, 0]] missed. 449,794×
583
      throw new Error('Too many covenant items.');
!
584

585
    for (let i = 0; i < count; i++)
449,794×
586
      this.items.push(br.readVarBytes());
894,816×
587

588
    return this;
449,794×
589
  }
590

591
  /**
592
   * Inject items from string.
593
   * @private
594
   * @param {String|String[]} items
595
   */
596

597
  fromString(items) {
598
    if (!Array.isArray(items)) {
Branches [[20, 0], [20, 1]] missed. !
599
      assert(typeof items === 'string');
!
600

601
      items = items.trim();
!
602

603
      if (items.length === 0)
Branches [[21, 0], [21, 1]] missed. !
604
        return this;
!
605

606
      items = items.split(/\s+/);
!
607
    }
608

609
    for (const item of items)
!
610
      this.items.push(util.parseHex(item, -1));
!
611

612
    return this;
!
613
  }
614

615
  /**
616
   * Inspect a covenant object.
617
   * @returns {String}
618
   */
619

620
  format() {
NEW
621
    const type = typesByVal[this.type] || this.type;
Branches [[22, 0], [22, 1]] missed. !
NEW
622
    return `<Covenant: ${type}:${this.toString()}>`;
!
623
  }
624

625
  /**
626
   * Insantiate covenant from an array of buffers.
627
   * @param {Buffer[]} items
628
   * @returns {Covenant}
629
   */
630

631
  static fromArray(items) {
632
    return new this().fromArray(items);
!
633
  }
634

635
  /**
636
   * Test an object to see if it is a covenant.
637
   * @param {Object} obj
638
   * @returns {Boolean}
639
   */
640

641
  static isCovenant(obj) {
642
    return obj instanceof Covenant;
!
643
  }
644
}
645

646
Covenant.types = types;
58×
647

648
/*
649
 * Expose
650
 */
651

652
module.exports = Covenant;
58×
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