# Week 18 — Update
## TL;DR
Implemented the ping protocol in zig-libp2p.
PR: https://github.com/zen-eth/eth-p2p-z/pull/85
---
## Wire behavior (protocol semantics)
- A Ping message must include a unique identifier (ping_id) that the responder reflects back in the Pong.
- On receiving a Ping, the peer must immediately (or as soon as possible) send a Pong with the same ping_id.
- On receiving a Pong, the originator matches the pong's ping_id with the outstanding Ping and computes RTT = now - ping_sent_time.
- Duplicate or stale Pong: if ping_id is unknown or already matched/expired, safely ignore.
- Ping to self: if the node receives a Ping from itself via looped connection, handle gracefully (respond with Pong) but do not count it as a measurement for peer health.
---
## Per-peer state & invariants
For each connected peer maintain a PingState:
- last_rtt: most recent RTT sample (ms).
- rtt_ema: exponentially-weighted moving average of RTT (for smoothing).
- outstanding: map ping_id -> sent_timestamp (only one outstanding ping per peer is simplest; implementations may allow a small window).
- consecutive_timeouts: integer count of sequential ping timeouts.
- next_ping_deadline: scheduled time for next proactive ping.
Invariants:
- outstanding count <= configured window (recommended 1).
- consecutive_timeouts resets to 0 on any successful Pong.
- If consecutive_timeouts > ping_retries threshold, mark peer unhealthy and take configured action (log, deprioritize, attempt reconnect, or disconnect).
---
## Internal APIs
- peer_ping.init(config)
- peer_ping.schedule_ping(peer_id) // schedule next ping per ping_interval
- peer_ping.send_ping(peer_id) -> ping_id
- records sent_timestamp in outstanding[ping_id]
- sends Ping RPC/frame on wire
- peer_ping.handle_pong(peer_id, ping_id) -> RTT
- lookup outstanding ping_id, compute RTT, update last_rtt and rtt_ema, clear outstanding, reset consecutive_timeouts
- peer_ping.on_ping_timeout(peer_id, ping_id) -> void
- if outstanding still present and timed out, increment consecutive_timeouts and either retry or escalate per policy
- peer_ping.handle_incoming_ping(peer_id, ping_id)
- immediately send Pong(peer_id, ping_id)
Configuration knobs:
- ping_interval (seconds)
- ping_timeout (seconds)
- ping_retries (count)
- rtt_ema_alpha (0..1) smoothing factor
---
## Example pseudocode
```pseudo
// Periodic worker or per-peer timer
function tick_peer_ping(peer):
if now >= peer.next_ping_deadline and peer.outstanding.is_empty():
ping_id = send_ping(peer)
peer.outstanding[ping_id] = now
schedule_timeout(peer, ping_id, ping_timeout)
peer.next_ping_deadline = now + ping_interval
function handle_pong(peer, ping_id):
if ping_id not in peer.outstanding: return // stale or duplicate
sent = peer.outstanding[ping_id]
rtt = now() - sent
peer.last_rtt = rtt
peer.rtt_ema = alpha * rtt + (1-alpha) * peer.rtt_ema
delete peer.outstanding[ping_id]
peer.consecutive_timeouts = 0
function on_ping_timeout(peer, ping_id):
if ping_id not in peer.outstanding: return // already handled
delete peer.outstanding[ping_id]
peer.consecutive_timeouts += 1
if peer.consecutive_timeouts > ping_retries:
mark_peer_unhealthy(peer)
else:
// optionally retry immediately, or wait until next tick
```
---
## Message format & transport
- Use existing libp2p RPC/subprotocol framing (match repo conventions).
- Ping message payload: { ping_id: [u8; N] } (N = 8 or 16 bytes nonce is sufficient).
- Pong message payload: { ping_id: same as Ping }.
- Ensure endianness and canonical encoding (if using protobuf/CBOR/bytes) match interop expectations.
- Respect per-message size limits; pings are tiny.
---
## Sequence diagram
```mermaid
sequenceDiagram
participant A as local
participant B as remote
A->>B: Ping(ping_id)
Note right of B: store recv time if needed
B->>A: Pong(ping_id)
Note left of A: compute RTT = now - sent_time
```
---
## Next
Integrate the ping health signals with the peer manager (connection scoring / eviction), wire metrics to the monitoring stack, and add the interop tests against other libp2p implementations. Monitor canary nodes to validate behavior in the wild and adjust ping_interval/ping_timeout/ping_retries if needed.
---
## References
- PR: https://github.com/zen-eth/eth-p2p-z/pull/85
- libp2p ping protocol (reference implementations for interop): go-libp2p / js-libp2p ping examples