UNPKG

22.3 kBJavaScriptView Raw
1'use strict';
2
3var _ = require('lodash');
4var $ = require('./util/preconditions');
5var errors = require('./errors');
6var Base58Check = require('./encoding/base58check');
7var Bech32 = require('./encoding/bech32');
8var Networks = require('./networks');
9var Hash = require('./crypto/hash');
10var JSUtil = require('./util/js');
11var PublicKey = require('./publickey');
12
13/**
14 * Instantiate an address from an address String or Buffer, a public key or script hash Buffer,
15 * or an instance of {@link PublicKey} or {@link Script}.
16 *
17 * This is an immutable class, and if the first parameter provided to this constructor is an
18 * `Address` instance, the same argument will be returned.
19 *
20 * An address has two key properties: `network` and `type`. The type is one of
21 * `Address.PayToPublicKeyHash` (value is the `'pubkeyhash'` string),
22 * `Address.PayToScriptHash` (the string `'scripthash'`),
23 * `Address.PayToWitnessPublicKeyHash` (the string `'witnesspubkeyhash'`),
24 * or `Address.PayToWitnessScriptHash` (the string `'witnessscripthash'`).
25 * The network is an instance of {@link Network}.
26 * You can quickly check whether an address is of a given kind by using the methods
27 * `isPayToPublicKeyHash`, `isPayToScriptHash`, `isPayToWitnessPublicKeyHash`,
28 * and `isPayToWitnessScriptHash`.
29 *
30 * @example
31 * ```javascript
32 * // validate that an input field is valid
33 * var error = Address.getValidationError(input, 'testnet');
34 * if (!error) {
35 * var address = Address(input, 'testnet');
36 * } else {
37 * // invalid network or checksum (typo?)
38 * var message = error.messsage;
39 * }
40 *
41 * // get an address from a public key
42 * var address = Address(publicKey, 'testnet').toString();
43 * ```
44 *
45 * @param {*} data - The encoded data in various formats
46 * @param {Network|String|number=} network - The network: 'livenet' or 'testnet'
47 * @param {string=} type - The type of address: 'scripthash', 'pubkeyhash', witnessscripthash or 'witnesspubkeyhash'
48 * @param {string=} multisigType - The type of multisig: 'scripthash' or 'witnessscripthash'
49 * @returns {Address} A new valid and frozen instance of an Address
50 * @constructor
51 */
52function Address(data, network, type, multisigType) {
53 /* jshint maxcomplexity: 12 */
54 /* jshint maxstatements: 20 */
55
56 if (!(this instanceof Address)) {
57 return new Address(data, network, type);
58 }
59
60 if (_.isArray(data) && _.isNumber(network)) {
61 return Address.createMultisig(data, network, type, false, multisigType);
62 }
63
64 if (data instanceof Address) {
65 // Immutable instance
66 return data;
67 }
68
69 $.checkArgument(data, 'First argument is required, please include address data.', 'guide/address.html');
70
71 if (network && !Networks.get(network)) {
72 throw new TypeError('Second argument must be "livenet" or "testnet".');
73 }
74
75 if (type && (
76 type !== Address.PayToPublicKeyHash
77 && type !== Address.PayToScriptHash
78 && type !== Address.PayToWitnessPublicKeyHash
79 && type !== Address.PayToWitnessScriptHash)) {
80 throw new TypeError('Third argument must be "pubkeyhash", "scripthash", "witnesspubkeyhash", or "witnessscripthash".');
81 }
82
83 var info = this._classifyArguments(data, network, type);
84
85 // set defaults if not set
86 info.network = info.network || Networks.get(network) || Networks.defaultNetwork;
87 info.type = info.type || type || Address.PayToPublicKeyHash;
88
89 JSUtil.defineImmutable(this, {
90 hashBuffer: info.hashBuffer,
91 network: info.network,
92 type: info.type
93 });
94
95 return this;
96}
97
98/**
99 * Internal function used to split different kinds of arguments of the constructor
100 * @param {*} data - The encoded data in various formats
101 * @param {Network|String|number=} network - The network: 'livenet' or 'testnet'
102 * @param {string=} type - The type of address: 'script' or 'pubkey'
103 * @returns {Object} An "info" object with "type", "network", and "hashBuffer"
104 */
105Address.prototype._classifyArguments = function(data, network, type) {
106 /* jshint maxcomplexity: 10 */
107 // transform and validate input data
108 if ((data instanceof Buffer || data instanceof Uint8Array) && (data.length === 20 || data.length === 32)) {
109 return Address._transformHash(data, network, type);
110 } else if ((data instanceof Buffer || data instanceof Uint8Array) && data.length >= 21) {
111 return Address._transformBuffer(data, network, type);
112 } else if (data instanceof PublicKey) {
113 return Address._transformPublicKey(data, network, type);
114 } else if (data instanceof Script) {
115 return Address._transformScript(data, network);
116 } else if (typeof(data) === 'string') {
117 return Address._transformString(data, network, type);
118 } else if (_.isObject(data)) {
119 return Address._transformObject(data);
120 } else {
121 throw new TypeError('First argument is an unrecognized data format.');
122 }
123};
124
125/** @static */
126Address.PayToPublicKeyHash = 'pubkeyhash';
127/** @static */
128Address.PayToScriptHash = 'scripthash';
129/** @static */
130Address.PayToWitnessPublicKeyHash = 'witnesspubkeyhash';
131/** @static */
132Address.PayToWitnessScriptHash = 'witnessscripthash';
133
134/**
135 * @param {Buffer} hash - An instance of a hash Buffer
136 * @param {string} type - either 'pubkeyhash', 'scripthash', 'witnesspubkeyhash', or 'witnessscripthash'
137 * @param {Network=} network - the name of the network associated
138 * @returns {Object} An object with keys: hashBuffer
139 * @private
140 */
141Address._transformHash = function(hash, network, type) {
142 var info = {};
143 if (!(hash instanceof Buffer) && !(hash instanceof Uint8Array)) {
144 throw new TypeError('Address supplied is not a buffer.');
145 }
146 if (hash.length !== 20 && hash.length !== 32) {
147 throw new TypeError('Address hashbuffers must be either 20 or 32 bytes.');
148 }
149 info.hashBuffer = hash;
150 info.network = Networks.get(network) || Networks.defaultNetwork;
151 info.type = type;
152 return info;
153};
154
155/**
156 * Deserializes an address serialized through `Address#toObject()`
157 * @param {Object} data
158 * @param {string} data.hash - the hash that this address encodes
159 * @param {string} data.type - either 'pubkeyhash', 'scripthash', 'witnesspubkeyhash', or 'witnessscripthash'
160 * @param {Network=} data.network - the name of the network associated
161 * @return {Address}
162 */
163Address._transformObject = function(data) {
164 $.checkArgument(data.hash || data.hashBuffer, 'Must provide a `hash` or `hashBuffer` property');
165 $.checkArgument(data.type, 'Must provide a `type` property');
166 return {
167 hashBuffer: data.hash ? Buffer.from(data.hash, 'hex') : data.hashBuffer,
168 network: Networks.get(data.network) || Networks.defaultNetwork,
169 type: data.type
170 };
171};
172
173/**
174 * Internal function to discover the network and type based on the first data byte
175 *
176 * @param {Buffer} buffer - An instance of a hex encoded address Buffer
177 * @returns {Object} An object with keys: network and type
178 * @private
179 */
180Address._classifyFromVersion = function(buffer) {
181 var version = {};
182
183 if (buffer.length > 21) {
184 var info = Bech32.decode(buffer.toString('utf8'));
185 if (info.version !== 0) {
186 throw new TypeError('Only witness v0 addresses are supported.');
187 }
188 if (info.data.length === 20) {
189 version.type = Address.PayToWitnessPublicKeyHash;
190 } else if (info.data.length === 32) {
191 version.type = Address.PayToWitnessScriptHash;
192 } else {
193 throw new TypeError('Witness data must be either 20 or 32 bytes.')
194 }
195 version.network = Networks.get(info.prefix, 'bech32prefix');
196 } else {
197
198 var pubkeyhashNetwork = Networks.get(buffer[0], 'pubkeyhash');
199 var scripthashNetwork = Networks.get(buffer[0], 'scripthash');
200
201 if (pubkeyhashNetwork) {
202 version.network = pubkeyhashNetwork;
203 version.type = Address.PayToPublicKeyHash;
204 } else if (scripthashNetwork) {
205 version.network = scripthashNetwork;
206 version.type = Address.PayToScriptHash;
207 }
208 }
209
210 return version;
211};
212
213/**
214 * Internal function to transform a bitcoin address buffer
215 *
216 * @param {Buffer} buffer - An instance of a hex encoded address Buffer
217 * @param {string=} network - The network: 'livenet' or 'testnet'
218 * @param {string=} type - The type: 'pubkeyhash', 'scripthash', 'witnesspubkeyhash', or 'witnessscripthash'
219 * @returns {Object} An object with keys: hashBuffer, network and type
220 * @private
221 */
222Address._transformBuffer = function(buffer, network, type) {
223 /* jshint maxcomplexity: 9 */
224 var info = {};
225 if (!(buffer instanceof Buffer) && !(buffer instanceof Uint8Array)) {
226 throw new TypeError('Address supplied is not a buffer.');
227 }
228
229 if (buffer.length < 21) {
230 throw new TypeError('Address buffer is incorrect length.');
231 }
232
233 var networkObj = Networks.get(network);
234 var bufferVersion = Address._classifyFromVersion(buffer);
235
236 if (network && !networkObj) {
237 throw new TypeError('Unknown network');
238 }
239
240 if (!bufferVersion.network || (networkObj && networkObj.xpubkey !== bufferVersion.network.xpubkey)) {
241 throw new TypeError('Address has mismatched network type.');
242 }
243
244 if (!bufferVersion.type || (type && type !== bufferVersion.type)) {
245 throw new TypeError('Address has mismatched type.');
246 }
247
248 if (buffer.length > 21) {
249 info.hashBuffer = Bech32.decode(buffer.toString('utf8')).data;
250 } else {
251 info.hashBuffer = buffer.slice(1);
252 }
253 info.network = bufferVersion.network;
254 info.type = bufferVersion.type;
255 return info;
256};
257
258/**
259 * Internal function to transform a {@link PublicKey}
260 *
261 * @param {PublicKey} pubkey - An instance of PublicKey
262 * @param {string} type - Either 'pubkeyhash', 'witnesspubkeyhash', or 'scripthash'
263 * @returns {Object} An object with keys: hashBuffer, type
264 * @private
265 */
266Address._transformPublicKey = function(pubkey, network, type) {
267 var info = {};
268 if (!(pubkey instanceof PublicKey)) {
269 throw new TypeError('Address must be an instance of PublicKey.');
270 }
271 if (type && type !== Address.PayToScriptHash && type !== Address.PayToWitnessPublicKeyHash && type !== Address.PayToPublicKeyHash) {
272 throw new TypeError('Type must be either pubkeyhash, witnesspubkeyhash, or scripthash to transform public key.');
273 }
274 if (!pubkey.compressed && (type === Address.PayToScriptHash || type === Address.PayToWitnessPublicKeyHash)) {
275 throw new TypeError('Witness addresses must use compressed public keys.');
276 }
277 if (type === Address.PayToScriptHash) {
278 info.hashBuffer = Hash.sha256ripemd160(Script.buildWitnessV0Out(pubkey).toBuffer());
279 } else {
280 info.hashBuffer = Hash.sha256ripemd160(pubkey.toBuffer());
281 }
282 info.type = type || Address.PayToPublicKeyHash;
283 return info;
284};
285
286/**
287 * Internal function to transform a {@link Script} into a `info` object.
288 *
289 * @param {Script} script - An instance of Script
290 * @returns {Object} An object with keys: hashBuffer, type
291 * @private
292 */
293Address._transformScript = function(script, network) {
294 $.checkArgument(script instanceof Script, 'script must be a Script instance');
295 var info = script.getAddressInfo(network);
296 if (!info) {
297 throw new errors.Script.CantDeriveAddress(script);
298 }
299 return info;
300};
301
302/**
303 * Creates a P2SH address from a set of public keys and a threshold.
304 *
305 * The addresses will be sorted lexicographically, as that is the trend in bitcoin.
306 * To create an address from unsorted public keys, use the {@link Script#buildMultisigOut}
307 * interface.
308 *
309 * @param {Array} publicKeys - a set of public keys to create an address
310 * @param {number} threshold - the number of signatures needed to release the funds
311 * @param {String|Network} network - either a Network instance, 'livenet', or 'testnet'
312 * @param {boolean=} nestedWitness - if the address uses a nested p2sh witness
313 * @param {string} type - Either 'scripthash' or 'witnessscripthash'. If nestedWitness is set, then this is ignored
314 * @return {Address}
315 */
316Address.createMultisig = function(publicKeys, threshold, network, nestedWitness, type) {
317 network = network || publicKeys[0].network || Networks.defaultNetwork;
318 if (type && type !== Address.PayToScriptHash && type !== Address.PayToWitnessScriptHash) {
319 throw new TypeError('Type must be either scripthash or witnessscripthash to create multisig.');
320 }
321 if (nestedWitness || type === Address.PayToWitnessScriptHash) {
322 publicKeys = _.map(publicKeys, PublicKey);
323 for (var i = 0; i < publicKeys.length; i++) {
324 if (!publicKeys[i].compressed) {
325 throw new TypeError('Witness addresses must use compressed public keys.');
326 }
327 }
328 }
329 var redeemScript = Script.buildMultisigOut(publicKeys, threshold);
330 if (nestedWitness) {
331 return Address.payingTo(Script.buildWitnessMultisigOutFromScript(redeemScript), network);
332 }
333 return Address.payingTo(redeemScript, network, type);
334};
335
336/**
337 * Internal function to transform a bitcoin address string
338 *
339 * @param {string} data
340 * @param {String|Network=} network - either a Network instance, 'livenet', or 'testnet'
341 * @param {string=} type - The type: 'pubkeyhash', 'scripthash', 'witnesspubkeyhash', or 'witnessscripthash'
342 * @returns {Object} An object with keys: hashBuffer, network and type
343 * @private
344 */
345Address._transformString = function(data, network, type) {
346 if (typeof(data) !== 'string') {
347 throw new TypeError('data parameter supplied is not a string.');
348 }
349
350 if(data.length > 100) {
351 throw new TypeError('address string is too long');
352 }
353
354 if (network && !Networks.get(network)) {
355 throw new TypeError('Unknown network');
356 }
357
358 data = data.trim();
359
360 try {
361 var info = Address._transformBuffer(Buffer.from(data, 'utf8'), network, type);
362 return info;
363 } catch (e) {
364 if (type === Address.PayToWitnessPublicKeyHash || type === Address.PayToWitnessScriptHash) {
365 throw e;
366 }
367 }
368
369 var addressBuffer = Base58Check.decode(data);
370 var info = Address._transformBuffer(addressBuffer, network, type);
371 return info;
372};
373
374/**
375 * Instantiate an address from a PublicKey instance
376 *
377 * @param {PublicKey} data
378 * @param {String|Network} network - either a Network instance, 'livenet', or 'testnet'
379 * @param {string} type - Either 'pubkeyhash', 'witnesspubkeyhash', or 'scripthash'
380 * @returns {Address} A new valid and frozen instance of an Address
381 */
382Address.fromPublicKey = function(data, network, type) {
383 var info = Address._transformPublicKey(data, network, type);
384 network = network || Networks.defaultNetwork;
385 return new Address(info.hashBuffer, network, info.type);
386};
387
388/**
389 * Instantiate an address from a ripemd160 public key hash
390 *
391 * @param {Buffer} hash - An instance of buffer of the hash
392 * @param {String|Network} network - either a Network instance, 'livenet', or 'testnet'
393 * @returns {Address} A new valid and frozen instance of an Address
394 */
395Address.fromPublicKeyHash = function(hash, network) {
396 var info = Address._transformHash(hash);
397 return new Address(info.hashBuffer, network, Address.PayToPublicKeyHash);
398};
399
400/**
401 * Instantiate an address from a ripemd160 script hash
402 *
403 * @param {Buffer} hash - An instance of buffer of the hash
404 * @param {String|Network} network - either a Network instance, 'livenet', or 'testnet'
405 * @param {string} type - Either 'scripthash' or 'witnessscripthash'
406 * @returns {Address} A new valid and frozen instance of an Address
407 */
408Address.fromScriptHash = function(hash, network, type) {
409 $.checkArgument(hash, 'hash parameter is required');
410 var info = Address._transformHash(hash);
411 if (type === Address.PayToWitnessScriptHash && hash.length !== 32) {
412 throw new TypeError('Address hashbuffer must be exactly 32 bytes for v0 witness script hash.');
413 }
414 var type = type || Address.PayToScriptHash;
415 return new Address(info.hashBuffer, network, type);
416};
417
418/**
419 * Builds a p2sh address paying to script. This will hash the script and
420 * use that to create the address.
421 * If you want to extract an address associated with a script instead,
422 * see {{Address#fromScript}}
423 *
424 * @param {Script} script - An instance of Script
425 * @param {String|Network} network - either a Network instance, 'livenet', or 'testnet'
426 * @param {string} type - Either 'scripthash' or 'witnessscripthash'
427 * @returns {Address} A new valid and frozen instance of an Address
428 */
429Address.payingTo = function(script, network, type) {
430 $.checkArgument(script, 'script is required');
431 $.checkArgument(script instanceof Script, 'script must be instance of Script');
432 var hash;
433 if (type === Address.PayToWitnessScriptHash) {
434 hash = Hash.sha256(script.toBuffer());
435 } else {
436 hash = Hash.sha256ripemd160(script.toBuffer());
437 }
438 var type = type || Address.PayToScriptHash;
439 return Address.fromScriptHash(hash, network, type);
440};
441
442/**
443 * Extract address from a Script. The script must be of one
444 * of the following types: p2pkh input, p2pkh output, p2sh input
445 * or p2sh output.
446 * This will analyze the script and extract address information from it.
447 * If you want to transform any script to a p2sh Address paying
448 * to that script's hash instead, use {{Address#payingTo}}
449 *
450 * @param {Script} script - An instance of Script
451 * @param {String|Network} network - either a Network instance, 'livenet', or 'testnet'
452 * @returns {Address} A new valid and frozen instance of an Address
453 */
454Address.fromScript = function(script, network) {
455 $.checkArgument(script instanceof Script, 'script must be a Script instance');
456 var info = Address._transformScript(script, network);
457 return new Address(info.hashBuffer, network, info.type);
458};
459
460/**
461 * Instantiate an address from a buffer of the address
462 *
463 * @param {Buffer} buffer - An instance of buffer of the address
464 * @param {String|Network=} network - either a Network instance, 'livenet', or 'testnet'
465 * @param {string=} type - The type of address: 'script' or 'pubkey'
466 * @returns {Address} A new valid and frozen instance of an Address
467 */
468Address.fromBuffer = function(buffer, network, type) {
469 var info = Address._transformBuffer(buffer, network, type);
470 return new Address(info.hashBuffer, info.network, info.type);
471};
472
473/**
474 * Instantiate an address from an address string
475 *
476 * @param {string} str - An string of the bitcoin address
477 * @param {String|Network=} network - either a Network instance, 'livenet', or 'testnet'
478 * @param {string=} type - The type of address: 'script' or 'pubkey'
479 * @returns {Address} A new valid and frozen instance of an Address
480 */
481Address.fromString = function(str, network, type) {
482 var info = Address._transformString(str, network, type);
483 return new Address(info.hashBuffer, info.network, info.type);
484};
485
486/**
487 * Instantiate an address from an Object
488 *
489 * @param {string} json - An JSON string or Object with keys: hash, network and type
490 * @returns {Address} A new valid instance of an Address
491 */
492Address.fromObject = function fromObject(obj) {
493 $.checkState(
494 JSUtil.isHexa(obj.hash),
495 'Unexpected hash property, "' + obj.hash + '", expected to be hex.'
496 );
497 var hashBuffer = Buffer.from(obj.hash, 'hex');
498 return new Address(hashBuffer, obj.network, obj.type);
499};
500
501/**
502 * Will return a validation error if exists
503 *
504 * @example
505 * ```javascript
506 * // a network mismatch error
507 * var error = Address.getValidationError('15vkcKf7gB23wLAnZLmbVuMiiVDc1Nm4a2', 'testnet');
508 * ```
509 *
510 * @param {string} data - The encoded data
511 * @param {String|Network} network - either a Network instance, 'livenet', or 'testnet'
512 * @param {string} type - The type of address: 'script' or 'pubkey'
513 * @returns {null|Error} The corresponding error message
514 */
515Address.getValidationError = function(data, network, type) {
516 var error;
517 try {
518 /* jshint nonew: false */
519 new Address(data, network, type);
520 } catch (e) {
521 error = e;
522 }
523 return error;
524};
525
526/**
527 * Will return a boolean if an address is valid
528 *
529 * @example
530 * ```javascript
531 * assert(Address.isValid('15vkcKf7gB23wLAnZLmbVuMiiVDc1Nm4a2', 'livenet'));
532 * ```
533 *
534 * @param {string} data - The encoded data
535 * @param {String|Network} network - either a Network instance, 'livenet', or 'testnet'
536 * @param {string} type - The type of address: 'script' or 'pubkey'
537 * @returns {boolean} The corresponding error message
538 */
539Address.isValid = function(data, network, type) {
540 return !Address.getValidationError(data, network, type);
541};
542
543/**
544 * Returns true if an address is of pay to public key hash type
545 * @return boolean
546 */
547Address.prototype.isPayToPublicKeyHash = function() {
548 return this.type === Address.PayToPublicKeyHash;
549};
550
551/**
552 * Returns true if an address is of pay to script hash type
553 * @return boolean
554 */
555Address.prototype.isPayToScriptHash = function() {
556 return this.type === Address.PayToScriptHash;
557};
558
559/**
560 * Returns true if an address is of pay to witness public key hash type
561 * @return boolean
562 */
563Address.prototype.isPayToWitnessPublicKeyHash = function() {
564 return this.type === Address.PayToWitnessPublicKeyHash;
565};
566
567/**
568 * Returns true if an address is of pay to witness script hash type
569 * @return boolean
570 */
571Address.prototype.isPayToWitnessScriptHash = function() {
572 return this.type === Address.PayToWitnessScriptHash;
573};
574
575/**
576 * Will return a buffer representation of the address
577 *
578 * @returns {Buffer} Bitcoin address buffer
579 */
580Address.prototype.toBuffer = function() {
581 if (this.isPayToWitnessPublicKeyHash() || this.isPayToWitnessScriptHash()) {
582 return Buffer.from(this.toString(), 'utf8')
583 }
584 var version = Buffer.from([this.network[this.type]]);
585 return Buffer.concat([version, this.hashBuffer]);
586};
587
588/**
589 * @returns {Object} A plain object with the address information
590 */
591Address.prototype.toObject = Address.prototype.toJSON = function toObject() {
592 return {
593 hash: this.hashBuffer.toString('hex'),
594 type: this.type,
595 network: this.network.toString()
596 };
597};
598
599/**
600 * Will return a the string representation of the address
601 *
602 * @returns {string} Bitcoin address
603 */
604Address.prototype.toString = function() {
605 if (this.isPayToWitnessPublicKeyHash() || this.isPayToWitnessScriptHash()) {
606 var prefix = this.network.bech32prefix;
607 var version = 0; // Only supporting segwit v0 for now
608 return Bech32.encode(prefix, version, this.hashBuffer);
609 }
610 return Base58Check.encode(this.toBuffer());
611};
612
613/**
614 * Will return a string formatted for the console
615 *
616 * @returns {string} Bitcoin address
617 */
618Address.prototype.inspect = function() {
619 return '<Address: ' + this.toString() + ', type: ' + this.type + ', network: ' + this.network + '>';
620};
621
622module.exports = Address;
623
624var Script = require('./script');