mirror of
https://github.com/logos-messaging/logos-delivery.git
synced 2026-06-06 22:19:30 +00:00
feat(mix): cover traffic with constant rate
- Integrate ConstantRateCoverTraffic from libp2p mix module with default totalSlots = userMessageLimit (or 2) and 10s epoch - Add --mix-user-message-limit and --mix-disable-spam-protection CLI flags with corresponding MixConfBuilder accessors and MixConf fields - Wrap mixRlnSpamProtection construction so it is skipped when spam protection is disabled, with a nil guard in setupSpamProtectionCallbacks - Add waku/common/option_shims.nim restoring valueOr/withValue templates for std/options (removed upstream by results), and import it across modules that relied on the old behavior - Sink chat2mix logs to textlines (stdout) instead of textlines[file] to work around a chronicles compile-time macro-eval bug under Nim 2.2.4 - Rename ExtendedKademliaDiscoveryParams -> ExtendedServiceDiscoveryParams to match the kad_disco -> service_discovery rename in nim-libp2p - Bump nim-libp2p to e1bbda4f6 (PR #2243 "cover traffic with constant rate") and mix-rln-spam-protection-plugin to 153d0c0 (PR #5 cover traffic epoch change support); both pre-libp2p_mix-extraction - Add simulations/mixnet/check_cover_traffic.sh for monitoring mix_cover_* / mix_slot_* metrics, plus per-node cover-traffic configs
This commit is contained in:
parent
cc71244499
commit
fc6af2fdf1
@ -24,6 +24,7 @@ The simulation includes:
|
|||||||
| `run_chat_mix.sh` | Chat app instance 1 |
|
| `run_chat_mix.sh` | Chat app instance 1 |
|
||||||
| `run_chat_mix1.sh` | Chat app instance 2 |
|
| `run_chat_mix1.sh` | Chat app instance 2 |
|
||||||
| `build_setup.sh` | Build and generate RLN credentials |
|
| `build_setup.sh` | Build and generate RLN credentials |
|
||||||
|
| `check_cover_traffic.sh` | Monitor cover traffic metrics from all nodes |
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@ -130,3 +131,35 @@ To exit the chat apps, enter `/exit`:
|
|||||||
>> /exit
|
>> /exit
|
||||||
quitting...
|
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.
|
||||||
|
|||||||
18
simulations/mixnet/check_cover_traffic.sh
Executable file
18
simulations/mixnet/check_cover_traffic.sh
Executable file
@ -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_slot_" | grep -v "^#" || echo " (no cover metrics yet)"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
@ -7,6 +7,7 @@ lightpush = true
|
|||||||
max-connections = 150
|
max-connections = 150
|
||||||
peer-exchange = false
|
peer-exchange = false
|
||||||
metrics-logging = false
|
metrics-logging = false
|
||||||
|
metrics-server = true
|
||||||
cluster-id = 2
|
cluster-id = 2
|
||||||
discv5-discovery = false
|
discv5-discovery = false
|
||||||
discv5-udp-port = 9000
|
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 = ["/ip4/127.0.0.1/tcp/60001"]
|
||||||
ext-multiaddr-only = true
|
ext-multiaddr-only = true
|
||||||
ip-colocation-limit=0
|
ip-colocation-limit=0
|
||||||
|
mix-user-message-limit=4
|
||||||
|
mix-disable-spam-protection=false
|
||||||
|
|||||||
@ -7,6 +7,7 @@ lightpush = true
|
|||||||
max-connections = 150
|
max-connections = 150
|
||||||
peer-exchange = false
|
peer-exchange = false
|
||||||
metrics-logging = false
|
metrics-logging = false
|
||||||
|
metrics-server = true
|
||||||
cluster-id = 2
|
cluster-id = 2
|
||||||
discv5-discovery = false
|
discv5-discovery = false
|
||||||
discv5-udp-port = 9001
|
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 = ["/ip4/127.0.0.1/tcp/60002"]
|
||||||
ext-multiaddr-only = true
|
ext-multiaddr-only = true
|
||||||
ip-colocation-limit=0
|
ip-colocation-limit=0
|
||||||
|
mix-user-message-limit=4
|
||||||
|
mix-disable-spam-protection=false
|
||||||
#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"]
|
#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"]
|
||||||
|
|||||||
@ -7,6 +7,7 @@ lightpush = true
|
|||||||
max-connections = 150
|
max-connections = 150
|
||||||
peer-exchange = false
|
peer-exchange = false
|
||||||
metrics-logging = false
|
metrics-logging = false
|
||||||
|
metrics-server = true
|
||||||
cluster-id = 2
|
cluster-id = 2
|
||||||
discv5-discovery = false
|
discv5-discovery = false
|
||||||
discv5-udp-port = 9002
|
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 = ["/ip4/127.0.0.1/tcp/60003"]
|
||||||
ext-multiaddr-only = true
|
ext-multiaddr-only = true
|
||||||
ip-colocation-limit=0
|
ip-colocation-limit=0
|
||||||
|
mix-user-message-limit=4
|
||||||
|
mix-disable-spam-protection=false
|
||||||
#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"]
|
#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"]
|
||||||
|
|||||||
@ -7,6 +7,7 @@ lightpush = true
|
|||||||
max-connections = 150
|
max-connections = 150
|
||||||
peer-exchange = false
|
peer-exchange = false
|
||||||
metrics-logging = false
|
metrics-logging = false
|
||||||
|
metrics-server = true
|
||||||
cluster-id = 2
|
cluster-id = 2
|
||||||
discv5-discovery = false
|
discv5-discovery = false
|
||||||
discv5-udp-port = 9003
|
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 = ["/ip4/127.0.0.1/tcp/60004"]
|
||||||
ext-multiaddr-only = true
|
ext-multiaddr-only = true
|
||||||
ip-colocation-limit=0
|
ip-colocation-limit=0
|
||||||
|
mix-user-message-limit=4
|
||||||
|
mix-disable-spam-protection=false
|
||||||
#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"]
|
#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"]
|
||||||
|
|||||||
@ -7,6 +7,7 @@ lightpush = true
|
|||||||
max-connections = 150
|
max-connections = 150
|
||||||
peer-exchange = false
|
peer-exchange = false
|
||||||
metrics-logging = false
|
metrics-logging = false
|
||||||
|
metrics-server = true
|
||||||
cluster-id = 2
|
cluster-id = 2
|
||||||
discv5-discovery = false
|
discv5-discovery = false
|
||||||
discv5-udp-port = 9004
|
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 = ["/ip4/127.0.0.1/tcp/60005"]
|
||||||
ext-multiaddr-only = true
|
ext-multiaddr-only = true
|
||||||
ip-colocation-limit=0
|
ip-colocation-limit=0
|
||||||
|
mix-user-message-limit=4
|
||||||
|
mix-disable-spam-protection=false
|
||||||
#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"]
|
#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"]
|
||||||
|
|||||||
@ -20,8 +20,8 @@ import
|
|||||||
|
|
||||||
const
|
const
|
||||||
KeystorePassword = "mix-rln-password" # Must match protocol.nim
|
KeystorePassword = "mix-rln-password" # Must match protocol.nim
|
||||||
DefaultUserMessageLimit = 100'u64 # Network-wide default rate limit
|
DefaultUserMessageLimit = 4'u64 # R=4 slots per 10s epoch
|
||||||
SpammerUserMessageLimit = 3'u64 # Lower limit for spammer testing
|
SpammerUserMessageLimit = 3'u64 # Higher limit for spammer testing
|
||||||
|
|
||||||
# Peer IDs derived from nodekeys in config files
|
# Peer IDs derived from nodekeys in config files
|
||||||
# config.toml: nodekey = "f98e3fba96c32e8d1967d460f1b79457380e1a895f7971cecc8528abe733781a"
|
# config.toml: nodekey = "f98e3fba96c32e8d1967d460f1b79457380e1a895f7971cecc8528abe733781a"
|
||||||
|
|||||||
@ -642,6 +642,18 @@ with the drawback of consuming some more bandwidth.""",
|
|||||||
name: "mixnode"
|
name: "mixnode"
|
||||||
.}: seq[MixNodePubInfo]
|
.}: 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
|
# Kademlia Discovery config
|
||||||
enableKadDiscovery* {.
|
enableKadDiscovery* {.
|
||||||
desc:
|
desc:
|
||||||
@ -1079,6 +1091,9 @@ proc toWakuConf*(n: WakuNodeConf): ConfResult[WakuConf] =
|
|||||||
b.withMix(n.mix)
|
b.withMix(n.mix)
|
||||||
if n.mixkey.isSome():
|
if n.mixkey.isSome():
|
||||||
b.mixConf.withMixKey(n.mixkey.get())
|
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.withEnabled(n.filter)
|
||||||
b.filterServiceConf.withSubscriptionTimeout(n.filterSubscriptionTimeout)
|
b.filterServiceConf.withSubscriptionTimeout(n.filterSubscriptionTimeout)
|
||||||
|
|||||||
@ -12,6 +12,8 @@ type MixConfBuilder* = object
|
|||||||
enabled: Option[bool]
|
enabled: Option[bool]
|
||||||
mixKey: Option[string]
|
mixKey: Option[string]
|
||||||
mixNodes: seq[MixNodePubInfo]
|
mixNodes: seq[MixNodePubInfo]
|
||||||
|
userMessageLimit: Option[int]
|
||||||
|
disableSpamProtection: bool
|
||||||
|
|
||||||
proc init*(T: type MixConfBuilder): MixConfBuilder =
|
proc init*(T: type MixConfBuilder): MixConfBuilder =
|
||||||
MixConfBuilder()
|
MixConfBuilder()
|
||||||
@ -25,6 +27,12 @@ proc withMixKey*(b: var MixConfBuilder, mixKey: string) =
|
|||||||
proc withMixNodes*(b: var MixConfBuilder, mixNodes: seq[MixNodePubInfo]) =
|
proc withMixNodes*(b: var MixConfBuilder, mixNodes: seq[MixNodePubInfo]) =
|
||||||
b.mixNodes = mixNodes
|
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] =
|
proc build*(b: MixConfBuilder): Result[Option[MixConf], string] =
|
||||||
if not b.enabled.get(false):
|
if not b.enabled.get(false):
|
||||||
return ok(none[MixConf]())
|
return ok(none[MixConf]())
|
||||||
@ -33,11 +41,27 @@ proc build*(b: MixConfBuilder): Result[Option[MixConf], string] =
|
|||||||
let mixPrivKey = intoCurve25519Key(ncrutils.fromHex(b.mixKey.get()))
|
let mixPrivKey = intoCurve25519Key(ncrutils.fromHex(b.mixKey.get()))
|
||||||
let mixPubKey = public(mixPrivKey)
|
let mixPubKey = public(mixPrivKey)
|
||||||
return ok(
|
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:
|
else:
|
||||||
let (mixPrivKey, mixPubKey) = generateKeyPair().valueOr:
|
let (mixPrivKey, mixPubKey) = generateKeyPair().valueOr:
|
||||||
return err("Generate key pair error: " & $error)
|
return err("Generate key pair error: " & $error)
|
||||||
return ok(
|
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,
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -164,7 +164,12 @@ proc setupProtocols(
|
|||||||
#mount mix
|
#mount mix
|
||||||
if conf.mixConf.isSome():
|
if conf.mixConf.isSome():
|
||||||
let mixConf = conf.mixConf.get()
|
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)
|
return err("failed to mount waku mix protocol: " & $error)
|
||||||
|
|
||||||
# Setup extended kademlia discovery
|
# Setup extended kademlia discovery
|
||||||
|
|||||||
@ -51,6 +51,8 @@ type MixConf* = ref object
|
|||||||
mixKey*: Curve25519Key
|
mixKey*: Curve25519Key
|
||||||
mixPubKey*: Curve25519Key
|
mixPubKey*: Curve25519Key
|
||||||
mixnodes*: seq[MixNodePubInfo]
|
mixnodes*: seq[MixNodePubInfo]
|
||||||
|
userMessageLimit*: Option[int]
|
||||||
|
disableSpamProtection*: bool
|
||||||
|
|
||||||
type KademliaDiscoveryConf* = object
|
type KademliaDiscoveryConf* = object
|
||||||
bootstrapNodes*: seq[(PeerId, seq[MultiAddress])]
|
bootstrapNodes*: seq[(PeerId, seq[MultiAddress])]
|
||||||
|
|||||||
@ -317,6 +317,7 @@ proc mountMix*(
|
|||||||
mixPrivKey: Curve25519Key,
|
mixPrivKey: Curve25519Key,
|
||||||
mixnodes: seq[MixNodePubInfo],
|
mixnodes: seq[MixNodePubInfo],
|
||||||
userMessageLimit: Option[int] = none(int),
|
userMessageLimit: Option[int] = none(int),
|
||||||
|
disableSpamProtection: bool = false,
|
||||||
): Future[Result[void, string]] {.async.} =
|
): Future[Result[void, string]] {.async.} =
|
||||||
info "mounting mix protocol", nodeId = node.info #TODO log the config used
|
info "mounting mix protocol", nodeId = node.info #TODO log the config used
|
||||||
|
|
||||||
@ -349,7 +350,7 @@ proc mountMix*(
|
|||||||
|
|
||||||
node.wakuMix = WakuMix.new(
|
node.wakuMix = WakuMix.new(
|
||||||
localaddrStr, node.peerManager, clusterId, mixPrivKey, mixnodes, publishMessage,
|
localaddrStr, node.peerManager, clusterId, mixPrivKey, mixnodes, publishMessage,
|
||||||
userMessageLimit,
|
userMessageLimit, disableSpamProtection,
|
||||||
).valueOr:
|
).valueOr:
|
||||||
error "Waku Mix protocol initialization failed", err = error
|
error "Waku Mix protocol initialization failed", err = error
|
||||||
return
|
return
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import
|
|||||||
libp2p_mix/mix_metrics,
|
libp2p_mix/mix_metrics,
|
||||||
libp2p_mix/delay_strategy,
|
libp2p_mix/delay_strategy,
|
||||||
libp2p_mix/spam_protection,
|
libp2p_mix/spam_protection,
|
||||||
|
libp2p_mix/cover_traffic,
|
||||||
libp2p/[multiaddress, multicodec, peerid, peerinfo],
|
libp2p/[multiaddress, multicodec, peerid, peerinfo],
|
||||||
eth/common/keys
|
eth/common/keys
|
||||||
|
|
||||||
@ -91,6 +92,7 @@ proc new*(
|
|||||||
bootnodes: seq[MixNodePubInfo],
|
bootnodes: seq[MixNodePubInfo],
|
||||||
publishMessage: PublishMessage,
|
publishMessage: PublishMessage,
|
||||||
userMessageLimit: Option[int] = none(int),
|
userMessageLimit: Option[int] = none(int),
|
||||||
|
disableSpamProtection: bool = false,
|
||||||
): WakuMixResult[T] =
|
): WakuMixResult[T] =
|
||||||
let mixPubKey = public(mixPrivKey)
|
let mixPubKey = public(mixPrivKey)
|
||||||
trace "mixPubKey", mixPubKey = mixPubKey
|
trace "mixPubKey", mixPubKey = mixPubKey
|
||||||
@ -101,36 +103,50 @@ proc new*(
|
|||||||
peermgr.switch.peerInfo.publicKey.skkey, peermgr.switch.peerInfo.privateKey.skkey,
|
peermgr.switch.peerInfo.publicKey.skkey, peermgr.switch.peerInfo.privateKey.skkey,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Initialize spam protection with persistent credentials
|
let totalSlots = userMessageLimit.get(2)
|
||||||
# Use peerID in keystore path so multiple peers can run from same directory
|
let ct = ConstantRateCoverTraffic.new(
|
||||||
# Tree path is shared across all nodes to maintain the full membership set
|
totalSlots = totalSlots,
|
||||||
let peerId = peermgr.switch.peerInfo.peerId
|
epochDuration = 10.seconds,
|
||||||
var spamProtectionConfig = defaultConfig()
|
useInternalEpochTimer = disableSpamProtection,
|
||||||
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 spamProtection = newMixRlnSpamProtection(spamProtectionConfig).valueOr:
|
var spamProtectionOpt = default(Opt[SpamProtection])
|
||||||
return err("failed to create spam protection: " & error)
|
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(
|
var m = WakuMix(
|
||||||
peerManager: peermgr,
|
peerManager: peermgr,
|
||||||
clusterId: clusterId,
|
clusterId: clusterId,
|
||||||
pubKey: mixPubKey,
|
pubKey: mixPubKey,
|
||||||
mixRlnSpamProtection: spamProtection,
|
|
||||||
publishMessage: publishMessage,
|
publishMessage: publishMessage,
|
||||||
|
mixRlnSpamProtection: mixRlnSpam,
|
||||||
)
|
)
|
||||||
procCall MixProtocol(m).init(
|
procCall MixProtocol(m).init(
|
||||||
localMixNodeInfo,
|
localMixNodeInfo,
|
||||||
peermgr.switch,
|
peermgr.switch,
|
||||||
spamProtection = Opt.some(SpamProtection(spamProtection)),
|
spamProtection = spamProtectionOpt,
|
||||||
delayStrategy = Opt.some(
|
delayStrategy = Opt.some(
|
||||||
DelayStrategy(
|
DelayStrategy(
|
||||||
ExponentialDelayStrategy.new(meanDelay = 100, rng = crypto.newRng())
|
ExponentialDelayStrategy.new(meanDelay = 100, rng = crypto.newRng())
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
coverTraffic = Opt.some(CoverTraffic(ct)),
|
||||||
)
|
)
|
||||||
|
|
||||||
processBootNodes(bootnodes, peermgr, m)
|
processBootNodes(bootnodes, peermgr, m)
|
||||||
@ -147,6 +163,8 @@ proc setupSpamProtectionCallbacks(mix: WakuMix) =
|
|||||||
## Set up the publish callback for spam protection coordination.
|
## Set up the publish callback for spam protection coordination.
|
||||||
## This enables the plugin to broadcast membership updates and proof metadata
|
## This enables the plugin to broadcast membership updates and proof metadata
|
||||||
## via Waku relay.
|
## via Waku relay.
|
||||||
|
if mix.mixRlnSpamProtection.isNil():
|
||||||
|
return
|
||||||
if mix.publishMessage.isNil():
|
if mix.publishMessage.isNil():
|
||||||
warn "PublishMessage callback not available, spam protection coordination disabled"
|
warn "PublishMessage callback not available, spam protection coordination disabled"
|
||||||
return
|
return
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user