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

handshake-org / hsd / 11554992838

28 Oct 2024 01:28PM UTC coverage: 71.256% (+1.2%) from 70.033%
11554992838

Pull #888

github

web-flow
Merge 904756e0d into 1b331eedb
Pull Request #888: Wallet TX Count and time indexing

8052 of 13150 branches covered (61.23%)

Branch coverage included in aggregate %.

827 of 886 new or added lines in 16 files covered. (93.34%)

2068 existing lines in 60 files now uncovered.

25716 of 34240 relevant lines covered (75.11%)

34507.36 hits per line

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

48.55
/lib/script/stack.js
1
/*!
2
 * stack.js - stack 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');
68✔
10
const bio = require('bufio');
68✔
11
const common = require('./common');
68✔
12
const ScriptNum = require('./scriptnum');
68✔
13

14
/**
15
 * Stack
16
 * Represents the stack of a Script during execution.
17
 * @alias module:script.Stack
18
 * @property {Buffer[]} items - Stack items.
19
 * @property {Number} length - Size of stack.
20
 */
21

22
class Stack extends bio.Struct {
23
  /**
24
   * Create a stack.
25
   * @constructor
26
   * @param {Buffer[]?} [items] - Stack items.
27
   */
28

29
  constructor(items) {
30
    super();
409,917✔
31
    this.items = items || [];
409,917✔
32
  }
33

34
  /**
35
   * Get length.
36
   * @returns {Number}
37
   */
38

39
  get length() {
40
    return this.items.length;
487,339✔
41
  }
42

43
  /**
44
   * Set length.
45
   * @param {Number} value
46
   */
47

48
  set length(value) {
49
    this.items.length = value;
96✔
50
  }
51

52
  /**
53
   * Instantiate a value-only iterator.
54
   * @returns {IterableIterator<Buffer>}
55
   */
56

57
  [Symbol.iterator]() {
58
    return this.items[Symbol.iterator]();
×
59
  }
60

61
  /**
62
   * Instantiate a value-only iterator.
63
   * @returns {IterableIterator<Buffer>}
64
   */
65

66
  values() {
67
    return this.items.values();
×
68
  }
69

70
  /**
71
   * Instantiate a key and value iterator.
72
   */
73

74
  entries() {
UNCOV
75
    return this.items.entries();
×
76
  }
77

78
  /**
79
   * Inspect the stack.
80
   * @returns {String} Human-readable stack.
81
   */
82

83
  format() {
UNCOV
84
    return `<Stack: ${this.toString()}>`;
×
85
  }
86

87
  /**
88
   * Convert the stack to a string.
89
   * @returns {String} Human-readable stack.
90
   */
91

92
  toString() {
UNCOV
93
    const out = [];
×
94

UNCOV
95
    for (const item of this.items)
×
96
      out.push(item.toString('hex'));
×
97

UNCOV
98
    return out.join(' ');
×
99
  }
100

101
  /**
102
   * Format the stack as bitcoind asm.
103
   * @param {Boolean?} decode - Attempt to decode hash types.
104
   * @returns {String} Human-readable script.
105
   */
106

107
  toASM(decode) {
UNCOV
108
    const out = [];
×
109

UNCOV
110
    for (const item of this.items)
×
111
      out.push(common.toASM(item, decode));
×
112

UNCOV
113
    return out.join(' ');
×
114
  }
115

116
  /**
117
   * Clone the stack.
118
   * @param {this} stack
119
   * @returns {this} Cloned stack.
120
   */
121

122
  inject(stack) {
123
    this.items = stack.items.slice();
×
124
    return this;
×
125
  }
126

127
  /**
128
   * Clear the stack.
129
   * @returns {Stack}
130
   */
131

132
  clear() {
133
    this.items.length = 0;
×
134
    return this;
×
135
  }
136

137
  /**
138
   * Get a stack item by index.
139
   * @param {Number} index
140
   * @returns {Buffer|null}
141
   */
142

143
  get(index) {
144
    if (index < 0)
304,042✔
145
      index += this.items.length;
148,364✔
146

147
    if (index < 0 || index >= this.items.length)
304,042!
148
      return null;
×
149

150
    return this.items[index];
304,042✔
151
  }
152

153
  /**
154
   * Pop a stack item.
155
   * @see Array#pop
156
   * @returns {Buffer|null}
157
   */
158

159
  pop() {
160
    const item = this.items.pop();
148,918✔
161
    return item || null;
148,918!
162
  }
163

164
  /**
165
   * Shift a stack item.
166
   * @see Array#shift
167
   * @returns {Buffer|null}
168
   */
169

170
  shift() {
171
    const item = this.items.shift();
×
172
    return item || null;
×
173
  }
174

175
  /**
176
   * Remove an item.
177
   * @param {Number} index
178
   * @returns {Buffer}
179
   */
180

181
  remove(index) {
182
    if (index < 0)
13✔
183
      index += this.items.length;
12✔
184

185
    if (index < 0 || index >= this.items.length)
13!
186
      return null;
×
187

188
    const items = this.items.splice(index, 1);
13✔
189

190
    if (items.length === 0)
13!
191
      return null;
×
192

193
    return items[0];
13✔
194
  }
195

196
  /**
197
   * Set stack item at index.
198
   * @param {Number} index
199
   * @param {Buffer} item
200
   * @returns {this}
201
   */
202

203
  set(index, item) {
204
    if (index < 0)
105,512!
205
      index += this.items.length;
×
206

207
    assert(Buffer.isBuffer(item));
105,512✔
208
    assert(index >= 0 && index <= this.items.length);
105,512✔
209

210
    this.items[index] = item;
105,512✔
211

212
    return this;
105,512✔
213
  }
214

215
  /**
216
   * Push item onto stack.
217
   * @see Array#push
218
   * @param {Buffer} item
219
   * @returns {this}
220
   */
221

222
  push(item) {
223
    assert(Buffer.isBuffer(item));
298,563✔
224
    this.items.push(item);
298,563✔
225
    return this;
298,563✔
226
  }
227

228
  /**
229
   * Unshift item from stack.
230
   * @see Array#unshift
231
   * @param {Buffer} item
232
   * @returns {this}
233
   */
234

235
  unshift(item) {
236
    assert(Buffer.isBuffer(item));
×
237
    this.items.unshift(item);
×
238
    return this;
×
239
  }
240

241
  /**
242
   * Insert an item.
243
   * @param {Number} index
244
   * @param {Buffer} item
245
   * @returns {this}
246
   */
247

248
  insert(index, item) {
249
    if (index < 0)
3!
250
      index += this.items.length;
3✔
251

252
    assert(Buffer.isBuffer(item));
3✔
253
    assert(index >= 0 && index <= this.items.length);
3✔
254

255
    this.items.splice(index, 0, item);
3✔
256

257
    return this;
3✔
258
  }
259

260
  /**
261
   * Erase stack items.
262
   * @param {Number} start
263
   * @param {Number} end
264
   * @returns {Buffer[]}
265
   */
266

267
  erase(start, end) {
268
    if (start < 0)
12!
269
      start = this.items.length + start;
12✔
270

271
    if (end < 0)
12!
272
      end = this.items.length + end;
12✔
273

274
    return this.items.splice(start, end - start);
12✔
275
  }
276

277
  /**
278
   * Swap stack values.
279
   * @param {Number} i1 - Index 1.
280
   * @param {Number} i2 - Index 2.
281
   */
282

283
  swap(i1, i2) {
284
    if (i1 < 0)
31!
285
      i1 = this.items.length + i1;
31✔
286

287
    if (i2 < 0)
31!
288
      i2 = this.items.length + i2;
31✔
289

290
    const v1 = this.items[i1];
31✔
291
    const v2 = this.items[i2];
31✔
292

293
    this.items[i1] = v2;
31✔
294
    this.items[i2] = v1;
31✔
295
  }
296

297
  /*
298
   * Data
299
   */
300

301
  getData(index) {
302
    return this.get(index);
×
303
  }
304

305
  popData() {
306
    return this.pop();
×
307
  }
308

309
  shiftData() {
310
    return this.shift();
×
311
  }
312

313
  removeData(index) {
314
    return this.remove(index);
×
315
  }
316

317
  setData(index, data) {
318
    return this.set(index, data);
76,364✔
319
  }
320

321
  pushData(data) {
322
    return this.push(data);
143,843✔
323
  }
324

325
  unshiftData(data) {
326
    return this.unshift(data);
×
327
  }
328

329
  insertData(index, data) {
330
    return this.insert(index, data);
×
331
  }
332

333
  /*
334
   * Length
335
   */
336

337
  getLength(index) {
338
    const item = this.get(index);
×
339
    return item ? item.length : -1;
×
340
  }
341

342
  /*
343
   * String
344
   */
345

346
  getString(index, enc) {
347
    const item = this.get(index);
×
348
    return item ? Stack.toString(item, enc) : null;
×
349
  }
350

351
  popString(enc) {
352
    const item = this.pop();
×
353
    return item ? Stack.toString(item, enc) : null;
×
354
  }
355

356
  shiftString(enc) {
357
    const item = this.shift();
×
358
    return item ? Stack.toString(item, enc) : null;
×
359
  }
360

361
  removeString(index, enc) {
362
    const item = this.remove(index);
×
363
    return item ? Stack.toString(item, enc) : null;
×
364
  }
365

366
  setString(index, str, enc) {
367
    return this.set(index, Stack.fromString(str, enc));
×
368
  }
369

370
  pushString(str, enc) {
371
    return this.push(Stack.fromString(str, enc));
×
372
  }
373

374
  unshiftString(str, enc) {
375
    return this.unshift(Stack.fromString(str, enc));
×
376
  }
377

378
  insertString(index, str, enc) {
379
    return this.insert(index, Stack.fromString(str, enc));
×
380
  }
381

382
  /*
383
   * Num
384
   */
385

386
  getNum(index, minimal, limit) {
387
    const item = this.get(index);
640✔
388
    return item ? Stack.toNum(item, minimal, limit) : null;
640!
389
  }
390

391
  popNum(minimal, limit) {
392
    const item = this.pop();
×
393
    return item ? Stack.toNum(item, minimal, limit) : null;
×
394
  }
395

396
  shiftNum(minimal, limit) {
397
    const item = this.shift();
×
398
    return item ? Stack.toNum(item, minimal, limit) : null;
×
399
  }
400

401
  removeNum(index, minimal, limit) {
402
    const item = this.remove(index);
×
403
    return item ? Stack.toNum(item, minimal, limit) : null;
×
404
  }
405

406
  setNum(index, num) {
407
    return this.set(index, Stack.fromNum(num));
×
408
  }
409

410
  pushNum(num) {
411
    return this.push(Stack.fromNum(num));
280✔
412
  }
413

414
  unshiftNum(num) {
415
    return this.unshift(Stack.fromNum(num));
×
416
  }
417

418
  insertNum(index, num) {
419
    return this.insert(index, Stack.fromNum(num));
×
420
  }
421

422
  /*
423
   * Int
424
   */
425

426
  getInt(index, minimal, limit) {
427
    const item = this.get(index);
723✔
428
    return item ? Stack.toInt(item, minimal, limit) : -1;
723!
429
  }
430

431
  popInt(minimal, limit) {
432
    const item = this.pop();
×
433
    return item ? Stack.toInt(item, minimal, limit) : -1;
×
434
  }
435

436
  shiftInt(minimal, limit) {
437
    const item = this.shift();
×
438
    return item ? Stack.toInt(item, minimal, limit) : -1;
×
439
  }
440

441
  removeInt(index, minimal, limit) {
442
    const item = this.remove(index);
×
443
    return item ? Stack.toInt(item, minimal, limit) : -1;
×
444
  }
445

446
  setInt(index, num) {
447
    return this.set(index, Stack.fromInt(num));
×
448
  }
449

450
  pushInt(num) {
451
    return this.push(Stack.fromInt(num));
32,836✔
452
  }
453

454
  unshiftInt(num) {
455
    return this.unshift(Stack.fromInt(num));
×
456
  }
457

458
  insertInt(index, num) {
459
    return this.insert(index, Stack.fromInt(num));
×
460
  }
461

462
  /*
463
   * Bool
464
   */
465

466
  getBool(index) {
467
    const item = this.get(index);
27,192✔
468
    return item ? Stack.toBool(item) : false;
27,192!
469
  }
470

471
  popBool() {
472
    const item = this.pop();
×
473
    return item ? Stack.toBool(item) : false;
×
474
  }
475

476
  shiftBool() {
477
    const item = this.shift();
×
478
    return item ? Stack.toBool(item) : false;
×
479
  }
480

481
  removeBool(index) {
482
    const item = this.remove(index);
×
483
    return item ? Stack.toBool(item) : false;
×
484
  }
485

486
  setBool(index, value) {
487
    return this.set(index, Stack.fromBool(value));
×
488
  }
489

490
  pushBool(value) {
491
    return this.push(Stack.fromBool(value));
47,748✔
492
  }
493

494
  unshiftBool(value) {
495
    return this.unshift(Stack.fromBool(value));
×
496
  }
497

498
  insertBool(index, value) {
499
    return this.insert(index, Stack.fromBool(value));
×
500
  }
501

502
  /**
503
   * Test an object to see if it is a Stack.
504
   * @param {Object} obj
505
   * @returns {Boolean}
506
   */
507

508
  static isStack(obj) {
509
    return obj instanceof Stack;
×
510
  }
511

512
  /*
513
   * Encoding
514
   */
515

516
  static toString(item, enc) {
517
    assert(Buffer.isBuffer(item));
×
518
    return item.toString(enc || 'utf8');
×
519
  }
520

521
  static fromString(str, enc) {
522
    assert(typeof str === 'string');
×
523
    return Buffer.from(str, enc || 'utf8');
×
524
  }
525

526
  static toNum(item, minimal, limit) {
527
    return ScriptNum.decode(item, minimal, limit);
1,363✔
528
  }
529

530
  static fromNum(num) {
531
    assert(ScriptNum.isScriptNum(num));
281✔
532
    return num.encode();
281✔
533
  }
534

535
  static toInt(item, minimal, limit) {
536
    const num = Stack.toNum(item, minimal, limit);
723✔
537
    return num.getInt();
716✔
538
  }
539

540
  static fromInt(int) {
541
    assert(typeof int === 'number');
80,584✔
542

543
    if (int >= -1 && int <= 16)
80,584✔
544
      return common.small[int + 1];
80,583✔
545

546
    const num = ScriptNum.fromNumber(int);
1✔
547

548
    return Stack.fromNum(num);
1✔
549
  }
550

551
  static toBool(item) {
552
    assert(Buffer.isBuffer(item));
27,192✔
553

554
    for (let i = 0; i < item.length; i++) {
27,192✔
555
      if (item[i] !== 0) {
27,042✔
556
        // Cannot be negative zero
557
        if (i === item.length - 1 && item[i] === 0x80)
27,031✔
558
          return false;
1✔
559
        return true;
27,030✔
560
      }
561
    }
562

563
    return false;
161✔
564
  }
565

566
  static fromBool(value) {
567
    assert(typeof value === 'boolean');
47,748✔
568
    return Stack.fromInt(value ? 1 : 0);
47,748✔
569
  }
570
}
571

572
/*
573
 * Expose
574
 */
575

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

© 2025 Coveralls, Inc