Skip to content

Commit 2a9ddaa

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 f9ad345 commit 2a9ddaa

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
@@ -846,8 +846,8 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
846846
}
847847
}};
848848
}
849-
macro_rules! make_channel {
850-
($source: expr, $dest: expr, $source_monitor: expr, $dest_monitor: expr, $dest_keys_manager: expr, $chan_id: expr) => {{
849+
macro_rules! connect_peers {
850+
($source: expr, $dest: expr) => {{
851851
let init_dest = Init {
852852
features: $dest.init_features(),
853853
networks: None,
@@ -860,7 +860,10 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
860860
remote_network_address: None,
861861
};
862862
$dest.peer_connected($source.get_our_node_id(), &init_src, false).unwrap();
863-
863+
}};
864+
}
865+
macro_rules! make_channel {
866+
($source: expr, $dest: expr, $source_monitor: expr, $dest_monitor: expr, $dest_keys_manager: expr, $chan_id: expr) => {{
864867
$source.create_channel($dest.get_our_node_id(), 100_000, 42, 0, None, None).unwrap();
865868
let open_channel = {
866869
let events = $source.get_and_clear_pending_msg_events();
@@ -1078,8 +1081,26 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
10781081

10791082
let mut nodes = [node_a, node_b, node_c];
10801083

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

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

10921113
lock_fundings!(nodes);
10931114

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

10991139
let mut p_ctr: u64 = 0;
11001140

1101-
let mut chan_a_disconnected = false;
1102-
let mut chan_b_disconnected = false;
1141+
let mut peers_ab_disconnected = false;
1142+
let mut peers_bc_disconnected = false;
11031143
let mut ab_events = Vec::new();
11041144
let mut ba_events = Vec::new();
11051145
let mut bc_events = Vec::new();
@@ -1114,9 +1154,9 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
11141154

11151155
macro_rules! test_return {
11161156
() => {{
1117-
assert_eq!(nodes[0].list_channels().len(), 1);
1118-
assert_eq!(nodes[1].list_channels().len(), 2);
1119-
assert_eq!(nodes[2].list_channels().len(), 1);
1157+
assert_eq!(nodes[0].list_channels().len(), 3);
1158+
assert_eq!(nodes[1].list_channels().len(), 6);
1159+
assert_eq!(nodes[2].list_channels().len(), 3);
11201160

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

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

17171773
0x0c => {
1718-
if !chan_a_disconnected {
1774+
if !peers_ab_disconnected {
17191775
nodes[0].peer_disconnected(nodes[1].get_our_node_id());
17201776
nodes[1].peer_disconnected(nodes[0].get_our_node_id());
1721-
chan_a_disconnected = true;
1777+
peers_ab_disconnected = true;
17221778
drain_msg_events_on_disconnect!(0);
17231779
}
17241780
},
17251781
0x0d => {
1726-
if !chan_b_disconnected {
1782+
if !peers_bc_disconnected {
17271783
nodes[1].peer_disconnected(nodes[2].get_our_node_id());
17281784
nodes[2].peer_disconnected(nodes[1].get_our_node_id());
1729-
chan_b_disconnected = true;
1785+
peers_bc_disconnected = true;
17301786
drain_msg_events_on_disconnect!(2);
17311787
}
17321788
},
17331789
0x0e => {
1734-
if chan_a_disconnected {
1790+
if peers_ab_disconnected {
17351791
let init_1 = Init {
17361792
features: nodes[1].init_features(),
17371793
networks: None,
@@ -1744,11 +1800,11 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
17441800
remote_network_address: None,
17451801
};
17461802
nodes[1].peer_connected(nodes[0].get_our_node_id(), &init_0, false).unwrap();
1747-
chan_a_disconnected = false;
1803+
peers_ab_disconnected = false;
17481804
}
17491805
},
17501806
0x0f => {
1751-
if chan_b_disconnected {
1807+
if peers_bc_disconnected {
17521808
let init_2 = Init {
17531809
features: nodes[2].init_features(),
17541810
networks: None,
@@ -1761,7 +1817,7 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
17611817
remote_network_address: None,
17621818
};
17631819
nodes[2].peer_connected(nodes[1].get_our_node_id(), &init_1, false).unwrap();
1764-
chan_b_disconnected = false;
1820+
peers_bc_disconnected = false;
17651821
}
17661822
},
17671823

@@ -2097,9 +2153,9 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
20972153
0xb0 | 0xb1 | 0xb2 => {
20982154
// Restart node A, picking among the in-flight `ChannelMonitor`s to use based on
20992155
// the value of `v` we're matching.
2100-
if !chan_a_disconnected {
2156+
if !peers_ab_disconnected {
21012157
nodes[1].peer_disconnected(nodes[0].get_our_node_id());
2102-
chan_a_disconnected = true;
2158+
peers_ab_disconnected = true;
21032159
push_excess_b_events!(
21042160
nodes[1].get_and_clear_pending_msg_events().drain(..),
21052161
Some(0)
@@ -2115,16 +2171,16 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
21152171
0xb3..=0xbb => {
21162172
// Restart node B, picking among the in-flight `ChannelMonitor`s to use based on
21172173
// the value of `v` we're matching.
2118-
if !chan_a_disconnected {
2174+
if !peers_ab_disconnected {
21192175
nodes[0].peer_disconnected(nodes[1].get_our_node_id());
2120-
chan_a_disconnected = true;
2176+
peers_ab_disconnected = true;
21212177
nodes[0].get_and_clear_pending_msg_events();
21222178
ab_events.clear();
21232179
ba_events.clear();
21242180
}
2125-
if !chan_b_disconnected {
2181+
if !peers_bc_disconnected {
21262182
nodes[2].peer_disconnected(nodes[1].get_our_node_id());
2127-
chan_b_disconnected = true;
2183+
peers_bc_disconnected = true;
21282184
nodes[2].get_and_clear_pending_msg_events();
21292185
bc_events.clear();
21302186
cb_events.clear();
@@ -2137,9 +2193,9 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
21372193
0xbc | 0xbd | 0xbe => {
21382194
// Restart node C, picking among the in-flight `ChannelMonitor`s to use based on
21392195
// the value of `v` we're matching.
2140-
if !chan_b_disconnected {
2196+
if !peers_bc_disconnected {
21412197
nodes[1].peer_disconnected(nodes[2].get_our_node_id());
2142-
chan_b_disconnected = true;
2198+
peers_bc_disconnected = true;
21432199
push_excess_b_events!(
21442200
nodes[1].get_and_clear_pending_msg_events().drain(..),
21452201
Some(2)
@@ -2153,28 +2209,76 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
21532209
monitor_c = new_monitor_c;
21542210
},
21552211

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

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

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

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

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

21762280
// First, make sure peers are all connected to each other
2177-
if chan_a_disconnected {
2281+
if peers_ab_disconnected {
21782282
let init_1 = Init {
21792283
features: nodes[1].init_features(),
21802284
networks: None,
@@ -2187,9 +2291,9 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
21872291
remote_network_address: None,
21882292
};
21892293
nodes[1].peer_connected(nodes[0].get_our_node_id(), &init_0, false).unwrap();
2190-
chan_a_disconnected = false;
2294+
peers_ab_disconnected = false;
21912295
}
2192-
if chan_b_disconnected {
2296+
if peers_bc_disconnected {
21932297
let init_2 = Init {
21942298
features: nodes[2].init_features(),
21952299
networks: None,
@@ -2202,7 +2306,7 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
22022306
remote_network_address: None,
22032307
};
22042308
nodes[2].peer_connected(nodes[1].get_our_node_id(), &init_1, false).unwrap();
2205-
chan_b_disconnected = false;
2309+
peers_bc_disconnected = false;
22062310
}
22072311

22082312
macro_rules! process_all_events {
@@ -2213,10 +2317,14 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
22132317
panic!("It may take may iterations to settle the state, but it should not take forever");
22142318
}
22152319
// Next, make sure no monitor updates are pending
2216-
complete_all_monitor_updates(&monitor_a, &chan_1_id);
2217-
complete_all_monitor_updates(&monitor_b, &chan_1_id);
2218-
complete_all_monitor_updates(&monitor_b, &chan_2_id);
2219-
complete_all_monitor_updates(&monitor_c, &chan_2_id);
2320+
for id in &chan_ab_ids {
2321+
complete_all_monitor_updates(&monitor_a, id);
2322+
complete_all_monitor_updates(&monitor_b, id);
2323+
}
2324+
for id in &chan_bc_ids {
2325+
complete_all_monitor_updates(&monitor_b, id);
2326+
complete_all_monitor_updates(&monitor_c, id);
2327+
}
22202328
// Then, make sure any current forwards make their way to their destination
22212329
if process_msg_events!(0, false, ProcessMessages::AllMessages) {
22222330
last_pass_no_updates = false;
@@ -2261,14 +2369,18 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
22612369
process_all_events!();
22622370

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

22732385
last_htlc_clear_fee_a = fee_est_a.ret_val.load(atomic::Ordering::Acquire);
22742386
last_htlc_clear_fee_b = fee_est_b.ret_val.load(atomic::Ordering::Acquire);

0 commit comments

Comments
 (0)