mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-11 09:07:44 +00:00
Merge branch 'master' into next
This commit is contained in:
+142
-13
@@ -448,7 +448,56 @@ impl Node {
|
||||
peer_config,
|
||||
reason,
|
||||
} => {
|
||||
warn!(npub = %peer_config.npub, error = %reason, "NAT traversal failed");
|
||||
let now_ms = Self::now_ms();
|
||||
let decision = bootstrap.record_traversal_failure(&peer_config.npub, now_ms);
|
||||
if decision.should_warn {
|
||||
warn!(
|
||||
npub = %peer_config.npub,
|
||||
error = %reason,
|
||||
consecutive_failures = decision.consecutive_failures,
|
||||
cooldown_secs = decision
|
||||
.cooldown_until_ms
|
||||
.map(|t| t.saturating_sub(now_ms) / 1000),
|
||||
"NAT traversal failed"
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
npub = %peer_config.npub,
|
||||
error = %reason,
|
||||
consecutive_failures = decision.consecutive_failures,
|
||||
"NAT traversal failed (suppressed by warn-rate-limit)"
|
||||
);
|
||||
}
|
||||
|
||||
// B6: stale-advert eviction on the streak-threshold
|
||||
// crossing. Fire-and-forget; the outcome is logged so
|
||||
// operators can see when peers get cleaned up.
|
||||
if decision.crossed_threshold {
|
||||
let bootstrap = bootstrap.clone();
|
||||
let npub = peer_config.npub.clone();
|
||||
tokio::spawn(async move {
|
||||
let outcome = bootstrap.refetch_advert_for_stale_check(&npub).await;
|
||||
match outcome {
|
||||
crate::discovery::nostr::NostrRefetchOutcome::Evicted => info!(
|
||||
npub = %npub,
|
||||
"stale-advert sweep: peer evicted from advert cache"
|
||||
),
|
||||
crate::discovery::nostr::NostrRefetchOutcome::Refreshed => info!(
|
||||
npub = %npub,
|
||||
"stale-advert sweep: peer republished, cache refreshed and streak reset"
|
||||
),
|
||||
crate::discovery::nostr::NostrRefetchOutcome::SameAdvert => debug!(
|
||||
npub = %npub,
|
||||
"stale-advert sweep: advert unchanged, cooldown stands"
|
||||
),
|
||||
crate::discovery::nostr::NostrRefetchOutcome::Skipped => debug!(
|
||||
npub = %npub,
|
||||
"stale-advert sweep: skipped (relay error or no advert_relays)"
|
||||
),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let peer_identity = match PeerIdentity::from_npub(&peer_config.npub) {
|
||||
Ok(identity) => identity,
|
||||
Err(_) => continue,
|
||||
@@ -462,7 +511,16 @@ impl Node {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.schedule_retry(*peer_identity.node_addr(), Self::now_ms());
|
||||
let node_addr = *peer_identity.node_addr();
|
||||
self.schedule_retry(node_addr, now_ms);
|
||||
if let Some(cooldown_until_ms) = decision.cooldown_until_ms
|
||||
&& let Some(state) = self.retry_pending.get_mut(&node_addr)
|
||||
{
|
||||
// Push the next retry past the cooldown so the
|
||||
// open-discovery sweep doesn't re-enqueue and the
|
||||
// per-attempt backoff doesn't fire sooner.
|
||||
state.retry_after_ms = state.retry_after_ms.max(cooldown_until_ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1247,6 +1305,7 @@ impl Node {
|
||||
let mut skipped_connecting = 0usize;
|
||||
let mut skipped_no_endpoints = 0usize;
|
||||
let mut skipped_invalid_npub = 0usize;
|
||||
let mut skipped_cooldown = 0usize;
|
||||
|
||||
for (npub, endpoints, created_at_secs) in candidates {
|
||||
if enqueue_budget == 0 {
|
||||
@@ -1285,6 +1344,10 @@ impl Node {
|
||||
skipped_retry_pending = skipped_retry_pending.saturating_add(1);
|
||||
continue;
|
||||
}
|
||||
if bootstrap.cooldown_until(&npub, now_ms).is_some() {
|
||||
skipped_cooldown = skipped_cooldown.saturating_add(1);
|
||||
continue;
|
||||
}
|
||||
let connecting = self.connections.values().any(|conn| {
|
||||
conn.expected_identity()
|
||||
.map(|id| id.node_addr() == &node_addr)
|
||||
@@ -1352,7 +1415,8 @@ impl Node {
|
||||
+ skipped_retry_pending
|
||||
+ skipped_connecting
|
||||
+ skipped_no_endpoints
|
||||
+ skipped_invalid_npub;
|
||||
+ skipped_invalid_npub
|
||||
+ skipped_cooldown;
|
||||
let should_summarize = caller == "startup" || enqueued > 0;
|
||||
if should_summarize {
|
||||
info!(
|
||||
@@ -1367,6 +1431,7 @@ impl Node {
|
||||
skipped_connecting = skipped_connecting,
|
||||
skipped_no_endpoints = skipped_no_endpoints,
|
||||
skipped_invalid_npub = skipped_invalid_npub,
|
||||
skipped_cooldown = skipped_cooldown,
|
||||
skipped_total = total_skipped,
|
||||
"open-discovery sweep complete"
|
||||
);
|
||||
@@ -1465,7 +1530,10 @@ impl Node {
|
||||
)
|
||||
}
|
||||
|
||||
fn build_overlay_advert(&self) -> Option<OverlayAdvert> {
|
||||
async fn build_overlay_advert(
|
||||
&self,
|
||||
bootstrap: &std::sync::Arc<NostrDiscovery>,
|
||||
) -> Option<OverlayAdvert> {
|
||||
if !self.config.node.discovery.nostr.enabled {
|
||||
return None;
|
||||
}
|
||||
@@ -1487,13 +1555,49 @@ impl Node {
|
||||
continue;
|
||||
}
|
||||
if cfg.is_public() {
|
||||
if let Some(addr) = handle.local_addr()
|
||||
&& !addr.ip().is_unspecified()
|
||||
{
|
||||
// Precedence:
|
||||
// 1. operator-supplied `external_addr` (skips STUN)
|
||||
// 2. non-wildcard `local_addr` (operator bound to
|
||||
// a specific public IP directly)
|
||||
// 3. STUN auto-discovery against ephemeral socket
|
||||
// 4. loud warn + omit endpoint
|
||||
if let Some(explicit) = cfg.external_advert_addr() {
|
||||
endpoints.push(OverlayEndpointAdvert {
|
||||
transport: OverlayTransportKind::Udp,
|
||||
addr: addr.to_string(),
|
||||
addr: explicit.to_string(),
|
||||
});
|
||||
} else {
|
||||
match handle.local_addr() {
|
||||
Some(addr) if !addr.ip().is_unspecified() => {
|
||||
endpoints.push(OverlayEndpointAdvert {
|
||||
transport: OverlayTransportKind::Udp,
|
||||
addr: addr.to_string(),
|
||||
});
|
||||
}
|
||||
Some(addr) => {
|
||||
let key = handle.transport_id().as_u32();
|
||||
let port = addr.port();
|
||||
if let Some(public) =
|
||||
bootstrap.learn_public_udp_addr(key, port).await
|
||||
{
|
||||
endpoints.push(OverlayEndpointAdvert {
|
||||
transport: OverlayTransportKind::Udp,
|
||||
addr: public.to_string(),
|
||||
});
|
||||
} else {
|
||||
warn!(
|
||||
transport_id = key,
|
||||
bind_addr = %addr,
|
||||
"advert: udp public=true bound to wildcard but \
|
||||
STUN observation failed; advertising no UDP \
|
||||
endpoint. Either set transports.udp.external_addr, \
|
||||
bind to a specific public IP, or ensure \
|
||||
node.discovery.nostr.stun_servers is reachable"
|
||||
);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
endpoints.push(OverlayEndpointAdvert {
|
||||
@@ -1510,13 +1614,38 @@ impl Node {
|
||||
if !cfg.advertise_on_nostr() {
|
||||
continue;
|
||||
}
|
||||
if let Some(addr) = handle.local_addr()
|
||||
&& !addr.ip().is_unspecified()
|
||||
{
|
||||
// Precedence:
|
||||
// 1. operator-supplied `external_addr` (only path that
|
||||
// works on cloud-NAT setups where the public IP is
|
||||
// not on a host interface).
|
||||
// 2. non-wildcard `local_addr` (operator bound to a
|
||||
// specific public IP directly).
|
||||
// 3. loud warn + omit endpoint (no TCP STUN equivalent).
|
||||
if let Some(explicit) = cfg.external_advert_addr() {
|
||||
endpoints.push(OverlayEndpointAdvert {
|
||||
transport: OverlayTransportKind::Tcp,
|
||||
addr: addr.to_string(),
|
||||
addr: explicit.to_string(),
|
||||
});
|
||||
} else {
|
||||
match handle.local_addr() {
|
||||
Some(addr) if !addr.ip().is_unspecified() => {
|
||||
endpoints.push(OverlayEndpointAdvert {
|
||||
transport: OverlayTransportKind::Tcp,
|
||||
addr: addr.to_string(),
|
||||
});
|
||||
}
|
||||
Some(addr) => {
|
||||
warn!(
|
||||
bind_addr = %addr,
|
||||
"advert: tcp advertise_on_nostr=true bound to wildcard \
|
||||
and no transports.tcp.external_addr set; advertising no \
|
||||
TCP endpoint. Either set external_addr to the public \
|
||||
IP (recommended for cloud 1:1-NAT setups) or bind \
|
||||
explicitly to the public IP"
|
||||
);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
"tor" => {
|
||||
@@ -1555,7 +1684,7 @@ impl Node {
|
||||
&self,
|
||||
bootstrap: &std::sync::Arc<NostrDiscovery>,
|
||||
) -> Result<(), crate::discovery::nostr::BootstrapError> {
|
||||
let advert = self.build_overlay_advert();
|
||||
let advert = self.build_overlay_advert(bootstrap).await;
|
||||
bootstrap.update_local_advert(advert).await
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user