@caravan/psbt 1.2.0 → 1.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
@@ -2,22 +2,15 @@
2
2
 
3
3
  A set of utilities for working with PSBTs.
4
4
 
5
- ## Table of contents
6
-
7
- - [PSBTv0](#psbtv0)
8
- - [Exports](#psbtv0-exports)
5
+ # Table of contents
6
+ - [Constants](#constants)
7
+ - [Exports](#exports)
9
8
  - [`const PSBT_MAGIC_HEX`](#const-psbt_magic_hex)
10
9
  - [`const PSBT_MAGIC_B64`](#const-psbt_magic_b64)
11
10
  - [`const PSBT_MAGIC_BYTES`](#const-psbt_magic_bytes)
12
- - [`function autoLoadPSBT`](#function-autoloadpsbt)
13
- - [`function psbtInputFormatter`](#function-psbtinputformatter)
14
- - [`function psbtOutputFormatter`](#function-psbtoutputformatter)
15
- - [`function translatePSBT`](#function-translatepsbt)
16
- - [`function addSignaturesToPSBT`](#function-addsignaturestopsbt)
17
- - [`function parseSignaturesFromPSBT`](#function-parsesignaturesfrompsbt)
18
- - [`function parseSignatureArrayFromPSBT`](#function-parsesignaturearrayfrompsbt)
11
+ - [PSBTv0](#psbtv0)
19
12
  - [PSBTv2](#psbtv2)
20
- - [Exports](#psbtv2-exports)
13
+ - [Exports](#exports-1)
21
14
  - [`class PsbtV2`](#class-psbtv2)
22
15
  - [`get isReadyForConstructor`](#get-isreadyforconstructor)
23
16
  - [`get isReadyForUpdater`](#get-isreadyforupdater)
@@ -40,11 +33,13 @@ A set of utilities for working with PSBTs.
40
33
  - [Concepts](#concepts)
41
34
  - [The operator role saga](#the-operator-role-saga)
42
35
  - [TODO](#todo)
36
+ - [PsbtV2](#psbtv2-1)
37
+ - [Operator role validation](#operator-role-validation)
38
+ - [Class constructor](#class-constructor)
39
+ - [Add input timelocks](#add-input-timelocks)
40
+ - [Add input sighash\_single](#add-input-sighash_single)
43
41
 
44
- ## PSBTv0
45
-
46
- [BIP 174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki)
47
-
42
+ ## Constants
48
43
  ### Exports
49
44
 
50
45
  #### `const PSBT_MAGIC_HEX`
@@ -59,43 +54,13 @@ A utility constant for base64 encoded psbt magic bytes equal to `"cHNidP8"`.
59
54
 
60
55
  A utility constant for `Buffer` instance of psbt magic bytes.
61
56
 
62
- #### `function autoLoadPSBT`
63
-
64
- Given a string, try to create a Psbt object based on MAGIC (hex or Base64).
65
-
66
- #### `function psbtInputFormatter`
67
-
68
- Take a `MultisigTransactionInput` and turn it into a `MultisigTransactionPSBTInput`.
69
-
70
- #### `function psbtOutputFormatter`
71
57
 
72
- Take a `MultisigTransactionOutput` and turn it into a `MultisigTransactionPSBTOutput`.
73
-
74
- #### `function translatePSBT`
75
-
76
- Translates a PSBT into inputs/outputs consumable by supported non-PSBT devices in the `@caravan/wallets` library.
77
-
78
- #### `function addSignaturesToPSBT`
79
-
80
- Given an unsigned PSBT, an array of signing public key(s) (one per input), an array of signature(s) (one per input) in the same order as the pubkey(s), adds partial signature object(s) to each input and returns the PSBT with partial signature(s) included.
81
-
82
- #### `function parseSignaturesFromPSBT`
83
-
84
- Extracts the signature(s) from a PSBT.
85
-
86
- NOTE: there should be one signature per input, per signer.
87
-
88
- ADDITIONAL NOTE: because of the restrictions we place on braids to march their multisig addresses (slices) forward at the _same_ index across each chain of the braid, we do not run into a possible collision with this data structure. BUT - to have this method accommodate the _most_ general form of signature parsing, it would be wise to wrap this one level deeper like:
58
+ ## PSBTv0
89
59
 
90
- ```
91
- address: [pubkey : [signature(s)]]
92
- ```
60
+ [BIP 174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki)
93
61
 
94
- that way if your braid only advanced one chain's (member's) index so that a pubkey could be used in more than one address, everything would still function properly.
95
62
 
96
- #### `function parseSignatureArrayFromPSBT`
97
63
 
98
- Extracts signatures in order of inputs and returns as array (or array of arrays if multiple signature sets).
99
64
 
100
65
  ## PSBTv2
101
66
 
@@ -246,22 +211,80 @@ Attempts to extract the version number as uint32LE from raw psbt regardless of p
246
211
 
247
212
  The PSBT is a resource which may be passed between several operators or services. It's best to look at the operator roles as stages of a saga. The next valid operator role(s) can be determined by the state of the PSBT. The actions allowed for a PSBT are determined by which operator role the PSBT can be now and which role it could be next. See the following blog article at Unchained for a more detailed illustration: [Operator roles: Life stages in the saga of a PSBT](https://unchained.com/blog/operator-roles-life-stages-in-the-saga-of-a-psbt/)
248
213
 
249
- ### TODO
214
+ ## TODO
250
215
 
251
- #### PsbtV2
216
+ ### PsbtV2
252
217
 
253
- ##### Operator role validation
218
+ #### Operator role validation
254
219
 
255
220
  Work remains for determining readiness for operator roles Input Finalizer and Transaction Extractor. The getters responsible for these checks are `isReadyForInputFinalizer` and `isReadyForTransactionExtractor`. Work also remains to expand the PsbtV2 method functionality beyond the Signer role. A huge benefit might be gained from building methods aimed at the Combiner role.
256
221
 
257
- ##### Class constructor
222
+ #### Class constructor
258
223
 
259
224
  The constructor must be able to handle values which the Creator role is responsible for. Currently, the constructor can only accept an optional psbt which it parses to configure itself. It would be ideal if a fresh PsbtV2 instance could be initialized with minimal arguments for which the Creator role is responsible. See `private create()`.
260
225
 
261
- ##### Add input timelocks
226
+ #### Add input timelocks
262
227
 
263
228
  The `public addInput` must be able to properly handle input locktimes which interact with the global value.
264
229
 
265
- ##### Add input sighash_single
230
+ #### Add input sighash_single
266
231
 
267
232
  The `public addInput` must be able to properly handle new inputs when the psbt has a `SIGHASH_SINGLE` flag on `PSBT_GLOBAL_TX_MODIFIABLE`.
233
+
234
+ ## Troubleshooting and FAQ
235
+ ### What's with the vendor version of tiny-secp256k1?
236
+ In v6 of bitcoinjs-lib, which @caravan/psbt upgraded to use relative v5 in the older psbt code in @caravan/bitcoin,
237
+ some functions of the library require an elliptic curve library to be initialized w/ bitcoinjs-lib (see [this issue](https://github.com/bitcoinjs/bitcoinjs-lib/issues/1889#issuecomment-1443792692)), e.g. for taproot functionality.
238
+ For some reason, the recommended library `tiny-secp256k1` fails on initialization saying the library is invalid. The cause
239
+ seems to be a comparison of a Buffer with Uint8Array (see [this issue](https://github.com/bitcoinjs/tiny-secp256k1/issues/136) for more info).
240
+
241
+ A proposed fix is pending review and approval [here](https://github.com/bitcoinjs/tiny-secp256k1/pull/137). Unfortunately, since there
242
+ is a special build requirement to get the package code, the easiest way to get bitcoinjs initialized was to include patched vendor code in
243
+ the caravan codebase for now.
244
+
245
+ If a fork needs to be maintained and updated, to build and update the code, you can fork the repo, and run the docker build steps:
246
+
247
+ ```
248
+ % docker build -t tiny-secp256k1 .
249
+ % docker run -it --rm -v `pwd`:/tiny-secp256k1 -w /tiny-secp256k1 tiny-secp256k1
250
+ # make build
251
+ ```
252
+ Then copy the resulting built code (ends up in the lib directory) into the vendor/tiny-secp256k1. Currently
253
+ we just use the asmjs build to avoid wasm complications in
254
+ the build system.
255
+
256
+ ### Experimental VM Modules
257
+ This is related to the tiny-secp256k1 library which has difficulties importing in different environments.
258
+
259
+ Note that the jest configs and the special prefix in the npm test scripts were needed
260
+ to let jest understand the esmodule imports from the tiny-secp256k1 library.
261
+
262
+ The npm script prefix in particular may result in a console warning when running tests:
263
+
264
+ ```
265
+ ExperimentalWarning: VM Modules is an experimental feature and might change at any time
266
+ ```
267
+
268
+ This can be safely ignored. See the [documentation in jest](https://jestjs.io/docs/ecmascript-modules)
269
+ for more information on running with `--experimental-vm-modules`.
270
+
271
+
272
+ ### What's with the `bitcoinjs-lib-v6` dependency?
273
+
274
+ npm workspaces and maybe vite have issues with nested dependencies with
275
+ mismatched versions. `@caravan/psbt` requires v6 of bitcoinjs-lib but in order
276
+ to avoid making massive breaking changes, other libraries like `@caravan/bitcoin` are
277
+ still using bitcoinjs-lib v5. Unfortunately when being built altogether, it's possible
278
+ that the wrong version takes precedence, causing the build to break.
279
+
280
+ Because npm lacks a `nohoist` option for workspaces, the workaround is to use a kind
281
+ of alias in the package.json. So we add `"bitcoinjs-lib-v6": "npm:bitcoinjs-lib@^6.1.5",`
282
+ to say that we want to use v6.1.5 from npm whenever use the alias `bitcoinjs-lib-v6`
283
+ in imports in our code. This forces build systems to look for this reference and
284
+ the correct version of the package and avoid using a mismatch. Unfortunately
285
+ this was the only workaround that worked. `overrides` for example was not being
286
+ respected.
287
+
288
+ Learn more [here](https://github.com/vitejs/vite/issues/4245),
289
+ [here](https://github.com/zackerydev/noist?tab=readme-ov-file), and
290
+ [here](https://github.com/prisma/prisma/issues/9649).
package/dist/index.d.ts CHANGED
@@ -1,3 +1,7 @@
1
+ import { Network } from '@caravan/bitcoin';
2
+ import { Psbt } from 'bitcoinjs-lib-v6';
3
+ import { MultisigWalletConfig } from '@caravan/multisig';
4
+
1
5
  /**
2
6
  * Hex encoded string containing `<keytype><keydata>`. A string is needed for
3
7
  * Map.get() since it matches by identity. Most commonly, a `Key` only contains a
@@ -321,4 +325,103 @@ declare class PsbtV2 extends PsbtV2Maps {
321
325
  static FromV0(psbt: string | Buffer, allowTxnVersion1?: boolean): PsbtV2;
322
326
  }
323
327
 
324
- export { PsbtV2, getPsbtVersionNumber };
328
+ interface PsbtInput {
329
+ hash: string | Buffer;
330
+ index: number;
331
+ transactionHex: string;
332
+ redeemScript?: Buffer;
333
+ witnessScript?: Buffer;
334
+ bip32Derivation?: {
335
+ masterFingerprint: Buffer;
336
+ path: string;
337
+ pubkey: Buffer;
338
+ }[];
339
+ spendingWallet: MultisigWalletConfig;
340
+ }
341
+ interface PsbtOutput {
342
+ address: string;
343
+ value: number;
344
+ bip32Derivation?: {
345
+ masterFingerprint: Buffer;
346
+ path: string;
347
+ pubkey: Buffer;
348
+ }[];
349
+ redeemScript?: Buffer;
350
+ witnessScript?: Buffer;
351
+ }
352
+ /**
353
+ * This function seeks to be an updated version of the legacy `unsignedMultisigPSBT` function
354
+ * from @caravan/bitcoin.
355
+ * It takes the network and a set of inputs and outputs which it creates a PSBT from.
356
+ * This combines several operator roles of the PSBT saga into one function, getting a PSBT
357
+ * ready to be signed. It optionally can also add the global xpubs to the PSBT.
358
+ */
359
+ declare const getUnsignedMultisigPsbtV0: ({ network, inputs, outputs, includeGlobalXpubs, }: {
360
+ network: Network;
361
+ inputs: PsbtInput[];
362
+ outputs: PsbtOutput[];
363
+ includeGlobalXpubs?: boolean | undefined;
364
+ }) => Psbt;
365
+ declare const addGlobalXpubs: (psbt: Psbt, inputs: PsbtInput[], network: Network) => void;
366
+ /**
367
+ * Validate the signature on a psbt for a given input. Returns false if no
368
+ * valid signature is found otherwise returns the public key that was signed for.
369
+ *
370
+ * This is a port of the validateMultisigSignature function from @caravan/bitcoin
371
+ * to support a newer API and be more PSBT-native.
372
+ */
373
+ declare const validateMultisigPsbtSignature: (raw: string | Buffer, inputIndex: number, inputSignature: Buffer, inputAmount?: string) => boolean | string;
374
+
375
+ /**
376
+ * @file This file primarily contains utility functions migrated from the
377
+ * legacy psbt module in @caravan/bitcoin. With the new @caravan/psbt
378
+ * module, the goal is to make a more modular and legible API. But in order
379
+ * to make migrations easier from the old API, we need to provide conversion functions
380
+ * for converting the deeply nested objects in the legacy API.
381
+ */
382
+
383
+ interface LegacyMultisig {
384
+ /**
385
+ * JSON stringified object with the following properties:
386
+ * braidDetails: {
387
+ * network: Network;
388
+ * addressType: number;
389
+ * extendedPublicKeys: string[];
390
+ * requiredSigners: number;
391
+ * index: string;
392
+ * };
393
+ */
394
+ braidDetails: string;
395
+ bip32Derivation?: {
396
+ masterFingerprint: string;
397
+ path: string;
398
+ pubkey: Buffer;
399
+ }[];
400
+ }
401
+ interface LegacyInput {
402
+ txid: string;
403
+ index: number;
404
+ transactionHex: string;
405
+ amountSats: number | string;
406
+ multisig: LegacyMultisig;
407
+ }
408
+ interface LegacyOutput {
409
+ address: string;
410
+ amountSats: number | string;
411
+ bip32Derivation?: {
412
+ masterFingerprint: string;
413
+ path: string;
414
+ pubkey: Buffer;
415
+ }[];
416
+ witnessScript?: Buffer;
417
+ redeemScript?: Buffer;
418
+ multisig?: LegacyMultisig;
419
+ }
420
+ declare const convertLegacyInput: (input: LegacyInput) => PsbtInput;
421
+ declare const convertLegacyOutput: (output: LegacyOutput) => PsbtOutput;
422
+
423
+ declare const PSBT_MAGIC_HEX = "70736274ff";
424
+ declare const PSBT_MAGIC_B64 = "cHNidP8";
425
+ declare const PSBT_MAGIC_BYTES: Buffer;
426
+
427
+ export { LegacyInput, LegacyMultisig, LegacyOutput, PSBT_MAGIC_B64, PSBT_MAGIC_BYTES, PSBT_MAGIC_HEX, PsbtInput, PsbtOutput, PsbtV2, addGlobalXpubs, convertLegacyInput, convertLegacyOutput, getPsbtVersionNumber, getUnsignedMultisigPsbtV0, validateMultisigPsbtSignature };