99#include < test/fuzz/util.h>
1010
1111#include < compare>
12+ #include < cmath>
1213#include < cstdint>
1314#include < iostream>
1415
@@ -32,6 +33,18 @@ arith_uint256 Abs256(int64_t x)
3233 }
3334}
3435
36+ /* * Construct an arith_uint256 whose value equals abs(x), for 96-bit x. */
37+ arith_uint256 Abs256 (std::pair<int64_t , uint32_t > x)
38+ {
39+ if (x.first >= 0 ) {
40+ // x.first and x.second are both non-negative; sum their absolute values.
41+ return (Abs256 (x.first ) << 32 ) + Abs256 (x.second );
42+ } else {
43+ // x.first is negative and x.second is non-negative; subtract the absolute values.
44+ return (Abs256 (x.first ) << 32 ) - Abs256 (x.second );
45+ }
46+ }
47+
3548std::strong_ordering MulCompare (int64_t a1, int64_t a2, int64_t b1, int64_t b2)
3649{
3750 // Compute and compare signs.
@@ -92,3 +105,98 @@ FUZZ_TARGET(feefrac)
92105 assert ((fr1 == fr2) == std::is_eq (cmp_total));
93106 assert ((fr1 != fr2) == std::is_neq (cmp_total));
94107}
108+
109+ FUZZ_TARGET (feefrac_div_fallback)
110+ {
111+ // Verify the behavior of FeeFrac::DivFallback over all possible inputs.
112+
113+ // Construct a 96-bit signed value num, and positive 31-bit value den.
114+ FuzzedDataProvider provider (buffer.data (), buffer.size ());
115+ auto num_high = provider.ConsumeIntegral <int64_t >();
116+ auto num_low = provider.ConsumeIntegral <uint32_t >();
117+ std::pair<int64_t , uint32_t > num{num_high, num_low};
118+ auto den = provider.ConsumeIntegralInRange <int32_t >(1 , std::numeric_limits<int32_t >::max ());
119+
120+ // Predict the sign of the actual result.
121+ bool is_negative = num_high < 0 ;
122+ // Evaluate absolute value using arith_uint256. If the actual result is negative, the absolute
123+ // value of the quotient is the rounded-up quotient of the absolute values.
124+ auto num_abs = Abs256 (num);
125+ auto den_abs = Abs256 (den);
126+ auto quot_abs = is_negative ? (num_abs + den_abs - 1 ) / den_abs : num_abs / den_abs;
127+
128+ // If the result is not representable by an int64_t, bail out.
129+ if ((is_negative && quot_abs > MAX_ABS_INT64) || (!is_negative && quot_abs >= MAX_ABS_INT64)) {
130+ return ;
131+ }
132+
133+ // Verify the behavior of FeeFrac::DivFallback.
134+ auto res = FeeFrac::DivFallback (num, den);
135+ assert ((res < 0 ) == is_negative);
136+ assert (Abs256 (res) == quot_abs);
137+
138+ // Compare approximately with floating-point.
139+ long double expect = std::floorl (num_high * 4294967296 .0L + num_low) / den;
140+ // Expect to be accurate within 50 bits of precision, +- 1 sat.
141+ if (expect == 0 .0L ) {
142+ assert (res >= -1 && res <= 1 );
143+ } else if (expect > 0 .0L ) {
144+ assert (res >= expect * 0 .999999999999999L - 1 .0L );
145+ assert (res <= expect * 1 .000000000000001L + 1 .0L );
146+ } else {
147+ assert (res >= expect * 1 .000000000000001L - 1 .0L );
148+ assert (res <= expect * 0 .999999999999999L + 1 .0L );
149+ }
150+ }
151+
152+ FUZZ_TARGET (feefrac_mul_div)
153+ {
154+ // Verify the behavior of:
155+ // - The combination of FeeFrac::Mul + FeeFrac::Div.
156+ // - The combination of FeeFrac::MulFallback + FeeFrac::DivFallback.
157+ // - FeeFrac::Evaluate.
158+
159+ // Construct a 32-bit signed multiplicand, a 64-bit signed multiplicand, and a positive 31-bit
160+ // divisor.
161+ FuzzedDataProvider provider (buffer.data (), buffer.size ());
162+ auto mul32 = provider.ConsumeIntegral <int32_t >();
163+ auto mul64 = provider.ConsumeIntegral <int64_t >();
164+ auto div = provider.ConsumeIntegralInRange <int32_t >(1 , std::numeric_limits<int32_t >::max ());
165+
166+ // Predict the sign of the overall result.
167+ bool is_negative = ((mul32 < 0 ) && (mul64 > 0 )) || ((mul32 > 0 ) && (mul64 < 0 ));
168+ // Evaluate absolute value using arith_uint256. If the actual result is negative, the absolute
169+ // value of the quotient is the rounded-up quotient of the absolute values.
170+ auto prod_abs = Abs256 (mul32) * Abs256 (mul64);
171+ auto div_abs = Abs256 (div);
172+ auto quot_abs = is_negative ? (prod_abs + div_abs - 1 ) / div_abs : prod_abs / div_abs;
173+
174+ // If the result is not representable by an int64_t, bail out.
175+ if ((is_negative && quot_abs > MAX_ABS_INT64) || (!is_negative && quot_abs >= MAX_ABS_INT64)) {
176+ // If 0 <= mul32 <= div, then the result is guaranteed to be representable.
177+ assert (mul32 < 0 || mul32 > div);
178+ return ;
179+ }
180+
181+ // Verify the behavior of FeeFrac::Mul + FeeFrac::Div.
182+ auto res = FeeFrac::Div (FeeFrac::Mul (mul64, mul32), div);
183+ assert ((res < 0 ) == is_negative);
184+ assert (Abs256 (res) == quot_abs);
185+
186+ // Verify the behavior of FeeFrac::MulFallback + FeeFrac::DivFallback.
187+ auto res_fallback = FeeFrac::DivFallback (FeeFrac::MulFallback (mul64, mul32), div);
188+ assert (res == res_fallback);
189+
190+ // Compare approximately with floating-point.
191+ long double expect = std::floorl (static_cast <long double >(mul32) * mul64 / div);
192+ // Expect to be accurate within 50 bits of precision, +- 1 sat.
193+ if (expect == 0 .0L ) {
194+ assert (res >= -1 && res <= 1 );
195+ } else if (expect > 0 .0L ) {
196+ assert (res >= expect * 0 .999999999999999L - 1 .0L );
197+ assert (res <= expect * 1 .000000000000001L + 1 .0L );
198+ } else {
199+ assert (res >= expect * 1 .000000000000001L - 1 .0L );
200+ assert (res <= expect * 0 .999999999999999L + 1 .0L );
201+ }
202+ }
0 commit comments