@caravan/psbt 1.5.0 → 1.7.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/dist/index.d.ts CHANGED
@@ -28,6 +28,12 @@ declare enum PsbtGlobalTxModifiableBits {
28
28
  OUTPUTS = "OUTPUTS",// 0b00000010
29
29
  SIGHASH_SINGLE = "SIGHASH_SINGLE"
30
30
  }
31
+ declare enum SighashType {
32
+ SIGHASH_ALL = 1,
33
+ SIGHASH_NONE = 2,
34
+ SIGHASH_SINGLE = 3,
35
+ SIGHASH_ANYONECANPAY = 128
36
+ }
31
37
  type InputOutputIndexType = number;
32
38
  type MapSelectorType = "global" | ["inputs", InputOutputIndexType] | ["outputs", InputOutputIndexType];
33
39
 
@@ -52,7 +58,7 @@ declare abstract class PsbtV2Maps {
52
58
  */
53
59
  serialize(format?: "base64" | "hex"): string;
54
60
  /**
55
- * Copies the maps in this PsbtV2 object to another PsbtV2 object.
61
+ * Copies the maps in this PsbtV2Maps object to another PsbtV2Maps object.
56
62
  *
57
63
  * NOTE: This copy method is made available to achieve parity with the PSBT
58
64
  * api required by `ledger-bitcoin` for creating merklized PSBTs. HOWEVER, it
@@ -60,7 +66,7 @@ declare abstract class PsbtV2Maps {
60
66
  * validation defined in the constructor, so it could create a psbtv2 in an
61
67
  * invalid psbt state. PsbtV2.serialize is preferable whenever possible.
62
68
  */
63
- copy(to: PsbtV2): void;
69
+ copy(to: PsbtV2Maps): void;
64
70
  private copyMaps;
65
71
  private copyMap;
66
72
  }
@@ -281,7 +287,7 @@ declare class PsbtV2 extends PsbtV2Maps {
281
287
  */
282
288
  dangerouslySetGlobalTxVersion1(): void;
283
289
  addGlobalXpub(xpub: Buffer, fingerprint: Buffer, path: string): void;
284
- addInput({ previousTxId, outputIndex, sequence, nonWitnessUtxo, witnessUtxo, redeemScript, witnessScript, bip32Derivation, }: {
290
+ addInput({ previousTxId, outputIndex, sequence, nonWitnessUtxo, witnessUtxo, redeemScript, witnessScript, bip32Derivation, sighashType, }: {
285
291
  previousTxId: Buffer | string;
286
292
  outputIndex: number;
287
293
  sequence?: number;
@@ -297,6 +303,7 @@ declare class PsbtV2 extends PsbtV2Maps {
297
303
  masterFingerprint: Buffer;
298
304
  path: string;
299
305
  }[];
306
+ sighashType?: SighashType;
300
307
  }): void;
301
308
  addOutput({ amount, script, redeemScript, witnessScript, bip32Derivation, }: {
302
309
  amount: number;
@@ -378,10 +385,16 @@ declare class PsbtV2 extends PsbtV2Maps {
378
385
  * Attempts to return a PsbtV2 by converting from a PsbtV0 string or Buffer.
379
386
  *
380
387
  * This method first starts with a fresh PsbtV2 having just been created. It
381
- * then takes the PsbtV2 through its operator saga through the Signer role. In
382
- * this sense validation for each operator role will be performed.
388
+ * then takes the PsbtV2 through its operator saga and through the Input
389
+ * Finalizer role. In this sense, validation for each operator role will be
390
+ * performed as the Psbt saga is replayed.
383
391
  */
384
392
  static FromV0(psbt: string | Buffer, allowTxnVersion1?: boolean): PsbtV2;
393
+ /**
394
+ * Outputs a serialized PSBTv0 from a best-attempt conversion of the fields in
395
+ * this PSBTv2. Accepts optional desired format as a string (default base64).
396
+ */
397
+ toV0(format?: "base64" | "hex"): string;
385
398
  }
386
399
 
387
400
  interface PsbtInput {
package/dist/index.js CHANGED
@@ -57,6 +57,54 @@ var PSBT_MAGIC_HEX = "70736274ff";
57
57
  var PSBT_MAGIC_B64 = "cHNidP8";
58
58
  var PSBT_MAGIC_BYTES = import_buffer.Buffer.from([112, 115, 98, 116, 255]);
59
59
 
60
+ // src/psbtv2/types.ts
61
+ var KeyType = /* @__PURE__ */ ((KeyType2) => {
62
+ KeyType2["PSBT_GLOBAL_XPUB"] = "01";
63
+ KeyType2["PSBT_GLOBAL_TX_VERSION"] = "02";
64
+ KeyType2["PSBT_GLOBAL_FALLBACK_LOCKTIME"] = "03";
65
+ KeyType2["PSBT_GLOBAL_INPUT_COUNT"] = "04";
66
+ KeyType2["PSBT_GLOBAL_OUTPUT_COUNT"] = "05";
67
+ KeyType2["PSBT_GLOBAL_TX_MODIFIABLE"] = "06";
68
+ KeyType2["PSBT_GLOBAL_VERSION"] = "fb";
69
+ KeyType2["PSBT_GLOBAL_PROPRIETARY"] = "fc";
70
+ KeyType2["PSBT_IN_NON_WITNESS_UTXO"] = "00";
71
+ KeyType2["PSBT_IN_WITNESS_UTXO"] = "01";
72
+ KeyType2["PSBT_IN_PARTIAL_SIG"] = "02";
73
+ KeyType2["PSBT_IN_SIGHASH_TYPE"] = "03";
74
+ KeyType2["PSBT_IN_REDEEM_SCRIPT"] = "04";
75
+ KeyType2["PSBT_IN_WITNESS_SCRIPT"] = "05";
76
+ KeyType2["PSBT_IN_BIP32_DERIVATION"] = "06";
77
+ KeyType2["PSBT_IN_FINAL_SCRIPTSIG"] = "07";
78
+ KeyType2["PSBT_IN_FINAL_SCRIPTWITNESS"] = "08";
79
+ KeyType2["PSBT_IN_POR_COMMITMENT"] = "09";
80
+ KeyType2["PSBT_IN_RIPEMD160"] = "0a";
81
+ KeyType2["PSBT_IN_SHA256"] = "0b";
82
+ KeyType2["PSBT_IN_HASH160"] = "0c";
83
+ KeyType2["PSBT_IN_HASH256"] = "0d";
84
+ KeyType2["PSBT_IN_PREVIOUS_TXID"] = "0e";
85
+ KeyType2["PSBT_IN_OUTPUT_INDEX"] = "0f";
86
+ KeyType2["PSBT_IN_SEQUENCE"] = "10";
87
+ KeyType2["PSBT_IN_REQUIRED_TIME_LOCKTIME"] = "11";
88
+ KeyType2["PSBT_IN_REQUIRED_HEIGHT_LOCKTIME"] = "12";
89
+ KeyType2["PSBT_IN_TAP_KEY_SIG"] = "13";
90
+ KeyType2["PSBT_IN_TAP_SCRIPT_SIG"] = "14";
91
+ KeyType2["PSBT_IN_TAP_LEAF_SCRIPT"] = "15";
92
+ KeyType2["PSBT_IN_TAP_BIP32_DERIVATION"] = "16";
93
+ KeyType2["PSBT_IN_TAP_INTERNAL_KEY"] = "17";
94
+ KeyType2["PSBT_IN_TAP_MERKLE_ROOT"] = "18";
95
+ KeyType2["PSBT_IN_PROPRIETARY"] = "fc";
96
+ KeyType2["PSBT_OUT_REDEEM_SCRIPT"] = "00";
97
+ KeyType2["PSBT_OUT_WITNESS_SCRIPT"] = "01";
98
+ KeyType2["PSBT_OUT_BIP32_DERIVATION"] = "02";
99
+ KeyType2["PSBT_OUT_AMOUNT"] = "03";
100
+ KeyType2["PSBT_OUT_SCRIPT"] = "04";
101
+ KeyType2["PSBT_OUT_TAP_INTERNAL_KEY"] = "05";
102
+ KeyType2["PSBT_OUT_TAP_TREE"] = "06";
103
+ KeyType2["PSBT_OUT_TAP_BIP32_DERIVATION"] = "07";
104
+ KeyType2["PSBT_OUT_PROPRIETARY"] = "fc";
105
+ return KeyType2;
106
+ })(KeyType || {});
107
+
60
108
  // src/psbtv2/values.ts
61
109
  var PSBT_MAP_SEPARATOR = import_buffer.Buffer.from([0]);
62
110
  var BIP_32_NODE_REGEX = /(\/[0-9]+'?)/gi;
@@ -152,10 +200,11 @@ function getPsbtVersionNumber(psbt) {
152
200
 
153
201
  // src/psbtv2/psbtv2.ts
154
202
  var import_bufio3 = require("bufio");
155
- var import_bitcoinjs_lib_v6 = require("bitcoinjs-lib-v6");
203
+ var import_bitcoinjs_lib_v62 = require("bitcoinjs-lib-v6");
156
204
 
157
205
  // src/psbtv2/psbtv2maps.ts
158
206
  var import_bufio2 = require("bufio");
207
+ var import_bitcoinjs_lib_v6 = require("bitcoinjs-lib-v6");
159
208
  var PsbtV2Maps = class {
160
209
  // These maps directly correspond to the maps defined in BIP0174 and extended
161
210
  // in BIP0370
@@ -207,7 +256,7 @@ var PsbtV2Maps = class {
207
256
  return bw.render().toString(format2);
208
257
  }
209
258
  /**
210
- * Copies the maps in this PsbtV2 object to another PsbtV2 object.
259
+ * Copies the maps in this PsbtV2Maps object to another PsbtV2Maps object.
211
260
  *
212
261
  * NOTE: This copy method is made available to achieve parity with the PSBT
213
262
  * api required by `ledger-bitcoin` for creating merklized PSBTs. HOWEVER, it
@@ -231,6 +280,86 @@ var PsbtV2Maps = class {
231
280
  from.forEach((v5, k4) => to.set(k4, import_buffer.Buffer.from(v5)));
232
281
  }
233
282
  };
283
+ var PsbtConversionMaps = class extends PsbtV2Maps {
284
+ /**
285
+ * Builds the unsigned transaction that is required on global map key 0x00 for
286
+ * a PSBTv0. Relies on bitcoinjs-lib to construct the transaction.
287
+ */
288
+ buildUnsignedTx() {
289
+ const tx = new import_bitcoinjs_lib_v6.Transaction();
290
+ tx.version = this.globalMap.get("02" /* PSBT_GLOBAL_TX_VERSION */)?.readUInt8() || 0;
291
+ tx.locktime = this.globalMap.get("03" /* PSBT_GLOBAL_FALLBACK_LOCKTIME */)?.readUInt32LE() || 0;
292
+ const numInputs = this.globalMap.get("04" /* PSBT_GLOBAL_INPUT_COUNT */)?.readUInt8() || 0;
293
+ const numOutputs = this.globalMap.get("05" /* PSBT_GLOBAL_OUTPUT_COUNT */)?.readUInt8() || 0;
294
+ for (let i6 = 0; i6 < numInputs; i6++) {
295
+ if (!this.inputMaps[i6].has("0e" /* PSBT_IN_PREVIOUS_TXID */)) {
296
+ console.warn(`Input ${i6} is missing previous txid. Skipping.`);
297
+ continue;
298
+ }
299
+ tx.addInput(
300
+ this.inputMaps[i6].get("0e" /* PSBT_IN_PREVIOUS_TXID */),
301
+ this.inputMaps[i6].get("0f" /* PSBT_IN_OUTPUT_INDEX */)?.readUint32LE() || 0,
302
+ this.inputMaps[i6].get("10" /* PSBT_IN_SEQUENCE */)?.readUint32LE()
303
+ );
304
+ }
305
+ for (let i6 = 0; i6 < numOutputs; i6++) {
306
+ if (!this.outputMaps[i6].has("04" /* PSBT_OUT_SCRIPT */) || !this.outputMaps[i6].has("03" /* PSBT_OUT_AMOUNT */)) {
307
+ console.warn(
308
+ `Output ${i6} is missing previous out script or amount. Skipping.`
309
+ );
310
+ continue;
311
+ }
312
+ const bigintAmount = this.outputMaps[i6].get("03" /* PSBT_OUT_AMOUNT */).readBigInt64LE();
313
+ const numberAmount = parseInt(bigintAmount.toString());
314
+ tx.addOutput(
315
+ this.outputMaps[i6].get("04" /* PSBT_OUT_SCRIPT */),
316
+ numberAmount
317
+ );
318
+ }
319
+ return tx.toBuffer();
320
+ }
321
+ /**
322
+ * Warns and then deletes map values. Intended for use when converting to a
323
+ * PsbtV0.
324
+ */
325
+ v0delete(map, key) {
326
+ if (map.has(key)) {
327
+ const keyName = Object.keys(KeyType)[Object.values(KeyType).indexOf(key)];
328
+ console.warn(
329
+ `Key ${keyName} key is not supported on PSBTv0 and will be omitted.`
330
+ );
331
+ }
332
+ map.delete(key);
333
+ }
334
+ /**
335
+ * Constructs an unsigned txn to be added to the PSBT_GLOBAL_UNSIGNED_TX key
336
+ * which is required on PsbtV0. Then removes all the fields which a PsbtV0 is
337
+ * incompatible with.
338
+ */
339
+ convertToV0 = () => {
340
+ this.globalMap.set(
341
+ "00" /* PSBT_GLOBAL_UNSIGNED_TX */,
342
+ this.buildUnsignedTx()
343
+ );
344
+ this.v0delete(this.globalMap, "02" /* PSBT_GLOBAL_TX_VERSION */);
345
+ this.v0delete(this.globalMap, "03" /* PSBT_GLOBAL_FALLBACK_LOCKTIME */);
346
+ this.v0delete(this.globalMap, "04" /* PSBT_GLOBAL_INPUT_COUNT */);
347
+ this.v0delete(this.globalMap, "05" /* PSBT_GLOBAL_OUTPUT_COUNT */);
348
+ this.v0delete(this.globalMap, "06" /* PSBT_GLOBAL_TX_MODIFIABLE */);
349
+ this.v0delete(this.globalMap, "fb" /* PSBT_GLOBAL_VERSION */);
350
+ for (const inputMap of this.inputMaps) {
351
+ this.v0delete(inputMap, "0e" /* PSBT_IN_PREVIOUS_TXID */);
352
+ this.v0delete(inputMap, "0f" /* PSBT_IN_OUTPUT_INDEX */);
353
+ this.v0delete(inputMap, "10" /* PSBT_IN_SEQUENCE */);
354
+ this.v0delete(inputMap, "11" /* PSBT_IN_REQUIRED_TIME_LOCKTIME */);
355
+ this.v0delete(inputMap, "12" /* PSBT_IN_REQUIRED_HEIGHT_LOCKTIME */);
356
+ }
357
+ for (const outputMap of this.outputMaps) {
358
+ this.v0delete(outputMap, "03" /* PSBT_OUT_AMOUNT */);
359
+ this.v0delete(outputMap, "04" /* PSBT_OUT_SCRIPT */);
360
+ }
361
+ };
362
+ };
234
363
 
235
364
  // src/psbtv2/psbtv2.ts
236
365
  var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
@@ -907,7 +1036,8 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
907
1036
  witnessUtxo,
908
1037
  redeemScript,
909
1038
  witnessScript,
910
- bip32Derivation
1039
+ bip32Derivation,
1040
+ sighashType
911
1041
  }) {
912
1042
  if (!this.isReadyForConstructor) {
913
1043
  throw Error(
@@ -958,6 +1088,10 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
958
1088
  map.set(key, bw.render());
959
1089
  }
960
1090
  }
1091
+ if (sighashType !== void 0) {
1092
+ bw.writeU32(sighashType);
1093
+ map.set("03" /* PSBT_IN_SIGHASH_TYPE */, bw.render());
1094
+ }
961
1095
  this.PSBT_GLOBAL_INPUT_COUNT = this.inputMaps.push(map);
962
1096
  }
963
1097
  addOutput({
@@ -1209,12 +1343,13 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
1209
1343
  * Attempts to return a PsbtV2 by converting from a PsbtV0 string or Buffer.
1210
1344
  *
1211
1345
  * This method first starts with a fresh PsbtV2 having just been created. It
1212
- * then takes the PsbtV2 through its operator saga through the Signer role. In
1213
- * this sense validation for each operator role will be performed.
1346
+ * then takes the PsbtV2 through its operator saga and through the Input
1347
+ * Finalizer role. In this sense, validation for each operator role will be
1348
+ * performed as the Psbt saga is replayed.
1214
1349
  */
1215
1350
  static FromV0(psbt, allowTxnVersion1 = false) {
1216
1351
  const psbtv0Buf = bufferize(psbt);
1217
- const psbtv0 = import_bitcoinjs_lib_v6.Psbt.fromBuffer(psbtv0Buf);
1352
+ const psbtv0 = import_bitcoinjs_lib_v62.Psbt.fromBuffer(psbtv0Buf);
1218
1353
  const psbtv0GlobalMap = psbtv0.data.globalMap;
1219
1354
  const psbtv2 = new _PsbtV2();
1220
1355
  const txVersion = psbtv0.data.getTransaction().readInt32LE(0);
@@ -1223,6 +1358,7 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
1223
1358
  } else {
1224
1359
  psbtv2.PSBT_GLOBAL_TX_VERSION = psbtv0.data.getTransaction().readInt32LE(0);
1225
1360
  }
1361
+ psbtv2.PSBT_GLOBAL_FALLBACK_LOCKTIME = psbtv0.locktime;
1226
1362
  for (const globalXpub of psbtv0GlobalMap.globalXpub ?? []) {
1227
1363
  psbtv2.addGlobalXpub(
1228
1364
  globalXpub.extendedPubkey,
@@ -1247,7 +1383,8 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
1247
1383
  },
1248
1384
  redeemScript: input.redeemScript,
1249
1385
  witnessScript: input.witnessScript,
1250
- bip32Derivation: input.bip32Derivation
1386
+ bip32Derivation: input.bip32Derivation,
1387
+ sighashType: input.sighashType
1251
1388
  });
1252
1389
  }
1253
1390
  const txOutputs = [];
@@ -1269,13 +1406,37 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
1269
1406
  psbtv2.addPartialSig(index, sig.pubkey, sig.signature);
1270
1407
  }
1271
1408
  }
1409
+ for (const [index, input] of psbtv0.data.inputs.entries()) {
1410
+ if (input.finalScriptSig) {
1411
+ psbtv2.inputMaps[index].set(
1412
+ "07" /* PSBT_IN_FINAL_SCRIPTSIG */,
1413
+ input.finalScriptSig
1414
+ );
1415
+ }
1416
+ if (input.finalScriptWitness) {
1417
+ psbtv2.inputMaps[index].set(
1418
+ "08" /* PSBT_IN_FINAL_SCRIPTWITNESS */,
1419
+ input.finalScriptWitness
1420
+ );
1421
+ }
1422
+ }
1272
1423
  return psbtv2;
1273
1424
  }
1425
+ /**
1426
+ * Outputs a serialized PSBTv0 from a best-attempt conversion of the fields in
1427
+ * this PSBTv2. Accepts optional desired format as a string (default base64).
1428
+ */
1429
+ toV0(format2) {
1430
+ const converterMap = new PsbtConversionMaps();
1431
+ this.copy(converterMap);
1432
+ converterMap.convertToV0();
1433
+ return converterMap.serialize(format2);
1434
+ }
1274
1435
  };
1275
1436
 
1276
1437
  // src/psbtv0/psbt.ts
1277
1438
  var import_bitcoin4 = require("@caravan/bitcoin");
1278
- var import_bitcoinjs_lib_v63 = require("bitcoinjs-lib-v6");
1439
+ var import_bitcoinjs_lib_v64 = require("bitcoinjs-lib-v6");
1279
1440
  var import_address = require("bitcoinjs-lib-v6/src/address.js");
1280
1441
 
1281
1442
  // vendor/tiny-secp256k1-asmjs/lib/index.js
@@ -93806,7 +93967,7 @@ var braidDetailsToWalletConfig = (braidDetails) => {
93806
93967
 
93807
93968
  // src/psbtv0/utils.ts
93808
93969
  var import_bignumber = __toESM(require("bignumber.js"));
93809
- var import_bitcoinjs_lib_v62 = require("bitcoinjs-lib-v6");
93970
+ var import_bitcoinjs_lib_v63 = require("bitcoinjs-lib-v6");
93810
93971
  var import_bufio4 = require("bufio");
93811
93972
  var idToHash = (txid) => {
93812
93973
  return import_buffer.Buffer.from(txid, "hex").reverse();
@@ -93883,7 +94044,7 @@ function autoLoadPSBT(psbtFromFile, options) {
93883
94044
  } catch (e8) {
93884
94045
  return null;
93885
94046
  }
93886
- return import_bitcoinjs_lib_v62.Psbt.fromBuffer(psbtBuff, options);
94047
+ return import_bitcoinjs_lib_v63.Psbt.fromBuffer(psbtBuff, options);
93887
94048
  }
93888
94049
 
93889
94050
  // src/psbtv0/psbt.ts
@@ -93894,7 +94055,7 @@ var getUnsignedMultisigPsbtV0 = ({
93894
94055
  outputs,
93895
94056
  includeGlobalXpubs = false
93896
94057
  }) => {
93897
- const psbt = new import_bitcoinjs_lib_v63.Psbt({ network: (0, import_bitcoin4.networkData)(network) });
94058
+ const psbt = new import_bitcoinjs_lib_v64.Psbt({ network: (0, import_bitcoin4.networkData)(network) });
93898
94059
  psbt.setVersion(1);
93899
94060
  for (const input of inputs) {
93900
94061
  const inputData = psbtInputFormatter(
@@ -93913,7 +94074,7 @@ var getUnsignedMultisigPsbtV0 = ({
93913
94074
  return psbt;
93914
94075
  };
93915
94076
  var psbtInputFormatter = (input, addressType) => {
93916
- const tx = import_bitcoinjs_lib_v63.Transaction.fromHex(input.transactionHex);
94077
+ const tx = import_bitcoinjs_lib_v64.Transaction.fromHex(input.transactionHex);
93917
94078
  const inputData = { ...input };
93918
94079
  const nonWitnessUtxo = tx.toBuffer();
93919
94080
  if (addressType === import_bitcoin4.P2SH) {
@@ -93988,7 +94149,7 @@ var formatGlobalXpub = (extendedPublicKey) => {
93988
94149
  return global2;
93989
94150
  };
93990
94151
  var validateMultisigPsbtSignature = (raw, inputIndex, inputSignature, inputAmount) => {
93991
- const psbt = import_bitcoinjs_lib_v63.Psbt.fromBuffer(bufferize(raw));
94152
+ const psbt = import_bitcoinjs_lib_v64.Psbt.fromBuffer(bufferize(raw));
93992
94153
  if (psbt.inputCount === 0 || psbt.inputCount < inputIndex + 1) {
93993
94154
  throw new Error("Input index is out of range.");
93994
94155
  }
@@ -94004,8 +94165,8 @@ var validateMultisigPsbtSignature = (raw, inputIndex, inputSignature, inputAmoun
94004
94165
  }
94005
94166
  return false;
94006
94167
  };
94007
- var getHashForSignature = (psbt, inputIndex, inputAmount, sigHashFlag = import_bitcoinjs_lib_v63.Transaction.SIGHASH_ALL) => {
94008
- const tx = import_bitcoinjs_lib_v63.Transaction.fromBuffer(psbt.data.globalMap.unsignedTx.toBuffer());
94168
+ var getHashForSignature = (psbt, inputIndex, inputAmount, sigHashFlag = import_bitcoinjs_lib_v64.Transaction.SIGHASH_ALL) => {
94169
+ const tx = import_bitcoinjs_lib_v64.Transaction.fromBuffer(psbt.data.globalMap.unsignedTx.toBuffer());
94009
94170
  const input = psbt.data.inputs[inputIndex];
94010
94171
  if (!input.witnessScript && input.redeemScript) {
94011
94172
  return tx.hashForSignature(inputIndex, input.redeemScript, sigHashFlag);
@@ -94024,11 +94185,6 @@ var getHashForSignature = (psbt, inputIndex, inputAmount, sigHashFlag = import_b
94024
94185
  throw new Error("No redeem or witness script found for input.");
94025
94186
  };
94026
94187
  function translatePSBT(network, addressType, psbt, signingKeyDetails) {
94027
- if (addressType !== import_bitcoin4.P2SH) {
94028
- throw new Error(
94029
- "Unsupported addressType -- only P2SH is supported right now"
94030
- );
94031
- }
94032
94188
  const localPSBT = autoLoadPSBT(psbt, { network: (0, import_bitcoin4.networkData)(network) });
94033
94189
  if (localPSBT === null)
94034
94190
  return null;
@@ -94051,12 +94207,17 @@ function translatePSBT(network, addressType, psbt, signingKeyDetails) {
94051
94207
  function getUnchainedInputsFromPSBT(network, addressType, psbt) {
94052
94208
  return psbt.txInputs.map((input, index) => {
94053
94209
  const dataInput = psbt.data.inputs[index];
94210
+ const spendingScript = dataInput.witnessScript || dataInput.redeemScript;
94211
+ if (!dataInput?.nonWitnessUtxo && addressType === import_bitcoin4.P2WSH) {
94212
+ throw new Error(`Non-witness UTXO now required for P2WSH
94213
+ inputs to protect against large fee attack`);
94214
+ }
94054
94215
  const fundingTxHex = dataInput.nonWitnessUtxo.toString("hex");
94055
- const fundingTx = import_bitcoinjs_lib_v63.Transaction.fromHex(fundingTxHex);
94216
+ const fundingTx = import_bitcoinjs_lib_v64.Transaction.fromHex(fundingTxHex);
94056
94217
  const multisig = (0, import_bitcoin4.generateMultisigFromHex)(
94057
94218
  network,
94058
94219
  addressType,
94059
- dataInput.redeemScript.toString("hex")
94220
+ spendingScript.toString("hex")
94060
94221
  );
94061
94222
  return {
94062
94223
  amountSats: fundingTx.outs[input.index].value,
package/dist/index.mjs CHANGED
@@ -16,6 +16,54 @@ var PSBT_MAGIC_HEX = "70736274ff";
16
16
  var PSBT_MAGIC_B64 = "cHNidP8";
17
17
  var PSBT_MAGIC_BYTES = Buffer2.from([112, 115, 98, 116, 255]);
18
18
 
19
+ // src/psbtv2/types.ts
20
+ var KeyType = /* @__PURE__ */ ((KeyType2) => {
21
+ KeyType2["PSBT_GLOBAL_XPUB"] = "01";
22
+ KeyType2["PSBT_GLOBAL_TX_VERSION"] = "02";
23
+ KeyType2["PSBT_GLOBAL_FALLBACK_LOCKTIME"] = "03";
24
+ KeyType2["PSBT_GLOBAL_INPUT_COUNT"] = "04";
25
+ KeyType2["PSBT_GLOBAL_OUTPUT_COUNT"] = "05";
26
+ KeyType2["PSBT_GLOBAL_TX_MODIFIABLE"] = "06";
27
+ KeyType2["PSBT_GLOBAL_VERSION"] = "fb";
28
+ KeyType2["PSBT_GLOBAL_PROPRIETARY"] = "fc";
29
+ KeyType2["PSBT_IN_NON_WITNESS_UTXO"] = "00";
30
+ KeyType2["PSBT_IN_WITNESS_UTXO"] = "01";
31
+ KeyType2["PSBT_IN_PARTIAL_SIG"] = "02";
32
+ KeyType2["PSBT_IN_SIGHASH_TYPE"] = "03";
33
+ KeyType2["PSBT_IN_REDEEM_SCRIPT"] = "04";
34
+ KeyType2["PSBT_IN_WITNESS_SCRIPT"] = "05";
35
+ KeyType2["PSBT_IN_BIP32_DERIVATION"] = "06";
36
+ KeyType2["PSBT_IN_FINAL_SCRIPTSIG"] = "07";
37
+ KeyType2["PSBT_IN_FINAL_SCRIPTWITNESS"] = "08";
38
+ KeyType2["PSBT_IN_POR_COMMITMENT"] = "09";
39
+ KeyType2["PSBT_IN_RIPEMD160"] = "0a";
40
+ KeyType2["PSBT_IN_SHA256"] = "0b";
41
+ KeyType2["PSBT_IN_HASH160"] = "0c";
42
+ KeyType2["PSBT_IN_HASH256"] = "0d";
43
+ KeyType2["PSBT_IN_PREVIOUS_TXID"] = "0e";
44
+ KeyType2["PSBT_IN_OUTPUT_INDEX"] = "0f";
45
+ KeyType2["PSBT_IN_SEQUENCE"] = "10";
46
+ KeyType2["PSBT_IN_REQUIRED_TIME_LOCKTIME"] = "11";
47
+ KeyType2["PSBT_IN_REQUIRED_HEIGHT_LOCKTIME"] = "12";
48
+ KeyType2["PSBT_IN_TAP_KEY_SIG"] = "13";
49
+ KeyType2["PSBT_IN_TAP_SCRIPT_SIG"] = "14";
50
+ KeyType2["PSBT_IN_TAP_LEAF_SCRIPT"] = "15";
51
+ KeyType2["PSBT_IN_TAP_BIP32_DERIVATION"] = "16";
52
+ KeyType2["PSBT_IN_TAP_INTERNAL_KEY"] = "17";
53
+ KeyType2["PSBT_IN_TAP_MERKLE_ROOT"] = "18";
54
+ KeyType2["PSBT_IN_PROPRIETARY"] = "fc";
55
+ KeyType2["PSBT_OUT_REDEEM_SCRIPT"] = "00";
56
+ KeyType2["PSBT_OUT_WITNESS_SCRIPT"] = "01";
57
+ KeyType2["PSBT_OUT_BIP32_DERIVATION"] = "02";
58
+ KeyType2["PSBT_OUT_AMOUNT"] = "03";
59
+ KeyType2["PSBT_OUT_SCRIPT"] = "04";
60
+ KeyType2["PSBT_OUT_TAP_INTERNAL_KEY"] = "05";
61
+ KeyType2["PSBT_OUT_TAP_TREE"] = "06";
62
+ KeyType2["PSBT_OUT_TAP_BIP32_DERIVATION"] = "07";
63
+ KeyType2["PSBT_OUT_PROPRIETARY"] = "fc";
64
+ return KeyType2;
65
+ })(KeyType || {});
66
+
19
67
  // src/psbtv2/values.ts
20
68
  var PSBT_MAP_SEPARATOR = Buffer2.from([0]);
21
69
  var BIP_32_NODE_REGEX = /(\/[0-9]+'?)/gi;
@@ -115,6 +163,7 @@ import { Psbt } from "bitcoinjs-lib-v6";
115
163
 
116
164
  // src/psbtv2/psbtv2maps.ts
117
165
  import { BufferReader as BufferReader2, BufferWriter as BufferWriter2 } from "bufio";
166
+ import { Transaction } from "bitcoinjs-lib-v6";
118
167
  var PsbtV2Maps = class {
119
168
  // These maps directly correspond to the maps defined in BIP0174 and extended
120
169
  // in BIP0370
@@ -166,7 +215,7 @@ var PsbtV2Maps = class {
166
215
  return bw.render().toString(format2);
167
216
  }
168
217
  /**
169
- * Copies the maps in this PsbtV2 object to another PsbtV2 object.
218
+ * Copies the maps in this PsbtV2Maps object to another PsbtV2Maps object.
170
219
  *
171
220
  * NOTE: This copy method is made available to achieve parity with the PSBT
172
221
  * api required by `ledger-bitcoin` for creating merklized PSBTs. HOWEVER, it
@@ -190,6 +239,86 @@ var PsbtV2Maps = class {
190
239
  from.forEach((v5, k4) => to.set(k4, Buffer2.from(v5)));
191
240
  }
192
241
  };
242
+ var PsbtConversionMaps = class extends PsbtV2Maps {
243
+ /**
244
+ * Builds the unsigned transaction that is required on global map key 0x00 for
245
+ * a PSBTv0. Relies on bitcoinjs-lib to construct the transaction.
246
+ */
247
+ buildUnsignedTx() {
248
+ const tx = new Transaction();
249
+ tx.version = this.globalMap.get("02" /* PSBT_GLOBAL_TX_VERSION */)?.readUInt8() || 0;
250
+ tx.locktime = this.globalMap.get("03" /* PSBT_GLOBAL_FALLBACK_LOCKTIME */)?.readUInt32LE() || 0;
251
+ const numInputs = this.globalMap.get("04" /* PSBT_GLOBAL_INPUT_COUNT */)?.readUInt8() || 0;
252
+ const numOutputs = this.globalMap.get("05" /* PSBT_GLOBAL_OUTPUT_COUNT */)?.readUInt8() || 0;
253
+ for (let i6 = 0; i6 < numInputs; i6++) {
254
+ if (!this.inputMaps[i6].has("0e" /* PSBT_IN_PREVIOUS_TXID */)) {
255
+ console.warn(`Input ${i6} is missing previous txid. Skipping.`);
256
+ continue;
257
+ }
258
+ tx.addInput(
259
+ this.inputMaps[i6].get("0e" /* PSBT_IN_PREVIOUS_TXID */),
260
+ this.inputMaps[i6].get("0f" /* PSBT_IN_OUTPUT_INDEX */)?.readUint32LE() || 0,
261
+ this.inputMaps[i6].get("10" /* PSBT_IN_SEQUENCE */)?.readUint32LE()
262
+ );
263
+ }
264
+ for (let i6 = 0; i6 < numOutputs; i6++) {
265
+ if (!this.outputMaps[i6].has("04" /* PSBT_OUT_SCRIPT */) || !this.outputMaps[i6].has("03" /* PSBT_OUT_AMOUNT */)) {
266
+ console.warn(
267
+ `Output ${i6} is missing previous out script or amount. Skipping.`
268
+ );
269
+ continue;
270
+ }
271
+ const bigintAmount = this.outputMaps[i6].get("03" /* PSBT_OUT_AMOUNT */).readBigInt64LE();
272
+ const numberAmount = parseInt(bigintAmount.toString());
273
+ tx.addOutput(
274
+ this.outputMaps[i6].get("04" /* PSBT_OUT_SCRIPT */),
275
+ numberAmount
276
+ );
277
+ }
278
+ return tx.toBuffer();
279
+ }
280
+ /**
281
+ * Warns and then deletes map values. Intended for use when converting to a
282
+ * PsbtV0.
283
+ */
284
+ v0delete(map, key) {
285
+ if (map.has(key)) {
286
+ const keyName = Object.keys(KeyType)[Object.values(KeyType).indexOf(key)];
287
+ console.warn(
288
+ `Key ${keyName} key is not supported on PSBTv0 and will be omitted.`
289
+ );
290
+ }
291
+ map.delete(key);
292
+ }
293
+ /**
294
+ * Constructs an unsigned txn to be added to the PSBT_GLOBAL_UNSIGNED_TX key
295
+ * which is required on PsbtV0. Then removes all the fields which a PsbtV0 is
296
+ * incompatible with.
297
+ */
298
+ convertToV0 = () => {
299
+ this.globalMap.set(
300
+ "00" /* PSBT_GLOBAL_UNSIGNED_TX */,
301
+ this.buildUnsignedTx()
302
+ );
303
+ this.v0delete(this.globalMap, "02" /* PSBT_GLOBAL_TX_VERSION */);
304
+ this.v0delete(this.globalMap, "03" /* PSBT_GLOBAL_FALLBACK_LOCKTIME */);
305
+ this.v0delete(this.globalMap, "04" /* PSBT_GLOBAL_INPUT_COUNT */);
306
+ this.v0delete(this.globalMap, "05" /* PSBT_GLOBAL_OUTPUT_COUNT */);
307
+ this.v0delete(this.globalMap, "06" /* PSBT_GLOBAL_TX_MODIFIABLE */);
308
+ this.v0delete(this.globalMap, "fb" /* PSBT_GLOBAL_VERSION */);
309
+ for (const inputMap of this.inputMaps) {
310
+ this.v0delete(inputMap, "0e" /* PSBT_IN_PREVIOUS_TXID */);
311
+ this.v0delete(inputMap, "0f" /* PSBT_IN_OUTPUT_INDEX */);
312
+ this.v0delete(inputMap, "10" /* PSBT_IN_SEQUENCE */);
313
+ this.v0delete(inputMap, "11" /* PSBT_IN_REQUIRED_TIME_LOCKTIME */);
314
+ this.v0delete(inputMap, "12" /* PSBT_IN_REQUIRED_HEIGHT_LOCKTIME */);
315
+ }
316
+ for (const outputMap of this.outputMaps) {
317
+ this.v0delete(outputMap, "03" /* PSBT_OUT_AMOUNT */);
318
+ this.v0delete(outputMap, "04" /* PSBT_OUT_SCRIPT */);
319
+ }
320
+ };
321
+ };
193
322
 
194
323
  // src/psbtv2/psbtv2.ts
195
324
  var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
@@ -866,7 +995,8 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
866
995
  witnessUtxo,
867
996
  redeemScript,
868
997
  witnessScript,
869
- bip32Derivation
998
+ bip32Derivation,
999
+ sighashType
870
1000
  }) {
871
1001
  if (!this.isReadyForConstructor) {
872
1002
  throw Error(
@@ -917,6 +1047,10 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
917
1047
  map.set(key, bw.render());
918
1048
  }
919
1049
  }
1050
+ if (sighashType !== void 0) {
1051
+ bw.writeU32(sighashType);
1052
+ map.set("03" /* PSBT_IN_SIGHASH_TYPE */, bw.render());
1053
+ }
920
1054
  this.PSBT_GLOBAL_INPUT_COUNT = this.inputMaps.push(map);
921
1055
  }
922
1056
  addOutput({
@@ -1168,8 +1302,9 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
1168
1302
  * Attempts to return a PsbtV2 by converting from a PsbtV0 string or Buffer.
1169
1303
  *
1170
1304
  * This method first starts with a fresh PsbtV2 having just been created. It
1171
- * then takes the PsbtV2 through its operator saga through the Signer role. In
1172
- * this sense validation for each operator role will be performed.
1305
+ * then takes the PsbtV2 through its operator saga and through the Input
1306
+ * Finalizer role. In this sense, validation for each operator role will be
1307
+ * performed as the Psbt saga is replayed.
1173
1308
  */
1174
1309
  static FromV0(psbt, allowTxnVersion1 = false) {
1175
1310
  const psbtv0Buf = bufferize(psbt);
@@ -1182,6 +1317,7 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
1182
1317
  } else {
1183
1318
  psbtv2.PSBT_GLOBAL_TX_VERSION = psbtv0.data.getTransaction().readInt32LE(0);
1184
1319
  }
1320
+ psbtv2.PSBT_GLOBAL_FALLBACK_LOCKTIME = psbtv0.locktime;
1185
1321
  for (const globalXpub of psbtv0GlobalMap.globalXpub ?? []) {
1186
1322
  psbtv2.addGlobalXpub(
1187
1323
  globalXpub.extendedPubkey,
@@ -1206,7 +1342,8 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
1206
1342
  },
1207
1343
  redeemScript: input.redeemScript,
1208
1344
  witnessScript: input.witnessScript,
1209
- bip32Derivation: input.bip32Derivation
1345
+ bip32Derivation: input.bip32Derivation,
1346
+ sighashType: input.sighashType
1210
1347
  });
1211
1348
  }
1212
1349
  const txOutputs = [];
@@ -1228,8 +1365,32 @@ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
1228
1365
  psbtv2.addPartialSig(index, sig.pubkey, sig.signature);
1229
1366
  }
1230
1367
  }
1368
+ for (const [index, input] of psbtv0.data.inputs.entries()) {
1369
+ if (input.finalScriptSig) {
1370
+ psbtv2.inputMaps[index].set(
1371
+ "07" /* PSBT_IN_FINAL_SCRIPTSIG */,
1372
+ input.finalScriptSig
1373
+ );
1374
+ }
1375
+ if (input.finalScriptWitness) {
1376
+ psbtv2.inputMaps[index].set(
1377
+ "08" /* PSBT_IN_FINAL_SCRIPTWITNESS */,
1378
+ input.finalScriptWitness
1379
+ );
1380
+ }
1381
+ }
1231
1382
  return psbtv2;
1232
1383
  }
1384
+ /**
1385
+ * Outputs a serialized PSBTv0 from a best-attempt conversion of the fields in
1386
+ * this PSBTv2. Accepts optional desired format as a string (default base64).
1387
+ */
1388
+ toV0(format2) {
1389
+ const converterMap = new PsbtConversionMaps();
1390
+ this.copy(converterMap);
1391
+ converterMap.convertToV0();
1392
+ return converterMap.serialize(format2);
1393
+ }
1233
1394
  };
1234
1395
 
1235
1396
  // src/psbtv0/psbt.ts
@@ -1241,9 +1402,10 @@ import {
1241
1402
  multisigSignatureBuffer,
1242
1403
  networkData,
1243
1404
  P2SH as P2SH2,
1405
+ P2WSH as P2WSH2,
1244
1406
  signatureNoSighashType
1245
1407
  } from "@caravan/bitcoin";
1246
- import { Psbt as Psbt3, Transaction } from "bitcoinjs-lib-v6";
1408
+ import { Psbt as Psbt3, Transaction as Transaction2 } from "bitcoinjs-lib-v6";
1247
1409
  import { toOutputScript } from "bitcoinjs-lib-v6/src/address.js";
1248
1410
 
1249
1411
  // vendor/tiny-secp256k1-asmjs/lib/index.js
@@ -93891,7 +94053,7 @@ var getUnsignedMultisigPsbtV0 = ({
93891
94053
  return psbt;
93892
94054
  };
93893
94055
  var psbtInputFormatter = (input, addressType) => {
93894
- const tx = Transaction.fromHex(input.transactionHex);
94056
+ const tx = Transaction2.fromHex(input.transactionHex);
93895
94057
  const inputData = { ...input };
93896
94058
  const nonWitnessUtxo = tx.toBuffer();
93897
94059
  if (addressType === P2SH2) {
@@ -93982,8 +94144,8 @@ var validateMultisigPsbtSignature = (raw, inputIndex, inputSignature, inputAmoun
93982
94144
  }
93983
94145
  return false;
93984
94146
  };
93985
- var getHashForSignature = (psbt, inputIndex, inputAmount, sigHashFlag = Transaction.SIGHASH_ALL) => {
93986
- const tx = Transaction.fromBuffer(psbt.data.globalMap.unsignedTx.toBuffer());
94147
+ var getHashForSignature = (psbt, inputIndex, inputAmount, sigHashFlag = Transaction2.SIGHASH_ALL) => {
94148
+ const tx = Transaction2.fromBuffer(psbt.data.globalMap.unsignedTx.toBuffer());
93987
94149
  const input = psbt.data.inputs[inputIndex];
93988
94150
  if (!input.witnessScript && input.redeemScript) {
93989
94151
  return tx.hashForSignature(inputIndex, input.redeemScript, sigHashFlag);
@@ -94002,11 +94164,6 @@ var getHashForSignature = (psbt, inputIndex, inputAmount, sigHashFlag = Transact
94002
94164
  throw new Error("No redeem or witness script found for input.");
94003
94165
  };
94004
94166
  function translatePSBT(network, addressType, psbt, signingKeyDetails) {
94005
- if (addressType !== P2SH2) {
94006
- throw new Error(
94007
- "Unsupported addressType -- only P2SH is supported right now"
94008
- );
94009
- }
94010
94167
  const localPSBT = autoLoadPSBT(psbt, { network: networkData(network) });
94011
94168
  if (localPSBT === null)
94012
94169
  return null;
@@ -94029,12 +94186,17 @@ function translatePSBT(network, addressType, psbt, signingKeyDetails) {
94029
94186
  function getUnchainedInputsFromPSBT(network, addressType, psbt) {
94030
94187
  return psbt.txInputs.map((input, index) => {
94031
94188
  const dataInput = psbt.data.inputs[index];
94189
+ const spendingScript = dataInput.witnessScript || dataInput.redeemScript;
94190
+ if (!dataInput?.nonWitnessUtxo && addressType === P2WSH2) {
94191
+ throw new Error(`Non-witness UTXO now required for P2WSH
94192
+ inputs to protect against large fee attack`);
94193
+ }
94032
94194
  const fundingTxHex = dataInput.nonWitnessUtxo.toString("hex");
94033
- const fundingTx = Transaction.fromHex(fundingTxHex);
94195
+ const fundingTx = Transaction2.fromHex(fundingTxHex);
94034
94196
  const multisig = generateMultisigFromHex(
94035
94197
  network,
94036
94198
  addressType,
94037
- dataInput.redeemScript.toString("hex")
94199
+ spendingScript.toString("hex")
94038
94200
  );
94039
94201
  return {
94040
94202
  amountSats: fundingTx.outs[input.index].value,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caravan/psbt",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "typescript library for working with PSBTs",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -51,6 +51,7 @@
51
51
  "author": "unchained capital",
52
52
  "license": "ISC",
53
53
  "devDependencies": {
54
+ "@caravan/bip32": "*",
54
55
  "@caravan/eslint-config": "*",
55
56
  "@caravan/multisig": "*",
56
57
  "@caravan/typescript-config": "*",