@scure/btc-signer 2.3.0 → 2.4.1
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 +49 -10
- package/index.d.ts +4 -3
- package/index.js +3 -3
- package/musig2.d.ts +17 -3
- package/musig2.js +18 -6
- package/net.js +7 -2
- package/package.json +12 -11
- package/payment.d.ts +16 -5
- package/payment.js +118 -27
- package/psbt.d.ts +680 -9
- package/psbt.js +187 -38
- package/src/_type_test.ts +14 -0
- package/src/index.ts +4 -2
- package/src/musig2.ts +32 -7
- package/src/net.ts +7 -2
- package/src/payment.ts +145 -32
- package/src/psbt.ts +210 -35
- package/src/transaction.ts +790 -136
- package/src/utils.ts +24 -2
- package/src/utxo.ts +185 -71
- package/transaction.d.ts +40 -6
- package/transaction.js +667 -123
- package/utils.d.ts +15 -1
- package/utils.js +21 -2
- package/utxo.d.ts +192 -1
- package/utxo.js +166 -69
package/payment.js
CHANGED
|
@@ -191,7 +191,7 @@ const OutTRNS = {
|
|
|
191
191
|
// BIP342 "Using a k-of-k script for every combination" documents the shape
|
|
192
192
|
// `<pubkey_1> CHECKSIGVERIFY ... <pubkey_n> CHECKSIG`; this matcher only
|
|
193
193
|
// classifies that embedded-pubkey form, so bare CHECKSIG stays unknown.
|
|
194
|
-
if (!pubkeys.length)
|
|
194
|
+
if (!pubkeys.length || pubkeys.length > 999)
|
|
195
195
|
return;
|
|
196
196
|
return { type: 'tr_ns', pubkeys };
|
|
197
197
|
},
|
|
@@ -322,20 +322,40 @@ export const OutScript = /* @__PURE__ */ (() => Object.freeze(P.validate(_OutScr
|
|
|
322
322
|
throw new Error('OutScript/multisig: invalid params');
|
|
323
323
|
}
|
|
324
324
|
if (i.type === 'tr_ns' || i.type === 'tr_ms') {
|
|
325
|
+
const n = i.pubkeys.length;
|
|
326
|
+
// BIP342 keeps the 1,000-element stack limit. Both supported tapscript multisig forms
|
|
327
|
+
// start with n signatures and then push a pubkey, so n must stay at or below 999.
|
|
328
|
+
if (n > 999)
|
|
329
|
+
throw new Error(`OutScript/${i.type}: invalid params`);
|
|
325
330
|
for (const p of i.pubkeys)
|
|
326
331
|
if (!isValidPubkey(p, u.PubT.schnorr))
|
|
327
332
|
throw new Error(`OutScript/${i.type}: wrong pubkey`);
|
|
328
333
|
}
|
|
329
334
|
if (i.type === 'tr_ms') {
|
|
330
335
|
const n = i.pubkeys.length;
|
|
331
|
-
// BIP 342 keeps the 1000-element stack limit. This CHECKSIG/CHECKSIGADD form
|
|
332
|
-
// momentarily has n witness items plus one pushed pubkey on the stack, so n must stay <= 999.
|
|
333
336
|
anumber(i.m, 'm');
|
|
334
|
-
if (i.m <= 0 ||
|
|
337
|
+
if (i.m <= 0 || i.m > n)
|
|
335
338
|
throw new Error('OutScript/tr_ms: invalid params');
|
|
336
339
|
}
|
|
337
340
|
return i;
|
|
338
341
|
})))();
|
|
342
|
+
// Internal raw-aware boundary for actual scriptPubKeys and P2SH-nested witness programs. Generic
|
|
343
|
+
// OutScript remains semantic because witnessScript and tapscript children may use the same opcodes
|
|
344
|
+
// without claiming BIP141 witness-program framing.
|
|
345
|
+
export const _WitnessOutScript = /* @__PURE__ */ (() => Object.freeze(P.wrap({
|
|
346
|
+
encodeStream: (w, value) => OutScript.encodeStream(w, value),
|
|
347
|
+
decodeStream: (r) => {
|
|
348
|
+
const raw = r.bytes(r.leftBytes);
|
|
349
|
+
const out = OutScript.decode(raw);
|
|
350
|
+
// Script.decode normalizes push opcodes, but BIP141 recognition requires the exact direct
|
|
351
|
+
// push. Reject only when this explicit outer-program boundary is selected by the caller.
|
|
352
|
+
if (out &&
|
|
353
|
+
(out.type === 'p2a' || out.type === 'wpkh' || out.type === 'wsh' || out.type === 'tr') &&
|
|
354
|
+
!u.equalBytes(raw, OutScript.encode(out)))
|
|
355
|
+
throw new Error('OutScript: non-canonical witness program');
|
|
356
|
+
return out;
|
|
357
|
+
},
|
|
358
|
+
})))();
|
|
339
359
|
// Basic sanity check for scripts
|
|
340
360
|
function checkWSH(s, witnessScript) {
|
|
341
361
|
if (!u.equalBytes(s.hash, u.sha256(witnessScript)))
|
|
@@ -370,7 +390,7 @@ export function checkScript(script, redeemScript, witnessScript) {
|
|
|
370
390
|
let hasWsh = false;
|
|
371
391
|
let r = undefined;
|
|
372
392
|
if (script) {
|
|
373
|
-
const s =
|
|
393
|
+
const s = _WitnessOutScript.decode(script);
|
|
374
394
|
// BIP174 Data Signers Check For bullets: provided redeemScript must match
|
|
375
395
|
// the scriptPubKey, and provided witnessScript must match the scriptPubKey
|
|
376
396
|
// or redeemScript instead of being silently ignored as stray metadata.
|
|
@@ -382,7 +402,7 @@ export function checkScript(script, redeemScript, witnessScript) {
|
|
|
382
402
|
throw new Error('checkScript: redeemScript without P2SH');
|
|
383
403
|
if (!u.equalBytes(s.hash, u.hash160(redeemScript)))
|
|
384
404
|
throw new Error('checkScript: sh wrong redeemScript hash');
|
|
385
|
-
r =
|
|
405
|
+
r = _WitnessOutScript.decode(redeemScript);
|
|
386
406
|
if (r?.type === 'tr' || r?.type === 'tr_ns' || r?.type === 'tr_ms')
|
|
387
407
|
throw new Error(`checkScript: P2${r.type} cannot be wrapped in P2SH`);
|
|
388
408
|
// Not sure if this unspendable, but we cannot represent this via PSBT
|
|
@@ -397,7 +417,7 @@ export function checkScript(script, redeemScript, witnessScript) {
|
|
|
397
417
|
}
|
|
398
418
|
if (redeemScript) {
|
|
399
419
|
if (r === undefined)
|
|
400
|
-
r =
|
|
420
|
+
r = _WitnessOutScript.decode(redeemScript);
|
|
401
421
|
if (r?.type === 'wsh') {
|
|
402
422
|
hasWsh = true;
|
|
403
423
|
if (witnessScript)
|
|
@@ -407,12 +427,21 @@ export function checkScript(script, redeemScript, witnessScript) {
|
|
|
407
427
|
if (witnessScript && !hasWsh)
|
|
408
428
|
throw new Error('checkScript: witnessScript without P2WSH');
|
|
409
429
|
}
|
|
410
|
-
function uniqPubkey(pubkeys) {
|
|
430
|
+
function uniqPubkey(pubkeys, type) {
|
|
411
431
|
const map = {};
|
|
412
432
|
for (const pub of pubkeys) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
433
|
+
u.validatePubkey(pub, type);
|
|
434
|
+
let normalized = pub;
|
|
435
|
+
if (type === u.PubT.ecdsa && pub.length === 65) {
|
|
436
|
+
// Compressed and uncompressed SEC1 encodings can represent the same curve point. Normalize
|
|
437
|
+
// valid uncompressed keys before comparing so one signer cannot occupy multiple threshold
|
|
438
|
+
// slots merely by changing the serialization. `allowSamePubkeys` remains the explicit
|
|
439
|
+
// compatibility escape hatch for callers that intentionally construct duplicate-key scripts.
|
|
440
|
+
normalized = new Uint8Array(33);
|
|
441
|
+
normalized[0] = 2 | (pub[64] & 1);
|
|
442
|
+
normalized.set(pub.subarray(1, 33), 1);
|
|
443
|
+
}
|
|
444
|
+
const key = hex.encode(normalized);
|
|
416
445
|
if (map[key])
|
|
417
446
|
throw new Error(`Multisig: non-uniq pubkey: ${pubkeys.map(hex.encode)}`);
|
|
418
447
|
map[key] = true;
|
|
@@ -466,10 +495,23 @@ export const p2pkh = (publicKey, network = NETWORK) => {
|
|
|
466
495
|
hash,
|
|
467
496
|
};
|
|
468
497
|
};
|
|
498
|
+
const checkCanonicalScript = (script, name, allowNonCanonicalScript) => {
|
|
499
|
+
if (allowNonCanonicalScript)
|
|
500
|
+
return;
|
|
501
|
+
const decoded = Script.decode(script);
|
|
502
|
+
const nonMinimalNumber = decoded.some((op) => u.isBytes(op) && op.length === 1 && ((1 <= op[0] && op[0] <= 16) || op[0] === 0x81));
|
|
503
|
+
// Core's default MINIMALDATA policy rejects these spends from its mempool, so address-producing
|
|
504
|
+
// helpers require an explicit opt-in even though the original bytes remain consensus-valid.
|
|
505
|
+
if (nonMinimalNumber || !u.equalBytes(script, Script.encode(decoded))) {
|
|
506
|
+
const wrapper = name === 'redeemScript' ? 'P2SH' : 'P2WSH';
|
|
507
|
+
throw new Error(`${wrapper}: non-canonical ${name}`);
|
|
508
|
+
}
|
|
509
|
+
};
|
|
469
510
|
/**
|
|
470
511
|
* Wraps a child script inside P2SH.
|
|
471
512
|
* @param child - child payment descriptor to wrap
|
|
472
513
|
* @param network - address network parameters
|
|
514
|
+
* @param allowNonCanonicalScript - whether to create an address for a non-minimal child script
|
|
473
515
|
* @returns P2SH descriptor preserving witness metadata when present.
|
|
474
516
|
* @throws If the wrapped script combination is invalid or unsupported. {@link Error}
|
|
475
517
|
* @example
|
|
@@ -480,7 +522,7 @@ export const p2pkh = (publicKey, network = NETWORK) => {
|
|
|
480
522
|
* p2sh(p2wsh(p2pk(hex.decode('0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'))));
|
|
481
523
|
* ```
|
|
482
524
|
*/
|
|
483
|
-
export const p2sh = (child, network = NETWORK) => {
|
|
525
|
+
export const p2sh = (child, network = NETWORK, allowNonCanonicalScript = false) => {
|
|
484
526
|
u.validateObject(child, {}, {}, 'child');
|
|
485
527
|
// It is already tested inside noble-hashes and checkScript
|
|
486
528
|
// BIP16 redeemScripts are pushed by scriptSig, so anything over the 520-byte pushed-element
|
|
@@ -491,6 +533,7 @@ export const p2sh = (child, network = NETWORK) => {
|
|
|
491
533
|
throw new Error(`Wrong script: ${typeof c.script}, expected Uint8Array`);
|
|
492
534
|
if (cs.length > MAX_SCRIPT_BYTE_LENGTH)
|
|
493
535
|
throw new Error(`P2SH: redeemScript exceeds ${MAX_SCRIPT_BYTE_LENGTH}-byte push limit: len=${cs.length}`);
|
|
536
|
+
checkCanonicalScript(cs, 'redeemScript', allowNonCanonicalScript);
|
|
494
537
|
const hash = u.hash160(cs);
|
|
495
538
|
const out = { type: 'sh', hash };
|
|
496
539
|
const script = OutScript.encode(out);
|
|
@@ -520,6 +563,7 @@ export const p2sh = (child, network = NETWORK) => {
|
|
|
520
563
|
* Wraps a child script inside native SegWit P2WSH.
|
|
521
564
|
* @param child - child payment descriptor to wrap
|
|
522
565
|
* @param network - address network parameters
|
|
566
|
+
* @param allowNonCanonicalScript - whether to create an address for a non-minimal child script
|
|
523
567
|
* @returns P2WSH descriptor.
|
|
524
568
|
* @throws If the wrapped script combination is invalid or unsupported. {@link Error}
|
|
525
569
|
* @example
|
|
@@ -530,7 +574,7 @@ export const p2sh = (child, network = NETWORK) => {
|
|
|
530
574
|
* p2wsh(p2pk(hex.decode('0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')));
|
|
531
575
|
* ```
|
|
532
576
|
*/
|
|
533
|
-
export const p2wsh = (child, network = NETWORK) => {
|
|
577
|
+
export const p2wsh = (child, network = NETWORK, allowNonCanonicalScript = false) => {
|
|
534
578
|
u.validateObject(child, {}, {}, 'child');
|
|
535
579
|
const cs = child.script;
|
|
536
580
|
if (!u.isBytes(cs))
|
|
@@ -539,6 +583,7 @@ export const p2wsh = (child, network = NETWORK) => {
|
|
|
539
583
|
// and that witnessScript is limited to 10,000 bytes, so larger wrapped scripts must reject.
|
|
540
584
|
if (cs.length > 10000)
|
|
541
585
|
throw new Error('P2WSH: witnessScript exceeds 10,000 bytes');
|
|
586
|
+
checkCanonicalScript(cs, 'witnessScript', allowNonCanonicalScript);
|
|
542
587
|
const hash = u.sha256(cs);
|
|
543
588
|
const script = OutScript.encode({ type: 'wsh', hash });
|
|
544
589
|
checkScript(script, undefined, cs);
|
|
@@ -597,7 +642,7 @@ export const p2ms = (m, pubkeys, allowSamePubkeys = false) => {
|
|
|
597
642
|
// BIP 11 only standardized bare multisig up to 3 keys; this helper still permits up to 16
|
|
598
643
|
// because the same script shape is commonly wrapped by p2sh()/p2wsh() instead of used bare.
|
|
599
644
|
if (!allowSamePubkeys)
|
|
600
|
-
uniqPubkey(pubkeys);
|
|
645
|
+
uniqPubkey(pubkeys, u.PubT.ecdsa);
|
|
601
646
|
return {
|
|
602
647
|
type: 'ms',
|
|
603
648
|
script: OutScript.encode({ type: 'ms', pubkeys, m }),
|
|
@@ -633,7 +678,7 @@ function checkTaprootScript(script, internalPubKey, allowUnknownOutputs = false,
|
|
|
633
678
|
const outms = out;
|
|
634
679
|
if (!allowUnknownOutputs && outms.pubkeys) {
|
|
635
680
|
for (const p of outms.pubkeys) {
|
|
636
|
-
if (u.equalBytes(p, u.
|
|
681
|
+
if (u.equalBytes(p, u.taprootNumsKey()))
|
|
637
682
|
throw new Error('Unspendable taproot key in leaf script');
|
|
638
683
|
// It's likely a mistake at this point:
|
|
639
684
|
// 1. p2tr(A, p2tr_ns(2, [A, B])) == p2tr(A, p2tr_pk(B)) (A or B key)
|
|
@@ -726,7 +771,13 @@ function taprootWalkTree(tree) {
|
|
|
726
771
|
// Keep a stable left-to-right DFS leaf order when flattening the annotated tree.
|
|
727
772
|
return [...taprootWalkTree(tree.left), ...taprootWalkTree(tree.right)];
|
|
728
773
|
}
|
|
729
|
-
|
|
774
|
+
// BIP 341 control blocks can encode at most 128 sibling hashes.
|
|
775
|
+
const TAPROOT_MAX_DEPTH = 128;
|
|
776
|
+
function taprootHashTree(tree, internalPubKey, allowUnknownOutputs = false, customScripts, depth = 0) {
|
|
777
|
+
// Reject before inspecting or descending into the next node, which also bounds the
|
|
778
|
+
// recursion used by the path annotation and tree-flattening passes below.
|
|
779
|
+
if (depth > TAPROOT_MAX_DEPTH)
|
|
780
|
+
throw new RangeError(`P2TR: tree depth exceeds ${TAPROOT_MAX_DEPTH}`);
|
|
730
781
|
if (tree === undefined)
|
|
731
782
|
throw new Error('taprootHashTree: empty tree');
|
|
732
783
|
if (!Array.isArray(tree) && !P.utils.isPlainObject(tree))
|
|
@@ -759,8 +810,8 @@ function taprootHashTree(tree, internalPubKey, allowUnknownOutputs = false, cust
|
|
|
759
810
|
throw new Error('hashTree: non binary tree!');
|
|
760
811
|
// branch
|
|
761
812
|
// Both nodes should exist
|
|
762
|
-
const left = taprootHashTree(tree[0], internalPubKey, allowUnknownOutputs, customScripts);
|
|
763
|
-
const right = taprootHashTree(tree[1], internalPubKey, allowUnknownOutputs, customScripts);
|
|
813
|
+
const left = taprootHashTree(tree[0], internalPubKey, allowUnknownOutputs, customScripts, depth + 1);
|
|
814
|
+
const right = taprootHashTree(tree[1], internalPubKey, allowUnknownOutputs, customScripts, depth + 1);
|
|
764
815
|
// BIP 341 sorts TapBranch child hashes lexicographically for hashing, but the original
|
|
765
816
|
// left/right structure still determines the control-block sibling paths for each leaf.
|
|
766
817
|
let [lH, rH] = [left.hash, right.hash];
|
|
@@ -805,7 +856,7 @@ export function p2tr(internalPubKey, tree, network = NETWORK, allowUnknownOutput
|
|
|
805
856
|
throw new Error('p2tr: should have pubKey or scriptTree (or both)');
|
|
806
857
|
const pubKey = typeof internalPubKey === 'string'
|
|
807
858
|
? hex.decode(internalPubKey)
|
|
808
|
-
: internalPubKey
|
|
859
|
+
: (internalPubKey ?? u.taprootNumsKey());
|
|
809
860
|
if (!isValidPubkey(pubKey, u.PubT.schnorr))
|
|
810
861
|
throw new Error('p2tr: non-schnorr pubkey');
|
|
811
862
|
if (tree) {
|
|
@@ -855,27 +906,47 @@ export function p2tr(internalPubKey, tree, network = NETWORK, allowUnknownOutput
|
|
|
855
906
|
};
|
|
856
907
|
}
|
|
857
908
|
}
|
|
909
|
+
/** Maximum number of combinations materialized by one default helper call. */
|
|
910
|
+
export const MAX_COMBINATIONS = 4096;
|
|
911
|
+
const combinationCount = (n, m) => {
|
|
912
|
+
const k = Math.min(m, n - m);
|
|
913
|
+
let count = 1;
|
|
914
|
+
for (let i = 1; i <= k; i++) {
|
|
915
|
+
count = (count * (n - k + i)) / i;
|
|
916
|
+
if (!Number.isSafeInteger(count))
|
|
917
|
+
return Number.POSITIVE_INFINITY;
|
|
918
|
+
}
|
|
919
|
+
return count;
|
|
920
|
+
};
|
|
858
921
|
// Returns all combinations of size M from lst
|
|
859
922
|
/**
|
|
860
923
|
* Returns all size-`m` combinations from a list.
|
|
861
924
|
* @param m - size of each combination
|
|
862
925
|
* @param list - input items to combine
|
|
926
|
+
* @param maxCombinations - maximum result rows to materialize
|
|
863
927
|
* @returns Array of combinations.
|
|
864
928
|
* @throws If the combination size or input list is invalid. {@link Error}
|
|
929
|
+
* @throws If the requested result exceeds the materialization limit. {@link RangeError}
|
|
865
930
|
* @example
|
|
866
931
|
* Enumerate all size-two subsets of a short list.
|
|
867
932
|
* ```ts
|
|
868
933
|
* combinations(2, [1, 2, 3]);
|
|
869
934
|
* ```
|
|
870
935
|
*/
|
|
871
|
-
export function combinations(m, list) {
|
|
936
|
+
export function combinations(m, list, maxCombinations = MAX_COMBINATIONS) {
|
|
872
937
|
const res = [];
|
|
873
938
|
if (!Array.isArray(list))
|
|
874
939
|
throw new Error('combinations: lst arg should be array');
|
|
875
940
|
const n = list.length;
|
|
876
941
|
anumber(m, 'm');
|
|
942
|
+
anumber(maxCombinations, 'maxCombinations');
|
|
877
943
|
if (m < 1 || m > n)
|
|
878
944
|
throw new Error('combinations: m must satisfy 1 <= m <= lst.length');
|
|
945
|
+
if (maxCombinations < 1)
|
|
946
|
+
throw new Error('combinations: maxCombinations must be >= 1');
|
|
947
|
+
const count = combinationCount(n, m);
|
|
948
|
+
if (count > maxCombinations)
|
|
949
|
+
throw new RangeError(`combinations: C(${n}, ${m}) exceeds materialization limit=${maxCombinations}`);
|
|
879
950
|
/*
|
|
880
951
|
Basically works as M nested loops like:
|
|
881
952
|
for (;idx[0]<lst.length;idx[0]++) for (idx[1]=idx[0]+1;idx[1]<lst.length;idx[1]++)
|
|
@@ -911,6 +982,7 @@ export function combinations(m, list) {
|
|
|
911
982
|
* @param allowSamePubkeys - whether duplicate keys are allowed
|
|
912
983
|
* @returns Array of taproot leaf descriptors.
|
|
913
984
|
* @throws If the taproot multisig parameters are invalid. {@link Error}
|
|
985
|
+
* @throws If the requested leaf set exceeds the materialization limit. {@link RangeError}
|
|
914
986
|
* @example
|
|
915
987
|
* Build the leaf set for an M-of-N taproot `CHECKSIGVERIFY` policy.
|
|
916
988
|
* ```ts
|
|
@@ -920,12 +992,29 @@ export function combinations(m, list) {
|
|
|
920
992
|
* ```
|
|
921
993
|
*/
|
|
922
994
|
export const p2tr_ns = (m, pubkeys, allowSamePubkeys = false) => {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
995
|
+
anumber(m, 'm');
|
|
996
|
+
if (m > 999)
|
|
997
|
+
throw new Error('OutScript/tr_ns: invalid params');
|
|
998
|
+
// Enforce the allocation bound before doing curve work on an attacker-controlled key list.
|
|
999
|
+
const keySets = combinations(m, pubkeys);
|
|
1000
|
+
if (allowSamePubkeys) {
|
|
1001
|
+
for (const pubkey of pubkeys)
|
|
1002
|
+
u.validatePubkey(pubkey, u.PubT.schnorr);
|
|
1003
|
+
}
|
|
1004
|
+
else
|
|
1005
|
+
uniqPubkey(pubkeys, u.PubT.schnorr);
|
|
1006
|
+
return keySets.map((keys) => {
|
|
1007
|
+
// Keys were validated once above. Encoding through OutScript here would repeat lift_x for
|
|
1008
|
+
// every key in every combination, turning a bounded leaf set into avoidable curve-level work.
|
|
1009
|
+
const ops = [];
|
|
1010
|
+
for (let i = 0; i < keys.length - 1; i++)
|
|
1011
|
+
ops.push(keys[i], 'CHECKSIGVERIFY');
|
|
1012
|
+
ops.push(keys[keys.length - 1], 'CHECKSIG');
|
|
1013
|
+
return {
|
|
1014
|
+
type: 'tr_ns',
|
|
1015
|
+
script: Script.encode(ops),
|
|
1016
|
+
};
|
|
1017
|
+
});
|
|
929
1018
|
};
|
|
930
1019
|
/**
|
|
931
1020
|
* Builds a single-key taproot leaf script.
|
|
@@ -934,6 +1023,7 @@ export const p2tr_ns = (m, pubkeys, allowSamePubkeys = false) => {
|
|
|
934
1023
|
* @param pubkey - Schnorr public key
|
|
935
1024
|
* @returns Taproot single-key leaf descriptor.
|
|
936
1025
|
* @throws If the taproot single-key leaf cannot be encoded. {@link Error}
|
|
1026
|
+
* @throws If the delegated leaf policy exceeds its supported range. {@link RangeError}
|
|
937
1027
|
* @example
|
|
938
1028
|
* Build a single-key tapscript leaf.
|
|
939
1029
|
* ```ts
|
|
@@ -960,7 +1050,7 @@ export const p2tr_pk = (pubkey) => p2tr_ns(1, [pubkey], undefined)[0];
|
|
|
960
1050
|
*/
|
|
961
1051
|
export function p2tr_ms(m, pubkeys, allowSamePubkeys = false) {
|
|
962
1052
|
if (!allowSamePubkeys)
|
|
963
|
-
uniqPubkey(pubkeys);
|
|
1053
|
+
uniqPubkey(pubkeys, u.PubT.schnorr);
|
|
964
1054
|
return {
|
|
965
1055
|
type: 'tr_ms',
|
|
966
1056
|
script: OutScript.encode({ type: 'tr_ms', pubkeys, m }),
|
|
@@ -974,6 +1064,7 @@ export function p2tr_ms(m, pubkeys, allowSamePubkeys = false) {
|
|
|
974
1064
|
* @param network - address network parameters
|
|
975
1065
|
* @returns Encoded Bitcoin address.
|
|
976
1066
|
* @throws If the requested address type is unknown. {@link Error}
|
|
1067
|
+
* @throws If a key-derived script value is outside its supported range. {@link RangeError}
|
|
977
1068
|
* @example
|
|
978
1069
|
* Pick the output type first, then derive the matching address from the private key.
|
|
979
1070
|
* ```ts
|