Refactor: input finalize and get fee shared logic
This commit is contained in:
parent
497d048ebf
commit
9749a216b8
2 changed files with 120 additions and 96 deletions
104
src/psbt.js
104
src/psbt.js
|
@ -164,69 +164,42 @@ class Psbt extends bip174_1.Psbt {
|
||||||
}
|
}
|
||||||
extractTransaction(disableFeeCheck) {
|
extractTransaction(disableFeeCheck) {
|
||||||
if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
|
if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
|
||||||
|
const c = this.__CACHE;
|
||||||
if (!disableFeeCheck) {
|
if (!disableFeeCheck) {
|
||||||
const feeRate = this.__CACHE.__FEE_RATE || this.getFeeRate();
|
checkFees(this, c, this.opts);
|
||||||
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.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (this.__CACHE.__EXTRACTED_TX) return this.__CACHE.__EXTRACTED_TX;
|
if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
|
||||||
const tx = this.__CACHE.__TX.clone();
|
const tx = c.__TX.clone();
|
||||||
this.inputs.forEach((input, idx) => {
|
inputFinalizeGetAmts(this.inputs, tx, c, true, false);
|
||||||
if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
|
c.__EXTRACTED_TX = tx;
|
||||||
if (input.finalScriptWitness) {
|
|
||||||
tx.ins[idx].witness = scriptWitnessToWitnessStack(
|
|
||||||
input.finalScriptWitness,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.__CACHE.__EXTRACTED_TX = tx;
|
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
getFeeRate() {
|
getFeeRate() {
|
||||||
if (!this.inputs.every(isFinalized))
|
if (!this.inputs.every(isFinalized))
|
||||||
throw new Error('PSBT must be finalized to calculate fee rate');
|
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 tx;
|
||||||
let inputAmount = 0;
|
|
||||||
let mustFinalize = true;
|
let mustFinalize = true;
|
||||||
if (this.__CACHE.__EXTRACTED_TX) {
|
if (c.__EXTRACTED_TX) {
|
||||||
tx = this.__CACHE.__EXTRACTED_TX;
|
tx = c.__EXTRACTED_TX;
|
||||||
mustFinalize = false;
|
mustFinalize = false;
|
||||||
} else {
|
} else {
|
||||||
tx = this.__CACHE.__TX.clone();
|
tx = c.__TX.clone();
|
||||||
}
|
}
|
||||||
this.inputs.forEach((input, idx) => {
|
const inputAmount = inputFinalizeGetAmts(
|
||||||
if (mustFinalize && input.finalScriptSig)
|
this.inputs,
|
||||||
tx.ins[idx].script = input.finalScriptSig;
|
tx,
|
||||||
if (mustFinalize && input.finalScriptWitness) {
|
c,
|
||||||
tx.ins[idx].witness = scriptWitnessToWitnessStack(
|
mustFinalize,
|
||||||
input.finalScriptWitness,
|
true,
|
||||||
);
|
);
|
||||||
}
|
c.__EXTRACTED_TX = tx;
|
||||||
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 outputAmount = tx.outs.reduce((total, o) => total + o.value, 0);
|
const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0);
|
||||||
const fee = inputAmount - outputAmount;
|
const fee = inputAmount - outputAmount;
|
||||||
const bytes = tx.virtualSize();
|
const bytes = tx.virtualSize();
|
||||||
this.__CACHE.__FEE_RATE = Math.floor(fee / bytes);
|
c.__FEE_RATE = Math.floor(fee / bytes);
|
||||||
return this.__CACHE.__FEE_RATE;
|
return c.__FEE_RATE;
|
||||||
}
|
}
|
||||||
finalizeAllInputs() {
|
finalizeAllInputs() {
|
||||||
const inputResults = range(this.inputs.length).map(idx =>
|
const inputResults = range(this.inputs.length).map(idx =>
|
||||||
|
@ -859,6 +832,41 @@ function getOutputAdder(cache) {
|
||||||
return selfCache.__TX.toBuffer();
|
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) {
|
function check32Bit(num) {
|
||||||
if (
|
if (
|
||||||
typeof num !== 'number' ||
|
typeof num !== 'number' ||
|
||||||
|
|
112
ts_src/psbt.ts
112
ts_src/psbt.ts
|
@ -204,73 +204,46 @@ export class Psbt extends PsbtBase {
|
||||||
|
|
||||||
extractTransaction(disableFeeCheck?: boolean): Transaction {
|
extractTransaction(disableFeeCheck?: boolean): Transaction {
|
||||||
if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
|
if (!this.inputs.every(isFinalized)) throw new Error('Not finalized');
|
||||||
|
const c = this.__CACHE;
|
||||||
if (!disableFeeCheck) {
|
if (!disableFeeCheck) {
|
||||||
const feeRate = this.__CACHE.__FEE_RATE || this.getFeeRate();
|
checkFees(this, c, this.opts);
|
||||||
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.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (this.__CACHE.__EXTRACTED_TX) return this.__CACHE.__EXTRACTED_TX;
|
if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
|
||||||
const tx = this.__CACHE.__TX.clone();
|
const tx = c.__TX.clone();
|
||||||
this.inputs.forEach((input, idx) => {
|
inputFinalizeGetAmts(this.inputs, tx, c, true, false);
|
||||||
if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
|
c.__EXTRACTED_TX = tx;
|
||||||
if (input.finalScriptWitness) {
|
|
||||||
tx.ins[idx].witness = scriptWitnessToWitnessStack(
|
|
||||||
input.finalScriptWitness,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.__CACHE.__EXTRACTED_TX = tx;
|
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFeeRate(): number {
|
getFeeRate(): number {
|
||||||
if (!this.inputs.every(isFinalized))
|
if (!this.inputs.every(isFinalized))
|
||||||
throw new Error('PSBT must be finalized to calculate fee rate');
|
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 tx: Transaction;
|
||||||
let inputAmount = 0;
|
|
||||||
let mustFinalize = true;
|
let mustFinalize = true;
|
||||||
if (this.__CACHE.__EXTRACTED_TX) {
|
if (c.__EXTRACTED_TX) {
|
||||||
tx = this.__CACHE.__EXTRACTED_TX;
|
tx = c.__EXTRACTED_TX;
|
||||||
mustFinalize = false;
|
mustFinalize = false;
|
||||||
} else {
|
} else {
|
||||||
tx = this.__CACHE.__TX.clone();
|
tx = c.__TX.clone();
|
||||||
}
|
}
|
||||||
this.inputs.forEach((input, idx) => {
|
const inputAmount = inputFinalizeGetAmts(
|
||||||
if (mustFinalize && input.finalScriptSig)
|
this.inputs,
|
||||||
tx.ins[idx].script = input.finalScriptSig;
|
tx,
|
||||||
if (mustFinalize && input.finalScriptWitness) {
|
c,
|
||||||
tx.ins[idx].witness = scriptWitnessToWitnessStack(
|
mustFinalize,
|
||||||
input.finalScriptWitness,
|
true,
|
||||||
);
|
);
|
||||||
}
|
c.__EXTRACTED_TX = tx;
|
||||||
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 outputAmount = (tx.outs as Output[]).reduce(
|
const outputAmount = (tx.outs as Output[]).reduce(
|
||||||
(total, o) => total + o.value,
|
(total, o) => total + o.value,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
const fee = inputAmount - outputAmount;
|
const fee = inputAmount - outputAmount;
|
||||||
const bytes = tx.virtualSize();
|
const bytes = tx.virtualSize();
|
||||||
this.__CACHE.__FEE_RATE = Math.floor(fee / bytes);
|
c.__FEE_RATE = Math.floor(fee / bytes);
|
||||||
return this.__CACHE.__FEE_RATE;
|
return c.__FEE_RATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
finalizeAllInputs(): {
|
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 {
|
function check32Bit(num: number): void {
|
||||||
if (
|
if (
|
||||||
typeof num !== 'number' ||
|
typeof num !== 'number' ||
|
||||||
|
|
Loading…
Add table
Reference in a new issue