Prem Chaitanya Prathi 3886ac1139
fixup(mix): address PR #3807 review + sim alignment
- waku/waku_mix/protocol.nim: drop the magic-2 cover-traffic fallback
  and the hardcoded 10s epoch. Source cover-traffic totalSlots and
  epochDuration from spamProtectionConfig when RLN is on (so cover
  emission can't outpace proof minting), and from named waku
  constants (WakuCoverTrafficTotalSlots=40, WakuCoverTrafficEpochDuration=60s,
  ~10 emissions/min/node) when RLN is disabled. Single ConstantRate
  CoverTraffic.new call site at the end of the block; the if/else
  only sets up spam protection. Addresses PR review comment on
  protocol.nim line 102.
- waku.nimble: bump mix-rln plugin to 8ec5dc24 (latest on
  feat/cover-traffic-epoch-support: messageId guard + drift-corrected
  epoch timer) and pin nim-lsquic to #6d2bc489 (v0.2.0) so libp2p
  1.15.3's certificate_ffi keeps finding EVP_PKEY in lsquic_ffi.nim.
- waku/common/option_shims: explain in a header comment that the file
  exists because libp2p 1.15.3 dropped Option[T] overloads of
  valueOr/withValue from libp2p/utility; can be removed once those are
  restored upstream. Addresses PR review comment.
- simulations/mixnet/setup_credentials: drop the unused
  SpammerUserMessageLimit constant (the "Higher" comment was wrong
  since 3 < DefaultUserMessageLimit=4, and zerokit blocks proof-gen
  past the per-user limit anyway, so it could never simulate a
  spammer). Addresses PR review comment.
- simulations/mixnet/run_chat_mix{,1}.sh: pass --rln-user-message-limit=4
  so the chat client's RLN budget matches the keystores baked at
  limit=4, otherwise cover-traffic totalSlots vs RLN-budget mismatch
  jams the sim.

Sim verified end-to-end:
  - RLN-on (default sim config): PASS, ≥2 proof-verified per node,
    cover-traffic metrics non-zero.
  - RLN-off (mix nodes only, ad-hoc config): cover-traffic emits at
    ~13/min/node from the new waku defaults (target ~10/min); the
    no-RLN code path is exercised correctly.
2026-05-21 11:30:57 +05:30
..

Mixnet simulation

Aim

Simulate a local mixnet along with a chat app to publish using mix. This is helpful to test any changes during development.

Simulation Details

The simulation includes:

  1. A 5-node mixnet where run_mix_node.sh is the bootstrap node for the other 4 nodes
  2. Two chat app instances that publish messages using lightpush protocol over the mixnet

Available Scripts

Script Description
run_mix_node.sh Bootstrap mix node (must be started first)
run_mix_node1.sh Mix node 1
run_mix_node2.sh Mix node 2
run_mix_node3.sh Mix node 3
run_mix_node4.sh Mix node 4
run_chat_mix.sh Chat app instance 1
run_chat_mix1.sh Chat app instance 2
build_setup.sh Build and generate RLN credentials
check_cover_traffic.sh Monitor cover traffic metrics from all nodes

Prerequisites

Before running the simulation, build wakunode2 and chat2mix:

cd <repo-root-dir>
source env.sh
make wakunode2 chat2mix

RLN Spam Protection Setup

Generate RLN credentials and the shared Merkle tree for all nodes:

cd simulations/mixnet
./build_setup.sh

This script will:

  1. Build and run the setup_credentials tool
  2. Generate RLN credentials for all nodes (5 mix nodes + 2 chat clients)
  3. Create rln_tree.db - the shared Merkle tree with all members
  4. Create keystore files (rln_keystore_{peerId}.json) for each node

Important: All scripts must be run from this directory (simulations/mixnet/) so they can access their credentials and tree file.

To regenerate credentials (e.g., after adding new nodes), run ./build_setup.sh again - it will clean up old files first.

Usage

Step 1: Start the Mix Nodes

Start the bootstrap node first (in a separate terminal):

./run_mix_node.sh

Look for the following log lines to ensure the node started successfully:

INF mounting mix protocol                      topics="waku node"
INF Node setup complete                        topics="wakunode main"

Verify RLN spam protection initialized correctly by checking for these logs:

INF Initializing MixRlnSpamProtection
INF MixRlnSpamProtection initialized, waiting for sync
DBG Tree loaded from file
INF MixRlnSpamProtection started

Then start the remaining mix nodes in separate terminals:

./run_mix_node1.sh
./run_mix_node2.sh
./run_mix_node3.sh
./run_mix_node4.sh

Step 2: Start the Chat Applications

Once all 5 mix nodes are running, start the first chat app:

./run_chat_mix.sh

Enter a nickname when prompted:

pubsub topic is: /waku/2/rs/2/0
Choose a nickname >>

Once you see the following log, the app is ready to publish messages over the mixnet:

Welcome, test!
Listening on
 /ip4/<local-network-ip>/tcp/60000/p2p/16Uiu2HAkxDGqix1ifY3wF1ZzojQWRAQEdKP75wn1LJMfoHhfHz57
ready to publish messages now

Start the second chat app in another terminal:

./run_chat_mix1.sh

Step 3: Test Messaging

Once both chat apps are running, send a message from one and verify it is received by the other.

To exit the chat apps, enter /exit:

>> /exit
quitting...

Running Without DoS Protection

To test cover traffic without RLN spam protection (avoids heavy proof generation compute), the config files include two flags:

mix-user-message-limit=2        # slots per epoch (reduce for lighter testing)
mix-disable-spam-protection=true # skip RLN proof generation/verification

These are already set in config.toml through config4.toml. To re-enable RLN, set mix-disable-spam-protection=false (or remove the line) and ensure credentials are generated via ./build_setup.sh.

When running without DoS protection, cover traffic uses an internal epoch timer and does not require RLN credentials or rln_tree.db.

Monitoring Cover Traffic

Use the metrics script to verify cover traffic is working:

./check_cover_traffic.sh

Key metrics to look for:

  • mix_cover_emitted_total — cover messages generated per node (should increase each epoch)
  • mix_cover_received_total — cover messages received back at origin after 3-hop mix path
  • mix_slots_exhausted_total — expected when slots per epoch are low

Note on Rate Limit and Expected Errors

The default mix-user-message-limit=2 (R=2) with path length L=3 yields a fractional cover target of R/(1+L) = 0.5 packets per epoch. Because this is not an integer, epoch boundary jitter can cause two cover emissions in one epoch, exhausting all slots and leaving none for forwarding. This produces SLOT_EXHAUSTED and SPAM_PROOF_GEN_FAILED errors at intermediate hops — these are expected with the default config.

For a clean setup, R should be a multiple of (1+L) = 4. Setting mix-user-message-limit=4 gives exactly 1 cover packet per epoch with 3 slots remaining for forwarding, eliminating these errors.