Skip to content

Commit 07a2769

Browse files
authored
Merge pull request #1463 from bitcoinjs/addGetFeePsbt
Add getFee and getVSize
2 parents 0d10a4d + 5bbf255 commit 07a2769

File tree

7 files changed

+79
-30
lines changed

7 files changed

+79
-30
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 5.1.5
2+
__added__
3+
- `Psbt` now has `getFee(): number` for use when all inputs are finalized. It returns the satoshi fee of the transaction. Calling getFee, getFeeRate, or extractTransaction will cache these values so if you call one after the other, the second call will return immediately.
4+
15
# 5.1.4
26
__changed__
37
- `Psbt` inputs using segwit scripts can now work with nonWitnessUtxo as well as the original witnessUtxo. The reasoning for this is that nonWitnessUtxo has all the information contained in the witnessUtxo, so rejecting signing even though we have all the info we need is unnecessary. Trying to sign a non-segwit script with a witnessUtxo will still throw an Error as it should.

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bitcoinjs-lib",
3-
"version": "5.1.4",
3+
"version": "5.1.5",
44
"description": "Client-side Bitcoin JavaScript library",
55
"main": "./src/index.js",
66
"types": "./types/index.d.ts",

src/psbt.js

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ class Psbt {
153153
if (input.nonWitnessUtxo) {
154154
addNonWitnessTxCache(this.__CACHE, input, inputIndex);
155155
}
156+
c.__FEE = undefined;
156157
c.__FEE_RATE = undefined;
157158
c.__EXTRACTED_TX = undefined;
158159
return this;
@@ -171,6 +172,7 @@ class Psbt {
171172
}
172173
const c = this.__CACHE;
173174
this.data.addOutput(outputData);
175+
c.__FEE = undefined;
174176
c.__FEE_RATE = undefined;
175177
c.__EXTRACTED_TX = undefined;
176178
return this;
@@ -187,20 +189,15 @@ class Psbt {
187189
return tx;
188190
}
189191
getFeeRate() {
190-
if (!this.data.inputs.every(isFinalized))
191-
throw new Error('PSBT must be finalized to calculate fee rate');
192-
const c = this.__CACHE;
193-
if (c.__FEE_RATE) return c.__FEE_RATE;
194-
let tx;
195-
let mustFinalize = true;
196-
if (c.__EXTRACTED_TX) {
197-
tx = c.__EXTRACTED_TX;
198-
mustFinalize = false;
199-
} else {
200-
tx = c.__TX.clone();
201-
}
202-
inputFinalizeGetAmts(this.data.inputs, tx, c, mustFinalize);
203-
return c.__FEE_RATE;
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);
204201
}
205202
finalizeAllInputs() {
206203
utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one
@@ -724,6 +721,23 @@ const checkWitnessScript = scriptCheckerFactory(
724721
payments.p2wsh,
725722
'Witness script',
726723
);
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+
}
727741
function getFinalScripts(
728742
script,
729743
scriptType,
@@ -1124,6 +1138,7 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize) {
11241138
throw new Error('Outputs are spending more than Inputs');
11251139
}
11261140
const bytes = tx.virtualSize();
1141+
cache.__FEE = fee;
11271142
cache.__EXTRACTED_TX = tx;
11281143
cache.__FEE_RATE = Math.floor(fee / bytes);
11291144
}

test/psbt.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ describe(`Psbt`, () => {
149149
const fr1 = psbt5.getFeeRate()
150150
const fr2 = psbt5.getFeeRate()
151151
assert.strictEqual(fr1, fr2)
152+
153+
const psbt6 = Psbt.fromBase64(f.psbt)
154+
const f1 = psbt6.getFee()
155+
const f2 = psbt6.getFee()
156+
assert.strictEqual(f1, f2)
152157
})
153158
})
154159
})

ts_src/psbt.ts

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ export class Psbt {
194194
if (input.nonWitnessUtxo) {
195195
addNonWitnessTxCache(this.__CACHE, input, inputIndex);
196196
}
197+
c.__FEE = undefined;
197198
c.__FEE_RATE = undefined;
198199
c.__EXTRACTED_TX = undefined;
199200
return this;
@@ -214,6 +215,7 @@ export class Psbt {
214215
}
215216
const c = this.__CACHE;
216217
this.data.addOutput(outputData);
218+
c.__FEE = undefined;
217219
c.__FEE_RATE = undefined;
218220
c.__EXTRACTED_TX = undefined;
219221
return this;
@@ -232,20 +234,16 @@ export class Psbt {
232234
}
233235

234236
getFeeRate(): number {
235-
if (!this.data.inputs.every(isFinalized))
236-
throw new Error('PSBT must be finalized to calculate fee rate');
237-
const c = this.__CACHE;
238-
if (c.__FEE_RATE) return c.__FEE_RATE;
239-
let tx: Transaction;
240-
let mustFinalize = true;
241-
if (c.__EXTRACTED_TX) {
242-
tx = c.__EXTRACTED_TX;
243-
mustFinalize = false;
244-
} else {
245-
tx = c.__TX.clone();
246-
}
247-
inputFinalizeGetAmts(this.data.inputs, tx, c, mustFinalize);
248-
return c.__FEE_RATE!;
237+
return getTxCacheValue(
238+
'__FEE_RATE',
239+
'fee rate',
240+
this.data.inputs,
241+
this.__CACHE,
242+
)!;
243+
}
244+
245+
getFee(): number {
246+
return getTxCacheValue('__FEE', 'fee', this.data.inputs, this.__CACHE)!;
249247
}
250248

251249
finalizeAllInputs(): this {
@@ -610,6 +608,7 @@ interface PsbtCache {
610608
__TX_IN_CACHE: { [index: string]: number };
611609
__TX: Transaction;
612610
__FEE_RATE?: number;
611+
__FEE?: number;
613612
__EXTRACTED_TX?: Transaction;
614613
}
615614

@@ -920,6 +919,30 @@ const checkWitnessScript = scriptCheckerFactory(
920919
'Witness script',
921920
);
922921

922+
type TxCacheNumberKey = '__FEE_RATE' | '__FEE';
923+
function getTxCacheValue(
924+
key: TxCacheNumberKey,
925+
name: string,
926+
inputs: PsbtInput[],
927+
c: PsbtCache,
928+
): number | undefined {
929+
if (!inputs.every(isFinalized))
930+
throw new Error(`PSBT must be finalized to calculate ${name}`);
931+
if (key === '__FEE_RATE' && c.__FEE_RATE) return c.__FEE_RATE;
932+
if (key === '__FEE' && c.__FEE) return c.__FEE;
933+
let tx: Transaction;
934+
let mustFinalize = true;
935+
if (c.__EXTRACTED_TX) {
936+
tx = c.__EXTRACTED_TX;
937+
mustFinalize = false;
938+
} else {
939+
tx = c.__TX.clone();
940+
}
941+
inputFinalizeGetAmts(inputs, tx, c, mustFinalize);
942+
if (key === '__FEE_RATE') return c.__FEE_RATE!;
943+
else if (key === '__FEE') return c.__FEE!;
944+
}
945+
923946
function getFinalScripts(
924947
script: Buffer,
925948
scriptType: string,
@@ -1398,6 +1421,7 @@ function inputFinalizeGetAmts(
13981421
throw new Error('Outputs are spending more than Inputs');
13991422
}
14001423
const bytes = tx.virtualSize();
1424+
cache.__FEE = fee;
14011425
cache.__EXTRACTED_TX = tx;
14021426
cache.__FEE_RATE = Math.floor(fee / bytes);
14031427
}

types/psbt.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export declare class Psbt {
5757
addOutput(outputData: PsbtOutputExtended): this;
5858
extractTransaction(disableFeeCheck?: boolean): Transaction;
5959
getFeeRate(): number;
60+
getFee(): number;
6061
finalizeAllInputs(): this;
6162
finalizeInput(inputIndex: number): this;
6263
validateSignaturesOfAllInputs(): boolean;

0 commit comments

Comments
 (0)