mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-08-25 15:31:16 +00:00
c3a1b6b052d50a91f51117ebfd0162bb0a808e24
2450
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c3a1b6b052 |
test(archive): pin the insert counter to what really got written
Each test writes once through a driver that refuses everything and once through a working one: the first half keeps the counter tied to successful writes -- it is what fails if the increment ever moves above the error check and turns into an attempt counter -- and the second half is what fails if the increment is dropped. The counters are process-global, so both compare against a baseline read inside the test rather than an absolute value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
2b5946e924 |
fix(archive): log a failed insert at a level the fleets keep
Both ingress paths logged a failed write at trace, which is compiled out at the level the fleets run, so a node discarding every message it received said nothing at all. Debug is enough to make it visible -- the delivery fleet runs at DEBUG, see infra-logos ansible/group_vars/delivery.yml -- and it keeps a broken database from turning every message into an error line. Left untested on purpose: there is no capture of the log output in this repo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b1fecb5051 |
feat(archive): count the messages the node actually writes
Nothing today tells a store node that writes nothing from a healthy one: the per-shard gauge resets on restart and never sees the store-sync ingress, and the stored-message count is a half-hourly total that retention keeps flat and that silently goes stale when its query fails. Count every successful write on both ingress paths, labelled by which one it came through, so that a node going quiet is a rate() of zero rather than a signal nobody has. Both series are published at zero when the archive is built, so a node that has not written yet is reported as writing nothing instead of not reported. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
69fbffa3a0 | fix(nix): emit and install the generated C header (#4136) | ||
|
|
1782941975 | chore: bump nim-ffi to v0.3.1 (#4135) | ||
|
|
27e0767e3b | feat(ffi): pin nim-ffi 0.3.0 and stop the node on destroy (#4111) | ||
|
|
195917898c |
Rework logging to be useful (#4110)
* Rework WARN and ERR logs during startup * Clean up noise from start up logs * Rework node hot path logs to only have crucial logs at INFO level * Logs rework archive, filter, LP, peer ex, rdv, store * Rework logs for waku_relay and rln * rework logs for waku_node layer * rework lgos for waku common and core * Rework discovery and factory logs * Rework logs for rest api endpoints * Rework logs for messaging delivery service and channel lifecycle * Rework logs in api directories * Minor log cleanups * Add info log for message recv by msg or reliable channels API * Add metric to send_service, revert node/rln metrics to INFO lvl * return WARN lvl to build conf preset * revert log lvl to error for healthLoop exception * Log error level for failures to serve API level requests * Revert some debug logs to error or warn level |
||
|
|
bc4a64f025 | fix: partition messages_lookup to stop the index bloat (#4106) | ||
|
|
517bef1591 | fix(ci): daily build times out (#4126) | ||
|
|
d3c0979dd5 |
chore(docker): rename compose folder and fix review follow-ups from #4057 (#4129)
Follow-up to the review of #4057. - Rename apps/logos_delivery_node/compose -> apps/logos_delivery_node/docker (review request); the only path reference was in the folder's own README. - run_certbot.sh: pass the configured EMAIL to certbot instead of the hardcoded admin@$DOMAIN, and forward EMAIL to the certbot container so setting it in .env takes effect. Empty still falls back to admin@$DOMAIN. - set_storage_retention.sh: `grep -c` always prints a count, so the old sudo test was always true and sudo was used whenever ./postgresql existed. Test readability of the data directory directly instead. - chkhealth.sh: drop the stray `GET` argument, which curl treated as an extra (failing) URL. - docker-compose.yml: put certbot behind the `wss` profile so it no longer publishes host port 80 on deployments that do not use WebSocket-Secure, and drop the node's meaningless `depends_on: certbot` (run_node.sh polls for the certificate on disk anyway). |
||
|
|
2fb625f463 |
ci(e2e): point the S11/S25 store peers at the built image too (#4128)
The job set NODE_2 but not NODE_1, so the two docker store peers in test_send_lightpush_and_edge.py (S11 and S25) silently fell back to the DEFAULT_NWAKU literal, wakuorg/nwaku:v0.38.1, instead of the image built by this run. Both tests are docker_required, so they run in the subset that does receive node_image; they were the only NODE_1 readers in the suite and the only ones missing out. An empty node_image keeps working: get_env_var treats "" as unset, so the non-docker subset and fork PRs still land on v0.38.1, exactly as NODE_2 already did. DEFAULT_NWAKU is left as the hardcoded literal it is. Nothing reachable from tests/wrappers_tests reads it except as the NODE_1/NODE_2 fallback - src/steps/sharding.py and src/steps/rln.py are its only direct consumers and neither is imported here - so making it env-driven would add a knob with no effect. ADDITIONAL_NODES is likewise unused: its only readers, setup_additional_publishing_nodes and setup_additional_store_nodes, have no callers anywhere in tests-e2e. |
||
|
|
0d433ea83f |
feat(compose): add logosdeliverynode docker-compose stack (#4057)
* feat(compose): add logosdeliverynode docker-compose stack Add apps/logos_delivery_node/compose/, a docker-compose project that runs the logosdeliverynode image (built from the repo Dockerfile) as a service node with a Postgres store and a Prometheus + Grafana monitoring stack. Ported from logos-messaging/logos-delivery-compose and adapted for logosdeliverynode: - Network selection via --preset (default logos.dev) and --entry-layer (default kernel), both configurable through PRESET / ENTRY_LAYER env vars. - Postgres-backed store; node startup gated on the postgres healthcheck to avoid a crash-loop on connection-refused. - WebSocket-Secure via certbot, enabled only when DOMAIN is explicitly set (no reverse-DNS auto-guess, which could deadlock the node waiting for a cert). - --mix=true passed explicitly (the preset alone sets the flag but does not mount the mix protocol). - Grafana branded with the Logos mark; dashboard file named logos-delivery-monitoring.json. - RLN, setup_wizard and RLN keystore tooling intentionally omitted for now. Also exclude /nimbledeps and /build from the Docker build context: a populated host nimbledeps/ leaks into the context and has its package submodules stripped by the **/vendor/* rule, breaking the in-container `make build-deps`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ci): keep build/ in docker context; serialize container-image builds Two fixes for the docker-build-ubuntu-22.04 job: - .dockerignore: drop /build. docker/binaries/Dockerfile.bn.amd64 ADDs the freshly compiled binaries from ./build/, so ignoring it makes the docker build fail deterministically with '"/build/wakunode2": not found'. /nimbledeps stays ignored (that was the actual context-bloat culprit). - container-image.yml: build wakunode2 and logosdeliverynode in sequential make invocations. Under a single `make -j` the two `nimble <task>` invocations re-resolve git deps concurrently and clobber each other in the shared ~/.nimble/pkgcache. Same fix as ci.yml's -j1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Review fixes: dashboard typo, missing target for docker build, env.example adjustment |
||
|
|
23b0d31e84 |
test(e2e): port the channel delivery tests and the isolated sender process (#4117)
Three open PRs in logos-delivery-interop-tests add 19 channel-API tests (RC07-RC09 #203, RC10-RC12 #209, RC13 #212). Their wrapper API surface is already covered here, but the pieces they import were not, so those tests could not be written in core. This ports them. subprocess_node.py, rc05/rc06 and the closed-channel test come from interop master; the two wrapper_helpers additions are byte-identical to what #209/#212 add, so the RC07-RC13 ports apply cleanly. Why a spawned subprocess: co-located nodes both default localStoragePath to ./data and share data/sds.db, so the receiver drops the sender's own channel message as a duplicate. The interop rationale blaming a process-wide Persistency singleton is false since #4109 made Persistency per-node; the shared storage path is the cause, and chdir to a private temp dir is what isolates it. A per-node localStoragePath would likely replace the spawn machinery, but RC07-RC13 build on the ported form. Why the re-subscribe in the closed-channel test: channel_close unsubscribes the content topic and subscriptions are not refcounted (#4081, tracked in #4087), so the receiver re-subscribes to keep message_received as the arrival witness. This follows the resolution in the still-open interop PR #212. Non-docker collection goes 48 -> 51; README updated. Suite: 48 passed, 2 skipped, 1 xfailed, verified over two back-to-back runs. |
||
|
|
e2a8c2b5d1 | fix(ci): nightly pre-release and release-assets fixes (#4112) | ||
|
|
f4c5a703e0 | fix(ffi): event listeners lifetime (#4118) | ||
|
|
56dda125ff |
ci: pull and publish the Logos Attic cache in nix builds (#4071)
Adopts logos-co/setup-nix-cache-action (as in logos-delivery-module#69); flake.nix substituter moves from the retired status.im nix-cache to cache.nix.logos.co/public. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ef651a7ccc |
fix(networks): move logos.dev preset to cluster-id 3 (#4113)
* fix(networks): move logos.dev preset to cluster-id 3 The Logos Dev Network is now deployed on cluster 3. Update the `logos.dev` preset accordingly, along with the CLI help text and the library README preset table. The deprecated "cluster-id implies preset" shim now triggers on `--cluster-id=3` instead of `2`: cluster 2 is still the Logos Test Network, so keeping the old mapping would silently move a node that explicitly asked for cluster 2 onto cluster 3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(conf): keep the legacy cluster-id preset shim on cluster 2 Remapping the deprecated `--cluster-id` -> preset shim onto cluster 3 hijacked every caller that uses 3 as a plain cluster id with no preset, applying the whole logos.dev preset (p2p reliability, mix, discv5, dev entry nodes) on top. tests/api/test_api_send.nim does exactly that, and the extra reliability layer made Send emit `Sent` events the tests never asked for. Revert that hunk; only the preset's own cluster id moves to 3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
342a965370 |
test(e2e): port remaining wrapper tests from the interop repo (#4077)
* test(e2e): port remaining wrapper tests from the interop repo The wrapper suite that moved into tests-e2e (#4027) was a reworked subset of the one still living in logos-delivery-interop-tests. Comparing both sides showed 21 tests here against 46 there, with no overlap in the delta: the 25 missing tests cover scenarios the reworked set never included. Ports those 25 tests, bringing the in-repo suite to the full 46: - 12 send scenarios: s01 (nil/destroyed handle), s03, s04, s05, s11, s13, s16, s18 (both orderings), s25, s29 - 7 channel lifecycle tests (rc01-rc04) - 6 wrapper corner cases: auto port allocation, MyBoundPorts, ENR Supporting changes the ported tests need: - wrapper_helpers: get_node_tcp_port, get_node_bound_ports, enr_udp_port - WrapperManager: channel_create/send/close, destroy_keep_ctx - vendored binding refreshed to the revision exposing the channel API (additive only; cffi resolves symbols lazily, so nothing existing moves) Two Edge senders were fixed while porting. build_node_config defaults relay and store to True, and the flat-JSON config path applies mode=Edge before explicit fields, so those defaults win: the Edge nodes in s11/s16/s25 came up as relay and store servers and exercised the relay path instead of lightpush. They now set relay=False and store=False, matching test_send_e2e_part2. s16 also dropped lightpush=True, which mounts the lightpush server and fails node start once relay is off; the lightpush client mounts unconditionally. Suite goes from 21 to 46 functions (53 collected). The docker subset grows from 3 to 5 as s11 and s25 need a store peer. Local run against a freshly built library: 45 passed, 2 skipped, 1 xfailed. * test(e2e): enable autosharding in the channel lifecycle tests channel_create subscribes to the channel's content topic since #4081, and resolving that topic to a shard needs autosharding. build_node_config leaves numShardsInNetwork at 0 and cluster 198 has no preset, so these nodes came up with static sharding and every channel_create failed with "autosharding is not configured; pass an explicit shard". Adds numShardsInNetwork=1 to the six tests that create a channel, matching what every other wrapper test that touches the send or channel API already does. rc02 is left alone: channel_send rejects on the id lookup before any shard is resolved. Verified locally against a fresh build: the five tests that complete now pass and the error string is gone from the run. |
||
|
|
4a85db1b6a | feat(ffi)!: migrate liblogosdelivery to the nim-ffi 0.3.0 typed C ABI (#4082) | ||
|
|
13d9b52f4a | fix(ci): misc CI fixes (#4107) | ||
|
|
e8566db8ee |
fix(persistency): own Persistency per node instead of a process-global singleton (#4109)
* fix(persistency): own Persistency per node instead of a process-global singleton
The Persistency singleton (gPersistency) was allocated on whichever FFI
thread first ran waku.start; under --mm:refc its memory belonged to that
thread's heap, so a second library context adopting it read a foreign
heap (SIGSEGV in sdsPersistence -> openJob -> tables.rawGet), one
context's stop stole the other's SDS persistence, and a destroyed
context poisoned re-init with a different local-storage-path.
- remove the singleton (instance/reset/gPersistency); Persistency.new is
the public constructor, no lock needed (instances are thread-confined)
- own the instance as Waku.persistency: created and provided in
waku.start, cleared and closed in waku.stop on the owning thread
- expose it via a sync GetPersistency RequestBroker scoped to the node's
BrokerContext; sdsPersistence resolves through it (same-thread ref
return, no marshalling)
- add InMemoryStoragePath (":memory:") support: private in-memory SQLite
per job worker, for tests
- rewrite test_singleton as per-instance + broker coverage; rewrite
test_thread_affinity from a known-failing UB repro into a regression
guard (two in-memory jobs, worker spinup/teardown, cross-thread broker
denial) and register it in test_all; migrate remaining tests to
new/close; the FFI lifecycle test's stop-steals and different-paths
cases now pass against the real dylib
The FFI destroy-without-stop teardown gap remains tracked in #4108.
Fixes #4103
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(persistency): create the instance only after startup fully succeeds
waku.start has many error return paths; creating Persistency early meant
every one of them left the field set and the GetPersistency provider
installed with no teardown. Persistency.new is inert (no threads or
files until the first openJob) and every consumer runs post-start, so
creating and providing it as the last startup step removes the need for
any error-path cleanup entirely.
Addresses PR #4109 review feedback.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(persistency): create early for startup-stage restores, tear down on failed start
Creating the instance as the last startup step made it impossible for
any stage of start to restore persisted data (e.g. a future store-state
restore). Restore the original ordering -- create and provide the
instance first -- and cover every error return path of waku.start with a
success-flag defer that clears the provider and closes the instance.
Teardown is factored into closePersistency, shared by stop and the
failed-start path.
Addresses PR #4109 review discussion.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore(persistency): address remaining Copilot review findings
- document that Persistency instances are thread-confined (not
thread-safe) on the type itself, pointing at the GetPersistency broker
as the sanctioned access path
- use tryRemoveFile for the FFI test's log cleanup so an unremovable
file cannot fail the test for unrelated reasons
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Fix comment
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
a5d781887e |
Add logging policy (#4104)
* Add a doc defining logging policy for contributors * Clarify wording and add msg send/rcv as INFO |
||
|
|
6a1af006b1 |
feat: attach and refresh RLN proofs in the send service (Stream B) (#4069)
* feat: attach RLN proofs at the SendService transmission stage The relay send path published without an RLN proof: proof generation lived client-side in (legacy)lightpushPublish, so messages dispatched through SendService -> RelaySendProcessor reached the network unproven and would be rejected by an RLN-enforcing relay. Adds Waku.attachRlnProof in the waku/api publish surface and calls it from SendService immediately after admission, in both send() and the retry loop. Placement is load-bearing: - After admit(), so a message rejected by the rate limiter never draws a nonce. - At transmission rather than API entry, because a proof binds to the epoch current when the message goes out, and a task can be retried for up to MaxTimeInCache after send() returns. attachRlnProof is a no-op without RLN mounted (message passes through unproven, as today) and short-circuits on a message that already carries a proof, so retrying a task neither redraws a nonce nor changes the bytes. It uses generateRLNProofWithRootRefresh rather than the plain generator: a task can wait in the task cache while the group root moves on chain, so the proof is validated against the acceptable-root window and regenerated once against a refetched merkle path if it went stale. Proof-generation failure parks the task as NextRoundRetry rather than failing it, matching the admission path: the dominant failure is NonceLimitReached (RLN's own per-epoch budget exhausted), which the service loop resolves as the epoch rolls over. Adds tests/messaging/test_rln_proof_attach.nim covering the unmounted pass-through, attach when mounted, and the idempotency contract that the retry loop depends on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: handle RLN publish rejections in the send service retry loop An RLN-invalid publish rejection now recovers through the send service's existing retry loop instead of an inline retry in the kernel. When a relay or lightpush publish is rejected as RLN-invalid, the processor clears the message's stale proof, schedules a background merkle-proof refresh, and parks the task as NextRoundRetry. The next loop round re-admits the task and regenerates the proof against the refreshed path. Clearing the proof is required: attachRlnProof short-circuits on a message that already carries one, so without the clear the task would resend the rejected proof until it ages out. The relay processor previously failed such tasks outright, with no recovery. Kernel changes supporting this: - Remove runRlnRefreshRetry from legacyLightpushPublish. The legacy path now schedules the refresh and returns the error tagged with RlnProofRefreshScheduledMsg, matching the non-legacy path; retrying is the caller's decision. Drops the now-unused RlnMerkleProofRefreshTimeout. - generateRLNProofWithRootRefresh reuses the nonce drawn for the first attempt when it regenerates after a stale root, rather than drawing a second. Only the merkle path differs between the two attempts, so a redraw would spend two message ids from the epoch budget on a single message and drift the rate limit manager's accounting away from the nonce manager's. Adds Waku.isRlnRejection / Waku.onRlnProofRejected as the messaging layer's handle on the kernel's RLN rejection detection and background refresh. Updates the legacy lightpush tests to the schedule-refresh contract. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: cover currentRlnEpochQuota (mounted and unmounted) currentRlnEpochQuota ships in the enforcement PR, but its mounted-RLN assertion needs the anvil-backed group-manager scaffolding that lives in this file, so the coverage rides along here: none when RLN is unmounted, and the epoch index + userMessageLimit when it is. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor: consolidate the RLN-rejection parking into parkForRlnProofRefresh The relay and lightpush processors duplicated the RLN-rejection recovery (schedule background refresh, clear the stale proof, reset admission, park as NextRoundRetry); both now call parkForRlnProofRefresh in send_processor, so the proof-clear the retry contract depends on cannot drift between the two processors. Also resets firstAdmittedTime so the regenerated proof's fresh nonce is re-admitted rather than sent uncharged. No behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: clarify that lightpush reuses an already-attached RLN proof The old wording ("attaches an RLN proof per attempt") reads as if every retry redraws a nonce. The flow proves a message only when it carries no proof, so a task admitted once reuses its proof and nonce across retries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: retry once on scheduled RLN proof refresh in the lightpush REST handlers The kernel lightpush publish paths no longer retry an RLN-invalid publish inline. On a stale merkle root they schedule a background cache refresh and return early, tagging the error with RlnProofRefreshScheduledMsg — retrying is the caller's decision, so the send service recovers through its own loop and the kernel exposes mechanism only. The synchronous REST endpoints have no such loop: they call publish once and map the result to an HTTP status. Left unchanged, a transient stale-root rejection that the kernel previously absorbed via runRlnRefreshRetry would now surface to the HTTP client as a 503. Restore the transparent retry where it belongs under this layering — at the caller — instead of back in the kernel where it would re-nest inside the send service's retry. Both the legacy and v3 handlers now retry the publish exactly once when the first result carries RlnProofRefreshScheduledMsg, under the same FutTimeoutForPushRequestProcessing bound. The handler's message carries no proof, so the retry regenerates against the refreshed merkle path. Any other error, and any error on the retry itself, maps to its response as before. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: retry RLN proof attach on a charged-but-unproven task admitAndProve set firstAdmittedTime before attaching the proof, then guarded its whole body on firstAdmittedTime.isSome(). A transient proof attach failure (e.g. NonceLimitReached) left the task charged but with an empty proof, and the next round's early-return skipped the attach entirely and shipped the message bare. The docstring's own invariant — "once admitted, a task keeps its slot and its proof" — was violated: it kept the slot but not the proof. Guard only the rate-limit charge on firstAdmittedTime, not the attach. attachRlnProof is already idempotent (short-circuits when RLN is unmounted or a proof is present), so it is safe to call every round: a charged-but- unproven task retries the attach until it sticks, then short-circuits. The ordering invariant holds (charge strictly before attach, so an over-budget message never draws a nonce), the charge stays once-per-task, and NO_PEERS retries remain free. This also removes a latent relay double-charge: previously a bare message reached the relay, was rejected as RLN-invalid, and parkForRlnProofRefresh reset firstAdmittedTime — re-charging a slot on the next round. The message now never leaves unproven. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4809b991df | chore(deps)!: bump nim-ffi to 0.3.0-rc.1 (per-listener event ABI) (#4070) | ||
|
|
230417e7b6 |
fix: stop host OpenSSL from hijacking bundled BoringSSL in liblogosdelivery (#4085) (#4086)
This commit is a workaround to a nim-boringssl issue; see: https://github.com/logos-messaging/logos-delivery/pull/4086#issuecomment-5140765772 |
||
|
|
f8b036594e |
fix(conf): default QUIC off on the messaging path (#4084)
The structured path was the only place defaulting quicSupport to true (kernel default is false). Booting that QUIC listener SIGSEGVs in lsquic setupSSLContext, so keep QUIC opt-in until the transport is validated. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ba28e32e58 |
feat(conf): add localStoragePath to MessagingClientConf (#4083)
The structured shapes had no way to set the node's storage directory: localStoragePath is kernel-only and the structured parser rejects bare kernel fields. Hosts (logos-delivery-module) inject a per-instance path and need a place the structured grammar accepts. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
504336efdb |
feat(node-info): expose MaxMessageSize node info item (#4018)
* feat(node-info): expose MaxMessageSize node info item Add a `MaxMessageSize` entry to `NodeInfoId` so the max message size (in bytes) accepted by the relay protocol can be queried through the node info surface, consumed by both the debug API and the FFI `get_node_info` / `get_available_node_info_ids` calls. The value is read from the mounted relay's `maxMessageSize` and is empty when the relay protocol is not mounted on the node (e.g. light nodes), mirroring the existing `MyMixPubKey` handling. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(node-info): source MaxMessageSize from node config Read the reported value from `conf.maxMessageSizeBytes` instead of the mounted relay. The relay only holds a copy of the configured value and is absent on relay-disabled (light) nodes, which would report an empty string. Sourcing from the config makes the item always available and reflects the node's configured limit directly. WakuStateInfo now holds the WakuConf it reports on; init takes it as an argument. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3288d2d88d | fix: subscribe to the channel's content topic on channel_create (#4081) | ||
|
|
afe90a9d31 |
chore(metrics): sync fleet dashboards from Grafana (#4079)
Refreshed the two shared-Grafana dashboards and re-applied the old-or-new metric name union from #4074, so the panels keep working across the rename. waku-fleet-dashboard.json v181 -> v191 drops the five deprecated Store v2 panels; the "Store V3 Times wire-to-wire" query moves to sum without (phase) and changes() instead of deriv(), which is the better staleness filter for a gauge that moves and returns waku-single-node-dashboard.json v7 -> v9 no functional change, a Grafana 12 re-save (pluginVersion, threshold base, schemaVersion 40 -> 41) The $dc variable is populated from libp2p_peers rather than waku_version as the export had it: libp2p_* comes from a dependency and is immune to our renames, so it cannot rot the next time metrics are renamed. Grafana keeps variable queries outside `expr`, so the #4074 union pass never saw this one. Store/Archive is collapsed again, matching the other rows; the export had it open because of whoever last hit save. "Store v3 query request rates" moves to x=0 to fill the space left by deleting its v2 sibling. The dashboards under apps/ are deliberately not synced. They are provisioned into the docker-compose stacks that ship beside them (docker-compose.yml mounts monitoring/configuration/dashboards into /var/lib/grafana/dashboards), so they must match the local datasources.yaml and prometheus-config.yml, not status.prod. The Grafana copies carry a datasource uid that the local stack does not provision and job selectors that its single scrape job cannot match. |
||
|
|
c873f74b4b |
feat: enforce the per-epoch rate limit at the send service (RLN-sourced) (#4062)
* feat: enforce per-epoch budget in RateLimitManager.admit Replaces the pass-through skeleton with a lazily rolled fixed window: admit() charges one message against the current epoch, resets the counter once epochPeriodSec has elapsed, and rejects with OverBudget when messagesPerEpoch is exhausted. Disabled or non-positive configurations admit everything, so the default-constructed MessagingClientConf (enabled = false) keeps today's behaviour. Parking of over-budget messages stays with the SendService scheduler (NextRoundRetry); the manager only answers whether one more transmission fits. The queue / dequeueReady stubs that anticipated manager-side parking are removed accordingly. Extends tests/messaging/test_rate_limit_manager.nim with budget boundary, epoch rollover, resetEpoch, and degenerate-config cases, replacing the enabled-pass-through placeholder test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: split rate limit manager into config, quota source, and enforcement modules Decomposes logos_delivery/messaging/rate_limit_manager/ into three modules with one responsibility each: - rate_limit_config: the configuration vocabulary (RateLimitConfig, RateLimitError, defaults, isEnforcing). The API conf layer now imports this alone instead of the enforcement engine. - quota_source: the RLN seam. A single QuotaProvider callback returns EpochQuota (epoch index + user message limit) so the two are read atomically and a read cannot straddle an epoch boundary. Returns none when RLN is unavailable, selecting the wall-clock fallback. The callback shape keeps the manager free of any dependency on the Waku kernel; the RLN-backed provider is built one layer up. - rate_limit_manager: enforcement only; re-exports the other two so existing single-import call sites are unchanged. Config field names now mirror RLN exactly, per the requirement that the rate limit share RLN Relay's format: epochSizeSec (was epochPeriodSec) and userMessageLimit (was messagesPerEpoch), both uint64 to match RlnConf. Renaming is contained to this branch: the config type moved into the new rate_limit_config module here. admit() now works in epoch-index terms: the epoch comes from the provider when set (RLN's calcEpoch value), else from an absolute wall-clock window (unixTime div epochSizeSec — absolute rather than anchored at first use, matching RLN's derivation). The effective limit is min(config.userMessageLimit, RLN's) — RLN can only tighten the configured limit, since exceeding it would fail at proof generation once the epoch's message ids are exhausted. With no provider wired (this commit), behaviour is the wall-clock window as before; RLN-backed provider wiring follows separately. Tests rewritten against injected fake providers: limit boundary, epoch rollover without sleeps, RLN-clamps-config, config-tightens-below-RLN, and the wall-clock fallback (7 tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: charge admission once per task and exempt budget-parked tasks from the reaper Two defects the send-service seam had once admission actually enforces, both fixed by a single DeliveryTask.firstAdmittedTime field: - The retry loop gated admission on firstPropagatedTime.isNone(), so a task that failed to propagate (e.g. no peers) was re-admitted on every 1s tick, re-charging the epoch budget and — with the shipped default of one message per epoch — starving all other traffic. Admission is now gated on firstAdmittedTime.isNone(): a task charges one slot / draws one nonce for its lifetime, and retries reuse it. - reportTaskResult reaped any never-propagated task older than MaxTimeInCache (60s) measured from message creation, so an over-budget task parked for a 600s epoch was hard-failed with a misleading "Unable to send within retry time window" long before the epoch could roll (issue #4049). The reaper now runs only for admitted tasks and measures from admission, so a task waiting for epoch budget is exempt and gets a full delivery window once it is finally admitted. The RLN-rejection branch in both processors resets firstAdmittedTime along with clearing the proof, so the regenerated proof's fresh nonce is re-admitted rather than sent uncharged. The reap decision is extracted to DeliveryTask.isDeliveryTimedOut and unit-tested (tests/messaging/test_delivery_task_reaping.nim); a scheduler-level integration test of park-and-release is a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: source the rate limit manager's epoch and budget from RLN Wires the quota seam to its producer, so enforcement tracks RLN rather than only the wall clock. - Waku.currentRlnEpochQuota (waku/api/publish) reads RLN's current epoch index and the epoch's user message limit together, returning none when RLN is not mounted (or its limit is unset). - MessagingClient.new builds a QuotaProvider closure over that accessor and hands it to the RateLimitManager. The closure is late-binding: it queries the kernel on each admission, so a node whose RLN mounts after construction upgrades from the wall-clock fallback to RLN's epoch and limit automatically, with no reconstruction. With this, admit() rolls its window on RLN's epoch and clamps the configured cap to RLN's user message limit; without RLN it still falls back to the absolute wall-clock window and the configured limit. Also switches the quota seam from std/options to results `Opt`, matching the kernel surface it now bridges (`groupManager.userMessageLimit` is `Opt`) and the rest of the messaging layer post-Opt migration. Tests: currentRlnEpochQuota is none unmounted and reports epoch + the configured userMessageLimit when mounted (anvil-backed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: cover admit-once and park/release at the send service scheduler The rate limit manager's budget logic and the delivery-timeout reaper are unit-covered, but the send service's use of them across the service loop was not. Drive the scheduler a tick at a time against a scripted fake processor and a fixed-epoch quota provider — no network, no sleeps — asserting: - a task is charged against the budget exactly once however many rounds delivery takes (firstAdmittedTime guards re-admission); - an over-budget task parks as NextRoundRetry without reaching the processor, then is admitted and delivered on the first tick after the epoch rolls. Two testability seams keep this deterministic without a live relay: an optional sendProcessor override on SendService.new injects the fake, and trySendMessages is exported to drive one loop tick. The task is built directly (like test_delivery_task_reaping) since DeliveryTask.new resolves its shard through a broker provider only registered once the node starts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: note rateLimit config is settable only programmatically MessagingClientConf.rateLimit cannot be set through the JSON config or a CLI flag: it carries no name pragma, and RateLimitConfig is a nested object with no parseCmdArg, so applyJsonFieldsToConf rejects it with "cannot be set via JSON". Record the limitation and the fix path on the field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor: consolidate the admission gate into admitOnce send() and the retry loop duplicated the admit-then-stamp-firstAdmittedTime sequence; both now call SendService.admitOnce, keeping the charge-once invariant in one place. No behavior change; messaging tests pass (14/14). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * use explicit return statement * Make comments mroe concise * refactor: give each rate-limit config field a single responsibility `isEnforcing` folded three fields into one "should I enforce" answer, so a zeroed `messagesPerEpoch` or `epochPeriodSec` silently disabled an enabled config. Split the responsibilities: - `enabled` alone gates enforcement; `admit` reads it directly and `isEnforcing` is removed. - a zero `messagesPerEpoch` now means what it says — admit nothing — which `admit` already yields (`0 >= 0` -> OverBudget), no special case. - `RateLimitManager.new` returns a Result and rejects an enabled config with `epochPeriodSec == 0`, the only value that could crash the wall-clock fallback (`unixTime div epochPeriodSec`). Callers thread the Result through; a zero-init `RateLimitConfig` stays valid (disabled), so default construction is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ed8e881c1d | fix: stop emitting receive events after channel_close (#4075) | ||
|
|
8125cd0262 |
refactor(metrics): give every metric a logos_delivery_ prefix (#4074)
* refactor(metrics): prefix node metrics with logos_delivery_
Every metric the node exports now starts with logos_delivery_. There was no
prefix mechanism before: nim-metrics derives the exported name from the Nim
identifier, no declaration passed an explicit `name = "..."`, and the waku_
convention was maintained by hand -- 69 of the 80 node metrics followed it and
11 did not (query_count, query_time_secs, event_loop_load,
event_loop_accumulated_lag_secs, postgres_payload_size_bytes, reconciliation_*,
total_* and the camelCase rendezvousPeerFoundTotal).
Identifiers are renamed rather than given a `name = "..."` argument, keeping the
invariant that the Nim identifier is the exported name and letting the compiler
check every call site.
rendezvousPeerFoundTotal becomes logos_delivery_rendezvous_peer_found: it was the
only camelCase metric, and the trailing Total was redundant since nim-metrics
already appends _total to counters at exposition time.
library/ is untouched on purpose -- `proc waku_version()` in
kernel_api/debug_node_api.nim is the exported libwaku C ABI symbol, not the gauge
of the same name in node_telemetry.nim.
BREAKING CHANGE: metric names change. Dashboards, alert rules and recording rules
that reference waku_* must be updated; see docs/operators/how-to/monitor.md.
* refactor(metrics): prefix auxiliary app metrics with logos_delivery_
Applies the same prefix to the tools shipped from this repo: liteprotocoltester
(lpt_*), networkmonitor (networkmonitor_*), chat2bridge (chat2_*) and the
lightpush_mix example (lp_mix_*).
These tools are not the delivery node and already had their own consistent
prefixes, so this commit is separable from the node rename if the intent was to
namespace only the node itself.
* chore(metrics): query old and new metric names in Grafana dashboards
212 expressions across 9 dashboards now match both the waku_* and the
logos_delivery_* spelling, so panels keep working across the upgrade and over
historical data:
sum by (type)((increase(waku_node_errors_total{...}[$__rate_interval])
or increase(logos_delivery_node_errors_total{...}[$__rate_interval])))
The `or` is placed around the leaf, inside every aggregation. That depth is
load-bearing: `or` keeps its right operand only for label sets absent from the
left, so `sum by (type)(old) or sum by (type)(new)` aggregates each half of the
fleet separately and then discards the right one entirely -- silently dropping
every already-upgraded node. 36 panels here collapse `instance`.
Measured against a local Prometheus scraping two targets, one exporting old names
at 10/s and one exporting new names at 20/s (truth 30/s): union outside the
aggregation gives 10, union around the leaf gives 30.
Where the leaf sits in a range vector the whole call is duplicated, since
`(a or b)[5m]` is not valid PromQL.
Once every scraped node runs a release with the new names and the old samples
have aged out of retention, the `or` half can be deleted.
* test(e2e): expect logos_delivery_-prefixed metric names
The e2e suite asserts against a live /metrics endpoint, which serves only the new
names, so these are replaced rather than unioned. libp2p_* entries are unchanged.
* docs(operators): document the logos_delivery_ metric prefix
Records that every metric the node exports is prefixed, that dependency metrics
(libp2p_*, nim_gc_*, process_*) keep their own names, and shows where the `or`
has to sit if operators maintain their own dashboards or alert rules.
* refactor(metrics): name the store fleet metrics after store, not relay
logos_delivery_relay_fleet_store_msg_size_bytes and _msg_count are declared in
waku_store/protocol_metrics.nim and recorded by the store client, but carried a
relay prefix. Renamed to logos_delivery_store_fleet_msg_size_bytes and
logos_delivery_store_fleet_msg_count.
The dashboard keeps matching the old exported name, which was
waku_relay_fleet_store_*.
Note that both metrics are wrong independently of their name, see the PR
description.
|
||
|
|
26e1d62524 |
ci: run docker subset of the API/e2e wrapper tests (#4052)
* Add in-repo API/e2e test suite (tests-e2e) Migrate the liblogosdelivery API/wrapper pytest suite into tests-e2e/ and run it in CI (e2e-api-tests.yml) against the built library. Edge senders use the real lightpush path (relay=False). * ci: run docker subset of the API/e2e wrapper tests Parametrize e2e-api-tests.yml with `subset` (non-docker|docker) and `node_image` inputs, and add a send-api-e2e-docker-tests job that runs the docker_required wrapper tests (S19/S20/S31) against the built node image. The docker job needs both the liblogosdelivery artifact and the container-image build, and skips when no image is available (fork PRs lack the registry secrets). The non-docker subset still needs only `build`, so a docker-image build failure cannot mask its result. * ci: run docker e2e subset on fork PRs against a pinned public image Drop the gate that skipped the docker subset when no PR-built node image is available. Fork PRs lack the quay secrets, so build-docker-image succeeds with an empty output and the subset now falls back to DEFAULT_NWAKU instead of being skipped. Pin DEFAULT_NWAKU to wakuorg/nwaku:v0.38.1. The rolling :latest tag is a daily upstream master build and currently fails to boot with "updateAddressInENR: Public key does not correspond with given private key", which would have made the subset permanently red on fork PRs. s31 passes against v0.38.1 and against the PR-built image. Echo the resolved peer image at the start of the test step. The job name is left static so the check can still be made required. |
||
|
|
b5f624f519 | fix: report the wire sender's id in channel_message_received (#4073) | ||
|
|
a7df0d9c56 |
chore: adjust artifact builds and uploads to contain logosdeliverynode app (#4059)
* feat(nix): add logosdeliverynode build target Generalize the wakucanary-only binary path in nix/default.nix into an app-target map so any app binary shares one build/install/rln-bundle path, and expose a `logosdeliverynode` flake package (with gitVersion for --version reporting). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(github): build logosdeliverynode in release/pre-release/windows workflows - release-assets.yml: build logosdeliverynode (POSTGRES=1) so it ships in the waku-<arch>-<os>.tar.gz release asset. - pre-release.yml: add logosdeliverynode to the make targets and to the nwaku nightly/RC tarball. - windows-build.yml: build logosdeliverynode.exe and assert it exists. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(jenkins): build & push logosdeliverynode docker image to harbor Jenkinsfile.release now builds a second image from the dedicated logosdeliverynode Dockerfile stage and pushes it to harbor.status.im/wakuorg/logosdelivery:<IMAGE_TAG> alongside the primary image. Add a logosdeliverynode stage to the root Dockerfile so the image entrypoint is /usr/bin/logosdeliverynode rather than the generic /usr/bin/wakunode symlink. Bump the pipeline timeout to 40m to cover the extra compile. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
92841b5041 | fix: bound the error label of failed_store_queries (#4053) | ||
|
|
296460a407 | fix: dial QUIC before TCP regardless of address list order (#4061) | ||
|
|
29861bc286 |
chore: bump nim-brokers to 3.3.0 and add nim-brokers skill (#4058)
* chore: bump nim-brokers to 3.3.0 and add nim-brokers skill Bump brokers requirement v3.1.4 -> v3.3.0 (nimble, lock, nix/deps.nix) and add the nim-brokers Claude skill copied from nim-brokers master (doc/CLAUDE_brokers_skill.md) at .claude/skills/nim-brokers/SKILL.md. * ci(container-image): serialize wakunode2 and logosdeliverynode builds |
||
|
|
8ad99f10f4 |
Add in-repo API/e2e test suite (tests-e2e) (#4027)
Migrate the liblogosdelivery API/wrapper pytest suite into tests-e2e/ and run it in CI (e2e-api-tests.yml) against the built library. Edge senders use the real lightpush path (relay=False). |
||
|
|
54360d47bc |
Move rate-limit-manager to messaging client layer (#4021)
* chore: drop rate-limit stage from reliable channel send pipeline Collapses the outgoing pipeline to `segmentation -> sds -> encryption -> dispatch` by folding the encrypt-and-dispatch tail of `onReadyToSend` directly into `send()`. Removes the `RateLimitManager` field, its constructor param, the `ReadyToSendEvent` listener, and the `awaitingDispatch` accounting that only existed to bridge the event-bus hop between `send()` and `onReadyToSend`. The `rate_limit_manager.nim` module itself is untouched — it will be relocated to the messaging layer (co-located with RLN) in a follow-up commit, where per-epoch admission actually belongs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: relocate RateLimitManager to messaging layer Moves rate_limit_manager.nim from `logos_delivery/channels/` to `logos_delivery/messaging/rate_limit_manager/` — the correct owner now that admission sits alongside RLN in the messaging layer instead of being fanned out to per-channel event listeners. The API surface changes: - `enqueueToSend` + `ReadyToSendEvent` (broker-based fan-out to a ReliableChannel listener) is replaced by `admit(msg): Future[Result[ void, RateLimitError]]`. Callers now branch directly on the result instead of subscribing to an event. - `channelId`, `SdsChannelID` and the SDS import are dropped — the messaging layer has no notion of channels; SDS was a channels-layer concern that only survived on this type because of the old broker fan-out. - `brokerCtx` is dropped for the same reason. Epoch config (`epochPeriodSec`), the wall-clock `currentEpochStart`, `queue`, `dequeueReady`, and `resetEpoch` are preserved exactly as the original owner designed them — this refactor is intentionally scoped to the API surface, not the epoch mechanism. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: meter SendService transmissions through RateLimitManager Wires the relocated RateLimitManager into the messaging layer at the transmission stage rather than the API entry point: - MessagingClientConf gains a rateLimit: RateLimitConfig field (defaulting to DefaultEpochPeriodSec / DefaultMessagesPerEpoch), and MessagingClient.new hands the constructed manager to SendService. - SendService consults admit() before the first transmission of a task, both in send() and in the retry loop. Re-publishes of an already-propagated message (firstPropagatedTime set) skip admission: they resend the same bytes, which reuse the same RLN proof/nullifier and consume no fresh epoch slot. - An over-budget task parks in the task cache as NextRoundRetry; the service loop re-admits it as the epoch budget frees up. The skeleton admit() is a pass-through, so behaviour is unchanged today. Gating transmissions instead of MessagingClient.send keeps SDS repair rebroadcasts free of API-entry rejection (SDS decides that a repair is needed; the transmission scheduler decides when it fits the budget) while every wire transmission still draws from one node-wide budget -- which network-side RLN enforcement applies to repairs regardless of any local bypass. It is also where RLN proof attachment must happen, since proofs bind to the epoch current at transmission time. Adds tests/messaging/test_rate_limit_manager.nim covering the current disabled + enabled pass-through behaviour, wired into all_tests_waku.nim. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: make MessagingClientConf.rateLimit reachable, warn on dead channel knobs Addresses two review findings on the move PR: - `rateLimit` was a plain `RateLimitConfig`, but `merge` only copies `Opt` fields (`when oField is Opt`), so every override path (`LogosDeliveryConf.init`, JSON `messagingOverrides`) silently dropped it and the field was always taken from `base` — unsettable by any caller. Make it `Opt[RateLimitConfig]` like every other field; `MessagingClient.new` falls back to `DefaultRateLimitConfig` (new const, rate limiting disabled) when unset. Adds a merge test. - The channel-level knobs (`rateLimitEnabled` / `rateLimitEpochPeriodSec` / `rateLimitMessagesPerEpoch`) are still parsed but unread since rate limiting moved to the messaging client, so setting them was silently ignored. `ReliableChannelManager.new` now logs a deprecation warning when any is set. Full removal remains a follow-up API decision. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
2dbf9a3cb5 | fix(channels): default channels to unencrypted so messages flow (#4051) | ||
|
|
9827be5990 |
feat: logos_delivery_node app + messaging REST API with event observability (#4014)
* WIP logosdeliverynode app initial commit * WIP - extra cli option * WIP: messaging client REST endpoints * Add event poll for messaging rest with cache mechanism * Messaging rest tests * test: assert 404 via raw string client in messaging REST test presto's typed REST client raises RestDecodingError when it cannot decode a non-2xx text error body into the response type. Add a RestResponse[string] stub (messagingGetSendEventsByIdRawV1) and point the "already-polled id -> 404" assertion at it, matching the relay REST test pattern. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * remove customized cli args as confutils has no support for it * Introduce --entry-layer and re-introduce --mode flags into cli args, applied new driver into LogosDelivery + tests * Add messaging REST client test * Add docker image build of logosdeliverynode for CI builds * Fix tests * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Refactor Messaging REST API to better match Messaging Send and Receive APIs * chore: migrate messaging REST API to Opt[T] Follow-up to the rebase onto master's repo-wide Option[T] -> Opt[T] change (#4035). Converts the code this branch adds to the new convention: - messaging/rest_api/types.nim: MessagingJsonEnvelope fields to Opt[T], Opt.some/Opt.none, and json_serialization/pkg/results instead of json_serialization/std/options. - tests: WakuNodeConf.clusterId is now Opt[uint16]; DTO fields are Opt. `Option[ContentBody]` in the handlers is presto's own API and stays as-is. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
ce918b0819 |
chore: replace Option with Opt (#4035)
* change all usage of std.options.Option[T] to results.Opt[T] * fix broken apps and examples (to validate refactor) * removed all std/options code added for libp2p v2 migration * add broker Opt codec to persistency/backend_comm.nim * add a readValue overload for Opt[T] in tools/confutils/cli_args.nim * keep Option where required (Presto, confutils' config_file.nim) * Change generateRlnProof error handling * Fix imports |
||
|
|
1c83c32397 | feat(channels): add channelExists to reliable channels API (#4045) | ||
|
|
0a19473a3b | fix: don't apply reconnect backoff to discovered relay peers (#4029) | ||
|
|
77cb8a6c7a |
refresh merkle proof cache reactively on msg publish rejection (#4013)
* refresh merkle proof cache reactively on lightpush rejection
* Add tests for lightpush force refresh regenerates proof
* Fix linting
* Add nonce rollback for lightpush publish retry
* Fix nonce rollback race: make rollbackNonce CAS-conditional
Two bugs in the previous blind-decrement rollbackNonce():
1. Concurrent draw: another proof generated between attempt 1 and the retry
advances nextNonce, so decrementing steals a nonce already claimed by
another live message — nullifier collision, on-chain slashable.
2. Pre-attached proof: if the incoming message already carries a proof,
checkAndGenerateRLNProof short-circuits and draws no nonce, yet the retry
still called rollback, stomping on whatever nonce the preceding message
legitimately drew.
Fix: rollbackNonce now takes the specific nonce as a parameter and only
decrements when nextNonce == nonce + 1. checkAndGenerateRLNProof returns
(msg, messageId) so callers can thread the drawn id through to the retry
as reuseMessageId; generateRLNProof no longer performs an implicit rollback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Extend reactive Merkle proof refresh to REST relay and broker paths
With PathCheckMinInterval removed, a non-empty merkleProofCache is trusted
forever unless force=true is passed. Previously only the two lightpush retry
paths ever passed force=true; the REST relay publish handlers (static and
auto-sharding) and the broker proof provider had no rejection feedback loop,
so a sliding root window would permanently reject their messages until restart.
- REST relay handlers: run validateMessage first; if it returns
RlnValidatorErrorMsg, force-refresh the cached path and retry validation once
before publishing — mirroring the lightpush reactive pattern.
- Broker provider (rln.nim): decode the generated proof bytes, call
validateRoot on the embedded Merkle root, and force-refresh + regenerate
when the root is outside the acceptance window — since the broker has no
external rejection signal to react to.
Tests added for all three new paths, using the corrupted-cache technique
(all-zero merkleProofCache → garbage root → rejection → retry).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Gate lightpush Merkle refresh on RlnValidatorErrorMsg for 420 responses
420 INVALID_MESSAGE is returned for any validateMessage failure, not just
RLN ones (e.g. oversized messages), so a bare status-code check triggers
an unbounded on-chain fetchMerkleProofElements RPC per rejected message.
Match the legacy-lightpush path: require the error description to contain
RlnValidatorErrorMsg before refreshing; 504 OUT_OF_RLN_PROOF remains
unconditional as it is unambiguously RLN-specific.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix merge conflict residue: replace RlnResult with Result in proof.nim
RlnResult[T] was removed as a type alias in commit
|
||
|
|
f28cc7118c |
Fix FFI peerId pretty print (#4023)
* Add import fix to node_api too as that has a single stringify peerId spot. Thx @fcecin |
||
|
|
53c084dfdb |
chore: move conf types to api/conf (#4024)
* move MessagingClientConf to api/conf/messaging_conf * move ReliableChannelManagerConf to new api/conf/channels_conf * impl modules now import their conf module, not the reverse * logos_delivery_conf uses channels_conf, not reliable_channel_manager * cleanup import lists |
||
|
|
9f2a1c89ff |
Move api config modules to api/conf/ (#4022)
Move api config modules to api/conf/ |