Merge bitcoin/bitcoin#35303: policy: fix negative CFeeRate::ToString() formatting

4200f8163a policy: fix negative CFeeRate::ToString() formatting (joaonevess)

Pull request description:

  `CFeeRate` may represent modified/effective fee rates, for example after a negative `prioritisetransaction` fee delta.

  Previously, `CFeeRate::ToString()` formatted the quotient and remainder directly. For negative values, C++ `%` produces a negative remainder, which could result in malformed strings such as `0.-01 sat/vB`.

  This changes formatting to emit the sign once and format non-negative quotient/remainder parts. Positive fee rate formatting is unchanged.

  This is a display-only change and does not affect fee calculation or policy behavior.

ACKs for top commit:
  polespinasa:
    ACK 4200f8163a
  winterrdog:
    Re-ACK 4200f8163a
  sedited:
    ACK 4200f8163a

Tree-SHA512: bf499078040154ac7828d0a58248b725156b7cfd763a8d6a2e48bcbd77f195a5e0ca9d3a973becdc7436e0bafb46a22cb49dd7ae947f98b434bed7d048d18397
This commit is contained in:
merge-script
2026-09-09 12:47:56 +02:00
2 changed files with 28 additions and 2 deletions

View File

@@ -7,6 +7,7 @@
#include <policy/feerate.h>
#include <tinyformat.h>
#include <cstdlib>
CFeeRate::CFeeRate(const CAmount& nFeePaid, int32_t virtual_bytes)
{
@@ -29,9 +30,16 @@ CAmount CFeeRate::GetFee(int32_t virtual_bytes) const
std::string CFeeRate::ToString(FeeRateFormat fee_rate_format) const
{
const CAmount feerate_per_kvb{GetFeePerK()};
const auto format_feerate = [](const CAmount fee_rate, const CAmount divisor, const int decimals, const std::string& currency_unit, const std::string& size_unit) {
Assert(divisor > 0);
const char* sign{fee_rate < 0 ? "-" : ""};
const CAmount quotient{std::abs(fee_rate / divisor)};
const CAmount remainder{std::abs(fee_rate % divisor)};
return strprintf("%s%d.%0*d %s/%s", sign, quotient, decimals, remainder, currency_unit, size_unit);
};
switch (fee_rate_format) {
case FeeRateFormat::BTC_KVB: return strprintf("%d.%08d %s/kvB", feerate_per_kvb / COIN, feerate_per_kvb % COIN, CURRENCY_UNIT);
case FeeRateFormat::SAT_VB: return strprintf("%d.%03d %s/vB", feerate_per_kvb / 1000, feerate_per_kvb % 1000, CURRENCY_ATOM);
case FeeRateFormat::BTC_KVB: return format_feerate(feerate_per_kvb, COIN, /*decimals=*/8, CURRENCY_UNIT, "kvB");
case FeeRateFormat::SAT_VB: return format_feerate(feerate_per_kvb, 1000, /*decimals=*/3, CURRENCY_ATOM, "vB");
} // no default case, so the compiler can warn about missing cases
assert(false);
}

View File

@@ -144,6 +144,24 @@ BOOST_AUTO_TEST_CASE(ToStringTest)
BOOST_CHECK_EQUAL(feeRate.ToString(), "0.00000001 BTC/kvB");
BOOST_CHECK_EQUAL(feeRate.ToString(FeeRateFormat::BTC_KVB), "0.00000001 BTC/kvB");
BOOST_CHECK_EQUAL(feeRate.ToString(FeeRateFormat::SAT_VB), "0.001 sat/vB");
feeRate = CFeeRate(0);
BOOST_CHECK_EQUAL(feeRate.ToString(), "0.00000000 BTC/kvB");
BOOST_CHECK_EQUAL(feeRate.ToString(FeeRateFormat::BTC_KVB), "0.00000000 BTC/kvB");
BOOST_CHECK_EQUAL(feeRate.ToString(FeeRateFormat::SAT_VB), "0.000 sat/vB");
feeRate = CFeeRate(-1);
BOOST_CHECK_EQUAL(feeRate.ToString(), "-0.00000001 BTC/kvB");
BOOST_CHECK_EQUAL(feeRate.ToString(FeeRateFormat::BTC_KVB), "-0.00000001 BTC/kvB");
BOOST_CHECK_EQUAL(feeRate.ToString(FeeRateFormat::SAT_VB), "-0.001 sat/vB");
feeRate = CFeeRate(-1000);
BOOST_CHECK_EQUAL(feeRate.ToString(), "-0.00001000 BTC/kvB");
BOOST_CHECK_EQUAL(feeRate.ToString(FeeRateFormat::SAT_VB), "-1.000 sat/vB");
feeRate = CFeeRate(-COIN - 1);
BOOST_CHECK_EQUAL(feeRate.ToString(), "-1.00000001 BTC/kvB");
BOOST_CHECK_EQUAL(feeRate.ToString(FeeRateFormat::SAT_VB), "-100000.001 sat/vB");
}
BOOST_AUTO_TEST_SUITE_END()