Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions crates/net/p2p/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,20 @@ pub enum SwarmBuildError {
/// [`beacon::scoring::ScoreSettings`].
pub(crate) const MESH_N: usize = 8;

/// How long gossipsub waits before grafting a peer again after either side
/// pruned the other, and, for this node, after leaving a topic before grafting
/// into it again.
///
/// The two are the same on purpose. A rust-libp2p peer (another ethlambda,
/// lighthouse) that sees a mesh peer unsubscribe backs off from it for its own
/// prune backoff, and the shorter one carried by the leaver's PRUNE cannot
/// shorten that. With gossipsub's shorter default for leaving, rejoining a topic
/// inside that window sends grafts every such peer rejects and penalizes, so
/// the mesh stays empty either way and the node pays a behaviour penalty for
/// it. Leaving an aggregator subnet at the end of a duty and rejoining it for
/// one two epochs later does exactly that.
const PRUNE_BACKOFF: Duration = Duration::from_secs(60);

/// The gossipsub parameters both wires share.
///
/// `mesh_n` 8, low 6, high 12, the 700ms heartbeat, and the 6/3 history already
Expand Down Expand Up @@ -846,6 +860,8 @@ pub(crate) fn gossipsub_config(
.fanout_ttl(Duration::from_secs(60))
.history_length(6)
.history_gossip(3)
.prune_backoff(PRUNE_BACKOFF)
.unsubscribe_backoff(PRUNE_BACKOFF)
.duplicate_cache_time(seen_ttl)
.validation_mode(ValidationMode::Anonymous)
.message_id_fn(compute_message_id)
Expand Down Expand Up @@ -3359,6 +3375,15 @@ mod tests {
assert!(!gossipsub_config(ttl, false).validate_messages());
}

#[test]
fn rejoining_a_topic_waits_out_the_backoff_peers_apply_to_a_leaver() {
// Lighthouse leaves gossipsub's prune backoff at its default.
let peers_backoff = libp2p::gossipsub::Config::default().prune_backoff();
let config = gossipsub_config(Duration::from_secs(1), true);
assert!(config.unsubscribe_backoff() >= peers_backoff);
assert!(config.unsubscribe_backoff() >= config.prune_backoff());
}

/// How many times [`concurrent_beacon_requests_on_different_protocols_never_cross`]
/// repeats its connect-and-fire cycle.
///
Expand Down
13 changes: 12 additions & 1 deletion docs/beacon_wire.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ scoring P3 there would penalize every mesh peer for this node's own gap. All
64 attestation subnets get parameters, not just the backbone ones, because
aggregator duties join others at runtime.

Two differences from lighthouse:
Three differences from lighthouse:

- `mesh_n` is this node's `D` (8), not lighthouse's 5. It only enters the
first-message-delivery cap.
Expand All @@ -299,6 +299,17 @@ Two differences from lighthouse:
follower it reported `synced` through a 10-minute catch-up up to 98 slots
behind, since its network-stall rule reads a lagging freshest-known block
as a stalled network.
- **Leaving a topic waits out the peers' backoff.** Gossipsub's default
backoff after unsubscribing (10 s) is shorter than its prune backoff
(60 s), and a rust-libp2p peer that sees a mesh peer unsubscribe backs off
from it for the longer one, which the leaver's PRUNE cannot shorten. An
aggregator subnet left when its duty expires and rejoined within that
window for the next one (a duty in the last slots of epoch N, none in N+1,
one in N+2) re-grafts into peers that reject the graft and charge a
behaviour penalty. `PRUNE_BACKOFF` sets both backoffs to the prune value,
so the node waits out the same window and is not penalized. Neither
consensus-specs nor lighthouse sets these: the spec defers gossipsub v1.1's
extra parameters, and lighthouse keeps the defaults.

The block, aggregate and attestation parameters depend on the active
validator count, so the p2p actor rebuilds them every slot from the head's
Expand Down
Loading