Skip to content

Commit 108f7a6

Browse files
author
Chandra Pratap
committed
fuzz-tests: add test for amount-{sat, msat} arithmetic
The fuzz-amount test doesn't fuzz the arithmetic operations for `struct amount_sat` and `struct amount_msat`. Add a test for the same.
1 parent 44972b2 commit 108f7a6

File tree

1 file changed

+270
-0
lines changed

1 file changed

+270
-0
lines changed

tests/fuzz/fuzz-amount-arith.c

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
#include "config.h"
2+
#include <assert.h>
3+
#include <math.h>
4+
#include <common/amount.h>
5+
#include <common/overflows.h>
6+
#include <tests/fuzz/libfuzz.h>
7+
8+
void init(int *argc, char ***argv) {}
9+
10+
enum op {
11+
OP_MSAT_ADD,
12+
OP_MSAT_SUB,
13+
OP_MSAT_MUL,
14+
OP_MSAT_DIV,
15+
OP_MSAT_RATIO,
16+
OP_MSAT_RATIO_FLOOR,
17+
OP_MSAT_RATIO_CEIL,
18+
OP_MSAT_SCALE,
19+
OP_MSAT_ADD_SAT,
20+
OP_MSAT_SUB_SAT,
21+
OP_SAT_ADD,
22+
OP_SAT_SUB,
23+
OP_SAT_MUL,
24+
OP_SAT_DIV,
25+
OP_SAT_SCALE,
26+
OP_FEE,
27+
OP_ADD_FEE,
28+
OP_SUB_FEE,
29+
OP_TX_FEE,
30+
OP_FEERATE,
31+
OP_COUNT
32+
};
33+
34+
void run(const uint8_t *data, size_t size) {
35+
if (size < sizeof(uint8_t) + 2 * sizeof(struct amount_msat) + sizeof(double))
36+
return;
37+
38+
uint8_t op = *data++ % OP_COUNT;
39+
40+
struct amount_msat a = fromwire_amount_msat(&data, &size);
41+
struct amount_msat b = fromwire_amount_msat(&data, &size);
42+
43+
double f;
44+
memcpy(&f, data, sizeof(f));
45+
data += sizeof(f);
46+
47+
struct amount_sat sa = amount_msat_to_sat_round_down(a);
48+
struct amount_sat sb = amount_msat_to_sat_round_down(b);
49+
50+
u64 u64_param;
51+
memcpy(&u64_param, &f, sizeof(u64_param));
52+
53+
struct amount_msat out_ms;
54+
struct amount_sat out_s;
55+
56+
switch (op) {
57+
case OP_MSAT_ADD:
58+
{
59+
if (amount_msat_add(&out_ms, a, b)) {
60+
assert(out_ms.millisatoshis == a.millisatoshis + b.millisatoshis);
61+
}
62+
break;
63+
}
64+
65+
case OP_MSAT_SUB:
66+
{
67+
if (amount_msat_sub(&out_ms, a, b)) {
68+
assert(out_ms.millisatoshis + b.millisatoshis == a.millisatoshis);
69+
}
70+
break;
71+
}
72+
73+
case OP_MSAT_MUL:
74+
{
75+
if (amount_msat_mul(&out_ms, a, u64_param)) {
76+
assert(out_ms.millisatoshis == a.millisatoshis * u64_param);
77+
}
78+
break;
79+
}
80+
81+
case OP_MSAT_DIV:
82+
{
83+
if (u64_param == 0)
84+
break;
85+
out_ms = amount_msat_div(a, u64_param);
86+
assert(out_ms.millisatoshis == a.millisatoshis / u64_param);
87+
break;
88+
}
89+
90+
case OP_MSAT_RATIO:
91+
{
92+
if (b.millisatoshis == 0)
93+
break;
94+
double ratio = amount_msat_ratio(a, b);
95+
double expected = (double)a.millisatoshis / b.millisatoshis;
96+
assert(ratio == expected);
97+
break;
98+
}
99+
100+
case OP_MSAT_RATIO_FLOOR:
101+
{
102+
if (b.millisatoshis == 0)
103+
break;
104+
u64 floor = amount_msat_ratio_floor(a, b);
105+
assert(floor == a.millisatoshis / b.millisatoshis);
106+
break;
107+
}
108+
109+
case OP_MSAT_RATIO_CEIL:
110+
{
111+
if (b.millisatoshis == 0)
112+
break;
113+
114+
// The assertion remains valid ONLY if there's no overflow
115+
if (a.millisatoshis > UINT64_MAX - b.millisatoshis + 1) {
116+
break;
117+
}
118+
119+
u64 ceil = amount_msat_ratio_ceil(a, b);
120+
u64 quotient = a.millisatoshis / b.millisatoshis;
121+
u64 remainder = a.millisatoshis % b.millisatoshis;
122+
123+
assert(ceil == quotient + (remainder != 0));
124+
break;
125+
}
126+
127+
case OP_MSAT_SCALE:
128+
{
129+
// if (amount_msat_scale(&out_ms, a, f)) {
130+
// double expect = (double)a.millisatoshis * f;
131+
// assert(fabs((double)out_ms.millisatoshis - expect) < 1.0);
132+
// }
133+
break;
134+
}
135+
136+
case OP_MSAT_ADD_SAT:
137+
{
138+
if (amount_msat_add_sat(&out_ms, a, sa)) {
139+
assert(out_ms.millisatoshis == sa.satoshis * MSAT_PER_SAT + a.millisatoshis);
140+
}
141+
break;
142+
}
143+
144+
case OP_MSAT_SUB_SAT:
145+
{
146+
if (amount_msat_sub_sat(&out_ms, a, sa)) {
147+
assert(out_ms.millisatoshis + sa.satoshis * MSAT_PER_SAT == a.millisatoshis);
148+
}
149+
break;
150+
}
151+
152+
case OP_SAT_ADD:
153+
{
154+
if (amount_sat_add(&out_s, sa, sb)) {
155+
assert(out_s.satoshis == sa.satoshis + sb.satoshis);
156+
}
157+
break;
158+
}
159+
160+
case OP_SAT_SUB:
161+
{
162+
if (amount_sat_sub(&out_s, sa, sb)) {
163+
assert(out_s.satoshis == sa.satoshis - sb.satoshis);
164+
}
165+
break;
166+
}
167+
168+
case OP_SAT_MUL:
169+
{
170+
if (amount_sat_mul(&out_s, sa, u64_param)) {
171+
assert(out_s.satoshis == sa.satoshis * u64_param);
172+
}
173+
break;
174+
}
175+
176+
case OP_SAT_DIV:
177+
{
178+
if (u64_param == 0)
179+
break;
180+
out_s = amount_sat_div(sa, u64_param);
181+
assert(out_s.satoshis == sa.satoshis / u64_param);
182+
break;
183+
}
184+
185+
case OP_SAT_SCALE:
186+
{
187+
// if (amount_sat_scale(&out_s, sa, f)) {
188+
// double expect = sa.satoshis * f;
189+
// assert(fabs((double)out_s.satoshis - expect) < 1.0);
190+
// }
191+
break;
192+
}
193+
194+
case OP_FEE:
195+
{
196+
if (amount_msat_fee(&out_ms, a, (u32)(a.millisatoshis & UINT32_MAX), (u32)(b.millisatoshis & UINT32_MAX))) {
197+
assert(out_ms.millisatoshis >= (a.millisatoshis & UINT32_MAX));
198+
}
199+
break;
200+
}
201+
202+
case OP_ADD_FEE:
203+
{
204+
u32 fee_base = (u32)(a.millisatoshis & UINT32_MAX);
205+
u32 fee_prop = (u32)(b.millisatoshis & UINT32_MAX);
206+
207+
struct amount_msat original = a;
208+
struct amount_msat fee;
209+
210+
if (amount_msat_fee(&fee, original, fee_base, fee_prop)) {
211+
struct amount_msat total;
212+
if (amount_msat_add(&total, original, fee)) {
213+
assert(amount_msat_greater_eq(total, fee));
214+
215+
struct amount_msat expected_total;
216+
assert(amount_msat_add(&expected_total, original, fee));
217+
assert(amount_msat_eq(total, expected_total));
218+
219+
a = total;
220+
}
221+
}
222+
}
223+
224+
case OP_SUB_FEE:
225+
{
226+
u32 fee_base = (u32)(a.millisatoshis & UINT32_MAX);
227+
u32 fee_prop = (u32)(b.millisatoshis & UINT32_MAX);
228+
struct amount_msat input = a;
229+
struct amount_msat output = amount_msat_sub_fee(input, fee_base, fee_prop);
230+
struct amount_msat fee;
231+
if (amount_msat_fee(&fee, output, fee_base, fee_prop)) {
232+
struct amount_msat sum;
233+
if (amount_msat_add(&sum, output, fee))
234+
assert(amount_msat_less_eq(sum, input));
235+
}
236+
break;
237+
}
238+
239+
case OP_TX_FEE:
240+
{
241+
if (b.millisatoshis > SIZE_MAX)
242+
break;
243+
u32 fee_per_kw = (u32)(a.millisatoshis & UINT32_MAX);
244+
size_t weight = (size_t)(b.millisatoshis);
245+
246+
/* weights > 2^32 are not real tx and hence, discarded */
247+
if (mul_overflows_u64(fee_per_kw, weight))
248+
break;
249+
struct amount_sat fee = amount_tx_fee(fee_per_kw, weight);
250+
u64 expected = (fee_per_kw * weight) / MSAT_PER_SAT;
251+
assert(fee.satoshis == expected);
252+
break;
253+
}
254+
255+
case OP_FEERATE:
256+
{
257+
struct amount_sat fee = amount_msat_to_sat_round_down(a);
258+
size_t weight = (size_t)(b.millisatoshis);
259+
u32 feerate;
260+
if (weight && amount_feerate(&feerate, fee, weight)) {
261+
u64 expected = (fee.satoshis * MSAT_PER_SAT) / weight;
262+
assert(feerate == expected);
263+
}
264+
break;
265+
}
266+
267+
default:
268+
assert(false && "unknown operation");
269+
}
270+
}

0 commit comments

Comments
 (0)