ecash-lib 4.9.0 → 4.11.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.
Files changed (43) hide show
  1. package/README.md +2 -0
  2. package/dist/consts.d.ts +2 -0
  3. package/dist/consts.d.ts.map +1 -1
  4. package/dist/consts.js +3 -1
  5. package/dist/consts.js.map +1 -1
  6. package/dist/ffi/ecash_lib_wasm_bg_browser.js +20495 -20495
  7. package/dist/ffi/ecash_lib_wasm_bg_browser.wasm +0 -0
  8. package/dist/ffi/ecash_lib_wasm_bg_nodejs.wasm +0 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/psbt.d.ts +65 -0
  14. package/dist/psbt.d.ts.map +1 -0
  15. package/dist/psbt.js +494 -0
  16. package/dist/psbt.js.map +1 -0
  17. package/dist/script.d.ts +60 -0
  18. package/dist/script.d.ts.map +1 -1
  19. package/dist/script.js +217 -0
  20. package/dist/script.js.map +1 -1
  21. package/dist/signatories.d.ts +36 -0
  22. package/dist/signatories.d.ts.map +1 -0
  23. package/dist/signatories.js +51 -0
  24. package/dist/signatories.js.map +1 -0
  25. package/dist/tx.d.ts +20 -0
  26. package/dist/tx.d.ts.map +1 -1
  27. package/dist/tx.js +51 -0
  28. package/dist/tx.js.map +1 -1
  29. package/dist/txBuilder.d.ts +2 -30
  30. package/dist/txBuilder.d.ts.map +1 -1
  31. package/dist/txBuilder.js +1 -45
  32. package/dist/txBuilder.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/consts.ts +3 -0
  35. package/src/ffi/ecash_lib_wasm_bg_browser.js +20495 -20495
  36. package/src/ffi/ecash_lib_wasm_bg_browser.wasm +0 -0
  37. package/src/ffi/ecash_lib_wasm_bg_nodejs.wasm +0 -0
  38. package/src/index.ts +2 -0
  39. package/src/psbt.ts +590 -0
  40. package/src/script.ts +312 -1
  41. package/src/signatories.ts +85 -0
  42. package/src/tx.ts +51 -0
  43. package/src/txBuilder.ts +2 -74
package/src/script.ts CHANGED
@@ -7,9 +7,21 @@ import { Writer } from './io/writer.js';
7
7
  import { WriterLength } from './io/writerlength.js';
8
8
  import { WriterBytes } from './io/writerbytes.js';
9
9
  import { toHex, fromHex } from './io/hex.js';
10
- import { Op, pushBytesOp, readOp, writeOp } from './op.js';
11
10
  import {
11
+ isPushOp,
12
+ Op,
13
+ parseNumberFromOp,
14
+ pushBytesOp,
15
+ pushNumberOp,
16
+ readOp,
17
+ writeOp,
18
+ } from './op.js';
19
+ import {
20
+ OP_0,
21
+ OP_1,
22
+ OP_16,
12
23
  OP_CHECKSIG,
24
+ OP_CHECKMULTISIG,
13
25
  OP_CODESEPARATOR,
14
26
  OP_DUP,
15
27
  OP_EQUAL,
@@ -17,6 +29,7 @@ import {
17
29
  OP_HASH160,
18
30
  } from './opcode.js';
19
31
  import { Bytes } from './io/bytes.js';
32
+ import { MAX_PUBKEYS_PER_MULTISIG } from './consts.js';
20
33
  import { Address } from './address/address';
21
34
 
22
35
  /** A Bitcoin Script locking/unlocking a UTXO */
@@ -172,6 +185,304 @@ export class Script {
172
185
  public static p2pkhSpend(pk: Uint8Array, sig: Uint8Array): Script {
173
186
  return Script.fromOps([pushBytesOp(sig), pushBytesOp(pk)]);
174
187
  }
188
+
189
+ /**
190
+ * Build m-of-n multisig script: OP_m pubkey1 pubkey2 ... pubkey_n OP_n OP_CHECKMULTISIG.
191
+ * Works for P2SH-wrapped or bare multisig.
192
+ * Pubkeys are used in the order given; callers who want canonical (sorted) multisig
193
+ * must sort pubkeys before calling.
194
+ */
195
+ public static multisig(minNumPks: number, pubkeys: Uint8Array[]): Script {
196
+ const numPubkeys = pubkeys.length;
197
+ if (minNumPks < 1) {
198
+ throw new Error(`minNumPks must be >= 1 (got ${minNumPks})`);
199
+ }
200
+ if (minNumPks > numPubkeys) {
201
+ throw new Error(
202
+ `minNumPks must be <= numPubkeys (got ${minNumPks} of ${numPubkeys})`,
203
+ );
204
+ }
205
+ if (numPubkeys > MAX_PUBKEYS_PER_MULTISIG) {
206
+ throw new Error(
207
+ `numPubkeys must be <= ${MAX_PUBKEYS_PER_MULTISIG} (got ${numPubkeys})`,
208
+ );
209
+ }
210
+ const ops: Op[] = [
211
+ pushNumberOp(minNumPks),
212
+ ...pubkeys.map(pk => pushBytesOp(pk)),
213
+ pushNumberOp(numPubkeys),
214
+ OP_CHECKMULTISIG,
215
+ ];
216
+ return Script.fromOps(ops);
217
+ }
218
+
219
+ /**
220
+ * Build scriptSig for multisig: <dummy> sig1 sig2 ... sig_m [redeemScript].
221
+ * Omit redeemScript for bare multisig; include for P2SH-wrapped.
222
+ * Use undefined for missing signatures (replaced with 0x01 placeholder).
223
+ * For Schnorr sigs, pass pubkeyIndices (set of signer indices) to build the
224
+ * checkbits dummy; signatures must be ordered by sorted pubkeyIndices.
225
+ * The full set of signer indices must be known when building Schnorr format.
226
+ * For partially signed inputs where the final signers are not yet known,
227
+ * use ECDSA format (omit pubkeyIndices) until the full signer set is determined.
228
+ * Ref spec https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/2019-11-15-schnorrmultisig.md
229
+ */
230
+ public static multisigSpend(params: {
231
+ signatures: (Uint8Array | undefined)[];
232
+ redeemScript?: Script;
233
+ pubkeyIndices?: Set<number>;
234
+ /** For bare Schnorr multisig: total pubkey count when redeemScript omitted */
235
+ numPubkeys?: number;
236
+ }): Script {
237
+ const { signatures, redeemScript, pubkeyIndices, numPubkeys } = params;
238
+ const PLACEHOLDER = new Uint8Array([0x01]);
239
+ let dummyOp: Op;
240
+ if (pubkeyIndices !== undefined) {
241
+ const nVal =
242
+ numPubkeys ??
243
+ (redeemScript !== undefined
244
+ ? redeemScript.parseMultisigRedeemScript().numPubkeys
245
+ : undefined);
246
+ if (nVal === undefined) {
247
+ throw new Error(
248
+ 'pubkeyIndices requires redeemScript or numPubkeys to derive checkbits',
249
+ );
250
+ }
251
+ const mVal = redeemScript
252
+ ? redeemScript.parseMultisigRedeemScript().numSignatures
253
+ : signatures.length;
254
+ if (pubkeyIndices.size !== mVal) {
255
+ throw new Error(
256
+ `pubkeyIndices must have ${mVal} elements for ${mVal}-of-${nVal}`,
257
+ );
258
+ }
259
+ for (const i of pubkeyIndices) {
260
+ if (i < 0 || i >= nVal) {
261
+ throw new Error(
262
+ `pubkeyIndices index ${i} out of range [0, ${nVal})`,
263
+ );
264
+ }
265
+ }
266
+ dummyOp = pushBytesOp(
267
+ checkbitsFromPubkeyIndices(pubkeyIndices, nVal),
268
+ );
269
+ } else {
270
+ dummyOp = OP_0;
271
+ }
272
+ const ops: Op[] = [
273
+ dummyOp,
274
+ ...signatures.map(sig =>
275
+ pushBytesOp(sig !== undefined ? sig : PLACEHOLDER),
276
+ ),
277
+ ];
278
+ if (redeemScript !== undefined) {
279
+ ops.push(pushBytesOp(redeemScript.bytecode));
280
+ }
281
+ return Script.fromOps(ops);
282
+ }
283
+
284
+ /**
285
+ * Parse P2SH multisig spend script to extract signatures and redeem script.
286
+ * Returns signatures (undefined for placeholder) and parsed numSignatures, numPubkeys from redeem script.
287
+ * Supports both ECDSA (OP_0 + sigs) and Schnorr (checkbits + sigs) formats.
288
+ * Schnorr format accepts partial scriptSigs (checkbits may have fewer bits set than
289
+ * numSignatures when earlier signers may not yet know who else will sign).
290
+ * Not for bare multisig (where the locking script is in output, not input).
291
+ */
292
+ public parseP2shMultisigSpend(): {
293
+ signatures: (Uint8Array | undefined)[];
294
+ redeemScript: Script;
295
+ numSignatures: number;
296
+ numPubkeys: number;
297
+ pubkeys: Uint8Array[];
298
+ isSchnorr: boolean;
299
+ /** Indices of signers (0..numPubkeys-1) for Schnorr; undefined for ECDSA */
300
+ pubkeyIndices?: Set<number>;
301
+ } {
302
+ const ops: Op[] = [];
303
+ const iter = this.ops();
304
+ let op: Op | undefined;
305
+ while ((op = iter.next()) !== undefined) {
306
+ ops.push(op);
307
+ }
308
+ if (ops.length < 3) {
309
+ throw new Error('Invalid multisig scriptSig: too few ops');
310
+ }
311
+ const redeemScriptOp = ops[ops.length - 1];
312
+ if (!isPushOp(redeemScriptOp)) {
313
+ throw new Error(
314
+ 'Invalid multisig scriptSig: redeem script must be final push',
315
+ );
316
+ }
317
+ const redeemScript = new Script(redeemScriptOp.data);
318
+ const parsed = Script.parseMultisigScriptSig(
319
+ ops.slice(0, -1),
320
+ redeemScript,
321
+ );
322
+ return { ...parsed, redeemScript };
323
+ }
324
+
325
+ /**
326
+ * Parse bare multisig spend scriptSig to extract signatures.
327
+ * For bare multisig the output script is the multisig script; the scriptSig
328
+ * is [dummy] sig1 sig2 ... sig_m with no redeem script.
329
+ * Assumes a fully-formed scriptSig (see parseP2shMultisigSpend for details).
330
+ * @param outputScript - The multisig output script (OP_m pubkey1 ... pubkey_n OP_n OP_CHECKMULTISIG)
331
+ */
332
+ public parseBareMultisigSpend(outputScript: Script): {
333
+ signatures: (Uint8Array | undefined)[];
334
+ numSignatures: number;
335
+ numPubkeys: number;
336
+ pubkeys: Uint8Array[];
337
+ isSchnorr: boolean;
338
+ pubkeyIndices?: Set<number>;
339
+ } {
340
+ const ops: Op[] = [];
341
+ const iter = this.ops();
342
+ let op: Op | undefined;
343
+ while ((op = iter.next()) !== undefined) {
344
+ ops.push(op);
345
+ }
346
+ return Script.parseMultisigScriptSig(ops, outputScript);
347
+ }
348
+
349
+ private parseMultisigRedeemScript(): {
350
+ numSignatures: number;
351
+ numPubkeys: number;
352
+ pubkeys: Uint8Array[];
353
+ } {
354
+ const ops: Op[] = [];
355
+ const iter = this.ops();
356
+ let op: Op | undefined;
357
+ while ((op = iter.next()) !== undefined) {
358
+ ops.push(op);
359
+ }
360
+ if (ops.length < 4) {
361
+ throw new Error('Invalid multisig redeem script');
362
+ }
363
+ const first = ops[0];
364
+ const lastBeforeCheck = ops[ops.length - 2];
365
+ const numSignatures = Number(parseNumberFromOp(first));
366
+ const numPubkeys = Number(parseNumberFromOp(lastBeforeCheck));
367
+ const pubkeys = ops
368
+ .slice(1, -2)
369
+ .filter((op): op is { opcode: number; data: Uint8Array } =>
370
+ isPushOp(op),
371
+ )
372
+ .map(op => op.data);
373
+ if (pubkeys.length !== numPubkeys) {
374
+ throw new Error(
375
+ `Invalid multisig redeem script: expected ${numPubkeys} pubkeys, got ${pubkeys.length}`,
376
+ );
377
+ }
378
+ return { numSignatures, numPubkeys, pubkeys };
379
+ }
380
+
381
+ private static parseMultisigScriptSig(
382
+ ops: Op[],
383
+ outputScript: Script,
384
+ ): {
385
+ signatures: (Uint8Array | undefined)[];
386
+ numSignatures: number;
387
+ numPubkeys: number;
388
+ pubkeys: Uint8Array[];
389
+ isSchnorr: boolean;
390
+ pubkeyIndices?: Set<number>;
391
+ } {
392
+ const { numSignatures, numPubkeys, pubkeys } =
393
+ outputScript.parseMultisigRedeemScript();
394
+ if (ops.length !== numSignatures + 1) {
395
+ throw new Error(
396
+ `Invalid multisig scriptSig: expected ${numSignatures + 1} ops (dummy + ${numSignatures} sigs), got ${ops.length}`,
397
+ );
398
+ }
399
+ const expectedCheckbitsLen = Math.floor((numPubkeys + 7) / 8);
400
+ let isSchnorr: boolean;
401
+ let sigOps: Op[];
402
+ let pubkeyIndices: Set<number> | undefined;
403
+
404
+ if (ops[0] === OP_0) {
405
+ isSchnorr = false;
406
+ sigOps = ops.slice(1);
407
+ } else {
408
+ let checkbitsData: Uint8Array;
409
+ const firstOp = ops[0];
410
+ if (
411
+ typeof firstOp === 'number' &&
412
+ firstOp >= OP_1 &&
413
+ firstOp <= OP_16
414
+ ) {
415
+ checkbitsData = new Uint8Array([firstOp - 0x50]);
416
+ } else if (isPushOp(firstOp) && firstOp.data.length > 0) {
417
+ checkbitsData = firstOp.data;
418
+ } else {
419
+ throw new Error(
420
+ 'Invalid multisig scriptSig: must start with OP_0 (ECDSA) or checkbits push (Schnorr)',
421
+ );
422
+ }
423
+ if (checkbitsData.length !== expectedCheckbitsLen) {
424
+ throw new Error(
425
+ `Invalid Schnorr multisig: checkbits length ${checkbitsData.length} != expected ${expectedCheckbitsLen} for numPubkeys=${numPubkeys}`,
426
+ );
427
+ }
428
+ isSchnorr = true;
429
+ pubkeyIndices = checkbitsToIndices(checkbitsData, numPubkeys);
430
+ sigOps = ops.slice(1);
431
+ }
432
+
433
+ if (sigOps.length !== numSignatures) {
434
+ throw new Error(
435
+ `Invalid multisig scriptSig: expected ${numSignatures} signatures, got ${sigOps.length}`,
436
+ );
437
+ }
438
+ const signatures: (Uint8Array | undefined)[] = sigOps.map(op => {
439
+ if (!isPushOp(op)) return undefined;
440
+ return op.data.length === 1 && op.data[0] === 0x01
441
+ ? undefined
442
+ : op.data;
443
+ });
444
+ return {
445
+ signatures,
446
+ numSignatures,
447
+ numPubkeys,
448
+ pubkeys,
449
+ isSchnorr,
450
+ ...(pubkeyIndices !== undefined && { pubkeyIndices }),
451
+ };
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Build checkbits byte array from pubkey indices for Schnorr multisig.
457
+ * Spec: length = floor((N+7)/8), little-endian, bit i = 1 iff index i in set.
458
+ */
459
+ function checkbitsFromPubkeyIndices(
460
+ indices: Set<number>,
461
+ numPubkeys: number,
462
+ ): Uint8Array {
463
+ const numBytes = Math.floor((numPubkeys + 7) / 8);
464
+ const bytes = new Uint8Array(numBytes);
465
+ for (const i of indices) {
466
+ bytes[i >>> 3] |= 1 << (i & 7);
467
+ }
468
+ return bytes;
469
+ }
470
+
471
+ /**
472
+ * Parse checkbits byte array to set of pubkey indices for Schnorr multisig.
473
+ * Inverse of checkbitsFromPubkeyIndices.
474
+ */
475
+ function checkbitsToIndices(
476
+ checkbits: Uint8Array,
477
+ numPubkeys: number,
478
+ ): Set<number> {
479
+ const indices = new Set<number>();
480
+ for (let i = 0; i < numPubkeys; i++) {
481
+ if ((checkbits[i >>> 3]! & (1 << (i & 7))) !== 0) {
482
+ indices.add(i);
483
+ }
484
+ }
485
+ return indices;
175
486
  }
176
487
 
177
488
  /** Iterator over the Ops of a Script. */
@@ -0,0 +1,85 @@
1
+ // Copyright (c) 2024-2026 The Bitcoin developers
2
+ // Distributed under the MIT software license, see the accompanying
3
+ // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
+
5
+ /**
6
+ * Signatories and signing helpers used with `TxBuilder` (`TxBuilderInput.signatory`).
7
+ */
8
+
9
+ import { Ecc } from './ecc.js';
10
+ import { sha256d } from './hash.js';
11
+ import { WriterBytes } from './io/writerbytes.js';
12
+ import { pushBytesOp } from './op.js';
13
+ import { Script } from './script.js';
14
+ import { SigHashType, SigHashTypeVariant } from './sigHashType.js';
15
+ import { UnsignedTxInput } from './unsignedTx.js';
16
+
17
+ /**
18
+ * Function that contains all the required data to sign a given `input` and
19
+ * return the scriptSig.
20
+ *
21
+ * Use it by attaching a `Signatory` to a TxBuilderInput, e.g. like this for a
22
+ * P2PKH input:
23
+ * ```ts
24
+ * new TxBuilder({
25
+ * inputs: [{
26
+ * input: { prevOut: ... },
27
+ * signatory: P2PKHSignatory(sk, pk, ALL_BIP143),
28
+ * }],
29
+ * ...
30
+ * })
31
+ * ```
32
+ **/
33
+ export type Signatory = (ecc: Ecc, input: UnsignedTxInput) => Script;
34
+
35
+ /** Append the sighash flags to the signature */
36
+ export function flagSignature(
37
+ sig: Uint8Array,
38
+ sigHashFlags: SigHashType,
39
+ ): Uint8Array {
40
+ const writer = new WriterBytes(sig.length + 1);
41
+ writer.putBytes(sig);
42
+ writer.putU8(sigHashFlags.toInt() & 0xff);
43
+ return writer.data;
44
+ }
45
+
46
+ /**
47
+ * Sign the sighash using Schnorr for BIP143 signatures and ECDSA for Legacy
48
+ * signatures, and then flags the signature correctly
49
+ **/
50
+ export function signWithSigHash(
51
+ ecc: Ecc,
52
+ sk: Uint8Array,
53
+ sigHash: Uint8Array,
54
+ sigHashType: SigHashType,
55
+ ): Uint8Array {
56
+ const sig =
57
+ sigHashType.variant == SigHashTypeVariant.LEGACY
58
+ ? ecc.ecdsaSign(sk, sigHash)
59
+ : ecc.schnorrSign(sk, sigHash);
60
+ return flagSignature(sig, sigHashType);
61
+ }
62
+
63
+ /** Signatory for a P2PKH input. Always uses Schnorr signatures */
64
+ export const P2PKHSignatory = (
65
+ sk: Uint8Array,
66
+ pk: Uint8Array,
67
+ sigHashType: SigHashType,
68
+ ) => {
69
+ return (ecc: Ecc, input: UnsignedTxInput): Script => {
70
+ const preimage = input.sigHashPreimage(sigHashType);
71
+ const sighash = sha256d(preimage.bytes);
72
+ const sigFlagged = signWithSigHash(ecc, sk, sighash, sigHashType);
73
+ return Script.p2pkhSpend(pk, sigFlagged);
74
+ };
75
+ };
76
+
77
+ /** Signatory for a P2PK input. Always uses Schnorr signatures */
78
+ export const P2PKSignatory = (sk: Uint8Array, sigHashType: SigHashType) => {
79
+ return (ecc: Ecc, input: UnsignedTxInput): Script => {
80
+ const preimage = input.sigHashPreimage(sigHashType);
81
+ const sighash = sha256d(preimage.bytes);
82
+ const sigFlagged = signWithSigHash(ecc, sk, sighash, sigHashType);
83
+ return Script.fromOps([pushBytesOp(sigFlagged)]);
84
+ };
85
+ };
package/src/tx.ts CHANGED
@@ -175,6 +175,57 @@ export class Tx {
175
175
  public txid(): string {
176
176
  return toHexRev(sha256d(this.ser()));
177
177
  }
178
+
179
+ /**
180
+ * Attempt to parse a **non-SegWit** serialized transaction from `data` — the same
181
+ * encoding as {@link Tx.deser} / `Tx.ser()` (version, inputs, outputs, locktime;
182
+ * no witness marker or witness stacks). eCash does not use SegWit transactions,
183
+ * so this is the full-transaction wire format on-chain here. Returns a `Tx` only
184
+ * when `data` is **exactly** one such transaction: the parse must consume the
185
+ * **entire** buffer (no trailing bytes). Returns `undefined` on malformed input
186
+ * or if any bytes remain after `locktime`.
187
+ *
188
+ * **PSBT-only motivation:** Bitcoin ABC’s PSBT input key `0x00` (`PSBT_IN_UTXO`)
189
+ * stores **either** a full previous transaction (BIP 174 “non-witness UTXO”) **or**
190
+ * a compact `CTxOut` (amount + `scriptPubKey`). Callers must disambiguate. Plain
191
+ * {@link Tx.deser} reads a tx from the start of `data` but **does not** require
192
+ * `data.length` to match the serialized length — leftover bytes are ignored, so
193
+ * you cannot use it to prove “this blob is solely a full tx.” This helper is
194
+ * used from PSBT (`resolveWitnessFromKey00` in `psbt.ts`): if `tryDeserExact`
195
+ * succeeds (and the txid matches), treat as non-witness UTXO; otherwise decode as
196
+ * `CTxOut`-shaped bytes.
197
+ */
198
+ public static tryDeserExact(data: Uint8Array): Tx | undefined {
199
+ try {
200
+ const bytes = new Bytes(data);
201
+ const version = bytes.readU32();
202
+ const numInputs = readVarSize(bytes);
203
+ const inputs: TxInput[] = [];
204
+ for (let i = 0; i < numInputs; ++i) {
205
+ const txid = bytes.readBytes(32);
206
+ const outIdx = bytes.readU32();
207
+ const script = Script.readWithSize(bytes);
208
+ const sequence = bytes.readU32();
209
+ inputs.push({
210
+ prevOut: { txid, outIdx },
211
+ script,
212
+ sequence,
213
+ });
214
+ }
215
+ const numOutputs = readVarSize(bytes);
216
+ const outputs: TxOutput[] = [];
217
+ for (let i = 0; i < numOutputs; ++i) {
218
+ outputs.push(readTxOutput(bytes));
219
+ }
220
+ const locktime = bytes.readU32();
221
+ if (bytes.idx !== data.length) {
222
+ return undefined;
223
+ }
224
+ return new Tx({ version, inputs, outputs, locktime });
225
+ } catch {
226
+ return undefined;
227
+ }
228
+ }
178
229
  }
179
230
 
180
231
  export function readTxOutput(bytes: Bytes): TxOutput {
package/src/txBuilder.ts CHANGED
@@ -3,11 +3,8 @@
3
3
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
4
 
5
5
  import { Ecc, EccDummy } from './ecc.js';
6
- import { sha256d } from './hash.js';
7
- import { WriterBytes } from './io/writerbytes.js';
8
- import { pushBytesOp } from './op.js';
6
+ import type { Signatory } from './signatories.js';
9
7
  import { Script } from './script.js';
10
- import { SigHashType, SigHashTypeVariant } from './sigHashType.js';
11
8
  import {
12
9
  DEFAULT_TX_VERSION,
13
10
  Tx,
@@ -18,27 +15,10 @@ import {
18
15
  } from './tx.js';
19
16
  import { UnsignedTx, UnsignedTxInput } from './unsignedTx.js';
20
17
 
21
- /**
22
- * Function that contains all the required data to sign a given `input` and
23
- * return the scriptSig.
24
- *
25
- * Use it by attaching a `Signatory` to a TxBuilderInput, e.g. like this for a
26
- * P2PKH input:
27
- * ```ts
28
- * new TxBuilder({
29
- * inputs: [{
30
- * input: { prevOut: ... },
31
- * signatory: P2PKHSignatory(sk, pk, ALL_BIP143),
32
- * }],
33
- * ...
34
- * })
35
- * ```
36
- **/
37
- export type Signatory = (ecc: Ecc, input: UnsignedTxInput) => Script;
38
-
39
18
  /** Builder input that bundles all the data required to sign a TxInput */
40
19
  export interface TxBuilderInput {
41
20
  input: TxInput;
21
+ /** Signing callback; see `Signatory` in `signatories.ts`. */
42
22
  signatory?: Signatory;
43
23
  }
44
24
 
@@ -226,55 +206,3 @@ export class TxBuilder {
226
206
  export function calcTxFee(txSize: number, feePerKb: bigint): bigint {
227
207
  return (BigInt(txSize) * BigInt(feePerKb) + 999n) / 1000n;
228
208
  }
229
-
230
- /** Append the sighash flags to the signature */
231
- export function flagSignature(
232
- sig: Uint8Array,
233
- sigHashFlags: SigHashType,
234
- ): Uint8Array {
235
- const writer = new WriterBytes(sig.length + 1);
236
- writer.putBytes(sig);
237
- writer.putU8(sigHashFlags.toInt() & 0xff);
238
- return writer.data;
239
- }
240
-
241
- /**
242
- * Sign the sighash using Schnorr for BIP143 signatures and ECDSA for Legacy
243
- * signatures, and then flags the signature correctly
244
- **/
245
- export function signWithSigHash(
246
- ecc: Ecc,
247
- sk: Uint8Array,
248
- sigHash: Uint8Array,
249
- sigHashType: SigHashType,
250
- ): Uint8Array {
251
- const sig =
252
- sigHashType.variant == SigHashTypeVariant.LEGACY
253
- ? ecc.ecdsaSign(sk, sigHash)
254
- : ecc.schnorrSign(sk, sigHash);
255
- return flagSignature(sig, sigHashType);
256
- }
257
-
258
- /** Signatory for a P2PKH input. Always uses Schnorr signatures */
259
- export const P2PKHSignatory = (
260
- sk: Uint8Array,
261
- pk: Uint8Array,
262
- sigHashType: SigHashType,
263
- ) => {
264
- return (ecc: Ecc, input: UnsignedTxInput): Script => {
265
- const preimage = input.sigHashPreimage(sigHashType);
266
- const sighash = sha256d(preimage.bytes);
267
- const sigFlagged = signWithSigHash(ecc, sk, sighash, sigHashType);
268
- return Script.p2pkhSpend(pk, sigFlagged);
269
- };
270
- };
271
-
272
- /** Signatory for a P2PK input. Always uses Schnorr signatures */
273
- export const P2PKSignatory = (sk: Uint8Array, sigHashType: SigHashType) => {
274
- return (ecc: Ecc, input: UnsignedTxInput): Script => {
275
- const preimage = input.sigHashPreimage(sigHashType);
276
- const sighash = sha256d(preimage.bytes);
277
- const sigFlagged = signWithSigHash(ecc, sk, sighash, sigHashType);
278
- return Script.fromOps([pushBytesOp(sigFlagged)]);
279
- };
280
- };