Skip to content

[Backport release-mainnet-1.2.0-rc] fix(libp2p): add network discriminators#4443

Merged
sveitser merged 1 commit into
release-mainnet-1.2.0-rcfrom
backport-4418-to-release-mainnet-1.2.0-rc
Jun 5, 2026
Merged

[Backport release-mainnet-1.2.0-rc] fix(libp2p): add network discriminators#4443
sveitser merged 1 commit into
release-mainnet-1.2.0-rcfrom
backport-4418-to-release-mainnet-1.2.0-rc

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented Jun 5, 2026

Backport of #4418 to release-mainnet-1.2.0-rc.

* test(libp2p): snapshot mainnet protocol identifiers

- Extract three helpers in `network/node.rs` returning the libp2p
  protocol identifiers used today (gossipsub prefix `None` = libp2p
  default, kademlia `/ipfs/kad/1.0.0`, direct message
  `/HotShot/direct_message/1.0`).
- Wire the helpers into the swarm builder so they are the single source
  of truth.
- Add an `insta` snapshot test that locks in the values. A future change
  that would silently shift these strings (and partition mainnet from
  itself) now fails the snapshot.

* feat(libp2p): partition non-mainnet networks via chain-id discriminator

- Add `network_discriminator: Option<String>` to `NetworkNodeConfig`.
  `None` keeps mainnet's libp2p protocol strings byte-identical to
  today; `Some(d)` appends `/{d}` to gossipsub's `protocol_id_prefix`,
  the kademlia `StreamProtocol`, and the request-response direct
  message `StreamProtocol`. Identify's `protocol_version` is left
  untouched since it does not gate substream negotiation.
- Plumb the discriminator through `Libp2pNetwork::from_config`.
- At the espresso-node call site, derive the discriminator from
  `genesis.chain_config.chain_id`: `None` when chain_id is 1
  (mainnet), else `Some(chain_id.to_string())`. Decaf and other
  non-mainnet networks now run on distinct libp2p protocols and
  cannot merge with mainnet at the gossipsub or kademlia layers.
- The snapshot test added in the previous commit confirms the
  mainnet path is unchanged.

* doc(libp2p): tighten network_discriminator field comment

* test(libp2p): snapshot decaf protocol identifiers

- Extract three `gossipsub_prefix` / `kad_protocol` /
  `direct_message_protocol` wrappers that take an optional
  discriminator and call the existing mainnet helpers when `None`.
- Replace the inline match arms in `NetworkNode::new` with calls to
  these wrappers so production code and tests share a single path.
- Add a decaf snapshot test alongside the mainnet one, both routed
  through a shared `snapshot_for(discriminator)` helper. Decaf's
  chain_id (`0xdecaf` = 912559) produces protocol IDs like
  `/ipfs/kad/1.0.0/912559`.

* refactor(libp2p): type network_discriminator as Option<U256>

- Discriminator is always a chain id; use `U256` rather than `String`
  at the libp2p-networking boundary.
- Format chain ids as hex with the `0x` prefix in the protocol
  strings, matching the spelling in `data/genesis/decaf.toml`. Decaf's
  protocols become `/ipfs/kad/1.0.0/0xdecaf`, etc.
- Test reads naturally as `snapshot_for(Some(U256::from(0xdecafu64)))`.
- Mainnet snapshot is unchanged (still goes through the `None`
  branch).

* refactor(node): promote MAINNET_CHAIN_ID to lib.rs, type as ChainId

- Move the constant out of `api/unlock_schedule.rs` so the libp2p
  call site can reuse it instead of comparing against a bare
  `U256::ONE`.
- Retype from `u64 = 1` to `ChainId = ChainId(U256::ONE)` so callers
  no longer wrap it in `U256::from(...)` or `ChainId(...)` at each
  use site.

(cherry picked from commit 284de7a)
@github-actions github-actions Bot marked this pull request as ready for review June 5, 2026 15:19
@sveitser sveitser closed this Jun 5, 2026
@sveitser sveitser reopened this Jun 5, 2026
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Jun 5, 2026

Claude finished @sveitser's task in 1m 18s —— View job


Reviewing backport PR #4443

  • Gather context (CLAUDE.md, existing comments, PR diff)
  • Review changed files in detail
  • Post review findings

Review Summary

This is a clean backport of #4418, adding network discriminators to libp2p protocol strings (gossipsub, Kademlia, direct messages) so that non-mainnet networks are isolated at the protocol level. Mainnet (ChainId == 1) keeps the existing protocol strings unchanged, while all other chain IDs append a hex-formatted discriminator suffix (e.g. /ipfs/kad/1.0.0/0xdecaf).

Verdict: Looks good. The design is sound and the implementation is clean. A few observations below.


Strengths

  • Backwards-compatible by design: mainnet (None discriminator) preserves exactly the existing protocol strings, so existing mainnet nodes won't be partitioned. Snapshot tests lock this down.
  • Clean separation: Protocol string construction is factored into standalone functions (gossipsub_prefix, kad_protocol, direct_message_protocol) that are individually testable and snapshot-tested.
  • Good use of insta snapshots: The mainnet_libp2p_protocol_identifiers test ensures that any accidental change to mainnet protocols is caught immediately.
  • MAINNET_CHAIN_ID consolidation: Moving the constant from unlock_schedule.rs to a shared lib.rs location avoids duplication.

Minor Observations

  1. Identify protocol not discriminated (node.rs:314): The identify protocol string ("HotShot/identify/1.0") is not modified by the discriminator. This is likely intentional since identify is used for peer metadata exchange and doesn't need network isolation — but worth confirming if nodes from different networks should avoid exchanging identify info.

  2. pub(crate) on MAINNET_CHAIN_ID (lib.rs:39): The constant is pub(crate) — appropriate for now, but if other crates ever need to check for mainnet, it'll need to be elevated. Not a concern for this PR.

  3. Snapshot test coverage: The tests cover None (mainnet) and Some(0xdecaf) as a sample discriminator. No edge-case test for Some(U256::ZERO) (chain ID 0) or very large U256 values, but the #x formatting is well-defined for all U256, so this is fine for a backport.

No Issues Found

The backport is faithful to the original PR. No bugs, security issues, or performance concerns identified. All call sites of Libp2pNetwork::from_config (production in lib.rs and example in infra.rs) are correctly updated with the new parameter. The NetworkNodeConfig builder and manual Clone impl both propagate the new field.


@sveitser sveitser merged commit e274ed9 into release-mainnet-1.2.0-rc Jun 5, 2026
139 of 140 checks passed
@sveitser sveitser deleted the backport-4418-to-release-mainnet-1.2.0-rc branch June 5, 2026 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant