@caravan/psbt 1.0.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.mjs ADDED
@@ -0,0 +1,924 @@
1
+ // ../../node_modules/esbuild-plugin-polyfill-node/polyfills/buffer.js
2
+ import { Buffer } from "buffer";
3
+
4
+ // src/psbtv2.ts
5
+ import { BufferReader, BufferWriter } from "bufio";
6
+ import { Psbt as Psbt2 } from "bitcoinjs-lib";
7
+ import { validateHex, validBase64, validateBIP32Path } from "@caravan/bitcoin";
8
+
9
+ // src/psbt.ts
10
+ import { Psbt, Transaction } from "bitcoinjs-lib";
11
+ import { reverseBuffer } from "bitcoinjs-lib/src/bufferutils.js";
12
+ import { toHexString } from "@caravan/bitcoin";
13
+ import {
14
+ generateMultisigFromHex,
15
+ multisigAddressType,
16
+ multisigBraidDetails,
17
+ multisigRedeemScript,
18
+ multisigWitnessScript,
19
+ bip32PathToSequence,
20
+ P2SH,
21
+ P2WSH,
22
+ P2SH_P2WSH,
23
+ generateBip32DerivationByIndex,
24
+ generateBraid,
25
+ networkData
26
+ } from "@caravan/bitcoin";
27
+ import BigNumber from "bignumber.js";
28
+ var PSBT_MAGIC_BYTES = Buffer.from([112, 115, 98, 116, 255]);
29
+
30
+ // src/psbtv2.ts
31
+ var PSBT_MAP_SEPARATOR = Buffer.from([0]);
32
+ var BIP_32_NODE_REGEX = /(\/[0-9]+'?)/gi;
33
+ var BIP_32_HARDENING_OFFSET = 2147483648;
34
+ function bufferize(psbt) {
35
+ if (Buffer.isBuffer(psbt)) {
36
+ return psbt;
37
+ }
38
+ if (typeof psbt === "string") {
39
+ if (validateHex(psbt) === "") {
40
+ return Buffer.from(psbt, "hex");
41
+ }
42
+ if (validBase64(psbt)) {
43
+ return Buffer.from(psbt, "base64");
44
+ }
45
+ }
46
+ throw Error("Input cannot be bufferized.");
47
+ }
48
+ function getNonUniqueKeyTypeValues(maps, keytype) {
49
+ if (Array.isArray(maps)) {
50
+ const values2 = maps.map(
51
+ (map2) => (
52
+ // TODO: Figure out a better way to type this
53
+ getNonUniqueKeyTypeValues(map2, keytype)
54
+ )
55
+ );
56
+ return values2;
57
+ }
58
+ const map = maps;
59
+ const values = [];
60
+ for (const [key, value] of map.entries()) {
61
+ if (key.startsWith(keytype)) {
62
+ values.push({ key, value: value?.toString("hex") || null });
63
+ }
64
+ }
65
+ return values;
66
+ }
67
+ function getOptionalMappedBytesAsHex(maps, keytype) {
68
+ return maps.map((map) => map.get(keytype)?.toString("hex") ?? null);
69
+ }
70
+ function getOptionalMappedBytesAsUInt(maps, keytype) {
71
+ return maps.map((map) => map.get(keytype)?.readUInt32LE(0) ?? null);
72
+ }
73
+ function parseDerivationPathNodesToBytes(path) {
74
+ const validationMessage = validateBIP32Path(path);
75
+ if (validationMessage !== "") {
76
+ throw Error(validationMessage);
77
+ }
78
+ const bw = new BufferWriter();
79
+ for (const node of path.match(BIP_32_NODE_REGEX) ?? []) {
80
+ let num = parseInt(node.slice(1), 10);
81
+ if (node.indexOf("'") > -1) {
82
+ num += BIP_32_HARDENING_OFFSET;
83
+ }
84
+ bw.writeU32(num);
85
+ }
86
+ return bw.render();
87
+ }
88
+ function readAndSetKeyPairs(map, br) {
89
+ const nextByte = br.readBytes(1);
90
+ if (nextByte.equals(PSBT_MAP_SEPARATOR)) {
91
+ return;
92
+ }
93
+ const keyLen = nextByte.readUInt8(0);
94
+ const key = br.readBytes(keyLen);
95
+ const value = br.readVarBytes();
96
+ map.set(key.toString("hex"), value);
97
+ readAndSetKeyPairs(map, br);
98
+ }
99
+ function serializeMap(map, bw) {
100
+ map.forEach((value, key) => {
101
+ const keyBuf = Buffer.from(key, "hex");
102
+ const keyLen = keyBuf.length;
103
+ bw.writeVarint(keyLen);
104
+ bw.writeString(key, "hex");
105
+ bw.writeVarint(value.length);
106
+ bw.writeBytes(value);
107
+ });
108
+ bw.writeBytes(PSBT_MAP_SEPARATOR);
109
+ }
110
+ var PsbtV2Maps = class {
111
+ // These maps directly correspond to the maps defined in BIP0174
112
+ globalMap = /* @__PURE__ */ new Map();
113
+ inputMaps = [];
114
+ outputMaps = [];
115
+ constructor(psbt) {
116
+ if (!psbt) {
117
+ return;
118
+ }
119
+ const buf = bufferize(psbt);
120
+ const br = new BufferReader(buf);
121
+ if (!br.readBytes(PSBT_MAGIC_BYTES.length, true).equals(PSBT_MAGIC_BYTES)) {
122
+ throw Error("PsbtV2 magic bytes are incorrect.");
123
+ }
124
+ readAndSetKeyPairs(this.globalMap, br);
125
+ if (
126
+ // Assuming that psbt being passed in is a valid psbtv2
127
+ !this.globalMap.has("fb" /* PSBT_GLOBAL_VERSION */) || !this.globalMap.has("02" /* PSBT_GLOBAL_TX_VERSION */) || !this.globalMap.has("04" /* PSBT_GLOBAL_INPUT_COUNT */) || !this.globalMap.has("05" /* PSBT_GLOBAL_OUTPUT_COUNT */) || this.globalMap.has("00")
128
+ ) {
129
+ throw Error("Provided PsbtV2 not valid. Missing required global keys.");
130
+ }
131
+ const inputCount = this.globalMap.get("04" /* PSBT_GLOBAL_INPUT_COUNT */)?.readUInt8(0) ?? 0;
132
+ for (let i = 0; i < inputCount; i++) {
133
+ const map = /* @__PURE__ */ new Map();
134
+ readAndSetKeyPairs(map, br);
135
+ this.inputMaps.push(map);
136
+ }
137
+ const outputCount = this.globalMap.get("05" /* PSBT_GLOBAL_OUTPUT_COUNT */)?.readUInt8(0) ?? 0;
138
+ for (let i = 0; i < outputCount; i++) {
139
+ const map = /* @__PURE__ */ new Map();
140
+ readAndSetKeyPairs(map, br);
141
+ this.outputMaps.push(map);
142
+ }
143
+ }
144
+ // Return the current state of the psbt as a string in the specified format.
145
+ serialize(format = "base64") {
146
+ let bw = new BufferWriter();
147
+ bw.writeBytes(PSBT_MAGIC_BYTES);
148
+ serializeMap(this.globalMap, bw);
149
+ for (const map of this.inputMaps) {
150
+ serializeMap(map, bw);
151
+ }
152
+ for (const map of this.outputMaps) {
153
+ serializeMap(map, bw);
154
+ }
155
+ return bw.render().toString(format);
156
+ }
157
+ // NOTE: This set of copy methods is made available to
158
+ // achieve parity with the PSBT api required by ledger-bitcoin
159
+ // for creating merklized PSBTs. HOWEVER, it is not recommended
160
+ // to use this when avoidable as copying maps bypasses the validation
161
+ // defined in the constructor, so it could create a psbtv2 in an invalid psbt status.
162
+ // PsbtV2.serialize is preferable whenever possible.
163
+ copy(to) {
164
+ this.copyMap(this.globalMap, to.globalMap);
165
+ this.copyMaps(this.inputMaps, to.inputMaps);
166
+ this.copyMaps(this.outputMaps, to.outputMaps);
167
+ }
168
+ copyMaps(from, to) {
169
+ from.forEach((m, index) => {
170
+ const to_index = /* @__PURE__ */ new Map();
171
+ this.copyMap(m, to_index);
172
+ to[index] = to_index;
173
+ });
174
+ }
175
+ // eslint-disable-next-line class-methods-use-this
176
+ copyMap(from, to) {
177
+ from.forEach((v, k) => to.set(k, Buffer.from(v)));
178
+ }
179
+ };
180
+ var PsbtV2 = class _PsbtV2 extends PsbtV2Maps {
181
+ constructor(psbt) {
182
+ super(psbt);
183
+ if (!psbt) {
184
+ this.create();
185
+ }
186
+ this.validate();
187
+ }
188
+ /**
189
+ * Globals Getters/Setters
190
+ */
191
+ get PSBT_GLOBAL_XPUB() {
192
+ return getNonUniqueKeyTypeValues(this.globalMap, "01" /* PSBT_GLOBAL_XPUB */);
193
+ }
194
+ get PSBT_GLOBAL_TX_VERSION() {
195
+ const val = this.globalMap.get("02" /* PSBT_GLOBAL_TX_VERSION */);
196
+ if (val === void 0) {
197
+ throw Error("PSBT_GLOBAL_TX_VERSION not set");
198
+ }
199
+ return val.readInt32LE(0);
200
+ }
201
+ set PSBT_GLOBAL_TX_VERSION(version) {
202
+ if (version < 2) {
203
+ throw Error(
204
+ `PsbtV2 cannot have a global tx version less than 2. Version ${version} specified.`
205
+ );
206
+ }
207
+ const bw = new BufferWriter();
208
+ bw.writeI32(version);
209
+ this.globalMap.set("02" /* PSBT_GLOBAL_TX_VERSION */, bw.render());
210
+ }
211
+ get PSBT_GLOBAL_FALLBACK_LOCKTIME() {
212
+ return this.globalMap.get("03" /* PSBT_GLOBAL_FALLBACK_LOCKTIME */)?.readUInt32LE(0) ?? null;
213
+ }
214
+ set PSBT_GLOBAL_FALLBACK_LOCKTIME(locktime) {
215
+ if (locktime === null) {
216
+ this.globalMap.delete("03" /* PSBT_GLOBAL_FALLBACK_LOCKTIME */);
217
+ } else {
218
+ const bw = new BufferWriter();
219
+ bw.writeI32(locktime);
220
+ this.globalMap.set("03" /* PSBT_GLOBAL_FALLBACK_LOCKTIME */, bw.render());
221
+ }
222
+ }
223
+ get PSBT_GLOBAL_INPUT_COUNT() {
224
+ const val = this.globalMap.get("04" /* PSBT_GLOBAL_INPUT_COUNT */);
225
+ if (val === void 0) {
226
+ throw Error("PSBT_GLOBAL_INPUT_COUNT not set");
227
+ }
228
+ return val.readUInt8(0);
229
+ }
230
+ set PSBT_GLOBAL_INPUT_COUNT(count) {
231
+ const bw = new BufferWriter();
232
+ bw.writeU8(count);
233
+ this.globalMap.set("04" /* PSBT_GLOBAL_INPUT_COUNT */, bw.render());
234
+ }
235
+ get PSBT_GLOBAL_OUTPUT_COUNT() {
236
+ const val = this.globalMap.get("05" /* PSBT_GLOBAL_OUTPUT_COUNT */);
237
+ if (val === void 0) {
238
+ throw Error("PSBT_GLOBAL_OUTPUT_COUNT not set");
239
+ }
240
+ return val.readUInt8(0);
241
+ }
242
+ set PSBT_GLOBAL_OUTPUT_COUNT(count) {
243
+ const bw = new BufferWriter();
244
+ bw.writeU8(count);
245
+ this.globalMap.set("05" /* PSBT_GLOBAL_OUTPUT_COUNT */, bw.render());
246
+ }
247
+ get PSBT_GLOBAL_TX_MODIFIABLE() {
248
+ const val = this.globalMap.get("06" /* PSBT_GLOBAL_TX_MODIFIABLE */)?.readUInt8(0) || 0;
249
+ let modifiable = [];
250
+ if (val & 1) {
251
+ modifiable.push("INPUTS" /* INPUTS */);
252
+ }
253
+ if (val & 2) {
254
+ modifiable.push("OUTPUTS" /* OUTPUTS */);
255
+ }
256
+ if (val & 4) {
257
+ modifiable.push("SIGHASH_SINGLE" /* SIGHASH_SINGLE */);
258
+ }
259
+ return modifiable;
260
+ }
261
+ set PSBT_GLOBAL_TX_MODIFIABLE(modifiable) {
262
+ let val = 0;
263
+ if (modifiable.includes("INPUTS" /* INPUTS */)) {
264
+ val |= 1;
265
+ }
266
+ if (modifiable.includes("OUTPUTS" /* OUTPUTS */)) {
267
+ val |= 2;
268
+ }
269
+ if (modifiable.includes("SIGHASH_SINGLE" /* SIGHASH_SINGLE */)) {
270
+ val |= 4;
271
+ }
272
+ const br = new BufferWriter();
273
+ br.writeU8(val);
274
+ this.globalMap.set("06" /* PSBT_GLOBAL_TX_MODIFIABLE */, br.render());
275
+ }
276
+ get PSBT_GLOBAL_VERSION() {
277
+ const version = this.globalMap.get("fb" /* PSBT_GLOBAL_VERSION */)?.readUInt32LE(0);
278
+ if (version === void 0) {
279
+ console.warn("PSBT_GLOBAL_VERSION key is missing! Setting to version 2.");
280
+ this.PSBT_GLOBAL_VERSION = 2;
281
+ }
282
+ return version ?? 2;
283
+ }
284
+ set PSBT_GLOBAL_VERSION(version) {
285
+ let workingVersion = version;
286
+ if (workingVersion < 2) {
287
+ console.warn(
288
+ `PsbtV2 cannot have a global version less than 2. Version ${workingVersion} specified. Setting to version 2.`
289
+ );
290
+ workingVersion = 2;
291
+ }
292
+ const bw = new BufferWriter();
293
+ bw.writeU32(workingVersion);
294
+ this.globalMap.set("fb" /* PSBT_GLOBAL_VERSION */, bw.render());
295
+ }
296
+ get PSBT_GLOBAL_PROPRIETARY() {
297
+ return getNonUniqueKeyTypeValues(
298
+ this.globalMap,
299
+ "fc" /* PSBT_GLOBAL_PROPRIETARY */
300
+ );
301
+ }
302
+ /**
303
+ * Input Getters/Setters
304
+ */
305
+ get PSBT_IN_NON_WITNESS_UTXO() {
306
+ return getOptionalMappedBytesAsHex(
307
+ this.inputMaps,
308
+ "00" /* PSBT_IN_NON_WITNESS_UTXO */
309
+ );
310
+ }
311
+ get PSBT_IN_WITNESS_UTXO() {
312
+ return getOptionalMappedBytesAsHex(
313
+ this.inputMaps,
314
+ "01" /* PSBT_IN_WITNESS_UTXO */
315
+ );
316
+ }
317
+ get PSBT_IN_PARTIAL_SIG() {
318
+ return getNonUniqueKeyTypeValues(
319
+ this.inputMaps,
320
+ "02" /* PSBT_IN_PARTIAL_SIG */
321
+ );
322
+ }
323
+ get PSBT_IN_SIGHASH_TYPE() {
324
+ return getOptionalMappedBytesAsUInt(
325
+ this.inputMaps,
326
+ "03" /* PSBT_IN_SIGHASH_TYPE */
327
+ );
328
+ }
329
+ get PSBT_IN_REDEEM_SCRIPT() {
330
+ return getOptionalMappedBytesAsHex(
331
+ this.inputMaps,
332
+ "04" /* PSBT_IN_REDEEM_SCRIPT */
333
+ );
334
+ }
335
+ get PSBT_IN_WITNESS_SCRIPT() {
336
+ return getOptionalMappedBytesAsHex(
337
+ this.inputMaps,
338
+ "05" /* PSBT_IN_WITNESS_SCRIPT */
339
+ );
340
+ }
341
+ get PSBT_IN_BIP32_DERIVATION() {
342
+ return getNonUniqueKeyTypeValues(
343
+ this.inputMaps,
344
+ "06" /* PSBT_IN_BIP32_DERIVATION */
345
+ );
346
+ }
347
+ get PSBT_IN_FINAL_SCRIPTSIG() {
348
+ return getOptionalMappedBytesAsHex(
349
+ this.inputMaps,
350
+ "07" /* PSBT_IN_FINAL_SCRIPTSIG */
351
+ );
352
+ }
353
+ get PSBT_IN_FINAL_SCRIPTWITNESS() {
354
+ return getOptionalMappedBytesAsHex(
355
+ this.inputMaps,
356
+ "08" /* PSBT_IN_FINAL_SCRIPTWITNESS */
357
+ );
358
+ }
359
+ get PSBT_IN_POR_COMMITMENT() {
360
+ return getOptionalMappedBytesAsHex(
361
+ this.inputMaps,
362
+ "09" /* PSBT_IN_POR_COMMITMENT */
363
+ );
364
+ }
365
+ get PSBT_IN_RIPEMD160() {
366
+ return getNonUniqueKeyTypeValues(this.inputMaps, "0a" /* PSBT_IN_RIPEMD160 */);
367
+ }
368
+ get PSBT_IN_SHA256() {
369
+ return getNonUniqueKeyTypeValues(this.inputMaps, "0b" /* PSBT_IN_SHA256 */);
370
+ }
371
+ get PSBT_IN_HASH160() {
372
+ return getNonUniqueKeyTypeValues(this.inputMaps, "0c" /* PSBT_IN_HASH160 */);
373
+ }
374
+ get PSBT_IN_HASH256() {
375
+ return getNonUniqueKeyTypeValues(this.inputMaps, "0d" /* PSBT_IN_HASH256 */);
376
+ }
377
+ get PSBT_IN_PREVIOUS_TXID() {
378
+ const indices = [];
379
+ for (const map of this.inputMaps) {
380
+ const value = map.get("0e" /* PSBT_IN_PREVIOUS_TXID */);
381
+ if (!value) {
382
+ throw Error("PSBT_IN_PREVIOUS_TXID not set for an input");
383
+ }
384
+ indices.push(value.toString("hex"));
385
+ }
386
+ return indices;
387
+ }
388
+ get PSBT_IN_OUTPUT_INDEX() {
389
+ const indices = [];
390
+ for (const map of this.inputMaps) {
391
+ const value = map.get("0f" /* PSBT_IN_OUTPUT_INDEX */);
392
+ if (!value) {
393
+ throw Error("PSBT_IN_OUTPUT_INDEX not set for an input");
394
+ }
395
+ indices.push(value.readUInt32LE(0));
396
+ }
397
+ return indices;
398
+ }
399
+ get PSBT_IN_SEQUENCE() {
400
+ return getOptionalMappedBytesAsUInt(
401
+ this.inputMaps,
402
+ "10" /* PSBT_IN_SEQUENCE */
403
+ );
404
+ }
405
+ get PSBT_IN_REQUIRED_TIME_LOCKTIME() {
406
+ return getOptionalMappedBytesAsUInt(
407
+ this.inputMaps,
408
+ "11" /* PSBT_IN_REQUIRED_TIME_LOCKTIME */
409
+ );
410
+ }
411
+ get PSBT_IN_REQUIRED_HEIGHT_LOCKTIME() {
412
+ return getOptionalMappedBytesAsUInt(
413
+ this.inputMaps,
414
+ "12" /* PSBT_IN_REQUIRED_HEIGHT_LOCKTIME */
415
+ );
416
+ }
417
+ get PSBT_IN_TAP_KEY_SIG() {
418
+ return getOptionalMappedBytesAsHex(
419
+ this.inputMaps,
420
+ "13" /* PSBT_IN_TAP_KEY_SIG */
421
+ );
422
+ }
423
+ get PSBT_IN_TAP_SCRIPT_SIG() {
424
+ return getNonUniqueKeyTypeValues(
425
+ this.inputMaps,
426
+ "14" /* PSBT_IN_TAP_SCRIPT_SIG */
427
+ );
428
+ }
429
+ get PSBT_IN_TAP_LEAF_SCRIPT() {
430
+ return getNonUniqueKeyTypeValues(
431
+ this.inputMaps,
432
+ "15" /* PSBT_IN_TAP_LEAF_SCRIPT */
433
+ );
434
+ }
435
+ get PSBT_IN_TAP_BIP32_DERIVATION() {
436
+ return getNonUniqueKeyTypeValues(
437
+ this.inputMaps,
438
+ "16" /* PSBT_IN_TAP_BIP32_DERIVATION */
439
+ );
440
+ }
441
+ get PSBT_IN_TAP_INTERNAL_KEY() {
442
+ return getOptionalMappedBytesAsHex(
443
+ this.inputMaps,
444
+ "17" /* PSBT_IN_TAP_INTERNAL_KEY */
445
+ );
446
+ }
447
+ get PSBT_IN_TAP_MERKLE_ROOT() {
448
+ return getOptionalMappedBytesAsHex(
449
+ this.inputMaps,
450
+ "18" /* PSBT_IN_TAP_MERKLE_ROOT */
451
+ );
452
+ }
453
+ get PSBT_IN_PROPRIETARY() {
454
+ return getNonUniqueKeyTypeValues(
455
+ this.inputMaps,
456
+ "fc" /* PSBT_IN_PROPRIETARY */
457
+ );
458
+ }
459
+ /**
460
+ * Output Getters/Setters
461
+ */
462
+ get PSBT_OUT_REDEEM_SCRIPT() {
463
+ return getOptionalMappedBytesAsHex(
464
+ this.outputMaps,
465
+ "00" /* PSBT_OUT_REDEEM_SCRIPT */
466
+ );
467
+ }
468
+ get PSBT_OUT_WITNESS_SCRIPT() {
469
+ return getOptionalMappedBytesAsHex(
470
+ this.outputMaps,
471
+ "01" /* PSBT_OUT_WITNESS_SCRIPT */
472
+ );
473
+ }
474
+ get PSBT_OUT_BIP32_DERIVATION() {
475
+ return getNonUniqueKeyTypeValues(
476
+ this.outputMaps,
477
+ "02" /* PSBT_OUT_BIP32_DERIVATION */
478
+ );
479
+ }
480
+ get PSBT_OUT_AMOUNT() {
481
+ const indices = [];
482
+ for (const map of this.outputMaps) {
483
+ const value = map.get("03" /* PSBT_OUT_AMOUNT */);
484
+ if (!value) {
485
+ throw Error("PSBT_OUT_AMOUNT not set for an output");
486
+ }
487
+ const br = new BufferReader(value);
488
+ indices.push(br.readBigI64(value));
489
+ }
490
+ return indices;
491
+ }
492
+ get PSBT_OUT_SCRIPT() {
493
+ const indices = [];
494
+ for (const map of this.outputMaps) {
495
+ const value = map.get("04" /* PSBT_OUT_SCRIPT */);
496
+ if (!value) {
497
+ throw Error("PSBT_OUT_SCRIPT not set for an output");
498
+ }
499
+ indices.push(value.toString("hex"));
500
+ }
501
+ return indices;
502
+ }
503
+ get PSBT_OUT_TAP_INTERNAL_KEY() {
504
+ return getOptionalMappedBytesAsHex(
505
+ this.outputMaps,
506
+ "05" /* PSBT_OUT_TAP_INTERNAL_KEY */
507
+ );
508
+ }
509
+ get PSBT_OUT_TAP_TREE() {
510
+ return getOptionalMappedBytesAsHex(
511
+ this.outputMaps,
512
+ "06" /* PSBT_OUT_TAP_TREE */
513
+ );
514
+ }
515
+ get PSBT_OUT_TAP_BIP32_DERIVATION() {
516
+ return getNonUniqueKeyTypeValues(
517
+ this.outputMaps,
518
+ "07" /* PSBT_OUT_TAP_BIP32_DERIVATION */
519
+ );
520
+ }
521
+ get PSBT_OUT_PROPRIETARY() {
522
+ return getNonUniqueKeyTypeValues(
523
+ this.outputMaps,
524
+ "fc" /* PSBT_OUT_PROPRIETARY */
525
+ );
526
+ }
527
+ /**
528
+ * Other Getters/Setters
529
+ */
530
+ get nLockTime() {
531
+ const inputCount = this.PSBT_GLOBAL_INPUT_COUNT;
532
+ const heightLocks = this.PSBT_IN_REQUIRED_HEIGHT_LOCKTIME;
533
+ const timeLocks = this.PSBT_IN_REQUIRED_TIME_LOCKTIME;
534
+ let heights = [];
535
+ let times = [];
536
+ for (let i = 0; i < this.PSBT_GLOBAL_INPUT_COUNT; i++) {
537
+ if (heightLocks[i] !== null) {
538
+ heights.push(heightLocks[i]);
539
+ }
540
+ if (timeLocks[i] !== null) {
541
+ times.push(timeLocks[i]);
542
+ }
543
+ }
544
+ if (heights.length === 0 && times.length === 0) {
545
+ return this.PSBT_GLOBAL_FALLBACK_LOCKTIME || 0;
546
+ }
547
+ if (heights.length === inputCount || heights.length > times.length) {
548
+ return Math.max(...heights);
549
+ }
550
+ if (times.length > heights.length) {
551
+ return Math.max(...times);
552
+ }
553
+ return null;
554
+ }
555
+ /**
556
+ * Creator/Constructor Methods
557
+ */
558
+ // This method ensures that global fields have initial values required by a
559
+ // PsbtV2 Creator. It is called by the constructor if constructed without a
560
+ // psbt.
561
+ create() {
562
+ this.PSBT_GLOBAL_VERSION = 2;
563
+ this.PSBT_GLOBAL_TX_VERSION = 2;
564
+ this.PSBT_GLOBAL_INPUT_COUNT = 0;
565
+ this.PSBT_GLOBAL_OUTPUT_COUNT = 0;
566
+ this.PSBT_GLOBAL_FALLBACK_LOCKTIME = 0;
567
+ }
568
+ // This method should check initial construction of any valid PsbtV2. It is
569
+ // called when a psbt is passed to the constructor or when a new psbt is being
570
+ // created. If constructed with a psbt, this method acts outside of the
571
+ // Creator role to validate the current state of the psbt.
572
+ validate() {
573
+ if (this.PSBT_GLOBAL_VERSION < 2) {
574
+ throw Error("PsbtV2 has a version field set less than 2");
575
+ }
576
+ if (this.PSBT_GLOBAL_TX_VERSION < 2) {
577
+ throw Error("PsbtV2 has a tx version field set less than 2");
578
+ }
579
+ for (const prevInTxid of this.PSBT_IN_PREVIOUS_TXID) {
580
+ if (!prevInTxid) {
581
+ throw Error("PsbtV2 input is missing PSBT_IN_PREVIOUS_TXID");
582
+ }
583
+ }
584
+ for (const prevInVOut of this.PSBT_IN_OUTPUT_INDEX) {
585
+ if (prevInVOut === void 0) {
586
+ throw Error("PsbtV2 input is missing PSBT_IN_OUTPUT_INDEX");
587
+ }
588
+ }
589
+ for (const amount of this.PSBT_OUT_AMOUNT) {
590
+ if (!amount) {
591
+ throw Error("PsbtV2 input is missing PSBT_OUT_AMOUNT");
592
+ }
593
+ }
594
+ for (const script of this.PSBT_OUT_SCRIPT) {
595
+ if (!script) {
596
+ throw Error("PsbtV2 input is missing PSBT_OUT_SCRIPT");
597
+ }
598
+ }
599
+ for (const locktime of this.PSBT_IN_REQUIRED_TIME_LOCKTIME) {
600
+ if (locktime && locktime < 5e8) {
601
+ throw Error("PsbtV2 input time locktime is less than 500000000.");
602
+ }
603
+ }
604
+ for (const locktime of this.PSBT_IN_REQUIRED_HEIGHT_LOCKTIME) {
605
+ if (locktime && locktime >= 5e8) {
606
+ throw Error("PsbtV2 input hight locktime is gte 500000000.");
607
+ }
608
+ }
609
+ }
610
+ // This method is provided for compatibility issues and probably shouldn't be
611
+ // used since a PsbtV2 with PSBT_GLOBAL_TX_VERSION = 1 is BIP0370
612
+ // non-compliant. No guarantees can be made here that a serialized PsbtV2
613
+ // which used this method will be compatible with outside consumers.
614
+ //
615
+ // One may wish to instance this class from a partially signed
616
+ // PSBTv0 with a txn version 1 by using the static PsbtV2.FromV0. This method
617
+ // provides a way to override validation logic for the txn version and roles
618
+ // lifecycle defined for PsbtV2.
619
+ dangerouslySetGlobalTxVersion1() {
620
+ console.warn("Dangerously setting PsbtV2.PSBT_GLOBAL_TX_VERSION to 1!");
621
+ const bw = new BufferWriter();
622
+ bw.writeI32(1);
623
+ this.globalMap.set("02" /* PSBT_GLOBAL_TX_VERSION */, bw.render());
624
+ }
625
+ // Is this a Creator/Constructor role action, or something else. BIPs don't
626
+ // define it well.
627
+ addGlobalXpub(xpub, fingerprint, path) {
628
+ const bw = new BufferWriter();
629
+ bw.writeBytes(Buffer.from("01" /* PSBT_GLOBAL_XPUB */, "hex"));
630
+ bw.writeBytes(xpub);
631
+ const key = bw.render().toString("hex");
632
+ bw.writeBytes(fingerprint);
633
+ const pathBytes = parseDerivationPathNodesToBytes(path);
634
+ bw.writeBytes(pathBytes);
635
+ const value = bw.render();
636
+ this.globalMap.set(key, value);
637
+ }
638
+ addInput({
639
+ previousTxId,
640
+ outputIndex,
641
+ sequence,
642
+ nonWitnessUtxo,
643
+ witnessUtxo,
644
+ redeemScript,
645
+ witnessScript,
646
+ bip32Derivation
647
+ }) {
648
+ if (!this.isModifiable(["INPUTS" /* INPUTS */])) {
649
+ throw Error(
650
+ "PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE inputs cannot be modified."
651
+ );
652
+ }
653
+ const map = /* @__PURE__ */ new Map();
654
+ const bw = new BufferWriter();
655
+ const prevTxIdBuf = bufferize(previousTxId);
656
+ bw.writeBytes(prevTxIdBuf);
657
+ map.set("0e" /* PSBT_IN_PREVIOUS_TXID */, bw.render());
658
+ bw.writeI32(outputIndex);
659
+ map.set("0f" /* PSBT_IN_OUTPUT_INDEX */, bw.render());
660
+ if (sequence) {
661
+ bw.writeI32(sequence);
662
+ map.set("10" /* PSBT_IN_SEQUENCE */, bw.render());
663
+ }
664
+ if (nonWitnessUtxo) {
665
+ bw.writeBytes(nonWitnessUtxo);
666
+ map.set("00" /* PSBT_IN_NON_WITNESS_UTXO */, bw.render());
667
+ }
668
+ if (witnessUtxo) {
669
+ bw.writeI64(witnessUtxo.amount);
670
+ bw.writeU8(witnessUtxo.script.length);
671
+ bw.writeBytes(witnessUtxo.script);
672
+ map.set("01" /* PSBT_IN_WITNESS_UTXO */, bw.render());
673
+ }
674
+ if (redeemScript) {
675
+ bw.writeBytes(redeemScript);
676
+ map.set("04" /* PSBT_IN_REDEEM_SCRIPT */, bw.render());
677
+ }
678
+ if (witnessScript) {
679
+ bw.writeBytes(witnessScript);
680
+ map.set("05" /* PSBT_IN_WITNESS_SCRIPT */, bw.render());
681
+ }
682
+ if (bip32Derivation) {
683
+ for (const bip32 of bip32Derivation) {
684
+ bw.writeString("06" /* PSBT_IN_BIP32_DERIVATION */, "hex");
685
+ bw.writeBytes(bip32.pubkey);
686
+ const key = bw.render().toString("hex");
687
+ bw.writeBytes(bip32.masterFingerprint);
688
+ bw.writeBytes(parseDerivationPathNodesToBytes(bip32.path));
689
+ map.set(key, bw.render());
690
+ }
691
+ }
692
+ this.PSBT_GLOBAL_INPUT_COUNT = this.inputMaps.push(map);
693
+ }
694
+ addOutput({
695
+ amount,
696
+ script,
697
+ redeemScript,
698
+ witnessScript,
699
+ bip32Derivation
700
+ }) {
701
+ if (!this.isModifiable(["OUTPUTS" /* OUTPUTS */])) {
702
+ throw Error(
703
+ "PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE outputs cannot be modified."
704
+ );
705
+ }
706
+ const map = /* @__PURE__ */ new Map();
707
+ const bw = new BufferWriter();
708
+ bw.writeI64(amount);
709
+ map.set("03" /* PSBT_OUT_AMOUNT */, bw.render());
710
+ bw.writeBytes(script);
711
+ map.set("04" /* PSBT_OUT_SCRIPT */, bw.render());
712
+ if (redeemScript) {
713
+ bw.writeBytes(redeemScript);
714
+ map.set("00" /* PSBT_OUT_REDEEM_SCRIPT */, bw.render());
715
+ }
716
+ if (witnessScript) {
717
+ bw.writeBytes(witnessScript);
718
+ map.set("01" /* PSBT_OUT_WITNESS_SCRIPT */, bw.render());
719
+ }
720
+ if (bip32Derivation) {
721
+ for (const bip32 of bip32Derivation) {
722
+ bw.writeString("02" /* PSBT_OUT_BIP32_DERIVATION */, "hex");
723
+ bw.writeBytes(bip32.pubkey);
724
+ const key = bw.render().toString("hex");
725
+ bw.writeBytes(bip32.masterFingerprint);
726
+ bw.writeBytes(parseDerivationPathNodesToBytes(bip32.path));
727
+ map.set(key, bw.render());
728
+ }
729
+ }
730
+ this.outputMaps.push(map);
731
+ this.PSBT_GLOBAL_OUTPUT_COUNT = this.outputMaps.length;
732
+ }
733
+ /**
734
+ * Updater/Signer Methods
735
+ */
736
+ // Removes an input-map from inputMaps
737
+ deleteInput(index) {
738
+ if (!this.isModifiable(["INPUTS" /* INPUTS */])) {
739
+ throw Error(
740
+ "PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE inputs cannot be modified."
741
+ );
742
+ }
743
+ const newInputs = this.inputMaps.filter((_, i) => i !== index);
744
+ this.inputMaps = newInputs;
745
+ this.PSBT_GLOBAL_INPUT_COUNT = this.inputMaps.length;
746
+ }
747
+ // Removes an output-map from outputMaps
748
+ deleteOutput(index) {
749
+ if (!this.isModifiable(["OUTPUTS" /* OUTPUTS */])) {
750
+ throw Error(
751
+ "PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE outputs cannot be modified."
752
+ );
753
+ }
754
+ const newOutputs = this.outputMaps.filter((_, i) => i !== index);
755
+ if (this.isModifiable(["SIGHASH_SINGLE" /* SIGHASH_SINGLE */])) {
756
+ this.removePartialSig(index);
757
+ }
758
+ this.outputMaps = newOutputs;
759
+ this.PSBT_GLOBAL_OUTPUT_COUNT = this.outputMaps.length;
760
+ }
761
+ // Checks that provided flags are present in PSBT_GLOBAL_TX_MODIFIABLE.
762
+ isModifiable(flags) {
763
+ for (const flag of flags) {
764
+ if (!this.PSBT_GLOBAL_TX_MODIFIABLE.includes(flag)) {
765
+ return false;
766
+ }
767
+ }
768
+ return true;
769
+ }
770
+ // The Signer, when it creates a signature, must add the partial sig keypair
771
+ // to the psbt for the input which it is signing. In the case that a
772
+ // particular signer does not, this method can be used to add a signature to
773
+ // the psbt. This method assumes the Signer did the validation outlined in
774
+ // BIP0174 before creating a signature.
775
+ // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer
776
+ addPartialSig(inputIndex, pubkey, sig) {
777
+ if (!this.inputMaps[inputIndex]) {
778
+ throw Error(`PsbtV2 has no input at ${inputIndex}`);
779
+ }
780
+ if (!pubkey || !sig) {
781
+ throw Error(
782
+ `PsbtV2.addPartialSig() missing argument ${!pubkey && "pubkey" || !sig && "sig"}`
783
+ );
784
+ }
785
+ const key = `${"02" /* PSBT_IN_PARTIAL_SIG */}${pubkey.toString("hex")}`;
786
+ if (this.inputMaps[inputIndex].has(key)) {
787
+ throw Error(
788
+ "PsbtV2 already has a signature for this input with this pubkey"
789
+ );
790
+ }
791
+ const modBackup = this.PSBT_GLOBAL_TX_MODIFIABLE;
792
+ try {
793
+ this.inputMaps[inputIndex].set(key, sig);
794
+ this.handleSighashType(sig);
795
+ } catch (err) {
796
+ console.error(err);
797
+ this.inputMaps[inputIndex].delete(key);
798
+ this.PSBT_GLOBAL_TX_MODIFIABLE = modBackup;
799
+ }
800
+ }
801
+ // Removes all sigs for an input unless a pubkey is specified.
802
+ removePartialSig(inputIndex, pubkey) {
803
+ const input = this.inputMaps[inputIndex];
804
+ if (!input) {
805
+ throw Error(`PsbtV2 has no input at ${inputIndex}`);
806
+ }
807
+ if (pubkey) {
808
+ const key = `${"02" /* PSBT_IN_PARTIAL_SIG */}${pubkey.toString("hex")}`;
809
+ const sig = this.PSBT_IN_PARTIAL_SIG[inputIndex].find(
810
+ (el) => el.key === key
811
+ );
812
+ if (!sig) {
813
+ throw Error(
814
+ `PsbtV2 input has no signature from pubkey ${pubkey.toString("hex")}`
815
+ );
816
+ }
817
+ input.delete(key);
818
+ } else {
819
+ const sigs = this.PSBT_IN_PARTIAL_SIG[inputIndex];
820
+ for (const sig of sigs) {
821
+ input.delete(sig.key);
822
+ }
823
+ }
824
+ }
825
+ // Used to ensure the PSBT is in the proper state when adding a partial sig
826
+ // keypair.
827
+ // https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#signer
828
+ handleSighashType(sig) {
829
+ const br = new BufferReader(sig.slice(-1));
830
+ let sighashVal = br.readU8();
831
+ let modifiable = this.PSBT_GLOBAL_TX_MODIFIABLE;
832
+ if (!(sighashVal & 128 /* SIGHASH_ANYONECANPAY */)) {
833
+ modifiable = modifiable.filter(
834
+ (val) => val !== "INPUTS" /* INPUTS */
835
+ );
836
+ } else {
837
+ sighashVal ^= 128 /* SIGHASH_ANYONECANPAY */;
838
+ }
839
+ if (sighashVal !== 2 /* SIGHASH_NONE */) {
840
+ modifiable = modifiable.filter(
841
+ (val) => val !== "OUTPUTS" /* OUTPUTS */
842
+ );
843
+ }
844
+ if (sighashVal === 3 /* SIGHASH_SINGLE */ && !modifiable.includes("SIGHASH_SINGLE" /* SIGHASH_SINGLE */)) {
845
+ modifiable.push("SIGHASH_SINGLE" /* SIGHASH_SINGLE */);
846
+ }
847
+ this.PSBT_GLOBAL_TX_MODIFIABLE = modifiable;
848
+ }
849
+ // Attempt to return a PsbtV2 by converting from a PsbtV0 string or Buffer
850
+ static FromV0(psbt, allowTxnVersion1 = false) {
851
+ const psbtv0Buf = bufferize(psbt);
852
+ const psbtv0 = Psbt2.fromBuffer(psbtv0Buf);
853
+ const psbtv0GlobalMap = psbtv0.data.globalMap;
854
+ const psbtv2 = new _PsbtV2();
855
+ psbtv2.PSBT_GLOBAL_TX_MODIFIABLE = [
856
+ "INPUTS" /* INPUTS */,
857
+ "OUTPUTS" /* OUTPUTS */
858
+ ];
859
+ const txVersion = psbtv0.data.getTransaction().readInt32LE(0);
860
+ if (txVersion === 1 && allowTxnVersion1) {
861
+ psbtv2.dangerouslySetGlobalTxVersion1();
862
+ } else {
863
+ psbtv2.PSBT_GLOBAL_TX_VERSION = psbtv0.data.getTransaction().readInt32LE(0);
864
+ }
865
+ for (const globalXpub of psbtv0GlobalMap.globalXpub ?? []) {
866
+ psbtv2.addGlobalXpub(
867
+ globalXpub.extendedPubkey,
868
+ globalXpub.masterFingerprint,
869
+ globalXpub.path
870
+ );
871
+ }
872
+ let txInputs = [];
873
+ for (const [index, txInput] of psbtv0.txInputs.entries()) {
874
+ txInputs[index] = txInput;
875
+ }
876
+ for (const [index, input] of psbtv0.data.inputs.entries()) {
877
+ const txInput = txInputs[index];
878
+ psbtv2.addInput({
879
+ previousTxId: txInput.hash,
880
+ outputIndex: txInput.index,
881
+ sequence: txInput.sequence,
882
+ nonWitnessUtxo: input.nonWitnessUtxo,
883
+ witnessUtxo: input.witnessUtxo && {
884
+ amount: input.witnessUtxo.value,
885
+ script: input.witnessUtxo.script
886
+ },
887
+ redeemScript: input.redeemScript,
888
+ witnessScript: input.witnessScript,
889
+ bip32Derivation: input.bip32Derivation
890
+ });
891
+ }
892
+ let txOutputs = [];
893
+ for (const [index, txOutput] of psbtv0.txOutputs.entries()) {
894
+ txOutputs[index] = txOutput;
895
+ }
896
+ for (const [index, output] of psbtv0.data.outputs.entries()) {
897
+ const txOutput = txOutputs[index];
898
+ psbtv2.addOutput({
899
+ amount: txOutput.value,
900
+ script: txOutput.script,
901
+ redeemScript: output.redeemScript,
902
+ witnessScript: output.witnessScript,
903
+ bip32Derivation: output.bip32Derivation
904
+ });
905
+ }
906
+ for (const [index, input] of psbtv0.data.inputs.entries()) {
907
+ for (const sig of input.partialSig || []) {
908
+ psbtv2.addPartialSig(index, sig.pubkey, sig.signature);
909
+ }
910
+ }
911
+ return psbtv2;
912
+ }
913
+ };
914
+ function getPsbtVersionNumber(psbt) {
915
+ const map = /* @__PURE__ */ new Map();
916
+ const buf = bufferize(psbt);
917
+ const br = new BufferReader(buf.slice(PSBT_MAGIC_BYTES.length));
918
+ readAndSetKeyPairs(map, br);
919
+ return map.get("fb" /* PSBT_GLOBAL_VERSION */)?.readUInt32LE(0) || 0;
920
+ }
921
+ export {
922
+ PsbtV2,
923
+ getPsbtVersionNumber
924
+ };