@caravan/psbt 2.0.6 → 2.1.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 +18 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +143 -2
- package/dist/index.mjs +143 -2
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ A set of utilities for working with PSBTs.
|
|
|
28
28
|
- [`public addPartialSig`](#public-addpartialsig)
|
|
29
29
|
- [`public removePartialSig`](#public-removepartialsig)
|
|
30
30
|
- [`public setProprietaryValue`](#public-setproprietaryvalue)
|
|
31
|
+
- [`public combine`](#public-combine)
|
|
31
32
|
- [`static PsbtV2.FromV0`](#static-psbtv2fromv0)
|
|
32
33
|
- [`function getPsbtVersionNumber`](#function-getpsbtversionnumber)
|
|
33
34
|
- [Concepts](#concepts)
|
|
@@ -197,6 +198,23 @@ Args:
|
|
|
197
198
|
|
|
198
199
|
From the provided args, a key with the following format will be generated: `0xFC<compact uint identifier length><bytes identifier><bytes subtype><bytes subkeydata>`
|
|
199
200
|
|
|
201
|
+
##### `public combine`
|
|
202
|
+
|
|
203
|
+
Combines multiple PSBTs into this PsbtV2. This implements the Combiner role as defined in [BIP 174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#user-content-Roles).
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
|
|
207
|
+
- `psbts` - An array of `PsbtV2` instances to combine into this PSBT.
|
|
208
|
+
|
|
209
|
+
Before combining, this method validates that:
|
|
210
|
+
1. This PsbtV2 is ready for the Combiner role (`isReadyForCombiner` returns `true`).
|
|
211
|
+
2. Each PSBT in the provided array is also ready for the Combiner role.
|
|
212
|
+
3. All PSBTs represent the same unsigned transaction. This is determined by comparing transaction IDs after setting all input sequence numbers to 0 (per [BIP 370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#unique-identification) unique identification rules).
|
|
213
|
+
|
|
214
|
+
When combining, later PSBTs in the array take precedence over earlier ones. The combination merges all global, input, and output map key-value pairs. This operation is atomic—if any error occurs during combination, the original state is preserved.
|
|
215
|
+
|
|
216
|
+
**Warning:** This method could potentially produce a PSBT in a bad state. For example, if a later PSBT has an input sequence without a signature, it could potentially invalidate signatures existing on this or earlier PSBTs in the list if the sequence numbers do not agree.
|
|
217
|
+
|
|
200
218
|
##### `static PsbtV2.FromV0`
|
|
201
219
|
|
|
202
220
|
Attempts to return a `PsbtV2` by converting from a PSBTv0 string or Buffer
|
package/dist/index.d.ts
CHANGED
|
@@ -69,6 +69,21 @@ declare abstract class PsbtV2Maps {
|
|
|
69
69
|
copy(to: PsbtV2Maps): void;
|
|
70
70
|
private copyMaps;
|
|
71
71
|
private copyMap;
|
|
72
|
+
/**
|
|
73
|
+
* For a given map, set the value to the given key if the value is not empty.
|
|
74
|
+
* This overrides the value at that key as long as the value will not be
|
|
75
|
+
* empty.
|
|
76
|
+
*/
|
|
77
|
+
private combineValue;
|
|
78
|
+
private validateCombineMaps;
|
|
79
|
+
/**
|
|
80
|
+
* Combines the maps in the provided PsbtV2Maps object into this one. Using
|
|
81
|
+
* this without validation may produce a PSBT in an invalid state.
|
|
82
|
+
*
|
|
83
|
+
* This operation is atomic - if any error occurs, the original state is
|
|
84
|
+
* preserved.
|
|
85
|
+
*/
|
|
86
|
+
protected combineMaps(from: PsbtV2Maps): void;
|
|
72
87
|
}
|
|
73
88
|
|
|
74
89
|
/**
|
|
@@ -408,6 +423,23 @@ declare class PsbtV2 extends PsbtV2Maps {
|
|
|
408
423
|
* Updates the PSBT_GLOBAL_OUTPUT_COUNT field in the global map.
|
|
409
424
|
*/
|
|
410
425
|
private updateGlobalOutputCount;
|
|
426
|
+
/**
|
|
427
|
+
* Validates this PsbtV2 and the one it's being combined with are ready for
|
|
428
|
+
* the Combiner role. Also, the calculated unsigned transaction IDs of the two
|
|
429
|
+
* must be the same.
|
|
430
|
+
*/
|
|
431
|
+
private validateCombine;
|
|
432
|
+
/**
|
|
433
|
+
* Combines multiple PsbtV2 objects into this one with the latest index taking
|
|
434
|
+
* precedence over earlier indices of the provided list. This action is
|
|
435
|
+
* atomic.
|
|
436
|
+
*
|
|
437
|
+
* WARNING: This method could potentially produce a PSBT in an bad state. For
|
|
438
|
+
* example, if a later PSBT has an input sequence without a signature, it
|
|
439
|
+
* could potentially invalidate signatures existing on this or earlier PSBTs
|
|
440
|
+
* in the list if the sequence numbers do not agree.
|
|
441
|
+
*/
|
|
442
|
+
combine(psbts: PsbtV2[]): void;
|
|
411
443
|
}
|
|
412
444
|
|
|
413
445
|
interface PsbtInput {
|
package/dist/index.js
CHANGED
|
@@ -295,6 +295,63 @@ var PsbtV2Maps = class {
|
|
|
295
295
|
copyMap(from, to) {
|
|
296
296
|
from.forEach((v, k) => to.set(k, Buffer.from(v)));
|
|
297
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* For a given map, set the value to the given key if the value is not empty.
|
|
300
|
+
* This overrides the value at that key as long as the value will not be
|
|
301
|
+
* empty.
|
|
302
|
+
*/
|
|
303
|
+
combineValue(map, value, toKey) {
|
|
304
|
+
if (value && value.length > 0) {
|
|
305
|
+
map.set(toKey, value);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
validateCombineMaps(from) {
|
|
309
|
+
if (from.inputMaps.length !== this.inputMaps.length) {
|
|
310
|
+
throw new Error(
|
|
311
|
+
`Cannot combine PSBTs with different input counts: this has ${this.inputMaps.length}, other has ${from.inputMaps.length}`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
if (from.outputMaps.length !== this.outputMaps.length) {
|
|
315
|
+
throw new Error(
|
|
316
|
+
`Cannot combine PSBTs with different output counts: this has ${this.outputMaps.length}, other has ${from.outputMaps.length}`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Combines the maps in the provided PsbtV2Maps object into this one. Using
|
|
322
|
+
* this without validation may produce a PSBT in an invalid state.
|
|
323
|
+
*
|
|
324
|
+
* This operation is atomic - if any error occurs, the original state is
|
|
325
|
+
* preserved.
|
|
326
|
+
*/
|
|
327
|
+
combineMaps(from) {
|
|
328
|
+
this.validateCombineMaps(from);
|
|
329
|
+
const newGlobalMap = new Map(this.globalMap);
|
|
330
|
+
const newInputMaps = this.inputMaps.map((m) => new Map(m));
|
|
331
|
+
const newOutputMaps = this.outputMaps.map((m) => new Map(m));
|
|
332
|
+
for (const [key, value] of from.globalMap) {
|
|
333
|
+
this.combineValue(newGlobalMap, value, key);
|
|
334
|
+
}
|
|
335
|
+
for (let i = 0; i < from.inputMaps.length; i++) {
|
|
336
|
+
if (!newInputMaps[i]) {
|
|
337
|
+
newInputMaps[i] = /* @__PURE__ */ new Map();
|
|
338
|
+
}
|
|
339
|
+
for (const [key, value] of from.inputMaps[i]) {
|
|
340
|
+
this.combineValue(newInputMaps[i], value, key);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
for (let i = 0; i < from.outputMaps.length; i++) {
|
|
344
|
+
if (!newOutputMaps[i]) {
|
|
345
|
+
newOutputMaps[i] = /* @__PURE__ */ new Map();
|
|
346
|
+
}
|
|
347
|
+
for (const [key, value] of from.outputMaps[i]) {
|
|
348
|
+
this.combineValue(newOutputMaps[i], value, key);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
this.globalMap = newGlobalMap;
|
|
352
|
+
this.inputMaps = newInputMaps;
|
|
353
|
+
this.outputMaps = newOutputMaps;
|
|
354
|
+
}
|
|
298
355
|
};
|
|
299
356
|
var PsbtConversionMaps = class extends PsbtV2Maps {
|
|
300
357
|
/**
|
|
@@ -375,6 +432,29 @@ var PsbtConversionMaps = class extends PsbtV2Maps {
|
|
|
375
432
|
this.v0delete(outputMap, "04" /* PSBT_OUT_SCRIPT */);
|
|
376
433
|
}
|
|
377
434
|
};
|
|
435
|
+
getTransactionId() {
|
|
436
|
+
const txBuf = this.buildUnsignedTx();
|
|
437
|
+
try {
|
|
438
|
+
return import_bitcoinjs_lib_v6.Transaction.fromBuffer(txBuf).getId();
|
|
439
|
+
} catch (e) {
|
|
440
|
+
const numInputs = this.inputMaps.length;
|
|
441
|
+
const numOutputs = this.outputMaps.length;
|
|
442
|
+
if (numInputs === 0 && numOutputs === 1) {
|
|
443
|
+
console.error(
|
|
444
|
+
`Cannot compute transaction ID for PSBT with 0 inputs and 1 output. The serialized transaction (${txBuf.toString("hex")}) is ambiguous with SegWit format and cannot be parsed.`
|
|
445
|
+
);
|
|
446
|
+
throw new Error(
|
|
447
|
+
"Cannot compute transaction ID: PSBT has 0 inputs and 1 output, which produces an ambiguous transaction format. "
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
console.error(
|
|
451
|
+
`Failed to parse transaction buffer: ${txBuf.toString("hex")}`
|
|
452
|
+
);
|
|
453
|
+
throw new Error(
|
|
454
|
+
`Failed to compute transaction ID (${numInputs} inputs, ${numOutputs} outputs): ${e instanceof Error ? e.message : e}`
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
378
458
|
};
|
|
379
459
|
|
|
380
460
|
// src/psbtv2/psbtv2.ts
|
|
@@ -961,9 +1041,9 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
|
|
|
961
1041
|
* https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki
|
|
962
1042
|
*/
|
|
963
1043
|
setInputSequence(inputIndex, sequence) {
|
|
964
|
-
if (!this.isReadyForUpdater) {
|
|
1044
|
+
if (!this.isReadyForUpdater && !this.isReadyForCombiner) {
|
|
965
1045
|
throw new Error(
|
|
966
|
-
"PSBT is not ready for the Updater role. Sequence cannot be changed."
|
|
1046
|
+
"PSBT is not ready for the Updater or Combiner role. Sequence cannot be changed."
|
|
967
1047
|
);
|
|
968
1048
|
}
|
|
969
1049
|
if (inputIndex < 0 || inputIndex >= this.PSBT_GLOBAL_INPUT_COUNT) {
|
|
@@ -1454,6 +1534,67 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
|
|
|
1454
1534
|
bw.writeU8(this.outputMaps.length);
|
|
1455
1535
|
this.globalMap.set("05" /* PSBT_GLOBAL_OUTPUT_COUNT */, bw.render());
|
|
1456
1536
|
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Validates this PsbtV2 and the one it's being combined with are ready for
|
|
1539
|
+
* the Combiner role. Also, the calculated unsigned transaction IDs of the two
|
|
1540
|
+
* must be the same.
|
|
1541
|
+
*/
|
|
1542
|
+
validateCombine(psbt) {
|
|
1543
|
+
if (!this.isReadyForCombiner) {
|
|
1544
|
+
throw Error("This PsbtV2 is not ready for Combiner role.");
|
|
1545
|
+
}
|
|
1546
|
+
if (!psbt.isReadyForCombiner) {
|
|
1547
|
+
throw Error("Provided PsbtV2 is not ready for Combiner role.");
|
|
1548
|
+
}
|
|
1549
|
+
const tempThis = new _PsbtV2(this.serialize(), true);
|
|
1550
|
+
const tempOther = new _PsbtV2(psbt.serialize(), true);
|
|
1551
|
+
for (const [index, sequence] of tempThis.PSBT_IN_SEQUENCE.entries()) {
|
|
1552
|
+
if (sequence !== 0) {
|
|
1553
|
+
tempThis.setInputSequence(index, 0);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
for (const [index, sequence] of tempOther.PSBT_IN_SEQUENCE.entries()) {
|
|
1557
|
+
if (sequence !== 0) {
|
|
1558
|
+
tempOther.setInputSequence(index, 0);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
const checkThis = new PsbtConversionMaps(tempThis.serialize());
|
|
1562
|
+
const checkOther = new PsbtConversionMaps(tempOther.serialize());
|
|
1563
|
+
if (checkThis.getTransactionId() !== checkOther.getTransactionId()) {
|
|
1564
|
+
throw Error("Cannot combine PSBTs for different unsigned transactions.");
|
|
1565
|
+
}
|
|
1566
|
+
for (const [index, sequence] of this.PSBT_IN_SEQUENCE.entries()) {
|
|
1567
|
+
const otherSequence = psbt.PSBT_IN_SEQUENCE[index];
|
|
1568
|
+
if (otherSequence !== sequence && this.PSBT_IN_PARTIAL_SIG[index].some((sig) => sig !== null)) {
|
|
1569
|
+
console.warn(
|
|
1570
|
+
`Combined PSBT updated sequence on signed input ${index}. This may invalidate the existing signature.`
|
|
1571
|
+
);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Combines multiple PsbtV2 objects into this one with the latest index taking
|
|
1577
|
+
* precedence over earlier indices of the provided list. This action is
|
|
1578
|
+
* atomic.
|
|
1579
|
+
*
|
|
1580
|
+
* WARNING: This method could potentially produce a PSBT in an bad state. For
|
|
1581
|
+
* example, if a later PSBT has an input sequence without a signature, it
|
|
1582
|
+
* could potentially invalidate signatures existing on this or earlier PSBTs
|
|
1583
|
+
* in the list if the sequence numbers do not agree.
|
|
1584
|
+
*/
|
|
1585
|
+
combine(psbts) {
|
|
1586
|
+
for (const psbt of psbts) {
|
|
1587
|
+
this.validateCombine(psbt);
|
|
1588
|
+
}
|
|
1589
|
+
try {
|
|
1590
|
+
for (const psbt of psbts) {
|
|
1591
|
+
this.combineMaps(psbt);
|
|
1592
|
+
}
|
|
1593
|
+
} catch (error) {
|
|
1594
|
+
console.error(error);
|
|
1595
|
+
throw Error("Failed to combine PSBTs.");
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1457
1598
|
};
|
|
1458
1599
|
|
|
1459
1600
|
// src/psbtv0/psbt.ts
|
package/dist/index.mjs
CHANGED
|
@@ -248,6 +248,63 @@ var PsbtV2Maps = class {
|
|
|
248
248
|
copyMap(from, to) {
|
|
249
249
|
from.forEach((v, k) => to.set(k, Buffer.from(v)));
|
|
250
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* For a given map, set the value to the given key if the value is not empty.
|
|
253
|
+
* This overrides the value at that key as long as the value will not be
|
|
254
|
+
* empty.
|
|
255
|
+
*/
|
|
256
|
+
combineValue(map, value, toKey) {
|
|
257
|
+
if (value && value.length > 0) {
|
|
258
|
+
map.set(toKey, value);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
validateCombineMaps(from) {
|
|
262
|
+
if (from.inputMaps.length !== this.inputMaps.length) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
`Cannot combine PSBTs with different input counts: this has ${this.inputMaps.length}, other has ${from.inputMaps.length}`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
if (from.outputMaps.length !== this.outputMaps.length) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`Cannot combine PSBTs with different output counts: this has ${this.outputMaps.length}, other has ${from.outputMaps.length}`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Combines the maps in the provided PsbtV2Maps object into this one. Using
|
|
275
|
+
* this without validation may produce a PSBT in an invalid state.
|
|
276
|
+
*
|
|
277
|
+
* This operation is atomic - if any error occurs, the original state is
|
|
278
|
+
* preserved.
|
|
279
|
+
*/
|
|
280
|
+
combineMaps(from) {
|
|
281
|
+
this.validateCombineMaps(from);
|
|
282
|
+
const newGlobalMap = new Map(this.globalMap);
|
|
283
|
+
const newInputMaps = this.inputMaps.map((m) => new Map(m));
|
|
284
|
+
const newOutputMaps = this.outputMaps.map((m) => new Map(m));
|
|
285
|
+
for (const [key, value] of from.globalMap) {
|
|
286
|
+
this.combineValue(newGlobalMap, value, key);
|
|
287
|
+
}
|
|
288
|
+
for (let i = 0; i < from.inputMaps.length; i++) {
|
|
289
|
+
if (!newInputMaps[i]) {
|
|
290
|
+
newInputMaps[i] = /* @__PURE__ */ new Map();
|
|
291
|
+
}
|
|
292
|
+
for (const [key, value] of from.inputMaps[i]) {
|
|
293
|
+
this.combineValue(newInputMaps[i], value, key);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
for (let i = 0; i < from.outputMaps.length; i++) {
|
|
297
|
+
if (!newOutputMaps[i]) {
|
|
298
|
+
newOutputMaps[i] = /* @__PURE__ */ new Map();
|
|
299
|
+
}
|
|
300
|
+
for (const [key, value] of from.outputMaps[i]) {
|
|
301
|
+
this.combineValue(newOutputMaps[i], value, key);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
this.globalMap = newGlobalMap;
|
|
305
|
+
this.inputMaps = newInputMaps;
|
|
306
|
+
this.outputMaps = newOutputMaps;
|
|
307
|
+
}
|
|
251
308
|
};
|
|
252
309
|
var PsbtConversionMaps = class extends PsbtV2Maps {
|
|
253
310
|
/**
|
|
@@ -328,6 +385,29 @@ var PsbtConversionMaps = class extends PsbtV2Maps {
|
|
|
328
385
|
this.v0delete(outputMap, "04" /* PSBT_OUT_SCRIPT */);
|
|
329
386
|
}
|
|
330
387
|
};
|
|
388
|
+
getTransactionId() {
|
|
389
|
+
const txBuf = this.buildUnsignedTx();
|
|
390
|
+
try {
|
|
391
|
+
return Transaction.fromBuffer(txBuf).getId();
|
|
392
|
+
} catch (e) {
|
|
393
|
+
const numInputs = this.inputMaps.length;
|
|
394
|
+
const numOutputs = this.outputMaps.length;
|
|
395
|
+
if (numInputs === 0 && numOutputs === 1) {
|
|
396
|
+
console.error(
|
|
397
|
+
`Cannot compute transaction ID for PSBT with 0 inputs and 1 output. The serialized transaction (${txBuf.toString("hex")}) is ambiguous with SegWit format and cannot be parsed.`
|
|
398
|
+
);
|
|
399
|
+
throw new Error(
|
|
400
|
+
"Cannot compute transaction ID: PSBT has 0 inputs and 1 output, which produces an ambiguous transaction format. "
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
console.error(
|
|
404
|
+
`Failed to parse transaction buffer: ${txBuf.toString("hex")}`
|
|
405
|
+
);
|
|
406
|
+
throw new Error(
|
|
407
|
+
`Failed to compute transaction ID (${numInputs} inputs, ${numOutputs} outputs): ${e instanceof Error ? e.message : e}`
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
331
411
|
};
|
|
332
412
|
|
|
333
413
|
// src/psbtv2/psbtv2.ts
|
|
@@ -914,9 +994,9 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
|
|
|
914
994
|
* https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki
|
|
915
995
|
*/
|
|
916
996
|
setInputSequence(inputIndex, sequence) {
|
|
917
|
-
if (!this.isReadyForUpdater) {
|
|
997
|
+
if (!this.isReadyForUpdater && !this.isReadyForCombiner) {
|
|
918
998
|
throw new Error(
|
|
919
|
-
"PSBT is not ready for the Updater role. Sequence cannot be changed."
|
|
999
|
+
"PSBT is not ready for the Updater or Combiner role. Sequence cannot be changed."
|
|
920
1000
|
);
|
|
921
1001
|
}
|
|
922
1002
|
if (inputIndex < 0 || inputIndex >= this.PSBT_GLOBAL_INPUT_COUNT) {
|
|
@@ -1407,6 +1487,67 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
|
|
|
1407
1487
|
bw.writeU8(this.outputMaps.length);
|
|
1408
1488
|
this.globalMap.set("05" /* PSBT_GLOBAL_OUTPUT_COUNT */, bw.render());
|
|
1409
1489
|
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Validates this PsbtV2 and the one it's being combined with are ready for
|
|
1492
|
+
* the Combiner role. Also, the calculated unsigned transaction IDs of the two
|
|
1493
|
+
* must be the same.
|
|
1494
|
+
*/
|
|
1495
|
+
validateCombine(psbt) {
|
|
1496
|
+
if (!this.isReadyForCombiner) {
|
|
1497
|
+
throw Error("This PsbtV2 is not ready for Combiner role.");
|
|
1498
|
+
}
|
|
1499
|
+
if (!psbt.isReadyForCombiner) {
|
|
1500
|
+
throw Error("Provided PsbtV2 is not ready for Combiner role.");
|
|
1501
|
+
}
|
|
1502
|
+
const tempThis = new _PsbtV2(this.serialize(), true);
|
|
1503
|
+
const tempOther = new _PsbtV2(psbt.serialize(), true);
|
|
1504
|
+
for (const [index, sequence] of tempThis.PSBT_IN_SEQUENCE.entries()) {
|
|
1505
|
+
if (sequence !== 0) {
|
|
1506
|
+
tempThis.setInputSequence(index, 0);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
for (const [index, sequence] of tempOther.PSBT_IN_SEQUENCE.entries()) {
|
|
1510
|
+
if (sequence !== 0) {
|
|
1511
|
+
tempOther.setInputSequence(index, 0);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
const checkThis = new PsbtConversionMaps(tempThis.serialize());
|
|
1515
|
+
const checkOther = new PsbtConversionMaps(tempOther.serialize());
|
|
1516
|
+
if (checkThis.getTransactionId() !== checkOther.getTransactionId()) {
|
|
1517
|
+
throw Error("Cannot combine PSBTs for different unsigned transactions.");
|
|
1518
|
+
}
|
|
1519
|
+
for (const [index, sequence] of this.PSBT_IN_SEQUENCE.entries()) {
|
|
1520
|
+
const otherSequence = psbt.PSBT_IN_SEQUENCE[index];
|
|
1521
|
+
if (otherSequence !== sequence && this.PSBT_IN_PARTIAL_SIG[index].some((sig) => sig !== null)) {
|
|
1522
|
+
console.warn(
|
|
1523
|
+
`Combined PSBT updated sequence on signed input ${index}. This may invalidate the existing signature.`
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Combines multiple PsbtV2 objects into this one with the latest index taking
|
|
1530
|
+
* precedence over earlier indices of the provided list. This action is
|
|
1531
|
+
* atomic.
|
|
1532
|
+
*
|
|
1533
|
+
* WARNING: This method could potentially produce a PSBT in an bad state. For
|
|
1534
|
+
* example, if a later PSBT has an input sequence without a signature, it
|
|
1535
|
+
* could potentially invalidate signatures existing on this or earlier PSBTs
|
|
1536
|
+
* in the list if the sequence numbers do not agree.
|
|
1537
|
+
*/
|
|
1538
|
+
combine(psbts) {
|
|
1539
|
+
for (const psbt of psbts) {
|
|
1540
|
+
this.validateCombine(psbt);
|
|
1541
|
+
}
|
|
1542
|
+
try {
|
|
1543
|
+
for (const psbt of psbts) {
|
|
1544
|
+
this.combineMaps(psbt);
|
|
1545
|
+
}
|
|
1546
|
+
} catch (error) {
|
|
1547
|
+
console.error(error);
|
|
1548
|
+
throw Error("Failed to combine PSBTs.");
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1410
1551
|
};
|
|
1411
1552
|
|
|
1412
1553
|
// src/psbtv0/psbt.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caravan/psbt",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "typescript library for working with PSBTs",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@caravan/bip32": "*",
|
|
46
46
|
"@caravan/bitcoin": "*",
|
|
47
|
+
"@caravan/multisig": "*",
|
|
47
48
|
"bignumber.js": "^8.1.1",
|
|
48
49
|
"bip174": "^2.1.1",
|
|
49
50
|
"bitcoinjs-lib-v6": "npm:bitcoinjs-lib@^6.1.5",
|
|
@@ -57,7 +58,6 @@
|
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@caravan/build-plugins": "*",
|
|
59
60
|
"@caravan/eslint-config": "*",
|
|
60
|
-
"@caravan/multisig": "*",
|
|
61
61
|
"@caravan/typescript-config": "*",
|
|
62
62
|
"eslint-plugin-prettier": "^5.1.3",
|
|
63
63
|
"lodash": "^4.17.21",
|
|
@@ -66,6 +66,11 @@
|
|
|
66
66
|
"typescript": "^5.3.3"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
|
-
"@caravan/bitcoin": "0.4.
|
|
69
|
+
"@caravan/bitcoin": "0.4.5"
|
|
70
|
+
},
|
|
71
|
+
"repository": {
|
|
72
|
+
"type": "git",
|
|
73
|
+
"url": "https://github.com/caravan-bitcoin/caravan",
|
|
74
|
+
"directory": "packages/caravan-psbt"
|
|
70
75
|
}
|
|
71
76
|
}
|