UNPKG

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