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 | && type !== Address.PayToTaproot)) {
|
81 | throw new TypeError('Third argument must be "pubkeyhash", "scripthash", "witnesspubkeyhash", "witnessscripthash", or "taproot".');
|
82 | }
|
83 |
|
84 | var info = this._classifyArguments(data, network, type);
|
85 |
|
86 |
|
87 | info.network = info.network || Networks.get(network) || Networks.defaultNetwork;
|
88 | info.type = info.type || type || Address.PayToPublicKeyHash;
|
89 |
|
90 | JSUtil.defineImmutable(this, {
|
91 | hashBuffer: info.hashBuffer,
|
92 | network: info.network,
|
93 | type: info.type
|
94 | });
|
95 |
|
96 | return this;
|
97 | }
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 | Address.prototype._classifyArguments = function(data, network, type) {
|
107 |
|
108 |
|
109 | if ((data instanceof Buffer || data instanceof Uint8Array) && (data.length === 20 || data.length === 32)) {
|
110 | return Address._transformHash(data, network, type);
|
111 | } else if ((data instanceof Buffer || data instanceof Uint8Array) && data.length >= 21) {
|
112 | return Address._transformBuffer(data, network, type);
|
113 | } else if (data instanceof PublicKey) {
|
114 | return Address._transformPublicKey(data, network, type);
|
115 | } else if (data instanceof Script) {
|
116 | return Address._transformScript(data, network);
|
117 | } else if (typeof(data) === 'string') {
|
118 | return Address._transformString(data, network, type);
|
119 | } else if (_.isObject(data)) {
|
120 | return Address._transformObject(data);
|
121 | } else {
|
122 | throw new TypeError('First argument is an unrecognized data format.');
|
123 | }
|
124 | };
|
125 |
|
126 |
|
127 | Address.PayToPublicKeyHash = 'pubkeyhash';
|
128 |
|
129 | Address.PayToScriptHash = 'scripthash';
|
130 |
|
131 | Address.PayToWitnessPublicKeyHash = 'witnesspubkeyhash';
|
132 |
|
133 | Address.PayToWitnessScriptHash = 'witnessscripthash';
|
134 |
|
135 | Address.PayToTaproot = 'taproot';
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 | Address._transformHash = function(hash, network, type) {
|
145 | var info = {};
|
146 | if (!(hash instanceof Buffer) && !(hash instanceof Uint8Array)) {
|
147 | throw new TypeError('Address supplied is not a buffer.');
|
148 | }
|
149 | if (hash.length !== 20 && hash.length !== 32) {
|
150 | throw new TypeError('Address hashbuffers must be either 20 or 32 bytes.');
|
151 | }
|
152 | info.hashBuffer = hash;
|
153 | info.network = Networks.get(network) || Networks.defaultNetwork;
|
154 | info.type = type;
|
155 | return info;
|
156 | };
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 | Address._transformObject = function(data) {
|
167 | $.checkArgument(data.hash || data.hashBuffer, 'Must provide a `hash` or `hashBuffer` property');
|
168 | $.checkArgument(data.type, 'Must provide a `type` property');
|
169 | return {
|
170 | hashBuffer: data.hash ? Buffer.from(data.hash, 'hex') : data.hashBuffer,
|
171 | network: Networks.get(data.network) || Networks.defaultNetwork,
|
172 | type: data.type
|
173 | };
|
174 | };
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 | Address._classifyFromVersion = function(buffer) {
|
184 | var version = {};
|
185 |
|
186 | if (buffer.length > 21) {
|
187 | var info = Bech32.decode(buffer.toString('utf8'));
|
188 | if (info.version !== 0 && info.version !== 1) {
|
189 | throw new TypeError('Only witness v0 and v1 addresses are supported.');
|
190 | }
|
191 |
|
192 | if (info.version === 0) {
|
193 | if (info.data.length === 20) {
|
194 | version.type = Address.PayToWitnessPublicKeyHash;
|
195 | } else if (info.data.length === 32) {
|
196 | version.type = Address.PayToWitnessScriptHash;
|
197 | } else {
|
198 | throw new TypeError('Witness data must be either 20 or 32 bytes.')
|
199 | }
|
200 | } else if (info.version === 1) {
|
201 | if (info.data.length === 32) {
|
202 | version.type = Address.PayToTaproot;
|
203 | } else {
|
204 | throw new TypeError('Witness data must be 32 bytes for v1');
|
205 | }
|
206 | } else {
|
207 | }
|
208 | version.network = Networks.get(info.prefix, 'bech32prefix');
|
209 | } else {
|
210 |
|
211 | var pubkeyhashNetwork = Networks.get(buffer[0], 'pubkeyhash');
|
212 | var scripthashNetwork = Networks.get(buffer[0], 'scripthash');
|
213 |
|
214 | if (pubkeyhashNetwork) {
|
215 | version.network = pubkeyhashNetwork;
|
216 | version.type = Address.PayToPublicKeyHash;
|
217 | } else if (scripthashNetwork) {
|
218 | version.network = scripthashNetwork;
|
219 | version.type = Address.PayToScriptHash;
|
220 | }
|
221 | }
|
222 |
|
223 | return version;
|
224 | };
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 | Address._transformBuffer = function(buffer, network, type) {
|
236 |
|
237 | var info = {};
|
238 | if (!(buffer instanceof Buffer) && !(buffer instanceof Uint8Array)) {
|
239 | throw new TypeError('Address supplied is not a buffer.');
|
240 | }
|
241 |
|
242 | if (buffer.length < 21) {
|
243 | throw new TypeError('Address buffer is incorrect length.');
|
244 | }
|
245 |
|
246 | var networkObj = Networks.get(network);
|
247 | var bufferVersion = Address._classifyFromVersion(buffer);
|
248 |
|
249 | if (network && !networkObj) {
|
250 | throw new TypeError('Unknown network');
|
251 | }
|
252 |
|
253 | if (!bufferVersion.network || (networkObj && networkObj.xpubkey !== bufferVersion.network.xpubkey)) {
|
254 | throw new TypeError('Address has mismatched network type.');
|
255 | }
|
256 |
|
257 | if (!bufferVersion.type || (type && type !== bufferVersion.type)) {
|
258 | throw new TypeError('Address has mismatched type.');
|
259 | }
|
260 |
|
261 | if (buffer.length > 21) {
|
262 | info.hashBuffer = Bech32.decode(buffer.toString('utf8')).data;
|
263 | } else {
|
264 | info.hashBuffer = buffer.slice(1);
|
265 | }
|
266 | info.network = bufferVersion.network;
|
267 | info.type = bufferVersion.type;
|
268 | return info;
|
269 | };
|
270 |
|
271 |
|
272 |
|
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 |
|
279 | Address._transformPublicKey = function(pubkey, network, type) {
|
280 | var info = {};
|
281 | if (!(pubkey instanceof PublicKey)) {
|
282 | throw new TypeError('Address must be an instance of PublicKey.');
|
283 | }
|
284 | if (type && type !== Address.PayToScriptHash && type !== Address.PayToWitnessPublicKeyHash && type !== Address.PayToPublicKeyHash && type !== Address.PayToTaproot) {
|
285 | throw new TypeError('Type must be either pubkeyhash, witnesspubkeyhash, scripthash, or taproot to transform public key.');
|
286 | }
|
287 | if (!pubkey.compressed && (type === Address.PayToScriptHash || type === Address.PayToWitnessPublicKeyHash)) {
|
288 | throw new TypeError('Witness addresses must use compressed public keys.');
|
289 | }
|
290 | if (type === Address.PayToScriptHash) {
|
291 | info.hashBuffer = Hash.sha256ripemd160(Script.buildWitnessV0Out(pubkey).toBuffer());
|
292 | } else if (type === Address.PayToTaproot) {
|
293 | info.hashBuffer = Hash.sha256ripemd160(Script.buildWitnessV1Out(pubkey).toBuffer());
|
294 | } else {
|
295 | info.hashBuffer = Hash.sha256ripemd160(pubkey.toBuffer());
|
296 | }
|
297 | info.type = type || Address.PayToPublicKeyHash;
|
298 | return info;
|
299 | };
|
300 |
|
301 |
|
302 |
|
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 | Address._transformScript = function(script, network) {
|
309 | $.checkArgument(script instanceof Script, 'script must be a Script instance');
|
310 | var info = script.getAddressInfo(network);
|
311 | if (!info) {
|
312 | throw new errors.Script.CantDeriveAddress(script);
|
313 | }
|
314 | return info;
|
315 | };
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 |
|
322 |
|
323 |
|
324 |
|
325 |
|
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 | Address.createMultisig = function(publicKeys, threshold, network, nestedWitness, type) {
|
332 | network = network || publicKeys[0].network || Networks.defaultNetwork;
|
333 | if (type && type !== Address.PayToScriptHash && type !== Address.PayToWitnessScriptHash) {
|
334 | throw new TypeError('Type must be either scripthash or witnessscripthash to create multisig.');
|
335 | }
|
336 | if (nestedWitness || type === Address.PayToWitnessScriptHash) {
|
337 | publicKeys = _.map(publicKeys, PublicKey);
|
338 | for (var i = 0; i < publicKeys.length; i++) {
|
339 | if (!publicKeys[i].compressed) {
|
340 | throw new TypeError('Witness addresses must use compressed public keys.');
|
341 | }
|
342 | }
|
343 | }
|
344 | var redeemScript = Script.buildMultisigOut(publicKeys, threshold);
|
345 | if (nestedWitness) {
|
346 | return Address.payingTo(Script.buildWitnessMultisigOutFromScript(redeemScript), network);
|
347 | }
|
348 | return Address.payingTo(redeemScript, network, type);
|
349 | };
|
350 |
|
351 |
|
352 |
|
353 |
|
354 |
|
355 |
|
356 |
|
357 |
|
358 |
|
359 |
|
360 | Address._transformString = function(data, network, type) {
|
361 | if (typeof(data) !== 'string') {
|
362 | throw new TypeError('data parameter supplied is not a string.');
|
363 | }
|
364 |
|
365 | if(data.length > 100) {
|
366 | throw new TypeError('address string is too long');
|
367 | }
|
368 |
|
369 | if (network && !Networks.get(network)) {
|
370 | throw new TypeError('Unknown network');
|
371 | }
|
372 |
|
373 | data = data.trim();
|
374 |
|
375 | try {
|
376 | var info = Address._transformBuffer(Buffer.from(data, 'utf8'), network, type);
|
377 | return info;
|
378 | } catch (e) {
|
379 | if (type === Address.PayToWitnessPublicKeyHash || type === Address.PayToWitnessScriptHash || type === Address.PayToTaproot) {
|
380 | throw e;
|
381 | }
|
382 | }
|
383 |
|
384 | var addressBuffer = Base58Check.decode(data);
|
385 | var info = Address._transformBuffer(addressBuffer, network, type);
|
386 | return info;
|
387 | };
|
388 |
|
389 |
|
390 |
|
391 |
|
392 |
|
393 |
|
394 |
|
395 |
|
396 |
|
397 | Address.fromPublicKey = function(data, network, type) {
|
398 | var info = Address._transformPublicKey(data, network, type);
|
399 | network = network || Networks.defaultNetwork;
|
400 | return new Address(info.hashBuffer, network, info.type);
|
401 | };
|
402 |
|
403 |
|
404 |
|
405 |
|
406 |
|
407 |
|
408 |
|
409 |
|
410 | Address.fromPublicKeyHash = function(hash, network) {
|
411 | var info = Address._transformHash(hash);
|
412 | return new Address(info.hashBuffer, network, Address.PayToPublicKeyHash);
|
413 | };
|
414 |
|
415 |
|
416 |
|
417 |
|
418 |
|
419 |
|
420 |
|
421 |
|
422 |
|
423 | Address.fromScriptHash = function(hash, network, type) {
|
424 | $.checkArgument(hash, 'hash parameter is required');
|
425 | var info = Address._transformHash(hash);
|
426 | if (type === Address.PayToWitnessScriptHash && hash.length !== 32) {
|
427 | throw new TypeError('Address hashbuffer must be exactly 32 bytes for v0 witness script hash.');
|
428 | }
|
429 | var type = type || Address.PayToScriptHash;
|
430 | return new Address(info.hashBuffer, network, type);
|
431 | };
|
432 |
|
433 |
|
434 |
|
435 |
|
436 |
|
437 |
|
438 |
|
439 |
|
440 |
|
441 |
|
442 |
|
443 |
|
444 | Address.payingTo = function(script, network, type) {
|
445 | $.checkArgument(script, 'script is required');
|
446 | $.checkArgument(script instanceof Script, 'script must be instance of Script');
|
447 | var hash;
|
448 | if (type === Address.PayToWitnessScriptHash) {
|
449 | hash = Hash.sha256(script.toBuffer());
|
450 | } else {
|
451 | hash = Hash.sha256ripemd160(script.toBuffer());
|
452 | }
|
453 | var type = type || Address.PayToScriptHash;
|
454 | return Address.fromScriptHash(hash, network, type);
|
455 | };
|
456 |
|
457 |
|
458 |
|
459 |
|
460 |
|
461 |
|
462 |
|
463 |
|
464 |
|
465 |
|
466 |
|
467 |
|
468 |
|
469 | Address.fromScript = function(script, network) {
|
470 | $.checkArgument(script instanceof Script, 'script must be a Script instance');
|
471 | var info = Address._transformScript(script, network);
|
472 | return new Address(info.hashBuffer, network, info.type);
|
473 | };
|
474 |
|
475 |
|
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 | Address.fromBuffer = function(buffer, network, type) {
|
484 | var info = Address._transformBuffer(buffer, network, type);
|
485 | return new Address(info.hashBuffer, info.network, info.type);
|
486 | };
|
487 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 |
|
493 |
|
494 |
|
495 |
|
496 | Address.fromString = function(str, network, type) {
|
497 | var info = Address._transformString(str, network, type);
|
498 | return new Address(info.hashBuffer, info.network, info.type);
|
499 | };
|
500 |
|
501 |
|
502 |
|
503 |
|
504 |
|
505 |
|
506 |
|
507 | Address.fromObject = function fromObject(obj) {
|
508 | $.checkState(
|
509 | JSUtil.isHexa(obj.hash),
|
510 | 'Unexpected hash property, "' + obj.hash + '", expected to be hex.'
|
511 | );
|
512 | var hashBuffer = Buffer.from(obj.hash, 'hex');
|
513 | return new Address(hashBuffer, obj.network, obj.type);
|
514 | };
|
515 |
|
516 |
|
517 |
|
518 |
|
519 |
|
520 |
|
521 |
|
522 |
|
523 |
|
524 |
|
525 |
|
526 |
|
527 |
|
528 |
|
529 |
|
530 | Address.getValidationError = function(data, network, type) {
|
531 | var error;
|
532 | try {
|
533 |
|
534 | new Address(data, network, type);
|
535 | } catch (e) {
|
536 | error = e;
|
537 | }
|
538 | return error;
|
539 | };
|
540 |
|
541 |
|
542 |
|
543 |
|
544 |
|
545 |
|
546 |
|
547 |
|
548 |
|
549 |
|
550 |
|
551 |
|
552 |
|
553 |
|
554 | Address.isValid = function(data, network, type) {
|
555 | return !Address.getValidationError(data, network, type);
|
556 | };
|
557 |
|
558 |
|
559 |
|
560 |
|
561 |
|
562 | Address.prototype.isPayToPublicKeyHash = function() {
|
563 | return this.type === Address.PayToPublicKeyHash;
|
564 | };
|
565 |
|
566 |
|
567 |
|
568 |
|
569 |
|
570 | Address.prototype.isPayToScriptHash = function() {
|
571 | return this.type === Address.PayToScriptHash;
|
572 | };
|
573 |
|
574 |
|
575 |
|
576 |
|
577 |
|
578 | Address.prototype.isPayToWitnessPublicKeyHash = function() {
|
579 | return this.type === Address.PayToWitnessPublicKeyHash;
|
580 | };
|
581 |
|
582 |
|
583 |
|
584 |
|
585 |
|
586 | Address.prototype.isPayToWitnessScriptHash = function() {
|
587 | return this.type === Address.PayToWitnessScriptHash;
|
588 | };
|
589 |
|
590 |
|
591 |
|
592 |
|
593 |
|
594 | Address.prototype.isPayToTaproot = function() {
|
595 | return this.type === Address.PayToTaproot;
|
596 | }
|
597 |
|
598 |
|
599 |
|
600 |
|
601 |
|
602 |
|
603 | Address.prototype.toBuffer = function() {
|
604 | if (this.isPayToWitnessPublicKeyHash() || this.isPayToWitnessScriptHash()) {
|
605 | return Buffer.from(this.toString(), 'utf8')
|
606 | }
|
607 | var version = Buffer.from([this.network[this.type]]);
|
608 | return Buffer.concat([version, this.hashBuffer]);
|
609 | };
|
610 |
|
611 |
|
612 |
|
613 |
|
614 | Address.prototype.toObject = Address.prototype.toJSON = function toObject() {
|
615 | return {
|
616 | hash: this.hashBuffer.toString('hex'),
|
617 | type: this.type,
|
618 | network: this.network.toString()
|
619 | };
|
620 | };
|
621 |
|
622 |
|
623 |
|
624 |
|
625 |
|
626 |
|
627 | Address.prototype.toString = function() {
|
628 | if (this.isPayToWitnessPublicKeyHash() || this.isPayToWitnessScriptHash() || this.isPayToTaproot()) {
|
629 | let prefix = this.network.bech32prefix;
|
630 | let version = 0;
|
631 | let encoding = Bech32.encodings.BECH32;
|
632 | if (this.isPayToTaproot()) {
|
633 | version = 1;
|
634 | encoding = Bech32.encodings.BECH32M;
|
635 | }
|
636 | return Bech32.encode(prefix, version, this.hashBuffer, encoding);
|
637 | }
|
638 | return Base58Check.encode(this.toBuffer());
|
639 | };
|
640 |
|
641 |
|
642 |
|
643 |
|
644 |
|
645 |
|
646 | Address.prototype.inspect = function() {
|
647 | return '<Address: ' + this.toString() + ', type: ' + this.type + ', network: ' + this.network + '>';
|
648 | };
|
649 |
|
650 | module.exports = Address;
|
651 |
|
652 | var Script = require('./script');
|