1 | 'use strict';
|
2 | Object.defineProperty(exports, '__esModule', { value: true });
|
3 | const bip174_1 = require('bip174');
|
4 | const varuint = require('bip174/src/lib/converter/varint');
|
5 | const utils_1 = require('bip174/src/lib/utils');
|
6 | const address_1 = require('./address');
|
7 | const bufferutils_1 = require('./bufferutils');
|
8 | const crypto_1 = require('./crypto');
|
9 | const ecpair_1 = require('./ecpair');
|
10 | const networks_1 = require('./networks');
|
11 | const payments = require('./payments');
|
12 | const bscript = require('./script');
|
13 | const transaction_1 = require('./transaction');
|
14 |
|
15 |
|
16 |
|
17 | const DEFAULT_OPTS = {
|
18 | |
19 |
|
20 |
|
21 |
|
22 | network: networks_1.bitcoin,
|
23 | |
24 |
|
25 |
|
26 |
|
27 |
|
28 | maximumFeeRate: 5000,
|
29 | };
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 | class Psbt {
|
63 | constructor(opts = {}, data = new bip174_1.Psbt(new PsbtTransaction())) {
|
64 | this.data = data;
|
65 |
|
66 | this.opts = Object.assign({}, DEFAULT_OPTS, opts);
|
67 | this.__CACHE = {
|
68 | __NON_WITNESS_UTXO_TX_CACHE: [],
|
69 | __NON_WITNESS_UTXO_BUF_CACHE: [],
|
70 | __TX_IN_CACHE: {},
|
71 | __TX: this.data.globalMap.unsignedTx.tx,
|
72 | };
|
73 | if (this.data.inputs.length === 0) this.setVersion(2);
|
74 |
|
75 | const dpew = (obj, attr, enumerable, writable) =>
|
76 | Object.defineProperty(obj, attr, {
|
77 | enumerable,
|
78 | writable,
|
79 | });
|
80 | dpew(this, '__CACHE', false, true);
|
81 | dpew(this, 'opts', false, true);
|
82 | }
|
83 | static fromBase64(data, opts = {}) {
|
84 | const buffer = Buffer.from(data, 'base64');
|
85 | return this.fromBuffer(buffer, opts);
|
86 | }
|
87 | static fromHex(data, opts = {}) {
|
88 | const buffer = Buffer.from(data, 'hex');
|
89 | return this.fromBuffer(buffer, opts);
|
90 | }
|
91 | static fromBuffer(buffer, opts = {}) {
|
92 | const psbtBase = bip174_1.Psbt.fromBuffer(buffer, transactionFromBuffer);
|
93 | const psbt = new Psbt(opts, psbtBase);
|
94 | checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE);
|
95 | return psbt;
|
96 | }
|
97 | get inputCount() {
|
98 | return this.data.inputs.length;
|
99 | }
|
100 | combine(...those) {
|
101 | this.data.combine(...those.map(o => o.data));
|
102 | return this;
|
103 | }
|
104 | clone() {
|
105 |
|
106 | const res = Psbt.fromBuffer(this.data.toBuffer());
|
107 | res.opts = JSON.parse(JSON.stringify(this.opts));
|
108 | return res;
|
109 | }
|
110 | setMaximumFeeRate(satoshiPerByte) {
|
111 | check32Bit(satoshiPerByte);
|
112 | this.opts.maximumFeeRate = satoshiPerByte;
|
113 | }
|
114 | setVersion(version) {
|
115 | check32Bit(version);
|
116 | checkInputsForPartialSig(this.data.inputs, 'setVersion');
|
117 | const c = this.__CACHE;
|
118 | c.__TX.version = version;
|
119 | c.__EXTRACTED_TX = undefined;
|
120 | return this;
|
121 | }
|
122 | setLocktime(locktime) {
|
123 | check32Bit(locktime);
|
124 | checkInputsForPartialSig(this.data.inputs, 'setLocktime');
|
125 | const c = this.__CACHE;
|
126 | c.__TX.locktime = locktime;
|
127 | c.__EXTRACTED_TX = undefined;
|
128 | return this;
|
129 | }
|
130 | setInputSequence(inputIndex, sequence) {
|
131 | check32Bit(sequence);
|
132 | checkInputsForPartialSig(this.data.inputs, 'setInputSequence');
|
133 | const c = this.__CACHE;
|
134 | if (c.__TX.ins.length <= inputIndex) {
|
135 | throw new Error('Input index too high');
|
136 | }
|
137 | c.__TX.ins[inputIndex].sequence = sequence;
|
138 | c.__EXTRACTED_TX = undefined;
|
139 | return this;
|
140 | }
|
141 | addInputs(inputDatas) {
|
142 | inputDatas.forEach(inputData => this.addInput(inputData));
|
143 | return this;
|
144 | }
|
145 | addInput(inputData) {
|
146 | checkInputsForPartialSig(this.data.inputs, 'addInput');
|
147 | const c = this.__CACHE;
|
148 | this.data.addInput(inputData);
|
149 | const txIn = c.__TX.ins[c.__TX.ins.length - 1];
|
150 | checkTxInputCache(c, txIn);
|
151 | const inputIndex = this.data.inputs.length - 1;
|
152 | const input = this.data.inputs[inputIndex];
|
153 | if (input.nonWitnessUtxo) {
|
154 | addNonWitnessTxCache(this.__CACHE, input, inputIndex);
|
155 | }
|
156 | c.__FEE = undefined;
|
157 | c.__FEE_RATE = undefined;
|
158 | c.__EXTRACTED_TX = undefined;
|
159 | return this;
|
160 | }
|
161 | addOutputs(outputDatas) {
|
162 | outputDatas.forEach(outputData => this.addOutput(outputData));
|
163 | return this;
|
164 | }
|
165 | addOutput(outputData) {
|
166 | checkInputsForPartialSig(this.data.inputs, 'addOutput');
|
167 | const { address } = outputData;
|
168 | if (typeof address === 'string') {
|
169 | const { network } = this.opts;
|
170 | const script = address_1.toOutputScript(address, network);
|
171 | outputData = Object.assign(outputData, { script });
|
172 | }
|
173 | const c = this.__CACHE;
|
174 | this.data.addOutput(outputData);
|
175 | c.__FEE = undefined;
|
176 | c.__FEE_RATE = undefined;
|
177 | c.__EXTRACTED_TX = undefined;
|
178 | return this;
|
179 | }
|
180 | extractTransaction(disableFeeCheck) {
|
181 | if (!this.data.inputs.every(isFinalized)) throw new Error('Not finalized');
|
182 | const c = this.__CACHE;
|
183 | if (!disableFeeCheck) {
|
184 | checkFees(this, c, this.opts);
|
185 | }
|
186 | if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
|
187 | const tx = c.__TX.clone();
|
188 | inputFinalizeGetAmts(this.data.inputs, tx, c, true);
|
189 | return tx;
|
190 | }
|
191 | getFeeRate() {
|
192 | return getTxCacheValue(
|
193 | '__FEE_RATE',
|
194 | 'fee rate',
|
195 | this.data.inputs,
|
196 | this.__CACHE,
|
197 | );
|
198 | }
|
199 | getFee() {
|
200 | return getTxCacheValue('__FEE', 'fee', this.data.inputs, this.__CACHE);
|
201 | }
|
202 | finalizeAllInputs() {
|
203 | utils_1.checkForInput(this.data.inputs, 0);
|
204 | range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx));
|
205 | return this;
|
206 | }
|
207 | finalizeInput(inputIndex) {
|
208 | const input = utils_1.checkForInput(this.data.inputs, inputIndex);
|
209 | const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
|
210 | inputIndex,
|
211 | input,
|
212 | this.__CACHE,
|
213 | );
|
214 | if (!script) throw new Error(`No script found for input #${inputIndex}`);
|
215 | const scriptType = classifyScript(script);
|
216 | if (!canFinalize(input, script, scriptType))
|
217 | throw new Error(`Can not finalize input #${inputIndex}`);
|
218 | checkPartialSigSighashes(input);
|
219 | const { finalScriptSig, finalScriptWitness } = getFinalScripts(
|
220 | script,
|
221 | scriptType,
|
222 | input.partialSig,
|
223 | isSegwit,
|
224 | isP2SH,
|
225 | isP2WSH,
|
226 | );
|
227 | if (finalScriptSig) this.data.updateInput(inputIndex, { finalScriptSig });
|
228 | if (finalScriptWitness)
|
229 | this.data.updateInput(inputIndex, { finalScriptWitness });
|
230 | if (!finalScriptSig && !finalScriptWitness)
|
231 | throw new Error(`Unknown error finalizing input #${inputIndex}`);
|
232 | this.data.clearFinalizedInput(inputIndex);
|
233 | return this;
|
234 | }
|
235 | validateSignaturesOfAllInputs() {
|
236 | utils_1.checkForInput(this.data.inputs, 0);
|
237 | const results = range(this.data.inputs.length).map(idx =>
|
238 | this.validateSignaturesOfInput(idx),
|
239 | );
|
240 | return results.reduce((final, res) => res === true && final, true);
|
241 | }
|
242 | validateSignaturesOfInput(inputIndex, pubkey) {
|
243 | const input = this.data.inputs[inputIndex];
|
244 | const partialSig = (input || {}).partialSig;
|
245 | if (!input || !partialSig || partialSig.length < 1)
|
246 | throw new Error('No signatures to validate');
|
247 | const mySigs = pubkey
|
248 | ? partialSig.filter(sig => sig.pubkey.equals(pubkey))
|
249 | : partialSig;
|
250 | if (mySigs.length < 1) throw new Error('No signatures for this pubkey');
|
251 | const results = [];
|
252 | let hashCache;
|
253 | let scriptCache;
|
254 | let sighashCache;
|
255 | for (const pSig of mySigs) {
|
256 | const sig = bscript.signature.decode(pSig.signature);
|
257 | const { hash, script } =
|
258 | sighashCache !== sig.hashType
|
259 | ? getHashForSig(
|
260 | inputIndex,
|
261 | Object.assign({}, input, { sighashType: sig.hashType }),
|
262 | this.__CACHE,
|
263 | )
|
264 | : { hash: hashCache, script: scriptCache };
|
265 | sighashCache = sig.hashType;
|
266 | hashCache = hash;
|
267 | scriptCache = script;
|
268 | checkScriptForPubkey(pSig.pubkey, script, 'verify');
|
269 | const keypair = ecpair_1.fromPublicKey(pSig.pubkey);
|
270 | results.push(keypair.verify(hash, sig.signature));
|
271 | }
|
272 | return results.every(res => res === true);
|
273 | }
|
274 | signAllInputsHD(
|
275 | hdKeyPair,
|
276 | sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
|
277 | ) {
|
278 | if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
279 | throw new Error('Need HDSigner to sign input');
|
280 | }
|
281 | const results = [];
|
282 | for (const i of range(this.data.inputs.length)) {
|
283 | try {
|
284 | this.signInputHD(i, hdKeyPair, sighashTypes);
|
285 | results.push(true);
|
286 | } catch (err) {
|
287 | results.push(false);
|
288 | }
|
289 | }
|
290 | if (results.every(v => v === false)) {
|
291 | throw new Error('No inputs were signed');
|
292 | }
|
293 | return this;
|
294 | }
|
295 | signAllInputsHDAsync(
|
296 | hdKeyPair,
|
297 | sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
|
298 | ) {
|
299 | return new Promise((resolve, reject) => {
|
300 | if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
301 | return reject(new Error('Need HDSigner to sign input'));
|
302 | }
|
303 | const results = [];
|
304 | const promises = [];
|
305 | for (const i of range(this.data.inputs.length)) {
|
306 | promises.push(
|
307 | this.signInputHDAsync(i, hdKeyPair, sighashTypes).then(
|
308 | () => {
|
309 | results.push(true);
|
310 | },
|
311 | () => {
|
312 | results.push(false);
|
313 | },
|
314 | ),
|
315 | );
|
316 | }
|
317 | return Promise.all(promises).then(() => {
|
318 | if (results.every(v => v === false)) {
|
319 | return reject(new Error('No inputs were signed'));
|
320 | }
|
321 | resolve();
|
322 | });
|
323 | });
|
324 | }
|
325 | signInputHD(
|
326 | inputIndex,
|
327 | hdKeyPair,
|
328 | sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
|
329 | ) {
|
330 | if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
331 | throw new Error('Need HDSigner to sign input');
|
332 | }
|
333 | const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
|
334 | signers.forEach(signer => this.signInput(inputIndex, signer, sighashTypes));
|
335 | return this;
|
336 | }
|
337 | signInputHDAsync(
|
338 | inputIndex,
|
339 | hdKeyPair,
|
340 | sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
|
341 | ) {
|
342 | return new Promise((resolve, reject) => {
|
343 | if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
344 | return reject(new Error('Need HDSigner to sign input'));
|
345 | }
|
346 | const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
|
347 | const promises = signers.map(signer =>
|
348 | this.signInputAsync(inputIndex, signer, sighashTypes),
|
349 | );
|
350 | return Promise.all(promises)
|
351 | .then(() => {
|
352 | resolve();
|
353 | })
|
354 | .catch(reject);
|
355 | });
|
356 | }
|
357 | signAllInputs(
|
358 | keyPair,
|
359 | sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
|
360 | ) {
|
361 | if (!keyPair || !keyPair.publicKey)
|
362 | throw new Error('Need Signer to sign input');
|
363 |
|
364 |
|
365 |
|
366 | const results = [];
|
367 | for (const i of range(this.data.inputs.length)) {
|
368 | try {
|
369 | this.signInput(i, keyPair, sighashTypes);
|
370 | results.push(true);
|
371 | } catch (err) {
|
372 | results.push(false);
|
373 | }
|
374 | }
|
375 | if (results.every(v => v === false)) {
|
376 | throw new Error('No inputs were signed');
|
377 | }
|
378 | return this;
|
379 | }
|
380 | signAllInputsAsync(
|
381 | keyPair,
|
382 | sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
|
383 | ) {
|
384 | return new Promise((resolve, reject) => {
|
385 | if (!keyPair || !keyPair.publicKey)
|
386 | return reject(new Error('Need Signer to sign input'));
|
387 |
|
388 |
|
389 |
|
390 | const results = [];
|
391 | const promises = [];
|
392 | for (const [i] of this.data.inputs.entries()) {
|
393 | promises.push(
|
394 | this.signInputAsync(i, keyPair, sighashTypes).then(
|
395 | () => {
|
396 | results.push(true);
|
397 | },
|
398 | () => {
|
399 | results.push(false);
|
400 | },
|
401 | ),
|
402 | );
|
403 | }
|
404 | return Promise.all(promises).then(() => {
|
405 | if (results.every(v => v === false)) {
|
406 | return reject(new Error('No inputs were signed'));
|
407 | }
|
408 | resolve();
|
409 | });
|
410 | });
|
411 | }
|
412 | signInput(
|
413 | inputIndex,
|
414 | keyPair,
|
415 | sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
|
416 | ) {
|
417 | if (!keyPair || !keyPair.publicKey)
|
418 | throw new Error('Need Signer to sign input');
|
419 | const { hash, sighashType } = getHashAndSighashType(
|
420 | this.data.inputs,
|
421 | inputIndex,
|
422 | keyPair.publicKey,
|
423 | this.__CACHE,
|
424 | sighashTypes,
|
425 | );
|
426 | const partialSig = [
|
427 | {
|
428 | pubkey: keyPair.publicKey,
|
429 | signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
|
430 | },
|
431 | ];
|
432 | this.data.updateInput(inputIndex, { partialSig });
|
433 | return this;
|
434 | }
|
435 | signInputAsync(
|
436 | inputIndex,
|
437 | keyPair,
|
438 | sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
|
439 | ) {
|
440 | return new Promise((resolve, reject) => {
|
441 | if (!keyPair || !keyPair.publicKey)
|
442 | return reject(new Error('Need Signer to sign input'));
|
443 | const { hash, sighashType } = getHashAndSighashType(
|
444 | this.data.inputs,
|
445 | inputIndex,
|
446 | keyPair.publicKey,
|
447 | this.__CACHE,
|
448 | sighashTypes,
|
449 | );
|
450 | Promise.resolve(keyPair.sign(hash)).then(signature => {
|
451 | const partialSig = [
|
452 | {
|
453 | pubkey: keyPair.publicKey,
|
454 | signature: bscript.signature.encode(signature, sighashType),
|
455 | },
|
456 | ];
|
457 | this.data.updateInput(inputIndex, { partialSig });
|
458 | resolve();
|
459 | });
|
460 | });
|
461 | }
|
462 | toBuffer() {
|
463 | return this.data.toBuffer();
|
464 | }
|
465 | toHex() {
|
466 | return this.data.toHex();
|
467 | }
|
468 | toBase64() {
|
469 | return this.data.toBase64();
|
470 | }
|
471 | updateGlobal(updateData) {
|
472 | this.data.updateGlobal(updateData);
|
473 | return this;
|
474 | }
|
475 | updateInput(inputIndex, updateData) {
|
476 | this.data.updateInput(inputIndex, updateData);
|
477 | if (updateData.nonWitnessUtxo) {
|
478 | addNonWitnessTxCache(
|
479 | this.__CACHE,
|
480 | this.data.inputs[inputIndex],
|
481 | inputIndex,
|
482 | );
|
483 | }
|
484 | return this;
|
485 | }
|
486 | updateOutput(outputIndex, updateData) {
|
487 | this.data.updateOutput(outputIndex, updateData);
|
488 | return this;
|
489 | }
|
490 | addUnknownKeyValToGlobal(keyVal) {
|
491 | this.data.addUnknownKeyValToGlobal(keyVal);
|
492 | return this;
|
493 | }
|
494 | addUnknownKeyValToInput(inputIndex, keyVal) {
|
495 | this.data.addUnknownKeyValToInput(inputIndex, keyVal);
|
496 | return this;
|
497 | }
|
498 | addUnknownKeyValToOutput(outputIndex, keyVal) {
|
499 | this.data.addUnknownKeyValToOutput(outputIndex, keyVal);
|
500 | return this;
|
501 | }
|
502 | clearFinalizedInput(inputIndex) {
|
503 | this.data.clearFinalizedInput(inputIndex);
|
504 | return this;
|
505 | }
|
506 | }
|
507 | exports.Psbt = Psbt;
|
508 |
|
509 |
|
510 |
|
511 |
|
512 |
|
513 | const transactionFromBuffer = buffer => new PsbtTransaction(buffer);
|
514 |
|
515 |
|
516 |
|
517 |
|
518 | class PsbtTransaction {
|
519 | constructor(buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
|
520 | this.tx = transaction_1.Transaction.fromBuffer(buffer);
|
521 | checkTxEmpty(this.tx);
|
522 | Object.defineProperty(this, 'tx', {
|
523 | enumerable: false,
|
524 | writable: true,
|
525 | });
|
526 | }
|
527 | getInputOutputCounts() {
|
528 | return {
|
529 | inputCount: this.tx.ins.length,
|
530 | outputCount: this.tx.outs.length,
|
531 | };
|
532 | }
|
533 | addInput(input) {
|
534 | if (
|
535 | input.hash === undefined ||
|
536 | input.index === undefined ||
|
537 | (!Buffer.isBuffer(input.hash) && typeof input.hash !== 'string') ||
|
538 | typeof input.index !== 'number'
|
539 | ) {
|
540 | throw new Error('Error adding input.');
|
541 | }
|
542 | const hash =
|
543 | typeof input.hash === 'string'
|
544 | ? bufferutils_1.reverseBuffer(Buffer.from(input.hash, 'hex'))
|
545 | : input.hash;
|
546 | this.tx.addInput(hash, input.index, input.sequence);
|
547 | }
|
548 | addOutput(output) {
|
549 | if (
|
550 | output.script === undefined ||
|
551 | output.value === undefined ||
|
552 | !Buffer.isBuffer(output.script) ||
|
553 | typeof output.value !== 'number'
|
554 | ) {
|
555 | throw new Error('Error adding output.');
|
556 | }
|
557 | this.tx.addOutput(output.script, output.value);
|
558 | }
|
559 | toBuffer() {
|
560 | return this.tx.toBuffer();
|
561 | }
|
562 | }
|
563 | function canFinalize(input, script, scriptType) {
|
564 | switch (scriptType) {
|
565 | case 'pubkey':
|
566 | case 'pubkeyhash':
|
567 | case 'witnesspubkeyhash':
|
568 | return hasSigs(1, input.partialSig);
|
569 | case 'multisig':
|
570 | const p2ms = payments.p2ms({ output: script });
|
571 | return hasSigs(p2ms.m, input.partialSig);
|
572 | default:
|
573 | return false;
|
574 | }
|
575 | }
|
576 | function hasSigs(neededSigs, partialSig) {
|
577 | if (!partialSig) return false;
|
578 | if (partialSig.length > neededSigs) throw new Error('Too many signatures');
|
579 | return partialSig.length === neededSigs;
|
580 | }
|
581 | function isFinalized(input) {
|
582 | return !!input.finalScriptSig || !!input.finalScriptWitness;
|
583 | }
|
584 | function isPaymentFactory(payment) {
|
585 | return script => {
|
586 | try {
|
587 | payment({ output: script });
|
588 | return true;
|
589 | } catch (err) {
|
590 | return false;
|
591 | }
|
592 | };
|
593 | }
|
594 | const isP2MS = isPaymentFactory(payments.p2ms);
|
595 | const isP2PK = isPaymentFactory(payments.p2pk);
|
596 | const isP2PKH = isPaymentFactory(payments.p2pkh);
|
597 | const isP2WPKH = isPaymentFactory(payments.p2wpkh);
|
598 | const isP2WSHScript = isPaymentFactory(payments.p2wsh);
|
599 | function check32Bit(num) {
|
600 | if (
|
601 | typeof num !== 'number' ||
|
602 | num !== Math.floor(num) ||
|
603 | num > 0xffffffff ||
|
604 | num < 0
|
605 | ) {
|
606 | throw new Error('Invalid 32 bit integer');
|
607 | }
|
608 | }
|
609 | function checkFees(psbt, cache, opts) {
|
610 | const feeRate = cache.__FEE_RATE || psbt.getFeeRate();
|
611 | const vsize = cache.__EXTRACTED_TX.virtualSize();
|
612 | const satoshis = feeRate * vsize;
|
613 | if (feeRate >= opts.maximumFeeRate) {
|
614 | throw new Error(
|
615 | `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
|
616 | `fees, which is ${feeRate} satoshi per byte for a transaction ` +
|
617 | `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
|
618 | `byte). Use setMaximumFeeRate method to raise your threshold, or ` +
|
619 | `pass true to the first arg of extractTransaction.`,
|
620 | );
|
621 | }
|
622 | }
|
623 | function checkInputsForPartialSig(inputs, action) {
|
624 | inputs.forEach(input => {
|
625 | let throws = false;
|
626 | let pSigs = [];
|
627 | if ((input.partialSig || []).length === 0) {
|
628 | if (!input.finalScriptSig && !input.finalScriptWitness) return;
|
629 | pSigs = getPsigsFromInputFinalScripts(input);
|
630 | } else {
|
631 | pSigs = input.partialSig;
|
632 | }
|
633 | pSigs.forEach(pSig => {
|
634 | const { hashType } = bscript.signature.decode(pSig.signature);
|
635 | const whitelist = [];
|
636 | const isAnyoneCanPay =
|
637 | hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY;
|
638 | if (isAnyoneCanPay) whitelist.push('addInput');
|
639 | const hashMod = hashType & 0x1f;
|
640 | switch (hashMod) {
|
641 | case transaction_1.Transaction.SIGHASH_ALL:
|
642 | break;
|
643 | case transaction_1.Transaction.SIGHASH_SINGLE:
|
644 | case transaction_1.Transaction.SIGHASH_NONE:
|
645 | whitelist.push('addOutput');
|
646 | whitelist.push('setInputSequence');
|
647 | break;
|
648 | }
|
649 | if (whitelist.indexOf(action) === -1) {
|
650 | throws = true;
|
651 | }
|
652 | });
|
653 | if (throws) {
|
654 | throw new Error('Can not modify transaction, signatures exist.');
|
655 | }
|
656 | });
|
657 | }
|
658 | function checkPartialSigSighashes(input) {
|
659 | if (!input.sighashType || !input.partialSig) return;
|
660 | const { partialSig, sighashType } = input;
|
661 | partialSig.forEach(pSig => {
|
662 | const { hashType } = bscript.signature.decode(pSig.signature);
|
663 | if (sighashType !== hashType) {
|
664 | throw new Error('Signature sighash does not match input sighash type');
|
665 | }
|
666 | });
|
667 | }
|
668 | function checkScriptForPubkey(pubkey, script, action) {
|
669 | const pubkeyHash = crypto_1.hash160(pubkey);
|
670 | const decompiled = bscript.decompile(script);
|
671 | if (decompiled === null) throw new Error('Unknown script error');
|
672 | const hasKey = decompiled.some(element => {
|
673 | if (typeof element === 'number') return false;
|
674 | return element.equals(pubkey) || element.equals(pubkeyHash);
|
675 | });
|
676 | if (!hasKey) {
|
677 | throw new Error(
|
678 | `Can not ${action} for this input with the key ${pubkey.toString('hex')}`,
|
679 | );
|
680 | }
|
681 | }
|
682 | function checkTxEmpty(tx) {
|
683 | const isEmpty = tx.ins.every(
|
684 | input =>
|
685 | input.script &&
|
686 | input.script.length === 0 &&
|
687 | input.witness &&
|
688 | input.witness.length === 0,
|
689 | );
|
690 | if (!isEmpty) {
|
691 | throw new Error('Format Error: Transaction ScriptSigs are not empty');
|
692 | }
|
693 | }
|
694 | function checkTxForDupeIns(tx, cache) {
|
695 | tx.ins.forEach(input => {
|
696 | checkTxInputCache(cache, input);
|
697 | });
|
698 | }
|
699 | function checkTxInputCache(cache, input) {
|
700 | const key =
|
701 | bufferutils_1.reverseBuffer(Buffer.from(input.hash)).toString('hex') +
|
702 | ':' +
|
703 | input.index;
|
704 | if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.');
|
705 | cache.__TX_IN_CACHE[key] = 1;
|
706 | }
|
707 | function scriptCheckerFactory(payment, paymentScriptName) {
|
708 | return (inputIndex, scriptPubKey, redeemScript) => {
|
709 | const redeemScriptOutput = payment({
|
710 | redeem: { output: redeemScript },
|
711 | }).output;
|
712 | if (!scriptPubKey.equals(redeemScriptOutput)) {
|
713 | throw new Error(
|
714 | `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`,
|
715 | );
|
716 | }
|
717 | };
|
718 | }
|
719 | const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
|
720 | const checkWitnessScript = scriptCheckerFactory(
|
721 | payments.p2wsh,
|
722 | 'Witness script',
|
723 | );
|
724 | function getTxCacheValue(key, name, inputs, c) {
|
725 | if (!inputs.every(isFinalized))
|
726 | throw new Error(`PSBT must be finalized to calculate ${name}`);
|
727 | if (key === '__FEE_RATE' && c.__FEE_RATE) return c.__FEE_RATE;
|
728 | if (key === '__FEE' && c.__FEE) return c.__FEE;
|
729 | let tx;
|
730 | let mustFinalize = true;
|
731 | if (c.__EXTRACTED_TX) {
|
732 | tx = c.__EXTRACTED_TX;
|
733 | mustFinalize = false;
|
734 | } else {
|
735 | tx = c.__TX.clone();
|
736 | }
|
737 | inputFinalizeGetAmts(inputs, tx, c, mustFinalize);
|
738 | if (key === '__FEE_RATE') return c.__FEE_RATE;
|
739 | else if (key === '__FEE') return c.__FEE;
|
740 | }
|
741 | function getFinalScripts(
|
742 | script,
|
743 | scriptType,
|
744 | partialSig,
|
745 | isSegwit,
|
746 | isP2SH,
|
747 | isP2WSH,
|
748 | ) {
|
749 | let finalScriptSig;
|
750 | let finalScriptWitness;
|
751 |
|
752 | const payment = getPayment(script, scriptType, partialSig);
|
753 | const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
|
754 | const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
|
755 | if (isSegwit) {
|
756 | if (p2wsh) {
|
757 | finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness);
|
758 | } else {
|
759 | finalScriptWitness = witnessStackToScriptWitness(payment.witness);
|
760 | }
|
761 | if (p2sh) {
|
762 | finalScriptSig = p2sh.input;
|
763 | }
|
764 | } else {
|
765 | if (p2sh) {
|
766 | finalScriptSig = p2sh.input;
|
767 | } else {
|
768 | finalScriptSig = payment.input;
|
769 | }
|
770 | }
|
771 | return {
|
772 | finalScriptSig,
|
773 | finalScriptWitness,
|
774 | };
|
775 | }
|
776 | function getHashAndSighashType(
|
777 | inputs,
|
778 | inputIndex,
|
779 | pubkey,
|
780 | cache,
|
781 | sighashTypes,
|
782 | ) {
|
783 | const input = utils_1.checkForInput(inputs, inputIndex);
|
784 | const { hash, sighashType, script } = getHashForSig(
|
785 | inputIndex,
|
786 | input,
|
787 | cache,
|
788 | sighashTypes,
|
789 | );
|
790 | checkScriptForPubkey(pubkey, script, 'sign');
|
791 | return {
|
792 | hash,
|
793 | sighashType,
|
794 | };
|
795 | }
|
796 | function getHashForSig(inputIndex, input, cache, sighashTypes) {
|
797 | const unsignedTx = cache.__TX;
|
798 | const sighashType =
|
799 | input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
|
800 | if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
|
801 | const str = sighashTypeToString(sighashType);
|
802 | throw new Error(
|
803 | `Sighash type is not allowed. Retry the sign method passing the ` +
|
804 | `sighashTypes array of whitelisted types. Sighash type: ${str}`,
|
805 | );
|
806 | }
|
807 | let hash;
|
808 | let script;
|
809 | if (input.nonWitnessUtxo) {
|
810 | const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
|
811 | cache,
|
812 | input,
|
813 | inputIndex,
|
814 | );
|
815 | const prevoutHash = unsignedTx.ins[inputIndex].hash;
|
816 | const utxoHash = nonWitnessUtxoTx.getHash();
|
817 |
|
818 | if (!prevoutHash.equals(utxoHash)) {
|
819 | throw new Error(
|
820 | `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
|
821 | );
|
822 | }
|
823 | const prevoutIndex = unsignedTx.ins[inputIndex].index;
|
824 | const prevout = nonWitnessUtxoTx.outs[prevoutIndex];
|
825 | if (input.redeemScript) {
|
826 |
|
827 | checkRedeemScript(inputIndex, prevout.script, input.redeemScript);
|
828 | script = input.redeemScript;
|
829 | } else {
|
830 | script = prevout.script;
|
831 | }
|
832 | if (isP2WSHScript(script)) {
|
833 | if (!input.witnessScript)
|
834 | throw new Error('Segwit input needs witnessScript if not P2WPKH');
|
835 | checkWitnessScript(inputIndex, script, input.witnessScript);
|
836 | hash = unsignedTx.hashForWitnessV0(
|
837 | inputIndex,
|
838 | input.witnessScript,
|
839 | prevout.value,
|
840 | sighashType,
|
841 | );
|
842 | script = input.witnessScript;
|
843 | } else if (isP2WPKH(script)) {
|
844 |
|
845 | const signingScript = payments.p2pkh({ hash: script.slice(2) }).output;
|
846 | hash = unsignedTx.hashForWitnessV0(
|
847 | inputIndex,
|
848 | signingScript,
|
849 | prevout.value,
|
850 | sighashType,
|
851 | );
|
852 | } else {
|
853 | hash = unsignedTx.hashForSignature(inputIndex, script, sighashType);
|
854 | }
|
855 | } else if (input.witnessUtxo) {
|
856 | let _script;
|
857 | if (input.redeemScript) {
|
858 |
|
859 | checkRedeemScript(
|
860 | inputIndex,
|
861 | input.witnessUtxo.script,
|
862 | input.redeemScript,
|
863 | );
|
864 | _script = input.redeemScript;
|
865 | } else {
|
866 | _script = input.witnessUtxo.script;
|
867 | }
|
868 | if (isP2WPKH(_script)) {
|
869 |
|
870 | const signingScript = payments.p2pkh({ hash: _script.slice(2) }).output;
|
871 | hash = unsignedTx.hashForWitnessV0(
|
872 | inputIndex,
|
873 | signingScript,
|
874 | input.witnessUtxo.value,
|
875 | sighashType,
|
876 | );
|
877 | script = _script;
|
878 | } else if (isP2WSHScript(_script)) {
|
879 | if (!input.witnessScript)
|
880 | throw new Error('Segwit input needs witnessScript if not P2WPKH');
|
881 | checkWitnessScript(inputIndex, _script, input.witnessScript);
|
882 | hash = unsignedTx.hashForWitnessV0(
|
883 | inputIndex,
|
884 | input.witnessScript,
|
885 | input.witnessUtxo.value,
|
886 | sighashType,
|
887 | );
|
888 |
|
889 | script = input.witnessScript;
|
890 | } else {
|
891 | throw new Error(
|
892 | `Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
|
893 | `${_script.toString('hex')}`,
|
894 | );
|
895 | }
|
896 | } else {
|
897 | throw new Error('Need a Utxo input item for signing');
|
898 | }
|
899 | return {
|
900 | script,
|
901 | sighashType,
|
902 | hash,
|
903 | };
|
904 | }
|
905 | function getPayment(script, scriptType, partialSig) {
|
906 | let payment;
|
907 | switch (scriptType) {
|
908 | case 'multisig':
|
909 | const sigs = getSortedSigs(script, partialSig);
|
910 | payment = payments.p2ms({
|
911 | output: script,
|
912 | signatures: sigs,
|
913 | });
|
914 | break;
|
915 | case 'pubkey':
|
916 | payment = payments.p2pk({
|
917 | output: script,
|
918 | signature: partialSig[0].signature,
|
919 | });
|
920 | break;
|
921 | case 'pubkeyhash':
|
922 | payment = payments.p2pkh({
|
923 | output: script,
|
924 | pubkey: partialSig[0].pubkey,
|
925 | signature: partialSig[0].signature,
|
926 | });
|
927 | break;
|
928 | case 'witnesspubkeyhash':
|
929 | payment = payments.p2wpkh({
|
930 | output: script,
|
931 | pubkey: partialSig[0].pubkey,
|
932 | signature: partialSig[0].signature,
|
933 | });
|
934 | break;
|
935 | }
|
936 | return payment;
|
937 | }
|
938 | function getPsigsFromInputFinalScripts(input) {
|
939 | const scriptItems = !input.finalScriptSig
|
940 | ? []
|
941 | : bscript.decompile(input.finalScriptSig) || [];
|
942 | const witnessItems = !input.finalScriptWitness
|
943 | ? []
|
944 | : bscript.decompile(input.finalScriptWitness) || [];
|
945 | return scriptItems
|
946 | .concat(witnessItems)
|
947 | .filter(item => {
|
948 | return Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item);
|
949 | })
|
950 | .map(sig => ({ signature: sig }));
|
951 | }
|
952 | function getScriptFromInput(inputIndex, input, cache) {
|
953 | const unsignedTx = cache.__TX;
|
954 | const res = {
|
955 | script: null,
|
956 | isSegwit: false,
|
957 | isP2SH: false,
|
958 | isP2WSH: false,
|
959 | };
|
960 | res.isP2SH = !!input.redeemScript;
|
961 | res.isP2WSH = !!input.witnessScript;
|
962 | if (input.witnessScript) {
|
963 | res.script = input.witnessScript;
|
964 | } else if (input.redeemScript) {
|
965 | res.script = input.redeemScript;
|
966 | } else {
|
967 | if (input.nonWitnessUtxo) {
|
968 | const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(
|
969 | cache,
|
970 | input,
|
971 | inputIndex,
|
972 | );
|
973 | const prevoutIndex = unsignedTx.ins[inputIndex].index;
|
974 | res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;
|
975 | } else if (input.witnessUtxo) {
|
976 | res.script = input.witnessUtxo.script;
|
977 | }
|
978 | }
|
979 | if (input.witnessScript || isP2WPKH(res.script)) {
|
980 | res.isSegwit = true;
|
981 | }
|
982 | return res;
|
983 | }
|
984 | function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
|
985 | const input = utils_1.checkForInput(inputs, inputIndex);
|
986 | if (!input.bip32Derivation || input.bip32Derivation.length === 0) {
|
987 | throw new Error('Need bip32Derivation to sign with HD');
|
988 | }
|
989 | const myDerivations = input.bip32Derivation
|
990 | .map(bipDv => {
|
991 | if (bipDv.masterFingerprint.equals(hdKeyPair.fingerprint)) {
|
992 | return bipDv;
|
993 | } else {
|
994 | return;
|
995 | }
|
996 | })
|
997 | .filter(v => !!v);
|
998 | if (myDerivations.length === 0) {
|
999 | throw new Error(
|
1000 | 'Need one bip32Derivation masterFingerprint to match the HDSigner fingerprint',
|
1001 | );
|
1002 | }
|
1003 | const signers = myDerivations.map(bipDv => {
|
1004 | const node = hdKeyPair.derivePath(bipDv.path);
|
1005 | if (!bipDv.pubkey.equals(node.publicKey)) {
|
1006 | throw new Error('pubkey did not match bip32Derivation');
|
1007 | }
|
1008 | return node;
|
1009 | });
|
1010 | return signers;
|
1011 | }
|
1012 | function getSortedSigs(script, partialSig) {
|
1013 | const p2ms = payments.p2ms({ output: script });
|
1014 |
|
1015 | return p2ms.pubkeys
|
1016 | .map(pk => {
|
1017 |
|
1018 | return (
|
1019 | partialSig.filter(ps => {
|
1020 | return ps.pubkey.equals(pk);
|
1021 | })[0] || {}
|
1022 | ).signature;
|
1023 |
|
1024 |
|
1025 | })
|
1026 | .filter(v => !!v);
|
1027 | }
|
1028 | function scriptWitnessToWitnessStack(buffer) {
|
1029 | let offset = 0;
|
1030 | function readSlice(n) {
|
1031 | offset += n;
|
1032 | return buffer.slice(offset - n, offset);
|
1033 | }
|
1034 | function readVarInt() {
|
1035 | const vi = varuint.decode(buffer, offset);
|
1036 | offset += varuint.decode.bytes;
|
1037 | return vi;
|
1038 | }
|
1039 | function readVarSlice() {
|
1040 | return readSlice(readVarInt());
|
1041 | }
|
1042 | function readVector() {
|
1043 | const count = readVarInt();
|
1044 | const vector = [];
|
1045 | for (let i = 0; i < count; i++) vector.push(readVarSlice());
|
1046 | return vector;
|
1047 | }
|
1048 | return readVector();
|
1049 | }
|
1050 | function sighashTypeToString(sighashType) {
|
1051 | let text =
|
1052 | sighashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY
|
1053 | ? 'SIGHASH_ANYONECANPAY | '
|
1054 | : '';
|
1055 | const sigMod = sighashType & 0x1f;
|
1056 | switch (sigMod) {
|
1057 | case transaction_1.Transaction.SIGHASH_ALL:
|
1058 | text += 'SIGHASH_ALL';
|
1059 | break;
|
1060 | case transaction_1.Transaction.SIGHASH_SINGLE:
|
1061 | text += 'SIGHASH_SINGLE';
|
1062 | break;
|
1063 | case transaction_1.Transaction.SIGHASH_NONE:
|
1064 | text += 'SIGHASH_NONE';
|
1065 | break;
|
1066 | }
|
1067 | return text;
|
1068 | }
|
1069 | function witnessStackToScriptWitness(witness) {
|
1070 | let buffer = Buffer.allocUnsafe(0);
|
1071 | function writeSlice(slice) {
|
1072 | buffer = Buffer.concat([buffer, Buffer.from(slice)]);
|
1073 | }
|
1074 | function writeVarInt(i) {
|
1075 | const currentLen = buffer.length;
|
1076 | const varintLen = varuint.encodingLength(i);
|
1077 | buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
|
1078 | varuint.encode(i, buffer, currentLen);
|
1079 | }
|
1080 | function writeVarSlice(slice) {
|
1081 | writeVarInt(slice.length);
|
1082 | writeSlice(slice);
|
1083 | }
|
1084 | function writeVector(vector) {
|
1085 | writeVarInt(vector.length);
|
1086 | vector.forEach(writeVarSlice);
|
1087 | }
|
1088 | writeVector(witness);
|
1089 | return buffer;
|
1090 | }
|
1091 | function addNonWitnessTxCache(cache, input, inputIndex) {
|
1092 | cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo;
|
1093 | const tx = transaction_1.Transaction.fromBuffer(input.nonWitnessUtxo);
|
1094 | cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx;
|
1095 | const self = cache;
|
1096 | const selfIndex = inputIndex;
|
1097 | delete input.nonWitnessUtxo;
|
1098 | Object.defineProperty(input, 'nonWitnessUtxo', {
|
1099 | enumerable: true,
|
1100 | get() {
|
1101 | const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex];
|
1102 | const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex];
|
1103 | if (buf !== undefined) {
|
1104 | return buf;
|
1105 | } else {
|
1106 | const newBuf = txCache.toBuffer();
|
1107 | self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf;
|
1108 | return newBuf;
|
1109 | }
|
1110 | },
|
1111 | set(data) {
|
1112 | self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data;
|
1113 | },
|
1114 | });
|
1115 | }
|
1116 | function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize) {
|
1117 | let inputAmount = 0;
|
1118 | inputs.forEach((input, idx) => {
|
1119 | if (mustFinalize && input.finalScriptSig)
|
1120 | tx.ins[idx].script = input.finalScriptSig;
|
1121 | if (mustFinalize && input.finalScriptWitness) {
|
1122 | tx.ins[idx].witness = scriptWitnessToWitnessStack(
|
1123 | input.finalScriptWitness,
|
1124 | );
|
1125 | }
|
1126 | if (input.witnessUtxo) {
|
1127 | inputAmount += input.witnessUtxo.value;
|
1128 | } else if (input.nonWitnessUtxo) {
|
1129 | const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx);
|
1130 | const vout = tx.ins[idx].index;
|
1131 | const out = nwTx.outs[vout];
|
1132 | inputAmount += out.value;
|
1133 | }
|
1134 | });
|
1135 | const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0);
|
1136 | const fee = inputAmount - outputAmount;
|
1137 | if (fee < 0) {
|
1138 | throw new Error('Outputs are spending more than Inputs');
|
1139 | }
|
1140 | const bytes = tx.virtualSize();
|
1141 | cache.__FEE = fee;
|
1142 | cache.__EXTRACTED_TX = tx;
|
1143 | cache.__FEE_RATE = Math.floor(fee / bytes);
|
1144 | }
|
1145 | function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
|
1146 | const c = cache.__NON_WITNESS_UTXO_TX_CACHE;
|
1147 | if (!c[inputIndex]) {
|
1148 | addNonWitnessTxCache(cache, input, inputIndex);
|
1149 | }
|
1150 | return c[inputIndex];
|
1151 | }
|
1152 | function classifyScript(script) {
|
1153 | if (isP2WPKH(script)) return 'witnesspubkeyhash';
|
1154 | if (isP2PKH(script)) return 'pubkeyhash';
|
1155 | if (isP2MS(script)) return 'multisig';
|
1156 | if (isP2PK(script)) return 'pubkey';
|
1157 | return 'nonstandard';
|
1158 | }
|
1159 | function range(n) {
|
1160 | return [...Array(n).keys()];
|
1161 | }
|