* 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>
Logos Messaging Nim
Introduction
This repository implements a set of libp2p protocols aimed to bring private communications.
- Nim implementation of these specs.
- C library that exposes the implemented protocols.
- CLI application that allows you to run a logos-delivery node.
- Examples.
- Various tests of above.
For more details see the source code
How to Build & Run ( Linux, MacOS & WSL )
These instructions are generic. For more detailed instructions, see the source code above.
Recommended and tested toolchain versions (these are installed when you follow the build instructions below):
- Nim 2.2.4
- Nimble 0.22.3
Prerequisites
The standard developer tools, including a C compiler, GNU Make, Bash, and Git.
In some distributions (Fedora linux for example), you may need to install
whichutility separately. Nimbus build system is relying on it.
You'll also need an installation of Rust and its toolchain (specifically rustc and cargo).
The easiest way to install these, is using rustup:
Rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Wakunode
# The first `make` invocation will initialize the local dependency state.
make wakunode2
# Build with custom compilation flags. Do not use NIM_PARAMS unless you know what you are doing.
# Replace with your own flags
make wakunode2 NIMFLAGS="-d:chronicles_colors:none -d:disableMarchNative"
# Run with DNS bootstrapping
./build/wakunode2 --dns-discovery --dns-discovery-url=DNS_BOOTSTRAP_NODE_URL
# Run with the QUIC transport enabled
./build/wakunode2 --quic-support=true
# See available command line options
./build/wakunode2 --help
To join the network, you need to know the address of at least one bootstrap node. Please refer to the Waku README for more information.
For more on how to run wakunode2, refer to:
Issues
WSL
If you encounter difficulties building the project on WSL, consider placing the project within WSL's filesystem, avoiding the /mnt/ directory.
How to Build & Run ( Windows )
Windows Build Instructions
1. Install Required Tools
- Git Bash Terminal: Download and install from https://git-scm.com/download/win
- MSYS2:
a. Download installer from https://www.msys2.org
b. Install at "C:" (default location). Remove/rename the msys folder in case of previous installation. c. Use the mingw64 terminal from msys64 directory for package installation.
2. Install Dependencies
Open MSYS2 mingw64 terminal and run the following one-by-one :
pacman -Syu --noconfirm
pacman -S --noconfirm --needed mingw-w64-x86_64-toolchain
pacman -S --noconfirm --needed base-devel make cmake upx
pacman -S --noconfirm --needed mingw-w64-x86_64-rust
pacman -S --noconfirm --needed mingw-w64-x86_64-postgresql
pacman -S --noconfirm --needed mingw-w64-x86_64-gcc
pacman -S --noconfirm --needed mingw-w64-x86_64-gcc-libs
pacman -S --noconfirm --needed mingw-w64-x86_64-libwinpthread-git
pacman -S --noconfirm --needed mingw-w64-x86_64-zlib
pacman -S --noconfirm --needed mingw-w64-x86_64-openssl
pacman -S --noconfirm --needed mingw-w64-x86_64-python
3. Build Wakunode
- Open Git Bash as administrator
- clone nwaku and cd nwaku
- Execute:
./scripts/build_windows.sh
4. Troubleshooting
If wakunode2.exe isn't generated:
- Missing Dependencies: Verify with:
which make cmake gcc g++ rustc cargo python3 upx
If missing, revisit Step 2 or ensure MSYS2 is atC:\ - Installation Conflicts: Remove existing MinGW/MSYS2/Git Bash installations and perform fresh install
Developing
Nim Runtime
This repository is bundled with a Nim runtime that includes the necessary dependencies for the project.
Before you can utilize the runtime you'll need to build the project, as detailed in a previous section.
This will generate a nimbledeps/pkgs2 directory containing various dependencies.
If everything went well, you should see your prompt suffixed with [SuccessX]. Now you can run nim commands as usual.
Test Suite
# Run all the Waku tests
make test
# Run a specific test file
make test <test_file_path>
# e.g. : make test tests/wakunode2/test_all.nim
# Run a specific test name from a specific test file
make test <test_file_path> <test_name>
# e.g. : make test tests/wakunode2/test_all.nim "node setup is successful with default configuration"
Building single test files
During development it is helpful to build and run a single test file. To support this make has a specific target:
targets:
build/<relative path to your test file.nim>test/<relative path to your test file.nim>
Binary will be created as <path to your test file.nim>.bin under the build directory .
# Build and run your test file separately
make test/tests/common/test_enr_builder.nim
Testing against js-waku
Refer to logos-delivery-js repo for instructions.
Formatting
Nim files are expected to be formatted using the nph version present in vendor/nph.
You can easily format file with the make nph/<relative path to nim> file command.
For example:
make nph/waku/waku_core.nim
A convenient git hook is provided to automatically format file at commit time. Run the following command to install it:
make install-nph
Examples
Examples can be found in the examples folder. This includes a fully featured chat example.
Tools
Different tools and their corresponding how-to guides can be found in the tools folder.
Bugs, Questions & Features
For an inquiry, or if you would like to propose new features, feel free to open a general issue.
For bug reports, please tag your issue with the bug label.
If you believe the reported issue requires critical attention, please use the critical label to assist with triaging.
To get help, or participate in the conversation, join the Logos Discord server.