@scure/btc-signer 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +196 -37
- package/index.d.ts +4 -4
- package/index.js +3 -4
- package/musig2.d.ts +28 -9
- package/musig2.js +46 -18
- package/net.d.ts +355 -0
- package/net.js +880 -0
- package/p2p.d.ts +0 -1
- package/p2p.js +23 -7
- package/package.json +18 -21
- package/payment.d.ts +18 -11
- package/payment.js +203 -58
- package/psbt.d.ts +680 -10
- package/psbt.js +204 -43
- package/script.d.ts +1 -2
- package/script.js +71 -59
- package/src/_type_test.ts +83 -0
- package/src/index.ts +4 -2
- package/src/musig2.ts +71 -19
- package/src/net.ts +1111 -0
- package/src/p2p.ts +22 -6
- package/src/payment.ts +233 -70
- package/src/psbt.ts +228 -39
- package/src/script.ts +57 -35
- package/src/transaction.ts +869 -168
- package/src/utils.ts +92 -4
- package/src/utxo.ts +223 -113
- package/transaction.d.ts +40 -7
- package/transaction.js +745 -151
- package/utils.d.ts +45 -2
- package/utils.js +80 -5
- package/utxo.d.ts +192 -2
- package/utxo.js +200 -99
- package/index.d.ts.map +0 -1
- package/index.js.map +0 -1
- package/musig2.d.ts.map +0 -1
- package/musig2.js.map +0 -1
- package/p2p.d.ts.map +0 -1
- package/p2p.js.map +0 -1
- package/payment.d.ts.map +0 -1
- package/payment.js.map +0 -1
- package/psbt.d.ts.map +0 -1
- package/psbt.js.map +0 -1
- package/script.d.ts.map +0 -1
- package/script.js.map +0 -1
- package/transaction.d.ts.map +0 -1
- package/transaction.js.map +0 -1
- package/utils.d.ts.map +0 -1
- package/utils.js.map +0 -1
- package/utxo.d.ts.map +0 -1
- package/utxo.js.map +0 -1
package/src/payment.ts
CHANGED
|
@@ -29,11 +29,18 @@ export type P2Ret = {
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
// Pay to Anchor (P2A)
|
|
32
|
+
// BIP433 Pay-to-Anchor witness program bytes; the scriptPubKey is `OP_1 <0x4e73>`.
|
|
33
|
+
const P2A_PROGRAM = /* @__PURE__ */ Uint8Array.from([0x4e, 0x73]);
|
|
32
34
|
type OutP2AType = { type: 'p2a'; script: Bytes };
|
|
33
35
|
const OutP2A: Coder<OptScript, OutP2AType | undefined> = {
|
|
34
36
|
encode(from: TArg<ScriptType>): TRet<OutP2AType | undefined> {
|
|
35
37
|
// BIP433 defines P2A as the exact OP_1 <0x4e73> scriptPubKey.
|
|
36
|
-
if (
|
|
38
|
+
if (
|
|
39
|
+
from.length !== 2 ||
|
|
40
|
+
from[0] !== 1 ||
|
|
41
|
+
!u.isBytes(from[1]) ||
|
|
42
|
+
!u.equalBytes(from[1], P2A_PROGRAM)
|
|
43
|
+
)
|
|
37
44
|
return;
|
|
38
45
|
return { type: 'p2a', script: Script.encode(from) } as TRet<OutP2AType | undefined>;
|
|
39
46
|
},
|
|
@@ -41,7 +48,7 @@ const OutP2A: Coder<OptScript, OutP2AType | undefined> = {
|
|
|
41
48
|
if (to.type !== 'p2a') return;
|
|
42
49
|
// The decoded object keeps `script` for caller convenience, but the `p2a`
|
|
43
50
|
// tag always canonicalizes back to the fixed BIP433 script.
|
|
44
|
-
return [1,
|
|
51
|
+
return [1, Uint8Array.from(P2A_PROGRAM)] as TRet<OptScript>;
|
|
45
52
|
},
|
|
46
53
|
};
|
|
47
54
|
|
|
@@ -87,8 +94,9 @@ const OutPKH: Coder<OptScript, OutPKHType | undefined> = {
|
|
|
87
94
|
encode(from: TArg<ScriptType>): TRet<OutPKHType | undefined> {
|
|
88
95
|
if (from.length !== 5 || from[0] !== 'DUP' || from[1] !== 'HASH160' || !u.isBytes(from[2]))
|
|
89
96
|
return;
|
|
90
|
-
//
|
|
91
|
-
//
|
|
97
|
+
// Require the exact 20-byte HASH160 here so near-miss scripts fall through
|
|
98
|
+
// to OutUnknown instead of throwing in the OutScript validator on decode.
|
|
99
|
+
if (from[2].length !== 20) return;
|
|
92
100
|
if (from[3] !== 'EQUALVERIFY' || from[4] !== 'CHECKSIG') return;
|
|
93
101
|
return { type: 'pkh', hash: from[2] } as TRet<OutPKHType | undefined>;
|
|
94
102
|
},
|
|
@@ -105,8 +113,9 @@ const OutSH: Coder<OptScript, OutSHType | undefined> = {
|
|
|
105
113
|
encode(from: TArg<ScriptType>): TRet<OutSHType | undefined> {
|
|
106
114
|
if (from.length !== 3 || from[0] !== 'HASH160' || !u.isBytes(from[1]) || from[2] !== 'EQUAL')
|
|
107
115
|
return;
|
|
108
|
-
//
|
|
109
|
-
//
|
|
116
|
+
// Require the exact 20-byte HASH160 here so near-miss scripts fall through
|
|
117
|
+
// to OutUnknown instead of throwing in the OutScript validator on decode.
|
|
118
|
+
if (from[1].length !== 20) return;
|
|
110
119
|
return { type: 'sh', hash: from[1] } as TRet<OutSHType | undefined>;
|
|
111
120
|
},
|
|
112
121
|
// OutScript validates `sh.hash` before this branch emits the canonical
|
|
@@ -159,9 +168,12 @@ const OutMS: Coder<OptScript, OutMSType | undefined> = {
|
|
|
159
168
|
if (typeof m !== 'number' || typeof n !== 'number') return;
|
|
160
169
|
const pubkeys = from.slice(1, -2);
|
|
161
170
|
if (n !== pubkeys.length) return;
|
|
162
|
-
|
|
163
|
-
//
|
|
164
|
-
//
|
|
171
|
+
// Require valid ECDSA pubkeys and `0 < m <= n` here so near-miss
|
|
172
|
+
// CHECKMULTISIG scripts (garbage keys, degenerate 0-of-0) fall through to
|
|
173
|
+
// OutUnknown instead of throwing in the OutScript validator on decode.
|
|
174
|
+
// Script.decode only yields 0..16 for opcode numbers, so n <= 16 holds.
|
|
175
|
+
for (const pub of pubkeys) if (!u.isBytes(pub) || !isValidPubkey(pub, u.PubT.ecdsa)) return;
|
|
176
|
+
if (!Number.isSafeInteger(m) || m < 1 || m > n) return;
|
|
165
177
|
// We don't need n here because it is the same as pubkeys.length.
|
|
166
178
|
return { type: 'ms', m, pubkeys: pubkeys as Bytes[] } as TRet<OutMSType | undefined>;
|
|
167
179
|
},
|
|
@@ -182,6 +194,10 @@ const OutTR: Coder<OptScript, OutTRType | undefined> = {
|
|
|
182
194
|
// BIP341 assigns native taproot meaning only to version 1 with a 32-byte x-only program;
|
|
183
195
|
// other OP_1 program lengths remain reserved future witness programs and should fall through.
|
|
184
196
|
if (from.length !== 2 || from[0] !== 1 || !u.isBytes(from[1]) || from[1].length !== 32) return;
|
|
197
|
+
// A 32-byte v1 program with an off-curve x coordinate is a fundable but
|
|
198
|
+
// taproot-unspendable output; classify it as unknown instead of throwing
|
|
199
|
+
// in the OutScript validator on decode.
|
|
200
|
+
if (!isValidPubkey(from[1], u.PubT.schnorr)) return;
|
|
185
201
|
return { type: 'tr', pubkey: from[1] } as TRet<OutTRType | undefined>;
|
|
186
202
|
},
|
|
187
203
|
// OutScript validates `tr.pubkey` before this branch emits the canonical
|
|
@@ -212,7 +228,7 @@ const OutTRNS: Coder<OptScript, OutTRNSType | undefined> = {
|
|
|
212
228
|
// BIP342 "Using a k-of-k script for every combination" documents the shape
|
|
213
229
|
// `<pubkey_1> CHECKSIGVERIFY ... <pubkey_n> CHECKSIG`; this matcher only
|
|
214
230
|
// classifies that embedded-pubkey form, so bare CHECKSIG stays unknown.
|
|
215
|
-
if (!pubkeys.length) return;
|
|
231
|
+
if (!pubkeys.length || pubkeys.length > 999) return;
|
|
216
232
|
return { type: 'tr_ns', pubkeys } as TRet<OutTRNSType | undefined>;
|
|
217
233
|
},
|
|
218
234
|
decode: (to: TArg<OutTRNSType>): TRet<OptScript> => {
|
|
@@ -243,9 +259,13 @@ const OutTRMS: Coder<OptScript, OutTRMSType | undefined> = {
|
|
|
243
259
|
if (elm !== (i === 1 ? 'CHECKSIG' : 'CHECKSIGADD')) return;
|
|
244
260
|
continue;
|
|
245
261
|
}
|
|
246
|
-
|
|
262
|
+
// Require actual Schnorr pubkeys here (same as tr_ns) so near-miss
|
|
263
|
+
// CHECKSIGADD scripts fall through to OutUnknown instead of throwing
|
|
264
|
+
// in the OutScript validator on decode.
|
|
265
|
+
if (!u.isBytes(elm) || !isValidPubkey(elm, u.PubT.schnorr)) return;
|
|
247
266
|
pubkeys.push(elm);
|
|
248
267
|
}
|
|
268
|
+
if (!Number.isSafeInteger(m) || m < 1 || m > pubkeys.length || pubkeys.length > 999) return;
|
|
249
269
|
return { type: 'tr_ms', pubkeys, m } as TRet<OutTRMSType | undefined>;
|
|
250
270
|
},
|
|
251
271
|
decode: (to: TArg<OutTRMSType>): TRet<OptScript> => {
|
|
@@ -373,16 +393,18 @@ export const OutScript: TRet<
|
|
|
373
393
|
if (i.m <= 0 || n > 16 || i.m > n) throw new Error('OutScript/multisig: invalid params');
|
|
374
394
|
}
|
|
375
395
|
if (i.type === 'tr_ns' || i.type === 'tr_ms') {
|
|
396
|
+
const n = i.pubkeys.length;
|
|
397
|
+
// BIP342 keeps the 1,000-element stack limit. Both supported tapscript multisig forms
|
|
398
|
+
// start with n signatures and then push a pubkey, so n must stay at or below 999.
|
|
399
|
+
if (n > 999) throw new Error(`OutScript/${i.type}: invalid params`);
|
|
376
400
|
for (const p of i.pubkeys)
|
|
377
401
|
if (!isValidPubkey(p, u.PubT.schnorr))
|
|
378
402
|
throw new Error(`OutScript/${i.type}: wrong pubkey`);
|
|
379
403
|
}
|
|
380
404
|
if (i.type === 'tr_ms') {
|
|
381
405
|
const n = i.pubkeys.length;
|
|
382
|
-
// BIP 342 keeps the 1000-element stack limit. This CHECKSIG/CHECKSIGADD form
|
|
383
|
-
// momentarily has n witness items plus one pushed pubkey on the stack, so n must stay <= 999.
|
|
384
406
|
anumber(i.m, 'm');
|
|
385
|
-
if (i.m <= 0 ||
|
|
407
|
+
if (i.m <= 0 || i.m > n) throw new Error('OutScript/tr_ms: invalid params');
|
|
386
408
|
}
|
|
387
409
|
return i;
|
|
388
410
|
})
|
|
@@ -406,9 +428,32 @@ export const OutScript: TRet<
|
|
|
406
428
|
>;
|
|
407
429
|
/** Type of the output-script coder. */
|
|
408
430
|
export type OutScriptType = typeof OutScript;
|
|
431
|
+
// Internal raw-aware boundary for actual scriptPubKeys and P2SH-nested witness programs. Generic
|
|
432
|
+
// OutScript remains semantic because witnessScript and tapscript children may use the same opcodes
|
|
433
|
+
// without claiming BIP141 witness-program framing.
|
|
434
|
+
export const _WitnessOutScript: OutScriptType = /* @__PURE__ */ (() =>
|
|
435
|
+
Object.freeze(
|
|
436
|
+
P.wrap<P.UnwrapCoder<typeof _OutScript>>({
|
|
437
|
+
encodeStream: (w, value) => OutScript.encodeStream(w, value),
|
|
438
|
+
decodeStream: (r) => {
|
|
439
|
+
const raw = r.bytes(r.leftBytes);
|
|
440
|
+
const out = OutScript.decode(raw);
|
|
441
|
+
// Script.decode normalizes push opcodes, but BIP141 recognition requires the exact direct
|
|
442
|
+
// push. Reject only when this explicit outer-program boundary is selected by the caller.
|
|
443
|
+
if (
|
|
444
|
+
out &&
|
|
445
|
+
(out.type === 'p2a' || out.type === 'wpkh' || out.type === 'wsh' || out.type === 'tr') &&
|
|
446
|
+
!u.equalBytes(raw, OutScript.encode(out))
|
|
447
|
+
)
|
|
448
|
+
throw new Error('OutScript: non-canonical witness program');
|
|
449
|
+
return out;
|
|
450
|
+
},
|
|
451
|
+
})
|
|
452
|
+
))() as OutScriptType;
|
|
409
453
|
// TRet-wrapping OutScript changes decode() to the normalized descriptor surface, but the local
|
|
410
454
|
// checkScript/Address caches still need an explicit alias that can carry the decode-side `undefined`.
|
|
411
|
-
type
|
|
455
|
+
type AddressValue = NonNullable<ReturnType<OutScriptType['decode']>>;
|
|
456
|
+
type OutScriptValue = AddressValue | undefined;
|
|
412
457
|
|
|
413
458
|
// Basic sanity check for scripts
|
|
414
459
|
function checkWSH(s: TArg<OutWSHType>, witnessScript: TArg<Bytes>) {
|
|
@@ -449,7 +494,7 @@ export function checkScript(
|
|
|
449
494
|
let hasWsh = false;
|
|
450
495
|
let r: OutScriptValue = undefined;
|
|
451
496
|
if (script) {
|
|
452
|
-
const s =
|
|
497
|
+
const s = _WitnessOutScript.decode(script);
|
|
453
498
|
// BIP174 Data Signers Check For bullets: provided redeemScript must match
|
|
454
499
|
// the scriptPubKey, and provided witnessScript must match the scriptPubKey
|
|
455
500
|
// or redeemScript instead of being silently ignored as stray metadata.
|
|
@@ -460,7 +505,7 @@ export function checkScript(
|
|
|
460
505
|
if (s.type !== 'sh') throw new Error('checkScript: redeemScript without P2SH');
|
|
461
506
|
if (!u.equalBytes(s.hash, u.hash160(redeemScript)))
|
|
462
507
|
throw new Error('checkScript: sh wrong redeemScript hash');
|
|
463
|
-
r =
|
|
508
|
+
r = _WitnessOutScript.decode(redeemScript) as OutScriptValue;
|
|
464
509
|
if (r?.type === 'tr' || r?.type === 'tr_ns' || r?.type === 'tr_ms')
|
|
465
510
|
throw new Error(`checkScript: P2${r.type} cannot be wrapped in P2SH`);
|
|
466
511
|
// Not sure if this unspendable, but we cannot represent this via PSBT
|
|
@@ -472,7 +517,7 @@ export function checkScript(
|
|
|
472
517
|
}
|
|
473
518
|
}
|
|
474
519
|
if (redeemScript) {
|
|
475
|
-
if (r === undefined) r =
|
|
520
|
+
if (r === undefined) r = _WitnessOutScript.decode(redeemScript) as OutScriptValue;
|
|
476
521
|
if (r?.type === 'wsh') {
|
|
477
522
|
hasWsh = true;
|
|
478
523
|
if (witnessScript) checkWSH(r as TArg<OutWSHType>, witnessScript);
|
|
@@ -481,12 +526,21 @@ export function checkScript(
|
|
|
481
526
|
if (witnessScript && !hasWsh) throw new Error('checkScript: witnessScript without P2WSH');
|
|
482
527
|
}
|
|
483
528
|
|
|
484
|
-
function uniqPubkey(pubkeys: TArg<Bytes[]
|
|
529
|
+
function uniqPubkey(pubkeys: TArg<Bytes[]>, type: u.PubT) {
|
|
485
530
|
const map: Record<string, boolean> = {};
|
|
486
531
|
for (const pub of pubkeys) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
532
|
+
u.validatePubkey(pub, type);
|
|
533
|
+
let normalized = pub;
|
|
534
|
+
if (type === u.PubT.ecdsa && pub.length === 65) {
|
|
535
|
+
// Compressed and uncompressed SEC1 encodings can represent the same curve point. Normalize
|
|
536
|
+
// valid uncompressed keys before comparing so one signer cannot occupy multiple threshold
|
|
537
|
+
// slots merely by changing the serialization. `allowSamePubkeys` remains the explicit
|
|
538
|
+
// compatibility escape hatch for callers that intentionally construct duplicate-key scripts.
|
|
539
|
+
normalized = new Uint8Array(33);
|
|
540
|
+
normalized[0] = 2 | (pub[64] & 1);
|
|
541
|
+
normalized.set(pub.subarray(1, 33), 1);
|
|
542
|
+
}
|
|
543
|
+
const key = hex.encode(normalized);
|
|
490
544
|
if (map[key]) throw new Error(`Multisig: non-uniq pubkey: ${pubkeys.map(hex.encode)}`);
|
|
491
545
|
map[key] = true;
|
|
492
546
|
}
|
|
@@ -588,10 +642,30 @@ export type P2SHWithoutWitness = Omit<P2SHBase, 'witnessScript'>;
|
|
|
588
642
|
export type P2SHReturn<T extends P2Ret> = T extends { witnessScript: Bytes }
|
|
589
643
|
? P2SHWithWitness
|
|
590
644
|
: P2SHWithoutWitness;
|
|
645
|
+
|
|
646
|
+
const checkCanonicalScript = (
|
|
647
|
+
script: TArg<Bytes>,
|
|
648
|
+
name: 'redeemScript' | 'witnessScript',
|
|
649
|
+
allowNonCanonicalScript: boolean
|
|
650
|
+
): void => {
|
|
651
|
+
if (allowNonCanonicalScript) return;
|
|
652
|
+
const decoded = Script.decode(script);
|
|
653
|
+
const nonMinimalNumber = decoded.some(
|
|
654
|
+
(op) => u.isBytes(op) && op.length === 1 && ((1 <= op[0] && op[0] <= 16) || op[0] === 0x81)
|
|
655
|
+
);
|
|
656
|
+
// Core's default MINIMALDATA policy rejects these spends from its mempool, so address-producing
|
|
657
|
+
// helpers require an explicit opt-in even though the original bytes remain consensus-valid.
|
|
658
|
+
if (nonMinimalNumber || !u.equalBytes(script, Script.encode(decoded))) {
|
|
659
|
+
const wrapper = name === 'redeemScript' ? 'P2SH' : 'P2WSH';
|
|
660
|
+
throw new Error(`${wrapper}: non-canonical ${name}`);
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
|
|
591
664
|
/**
|
|
592
665
|
* Wraps a child script inside P2SH.
|
|
593
666
|
* @param child - child payment descriptor to wrap
|
|
594
667
|
* @param network - address network parameters
|
|
668
|
+
* @param allowNonCanonicalScript - whether to create an address for a non-minimal child script
|
|
595
669
|
* @returns P2SH descriptor preserving witness metadata when present.
|
|
596
670
|
* @throws If the wrapped script combination is invalid or unsupported. {@link Error}
|
|
597
671
|
* @example
|
|
@@ -604,8 +678,10 @@ export type P2SHReturn<T extends P2Ret> = T extends { witnessScript: Bytes }
|
|
|
604
678
|
*/
|
|
605
679
|
export const p2sh = <T extends P2Ret>(
|
|
606
680
|
child: TArg<T>,
|
|
607
|
-
network: BTC_NETWORK = NETWORK
|
|
681
|
+
network: BTC_NETWORK = NETWORK,
|
|
682
|
+
allowNonCanonicalScript = false
|
|
608
683
|
): TRet<Extends<P2SHReturn<T>, P2Ret>> => {
|
|
684
|
+
u.validateObject(child as Record<string, any>, {}, {}, 'child');
|
|
609
685
|
// It is already tested inside noble-hashes and checkScript
|
|
610
686
|
// BIP16 redeemScripts are pushed by scriptSig, so anything over the 520-byte pushed-element
|
|
611
687
|
// limit would be fundable by HASH160 but unspendable once wrapped in P2SH.
|
|
@@ -616,6 +692,7 @@ export const p2sh = <T extends P2Ret>(
|
|
|
616
692
|
throw new Error(
|
|
617
693
|
`P2SH: redeemScript exceeds ${MAX_SCRIPT_BYTE_LENGTH}-byte push limit: len=${cs.length}`
|
|
618
694
|
);
|
|
695
|
+
checkCanonicalScript(cs, 'redeemScript', allowNonCanonicalScript);
|
|
619
696
|
const hash = u.hash160(cs);
|
|
620
697
|
const out = { type: 'sh', hash } as const;
|
|
621
698
|
const script = OutScript.encode(out);
|
|
@@ -658,6 +735,7 @@ export type P2WSH = {
|
|
|
658
735
|
* Wraps a child script inside native SegWit P2WSH.
|
|
659
736
|
* @param child - child payment descriptor to wrap
|
|
660
737
|
* @param network - address network parameters
|
|
738
|
+
* @param allowNonCanonicalScript - whether to create an address for a non-minimal child script
|
|
661
739
|
* @returns P2WSH descriptor.
|
|
662
740
|
* @throws If the wrapped script combination is invalid or unsupported. {@link Error}
|
|
663
741
|
* @example
|
|
@@ -670,13 +748,16 @@ export type P2WSH = {
|
|
|
670
748
|
*/
|
|
671
749
|
export const p2wsh = (
|
|
672
750
|
child: TArg<P2Ret>,
|
|
673
|
-
network: BTC_NETWORK = NETWORK
|
|
751
|
+
network: BTC_NETWORK = NETWORK,
|
|
752
|
+
allowNonCanonicalScript = false
|
|
674
753
|
): TRet<Extends<P2WSH, P2Ret>> => {
|
|
754
|
+
u.validateObject(child as Record<string, any>, {}, {}, 'child');
|
|
675
755
|
const cs = child.script;
|
|
676
756
|
if (!u.isBytes(cs)) throw new Error(`Wrong script: ${typeof cs}, expected Uint8Array`);
|
|
677
757
|
// BIP141 P2WSH says the witness "must consist of ... a serialized script (witnessScript)"
|
|
678
758
|
// and that witnessScript is limited to 10,000 bytes, so larger wrapped scripts must reject.
|
|
679
759
|
if (cs.length > 10000) throw new Error('P2WSH: witnessScript exceeds 10,000 bytes');
|
|
760
|
+
checkCanonicalScript(cs, 'witnessScript', allowNonCanonicalScript);
|
|
680
761
|
const hash = u.sha256(cs);
|
|
681
762
|
const script = OutScript.encode({ type: 'wsh', hash });
|
|
682
763
|
checkScript(script, undefined, cs);
|
|
@@ -759,7 +840,7 @@ export const p2ms = (
|
|
|
759
840
|
): TRet<Extends<P2MS, P2Ret>> => {
|
|
760
841
|
// BIP 11 only standardized bare multisig up to 3 keys; this helper still permits up to 16
|
|
761
842
|
// because the same script shape is commonly wrapped by p2sh()/p2wsh() instead of used bare.
|
|
762
|
-
if (!allowSamePubkeys) uniqPubkey(pubkeys);
|
|
843
|
+
if (!allowSamePubkeys) uniqPubkey(pubkeys, u.PubT.ecdsa);
|
|
763
844
|
return {
|
|
764
845
|
type: 'ms',
|
|
765
846
|
script: OutScript.encode({ type: 'ms', pubkeys, m }),
|
|
@@ -782,9 +863,16 @@ function checkTaprootScript(
|
|
|
782
863
|
// disable custom. All custom scripts for taproot should have prefix 'tr_'
|
|
783
864
|
if (customScripts) {
|
|
784
865
|
const cs = P.apply(Script, P.coders.match(customScripts));
|
|
785
|
-
|
|
866
|
+
let c;
|
|
867
|
+
// match() throws when no custom coder matches; treat that as "not a custom
|
|
868
|
+
// script" so the allowUnknownOutputs escape below stays reachable.
|
|
869
|
+
try {
|
|
870
|
+
c = cs.decode(script);
|
|
871
|
+
} catch (e) {
|
|
872
|
+
c = undefined;
|
|
873
|
+
}
|
|
786
874
|
if (c !== undefined) {
|
|
787
|
-
if (
|
|
875
|
+
if (!u.astring(c.type, 'c.type').startsWith('tr_'))
|
|
788
876
|
throw new Error(`P2TR: invalid custom type=${c.type}`);
|
|
789
877
|
return;
|
|
790
878
|
}
|
|
@@ -796,7 +884,7 @@ function checkTaprootScript(
|
|
|
796
884
|
const outms = out as OutTRNSType | OutTRMSType;
|
|
797
885
|
if (!allowUnknownOutputs && outms.pubkeys) {
|
|
798
886
|
for (const p of outms.pubkeys) {
|
|
799
|
-
if (u.equalBytes(p, u.
|
|
887
|
+
if (u.equalBytes(p, u.taprootNumsKey()))
|
|
800
888
|
throw new Error('Unspendable taproot key in leaf script');
|
|
801
889
|
// It's likely a mistake at this point:
|
|
802
890
|
// 1. p2tr(A, p2tr_ns(2, [A, B])) == p2tr(A, p2tr_pk(B)) (A or B key)
|
|
@@ -867,6 +955,13 @@ type _TaprootTreeInternal = {
|
|
|
867
955
|
* ```
|
|
868
956
|
*/
|
|
869
957
|
export function taprootListToTree(taprootList: TArg<TaprootScriptList>): TRet<TaprootScriptTree> {
|
|
958
|
+
u.aarray<TaprootScriptList[number]>(taprootList, 'taprootList', (leaf, title) => {
|
|
959
|
+
// p2tr reduces non-binary trees through this helper, so nested branch arrays are valid here.
|
|
960
|
+
if (Array.isArray(leaf)) return;
|
|
961
|
+
u.validateObject(leaf as Record<string, any>, {}, {}, title);
|
|
962
|
+
// This helper only arranges weighted tree nodes; p2tr validates leaf scripts while hashing.
|
|
963
|
+
if (leaf.weight !== undefined) anumber(leaf.weight, title + '.weight');
|
|
964
|
+
});
|
|
870
965
|
// Empty flat lists cannot represent a taproot script tree; omit the tree entirely for
|
|
871
966
|
// key-path-only outputs instead of passing [] here, otherwise this helper would return
|
|
872
967
|
// undefined and downstream taproot tree walkers would fail much later on a non-tree value.
|
|
@@ -941,23 +1036,35 @@ function taprootWalkTree(tree: TArg<HashedTreeWithPath>): TRet<TaprootLeaf[]> {
|
|
|
941
1036
|
return [...taprootWalkTree(tree.left), ...taprootWalkTree(tree.right)] as TRet<TaprootLeaf[]>;
|
|
942
1037
|
}
|
|
943
1038
|
|
|
1039
|
+
// BIP 341 control blocks can encode at most 128 sibling hashes.
|
|
1040
|
+
const TAPROOT_MAX_DEPTH = 128;
|
|
944
1041
|
function taprootHashTree(
|
|
945
1042
|
tree: TArg<TaprootScriptTree>,
|
|
946
1043
|
internalPubKey: TArg<Bytes>,
|
|
947
1044
|
allowUnknownOutputs = false,
|
|
948
|
-
customScripts?: TArg<CustomScript[]
|
|
1045
|
+
customScripts?: TArg<CustomScript[]>,
|
|
1046
|
+
depth = 0
|
|
949
1047
|
): TRet<HashedTree> {
|
|
950
|
-
|
|
1048
|
+
// Reject before inspecting or descending into the next node, which also bounds the
|
|
1049
|
+
// recursion used by the path annotation and tree-flattening passes below.
|
|
1050
|
+
if (depth > TAPROOT_MAX_DEPTH)
|
|
1051
|
+
throw new RangeError(`P2TR: tree depth exceeds ${TAPROOT_MAX_DEPTH}`);
|
|
1052
|
+
if (tree === undefined) throw new Error('taprootHashTree: empty tree');
|
|
1053
|
+
if (!Array.isArray(tree) && !P.utils.isPlainObject(tree))
|
|
1054
|
+
throw new TypeError('"tree" expected object or array, got type=' + typeof tree);
|
|
951
1055
|
if (Array.isArray(tree) && tree.length === 1) tree = tree[0];
|
|
952
1056
|
// Terminal node (leaf)
|
|
953
1057
|
if (!Array.isArray(tree)) {
|
|
1058
|
+
u.validateObject(tree as Record<string, any>, {}, {}, 'tree');
|
|
954
1059
|
const version = tree.leafVersion;
|
|
955
1060
|
const { script: leafScript } = tree;
|
|
956
1061
|
// Earliest tree walk where we can validate tapScripts
|
|
957
1062
|
if (tree.tapLeafScript || (tree.tapMerkleRoot && !u.equalBytes(tree.tapMerkleRoot, P.EMPTY)))
|
|
958
1063
|
throw new Error('P2TR: tapRoot leafScript cannot have tree');
|
|
959
|
-
const script =
|
|
960
|
-
|
|
1064
|
+
const script =
|
|
1065
|
+
typeof leafScript === 'string'
|
|
1066
|
+
? hex.decode(leafScript)
|
|
1067
|
+
: abytes(leafScript, undefined, 'tree.script');
|
|
961
1068
|
checkTaprootScript(script, internalPubKey, allowUnknownOutputs, customScripts);
|
|
962
1069
|
return {
|
|
963
1070
|
type: 'leaf',
|
|
@@ -971,8 +1078,20 @@ function taprootHashTree(
|
|
|
971
1078
|
if (tree.length !== 2) throw new Error('hashTree: non binary tree!');
|
|
972
1079
|
// branch
|
|
973
1080
|
// Both nodes should exist
|
|
974
|
-
const left = taprootHashTree(
|
|
975
|
-
|
|
1081
|
+
const left = taprootHashTree(
|
|
1082
|
+
tree[0],
|
|
1083
|
+
internalPubKey,
|
|
1084
|
+
allowUnknownOutputs,
|
|
1085
|
+
customScripts,
|
|
1086
|
+
depth + 1
|
|
1087
|
+
);
|
|
1088
|
+
const right = taprootHashTree(
|
|
1089
|
+
tree[1],
|
|
1090
|
+
internalPubKey,
|
|
1091
|
+
allowUnknownOutputs,
|
|
1092
|
+
customScripts,
|
|
1093
|
+
depth + 1
|
|
1094
|
+
);
|
|
976
1095
|
// BIP 341 sorts TapBranch child hashes lexicographically for hashing, but the original
|
|
977
1096
|
// left/right structure still determines the control-block sibling paths for each leaf.
|
|
978
1097
|
let [lH, rH] = [left.hash, right.hash];
|
|
@@ -1014,7 +1133,7 @@ export const tapLeafHash = (script: TArg<Bytes>, version: number = TAP_LEAF_VERS
|
|
|
1014
1133
|
|
|
1015
1134
|
// Works as key OR tree.
|
|
1016
1135
|
// If we only have tree, need to add unspendable key, otherwise
|
|
1017
|
-
// complex multisig wallet can be spent by owner of key only. See
|
|
1136
|
+
// complex multisig wallet can be spent by owner of key only. See taprootNumsKey
|
|
1018
1137
|
/** Conditional taproot return type for key-only or tree-backed outputs. */
|
|
1019
1138
|
export type P2TRRet<T> = T extends TaprootScriptTree ? P2TR_TREE : P2TR;
|
|
1020
1139
|
/**
|
|
@@ -1026,6 +1145,7 @@ export type P2TRRet<T> = T extends TaprootScriptTree ? P2TR_TREE : P2TR;
|
|
|
1026
1145
|
* @param customScripts - optional custom script codecs for taproot leaves
|
|
1027
1146
|
* @returns Taproot descriptor with optional script-path metadata.
|
|
1028
1147
|
* @throws If the internal key or taproot script tree is invalid. {@link Error}
|
|
1148
|
+
* @throws If a numeric script value is outside its supported range. {@link RangeError}
|
|
1029
1149
|
* @example
|
|
1030
1150
|
* Combine script leaves into a final taproot output descriptor and address.
|
|
1031
1151
|
* ```ts
|
|
@@ -1063,7 +1183,7 @@ export function p2tr(
|
|
|
1063
1183
|
const pubKey =
|
|
1064
1184
|
typeof internalPubKey === 'string'
|
|
1065
1185
|
? hex.decode(internalPubKey)
|
|
1066
|
-
: internalPubKey
|
|
1186
|
+
: (internalPubKey ?? u.taprootNumsKey());
|
|
1067
1187
|
if (!isValidPubkey(pubKey, u.PubT.schnorr)) throw new Error('p2tr: non-schnorr pubkey');
|
|
1068
1188
|
if (tree) {
|
|
1069
1189
|
let hashedTree = taprootAddPath(
|
|
@@ -1071,18 +1191,20 @@ export function p2tr(
|
|
|
1071
1191
|
);
|
|
1072
1192
|
const tapMerkleRoot = hashedTree.hash;
|
|
1073
1193
|
const [tweakedPubkey, parity] = u.taprootTweakPubkey(pubKey, tapMerkleRoot);
|
|
1194
|
+
const tapLeafScript: NonNullable<TransactionInput['tapLeafScript']> = [];
|
|
1074
1195
|
const leaves = taprootWalkTree(hashedTree).map((l) => {
|
|
1075
1196
|
const version = tapLeafVersion(l.version);
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
internalKey: pubKey,
|
|
1083
|
-
merklePath: l.path,
|
|
1084
|
-
}),
|
|
1197
|
+
// Leaf versions are stored as the base even byte; only the control block adds the
|
|
1198
|
+
// output-key parity bit required by BIP 341 script-path spending.
|
|
1199
|
+
const controlBlock = {
|
|
1200
|
+
version: version + parity,
|
|
1201
|
+
internalKey: pubKey,
|
|
1202
|
+
merklePath: l.path,
|
|
1085
1203
|
};
|
|
1204
|
+
// Skip an encode/decode copy for performance; callers must treat returned metadata as
|
|
1205
|
+
// immutable.
|
|
1206
|
+
tapLeafScript.push([controlBlock, u.concatBytes(l.script, new Uint8Array([version]))]);
|
|
1207
|
+
return { ...l, controlBlock: TaprootControlBlock.encode(controlBlock) };
|
|
1086
1208
|
});
|
|
1087
1209
|
return {
|
|
1088
1210
|
type: 'tr',
|
|
@@ -1093,10 +1215,7 @@ export function p2tr(
|
|
|
1093
1215
|
// PSBT stuff
|
|
1094
1216
|
tapInternalKey: pubKey,
|
|
1095
1217
|
leaves,
|
|
1096
|
-
tapLeafScript
|
|
1097
|
-
TaprootControlBlock.decode(l.controlBlock),
|
|
1098
|
-
u.concatBytes(l.script, new Uint8Array([tapLeafVersion(l.version)])),
|
|
1099
|
-
]),
|
|
1218
|
+
tapLeafScript,
|
|
1100
1219
|
tapMerkleRoot,
|
|
1101
1220
|
} as const as TRet<Extends<P2TR_TREE, P2Ret>>;
|
|
1102
1221
|
} else {
|
|
@@ -1115,25 +1234,47 @@ export function p2tr(
|
|
|
1115
1234
|
}
|
|
1116
1235
|
}
|
|
1117
1236
|
|
|
1237
|
+
/** Maximum number of combinations materialized by one default helper call. */
|
|
1238
|
+
export const MAX_COMBINATIONS = 4096;
|
|
1239
|
+
|
|
1240
|
+
const combinationCount = (n: number, m: number): number => {
|
|
1241
|
+
const k = Math.min(m, n - m);
|
|
1242
|
+
let count = 1;
|
|
1243
|
+
for (let i = 1; i <= k; i++) {
|
|
1244
|
+
count = (count * (n - k + i)) / i;
|
|
1245
|
+
if (!Number.isSafeInteger(count)) return Number.POSITIVE_INFINITY;
|
|
1246
|
+
}
|
|
1247
|
+
return count;
|
|
1248
|
+
};
|
|
1249
|
+
|
|
1118
1250
|
// Returns all combinations of size M from lst
|
|
1119
1251
|
/**
|
|
1120
1252
|
* Returns all size-`m` combinations from a list.
|
|
1121
1253
|
* @param m - size of each combination
|
|
1122
1254
|
* @param list - input items to combine
|
|
1255
|
+
* @param maxCombinations - maximum result rows to materialize
|
|
1123
1256
|
* @returns Array of combinations.
|
|
1124
1257
|
* @throws If the combination size or input list is invalid. {@link Error}
|
|
1258
|
+
* @throws If the requested result exceeds the materialization limit. {@link RangeError}
|
|
1125
1259
|
* @example
|
|
1126
1260
|
* Enumerate all size-two subsets of a short list.
|
|
1127
1261
|
* ```ts
|
|
1128
1262
|
* combinations(2, [1, 2, 3]);
|
|
1129
1263
|
* ```
|
|
1130
1264
|
*/
|
|
1131
|
-
export function combinations<T>(m: number, list: T[]): T[][] {
|
|
1265
|
+
export function combinations<T>(m: number, list: T[], maxCombinations = MAX_COMBINATIONS): T[][] {
|
|
1132
1266
|
const res: T[][] = [];
|
|
1133
1267
|
if (!Array.isArray(list)) throw new Error('combinations: lst arg should be array');
|
|
1134
1268
|
const n = list.length;
|
|
1135
1269
|
anumber(m, 'm');
|
|
1270
|
+
anumber(maxCombinations, 'maxCombinations');
|
|
1136
1271
|
if (m < 1 || m > n) throw new Error('combinations: m must satisfy 1 <= m <= lst.length');
|
|
1272
|
+
if (maxCombinations < 1) throw new Error('combinations: maxCombinations must be >= 1');
|
|
1273
|
+
const count = combinationCount(n, m);
|
|
1274
|
+
if (count > maxCombinations)
|
|
1275
|
+
throw new RangeError(
|
|
1276
|
+
`combinations: C(${n}, ${m}) exceeds materialization limit=${maxCombinations}`
|
|
1277
|
+
);
|
|
1137
1278
|
/*
|
|
1138
1279
|
Basically works as M nested loops like:
|
|
1139
1280
|
for (;idx[0]<lst.length;idx[0]++) for (idx[1]=idx[0]+1;idx[1]<lst.length;idx[1]++)
|
|
@@ -1163,8 +1304,8 @@ export function combinations<T>(m: number, list: T[]): T[][] {
|
|
|
1163
1304
|
|
|
1164
1305
|
/**
|
|
1165
1306
|
* M-of-N multi-leaf wallet via p2tr_ns. If m == n, single script is emitted.
|
|
1166
|
-
*
|
|
1167
|
-
*
|
|
1307
|
+
* It materializes C(n, m) leaves up to {@link MAX_COMBINATIONS}; middle-of-the-range thresholds
|
|
1308
|
+
* above that bound are rejected before allocation.
|
|
1168
1309
|
* `2-of-[A,B,C] => [A,B] | [A,C] | [B,C]`
|
|
1169
1310
|
*/
|
|
1170
1311
|
export type P2TR_NS = {
|
|
@@ -1180,6 +1321,7 @@ export type P2TR_NS = {
|
|
|
1180
1321
|
* @param allowSamePubkeys - whether duplicate keys are allowed
|
|
1181
1322
|
* @returns Array of taproot leaf descriptors.
|
|
1182
1323
|
* @throws If the taproot multisig parameters are invalid. {@link Error}
|
|
1324
|
+
* @throws If the requested leaf set exceeds the materialization limit. {@link RangeError}
|
|
1183
1325
|
* @example
|
|
1184
1326
|
* Build the leaf set for an M-of-N taproot `CHECKSIGVERIFY` policy.
|
|
1185
1327
|
* ```ts
|
|
@@ -1193,14 +1335,24 @@ export const p2tr_ns = (
|
|
|
1193
1335
|
pubkeys: TArg<Bytes[]>,
|
|
1194
1336
|
allowSamePubkeys = false
|
|
1195
1337
|
): TRet<Extends<P2TR_NS, P2Ret>[]> => {
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
)
|
|
1338
|
+
anumber(m, 'm');
|
|
1339
|
+
if (m > 999) throw new Error('OutScript/tr_ns: invalid params');
|
|
1340
|
+
// Enforce the allocation bound before doing curve work on an attacker-controlled key list.
|
|
1341
|
+
const keySets = combinations(m, pubkeys);
|
|
1342
|
+
if (allowSamePubkeys) {
|
|
1343
|
+
for (const pubkey of pubkeys) u.validatePubkey(pubkey, u.PubT.schnorr);
|
|
1344
|
+
} else uniqPubkey(pubkeys, u.PubT.schnorr);
|
|
1345
|
+
return keySets.map((keys) => {
|
|
1346
|
+
// Keys were validated once above. Encoding through OutScript here would repeat lift_x for
|
|
1347
|
+
// every key in every combination, turning a bounded leaf set into avoidable curve-level work.
|
|
1348
|
+
const ops: ScriptType = [];
|
|
1349
|
+
for (let i = 0; i < keys.length - 1; i++) ops.push(keys[i], 'CHECKSIGVERIFY');
|
|
1350
|
+
ops.push(keys[keys.length - 1], 'CHECKSIG');
|
|
1351
|
+
return {
|
|
1352
|
+
type: 'tr_ns',
|
|
1353
|
+
script: Script.encode(ops),
|
|
1354
|
+
} as const as TRet<Extends<P2TR_NS, P2Ret>>;
|
|
1355
|
+
}) as TRet<Extends<P2TR_NS, P2Ret>[]>;
|
|
1204
1356
|
};
|
|
1205
1357
|
// Taproot public key (case of p2tr_ns)
|
|
1206
1358
|
/** Single-key taproot leaf descriptor. */
|
|
@@ -1212,6 +1364,7 @@ export type P2TR_PK = P2TR_NS;
|
|
|
1212
1364
|
* @param pubkey - Schnorr public key
|
|
1213
1365
|
* @returns Taproot single-key leaf descriptor.
|
|
1214
1366
|
* @throws If the taproot single-key leaf cannot be encoded. {@link Error}
|
|
1367
|
+
* @throws If the delegated leaf policy exceeds its supported range. {@link RangeError}
|
|
1215
1368
|
* @example
|
|
1216
1369
|
* Build a single-key tapscript leaf.
|
|
1217
1370
|
* ```ts
|
|
@@ -1250,7 +1403,7 @@ export function p2tr_ms(
|
|
|
1250
1403
|
pubkeys: TArg<Bytes[]>,
|
|
1251
1404
|
allowSamePubkeys = false
|
|
1252
1405
|
): TRet<Extends<P2TR_MS, P2Ret>> {
|
|
1253
|
-
if (!allowSamePubkeys) uniqPubkey(pubkeys);
|
|
1406
|
+
if (!allowSamePubkeys) uniqPubkey(pubkeys, u.PubT.schnorr);
|
|
1254
1407
|
return {
|
|
1255
1408
|
type: 'tr_ms',
|
|
1256
1409
|
script: OutScript.encode({ type: 'tr_ms', pubkeys, m }),
|
|
@@ -1265,6 +1418,7 @@ export function p2tr_ms(
|
|
|
1265
1418
|
* @param network - address network parameters
|
|
1266
1419
|
* @returns Encoded Bitcoin address.
|
|
1267
1420
|
* @throws If the requested address type is unknown. {@link Error}
|
|
1421
|
+
* @throws If a key-derived script value is outside its supported range. {@link RangeError}
|
|
1268
1422
|
* @example
|
|
1269
1423
|
* Pick the output type first, then derive the matching address from the private key.
|
|
1270
1424
|
* ```ts
|
|
@@ -1278,6 +1432,7 @@ export function getAddress(
|
|
|
1278
1432
|
privKey: TArg<Bytes>,
|
|
1279
1433
|
network: BTC_NETWORK = NETWORK
|
|
1280
1434
|
): string {
|
|
1435
|
+
u.astring(type, 'type');
|
|
1281
1436
|
if (type === 'tr') {
|
|
1282
1437
|
return p2tr(u.pubSchnorr(privKey), undefined, network).address;
|
|
1283
1438
|
}
|
|
@@ -1429,18 +1584,24 @@ export function WIF(network: BTC_NETWORK = NETWORK): TRet<Coder<Bytes, string>>
|
|
|
1429
1584
|
* coder.encode(p2wpkh(pubECDSA(randomPrivateKeyBytes())));
|
|
1430
1585
|
* ```
|
|
1431
1586
|
*/
|
|
1432
|
-
export function Address(network: BTC_NETWORK = NETWORK) {
|
|
1587
|
+
export function Address(network: BTC_NETWORK = NETWORK): TRet<P.Coder<AddressValue, string>> {
|
|
1588
|
+
u.validateObject(network as Record<string, any>, {}, {}, 'network');
|
|
1433
1589
|
return {
|
|
1434
|
-
encode(from:
|
|
1590
|
+
encode(from: TArg<AddressValue>): string {
|
|
1591
|
+
u.validateObject(from as Record<string, any>, {}, {}, 'from');
|
|
1435
1592
|
const { type } = from;
|
|
1593
|
+
u.astring(type, 'from.type');
|
|
1436
1594
|
if (type === 'wpkh') return programToWitness(0, from.hash, network);
|
|
1437
1595
|
else if (type === 'wsh') return programToWitness(0, from.hash, network);
|
|
1438
1596
|
else if (type === 'tr') return programToWitness(1, from.pubkey, network);
|
|
1597
|
+
// BIP433 P2A is the fixed v1 witness program 0x4e73 ('bc1pfeessrawgf').
|
|
1598
|
+
else if (type === 'p2a') return programToWitness(1, P2A_PROGRAM, network);
|
|
1439
1599
|
else if (type === 'pkh') return formatKey(from.hash, [network.pubKeyHash]);
|
|
1440
1600
|
else if (type === 'sh') return formatKey(from.hash, [network.scriptHash]);
|
|
1441
1601
|
throw new Error(`Unknown address type=${type}`);
|
|
1442
1602
|
},
|
|
1443
|
-
decode(address: string):
|
|
1603
|
+
decode(address: string): TRet<AddressValue> {
|
|
1604
|
+
u.astring(address, 'address');
|
|
1444
1605
|
if (address.length < 14 || address.length > 74) throw new Error('Invalid address length');
|
|
1445
1606
|
// Bech32
|
|
1446
1607
|
if (network.bech32 && address.toLowerCase().startsWith(`${network.bech32}1`)) {
|
|
@@ -1458,25 +1619,27 @@ export function Address(network: BTC_NETWORK = NETWORK) {
|
|
|
1458
1619
|
const data = bech32.fromWords(program);
|
|
1459
1620
|
validateWitness(version, data);
|
|
1460
1621
|
if (version === 0 && data.length === 32)
|
|
1461
|
-
return { type: 'wsh', hash: data } as
|
|
1622
|
+
return { type: 'wsh', hash: data } as TRet<AddressValue>;
|
|
1462
1623
|
else if (version === 0 && data.length === 20)
|
|
1463
|
-
return { type: 'wpkh', hash: data } as
|
|
1624
|
+
return { type: 'wpkh', hash: data } as TRet<AddressValue>;
|
|
1464
1625
|
else if (version === 1 && data.length === 32)
|
|
1465
|
-
return { type: 'tr', pubkey: data } as
|
|
1626
|
+
return { type: 'tr', pubkey: data } as TRet<AddressValue>;
|
|
1627
|
+
else if (version === 1 && u.equalBytes(data, P2A_PROGRAM))
|
|
1628
|
+
return { type: 'p2a', script: Script.encode([1, data]) } as TRet<AddressValue>;
|
|
1466
1629
|
// Future witness versions can still be valid addresses, but this helper
|
|
1467
|
-
// only returns typed descriptors for recognized v0 and
|
|
1630
|
+
// only returns typed descriptors for recognized v0, taproot and P2A templates.
|
|
1468
1631
|
else throw new Error('Unknown witness program');
|
|
1469
1632
|
}
|
|
1470
1633
|
const data = base58check.decode(address);
|
|
1471
1634
|
if (data.length !== 21) throw new Error('Invalid base58 address');
|
|
1472
1635
|
// Pay To Public Key Hash
|
|
1473
1636
|
if (data[0] === network.pubKeyHash) {
|
|
1474
|
-
return { type: 'pkh', hash: data.slice(1) } as
|
|
1637
|
+
return { type: 'pkh', hash: data.slice(1) } as TRet<AddressValue>;
|
|
1475
1638
|
} else if (data[0] === network.scriptHash) {
|
|
1476
1639
|
return {
|
|
1477
1640
|
type: 'sh',
|
|
1478
1641
|
hash: data.slice(1),
|
|
1479
|
-
} as
|
|
1642
|
+
} as TRet<AddressValue>;
|
|
1480
1643
|
}
|
|
1481
1644
|
throw new Error(`Invalid address prefix=${data[0]}`);
|
|
1482
1645
|
},
|