@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/README.md CHANGED
@@ -4,7 +4,7 @@ Audited & minimal library for creating, signing & decoding Bitcoin transactions.
4
4
 
5
5
  - ๐Ÿ”’ [**Audited**](#security) by an independent security firm
6
6
  - โœ๏ธ Create transactions, inputs, outputs, sign them
7
- - ๐Ÿ“ก No network code: simplified audits and offline usage
7
+ - ๐Ÿ“ก Optional network helper; core signer works offline
8
8
  - ๐Ÿ”€ UTXO selection with different strategies
9
9
  - ๐ŸŽป Classic & SegWit: P2PK, P2PKH, P2WPKH, P2SH, P2WSH, P2MS
10
10
  - ๐Ÿงช Schnorr & Taproot BIP340/BIP341: P2TR, P2TR-NS, P2TR-MS
@@ -49,6 +49,40 @@ For React Native, you may need a [polyfill for crypto.getRandomValues](https://g
49
49
  import * as btc from '@scure/btc-signer';
50
50
  ```
51
51
 
52
+ ### Quickstart
53
+
54
+ Create a Taproot address, build a transaction spending one of its UTXOs, sign and finalize it:
55
+
56
+ ```ts
57
+ import * as btc from '@scure/btc-signer';
58
+ import { hex } from '@scure/base';
59
+
60
+ const privKey = hex.decode('a1547d0a01c9acb2b8a4128f97bbcd74d9a5750a9a3d1571ee3d9840b41d1fbb');
61
+ const pubKey = btc.utils.pubSchnorr(privKey);
62
+
63
+ // Taproot payment for a single key. Also check out p2wpkh, p2sh, p2wsh & others below
64
+ const spend = btc.p2tr(pubKey);
65
+ console.log(spend.address);
66
+ // bc1pkw4e67whvet7q6xa854tstt7kjn9f7n3gqvyjqznezqwux0gnzhsrtlvy3
67
+
68
+ const tx = new btc.Transaction();
69
+ tx.addInput({
70
+ ...spend, // adds tapInternalKey & other fields required for signing
71
+ txid: '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858',
72
+ index: 0,
73
+ witnessUtxo: { script: spend.script, amount: 100_000n }, // amounts are always bigint sats
74
+ });
75
+ tx.addOutputAddress(spend.address!, 90_000n); // leftover 10k sats become the fee
76
+ tx.sign(privKey);
77
+ tx.finalize();
78
+
79
+ console.log(tx.id);
80
+ // 927bc3441c7b376a83913b534bf10430139d9f64d83cbc38eb3a4e24cce54dc5
81
+ console.log(tx.hex); // ready to broadcast, e.g. via net.ts EsploraProvider
82
+ ```
83
+
84
+ The rest of the docs cover every payment type, PSBT workflows, UTXO selection and more:
85
+
52
86
  - [Payments](#payments)
53
87
  - [P2PK Pay To Public Key](#p2pk-pay-to-public-key)
54
88
  - [P2PKH Public Key Hash](#p2pkh-public-key-hash)
@@ -69,6 +103,7 @@ import * as btc from '@scure/btc-signer';
69
103
  - [Basic transaction sign](#basic-transaction-sign)
70
104
  - [BIP174 PSBT multi-sig example](#bip174-psbt-multi-sig-example)
71
105
  - [UTXO selection](#utxo-selection)
106
+ - [Network](#network)
72
107
  - [MuSig2](#musig2)
73
108
  - [P2P, ElligatorSwift, BIP324](#p2p-elligatorswift-bip324)
74
109
  - [Ordinals and custom scripts](#ordinals-and-custom-scripts)
@@ -519,27 +554,27 @@ type DerivationPath = { fingerprint: number; path: number[] };
519
554
  type TapScriptSigKey = { pubKey: Bytes; leafHash: Bytes };
520
555
  type TapLeafScriptKey = { version: number; internalKey: Bytes; merklePath: Bytes[] };
521
556
  type TransactionInput = {
522
- txid?: Bytes,
523
- index?: number,
524
- nonWitnessUtxo?: RawTransactionBytesOrHex,
525
- witnessUtxo?: { script?: Bytes; amount: bigint },
557
+ txid?: Bytes;
558
+ index?: number;
559
+ nonWitnessUtxo?: RawTransactionBytesOrHex;
560
+ witnessUtxo?: { script?: Bytes; amount: bigint };
526
561
  partialSig?: [Bytes, Bytes][]; // [PubKey, Signature]
527
- sighashType?: number,
528
- redeemScript?: Bytes,
529
- witnessScript?: Bytes,
562
+ sighashType?: number;
563
+ redeemScript?: Bytes;
564
+ witnessScript?: Bytes;
530
565
  bip32Derivation?: [Bytes, DerivationPath | undefined][]; // [PubKey, DeriviationPath]
531
- finalScriptSig?: Bytes,
532
- finalScriptWitness?: Bytes[],
533
- porCommitment?: Bytes,
534
- sequence?: number,
535
- requiredTimeLocktime?: number,
536
- requiredHeightLocktime?: number,
537
- tapKeySig?: Bytes,
566
+ finalScriptSig?: Bytes;
567
+ finalScriptWitness?: Bytes[];
568
+ porCommitment?: Bytes;
569
+ sequence?: number;
570
+ requiredTimeLocktime?: number;
571
+ requiredHeightLocktime?: number;
572
+ tapKeySig?: Bytes;
538
573
  tapScriptSig?: [TapScriptSigKey, Bytes][]; // [PubKeySchnorr, LeafHash]
539
574
  // [ControlBlock, ScriptWithVersion]
540
575
  tapLeafScript?: [TapLeafScriptKey, Bytes][];
541
- tapInternalKey?: Bytes,
542
- tapMerkleRoot?: Bytes,
576
+ tapInternalKey?: Bytes;
577
+ tapMerkleRoot?: Bytes;
543
578
  };
544
579
 
545
580
  // tx.addInput(input: TransactionInput): number;
@@ -632,12 +667,12 @@ const tx = new btc.Transaction();
632
667
  type Bytes = Uint8Array | string;
633
668
  type DerivationPath = { fingerprint: number; path: number[] };
634
669
  type TransactionOutput = {
635
- script?: Bytes,
636
- amount?: bigint,
637
- redeemScript?: Bytes,
638
- witnessScript?: Bytes,
670
+ script?: Bytes;
671
+ amount?: bigint;
672
+ redeemScript?: Bytes;
673
+ witnessScript?: Bytes;
639
674
  bip32Derivation?: [Bytes, DerivationPath | undefined][]; // [PubKey, DeriviationPath]
640
- tapInternalKey?: Bytes,
675
+ tapInternalKey?: Bytes;
641
676
  };
642
677
 
643
678
  // tx.addOutput(o: TransactionOutput): number;
@@ -1025,6 +1060,97 @@ deepStrictEqual(tx.id, 'b702078d65edd65a84b2a97a669df5631b06f42a67b0d7090e540b02
1025
1060
  deepStrictEqual(tx.fee, 394n);
1026
1061
  ```
1027
1062
 
1063
+ ## Network
1064
+
1065
+ Bitcoin nodes can't be used as source-of-truth to construct transactions & get UTXOs.
1066
+ An extra indexer is required. Our `net.js` submodule allows to easily fetch UTXOs,
1067
+ balances, and other data for an address.
1068
+ See [README-fullnode.md](./README-fullnode.md) for details and
1069
+ guide on running a full node with an indexer.
1070
+
1071
+ ```ts
1072
+ import * as btc from '@scure/btc-signer';
1073
+ import { EsploraProvider } from '@scure/btc-signer/net.js';
1074
+ import { pubECDSA } from '@scure/btc-signer/utils.js';
1075
+ const net = new EsploraProvider(fetch, 'http://127.0.0.1:3000');
1076
+ // Methods: `height`, `blockInfo`, `fee`, `balance`, `txCount`, `sendTx`, `waitForTx`, `txInfo`,
1077
+ // `unspent`, `transfers`, `history`, `historyMulti`.
1078
+ // Transient backend failures (429/5xx, dropped connections) are retried with backoff on GETs.
1079
+ // Long scans accept `signal` (AbortSignal) and `onProgress`; raw-tx fan-out is
1080
+ // bounded by `concurrency` (default 8).
1081
+
1082
+ // Get latest block.
1083
+ async function latestBlock() {
1084
+ const height = await net.height();
1085
+ const block = await net.blockInfo(height);
1086
+ return { number: block.number, hash: block.hash, timestamp: new Date(block.timestamp) };
1087
+ }
1088
+
1089
+ // Get per-address transaction history.
1090
+ async function addressTransactions(address: string) {
1091
+ const txs = await net.transfers(address, { limit: 10 });
1092
+ return txs.map((tx) => ({ txid: tx.txid, block: tx.block, fee: tx.info.fee }));
1093
+ }
1094
+
1095
+ // Stream history instead of buffering it: rows arrive newest-first while pages
1096
+ // are fetched, and stopping early also stops fetching.
1097
+ async function streamHistory(address: string) {
1098
+ for await (const tx of net.history(address, { onProgress: (p) => console.log(p.percent) })) {
1099
+ if (tx.block !== undefined && tx.block < 800_000) break;
1100
+ console.log(tx.txid, tx.transfers);
1101
+ }
1102
+ }
1103
+
1104
+ // Merged history for a set of addresses (HD wallets): one txid-deduplicated
1105
+ // stream; each row lists the watched addresses participating in it.
1106
+ async function walletHistory(addresses: string[]) {
1107
+ for await (const tx of net.historyMulti(addresses)) console.log(tx.txid, tx.addresses);
1108
+ }
1109
+
1110
+ // Broadcast, then wait for confirmation.
1111
+ async function sendAndWait(rawTx: string) {
1112
+ const txid = await net.sendTx(rawTx);
1113
+ return await net.waitForTx(txid, { confirmations: 2, timeoutMs: 3_600_000 });
1114
+ }
1115
+
1116
+ // Get UTXOs, select inputs, and sign. Call with a key that controls funded UTXOs.
1117
+ async function signSpend(privKey: Uint8Array, to: string, amount: bigint) {
1118
+ const spend = btc.p2wpkh(pubECDSA(privKey));
1119
+ const unspent = await net.unspent(spend.address!);
1120
+ const feePerByte = await net.fee(2);
1121
+ const selected = btc.selectUTXO(unspent.utxo, [{ address: to, amount }], 'default', {
1122
+ feePerByte,
1123
+ changeAddress: spend.address!,
1124
+ });
1125
+ if (!selected) throw new Error(`not enough funds for ${spend.address}`);
1126
+ const { tx } = selected;
1127
+ tx.sign(privKey);
1128
+ tx.finalize();
1129
+ return { txid: tx.id, raw: tx.hex };
1130
+ }
1131
+
1132
+ // For wrapped or script spends, add caller-owned metadata before selection:
1133
+ // const wrapped = btc.p2sh(btc.p2wpkh(pubECDSA(privKey)));
1134
+ // const base = await net.unspent(wrapped.address!);
1135
+ // const utxo = base.utxo.map((u) => ({ ...u, redeemScript: wrapped.redeemScript }));
1136
+ // const selected = btc.selectUTXO(utxo, outputs, 'default', opts);
1137
+ ```
1138
+
1139
+ First argument is `fetch` API-compatible transport.
1140
+ We suggest using `micro-ftch` package - a wrapper, which supports kill-switch,
1141
+ logging, timeouts, concurrency limits, replay fixtures, and other useful network controls:
1142
+
1143
+ ```ts
1144
+ import { ftch } from 'micro-ftch';
1145
+ let NETWORK_ENABLED = true;
1146
+ const fetcher = ftch(fetch, {
1147
+ isValidRequest: () => NETWORK_ENABLED,
1148
+ timeout: 10_000,
1149
+ concurrencyLimit: 4,
1150
+ });
1151
+ const net = new EsploraProvider(fetcher, 'http://127.0.0.1:3000')
1152
+ ```
1153
+
1028
1154
  ## MuSig2
1029
1155
 
1030
1156
  MuSig2 implementation conforming to [BIP-327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki)
@@ -1348,12 +1474,6 @@ For this package, there are 4 dependencies; and a few dev dependencies:
1348
1474
  - jsbt is used for benchmarking / testing / build tooling and developed by the same author
1349
1475
  - prettier, fast-check and typescript are used for code quality / test generation / ts compilation. It's hard to audit their source code thoroughly and fully because of their size
1350
1476
 
1351
- ## Contributing & testing
1352
-
1353
- - `npm install && npm run build && npm test` will build the code and run tests.
1354
- - `npm run lint` / `npm run format` will run linter / fix linter issues.
1355
- - `npm run build:release` will build single file
1356
-
1357
1477
  ## Learning & documentation
1358
1478
 
1359
1479
  There are several nice resources on the topic:
package/index.d.ts CHANGED
@@ -29,4 +29,3 @@ export type { CustomScript, OptScript } from './payment.ts';
29
29
  export { _DebugPSBT, TaprootControlBlock } from './psbt.ts';
30
30
  export { bip32Path, Decimal, DEFAULT_SEQUENCE, PSBTCombine, SigHash } from './transaction.ts';
31
31
  export { _cmpBig, _Estimator } from './utxo.ts';
32
- //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -29,4 +29,3 @@ export { _DebugPSBT, TaprootControlBlock } from "./psbt.js";
29
29
  // remove
30
30
  export { bip32Path, Decimal, DEFAULT_SEQUENCE, PSBTCombine, SigHash } from "./transaction.js";
31
31
  export { _cmpBig, _Estimator } from "./utxo.js";
32
- //# sourceMappingURL=index.js.map
package/musig2.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { WeierstrassPoint } from '@noble/curves/abstract/weierstrass.js';
1
2
  import { type TArg, type TRet } from './utils.ts';
2
3
  /** Represents a pair of public and secret nonces used in MuSig2 signing. */
3
4
  export type Nonces = {
@@ -16,6 +17,15 @@ export type DetNonce = {
16
17
  /** Partial signature produced after combining all participant data. */
17
18
  partialSig: Uint8Array;
18
19
  };
20
+ /** MuSig2 key aggregation context used by signing sessions. */
21
+ export type KeyAggregate = {
22
+ /** Aggregate public key before x-only export. */
23
+ aggPublicKey: WeierstrassPoint<bigint>;
24
+ /** Accumulated sign from x-only tweaks. */
25
+ gAcc: bigint;
26
+ /** Accumulated tweak scalar. */
27
+ tweakAcc: bigint;
28
+ };
19
29
  /**
20
30
  * Represents an error indicating an invalid contribution from a signer.
21
31
  * This allows pointing out which participant is malicious and what specifically is wrong.
@@ -86,11 +96,7 @@ export declare function sortKeys(publicKeys: TArg<Uint8Array[]>): TRet<Uint8Arra
86
96
  * ]);
87
97
  * ```
88
98
  */
89
- export declare function keyAggregate(publicKeys: TArg<Uint8Array[]>, tweaks?: TArg<Uint8Array[]>, isXonly?: boolean[]): {
90
- aggPublicKey: import("@noble/curves/abstract/weierstrass.js").WeierstrassPoint<bigint>;
91
- gAcc: bigint;
92
- tweakAcc: bigint;
93
- };
99
+ export declare function keyAggregate(publicKeys: TArg<Uint8Array[]>, tweaks?: TArg<Uint8Array[]>, isXonly?: boolean[]): KeyAggregate;
94
100
  /**
95
101
  * Exports the aggregate public key to a byte array.
96
102
  * @param ctx - result of {@link keyAggregate}
@@ -283,4 +289,3 @@ export declare class Session {
283
289
  * ```
284
290
  */
285
291
  export declare function deterministicSign(secret: TArg<Uint8Array>, aggOtherNonce: TArg<Uint8Array>, publicKeys: TArg<Uint8Array[]>, msg: TArg<Uint8Array>, tweaks?: TArg<Uint8Array[]>, isXonly?: boolean[], rand?: TArg<Uint8Array>, fastSign?: boolean): TRet<DetNonce>;
286
- //# sourceMappingURL=musig2.d.ts.map
package/musig2.js CHANGED
@@ -2,7 +2,7 @@ import { schnorr, secp256k1 } from '@noble/curves/secp256k1.js';
2
2
  import { aInRange, concatBytes, equalBytes, numberToBytesBE } from '@noble/curves/utils.js';
3
3
  import { abytes, anumber, randomBytes } from '@noble/hashes/utils.js';
4
4
  import * as P from 'micro-packed';
5
- import { compareBytes, hasEven } from "./utils.js";
5
+ import { compareBytes, hasEven, validateObject } from "./utils.js";
6
6
  /**
7
7
  * Represents an error indicating an invalid contribution from a signer.
8
8
  * This allows pointing out which participant is malicious and what specifically is wrong.
@@ -43,6 +43,9 @@ const PUBKEY_LEN = /* @__PURE__ */ (() => secp256k1.lengths.publicKey)();
43
43
  // BIP327 uses bytes(33, 0) both as cbytes_ext(Point.ZERO) for infinity and as GetSecondKey's
44
44
  // "no second distinct key" sentinel, so this all-zero compressed slot is intentionally out-of-band.
45
45
  const ZERO = /* @__PURE__ */ new Uint8Array(PUBKEY_LEN); // Compressed zero point
46
+ // Be friendly to bad ECMAScript parsers by not using bigint literals.
47
+ // prettier-ignore
48
+ const _0n = /* @__PURE__ */ BigInt(0), _1n = /* @__PURE__ */ BigInt(1);
46
49
  // Encoding
47
50
  // TODO: re-use in PSBT?
48
51
  // This is BIP327's cbytes_ext/cpoint_ext adapter: normal points stay in compressed SEC1,
@@ -54,7 +57,7 @@ const compressed = /* @__PURE__ */ (() => P.apply(P.bytes(33), {
54
57
  // This coder is only for stored secnonce limbs k1/k2, which BIP327 requires to be
55
58
  // nonzero scalars in [1, n); tweak scalars use different validation because 0 is allowed there.
56
59
  const scalar = /* @__PURE__ */ (() => P.validate(P.U256BE, (n) => {
57
- aInRange('n', n, 1n, Fn.ORDER);
60
+ aInRange('n', n, _1n, Fn.ORDER);
58
61
  return n;
59
62
  }))();
60
63
  // Shared for both per-signer pubnonce bytes and aggregate aggnonce bytes. Because it accepts
@@ -172,7 +175,7 @@ function keyAggCoeffInternal(publicKey1, publicKey2, L) {
172
175
  abytes(publicKey1, PUBKEY_LEN);
173
176
  abytes(publicKey2, PUBKEY_LEN);
174
177
  if (equalBytes(publicKey1, publicKey2))
175
- return 1n;
178
+ return _1n;
176
179
  return taggedInt('KeyAgg coefficient', L, publicKey1);
177
180
  }
178
181
  /**
@@ -223,6 +226,10 @@ export function keyAggregate(publicKeys, tweaks = [], isXonly = []) {
223
226
  }
224
227
  aggPublicKey = aggPublicKey.add(Pi.multiply(keyAggCoeffInternal(publicKeys[i], pk2, L)));
225
228
  }
229
+ // BIP327 KeyAggInternal: "Fail if is_infinite(Q)". Computationally unreachable for
230
+ // hash-derived coefficients, but the spec mandates the explicit check before tweaking.
231
+ if (isZero(aggPublicKey))
232
+ throw new Error('keyAggregate: aggregate public key cannot be infinity');
226
233
  let gAcc = Fn.ONE;
227
234
  let tweakAcc = Fn.ZERO;
228
235
  // Apply tweaks
@@ -257,6 +264,9 @@ export function keyAggregate(publicKeys, tweaks = [], isXonly = []) {
257
264
  * ```
258
265
  */
259
266
  export function keyAggExport(ctx) {
267
+ validateObject(ctx, {}, {}, 'ctx');
268
+ if (!(ctx.aggPublicKey instanceof Point))
269
+ throw new TypeError('"ctx.aggPublicKey" expected point, got type=' + typeof ctx.aggPublicKey);
260
270
  // BIP327 GetXonlyPubkey returns xbytes(Q), so this is the 32-byte x-only aggregate key
261
271
  // instead of the 33-byte compressed SEC1 form.
262
272
  return pointToBytes(ctx.aggPublicKey);
@@ -425,6 +435,7 @@ export class Session {
425
435
  * @throws If the input is invalid, such as wrong array sizes or lengths. {@link Error}
426
436
  */
427
437
  constructor(aggNonce, publicKeys, msg, tweaks = [], isXonly = []) {
438
+ abytes(aggNonce, 66);
428
439
  abytesArray(publicKeys, 33);
429
440
  abytesArray(tweaks, 32);
430
441
  aXonly(isXonly);
@@ -443,7 +454,10 @@ export class Session {
443
454
  this.gAcc = gAcc;
444
455
  this.tweakAcc = tweakAcc;
445
456
  this.b = taggedInt('MuSig/noncecoef', aggNonce, pointToBytes(aggPublicKey), msg);
446
- const R = R1.add(R2.multiply(this.b));
457
+ // b and the nonce points are public session values, so the faster variable-time
458
+ // multiplication is safe here; it also matches the reference point_mul, which
459
+ // maps a (negligible-probability) zero coefficient to infinity instead of failing.
460
+ const R = R1.add(R2.multiplyUnsafe(this.b));
447
461
  this.R = isZero(R) ? Point.BASE : R;
448
462
  this.e = taggedInt('BIP0340/challenge', pointToBytes(this.R), pointToBytes(aggPublicKey), msg);
449
463
  this.tweaks = tweaks.map((t) => Uint8Array.from(t));
@@ -476,13 +490,16 @@ export class Session {
476
490
  // BIP327 PartialSigVerifyInternal: `Let s = int(psig); fail if s >= n`, so s=0 must stay
477
491
  // in the public verification equation and return false on mismatch instead of throwing.
478
492
  const { R1, R2 } = PubNonce.decode(publicNonce);
479
- const Re_s_ = R1.add(R2.multiply(b));
493
+ // Verification only handles public data (nonces, pubkeys, hash-derived scalars),
494
+ // so the faster variable-time multiplications are safe here; they also match the
495
+ // reference point_mul, which maps zero scalars to infinity instead of failing.
496
+ const Re_s_ = R1.add(R2.multiplyUnsafe(b));
480
497
  const Re_s = hasEven(R.y) ? Re_s_ : Re_s_.negate();
481
498
  const P = Point.fromBytes(publicKey);
482
499
  const a = this.getSessionKeyAggCoeff(P);
483
- const g = Fn.mul(evenScalar(Q, 1n), gAcc);
500
+ const g = Fn.mul(evenScalar(Q, _1n), gAcc);
484
501
  const left = Point.BASE.multiplyUnsafe(s);
485
- const right = Re_s.add(P.multiply(Fn.mul(e, Fn.mul(a, g))));
502
+ const right = Re_s.add(P.multiplyUnsafe(Fn.mul(e, Fn.mul(a, g))));
486
503
  return left.equals(right);
487
504
  }
488
505
  /**
@@ -510,7 +527,7 @@ export class Session {
510
527
  if (!Fn.isValid(k1_))
511
528
  throw new Error('wrong k1');
512
529
  if (!Fn.isValid(k2_))
513
- throw new Error('wrong k1');
530
+ throw new Error('wrong k2');
514
531
  const k1 = evenScalar(R, k1_);
515
532
  const k2 = evenScalar(R, k2_);
516
533
  const d_ = Fn.fromBytes(secret);
@@ -521,7 +538,7 @@ export class Session {
521
538
  if (!equalBytes(pk, originalPk))
522
539
  throw new Error('Public key does not match nonceGen argument');
523
540
  const a = this.getSessionKeyAggCoeff(P);
524
- const g = evenScalar(Q, 1n);
541
+ const g = evenScalar(Q, _1n);
525
542
  const d = Fn.mul(g, Fn.mul(gAcc, d_));
526
543
  /// k1 + (b*k2) + (e*a*d)
527
544
  const s = Fn.add(k1, Fn.add(Fn.mul(b, k2), Fn.mul(e, Fn.mul(a, d))));
@@ -582,14 +599,14 @@ export class Session {
582
599
  if (partialSigs.length < 1)
583
600
  throw new RangeError('partialSigs.length must be >= 1');
584
601
  const { Q, tweakAcc, R, e } = this;
585
- let s = 0n;
602
+ let s = _0n;
586
603
  for (let i = 0; i < partialSigs.length; i++) {
587
604
  const si = Fn.fromBytes(partialSigs[i], true);
588
605
  if (!Fn.isValid(si))
589
606
  throw new InvalidContributionErr(i, 'psig');
590
607
  s = Fn.add(s, si);
591
608
  }
592
- const g = evenScalar(Q, 1n);
609
+ const g = evenScalar(Q, _1n);
593
610
  s = Fn.add(s, Fn.mul(e, Fn.mul(g, tweakAcc))); // s + e * g * tweakAcc
594
611
  return concatBytes(pointToBytes(R), Fn.toBytes(s));
595
612
  }
@@ -653,4 +670,3 @@ export function deterministicSign(secret, aggOtherNonce, publicKeys, msg, tweaks
653
670
  const partialSig = session.sign(secretNonce, secret, fastSign);
654
671
  return { publicNonce, partialSig };
655
672
  }
656
- //# sourceMappingURL=musig2.js.map