From ba33f0317f3d102be69ad269eb584a12ff7d5bcc Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 13:55:02 +0900 Subject: [PATCH] Add check for spending more than you have --- src/psbt.js | 36 ++++++++++++++++-------------------- ts_src/psbt.ts | 45 ++++++++++++++++++++------------------------- 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index aa73949..4c80a31 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -167,8 +167,7 @@ class Psbt extends bip174_1.Psbt { } if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX; const tx = c.__TX.clone(); - inputFinalizeGetAmts(this.inputs, tx, c, true, false); - c.__EXTRACTED_TX = tx; + inputFinalizeGetAmts(this.inputs, tx, c, true); return tx; } getFeeRate() { @@ -184,18 +183,7 @@ class Psbt extends bip174_1.Psbt { } else { tx = c.__TX.clone(); } - const inputAmount = inputFinalizeGetAmts( - this.inputs, - tx, - c, - mustFinalize, - true, - ); - c.__EXTRACTED_TX = tx; - const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0); - const fee = inputAmount - outputAmount; - const bytes = tx.virtualSize(); - c.__FEE_RATE = Math.floor(fee / bytes); + inputFinalizeGetAmts(this.inputs, tx, c, mustFinalize); return c.__FEE_RATE; } finalizeAllInputs() { @@ -830,7 +818,7 @@ function addNonWitnessTxCache(cache, input, inputIndex) { }, }); } -function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) { +function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize) { let inputAmount = 0; inputs.forEach((input, idx) => { if (mustFinalize && input.finalScriptSig) @@ -840,22 +828,30 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) { input.finalScriptWitness, ); } - if (getAmounts && input.witnessUtxo) { + if (input.witnessUtxo) { inputAmount += input.witnessUtxo.value; - } else if (getAmounts && input.nonWitnessUtxo) { + } else if (input.nonWitnessUtxo) { const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx); const vout = tx.ins[idx].index; const out = nwTx.outs[vout]; inputAmount += out.value; } }); - return inputAmount; + const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0); + const fee = inputAmount - outputAmount; + if (fee < 0) { + throw new Error('Outputs are spending more than Inputs'); + } + const bytes = tx.virtualSize(); + cache.__EXTRACTED_TX = tx; + cache.__FEE_RATE = Math.floor(fee / bytes); } function nonWitnessUtxoTxFromCache(cache, input, inputIndex) { - if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + const c = cache.__NON_WITNESS_UTXO_TX_CACHE; + if (!c[inputIndex]) { addNonWitnessTxCache(cache, input, inputIndex); } - return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; + return c[inputIndex]; } function classifyScript(script) { if (isP2WPKH(script)) return 'witnesspubkeyhash'; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index a78b37b..2e4c826 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -212,8 +212,7 @@ export class Psbt extends PsbtBase { } if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX; const tx = c.__TX.clone(); - inputFinalizeGetAmts(this.inputs, tx, c, true, false); - c.__EXTRACTED_TX = tx; + inputFinalizeGetAmts(this.inputs, tx, c, true); return tx; } @@ -230,22 +229,8 @@ export class Psbt extends PsbtBase { } else { tx = c.__TX.clone(); } - const inputAmount = inputFinalizeGetAmts( - this.inputs, - tx, - c, - mustFinalize, - true, - ); - c.__EXTRACTED_TX = tx; - const outputAmount = (tx.outs as Output[]).reduce( - (total, o) => total + o.value, - 0, - ); - const fee = inputAmount - outputAmount; - const bytes = tx.virtualSize(); - c.__FEE_RATE = Math.floor(fee / bytes); - return c.__FEE_RATE; + inputFinalizeGetAmts(this.inputs, tx, c, mustFinalize); + return c.__FEE_RATE!; } finalizeAllInputs(): { @@ -1031,8 +1016,7 @@ function inputFinalizeGetAmts( tx: Transaction, cache: PsbtCache, mustFinalize: boolean, - getAmounts: boolean, -): number { +): void { let inputAmount = 0; inputs.forEach((input, idx) => { if (mustFinalize && input.finalScriptSig) @@ -1042,16 +1026,26 @@ function inputFinalizeGetAmts( input.finalScriptWitness, ); } - if (getAmounts && input.witnessUtxo) { + if (input.witnessUtxo) { inputAmount += input.witnessUtxo.value; - } else if (getAmounts && input.nonWitnessUtxo) { + } else if (input.nonWitnessUtxo) { const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx); const vout = tx.ins[idx].index; const out = nwTx.outs[vout] as Output; inputAmount += out.value; } }); - return inputAmount; + const outputAmount = (tx.outs as Output[]).reduce( + (total, o) => total + o.value, + 0, + ); + const fee = inputAmount - outputAmount; + if (fee < 0) { + throw new Error('Outputs are spending more than Inputs'); + } + const bytes = tx.virtualSize(); + cache.__EXTRACTED_TX = tx; + cache.__FEE_RATE = Math.floor(fee / bytes); } function nonWitnessUtxoTxFromCache( @@ -1059,10 +1053,11 @@ function nonWitnessUtxoTxFromCache( input: PsbtInput, inputIndex: number, ): Transaction { - if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + const c = cache.__NON_WITNESS_UTXO_TX_CACHE; + if (!c[inputIndex]) { addNonWitnessTxCache(cache, input, inputIndex); } - return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; + return c[inputIndex]; } function classifyScript(script: Buffer): string {