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

handshake-org / hsd / 11142522588

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

push

github

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

7761 of 12916 branches covered (60.09%)

Branch coverage included in aggregate %.

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

8 existing lines in 5 files now uncovered.

24790 of 33559 relevant lines covered (73.87%)

34019.86 hits per line

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

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

7
'use strict';
8

9
const assert = require('bsert');
70✔
10
const bio = require('bufio');
70✔
11
const bech32 = require('bcrypto/lib/encoding/bech32');
70✔
12
const blake2b = require('bcrypto/lib/blake2b');
70✔
13
const sha3 = require('bcrypto/lib/sha3');
70✔
14
const Network = require('../protocol/network');
70✔
15
const consensus = require('../protocol/consensus');
70✔
16

17
/** @typedef {import('../types').Hash} Hash */
18
/** @typedef {import('../types').NetworkType} NetworkType */
19
/** @typedef {import('../types').BufioWriter} BufioWriter */
20
/** @typedef {import('../script/script')} Script */
21
/** @typedef {import('../script/witness')} Witness */
22

23
/*
24
 * Constants
25
 */
26

27
const ZERO_HASH160 = Buffer.alloc(20, 0x00);
70✔
28

29
/**
30
 * @typedef {Object} AddressOptions
31
 * @property {Hash} hash
32
 * @property {Number} version
33
 */
34

35
/**
36
 * Address
37
 * Represents an address.
38
 * @alias module:primitives.Address
39
 * @property {Number} version
40
 * @property {Buffer} hash
41
 */
42

43
class Address extends bio.Struct {
44
  /**
45
   * Create an address.
46
   * @constructor
47
   * @param {AddressOptions|String} [options]
48
   * @param {(NetworkType|Network)?} [network]
49
   */
50

51
  constructor(options, network) {
52
    super();
1,304,352✔
53

54
    this.version = 0;
1,304,352✔
55
    this.hash = ZERO_HASH160;
1,304,352✔
56

57
    if (options)
1,304,352✔
58
      this.fromOptions(options, network);
49✔
59
  }
60

61
  /**
62
   * Inject properties from options object.
63
   * @param {AddressOptions|String} options
64
   * @param {(NetworkType|Network)?} [network]
65
   */
66

67
  fromOptions(options, network) {
68
    if (typeof options === 'string')
114,433✔
69
      return this.fromString(options, network);
1,679✔
70

71
    assert(options);
112,754✔
72

73
    const {hash, version} = options;
112,754✔
74

75
    return this.fromHash(hash, version);
112,754✔
76
  }
77

78
  /**
79
   * Count the sigops in a script, taking into account witness programs.
80
   * @param {Witness} witness
81
   * @returns {Number} sigop count
82
   */
83

84
  getSigops(witness) {
85
    if (this.version === 0) {
47,501✔
86
      if (this.hash.length === 20)
47,500✔
87
        return 1;
43,079✔
88

89
      if (this.hash.length === 32 && witness.items.length > 0) {
4,421!
90
        const redeem = witness.getRedeem();
4,421✔
91
        return redeem.getSigops();
4,421✔
92
      }
93
    }
94

95
    return 0;
1✔
96
  }
97

98
  /**
99
   * Get the address hash.
100
   * @returns {Hash}
101
   */
102

103
  getHash() {
104
    return this.hash;
9,022,036✔
105
  }
106

107
  /**
108
   * Test whether the address is null.
109
   * @returns {Boolean}
110
   */
111

112
  isNull() {
113
    if (this.hash.length === 20)
12,105✔
114
      return this.hash.equals(ZERO_HASH160);
12,064✔
115

116
    if (this.hash.length === 32)
41✔
117
      return this.hash.equals(consensus.ZERO_HASH);
40✔
118

119
    for (let i = 0; i < this.hash.length; i++) {
1✔
120
      if (this.hash[i] !== 0)
1!
121
        return false;
1✔
122
    }
123

124
    return true;
×
125
  }
126

127
  /**
128
   * Test whether the address is unspendable.
129
   * @returns {Boolean}
130
   */
131

132
  isUnspendable() {
133
    return this.isNulldata();
210,667✔
134
  }
135

136
  /**
137
   * Test equality against another address.
138
   * @param {Address} addr
139
   * @returns {Boolean}
140
   */
141

142
  equals(addr) {
143
    assert(addr instanceof Address);
8,333✔
144

145
    return this.version === addr.version
8,333✔
146
      && this.hash.equals(addr.hash);
147
  }
148

149
  /**
150
   * Compare against another address.
151
   * @param {Address} addr
152
   * @returns {Number}
153
   */
154

155
  compare(addr) {
156
    assert(addr instanceof Address);
16,471✔
157

158
    const cmp = this.version - addr.version;
16,471✔
159

160
    if (cmp !== 0)
16,471!
161
      return cmp;
×
162

163
    return this.hash.compare(addr.hash);
16,471✔
164
  }
165

166
  /**
167
   * Inject properties from another address.
168
   * @param {Address} addr
169
   * @returns {this}
170
   */
171

172
  inject(addr) {
173
    this.version = addr.version;
160,853✔
174
    this.hash = addr.hash;
160,853✔
175
    return this;
160,853✔
176
  }
177

178
  /**
179
   * Clone address.
180
   * @returns {this}
181
   */
182

183
  clone() {
184
    // @ts-ignore
UNCOV
185
    return new this.constructor().inject(this);
×
186
  }
187

188
  /**
189
   * Compile the address object to a bech32 address.
190
   * @param {(NetworkType|Network)?} [network]
191
   * @returns {String}
192
   * @throws Error on bad hash/prefix.
193
   */
194

195
  toString(network) {
196
    const version = this.version;
5,294✔
197
    const hash = this.hash;
5,294✔
198

199
    assert(version <= 31);
5,294✔
200
    assert(hash.length >= 2 && hash.length <= 40);
5,294✔
201

202
    network = Network.get(network);
5,294✔
203

204
    const hrp = network.addressPrefix;
5,294✔
205

206
    return bech32.encode(hrp, version, hash);
5,294✔
207
  }
208

209
  /**
210
   * Instantiate address from pubkey.
211
   * @param {Buffer} key
212
   * @returns {Address}
213
   */
214

215
  fromPubkey(key) {
216
    assert(Buffer.isBuffer(key) && key.length === 33);
60✔
217
    return this.fromHash(blake2b.digest(key, 20), 0);
60✔
218
  }
219

220
  /**
221
   * Instantiate address from script.
222
   * @param {Script} script
223
   * @returns {Address}
224
   */
225

226
  fromScript(script) {
227
    assert(script && typeof script.encode === 'function');
902✔
228
    return this.fromHash(sha3.digest(script.encode()), 0);
902✔
229
  }
230

231
  /**
232
   * Inject properties from bech32 address.
233
   * @param {String} data
234
   * @param {(NetworkType|Network)?} [network]
235
   * @throws Parse error
236
   */
237

238
  fromString(data, network) {
239
    assert(typeof data === 'string');
2,691✔
240

241
    const [hrp, version, hash] = bech32.decode(data);
2,691✔
242

243
    Network.fromAddress(hrp, network);
2,689✔
244

245
    return this.fromHash(hash, version);
2,687✔
246
  }
247

248
  /**
249
   * Inject properties from witness.
250
   * @param {Witness} witness
251
   * @returns {Address|null}
252
   */
253

254
  fromWitness(witness) {
255
    const [, pk] = witness.getPubkeyhashInput();
27,171✔
256

257
    if (pk) {
27,171✔
258
      this.hash = blake2b.digest(pk, 20);
23,947✔
259
      this.version = 0;
23,947✔
260
      return this;
23,947✔
261
    }
262

263
    const redeem = witness.getScripthashInput();
3,224✔
264

265
    if (redeem) {
3,224✔
266
      this.hash = sha3.digest(redeem);
3,047✔
267
      this.version = 0;
3,047✔
268
      return this;
3,047✔
269
    }
270

271
    return null;
177✔
272
  }
273

274
  /**
275
   * Inject properties from a hash.
276
   * @param {Hash} hash
277
   * @param {Number} [version=0]
278
   * @throws on bad hash size
279
   */
280

281
  fromHash(hash, version) {
282
    if (version == null)
711,882✔
283
      version = 0;
23✔
284

285
    assert(Buffer.isBuffer(hash));
711,882✔
286
    assert((version & 0xff) === version);
711,882✔
287

288
    assert(version >= 0 && version <= 31, 'Bad program version.');
711,882✔
289
    assert(hash.length >= 2 && hash.length <= 40, 'Hash is the wrong size.');
711,882✔
290

291
    if (version === 0) {
711,882✔
292
      assert(hash.length === 20 || hash.length === 32,
711,866✔
293
        'Witness program hash is the wrong size.');
294
    }
295

296
    this.hash = hash;
711,882✔
297
    this.version = version;
711,882✔
298

299
    return this;
711,882✔
300
  }
301

302
  /**
303
   * Inject properties from witness pubkeyhash.
304
   * @param {Buffer} hash
305
   * @returns {Address}
306
   */
307

308
  fromPubkeyhash(hash) {
309
    assert(hash && hash.length === 20, 'P2WPKH must be 20 bytes.');
57,340✔
310
    return this.fromHash(hash, 0);
57,340✔
311
  }
312

313
  /**
314
   * Inject properties from witness scripthash.
315
   * @param {Buffer} hash
316
   * @returns {Address}
317
   */
318

319
  fromScripthash(hash) {
320
    assert(hash && hash.length === 32, 'P2WSH must be 32 bytes.');
36✔
321
    return this.fromHash(hash, 0);
36✔
322
  }
323

324
  /**
325
   * Inject properties from witness program.
326
   * @param {Number} version
327
   * @param {Buffer} hash
328
   * @returns {Address}
329
   */
330

331
  fromProgram(version, hash) {
332
    assert(version >= 0, 'Bad version for witness program.');
14✔
333
    return this.fromHash(hash, version);
14✔
334
  }
335

336
  /**
337
   * Instantiate address from nulldata.
338
   * @param {Buffer} data
339
   * @returns {Address}
340
   */
341

342
  fromNulldata(data) {
343
    return this.fromHash(data, 31);
1✔
344
  }
345

346
  /**
347
   * Test whether the address is witness pubkeyhash.
348
   * @returns {Boolean}
349
   */
350

351
  isPubkeyhash() {
352
    return this.version === 0 && this.hash.length === 20;
101,769✔
353
  }
354

355
  /**
356
   * Test whether the address is witness scripthash.
357
   * @returns {Boolean}
358
   */
359

360
  isScripthash() {
361
    return this.version === 0 && this.hash.length === 32;
79,911✔
362
  }
363

364
  /**
365
   * Test whether the address is unspendable.
366
   * @returns {Boolean}
367
   */
368

369
  isNulldata() {
370
    return this.version === 31;
210,667✔
371
  }
372

373
  /**
374
   * Test whether the address is an unknown witness program.
375
   * @returns {Boolean}
376
   */
377

378
  isUnknown() {
379
    switch (this.version) {
44✔
380
      case 0:
381
        return this.hash.length !== 20 && this.hash.length !== 32;
4✔
382
      case 31:
383
        return false;
39✔
384
    }
385
    return true;
1✔
386
  }
387

388
  /**
389
   * Test address validity.
390
   * @returns {Boolean}
391
   */
392

393
  isValid() {
394
    assert(this.version >= 0);
92,169✔
395

396
    if (this.version > 31)
92,169!
397
      return false;
×
398

399
    if (this.hash.length < 2 || this.hash.length > 40)
92,169!
400
      return false;
×
401

402
    return true;
92,169✔
403
  }
404

405
  /**
406
   * Calculate address size.
407
   * @returns {Number}
408
   */
409

410
  getSize() {
411
    return 1 + 1 + this.hash.length;
6,549,926✔
412
  }
413

414
  /**
415
   * Write address to buffer writer.
416
   * @param {BufioWriter} bw
417
   * @returns {BufioWriter}
418
   */
419

420
  write(bw) {
421
    bw.writeU8(this.version);
5,004,667✔
422
    bw.writeU8(this.hash.length);
5,004,667✔
423
    bw.writeBytes(this.hash);
5,004,667✔
424
    return bw;
5,004,667✔
425
  }
426

427
  /**
428
   * Read address from buffer reader.
429
   * @param {bio.BufferReader} br
430
   * @returns {this}
431
   */
432

433
  read(br) {
434
    const version = br.readU8();
537,170✔
435
    assert(version <= 31);
537,170✔
436

437
    const size = br.readU8();
537,168✔
438
    assert(size >= 2 && size <= 40);
537,168✔
439

440
    const hash = br.readBytes(size);
537,168✔
441

442
    return this.fromHash(hash, version);
537,168✔
443
  }
444

445
  /**
446
   * Inspect the Address.
447
   * @returns {String}
448
   */
449

450
  format() {
451
    return '<Address:'
1✔
452
      + ` version=${this.version}`
453
      + ` str=${this.toString()}`
454
      + '>';
455
  }
456

457
  /**
458
   * Instantiate address from pubkey.
459
   * @param {Buffer} key
460
   * @returns {Address}
461
   */
462

463
  static fromPubkey(key) {
464
    return new this().fromPubkey(key);
60✔
465
  }
466

467
  /**
468
   * Instantiate address from script.
469
   * @param {Script} script
470
   * @returns {Address}
471
   */
472

473
  static fromScript(script) {
474
    return new this().fromScript(script);
901✔
475
  }
476

477
  /**
478
   * Create an Address from a witness.
479
   * Attempt to extract address
480
   * properties from a witness.
481
   * @param {Witness} witness
482
   * @returns {Address|null}
483
   */
484

485
  static fromWitness(witness) {
486
    return new this().fromWitness(witness);
27,171✔
487
  }
488

489
  /**
490
   * Create a naked address from hash/version.
491
   * @param {Hash} hash
492
   * @param {Number} [version=0]
493
   * @returns {Address}
494
   * @throws on bad hash size
495
   */
496

497
  static fromHash(hash, version) {
498
    return new this().fromHash(hash, version);
920✔
499
  }
500

501
  /**
502
   * Instantiate address from witness pubkeyhash.
503
   * @param {Buffer} hash
504
   * @returns {Address}
505
   */
506

507
  static fromPubkeyhash(hash) {
508
    return new this().fromPubkeyhash(hash);
19,648✔
509
  }
510

511
  /**
512
   * Instantiate address from witness scripthash.
513
   * @param {Buffer} hash
514
   * @returns {Address}
515
   */
516

517
  static fromScripthash(hash) {
518
    return new this().fromScripthash(hash);
36✔
519
  }
520

521
  /**
522
   * Instantiate address from witness program.
523
   * @param {Number} version
524
   * @param {Buffer} hash
525
   * @returns {Address}
526
   */
527

528
  static fromProgram(version, hash) {
529
    return new this().fromProgram(version, hash);
14✔
530
  }
531

532
  /**
533
   * Instantiate address from nulldata.
534
   * @param {Buffer} data
535
   * @returns {Address}
536
   */
537

538
  static fromNulldata(data) {
539
    return new this().fromNulldata(data);
1✔
540
  }
541

542
  /**
543
   * Get the hash of a base58 address or address-related object.
544
   * @param {Address|Hash} data
545
   * @returns {Hash}
546
   */
547

548
  static getHash(data) {
549
    if (!data)
66,033!
550
      throw new Error('Object is not an address.');
×
551

552
    if (Buffer.isBuffer(data))
66,033✔
553
      return data;
65,296✔
554

555
    if (data instanceof Address)
737!
556
      return data.hash;
737✔
557

558
    throw new Error('Object is not an address.');
×
559
  }
560
}
561

562
/*
563
 * Expose
564
 */
565

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

© 2026 Coveralls, Inc