@scure/btc-signer 1.7.0 → 1.8.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 +52 -1
- package/esm/_type_test.js +1 -1
- package/esm/index.d.ts +12 -12
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +10 -10
- package/esm/index.js.map +1 -1
- package/esm/musig2.js +1 -1
- package/esm/p2p.d.ts +38 -0
- package/esm/p2p.d.ts.map +1 -0
- package/esm/p2p.js +156 -0
- package/esm/p2p.js.map +1 -0
- package/esm/payment.d.ts +77 -21
- package/esm/payment.d.ts.map +1 -1
- package/esm/payment.js +63 -60
- package/esm/payment.js.map +1 -1
- package/esm/psbt.d.ts +1 -1
- package/esm/psbt.d.ts.map +1 -1
- package/esm/psbt.js +2 -35
- package/esm/psbt.js.map +1 -1
- package/esm/script.js +1 -1
- package/esm/transaction.d.ts +56 -4
- package/esm/transaction.d.ts.map +1 -1
- package/esm/transaction.js +151 -8
- package/esm/transaction.js.map +1 -1
- package/esm/utxo.d.ts +3 -55
- package/esm/utxo.d.ts.map +1 -1
- package/esm/utxo.js +5 -114
- package/esm/utxo.js.map +1 -1
- package/index.d.ts +12 -12
- package/index.d.ts.map +1 -1
- package/index.js +58 -58
- package/index.js.map +1 -1
- package/musig2.d.ts +1 -1
- package/musig2.js +2 -2
- package/p2p.d.ts +38 -0
- package/p2p.d.ts.map +1 -0
- package/p2p.js +158 -0
- package/p2p.js.map +1 -0
- package/package.json +11 -11
- package/payment.d.ts +77 -21
- package/payment.d.ts.map +1 -1
- package/payment.js +81 -78
- package/payment.js.map +1 -1
- package/psbt.d.ts +1 -1
- package/psbt.d.ts.map +1 -1
- package/psbt.js +22 -55
- package/psbt.js.map +1 -1
- package/script.js +3 -3
- package/src/_type_test.ts +1 -1
- package/src/index.ts +12 -12
- package/src/musig2.ts +1 -1
- package/src/p2p.ts +158 -0
- package/src/payment.ts +160 -75
- package/src/psbt.ts +2 -36
- package/src/script.ts +1 -1
- package/src/transaction.ts +156 -12
- package/src/utxo.ts +9 -111
- package/transaction.d.ts +56 -4
- package/transaction.d.ts.map +1 -1
- package/transaction.js +222 -75
- package/transaction.js.map +1 -1
- package/utxo.d.ts +3 -55
- package/utxo.d.ts.map +1 -1
- package/utxo.js +43 -156
- package/utxo.js.map +1 -1
package/transaction.js
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Transaction = exports.SigHash = exports.SignatureHash = exports.def = exports.Decimal = exports.DEFAULT_SEQUENCE = exports.DEFAULT_LOCKTIME = exports.DEFAULT_VERSION = exports.PRECISION = void 0;
|
|
3
|
+
exports.Transaction = exports.SigHash = exports.SignatureHash = exports.def = exports.Decimal = exports.DEFAULT_SEQUENCE = exports.DEFAULT_LOCKTIME = exports.DEFAULT_VERSION = exports.PRECISION = exports.toVsize = void 0;
|
|
4
4
|
exports.cloneDeep = cloneDeep;
|
|
5
5
|
exports.inputBeforeSign = inputBeforeSign;
|
|
6
|
+
exports.getPrevOut = getPrevOut;
|
|
7
|
+
exports.normalizeInput = normalizeInput;
|
|
8
|
+
exports.getInputType = getInputType;
|
|
6
9
|
exports.PSBTCombine = PSBTCombine;
|
|
7
10
|
exports.bip32Path = bip32Path;
|
|
8
11
|
const base_1 = require("@scure/base");
|
|
9
12
|
const P = require("micro-packed");
|
|
10
|
-
const
|
|
11
|
-
const psbt = require("./psbt.js");
|
|
12
|
-
const
|
|
13
|
+
const payment_ts_1 = require("./payment.js");
|
|
14
|
+
const psbt = require("./psbt.js");
|
|
15
|
+
const script_ts_1 = require("./script.js");
|
|
13
16
|
const u = require("./utils.js");
|
|
14
|
-
const
|
|
15
|
-
const utxo_js_1 = require("./utxo.js"); // circular
|
|
17
|
+
const utils_ts_1 = require("./utils.js");
|
|
16
18
|
const EMPTY32 = new Uint8Array(32);
|
|
17
19
|
const EMPTY_OUTPUT = {
|
|
18
20
|
amount: 0xffffffffffffffffn,
|
|
19
21
|
script: P.EMPTY,
|
|
20
22
|
};
|
|
23
|
+
const toVsize = (weight) => Math.ceil(weight / 4);
|
|
24
|
+
exports.toVsize = toVsize;
|
|
21
25
|
exports.PRECISION = 8;
|
|
22
26
|
exports.DEFAULT_VERSION = 2;
|
|
23
27
|
exports.DEFAULT_LOCKTIME = 0;
|
|
@@ -30,7 +34,7 @@ function cloneDeep(obj) {
|
|
|
30
34
|
if (Array.isArray(obj))
|
|
31
35
|
return obj.map((i) => cloneDeep(i));
|
|
32
36
|
// slice of nodejs Buffer doesn't copy
|
|
33
|
-
else if ((0,
|
|
37
|
+
else if ((0, utils_ts_1.isBytes)(obj))
|
|
34
38
|
return Uint8Array.from(obj);
|
|
35
39
|
// immutable
|
|
36
40
|
else if (['number', 'bigint', 'boolean', 'string', 'undefined'].includes(typeof obj))
|
|
@@ -68,7 +72,7 @@ var SigHash;
|
|
|
68
72
|
SigHash[SigHash["SINGLE_ANYONECANPAY"] = 131] = "SINGLE_ANYONECANPAY";
|
|
69
73
|
})(SigHash || (exports.SigHash = SigHash = {}));
|
|
70
74
|
function getTaprootKeys(privKey, pubKey, internalKey, merkleRoot = P.EMPTY) {
|
|
71
|
-
if ((0,
|
|
75
|
+
if ((0, utils_ts_1.equalBytes)(internalKey, pubKey)) {
|
|
72
76
|
privKey = u.taprootTweakPrivKey(privKey, merkleRoot);
|
|
73
77
|
pubKey = u.pubSchnorr(privKey);
|
|
74
78
|
}
|
|
@@ -166,6 +170,149 @@ function validateOpts(opts) {
|
|
|
166
170
|
}
|
|
167
171
|
return Object.freeze(_opts);
|
|
168
172
|
}
|
|
173
|
+
// NOTE: we cannot do this inside PSBTInput coder, because there is no index/txid at this point!
|
|
174
|
+
function validateInput(i) {
|
|
175
|
+
if (i.nonWitnessUtxo && i.index !== undefined) {
|
|
176
|
+
const last = i.nonWitnessUtxo.outputs.length - 1;
|
|
177
|
+
if (i.index > last)
|
|
178
|
+
throw new Error(`validateInput: index(${i.index}) not in nonWitnessUtxo`);
|
|
179
|
+
const prevOut = i.nonWitnessUtxo.outputs[i.index];
|
|
180
|
+
if (i.witnessUtxo &&
|
|
181
|
+
(!(0, utils_ts_1.equalBytes)(i.witnessUtxo.script, prevOut.script) || i.witnessUtxo.amount !== prevOut.amount))
|
|
182
|
+
throw new Error('validateInput: witnessUtxo different from nonWitnessUtxo');
|
|
183
|
+
if (i.txid) {
|
|
184
|
+
const outputs = i.nonWitnessUtxo.outputs;
|
|
185
|
+
if (outputs.length - 1 < i.index)
|
|
186
|
+
throw new Error('nonWitnessUtxo: incorect output index');
|
|
187
|
+
// At this point, we are using previous tx output to create new input.
|
|
188
|
+
// Script safety checks are unnecessary:
|
|
189
|
+
// - User has no control over previous tx. If somebody send money in same tx
|
|
190
|
+
// as unspendable output, we still want user able to spend money
|
|
191
|
+
// - We still want some checks to notify user about possible errors early
|
|
192
|
+
// in case user wants to use wrong input by mistake
|
|
193
|
+
// - Worst case: tx will be rejected by nodes. Still better than disallowing user
|
|
194
|
+
// to spend real input, no matter how broken it looks
|
|
195
|
+
const tx = Transaction.fromRaw(script_ts_1.RawTx.encode(i.nonWitnessUtxo), {
|
|
196
|
+
allowUnknownOutputs: true,
|
|
197
|
+
disableScriptCheck: true,
|
|
198
|
+
allowUnknownInputs: true,
|
|
199
|
+
});
|
|
200
|
+
const txid = base_1.hex.encode(i.txid);
|
|
201
|
+
// PSBTv2 vectors have non-final tx in inputs
|
|
202
|
+
if (tx.isFinal && tx.id !== txid)
|
|
203
|
+
throw new Error(`nonWitnessUtxo: wrong txid, exp=${txid} got=${tx.id}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return i;
|
|
207
|
+
}
|
|
208
|
+
// Normalizes input
|
|
209
|
+
function getPrevOut(input) {
|
|
210
|
+
if (input.nonWitnessUtxo) {
|
|
211
|
+
if (input.index === undefined)
|
|
212
|
+
throw new Error('Unknown input index');
|
|
213
|
+
return input.nonWitnessUtxo.outputs[input.index];
|
|
214
|
+
}
|
|
215
|
+
else if (input.witnessUtxo)
|
|
216
|
+
return input.witnessUtxo;
|
|
217
|
+
else
|
|
218
|
+
throw new Error('Cannot find previous output info');
|
|
219
|
+
}
|
|
220
|
+
function normalizeInput(i, cur, allowedFields, disableScriptCheck = false, allowUnknown = false) {
|
|
221
|
+
let { nonWitnessUtxo, txid } = i;
|
|
222
|
+
// String support for common fields. We usually prefer Uint8Array to avoid errors
|
|
223
|
+
// like hex looking string accidentally passed, however, in case of nonWitnessUtxo
|
|
224
|
+
// it is better to expect string, since constructing this complex object will be
|
|
225
|
+
// difficult for user
|
|
226
|
+
if (typeof nonWitnessUtxo === 'string')
|
|
227
|
+
nonWitnessUtxo = base_1.hex.decode(nonWitnessUtxo);
|
|
228
|
+
if ((0, utils_ts_1.isBytes)(nonWitnessUtxo))
|
|
229
|
+
nonWitnessUtxo = script_ts_1.RawTx.decode(nonWitnessUtxo);
|
|
230
|
+
if (!('nonWitnessUtxo' in i) && nonWitnessUtxo === undefined)
|
|
231
|
+
nonWitnessUtxo = cur?.nonWitnessUtxo;
|
|
232
|
+
if (typeof txid === 'string')
|
|
233
|
+
txid = base_1.hex.decode(txid);
|
|
234
|
+
// TODO: if we have nonWitnessUtxo, we can extract txId from here
|
|
235
|
+
if (txid === undefined)
|
|
236
|
+
txid = cur?.txid;
|
|
237
|
+
let res = { ...cur, ...i, nonWitnessUtxo, txid };
|
|
238
|
+
if (!('nonWitnessUtxo' in i) && res.nonWitnessUtxo === undefined)
|
|
239
|
+
delete res.nonWitnessUtxo;
|
|
240
|
+
if (res.sequence === undefined)
|
|
241
|
+
res.sequence = exports.DEFAULT_SEQUENCE;
|
|
242
|
+
if (res.tapMerkleRoot === null)
|
|
243
|
+
delete res.tapMerkleRoot;
|
|
244
|
+
res = psbt.mergeKeyMap(psbt.PSBTInput, res, cur, allowedFields, allowUnknown);
|
|
245
|
+
psbt.PSBTInputCoder.encode(res); // Validates that everything is correct at this point
|
|
246
|
+
let prevOut;
|
|
247
|
+
if (res.nonWitnessUtxo && res.index !== undefined)
|
|
248
|
+
prevOut = res.nonWitnessUtxo.outputs[res.index];
|
|
249
|
+
else if (res.witnessUtxo)
|
|
250
|
+
prevOut = res.witnessUtxo;
|
|
251
|
+
if (prevOut && !disableScriptCheck)
|
|
252
|
+
(0, payment_ts_1.checkScript)(prevOut && prevOut.script, res.redeemScript, res.witnessScript);
|
|
253
|
+
return res;
|
|
254
|
+
}
|
|
255
|
+
function getInputType(input, allowLegacyWitnessUtxo = false) {
|
|
256
|
+
let txType = 'legacy';
|
|
257
|
+
let defaultSighash = SignatureHash.ALL;
|
|
258
|
+
const prevOut = getPrevOut(input);
|
|
259
|
+
const first = payment_ts_1.OutScript.decode(prevOut.script);
|
|
260
|
+
let type = first.type;
|
|
261
|
+
let cur = first;
|
|
262
|
+
const stack = [first];
|
|
263
|
+
if (first.type === 'tr') {
|
|
264
|
+
defaultSighash = SignatureHash.DEFAULT;
|
|
265
|
+
return {
|
|
266
|
+
txType: 'taproot',
|
|
267
|
+
type: 'tr',
|
|
268
|
+
last: first,
|
|
269
|
+
lastScript: prevOut.script,
|
|
270
|
+
defaultSighash,
|
|
271
|
+
sighash: input.sighashType || defaultSighash,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
if (first.type === 'wpkh' || first.type === 'wsh')
|
|
276
|
+
txType = 'segwit';
|
|
277
|
+
if (first.type === 'sh') {
|
|
278
|
+
if (!input.redeemScript)
|
|
279
|
+
throw new Error('inputType: sh without redeemScript');
|
|
280
|
+
let child = payment_ts_1.OutScript.decode(input.redeemScript);
|
|
281
|
+
if (child.type === 'wpkh' || child.type === 'wsh')
|
|
282
|
+
txType = 'segwit';
|
|
283
|
+
stack.push(child);
|
|
284
|
+
cur = child;
|
|
285
|
+
type += `-${child.type}`;
|
|
286
|
+
}
|
|
287
|
+
// wsh can be inside sh
|
|
288
|
+
if (cur.type === 'wsh') {
|
|
289
|
+
if (!input.witnessScript)
|
|
290
|
+
throw new Error('inputType: wsh without witnessScript');
|
|
291
|
+
let child = payment_ts_1.OutScript.decode(input.witnessScript);
|
|
292
|
+
if (child.type === 'wsh')
|
|
293
|
+
txType = 'segwit';
|
|
294
|
+
stack.push(child);
|
|
295
|
+
cur = child;
|
|
296
|
+
type += `-${child.type}`;
|
|
297
|
+
}
|
|
298
|
+
const last = stack[stack.length - 1];
|
|
299
|
+
if (last.type === 'sh' || last.type === 'wsh')
|
|
300
|
+
throw new Error('inputType: sh/wsh cannot be terminal type');
|
|
301
|
+
const lastScript = payment_ts_1.OutScript.encode(last);
|
|
302
|
+
const res = {
|
|
303
|
+
type,
|
|
304
|
+
txType,
|
|
305
|
+
last,
|
|
306
|
+
lastScript,
|
|
307
|
+
defaultSighash,
|
|
308
|
+
sighash: input.sighashType || defaultSighash,
|
|
309
|
+
};
|
|
310
|
+
if (txType === 'legacy' && !allowLegacyWitnessUtxo && !input.nonWitnessUtxo) {
|
|
311
|
+
throw new Error(`Transaction/sign: legacy input without nonWitnessUtxo, can result in attack that forces paying higher fees. Pass allowLegacyWitnessUtxo=true, if you sure`);
|
|
312
|
+
}
|
|
313
|
+
return res;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
169
316
|
class Transaction {
|
|
170
317
|
constructor(opts = {}) {
|
|
171
318
|
this.global = {};
|
|
@@ -179,7 +326,7 @@ class Transaction {
|
|
|
179
326
|
}
|
|
180
327
|
// Import
|
|
181
328
|
static fromRaw(raw, opts = {}) {
|
|
182
|
-
const parsed =
|
|
329
|
+
const parsed = script_ts_1.RawTx.decode(raw);
|
|
183
330
|
const tx = new Transaction({ ...opts, version: parsed.version, lockTime: parsed.lockTime });
|
|
184
331
|
for (const o of parsed.outputs)
|
|
185
332
|
tx.addOutput(o);
|
|
@@ -215,7 +362,7 @@ class Transaction {
|
|
|
215
362
|
const tx = new Transaction({ ...opts, version, lockTime, PSBTVersion });
|
|
216
363
|
// We need slice here, because otherwise
|
|
217
364
|
const inputCount = PSBTVersion === 0 ? unsigned?.inputs.length : parsed.global.inputCount;
|
|
218
|
-
tx.inputs = parsed.inputs.slice(0, inputCount).map((i, j) => ({
|
|
365
|
+
tx.inputs = parsed.inputs.slice(0, inputCount).map((i, j) => validateInput({
|
|
219
366
|
finalScriptSig: P.EMPTY,
|
|
220
367
|
...parsed.global.unsignedTx?.inputs[j],
|
|
221
368
|
...i,
|
|
@@ -238,7 +385,7 @@ class Transaction {
|
|
|
238
385
|
// 'PSBT version=0 export for transaction without inputs disabled, please use version=2. Please check `toPSBT` method for explanation.'
|
|
239
386
|
// );
|
|
240
387
|
// }
|
|
241
|
-
const inputs = this.inputs.map((i) => psbt.cleanPSBTFields(PSBTVersion, psbt.PSBTInput, i));
|
|
388
|
+
const inputs = this.inputs.map((i) => validateInput(psbt.cleanPSBTFields(PSBTVersion, psbt.PSBTInput, i)));
|
|
242
389
|
for (const inp of inputs) {
|
|
243
390
|
// Don't serialize empty fields
|
|
244
391
|
if (inp.partialSig && !inp.partialSig.length)
|
|
@@ -257,7 +404,7 @@ class Transaction {
|
|
|
257
404
|
- Parsing of PSBTv0 depends on unsignedTx (it looks for input count here)
|
|
258
405
|
- BIP-174 requires old serialization format (without witnesses) inside global, which solves this
|
|
259
406
|
*/
|
|
260
|
-
global.unsignedTx =
|
|
407
|
+
global.unsignedTx = script_ts_1.RawOldTx.decode(script_ts_1.RawOldTx.encode({
|
|
261
408
|
version: this.version,
|
|
262
409
|
lockTime: this.lockTime,
|
|
263
410
|
inputs: this.inputs.map(inputBeforeSign).map((i) => ({
|
|
@@ -402,25 +549,25 @@ class Transaction {
|
|
|
402
549
|
let out = 32;
|
|
403
550
|
// Outputs
|
|
404
551
|
const outputs = this.outputs.map(outputBeforeSign);
|
|
405
|
-
out += 4 *
|
|
552
|
+
out += 4 * script_ts_1.CompactSizeLen.encode(this.outputs.length).length;
|
|
406
553
|
for (const o of outputs)
|
|
407
|
-
out += 32 + 4 *
|
|
554
|
+
out += 32 + 4 * script_ts_1.VarBytes.encode(o.script).length;
|
|
408
555
|
// Inputs
|
|
409
556
|
if (this.hasWitnesses)
|
|
410
557
|
out += 2;
|
|
411
|
-
out += 4 *
|
|
558
|
+
out += 4 * script_ts_1.CompactSizeLen.encode(this.inputs.length).length;
|
|
412
559
|
for (const i of this.inputs) {
|
|
413
|
-
out += 160 + 4 *
|
|
560
|
+
out += 160 + 4 * script_ts_1.VarBytes.encode(i.finalScriptSig || P.EMPTY).length;
|
|
414
561
|
if (this.hasWitnesses && i.finalScriptWitness)
|
|
415
|
-
out +=
|
|
562
|
+
out += script_ts_1.RawWitness.encode(i.finalScriptWitness).length;
|
|
416
563
|
}
|
|
417
564
|
return out;
|
|
418
565
|
}
|
|
419
566
|
get vsize() {
|
|
420
|
-
return (0,
|
|
567
|
+
return (0, exports.toVsize)(this.weight);
|
|
421
568
|
}
|
|
422
569
|
toBytes(withScriptSig = false, withWitness = false) {
|
|
423
|
-
return
|
|
570
|
+
return script_ts_1.RawTx.encode({
|
|
424
571
|
version: this.version,
|
|
425
572
|
lockTime: this.lockTime,
|
|
426
573
|
inputs: this.inputs.map(inputBeforeSign).map((i) => ({
|
|
@@ -464,7 +611,7 @@ class Transaction {
|
|
|
464
611
|
addInput(input, _ignoreSignStatus = false) {
|
|
465
612
|
if (!_ignoreSignStatus && !this.signStatus().addInput)
|
|
466
613
|
throw new Error('Tx has signed inputs, cannot add new one');
|
|
467
|
-
this.inputs.push(
|
|
614
|
+
this.inputs.push(normalizeInput(input, undefined, undefined, this.opts.disableScriptCheck));
|
|
468
615
|
return this.inputs.length - 1;
|
|
469
616
|
}
|
|
470
617
|
updateInput(idx, input, _ignoreSignStatus = false) {
|
|
@@ -475,7 +622,7 @@ class Transaction {
|
|
|
475
622
|
if (!status.addInput || status.inputs.includes(idx))
|
|
476
623
|
allowedFields = psbt.PSBTInputUnsignedKeys;
|
|
477
624
|
}
|
|
478
|
-
this.inputs[idx] =
|
|
625
|
+
this.inputs[idx] = normalizeInput(input, this.inputs[idx], allowedFields, this.opts.disableScriptCheck, this.opts.allowUnknown);
|
|
479
626
|
}
|
|
480
627
|
// Output stuff
|
|
481
628
|
checkOutputIdx(idx) {
|
|
@@ -486,11 +633,11 @@ class Transaction {
|
|
|
486
633
|
this.checkOutputIdx(idx);
|
|
487
634
|
return cloneDeep(this.outputs[idx]);
|
|
488
635
|
}
|
|
489
|
-
getOutputAddress(idx, network =
|
|
636
|
+
getOutputAddress(idx, network = utils_ts_1.NETWORK) {
|
|
490
637
|
const out = this.getOutput(idx);
|
|
491
638
|
if (!out.script)
|
|
492
639
|
return;
|
|
493
|
-
return (0,
|
|
640
|
+
return (0, payment_ts_1.Address)(network).encode(payment_ts_1.OutScript.decode(out.script));
|
|
494
641
|
}
|
|
495
642
|
get outputsLength() {
|
|
496
643
|
return this.outputs.length;
|
|
@@ -512,11 +659,11 @@ class Transaction {
|
|
|
512
659
|
psbt.PSBTOutputCoder.encode(res);
|
|
513
660
|
if (res.script &&
|
|
514
661
|
!this.opts.allowUnknownOutputs &&
|
|
515
|
-
|
|
662
|
+
payment_ts_1.OutScript.decode(res.script).type === 'unknown') {
|
|
516
663
|
throw new Error('Transaction/output: unknown output script type, there is a chance that input is unspendable. Pass allowUnknownOutputs=true, if you sure');
|
|
517
664
|
}
|
|
518
665
|
if (!this.opts.disableScriptCheck)
|
|
519
|
-
(0,
|
|
666
|
+
(0, payment_ts_1.checkScript)(res.script, res.redeemScript, res.witnessScript);
|
|
520
667
|
return res;
|
|
521
668
|
}
|
|
522
669
|
addOutput(o, _ignoreSignStatus = false) {
|
|
@@ -535,14 +682,14 @@ class Transaction {
|
|
|
535
682
|
}
|
|
536
683
|
this.outputs[idx] = this.normalizeOutput(output, this.outputs[idx], allowedFields);
|
|
537
684
|
}
|
|
538
|
-
addOutputAddress(address, amount, network =
|
|
539
|
-
return this.addOutput({ script:
|
|
685
|
+
addOutputAddress(address, amount, network = utils_ts_1.NETWORK) {
|
|
686
|
+
return this.addOutput({ script: payment_ts_1.OutScript.encode((0, payment_ts_1.Address)(network).decode(address)), amount });
|
|
540
687
|
}
|
|
541
688
|
// Utils
|
|
542
689
|
get fee() {
|
|
543
690
|
let res = 0n;
|
|
544
691
|
for (const i of this.inputs) {
|
|
545
|
-
const prevOut =
|
|
692
|
+
const prevOut = getPrevOut(i);
|
|
546
693
|
if (!prevOut)
|
|
547
694
|
throw new Error('Empty input amount');
|
|
548
695
|
res += prevOut.amount;
|
|
@@ -562,7 +709,7 @@ class Transaction {
|
|
|
562
709
|
throw new Error(`Invalid input idx=${idx}`);
|
|
563
710
|
if ((isSingle && idx >= this.outputs.length) || idx >= this.inputs.length)
|
|
564
711
|
return P.U256BE.encode(1n);
|
|
565
|
-
prevOutScript =
|
|
712
|
+
prevOutScript = script_ts_1.Script.encode(script_ts_1.Script.decode(prevOutScript).filter((i) => i !== 'CODESEPARATOR'));
|
|
566
713
|
let inputs = this.inputs
|
|
567
714
|
.map(inputBeforeSign)
|
|
568
715
|
.map((input, inputIdx) => ({
|
|
@@ -583,7 +730,7 @@ class Transaction {
|
|
|
583
730
|
else if (isSingle) {
|
|
584
731
|
outputs = outputs.slice(0, idx).fill(EMPTY_OUTPUT).concat([outputs[idx]]);
|
|
585
732
|
}
|
|
586
|
-
const tmpTx =
|
|
733
|
+
const tmpTx = script_ts_1.RawTx.encode({
|
|
587
734
|
lockTime: this.lockTime,
|
|
588
735
|
version: this.version,
|
|
589
736
|
segwitFlag: false,
|
|
@@ -604,12 +751,12 @@ class Transaction {
|
|
|
604
751
|
if (!isAny && !isSingle && !isNone)
|
|
605
752
|
sequenceHash = u.sha256x2(...inputs.map((i) => P.U32LE.encode(i.sequence)));
|
|
606
753
|
if (!isSingle && !isNone) {
|
|
607
|
-
outputHash = u.sha256x2(...outputs.map(
|
|
754
|
+
outputHash = u.sha256x2(...outputs.map(script_ts_1.RawOutput.encode));
|
|
608
755
|
}
|
|
609
756
|
else if (isSingle && idx < outputs.length)
|
|
610
|
-
outputHash = u.sha256x2(
|
|
757
|
+
outputHash = u.sha256x2(script_ts_1.RawOutput.encode(outputs[idx]));
|
|
611
758
|
const input = inputs[idx];
|
|
612
|
-
return u.sha256x2(P.I32LE.encode(this.version), inputHash, sequenceHash, P.bytes(32, true).encode(input.txid), P.U32LE.encode(input.index),
|
|
759
|
+
return u.sha256x2(P.I32LE.encode(this.version), inputHash, sequenceHash, P.bytes(32, true).encode(input.txid), P.U32LE.encode(input.index), script_ts_1.VarBytes.encode(prevOutScript), P.U64LE.encode(amount), P.U32LE.encode(input.sequence), outputHash, P.U32LE.encode(this.lockTime), P.U32LE.encode(hashType));
|
|
613
760
|
}
|
|
614
761
|
preimageWitnessV1(idx, prevOutScript, hashType, amount, codeSeparator = -1, leafScript, leafVer = 0xc0, annex) {
|
|
615
762
|
if (!Array.isArray(amount) || this.inputs.length !== amount.length)
|
|
@@ -630,36 +777,36 @@ class Transaction {
|
|
|
630
777
|
out.push(...[
|
|
631
778
|
inputs.map(TxHashIdx.encode),
|
|
632
779
|
amount.map(P.U64LE.encode),
|
|
633
|
-
prevOutScript.map(
|
|
780
|
+
prevOutScript.map(script_ts_1.VarBytes.encode),
|
|
634
781
|
inputs.map((i) => P.U32LE.encode(i.sequence)),
|
|
635
|
-
].map((i) => u.sha256((0,
|
|
782
|
+
].map((i) => u.sha256((0, utils_ts_1.concatBytes)(...i))));
|
|
636
783
|
}
|
|
637
784
|
if (outType === SignatureHash.ALL) {
|
|
638
|
-
out.push(u.sha256((0,
|
|
785
|
+
out.push(u.sha256((0, utils_ts_1.concatBytes)(...outputs.map(script_ts_1.RawOutput.encode))));
|
|
639
786
|
}
|
|
640
787
|
const spendType = (annex ? 1 : 0) | (leafScript ? 2 : 0);
|
|
641
788
|
out.push(new Uint8Array([spendType]));
|
|
642
789
|
if (inType === SignatureHash.ANYONECANPAY) {
|
|
643
790
|
const inp = inputs[idx];
|
|
644
|
-
out.push(TxHashIdx.encode(inp), P.U64LE.encode(amount[idx]),
|
|
791
|
+
out.push(TxHashIdx.encode(inp), P.U64LE.encode(amount[idx]), script_ts_1.VarBytes.encode(prevOutScript[idx]), P.U32LE.encode(inp.sequence));
|
|
645
792
|
}
|
|
646
793
|
else
|
|
647
794
|
out.push(P.U32LE.encode(idx));
|
|
648
795
|
if (spendType & 1)
|
|
649
|
-
out.push(u.sha256(
|
|
796
|
+
out.push(u.sha256(script_ts_1.VarBytes.encode(annex || P.EMPTY)));
|
|
650
797
|
if (outType === SignatureHash.SINGLE)
|
|
651
|
-
out.push(idx < outputs.length ? u.sha256(
|
|
798
|
+
out.push(idx < outputs.length ? u.sha256(script_ts_1.RawOutput.encode(outputs[idx])) : EMPTY32);
|
|
652
799
|
if (leafScript)
|
|
653
|
-
out.push((0,
|
|
800
|
+
out.push((0, payment_ts_1.tapLeafHash)(leafScript, leafVer), P.U8.encode(0), P.I32LE.encode(codeSeparator));
|
|
654
801
|
return u.tagSchnorr('TapSighash', ...out);
|
|
655
802
|
}
|
|
656
803
|
// Signer can be privateKey OR instance of bip32 HD stuff
|
|
657
804
|
signIdx(privateKey, idx, allowedSighash, _auxRand) {
|
|
658
805
|
this.checkInputIdx(idx);
|
|
659
806
|
const input = this.inputs[idx];
|
|
660
|
-
const inputType =
|
|
807
|
+
const inputType = getInputType(input, this.opts.allowLegacyWitnessUtxo);
|
|
661
808
|
// Handle BIP32 HDKey
|
|
662
|
-
if (!(0,
|
|
809
|
+
if (!(0, utils_ts_1.isBytes)(privateKey)) {
|
|
663
810
|
if (!input.bip32Derivation || !input.bip32Derivation.length)
|
|
664
811
|
throw new Error('bip32Derivation: empty');
|
|
665
812
|
const signers = input.bip32Derivation
|
|
@@ -668,7 +815,7 @@ class Transaction {
|
|
|
668
815
|
let s = privateKey;
|
|
669
816
|
for (const i of path)
|
|
670
817
|
s = s.deriveChild(i);
|
|
671
|
-
if (!(0,
|
|
818
|
+
if (!(0, utils_ts_1.equalBytes)(s.publicKey, pubKey))
|
|
672
819
|
throw new Error('bip32Derivation: wrong pubKey');
|
|
673
820
|
if (!s.privateKey)
|
|
674
821
|
throw new Error('bip32Derivation: no privateKey');
|
|
@@ -702,9 +849,9 @@ class Transaction {
|
|
|
702
849
|
}
|
|
703
850
|
// Actual signing
|
|
704
851
|
// Taproot
|
|
705
|
-
const prevOut =
|
|
852
|
+
const prevOut = getPrevOut(input);
|
|
706
853
|
if (inputType.txType === 'taproot') {
|
|
707
|
-
const prevOuts = this.inputs.map(
|
|
854
|
+
const prevOuts = this.inputs.map(getPrevOut);
|
|
708
855
|
const prevOutScript = prevOuts.map((i) => i.script);
|
|
709
856
|
const amount = prevOuts.map((i) => i.amount);
|
|
710
857
|
let signed = false;
|
|
@@ -717,9 +864,9 @@ class Transaction {
|
|
|
717
864
|
// priv key to be provided
|
|
718
865
|
const { pubKey, privKey } = getTaprootKeys(privateKey, schnorrPub, input.tapInternalKey, merkleRoot);
|
|
719
866
|
const [taprootPubKey, _] = u.taprootTweakPubkey(input.tapInternalKey, merkleRoot);
|
|
720
|
-
if ((0,
|
|
867
|
+
if ((0, utils_ts_1.equalBytes)(taprootPubKey, pubKey)) {
|
|
721
868
|
const hash = this.preimageWitnessV1(idx, prevOutScript, sighash, amount);
|
|
722
|
-
const sig = (0,
|
|
869
|
+
const sig = (0, utils_ts_1.concatBytes)(u.signSchnorr(hash, privKey, _auxRand), sighash !== SignatureHash.DEFAULT ? new Uint8Array([sighash]) : P.EMPTY);
|
|
723
870
|
this.updateInput(idx, { tapKeySig: sig }, true);
|
|
724
871
|
signed = true;
|
|
725
872
|
}
|
|
@@ -728,16 +875,16 @@ class Transaction {
|
|
|
728
875
|
input.tapScriptSig = input.tapScriptSig || [];
|
|
729
876
|
for (const [_, _script] of input.tapLeafScript) {
|
|
730
877
|
const script = _script.subarray(0, -1);
|
|
731
|
-
const scriptDecoded =
|
|
878
|
+
const scriptDecoded = script_ts_1.Script.decode(script);
|
|
732
879
|
const ver = _script[_script.length - 1];
|
|
733
|
-
const hash = (0,
|
|
880
|
+
const hash = (0, payment_ts_1.tapLeafHash)(script, ver);
|
|
734
881
|
// NOTE: no need to tweak internal key here, since we don't support nested p2tr
|
|
735
|
-
const pos = scriptDecoded.findIndex((i) => (0,
|
|
882
|
+
const pos = scriptDecoded.findIndex((i) => (0, utils_ts_1.isBytes)(i) && (0, utils_ts_1.equalBytes)(i, schnorrPub));
|
|
736
883
|
// Skip if there is no public key in tapLeafScript
|
|
737
884
|
if (pos === -1)
|
|
738
885
|
continue;
|
|
739
886
|
const msg = this.preimageWitnessV1(idx, prevOutScript, sighash, amount, undefined, script, ver);
|
|
740
|
-
const sig = (0,
|
|
887
|
+
const sig = (0, utils_ts_1.concatBytes)(u.signSchnorr(msg, privateKey, _auxRand), sighash !== SignatureHash.DEFAULT ? new Uint8Array([sighash]) : P.EMPTY);
|
|
741
888
|
this.updateInput(idx, { tapScriptSig: [[{ pubKey: schnorrPub, leafHash: hash }, sig]] }, true);
|
|
742
889
|
signed = true;
|
|
743
890
|
}
|
|
@@ -753,8 +900,8 @@ class Transaction {
|
|
|
753
900
|
// Check if script has public key or its has inside
|
|
754
901
|
let hasPubkey = false;
|
|
755
902
|
const pubKeyHash = u.hash160(pubKey);
|
|
756
|
-
for (const i of
|
|
757
|
-
if ((0,
|
|
903
|
+
for (const i of script_ts_1.Script.decode(inputType.lastScript)) {
|
|
904
|
+
if ((0, utils_ts_1.isBytes)(i) && ((0, utils_ts_1.equalBytes)(i, pubKey) || (0, utils_ts_1.equalBytes)(i, pubKeyHash)))
|
|
758
905
|
hasPubkey = true;
|
|
759
906
|
}
|
|
760
907
|
if (!hasPubkey)
|
|
@@ -767,14 +914,14 @@ class Transaction {
|
|
|
767
914
|
let script = inputType.lastScript;
|
|
768
915
|
// If wpkh OR sh-wpkh, wsh-wpkh is impossible, so looks ok
|
|
769
916
|
if (inputType.last.type === 'wpkh')
|
|
770
|
-
script =
|
|
917
|
+
script = payment_ts_1.OutScript.encode({ type: 'pkh', hash: inputType.last.hash });
|
|
771
918
|
hash = this.preimageWitnessV0(idx, script, sighash, prevOut.amount);
|
|
772
919
|
}
|
|
773
920
|
else
|
|
774
921
|
throw new Error(`Transaction/sign: unknown tx type: ${inputType.txType}`);
|
|
775
922
|
const sig = u.signECDSA(hash, privateKey, this.opts.lowR);
|
|
776
923
|
this.updateInput(idx, {
|
|
777
|
-
partialSig: [[pubKey, (0,
|
|
924
|
+
partialSig: [[pubKey, (0, utils_ts_1.concatBytes)(sig, new Uint8Array([sighash]))]],
|
|
778
925
|
}, true);
|
|
779
926
|
}
|
|
780
927
|
return true;
|
|
@@ -804,7 +951,7 @@ class Transaction {
|
|
|
804
951
|
if (this.fee < 0n)
|
|
805
952
|
throw new Error('Outputs spends more than inputs amount');
|
|
806
953
|
const input = this.inputs[idx];
|
|
807
|
-
const inputType =
|
|
954
|
+
const inputType = getInputType(input, this.opts.allowLegacyWitnessUtxo);
|
|
808
955
|
// Taproot finalize
|
|
809
956
|
if (inputType.txType === 'taproot') {
|
|
810
957
|
if (input.tapKeySig)
|
|
@@ -817,16 +964,16 @@ class Transaction {
|
|
|
817
964
|
// Last byte is version
|
|
818
965
|
const script = _script.slice(0, -1);
|
|
819
966
|
const ver = _script[_script.length - 1];
|
|
820
|
-
const outScript =
|
|
821
|
-
const hash = (0,
|
|
822
|
-
const scriptSig = input.tapScriptSig.filter((i) => (0,
|
|
967
|
+
const outScript = payment_ts_1.OutScript.decode(script);
|
|
968
|
+
const hash = (0, payment_ts_1.tapLeafHash)(script, ver);
|
|
969
|
+
const scriptSig = input.tapScriptSig.filter((i) => (0, utils_ts_1.equalBytes)(i[0].leafHash, hash));
|
|
823
970
|
let signatures = [];
|
|
824
971
|
if (outScript.type === 'tr_ms') {
|
|
825
972
|
const m = outScript.m;
|
|
826
973
|
const pubkeys = outScript.pubkeys;
|
|
827
974
|
let added = 0;
|
|
828
975
|
for (const pub of pubkeys) {
|
|
829
|
-
const sigIdx = scriptSig.findIndex((i) => (0,
|
|
976
|
+
const sigIdx = scriptSig.findIndex((i) => (0, utils_ts_1.equalBytes)(i[0].pubKey, pub));
|
|
830
977
|
// Should have exact amount of signatures (more -- will fail)
|
|
831
978
|
if (added === m || sigIdx === -1) {
|
|
832
979
|
signatures.push(P.EMPTY);
|
|
@@ -841,7 +988,7 @@ class Transaction {
|
|
|
841
988
|
}
|
|
842
989
|
else if (outScript.type === 'tr_ns') {
|
|
843
990
|
for (const pub of outScript.pubkeys) {
|
|
844
|
-
const sigIdx = scriptSig.findIndex((i) => (0,
|
|
991
|
+
const sigIdx = scriptSig.findIndex((i) => (0, utils_ts_1.equalBytes)(i[0].pubKey, pub));
|
|
845
992
|
if (sigIdx === -1)
|
|
846
993
|
continue;
|
|
847
994
|
signatures.push(scriptSig[sigIdx][1]);
|
|
@@ -851,10 +998,10 @@ class Transaction {
|
|
|
851
998
|
}
|
|
852
999
|
else if (outScript.type === 'unknown' && this.opts.allowUnknownInputs) {
|
|
853
1000
|
// Trying our best to sign what we can
|
|
854
|
-
const scriptDecoded =
|
|
1001
|
+
const scriptDecoded = script_ts_1.Script.decode(script);
|
|
855
1002
|
signatures = scriptSig
|
|
856
1003
|
.map(([{ pubKey }, signature]) => {
|
|
857
|
-
const pos = scriptDecoded.findIndex((i) => (0,
|
|
1004
|
+
const pos = scriptDecoded.findIndex((i) => (0, utils_ts_1.isBytes)(i) && (0, utils_ts_1.equalBytes)(i, pubKey));
|
|
858
1005
|
if (pos === -1)
|
|
859
1006
|
throw new Error('finalize/taproot: cannot find position of pubkey in script');
|
|
860
1007
|
return { signature, pos };
|
|
@@ -871,7 +1018,7 @@ class Transaction {
|
|
|
871
1018
|
for (const c of custom) {
|
|
872
1019
|
if (!c.finalizeTaproot)
|
|
873
1020
|
continue;
|
|
874
|
-
const scriptDecoded =
|
|
1021
|
+
const scriptDecoded = script_ts_1.Script.decode(script);
|
|
875
1022
|
const csEncoded = c.encode(scriptDecoded);
|
|
876
1023
|
if (csEncoded === undefined)
|
|
877
1024
|
continue;
|
|
@@ -913,7 +1060,7 @@ class Transaction {
|
|
|
913
1060
|
let signatures = [];
|
|
914
1061
|
// partial: [pubkey, sign]
|
|
915
1062
|
for (const pub of pubkeys) {
|
|
916
|
-
const sign = input.partialSig.find((s) => (0,
|
|
1063
|
+
const sign = input.partialSig.find((s) => (0, utils_ts_1.equalBytes)(pub, s[0]));
|
|
917
1064
|
if (!sign)
|
|
918
1065
|
continue;
|
|
919
1066
|
signatures.push(sign[1]);
|
|
@@ -922,13 +1069,13 @@ class Transaction {
|
|
|
922
1069
|
if (signatures.length !== m) {
|
|
923
1070
|
throw new Error(`Multisig: wrong signatures count, m=${m} n=${pubkeys.length} signatures=${signatures.length}`);
|
|
924
1071
|
}
|
|
925
|
-
inputScript =
|
|
1072
|
+
inputScript = script_ts_1.Script.encode([0, ...signatures]);
|
|
926
1073
|
}
|
|
927
1074
|
else if (inputType.last.type === 'pk') {
|
|
928
|
-
inputScript =
|
|
1075
|
+
inputScript = script_ts_1.Script.encode([input.partialSig[0][1]]);
|
|
929
1076
|
}
|
|
930
1077
|
else if (inputType.last.type === 'pkh') {
|
|
931
|
-
inputScript =
|
|
1078
|
+
inputScript = script_ts_1.Script.encode([input.partialSig[0][1], input.partialSig[0][0]]);
|
|
932
1079
|
}
|
|
933
1080
|
else if (inputType.last.type === 'wpkh') {
|
|
934
1081
|
inputScript = P.EMPTY;
|
|
@@ -941,10 +1088,10 @@ class Transaction {
|
|
|
941
1088
|
if (inputType.type.includes('wsh-')) {
|
|
942
1089
|
// P2WSH
|
|
943
1090
|
if (inputScript.length && inputType.lastScript.length) {
|
|
944
|
-
witness =
|
|
1091
|
+
witness = script_ts_1.Script.decode(inputScript).map((i) => {
|
|
945
1092
|
if (i === 0)
|
|
946
1093
|
return P.EMPTY;
|
|
947
|
-
if ((0,
|
|
1094
|
+
if ((0, utils_ts_1.isBytes)(i))
|
|
948
1095
|
return i;
|
|
949
1096
|
throw new Error(`Wrong witness op=${i}`);
|
|
950
1097
|
});
|
|
@@ -954,10 +1101,10 @@ class Transaction {
|
|
|
954
1101
|
if (inputType.txType === 'segwit')
|
|
955
1102
|
finalScriptWitness = witness;
|
|
956
1103
|
if (inputType.type.startsWith('sh-wsh-')) {
|
|
957
|
-
finalScriptSig =
|
|
1104
|
+
finalScriptSig = script_ts_1.Script.encode([script_ts_1.Script.encode([0, u.sha256(inputType.lastScript)])]);
|
|
958
1105
|
}
|
|
959
1106
|
else if (inputType.type.startsWith('sh-')) {
|
|
960
|
-
finalScriptSig =
|
|
1107
|
+
finalScriptSig = script_ts_1.Script.encode([...script_ts_1.Script.decode(inputScript), inputType.lastScript]);
|
|
961
1108
|
}
|
|
962
1109
|
else if (inputType.type.startsWith('wsh-')) {
|
|
963
1110
|
}
|
|
@@ -995,11 +1142,11 @@ class Transaction {
|
|
|
995
1142
|
throw new Error(`Transaction/combine: different ${k} length this=${this[k].length} other=${other[k].length}`);
|
|
996
1143
|
}
|
|
997
1144
|
}
|
|
998
|
-
const thisUnsigned = this.global.unsignedTx ?
|
|
1145
|
+
const thisUnsigned = this.global.unsignedTx ? script_ts_1.RawOldTx.encode(this.global.unsignedTx) : P.EMPTY;
|
|
999
1146
|
const otherUnsigned = other.global.unsignedTx
|
|
1000
|
-
?
|
|
1147
|
+
? script_ts_1.RawOldTx.encode(other.global.unsignedTx)
|
|
1001
1148
|
: P.EMPTY;
|
|
1002
|
-
if (!(0,
|
|
1149
|
+
if (!(0, utils_ts_1.equalBytes)(thisUnsigned, otherUnsigned))
|
|
1003
1150
|
throw new Error(`Transaction/combine: different unsigned tx`);
|
|
1004
1151
|
this.global = psbt.mergeKeyMap(psbt.PSBTGlobal, this.global, other.global, undefined, this.opts.allowUnknown);
|
|
1005
1152
|
for (let i = 0; i < this.inputs.length; i++)
|