Skip to content

Commit 24c492d

Browse files
joostjagerclaude
andcommitted
fuzz: expand chanmon_consistency to 6 channels (3 per peer pair)
This expands the channel monitor consistency fuzz test from 2 channels to 6 channels (3 between A-B and 3 between B-C), enabling future MPP payment testing. Changes: - Extract `connect_peers!` macro from `make_channel!` to avoid duplicate peer connections - Create channel arrays `chan_ab_ids[3]` and `chan_bc_ids[3]` - Store SCIDs in `chan_ab_scids[3]` and `chan_bc_scids[3]` - Use funding transaction versions 1-6 to avoid txid collisions under fuzz hashing (which XORs all bytes to a single byte, causing versions 0-5 to collide between A-B and B-C channel pairs) - Update `test_return!` assertions to expect 3/6/3 channels Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 372ba65 commit 24c492d

1 file changed

Lines changed: 174 additions & 62 deletions

File tree

fuzz/src/chanmon_consistency.rs

Lines changed: 174 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -848,8 +848,8 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
848848
}
849849
}};
850850
}
851-
macro_rules! make_channel {
852-
($source: expr, $dest: expr, $source_monitor: expr, $dest_monitor: expr, $dest_keys_manager: expr, $chan_id: expr) => {{
851+
macro_rules! connect_peers {
852+
($source: expr, $dest: expr) => {{
853853
let init_dest = Init {
854854
features: $dest.init_features(),
855855
networks: None,
@@ -862,7 +862,10 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
862862
remote_network_address: None,
863863
};
864864
$dest.peer_connected($source.get_our_node_id(), &init_src, false).unwrap();
865-
865+
}};
866+
}
867+
macro_rules! make_channel {
868+
($source: expr, $dest: expr, $source_monitor: expr, $dest_monitor: expr, $dest_keys_manager: expr, $chan_id: expr) => {{
866869
$source.create_channel($dest.get_our_node_id(), 100_000, 42, 0, None, None).unwrap();
867870
let open_channel = {
868871
let events = $source.get_and_clear_pending_msg_events();
@@ -1080,8 +1083,26 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
10801083

10811084
let mut nodes = [node_a, node_b, node_c];
10821085

1083-
let chan_1_id = make_channel!(nodes[0], nodes[1], monitor_a, monitor_b, keys_manager_b, 0);
1084-
let chan_2_id = make_channel!(nodes[1], nodes[2], monitor_b, monitor_c, keys_manager_c, 1);
1086+
// Connect peers first, then create channels
1087+
connect_peers!(nodes[0], nodes[1]);
1088+
connect_peers!(nodes[1], nodes[2]);
1089+
1090+
// Create 3 channels between A-B and 3 channels between B-C (6 total).
1091+
//
1092+
// Use version numbers 1-6 to avoid txid collisions under fuzz hashing.
1093+
// Fuzz mode uses XOR-based hashing (all bytes XOR to one byte), and
1094+
// versions 0-5 cause collisions between A-B and B-C channel pairs
1095+
// (e.g., A-B with Version(1) collides with B-C with Version(3)).
1096+
let chan_ab_ids = [
1097+
make_channel!(nodes[0], nodes[1], monitor_a, monitor_b, keys_manager_b, 1),
1098+
make_channel!(nodes[0], nodes[1], monitor_a, monitor_b, keys_manager_b, 2),
1099+
make_channel!(nodes[0], nodes[1], monitor_a, monitor_b, keys_manager_b, 3),
1100+
];
1101+
let chan_bc_ids = [
1102+
make_channel!(nodes[1], nodes[2], monitor_b, monitor_c, keys_manager_c, 4),
1103+
make_channel!(nodes[1], nodes[2], monitor_b, monitor_c, keys_manager_c, 5),
1104+
make_channel!(nodes[1], nodes[2], monitor_b, monitor_c, keys_manager_c, 6),
1105+
];
10851106

10861107
// Wipe the transactions-broadcasted set to make sure we don't broadcast any transactions
10871108
// during normal operation in `test_return`.
@@ -1093,15 +1114,34 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
10931114

10941115
lock_fundings!(nodes);
10951116

1096-
let chan_a = nodes[0].list_usable_channels()[0].short_channel_id.unwrap();
1097-
let chan_a_id = nodes[0].list_usable_channels()[0].channel_id;
1098-
let chan_b = nodes[2].list_usable_channels()[0].short_channel_id.unwrap();
1099-
let chan_b_id = nodes[2].list_usable_channels()[0].channel_id;
1117+
// Get SCIDs for all A-B channels (from node A's perspective)
1118+
let node_a_chans: Vec<_> = nodes[0].list_usable_channels();
1119+
let chan_ab_scids: [u64; 3] = [
1120+
node_a_chans[0].short_channel_id.unwrap(),
1121+
node_a_chans[1].short_channel_id.unwrap(),
1122+
node_a_chans[2].short_channel_id.unwrap(),
1123+
];
1124+
let chan_ab_chan_ids: [ChannelId; 3] =
1125+
[node_a_chans[0].channel_id, node_a_chans[1].channel_id, node_a_chans[2].channel_id];
1126+
// Get SCIDs for all B-C channels (from node C's perspective)
1127+
let node_c_chans: Vec<_> = nodes[2].list_usable_channels();
1128+
let chan_bc_scids: [u64; 3] = [
1129+
node_c_chans[0].short_channel_id.unwrap(),
1130+
node_c_chans[1].short_channel_id.unwrap(),
1131+
node_c_chans[2].short_channel_id.unwrap(),
1132+
];
1133+
let chan_bc_chan_ids: [ChannelId; 3] =
1134+
[node_c_chans[0].channel_id, node_c_chans[1].channel_id, node_c_chans[2].channel_id];
1135+
// Keep old names for backward compatibility in existing code
1136+
let chan_a = chan_ab_scids[0];
1137+
let chan_a_id = chan_ab_chan_ids[0];
1138+
let chan_b = chan_bc_scids[0];
1139+
let chan_b_id = chan_bc_chan_ids[0];
11001140

11011141
let mut p_ctr: u64 = 0;
11021142

1103-
let mut chan_a_disconnected = false;
1104-
let mut chan_b_disconnected = false;
1143+
let mut peers_ab_disconnected = false;
1144+
let mut peers_bc_disconnected = false;
11051145
let mut ab_events = Vec::new();
11061146
let mut ba_events = Vec::new();
11071147
let mut bc_events = Vec::new();
@@ -1116,9 +1156,9 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
11161156

11171157
macro_rules! test_return {
11181158
() => {{
1119-
assert_eq!(nodes[0].list_channels().len(), 1);
1120-
assert_eq!(nodes[1].list_channels().len(), 2);
1121-
assert_eq!(nodes[2].list_channels().len(), 1);
1159+
assert_eq!(nodes[0].list_channels().len(), 3);
1160+
assert_eq!(nodes[1].list_channels().len(), 6);
1161+
assert_eq!(nodes[2].list_channels().len(), 3);
11221162

11231163
// At no point should we have broadcasted any transactions after the initial channel
11241164
// opens.
@@ -1711,29 +1751,45 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
17111751
*mon_style[2].borrow_mut() = ChannelMonitorUpdateStatus::Completed;
17121752
},
17131753

1714-
0x08 => complete_all_monitor_updates(&monitor_a, &chan_1_id),
1715-
0x09 => complete_all_monitor_updates(&monitor_b, &chan_1_id),
1716-
0x0a => complete_all_monitor_updates(&monitor_b, &chan_2_id),
1717-
0x0b => complete_all_monitor_updates(&monitor_c, &chan_2_id),
1754+
0x08 => {
1755+
for id in &chan_ab_ids {
1756+
complete_all_monitor_updates(&monitor_a, id);
1757+
}
1758+
},
1759+
0x09 => {
1760+
for id in &chan_ab_ids {
1761+
complete_all_monitor_updates(&monitor_b, id);
1762+
}
1763+
},
1764+
0x0a => {
1765+
for id in &chan_bc_ids {
1766+
complete_all_monitor_updates(&monitor_b, id);
1767+
}
1768+
},
1769+
0x0b => {
1770+
for id in &chan_bc_ids {
1771+
complete_all_monitor_updates(&monitor_c, id);
1772+
}
1773+
},
17181774

17191775
0x0c => {
1720-
if !chan_a_disconnected {
1776+
if !peers_ab_disconnected {
17211777
nodes[0].peer_disconnected(nodes[1].get_our_node_id());
17221778
nodes[1].peer_disconnected(nodes[0].get_our_node_id());
1723-
chan_a_disconnected = true;
1779+
peers_ab_disconnected = true;
17241780
drain_msg_events_on_disconnect!(0);
17251781
}
17261782
},
17271783
0x0d => {
1728-
if !chan_b_disconnected {
1784+
if !peers_bc_disconnected {
17291785
nodes[1].peer_disconnected(nodes[2].get_our_node_id());
17301786
nodes[2].peer_disconnected(nodes[1].get_our_node_id());
1731-
chan_b_disconnected = true;
1787+
peers_bc_disconnected = true;
17321788
drain_msg_events_on_disconnect!(2);
17331789
}
17341790
},
17351791
0x0e => {
1736-
if chan_a_disconnected {
1792+
if peers_ab_disconnected {
17371793
let init_1 = Init {
17381794
features: nodes[1].init_features(),
17391795
networks: None,
@@ -1746,11 +1802,11 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
17461802
remote_network_address: None,
17471803
};
17481804
nodes[1].peer_connected(nodes[0].get_our_node_id(), &init_0, false).unwrap();
1749-
chan_a_disconnected = false;
1805+
peers_ab_disconnected = false;
17501806
}
17511807
},
17521808
0x0f => {
1753-
if chan_b_disconnected {
1809+
if peers_bc_disconnected {
17541810
let init_2 = Init {
17551811
features: nodes[2].init_features(),
17561812
networks: None,
@@ -1763,7 +1819,7 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
17631819
remote_network_address: None,
17641820
};
17651821
nodes[2].peer_connected(nodes[1].get_our_node_id(), &init_1, false).unwrap();
1766-
chan_b_disconnected = false;
1822+
peers_bc_disconnected = false;
17671823
}
17681824
},
17691825

@@ -2099,9 +2155,9 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
20992155
0xb0 | 0xb1 | 0xb2 => {
21002156
// Restart node A, picking among the in-flight `ChannelMonitor`s to use based on
21012157
// the value of `v` we're matching.
2102-
if !chan_a_disconnected {
2158+
if !peers_ab_disconnected {
21032159
nodes[1].peer_disconnected(nodes[0].get_our_node_id());
2104-
chan_a_disconnected = true;
2160+
peers_ab_disconnected = true;
21052161
push_excess_b_events!(
21062162
nodes[1].get_and_clear_pending_msg_events().drain(..),
21072163
Some(0)
@@ -2117,16 +2173,16 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
21172173
0xb3..=0xbb => {
21182174
// Restart node B, picking among the in-flight `ChannelMonitor`s to use based on
21192175
// the value of `v` we're matching.
2120-
if !chan_a_disconnected {
2176+
if !peers_ab_disconnected {
21212177
nodes[0].peer_disconnected(nodes[1].get_our_node_id());
2122-
chan_a_disconnected = true;
2178+
peers_ab_disconnected = true;
21232179
nodes[0].get_and_clear_pending_msg_events();
21242180
ab_events.clear();
21252181
ba_events.clear();
21262182
}
2127-
if !chan_b_disconnected {
2183+
if !peers_bc_disconnected {
21282184
nodes[2].peer_disconnected(nodes[1].get_our_node_id());
2129-
chan_b_disconnected = true;
2185+
peers_bc_disconnected = true;
21302186
nodes[2].get_and_clear_pending_msg_events();
21312187
bc_events.clear();
21322188
cb_events.clear();
@@ -2139,9 +2195,9 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
21392195
0xbc | 0xbd | 0xbe => {
21402196
// Restart node C, picking among the in-flight `ChannelMonitor`s to use based on
21412197
// the value of `v` we're matching.
2142-
if !chan_b_disconnected {
2198+
if !peers_bc_disconnected {
21432199
nodes[1].peer_disconnected(nodes[2].get_our_node_id());
2144-
chan_b_disconnected = true;
2200+
peers_bc_disconnected = true;
21452201
push_excess_b_events!(
21462202
nodes[1].get_and_clear_pending_msg_events().drain(..),
21472203
Some(2)
@@ -2155,28 +2211,76 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
21552211
monitor_c = new_monitor_c;
21562212
},
21572213

2158-
0xf0 => complete_monitor_update(&monitor_a, &chan_1_id, &complete_first),
2159-
0xf1 => complete_monitor_update(&monitor_a, &chan_1_id, &complete_second),
2160-
0xf2 => complete_monitor_update(&monitor_a, &chan_1_id, &Vec::pop),
2214+
0xf0 => {
2215+
for id in &chan_ab_ids {
2216+
complete_monitor_update(&monitor_a, id, &complete_first);
2217+
}
2218+
},
2219+
0xf1 => {
2220+
for id in &chan_ab_ids {
2221+
complete_monitor_update(&monitor_a, id, &complete_second);
2222+
}
2223+
},
2224+
0xf2 => {
2225+
for id in &chan_ab_ids {
2226+
complete_monitor_update(&monitor_a, id, &Vec::pop);
2227+
}
2228+
},
21612229

2162-
0xf4 => complete_monitor_update(&monitor_b, &chan_1_id, &complete_first),
2163-
0xf5 => complete_monitor_update(&monitor_b, &chan_1_id, &complete_second),
2164-
0xf6 => complete_monitor_update(&monitor_b, &chan_1_id, &Vec::pop),
2230+
0xf4 => {
2231+
for id in &chan_ab_ids {
2232+
complete_monitor_update(&monitor_b, id, &complete_first);
2233+
}
2234+
},
2235+
0xf5 => {
2236+
for id in &chan_ab_ids {
2237+
complete_monitor_update(&monitor_b, id, &complete_second);
2238+
}
2239+
},
2240+
0xf6 => {
2241+
for id in &chan_ab_ids {
2242+
complete_monitor_update(&monitor_b, id, &Vec::pop);
2243+
}
2244+
},
21652245

2166-
0xf8 => complete_monitor_update(&monitor_b, &chan_2_id, &complete_first),
2167-
0xf9 => complete_monitor_update(&monitor_b, &chan_2_id, &complete_second),
2168-
0xfa => complete_monitor_update(&monitor_b, &chan_2_id, &Vec::pop),
2246+
0xf8 => {
2247+
for id in &chan_bc_ids {
2248+
complete_monitor_update(&monitor_b, id, &complete_first);
2249+
}
2250+
},
2251+
0xf9 => {
2252+
for id in &chan_bc_ids {
2253+
complete_monitor_update(&monitor_b, id, &complete_second);
2254+
}
2255+
},
2256+
0xfa => {
2257+
for id in &chan_bc_ids {
2258+
complete_monitor_update(&monitor_b, id, &Vec::pop);
2259+
}
2260+
},
21692261

2170-
0xfc => complete_monitor_update(&monitor_c, &chan_2_id, &complete_first),
2171-
0xfd => complete_monitor_update(&monitor_c, &chan_2_id, &complete_second),
2172-
0xfe => complete_monitor_update(&monitor_c, &chan_2_id, &Vec::pop),
2262+
0xfc => {
2263+
for id in &chan_bc_ids {
2264+
complete_monitor_update(&monitor_c, id, &complete_first);
2265+
}
2266+
},
2267+
0xfd => {
2268+
for id in &chan_bc_ids {
2269+
complete_monitor_update(&monitor_c, id, &complete_second);
2270+
}
2271+
},
2272+
0xfe => {
2273+
for id in &chan_bc_ids {
2274+
complete_monitor_update(&monitor_c, id, &Vec::pop);
2275+
}
2276+
},
21732277

21742278
0xff => {
21752279
// Test that no channel is in a stuck state where neither party can send funds even
21762280
// after we resolve all pending events.
21772281

21782282
// First, make sure peers are all connected to each other
2179-
if chan_a_disconnected {
2283+
if peers_ab_disconnected {
21802284
let init_1 = Init {
21812285
features: nodes[1].init_features(),
21822286
networks: None,
@@ -2189,9 +2293,9 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
21892293
remote_network_address: None,
21902294
};
21912295
nodes[1].peer_connected(nodes[0].get_our_node_id(), &init_0, false).unwrap();
2192-
chan_a_disconnected = false;
2296+
peers_ab_disconnected = false;
21932297
}
2194-
if chan_b_disconnected {
2298+
if peers_bc_disconnected {
21952299
let init_2 = Init {
21962300
features: nodes[2].init_features(),
21972301
networks: None,
@@ -2204,7 +2308,7 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
22042308
remote_network_address: None,
22052309
};
22062310
nodes[2].peer_connected(nodes[1].get_our_node_id(), &init_1, false).unwrap();
2207-
chan_b_disconnected = false;
2311+
peers_bc_disconnected = false;
22082312
}
22092313

22102314
macro_rules! process_all_events {
@@ -2215,10 +2319,14 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
22152319
panic!("It may take may iterations to settle the state, but it should not take forever");
22162320
}
22172321
// Next, make sure no monitor updates are pending
2218-
complete_all_monitor_updates(&monitor_a, &chan_1_id);
2219-
complete_all_monitor_updates(&monitor_b, &chan_1_id);
2220-
complete_all_monitor_updates(&monitor_b, &chan_2_id);
2221-
complete_all_monitor_updates(&monitor_c, &chan_2_id);
2322+
for id in &chan_ab_ids {
2323+
complete_all_monitor_updates(&monitor_a, id);
2324+
complete_all_monitor_updates(&monitor_b, id);
2325+
}
2326+
for id in &chan_bc_ids {
2327+
complete_all_monitor_updates(&monitor_b, id);
2328+
complete_all_monitor_updates(&monitor_c, id);
2329+
}
22222330
// Then, make sure any current forwards make their way to their destination
22232331
if process_msg_events!(0, false, ProcessMessages::AllMessages) {
22242332
last_pass_no_updates = false;
@@ -2263,14 +2371,18 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
22632371
process_all_events!();
22642372

22652373
// Finally, make sure that at least one end of each channel can make a substantial payment
2266-
assert!(
2267-
send(0, 1, chan_a, 10_000_000, &mut p_ctr)
2268-
|| send(1, 0, chan_a, 10_000_000, &mut p_ctr)
2269-
);
2270-
assert!(
2271-
send(1, 2, chan_b, 10_000_000, &mut p_ctr)
2272-
|| send(2, 1, chan_b, 10_000_000, &mut p_ctr)
2273-
);
2374+
for &scid in &chan_ab_scids {
2375+
assert!(
2376+
send(0, 1, scid, 10_000_000, &mut p_ctr)
2377+
|| send(1, 0, scid, 10_000_000, &mut p_ctr)
2378+
);
2379+
}
2380+
for &scid in &chan_bc_scids {
2381+
assert!(
2382+
send(1, 2, scid, 10_000_000, &mut p_ctr)
2383+
|| send(2, 1, scid, 10_000_000, &mut p_ctr)
2384+
);
2385+
}
22742386

22752387
last_htlc_clear_fee_a = fee_est_a.ret_val.load(atomic::Ordering::Acquire);
22762388
last_htlc_clear_fee_b = fee_est_b.ret_val.load(atomic::Ordering::Acquire);

0 commit comments

Comments
 (0)