P2P (podseq-p2p)
The p2p crate handles block propagation between the sequencer and full nodes. It is built on Commonware primitives.
Stack#
| Primitive | Role |
|---|---|
commonware-p2p |
Authenticated, encrypted peer connections and peer discovery via the authenticated::discovery module. |
commonware-broadcast |
Buffered broadcast engine for block data (deduplication, caching, pull-by-digest). |
commonware-cryptography |
ed25519 peer identity keys. |
commonware-runtime (tokio) |
Commonware's tokio backend, shared with the node binary for coexistence. |
Two-channel protocol#
Two p2p channels are registered on the discovery network:
| Channel | ID | Purpose |
|---|---|---|
| Broadcast (buffered) | 0 | Full blocks. The broadcast engine caches blocks per peer and serves them by digest. |
| Announce (raw) | 1 | 40-byte (height, digest) notifications. The sequencer sends these so full nodes know which digests to pull. |
Flow#
- Sequencer produces a block → signs it → persists it →
broadcast(block)on channel 0 +announce(height, digest)on channel 1. - Full node polls channel 1 (async, 100ms timeout) → on receiving an
announce, callsreceive(digest)on channel 0 → pulls the block from the
broadcast cache → verifies signature → applies to Reth as an optimistic head only. p2p blocks are never marked safe or finalized. - DA sync is the verification path. Every height is fetched from the Sui
Registry → Walrus. If the block was already pulled in via p2p (and matches
canonical DA block), it is only finalized: the
safe/finalizedfork-choice pointers advance without re-submitting the payload. Only a mismatch (sequencer equivocation) re-applies the canonical DA block and reorgs onto it. p2p reduces latency to head; finality always comes from DA. If a p2p announce is dropped, the block still arrives through DA.
Public API#
P2pConfig: identity key path, listen address, bootstrap peers.P2pNode::new(context, config): creates the network, registers channels, starts the broadcast engine and background tasks. Returns broadcaster + receiver.BlockBroadcaster:broadcast(block)andannounce(height, digest).BlockReceiver:poll_next()(announce channel) andreceive(digest)(pull from broadcast cache).
Configuration#
[p2p]
key_path = "p2p.key" # 64-char hex ed25519 seed
listen_addr = "0.0.0.0:9000" # socket to bind
dialable_addr = "1.2.3.4:9000" # optional, for NAT
bootstrap_peers = [ # optional
"abc123...@1.2.3.4:9000",
]The identity key is generated automatically on first start and persisted to
key_path.