diff --git a/src/psbt.js b/src/psbt.js index eff98ae..c2f9bd3 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -164,69 +164,42 @@ class Psbt extends bip174_1.Psbt { } extractTransaction(disableFeeCheck) { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); + const c = this.__CACHE; if (!disableFeeCheck) { - const feeRate = this.__CACHE.__FEE_RATE || this.getFeeRate(); - const vsize = this.__CACHE.__EXTRACTED_TX.virtualSize(); - const satoshis = feeRate * vsize; - if (feeRate >= this.opts.maximumFeeRate) { - throw new Error( - `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + - `fees, which is ${feeRate} satoshi per byte for a transaction ` + - `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + - `byte). Use setMaximumFeeRate method to raise your threshold, or ` + - `pass true to the first arg of extractTransaction.`, - ); - } + checkFees(this, c, this.opts); } - if (this.__CACHE.__EXTRACTED_TX) return this.__CACHE.__EXTRACTED_TX; - const tx = this.__CACHE.__TX.clone(); - this.inputs.forEach((input, idx) => { - if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; - if (input.finalScriptWitness) { - tx.ins[idx].witness = scriptWitnessToWitnessStack( - input.finalScriptWitness, - ); - } - }); - this.__CACHE.__EXTRACTED_TX = tx; + if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX; + const tx = c.__TX.clone(); + inputFinalizeGetAmts(this.inputs, tx, c, true, false); + c.__EXTRACTED_TX = tx; return tx; } getFeeRate() { if (!this.inputs.every(isFinalized)) throw new Error('PSBT must be finalized to calculate fee rate'); - if (this.__CACHE.__FEE_RATE) return this.__CACHE.__FEE_RATE; + const c = this.__CACHE; + if (c.__FEE_RATE) return c.__FEE_RATE; let tx; - let inputAmount = 0; let mustFinalize = true; - if (this.__CACHE.__EXTRACTED_TX) { - tx = this.__CACHE.__EXTRACTED_TX; + if (c.__EXTRACTED_TX) { + tx = c.__EXTRACTED_TX; mustFinalize = false; } else { - tx = this.__CACHE.__TX.clone(); + tx = c.__TX.clone(); } - this.inputs.forEach((input, idx) => { - if (mustFinalize && input.finalScriptSig) - tx.ins[idx].script = input.finalScriptSig; - if (mustFinalize && input.finalScriptWitness) { - tx.ins[idx].witness = scriptWitnessToWitnessStack( - input.finalScriptWitness, - ); - } - if (input.witnessUtxo) { - inputAmount += input.witnessUtxo.value; - } else if (input.nonWitnessUtxo) { - const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx); - const vout = this.__CACHE.__TX.ins[idx].index; - const out = nwTx.outs[vout]; - inputAmount += out.value; - } - }); - this.__CACHE.__EXTRACTED_TX = tx; + 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(); - this.__CACHE.__FEE_RATE = Math.floor(fee / bytes); - return this.__CACHE.__FEE_RATE; + c.__FEE_RATE = Math.floor(fee / bytes); + return c.__FEE_RATE; } finalizeAllInputs() { const inputResults = range(this.inputs.length).map(idx => @@ -859,6 +832,41 @@ function getOutputAdder(cache) { return selfCache.__TX.toBuffer(); }; } +function checkFees(psbt, cache, opts) { + const feeRate = cache.__FEE_RATE || psbt.getFeeRate(); + const vsize = cache.__EXTRACTED_TX.virtualSize(); + const satoshis = feeRate * vsize; + if (feeRate >= opts.maximumFeeRate) { + throw new Error( + `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + + `fees, which is ${feeRate} satoshi per byte for a transaction ` + + `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + + `byte). Use setMaximumFeeRate method to raise your threshold, or ` + + `pass true to the first arg of extractTransaction.`, + ); + } +} +function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) { + let inputAmount = 0; + inputs.forEach((input, idx) => { + if (mustFinalize && input.finalScriptSig) + tx.ins[idx].script = input.finalScriptSig; + if (mustFinalize && input.finalScriptWitness) { + tx.ins[idx].witness = scriptWitnessToWitnessStack( + input.finalScriptWitness, + ); + } + if (getAmounts && input.witnessUtxo) { + inputAmount += input.witnessUtxo.value; + } else if (getAmounts && input.nonWitnessUtxo) { + const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx); + const vout = tx.ins[idx].index; + const out = nwTx.outs[vout]; + inputAmount += out.value; + } + }); + return inputAmount; +} function check32Bit(num) { if ( typeof num !== 'number' || diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 370b481..0771436 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -204,73 +204,46 @@ export class Psbt extends PsbtBase { extractTransaction(disableFeeCheck?: boolean): Transaction { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); + const c = this.__CACHE; if (!disableFeeCheck) { - const feeRate = this.__CACHE.__FEE_RATE || this.getFeeRate(); - const vsize = this.__CACHE.__EXTRACTED_TX!.virtualSize(); - const satoshis = feeRate * vsize; - if (feeRate >= this.opts.maximumFeeRate) { - throw new Error( - `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + - `fees, which is ${feeRate} satoshi per byte for a transaction ` + - `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + - `byte). Use setMaximumFeeRate method to raise your threshold, or ` + - `pass true to the first arg of extractTransaction.`, - ); - } + checkFees(this, c, this.opts); } - if (this.__CACHE.__EXTRACTED_TX) return this.__CACHE.__EXTRACTED_TX; - const tx = this.__CACHE.__TX.clone(); - this.inputs.forEach((input, idx) => { - if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; - if (input.finalScriptWitness) { - tx.ins[idx].witness = scriptWitnessToWitnessStack( - input.finalScriptWitness, - ); - } - }); - this.__CACHE.__EXTRACTED_TX = tx; + if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX; + const tx = c.__TX.clone(); + inputFinalizeGetAmts(this.inputs, tx, c, true, false); + c.__EXTRACTED_TX = tx; return tx; } getFeeRate(): number { if (!this.inputs.every(isFinalized)) throw new Error('PSBT must be finalized to calculate fee rate'); - if (this.__CACHE.__FEE_RATE) return this.__CACHE.__FEE_RATE; + const c = this.__CACHE; + if (c.__FEE_RATE) return c.__FEE_RATE; let tx: Transaction; - let inputAmount = 0; let mustFinalize = true; - if (this.__CACHE.__EXTRACTED_TX) { - tx = this.__CACHE.__EXTRACTED_TX; + if (c.__EXTRACTED_TX) { + tx = c.__EXTRACTED_TX; mustFinalize = false; } else { - tx = this.__CACHE.__TX.clone(); + tx = c.__TX.clone(); } - this.inputs.forEach((input, idx) => { - if (mustFinalize && input.finalScriptSig) - tx.ins[idx].script = input.finalScriptSig; - if (mustFinalize && input.finalScriptWitness) { - tx.ins[idx].witness = scriptWitnessToWitnessStack( - input.finalScriptWitness, - ); - } - if (input.witnessUtxo) { - inputAmount += input.witnessUtxo.value; - } else if (input.nonWitnessUtxo) { - const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx); - const vout = this.__CACHE.__TX.ins[idx].index; - const out = nwTx.outs[vout] as Output; - inputAmount += out.value; - } - }); - this.__CACHE.__EXTRACTED_TX = tx; + 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(); - this.__CACHE.__FEE_RATE = Math.floor(fee / bytes); - return this.__CACHE.__FEE_RATE; + c.__FEE_RATE = Math.floor(fee / bytes); + return c.__FEE_RATE; } finalizeAllInputs(): { @@ -1075,6 +1048,49 @@ function getOutputAdder( }; } +function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void { + const feeRate = cache.__FEE_RATE || psbt.getFeeRate(); + const vsize = cache.__EXTRACTED_TX!.virtualSize(); + const satoshis = feeRate * vsize; + if (feeRate >= opts.maximumFeeRate) { + throw new Error( + `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + + `fees, which is ${feeRate} satoshi per byte for a transaction ` + + `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + + `byte). Use setMaximumFeeRate method to raise your threshold, or ` + + `pass true to the first arg of extractTransaction.`, + ); + } +} + +function inputFinalizeGetAmts( + inputs: PsbtInput[], + tx: Transaction, + cache: PsbtCache, + mustFinalize: boolean, + getAmounts: boolean, +): number { + let inputAmount = 0; + inputs.forEach((input, idx) => { + if (mustFinalize && input.finalScriptSig) + tx.ins[idx].script = input.finalScriptSig; + if (mustFinalize && input.finalScriptWitness) { + tx.ins[idx].witness = scriptWitnessToWitnessStack( + input.finalScriptWitness, + ); + } + if (getAmounts && input.witnessUtxo) { + inputAmount += input.witnessUtxo.value; + } else if (getAmounts && 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; +} + function check32Bit(num: number): void { if ( typeof num !== 'number' ||