Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2024-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <arith_uint256.h>6#include <policy/feerate.h>7#include <util/feefrac.h>8#include <test/fuzz/FuzzedDataProvider.h>9#include <test/fuzz/fuzz.h>10#include <test/fuzz/util.h>12#include <compare>13#include <cmath>14#include <cstdint>15#include <iostream>17namespace {19/** The maximum absolute value of an int64_t, as an arith_uint256 (2^63). */20const auto MAX_ABS_INT64 = arith_uint256{1} << 63;22/** Construct an arith_uint256 whose value equals abs(x). */23arith_uint256 Abs256(int64_t x)24{25if (x >= 0) {26// For positive numbers, pass through the value.27return arith_uint256{static_cast<uint64_t>(x)};28} else if (x > std::numeric_limits<int64_t>::min()) {29// For negative numbers, negate first.30return arith_uint256{static_cast<uint64_t>(-x)};31} else {32// Special case for x == -2^63 (for which -x results in integer overflow).33return MAX_ABS_INT64;34}35}37/** Construct an arith_uint256 whose value equals abs(x), for 96-bit x. */38arith_uint256 Abs256(std::pair<int64_t, uint32_t> x)39{40if (x.first >= 0) {41// x.first and x.second are both non-negative; sum their absolute values.42return (Abs256(x.first) << 32) + Abs256(x.second);43} else {44// x.first is negative and x.second is non-negative; subtract the absolute values.45return (Abs256(x.first) << 32) - Abs256(x.second);46}47}49std::strong_ordering MulCompare(int64_t a1, int64_t a2, int64_t b1, int64_t b2)50{51// Compute and compare signs.52int sign_a = (a1 == 0 ? 0 : a1 < 0 ? -1 : 1) * (a2 == 0 ? 0 : a2 < 0 ? -1 : 1);53int sign_b = (b1 == 0 ? 0 : b1 < 0 ? -1 : 1) * (b2 == 0 ? 0 : b2 < 0 ? -1 : 1);54if (sign_a != sign_b) return sign_a <=> sign_b;56// Compute absolute values of products.57auto mul_abs_a = Abs256(a1) * Abs256(a2), mul_abs_b = Abs256(b1) * Abs256(b2);59// Compute products of absolute values.60if (sign_a < 0) {61return mul_abs_b <=> mul_abs_a;62} else {63return mul_abs_a <=> mul_abs_b;64}65}67} // namespace69FUZZ_TARGET(feefrac)70{71FuzzedDataProvider provider(buffer.data(), buffer.size());73int64_t f1 = provider.ConsumeIntegral<int64_t>();74int32_t s1 = provider.ConsumeIntegral<int32_t>();75if (s1 == 0) f1 = 0;76FeeFrac fr1(f1, s1);77assert(fr1.IsEmpty() == (s1 == 0));79int64_t f2 = provider.ConsumeIntegral<int64_t>();80int32_t s2 = provider.ConsumeIntegral<int32_t>();81if (s2 == 0) f2 = 0;82FeeFrac fr2(f2, s2);83assert(fr2.IsEmpty() == (s2 == 0));85// Feerate comparisons86auto cmp_feerate = MulCompare(f1, s2, f2, s1);87assert((ByRatio{fr1} <=> ByRatio{fr2}) == cmp_feerate);88assert((ByRatio{fr1} < ByRatio{fr2}) == std::is_lt(cmp_feerate));89assert((ByRatio{fr1} > ByRatio{fr2}) == std::is_gt(cmp_feerate));91// Compare with manual invocation of FeeFrac::Mul.92auto cmp_mul = FeeFrac::Mul(f1, s2) <=> FeeFrac::Mul(f2, s1);93assert(cmp_mul == cmp_feerate);95// Same, but using FeeFrac::MulFallback.96auto cmp_fallback = FeeFrac::MulFallback(f1, s2) <=> FeeFrac::MulFallback(f2, s1);97assert(cmp_fallback == cmp_feerate);99// Total order comparisons100auto cmp_total = std::is_eq(cmp_feerate) ? (s2 <=> s1) : cmp_feerate;101assert((ByRatioNegSize{fr1} <=> ByRatioNegSize{fr2}) == cmp_total);102assert((ByRatioNegSize{fr1} < ByRatioNegSize{fr2}) == std::is_lt(cmp_total));103assert((ByRatioNegSize{fr1} > ByRatioNegSize{fr2}) == std::is_gt(cmp_total));104assert((ByRatioNegSize{fr1} <= ByRatioNegSize{fr2}) == std::is_lteq(cmp_total));105assert((ByRatioNegSize{fr1} >= ByRatioNegSize{fr2}) == std::is_gteq(cmp_total));106assert((ByRatioNegSize{fr1} == ByRatioNegSize{fr2}) == std::is_eq(cmp_total));107assert((ByRatioNegSize{fr1} != ByRatioNegSize{fr2}) == std::is_neq(cmp_total));108}110FUZZ_TARGET(feefrac_div_fallback)111{112// Verify the behavior of FeeFrac::DivFallback over all possible inputs.114// Construct a 96-bit signed value num, a positive 31-bit value den, and rounding mode.115FuzzedDataProvider provider(buffer.data(), buffer.size());116auto num_high = provider.ConsumeIntegral<int64_t>();117auto num_low = provider.ConsumeIntegral<uint32_t>();118std::pair<int64_t, uint32_t> num{num_high, num_low};119auto den = provider.ConsumeIntegralInRange<int32_t>(1, std::numeric_limits<int32_t>::max());120auto round_down = provider.ConsumeBool();122// Predict the sign of the actual result.123bool is_negative = num_high < 0;124// Evaluate absolute value using arith_uint256. If the actual result is negative and we are125// rounding down, or positive and we are rounding up, the absolute value of the quotient is126// the rounded-up quotient of the absolute values.127auto num_abs = Abs256(num);128auto den_abs = Abs256(den);129auto quot_abs = (is_negative == round_down) ?130(num_abs + den_abs - 1) / den_abs :131num_abs / den_abs;133// If the result is not representable by an int64_t, bail out.134if ((is_negative && quot_abs > MAX_ABS_INT64) || (!is_negative && quot_abs >= MAX_ABS_INT64)) {135return;136}138// Verify the behavior of FeeFrac::DivFallback.139auto res = FeeFrac::DivFallback(num, den, round_down);140assert(res == 0 || (res < 0) == is_negative);141assert(Abs256(res) == quot_abs);143// Compare approximately with floating-point.144long double expect = round_down ? std::floor(num_high * 4294967296.0L + num_low) / den145: std::ceil(num_high * 4294967296.0L + num_low) / den;146// Expect to be accurate within 50 bits of precision, +- 1 sat.147if (expect == 0.0L) {148assert(res >= -1 && res <= 1);149} else if (expect > 0.0L) {150assert(res >= expect * 0.999999999999999L - 1.0L);151assert(res <= expect * 1.000000000000001L + 1.0L);152} else {153assert(res >= expect * 1.000000000000001L - 1.0L);154assert(res <= expect * 0.999999999999999L + 1.0L);155}156}158FUZZ_TARGET(feefrac_mul_div)159{160// Verify the behavior of:161// - The combination of FeeFrac::Mul + FeeFrac::Div.162// - The combination of FeeFrac::MulFallback + FeeFrac::DivFallback.163// - FeeFrac::Evaluate.165// Construct a 32-bit signed multiplicand, a 64-bit signed multiplicand, a positive 31-bit166// divisor, and a rounding mode.167FuzzedDataProvider provider(buffer.data(), buffer.size());168auto mul32 = provider.ConsumeIntegral<int32_t>();169auto mul64 = provider.ConsumeIntegral<int64_t>();170auto div = provider.ConsumeIntegralInRange<int32_t>(1, std::numeric_limits<int32_t>::max());171auto round_down = provider.ConsumeBool();173// Predict the sign of the overall result.174bool is_negative = ((mul32 < 0) && (mul64 > 0)) || ((mul32 > 0) && (mul64 < 0));175// Evaluate absolute value using arith_uint256. If the actual result is negative and we are176// rounding down or positive and we rounding up, the absolute value of the quotient is the177// rounded-up quotient of the absolute values.178auto prod_abs = Abs256(mul32) * Abs256(mul64);179auto div_abs = Abs256(div);180auto quot_abs = (is_negative == round_down) ?181(prod_abs + div_abs - 1) / div_abs :182prod_abs / div_abs;184// If the result is not representable by an int64_t, bail out.185if ((is_negative && quot_abs > MAX_ABS_INT64) || (!is_negative && quot_abs >= MAX_ABS_INT64)) {186// If 0 <= mul32 <= div, then the result is guaranteed to be representable. In the context187// of the Evaluate{Down,Up} calls below, this corresponds to 0 <= at_size <= feefrac.size.188assert(mul32 < 0 || mul32 > div);189return;190}192// Verify the behavior of FeeFrac::Mul + FeeFrac::Div.193auto res = FeeFrac::Div(FeeFrac::Mul(mul64, mul32), div, round_down);194assert(res == 0 || (res < 0) == is_negative);195assert(Abs256(res) == quot_abs);197// Verify the behavior of FeeFrac::MulFallback + FeeFrac::DivFallback.198auto res_fallback = FeeFrac::DivFallback(FeeFrac::MulFallback(mul64, mul32), div, round_down);199assert(res == res_fallback);201// Compare approximately with floating-point.202long double expect = round_down ? std::floor(static_cast<long double>(mul32) * mul64 / div)203: std::ceil(static_cast<long double>(mul32) * mul64 / div);204// Expect to be accurate within 50 bits of precision, +- 1 sat.205if (expect == 0.0L) {206assert(res >= -1 && res <= 1);207} else if (expect > 0.0L) {208assert(res >= expect * 0.999999999999999L - 1.0L);209assert(res <= expect * 1.000000000000001L + 1.0L);210} else {211assert(res >= expect * 1.000000000000001L - 1.0L);212assert(res <= expect * 0.999999999999999L + 1.0L);213}215// Verify the behavior of FeeFrac::Evaluate{Down,Up}.216if (mul32 >= 0) {217auto res_fee = round_down ?218FeeFrac{mul64, div}.EvaluateFeeDown(mul32) :219FeeFrac{mul64, div}.EvaluateFeeUp(mul32);220assert(res == res_fee);222// Compare approximately with CFeeRate.223if (mul64 < std::numeric_limits<int64_t>::max() / 1000 &&224mul64 > std::numeric_limits<int64_t>::min() / 1000 &&225quot_abs < arith_uint256{std::numeric_limits<int64_t>::max() / 1000}) {226CFeeRate feerate(mul64, div);227CAmount feerate_fee{feerate.GetFee(mul32)};228auto allowed_gap = static_cast<int64_t>(mul32 / 1000 + 3 + round_down);229assert(feerate_fee - res_fee >= -allowed_gap);230assert(feerate_fee - res_fee <= allowed_gap);231}232}233}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.