@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 +77 -54
- package/dist/index.d.ts +104 -1
- package/dist/index.js +92776 -42
- package/dist/index.mjs +92798 -64
- package/package.json +28 -7
- package/.eslintrc.cjs +0 -6
- package/.prettierrc +0 -4
- package/.turbo/turbo-build.log +0 -19
- package/.turbo/turbo-lint.log +0 -61
- package/.turbo/turbo-test.log +0 -99
- package/CHANGELOG.md +0 -28
- package/dist/index.d.mts +0 -324
- package/jest.config.js +0 -4
- package/src/index.ts +0 -2
- package/src/psbt.test.ts +0 -338
- package/src/psbt.ts +0 -440
- package/src/psbtv2/functions.ts +0 -169
- package/src/psbtv2/index.ts +0 -3
- package/src/psbtv2/psbtv2.test.ts +0 -1587
- package/src/psbtv2/psbtv2.ts +0 -1307
- package/src/psbtv2/psbtv2maps.ts +0 -111
- package/src/psbtv2/types.ts +0 -91
- package/src/psbtv2/values.ts +0 -4
- package/tsconfig.json +0 -3
- package/tsup.config.ts +0 -6
package/src/psbtv2/psbtv2.ts
DELETED
|
@@ -1,1307 +0,0 @@
|
|
|
1
|
-
import { BufferReader, BufferWriter } from "bufio";
|
|
2
|
-
import { Psbt } from "bitcoinjs-lib";
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
Key,
|
|
6
|
-
Value,
|
|
7
|
-
NonUniqueKeyTypeValue,
|
|
8
|
-
KeyType,
|
|
9
|
-
PsbtGlobalTxModifiableBits,
|
|
10
|
-
SighashType,
|
|
11
|
-
MapSelectorType,
|
|
12
|
-
} from "./types";
|
|
13
|
-
import {
|
|
14
|
-
bufferize,
|
|
15
|
-
getNonUniqueKeyTypeValues,
|
|
16
|
-
getOptionalMappedBytesAsHex,
|
|
17
|
-
getOptionalMappedBytesAsUInt,
|
|
18
|
-
parseDerivationPathNodesToBytes,
|
|
19
|
-
} from "./functions";
|
|
20
|
-
import { PsbtV2Maps } from "./psbtv2maps";
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* The PsbtV2 class is intended to represent an easily modifiable and
|
|
24
|
-
* serializable psbt of version 2 conforming to BIP0174. Getters exist for all
|
|
25
|
-
* BIP-defined keytypes. Very few setters and modifier methods exist. As they
|
|
26
|
-
* are added, they should enforce implied and documented rules and limitations.
|
|
27
|
-
*
|
|
28
|
-
* Defining BIPs:
|
|
29
|
-
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
|
|
30
|
-
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki
|
|
31
|
-
*/
|
|
32
|
-
export class PsbtV2 extends PsbtV2Maps {
|
|
33
|
-
constructor(psbt?: Buffer | string) {
|
|
34
|
-
super(psbt);
|
|
35
|
-
|
|
36
|
-
if (!psbt) {
|
|
37
|
-
this.create();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
this.validate();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Globals Getters/Setters
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
get PSBT_GLOBAL_XPUB() {
|
|
48
|
-
return getNonUniqueKeyTypeValues(
|
|
49
|
-
this.globalMap,
|
|
50
|
-
KeyType.PSBT_GLOBAL_XPUB,
|
|
51
|
-
) as NonUniqueKeyTypeValue[];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
get PSBT_GLOBAL_TX_VERSION() {
|
|
55
|
-
const val = this.globalMap.get(KeyType.PSBT_GLOBAL_TX_VERSION);
|
|
56
|
-
|
|
57
|
-
if (val === undefined) {
|
|
58
|
-
throw Error("PSBT_GLOBAL_TX_VERSION not set");
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return val.readInt32LE(0);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
set PSBT_GLOBAL_TX_VERSION(version: number) {
|
|
65
|
-
if (version < 2) {
|
|
66
|
-
// It's unfortunate this setter has to throw, but a PsbtV2 is invalid with
|
|
67
|
-
// a txn version < 2. The Creator role is responsible for setting this
|
|
68
|
-
// value and BIP0370 specifies that it cannot be less than 2.
|
|
69
|
-
// https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#cite_note-3
|
|
70
|
-
throw Error(
|
|
71
|
-
`PsbtV2 cannot have a global tx version less than 2. Version ${version} specified.`,
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const bw = new BufferWriter();
|
|
76
|
-
bw.writeI32(version);
|
|
77
|
-
this.globalMap.set(KeyType.PSBT_GLOBAL_TX_VERSION, bw.render());
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
get PSBT_GLOBAL_FALLBACK_LOCKTIME() {
|
|
81
|
-
return (
|
|
82
|
-
this.globalMap
|
|
83
|
-
.get(KeyType.PSBT_GLOBAL_FALLBACK_LOCKTIME)
|
|
84
|
-
?.readUInt32LE(0) ?? null
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
set PSBT_GLOBAL_FALLBACK_LOCKTIME(locktime: number | null) {
|
|
89
|
-
if (locktime === null) {
|
|
90
|
-
this.globalMap.delete(KeyType.PSBT_GLOBAL_FALLBACK_LOCKTIME);
|
|
91
|
-
} else {
|
|
92
|
-
const bw = new BufferWriter();
|
|
93
|
-
bw.writeI32(locktime);
|
|
94
|
-
this.globalMap.set(KeyType.PSBT_GLOBAL_FALLBACK_LOCKTIME, bw.render());
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
get PSBT_GLOBAL_INPUT_COUNT() {
|
|
99
|
-
const val = this.globalMap.get(KeyType.PSBT_GLOBAL_INPUT_COUNT);
|
|
100
|
-
|
|
101
|
-
if (val === undefined) {
|
|
102
|
-
throw Error("PSBT_GLOBAL_INPUT_COUNT not set");
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return val.readUInt8(0);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
set PSBT_GLOBAL_INPUT_COUNT(count: number) {
|
|
109
|
-
const bw = new BufferWriter();
|
|
110
|
-
bw.writeU8(count);
|
|
111
|
-
this.globalMap.set(KeyType.PSBT_GLOBAL_INPUT_COUNT, bw.render());
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
get PSBT_GLOBAL_OUTPUT_COUNT() {
|
|
115
|
-
const val = this.globalMap.get(KeyType.PSBT_GLOBAL_OUTPUT_COUNT);
|
|
116
|
-
|
|
117
|
-
if (val === undefined) {
|
|
118
|
-
throw Error("PSBT_GLOBAL_OUTPUT_COUNT not set");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return val.readUInt8(0);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
set PSBT_GLOBAL_OUTPUT_COUNT(count: number) {
|
|
125
|
-
const bw = new BufferWriter();
|
|
126
|
-
bw.writeU8(count);
|
|
127
|
-
this.globalMap.set(KeyType.PSBT_GLOBAL_OUTPUT_COUNT, bw.render());
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
get PSBT_GLOBAL_TX_MODIFIABLE() {
|
|
131
|
-
const val =
|
|
132
|
-
this.globalMap.get(KeyType.PSBT_GLOBAL_TX_MODIFIABLE)?.readUInt8(0) || 0;
|
|
133
|
-
let modifiable: PsbtGlobalTxModifiableBits[] = [];
|
|
134
|
-
|
|
135
|
-
if (val & 0b00000001) {
|
|
136
|
-
modifiable.push(PsbtGlobalTxModifiableBits.INPUTS);
|
|
137
|
-
}
|
|
138
|
-
if (val & 0b00000010) {
|
|
139
|
-
modifiable.push(PsbtGlobalTxModifiableBits.OUTPUTS);
|
|
140
|
-
}
|
|
141
|
-
if (val & 0b00000100) {
|
|
142
|
-
modifiable.push(PsbtGlobalTxModifiableBits.SIGHASH_SINGLE);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return modifiable;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
set PSBT_GLOBAL_TX_MODIFIABLE(modifiable: PsbtGlobalTxModifiableBits[]) {
|
|
149
|
-
let val = 0b00000000;
|
|
150
|
-
|
|
151
|
-
if (modifiable.includes(PsbtGlobalTxModifiableBits.INPUTS)) {
|
|
152
|
-
val |= 0b00000001;
|
|
153
|
-
}
|
|
154
|
-
if (modifiable.includes(PsbtGlobalTxModifiableBits.OUTPUTS)) {
|
|
155
|
-
val |= 0b00000010;
|
|
156
|
-
}
|
|
157
|
-
if (modifiable.includes(PsbtGlobalTxModifiableBits.SIGHASH_SINGLE)) {
|
|
158
|
-
val |= 0b00000100;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const br = new BufferWriter();
|
|
162
|
-
br.writeU8(val);
|
|
163
|
-
this.globalMap.set(KeyType.PSBT_GLOBAL_TX_MODIFIABLE, br.render());
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
get PSBT_GLOBAL_VERSION() {
|
|
167
|
-
const version = this.globalMap
|
|
168
|
-
.get(KeyType.PSBT_GLOBAL_VERSION)
|
|
169
|
-
?.readUInt32LE(0);
|
|
170
|
-
if (version === undefined) {
|
|
171
|
-
// This should never happen.
|
|
172
|
-
console.warn("PSBT_GLOBAL_VERSION key is missing! Setting to version 2.");
|
|
173
|
-
this.PSBT_GLOBAL_VERSION = 2;
|
|
174
|
-
}
|
|
175
|
-
return version ?? 2;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
set PSBT_GLOBAL_VERSION(version: number) {
|
|
179
|
-
let workingVersion = version;
|
|
180
|
-
if (workingVersion < 2) {
|
|
181
|
-
console.warn(
|
|
182
|
-
`PsbtV2 cannot have a global version less than 2. Version ${workingVersion} specified. Setting to version 2.`,
|
|
183
|
-
);
|
|
184
|
-
workingVersion = 2;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const bw = new BufferWriter();
|
|
188
|
-
bw.writeU32(workingVersion);
|
|
189
|
-
this.globalMap.set(KeyType.PSBT_GLOBAL_VERSION, bw.render());
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
get PSBT_GLOBAL_PROPRIETARY() {
|
|
193
|
-
return getNonUniqueKeyTypeValues(
|
|
194
|
-
this.globalMap,
|
|
195
|
-
KeyType.PSBT_GLOBAL_PROPRIETARY,
|
|
196
|
-
) as NonUniqueKeyTypeValue[];
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Input Getters/Setters
|
|
201
|
-
*/
|
|
202
|
-
|
|
203
|
-
get PSBT_IN_NON_WITNESS_UTXO() {
|
|
204
|
-
return getOptionalMappedBytesAsHex(
|
|
205
|
-
this.inputMaps,
|
|
206
|
-
KeyType.PSBT_IN_NON_WITNESS_UTXO,
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
get PSBT_IN_WITNESS_UTXO() {
|
|
211
|
-
return getOptionalMappedBytesAsHex(
|
|
212
|
-
this.inputMaps,
|
|
213
|
-
KeyType.PSBT_IN_WITNESS_UTXO,
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
get PSBT_IN_PARTIAL_SIG() {
|
|
218
|
-
return getNonUniqueKeyTypeValues(
|
|
219
|
-
this.inputMaps,
|
|
220
|
-
KeyType.PSBT_IN_PARTIAL_SIG,
|
|
221
|
-
) as NonUniqueKeyTypeValue[][];
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
get PSBT_IN_SIGHASH_TYPE() {
|
|
225
|
-
return getOptionalMappedBytesAsUInt(
|
|
226
|
-
this.inputMaps,
|
|
227
|
-
KeyType.PSBT_IN_SIGHASH_TYPE,
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
get PSBT_IN_REDEEM_SCRIPT() {
|
|
232
|
-
return getOptionalMappedBytesAsHex(
|
|
233
|
-
this.inputMaps,
|
|
234
|
-
KeyType.PSBT_IN_REDEEM_SCRIPT,
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
get PSBT_IN_WITNESS_SCRIPT() {
|
|
239
|
-
return getOptionalMappedBytesAsHex(
|
|
240
|
-
this.inputMaps,
|
|
241
|
-
KeyType.PSBT_IN_WITNESS_SCRIPT,
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
get PSBT_IN_BIP32_DERIVATION() {
|
|
246
|
-
return getNonUniqueKeyTypeValues(
|
|
247
|
-
this.inputMaps,
|
|
248
|
-
KeyType.PSBT_IN_BIP32_DERIVATION,
|
|
249
|
-
) as NonUniqueKeyTypeValue[][];
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
get PSBT_IN_FINAL_SCRIPTSIG() {
|
|
253
|
-
return getOptionalMappedBytesAsHex(
|
|
254
|
-
this.inputMaps,
|
|
255
|
-
KeyType.PSBT_IN_FINAL_SCRIPTSIG,
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
get PSBT_IN_FINAL_SCRIPTWITNESS() {
|
|
260
|
-
return getOptionalMappedBytesAsHex(
|
|
261
|
-
this.inputMaps,
|
|
262
|
-
KeyType.PSBT_IN_FINAL_SCRIPTWITNESS,
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
get PSBT_IN_POR_COMMITMENT() {
|
|
267
|
-
return getOptionalMappedBytesAsHex(
|
|
268
|
-
this.inputMaps,
|
|
269
|
-
KeyType.PSBT_IN_POR_COMMITMENT,
|
|
270
|
-
);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
get PSBT_IN_RIPEMD160() {
|
|
274
|
-
return getNonUniqueKeyTypeValues(
|
|
275
|
-
this.inputMaps,
|
|
276
|
-
KeyType.PSBT_IN_RIPEMD160,
|
|
277
|
-
) as NonUniqueKeyTypeValue[][];
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
get PSBT_IN_SHA256() {
|
|
281
|
-
return getNonUniqueKeyTypeValues(
|
|
282
|
-
this.inputMaps,
|
|
283
|
-
KeyType.PSBT_IN_SHA256,
|
|
284
|
-
) as NonUniqueKeyTypeValue[][];
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
get PSBT_IN_HASH160() {
|
|
288
|
-
return getNonUniqueKeyTypeValues(
|
|
289
|
-
this.inputMaps,
|
|
290
|
-
KeyType.PSBT_IN_HASH160,
|
|
291
|
-
) as NonUniqueKeyTypeValue[][];
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
get PSBT_IN_HASH256() {
|
|
295
|
-
return getNonUniqueKeyTypeValues(
|
|
296
|
-
this.inputMaps,
|
|
297
|
-
KeyType.PSBT_IN_HASH256,
|
|
298
|
-
) as NonUniqueKeyTypeValue[][];
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
get PSBT_IN_PREVIOUS_TXID() {
|
|
302
|
-
const indices: string[] = [];
|
|
303
|
-
for (const map of this.inputMaps) {
|
|
304
|
-
const value = map.get(KeyType.PSBT_IN_PREVIOUS_TXID);
|
|
305
|
-
if (!value) {
|
|
306
|
-
throw Error("PSBT_IN_PREVIOUS_TXID not set for an input");
|
|
307
|
-
}
|
|
308
|
-
indices.push(value.toString("hex"));
|
|
309
|
-
}
|
|
310
|
-
return indices;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
get PSBT_IN_OUTPUT_INDEX() {
|
|
314
|
-
const indices: number[] = [];
|
|
315
|
-
for (const map of this.inputMaps) {
|
|
316
|
-
const value = map.get(KeyType.PSBT_IN_OUTPUT_INDEX);
|
|
317
|
-
if (!value) {
|
|
318
|
-
throw Error("PSBT_IN_OUTPUT_INDEX not set for an input");
|
|
319
|
-
}
|
|
320
|
-
indices.push(value.readUInt32LE(0));
|
|
321
|
-
}
|
|
322
|
-
return indices;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
get PSBT_IN_SEQUENCE() {
|
|
326
|
-
return getOptionalMappedBytesAsUInt(
|
|
327
|
-
this.inputMaps,
|
|
328
|
-
KeyType.PSBT_IN_SEQUENCE,
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
get PSBT_IN_REQUIRED_TIME_LOCKTIME() {
|
|
333
|
-
return getOptionalMappedBytesAsUInt(
|
|
334
|
-
this.inputMaps,
|
|
335
|
-
KeyType.PSBT_IN_REQUIRED_TIME_LOCKTIME,
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
get PSBT_IN_REQUIRED_HEIGHT_LOCKTIME() {
|
|
340
|
-
return getOptionalMappedBytesAsUInt(
|
|
341
|
-
this.inputMaps,
|
|
342
|
-
KeyType.PSBT_IN_REQUIRED_HEIGHT_LOCKTIME,
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
get PSBT_IN_TAP_KEY_SIG() {
|
|
347
|
-
return getOptionalMappedBytesAsHex(
|
|
348
|
-
this.inputMaps,
|
|
349
|
-
KeyType.PSBT_IN_TAP_KEY_SIG,
|
|
350
|
-
);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
get PSBT_IN_TAP_SCRIPT_SIG() {
|
|
354
|
-
return getNonUniqueKeyTypeValues(
|
|
355
|
-
this.inputMaps,
|
|
356
|
-
KeyType.PSBT_IN_TAP_SCRIPT_SIG,
|
|
357
|
-
) as NonUniqueKeyTypeValue[][];
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
get PSBT_IN_TAP_LEAF_SCRIPT() {
|
|
361
|
-
return getNonUniqueKeyTypeValues(
|
|
362
|
-
this.inputMaps,
|
|
363
|
-
KeyType.PSBT_IN_TAP_LEAF_SCRIPT,
|
|
364
|
-
) as NonUniqueKeyTypeValue[][];
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
get PSBT_IN_TAP_BIP32_DERIVATION() {
|
|
368
|
-
return getNonUniqueKeyTypeValues(
|
|
369
|
-
this.inputMaps,
|
|
370
|
-
KeyType.PSBT_IN_TAP_BIP32_DERIVATION,
|
|
371
|
-
) as NonUniqueKeyTypeValue[][];
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
get PSBT_IN_TAP_INTERNAL_KEY() {
|
|
375
|
-
return getOptionalMappedBytesAsHex(
|
|
376
|
-
this.inputMaps,
|
|
377
|
-
KeyType.PSBT_IN_TAP_INTERNAL_KEY,
|
|
378
|
-
);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
get PSBT_IN_TAP_MERKLE_ROOT() {
|
|
382
|
-
return getOptionalMappedBytesAsHex(
|
|
383
|
-
this.inputMaps,
|
|
384
|
-
KeyType.PSBT_IN_TAP_MERKLE_ROOT,
|
|
385
|
-
);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
get PSBT_IN_PROPRIETARY() {
|
|
389
|
-
return getNonUniqueKeyTypeValues(
|
|
390
|
-
this.inputMaps,
|
|
391
|
-
KeyType.PSBT_IN_PROPRIETARY,
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Output Getters/Setters
|
|
397
|
-
*/
|
|
398
|
-
|
|
399
|
-
get PSBT_OUT_REDEEM_SCRIPT() {
|
|
400
|
-
return getOptionalMappedBytesAsHex(
|
|
401
|
-
this.outputMaps,
|
|
402
|
-
KeyType.PSBT_OUT_REDEEM_SCRIPT,
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
get PSBT_OUT_WITNESS_SCRIPT() {
|
|
407
|
-
return getOptionalMappedBytesAsHex(
|
|
408
|
-
this.outputMaps,
|
|
409
|
-
KeyType.PSBT_OUT_WITNESS_SCRIPT,
|
|
410
|
-
);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
get PSBT_OUT_BIP32_DERIVATION() {
|
|
414
|
-
return getNonUniqueKeyTypeValues(
|
|
415
|
-
this.outputMaps,
|
|
416
|
-
KeyType.PSBT_OUT_BIP32_DERIVATION,
|
|
417
|
-
);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
get PSBT_OUT_AMOUNT() {
|
|
421
|
-
const indices: bigint[] = [];
|
|
422
|
-
for (const map of this.outputMaps) {
|
|
423
|
-
const value = map.get(KeyType.PSBT_OUT_AMOUNT);
|
|
424
|
-
if (!value) {
|
|
425
|
-
throw Error("PSBT_OUT_AMOUNT not set for an output");
|
|
426
|
-
}
|
|
427
|
-
const br = new BufferReader(value);
|
|
428
|
-
indices.push(br.readBigI64(value));
|
|
429
|
-
}
|
|
430
|
-
return indices;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
get PSBT_OUT_SCRIPT() {
|
|
434
|
-
const indices: string[] = [];
|
|
435
|
-
for (const map of this.outputMaps) {
|
|
436
|
-
const value = map.get(KeyType.PSBT_OUT_SCRIPT);
|
|
437
|
-
if (!value) {
|
|
438
|
-
// This should never happen, but it can't be gracefully handled.
|
|
439
|
-
throw Error("PSBT_OUT_SCRIPT not set for an output");
|
|
440
|
-
}
|
|
441
|
-
indices.push(value.toString("hex"));
|
|
442
|
-
}
|
|
443
|
-
return indices;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
get PSBT_OUT_TAP_INTERNAL_KEY() {
|
|
447
|
-
return getOptionalMappedBytesAsHex(
|
|
448
|
-
this.outputMaps,
|
|
449
|
-
KeyType.PSBT_OUT_TAP_INTERNAL_KEY,
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
get PSBT_OUT_TAP_TREE() {
|
|
454
|
-
return getOptionalMappedBytesAsHex(
|
|
455
|
-
this.outputMaps,
|
|
456
|
-
KeyType.PSBT_OUT_TAP_TREE,
|
|
457
|
-
);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
get PSBT_OUT_TAP_BIP32_DERIVATION() {
|
|
461
|
-
return getNonUniqueKeyTypeValues(
|
|
462
|
-
this.outputMaps,
|
|
463
|
-
KeyType.PSBT_OUT_TAP_BIP32_DERIVATION,
|
|
464
|
-
);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
get PSBT_OUT_PROPRIETARY() {
|
|
468
|
-
return getNonUniqueKeyTypeValues(
|
|
469
|
-
this.outputMaps,
|
|
470
|
-
KeyType.PSBT_OUT_PROPRIETARY,
|
|
471
|
-
);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Operator Role Validation Getters
|
|
476
|
-
*/
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Returns true if the PsbtV2 is ready for an operator taking the Constructor
|
|
480
|
-
* role.
|
|
481
|
-
*
|
|
482
|
-
* This check assumes that the Creator used this class's constructor method to
|
|
483
|
-
* initialize the PsbtV2 without passing a psbt (constructor defaults were
|
|
484
|
-
* set).
|
|
485
|
-
*/
|
|
486
|
-
get isReadyForConstructor() {
|
|
487
|
-
// The Creator role (likely via the class constructor) must ensure at least
|
|
488
|
-
// the following value has been initialized. The psbt cannot be passed to
|
|
489
|
-
// the Constructor until it is set.
|
|
490
|
-
if (this.PSBT_GLOBAL_FALLBACK_LOCKTIME === null) {
|
|
491
|
-
return false;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// At least inputs or outputs must still be modifiable.
|
|
495
|
-
if (
|
|
496
|
-
!this.isModifiable([PsbtGlobalTxModifiableBits.INPUTS]) &&
|
|
497
|
-
!this.isModifiable([PsbtGlobalTxModifiableBits.OUTPUTS])
|
|
498
|
-
) {
|
|
499
|
-
return false;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
return true;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
/**
|
|
506
|
-
* Returns true if the PsbtV2 is ready for an operator taking the Updater
|
|
507
|
-
* role.
|
|
508
|
-
*
|
|
509
|
-
* Before signatures are added, but after an input is added, a PsbtV2 is
|
|
510
|
-
* likely to be ready for Constructor, ready for Updater, and ready for Signer
|
|
511
|
-
* simultaneously.
|
|
512
|
-
*
|
|
513
|
-
* According to BIP370, the Updater can modify the sequence number, but it is
|
|
514
|
-
* unclear if the Updater retains permissions provided in psbtv0 (BIP174). It
|
|
515
|
-
* is likely not the case that the Updater has the same permissions as
|
|
516
|
-
* previously because it seems to now be the realm of the Constructor to add
|
|
517
|
-
* inputs and outputs.
|
|
518
|
-
*/
|
|
519
|
-
get isReadyForUpdater() {
|
|
520
|
-
// In psbtv2, the Updater can set the sequence number, but an input must
|
|
521
|
-
// exist for this to be set.
|
|
522
|
-
if (this.PSBT_GLOBAL_INPUT_COUNT === 0) {
|
|
523
|
-
return false;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// Inputs must still be modifiable
|
|
527
|
-
if (!this.isModifiable([PsbtGlobalTxModifiableBits.INPUTS])) {
|
|
528
|
-
return false;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
return true;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/**
|
|
535
|
-
* Returns true if the PsbtV2 is ready for an operator taking the Signer role.
|
|
536
|
-
*/
|
|
537
|
-
get isReadyForSigner() {
|
|
538
|
-
// An input must exist before it can be signed.
|
|
539
|
-
if (this.PSBT_GLOBAL_INPUT_COUNT === 0) {
|
|
540
|
-
return false;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// TODO: Maybe it makes sense to more granularly check if the psbt is fully
|
|
544
|
-
// signed or has minimum signatures. Until then, just check that sigs have
|
|
545
|
-
// not been finalized.
|
|
546
|
-
if (this.isReadyForTransactionExtractor) {
|
|
547
|
-
return false;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return true;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
* Returns true if the PsbtV2 is ready for an operator taking the Combiner
|
|
555
|
-
* role.
|
|
556
|
-
*/
|
|
557
|
-
get isReadyForCombiner() {
|
|
558
|
-
// The combiner can potentially provide everything that's missing when
|
|
559
|
-
// merging another psbt. If it's at least ready for updates from the
|
|
560
|
-
// following roles, then it's ready for a Combiner.
|
|
561
|
-
return (
|
|
562
|
-
this.isReadyForConstructor ||
|
|
563
|
-
this.isReadyForUpdater ||
|
|
564
|
-
this.isReadyForSigner
|
|
565
|
-
);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* Unimplemented. Returns false.
|
|
570
|
-
*/
|
|
571
|
-
get isReadyForInputFinalizer() {
|
|
572
|
-
// Checks to see if the psbt contains everything needed to finalize inputs.
|
|
573
|
-
// This can become quite complicated considering multisig and taproot.
|
|
574
|
-
console.warn(
|
|
575
|
-
"PsbtV2.isReadyForInputFinalizer has been called, however, this getter is unimplemented and shouldn't be used.",
|
|
576
|
-
);
|
|
577
|
-
return false;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
/**
|
|
581
|
-
* Returns true if the PsbtV2 is ready for an operator taking the Transaction
|
|
582
|
-
* Extractor role.
|
|
583
|
-
*
|
|
584
|
-
* If all the inputs have been finalized, then the psbt is ready for the
|
|
585
|
-
* Transaction Extractor. According to BIP 174, it's the responsibility of the
|
|
586
|
-
* Input Finalizer to add scriptSigs or scriptWitnesses and then remove other
|
|
587
|
-
* details besides the UTXO. This getter checks that the Input Finalizer has
|
|
588
|
-
* finished its job.
|
|
589
|
-
*/
|
|
590
|
-
get isReadyForTransactionExtractor() {
|
|
591
|
-
// Iterate over all inputs
|
|
592
|
-
|
|
593
|
-
for (let i = 0; i < this.PSBT_GLOBAL_INPUT_COUNT; i++) {
|
|
594
|
-
// Check for finalized script
|
|
595
|
-
if (
|
|
596
|
-
!this.PSBT_IN_FINAL_SCRIPTSIG[i] &&
|
|
597
|
-
!this.PSBT_IN_FINAL_SCRIPTWITNESS[i]
|
|
598
|
-
) {
|
|
599
|
-
return false;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// Check that the corresponding UTXO is still available
|
|
603
|
-
if (
|
|
604
|
-
(this.PSBT_IN_FINAL_SCRIPTSIG[i] &&
|
|
605
|
-
!this.PSBT_IN_NON_WITNESS_UTXO[i]) ||
|
|
606
|
-
(this.PSBT_IN_FINAL_SCRIPTWITNESS[i] && !this.PSBT_IN_WITNESS_UTXO[i])
|
|
607
|
-
) {
|
|
608
|
-
return false;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
// Check that Input Finalizer removed other values from the input.
|
|
612
|
-
//
|
|
613
|
-
// Test vectors from BIP 370 indicate that a missing PSBT_IN_OUTPUT_INDEX
|
|
614
|
-
// or PSBT_IN_PREVIOUS_TXID should be an invalid psbt, so the getters for
|
|
615
|
-
// these keys will throw unless the values are set. However, the BIP also
|
|
616
|
-
// requires that the Input Finalizer removes all other values from the
|
|
617
|
-
// input map except for the finalized scripts and UTXOs. Since removal of
|
|
618
|
-
// the above mentioned keys will result in an invalid psbt, it's decided
|
|
619
|
-
// here that it's safe to ignore the fact that those keys have not been
|
|
620
|
-
// removed.
|
|
621
|
-
if (
|
|
622
|
-
// Strings
|
|
623
|
-
this.PSBT_IN_REDEEM_SCRIPT[i] ||
|
|
624
|
-
this.PSBT_IN_WITNESS_SCRIPT[i] ||
|
|
625
|
-
this.PSBT_IN_POR_COMMITMENT[i] ||
|
|
626
|
-
this.PSBT_IN_TAP_KEY_SIG[i] ||
|
|
627
|
-
this.PSBT_IN_TAP_INTERNAL_KEY[i] ||
|
|
628
|
-
this.PSBT_IN_TAP_MERKLE_ROOT[i] ||
|
|
629
|
-
// Numbers
|
|
630
|
-
this.PSBT_IN_SIGHASH_TYPE[i] !== null ||
|
|
631
|
-
this.PSBT_IN_SEQUENCE[i] !== null ||
|
|
632
|
-
this.PSBT_IN_REQUIRED_TIME_LOCKTIME[i] !== null ||
|
|
633
|
-
this.PSBT_IN_REQUIRED_HEIGHT_LOCKTIME[i] !== null ||
|
|
634
|
-
// Arrays of non-unique keytype values
|
|
635
|
-
this.PSBT_IN_PARTIAL_SIG[i].filter((el) => el !== null).length > 0 ||
|
|
636
|
-
this.PSBT_IN_BIP32_DERIVATION[i].filter((el) => el !== null).length >
|
|
637
|
-
0 ||
|
|
638
|
-
this.PSBT_IN_RIPEMD160[i].filter((el) => el !== null).length > 0 ||
|
|
639
|
-
this.PSBT_IN_SHA256[i].filter((el) => el !== null).length > 0 ||
|
|
640
|
-
this.PSBT_IN_HASH160[i].filter((el) => el !== null).length > 0 ||
|
|
641
|
-
this.PSBT_IN_HASH256[i].filter((el) => el !== null).length > 0 ||
|
|
642
|
-
this.PSBT_IN_TAP_SCRIPT_SIG[i].filter((el) => el !== null).length > 0 ||
|
|
643
|
-
this.PSBT_IN_TAP_LEAF_SCRIPT[i].filter((el) => el !== null).length >
|
|
644
|
-
0 ||
|
|
645
|
-
this.PSBT_IN_TAP_BIP32_DERIVATION[i].filter((el) => el !== null)
|
|
646
|
-
.length > 0
|
|
647
|
-
) {
|
|
648
|
-
return false;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// This input has been finalized. Continue checking the next one.
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// All inputs have been finalized, so this psbt is ready for transaction
|
|
655
|
-
// extraction.
|
|
656
|
-
return true;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
/**
|
|
660
|
-
* Other Getters/Setters
|
|
661
|
-
*/
|
|
662
|
-
|
|
663
|
-
/**
|
|
664
|
-
* Returns the `nLockTime` field for the psbt as if it were a bitcoin
|
|
665
|
-
* transaction.
|
|
666
|
-
*/
|
|
667
|
-
get nLockTime() {
|
|
668
|
-
// From BIP0370: The nLockTime field of a transaction is determined by
|
|
669
|
-
// inspecting the PSBT_GLOBAL_FALLBACK_LOCKTIME and each input's
|
|
670
|
-
// PSBT_IN_REQUIRED_TIME_LOCKTIME and PSBT_IN_REQUIRED_HEIGHT_LOCKTIME
|
|
671
|
-
// fields.
|
|
672
|
-
//
|
|
673
|
-
// First collect total locks
|
|
674
|
-
const inputCount = this.PSBT_GLOBAL_INPUT_COUNT;
|
|
675
|
-
const heightLocks = this.PSBT_IN_REQUIRED_HEIGHT_LOCKTIME;
|
|
676
|
-
const timeLocks = this.PSBT_IN_REQUIRED_TIME_LOCKTIME;
|
|
677
|
-
let heights: number[] = [];
|
|
678
|
-
let times: number[] = [];
|
|
679
|
-
for (let i = 0; i < this.PSBT_GLOBAL_INPUT_COUNT; i++) {
|
|
680
|
-
if (heightLocks[i] !== null) {
|
|
681
|
-
heights.push(heightLocks[i] as number);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
if (timeLocks[i] !== null) {
|
|
685
|
-
times.push(timeLocks[i] as number);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// From BIP0370: If none of the inputs have a PSBT_IN_REQUIRED_TIME_LOCKTIME
|
|
690
|
-
// and *(or) PSBT_IN_REQUIRED_HEIGHT_LOCKTIME, then
|
|
691
|
-
// PSBT_GLOBAL_FALLBACK_LOCKTIME must be used. If
|
|
692
|
-
// PSBT_GLOBAL_FALLBACK_LOCKTIME is not provided, then it is assumed to be
|
|
693
|
-
// 0.
|
|
694
|
-
if (heights.length === 0 && times.length === 0) {
|
|
695
|
-
return this.PSBT_GLOBAL_FALLBACK_LOCKTIME || 0;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// From BIP0370: If one or more inputs have a PSBT_IN_REQUIRED_TIME_LOCKTIME
|
|
699
|
-
// or PSBT_IN_REQUIRED_HEIGHT_LOCKTIME, then the field chosen is the one
|
|
700
|
-
// which is supported by all of the inputs. This can be determined by
|
|
701
|
-
// looking at all of the inputs which specify a locktime in either of those
|
|
702
|
-
// fields, and choosing the field which is present in all of those inputs.
|
|
703
|
-
// Inputs not specifying a lock time field can take both types of lock
|
|
704
|
-
// times, as can those that specify both. The lock time chosen is then the
|
|
705
|
-
// maximum value of the chosen type of lock time.
|
|
706
|
-
//
|
|
707
|
-
// If a PSBT has both types of locktimes possible because one or more inputs
|
|
708
|
-
// specify both PSBT_IN_REQUIRED_TIME_LOCKTIME and
|
|
709
|
-
// PSBT_IN_REQUIRED_HEIGHT_LOCKTIME, then locktime determined by looking at
|
|
710
|
-
// the PSBT_IN_REQUIRED_HEIGHT_LOCKTIME fields of the inputs must be chosen.
|
|
711
|
-
if (heights.length === inputCount || heights.length > times.length) {
|
|
712
|
-
return Math.max(...heights);
|
|
713
|
-
}
|
|
714
|
-
if (times.length > heights.length) {
|
|
715
|
-
return Math.max(...times);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
return null;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
/**
|
|
722
|
-
* Creator/Constructor Methods
|
|
723
|
-
*/
|
|
724
|
-
|
|
725
|
-
/**
|
|
726
|
-
* Ensures that global fields have initial values required by a PsbtV2
|
|
727
|
-
* Creator. It is called by the constructor if constructed without a psbt.
|
|
728
|
-
*/
|
|
729
|
-
private create() {
|
|
730
|
-
this.PSBT_GLOBAL_VERSION = 2;
|
|
731
|
-
this.PSBT_GLOBAL_TX_VERSION = 2;
|
|
732
|
-
this.PSBT_GLOBAL_INPUT_COUNT = 0;
|
|
733
|
-
this.PSBT_GLOBAL_OUTPUT_COUNT = 0;
|
|
734
|
-
|
|
735
|
-
// TODO: Right now these values are setting a default. How can it be made to
|
|
736
|
-
// accept values on the constructor method? The Creator role should be
|
|
737
|
-
// allowed to configure these.
|
|
738
|
-
this.PSBT_GLOBAL_FALLBACK_LOCKTIME = 0;
|
|
739
|
-
this.PSBT_GLOBAL_TX_MODIFIABLE = [
|
|
740
|
-
PsbtGlobalTxModifiableBits.INPUTS,
|
|
741
|
-
PsbtGlobalTxModifiableBits.OUTPUTS,
|
|
742
|
-
];
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
/**
|
|
746
|
-
* Checks initial construction of any valid PsbtV2. It is called when a psbt
|
|
747
|
-
* is passed to the constructor or when a new psbt is being created. If
|
|
748
|
-
* constructed with a psbt, this method acts outside of the Creator role to
|
|
749
|
-
* validate the current state of the psbt.
|
|
750
|
-
*/
|
|
751
|
-
private validate() {
|
|
752
|
-
if (this.PSBT_GLOBAL_VERSION < 2) {
|
|
753
|
-
throw Error("PsbtV2 has a version field set less than 2");
|
|
754
|
-
}
|
|
755
|
-
if (this.PSBT_GLOBAL_TX_VERSION < 2) {
|
|
756
|
-
throw Error("PsbtV2 has a tx version field set less than 2");
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
for (const prevInTxid of this.PSBT_IN_PREVIOUS_TXID) {
|
|
760
|
-
if (!prevInTxid) {
|
|
761
|
-
throw Error("PsbtV2 input is missing PSBT_IN_PREVIOUS_TXID");
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
for (const prevInVOut of this.PSBT_IN_OUTPUT_INDEX) {
|
|
765
|
-
if (prevInVOut === undefined) {
|
|
766
|
-
throw Error("PsbtV2 input is missing PSBT_IN_OUTPUT_INDEX");
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
for (const amount of this.PSBT_OUT_AMOUNT) {
|
|
770
|
-
if (!amount) {
|
|
771
|
-
throw Error("PsbtV2 input is missing PSBT_OUT_AMOUNT");
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
for (const script of this.PSBT_OUT_SCRIPT) {
|
|
775
|
-
if (!script) {
|
|
776
|
-
throw Error("PsbtV2 input is missing PSBT_OUT_SCRIPT");
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
for (const locktime of this.PSBT_IN_REQUIRED_TIME_LOCKTIME) {
|
|
780
|
-
if (locktime && locktime < 500000000) {
|
|
781
|
-
throw Error("PsbtV2 input time locktime is less than 500000000.");
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
for (const locktime of this.PSBT_IN_REQUIRED_HEIGHT_LOCKTIME) {
|
|
785
|
-
if (locktime && locktime >= 500000000) {
|
|
786
|
-
throw Error("PsbtV2 input hight locktime is gte 500000000.");
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
/**
|
|
792
|
-
* This method is provided for compatibility issues and probably shouldn't be
|
|
793
|
-
* used since a PsbtV2 with PSBT_GLOBAL_TX_VERSION = 1 is BIP0370
|
|
794
|
-
* non-compliant. No guarantees can be made here that a serialized PsbtV2
|
|
795
|
-
* which used this method will be compatible with outside consumers.
|
|
796
|
-
*
|
|
797
|
-
* One may wish to instance this class from a partially signed PSBTv0 with a
|
|
798
|
-
* txn version 1 by using the static PsbtV2.FromV0. This method provides a way
|
|
799
|
-
* to override validation logic for the txn version and roles lifecycle
|
|
800
|
-
* defined for PsbtV2.
|
|
801
|
-
*/
|
|
802
|
-
public dangerouslySetGlobalTxVersion1() {
|
|
803
|
-
if (!this.isReadyForConstructor) {
|
|
804
|
-
throw Error(
|
|
805
|
-
"The PsbtV2 is not ready for a Constructor. The PSBT_GLOBAL_TX_VERSION should not be forced to version 1.",
|
|
806
|
-
);
|
|
807
|
-
}
|
|
808
|
-
console.warn("Dangerously setting PsbtV2.PSBT_GLOBAL_TX_VERSION to 1!");
|
|
809
|
-
const bw = new BufferWriter();
|
|
810
|
-
bw.writeI32(1);
|
|
811
|
-
this.globalMap.set(KeyType.PSBT_GLOBAL_TX_VERSION, bw.render());
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
// Is this a Creator/Constructor role action, or something else? BIPs don't
|
|
815
|
-
// define it well.
|
|
816
|
-
public addGlobalXpub(xpub: Buffer, fingerprint: Buffer, path: string) {
|
|
817
|
-
const bw = new BufferWriter();
|
|
818
|
-
bw.writeBytes(Buffer.from(KeyType.PSBT_GLOBAL_XPUB, "hex"));
|
|
819
|
-
bw.writeBytes(xpub);
|
|
820
|
-
const key = bw.render().toString("hex");
|
|
821
|
-
bw.writeBytes(fingerprint);
|
|
822
|
-
const pathBytes = parseDerivationPathNodesToBytes(path);
|
|
823
|
-
bw.writeBytes(pathBytes);
|
|
824
|
-
const value = bw.render();
|
|
825
|
-
this.globalMap.set(key, value);
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
public addInput({
|
|
829
|
-
previousTxId,
|
|
830
|
-
outputIndex,
|
|
831
|
-
sequence,
|
|
832
|
-
nonWitnessUtxo,
|
|
833
|
-
witnessUtxo,
|
|
834
|
-
redeemScript,
|
|
835
|
-
witnessScript,
|
|
836
|
-
bip32Derivation,
|
|
837
|
-
}: {
|
|
838
|
-
previousTxId: Buffer | string;
|
|
839
|
-
outputIndex: number;
|
|
840
|
-
sequence?: number;
|
|
841
|
-
nonWitnessUtxo?: Buffer;
|
|
842
|
-
witnessUtxo?: { amount: number; script: Buffer };
|
|
843
|
-
redeemScript?: Buffer;
|
|
844
|
-
witnessScript?: Buffer;
|
|
845
|
-
bip32Derivation?: {
|
|
846
|
-
pubkey: Buffer;
|
|
847
|
-
masterFingerprint: Buffer;
|
|
848
|
-
path: string;
|
|
849
|
-
}[];
|
|
850
|
-
}) {
|
|
851
|
-
// TODO: This must accept and add appropriate locktime fields. There is
|
|
852
|
-
// significant validation concerning this step detailed in the BIP0370
|
|
853
|
-
// Constructor role:
|
|
854
|
-
// https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#constructor
|
|
855
|
-
//
|
|
856
|
-
// TODO: This method must properly handle the SIGHASH_SINGLE flag. If the
|
|
857
|
-
// `PSBT_GLOBAL_TX_MODIFIABLE` flag `SIGHASH_SINGLE` is present and a
|
|
858
|
-
// signature is present, then adding or removing inputs or outputs before a
|
|
859
|
-
// signature with sighash_single must happen atomically in pairs.
|
|
860
|
-
|
|
861
|
-
if (!this.isReadyForConstructor) {
|
|
862
|
-
throw Error(
|
|
863
|
-
"The PsbtV2 is not ready for a Constructor. Inputs cannot be added.",
|
|
864
|
-
);
|
|
865
|
-
}
|
|
866
|
-
if (!this.isModifiable([PsbtGlobalTxModifiableBits.INPUTS])) {
|
|
867
|
-
throw Error(
|
|
868
|
-
"PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE inputs cannot be modified.",
|
|
869
|
-
);
|
|
870
|
-
}
|
|
871
|
-
const map = new Map<Key, Value>();
|
|
872
|
-
const bw = new BufferWriter();
|
|
873
|
-
const prevTxIdBuf = bufferize(previousTxId);
|
|
874
|
-
bw.writeBytes(prevTxIdBuf);
|
|
875
|
-
|
|
876
|
-
map.set(KeyType.PSBT_IN_PREVIOUS_TXID, bw.render());
|
|
877
|
-
bw.writeI32(outputIndex);
|
|
878
|
-
map.set(KeyType.PSBT_IN_OUTPUT_INDEX, bw.render());
|
|
879
|
-
if (sequence) {
|
|
880
|
-
bw.writeI32(sequence);
|
|
881
|
-
map.set(KeyType.PSBT_IN_SEQUENCE, bw.render());
|
|
882
|
-
}
|
|
883
|
-
if (nonWitnessUtxo) {
|
|
884
|
-
bw.writeBytes(nonWitnessUtxo);
|
|
885
|
-
map.set(KeyType.PSBT_IN_NON_WITNESS_UTXO, bw.render());
|
|
886
|
-
}
|
|
887
|
-
if (witnessUtxo) {
|
|
888
|
-
bw.writeI64(witnessUtxo.amount);
|
|
889
|
-
bw.writeU8(witnessUtxo.script.length);
|
|
890
|
-
bw.writeBytes(witnessUtxo.script);
|
|
891
|
-
map.set(KeyType.PSBT_IN_WITNESS_UTXO, bw.render());
|
|
892
|
-
}
|
|
893
|
-
if (redeemScript) {
|
|
894
|
-
bw.writeBytes(redeemScript);
|
|
895
|
-
map.set(KeyType.PSBT_IN_REDEEM_SCRIPT, bw.render());
|
|
896
|
-
}
|
|
897
|
-
if (witnessScript) {
|
|
898
|
-
bw.writeBytes(witnessScript);
|
|
899
|
-
map.set(KeyType.PSBT_IN_WITNESS_SCRIPT, bw.render());
|
|
900
|
-
}
|
|
901
|
-
if (bip32Derivation) {
|
|
902
|
-
for (const bip32 of bip32Derivation) {
|
|
903
|
-
bw.writeString(KeyType.PSBT_IN_BIP32_DERIVATION, "hex");
|
|
904
|
-
bw.writeBytes(bip32.pubkey);
|
|
905
|
-
const key = bw.render().toString("hex");
|
|
906
|
-
bw.writeBytes(bip32.masterFingerprint);
|
|
907
|
-
bw.writeBytes(parseDerivationPathNodesToBytes(bip32.path));
|
|
908
|
-
map.set(key, bw.render());
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
this.PSBT_GLOBAL_INPUT_COUNT = this.inputMaps.push(map);
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
public addOutput({
|
|
916
|
-
amount,
|
|
917
|
-
script,
|
|
918
|
-
redeemScript,
|
|
919
|
-
witnessScript,
|
|
920
|
-
bip32Derivation,
|
|
921
|
-
}: {
|
|
922
|
-
amount: number;
|
|
923
|
-
script: Buffer;
|
|
924
|
-
redeemScript?: Buffer;
|
|
925
|
-
witnessScript?: Buffer;
|
|
926
|
-
bip32Derivation?: {
|
|
927
|
-
pubkey: Buffer;
|
|
928
|
-
masterFingerprint: Buffer;
|
|
929
|
-
path: string;
|
|
930
|
-
}[];
|
|
931
|
-
}) {
|
|
932
|
-
if (!this.isReadyForConstructor) {
|
|
933
|
-
throw Error(
|
|
934
|
-
"The PsbtV2 is not ready for a Constructor. Outputs cannot be added.",
|
|
935
|
-
);
|
|
936
|
-
}
|
|
937
|
-
if (!this.isModifiable([PsbtGlobalTxModifiableBits.OUTPUTS])) {
|
|
938
|
-
throw Error(
|
|
939
|
-
"PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE outputs cannot be modified.",
|
|
940
|
-
);
|
|
941
|
-
}
|
|
942
|
-
const map = new Map<Key, Value>();
|
|
943
|
-
const bw = new BufferWriter();
|
|
944
|
-
bw.writeI64(amount);
|
|
945
|
-
map.set(KeyType.PSBT_OUT_AMOUNT, bw.render());
|
|
946
|
-
bw.writeBytes(script);
|
|
947
|
-
map.set(KeyType.PSBT_OUT_SCRIPT, bw.render());
|
|
948
|
-
|
|
949
|
-
if (redeemScript) {
|
|
950
|
-
bw.writeBytes(redeemScript);
|
|
951
|
-
map.set(KeyType.PSBT_OUT_REDEEM_SCRIPT, bw.render());
|
|
952
|
-
}
|
|
953
|
-
if (witnessScript) {
|
|
954
|
-
bw.writeBytes(witnessScript);
|
|
955
|
-
map.set(KeyType.PSBT_OUT_WITNESS_SCRIPT, bw.render());
|
|
956
|
-
}
|
|
957
|
-
if (bip32Derivation) {
|
|
958
|
-
for (const bip32 of bip32Derivation) {
|
|
959
|
-
bw.writeString(KeyType.PSBT_OUT_BIP32_DERIVATION, "hex");
|
|
960
|
-
bw.writeBytes(bip32.pubkey);
|
|
961
|
-
const key = bw.render().toString("hex");
|
|
962
|
-
bw.writeBytes(bip32.masterFingerprint);
|
|
963
|
-
bw.writeBytes(parseDerivationPathNodesToBytes(bip32.path));
|
|
964
|
-
map.set(key, bw.render());
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
this.outputMaps.push(map);
|
|
969
|
-
this.PSBT_GLOBAL_OUTPUT_COUNT = this.outputMaps.length;
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
/**
|
|
973
|
-
* Updater/Signer Methods
|
|
974
|
-
*/
|
|
975
|
-
|
|
976
|
-
/**
|
|
977
|
-
* Removes an input-map from inputMaps.
|
|
978
|
-
*/
|
|
979
|
-
public deleteInput(index: number) {
|
|
980
|
-
if (!this.isReadyForConstructor) {
|
|
981
|
-
throw Error(
|
|
982
|
-
"The PsbtV2 is not ready for a Constructor. Inputs cannot be removed.",
|
|
983
|
-
);
|
|
984
|
-
}
|
|
985
|
-
if (!this.isModifiable([PsbtGlobalTxModifiableBits.INPUTS])) {
|
|
986
|
-
throw Error(
|
|
987
|
-
"PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE inputs cannot be modified.",
|
|
988
|
-
);
|
|
989
|
-
}
|
|
990
|
-
const newInputs = this.inputMaps.filter((_, i) => i !== index);
|
|
991
|
-
this.inputMaps = newInputs;
|
|
992
|
-
this.PSBT_GLOBAL_INPUT_COUNT = this.inputMaps.length;
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
/**
|
|
996
|
-
* Removes an output-map from outputMaps.
|
|
997
|
-
*/
|
|
998
|
-
public deleteOutput(index: number) {
|
|
999
|
-
if (!this.isReadyForConstructor) {
|
|
1000
|
-
throw Error(
|
|
1001
|
-
"The PsbtV2 is not ready for a Constructor. Outputs cannot be removed.",
|
|
1002
|
-
);
|
|
1003
|
-
}
|
|
1004
|
-
if (!this.isModifiable([PsbtGlobalTxModifiableBits.OUTPUTS])) {
|
|
1005
|
-
throw Error(
|
|
1006
|
-
"PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE outputs cannot be modified.",
|
|
1007
|
-
);
|
|
1008
|
-
// Alternatively, an output could be removed, but depending on the sighash
|
|
1009
|
-
// flags for each signature, it might prompt removing all sigs.
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
const newOutputs = this.outputMaps.filter((_, i) => i !== index);
|
|
1013
|
-
|
|
1014
|
-
if (this.isModifiable([PsbtGlobalTxModifiableBits.SIGHASH_SINGLE])) {
|
|
1015
|
-
// SIGHASH_SINGLE ties the input to the output, so remove input sig since
|
|
1016
|
-
// it is no longer valid.
|
|
1017
|
-
this.removePartialSig(index);
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
this.outputMaps = newOutputs;
|
|
1021
|
-
this.PSBT_GLOBAL_OUTPUT_COUNT = this.outputMaps.length;
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
/**
|
|
1025
|
-
* Checks that all provided flags are present in PSBT_GLOBAL_TX_MODIFIABLE.
|
|
1026
|
-
*/
|
|
1027
|
-
private isModifiable(flags: PsbtGlobalTxModifiableBits[]) {
|
|
1028
|
-
for (const flag of flags) {
|
|
1029
|
-
if (!this.PSBT_GLOBAL_TX_MODIFIABLE.includes(flag)) {
|
|
1030
|
-
return false;
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
return true;
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
/**
|
|
1038
|
-
* Adds a signature for an input. Validates that the input is mapped and does
|
|
1039
|
-
* not already have a signature for the pubkey. Also validates for sighash.
|
|
1040
|
-
* Other validation is incomplete. Also validates for required args in case
|
|
1041
|
-
* typescript is not being used to call the method.
|
|
1042
|
-
*
|
|
1043
|
-
* The Signer, when it creates a signature, must add the partial sig keypair
|
|
1044
|
-
* to the psbt for the input which it is signing. In the case that a
|
|
1045
|
-
* particular signer does not, this method can be used to add a signature to
|
|
1046
|
-
* the psbt. This method assumes the Signer did the validation outlined in
|
|
1047
|
-
* BIP0174 before creating a signature.
|
|
1048
|
-
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer
|
|
1049
|
-
*/
|
|
1050
|
-
public addPartialSig(inputIndex: number, pubkey: Buffer, sig: Buffer) {
|
|
1051
|
-
if (!this.isReadyForSigner) {
|
|
1052
|
-
throw Error(
|
|
1053
|
-
"The PsbtV2 is not ready for a Signer. Partial sigs cannot be added.",
|
|
1054
|
-
);
|
|
1055
|
-
}
|
|
1056
|
-
if (!this.inputMaps[inputIndex]) {
|
|
1057
|
-
throw Error(`PsbtV2 has no input at ${inputIndex}`);
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
if (!pubkey || !sig) {
|
|
1061
|
-
throw Error(
|
|
1062
|
-
`PsbtV2.addPartialSig() missing argument ${
|
|
1063
|
-
(!pubkey && "pubkey") || (!sig && "sig")
|
|
1064
|
-
}`,
|
|
1065
|
-
);
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
const key = `${KeyType.PSBT_IN_PARTIAL_SIG}${pubkey.toString("hex")}`;
|
|
1069
|
-
if (this.inputMaps[inputIndex].has(key)) {
|
|
1070
|
-
throw Error(
|
|
1071
|
-
"PsbtV2 already has a signature for this input with this pubkey",
|
|
1072
|
-
);
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
const modBackup = this.PSBT_GLOBAL_TX_MODIFIABLE;
|
|
1076
|
-
try {
|
|
1077
|
-
this.inputMaps[inputIndex].set(key, sig);
|
|
1078
|
-
this.handleSighashType(sig);
|
|
1079
|
-
} catch (err) {
|
|
1080
|
-
console.error(err);
|
|
1081
|
-
// To remain atomic, attempt to reset everything to the way it was.
|
|
1082
|
-
this.inputMaps[inputIndex].delete(key);
|
|
1083
|
-
this.PSBT_GLOBAL_TX_MODIFIABLE = modBackup;
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
/**
|
|
1088
|
-
* Removes all sigs for an input unless a pubkey is specified. Validates that
|
|
1089
|
-
* the input exists. When providing a pubkey, this validates that a sig for
|
|
1090
|
-
* the pubkey exists.
|
|
1091
|
-
*/
|
|
1092
|
-
public removePartialSig(inputIndex: number, pubkey?: Buffer) {
|
|
1093
|
-
// TODO: What role is allowed to remove a partial sig? Perform that
|
|
1094
|
-
// role-check validation here.
|
|
1095
|
-
const input = this.inputMaps[inputIndex];
|
|
1096
|
-
|
|
1097
|
-
if (!input) {
|
|
1098
|
-
throw Error(`PsbtV2 has no input at ${inputIndex}`);
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
if (pubkey) {
|
|
1102
|
-
// Pubkey has been provided to remove a specific sig on the input.
|
|
1103
|
-
const key = `${KeyType.PSBT_IN_PARTIAL_SIG}${pubkey.toString("hex")}`;
|
|
1104
|
-
const sig = this.PSBT_IN_PARTIAL_SIG[inputIndex].find(
|
|
1105
|
-
(el) => el.key === key,
|
|
1106
|
-
);
|
|
1107
|
-
|
|
1108
|
-
if (!sig) {
|
|
1109
|
-
throw Error(
|
|
1110
|
-
`PsbtV2 input has no signature from pubkey ${pubkey.toString("hex")}`,
|
|
1111
|
-
);
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
input.delete(key);
|
|
1115
|
-
} else {
|
|
1116
|
-
// Remove all sigs on an input.
|
|
1117
|
-
const sigs = this.PSBT_IN_PARTIAL_SIG[inputIndex];
|
|
1118
|
-
for (const sig of sigs) {
|
|
1119
|
-
input.delete(sig.key);
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
/**
|
|
1125
|
-
* Sets values on the proprietary keytype for a global, input, or output map.
|
|
1126
|
-
* BIP 174 allows for proprietary values to be set on all maps with the
|
|
1127
|
-
* keytype `0xFC`. This method sets byte data to key values defined by the
|
|
1128
|
-
* args.
|
|
1129
|
-
*
|
|
1130
|
-
* Args:
|
|
1131
|
-
* - `mapSelector` selects which map to set the proprietary value. If this
|
|
1132
|
-
* value is not `"global"`, then a tuple must be provided with `"inputs"` or
|
|
1133
|
-
* `"outputs"` as the first element and the index `number` on the second
|
|
1134
|
-
* element representing which input or output map to set the value to. An
|
|
1135
|
-
* example looks like `["inputs", 0]`. If the map name doesn't match, the
|
|
1136
|
-
* values will be set to the global map. If the index is missing on
|
|
1137
|
-
* `"inputs"` or `"outputs"`, then it will throw.
|
|
1138
|
-
* - `identifier` should be the bytes identifier for the set of proprietary
|
|
1139
|
-
* keytypes.
|
|
1140
|
-
* - `subkeyType` accepts bytes proprietary keytype.
|
|
1141
|
-
* - `subkeyData` accepts bytes proprietary keydata.
|
|
1142
|
-
* - `valueData` accepts bytes which will be written as the proprietary value.
|
|
1143
|
-
*
|
|
1144
|
-
* From the provided args, a key with the following format will be generated:
|
|
1145
|
-
* `0xFC<compact uint identifier length><bytes identifier><bytes
|
|
1146
|
-
* subtype><bytes subkeydata>`
|
|
1147
|
-
*/
|
|
1148
|
-
public setProprietaryValue(
|
|
1149
|
-
mapSelector: MapSelectorType,
|
|
1150
|
-
identifier: Buffer,
|
|
1151
|
-
subkeyType: Buffer,
|
|
1152
|
-
subkeyData: Buffer,
|
|
1153
|
-
valueData: Buffer,
|
|
1154
|
-
) {
|
|
1155
|
-
if (
|
|
1156
|
-
(mapSelector[0] === "inputs" || mapSelector[0] === "outputs") &&
|
|
1157
|
-
typeof mapSelector[1] !== "number"
|
|
1158
|
-
) {
|
|
1159
|
-
throw Error(
|
|
1160
|
-
"Must specify an index when setting proprietary values to inputs or outputs.",
|
|
1161
|
-
);
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
let classMap: Map<string, Buffer> = this.globalMap,
|
|
1165
|
-
keyType = KeyType.PSBT_GLOBAL_PROPRIETARY;
|
|
1166
|
-
if (mapSelector[0] === "inputs") {
|
|
1167
|
-
classMap = this.inputMaps[mapSelector[1]];
|
|
1168
|
-
keyType = KeyType.PSBT_IN_PROPRIETARY;
|
|
1169
|
-
} else if (mapSelector[0] === "outputs") {
|
|
1170
|
-
classMap = this.outputMaps[mapSelector[1]];
|
|
1171
|
-
keyType = KeyType.PSBT_OUT_PROPRIETARY;
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
if (!classMap) {
|
|
1175
|
-
throw Error("Map does not exist at that index.");
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
const bw = new BufferWriter();
|
|
1179
|
-
bw.writeBytes(Buffer.from(keyType, "hex"));
|
|
1180
|
-
bw.writeU8(identifier.length);
|
|
1181
|
-
bw.writeBytes(identifier);
|
|
1182
|
-
bw.writeBytes(subkeyType);
|
|
1183
|
-
bw.writeBytes(subkeyData);
|
|
1184
|
-
const key = bw.render().toString("hex");
|
|
1185
|
-
bw.writeBytes(valueData);
|
|
1186
|
-
const value = bw.render();
|
|
1187
|
-
classMap.set(key, value);
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
/**
|
|
1191
|
-
* Ensures the PSBT is in the proper state when adding a partial sig keypair.
|
|
1192
|
-
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#signer
|
|
1193
|
-
*/
|
|
1194
|
-
private handleSighashType(sig: Buffer) {
|
|
1195
|
-
const br = new BufferReader(sig.slice(-1));
|
|
1196
|
-
let sighashVal = br.readU8();
|
|
1197
|
-
let modifiable = this.PSBT_GLOBAL_TX_MODIFIABLE;
|
|
1198
|
-
|
|
1199
|
-
if (!(sighashVal & SighashType.SIGHASH_ANYONECANPAY)) {
|
|
1200
|
-
modifiable = modifiable.filter(
|
|
1201
|
-
(val) => val !== PsbtGlobalTxModifiableBits.INPUTS,
|
|
1202
|
-
);
|
|
1203
|
-
} else {
|
|
1204
|
-
// Unset SIGHASH_ANYONECANPAY bit for simpler comparisons
|
|
1205
|
-
sighashVal ^= SighashType.SIGHASH_ANYONECANPAY;
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
// Can't use bitwise the whole way because SIGHASH_SINGLE is a 3.
|
|
1209
|
-
if (sighashVal !== SighashType.SIGHASH_NONE) {
|
|
1210
|
-
modifiable = modifiable.filter(
|
|
1211
|
-
(val) => val !== PsbtGlobalTxModifiableBits.OUTPUTS,
|
|
1212
|
-
);
|
|
1213
|
-
}
|
|
1214
|
-
if (
|
|
1215
|
-
sighashVal === SighashType.SIGHASH_SINGLE &&
|
|
1216
|
-
!modifiable.includes(PsbtGlobalTxModifiableBits.SIGHASH_SINGLE)
|
|
1217
|
-
) {
|
|
1218
|
-
modifiable.push(PsbtGlobalTxModifiableBits.SIGHASH_SINGLE);
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
this.PSBT_GLOBAL_TX_MODIFIABLE = modifiable;
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
/**
|
|
1225
|
-
* Attempts to return a PsbtV2 by converting from a PsbtV0 string or Buffer.
|
|
1226
|
-
*
|
|
1227
|
-
* This method first starts with a fresh PsbtV2 having just been created. It
|
|
1228
|
-
* then takes the PsbtV2 through its operator saga through the Signer role. In
|
|
1229
|
-
* this sense validation for each operator role will be performed.
|
|
1230
|
-
*/
|
|
1231
|
-
static FromV0(psbt: string | Buffer, allowTxnVersion1 = false): PsbtV2 {
|
|
1232
|
-
const psbtv0Buf = bufferize(psbt);
|
|
1233
|
-
const psbtv0 = Psbt.fromBuffer(psbtv0Buf);
|
|
1234
|
-
const psbtv0GlobalMap = psbtv0.data.globalMap;
|
|
1235
|
-
|
|
1236
|
-
// Creator Role
|
|
1237
|
-
const psbtv2 = new PsbtV2();
|
|
1238
|
-
const txVersion = psbtv0.data.getTransaction().readInt32LE(0);
|
|
1239
|
-
if (txVersion === 1 && allowTxnVersion1) {
|
|
1240
|
-
psbtv2.dangerouslySetGlobalTxVersion1();
|
|
1241
|
-
} else {
|
|
1242
|
-
psbtv2.PSBT_GLOBAL_TX_VERSION = psbtv0.data
|
|
1243
|
-
.getTransaction()
|
|
1244
|
-
.readInt32LE(0);
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
// Constructor Role
|
|
1248
|
-
for (const globalXpub of psbtv0GlobalMap.globalXpub ?? []) {
|
|
1249
|
-
psbtv2.addGlobalXpub(
|
|
1250
|
-
globalXpub.extendedPubkey,
|
|
1251
|
-
globalXpub.masterFingerprint,
|
|
1252
|
-
globalXpub.path,
|
|
1253
|
-
);
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
const txInputs: any = [];
|
|
1257
|
-
for (const [index, txInput] of psbtv0.txInputs.entries()) {
|
|
1258
|
-
txInputs[index] = txInput;
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
for (const [index, input] of psbtv0.data.inputs.entries()) {
|
|
1262
|
-
const txInput = txInputs[index];
|
|
1263
|
-
psbtv2.addInput({
|
|
1264
|
-
previousTxId: txInput.hash,
|
|
1265
|
-
outputIndex: txInput.index,
|
|
1266
|
-
sequence: txInput.sequence,
|
|
1267
|
-
nonWitnessUtxo: input.nonWitnessUtxo,
|
|
1268
|
-
witnessUtxo: input.witnessUtxo && {
|
|
1269
|
-
amount: input.witnessUtxo.value,
|
|
1270
|
-
script: input.witnessUtxo.script,
|
|
1271
|
-
},
|
|
1272
|
-
redeemScript: input.redeemScript,
|
|
1273
|
-
witnessScript: input.witnessScript,
|
|
1274
|
-
bip32Derivation: input.bip32Derivation,
|
|
1275
|
-
});
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
const txOutputs: any = [];
|
|
1279
|
-
for (const [index, txOutput] of psbtv0.txOutputs.entries()) {
|
|
1280
|
-
txOutputs[index] = txOutput;
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
for (const [index, output] of psbtv0.data.outputs.entries()) {
|
|
1284
|
-
const txOutput = txOutputs[index];
|
|
1285
|
-
psbtv2.addOutput({
|
|
1286
|
-
amount: txOutput.value,
|
|
1287
|
-
script: txOutput.script,
|
|
1288
|
-
redeemScript: output.redeemScript,
|
|
1289
|
-
witnessScript: output.witnessScript,
|
|
1290
|
-
bip32Derivation: output.bip32Derivation,
|
|
1291
|
-
});
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
// Signer Role
|
|
1295
|
-
|
|
1296
|
-
// Finally, add partialSigs to inputs. This has to be performed last since
|
|
1297
|
-
// it may change PSBT_GLOBAL_TX_MODIFIABLE preventing inputs or outputs from
|
|
1298
|
-
// being added.
|
|
1299
|
-
for (const [index, input] of psbtv0.data.inputs.entries()) {
|
|
1300
|
-
for (const sig of input.partialSig || []) {
|
|
1301
|
-
psbtv2.addPartialSig(index, sig.pubkey, sig.signature);
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
return psbtv2;
|
|
1306
|
-
}
|
|
1307
|
-
}
|