From 3886ac1139b4c1dec25003335904929853442b92 Mon Sep 17 00:00:00 2001 From: Prem Chaitanya Prathi Date: Thu, 21 May 2026 10:38:31 +0530 Subject: [PATCH] fixup(mix): address PR #3807 review + sim alignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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. --- simulations/mixnet/run_chat_mix.sh | 2 +- simulations/mixnet/run_chat_mix1.sh | 2 +- simulations/mixnet/setup_credentials.nim | 2 -- waku.nimble | 4 +-- waku/common/option_shims.nim | 9 ++++-- waku/waku_mix/protocol.nim | 37 ++++++++++++++++++++---- 6 files changed, 41 insertions(+), 15 deletions(-) diff --git a/simulations/mixnet/run_chat_mix.sh b/simulations/mixnet/run_chat_mix.sh index ef0575375..e4841e2da 100755 --- a/simulations/mixnet/run_chat_mix.sh +++ b/simulations/mixnet/run_chat_mix.sh @@ -1,2 +1,2 @@ -../../build/chat2mix --cluster-id=2 --num-shards-in-network=1 --shard=0 --servicenode="/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o" --log-level=TRACE --nodekey="cb6fe589db0e5d5b48f7e82d33093e4d9d35456f4aaffc2322c473a173b2ac49" --kad-bootstrap-node="/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o" --fleet="none" +../../build/chat2mix --cluster-id=2 --num-shards-in-network=1 --shard=0 --servicenode="/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o" --log-level=TRACE --nodekey="cb6fe589db0e5d5b48f7e82d33093e4d9d35456f4aaffc2322c473a173b2ac49" --kad-bootstrap-node="/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o" --rln-user-message-limit=4 --fleet="none" #--mixnode="/ip4/127.0.0.1/tcp/60002/p2p/16Uiu2HAmLtKaFaSWDohToWhWUZFLtqzYZGPFuXwKrojFVF6az5UF:9231e86da6432502900a84f867004ce78632ab52cd8e30b1ec322cd795710c2a" --mixnode="/ip4/127.0.0.1/tcp/60003/p2p/16Uiu2HAmTEDHwAziWUSz6ZE23h5vxG2o4Nn7GazhMor4bVuMXTrA:275cd6889e1f29ca48e5b9edb800d1a94f49f13d393a0ecf1a07af753506de6c" --mixnode="/ip4/127.0.0.1/tcp/60004/p2p/16Uiu2HAmPwRKZajXtfb1Qsv45VVfRZgK3ENdfmnqzSrVm3BczF6f:e0ed594a8d506681be075e8e23723478388fb182477f7a469309a25e7076fc18" --mixnode="/ip4/127.0.0.1/tcp/60005/p2p/16Uiu2HAmRhxmCHBYdXt1RibXrjAUNJbduAhzaTHwFCZT4qWnqZAu:8fd7a1a7c19b403d231452a9b1ea40eb1cc76f455d918ef8980e7685f9eeeb1f" diff --git a/simulations/mixnet/run_chat_mix1.sh b/simulations/mixnet/run_chat_mix1.sh index 5961fce45..a0aa4813b 100755 --- a/simulations/mixnet/run_chat_mix1.sh +++ b/simulations/mixnet/run_chat_mix1.sh @@ -1 +1 @@ -../../build/chat2mix --cluster-id=2 --num-shards-in-network=1 --shard=0 --servicenode="/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o" --log-level=TRACE --nodekey="35eace7ccb246f20c487e05015ca77273d8ecaed0ed683de3d39bf4f69336feb" --mixnode="/ip4/127.0.0.1/tcp/60002/p2p/16Uiu2HAmLtKaFaSWDohToWhWUZFLtqzYZGPFuXwKrojFVF6az5UF:9231e86da6432502900a84f867004ce78632ab52cd8e30b1ec322cd795710c2a" --mixnode="/ip4/127.0.0.1/tcp/60003/p2p/16Uiu2HAmTEDHwAziWUSz6ZE23h5vxG2o4Nn7GazhMor4bVuMXTrA:275cd6889e1f29ca48e5b9edb800d1a94f49f13d393a0ecf1a07af753506de6c" --mixnode="/ip4/127.0.0.1/tcp/60004/p2p/16Uiu2HAmPwRKZajXtfb1Qsv45VVfRZgK3ENdfmnqzSrVm3BczF6f:e0ed594a8d506681be075e8e23723478388fb182477f7a469309a25e7076fc18" --mixnode="/ip4/127.0.0.1/tcp/60005/p2p/16Uiu2HAmRhxmCHBYdXt1RibXrjAUNJbduAhzaTHwFCZT4qWnqZAu:8fd7a1a7c19b403d231452a9b1ea40eb1cc76f455d918ef8980e7685f9eeeb1f" --mixnode="/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o:9d09ce624f76e8f606265edb9cca2b7de9b41772a6d784bddaf92ffa8fba7d2c" --fleet="none" +../../build/chat2mix --cluster-id=2 --num-shards-in-network=1 --shard=0 --servicenode="/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o" --log-level=TRACE --nodekey="35eace7ccb246f20c487e05015ca77273d8ecaed0ed683de3d39bf4f69336feb" --mixnode="/ip4/127.0.0.1/tcp/60002/p2p/16Uiu2HAmLtKaFaSWDohToWhWUZFLtqzYZGPFuXwKrojFVF6az5UF:9231e86da6432502900a84f867004ce78632ab52cd8e30b1ec322cd795710c2a" --mixnode="/ip4/127.0.0.1/tcp/60003/p2p/16Uiu2HAmTEDHwAziWUSz6ZE23h5vxG2o4Nn7GazhMor4bVuMXTrA:275cd6889e1f29ca48e5b9edb800d1a94f49f13d393a0ecf1a07af753506de6c" --mixnode="/ip4/127.0.0.1/tcp/60004/p2p/16Uiu2HAmPwRKZajXtfb1Qsv45VVfRZgK3ENdfmnqzSrVm3BczF6f:e0ed594a8d506681be075e8e23723478388fb182477f7a469309a25e7076fc18" --mixnode="/ip4/127.0.0.1/tcp/60005/p2p/16Uiu2HAmRhxmCHBYdXt1RibXrjAUNJbduAhzaTHwFCZT4qWnqZAu:8fd7a1a7c19b403d231452a9b1ea40eb1cc76f455d918ef8980e7685f9eeeb1f" --mixnode="/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o:9d09ce624f76e8f606265edb9cca2b7de9b41772a6d784bddaf92ffa8fba7d2c" --rln-user-message-limit=4 --fleet="none" diff --git a/simulations/mixnet/setup_credentials.nim b/simulations/mixnet/setup_credentials.nim index cffd60e9e..f1f053d80 100644 --- a/simulations/mixnet/setup_credentials.nim +++ b/simulations/mixnet/setup_credentials.nim @@ -21,7 +21,6 @@ import const KeystorePassword = "mix-rln-password" # Must match protocol.nim DefaultUserMessageLimit = 4'u64 # R=4 slots per 10s epoch - SpammerUserMessageLimit = 3'u64 # Higher limit for spammer testing # Peer IDs derived from nodekeys in config files # config.toml: nodekey = "f98e3fba96c32e8d1967d460f1b79457380e1a895f7971cecc8528abe733781a" @@ -131,7 +130,6 @@ proc setupCredentialsAndTree() {.async.} = echo " Keystores: rln_keystore_{peerId}.json" echo " Password: ", KeystorePassword echo " Default rate limit: ", DefaultUserMessageLimit - echo " Spammer rate limit: ", SpammerUserMessageLimit echo "" echo "Note: All nodes must use the same rln_tree.db file." diff --git a/waku.nimble b/waku.nimble index cf8c73abc..d36513496 100644 --- a/waku.nimble +++ b/waku.nimble @@ -60,7 +60,7 @@ requires "nim >= 2.2.4", # Packages not on nimble (use git URLs) requires "https://github.com/logos-messaging/nim-ffi" -requires "https://github.com/logos-co/mix-rln-spam-protection-plugin.git#153d0c04aec4cce0109c028140189c648180c866" +requires "https://github.com/logos-co/mix-rln-spam-protection-plugin.git#8ec5dc24e9779a75494431e269c107b5de732c1d" requires "https://github.com/logos-messaging/nim-sds.git#2e9a7683f0e180bf112135fae3a3803eed8490d4" @@ -74,7 +74,7 @@ requires "https://github.com/logos-messaging/nim-sds.git#2e9a7683f0e180bf112135f # updated. requires "https://github.com/NagyZoltanPeter/nim-brokers.git#v2.0.1" -requires "https://github.com/vacp2p/nim-lsquic" +requires "https://github.com/vacp2p/nim-lsquic#6d2bc489d05a0a33636a144d48bd99c707285a42" requires "https://github.com/vacp2p/nim-jwt.git#057ec95eb5af0eea9c49bfe9025b3312c95dc5f2" proc getMyCPU(): string = diff --git a/waku/common/option_shims.nim b/waku/common/option_shims.nim index 6ad67635f..18e433574 100644 --- a/waku/common/option_shims.nim +++ b/waku/common/option_shims.nim @@ -1,6 +1,9 @@ -# Compatibility shims for std/options -# The results library removed valueOr/withValue support for Option[T]. -# These templates restore that functionality. +# Compatibility shims for std/options. +# libp2p 1.15.2 (and earlier) exposed `valueOr` / `withValue` for std `Option[T]` +# via `libp2p/utility`. libp2p 1.15.3 dropped those overloads (only `Opt[T]` and +# `Result[T, E]` remain), which breaks existing waku code that still uses +# std `Option`. Restore the templates locally so the codebase keeps compiling +# while the upstream API gap is resolved. {.push raises: [].} diff --git a/waku/waku_mix/protocol.nim b/waku/waku_mix/protocol.nim index db2d04f9e..1f95ef09b 100644 --- a/waku/waku_mix/protocol.nim +++ b/waku/waku_mix/protocol.nim @@ -29,6 +29,23 @@ logScope: const minMixPoolSize = 4 +# Waku-side cover-traffic defaults for the no-RLN path (i.e. when +# `disableSpamProtection = true`). When RLN is enabled, both values are +# overridden below by the spam-protection plugin's config so cover +# emission can never outpace proof minting. +# +# Emission rate is given by: +# emissionInterval = epochDuration * (1 + PathLength) / totalSlots +# With PathLength = 3 and the values below: 60s * 4 / 40 = 6s, i.e. ~10 +# cover packets per minute per node. Tuned to be light enough not to +# saturate a small testnet while still exercising cover-traffic flow. +const + WakuCoverTrafficTotalSlots = 40 + ## Cover-traffic budget per epoch when RLN is disabled (slot pool size). + WakuCoverTrafficEpochDuration = 60.seconds + ## Cover-traffic epoch duration when RLN is disabled. Slot pool resets + ## at this cadence; the internal epoch timer fires on this interval. + type PublishMessage* = proc(message: WakuMessage): Future[Result[void, string]] {. async, gcsafe, raises: [] @@ -103,12 +120,11 @@ proc new*( peermgr.switch.peerInfo.publicKey.skkey, peermgr.switch.peerInfo.privateKey.skkey, ) - let totalSlots = userMessageLimit.get(2) - let ct = ConstantRateCoverTraffic.new( - totalSlots = totalSlots, - epochDuration = 10.seconds, - useInternalEpochTimer = disableSpamProtection, - ) + # Start with waku's no-RLN cover-traffic defaults. The spam-protection + # branch below overrides these from the plugin's config so cover emission + # can't outpace proof minting when RLN is on. + var ctTotalSlots = WakuCoverTrafficTotalSlots + var ctEpochDuration = WakuCoverTrafficEpochDuration var spamProtectionOpt = default(Opt[SpamProtection]) if not disableSpamProtection: @@ -120,12 +136,21 @@ proc new*( if userMessageLimit.isSome(): spamProtectionConfig.userMessageLimit = userMessageLimit.get() + ctTotalSlots = spamProtectionConfig.userMessageLimit + ctEpochDuration = spamProtectionConfig.epochDurationSeconds.int.seconds + let spamProtection = newMixRlnSpamProtection(spamProtectionConfig).valueOr: return err("failed to create spam protection: " & error) spamProtectionOpt = Opt.some(SpamProtection(spamProtection)) else: info "mix spam protection disabled" + let ct = ConstantRateCoverTraffic.new( + totalSlots = ctTotalSlots, + epochDuration = ctEpochDuration, + useInternalEpochTimer = disableSpamProtection, + ) + var mixRlnSpam: MixRlnSpamProtection if spamProtectionOpt.isSome(): mixRlnSpam = MixRlnSpamProtection(spamProtectionOpt.get())