diff --git a/simulations/mixnet/README.md b/simulations/mixnet/README.md index 99b0ba50b..7db1d0579 100644 --- a/simulations/mixnet/README.md +++ b/simulations/mixnet/README.md @@ -24,6 +24,7 @@ The simulation includes: | `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 @@ -130,3 +131,35 @@ 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: + +```toml +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: + +```bash +./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. diff --git a/simulations/mixnet/check_cover_traffic.sh b/simulations/mixnet/check_cover_traffic.sh new file mode 100755 index 000000000..f79ab5893 --- /dev/null +++ b/simulations/mixnet/check_cover_traffic.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Check cover traffic metrics from all mix nodes. +# Ports: 8008 + ports-shift (1-5) = 8009-8013 + +echo "=== Cover Traffic Metrics ===" +echo "" + +for i in 1 2 3 4 5; do + port=$((8008 + i)) + echo "--- Node $i (port $port) ---" + metrics=$(curl -s "http://127.0.0.1:$port/metrics" 2>/dev/null) + if [ -z "$metrics" ]; then + echo " (unreachable)" + else + echo "$metrics" | grep -E "mix_cover_|mix_slots_" | grep -v "^#" || echo " (no cover metrics yet)" + fi + echo "" +done diff --git a/simulations/mixnet/config.toml b/simulations/mixnet/config.toml index 5cd1aa936..44ad6addf 100644 --- a/simulations/mixnet/config.toml +++ b/simulations/mixnet/config.toml @@ -7,6 +7,7 @@ lightpush = true max-connections = 150 peer-exchange = false metrics-logging = false +metrics-server = true cluster-id = 2 discv5-discovery = false discv5-udp-port = 9000 @@ -26,3 +27,5 @@ nat = "extip:127.0.0.1" ext-multiaddr = ["/ip4/127.0.0.1/tcp/60001"] ext-multiaddr-only = true ip-colocation-limit=0 +mix-user-message-limit=2 +mix-disable-spam-protection=true diff --git a/simulations/mixnet/config1.toml b/simulations/mixnet/config1.toml index 73cccb8c6..ad5fccea7 100644 --- a/simulations/mixnet/config1.toml +++ b/simulations/mixnet/config1.toml @@ -7,6 +7,7 @@ lightpush = true max-connections = 150 peer-exchange = false metrics-logging = false +metrics-server = true cluster-id = 2 discv5-discovery = false discv5-udp-port = 9001 @@ -27,4 +28,6 @@ nat = "extip:127.0.0.1" ext-multiaddr = ["/ip4/127.0.0.1/tcp/60002"] ext-multiaddr-only = true ip-colocation-limit=0 +mix-user-message-limit=2 +mix-disable-spam-protection=true #staticnode = ["/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o", "/ip4/127.0.0.1/tcp/60003/p2p/16Uiu2HAmTEDHwAziWUSz6ZE23h5vxG2o4Nn7GazhMor4bVuMXTrA","/ip4/127.0.0.1/tcp/60004/p2p/16Uiu2HAmPwRKZajXtfb1Qsv45VVfRZgK3ENdfmnqzSrVm3BczF6f","/ip4/127.0.0.1/tcp/60005/p2p/16Uiu2HAmRhxmCHBYdXt1RibXrjAUNJbduAhzaTHwFCZT4qWnqZAu"] diff --git a/simulations/mixnet/config2.toml b/simulations/mixnet/config2.toml index 3acd2bf8a..dc7ea12c6 100644 --- a/simulations/mixnet/config2.toml +++ b/simulations/mixnet/config2.toml @@ -7,6 +7,7 @@ lightpush = true max-connections = 150 peer-exchange = false metrics-logging = false +metrics-server = true cluster-id = 2 discv5-discovery = false discv5-udp-port = 9002 @@ -27,4 +28,6 @@ nat = "extip:127.0.0.1" ext-multiaddr = ["/ip4/127.0.0.1/tcp/60003"] ext-multiaddr-only = true ip-colocation-limit=0 +mix-user-message-limit=2 +mix-disable-spam-protection=true #staticnode = ["/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o", "/ip4/127.0.0.1/tcp/60002/p2p/16Uiu2HAmLtKaFaSWDohToWhWUZFLtqzYZGPFuXwKrojFVF6az5UF","/ip4/127.0.0.1/tcp/60004/p2p/16Uiu2HAmPwRKZajXtfb1Qsv45VVfRZgK3ENdfmnqzSrVm3BczF6f","/ip4/127.0.0.1/tcp/60005/p2p/16Uiu2HAmRhxmCHBYdXt1RibXrjAUNJbduAhzaTHwFCZT4qWnqZAu"] diff --git a/simulations/mixnet/config3.toml b/simulations/mixnet/config3.toml index bd8e7c4e9..bdf2478e1 100644 --- a/simulations/mixnet/config3.toml +++ b/simulations/mixnet/config3.toml @@ -7,6 +7,7 @@ lightpush = true max-connections = 150 peer-exchange = false metrics-logging = false +metrics-server = true cluster-id = 2 discv5-discovery = false discv5-udp-port = 9003 @@ -27,4 +28,6 @@ nat = "extip:127.0.0.1" ext-multiaddr = ["/ip4/127.0.0.1/tcp/60004"] ext-multiaddr-only = true ip-colocation-limit=0 +mix-user-message-limit=2 +mix-disable-spam-protection=true #staticnode = ["/ip4/127.0.0.1/tcp/60002/p2p/16Uiu2HAmLtKaFaSWDohToWhWUZFLtqzYZGPFuXwKrojFVF6az5UF", "/ip4/127.0.0.1/tcp/60003/p2p/16Uiu2HAmTEDHwAziWUSz6ZE23h5vxG2o4Nn7GazhMor4bVuMXTrA","/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o","/ip4/127.0.0.1/tcp/60005/p2p/16Uiu2HAmRhxmCHBYdXt1RibXrjAUNJbduAhzaTHwFCZT4qWnqZAu"] diff --git a/simulations/mixnet/config4.toml b/simulations/mixnet/config4.toml index f174250d5..d667cd963 100644 --- a/simulations/mixnet/config4.toml +++ b/simulations/mixnet/config4.toml @@ -7,6 +7,7 @@ lightpush = true max-connections = 150 peer-exchange = false metrics-logging = false +metrics-server = true cluster-id = 2 discv5-discovery = false discv5-udp-port = 9004 @@ -27,4 +28,6 @@ nat = "extip:127.0.0.1" ext-multiaddr = ["/ip4/127.0.0.1/tcp/60005"] ext-multiaddr-only = true ip-colocation-limit=0 +mix-user-message-limit=2 +mix-disable-spam-protection=true #staticnode = ["/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o", "/ip4/127.0.0.1/tcp/60003/p2p/16Uiu2HAmTEDHwAziWUSz6ZE23h5vxG2o4Nn7GazhMor4bVuMXTrA","/ip4/127.0.0.1/tcp/60004/p2p/16Uiu2HAmPwRKZajXtfb1Qsv45VVfRZgK3ENdfmnqzSrVm3BczF6f","/ip4/127.0.0.1/tcp/60002/p2p/16Uiu2HAmLtKaFaSWDohToWhWUZFLtqzYZGPFuXwKrojFVF6az5UF"] diff --git a/simulations/mixnet/setup_credentials.nim b/simulations/mixnet/setup_credentials.nim index 77c796354..ef3ee88d7 100644 --- a/simulations/mixnet/setup_credentials.nim +++ b/simulations/mixnet/setup_credentials.nim @@ -20,8 +20,8 @@ import const KeystorePassword = "mix-rln-password" # Must match protocol.nim - DefaultUserMessageLimit = 100'u64 # Network-wide default rate limit - SpammerUserMessageLimit = 3'u64 # Lower limit for spammer testing + DefaultUserMessageLimit = 2'u64 # ~12 msgs/min with 10s epochs + SpammerUserMessageLimit = 3'u64 # Higher limit for spammer testing # Peer IDs derived from nodekeys in config files # config.toml: nodekey = "f98e3fba96c32e8d1967d460f1b79457380e1a895f7971cecc8528abe733781a" diff --git a/tests/test_wakunode.nim b/tests/test_wakunode.nim index a7f1084fb..39452890d 100644 --- a/tests/test_wakunode.nim +++ b/tests/test_wakunode.nim @@ -1,7 +1,7 @@ {.used.} import - std/[sequtils, strutils, net], + std/[options, sequtils, strutils, net], stew/byteutils, testutils/unittests, chronicles, diff --git a/tests/waku_relay/test_wakunode_relay.nim b/tests/waku_relay/test_wakunode_relay.nim index a687119bd..ebfeef22c 100644 --- a/tests/waku_relay/test_wakunode_relay.nim +++ b/tests/waku_relay/test_wakunode_relay.nim @@ -1,7 +1,7 @@ {.used.} import - std/[os, strutils, sequtils, sysrand, math], + std/[options, os, strutils, sequtils, sysrand, math], stew/byteutils, testutils/unittests, chronos, diff --git a/tests/waku_store/test_wakunode_store.nim b/tests/waku_store/test_wakunode_store.nim index fa73cd16d..b7732aa65 100644 --- a/tests/waku_store/test_wakunode_store.nim +++ b/tests/waku_store/test_wakunode_store.nim @@ -1,7 +1,7 @@ {.used.} import - std/sequtils, + std/[options, sequtils], testutils/unittests, chronicles, chronos, diff --git a/tests/waku_store_legacy/test_wakunode_store.nim b/tests/waku_store_legacy/test_wakunode_store.nim index 58e3ca9e0..d174e87d2 100644 --- a/tests/waku_store_legacy/test_wakunode_store.nim +++ b/tests/waku_store_legacy/test_wakunode_store.nim @@ -1,7 +1,7 @@ {.used.} import - std/net, + std/[net, options], testutils/unittests, chronos, libp2p/crypto/crypto, diff --git a/tools/confutils/cli_args.nim b/tools/confutils/cli_args.nim index 112e3911a..6c99dc500 100644 --- a/tools/confutils/cli_args.nim +++ b/tools/confutils/cli_args.nim @@ -643,6 +643,17 @@ with the drawback of consuming some more bandwidth.""", name: "mixnode" .}: seq[MixNodePubInfo] + mixUserMessageLimit* {. + desc: "Maximum messages per RLN epoch for mix cover traffic. If not set, uses plugin default.", + name: "mix-user-message-limit" + .}: Option[int] + + mixDisableSpamProtection* {. + desc: "Disable RLN spam protection for mix protocol (for testing only).", + defaultValue: false, + name: "mix-disable-spam-protection" + .}: bool + # Kademlia Discovery config enableKadDiscovery* {. desc: @@ -1073,6 +1084,9 @@ proc toWakuConf*(n: WakuNodeConf): ConfResult[WakuConf] = b.withMix(n.mix) if n.mixkey.isSome(): b.mixConf.withMixKey(n.mixkey.get()) + if n.mixUserMessageLimit.isSome(): + b.mixConf.withUserMessageLimit(n.mixUserMessageLimit.get()) + b.mixConf.withDisableSpamProtection(n.mixDisableSpamProtection) b.filterServiceConf.withEnabled(n.filter) b.filterServiceConf.withSubscriptionTimeout(n.filterSubscriptionTimeout) diff --git a/vendor/mix-rln-spam-protection-plugin b/vendor/mix-rln-spam-protection-plugin index 037f8e100..bf56c9b9a 160000 --- a/vendor/mix-rln-spam-protection-plugin +++ b/vendor/mix-rln-spam-protection-plugin @@ -1 +1 @@ -Subproject commit 037f8e100bfedffdbad1c4442e760d10a2437428 +Subproject commit bf56c9b9a640e612ce34aa3776c4eedfd5366ab3 diff --git a/vendor/nim-libp2p b/vendor/nim-libp2p index ff8d51857..145dcbc2a 160000 --- a/vendor/nim-libp2p +++ b/vendor/nim-libp2p @@ -1 +1 @@ -Subproject commit ff8d51857b4b79a68468e7bcc27b2026cca02996 +Subproject commit 145dcbc2aa4d5dc3025d7578b147561b5aa40388 diff --git a/waku/common/option_shims.nim b/waku/common/option_shims.nim new file mode 100644 index 000000000..6ad67635f --- /dev/null +++ b/waku/common/option_shims.nim @@ -0,0 +1,28 @@ +# Compatibility shims for std/options +# The results library removed valueOr/withValue support for Option[T]. +# These templates restore that functionality. + +{.push raises: [].} + +import std/options + +template valueOr*[T](self: Option[T], def: untyped): T = + let tmp = self + if tmp.isSome(): + tmp.get() + else: + def + +template withValue*[T](self: Option[T], value, body: untyped): untyped = + let tmp = self + if tmp.isSome(): + let value {.inject.} = tmp.get() + body + +template withValue*[T](self: Option[T], value, body, elseBody: untyped): untyped = + let tmp = self + if tmp.isSome(): + let value {.inject.} = tmp.get() + body + else: + elseBody diff --git a/waku/common/rate_limit/request_limiter.nim b/waku/common/rate_limit/request_limiter.nim index bc318e151..8db84f833 100644 --- a/waku/common/rate_limit/request_limiter.nim +++ b/waku/common/rate_limit/request_limiter.nim @@ -24,6 +24,7 @@ import import std/times except TimeInterval, Duration, seconds, minutes +import ../option_shims import ./[single_token_limiter, service_metrics, timed_map] export token_bucket, setting, service_metrics diff --git a/waku/discovery/waku_discv5.nim b/waku/discovery/waku_discv5.nim index 0eb329fa4..6b85165ef 100644 --- a/waku/discovery/waku_discv5.nim +++ b/waku/discovery/waku_discv5.nim @@ -10,7 +10,7 @@ import eth/keys as eth_keys, eth/p2p/discoveryv5/node, eth/p2p/discoveryv5/protocol -import ../node/peer_manager/peer_manager, ../waku_core, ../waku_enr +import ../common/option_shims, ../node/peer_manager/peer_manager, ../waku_core, ../waku_enr export protocol, waku_enr diff --git a/waku/factory/conf_builder/mix_conf_builder.nim b/waku/factory/conf_builder/mix_conf_builder.nim index 145ccb76e..7af7a4ad1 100644 --- a/waku/factory/conf_builder/mix_conf_builder.nim +++ b/waku/factory/conf_builder/mix_conf_builder.nim @@ -12,6 +12,8 @@ type MixConfBuilder* = object enabled: Option[bool] mixKey: Option[string] mixNodes: seq[MixNodePubInfo] + userMessageLimit: Option[int] + disableSpamProtection: bool proc init*(T: type MixConfBuilder): MixConfBuilder = MixConfBuilder() @@ -25,6 +27,12 @@ proc withMixKey*(b: var MixConfBuilder, mixKey: string) = proc withMixNodes*(b: var MixConfBuilder, mixNodes: seq[MixNodePubInfo]) = b.mixNodes = mixNodes +proc withUserMessageLimit*(b: var MixConfBuilder, limit: int) = + b.userMessageLimit = some(limit) + +proc withDisableSpamProtection*(b: var MixConfBuilder, disable: bool) = + b.disableSpamProtection = disable + proc build*(b: MixConfBuilder): Result[Option[MixConf], string] = if not b.enabled.get(false): return ok(none[MixConf]()) @@ -33,11 +41,11 @@ proc build*(b: MixConfBuilder): Result[Option[MixConf], string] = let mixPrivKey = intoCurve25519Key(ncrutils.fromHex(b.mixKey.get())) let mixPubKey = public(mixPrivKey) return ok( - some(MixConf(mixKey: mixPrivKey, mixPubKey: mixPubKey, mixNodes: b.mixNodes)) + some(MixConf(mixKey: mixPrivKey, mixPubKey: mixPubKey, mixNodes: b.mixNodes, userMessageLimit: b.userMessageLimit, disableSpamProtection: b.disableSpamProtection)) ) else: let (mixPrivKey, mixPubKey) = generateKeyPair().valueOr: return err("Generate key pair error: " & $error) return ok( - some(MixConf(mixKey: mixPrivKey, mixPubKey: mixPubKey, mixNodes: b.mixNodes)) + some(MixConf(mixKey: mixPrivKey, mixPubKey: mixPubKey, mixNodes: b.mixNodes, userMessageLimit: b.userMessageLimit, disableSpamProtection: b.disableSpamProtection)) ) diff --git a/waku/factory/node_factory.nim b/waku/factory/node_factory.nim index ebf91f415..38b96ab0b 100644 --- a/waku/factory/node_factory.nim +++ b/waku/factory/node_factory.nim @@ -167,7 +167,7 @@ proc setupProtocols( #mount mix if conf.mixConf.isSome(): let mixConf = conf.mixConf.get() - (await node.mountMix(conf.clusterId, mixConf.mixKey, mixConf.mixnodes)).isOkOr: + (await node.mountMix(conf.clusterId, mixConf.mixKey, mixConf.mixnodes, mixConf.userMessageLimit, mixConf.disableSpamProtection)).isOkOr: return err("failed to mount waku mix protocol: " & $error) # Setup extended kademlia discovery diff --git a/waku/factory/waku_conf.nim b/waku/factory/waku_conf.nim index 6ed34e131..0ff16cc85 100644 --- a/waku/factory/waku_conf.nim +++ b/waku/factory/waku_conf.nim @@ -51,6 +51,8 @@ type MixConf* = ref object mixKey*: Curve25519Key mixPubKey*: Curve25519Key mixnodes*: seq[MixNodePubInfo] + userMessageLimit*: Option[int] + disableSpamProtection*: bool type KademliaDiscoveryConf* = object bootstrapNodes*: seq[(PeerId, seq[MultiAddress])] diff --git a/waku/node/delivery_service/send_service/lightpush_processor.nim b/waku/node/delivery_service/send_service/lightpush_processor.nim index 40a754757..33bcc2c16 100644 --- a/waku/node/delivery_service/send_service/lightpush_processor.nim +++ b/waku/node/delivery_service/send_service/lightpush_processor.nim @@ -2,6 +2,7 @@ import chronicles, chronos, results import std/options import + waku/common/option_shims, waku/node/peer_manager, waku/waku_core, waku/waku_lightpush/[common, client, rpc], diff --git a/waku/node/kernel_api/lightpush.nim b/waku/node/kernel_api/lightpush.nim index ffe2afdac..2b7d1064d 100644 --- a/waku/node/kernel_api/lightpush.nim +++ b/waku/node/kernel_api/lightpush.nim @@ -28,6 +28,7 @@ import ../../waku_lightpush/client as lightpush_client, ../../waku_lightpush as lightpush_protocol, ../peer_manager, + ../../common/option_shims, ../../common/rate_limit/setting, ../../waku_rln_relay diff --git a/waku/node/kernel_api/ping.nim b/waku/node/kernel_api/ping.nim index 9dc649fd8..9acd8de3d 100644 --- a/waku/node/kernel_api/ping.nim +++ b/waku/node/kernel_api/ping.nim @@ -11,7 +11,7 @@ import libp2p/transports/tcptransport, libp2p/utility -import ../waku_node, ../peer_manager +import ../../common/option_shims, ../waku_node, ../peer_manager logScope: topics = "waku node ping api" diff --git a/waku/node/kernel_api/relay.nim b/waku/node/kernel_api/relay.nim index 2696806e9..9f6102fa1 100644 --- a/waku/node/kernel_api/relay.nim +++ b/waku/node/kernel_api/relay.nim @@ -31,6 +31,7 @@ import waku_mix, node/waku_node, node/peer_manager, + common/option_shims, common/broker/broker_context, events/message_events, ] diff --git a/waku/node/peer_manager/peer_manager.nim b/waku/node/peer_manager/peer_manager.nim index dc0da9624..15958a5a2 100644 --- a/waku/node/peer_manager/peer_manager.nim +++ b/waku/node/peer_manager/peer_manager.nim @@ -19,6 +19,7 @@ import events/peer_events, common/nimchronos, common/enr, + common/option_shims, common/callbacks, common/utils/parse_size_units, common/broker/broker_context, diff --git a/waku/node/peer_manager/waku_peer_store.nim b/waku/node/peer_manager/waku_peer_store.nim index 93ac9ad2e..f20f22c3a 100644 --- a/waku/node/peer_manager/waku_peer_store.nim +++ b/waku/node/peer_manager/waku_peer_store.nim @@ -10,6 +10,7 @@ import libp2p/crypto/curve25519 import + ../../common/option_shims, ../../waku_core, ../../waku_enr/sharding, ../../waku_enr/capabilities, diff --git a/waku/node/waku_node.nim b/waku/node/waku_node.nim index 0dc927f3a..d73d8250f 100644 --- a/waku/node/waku_node.nim +++ b/waku/node/waku_node.nim @@ -57,6 +57,7 @@ import waku_rln_relay, common/rate_limit/setting, common/callbacks, + common/option_shims, common/nimchronos, common/broker/broker_context, common/broker/request_broker, @@ -329,6 +330,7 @@ proc mountMix*( mixPrivKey: Curve25519Key, mixnodes: seq[MixNodePubInfo], userMessageLimit: Option[int] = none(int), + disableSpamProtection: bool = false, ): Future[Result[void, string]] {.async.} = info "mounting mix protocol", nodeId = node.info #TODO log the config used @@ -361,7 +363,7 @@ proc mountMix*( node.wakuMix = WakuMix.new( localaddrStr, node.peerManager, clusterId, mixPrivKey, mixnodes, publishMessage, - userMessageLimit, + userMessageLimit, disableSpamProtection, ).valueOr: error "Waku Mix protocol initialization failed", err = error return diff --git a/waku/rest_api/endpoint/filter/handlers.nim b/waku/rest_api/endpoint/filter/handlers.nim index 61d7eb96f..363b9bd1f 100644 --- a/waku/rest_api/endpoint/filter/handlers.nim +++ b/waku/rest_api/endpoint/filter/handlers.nim @@ -10,6 +10,7 @@ import presto/route, presto/common import + ../../../common/option_shims, ../../../waku_core, ../../../waku_node, ../../../node/peer_manager, diff --git a/waku/rest_api/endpoint/legacy_lightpush/handlers.nim b/waku/rest_api/endpoint/legacy_lightpush/handlers.nim index 7a3c5b1ed..a2ca0f79b 100644 --- a/waku/rest_api/endpoint/legacy_lightpush/handlers.nim +++ b/waku/rest_api/endpoint/legacy_lightpush/handlers.nim @@ -10,6 +10,7 @@ import presto/common import + waku/common/option_shims, waku/node/peer_manager, waku/waku_lightpush_legacy/common, ../../../waku_node, diff --git a/waku/rest_api/endpoint/legacy_store/handlers.nim b/waku/rest_api/endpoint/legacy_store/handlers.nim index 4ed58f799..8cc441b61 100644 --- a/waku/rest_api/endpoint/legacy_store/handlers.nim +++ b/waku/rest_api/endpoint/legacy_store/handlers.nim @@ -3,6 +3,7 @@ import std/[strformat, sugar], results, chronicles, uri, json_serialization, presto/route import + ../../../common/option_shims, ../../../waku_core, ../../../waku_store_legacy/common, ../../../waku_store_legacy/self_req_handler, diff --git a/waku/rest_api/endpoint/lightpush/handlers.nim b/waku/rest_api/endpoint/lightpush/handlers.nim index 342053e72..e334b3420 100644 --- a/waku/rest_api/endpoint/lightpush/handlers.nim +++ b/waku/rest_api/endpoint/lightpush/handlers.nim @@ -10,6 +10,7 @@ import presto/common import + waku/common/option_shims, waku/node/peer_manager, waku/waku_lightpush/common, ../../../waku_node, diff --git a/waku/rest_api/endpoint/store/handlers.nim b/waku/rest_api/endpoint/store/handlers.nim index 7d37191fb..fc07c8fc8 100644 --- a/waku/rest_api/endpoint/store/handlers.nim +++ b/waku/rest_api/endpoint/store/handlers.nim @@ -3,6 +3,7 @@ import std/[strformat, sugar], results, chronicles, uri, json_serialization, presto/route import + ../../../common/option_shims, ../../../waku_core, ../../../waku_store/common, ../../../waku_store/self_req_handler, diff --git a/waku/waku_enr/sharding.nim b/waku/waku_enr/sharding.nim index 2aeb96a9d..f35bca275 100644 --- a/waku/waku_enr/sharding.nim +++ b/waku/waku_enr/sharding.nim @@ -8,7 +8,7 @@ import eth/keys, libp2p/[multiaddress, multicodec], libp2p/crypto/crypto -import ../common/enr, ../waku_core/topics/pubsub_topic +import ../common/enr, ../common/option_shims, ../waku_core/topics/pubsub_topic logScope: topics = "waku enr sharding" diff --git a/waku/waku_lightpush/client.nim b/waku/waku_lightpush/client.nim index fd12c49d2..c76aaa680 100644 --- a/waku/waku_lightpush/client.nim +++ b/waku/waku_lightpush/client.nim @@ -3,6 +3,7 @@ import std/options, results, chronicles, chronos, metrics, bearssl/rand, stew/byteutils import libp2p/peerid, libp2p/stream/connection import + ../common/option_shims, ../waku_core/peers, ../node/peer_manager, ../utils/requests, diff --git a/waku/waku_lightpush/protocol.nim b/waku/waku_lightpush/protocol.nim index ecbff8461..39fe066de 100644 --- a/waku/waku_lightpush/protocol.nim +++ b/waku/waku_lightpush/protocol.nim @@ -9,6 +9,7 @@ import metrics, bearssl/rand import + ../common/option_shims, ../node/peer_manager/peer_manager, ../waku_core, ../waku_core/topics/sharding, diff --git a/waku/waku_lightpush_legacy/client.nim b/waku/waku_lightpush_legacy/client.nim index ab489bec9..4079e24ef 100644 --- a/waku/waku_lightpush_legacy/client.nim +++ b/waku/waku_lightpush_legacy/client.nim @@ -3,6 +3,7 @@ import std/options, results, chronicles, chronos, metrics, bearssl/rand, stew/byteutils import libp2p/peerid import + ../common/option_shims, ../waku_core/peers, ../node/peer_manager, ../utils/requests, diff --git a/waku/waku_mix/protocol.nim b/waku/waku_mix/protocol.nim index 7400d7011..04cf02d5c 100644 --- a/waku/waku_mix/protocol.nim +++ b/waku/waku_mix/protocol.nim @@ -11,6 +11,7 @@ import libp2p/protocols/mix/mix_metrics, libp2p/protocols/mix/delay_strategy, libp2p/protocols/mix/spam_protection, + libp2p/protocols/mix/cover_traffic, libp2p/[multiaddress, multicodec, peerid, peerinfo], eth/common/keys @@ -87,6 +88,7 @@ proc new*( bootnodes: seq[MixNodePubInfo], publishMessage: PublishMessage, userMessageLimit: Option[int] = none(int), + disableSpamProtection: bool = false, ): WakuMixResult[T] = let mixPubKey = public(mixPrivKey) trace "mixPubKey", mixPubKey = mixPubKey @@ -97,33 +99,47 @@ proc new*( peermgr.switch.peerInfo.publicKey.skkey, peermgr.switch.peerInfo.privateKey.skkey, ) - # Initialize spam protection with persistent credentials - # Use peerID in keystore path so multiple peers can run from same directory - # Tree path is shared across all nodes to maintain the full membership set - let peerId = peermgr.switch.peerInfo.peerId - var spamProtectionConfig = defaultConfig() - spamProtectionConfig.keystorePath = "rln_keystore_" & $peerId & ".json" - spamProtectionConfig.keystorePassword = "mix-rln-password" - if userMessageLimit.isSome(): - spamProtectionConfig.userMessageLimit = userMessageLimit.get() - # rlnResourcesPath left empty to use bundled resources (via "tree_height_/" placeholder) + let totalSlots = userMessageLimit.get(2) + let ct = ConstantRateCoverTraffic.new( + totalSlots = totalSlots, + epochDurationSec = 10.0, + useInternalEpochTimer = disableSpamProtection, + ) - let spamProtection = newMixRlnSpamProtection(spamProtectionConfig).valueOr: - return err("failed to create spam protection: " & error) + var spamProtectionOpt = default(Opt[SpamProtection]) + if not disableSpamProtection: + # Initialize spam protection with persistent credentials + let peerId = peermgr.switch.peerInfo.peerId + var spamProtectionConfig = defaultConfig() + spamProtectionConfig.keystorePath = "rln_keystore_" & $peerId & ".json" + spamProtectionConfig.keystorePassword = "mix-rln-password" + if userMessageLimit.isSome(): + spamProtectionConfig.userMessageLimit = userMessageLimit.get() + + let spamProtection = newMixRlnSpamProtection(spamProtectionConfig).valueOr: + return err("failed to create spam protection: " & error) + spamProtectionOpt = Opt.some(SpamProtection(spamProtection)) + else: + info "mix spam protection disabled" + + var mixRlnSpam: MixRlnSpamProtection + if spamProtectionOpt.isSome(): + mixRlnSpam = MixRlnSpamProtection(spamProtectionOpt.get()) var m = WakuMix( peerManager: peermgr, clusterId: clusterId, pubKey: mixPubKey, - mixRlnSpamProtection: spamProtection, publishMessage: publishMessage, + mixRlnSpamProtection: mixRlnSpam, ) procCall MixProtocol(m).init( localMixNodeInfo, peermgr.switch, - spamProtection = Opt.some(SpamProtection(spamProtection)), - delayStrategy = - ExponentialDelayStrategy.new(meanDelayMs = 100, rng = crypto.newRng()), + spamProtection = spamProtectionOpt, + delayStrategy = Opt.some(DelayStrategy( + ExponentialDelayStrategy.new(meanDelay = 100, rng = crypto.newRng()))), + coverTraffic = Opt.some(CoverTraffic(ct)), ) processBootNodes(bootnodes, peermgr, m) @@ -140,6 +156,8 @@ proc setupSpamProtectionCallbacks(mix: WakuMix) = ## Set up the publish callback for spam protection coordination. ## This enables the plugin to broadcast membership updates and proof metadata ## via Waku relay. + if mix.mixRlnSpamProtection.isNil(): + return if mix.publishMessage.isNil(): warn "PublishMessage callback not available, spam protection coordination disabled" return diff --git a/waku/waku_rendezvous/client.nim b/waku/waku_rendezvous/client.nim index 09e789774..59a3a8086 100644 --- a/waku/waku_rendezvous/client.nim +++ b/waku/waku_rendezvous/client.nim @@ -8,11 +8,12 @@ import libp2p/protocols/rendezvous, libp2p/crypto/curve25519, libp2p/switch, - libp2p/utils/semaphore + chronos/asyncsync import metrics except collect import + waku/common/option_shims, waku/node/peer_manager, waku/waku_core/peers, waku/waku_core/codecs, diff --git a/waku/waku_rendezvous/protocol.nim b/waku/waku_rendezvous/protocol.nim index 00b5f1a5c..e1600bfbb 100644 --- a/waku/waku_rendezvous/protocol.nim +++ b/waku/waku_rendezvous/protocol.nim @@ -8,7 +8,7 @@ import stew/byteutils, libp2p/protocols/rendezvous, libp2p/protocols/rendezvous/protobuf, - libp2p/utils/semaphore, + chronos/asyncsync, libp2p/utils/offsettedseq, libp2p/crypto/curve25519, libp2p/switch, @@ -19,6 +19,7 @@ import metrics except collect import ../node/peer_manager, ../common/callbacks, + ../common/option_shims, ../waku_enr/capabilities, ../waku_core/peers, ../waku_core/codecs, diff --git a/waku/waku_store/client.nim b/waku/waku_store/client.nim index 5b261af47..8f3b3f00c 100644 --- a/waku/waku_store/client.nim +++ b/waku/waku_store/client.nim @@ -8,6 +8,7 @@ import metrics, bearssl/rand import + ../common/option_shims, ../node/peer_manager, ../utils/requests, ./protocol_metrics, ./common, ./rpc_codec logScope: diff --git a/waku/waku_store/resume.nim b/waku/waku_store/resume.nim index b7864da94..04ef7e858 100644 --- a/waku/waku_store/resume.nim +++ b/waku/waku_store/resume.nim @@ -13,6 +13,7 @@ import import ../common/databases/db_sqlite, + ../common/option_shims, ../waku_core, ../waku_archive, ../common/nimchronos, diff --git a/waku/waku_store_sync/reconciliation.nim b/waku/waku_store_sync/reconciliation.nim index 23f513322..300e04a34 100644 --- a/waku/waku_store_sync/reconciliation.nim +++ b/waku/waku_store_sync/reconciliation.nim @@ -14,6 +14,7 @@ import eth/p2p/discoveryv5/enr import ../common/nimchronos, + ../common/option_shims, ../common/protobuf, ../common/paging, ../waku_enr, diff --git a/waku/waku_store_sync/transfer.nim b/waku/waku_store_sync/transfer.nim index 6a600b4e3..654fb6469 100644 --- a/waku/waku_store_sync/transfer.nim +++ b/waku/waku_store_sync/transfer.nim @@ -13,6 +13,7 @@ import eth/p2p/discoveryv5/enr import ../common/nimchronos, + ../common/option_shims, ../common/protobuf, ../waku_enr, ../waku_core/codecs,