Skip to content

Commit 48ced70

Browse files
Chandra Prataprustyrussell
authored andcommitted
fuzz-tests: Add a test for calculate_our_funding()
Changelog-None: `calculate_our_funding()` in `plugins/funder_policy.c` is responsible for calculating our funding policy. Add a test for it.
1 parent 71ddba2 commit 48ced70

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed

tests/fuzz/fuzz-funder-policy.c

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#include "config.h"
2+
#include <ccan/array_size/array_size.h>
3+
#include <common/setup.h>
4+
#include <wire/wire.h>
5+
#include <stdio.h>
6+
#include <tests/fuzz/libfuzz.h>
7+
8+
#include "../../plugins/funder_policy.c"
9+
10+
/* AUTOGENERATED MOCKS START */
11+
/* Generated stub for json_add_string */
12+
void json_add_string(struct json_stream *js UNNEEDED,
13+
const char *fieldname UNNEEDED,
14+
const char *str TAKES UNNEEDED)
15+
{ fprintf(stderr, "json_add_string called!\n"); abort(); }
16+
/* AUTOGENERATED MOCKS END */
17+
18+
#define MAX_SATS ((u64)WALLY_SATOSHI_PER_BTC * WALLY_BTC_MAX)
19+
20+
struct test_case {
21+
struct amount_sat their_funds;
22+
struct amount_sat available_funds;
23+
struct amount_sat *our_last_funds;
24+
struct amount_sat max_channel_size;
25+
struct amount_sat lease_request;
26+
struct funder_policy policy;
27+
};
28+
29+
static struct amount_sat fromwire_amount_sat_bounded(const u8 **cursor, size_t *max)
30+
{
31+
struct amount_sat amt = fromwire_amount_sat(cursor, max);
32+
amt.satoshis %= (MAX_SATS + 1);
33+
return amt;
34+
}
35+
36+
static struct test_case *new_test_case(const tal_t *ctx, const u8 **cursor, size_t *max)
37+
{
38+
struct test_case *tcase = tal(ctx, struct test_case);
39+
tcase->their_funds = fromwire_amount_sat_bounded(cursor, max);
40+
tcase->available_funds = fromwire_amount_sat_bounded(cursor, max);
41+
42+
/* Read flag for our_last_funds */
43+
u8 flag = fromwire_u8(cursor, max);
44+
45+
/* Handle our_last_funds conditionally */
46+
struct amount_sat *our_last_funds_val = tal(ctx, struct amount_sat);
47+
if (flag)
48+
{
49+
*our_last_funds_val = fromwire_amount_sat_bounded(cursor, max);
50+
tcase->our_last_funds = our_last_funds_val;
51+
}
52+
else
53+
tcase->our_last_funds = NULL;
54+
55+
tcase->max_channel_size = fromwire_amount_sat_bounded(cursor, max);
56+
tcase->lease_request = fromwire_amount_sat_bounded(cursor, max);
57+
58+
tcase->policy.opt = (enum funder_opt)(fromwire_u8(cursor, max) % 3);
59+
tcase->policy.mod = fromwire_u64(cursor, max);
60+
switch (tcase->policy.opt)
61+
{
62+
case MATCH:
63+
tcase->policy.mod %= 201;
64+
break;
65+
case AVAILABLE:
66+
tcase->policy.mod %= 101;
67+
break;
68+
case FIXED:
69+
tcase->policy.mod %= MAX_SATS + 1;
70+
break;
71+
default:
72+
assert(false && "invalid policy");
73+
}
74+
tcase->policy.min_their_funding = fromwire_amount_sat_bounded(cursor, max);
75+
tcase->policy.max_their_funding = fromwire_amount_sat_bounded(cursor, max);
76+
77+
if (amount_sat_greater(tcase->policy.min_their_funding, tcase->policy.max_their_funding)) {
78+
struct amount_sat tmp = tcase->policy.min_their_funding;
79+
tcase->policy.min_their_funding = tcase->policy.max_their_funding;
80+
tcase->policy.max_their_funding = tmp;
81+
}
82+
83+
tcase->policy.per_channel_max = fromwire_amount_sat_bounded(cursor, max);
84+
tcase->policy.per_channel_min = fromwire_amount_sat_bounded(cursor, max);
85+
86+
if (amount_sat_greater(tcase->policy.per_channel_min, tcase->policy.per_channel_max)) {
87+
struct amount_sat tmp = tcase->policy.per_channel_min;
88+
tcase->policy.per_channel_min = tcase->policy.per_channel_max;
89+
tcase->policy.per_channel_max = tmp;
90+
}
91+
92+
tcase->policy.fuzz_factor = fromwire_u8(cursor, max) % 101;
93+
tcase->policy.reserve_tank = fromwire_amount_sat_bounded(cursor, max);
94+
tcase->policy.fund_probability = fromwire_u8(cursor, max) % 101;
95+
tcase->policy.leases_only = fromwire_u8(cursor, max) & 1;
96+
97+
return tcase;
98+
}
99+
100+
void init(int *argc, char ***argv)
101+
{}
102+
103+
void run(const u8 *data, size_t size)
104+
{
105+
struct test_case *tcase = new_test_case(tmpctx, &data, &size);
106+
107+
struct node_id id;
108+
memset(&id, 1, sizeof(id));
109+
const char *err;
110+
struct amount_sat our_funds;
111+
112+
/* Call the function under test */
113+
err = calculate_our_funding(&tcase->policy, id,
114+
tcase->their_funds,
115+
tcase->our_last_funds,
116+
tcase->available_funds,
117+
tcase->max_channel_size,
118+
tcase->lease_request,
119+
&our_funds);
120+
121+
/* Validate invariants */
122+
if (!err)
123+
{
124+
/* Check total doesn't exceed max_channel_size */
125+
struct amount_sat total;
126+
if (!amount_sat_add(&total, tcase->their_funds, our_funds)) {
127+
fprintf(stderr, "Overflow in total channel capacity\n");
128+
abort();
129+
}
130+
if (amount_sat_greater(total, tcase->max_channel_size)) {
131+
fprintf(stderr, "Total channel capacity %"PRIu64" exceeds size %"PRIu64"\n",
132+
total.satoshis, tcase->max_channel_size.satoshis);
133+
abort();
134+
}
135+
136+
/* Check our_funds is within per-channel limits */
137+
if (amount_sat_less(our_funds, tcase->policy.per_channel_min) &&
138+
!amount_sat_is_zero(our_funds)) {
139+
fprintf(stderr, "our_funds %"PRIu64" < per_channel_min %"PRIu64"\n",
140+
our_funds.satoshis, tcase->policy.per_channel_min.satoshis);
141+
abort();
142+
}
143+
if (amount_sat_greater(our_funds, tcase->policy.per_channel_max)) {
144+
fprintf(stderr, "our_funds %"PRIu64" > per_max_channel_size %"PRIu64"\n",
145+
our_funds.satoshis, tcase->policy.per_channel_max.satoshis);
146+
abort();
147+
}
148+
}
149+
150+
/* Check available funds constraint */
151+
struct amount_sat available_minus_reserve;
152+
if (amount_sat_sub(&available_minus_reserve, tcase->available_funds, tcase->policy.reserve_tank)) {
153+
if (amount_sat_greater(our_funds, available_minus_reserve)) {
154+
fprintf(stderr, "our_funds %"PRIu64" > available %"PRIu64" - reserve %"PRIu64"\n",
155+
our_funds.satoshis, tcase->available_funds.satoshis,
156+
tcase->policy.reserve_tank.satoshis);
157+
abort();
158+
}
159+
} else if (!amount_sat_eq(our_funds, AMOUNT_SAT(0))) {
160+
fprintf(stderr, "Reserve %"PRIu64" >= available %"PRIu64" but our_funds %"PRIu64" != 0\n",
161+
tcase->policy.reserve_tank.satoshis, tcase->available_funds.satoshis,
162+
our_funds.satoshis);
163+
abort();
164+
}
165+
166+
clean_tmpctx();
167+
}

0 commit comments

Comments
 (0)