# Week 14 — Update
## TL;DR
Implemented gossipsub topic membership (JOIN / LEAVE) in zig-libp2p.
PR: https://github.com/zen-eth/eth-p2p-z/pull/83/commits/eb84618eeeb6242a736be147d5e2e743aebf5ac0
---
## High-level behavior
- The pubsub RPC (SUBSCRIBE / UNSUBSCRIBE) announces subscription changes to all peers. We assume a pubsub framework sends those RPCs.
- Gossipsub must additionally maintain its topic mesh state locally. JOIN(topic) and LEAVE(topic) perform mesh-specific actions on top of the RPCs.
- On JOIN(topic), the router builds mesh[topic] by moving peers from fanout[topic] (if any) and selecting peers from peers.gossipsub[topic] up to D; then it informs those peers with GRAFT messages.
- On LEAVE(topic), the router sends PRUNE to peers in mesh[topic] and removes mesh[topic] from local state.
---
## JOIN(topic) — explicit steps
When application calls JOIN(topic):
1. Ensure the pubsub framework sends SUBSCRIBE(topic) RPC (assumed).
2. Prepare mesh[topic]:
- If fanout[topic] exists and contains peers:
- Move as many peers from fanout[topic] into mesh[topic] as possible (up to D).
- If mesh[topic] is still smaller than D:
- Select peers from peers.gossipsub[topic] (excluding self and already-chosen peers) to fill mesh[topic] up to D.
3. For each peer newly added to mesh[topic], send a GRAFT control message indicating we added them to the mesh.
4. Persist mesh[topic] and begin mesh maintenance (heartbeat, mesh trimming/expansion as per gossipsub maintenance rules).
Notes:
- If fanout[topic] had more than D peers, take up to D into mesh and leave the rest (or prune depending on policy).
- Only send GRAFT to peers that are not already known to be in the mesh on the remote side (avoid redundant grafts when possible).
- Do not re-populate fanout[topic] from mesh during JOIN; fanout is for when we are not subscribed.
---
## LEAVE(topic) — explicit steps
When application calls LEAVE(topic):
1. Ensure the pubsub framework sends UNSUBSCRIBE(topic) RPC (assumed).
2. If mesh[topic] exists:
- For each peer in mesh[topic], send a PRUNE control message.
- Remove mesh[topic] from local state (forget mesh membership).
3. Optionally preserve or clear fanout[topic] depending on policy:
- If you want to publish later without joining, keep fanout; otherwise delete fanout[topic].
Notes:
- PRUNE instructs peers to remove the link from their own mesh; follow-up mesh maintenance on their side will act accordingly.
- After LEAVE, the router should stop treating topic as "subscribed" for publish behavior.
---
## Sequence diagram
```mermaid
sequenceDiagram
participant App
participant Router
participant Fanout as fanout[T]
participant Gossip as peers.gossipsub[T]
participant Mesh as mesh[T]
App->>Router: JOIN(T)
Router->>Fanout: take up to D peers -> move to mesh[T]
alt mesh[T] < D
Router->>Gossip: select peers to fill mesh[T] up to D
end
Router->>Mesh: send GRAFT to newly added peers
```
---
## Pseudocode
```pseudo
function join(topic):
// pubsub framework should SUBSCRIBE(topic) externally
mesh = mesh[topic] or empty set
added = empty set
if fanout[topic] exists:
take = min(D - size(mesh), size(fanout[topic]))
move_any(take, fanout[topic], mesh)
added += those moved
if size(mesh) < D:
candidates = peers.gossipsub[topic] - mesh - {self}
select = choose_up_to(D - size(mesh), candidates)
mesh += select
added += select
mesh[topic] = mesh
for p in added:
send_graft(p, topic)
```
```pseudo
function leave(topic):
// pubsub framework should UNSUBSCRIBE(topic) externally
if mesh[topic] exists:
for p in mesh[topic]:
send_prune(p, topic)
delete mesh[topic]
```
---
## Next
Start refactoring topic memory management: implement fanout lifecycle (TTL and replacements), make selection policy pluggable and testable, add unit tests above, and wire JOIN/LEAVE into the existing mesh maintenance heartbeat.
---
## References
- Gossipsub v1.0 — Topic membership: https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.0.md#topic-membership