Skip to content

Commit d529be1

Browse files
committed
feat(mix): DoS protection + libp2p v2.0.0 + stateless RLN + tests (rebased onto #3935)
Squash of 13 commits from feat/mix-dos-protection-libp2p-v2.0.0 onto the logos_delivery/ folder-restructure base from #3935 (build-messaging-folder). Original commit history (squashed): - d8e6dce feat(mix): integrate mix protocol with extended kademlia + RLN spam protection - fb72f18 refactor(mix): split DoS-protection self-registration into background retry - d8bbef0 feat(mix): bump libp2p stack to v2.0.0 + adopt stateless RLN spam protection - 2f24448 fix(tests): use HmacDrbgContext.new() instead of crypto.newRng() - 5a21455 fix(ci): regen nimble.lock for v2.0.0 + disambiguate rng in wakucore - 03ef02a fix(tests): wrap HmacDrbgContext via newBearSslRng for libp2p v2.0.0 - 167ab1d fix(nix): regenerate deps.nix from updated nimble.lock - 97a2722 fix(tests): wrap or pass Rng correctly for 3-arg PrivateKey.random - 5561fcb fix(tests): replace removed newStandardSwitch with SwitchBuilder - ba39ee4 fix(tests): libp2p v2.0.0 API migrations across test suite - 328e11d fix: gitignore test binaries + remove accidentally-committed binary - cc71244 fix(tests): more v2.0.0 API migrations (rng template, PeerId.random, etc.) - 412d97a fix(tests): unblock CI — nph, excise orphan waku_noise, complete v2.0.0 Rng migration Conflict resolutions (#3935 → ours): - 11 import-path migrations: waku/X → logos_delivery/waku/X - waku_node/waku_node/relay.nim: dropped our `registerRelayHandler` proc (relocated to subscription_manager.nim by #3935; see cascade fix below) - factory/builder.nim: combined both sides' new imports (net_config + waku_switch) - factory/conf_builder/mix_conf_builder.nim: libp2p_mix package (not libp2p/protocols/mix) - waku_mix/protocol.nim: combined paths + our mix_rln_spam_protection/relay/nimchronos imports - 3 test files: dropped noise_utils import (replicates noise excision from original PR) - 2 UA file moves: option_shims.nim and waku_mix_coordination.nim added at new paths Cascade fixes (#3935 lost our work, restored): - subscription_manager.nim: added `mixHandler` to #3935's `registerRelayHandler`, and added `waku_mix` to its imports. Without this, mix messages were silently dropped from the relay handler chain. - config.nims: option_shims auto-import path migrated to logos_delivery/... Validation: - nph check on all 82 staged .nim files: clean (0 reformats needed) - wakunode2 build: exit 0, 38 MB binary - (sim PASS confirmed in earlier identical-state run: 5/5 mix init, 5 RLN proofs gen/verify, 0 errors) Backup tag at original tip: backup/3931-pre-3935-rebase (412d97a).
1 parent faa6741 commit d529be1

82 files changed

Lines changed: 1527 additions & 443 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
# Executables shall be put in an ignored build/ directory
44
/build
55

6+
# Test binaries (built by `nim c tests/...nim` for local debug compile)
7+
/tests/all_tests_common
8+
/tests/all_tests_waku
9+
/tests/all_tests_wakunode2
10+
611
# Generated Files
712
*.generated.nim
813

@@ -41,6 +46,7 @@ node_modules/
4146

4247
# RLN / keystore
4348
rlnKeystore.json
49+
rln_keystore*.json
4450
*.tar.gz
4551

4652
# sqlite db
@@ -91,3 +97,6 @@ nimbledeps
9197
# Python bytecode from tests/simulator
9298
__pycache__/
9399
*.pyc
100+
101+
# sim driver script (local dev tool, not part of build/CI)
102+
simulations/mixnet/roundtrip_check.sh

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ $(LIBRLN_FILE):
195195
echo -e $(BUILD_MSG) "$@" && \
196196
bash scripts/build_rln.sh $(LIBRLN_BUILDDIR) $(LIBRLN_VERSION) $(LIBRLN_FILE)
197197

198+
# Single zerokit archive (stateless features) for both relay and mix plugin.
199+
# Plugin keeps its Merkle tree Nim-side, so it does not need the pmtree FFIs
200+
# the default features would expose -- and including a second archive built
201+
# with different features causes duplicate-symbol link errors.
198202
librln: | $(LIBRLN_FILE)
199203
$(eval NIM_PARAMS += --passL:$(LIBRLN_FILE) --passL:-lm)
200204

apps/chat2mix/chat2mix.nim

Lines changed: 129 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import
1818
metrics,
1919
metrics/chronos_httpserver
2020
import
21+
libp2p/crypto/rng as libp2p_rng,
2122
libp2p/[
2223
switch, # manage transports, a single entry point for dialing and listening
2324
crypto/crypto, # cryptographic functions
@@ -29,12 +30,13 @@ import
2930
peerid, # Implement how peers interact
3031
protobuf/minprotobuf, # message serialisation/deserialisation from and to protobufs
3132
nameresolving/dnsresolver,
32-
protocols/mix/curve25519,
33-
protocols/mix/mix_protocol,
3433
] # define DNS resolution
34+
# libp2p_mix has been extracted into its own package; import from there.
35+
import libp2p_mix/curve25519, libp2p_mix/mix_protocol
3536
import
3637
logos_delivery/waku/[
3738
waku_core,
39+
waku_core/topics/sharding,
3840
waku_lightpush/common,
3941
waku_lightpush/rpc,
4042
waku_enr,
@@ -47,6 +49,8 @@ import
4749
common/utils/nat,
4850
waku_store/common,
4951
waku_filter_v2/client,
52+
waku_filter_v2/common as filter_common,
53+
waku_mix/protocol,
5054
common/logging,
5155
],
5256
./config_chat2mix
@@ -57,6 +61,105 @@ import ../../logos_delivery/waku/waku_rln_relay
5761
logScope:
5862
topics = "chat2 mix"
5963

64+
#########################
65+
## Mix Spam Protection ##
66+
#########################
67+
68+
# Forward declaration
69+
proc maintainSpamProtectionSubscription(
70+
node: WakuNode, contentTopics: seq[ContentTopic]
71+
) {.async.}
72+
73+
proc setupMixSpamProtectionViaFilter(node: WakuNode) {.async.} =
74+
## Setup filter-based spam protection coordination for mix protocol.
75+
## Since chat2mix doesn't use relay, we subscribe via filter to receive
76+
## spam protection coordination messages.
77+
78+
# Register message handler for spam protection coordination
79+
let spamTopics = node.wakuMix.getSpamProtectionContentTopics()
80+
81+
proc handleSpamMessage(
82+
pubsubTopic: PubsubTopic, message: WakuMessage
83+
): Future[void] {.async, gcsafe.} =
84+
await node.wakuMix.handleMessage(pubsubTopic, message)
85+
86+
node.wakuFilterClient.registerPushHandler(handleSpamMessage)
87+
88+
# Wait for filter peer and maintain subscription
89+
asyncSpawn maintainSpamProtectionSubscription(node, spamTopics)
90+
91+
proc maintainSpamProtectionSubscription(
92+
node: WakuNode, contentTopics: seq[ContentTopic]
93+
) {.async.} =
94+
## Maintain filter subscription for spam protection topics.
95+
## Monitors subscription health with periodic pings and re-subscribes on failure.
96+
const RetryInterval = chronos.seconds(5)
97+
const SubscriptionMaintenance = chronos.seconds(30)
98+
const MaxFailedSubscribes = 3
99+
var currentFilterPeer: Option[RemotePeerInfo] = none(RemotePeerInfo)
100+
var noFailedSubscribes = 0
101+
102+
while true:
103+
# Select or reuse filter peer
104+
if currentFilterPeer.isNone():
105+
let filterPeerOpt = node.peerManager.selectPeer(WakuFilterSubscribeCodec)
106+
if filterPeerOpt.isNone():
107+
debug "No filter peer available yet for spam protection, retrying..."
108+
await sleepAsync(RetryInterval)
109+
continue
110+
currentFilterPeer = some(filterPeerOpt.get())
111+
info "Selected filter peer for spam protection",
112+
peer = currentFilterPeer.get().peerId
113+
114+
# Check if subscription is still alive with ping
115+
let pingErr = (await node.wakuFilterClient.ping(currentFilterPeer.get())).errorOr:
116+
# Subscription is alive, wait before next check
117+
await sleepAsync(SubscriptionMaintenance)
118+
if noFailedSubscribes > 0:
119+
noFailedSubscribes = 0
120+
continue
121+
122+
# Subscription lost, need to re-subscribe
123+
warn "Spam protection filter subscription ping failed, re-subscribing",
124+
error = pingErr, peer = currentFilterPeer.get().peerId
125+
126+
# Determine pubsub topic from content topics (using auto-sharding)
127+
if node.wakuAutoSharding.isNone():
128+
error "Auto-sharding not configured, cannot determine pubsub topic for spam protection"
129+
await sleepAsync(RetryInterval)
130+
continue
131+
132+
let shardRes = node.wakuAutoSharding.get().getShard(contentTopics[0])
133+
if shardRes.isErr():
134+
error "Failed to determine shard for spam protection", error = shardRes.error
135+
await sleepAsync(RetryInterval)
136+
continue
137+
138+
let shard = shardRes.get()
139+
let pubsubTopic: PubsubTopic = shard # converter toPubsubTopic
140+
141+
# Subscribe to spam protection topics
142+
let res = await node.wakuFilterClient.subscribe(
143+
currentFilterPeer.get(), pubsubTopic, contentTopics
144+
)
145+
if res.isErr():
146+
noFailedSubscribes += 1
147+
warn "Failed to subscribe to spam protection topics via filter",
148+
error = res.error, topics = contentTopics, failCount = noFailedSubscribes
149+
150+
if noFailedSubscribes >= MaxFailedSubscribes:
151+
# Try with a different peer
152+
warn "Max subscription failures reached, selecting new filter peer"
153+
currentFilterPeer = none(RemotePeerInfo)
154+
noFailedSubscribes = 0
155+
156+
await sleepAsync(RetryInterval)
157+
else:
158+
info "Successfully subscribed to spam protection topics via filter",
159+
topics = contentTopics, peer = currentFilterPeer.get().peerId
160+
noFailedSubscribes = 0
161+
await sleepAsync(SubscriptionMaintenance)
162+
60163
const Help = """
61164
Commands: /[?|help|connect|nick|exit]
62165
help: Prints this help
@@ -209,20 +312,21 @@ proc publish(c: Chat, line: string) {.async.} =
209312
try:
210313
if not c.node.wakuLightpushClient.isNil():
211314
# Attempt lightpush with mix
212-
213-
(
214-
waitFor c.node.lightpushPublish(
215-
some(c.conf.getPubsubTopic(c.node, c.contentTopic)),
216-
message,
217-
none(RemotePeerInfo),
218-
true,
219-
)
220-
).isOkOr:
221-
error "failed to publish lightpush message", error = error
315+
let res = await c.node.lightpushPublish(
316+
some(c.conf.getPubsubTopic(c.node, c.contentTopic)),
317+
message,
318+
none(RemotePeerInfo),
319+
true,
320+
)
321+
if res.isErr():
322+
error "failed to publish lightpush message", error = res.error
323+
echo "Error: " & res.error.desc.get("unknown error")
222324
else:
223325
error "failed to publish message as lightpush client is not initialized"
326+
echo "Error: lightpush client is not initialized"
224327
except CatchableError:
225328
error "caught error publishing message: ", error = getCurrentExceptionMsg()
329+
echo "Error: " & getCurrentExceptionMsg()
226330

227331
# TODO This should read or be subscribe handler subscribe
228332
proc readAndPrint(c: Chat) {.async.} =
@@ -396,7 +500,7 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
396500
if conf.nodekey.isSome():
397501
conf.nodekey.get()
398502
else:
399-
PrivateKey.random(Secp256k1, rng[]).tryGet()
503+
PrivateKey.random(Secp256k1, libp2p_rng.newBearSslRng(rng)).tryGet()
400504

401505
# set log level
402506
if conf.logLevel != LogLevel.NONE:
@@ -451,7 +555,11 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
451555
error "failed to generate mix key pair", error = error
452556
return
453557

454-
(await node.mountMix(conf.clusterId, mixPrivKey, conf.mixnodes)).isOkOr:
558+
(
559+
await node.mountMix(
560+
conf.clusterId, mixPrivKey, conf.mixnodes, some(conf.rlnUserMessageLimit)
561+
)
562+
).isOkOr:
455563
error "failed to mount waku mix protocol: ", error = $error
456564
quit(QuitFailure)
457565

@@ -467,12 +575,13 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
467575
if kadBootstrapPeers.len > 0:
468576
node.wakuKademlia = WakuKademlia.new(
469577
node.switch,
470-
ExtendedKademliaDiscoveryParams(
578+
ExtendedServiceDiscoveryParams(
471579
bootstrapNodes: kadBootstrapPeers,
472580
mixPubKey: some(mixPubKey),
473581
advertiseMix: false,
474582
),
475583
node.peerManager,
584+
rng = libp2p_rng.newBearSslRng(node.rng),
476585
getMixNodePoolSize = proc(): int {.gcsafe, raises: [].} =
477586
if node.wakuMix.isNil():
478587
0
@@ -486,6 +595,10 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
486595

487596
#await node.mountRendezvousClient(conf.clusterId)
488597

598+
# Subscribe to spam protection coordination topics via filter since chat2mix doesn't use relay
599+
if not node.wakuFilterClient.isNil():
600+
asyncSpawn setupMixSpamProtectionViaFilter(node)
601+
489602
await node.start()
490603

491604
node.peerManager.start()
@@ -675,7 +788,7 @@ proc main(rng: ref HmacDrbgContext) {.async.} =
675788
raise e
676789

677790
when isMainModule: # isMainModule = true when the module is compiled as the main file
678-
let rng = crypto.newRng()
791+
let rng = HmacDrbgContext.new()
679792
try:
680793
waitFor(main(rng))
681794
except CatchableError as e:

apps/chat2mix/config_chat2mix.nim

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,13 @@ type
240240
name: "kad-bootstrap-node"
241241
.}: seq[string]
242242

243+
## RLN spam protection config
244+
rlnUserMessageLimit* {.
245+
desc: "Maximum messages per epoch for RLN spam protection.",
246+
defaultValue: 100,
247+
name: "rln-user-message-limit"
248+
.}: int
249+
243250
proc parseCmdArg*(T: type MixNodePubInfo, p: string): T =
244251
let elements = p.split(":")
245252
if elements.len != 2:

config.nims

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ if not defined(macosx) and not defined(android):
9090
nimStackTraceOverride
9191
switch("import", "libbacktrace")
9292

93+
# Auto-import the Option[T] valueOr/withValue shim for every compilation
94+
# unit (libp2p 1.15.3 dropped the Option overloads previously provided by
95+
# libp2p/utility). Per-file imports follow cover-traffic's pattern for
96+
# the 15 files touched there, but our branch has additional downstream
97+
# call sites that need the shim too -- auto-import covers them without
98+
# requiring every file to remember the explicit import.
99+
switch("import", "logos_delivery/waku/common/option_shims")
100+
93101
--define:
94102
nimOldCaseObjects
95103
# https://github.com/status-im/nim-confutils/issues/9

examples/lightpush_mix/lightpush_publisher_mix.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import
77
confutils,
88
libp2p/crypto/crypto,
99
libp2p/crypto/curve25519,
10-
libp2p/protocols/mix,
11-
libp2p/protocols/mix/curve25519,
10+
libp2p_mix,
11+
libp2p_mix/curve25519,
1212
libp2p/multiaddress,
1313
eth/keys,
1414
eth/p2p/discoveryv5/enr,

logos_delivery.nimble

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,23 @@ requires "nim >= 2.2.4",
2828
"toml_serialization",
2929
"faststreams",
3030
# Networking & P2P
31-
"https://github.com/vacp2p/nim-libp2p.git#ff8d51857b4b79a68468e7bcc27b2026cca02996",
31+
# nim-libp2p: release/v2.0.0 tip (c43199378). SHA-pinned because vacp2p
32+
# hasn't published a v2.0.0 git tag yet.
33+
"https://github.com/vacp2p/nim-libp2p.git#c43199378f46d0aaf61be1cad1ee1d63e8f665d6",
3234
"eth",
3335
"nat_traversal",
3436
"dnsdisc",
3537
"dnsclient",
3638
"httputils >= 0.4.1",
37-
"websock >= 0.3.0",
39+
"https://github.com/status-im/nim-websock >= 0.4.0",
3840
# Cryptography
3941
"nimcrypto == 0.6.4", # 0.6.4 used in libp2p. Version 0.7.3 makes test to crash on Ubuntu.
4042
"secp256k1",
4143
"bearssl",
4244
# RPC & APIs
43-
"https://github.com/status-im/nim-json-rpc.git#43bbf499143eb45046c83ac9794c9e3280a2b8e7",
45+
# TODO: revert to status-im/nim-json-rpc once
46+
# https://github.com/status-im/nim-json-rpc/pull/277 merges + tag cut.
47+
"https://github.com/chaitanyaprem/nim-json-rpc.git#f05fad251a1ceb845db963902b54295e7f37fb99",
4448
"presto",
4549
"web3",
4650
# Database
@@ -62,12 +66,21 @@ requires "nim >= 2.2.4",
6266
# Packages not on nimble (use git URLs)
6367

6468
requires "https://github.com/logos-messaging/nim-ffi#v0.1.3"
69+
requires "https://github.com/logos-co/mix-rln-spam-protection-plugin.git#23b278b4ab21193ad4e9ce76015f008db7332a6f"
70+
71+
# nim-libp2p-mix: extracted mix protocol used by the plugin and by waku's
72+
# mix integration layer. Tip of experiment/drop-nimble-lock (PR #14, stacked
73+
# on chore/bump-libp2p-v2.0.0). Carries the v2.0.0 bump + sink overrides +
74+
# AddressConfidence.Infinite + deeper move-semantics propagation + the
75+
# lockfile-as-build-artefact cleanup. Re-bump to master SHA once #14 lands.
76+
# The plugin pins the same SHA — keeps the diamond dep collapsed.
77+
requires "https://github.com/logos-co/nim-libp2p-mix.git#50c4ab4fa788a33eb12a0a2cecaa708873352b58"
6578

6679
requires "https://github.com/logos-messaging/nim-sds.git#abdd40cc645f1b024c3ee99cced7e287c4e4c441"
6780

6881
requires "https://github.com/NagyZoltanPeter/nim-brokers.git#v3.1.1"
6982

70-
requires "https://github.com/vacp2p/nim-lsquic"
83+
requires "lsquic >= 0.4.1"
7184
requires "https://github.com/vacp2p/nim-jwt.git#057ec95eb5af0eea9c49bfe9025b3312c95dc5f2"
7285

7386
proc getMyCPU(): string =

logos_delivery/channels/reliable_channel.nim

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,10 @@ proc new*(
400400
channelId: channelId,
401401
contentTopic: contentTopic,
402402
senderId: senderId,
403-
rng: libp2p_crypto.newRng(),
403+
# libp2p v2.0.0: newRng() now returns the `Rng` wrapper type, but the
404+
# `rng` field is typed `ref HmacDrbgContext`. Construct an
405+
# HmacDrbgContext directly (from bearssl/rand) to keep the field shape.
406+
rng: HmacDrbgContext.new(),
404407
segmentation: SegmentationHandler.new(segConfig),
405408
sdsHandler: SdsHandler.new(sdsConfig, senderId),
406409
rateLimit: RateLimitManager.new(rateConfig, channelId, brokerCtx),

logos_delivery/messaging/delivery_service/send_service/lightpush_processor.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import chronicles, chronos, results
22
import std/options
33
import brokers/broker_context
44
import
5+
logos_delivery/waku/common/option_shims,
56
logos_delivery/waku/node/peer_manager,
67
logos_delivery/waku/waku_core,
78
logos_delivery/waku/waku_lightpush/[common, client, rpc]

0 commit comments

Comments
 (0)