@caravan/psbt 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +19 -20
- package/.turbo/turbo-lint.log +54 -49
- package/.turbo/turbo-test.log +99 -196
- package/CHANGELOG.md +12 -0
- package/README.md +71 -16
- package/dist/index.d.mts +172 -25
- package/dist/index.d.ts +172 -25
- package/dist/index.js +268 -75
- package/dist/index.mjs +274 -76
- package/package.json +1 -1
- package/src/psbtv2/functions.ts +169 -0
- package/src/psbtv2/index.ts +3 -0
- package/src/{psbtv2.test.ts → psbtv2/psbtv2.test.ts} +175 -26
- package/src/{psbtv2.ts → psbtv2/psbtv2.ts} +289 -401
- package/src/psbtv2/psbtv2maps.ts +111 -0
- package/src/psbtv2/types.ts +85 -0
- package/src/psbtv2/values.ts +4 -0
- package/.turbo/turbo-ci.log +0 -258
- package/.turbo/turbo-test$colon$watch.log +0 -223
|
@@ -1,3 +1,23 @@
|
|
|
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
|
+
} from "./types";
|
|
12
|
+
import {
|
|
13
|
+
bufferize,
|
|
14
|
+
getNonUniqueKeyTypeValues,
|
|
15
|
+
getOptionalMappedBytesAsHex,
|
|
16
|
+
getOptionalMappedBytesAsUInt,
|
|
17
|
+
parseDerivationPathNodesToBytes,
|
|
18
|
+
} from "./functions";
|
|
19
|
+
import { PsbtV2Maps } from "./psbtv2maps";
|
|
20
|
+
|
|
1
21
|
/**
|
|
2
22
|
* The PsbtV2 class is intended to represent an easily modifiable and
|
|
3
23
|
* serializable psbt of version 2 conforming to BIP0174. Getters exist for all
|
|
@@ -8,370 +28,6 @@
|
|
|
8
28
|
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
|
|
9
29
|
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki
|
|
10
30
|
*/
|
|
11
|
-
|
|
12
|
-
import { BufferReader, BufferWriter } from "bufio";
|
|
13
|
-
import { Psbt } from "bitcoinjs-lib";
|
|
14
|
-
|
|
15
|
-
import { validateHex, validBase64, validateBIP32Path } from "@caravan/bitcoin";
|
|
16
|
-
import { PSBT_MAGIC_BYTES } from "./psbt";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Global Types
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Hex encoded string containing `<keytype><keydata>`. A string is needed for
|
|
24
|
-
* Map.get() since it matches by identity. Most commonly, a `Key` only contains a
|
|
25
|
-
* keytype byte, however, some with keydata can allow for multiple unique keys
|
|
26
|
-
* of the same type.
|
|
27
|
-
*/
|
|
28
|
-
type Key = string;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Values can be of various different types or formats. Here we leave them as
|
|
32
|
-
* Buffers so that getters can decide how they should be formatted.
|
|
33
|
-
*/
|
|
34
|
-
type Value = Buffer;
|
|
35
|
-
|
|
36
|
-
type NonUniqueKeyTypeValue = { key: string; value: string | null };
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* KeyTypes are hex bytes, but within this module are used as string enums to
|
|
40
|
-
* assist in Map lookups. See type `Key` for more info.
|
|
41
|
-
*/
|
|
42
|
-
//eslint-disable-next-line no-shadow
|
|
43
|
-
enum KeyType {
|
|
44
|
-
PSBT_GLOBAL_XPUB = "01",
|
|
45
|
-
PSBT_GLOBAL_TX_VERSION = "02",
|
|
46
|
-
PSBT_GLOBAL_FALLBACK_LOCKTIME = "03",
|
|
47
|
-
PSBT_GLOBAL_INPUT_COUNT = "04",
|
|
48
|
-
PSBT_GLOBAL_OUTPUT_COUNT = "05",
|
|
49
|
-
PSBT_GLOBAL_TX_MODIFIABLE = "06",
|
|
50
|
-
PSBT_GLOBAL_VERSION = "fb",
|
|
51
|
-
PSBT_GLOBAL_PROPRIETARY = "fc",
|
|
52
|
-
|
|
53
|
-
PSBT_IN_NON_WITNESS_UTXO = "00",
|
|
54
|
-
PSBT_IN_WITNESS_UTXO = "01",
|
|
55
|
-
PSBT_IN_PARTIAL_SIG = "02",
|
|
56
|
-
PSBT_IN_SIGHASH_TYPE = "03",
|
|
57
|
-
PSBT_IN_REDEEM_SCRIPT = "04",
|
|
58
|
-
PSBT_IN_WITNESS_SCRIPT = "05",
|
|
59
|
-
PSBT_IN_BIP32_DERIVATION = "06",
|
|
60
|
-
PSBT_IN_FINAL_SCRIPTSIG = "07",
|
|
61
|
-
PSBT_IN_FINAL_SCRIPTWITNESS = "08",
|
|
62
|
-
PSBT_IN_POR_COMMITMENT = "09",
|
|
63
|
-
PSBT_IN_RIPEMD160 = "0a",
|
|
64
|
-
PSBT_IN_SHA256 = "0b",
|
|
65
|
-
PSBT_IN_HASH160 = "0c",
|
|
66
|
-
PSBT_IN_HASH256 = "0d",
|
|
67
|
-
PSBT_IN_PREVIOUS_TXID = "0e",
|
|
68
|
-
PSBT_IN_OUTPUT_INDEX = "0f",
|
|
69
|
-
PSBT_IN_SEQUENCE = "10",
|
|
70
|
-
PSBT_IN_REQUIRED_TIME_LOCKTIME = "11",
|
|
71
|
-
PSBT_IN_REQUIRED_HEIGHT_LOCKTIME = "12",
|
|
72
|
-
PSBT_IN_TAP_KEY_SIG = "13",
|
|
73
|
-
PSBT_IN_TAP_SCRIPT_SIG = "14",
|
|
74
|
-
PSBT_IN_TAP_LEAF_SCRIPT = "15",
|
|
75
|
-
PSBT_IN_TAP_BIP32_DERIVATION = "16",
|
|
76
|
-
PSBT_IN_TAP_INTERNAL_KEY = "17",
|
|
77
|
-
PSBT_IN_TAP_MERKLE_ROOT = "18",
|
|
78
|
-
PSBT_IN_PROPRIETARY = "fc",
|
|
79
|
-
|
|
80
|
-
PSBT_OUT_REDEEM_SCRIPT = "00",
|
|
81
|
-
PSBT_OUT_WITNESS_SCRIPT = "01",
|
|
82
|
-
PSBT_OUT_BIP32_DERIVATION = "02",
|
|
83
|
-
PSBT_OUT_AMOUNT = "03",
|
|
84
|
-
PSBT_OUT_SCRIPT = "04",
|
|
85
|
-
PSBT_OUT_TAP_INTERNAL_KEY = "05",
|
|
86
|
-
PSBT_OUT_TAP_TREE = "06",
|
|
87
|
-
PSBT_OUT_TAP_BIP32_DERIVATION = "07",
|
|
88
|
-
PSBT_OUT_PROPRIETARY = "fc",
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Provided to friendly-format the `PSBT_GLOBAL_TX_MODIFIABLE` bitmask from
|
|
93
|
-
* `PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE` which returns
|
|
94
|
-
* `PsbtGlobalTxModifiableBits[]`.
|
|
95
|
-
*/
|
|
96
|
-
// eslint-disable-next-line no-shadow
|
|
97
|
-
enum PsbtGlobalTxModifiableBits {
|
|
98
|
-
INPUTS = "INPUTS", // 0b00000001
|
|
99
|
-
OUTPUTS = "OUTPUTS", // 0b00000010
|
|
100
|
-
SIGHASH_SINGLE = "SIGHASH_SINGLE", // 0b00000100
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// eslint-disable-next-line no-shadow
|
|
104
|
-
enum SighashType {
|
|
105
|
-
SIGHASH_ALL = 0x01,
|
|
106
|
-
SIGHASH_NONE = 0x02,
|
|
107
|
-
SIGHASH_SINGLE = 0x03,
|
|
108
|
-
SIGHASH_ANYONECANPAY = 0x80,
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Global Constants
|
|
113
|
-
*/
|
|
114
|
-
|
|
115
|
-
const PSBT_MAP_SEPARATOR = Buffer.from([0x00]);
|
|
116
|
-
const BIP_32_NODE_REGEX = /(\/[0-9]+'?)/gi;
|
|
117
|
-
const BIP_32_HARDENING_OFFSET = 0x80000000;
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Helper Functions
|
|
121
|
-
*/
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Ensure base64 and hex strings are a buffer. No-op if already a buffer.
|
|
125
|
-
*/
|
|
126
|
-
function bufferize(psbt: string | Buffer): Buffer {
|
|
127
|
-
if (Buffer.isBuffer(psbt)) {
|
|
128
|
-
return psbt;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (typeof psbt === "string") {
|
|
132
|
-
if (validateHex(psbt) === "") {
|
|
133
|
-
return Buffer.from(psbt, "hex");
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (validBase64(psbt)) {
|
|
137
|
-
return Buffer.from(psbt, "base64");
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
throw Error("Input cannot be bufferized.");
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Some keytypes have keydata which allows for multiple unique keys of the same
|
|
146
|
-
* keytype. Getters which return values from these keys should search and return
|
|
147
|
-
* values from all keys of that keytype. This function matches on the first byte
|
|
148
|
-
* of each key string (hex encoded) and returns all values associated with those
|
|
149
|
-
* keys as an array of string (hex encoded) values.
|
|
150
|
-
*/
|
|
151
|
-
function getNonUniqueKeyTypeValues(
|
|
152
|
-
maps: Map<Key, Value> | Map<Key, Value>[],
|
|
153
|
-
keytype: KeyType,
|
|
154
|
-
) {
|
|
155
|
-
if (Array.isArray(maps)) {
|
|
156
|
-
// It's a set of input or output maps, so recursively check each map and set
|
|
157
|
-
// values.
|
|
158
|
-
const values: NonUniqueKeyTypeValue[][] = maps.map(
|
|
159
|
-
(map) =>
|
|
160
|
-
// TODO: Figure out a better way to type this
|
|
161
|
-
getNonUniqueKeyTypeValues(map, keytype) as NonUniqueKeyTypeValue[],
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
return values;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const map = maps; // Not an array
|
|
168
|
-
const values: NonUniqueKeyTypeValue[] = [];
|
|
169
|
-
|
|
170
|
-
for (const [key, value] of map.entries()) {
|
|
171
|
-
if (key.startsWith(keytype)) {
|
|
172
|
-
values.push({ key, value: value?.toString("hex") || null });
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return values;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* A getter helper for optional keytypes which returns lists of values as hex
|
|
181
|
-
* strings.
|
|
182
|
-
*/
|
|
183
|
-
function getOptionalMappedBytesAsHex(
|
|
184
|
-
maps: Map<Key, Value>[],
|
|
185
|
-
keytype: KeyType,
|
|
186
|
-
) {
|
|
187
|
-
return maps.map((map) => map.get(keytype)?.toString("hex") ?? null);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* A getter helper for optional keytypes which returns lists of values as
|
|
192
|
-
* numbers.
|
|
193
|
-
*/
|
|
194
|
-
function getOptionalMappedBytesAsUInt(
|
|
195
|
-
maps: Map<Key, Value>[],
|
|
196
|
-
keytype: KeyType,
|
|
197
|
-
) {
|
|
198
|
-
return maps.map((map) => map.get(keytype)?.readUInt32LE(0) ?? null);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Accepts a BIP0032 path as a string and returns a Buffer containing uint32
|
|
203
|
-
* values for each path node.
|
|
204
|
-
*/
|
|
205
|
-
function parseDerivationPathNodesToBytes(path: string): Buffer {
|
|
206
|
-
const validationMessage = validateBIP32Path(path);
|
|
207
|
-
if (validationMessage !== "") {
|
|
208
|
-
throw Error(validationMessage);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const bw = new BufferWriter();
|
|
212
|
-
|
|
213
|
-
for (const node of path.match(BIP_32_NODE_REGEX) ?? []) {
|
|
214
|
-
// Skip slash and parse int
|
|
215
|
-
let num = parseInt(node.slice(1), 10);
|
|
216
|
-
|
|
217
|
-
if (node.indexOf("'") > -1) {
|
|
218
|
-
// Hardened node needs hardening
|
|
219
|
-
num += BIP_32_HARDENING_OFFSET;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
bw.writeU32(num);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return bw.render();
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Takes a BufferReader and a Map then reads keypairs until it gets to a map
|
|
230
|
-
* separator (keyLen 0x00 byte).
|
|
231
|
-
*/
|
|
232
|
-
function readAndSetKeyPairs(map: Map<Key, Buffer>, br: BufferReader) {
|
|
233
|
-
const nextByte: Buffer = br.readBytes(1);
|
|
234
|
-
if (nextByte.equals(PSBT_MAP_SEPARATOR)) {
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const keyLen = nextByte.readUInt8(0);
|
|
239
|
-
const key = br.readBytes(keyLen);
|
|
240
|
-
const value = br.readVarBytes();
|
|
241
|
-
|
|
242
|
-
map.set(key.toString("hex"), value);
|
|
243
|
-
readAndSetKeyPairs(map, br);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Serializes a Map containing keypairs, includes keylen, and writes to the
|
|
248
|
-
* BufferWriter.
|
|
249
|
-
*/
|
|
250
|
-
function serializeMap(map: Map<Key, Value>, bw: BufferWriter): void {
|
|
251
|
-
map.forEach((value, key) => {
|
|
252
|
-
// Add <keylen><keytype><keydata>
|
|
253
|
-
const keyBuf = Buffer.from(key, "hex");
|
|
254
|
-
const keyLen = keyBuf.length;
|
|
255
|
-
bw.writeVarint(keyLen);
|
|
256
|
-
bw.writeString(key, "hex");
|
|
257
|
-
|
|
258
|
-
// Add <valuelen><valuedata>
|
|
259
|
-
bw.writeVarint(value.length);
|
|
260
|
-
bw.writeBytes(value);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
bw.writeBytes(PSBT_MAP_SEPARATOR);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* This abstract class is provided for utility to allow for mapping, map
|
|
268
|
-
* copying, and serialization operations for psbts. This does almost no
|
|
269
|
-
* validation, so do not rely on it for ensuring a valid psbt.
|
|
270
|
-
*/
|
|
271
|
-
export abstract class PsbtV2Maps {
|
|
272
|
-
// These maps directly correspond to the maps defined in BIP0174
|
|
273
|
-
protected globalMap: Map<Key, Value> = new Map();
|
|
274
|
-
protected inputMaps: Map<Key, Value>[] = [];
|
|
275
|
-
protected outputMaps: Map<Key, Value>[] = [];
|
|
276
|
-
|
|
277
|
-
constructor(psbt?: Buffer | string) {
|
|
278
|
-
if (!psbt) {
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const buf = bufferize(psbt);
|
|
283
|
-
const br = new BufferReader(buf);
|
|
284
|
-
if (!br.readBytes(PSBT_MAGIC_BYTES.length, true).equals(PSBT_MAGIC_BYTES)) {
|
|
285
|
-
throw Error("PsbtV2 magic bytes are incorrect.");
|
|
286
|
-
}
|
|
287
|
-
// Build globalMap
|
|
288
|
-
readAndSetKeyPairs(this.globalMap, br);
|
|
289
|
-
if (
|
|
290
|
-
// Assuming that psbt being passed in is a valid psbtv2
|
|
291
|
-
!this.globalMap.has(KeyType.PSBT_GLOBAL_VERSION) ||
|
|
292
|
-
!this.globalMap.has(KeyType.PSBT_GLOBAL_TX_VERSION) ||
|
|
293
|
-
!this.globalMap.has(KeyType.PSBT_GLOBAL_INPUT_COUNT) ||
|
|
294
|
-
!this.globalMap.has(KeyType.PSBT_GLOBAL_OUTPUT_COUNT) ||
|
|
295
|
-
this.globalMap.has("00") // PsbtV2 must exclude key 0x00
|
|
296
|
-
) {
|
|
297
|
-
throw Error("Provided PsbtV2 not valid. Missing required global keys.");
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Build inputMaps
|
|
301
|
-
const inputCount =
|
|
302
|
-
this.globalMap.get(KeyType.PSBT_GLOBAL_INPUT_COUNT)?.readUInt8(0) ?? 0;
|
|
303
|
-
for (let i = 0; i < inputCount; i++) {
|
|
304
|
-
const map = new Map<Key, Value>();
|
|
305
|
-
readAndSetKeyPairs(map, br);
|
|
306
|
-
this.inputMaps.push(map);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Build outputMaps
|
|
310
|
-
const outputCount =
|
|
311
|
-
this.globalMap.get(KeyType.PSBT_GLOBAL_OUTPUT_COUNT)?.readUInt8(0) ?? 0;
|
|
312
|
-
for (let i = 0; i < outputCount; i++) {
|
|
313
|
-
const map = new Map<Key, Value>();
|
|
314
|
-
readAndSetKeyPairs(map, br);
|
|
315
|
-
this.outputMaps.push(map);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Return the current state of the psbt as a string in the specified format.
|
|
321
|
-
*/
|
|
322
|
-
public serialize(format: "base64" | "hex" = "base64"): string {
|
|
323
|
-
// Build hex string from maps
|
|
324
|
-
let bw = new BufferWriter();
|
|
325
|
-
bw.writeBytes(PSBT_MAGIC_BYTES);
|
|
326
|
-
serializeMap(this.globalMap, bw);
|
|
327
|
-
|
|
328
|
-
for (const map of this.inputMaps) {
|
|
329
|
-
serializeMap(map, bw);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
for (const map of this.outputMaps) {
|
|
333
|
-
serializeMap(map, bw);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return bw.render().toString(format);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Copies the maps in this PsbtV2 object to another PsbtV2 object.
|
|
341
|
-
*
|
|
342
|
-
* NOTE: This copy method is made available to achieve parity with the PSBT
|
|
343
|
-
* api required by `ledger-bitcoin` for creating merklized PSBTs. HOWEVER, it
|
|
344
|
-
* is not recommended to use this when avoidable as copying maps bypasses the
|
|
345
|
-
* validation defined in the constructor, so it could create a psbtv2 in an
|
|
346
|
-
* invalid psbt state. PsbtV2.serialize is preferable whenever possible.
|
|
347
|
-
*/
|
|
348
|
-
public copy(to: PsbtV2) {
|
|
349
|
-
this.copyMap(this.globalMap, to.globalMap);
|
|
350
|
-
this.copyMaps(this.inputMaps, to.inputMaps);
|
|
351
|
-
this.copyMaps(this.outputMaps, to.outputMaps);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
private copyMaps(
|
|
355
|
-
from: readonly ReadonlyMap<string, Buffer>[],
|
|
356
|
-
to: Map<string, Buffer>[],
|
|
357
|
-
) {
|
|
358
|
-
from.forEach((m, index) => {
|
|
359
|
-
const to_index = new Map<Key, Value>();
|
|
360
|
-
this.copyMap(m, to_index);
|
|
361
|
-
to[index] = to_index;
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// eslint-disable-next-line class-methods-use-this
|
|
366
|
-
private copyMap(from: ReadonlyMap<string, Buffer>, to: Map<string, Buffer>) {
|
|
367
|
-
from.forEach((v, k) => to.set(k, Buffer.from(v)));
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Provides utilities for working with v2 PSBTs and tranlations or conversions
|
|
373
|
-
* between v0 and v2.
|
|
374
|
-
*/
|
|
375
31
|
export class PsbtV2 extends PsbtV2Maps {
|
|
376
32
|
constructor(psbt?: Buffer | string) {
|
|
377
33
|
super(psbt);
|
|
@@ -388,7 +44,10 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
388
44
|
*/
|
|
389
45
|
|
|
390
46
|
get PSBT_GLOBAL_XPUB() {
|
|
391
|
-
return getNonUniqueKeyTypeValues(
|
|
47
|
+
return getNonUniqueKeyTypeValues(
|
|
48
|
+
this.globalMap,
|
|
49
|
+
KeyType.PSBT_GLOBAL_XPUB,
|
|
50
|
+
) as NonUniqueKeyTypeValue[];
|
|
392
51
|
}
|
|
393
52
|
|
|
394
53
|
get PSBT_GLOBAL_TX_VERSION() {
|
|
@@ -533,7 +192,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
533
192
|
return getNonUniqueKeyTypeValues(
|
|
534
193
|
this.globalMap,
|
|
535
194
|
KeyType.PSBT_GLOBAL_PROPRIETARY,
|
|
536
|
-
);
|
|
195
|
+
) as NonUniqueKeyTypeValue[];
|
|
537
196
|
}
|
|
538
197
|
|
|
539
198
|
/**
|
|
@@ -554,7 +213,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
554
213
|
);
|
|
555
214
|
}
|
|
556
215
|
|
|
557
|
-
get PSBT_IN_PARTIAL_SIG()
|
|
216
|
+
get PSBT_IN_PARTIAL_SIG() {
|
|
558
217
|
return getNonUniqueKeyTypeValues(
|
|
559
218
|
this.inputMaps,
|
|
560
219
|
KeyType.PSBT_IN_PARTIAL_SIG,
|
|
@@ -586,7 +245,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
586
245
|
return getNonUniqueKeyTypeValues(
|
|
587
246
|
this.inputMaps,
|
|
588
247
|
KeyType.PSBT_IN_BIP32_DERIVATION,
|
|
589
|
-
);
|
|
248
|
+
) as NonUniqueKeyTypeValue[][];
|
|
590
249
|
}
|
|
591
250
|
|
|
592
251
|
get PSBT_IN_FINAL_SCRIPTSIG() {
|
|
@@ -611,19 +270,31 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
611
270
|
}
|
|
612
271
|
|
|
613
272
|
get PSBT_IN_RIPEMD160() {
|
|
614
|
-
return getNonUniqueKeyTypeValues(
|
|
273
|
+
return getNonUniqueKeyTypeValues(
|
|
274
|
+
this.inputMaps,
|
|
275
|
+
KeyType.PSBT_IN_RIPEMD160,
|
|
276
|
+
) as NonUniqueKeyTypeValue[][];
|
|
615
277
|
}
|
|
616
278
|
|
|
617
279
|
get PSBT_IN_SHA256() {
|
|
618
|
-
return getNonUniqueKeyTypeValues(
|
|
280
|
+
return getNonUniqueKeyTypeValues(
|
|
281
|
+
this.inputMaps,
|
|
282
|
+
KeyType.PSBT_IN_SHA256,
|
|
283
|
+
) as NonUniqueKeyTypeValue[][];
|
|
619
284
|
}
|
|
620
285
|
|
|
621
286
|
get PSBT_IN_HASH160() {
|
|
622
|
-
return getNonUniqueKeyTypeValues(
|
|
287
|
+
return getNonUniqueKeyTypeValues(
|
|
288
|
+
this.inputMaps,
|
|
289
|
+
KeyType.PSBT_IN_HASH160,
|
|
290
|
+
) as NonUniqueKeyTypeValue[][];
|
|
623
291
|
}
|
|
624
292
|
|
|
625
293
|
get PSBT_IN_HASH256() {
|
|
626
|
-
return getNonUniqueKeyTypeValues(
|
|
294
|
+
return getNonUniqueKeyTypeValues(
|
|
295
|
+
this.inputMaps,
|
|
296
|
+
KeyType.PSBT_IN_HASH256,
|
|
297
|
+
) as NonUniqueKeyTypeValue[][];
|
|
627
298
|
}
|
|
628
299
|
|
|
629
300
|
get PSBT_IN_PREVIOUS_TXID() {
|
|
@@ -682,21 +353,21 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
682
353
|
return getNonUniqueKeyTypeValues(
|
|
683
354
|
this.inputMaps,
|
|
684
355
|
KeyType.PSBT_IN_TAP_SCRIPT_SIG,
|
|
685
|
-
);
|
|
356
|
+
) as NonUniqueKeyTypeValue[][];
|
|
686
357
|
}
|
|
687
358
|
|
|
688
359
|
get PSBT_IN_TAP_LEAF_SCRIPT() {
|
|
689
360
|
return getNonUniqueKeyTypeValues(
|
|
690
361
|
this.inputMaps,
|
|
691
362
|
KeyType.PSBT_IN_TAP_LEAF_SCRIPT,
|
|
692
|
-
);
|
|
363
|
+
) as NonUniqueKeyTypeValue[][];
|
|
693
364
|
}
|
|
694
365
|
|
|
695
366
|
get PSBT_IN_TAP_BIP32_DERIVATION() {
|
|
696
367
|
return getNonUniqueKeyTypeValues(
|
|
697
368
|
this.inputMaps,
|
|
698
369
|
KeyType.PSBT_IN_TAP_BIP32_DERIVATION,
|
|
699
|
-
);
|
|
370
|
+
) as NonUniqueKeyTypeValue[][];
|
|
700
371
|
}
|
|
701
372
|
|
|
702
373
|
get PSBT_IN_TAP_INTERNAL_KEY() {
|
|
@@ -799,6 +470,191 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
799
470
|
);
|
|
800
471
|
}
|
|
801
472
|
|
|
473
|
+
/**
|
|
474
|
+
* Operator Role Validation Getters
|
|
475
|
+
*/
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Returns true if the PsbtV2 is ready for an operator taking the Constructor
|
|
479
|
+
* role.
|
|
480
|
+
*
|
|
481
|
+
* This check assumes that the Creator used this class's constructor method to
|
|
482
|
+
* initialize the PsbtV2 without passing a psbt (constructor defaults were
|
|
483
|
+
* set).
|
|
484
|
+
*/
|
|
485
|
+
get isReadyForConstructor() {
|
|
486
|
+
// The Creator role (likely via the class constructor) must ensure at least
|
|
487
|
+
// the following value has been initialized. The psbt cannot be passed to
|
|
488
|
+
// the Constructor until it is set.
|
|
489
|
+
if (this.PSBT_GLOBAL_FALLBACK_LOCKTIME === null) {
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// At least inputs or outputs must still be modifiable.
|
|
494
|
+
if (
|
|
495
|
+
!this.isModifiable([PsbtGlobalTxModifiableBits.INPUTS]) &&
|
|
496
|
+
!this.isModifiable([PsbtGlobalTxModifiableBits.OUTPUTS])
|
|
497
|
+
) {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Returns true if the PsbtV2 is ready for an operator taking the Updater
|
|
506
|
+
* role.
|
|
507
|
+
*
|
|
508
|
+
* Before signatures are added, but after an input is added, a PsbtV2 is
|
|
509
|
+
* likely to be ready for Constructor, ready for Updater, and ready for Signer
|
|
510
|
+
* simultaneously.
|
|
511
|
+
*
|
|
512
|
+
* According to BIP370, the Updater can modify the sequence number, but it is
|
|
513
|
+
* unclear if the Updater retains permissions provided in psbtv0 (BIP174). It
|
|
514
|
+
* is likely not the case that the Updater has the same permissions as
|
|
515
|
+
* previously because it seems to now be the realm of the Constructor to add
|
|
516
|
+
* inputs and outputs.
|
|
517
|
+
*/
|
|
518
|
+
get isReadyForUpdater() {
|
|
519
|
+
// In psbtv2, the Updater can set the sequence number, but an input must
|
|
520
|
+
// exist for this to be set.
|
|
521
|
+
if (this.PSBT_GLOBAL_INPUT_COUNT === 0) {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Inputs must still be modifiable
|
|
526
|
+
if (!this.isModifiable([PsbtGlobalTxModifiableBits.INPUTS])) {
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Returns true if the PsbtV2 is ready for an operator taking the Signer role.
|
|
535
|
+
*/
|
|
536
|
+
get isReadyForSigner() {
|
|
537
|
+
// An input must exist before it can be signed.
|
|
538
|
+
if (this.PSBT_GLOBAL_INPUT_COUNT === 0) {
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// TODO: Maybe it makes sense to more granularly check if the psbt is fully
|
|
543
|
+
// signed or has minimum signatures. Until then, just check that sigs have
|
|
544
|
+
// not been finalized.
|
|
545
|
+
if (this.isReadyForTransactionExtractor) {
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Returns true if the PsbtV2 is ready for an operator taking the Combiner
|
|
554
|
+
* role.
|
|
555
|
+
*/
|
|
556
|
+
get isReadyForCombiner() {
|
|
557
|
+
// The combiner can potentially provide everything that's missing when
|
|
558
|
+
// merging another psbt. If it's at least ready for updates from the
|
|
559
|
+
// following roles, then it's ready for a Combiner.
|
|
560
|
+
return (
|
|
561
|
+
this.isReadyForConstructor ||
|
|
562
|
+
this.isReadyForUpdater ||
|
|
563
|
+
this.isReadyForSigner
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Unimplemented. Returns false.
|
|
569
|
+
*/
|
|
570
|
+
get isReadyForInputFinalizer() {
|
|
571
|
+
// Checks to see if the psbt contains everything needed to finalize inputs.
|
|
572
|
+
// This can become quite complicated considering multisig and taproot.
|
|
573
|
+
console.warn(
|
|
574
|
+
"PsbtV2.isReadyForInputFinalizer has been called, however, this getter is unimplemented and shouldn't be used.",
|
|
575
|
+
);
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Returns true if the PsbtV2 is ready for an operator taking the Transaction
|
|
581
|
+
* Extractor role.
|
|
582
|
+
*
|
|
583
|
+
* If all the inputs have been finalized, then the psbt is ready for the
|
|
584
|
+
* Transaction Extractor. According to BIP 174, it's the responsibility of the
|
|
585
|
+
* Input Finalizer to add scriptSigs or scriptWitnesses and then remove other
|
|
586
|
+
* details besides the UTXO. This getter checks that the Input Finalizer has
|
|
587
|
+
* finished its job.
|
|
588
|
+
*/
|
|
589
|
+
get isReadyForTransactionExtractor() {
|
|
590
|
+
// Iterate over all inputs
|
|
591
|
+
|
|
592
|
+
for (let i = 0; i < this.PSBT_GLOBAL_INPUT_COUNT; i++) {
|
|
593
|
+
// Check for finalized script
|
|
594
|
+
if (
|
|
595
|
+
!this.PSBT_IN_FINAL_SCRIPTSIG[i] &&
|
|
596
|
+
!this.PSBT_IN_FINAL_SCRIPTWITNESS[i]
|
|
597
|
+
) {
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Check that the corresponding UTXO is still available
|
|
602
|
+
if (
|
|
603
|
+
(this.PSBT_IN_FINAL_SCRIPTSIG[i] &&
|
|
604
|
+
!this.PSBT_IN_NON_WITNESS_UTXO[i]) ||
|
|
605
|
+
(this.PSBT_IN_FINAL_SCRIPTWITNESS[i] && !this.PSBT_IN_WITNESS_UTXO[i])
|
|
606
|
+
) {
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Check that Input Finalizer removed other values from the input.
|
|
611
|
+
//
|
|
612
|
+
// Test vectors from BIP 370 indicate that a missing PSBT_IN_OUTPUT_INDEX
|
|
613
|
+
// or PSBT_IN_PREVIOUS_TXID should be an invalid psbt, so the getters for
|
|
614
|
+
// these keys will throw unless the values are set. However, the BIP also
|
|
615
|
+
// requires that the Input Finalizer removes all other values from the
|
|
616
|
+
// input map except for the finalized scripts and UTXOs. Since removal of
|
|
617
|
+
// the above mentioned keys will result in an invalid psbt, it's decided
|
|
618
|
+
// here that it's safe to ignore the fact that those keys have not been
|
|
619
|
+
// removed.
|
|
620
|
+
if (
|
|
621
|
+
// Strings
|
|
622
|
+
this.PSBT_IN_REDEEM_SCRIPT[i] ||
|
|
623
|
+
this.PSBT_IN_WITNESS_SCRIPT[i] ||
|
|
624
|
+
this.PSBT_IN_POR_COMMITMENT[i] ||
|
|
625
|
+
this.PSBT_IN_TAP_KEY_SIG[i] ||
|
|
626
|
+
this.PSBT_IN_TAP_INTERNAL_KEY[i] ||
|
|
627
|
+
this.PSBT_IN_TAP_MERKLE_ROOT[i] ||
|
|
628
|
+
// Numbers
|
|
629
|
+
this.PSBT_IN_SIGHASH_TYPE[i] !== null ||
|
|
630
|
+
this.PSBT_IN_SEQUENCE[i] !== null ||
|
|
631
|
+
this.PSBT_IN_REQUIRED_TIME_LOCKTIME[i] !== null ||
|
|
632
|
+
this.PSBT_IN_REQUIRED_HEIGHT_LOCKTIME[i] !== null ||
|
|
633
|
+
// Arrays of non-unique keytype values
|
|
634
|
+
this.PSBT_IN_PARTIAL_SIG[i].filter((el) => el !== null).length > 0 ||
|
|
635
|
+
this.PSBT_IN_BIP32_DERIVATION[i].filter((el) => el !== null).length >
|
|
636
|
+
0 ||
|
|
637
|
+
this.PSBT_IN_RIPEMD160[i].filter((el) => el !== null).length > 0 ||
|
|
638
|
+
this.PSBT_IN_SHA256[i].filter((el) => el !== null).length > 0 ||
|
|
639
|
+
this.PSBT_IN_HASH160[i].filter((el) => el !== null).length > 0 ||
|
|
640
|
+
this.PSBT_IN_HASH256[i].filter((el) => el !== null).length > 0 ||
|
|
641
|
+
this.PSBT_IN_TAP_SCRIPT_SIG[i].filter((el) => el !== null).length > 0 ||
|
|
642
|
+
this.PSBT_IN_TAP_LEAF_SCRIPT[i].filter((el) => el !== null).length >
|
|
643
|
+
0 ||
|
|
644
|
+
this.PSBT_IN_TAP_BIP32_DERIVATION[i].filter((el) => el !== null)
|
|
645
|
+
.length > 0
|
|
646
|
+
) {
|
|
647
|
+
return false;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// This input has been finalized. Continue checking the next one.
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// All inputs have been finalized, so this psbt is ready for transaction
|
|
654
|
+
// extraction.
|
|
655
|
+
return true;
|
|
656
|
+
}
|
|
657
|
+
|
|
802
658
|
/**
|
|
803
659
|
* Other Getters/Setters
|
|
804
660
|
*/
|
|
@@ -874,7 +730,15 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
874
730
|
this.PSBT_GLOBAL_TX_VERSION = 2;
|
|
875
731
|
this.PSBT_GLOBAL_INPUT_COUNT = 0;
|
|
876
732
|
this.PSBT_GLOBAL_OUTPUT_COUNT = 0;
|
|
733
|
+
|
|
734
|
+
// TODO: Right now these values are setting a default. How can it be made to
|
|
735
|
+
// accept values on the constructor method? The Creator role should be
|
|
736
|
+
// allowed to configure these.
|
|
877
737
|
this.PSBT_GLOBAL_FALLBACK_LOCKTIME = 0;
|
|
738
|
+
this.PSBT_GLOBAL_TX_MODIFIABLE = [
|
|
739
|
+
PsbtGlobalTxModifiableBits.INPUTS,
|
|
740
|
+
PsbtGlobalTxModifiableBits.OUTPUTS,
|
|
741
|
+
];
|
|
878
742
|
}
|
|
879
743
|
|
|
880
744
|
/**
|
|
@@ -935,13 +799,18 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
935
799
|
* defined for PsbtV2.
|
|
936
800
|
*/
|
|
937
801
|
public dangerouslySetGlobalTxVersion1() {
|
|
802
|
+
if (!this.isReadyForConstructor) {
|
|
803
|
+
throw Error(
|
|
804
|
+
"The PsbtV2 is not ready for a Constructor. The PSBT_GLOBAL_TX_VERSION should not be forced to version 1.",
|
|
805
|
+
);
|
|
806
|
+
}
|
|
938
807
|
console.warn("Dangerously setting PsbtV2.PSBT_GLOBAL_TX_VERSION to 1!");
|
|
939
808
|
const bw = new BufferWriter();
|
|
940
809
|
bw.writeI32(1);
|
|
941
810
|
this.globalMap.set(KeyType.PSBT_GLOBAL_TX_VERSION, bw.render());
|
|
942
811
|
}
|
|
943
812
|
|
|
944
|
-
// Is this a Creator/Constructor role action, or something else
|
|
813
|
+
// Is this a Creator/Constructor role action, or something else? BIPs don't
|
|
945
814
|
// define it well.
|
|
946
815
|
public addGlobalXpub(xpub: Buffer, fingerprint: Buffer, path: string) {
|
|
947
816
|
const bw = new BufferWriter();
|
|
@@ -982,6 +851,17 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
982
851
|
// significant validation concerning this step detailed in the BIP0370
|
|
983
852
|
// Constructor role:
|
|
984
853
|
// https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#constructor
|
|
854
|
+
//
|
|
855
|
+
// TODO: This method must properly handle the SIGHASH_SINGLE flag. If the
|
|
856
|
+
// `PSBT_GLOBAL_TX_MODIFIABLE` flag `SIGHASH_SINGLE` is present and a
|
|
857
|
+
// signature is present, then adding or removing inputs or outputs before a
|
|
858
|
+
// signature with sighash_single must happen atomically in pairs.
|
|
859
|
+
|
|
860
|
+
if (!this.isReadyForConstructor) {
|
|
861
|
+
throw Error(
|
|
862
|
+
"The PsbtV2 is not ready for a Constructor. Inputs cannot be added.",
|
|
863
|
+
);
|
|
864
|
+
}
|
|
985
865
|
if (!this.isModifiable([PsbtGlobalTxModifiableBits.INPUTS])) {
|
|
986
866
|
throw Error(
|
|
987
867
|
"PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE inputs cannot be modified.",
|
|
@@ -1048,6 +928,11 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1048
928
|
path: string;
|
|
1049
929
|
}[];
|
|
1050
930
|
}) {
|
|
931
|
+
if (!this.isReadyForConstructor) {
|
|
932
|
+
throw Error(
|
|
933
|
+
"The PsbtV2 is not ready for a Constructor. Outputs cannot be added.",
|
|
934
|
+
);
|
|
935
|
+
}
|
|
1051
936
|
if (!this.isModifiable([PsbtGlobalTxModifiableBits.OUTPUTS])) {
|
|
1052
937
|
throw Error(
|
|
1053
938
|
"PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE outputs cannot be modified.",
|
|
@@ -1091,6 +976,11 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1091
976
|
* Removes an input-map from inputMaps.
|
|
1092
977
|
*/
|
|
1093
978
|
public deleteInput(index: number) {
|
|
979
|
+
if (!this.isReadyForConstructor) {
|
|
980
|
+
throw Error(
|
|
981
|
+
"The PsbtV2 is not ready for a Constructor. Inputs cannot be removed.",
|
|
982
|
+
);
|
|
983
|
+
}
|
|
1094
984
|
if (!this.isModifiable([PsbtGlobalTxModifiableBits.INPUTS])) {
|
|
1095
985
|
throw Error(
|
|
1096
986
|
"PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE inputs cannot be modified.",
|
|
@@ -1105,6 +995,11 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1105
995
|
* Removes an output-map from outputMaps.
|
|
1106
996
|
*/
|
|
1107
997
|
public deleteOutput(index: number) {
|
|
998
|
+
if (!this.isReadyForConstructor) {
|
|
999
|
+
throw Error(
|
|
1000
|
+
"The PsbtV2 is not ready for a Constructor. Outputs cannot be removed.",
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1108
1003
|
if (!this.isModifiable([PsbtGlobalTxModifiableBits.OUTPUTS])) {
|
|
1109
1004
|
throw Error(
|
|
1110
1005
|
"PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE outputs cannot be modified.",
|
|
@@ -1126,7 +1021,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1126
1021
|
}
|
|
1127
1022
|
|
|
1128
1023
|
/**
|
|
1129
|
-
* Checks that provided flags are present in PSBT_GLOBAL_TX_MODIFIABLE.
|
|
1024
|
+
* Checks that all provided flags are present in PSBT_GLOBAL_TX_MODIFIABLE.
|
|
1130
1025
|
*/
|
|
1131
1026
|
private isModifiable(flags: PsbtGlobalTxModifiableBits[]) {
|
|
1132
1027
|
for (const flag of flags) {
|
|
@@ -1152,6 +1047,11 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1152
1047
|
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer
|
|
1153
1048
|
*/
|
|
1154
1049
|
public addPartialSig(inputIndex: number, pubkey: Buffer, sig: Buffer) {
|
|
1050
|
+
if (!this.isReadyForSigner) {
|
|
1051
|
+
throw Error(
|
|
1052
|
+
"The PsbtV2 is not ready for a Signer. Partial sigs cannot be added.",
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1155
1055
|
if (!this.inputMaps[inputIndex]) {
|
|
1156
1056
|
throw Error(`PsbtV2 has no input at ${inputIndex}`);
|
|
1157
1057
|
}
|
|
@@ -1189,6 +1089,8 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1189
1089
|
* the pubkey exists.
|
|
1190
1090
|
*/
|
|
1191
1091
|
public removePartialSig(inputIndex: number, pubkey?: Buffer) {
|
|
1092
|
+
// TODO: What role is allowed to remove a partial sig? Perform that
|
|
1093
|
+
// role-check validation here.
|
|
1192
1094
|
const input = this.inputMaps[inputIndex];
|
|
1193
1095
|
|
|
1194
1096
|
if (!input) {
|
|
@@ -1253,7 +1155,11 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1253
1155
|
}
|
|
1254
1156
|
|
|
1255
1157
|
/**
|
|
1256
|
-
* Attempts to return a PsbtV2 by converting from a PsbtV0 string or Buffer
|
|
1158
|
+
* Attempts to return a PsbtV2 by converting from a PsbtV0 string or Buffer.
|
|
1159
|
+
*
|
|
1160
|
+
* This method first starts with a fresh PsbtV2 having just been created. It
|
|
1161
|
+
* then takes the PsbtV2 through its operator saga through the Signer role. In
|
|
1162
|
+
* this sense validation for each operator role will be performed.
|
|
1257
1163
|
*/
|
|
1258
1164
|
static FromV0(psbt: string | Buffer, allowTxnVersion1 = false): PsbtV2 {
|
|
1259
1165
|
const psbtv0Buf = bufferize(psbt);
|
|
@@ -1262,11 +1168,6 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1262
1168
|
|
|
1263
1169
|
// Creator Role
|
|
1264
1170
|
const psbtv2 = new PsbtV2();
|
|
1265
|
-
// Set it fully modifiable so that we can add the v0 inputs and outputs.
|
|
1266
|
-
psbtv2.PSBT_GLOBAL_TX_MODIFIABLE = [
|
|
1267
|
-
PsbtGlobalTxModifiableBits.INPUTS,
|
|
1268
|
-
PsbtGlobalTxModifiableBits.OUTPUTS,
|
|
1269
|
-
];
|
|
1270
1171
|
const txVersion = psbtv0.data.getTransaction().readInt32LE(0);
|
|
1271
1172
|
if (txVersion === 1 && allowTxnVersion1) {
|
|
1272
1173
|
psbtv2.dangerouslySetGlobalTxVersion1();
|
|
@@ -1276,7 +1177,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1276
1177
|
.readInt32LE(0);
|
|
1277
1178
|
}
|
|
1278
1179
|
|
|
1279
|
-
//
|
|
1180
|
+
// Constructor Role
|
|
1280
1181
|
for (const globalXpub of psbtv0GlobalMap.globalXpub ?? []) {
|
|
1281
1182
|
psbtv2.addGlobalXpub(
|
|
1282
1183
|
globalXpub.extendedPubkey,
|
|
@@ -1285,8 +1186,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1285
1186
|
);
|
|
1286
1187
|
}
|
|
1287
1188
|
|
|
1288
|
-
|
|
1289
|
-
let txInputs: any = [];
|
|
1189
|
+
const txInputs: any = [];
|
|
1290
1190
|
for (const [index, txInput] of psbtv0.txInputs.entries()) {
|
|
1291
1191
|
txInputs[index] = txInput;
|
|
1292
1192
|
}
|
|
@@ -1308,7 +1208,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1308
1208
|
});
|
|
1309
1209
|
}
|
|
1310
1210
|
|
|
1311
|
-
|
|
1211
|
+
const txOutputs: any = [];
|
|
1312
1212
|
for (const [index, txOutput] of psbtv0.txOutputs.entries()) {
|
|
1313
1213
|
txOutputs[index] = txOutput;
|
|
1314
1214
|
}
|
|
@@ -1324,6 +1224,8 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1324
1224
|
});
|
|
1325
1225
|
}
|
|
1326
1226
|
|
|
1227
|
+
// Signer Role
|
|
1228
|
+
|
|
1327
1229
|
// Finally, add partialSigs to inputs. This has to be performed last since
|
|
1328
1230
|
// it may change PSBT_GLOBAL_TX_MODIFIABLE preventing inputs or outputs from
|
|
1329
1231
|
// being added.
|
|
@@ -1336,17 +1238,3 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
1336
1238
|
return psbtv2;
|
|
1337
1239
|
}
|
|
1338
1240
|
}
|
|
1339
|
-
|
|
1340
|
-
/**
|
|
1341
|
-
* Attempts to extract the version number as uint32LE from raw psbt regardless
|
|
1342
|
-
* of psbt validity.
|
|
1343
|
-
* @param {string | Buffer} psbt - hex, base64 or buffer of psbt
|
|
1344
|
-
* @returns {number} version number
|
|
1345
|
-
*/
|
|
1346
|
-
export function getPsbtVersionNumber(psbt: string | Buffer): number {
|
|
1347
|
-
const map = new Map<Key, Value>();
|
|
1348
|
-
const buf = bufferize(psbt);
|
|
1349
|
-
const br = new BufferReader(buf.slice(PSBT_MAGIC_BYTES.length));
|
|
1350
|
-
readAndSetKeyPairs(map, br);
|
|
1351
|
-
return map.get(KeyType.PSBT_GLOBAL_VERSION)?.readUInt32LE(0) || 0;
|
|
1352
|
-
}
|