@scure/btc-signer 1.8.1 → 2.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/README.md +8 -5
- package/index.js +17 -61
- package/index.js.map +1 -1
- package/musig2.d.ts +2 -2
- package/musig2.d.ts.map +1 -1
- package/musig2.js +93 -90
- package/musig2.js.map +1 -1
- package/p2p.d.ts +7 -8
- package/p2p.d.ts.map +1 -1
- package/p2p.js +32 -38
- package/p2p.js.map +1 -1
- package/package.json +33 -103
- package/payment.js +73 -96
- package/payment.js.map +1 -1
- package/psbt.d.ts +224 -224
- package/psbt.d.ts.map +1 -1
- package/psbt.js +58 -63
- package/psbt.js.map +1 -1
- package/script.d.ts +125 -120
- package/script.d.ts.map +1 -1
- package/script.js +46 -146
- package/script.js.map +1 -1
- package/src/musig2.ts +43 -50
- package/src/p2p.ts +28 -40
- package/src/psbt.ts +7 -7
- package/src/script.ts +29 -26
- package/src/transaction.ts +21 -21
- package/src/utils.ts +41 -22
- package/transaction.d.ts +22 -28
- package/transaction.d.ts.map +1 -1
- package/transaction.js +134 -151
- package/transaction.js.map +1 -1
- package/utils.d.ts +11 -5
- package/utils.d.ts.map +1 -1
- package/utils.js +60 -62
- package/utils.js.map +1 -1
- package/utxo.d.ts +45 -45
- package/utxo.d.ts.map +1 -1
- package/utxo.js +62 -58
- package/utxo.js.map +1 -1
- package/_type_test.d.ts +0 -2
- package/_type_test.d.ts.map +0 -1
- package/_type_test.js +0 -59
- package/_type_test.js.map +0 -1
- package/esm/_type_test.d.ts +0 -2
- package/esm/_type_test.d.ts.map +0 -1
- package/esm/_type_test.js +0 -57
- package/esm/_type_test.js.map +0 -1
- package/esm/index.d.ts +0 -22
- package/esm/index.d.ts.map +0 -1
- package/esm/index.js +0 -22
- package/esm/index.js.map +0 -1
- package/esm/musig2.d.ts +0 -148
- package/esm/musig2.d.ts.map +0 -1
- package/esm/musig2.js +0 -404
- package/esm/musig2.js.map +0 -1
- package/esm/p2p.d.ts +0 -38
- package/esm/p2p.d.ts.map +0 -1
- package/esm/p2p.js +0 -156
- package/esm/p2p.js.map +0 -1
- package/esm/package.json +0 -1
- package/esm/payment.d.ts +0 -200
- package/esm/payment.d.ts.map +0 -1
- package/esm/payment.js +0 -697
- package/esm/payment.js.map +0 -1
- package/esm/psbt.d.ts +0 -820
- package/esm/psbt.d.ts.map +0 -1
- package/esm/psbt.js +0 -409
- package/esm/psbt.js.map +0 -1
- package/esm/script.d.ts +0 -170
- package/esm/script.d.ts.map +0 -1
- package/esm/script.js +0 -356
- package/esm/script.js.map +0 -1
- package/esm/transaction.d.ts +0 -172
- package/esm/transaction.d.ts.map +0 -1
- package/esm/transaction.js +0 -1185
- package/esm/transaction.js.map +0 -1
- package/esm/utils.d.ts +0 -35
- package/esm/utils.d.ts.map +0 -1
- package/esm/utils.js +0 -118
- package/esm/utils.js.map +0 -1
- package/esm/utxo.d.ts +0 -198
- package/esm/utxo.d.ts.map +0 -1
- package/esm/utxo.js +0 -461
- package/esm/utxo.js.map +0 -1
- package/src/_type_test.ts +0 -69
- package/src/package.json +0 -3
package/transaction.js
CHANGED
|
@@ -1,40 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
exports.getInputType = getInputType;
|
|
9
|
-
exports.PSBTCombine = PSBTCombine;
|
|
10
|
-
exports.bip32Path = bip32Path;
|
|
11
|
-
const base_1 = require("@scure/base");
|
|
12
|
-
const P = require("micro-packed");
|
|
13
|
-
const payment_ts_1 = require("./payment.js");
|
|
14
|
-
const psbt = require("./psbt.js");
|
|
15
|
-
const script_ts_1 = require("./script.js");
|
|
16
|
-
const u = require("./utils.js");
|
|
17
|
-
const utils_ts_1 = require("./utils.js");
|
|
1
|
+
import { hex } from '@scure/base';
|
|
2
|
+
import * as P from 'micro-packed';
|
|
3
|
+
import { Address, OutScript, checkScript, tapLeafHash } from "./payment.js";
|
|
4
|
+
import * as psbt from "./psbt.js";
|
|
5
|
+
import { CompactSizeLen, RawOldTx, RawOutput, RawTx, RawWitness, Script, VarBytes, } from "./script.js";
|
|
6
|
+
import * as u from "./utils.js";
|
|
7
|
+
import { NETWORK, concatBytes, equalBytes, isBytes } from "./utils.js";
|
|
18
8
|
const EMPTY32 = new Uint8Array(32);
|
|
19
9
|
const EMPTY_OUTPUT = {
|
|
20
10
|
amount: 0xffffffffffffffffn,
|
|
21
11
|
script: P.EMPTY,
|
|
22
12
|
};
|
|
23
|
-
const toVsize = (weight) => Math.ceil(weight / 4);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
exports.Decimal = P.coders.decimal(exports.PRECISION);
|
|
13
|
+
export const toVsize = (weight) => Math.ceil(weight / 4);
|
|
14
|
+
export const PRECISION = 8;
|
|
15
|
+
export const DEFAULT_VERSION = 2;
|
|
16
|
+
export const DEFAULT_LOCKTIME = 0;
|
|
17
|
+
export const DEFAULT_SEQUENCE = 4294967295;
|
|
18
|
+
export const Decimal = P.coders.decimal(PRECISION);
|
|
30
19
|
// Same as value || def, but doesn't overwrites zero ('0', 0, 0n, etc)
|
|
31
|
-
const def = (value, def) => (value === undefined ? def : value);
|
|
32
|
-
|
|
33
|
-
function cloneDeep(obj) {
|
|
20
|
+
export const def = (value, def) => (value === undefined ? def : value);
|
|
21
|
+
export function cloneDeep(obj) {
|
|
34
22
|
if (Array.isArray(obj))
|
|
35
23
|
return obj.map((i) => cloneDeep(i));
|
|
36
24
|
// slice of nodejs Buffer doesn't copy
|
|
37
|
-
else if (
|
|
25
|
+
else if (isBytes(obj))
|
|
38
26
|
return Uint8Array.from(obj);
|
|
39
27
|
// immutable
|
|
40
28
|
else if (['number', 'bigint', 'boolean', 'string', 'undefined'].includes(typeof obj))
|
|
@@ -52,27 +40,26 @@ function cloneDeep(obj) {
|
|
|
52
40
|
* Internal, exported only for backwards-compat. Use `SigHash` instead.
|
|
53
41
|
* @deprecated
|
|
54
42
|
*/
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
})(SigHash || (exports.SigHash = SigHash = {}));
|
|
43
|
+
export const SignatureHash = {
|
|
44
|
+
DEFAULT: 0,
|
|
45
|
+
ALL: 1,
|
|
46
|
+
NONE: 2,
|
|
47
|
+
SINGLE: 3,
|
|
48
|
+
ANYONECANPAY: 0x80,
|
|
49
|
+
};
|
|
50
|
+
export const SigHash = {
|
|
51
|
+
DEFAULT: SignatureHash.DEFAULT,
|
|
52
|
+
ALL: SignatureHash.ALL,
|
|
53
|
+
NONE: SignatureHash.NONE,
|
|
54
|
+
SINGLE: SignatureHash.SINGLE,
|
|
55
|
+
DEFAULT_ANYONECANPAY: SignatureHash.DEFAULT | SignatureHash.ANYONECANPAY,
|
|
56
|
+
ALL_ANYONECANPAY: SignatureHash.ALL | SignatureHash.ANYONECANPAY,
|
|
57
|
+
NONE_ANYONECANPAY: SignatureHash.NONE | SignatureHash.ANYONECANPAY,
|
|
58
|
+
SINGLE_ANYONECANPAY: SignatureHash.SINGLE | SignatureHash.ANYONECANPAY,
|
|
59
|
+
};
|
|
60
|
+
export const SigHashNames = u.reverseObject(SigHash);
|
|
74
61
|
function getTaprootKeys(privKey, pubKey, internalKey, merkleRoot = P.EMPTY) {
|
|
75
|
-
if (
|
|
62
|
+
if (equalBytes(internalKey, pubKey)) {
|
|
76
63
|
privKey = u.taprootTweakPrivKey(privKey, merkleRoot);
|
|
77
64
|
pubKey = u.pubSchnorr(privKey);
|
|
78
65
|
}
|
|
@@ -85,14 +72,14 @@ function outputBeforeSign(i) {
|
|
|
85
72
|
return { script: i.script, amount: i.amount };
|
|
86
73
|
}
|
|
87
74
|
// Force check index/txid/sequence
|
|
88
|
-
function inputBeforeSign(i) {
|
|
75
|
+
export function inputBeforeSign(i) {
|
|
89
76
|
if (i.txid === undefined || i.index === undefined)
|
|
90
77
|
throw new Error('Transaction/input: txid and index required');
|
|
91
78
|
return {
|
|
92
79
|
txid: i.txid,
|
|
93
80
|
index: i.index,
|
|
94
|
-
sequence:
|
|
95
|
-
finalScriptSig:
|
|
81
|
+
sequence: def(i.sequence, DEFAULT_SEQUENCE),
|
|
82
|
+
finalScriptSig: def(i.finalScriptSig, P.EMPTY),
|
|
96
83
|
};
|
|
97
84
|
}
|
|
98
85
|
function cleanFinalInput(i) {
|
|
@@ -105,7 +92,7 @@ function cleanFinalInput(i) {
|
|
|
105
92
|
// (TxHash, Idx)
|
|
106
93
|
const TxHashIdx = P.struct({ txid: P.bytes(32, true), index: P.U32LE });
|
|
107
94
|
function validateSigHash(s) {
|
|
108
|
-
if (typeof s !== 'number' || typeof
|
|
95
|
+
if (typeof s !== 'number' || typeof SigHashNames[s] !== 'string')
|
|
109
96
|
throw new Error(`Invalid SigHash=${s}`);
|
|
110
97
|
return s;
|
|
111
98
|
}
|
|
@@ -123,9 +110,9 @@ function validateOpts(opts) {
|
|
|
123
110
|
const _opts = {
|
|
124
111
|
...opts,
|
|
125
112
|
// Defaults
|
|
126
|
-
version:
|
|
127
|
-
lockTime:
|
|
128
|
-
PSBTVersion:
|
|
113
|
+
version: def(opts.version, DEFAULT_VERSION),
|
|
114
|
+
lockTime: def(opts.lockTime, 0),
|
|
115
|
+
PSBTVersion: def(opts.PSBTVersion, 0),
|
|
129
116
|
};
|
|
130
117
|
if (typeof _opts.allowUnknowInput !== 'undefined')
|
|
131
118
|
opts.allowUnknownInputs = _opts.allowUnknowInput;
|
|
@@ -181,7 +168,7 @@ function validateInput(i) {
|
|
|
181
168
|
throw new Error(`validateInput: index(${i.index}) not in nonWitnessUtxo`);
|
|
182
169
|
const prevOut = i.nonWitnessUtxo.outputs[i.index];
|
|
183
170
|
if (i.witnessUtxo &&
|
|
184
|
-
(!
|
|
171
|
+
(!equalBytes(i.witnessUtxo.script, prevOut.script) || i.witnessUtxo.amount !== prevOut.amount))
|
|
185
172
|
throw new Error('validateInput: witnessUtxo different from nonWitnessUtxo');
|
|
186
173
|
if (i.txid) {
|
|
187
174
|
const outputs = i.nonWitnessUtxo.outputs;
|
|
@@ -195,12 +182,12 @@ function validateInput(i) {
|
|
|
195
182
|
// in case user wants to use wrong input by mistake
|
|
196
183
|
// - Worst case: tx will be rejected by nodes. Still better than disallowing user
|
|
197
184
|
// to spend real input, no matter how broken it looks
|
|
198
|
-
const tx = Transaction.fromRaw(
|
|
185
|
+
const tx = Transaction.fromRaw(RawTx.encode(i.nonWitnessUtxo), {
|
|
199
186
|
allowUnknownOutputs: true,
|
|
200
187
|
disableScriptCheck: true,
|
|
201
188
|
allowUnknownInputs: true,
|
|
202
189
|
});
|
|
203
|
-
const txid =
|
|
190
|
+
const txid = hex.encode(i.txid);
|
|
204
191
|
// PSBTv2 vectors have non-final tx in inputs
|
|
205
192
|
if (tx.isFinal && tx.id !== txid)
|
|
206
193
|
throw new Error(`nonWitnessUtxo: wrong txid, exp=${txid} got=${tx.id}`);
|
|
@@ -209,7 +196,7 @@ function validateInput(i) {
|
|
|
209
196
|
return i;
|
|
210
197
|
}
|
|
211
198
|
// Normalizes input
|
|
212
|
-
function getPrevOut(input) {
|
|
199
|
+
export function getPrevOut(input) {
|
|
213
200
|
if (input.nonWitnessUtxo) {
|
|
214
201
|
if (input.index === undefined)
|
|
215
202
|
throw new Error('Unknown input index');
|
|
@@ -220,20 +207,20 @@ function getPrevOut(input) {
|
|
|
220
207
|
else
|
|
221
208
|
throw new Error('Cannot find previous output info');
|
|
222
209
|
}
|
|
223
|
-
function normalizeInput(i, cur, allowedFields, disableScriptCheck = false, allowUnknown = false) {
|
|
210
|
+
export function normalizeInput(i, cur, allowedFields, disableScriptCheck = false, allowUnknown = false) {
|
|
224
211
|
let { nonWitnessUtxo, txid } = i;
|
|
225
212
|
// String support for common fields. We usually prefer Uint8Array to avoid errors
|
|
226
213
|
// like hex looking string accidentally passed, however, in case of nonWitnessUtxo
|
|
227
214
|
// it is better to expect string, since constructing this complex object will be
|
|
228
215
|
// difficult for user
|
|
229
216
|
if (typeof nonWitnessUtxo === 'string')
|
|
230
|
-
nonWitnessUtxo =
|
|
231
|
-
if (
|
|
232
|
-
nonWitnessUtxo =
|
|
217
|
+
nonWitnessUtxo = hex.decode(nonWitnessUtxo);
|
|
218
|
+
if (isBytes(nonWitnessUtxo))
|
|
219
|
+
nonWitnessUtxo = RawTx.decode(nonWitnessUtxo);
|
|
233
220
|
if (!('nonWitnessUtxo' in i) && nonWitnessUtxo === undefined)
|
|
234
221
|
nonWitnessUtxo = cur?.nonWitnessUtxo;
|
|
235
222
|
if (typeof txid === 'string')
|
|
236
|
-
txid =
|
|
223
|
+
txid = hex.decode(txid);
|
|
237
224
|
// TODO: if we have nonWitnessUtxo, we can extract txId from here
|
|
238
225
|
if (txid === undefined)
|
|
239
226
|
txid = cur?.txid;
|
|
@@ -241,7 +228,7 @@ function normalizeInput(i, cur, allowedFields, disableScriptCheck = false, allow
|
|
|
241
228
|
if (!('nonWitnessUtxo' in i) && res.nonWitnessUtxo === undefined)
|
|
242
229
|
delete res.nonWitnessUtxo;
|
|
243
230
|
if (res.sequence === undefined)
|
|
244
|
-
res.sequence =
|
|
231
|
+
res.sequence = DEFAULT_SEQUENCE;
|
|
245
232
|
if (res.tapMerkleRoot === null)
|
|
246
233
|
delete res.tapMerkleRoot;
|
|
247
234
|
res = psbt.mergeKeyMap(psbt.PSBTInput, res, cur, allowedFields, allowUnknown);
|
|
@@ -252,14 +239,14 @@ function normalizeInput(i, cur, allowedFields, disableScriptCheck = false, allow
|
|
|
252
239
|
else if (res.witnessUtxo)
|
|
253
240
|
prevOut = res.witnessUtxo;
|
|
254
241
|
if (prevOut && !disableScriptCheck)
|
|
255
|
-
|
|
242
|
+
checkScript(prevOut && prevOut.script, res.redeemScript, res.witnessScript);
|
|
256
243
|
return res;
|
|
257
244
|
}
|
|
258
|
-
function getInputType(input, allowLegacyWitnessUtxo = false) {
|
|
245
|
+
export function getInputType(input, allowLegacyWitnessUtxo = false) {
|
|
259
246
|
let txType = 'legacy';
|
|
260
247
|
let defaultSighash = SignatureHash.ALL;
|
|
261
248
|
const prevOut = getPrevOut(input);
|
|
262
|
-
const first =
|
|
249
|
+
const first = OutScript.decode(prevOut.script);
|
|
263
250
|
let type = first.type;
|
|
264
251
|
let cur = first;
|
|
265
252
|
const stack = [first];
|
|
@@ -280,7 +267,7 @@ function getInputType(input, allowLegacyWitnessUtxo = false) {
|
|
|
280
267
|
if (first.type === 'sh') {
|
|
281
268
|
if (!input.redeemScript)
|
|
282
269
|
throw new Error('inputType: sh without redeemScript');
|
|
283
|
-
let child =
|
|
270
|
+
let child = OutScript.decode(input.redeemScript);
|
|
284
271
|
if (child.type === 'wpkh' || child.type === 'wsh')
|
|
285
272
|
txType = 'segwit';
|
|
286
273
|
stack.push(child);
|
|
@@ -291,7 +278,7 @@ function getInputType(input, allowLegacyWitnessUtxo = false) {
|
|
|
291
278
|
if (cur.type === 'wsh') {
|
|
292
279
|
if (!input.witnessScript)
|
|
293
280
|
throw new Error('inputType: wsh without witnessScript');
|
|
294
|
-
let child =
|
|
281
|
+
let child = OutScript.decode(input.witnessScript);
|
|
295
282
|
if (child.type === 'wsh')
|
|
296
283
|
txType = 'segwit';
|
|
297
284
|
stack.push(child);
|
|
@@ -301,7 +288,7 @@ function getInputType(input, allowLegacyWitnessUtxo = false) {
|
|
|
301
288
|
const last = stack[stack.length - 1];
|
|
302
289
|
if (last.type === 'sh' || last.type === 'wsh')
|
|
303
290
|
throw new Error('inputType: sh/wsh cannot be terminal type');
|
|
304
|
-
const lastScript =
|
|
291
|
+
const lastScript = OutScript.encode(last);
|
|
305
292
|
const res = {
|
|
306
293
|
type,
|
|
307
294
|
txType,
|
|
@@ -316,20 +303,21 @@ function getInputType(input, allowLegacyWitnessUtxo = false) {
|
|
|
316
303
|
return res;
|
|
317
304
|
}
|
|
318
305
|
}
|
|
319
|
-
class Transaction {
|
|
306
|
+
export class Transaction {
|
|
307
|
+
global = {};
|
|
308
|
+
inputs = []; // use getInput()
|
|
309
|
+
outputs = []; // use getOutput()
|
|
310
|
+
opts;
|
|
320
311
|
constructor(opts = {}) {
|
|
321
|
-
this.global = {};
|
|
322
|
-
this.inputs = []; // use getInput()
|
|
323
|
-
this.outputs = []; // use getOutput()
|
|
324
312
|
const _opts = (this.opts = validateOpts(opts));
|
|
325
313
|
// Merge with global structure of PSBTv2
|
|
326
|
-
if (_opts.lockTime !==
|
|
314
|
+
if (_opts.lockTime !== DEFAULT_LOCKTIME)
|
|
327
315
|
this.global.fallbackLocktime = _opts.lockTime;
|
|
328
316
|
this.global.txVersion = _opts.version;
|
|
329
317
|
}
|
|
330
318
|
// Import
|
|
331
319
|
static fromRaw(raw, opts = {}) {
|
|
332
|
-
const parsed =
|
|
320
|
+
const parsed = RawTx.decode(raw);
|
|
333
321
|
const tx = new Transaction({ ...opts, version: parsed.version, lockTime: parsed.lockTime });
|
|
334
322
|
for (const o of parsed.outputs)
|
|
335
323
|
tx.addOutput(o);
|
|
@@ -376,7 +364,7 @@ class Transaction {
|
|
|
376
364
|
...parsed.global.unsignedTx?.outputs[j],
|
|
377
365
|
}));
|
|
378
366
|
tx.global = { ...parsed.global, txVersion: version }; // just in case proprietary/unknown fields
|
|
379
|
-
if (lockTime !==
|
|
367
|
+
if (lockTime !== DEFAULT_LOCKTIME)
|
|
380
368
|
tx.global.fallbackLocktime = lockTime;
|
|
381
369
|
return tx;
|
|
382
370
|
}
|
|
@@ -407,7 +395,7 @@ class Transaction {
|
|
|
407
395
|
- Parsing of PSBTv0 depends on unsignedTx (it looks for input count here)
|
|
408
396
|
- BIP-174 requires old serialization format (without witnesses) inside global, which solves this
|
|
409
397
|
*/
|
|
410
|
-
global.unsignedTx =
|
|
398
|
+
global.unsignedTx = RawOldTx.decode(RawOldTx.encode({
|
|
411
399
|
version: this.version,
|
|
412
400
|
lockTime: this.lockTime,
|
|
413
401
|
inputs: this.inputs.map(inputBeforeSign).map((i) => ({
|
|
@@ -424,7 +412,7 @@ class Transaction {
|
|
|
424
412
|
global.txVersion = this.version;
|
|
425
413
|
global.inputCount = this.inputs.length;
|
|
426
414
|
global.outputCount = this.outputs.length;
|
|
427
|
-
if (global.fallbackLocktime && global.fallbackLocktime ===
|
|
415
|
+
if (global.fallbackLocktime && global.fallbackLocktime === DEFAULT_LOCKTIME)
|
|
428
416
|
delete global.fallbackLocktime;
|
|
429
417
|
}
|
|
430
418
|
if (this.opts.bip174jsCompat) {
|
|
@@ -441,9 +429,9 @@ class Transaction {
|
|
|
441
429
|
}
|
|
442
430
|
// BIP370 lockTime (https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#determining-lock-time)
|
|
443
431
|
get lockTime() {
|
|
444
|
-
let height =
|
|
432
|
+
let height = DEFAULT_LOCKTIME;
|
|
445
433
|
let heightCnt = 0;
|
|
446
|
-
let time =
|
|
434
|
+
let time = DEFAULT_LOCKTIME;
|
|
447
435
|
let timeCnt = 0;
|
|
448
436
|
for (const i of this.inputs) {
|
|
449
437
|
if (i.requiredHeightLocktime) {
|
|
@@ -457,9 +445,9 @@ class Transaction {
|
|
|
457
445
|
}
|
|
458
446
|
if (heightCnt && heightCnt >= timeCnt)
|
|
459
447
|
return height;
|
|
460
|
-
if (time !==
|
|
448
|
+
if (time !== DEFAULT_LOCKTIME)
|
|
461
449
|
return time;
|
|
462
|
-
return this.global.fallbackLocktime ||
|
|
450
|
+
return this.global.fallbackLocktime || DEFAULT_LOCKTIME;
|
|
463
451
|
}
|
|
464
452
|
get version() {
|
|
465
453
|
// Should be not possible
|
|
@@ -552,25 +540,25 @@ class Transaction {
|
|
|
552
540
|
let out = 32;
|
|
553
541
|
// Outputs
|
|
554
542
|
const outputs = this.outputs.map(outputBeforeSign);
|
|
555
|
-
out += 4 *
|
|
543
|
+
out += 4 * CompactSizeLen.encode(this.outputs.length).length;
|
|
556
544
|
for (const o of outputs)
|
|
557
|
-
out += 32 + 4 *
|
|
545
|
+
out += 32 + 4 * VarBytes.encode(o.script).length;
|
|
558
546
|
// Inputs
|
|
559
547
|
if (this.hasWitnesses)
|
|
560
548
|
out += 2;
|
|
561
|
-
out += 4 *
|
|
549
|
+
out += 4 * CompactSizeLen.encode(this.inputs.length).length;
|
|
562
550
|
for (const i of this.inputs) {
|
|
563
|
-
out += 160 + 4 *
|
|
551
|
+
out += 160 + 4 * VarBytes.encode(i.finalScriptSig || P.EMPTY).length;
|
|
564
552
|
if (this.hasWitnesses && i.finalScriptWitness)
|
|
565
|
-
out +=
|
|
553
|
+
out += RawWitness.encode(i.finalScriptWitness).length;
|
|
566
554
|
}
|
|
567
555
|
return out;
|
|
568
556
|
}
|
|
569
557
|
get vsize() {
|
|
570
|
-
return
|
|
558
|
+
return toVsize(this.weight);
|
|
571
559
|
}
|
|
572
560
|
toBytes(withScriptSig = false, withWitness = false) {
|
|
573
|
-
return
|
|
561
|
+
return RawTx.encode({
|
|
574
562
|
version: this.version,
|
|
575
563
|
lockTime: this.lockTime,
|
|
576
564
|
inputs: this.inputs.map(inputBeforeSign).map((i) => ({
|
|
@@ -586,17 +574,13 @@ class Transaction {
|
|
|
586
574
|
return this.toBytes(false, false);
|
|
587
575
|
}
|
|
588
576
|
get hex() {
|
|
589
|
-
return
|
|
577
|
+
return hex.encode(this.toBytes(true, this.hasWitnesses));
|
|
590
578
|
}
|
|
591
579
|
get hash() {
|
|
592
|
-
|
|
593
|
-
throw new Error('Transaction is not finalized');
|
|
594
|
-
return base_1.hex.encode(u.sha256x2(this.toBytes(true)));
|
|
580
|
+
return hex.encode(u.sha256x2(this.toBytes(true)));
|
|
595
581
|
}
|
|
596
582
|
get id() {
|
|
597
|
-
|
|
598
|
-
throw new Error('Transaction is not finalized');
|
|
599
|
-
return base_1.hex.encode(u.sha256x2(this.toBytes(true)).reverse());
|
|
583
|
+
return hex.encode(u.sha256x2(this.toBytes(true)).reverse());
|
|
600
584
|
}
|
|
601
585
|
// Input stuff
|
|
602
586
|
checkInputIdx(idx) {
|
|
@@ -636,11 +620,11 @@ class Transaction {
|
|
|
636
620
|
this.checkOutputIdx(idx);
|
|
637
621
|
return cloneDeep(this.outputs[idx]);
|
|
638
622
|
}
|
|
639
|
-
getOutputAddress(idx, network =
|
|
623
|
+
getOutputAddress(idx, network = NETWORK) {
|
|
640
624
|
const out = this.getOutput(idx);
|
|
641
625
|
if (!out.script)
|
|
642
626
|
return;
|
|
643
|
-
return
|
|
627
|
+
return Address(network).encode(OutScript.decode(out.script));
|
|
644
628
|
}
|
|
645
629
|
get outputsLength() {
|
|
646
630
|
return this.outputs.length;
|
|
@@ -652,7 +636,7 @@ class Transaction {
|
|
|
652
636
|
if (typeof amount !== 'bigint')
|
|
653
637
|
throw new Error(`Wrong amount type, should be of type bigint in sats, but got ${amount} of type ${typeof amount}`);
|
|
654
638
|
if (typeof script === 'string')
|
|
655
|
-
script =
|
|
639
|
+
script = hex.decode(script);
|
|
656
640
|
if (script === undefined)
|
|
657
641
|
script = cur?.script;
|
|
658
642
|
let res = { ...cur, ...o, amount, script };
|
|
@@ -662,11 +646,11 @@ class Transaction {
|
|
|
662
646
|
psbt.PSBTOutputCoder.encode(res);
|
|
663
647
|
if (res.script &&
|
|
664
648
|
!this.opts.allowUnknownOutputs &&
|
|
665
|
-
|
|
649
|
+
OutScript.decode(res.script).type === 'unknown') {
|
|
666
650
|
throw new Error('Transaction/output: unknown output script type, there is a chance that input is unspendable. Pass allowUnknownOutputs=true, if you sure');
|
|
667
651
|
}
|
|
668
652
|
if (!this.opts.disableScriptCheck)
|
|
669
|
-
|
|
653
|
+
checkScript(res.script, res.redeemScript, res.witnessScript);
|
|
670
654
|
return res;
|
|
671
655
|
}
|
|
672
656
|
addOutput(o, _ignoreSignStatus = false) {
|
|
@@ -685,8 +669,8 @@ class Transaction {
|
|
|
685
669
|
}
|
|
686
670
|
this.outputs[idx] = this.normalizeOutput(output, this.outputs[idx], allowedFields);
|
|
687
671
|
}
|
|
688
|
-
addOutputAddress(address, amount, network =
|
|
689
|
-
return this.addOutput({ script:
|
|
672
|
+
addOutputAddress(address, amount, network = NETWORK) {
|
|
673
|
+
return this.addOutput({ script: OutScript.encode(Address(network).decode(address)), amount });
|
|
690
674
|
}
|
|
691
675
|
// Utils
|
|
692
676
|
get fee() {
|
|
@@ -712,7 +696,7 @@ class Transaction {
|
|
|
712
696
|
throw new Error(`Invalid input idx=${idx}`);
|
|
713
697
|
if ((isSingle && idx >= this.outputs.length) || idx >= this.inputs.length)
|
|
714
698
|
return P.U256BE.encode(1n);
|
|
715
|
-
prevOutScript =
|
|
699
|
+
prevOutScript = Script.encode(Script.decode(prevOutScript).filter((i) => i !== 'CODESEPARATOR'));
|
|
716
700
|
let inputs = this.inputs
|
|
717
701
|
.map(inputBeforeSign)
|
|
718
702
|
.map((input, inputIdx) => ({
|
|
@@ -733,7 +717,7 @@ class Transaction {
|
|
|
733
717
|
else if (isSingle) {
|
|
734
718
|
outputs = outputs.slice(0, idx).fill(EMPTY_OUTPUT).concat([outputs[idx]]);
|
|
735
719
|
}
|
|
736
|
-
const tmpTx =
|
|
720
|
+
const tmpTx = RawTx.encode({
|
|
737
721
|
lockTime: this.lockTime,
|
|
738
722
|
version: this.version,
|
|
739
723
|
segwitFlag: false,
|
|
@@ -754,12 +738,12 @@ class Transaction {
|
|
|
754
738
|
if (!isAny && !isSingle && !isNone)
|
|
755
739
|
sequenceHash = u.sha256x2(...inputs.map((i) => P.U32LE.encode(i.sequence)));
|
|
756
740
|
if (!isSingle && !isNone) {
|
|
757
|
-
outputHash = u.sha256x2(...outputs.map(
|
|
741
|
+
outputHash = u.sha256x2(...outputs.map(RawOutput.encode));
|
|
758
742
|
}
|
|
759
743
|
else if (isSingle && idx < outputs.length)
|
|
760
|
-
outputHash = u.sha256x2(
|
|
744
|
+
outputHash = u.sha256x2(RawOutput.encode(outputs[idx]));
|
|
761
745
|
const input = inputs[idx];
|
|
762
|
-
return u.sha256x2(P.I32LE.encode(this.version), inputHash, sequenceHash, P.bytes(32, true).encode(input.txid), P.U32LE.encode(input.index),
|
|
746
|
+
return u.sha256x2(P.I32LE.encode(this.version), inputHash, sequenceHash, P.bytes(32, true).encode(input.txid), P.U32LE.encode(input.index), VarBytes.encode(prevOutScript), P.U64LE.encode(amount), P.U32LE.encode(input.sequence), outputHash, P.U32LE.encode(this.lockTime), P.U32LE.encode(hashType));
|
|
763
747
|
}
|
|
764
748
|
preimageWitnessV1(idx, prevOutScript, hashType, amount, codeSeparator = -1, leafScript, leafVer = 0xc0, annex) {
|
|
765
749
|
if (!Array.isArray(amount) || this.inputs.length !== amount.length)
|
|
@@ -780,27 +764,27 @@ class Transaction {
|
|
|
780
764
|
out.push(...[
|
|
781
765
|
inputs.map(TxHashIdx.encode),
|
|
782
766
|
amount.map(P.U64LE.encode),
|
|
783
|
-
prevOutScript.map(
|
|
767
|
+
prevOutScript.map(VarBytes.encode),
|
|
784
768
|
inputs.map((i) => P.U32LE.encode(i.sequence)),
|
|
785
|
-
].map((i) => u.sha256(
|
|
769
|
+
].map((i) => u.sha256(concatBytes(...i))));
|
|
786
770
|
}
|
|
787
771
|
if (outType === SignatureHash.ALL) {
|
|
788
|
-
out.push(u.sha256(
|
|
772
|
+
out.push(u.sha256(concatBytes(...outputs.map(RawOutput.encode))));
|
|
789
773
|
}
|
|
790
774
|
const spendType = (annex ? 1 : 0) | (leafScript ? 2 : 0);
|
|
791
775
|
out.push(new Uint8Array([spendType]));
|
|
792
776
|
if (inType === SignatureHash.ANYONECANPAY) {
|
|
793
777
|
const inp = inputs[idx];
|
|
794
|
-
out.push(TxHashIdx.encode(inp), P.U64LE.encode(amount[idx]),
|
|
778
|
+
out.push(TxHashIdx.encode(inp), P.U64LE.encode(amount[idx]), VarBytes.encode(prevOutScript[idx]), P.U32LE.encode(inp.sequence));
|
|
795
779
|
}
|
|
796
780
|
else
|
|
797
781
|
out.push(P.U32LE.encode(idx));
|
|
798
782
|
if (spendType & 1)
|
|
799
|
-
out.push(u.sha256(
|
|
783
|
+
out.push(u.sha256(VarBytes.encode(annex || P.EMPTY)));
|
|
800
784
|
if (outType === SignatureHash.SINGLE)
|
|
801
|
-
out.push(idx < outputs.length ? u.sha256(
|
|
785
|
+
out.push(idx < outputs.length ? u.sha256(RawOutput.encode(outputs[idx])) : EMPTY32);
|
|
802
786
|
if (leafScript)
|
|
803
|
-
out.push(
|
|
787
|
+
out.push(tapLeafHash(leafScript, leafVer), P.U8.encode(0), P.I32LE.encode(codeSeparator));
|
|
804
788
|
return u.tagSchnorr('TapSighash', ...out);
|
|
805
789
|
}
|
|
806
790
|
// Signer can be privateKey OR instance of bip32 HD stuff
|
|
@@ -809,7 +793,7 @@ class Transaction {
|
|
|
809
793
|
const input = this.inputs[idx];
|
|
810
794
|
const inputType = getInputType(input, this.opts.allowLegacyWitnessUtxo);
|
|
811
795
|
// Handle BIP32 HDKey
|
|
812
|
-
if (!
|
|
796
|
+
if (!isBytes(privateKey)) {
|
|
813
797
|
if (!input.bip32Derivation || !input.bip32Derivation.length)
|
|
814
798
|
throw new Error('bip32Derivation: empty');
|
|
815
799
|
const signers = input.bip32Derivation
|
|
@@ -818,7 +802,7 @@ class Transaction {
|
|
|
818
802
|
let s = privateKey;
|
|
819
803
|
for (const i of path)
|
|
820
804
|
s = s.deriveChild(i);
|
|
821
|
-
if (!
|
|
805
|
+
if (!equalBytes(s.publicKey, pubKey))
|
|
822
806
|
throw new Error('bip32Derivation: wrong pubKey');
|
|
823
807
|
if (!s.privateKey)
|
|
824
808
|
throw new Error('bip32Derivation: no privateKey');
|
|
@@ -867,9 +851,9 @@ class Transaction {
|
|
|
867
851
|
// priv key to be provided
|
|
868
852
|
const { pubKey, privKey } = getTaprootKeys(privateKey, schnorrPub, input.tapInternalKey, merkleRoot);
|
|
869
853
|
const [taprootPubKey, _] = u.taprootTweakPubkey(input.tapInternalKey, merkleRoot);
|
|
870
|
-
if (
|
|
854
|
+
if (equalBytes(taprootPubKey, pubKey)) {
|
|
871
855
|
const hash = this.preimageWitnessV1(idx, prevOutScript, sighash, amount);
|
|
872
|
-
const sig =
|
|
856
|
+
const sig = concatBytes(u.signSchnorr(hash, privKey, _auxRand), sighash !== SignatureHash.DEFAULT ? new Uint8Array([sighash]) : P.EMPTY);
|
|
873
857
|
this.updateInput(idx, { tapKeySig: sig }, true);
|
|
874
858
|
signed = true;
|
|
875
859
|
}
|
|
@@ -878,16 +862,16 @@ class Transaction {
|
|
|
878
862
|
input.tapScriptSig = input.tapScriptSig || [];
|
|
879
863
|
for (const [_, _script] of input.tapLeafScript) {
|
|
880
864
|
const script = _script.subarray(0, -1);
|
|
881
|
-
const scriptDecoded =
|
|
865
|
+
const scriptDecoded = Script.decode(script);
|
|
882
866
|
const ver = _script[_script.length - 1];
|
|
883
|
-
const hash =
|
|
867
|
+
const hash = tapLeafHash(script, ver);
|
|
884
868
|
// NOTE: no need to tweak internal key here, since we don't support nested p2tr
|
|
885
|
-
const pos = scriptDecoded.findIndex((i) =>
|
|
869
|
+
const pos = scriptDecoded.findIndex((i) => isBytes(i) && equalBytes(i, schnorrPub));
|
|
886
870
|
// Skip if there is no public key in tapLeafScript
|
|
887
871
|
if (pos === -1)
|
|
888
872
|
continue;
|
|
889
873
|
const msg = this.preimageWitnessV1(idx, prevOutScript, sighash, amount, undefined, script, ver);
|
|
890
|
-
const sig =
|
|
874
|
+
const sig = concatBytes(u.signSchnorr(msg, privateKey, _auxRand), sighash !== SignatureHash.DEFAULT ? new Uint8Array([sighash]) : P.EMPTY);
|
|
891
875
|
this.updateInput(idx, { tapScriptSig: [[{ pubKey: schnorrPub, leafHash: hash }, sig]] }, true);
|
|
892
876
|
signed = true;
|
|
893
877
|
}
|
|
@@ -903,8 +887,8 @@ class Transaction {
|
|
|
903
887
|
// Check if script has public key or its has inside
|
|
904
888
|
let hasPubkey = false;
|
|
905
889
|
const pubKeyHash = u.hash160(pubKey);
|
|
906
|
-
for (const i of
|
|
907
|
-
if (
|
|
890
|
+
for (const i of Script.decode(inputType.lastScript)) {
|
|
891
|
+
if (isBytes(i) && (equalBytes(i, pubKey) || equalBytes(i, pubKeyHash)))
|
|
908
892
|
hasPubkey = true;
|
|
909
893
|
}
|
|
910
894
|
if (!hasPubkey)
|
|
@@ -917,14 +901,14 @@ class Transaction {
|
|
|
917
901
|
let script = inputType.lastScript;
|
|
918
902
|
// If wpkh OR sh-wpkh, wsh-wpkh is impossible, so looks ok
|
|
919
903
|
if (inputType.last.type === 'wpkh')
|
|
920
|
-
script =
|
|
904
|
+
script = OutScript.encode({ type: 'pkh', hash: inputType.last.hash });
|
|
921
905
|
hash = this.preimageWitnessV0(idx, script, sighash, prevOut.amount);
|
|
922
906
|
}
|
|
923
907
|
else
|
|
924
908
|
throw new Error(`Transaction/sign: unknown tx type: ${inputType.txType}`);
|
|
925
909
|
const sig = u.signECDSA(hash, privateKey, this.opts.lowR);
|
|
926
910
|
this.updateInput(idx, {
|
|
927
|
-
partialSig: [[pubKey,
|
|
911
|
+
partialSig: [[pubKey, concatBytes(sig, new Uint8Array([sighash]))]],
|
|
928
912
|
}, true);
|
|
929
913
|
}
|
|
930
914
|
return true;
|
|
@@ -967,16 +951,16 @@ class Transaction {
|
|
|
967
951
|
// Last byte is version
|
|
968
952
|
const script = _script.slice(0, -1);
|
|
969
953
|
const ver = _script[_script.length - 1];
|
|
970
|
-
const outScript =
|
|
971
|
-
const hash =
|
|
972
|
-
const scriptSig = input.tapScriptSig.filter((i) =>
|
|
954
|
+
const outScript = OutScript.decode(script);
|
|
955
|
+
const hash = tapLeafHash(script, ver);
|
|
956
|
+
const scriptSig = input.tapScriptSig.filter((i) => equalBytes(i[0].leafHash, hash));
|
|
973
957
|
let signatures = [];
|
|
974
958
|
if (outScript.type === 'tr_ms') {
|
|
975
959
|
const m = outScript.m;
|
|
976
960
|
const pubkeys = outScript.pubkeys;
|
|
977
961
|
let added = 0;
|
|
978
962
|
for (const pub of pubkeys) {
|
|
979
|
-
const sigIdx = scriptSig.findIndex((i) =>
|
|
963
|
+
const sigIdx = scriptSig.findIndex((i) => equalBytes(i[0].pubKey, pub));
|
|
980
964
|
// Should have exact amount of signatures (more -- will fail)
|
|
981
965
|
if (added === m || sigIdx === -1) {
|
|
982
966
|
signatures.push(P.EMPTY);
|
|
@@ -991,7 +975,7 @@ class Transaction {
|
|
|
991
975
|
}
|
|
992
976
|
else if (outScript.type === 'tr_ns') {
|
|
993
977
|
for (const pub of outScript.pubkeys) {
|
|
994
|
-
const sigIdx = scriptSig.findIndex((i) =>
|
|
978
|
+
const sigIdx = scriptSig.findIndex((i) => equalBytes(i[0].pubKey, pub));
|
|
995
979
|
if (sigIdx === -1)
|
|
996
980
|
continue;
|
|
997
981
|
signatures.push(scriptSig[sigIdx][1]);
|
|
@@ -1001,10 +985,10 @@ class Transaction {
|
|
|
1001
985
|
}
|
|
1002
986
|
else if (outScript.type === 'unknown' && this.opts.allowUnknownInputs) {
|
|
1003
987
|
// Trying our best to sign what we can
|
|
1004
|
-
const scriptDecoded =
|
|
988
|
+
const scriptDecoded = Script.decode(script);
|
|
1005
989
|
signatures = scriptSig
|
|
1006
990
|
.map(([{ pubKey }, signature]) => {
|
|
1007
|
-
const pos = scriptDecoded.findIndex((i) =>
|
|
991
|
+
const pos = scriptDecoded.findIndex((i) => isBytes(i) && equalBytes(i, pubKey));
|
|
1008
992
|
if (pos === -1)
|
|
1009
993
|
throw new Error('finalize/taproot: cannot find position of pubkey in script');
|
|
1010
994
|
return { signature, pos };
|
|
@@ -1021,7 +1005,7 @@ class Transaction {
|
|
|
1021
1005
|
for (const c of custom) {
|
|
1022
1006
|
if (!c.finalizeTaproot)
|
|
1023
1007
|
continue;
|
|
1024
|
-
const scriptDecoded =
|
|
1008
|
+
const scriptDecoded = Script.decode(script);
|
|
1025
1009
|
const csEncoded = c.encode(scriptDecoded);
|
|
1026
1010
|
if (csEncoded === undefined)
|
|
1027
1011
|
continue;
|
|
@@ -1063,7 +1047,7 @@ class Transaction {
|
|
|
1063
1047
|
let signatures = [];
|
|
1064
1048
|
// partial: [pubkey, sign]
|
|
1065
1049
|
for (const pub of pubkeys) {
|
|
1066
|
-
const sign = input.partialSig.find((s) =>
|
|
1050
|
+
const sign = input.partialSig.find((s) => equalBytes(pub, s[0]));
|
|
1067
1051
|
if (!sign)
|
|
1068
1052
|
continue;
|
|
1069
1053
|
signatures.push(sign[1]);
|
|
@@ -1072,13 +1056,13 @@ class Transaction {
|
|
|
1072
1056
|
if (signatures.length !== m) {
|
|
1073
1057
|
throw new Error(`Multisig: wrong signatures count, m=${m} n=${pubkeys.length} signatures=${signatures.length}`);
|
|
1074
1058
|
}
|
|
1075
|
-
inputScript =
|
|
1059
|
+
inputScript = Script.encode([0, ...signatures]);
|
|
1076
1060
|
}
|
|
1077
1061
|
else if (inputType.last.type === 'pk') {
|
|
1078
|
-
inputScript =
|
|
1062
|
+
inputScript = Script.encode([input.partialSig[0][1]]);
|
|
1079
1063
|
}
|
|
1080
1064
|
else if (inputType.last.type === 'pkh') {
|
|
1081
|
-
inputScript =
|
|
1065
|
+
inputScript = Script.encode([input.partialSig[0][1], input.partialSig[0][0]]);
|
|
1082
1066
|
}
|
|
1083
1067
|
else if (inputType.last.type === 'wpkh') {
|
|
1084
1068
|
inputScript = P.EMPTY;
|
|
@@ -1091,10 +1075,10 @@ class Transaction {
|
|
|
1091
1075
|
if (inputType.type.includes('wsh-')) {
|
|
1092
1076
|
// P2WSH
|
|
1093
1077
|
if (inputScript.length && inputType.lastScript.length) {
|
|
1094
|
-
witness =
|
|
1078
|
+
witness = Script.decode(inputScript).map((i) => {
|
|
1095
1079
|
if (i === 0)
|
|
1096
1080
|
return P.EMPTY;
|
|
1097
|
-
if (
|
|
1081
|
+
if (isBytes(i))
|
|
1098
1082
|
return i;
|
|
1099
1083
|
throw new Error(`Wrong witness op=${i}`);
|
|
1100
1084
|
});
|
|
@@ -1104,10 +1088,10 @@ class Transaction {
|
|
|
1104
1088
|
if (inputType.txType === 'segwit')
|
|
1105
1089
|
finalScriptWitness = witness;
|
|
1106
1090
|
if (inputType.type.startsWith('sh-wsh-')) {
|
|
1107
|
-
finalScriptSig =
|
|
1091
|
+
finalScriptSig = Script.encode([Script.encode([0, u.sha256(inputType.lastScript)])]);
|
|
1108
1092
|
}
|
|
1109
1093
|
else if (inputType.type.startsWith('sh-')) {
|
|
1110
|
-
finalScriptSig =
|
|
1094
|
+
finalScriptSig = Script.encode([...Script.decode(inputScript), inputType.lastScript]);
|
|
1111
1095
|
}
|
|
1112
1096
|
else if (inputType.type.startsWith('wsh-')) {
|
|
1113
1097
|
}
|
|
@@ -1145,11 +1129,11 @@ class Transaction {
|
|
|
1145
1129
|
throw new Error(`Transaction/combine: different ${k} length this=${this[k].length} other=${other[k].length}`);
|
|
1146
1130
|
}
|
|
1147
1131
|
}
|
|
1148
|
-
const thisUnsigned = this.global.unsignedTx ?
|
|
1132
|
+
const thisUnsigned = this.global.unsignedTx ? RawOldTx.encode(this.global.unsignedTx) : P.EMPTY;
|
|
1149
1133
|
const otherUnsigned = other.global.unsignedTx
|
|
1150
|
-
?
|
|
1134
|
+
? RawOldTx.encode(other.global.unsignedTx)
|
|
1151
1135
|
: P.EMPTY;
|
|
1152
|
-
if (!
|
|
1136
|
+
if (!equalBytes(thisUnsigned, otherUnsigned))
|
|
1153
1137
|
throw new Error(`Transaction/combine: different unsigned tx`);
|
|
1154
1138
|
this.global = psbt.mergeKeyMap(psbt.PSBTGlobal, this.global, other.global, undefined, this.opts.allowUnknown);
|
|
1155
1139
|
for (let i = 0; i < this.inputs.length; i++)
|
|
@@ -1163,8 +1147,7 @@ class Transaction {
|
|
|
1163
1147
|
return Transaction.fromPSBT(this.toPSBT(this.opts.PSBTVersion), this.opts);
|
|
1164
1148
|
}
|
|
1165
1149
|
}
|
|
1166
|
-
|
|
1167
|
-
function PSBTCombine(psbts) {
|
|
1150
|
+
export function PSBTCombine(psbts) {
|
|
1168
1151
|
if (!psbts || !Array.isArray(psbts) || !psbts.length)
|
|
1169
1152
|
throw new Error('PSBTCombine: wrong PSBT list');
|
|
1170
1153
|
const tx = Transaction.fromPSBT(psbts[0]);
|
|
@@ -1174,7 +1157,7 @@ function PSBTCombine(psbts) {
|
|
|
1174
1157
|
}
|
|
1175
1158
|
// Copy-pasted from bip32 derive, maybe do something like 'bip32.parsePath'?
|
|
1176
1159
|
const HARDENED_OFFSET = 0x80000000;
|
|
1177
|
-
function bip32Path(path) {
|
|
1160
|
+
export function bip32Path(path) {
|
|
1178
1161
|
const out = [];
|
|
1179
1162
|
if (!/^[mM]'?/.test(path))
|
|
1180
1163
|
throw new Error('Path must start with "m" or "M"');
|