1 | 'use strict';
|
2 |
|
3 | var _ = require('lodash');
|
4 | var $ = require('./util/preconditions');
|
5 | var errors = require('./errors');
|
6 | var Base58Check = require('./encoding/base58check');
|
7 | var Bech32 = require('./encoding/bech32');
|
8 | var Networks = require('./networks');
|
9 | var Hash = require('./crypto/hash');
|
10 | var JSUtil = require('./util/js');
|
11 | var PublicKey = require('./publickey');
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 | function Address(data, network, type, multisigType) {
|
53 |
|
54 |
|
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 |
|
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 |
|
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 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 | Address.prototype._classifyArguments = function(data, network, type) {
|
106 |
|
107 |
|
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 |
|
126 | Address.PayToPublicKeyHash = 'pubkeyhash';
|
127 |
|
128 | Address.PayToScriptHash = 'scripthash';
|
129 |
|
130 | Address.PayToWitnessPublicKeyHash = 'witnesspubkeyhash';
|
131 |
|
132 | Address.PayToWitnessScriptHash = 'witnessscripthash';
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 | Address._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 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 | Address._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 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 | Address._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 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 |
|
222 | Address._transformBuffer = function(buffer, network, type) {
|
223 |
|
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 |
|
260 |
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 | Address._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 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 | Address._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 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 | Address.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 |
|
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 |
|
345 | Address._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 |
|
376 |
|
377 |
|
378 |
|
379 |
|
380 |
|
381 |
|
382 | Address.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 |
|
390 |
|
391 |
|
392 |
|
393 |
|
394 |
|
395 | Address.fromPublicKeyHash = function(hash, network) {
|
396 | var info = Address._transformHash(hash);
|
397 | return new Address(info.hashBuffer, network, Address.PayToPublicKeyHash);
|
398 | };
|
399 |
|
400 |
|
401 |
|
402 |
|
403 |
|
404 |
|
405 |
|
406 |
|
407 |
|
408 | Address.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 |
|
420 |
|
421 |
|
422 |
|
423 |
|
424 |
|
425 |
|
426 |
|
427 |
|
428 |
|
429 | Address.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 |
|
444 |
|
445 |
|
446 |
|
447 |
|
448 |
|
449 |
|
450 |
|
451 |
|
452 |
|
453 |
|
454 | Address.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 |
|
462 |
|
463 |
|
464 |
|
465 |
|
466 |
|
467 |
|
468 | Address.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 |
|
475 |
|
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 | Address.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 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 | Address.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 |
|
503 |
|
504 |
|
505 |
|
506 |
|
507 |
|
508 |
|
509 |
|
510 |
|
511 |
|
512 |
|
513 |
|
514 |
|
515 | Address.getValidationError = function(data, network, type) {
|
516 | var error;
|
517 | try {
|
518 |
|
519 | new Address(data, network, type);
|
520 | } catch (e) {
|
521 | error = e;
|
522 | }
|
523 | return error;
|
524 | };
|
525 |
|
526 |
|
527 |
|
528 |
|
529 |
|
530 |
|
531 |
|
532 |
|
533 |
|
534 |
|
535 |
|
536 |
|
537 |
|
538 |
|
539 | Address.isValid = function(data, network, type) {
|
540 | return !Address.getValidationError(data, network, type);
|
541 | };
|
542 |
|
543 |
|
544 |
|
545 |
|
546 |
|
547 | Address.prototype.isPayToPublicKeyHash = function() {
|
548 | return this.type === Address.PayToPublicKeyHash;
|
549 | };
|
550 |
|
551 |
|
552 |
|
553 |
|
554 |
|
555 | Address.prototype.isPayToScriptHash = function() {
|
556 | return this.type === Address.PayToScriptHash;
|
557 | };
|
558 |
|
559 |
|
560 |
|
561 |
|
562 |
|
563 | Address.prototype.isPayToWitnessPublicKeyHash = function() {
|
564 | return this.type === Address.PayToWitnessPublicKeyHash;
|
565 | };
|
566 |
|
567 |
|
568 |
|
569 |
|
570 |
|
571 | Address.prototype.isPayToWitnessScriptHash = function() {
|
572 | return this.type === Address.PayToWitnessScriptHash;
|
573 | };
|
574 |
|
575 |
|
576 |
|
577 |
|
578 |
|
579 |
|
580 | Address.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 |
|
590 |
|
591 | Address.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 |
|
601 |
|
602 |
|
603 |
|
604 | Address.prototype.toString = function() {
|
605 | if (this.isPayToWitnessPublicKeyHash() || this.isPayToWitnessScriptHash()) {
|
606 | var prefix = this.network.bech32prefix;
|
607 | var version = 0;
|
608 | return Bech32.encode(prefix, version, this.hashBuffer);
|
609 | }
|
610 | return Base58Check.encode(this.toBuffer());
|
611 | };
|
612 |
|
613 |
|
614 |
|
615 |
|
616 |
|
617 |
|
618 | Address.prototype.inspect = function() {
|
619 | return '<Address: ' + this.toString() + ', type: ' + this.type + ', network: ' + this.network + '>';
|
620 | };
|
621 |
|
622 | module.exports = Address;
|
623 |
|
624 | var Script = require('./script');
|