Skip to content

Commit a7dd8e3

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

File tree

1 file changed

+237
-0
lines changed

1 file changed

+237
-0
lines changed

tests/fuzz/fuzz-amount-arith.c

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

0 commit comments

Comments
 (0)