- 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.
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:
- A 5-node mixnet where
run_mix_node.shis the bootstrap node for the other 4 nodes - 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:
- Build and run the
setup_credentialstool - Generate RLN credentials for all nodes (5 mix nodes + 2 chat clients)
- Create
rln_tree.db- the shared Merkle tree with all members - 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 pathmix_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.