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