Skip to content

Commit 80dc829

Browse files
committed
tests: Calculate fees more similarly to CFeeRate::GetFee
Because of floating point precision issues, not all of the rounding done is always correct. To fix this, the fee calculation for assert_fee_amount is changed to better reflect how CFeeRate::GetFee does it. First the feerate is converted to an int representing sat/kvb. Then this is multiplied by the transaction size, divivided by 1000, and rounded up to the nearest sat. The result is then converted back to BTC (divided by 1e8) and then rounded down to the nearest sat to avoid precision errors.
1 parent ce2cc44 commit 80dc829

File tree

1 file changed

+15
-3
lines changed
  • test/functional/test_framework

1 file changed

+15
-3
lines changed

test/functional/test_framework/util.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ def assert_approx(v, vexp, vspan=0.00001):
3636

3737
def assert_fee_amount(fee, tx_size, feerate_BTC_kvB):
3838
"""Assert the fee is in range."""
39-
feerate_BTC_vB = feerate_BTC_kvB / 1000
40-
target_fee = satoshi_round(tx_size * feerate_BTC_vB)
39+
target_fee = get_fee(tx_size, feerate_BTC_kvB)
4140
if fee < target_fee:
4241
raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)" % (str(fee), str(target_fee)))
4342
# allow the wallet's estimation to be at most 2 bytes off
44-
if fee > (tx_size + 2) * feerate_BTC_vB:
43+
high_fee = get_fee(tx_size + 2, feerate_BTC_kvB)
44+
if fee > high_fee:
4545
raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)" % (str(fee), str(target_fee)))
4646

4747

@@ -218,6 +218,18 @@ def str_to_b64str(string):
218218
return b64encode(string.encode('utf-8')).decode('ascii')
219219

220220

221+
def ceildiv(a, b):
222+
"""Divide 2 ints and round up to next int rather than round down"""
223+
return -(-a // b)
224+
225+
226+
def get_fee(tx_size, feerate_btc_kvb):
227+
"""Calculate the fee in BTC given a feerate is BTC/kvB. Reflects CFeeRate::GetFee"""
228+
feerate_sat_kvb = int(feerate_btc_kvb * Decimal(1e8)) # Fee in sat/kvb as an int to avoid float precision errors
229+
target_fee_sat = ceildiv(feerate_sat_kvb * tx_size, 1000) # Round calculated fee up to nearest sat
230+
return satoshi_round(target_fee_sat / Decimal(1e8)) # Truncate BTC result to nearest sat
231+
232+
221233
def satoshi_round(amount):
222234
return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
223235

0 commit comments

Comments
 (0)