Skip to content

Commit e590035

Browse files
committed
fix(connectivity): ensure gateway registers peer mesh bootstrap
1 parent e7ab1fb commit e590035

File tree

3 files changed

+63
-72
lines changed

3 files changed

+63
-72
lines changed

crates/core/src/operations/connect.rs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,32 +1106,38 @@ where
11061106
// However, we still respect max_connections - this only applies when there's capacity.
11071107
const EARLY_NETWORK_THRESHOLD: usize = 4;
11081108
let has_capacity = num_connections + num_reserved < max_connections;
1109-
let is_early_network = is_gateway && accepted && num_connections < EARLY_NETWORK_THRESHOLD;
1110-
1111-
if num_connections == 0 || (is_early_network && has_capacity) {
1112-
if num_reserved == 1 && is_gateway && accepted {
1113-
tracing::info!(
1114-
tx = %id,
1115-
joiner = %joiner.peer,
1116-
connections = num_connections,
1117-
has_capacity = %has_capacity,
1118-
"Gateway early network: accepting connection directly (will register immediately)",
1119-
);
1120-
let connectivity_info = ConnectivityInfo::new_bootstrap(
1121-
joiner.clone(),
1122-
1, // Single check for direct connection
1123-
);
1124-
return Ok(Some(ConnectState::AwaitingConnectivity(connectivity_info)));
1125-
} else if num_connections == 0 {
1109+
if is_gateway
1110+
&& accepted
1111+
&& (num_connections == 0 || (num_connections < EARLY_NETWORK_THRESHOLD && has_capacity))
1112+
{
1113+
if num_reserved != 1 {
11261114
tracing::debug!(
11271115
tx = %id,
11281116
joiner = %joiner.peer,
1129-
is_gateway = %is_gateway,
1130-
num_reserved = %num_reserved,
1131-
"Cannot forward or accept: no existing connections, or reserved connections pending",
1117+
num_reserved,
1118+
"Gateway bootstrap registration proceeding despite reserved count"
11321119
);
1133-
return Ok(None);
11341120
}
1121+
tracing::info!(
1122+
tx = %id,
1123+
joiner = %joiner.peer,
1124+
connections = num_connections,
1125+
has_capacity = %has_capacity,
1126+
"Gateway early network: accepting connection directly (will register immediately)",
1127+
);
1128+
let connectivity_info = ConnectivityInfo::new_bootstrap(joiner.clone(), 1); // Single check for direct connection
1129+
return Ok(Some(ConnectState::AwaitingConnectivity(connectivity_info)));
1130+
}
1131+
1132+
if num_connections == 0 {
1133+
tracing::debug!(
1134+
tx = %id,
1135+
joiner = %joiner.peer,
1136+
is_gateway = %is_gateway,
1137+
num_reserved = %num_reserved,
1138+
"Cannot forward or accept: no existing connections, or reserved connections pending",
1139+
);
1140+
return Ok(None);
11351141
}
11361142

11371143
// Try to forward the connection request to an existing peer

crates/core/src/ring/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,6 @@ impl Ring {
468468
);
469469
error
470470
})?;
471-
472471
if live_tx.is_none() {
473472
let conns = self.connection_manager.get_open_connections();
474473
tracing::warn!(

crates/core/tests/connectivity.rs

Lines changed: 36 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use anyhow::bail;
1+
use anyhow::{bail, Context};
22
use freenet::test_utils::{self, make_get, make_put, TestContext};
33
use freenet_macros::freenet_test;
44
use freenet_stdlib::{
@@ -273,6 +273,18 @@ async fn test_three_node_network_connectivity(ctx: &mut TestContext) -> TestResu
273273
let peer1 = ctx.node("peer1")?;
274274
let peer2 = ctx.node("peer2")?;
275275

276+
let peer1_public_port = peer1.network_port.context(
277+
"peer1 missing network port; auto_connect_peers requires public_port for mesh connectivity",
278+
)?;
279+
let peer2_public_port = peer2.network_port.context(
280+
"peer2 missing network port; auto_connect_peers requires public_port for mesh connectivity",
281+
)?;
282+
tracing::info!(
283+
peer1_port = peer1_public_port,
284+
peer2_port = peer2_public_port,
285+
"Verified peer network ports for direct connectivity"
286+
);
287+
276288
let gateway_ws_port = gateway.ws_port;
277289
let peer1_ws_port = peer1.ws_port;
278290
let peer2_ws_port = peer2.ws_port;
@@ -298,34 +310,14 @@ async fn test_three_node_network_connectivity(ctx: &mut TestContext) -> TestResu
298310

299311
// Retry loop to wait for full mesh connectivity
300312
const MAX_RETRIES: usize = 30;
301-
const DIRECT_WAIT_ATTEMPTS: usize = 3;
302313
const RETRY_DELAY: Duration = Duration::from_secs(1);
303-
let mut retry_count = 0;
304-
let mut direct_mesh_established = false;
305-
let mut fell_back_to_gateway = false;
306-
let mut minimal_connectivity_ready = false;
307-
308-
loop {
309-
retry_count += 1;
310-
if retry_count > MAX_RETRIES {
311-
if minimal_connectivity_ready {
312-
tracing::warn!(
313-
"Max retries ({}) reached; continuing with gateway-mediated topology.",
314-
MAX_RETRIES
315-
);
316-
fell_back_to_gateway = true;
317-
break;
318-
} else {
319-
bail!(
320-
"Failed to establish minimum connectivity after {} seconds",
321-
MAX_RETRIES * 2
322-
);
323-
}
324-
}
314+
let mut mesh_established = false;
315+
let mut last_snapshot = (String::new(), String::new(), String::new());
325316

317+
for attempt in 1..=MAX_RETRIES {
326318
tracing::info!(
327319
"Attempt {}/{}: Querying all nodes for connected peers...",
328-
retry_count,
320+
attempt,
329321
MAX_RETRIES
330322
);
331323

@@ -367,45 +359,39 @@ async fn test_three_node_network_connectivity(ctx: &mut TestContext) -> TestResu
367359
tracing::debug!("Peer1 peers: {:?}", peer1_peers);
368360
tracing::debug!("Peer2 peers: {:?}", peer2_peers);
369361

362+
last_snapshot = (
363+
format!("{:?}", gw_peers),
364+
format!("{:?}", peer1_peers),
365+
format!("{:?}", peer2_peers),
366+
);
367+
370368
let gateway_sees_all = gw_peers.len() >= 2;
371-
let peer1_connected = !peer1_peers.is_empty();
372-
let peer2_connected = !peer2_peers.is_empty();
373369
let peer1_direct = peer1_peers.len() >= 2;
374370
let peer2_direct = peer2_peers.len() >= 2;
375371

376-
if gateway_sees_all && peer1_connected && peer2_connected {
377-
minimal_connectivity_ready = true;
378-
if peer1_direct && peer2_direct {
379-
tracing::info!("✅ Full mesh connectivity established!");
380-
direct_mesh_established = true;
381-
break;
382-
}
383-
}
384-
385-
if !direct_mesh_established
386-
&& minimal_connectivity_ready
387-
&& retry_count >= DIRECT_WAIT_ATTEMPTS
388-
{
389-
tracing::warn!(
390-
"Peer topology stabilized via gateway only (peer1 direct: {}, peer2 direct: {}). Proceeding with fallback.",
391-
peer1_direct,
392-
peer2_direct
393-
);
394-
fell_back_to_gateway = true;
372+
if gateway_sees_all && peer1_direct && peer2_direct {
373+
tracing::info!("✅ Full mesh connectivity established!");
374+
mesh_established = true;
395375
break;
396376
}
397377

398378
tracing::info!("Network not fully connected yet, waiting...");
399379
tokio::time::sleep(RETRY_DELAY).await;
400380
}
401381

382+
if !mesh_established {
383+
bail!(
384+
"Failed to establish full mesh connectivity after {} attempts. Gateway peers: {}; peer1 peers: {}; peer2 peers: {}",
385+
MAX_RETRIES,
386+
last_snapshot.0,
387+
last_snapshot.1,
388+
last_snapshot.2
389+
);
390+
}
391+
402392
// Verify functionality with PUT/GET
403393
tracing::info!("Verifying network functionality with PUT/GET operations");
404394

405-
if fell_back_to_gateway && !direct_mesh_established {
406-
tracing::warn!("Gateway-mediated routing is being exercised; direct peer links were not observed within {} attempts.", DIRECT_WAIT_ATTEMPTS);
407-
}
408-
409395
make_put(&mut client1, wrapped_state.clone(), contract.clone(), false).await?;
410396
let resp = tokio::time::timeout(Duration::from_secs(60), client1.recv()).await;
411397
match resp {

0 commit comments

Comments
 (0)