@scure/btc-signer 2.2.0 → 2.3.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/p2p.d.ts CHANGED
@@ -44,4 +44,3 @@ export declare const elligatorSwift: Readonly<{
44
44
  getSharedSecret: (privateKeyA: TArg<Uint8Array>, publicKeyB: TArg<Uint8Array>) => TRet<Bytes>;
45
45
  getSharedSecretBip324: (privateKeyOurs: TArg<Uint8Array>, publicKeyTheirs: TArg<Uint8Array>, publicKeyOurs: TArg<Uint8Array>, initiating: boolean) => TRet<Uint8Array>;
46
46
  }>;
47
- //# sourceMappingURL=p2p.d.ts.map
package/p2p.js CHANGED
@@ -39,6 +39,9 @@ const MINUS_3_SQRT = Fp.sqrt(Fp.create(BigInt(-3)));
39
39
  const _3n = BigInt(3);
40
40
  const _4n = BigInt(4);
41
41
  const _7n = BigInt(7);
42
+ // Precomputed 1/2 mod p: turns the frequent "divide by 2" steps of XSwiftEC/XSwiftECInv
43
+ // into single multiplications instead of one modular inversion per call.
44
+ const INV_2 = Fp.inv(Fp.create(_2n));
42
45
  // This is the "lift_x(x) succeeds" predicate for field-normalized x values.
43
46
  // Raw x >= p would need the full BIP340 range check before reducing modulo p.
44
47
  const isValidX = (x) => FpIsSquare(Fp, Fp.add(Fp.mul(Fp.mul(x, x), x), _7n));
@@ -93,7 +96,7 @@ export const elligatorSwift = /* @__PURE__ */ Object.freeze({
93
96
  return; // [2 condition]
94
97
  if (ellCase & 1 && Fp.is0(r))
95
98
  return;
96
- v = Fp.div(Fp.add(Fp.neg(u), Fp.div(r, s)), _2n); // v = (-u + r / s) / 2
99
+ v = Fp.mul(Fp.add(Fp.neg(u), Fp.div(r, s)), INV_2); // v = (-u + r / s) / 2
97
100
  }
98
101
  const w = trySqrt(s);
99
102
  if (w === undefined)
@@ -102,7 +105,7 @@ export const elligatorSwift = /* @__PURE__ */ Object.freeze({
102
105
  const t0 = last & 1 ? Fp.add(_1n, MINUS_3_SQRT) : Fp.sub(_1n, MINUS_3_SQRT);
103
106
  const w0 = last === 0 || last === 5 ? Fp.neg(w) : w; // -w | w
104
107
  // w0 * (u * t0 / 2 + v)
105
- return Fp.mul(w0, Fp.add(Fp.div(Fp.mul(u, t0), _2n), v));
108
+ return Fp.mul(w0, Fp.add(Fp.mul(Fp.mul(u, t0), INV_2), v));
106
109
  },
107
110
  // Encode public key (point or x coordinate bigint) into 64-byte pseudorandom encoding
108
111
  // BIP324 samples encodings for x(P), so callers must pass a curve X coordinate in 0..p-1;
@@ -112,11 +115,19 @@ export const elligatorSwift = /* @__PURE__ */ Object.freeze({
112
115
  // so encode() must reject out-of-range x instead of silently reducing a different bigint modulo p.
113
116
  if (!Fp.isValid(x))
114
117
  throw new RangeError('elligatorSwift.encode: expected x coordinate in range 0..p-1');
118
+ // Off-curve x cannot round-trip: decode() only returns lift_x-able candidates, so
119
+ // the loop below would silently emit an encoding of a *different* public key.
120
+ if (!isValidX(x))
121
+ throw new RangeError('elligatorSwift.encode: expected x coordinate of a curve point');
115
122
  // 200k test cycles per keygen: avg=4 max=48
116
123
  // seems too much, but same as for reference implementation
117
124
  while (true) {
118
- // random scalar 1..Fp.ORDER
119
- const u = Fp.create(Fp.fromBytes(secp256k1.utils.randomSecretKey()));
125
+ // Random field element 1..p-1: BIP324 samples u over the whole field (the previous
126
+ // secret-key sampler silently restricted u to 1..n-1); decode() remaps u = 0, so
127
+ // zero cannot round-trip and is skipped.
128
+ const u = Fp.create(Fp.fromBytes(randomBytes(32), true));
129
+ if (Fp.is0(u))
130
+ continue;
120
131
  const ellCase = randomBytes(1)[0] & 7; // [0..8)
121
132
  const t = elligatorSwift._inv(x, u, ellCase);
122
133
  if (!t)
@@ -148,10 +159,12 @@ export const elligatorSwift = /* @__PURE__ */ Object.freeze({
148
159
  let res = Fp.add(u, Fp.mul(Fp.mul(y, y), _4n)); // u + 4 * Y ** 2,
149
160
  if (isValidX(res))
150
161
  return Fp.toBytes(res);
151
- res = Fp.div(Fp.sub(Fp.div(Fp.neg(x), y), u), _2n); // (-X / Y - u) / 2
162
+ // X / Y is shared by the remaining candidates; computing it once saves an inversion.
163
+ const xDivY = Fp.div(x, y);
164
+ res = Fp.mul(Fp.sub(Fp.neg(xDivY), u), INV_2); // (-X / Y - u) / 2
152
165
  if (isValidX(res))
153
166
  return Fp.toBytes(res);
154
- res = Fp.div(Fp.sub(Fp.div(x, y), u), _2n); // (X / Y - u) / 2
167
+ res = Fp.mul(Fp.sub(xDivY, u), INV_2); // (X / Y - u) / 2
155
168
  if (isValidX(res))
156
169
  return Fp.toBytes(res);
157
170
  throw new Error('elligatorSwift: cannot decode public key');
@@ -178,6 +191,10 @@ export const elligatorSwift = /* @__PURE__ */ Object.freeze({
178
191
  getSharedSecretBip324: (privateKeyOurs, publicKeyTheirs, publicKeyOurs, initiating) => {
179
192
  // BIP324 Shared secret computation hashes "the exactly 64-byte public keys'
180
193
  // encodings sent over the wire", so both ElligatorSwift inputs must be 64 bytes here.
194
+ // Initiator/responder ordering decides the hash-input order, so require a real boolean
195
+ // instead of letting arbitrary truthy values pick a side.
196
+ if (typeof initiating !== 'boolean')
197
+ throw new TypeError('"initiating" expected boolean, got type=' + typeof initiating);
181
198
  const ours = abytes(publicKeyOurs, 64, 'publicKeyOurs');
182
199
  const theirs = abytes(publicKeyTheirs, 64, 'publicKeyTheirs');
183
200
  const ecdhPoint = elligatorSwift.getSharedSecret(privateKeyOurs, theirs);
@@ -185,4 +202,3 @@ export const elligatorSwift = /* @__PURE__ */ Object.freeze({
185
202
  return tagSchnorr('bip324_ellswift_xonly_ecdh', ...pubs, ecdhPoint);
186
203
  },
187
204
  });
188
- //# sourceMappingURL=p2p.js.map
package/package.json CHANGED
@@ -1,46 +1,42 @@
1
1
  {
2
2
  "name": "@scure/btc-signer",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Audited & minimal library for Bitcoin. Handle transactions, Schnorr, Taproot, UTXO & PSBT",
5
5
  "files": [
6
6
  "*.js",
7
7
  "*.d.ts",
8
- "*.js.map",
9
- "*.d.ts.map",
10
8
  "src",
11
9
  "!_type_test.*"
12
10
  ],
13
11
  "dependencies": {
14
- "@noble/curves": "~2.2.0",
15
- "@noble/hashes": "~2.2.0",
16
- "@scure/base": "~2.2.0",
17
- "micro-packed": "~0.9.0"
12
+ "@noble/curves": "~2.3.0",
13
+ "@noble/hashes": "~2.3.0",
14
+ "@scure/base": "~2.3.0",
15
+ "micro-packed": "~0.11.0"
18
16
  },
19
17
  "devDependencies": {
20
- "@paulmillr/jsbt": "0.5.0",
21
- "@scure/bip32": "2.0.0",
18
+ "@paulmillr/jsbt": "0.6.5",
19
+ "@scure/bip32": "2.2.0",
20
+ "micro-ftch": "^1.1.0",
22
21
  "prettier": "3.6.2",
23
22
  "typescript": "6.0.2"
24
23
  },
25
24
  "scripts": {
25
+ "benchmark:size": "npx bismar@0.1.3 -s",
26
26
  "build": "tsc",
27
- "build:clean": "rm -f *.{js,d.ts,js.map,d.ts.map}",
28
- "build:release": "npx --no @paulmillr/jsbt esbuild test/build",
29
- "check": "npx --no @paulmillr/jsbt check package.json",
30
- "check:readme": "npx --no @paulmillr/jsbt readme package.json",
31
- "check:treeshake": "npx --no @paulmillr/jsbt treeshake package.json test/build/out-treeshake",
32
- "check:jsdoc": "npx --no @paulmillr/jsbt tsdoc package.json",
27
+ "build:clean": "rm -f *.{js,d.ts}",
28
+ "check": "jsbt-check",
29
+ "benchmark": "node benchmark/index.ts",
30
+ "benchmark:network": "node benchmark/network.ts",
33
31
  "format": "prettier --write src test/*.test.*",
34
- "test": "node --no-warnings test/index.ts",
35
- "test:bun": "bun test/index.ts",
36
- "test:deno": "deno --allow-env --allow-read test/index.ts",
37
- "test:node20": "cd test; npx tsc; node compiled/test/index.js",
32
+ "test": "node test/index.ts",
38
33
  "test:slow": "node test/slow.test.ts",
39
34
  "test:extended": "node --experimental-loader ./test/bitcoinjs-test/esm-loader.js ./test/bitcoinjs-test/index.test.js"
40
35
  },
41
36
  "exports": {
42
37
  ".": "./index.js",
43
38
  "./musig2.js": "./musig2.js",
39
+ "./net.js": "./net.js",
44
40
  "./p2p.js": "./p2p.js",
45
41
  "./payment.js": "./payment.js",
46
42
  "./psbt.js": "./psbt.js",
package/payment.d.ts CHANGED
@@ -91,7 +91,7 @@ export type CustomScript = Coder<OptScript, CustomScriptOut | undefined> & {
91
91
  export declare const OutScript: TRet<P.CoderType<NonNullable<OutP2AType | OutPKType | OutPKHType | OutSHType | OutWSHType | OutWPKHType | OutMSType | OutTRType | OutTRNSType | OutTRMSType | OutUnknownType | undefined>>>;
92
92
  /** Type of the output-script coder. */
93
93
  export type OutScriptType = typeof OutScript;
94
- type OutScriptValue = ReturnType<OutScriptType['decode']> | undefined;
94
+ type AddressValue = NonNullable<ReturnType<OutScriptType['decode']>>;
95
95
  /**
96
96
  * Validates that nested redeem and witness scripts match their wrappers.
97
97
  * @param script - top-level output script
@@ -561,9 +561,5 @@ export declare function WIF(network?: BTC_NETWORK): TRet<Coder<Bytes, string>>;
561
561
  * coder.encode(p2wpkh(pubECDSA(randomPrivateKeyBytes())));
562
562
  * ```
563
563
  */
564
- export declare function Address(network?: BTC_NETWORK): {
565
- encode(from: Exclude<OutScriptValue, undefined>): string;
566
- decode(address: string): OutScriptValue;
567
- };
564
+ export declare function Address(network?: BTC_NETWORK): TRet<P.Coder<AddressValue, string>>;
568
565
  export {};
569
- //# sourceMappingURL=payment.d.ts.map
package/payment.js CHANGED
@@ -6,10 +6,16 @@ import { TaprootControlBlock } from "./psbt.js";
6
6
  import { MAX_SCRIPT_BYTE_LENGTH, OpToNum, Script, VarBytes } from "./script.js";
7
7
  import * as u from "./utils.js";
8
8
  import { NETWORK } from "./utils.js";
9
+ // Pay to Anchor (P2A)
10
+ // BIP433 Pay-to-Anchor witness program bytes; the scriptPubKey is `OP_1 <0x4e73>`.
11
+ const P2A_PROGRAM = /* @__PURE__ */ Uint8Array.from([0x4e, 0x73]);
9
12
  const OutP2A = {
10
13
  encode(from) {
11
14
  // BIP433 defines P2A as the exact OP_1 <0x4e73> scriptPubKey.
12
- if (from.length !== 2 || from[0] !== 1 || !u.isBytes(from[1]) || hex.encode(from[1]) !== '4e73')
15
+ if (from.length !== 2 ||
16
+ from[0] !== 1 ||
17
+ !u.isBytes(from[1]) ||
18
+ !u.equalBytes(from[1], P2A_PROGRAM))
13
19
  return;
14
20
  return { type: 'p2a', script: Script.encode(from) };
15
21
  },
@@ -18,7 +24,7 @@ const OutP2A = {
18
24
  return;
19
25
  // The decoded object keeps `script` for caller convenience, but the `p2a`
20
26
  // tag always canonicalizes back to the fixed BIP433 script.
21
- return [1, hex.decode('4e73')];
27
+ return [1, Uint8Array.from(P2A_PROGRAM)];
22
28
  },
23
29
  };
24
30
  function isValidPubkey(pub, type) {
@@ -54,8 +60,10 @@ const OutPKH = {
54
60
  encode(from) {
55
61
  if (from.length !== 5 || from[0] !== 'DUP' || from[1] !== 'HASH160' || !u.isBytes(from[2]))
56
62
  return;
57
- // OutScript validates that the pushed HASH160 is exactly 20 bytes.
58
- // This child matcher only recognizes the canonical P2PKH opcode skeleton.
63
+ // Require the exact 20-byte HASH160 here so near-miss scripts fall through
64
+ // to OutUnknown instead of throwing in the OutScript validator on decode.
65
+ if (from[2].length !== 20)
66
+ return;
59
67
  if (from[3] !== 'EQUALVERIFY' || from[4] !== 'CHECKSIG')
60
68
  return;
61
69
  return { type: 'pkh', hash: from[2] };
@@ -70,8 +78,10 @@ const OutSH = {
70
78
  encode(from) {
71
79
  if (from.length !== 3 || from[0] !== 'HASH160' || !u.isBytes(from[1]) || from[2] !== 'EQUAL')
72
80
  return;
73
- // OutScript validates that the pushed HASH160 is exactly 20 bytes.
74
- // This child matcher only recognizes the canonical P2SH opcode skeleton.
81
+ // Require the exact 20-byte HASH160 here so near-miss scripts fall through
82
+ // to OutUnknown instead of throwing in the OutScript validator on decode.
83
+ if (from[1].length !== 20)
84
+ return;
75
85
  return { type: 'sh', hash: from[1] };
76
86
  },
77
87
  // OutScript validates `sh.hash` before this branch emits the canonical
@@ -119,11 +129,15 @@ const OutMS = {
119
129
  const pubkeys = from.slice(1, -2);
120
130
  if (n !== pubkeys.length)
121
131
  return;
132
+ // Require valid ECDSA pubkeys and `0 < m <= n` here so near-miss
133
+ // CHECKMULTISIG scripts (garbage keys, degenerate 0-of-0) fall through to
134
+ // OutUnknown instead of throwing in the OutScript validator on decode.
135
+ // Script.decode only yields 0..16 for opcode numbers, so n <= 16 holds.
122
136
  for (const pub of pubkeys)
123
- if (!u.isBytes(pub))
137
+ if (!u.isBytes(pub) || !isValidPubkey(pub, u.PubT.ecdsa))
124
138
  return;
125
- // OutScript validates pubkey encodings and `0 < m <= n <= 16`.
126
- // This child matcher only recognizes the canonical CHECKMULTISIG skeleton.
139
+ if (!Number.isSafeInteger(m) || m < 1 || m > n)
140
+ return;
127
141
  // We don't need n here because it is the same as pubkeys.length.
128
142
  return { type: 'ms', m, pubkeys: pubkeys };
129
143
  },
@@ -143,6 +157,11 @@ const OutTR = {
143
157
  // other OP_1 program lengths remain reserved future witness programs and should fall through.
144
158
  if (from.length !== 2 || from[0] !== 1 || !u.isBytes(from[1]) || from[1].length !== 32)
145
159
  return;
160
+ // A 32-byte v1 program with an off-curve x coordinate is a fundable but
161
+ // taproot-unspendable output; classify it as unknown instead of throwing
162
+ // in the OutScript validator on decode.
163
+ if (!isValidPubkey(from[1], u.PubT.schnorr))
164
+ return;
146
165
  return { type: 'tr', pubkey: from[1] };
147
166
  },
148
167
  // OutScript validates `tr.pubkey` before this branch emits the canonical
@@ -206,10 +225,15 @@ const OutTRMS = {
206
225
  return;
207
226
  continue;
208
227
  }
209
- if (!u.isBytes(elm))
228
+ // Require actual Schnorr pubkeys here (same as tr_ns) so near-miss
229
+ // CHECKSIGADD scripts fall through to OutUnknown instead of throwing
230
+ // in the OutScript validator on decode.
231
+ if (!u.isBytes(elm) || !isValidPubkey(elm, u.PubT.schnorr))
210
232
  return;
211
233
  pubkeys.push(elm);
212
234
  }
235
+ if (!Number.isSafeInteger(m) || m < 1 || m > pubkeys.length || pubkeys.length > 999)
236
+ return;
213
237
  return { type: 'tr_ms', pubkeys, m };
214
238
  },
215
239
  decode: (to) => {
@@ -457,6 +481,7 @@ export const p2pkh = (publicKey, network = NETWORK) => {
457
481
  * ```
458
482
  */
459
483
  export const p2sh = (child, network = NETWORK) => {
484
+ u.validateObject(child, {}, {}, 'child');
460
485
  // It is already tested inside noble-hashes and checkScript
461
486
  // BIP16 redeemScripts are pushed by scriptSig, so anything over the 520-byte pushed-element
462
487
  // limit would be fundable by HASH160 but unspendable once wrapped in P2SH.
@@ -506,6 +531,7 @@ export const p2sh = (child, network = NETWORK) => {
506
531
  * ```
507
532
  */
508
533
  export const p2wsh = (child, network = NETWORK) => {
534
+ u.validateObject(child, {}, {}, 'child');
509
535
  const cs = child.script;
510
536
  if (!u.isBytes(cs))
511
537
  throw new Error(`Wrong script: ${typeof cs}, expected Uint8Array`);
@@ -584,9 +610,17 @@ function checkTaprootScript(script, internalPubKey, allowUnknownOutputs = false,
584
610
  // disable custom. All custom scripts for taproot should have prefix 'tr_'
585
611
  if (customScripts) {
586
612
  const cs = P.apply(Script, P.coders.match(customScripts));
587
- const c = cs.decode(script);
613
+ let c;
614
+ // match() throws when no custom coder matches; treat that as "not a custom
615
+ // script" so the allowUnknownOutputs escape below stays reachable.
616
+ try {
617
+ c = cs.decode(script);
618
+ }
619
+ catch (e) {
620
+ c = undefined;
621
+ }
588
622
  if (c !== undefined) {
589
- if (typeof c.type !== 'string' || !c.type.startsWith('tr_'))
623
+ if (!u.astring(c.type, 'c.type').startsWith('tr_'))
590
624
  throw new Error(`P2TR: invalid custom type=${c.type}`);
591
625
  return;
592
626
  }
@@ -632,6 +666,15 @@ function checkTaprootScript(script, internalPubKey, allowUnknownOutputs = false,
632
666
  * ```
633
667
  */
634
668
  export function taprootListToTree(taprootList) {
669
+ u.aarray(taprootList, 'taprootList', (leaf, title) => {
670
+ // p2tr reduces non-binary trees through this helper, so nested branch arrays are valid here.
671
+ if (Array.isArray(leaf))
672
+ return;
673
+ u.validateObject(leaf, {}, {}, title);
674
+ // This helper only arranges weighted tree nodes; p2tr validates leaf scripts while hashing.
675
+ if (leaf.weight !== undefined)
676
+ anumber(leaf.weight, title + '.weight');
677
+ });
635
678
  // Empty flat lists cannot represent a taproot script tree; omit the tree entirely for
636
679
  // key-path-only outputs instead of passing [] here, otherwise this helper would return
637
680
  // undefined and downstream taproot tree walkers would fail much later on a non-tree value.
@@ -684,20 +727,23 @@ function taprootWalkTree(tree) {
684
727
  return [...taprootWalkTree(tree.left), ...taprootWalkTree(tree.right)];
685
728
  }
686
729
  function taprootHashTree(tree, internalPubKey, allowUnknownOutputs = false, customScripts) {
687
- if (!tree)
730
+ if (tree === undefined)
688
731
  throw new Error('taprootHashTree: empty tree');
732
+ if (!Array.isArray(tree) && !P.utils.isPlainObject(tree))
733
+ throw new TypeError('"tree" expected object or array, got type=' + typeof tree);
689
734
  if (Array.isArray(tree) && tree.length === 1)
690
735
  tree = tree[0];
691
736
  // Terminal node (leaf)
692
737
  if (!Array.isArray(tree)) {
738
+ u.validateObject(tree, {}, {}, 'tree');
693
739
  const version = tree.leafVersion;
694
740
  const { script: leafScript } = tree;
695
741
  // Earliest tree walk where we can validate tapScripts
696
742
  if (tree.tapLeafScript || (tree.tapMerkleRoot && !u.equalBytes(tree.tapMerkleRoot, P.EMPTY)))
697
743
  throw new Error('P2TR: tapRoot leafScript cannot have tree');
698
- const script = typeof leafScript === 'string' ? hex.decode(leafScript) : leafScript;
699
- if (!u.isBytes(script))
700
- throw new Error(`checkScript: wrong script type=${script}`);
744
+ const script = typeof leafScript === 'string'
745
+ ? hex.decode(leafScript)
746
+ : abytes(leafScript, undefined, 'tree.script');
701
747
  checkTaprootScript(script, internalPubKey, allowUnknownOutputs, customScripts);
702
748
  return {
703
749
  type: 'leaf',
@@ -766,18 +812,20 @@ export function p2tr(internalPubKey, tree, network = NETWORK, allowUnknownOutput
766
812
  let hashedTree = taprootAddPath(taprootHashTree(tree, pubKey, allowUnknownOutputs, customScripts));
767
813
  const tapMerkleRoot = hashedTree.hash;
768
814
  const [tweakedPubkey, parity] = u.taprootTweakPubkey(pubKey, tapMerkleRoot);
815
+ const tapLeafScript = [];
769
816
  const leaves = taprootWalkTree(hashedTree).map((l) => {
770
817
  const version = tapLeafVersion(l.version);
771
- return {
772
- ...l,
773
- // Leaf versions are stored as the base even byte; only the control block adds the
774
- // output-key parity bit required by BIP 341 script-path spending.
775
- controlBlock: TaprootControlBlock.encode({
776
- version: version + parity,
777
- internalKey: pubKey,
778
- merklePath: l.path,
779
- }),
818
+ // Leaf versions are stored as the base even byte; only the control block adds the
819
+ // output-key parity bit required by BIP 341 script-path spending.
820
+ const controlBlock = {
821
+ version: version + parity,
822
+ internalKey: pubKey,
823
+ merklePath: l.path,
780
824
  };
825
+ // Skip an encode/decode copy for performance; callers must treat returned metadata as
826
+ // immutable.
827
+ tapLeafScript.push([controlBlock, u.concatBytes(l.script, new Uint8Array([version]))]);
828
+ return { ...l, controlBlock: TaprootControlBlock.encode(controlBlock) };
781
829
  });
782
830
  return {
783
831
  type: 'tr',
@@ -788,10 +836,7 @@ export function p2tr(internalPubKey, tree, network = NETWORK, allowUnknownOutput
788
836
  // PSBT stuff
789
837
  tapInternalKey: pubKey,
790
838
  leaves,
791
- tapLeafScript: leaves.map((l) => [
792
- TaprootControlBlock.decode(l.controlBlock),
793
- u.concatBytes(l.script, new Uint8Array([tapLeafVersion(l.version)])),
794
- ]),
839
+ tapLeafScript,
795
840
  tapMerkleRoot,
796
841
  };
797
842
  }
@@ -938,6 +983,7 @@ export function p2tr_ms(m, pubkeys, allowSamePubkeys = false) {
938
983
  * ```
939
984
  */
940
985
  export function getAddress(type, privKey, network = NETWORK) {
986
+ u.astring(type, 'type');
941
987
  if (type === 'tr') {
942
988
  return p2tr(u.pubSchnorr(privKey), undefined, network).address;
943
989
  }
@@ -1076,15 +1122,21 @@ export function WIF(network = NETWORK) {
1076
1122
  * ```
1077
1123
  */
1078
1124
  export function Address(network = NETWORK) {
1125
+ u.validateObject(network, {}, {}, 'network');
1079
1126
  return {
1080
1127
  encode(from) {
1128
+ u.validateObject(from, {}, {}, 'from');
1081
1129
  const { type } = from;
1130
+ u.astring(type, 'from.type');
1082
1131
  if (type === 'wpkh')
1083
1132
  return programToWitness(0, from.hash, network);
1084
1133
  else if (type === 'wsh')
1085
1134
  return programToWitness(0, from.hash, network);
1086
1135
  else if (type === 'tr')
1087
1136
  return programToWitness(1, from.pubkey, network);
1137
+ // BIP433 P2A is the fixed v1 witness program 0x4e73 ('bc1pfeessrawgf').
1138
+ else if (type === 'p2a')
1139
+ return programToWitness(1, P2A_PROGRAM, network);
1088
1140
  else if (type === 'pkh')
1089
1141
  return formatKey(from.hash, [network.pubKeyHash]);
1090
1142
  else if (type === 'sh')
@@ -1092,6 +1144,7 @@ export function Address(network = NETWORK) {
1092
1144
  throw new Error(`Unknown address type=${type}`);
1093
1145
  },
1094
1146
  decode(address) {
1147
+ u.astring(address, 'address');
1095
1148
  if (address.length < 14 || address.length > 74)
1096
1149
  throw new Error('Invalid address length');
1097
1150
  // Bech32
@@ -1119,8 +1172,10 @@ export function Address(network = NETWORK) {
1119
1172
  return { type: 'wpkh', hash: data };
1120
1173
  else if (version === 1 && data.length === 32)
1121
1174
  return { type: 'tr', pubkey: data };
1175
+ else if (version === 1 && u.equalBytes(data, P2A_PROGRAM))
1176
+ return { type: 'p2a', script: Script.encode([1, data]) };
1122
1177
  // Future witness versions can still be valid addresses, but this helper
1123
- // only returns typed descriptors for recognized v0 and taproot templates.
1178
+ // only returns typed descriptors for recognized v0, taproot and P2A templates.
1124
1179
  else
1125
1180
  throw new Error('Unknown witness program');
1126
1181
  }
@@ -1141,4 +1196,3 @@ export function Address(network = NETWORK) {
1141
1196
  },
1142
1197
  };
1143
1198
  }
1144
- //# sourceMappingURL=payment.js.map
package/psbt.d.ts CHANGED
@@ -3216,4 +3216,3 @@ export declare const RawPSBTV2: Readonly<P.CoderType<P.StructInput<{
3216
3216
  })[];
3217
3217
  }>>>;
3218
3218
  export {};
3219
- //# sourceMappingURL=psbt.d.ts.map
package/psbt.js CHANGED
@@ -1,8 +1,12 @@
1
1
  import { hex } from '@scure/base';
2
+ import { anumber } from '@noble/hashes/utils.js';
2
3
  import * as P from 'micro-packed';
3
4
  import { CompactSize, CompactSizeLen, RawOldTx, RawOutput, RawTx, RawWitness, VarBytes, } from "./script.js";
4
- import { compareBytes, equalBytes, PubT, validatePubkey, } from "./utils.js";
5
+ import { aarray, compareBytes, equalBytes, PubT, validateObject, validatePubkey, } from "./utils.js";
5
6
  // PSBT BIP174, BIP370, BIP371
7
+ // Be friendly to bad ECMAScript parsers by not using bigint literals.
8
+ // prettier-ignore
9
+ const _0n = /* @__PURE__ */ BigInt(0), _1n = /* @__PURE__ */ BigInt(1);
6
10
  // BIP174 keydata only says "public key", so legacy PSBT ECDSA fields still accept both
7
11
  // compressed (33-byte) and uncompressed (65-byte) SEC1 encodings, but not x-only keys.
8
12
  const PubKeyECDSA = /* @__PURE__ */ (() => P.validate(P.bytes(null), (pub) => validatePubkey(pub, PubT.ecdsa)))();
@@ -121,10 +125,10 @@ const tapTree = /* @__PURE__ */ (() => P.validate(P.array(null, P.struct({
121
125
  next.push(0);
122
126
  path = next;
123
127
  }
124
- let leaves = 0n;
128
+ let leaves = _0n;
125
129
  for (let i = 0; i < tree.length; i++)
126
- leaves += 1n << BigInt(maxDepth - tree[i].depth);
127
- if (leaves !== 1n << BigInt(maxDepth))
130
+ leaves += _1n << BigInt(maxDepth - tree[i].depth);
131
+ if (leaves !== _1n << BigInt(maxDepth))
128
132
  throw new Error('tapTree: tuples must describe a complete binary tree');
129
133
  return tree;
130
134
  }))();
@@ -486,7 +490,7 @@ export const PSBTOutputCoder = /* @__PURE__ */ (() => Object.freeze(P.validate(P
486
490
  // in the field coder itself because BIP371 constrains the tuple value, not just the row shape.
487
491
  // BIP174/BIP370 define PSBT_OUT_AMOUNT as a signed int64 transport field, but it still
488
492
  // represents the transaction output amount in satoshis, so negative output values are invalid.
489
- if (o.amount !== undefined && o.amount < 0n)
493
+ if (o.amount !== undefined && o.amount < _0n)
490
494
  throw new Error(`validateOutput: wrong amount=${o.amount}`);
491
495
  if (o.bip32Derivation)
492
496
  for (const [k] of o.bip32Derivation)
@@ -574,6 +578,9 @@ function validatePSBTFields(version, info, lst) {
574
578
  * ```
575
579
  */
576
580
  export function cleanPSBTFields(version, info, lst) {
581
+ anumber(version, 'version');
582
+ validateObject(info, {}, {}, 'info');
583
+ validateObject(lst, {}, {}, 'lst');
577
584
  const _lst = lst;
578
585
  const out = {};
579
586
  for (const _k in _lst) {
@@ -647,6 +654,12 @@ function validatePSBT(tx) {
647
654
  * ```
648
655
  */
649
656
  export function mergeKeyMap(psbtEnum, val, cur, allowedFields, allowUnknown) {
657
+ validateObject(psbtEnum, {}, {}, 'psbtEnum');
658
+ validateObject(val, {}, {}, 'val');
659
+ if (cur !== undefined)
660
+ validateObject(cur, {}, {}, 'cur');
661
+ if (allowedFields !== undefined)
662
+ aarray(allowedFields, 'allowedFields');
650
663
  const _val = val;
651
664
  const _cur = cur;
652
665
  const _allowedFields = allowedFields;
@@ -750,4 +763,3 @@ export const RawPSBTV0 = /* @__PURE__ */ (() => Object.freeze(P.validate(_RawPSB
750
763
  // This wrapper only layers `validatePSBT`'s PSBTv2 required-field/count reconciliation on top
751
764
  // of `_RawPSBTV2`; nested input/output/global field invariants still depend on the coders below.
752
765
  export const RawPSBTV2 = /* @__PURE__ */ (() => Object.freeze(P.validate(_RawPSBTV2, validatePSBT)))();
753
- //# sourceMappingURL=psbt.js.map
package/script.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as P from 'micro-packed';
2
- import { type ValueOf, type Bytes, type TArg, type TRet } from './utils.ts';
2
+ import { type Bytes, type TArg, type TRet, type ValueOf } from './utils.ts';
3
3
  /**
4
4
  * Maximum byte size allowed for a single pushed script element.
5
5
  * BIP 342 keeps this 520-byte stack-element limit even though tapscript removes
@@ -351,4 +351,3 @@ export declare const RawOldTx: Readonly<P.CoderType<P.StructInput<{
351
351
  lockTime: number;
352
352
  }>>>;
353
353
  export {};
354
- //# sourceMappingURL=script.d.ts.map