From 319c0c68fe608d16fc8cd8106c9ccc3d30b5ae4e Mon Sep 17 00:00:00 2001 From: Ivan FB <128452529+Ivansete-status@users.noreply.github.com> Date: Fri, 19 Jun 2026 15:29:33 +0200 Subject: [PATCH 01/33] =?UTF-8?q?chore:=20simplify=20release=20process=20?= =?UTF-8?q?=E2=80=94=20merge=20deploy=20into=20prepare=5Frelease=20(#3964)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Igor Sirotin --- .github/ISSUE_TEMPLATE/deploy_release.md | 55 ----------- .github/ISSUE_TEMPLATE/prepare_release.md | 106 +++++++++++++--------- 2 files changed, 62 insertions(+), 99 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/deploy_release.md diff --git a/.github/ISSUE_TEMPLATE/deploy_release.md b/.github/ISSUE_TEMPLATE/deploy_release.md deleted file mode 100644 index 0c584cc81..000000000 --- a/.github/ISSUE_TEMPLATE/deploy_release.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -name: Deploy Release -about: Execute tasks for deploying a new version in a fleet -title: 'Deploy release vX.X.X in logos.test and/or status.prod fleet' -labels: deploy-release -assignees: '' - ---- - - - -### Link to the Release PR - - - -### Items to complete, in order - - - -- [ ] Receive sign-off from DST. - - [ ] Inform DST team about what are the expectations for this release. For example, if we expect higher, same or lower bandwidth consumption. Or a new protocol appears, etc. - - [ ] Ask DST to add a comment approving this deployment and add a link to the analysis report. - -- [ ] Deploy to logos.test - - [ ] Coordinate with Infra Team about possible changes in CI behavior - - [ ] Update logos.test with the corresponding deployment job in [Jenkins](https://ci.infra.status.im/) (ask the Infra Team if you don't have access or can't find the job). - -- [ ] Deploy to status.prod - - [ ] Coordinate with Infra Team about possible changes in CI behavior - - [ ] Ask Status admin to add a comment approving that this deployment to happen now. - - [ ] Update status.prod with [this deployment job](https://ci.infra.status.im/job/nim-waku/job/deploy-status-prod/). - -- [ ] Update infra config - - [ ] Submit PRs into infra repos to adjust deprecated or changed arguments (review CHANGELOG.md for that release). And confirm the fleet can run after that. This requires coordination with infra team. - -### Reference Links - -- [Release process](https://github.com/logos-messaging/logos-delivery/blob/master/docs/contributors/release-process.md) -- [Release notes](https://github.com/logos-messaging/logos-delivery/blob/master/CHANGELOG.md) -- [Infra-role-logos-node](https://github.com/status-im/infra-role-logos-node) -- [Infra-logos](https://github.com/status-im/infra-logos) -- [Infra-Status](https://github.com/status-im/infra-status) -- [Jenkins](https://ci.infra.status.im/) -- [Fleets](https://fleets.logos.co/) -- [Harbor](https://harbor.status.im/harbor/projects/32/repositories/logos-node/artifacts-tab) -- [Kibana](https://kibana.infra.status.im/app/) diff --git a/.github/ISSUE_TEMPLATE/prepare_release.md b/.github/ISSUE_TEMPLATE/prepare_release.md index 1ad9c95b3..86ce6a099 100644 --- a/.github/ISSUE_TEMPLATE/prepare_release.md +++ b/.github/ISSUE_TEMPLATE/prepare_release.md @@ -1,6 +1,6 @@ --- name: Prepare Release -about: Execute tasks for the creation and publishing of a new full release +about: Execute tasks for the creation, validation, deployment and publishing of a new full release title: 'Prepare release 0.0.0' labels: full-release assignees: '' @@ -8,64 +8,81 @@ assignees: '' --- -### Items to complete +### Items to complete, in order -All items below are to be completed by the owner of the given release. +#### Initial validation + - [ ] Confirm waku.test has been running with new commits on each merge into master (see Grafana boards.) + - [ ] Search [Kibana logs](https://kibana.infra.status.im/app/discover) since the last release for crashes or errors in waku.test that we didn't detect in CI. + - Most relevant search queries: `(fleet: "waku.test" AND message: "SIGSEGV")`, `(fleet: "waku.test" AND message: "exception")`, `(fleet: "waku.test" AND message: "error")`. + - Detect unexpected reboots. + - Document any crashes or errors found and fix them before proceeding. -- [ ] Create release branch with major and minor only ( e.g. release/v0.X ) if it doesn't exist. -- [ ] Update the `version` field in `logos_delivery.nimble` to match the release version (e.g. `version = "0.X.0"`) **and merge it before assigning any tag** - the `release-assets` workflow gates artifact build/upload. +### Create the release candidate + +- [ ] Create release branch with major and minor only ( e.g. release/v0.X ). +- [ ] In release branch, update the `version` field in `logos_delivery.nimble` to match the release version (e.g. `version = "0.X.0"`). - [ ] Assign release candidate tag to the release branch HEAD (e.g. `v0.X.0-rc.0`, `v0.X.0-rc.1`, ... `v0.X.0-rc.N`). -- [ ] Generate and edit release notes in CHANGELOG.md. -- [ ] **Validation of release candidate** +#### Validate the release candidate - - [ ] **Automated testing** - - [ ] Ensure all the unit tests (specifically logos-messaging-js tests) are green against the release candidate. + - [ ] 1. Essential test + - [ ] Ensure all unit tests are green + - [ ] Get interop tests results from QA - - [ ] **QA testing** - - [ ] Ask QA to run their available tests against the release candidate. - - - [ ] **Logos fleet testing** - - [ ] Deploy the release candidate to `logos.dev` fleet. + - [ ] 2. Fleet validation (pre-requisite: 1. Can be done in parallel with 3.) + - [ ] Deploy the release candidate to the waku.test fleet. - Start the deployment job in [Jenkins](https://ci.infra.status.im/) and wait for it to finish (Jenkins access required; ask the infra team if you don't have it). - - After completion, disable fleet so that daily CI does not override your release candidate. - - Verify at https://fleets.logos.co/ that the fleet is locked to the release candidate image. - Confirm the container image exists on [Harbor](https://harbor.status.im/harbor/projects/32/repositories/logos-node/artifacts-tab). - - [ ] Search [Kibana logs](https://kibana.infra.status.im/app/discover) from the previous month (since the last release was deployed) for possible crashes or errors in `logos.dev`. - - Set time range to "Last 30 days" (or since last release). - - Most relevant search query: `(fleet: "logos.dev" AND message: "SIGSEGV")`, `(fleet: "logos.dev" AND message: "exception")`, `(fleet: "logos.dev" AND message: "error")`. - - Document any crashes or errors found. - - [ ] Ask QA to perform tests against `logos.dev`, if any. Then, after that, review Kibana for possible issues or unexpected restart. - - [ ] Enable the `logos.dev` fleet again to resume auto-deployment of the latest `master` commit. + - After completion, disable the fleet so that daily CI does not override your release candidate. + - Verify at https://fleets.logos.co/ that the fleet is locked to the release candidate image. + + - [ ] Ask QA to run tests against waku.test and attach a screenshot as evidence. + - [ ] Re-enable the waku.test fleet to resume auto-deployment of the latest master commit. + - - [ ] **Status testing** - - [ ] Get QA approval to deploy a new version in `status.staging`. - - [ ] Deploy release candidate to `status.staging`. - - [ ] Perform [sanity check](https://www.notion.so/How-to-test-Nwaku-on-Status-12c6e4b9bf06420ca868bd199129b425) and log results as comments in this issue. - - [ ] Connect 2 instances to `status.staging` fleet, one in relay mode, the other one in light client mode. - - 1:1 Chats with each other. - - Send and receive messages in a community. - - Close one instance, send messages with second instance, reopen first instance and confirm messages sent while offline are retrieved from store. - - [ ] Perform checks based on _end user impact_ - - [ ] Inform other (Waku and Status) CCs to point their instances to `status.staging` for a few days. Ping Status colleagues on their Discord server or in the [Status community](https://status.app/c/G3kAAMSQtb05kog3aGbr3kiaxN4tF5xy4BAGEkkLwILk2z3GcoYlm5hSJXGn7J3laft-tnTwDWmYJ18dP_3bgX96dqr_8E3qKAvxDf3NrrCMUBp4R9EYkQez9XSM4486mXoC3mIln2zc-TNdvjdfL9eHVZ-mGgs=#zQ3shZeEJqTC1xhGUjxuS4rtHSrhJ8vUYp64v6qWkLpvdy9L9) (this is not a blocking point.) - - [ ] Ask QA to perform sanity checks (as described above) and checks based on _end user impact_; specify the version being tested - - [ ] Ask QA or infra to run the automated Status e2e tests against `status.staging` - - [ ] Get other CCs' sign-off: they should comment on this PR, e.g., "Used the app for a week, no problem." If problems are reported, resolve them and create a new RC. + - [ ] 3. DST sign-off (pre-requisite: 1) + - [ ] Inform the DST team about the expectations for this release. For example, if we expect higher, same or lower bandwidth consumption, or a new protocol appears, etc. + - [ ] Ask DST to add a comment approving this release and add a summary analysis report. -- [ ] **Proceed with release** + - [ ] 4. Status testing (pre-requisite: 1, 2, 3) + - [ ] Bump logos-delivery dependency in [logos-delivery-go-bindings](https://github.com/logos-messaging/logos-delivery-go-bindings) and make sure all tests work. + - [ ] Submit a PR on [status-go](https://github.com/status-im/status-go/blob/1f9061064587e1167e32d965d5a6f2b745324d5e/tests-functional/docker-compose.waku.yml#L3) bumping logos-delivery to this release candidate. + - [ ] Submit a PR on [status-app](https://github.com/status-im/status-app/blob/3639e28374ca3c2158ac2dac6af35dbce439b10a/docker-compose.waku.yml#L3) bumping logos-delivery to this release candidate. + - [ ] Both PRs must be merged before the release is considered created. + - [ ] Deploy the release candidate in status.staging. That may alert about needed infra changes. - - [ ] Assign a final release tag (`v0.X.0`) to the same commit that contains the validated release-candidate tag (e.g. `git tag -as v0.X.0 -m "final release."`). + - [ ] 5. Submit a PR against release/v0.X with CHANGELOG.md updates (pre-requisite: all previous.) + +#### Complete the release + - [ ] Assign a final release tag (`v0.X.0`) to the same commit that contains the latest release-candidate tag (e.g. `git tag -as v0.X.0 -m "final release."`). - [ ] Update [logos-delivery-compose](https://github.com/logos-messaging/logos-delivery-compose) and [logos-delivery-simulator](https://github.com/logos-messaging/logos-delivery-simulator) according to the new release. - - [ ] Bump logos-delivery dependency in [logos-delivery-rust-bindings](https://github.com/logos-messaging/logos-delivery-rust-bindings) and make sure all examples and tests work. - - [ ] Bump logos-delivery dependency in [logos-delivery-go-bindings](https://github.com/logos-messaging/logos-delivery-go-bindings) and make sure all tests work. - [ ] Create GitHub release (https://github.com/logos-messaging/logos-delivery/releases). - - [ ] Submit a PR to merge the release branch back to `master`. Make sure you use the option "Merge pull request (Create a merge commit)" to perform the merge. Ping repo admin if this option is not available. - - [ ] Create a deployment issue with the recently created release. + - [ ] Merge release branch into master + - [ ] Create a temporary branch from the release branch. This is needed in case we need to rebase that branch without impacting the release branch. Notice that the release branch should live forever. + - [ ] Submit a PR from temporary branch to master. Make sure you use the option "Merge pull request (Create a merge commit)" to perform the merge. Ping repo admin if this option is not available. + - [ ] Adjust the status-go and status-app `docker-compose.waku.yml` files (the ones bumped above) so they use the final tag instead of the release candidate one. + +#### Deployment + - [ ] Deploy to waku.sandbox + - [ ] Confirm the release candidate ran properly in waku.test with the current infra config. + - [ ] If it did NOT (e.g. CLI params changed): submit PRs to the infra repos adjusting the deprecated or changed arguments (review CHANGELOG.md for that release), add links to them, and wait until they are merged. Infra changes are deployed by the infra team, so this requires coordination with them. + - [ ] If it ran fine with the current config, no infra PR is needed. + - [ ] Deploy: + - [ ] Coordinate with the Infra Team about the deployment timing. + - [ ] Update waku.sandbox with [this deployment job](https://ci.infra.status.im/job/nim-waku/job/deploy-waku-sandbox/). + - [ ] Confirm the fleet runs properly after the deployment. + - [ ] Deploy to status.prod + - [ ] Confirm the release candidate ran properly in status.staging with the current infra config. + - [ ] If it did NOT (e.g. CLI params changed): submit PRs to the infra repos adjusting the deprecated or changed arguments (review CHANGELOG.md for that release), add links to them, and wait until they are merged. Infra changes are deployed by the infra team, so this requires coordination with them. + - [ ] If it ran fine with the current config, no infra PR is needed. + - [ ] Deploy: + - [ ] Ask the Status admin to add a comment approving that this deployment happen now. + - [ ] Update status.prod with [this deployment job](https://ci.infra.status.im/job/nim-waku/job/deploy-status-prod/). + - [ ] Confirm the fleet runs properly after the deployment. + ### Links @@ -73,6 +90,7 @@ All items below are to be completed by the owner of the given release. - [Release notes](https://github.com/logos-messaging/logos-delivery/blob/master/CHANGELOG.md) - [Fleet ownership](https://www.notion.so/Fleet-Ownership-7532aad8896d46599abac3c274189741?pvs=4#d2d2f0fe4b3c429fbd860a1d64f89a64) - [Infra-logos](https://github.com/status-im/infra-logos) +- [Infra-Status](https://github.com/status-im/infra-status) - [Jenkins](https://ci.infra.status.im/) - [Fleets](https://fleets.logos.co/) - [Harbor](https://harbor.status.im/harbor/projects/32/repositories/logos-node/artifacts-tab) From 2fe7e1c3738797ed174940e879835f74b3b6bec3 Mon Sep 17 00:00:00 2001 From: NagyZoltanPeter <113987313+NagyZoltanPeter@users.noreply.github.com> Date: Mon, 22 Jun 2026 10:01:54 +0200 Subject: [PATCH 02/33] Workaround MacOS build issue caused by between new bearssl 0.2.9 and crhonos 2.2.4 (#3969) --- config.nims | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/config.nims b/config.nims index efff8a552..aaba3854e 100644 --- a/config.nims +++ b/config.nims @@ -63,6 +63,27 @@ else: switch("passC", "-mno-avx512f") switch("passL", "-mno-avx512f") +when defined(macosx): + # TEMPORARY (added 2026-06-19) — remove once nim-chronos is fixed/bumped upstream. + # + # macOS / Apple Clang 16+ promotes -Wincompatible-function-pointer-types to a + # DEFAULT error (not gated behind -Werror). It fires on a dependency↔dependency + # call in nim-chronos — none of our code is involved: + # chronos/streams/tlsstream.nim:718 ctx.setdest(itemAppend, ...) + # chronos declares the PEM callback `itemAppend(ctx, pbytes: pointer, nbytes)` + # while bearssl's `br_pem_decoder_setdest` expects `pbytes: const void*`. The + # callback only READS the buffer (copyMem from pbytes), so the missing `const` + # is benign — we demote the diagnostic back to a warning instead of failing. + # + # Why it appeared now (not a change on our side): the `const` was introduced in + # nim-bearssl v0.2.9, published 2026-06-19 11:22 UTC. CI does not honor the + # locked bearssl (nimble.lock pins 0.2.8) and fresh-resolves to latest, so any + # run after 11:22 UTC picks up 0.2.9 and hits this. Earlier-running PRs that + # resolved 0.2.8 are green only by timing; re-running them now reproduces it. + # + # Proper fix: chronos should declare `itemAppend`'s `pbytes` as `const pointer`. + switch("passC", "-Wno-error=incompatible-function-pointer-types") + --threads: on --opt: From 1a3b3204fa34a55d2ba790232017a365e7181ded Mon Sep 17 00:00:00 2001 From: Ivan FB <128452529+Ivansete-status@users.noreply.github.com> Date: Tue, 23 Jun 2026 01:20:09 +0200 Subject: [PATCH 03/33] feat: add LogosDelivery orchestrator as project entry point for FFI (#3970) --- .github/workflows/ci.yml | 9 +- examples/api_example/api_example.nim | 14 +- library/declare_lib.nim | 6 +- library/kernel_api/debug_node_api.nim | 20 +-- library/kernel_api/discovery_api.nim | 18 +- library/kernel_api/node_lifecycle_api.nim | 22 +-- library/kernel_api/peer_manager_api.nim | 43 +++-- library/kernel_api/ping_api.nim | 9 +- library/kernel_api/protocols/filter_api.nim | 26 +-- .../kernel_api/protocols/lightpush_api.nim | 8 +- library/kernel_api/protocols/relay_api.nim | 42 ++--- library/kernel_api/protocols/store_api.nim | 4 +- library/liblogosdelivery.nim | 5 +- library/logos_delivery_api/debug_api.nim | 10 +- library/logos_delivery_api/messaging_api.nim | 14 +- library/logos_delivery_api/node_api.nim | 44 ++--- logos_delivery.nim | 21 ++- .../channels/reliable_channel_manager.nim | 38 ++-- logos_delivery/logos_delivery.nim | 104 +++++++++++ logos_delivery/messaging/messaging_client.nim | 23 ++- logos_delivery/waku/api/api.nim | 8 - logos_delivery/waku/factory/waku.nim | 49 ------ tests/api/test_api_health.nim | 76 ++++---- tests/api/test_api_receive.nim | 19 +- tests/api/test_api_send.nim | 68 +++----- tests/api/test_api_subscription.nim | 163 +++++++++--------- .../test_reliable_channel_send_receive.nim | 96 ++++------- tests/node/test_wakunode_health_monitor.nim | 10 +- 28 files changed, 500 insertions(+), 469 deletions(-) create mode 100644 logos_delivery/logos_delivery.nim diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 278f37a05..a407e37f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,7 +101,14 @@ jobs: touch nimbledeps/.nimble-setup - name: Build binaries - run: make V=1 all + # -j1: `all` builds wakunode2 and liblogosdelivery, each via `nimble + # `. Under -j2 the two nimble invocations re-resolve git deps + # concurrently and clobber each other in the shared ~/.nimble/pkgcache + # (e.g. "destination already exists", "could not lock config file"), + # failing the build. Serializing the targets removes the race; Nim's + # own --parallelBuild still parallelizes compilation. Mirrors the test + # job, which already forces -j1. + run: make V=1 -j1 all build-windows: needs: changes diff --git a/examples/api_example/api_example.nim b/examples/api_example/api_example.nim index 9e96858db..8c8759191 100644 --- a/examples/api_example/api_example.nim +++ b/examples/api_example/api_example.nim @@ -7,7 +7,7 @@ type CliArgs = object defaultValue: "", desc: "ETH RPC Endpoint, if passed, RLN is enabled" .}: string -proc periodicSender(w: Waku): Future[void] {.async.} = +proc periodicSender(logos: LogosDelivery): Future[void] {.async.} = let sentListener = MessageSentEvent.listen( proc(event: MessageSentEvent) {.async: (raises: []).} = echo "Message sent with request ID: ", @@ -45,7 +45,7 @@ proc periodicSender(w: Waku): Future[void] {.async.} = payload = "Hello Waku! Message number: " & $counter, ) - let sendRequestId = (await w.send(envelope)).valueOr: + let sendRequestId = (await logos.messagingClient.send(envelope)).valueOr: echo "Failed to send message: ", error quit(QuitFailure) @@ -75,16 +75,12 @@ when isMainModule: conf.preset = "twn" conf.ethClientUrls = @[EthRpcUrl(args.ethRpcEndpoint)] - # Create the node using the library API's createNode function - let node = (waitFor createNode(conf)).valueOr: + # Create the full Logos Messaging stack (Waku + messaging + channels) + let node = (waitFor LogosDelivery.new(conf)).valueOr: echo "Failed to create node: ", error quit(QuitFailure) - echo("Waku node created successfully!") - - node.mountMessagingClient().isOkOr: - echo "Failed to mount messaging: ", error - quit(QuitFailure) + echo("Logos Messaging node created successfully!") # Start the node (waitFor node.start()).isOkOr: diff --git a/library/declare_lib.nim b/library/declare_lib.nim index 77e992eea..eaf8f6315 100644 --- a/library/declare_lib.nim +++ b/library/declare_lib.nim @@ -1,6 +1,6 @@ import ffi import std/locks -import logos_delivery/waku/factory/waku +import logos_delivery declareLibrary("logosdelivery") @@ -8,7 +8,7 @@ var eventCallbackLock: Lock initLock(eventCallbackLock) template requireInitializedNode*( - ctx: ptr FFIContext[Waku], opName: string, onError: untyped + ctx: ptr FFIContext[LogosDelivery], opName: string, onError: untyped ) = if isNil(ctx): let errMsg {.inject.} = opName & " failed: invalid context" @@ -18,7 +18,7 @@ template requireInitializedNode*( onError proc logosdelivery_set_event_callback( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.dynlib, exportc, cdecl.} = if isNil(ctx): echo "error: invalid context in logosdelivery_set_event_callback" diff --git a/library/kernel_api/debug_node_api.nim b/library/kernel_api/debug_node_api.nim index bbce84939..b1229d660 100644 --- a/library/kernel_api/debug_node_api.nim +++ b/library/kernel_api/debug_node_api.nim @@ -22,32 +22,32 @@ proc getMetrics(): string = return defaultRegistry.toText() ## defaultRegistry is {.global.} in metrics module proc waku_version( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = return ok(WakuNodeVersionString) proc waku_listen_addresses( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = ## returns a comma-separated string of the listen addresses - return ok(ctx.myLib[].node.getMultiaddresses().join(",")) + return ok(ctx.myLib[].waku.node.getMultiaddresses().join(",")) proc waku_get_my_enr( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - return ok(ctx.myLib[].node.enr.toURI()) + return ok(ctx.myLib[].waku.node.enr.toURI()) proc waku_get_my_peerid( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - return ok($ctx.myLib[].node.peerId()) + return ok($ctx.myLib[].waku.node.peerId()) proc waku_get_metrics( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = return ok(getMetrics()) proc waku_is_online( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - return ok($ctx.myLib[].healthMonitor.onlineMonitor.amIOnline()) + return ok($ctx.myLib[].waku.healthMonitor.onlineMonitor.amIOnline()) diff --git a/library/kernel_api/discovery_api.nim b/library/kernel_api/discovery_api.nim index 9dec4575a..7ae7d1dea 100644 --- a/library/kernel_api/discovery_api.nim +++ b/library/kernel_api/discovery_api.nim @@ -39,7 +39,7 @@ proc performPeerExchangeRequestTo*( return ok(numPeersRecv) proc waku_discv5_update_bootnodes( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, bootnodes: cstring, @@ -47,14 +47,14 @@ proc waku_discv5_update_bootnodes( ## Updates the bootnode list used for discovering new peers via DiscoveryV5 ## bootnodes - JSON array containing the bootnode ENRs i.e. `["enr:...", "enr:..."]` - updateDiscv5BootstrapNodes($bootnodes, ctx.myLib[]).isOkOr: + updateDiscv5BootstrapNodes($bootnodes, ctx.myLib[].waku).isOkOr: error "UPDATE_DISCV5_BOOTSTRAP_NODES failed", error = error return err($error) return ok("discovery request processed correctly") proc waku_dns_discovery( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, enrTreeUrl: cstring, @@ -69,27 +69,27 @@ proc waku_dns_discovery( return ok(nodes.join(",")) proc waku_start_discv5( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - (await ctx.myLib[].wakuDiscv5.start()).isOkOr: + (await ctx.myLib[].waku.wakuDiscv5.start()).isOkOr: error "START_DISCV5 failed", error = error return err("error starting discv5: " & $error) return ok("discv5 started correctly") proc waku_stop_discv5( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - await ctx.myLib[].wakuDiscv5.stop() + await ctx.myLib[].waku.wakuDiscv5.stop() return ok("discv5 stopped correctly") proc waku_peer_exchange_request( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, numPeers: uint64, ) {.ffi.} = - let numValidPeers = (await performPeerExchangeRequestTo(numPeers, ctx.myLib[])).valueOr: + let numValidPeers = (await performPeerExchangeRequestTo(numPeers, ctx.myLib[].waku)).valueOr: error "waku_peer_exchange_request failed", error = error return err("failed peer exchange: " & $error) diff --git a/library/kernel_api/node_lifecycle_api.nim b/library/kernel_api/node_lifecycle_api.nim index 8ee735f52..279480031 100644 --- a/library/kernel_api/node_lifecycle_api.nim +++ b/library/kernel_api/node_lifecycle_api.nim @@ -13,7 +13,7 @@ import proc createWaku( configJson: cstring, appCallbacks: AppCallbacks = nil -): Future[Result[Waku, string]] {.async.} = +): Future[Result[LogosDelivery, string]] {.async.} = var conf = defaultWakuNodeConf().valueOr: return err("Failed creating node: " & error) @@ -47,19 +47,15 @@ proc createWaku( appCallbacks.relayHandler = nil appCallbacks.topicHealthChangeHandler = nil - # TODO: Convert `confJson` directly to `WakuConf` - var wakuConf = conf.toWakuConf().valueOr: - return err("Configuration error: " & $error) + conf.rest = false ## libwaku never runs the REST server - wakuConf.restServerConf = none(RestServerConf) ## don't want REST in libwaku + let logosRes = (await LogosDelivery.new(conf, appCallbacks)).valueOr: + error "LogosDelivery initialization failed", error = error + return err("Failed setting up LogosDelivery: " & $error) - let wakuRes = (await Waku.new(wakuConf, appCallbacks)).valueOr: - error "waku initialization failed", error = error - return err("Failed setting up Waku: " & $error) + return ok(logosRes) - return ok(wakuRes) - -registerReqFFI(CreateNodeWithCallbacksRequest, ctx: ptr FFIContext[Waku]): +registerReqFFI(CreateNodeWithCallbacksRequest, ctx: ptr FFIContext[LogosDelivery]): proc( configJson: cstring, appCallbacks: AppCallbacks ): Future[Result[string, string]] {.async.} = @@ -70,7 +66,7 @@ registerReqFFI(CreateNodeWithCallbacksRequest, ctx: ptr FFIContext[Waku]): return ok("") proc waku_start( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = (await ctx.myLib[].start()).isOkOr: error "START_NODE failed", error = error @@ -78,7 +74,7 @@ proc waku_start( return ok("") proc waku_stop( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = (await ctx.myLib[].stop()).isOkOr: error "STOP_NODE failed", error = error diff --git a/library/kernel_api/peer_manager_api.nim b/library/kernel_api/peer_manager_api.nim index 775e58cae..38c15fc3d 100644 --- a/library/kernel_api/peer_manager_api.nim +++ b/library/kernel_api/peer_manager_api.nim @@ -12,41 +12,46 @@ type PeerInfo = object addresses: seq[string] proc waku_get_peerids_from_peerstore( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = ## returns a comma-separated string of peerIDs - let peerIDs = - ctx.myLib[].node.peerManager.switch.peerStore.peers().mapIt($it.peerId).join(",") + let peerIDs = ctx.myLib[].waku.node.peerManager.switch.peerStore + .peers() + .mapIt($it.peerId) + .join(",") return ok(peerIDs) proc waku_connect( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, peerMultiAddr: cstring, timeoutMs: cuint, ) {.ffi.} = let peers = ($peerMultiAddr).split(",").mapIt(strip(it)) - await ctx.myLib[].node.connectToNodes(peers, source = "static") + await ctx.myLib[].waku.node.connectToNodes(peers, source = "static") return ok("") proc waku_disconnect_peer_by_id( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer, peerId: cstring + ctx: ptr FFIContext[LogosDelivery], + callback: FFICallBack, + userData: pointer, + peerId: cstring, ) {.ffi.} = let pId = PeerId.init($peerId).valueOr: error "DISCONNECT_PEER_BY_ID failed", error = $error return err($error) - await ctx.myLib[].node.peerManager.disconnectNode(pId) + await ctx.myLib[].waku.node.peerManager.disconnectNode(pId) return ok("") proc waku_disconnect_all_peers( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - await ctx.myLib[].node.peerManager.disconnectAllPeers() + await ctx.myLib[].waku.node.peerManager.disconnectAllPeers() return ok("") proc waku_dial_peer( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, peerMultiAddr: cstring, @@ -56,7 +61,7 @@ proc waku_dial_peer( let remotePeerInfo = parsePeerInfo($peerMultiAddr).valueOr: error "DIAL_PEER failed", error = $error return err($error) - let conn = await ctx.myLib[].node.peerManager.dialPeer(remotePeerInfo, $protocol) + let conn = await ctx.myLib[].waku.node.peerManager.dialPeer(remotePeerInfo, $protocol) if conn.isNone(): let msg = "failed dialing peer" error "DIAL_PEER failed", error = msg, peerId = $remotePeerInfo.peerId @@ -64,7 +69,7 @@ proc waku_dial_peer( return ok("") proc waku_dial_peer_by_id( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, peerId: cstring, @@ -74,7 +79,7 @@ proc waku_dial_peer_by_id( let pId = PeerId.init($peerId).valueOr: error "DIAL_PEER_BY_ID failed", error = $error return err($error) - let conn = await ctx.myLib[].node.peerManager.dialPeer(pId, $protocol) + let conn = await ctx.myLib[].waku.node.peerManager.dialPeer(pId, $protocol) if conn.isNone(): let msg = "failed dialing peer" error "DIAL_PEER_BY_ID failed", error = msg, peerId = $peerId @@ -83,12 +88,12 @@ proc waku_dial_peer_by_id( return ok("") proc waku_get_connected_peers_info( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = ## returns a JSON string mapping peerIDs to objects with protocols and addresses var peersMap = initTable[string, PeerInfo]() - let peers = ctx.myLib[].node.peerManager.switch.peerStore.peers().filterIt( + let peers = ctx.myLib[].waku.node.peerManager.switch.peerStore.peers().filterIt( it.connectedness == Connected ) @@ -104,23 +109,23 @@ proc waku_get_connected_peers_info( return ok(jsonStr) proc waku_get_connected_peers( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = ## returns a comma-separated string of peerIDs let - (inPeerIds, outPeerIds) = ctx.myLib[].node.peerManager.connectedPeers() + (inPeerIds, outPeerIds) = ctx.myLib[].waku.node.peerManager.connectedPeers() connectedPeerids = concat(inPeerIds, outPeerIds) return ok(connectedPeerids.mapIt($it).join(",")) proc waku_get_peerids_by_protocol( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, protocol: cstring, ) {.ffi.} = ## returns a comma-separated string of peerIDs that mount the given protocol - let connectedPeers = ctx.myLib[].node.peerManager.switch.peerStore + let connectedPeers = ctx.myLib[].waku.node.peerManager.switch.peerStore .peers($protocol) .filterIt(it.connectedness == Connected) .mapIt($it.peerId) diff --git a/library/kernel_api/ping_api.nim b/library/kernel_api/ping_api.nim index da830bc5d..8313004d3 100644 --- a/library/kernel_api/ping_api.nim +++ b/library/kernel_api/ping_api.nim @@ -6,7 +6,7 @@ import library/declare_lib proc waku_ping_peer( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, peerAddr: cstring, @@ -18,12 +18,13 @@ proc waku_ping_peer( let timeout = chronos.milliseconds(timeoutMs) proc ping(): Future[Result[Duration, string]] {.async, gcsafe.} = try: - let conn = - await ctx.myLib[].node.switch.dial(peerInfo.peerId, peerInfo.addrs, PingCodec) + let conn = await ctx.myLib[].waku.node.switch.dial( + peerInfo.peerId, peerInfo.addrs, PingCodec + ) defer: await conn.close() - let pingRTT = await ctx.myLib[].node.libp2pPing.ping(conn) + let pingRTT = await ctx.myLib[].waku.node.libp2pPing.ping(conn) if pingRTT == 0.nanos: return err("could not ping peer: rtt-0") return ok(pingRTT) diff --git a/library/kernel_api/protocols/filter_api.nim b/library/kernel_api/protocols/filter_api.nim index 5e2628953..fb1c905ca 100644 --- a/library/kernel_api/protocols/filter_api.nim +++ b/library/kernel_api/protocols/filter_api.nim @@ -25,7 +25,7 @@ proc checkFilterClientMounted(waku: Waku): Result[string, string] = return ok("") proc waku_filter_subscribe( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, pubSubTopic: cstring, @@ -36,18 +36,18 @@ proc waku_filter_subscribe( callEventCallback(ctx, "onReceivedMessage"): $JsonMessageEvent.new(pubsubTopic, msg) - checkFilterClientMounted(ctx.myLib[]).isOkOr: + checkFilterClientMounted(ctx.myLib[].waku).isOkOr: return err($error) var filterPushEventCallback = FilterPushHandler(onReceivedMessage(ctx)) - ctx.myLib[].node.wakuFilterClient.registerPushHandler(filterPushEventCallback) + ctx.myLib[].waku.node.wakuFilterClient.registerPushHandler(filterPushEventCallback) - let peer = ctx.myLib[].node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: + let peer = ctx.myLib[].waku.node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: let errorMsg = "could not find peer with WakuFilterSubscribeCodec when subscribing" error "fail filter subscribe", error = errorMsg return err(errorMsg) - let subFut = ctx.myLib[].node.filterSubscribe( + let subFut = ctx.myLib[].waku.node.filterSubscribe( some(PubsubTopic($pubsubTopic)), ($contentTopics).split(",").mapIt(ContentTopic(it)), peer, @@ -61,22 +61,22 @@ proc waku_filter_subscribe( return ok("") proc waku_filter_unsubscribe( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, pubSubTopic: cstring, contentTopics: cstring, ) {.ffi.} = - checkFilterClientMounted(ctx.myLib[]).isOkOr: + checkFilterClientMounted(ctx.myLib[].waku).isOkOr: return err($error) - let peer = ctx.myLib[].node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: + let peer = ctx.myLib[].waku.node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: let errorMsg = "could not find peer with WakuFilterSubscribeCodec when unsubscribing" error "fail filter process", error = errorMsg return err(errorMsg) - let subFut = ctx.myLib[].node.filterUnsubscribe( + let subFut = ctx.myLib[].waku.node.filterUnsubscribe( some(PubsubTopic($pubsubTopic)), ($contentTopics).split(",").mapIt(ContentTopic(it)), peer, @@ -88,18 +88,18 @@ proc waku_filter_unsubscribe( return ok("") proc waku_filter_unsubscribe_all( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - checkFilterClientMounted(ctx.myLib[]).isOkOr: + checkFilterClientMounted(ctx.myLib[].waku).isOkOr: return err($error) - let peer = ctx.myLib[].node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: + let peer = ctx.myLib[].waku.node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: let errorMsg = "could not find peer with WakuFilterSubscribeCodec when unsubscribing all" error "fail filter unsubscribe all", error = errorMsg return err(errorMsg) - let unsubFut = ctx.myLib[].node.filterUnsubscribeAll(peer) + let unsubFut = ctx.myLib[].waku.node.filterUnsubscribeAll(peer) if not await unsubFut.withTimeout(FilterOpTimeout): let errorMsg = "filter un-subscription all timed out" diff --git a/library/kernel_api/protocols/lightpush_api.nim b/library/kernel_api/protocols/lightpush_api.nim index dc9197481..287e37aa5 100644 --- a/library/kernel_api/protocols/lightpush_api.nim +++ b/library/kernel_api/protocols/lightpush_api.nim @@ -13,13 +13,13 @@ import library/declare_lib proc waku_lightpush_publish( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, pubSubTopic: cstring, jsonWakuMessage: cstring, ) {.ffi.} = - if ctx.myLib[].node.wakuLightpushClient.isNil(): + if ctx.myLib[].waku.node.wakuLightpushClient.isNil(): let errorMsg = "LightpushRequest waku.node.wakuLightpushClient is nil" error "PUBLISH failed", error = errorMsg return err(errorMsg) @@ -35,14 +35,14 @@ proc waku_lightpush_publish( let msg = json_message_event.toWakuMessage(jsonMessage).valueOr: return err("Problem building the WakuMessage: " & $error) - let peerOpt = ctx.myLib[].node.peerManager.selectPeer(WakuLightPushCodec) + let peerOpt = ctx.myLib[].waku.node.peerManager.selectPeer(WakuLightPushCodec) if peerOpt.isNone(): let errorMsg = "failed to lightpublish message, no suitable remote peers" error "PUBLISH failed", error = errorMsg return err(errorMsg) let msgHashHex = ( - await ctx.myLib[].node.wakuLegacyLightpushClient.publish( + await ctx.myLib[].waku.node.wakuLegacyLightpushClient.publish( $pubsubTopic, msg, peer = peerOpt.get() ) ).valueOr: diff --git a/library/kernel_api/protocols/relay_api.nim b/library/kernel_api/protocols/relay_api.nim index ae7e29ceb..2195cdffd 100644 --- a/library/kernel_api/protocols/relay_api.nim +++ b/library/kernel_api/protocols/relay_api.nim @@ -15,54 +15,54 @@ import library/declare_lib proc waku_relay_get_peers_in_mesh( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, pubSubTopic: cstring, ) {.ffi.} = - let meshPeers = ctx.myLib[].node.wakuRelay.getPeersInMesh($pubsubTopic).valueOr: + let meshPeers = ctx.myLib[].waku.node.wakuRelay.getPeersInMesh($pubsubTopic).valueOr: error "LIST_MESH_PEERS failed", error = error return err($error) ## returns a comma-separated string of peerIDs return ok(meshPeers.mapIt($it).join(",")) proc waku_relay_get_num_peers_in_mesh( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, pubSubTopic: cstring, ) {.ffi.} = - let numPeersInMesh = ctx.myLib[].node.wakuRelay.getNumPeersInMesh($pubsubTopic).valueOr: + let numPeersInMesh = ctx.myLib[].waku.node.wakuRelay.getNumPeersInMesh($pubsubTopic).valueOr: error "NUM_MESH_PEERS failed", error = error return err($error) return ok($numPeersInMesh) proc waku_relay_get_connected_peers( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, pubSubTopic: cstring, ) {.ffi.} = ## Returns the list of all connected peers to an specific pubsub topic - let connPeers = ctx.myLib[].node.wakuRelay.getConnectedPeers($pubsubTopic).valueOr: + let connPeers = ctx.myLib[].waku.node.wakuRelay.getConnectedPeers($pubsubTopic).valueOr: error "LIST_CONNECTED_PEERS failed", error = error return err($error) ## returns a comma-separated string of peerIDs return ok(connPeers.mapIt($it).join(",")) proc waku_relay_get_num_connected_peers( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, pubSubTopic: cstring, ) {.ffi.} = - let numConnPeers = ctx.myLib[].node.wakuRelay.getNumConnectedPeers($pubsubTopic).valueOr: + let numConnPeers = ctx.myLib[].waku.node.wakuRelay.getNumConnectedPeers($pubsubTopic).valueOr: error "NUM_CONNECTED_PEERS failed", error = error return err($error) return ok($numConnPeers) proc waku_relay_add_protected_shard( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, clusterId: cint, @@ -73,7 +73,7 @@ proc waku_relay_add_protected_shard( try: let relayShard = RelayShard(clusterId: uint16(clusterId), shardId: uint16(shardId)) let protectedShard = ProtectedShard.parseCmdArg($relayShard & ":" & $publicKey) - ctx.myLib[].node.wakuRelay.addSignedShardsValidator( + ctx.myLib[].waku.node.wakuRelay.addSignedShardsValidator( @[protectedShard], uint16(clusterId) ) except ValueError as exc: @@ -82,20 +82,20 @@ proc waku_relay_add_protected_shard( return ok("") proc waku_relay_subscribe( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, pubSubTopic: cstring, ) {.ffi.} = echo "Subscribing to topic: " & $pubSubTopic & " ..." - proc onReceivedMessage(ctx: ptr FFIContext[Waku]): WakuRelayHandler = + proc onReceivedMessage(ctx: ptr FFIContext[LogosDelivery]): WakuRelayHandler = return proc(pubsubTopic: PubsubTopic, msg: WakuMessage) {.async.} = callEventCallback(ctx, "onReceivedMessage"): $JsonMessageEvent.new(pubsubTopic, msg) var cb = onReceivedMessage(ctx) - ctx.myLib[].node.subscribe( + ctx.myLib[].waku.node.subscribe( (kind: SubscriptionKind.PubsubSub, topic: $pubsubTopic), handler = WakuRelayHandler(cb), ).isOkOr: @@ -104,19 +104,21 @@ proc waku_relay_subscribe( return ok("") proc waku_relay_unsubscribe( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, pubSubTopic: cstring, ) {.ffi.} = - ctx.myLib[].node.unsubscribe((kind: SubscriptionKind.PubsubSub, topic: $pubsubTopic)).isOkOr: + ctx.myLib[].waku.node.unsubscribe( + (kind: SubscriptionKind.PubsubSub, topic: $pubsubTopic) + ).isOkOr: error "UNSUBSCRIBE failed", error = error return err($error) return ok("") proc waku_relay_publish( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, pubSubTopic: cstring, @@ -136,7 +138,7 @@ proc waku_relay_publish( let msg = json_message_event.toWakuMessage(jsonMessage).valueOr: return err("Problem building the WakuMessage: " & $error) - (await ctx.myLib[].node.wakuRelay.publish($pubsubTopic, msg)).isOkOr: + (await ctx.myLib[].waku.node.wakuRelay.publish($pubsubTopic, msg)).isOkOr: error "PUBLISH failed", error = error return err($error) @@ -144,13 +146,13 @@ proc waku_relay_publish( return ok(msgHash) proc waku_default_pubsub_topic( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = # https://rfc.vac.dev/spec/36/#extern-char-waku_default_pubsub_topic return ok(DefaultPubsubTopic) proc waku_content_topic( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, appName: cstring, @@ -163,7 +165,7 @@ proc waku_content_topic( return ok(fmt"/{$appName}/{$appVersion}/{$contentTopicName}/{$encoding}") proc waku_pubsub_topic( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, topicName: cstring, diff --git a/library/kernel_api/protocols/store_api.nim b/library/kernel_api/protocols/store_api.nim index 22d514d14..7083f93f2 100644 --- a/library/kernel_api/protocols/store_api.nim +++ b/library/kernel_api/protocols/store_api.nim @@ -68,7 +68,7 @@ func fromJsonNode(jsonContent: JsonNode): Result[StoreQueryRequest, string] = ) proc waku_store_query( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, jsonQuery: cstring, @@ -87,7 +87,7 @@ proc waku_store_query( return err("StoreRequest failed to parse peer addr: " & $error) let queryResponse = ( - await ctx.myLib[].node.wakuStoreClient.query(storeQueryRequest, peer) + await ctx.myLib[].waku.node.wakuStoreClient.query(storeQueryRequest, peer) ).valueOr: return err("StoreRequest failed store query: " & $error) diff --git a/library/liblogosdelivery.nim b/library/liblogosdelivery.nim index 08541634a..8dbcfac29 100644 --- a/library/liblogosdelivery.nim +++ b/library/liblogosdelivery.nim @@ -5,6 +5,7 @@ import logos_delivery/waku/waku_core/message/message, logos_delivery/waku/waku_core/topics/pubsub_topic, logos_delivery/waku/waku_relay, + logos_delivery, logos_delivery/waku/factory/waku, logos_delivery/waku/node/waku_node, logos_delivery/waku/node/health_monitor/health_status, @@ -46,7 +47,7 @@ proc waku_new( return nil ## Create the Waku thread that will keep waiting for req from the main thread. - var ctx = ffi.createFFIContext[Waku]().valueOr: + var ctx = ffi.createFFIContext[LogosDelivery]().valueOr: let msg = "Error in createFFIContext: " & $error callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) return nil @@ -93,7 +94,7 @@ proc waku_new( return ctx proc waku_destroy( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ): cint {.dynlib, exportc, cdecl.} = initializeLibrary() checkParams(ctx, callback, userData) diff --git a/library/logos_delivery_api/debug_api.nim b/library/logos_delivery_api/debug_api.nim index e93467d84..98d48c97c 100644 --- a/library/logos_delivery_api/debug_api.nim +++ b/library/logos_delivery_api/debug_api.nim @@ -3,17 +3,17 @@ import logos_delivery/waku/factory/waku_state_info import tools/confutils/[cli_args, config_option_meta] proc logosdelivery_get_available_node_info_ids( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = ## Returns the list of all available node info item ids that ## can be queried with `get_node_info_item`. requireInitializedNode(ctx, "GetNodeInfoIds"): return err(errMsg) - return ok($ctx.myLib[].stateInfo.getAllPossibleInfoItemIds()) + return ok($ctx.myLib[].waku.stateInfo.getAllPossibleInfoItemIds()) proc logosdelivery_get_node_info( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, nodeInfoId: cstring, @@ -28,10 +28,10 @@ proc logosdelivery_get_node_info( except ValueError: return err("Invalid node info id: " & $nodeInfoId) - return ok(ctx.myLib[].stateInfo.getNodeInfoItem(infoItemIdEnum)) + return ok(ctx.myLib[].waku.stateInfo.getNodeInfoItem(infoItemIdEnum)) proc logosdelivery_get_available_configs( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = ## Returns information about the accepted config items. requireInitializedNode(ctx, "GetAvailableConfigs"): diff --git a/library/logos_delivery_api/messaging_api.nim b/library/logos_delivery_api/messaging_api.nim index ff7c7f6d0..3c86138b4 100644 --- a/library/logos_delivery_api/messaging_api.nim +++ b/library/logos_delivery_api/messaging_api.nim @@ -9,7 +9,7 @@ import ../declare_lib proc logosdelivery_subscribe( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, contentTopicStr: cstring, @@ -20,14 +20,14 @@ proc logosdelivery_subscribe( # ContentTopic is just a string type alias let contentTopic = ContentTopic($contentTopicStr) - (await api.subscribe(ctx.myLib[], contentTopic)).isOkOr: + (await api.subscribe(ctx.myLib[].waku, contentTopic)).isOkOr: let errMsg = $error return err("Subscribe failed: " & errMsg) return ok("") proc logosdelivery_unsubscribe( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, contentTopicStr: cstring, @@ -38,14 +38,14 @@ proc logosdelivery_unsubscribe( # ContentTopic is just a string type alias let contentTopic = ContentTopic($contentTopicStr) - api.unsubscribe(ctx.myLib[], contentTopic).isOkOr: + api.unsubscribe(ctx.myLib[].waku, contentTopic).isOkOr: let errMsg = $error return err("Unsubscribe failed: " & errMsg) return ok("") proc logosdelivery_send( - ctx: ptr FFIContext[Waku], + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer, messageJson: cstring, @@ -83,8 +83,8 @@ proc logosdelivery_send( contentTopic = contentTopic, payload = payload, ephemeral = ephemeral ) - # Send the message - let requestId = (await api.send(ctx.myLib[], envelope)).valueOr: + # Send the message via the messaging layer's own API. + let requestId = (await ctx.myLib[].messagingClient.send(envelope)).valueOr: let errMsg = $error return err("Send failed: " & errMsg) diff --git a/library/logos_delivery_api/node_api.nim b/library/logos_delivery_api/node_api.nim index 58785e80d..f6a0945ec 100644 --- a/library/logos_delivery_api/node_api.nim +++ b/library/logos_delivery_api/node_api.nim @@ -1,7 +1,7 @@ import std/json import chronos, chronicles, results, ffi import - logos_delivery/waku/factory/waku, + logos_delivery, logos_delivery/waku/node/waku_node, logos_delivery/waku/api/[api, types], logos_delivery/waku/events/[message_events, health_events], @@ -13,14 +13,14 @@ import proc `%`*(id: RequestId): JsonNode = %($id) -registerReqFFI(CreateNodeRequest, ctx: ptr FFIContext[Waku]): +registerReqFFI(CreateNodeRequest, ctx: ptr FFIContext[LogosDelivery]): proc(configJson: cstring): Future[Result[string, string]] {.async.} = let conf = parseNodeConfFromJson($configJson).valueOr: error "Failed to assemble WakuNodeConf from JSON", error = error, configJson = $configJson return err("failed parseNodeConfFromJson " & error) - ctx.myLib[] = (await api.createNode(conf)).valueOr: + ctx.myLib[] = (await LogosDelivery.new(conf)).valueOr: let errMsg = $error chronicles.error "CreateNodeRequest failed", err = errMsg return err(errMsg) @@ -28,7 +28,7 @@ registerReqFFI(CreateNodeRequest, ctx: ptr FFIContext[Waku]): return ok("") proc logosdelivery_destroy( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ): cint {.dynlib, exportc, cdecl.} = initializeLibrary() checkParams(ctx, callback, userData) @@ -52,7 +52,7 @@ proc logosdelivery_create_node( echo "error: missing callback in logosdelivery_create_node" return nil - var ctx = ffi.createFFIContext[Waku]().valueOr: + var ctx = ffi.createFFIContext[LogosDelivery]().valueOr: let msg = "Error in createFFIContext: " & $error callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) return nil @@ -73,14 +73,14 @@ proc logosdelivery_create_node( return ctx proc logosdelivery_start_node( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = requireInitializedNode(ctx, "START_NODE"): return err(errMsg) # setting up outgoing event listeners let sentListener = MessageSentEvent.listen( - ctx.myLib[].brokerCtx, + ctx.myLib[].waku.brokerCtx, proc(event: MessageSentEvent) {.async: (raises: []).} = callEventCallback(ctx, "onMessageSent"): $newJsonEvent("message_sent", event), @@ -89,7 +89,7 @@ proc logosdelivery_start_node( return err("MessageSentEvent.listen failed: " & $error) let errorListener = MessageErrorEvent.listen( - ctx.myLib[].brokerCtx, + ctx.myLib[].waku.brokerCtx, proc(event: MessageErrorEvent) {.async: (raises: []).} = callEventCallback(ctx, "onMessageError"): $newJsonEvent("message_error", event), @@ -98,7 +98,7 @@ proc logosdelivery_start_node( return err("MessageErrorEvent.listen failed: " & $error) let propagatedListener = MessagePropagatedEvent.listen( - ctx.myLib[].brokerCtx, + ctx.myLib[].waku.brokerCtx, proc(event: MessagePropagatedEvent) {.async: (raises: []).} = callEventCallback(ctx, "onMessagePropagated"): $newJsonEvent("message_propagated", event), @@ -107,7 +107,7 @@ proc logosdelivery_start_node( return err("MessagePropagatedEvent.listen failed: " & $error) let receivedListener = MessageReceivedEvent.listen( - ctx.myLib[].brokerCtx, + ctx.myLib[].waku.brokerCtx, proc(event: MessageReceivedEvent) {.async: (raises: []).} = callEventCallback(ctx, "onMessageReceived"): $newJsonEvent("message_received", event), @@ -116,7 +116,7 @@ proc logosdelivery_start_node( return err("MessageReceivedEvent.listen failed: " & $error) let ConnectionStatusChangeListener = EventConnectionStatusChange.listen( - ctx.myLib[].brokerCtx, + ctx.myLib[].waku.brokerCtx, proc(event: EventConnectionStatusChange) {.async: (raises: []).} = callEventCallback(ctx, "onConnectionStatusChange"): $newJsonEvent("connection_status_change", event), @@ -124,16 +124,6 @@ proc logosdelivery_start_node( chronicles.error "ConnectionStatusChange.listen failed", err = $error return err("ConnectionStatusChange.listen failed: " & $error) - ctx.myLib[].mountMessagingClient().isOkOr: - let errMsg = $error - chronicles.error "mountMessagingClient failed", error = errMsg - return err("failed to mount messaging: " & errMsg) - - ctx.myLib[].mountReliableChannelManager().isOkOr: - let errMsg = $error - chronicles.error "mountReliableChannelManager failed", err = errMsg - return err("failed to mount reliable channel manager: " & errMsg) - (await ctx.myLib[].start()).isOkOr: let errMsg = $error chronicles.error "START_NODE failed", err = errMsg @@ -141,16 +131,16 @@ proc logosdelivery_start_node( return ok("") proc logosdelivery_stop_node( - ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer + ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = requireInitializedNode(ctx, "STOP_NODE"): return err(errMsg) - await MessageErrorEvent.dropAllListeners(ctx.myLib[].brokerCtx) - await MessageSentEvent.dropAllListeners(ctx.myLib[].brokerCtx) - await MessagePropagatedEvent.dropAllListeners(ctx.myLib[].brokerCtx) - await MessageReceivedEvent.dropAllListeners(ctx.myLib[].brokerCtx) - await EventConnectionStatusChange.dropAllListeners(ctx.myLib[].brokerCtx) + await MessageErrorEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) + await MessageSentEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) + await MessagePropagatedEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) + await MessageReceivedEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) + await EventConnectionStatusChange.dropAllListeners(ctx.myLib[].waku.brokerCtx) (await ctx.myLib[].stop()).isOkOr: let errMsg = $error diff --git a/logos_delivery.nim b/logos_delivery.nim index c4edbe6d6..bab1d3e24 100644 --- a/logos_delivery.nim +++ b/logos_delivery.nim @@ -1,10 +1,15 @@ -## Main module for using nwaku as a Nimble library +## Package entry point for using Logos Messaging as a Nimble library. ## -## This module re-exports the public API for creating and managing Waku nodes -## when using nwaku as a library dependency. +## This root module is a thin aggregator, following the standard Nimble layout: +## the implementation lives under `./logos_delivery/`, and importing the package +## name re-exports `LogosDelivery` together with every per-layer public API. +## +## See `logos_delivery/logos_delivery.nim` for `LogosDelivery`, the pure +## concentrator that owns one instance of each API layer +## +## Waku <- MessagingClient <- ReliableChannelManager +## +## and drives their shared `new` / `start` / `stop` lifecycle. -import logos_delivery/waku/api -export api - -import logos_delivery/waku/factory/waku -export waku +import ./logos_delivery/logos_delivery as logos_delivery_impl +export logos_delivery_impl diff --git a/logos_delivery/channels/reliable_channel_manager.nim b/logos_delivery/channels/reliable_channel_manager.nim index 0717eb3f8..887444b19 100644 --- a/logos_delivery/channels/reliable_channel_manager.nim +++ b/logos_delivery/channels/reliable_channel_manager.nim @@ -15,6 +15,7 @@ import brokers/broker_context import logos_delivery/waku/events/message_events as waku_message_events import logos_delivery/messaging/messaging_client +import logos_delivery/waku/api/types import logos_delivery/waku/waku_core/topics import logos_delivery/waku/persistency/sds_persistency @@ -27,30 +28,43 @@ const SdsJobId = "sds" ## One persistency job shared by every channel's SDS state; rows are ## keyed by channelId. -type ReliableChannelManager* = ref object - channels: Table[ChannelId, ReliableChannel] - messagingClient: MessagingClient ## Borrowed from the owning `Waku`. - sendHandler: SendHandler - ## Default egress dispatch for channels created through this manager. - ## Constructed at mount time as a closure over `MessagingClient.send` - ## so the channel layer itself stays callable-only. - brokerCtx: BrokerContext +type + ReliableChannelManagerConf* = object + ## Per-layer config object for the reliable + ## channel API. Placeholder for now (segmentation / SDS / rate-limit defaults + ## will move here in a follow-up PR); kept so each layer owns its own config. + + ReliableChannelManager* = ref object + channels: Table[ChannelId, ReliableChannel] + messagingClient: MessagingClient ## The channel layer chains onto messaging. + sendHandler: SendHandler + ## Default egress dispatch for channels created through this manager. + ## Built in `new` as a closure over `MessagingClient.send` so the channel + ## layer itself stays callable-only. + brokerCtx: BrokerContext proc new*( T: type ReliableChannelManager, + conf: ReliableChannelManagerConf, messagingClient: MessagingClient, - sendHandler: SendHandler, brokerCtx: BrokerContext = globalBrokerContext(), ): Result[T, string] = + ## The reliable channel layer chains onto the messaging layer: its default + ## egress is `MessagingClient.send`, wrapped here so callers never wire the + ## handler themselves. if messagingClient.isNil(): return err("messaging client is required") - if sendHandler.isNil(): - return err("sendHandler is required") + + let defaultSendHandler: SendHandler = proc( + envelope: MessageEnvelope + ): Future[Result[RequestId, string]] {.async: (raises: [CatchableError]), gcsafe.} = + return await messagingClient.send(envelope) + return ok( T( channels: initTable[ChannelId, ReliableChannel](), messagingClient: messagingClient, - sendHandler: sendHandler, + sendHandler: defaultSendHandler, brokerCtx: brokerCtx, ) ) diff --git a/logos_delivery/logos_delivery.nim b/logos_delivery/logos_delivery.nim new file mode 100644 index 000000000..f01cf4207 --- /dev/null +++ b/logos_delivery/logos_delivery.nim @@ -0,0 +1,104 @@ +## `LogosDelivery` is the project entry point. It is a pure concentrator: it +## owns exactly one instance of each API layer +## +## Waku <- MessagingClient <- ReliableChannelManager +## +## and chains them together (each layer drives the one below it). Every layer +## keeps its own, separate public API — `LogosDelivery` only wires them up and +## drives the shared `new` / `start` / `stop` lifecycle. + +{.push raises: [].} + +import results, chronos, chronicles + +import logos_delivery/waku/api +export api +import logos_delivery/waku/factory/waku +export waku +import logos_delivery/messaging/messaging_client +export messaging_client +import logos_delivery/channels/reliable_channel_manager +export reliable_channel_manager + +import logos_delivery/waku/factory/waku_conf +import logos_delivery/waku/factory/app_callbacks +import logos_delivery/waku/api/[api_conf, types] + +logScope: + topics = "logosdelivery" + +type + LogosDeliveryConf* = object + ## Aggregates the per-layer config objects. For now + ## the sub-configs are derived from `WakuConf`; richer per-layer configuration + ## (and how it is sourced) lands in a follow-up PR. + waku*: WakuConf + messaging*: MessagingClientConf + reliableChannel*: ReliableChannelManagerConf + + LogosDelivery* = ref object ## Entry point. Holds one instance of each API layer. + waku*: Waku + messagingClient*: MessagingClient + reliableChannelManager*: ReliableChannelManager + +proc init*(T: type LogosDeliveryConf, wakuConf: WakuConf): LogosDeliveryConf = + ## Builds the aggregated config from a `WakuConf`. The messaging / reliable + ## channel layers carry trivial config today; this is the seam where their + ## dedicated config will be threaded through later. + LogosDeliveryConf( + waku: wakuConf, + messaging: MessagingClientConf(useP2PReliability: wakuConf.p2pReliability), + reliableChannel: ReliableChannelManagerConf(), + ) + +proc new*( + T: type LogosDelivery, conf: WakuNodeConf, appCallbacks: AppCallbacks = nil +): Future[Result[LogosDelivery, string]] {.async.} = + ## Single entry point, from the CLI configuration type. Derives the aggregated + ## per-layer config, then creates the full stack bottom-up so each layer can + ## chain onto the one below. + let wakuConf = conf.toWakuConf().valueOr: + return err("failed to handle the configuration: " & error) + let layerConf = LogosDeliveryConf.init(wakuConf) + + let waku = (await Waku.new(layerConf.waku, appCallbacks)).valueOr: + return err("failed to create Waku: " & error) + + let messagingClient = MessagingClient.new(layerConf.messaging, waku.node).valueOr: + return err("failed to create MessagingClient: " & error) + + let reliableChannelManager = ReliableChannelManager.new( + layerConf.reliableChannel, messagingClient, waku.brokerCtx + ).valueOr: + return err("failed to create ReliableChannelManager: " & error) + + return ok( + T( + waku: waku, + messagingClient: messagingClient, + reliableChannelManager: reliableChannelManager, + ) + ) + +proc start*(self: LogosDelivery): Future[Result[void, string]] {.async.} = + ## Starts each layer bottom-up: transport first, then messaging, then channels. + (await self.waku.start()).isOkOr: + return err("failed to start Waku: " & error) + + self.messagingClient.start().isOkOr: + return err("failed to start MessagingClient: " & error) + + self.reliableChannelManager.start().isOkOr: + return err("failed to start ReliableChannelManager: " & error) + + return ok() + +proc stop*(self: LogosDelivery): Future[Result[void, string]] {.async.} = + ## Stops in reverse order so higher layers drain before their dependencies. + await self.reliableChannelManager.stop() + await self.messagingClient.stop() + + (await self.waku.stop()).isOkOr: + return err("failed to stop Waku: " & error) + + return ok() diff --git a/logos_delivery/messaging/messaging_client.nim b/logos_delivery/messaging/messaging_client.nim index 1d3892250..652671e46 100644 --- a/logos_delivery/messaging/messaging_client.nim +++ b/logos_delivery/messaging/messaging_client.nim @@ -6,16 +6,25 @@ import logos_delivery/messaging/delivery_service/[recv_service, send_service], logos_delivery/messaging/delivery_service/send_service/delivery_task -type MessagingClient* = ref object - node: WakuNode - sendService*: SendService - recvService*: RecvService - started: bool +type + MessagingClientConf* = object + ## Per-layer config object for the messaging API. + ## Kept intentionally minimal for now; the full config surface lands in a + ## follow-up PR. Today it only carries the p2p reliability toggle. + useP2PReliability*: bool + + MessagingClient* = ref object + node: WakuNode + sendService*: SendService + recvService*: RecvService + started: bool proc new*( - T: type MessagingClient, useP2PReliability: bool, node: WakuNode + T: type MessagingClient, conf: MessagingClientConf, node: WakuNode ): Result[T, string] = - let sendService = ?SendService.new(useP2PReliability, node) + ## The messaging layer chains onto Waku: it drives the underlying + ## `WakuNode` (Waku's core) for transport while exposing its own send/recv API. + let sendService = ?SendService.new(conf.useP2PReliability, node) let recvService = RecvService.new(node) ok(T(node: node, sendService: sendService, recvService: recvService)) diff --git a/logos_delivery/waku/api/api.nim b/logos_delivery/waku/api/api.nim index 5be1e2086..0ffc38dc8 100644 --- a/logos_delivery/waku/api/api.nim +++ b/logos_delivery/waku/api/api.nim @@ -4,9 +4,7 @@ import std/[net, options] import chronicles, chronos, libp2p/peerid, results import logos_delivery/waku/factory/waku -import logos_delivery/messaging/messaging_client import logos_delivery/waku/[requests/health_requests, waku_core, waku_node] -import logos_delivery/messaging/delivery_service/send_service import logos_delivery/waku/node/subscription_manager import libp2p/peerid import tools/confutils/cli_args @@ -48,9 +46,3 @@ proc unsubscribe*(w: Waku, contentTopic: ContentTopic): Result[void, string] = ?checkApiAvailability(w) return w.node.subscriptionManager.unsubscribe(contentTopic) - -proc send*( - w: Waku, envelope: MessageEnvelope -): Future[Result[RequestId, string]] {.async.} = - ?checkApiAvailability(w) - return await w.messagingClient.send(envelope) diff --git a/logos_delivery/waku/factory/waku.nim b/logos_delivery/waku/factory/waku.nim index e15a92fd0..a797b8294 100644 --- a/logos_delivery/waku/factory/waku.nim +++ b/logos_delivery/waku/factory/waku.nim @@ -49,8 +49,6 @@ import factory/app_callbacks, persistency/persistency, ], - logos_delivery/channels/reliable_channel_manager, - logos_delivery/messaging/messaging_client, ./waku_conf, ./waku_state_info @@ -76,10 +74,6 @@ type Waku* = ref object healthMonitor*: NodeHealthMonitor - messagingClient*: MessagingClient - - reliableChannelManager*: ReliableChannelManager - restServer*: WakuRestServerRef metricsServer*: MetricsHttpServerRef appCallbacks*: AppCallbacks @@ -384,35 +378,6 @@ proc startDnsDiscoveryRetryLoop(waku: Waku): Future[void] {.async.} = error "failed to connect to dynamic bootstrap nodes: " & getCurrentExceptionMsg() return -proc mountMessagingClient*(waku: Waku): Result[void, string] = - if not waku.messagingClient.isNil(): - return err("messaging client already mounted") - if waku.node.started: - return err("cannot mount messaging client on a started node") - waku.messagingClient = MessagingClient.new(waku.conf.p2pReliability, waku.node).valueOr: - return err("could not create messaging client: " & $error) - return ok() - -proc mountReliableChannelManager*(waku: Waku): Result[void, string] = - if not waku.reliableChannelManager.isNil(): - return err("reliable channel manager already mounted") - if waku.messagingClient.isNil(): - return err("reliable channel manager requires a mounted messaging client") - if waku.node.started: - return err("cannot mount reliable channel manager on a started node") - - let messagingClient = waku.messagingClient - let defaultSendHandler: SendHandler = proc( - envelope: MessageEnvelope - ): Future[Result[RequestId, string]] {.async: (raises: [CatchableError]), gcsafe.} = - return await messagingClient.send(envelope) - - waku.reliableChannelManager = ReliableChannelManager.new( - messagingClient, defaultSendHandler, waku.brokerCtx - ).valueOr: - return err("could not create reliable channel manager: " & $error) - return ok() - proc start*(waku: Waku): Future[Result[void, string]] {.async: (raises: []).} = if waku.node.started: warn "start: waku node already started" @@ -565,14 +530,6 @@ proc start*(waku: Waku): Future[Result[void, string]] {.async: (raises: []).} = ) waku.healthMonitor.setOverallHealth(HealthStatus.READY) - if not waku.messagingClient.isNil(): - waku.messagingClient.start().isOkOr: - return err("failed to start messaging client: " & $error) - - if not waku.reliableChannelManager.isNil(): - waku.reliableChannelManager.start().isOkOr: - return err("failed to start reliable channel manager: " & $error) - return ok() proc stop*(waku: Waku): Future[Result[void, string]] {.async: (raises: []).} = @@ -590,12 +547,6 @@ proc stop*(waku: Waku): Future[Result[void, string]] {.async: (raises: []).} = if not waku.wakuDiscv5.isNil(): await waku.wakuDiscv5.stop() - if not waku.reliableChannelManager.isNil(): - await waku.reliableChannelManager.stop() - - if not waku.messagingClient.isNil(): - await waku.messagingClient.stop() - if not waku.node.isNil(): await waku.node.stop() diff --git a/tests/api/test_api_health.nim b/tests/api/test_api_health.nim index d8a7eabf2..83efedd18 100644 --- a/tests/api/test_api_health.nim +++ b/tests/api/test_api_health.nim @@ -74,7 +74,7 @@ proc waitForShardHealthy( suite "LM API health checking": var serviceNode {.threadvar.}: WakuNode - client {.threadvar.}: Waku + client {.threadvar.}: LogosDelivery servicePeerInfo {.threadvar.}: RemotePeerInfo asyncSetup: @@ -102,9 +102,7 @@ suite "LM API health checking": conf.numShardsInNetwork = 1 conf.rest = false - client = (await createNode(conf)).valueOr: - raiseAssert error - client.mountMessagingClient().isOkOr: + client = (await LogosDelivery.new(conf)).valueOr: raiseAssert error (await client.start()).isOkOr: raiseAssert error @@ -114,13 +112,13 @@ suite "LM API health checking": await serviceNode.stop() asyncTest "RequestShardTopicsHealth, check PubsubTopic health": - client.node.wakuRelay.subscribe(DefaultShard, dummyHandler) - await client.node.connectToNodes(@[servicePeerInfo]) + client.waku.node.wakuRelay.subscribe(DefaultShard, dummyHandler) + await client.waku.node.connectToNodes(@[servicePeerInfo]) var isHealthy = false let start = Moment.now() while Moment.now() - start < TestTimeout: - let req = RequestShardTopicsHealth.request(client.brokerCtx, @[DefaultShard]).valueOr: + let req = RequestShardTopicsHealth.request(client.waku.brokerCtx, @[DefaultShard]).valueOr: raiseAssert "RequestShardTopicsHealth failed" if req.topicHealth.len > 0: @@ -134,22 +132,22 @@ suite "LM API health checking": asyncTest "RequestShardTopicsHealth, check disconnected PubsubTopic": const GhostShard = PubsubTopic("/waku/2/rs/1/666") - client.node.wakuRelay.subscribe(GhostShard, dummyHandler) + client.waku.node.wakuRelay.subscribe(GhostShard, dummyHandler) - let req = RequestShardTopicsHealth.request(client.brokerCtx, @[GhostShard]).valueOr: + let req = RequestShardTopicsHealth.request(client.waku.brokerCtx, @[GhostShard]).valueOr: raiseAssert "Request failed" check req.topicHealth.len > 0 check req.topicHealth[0].health == TopicHealth.UNHEALTHY asyncTest "RequestProtocolHealth, check relay status": - await client.node.connectToNodes(@[servicePeerInfo]) + await client.waku.node.connectToNodes(@[servicePeerInfo]) var isReady = false let start = Moment.now() while Moment.now() - start < TestTimeout: let relayReq = await RequestProtocolHealth.request( - client.brokerCtx, WakuProtocol.RelayProtocol + client.waku.brokerCtx, WakuProtocol.RelayProtocol ) if relayReq.isOk() and relayReq.get().healthStatus.health == HealthStatus.READY: isReady = true @@ -158,14 +156,16 @@ suite "LM API health checking": check isReady == true - let storeReq = - await RequestProtocolHealth.request(client.brokerCtx, WakuProtocol.StoreProtocol) + let storeReq = await RequestProtocolHealth.request( + client.waku.brokerCtx, WakuProtocol.StoreProtocol + ) if storeReq.isOk(): check storeReq.get().healthStatus.health != HealthStatus.READY asyncTest "RequestProtocolHealth, check unmounted protocol": - let req = - await RequestProtocolHealth.request(client.brokerCtx, WakuProtocol.StoreProtocol) + let req = await RequestProtocolHealth.request( + client.waku.brokerCtx, WakuProtocol.StoreProtocol + ) check req.isOk() let status = req.get().healthStatus @@ -173,16 +173,16 @@ suite "LM API health checking": check status.desc.isNone() asyncTest "RequestConnectionStatus, check connectivity state": - let initialReq = RequestConnectionStatus.request(client.brokerCtx).valueOr: + let initialReq = RequestConnectionStatus.request(client.waku.brokerCtx).valueOr: raiseAssert "RequestConnectionStatus failed" check initialReq.connectionStatus == ConnectionStatus.Disconnected - await client.node.connectToNodes(@[servicePeerInfo]) + await client.waku.node.connectToNodes(@[servicePeerInfo]) var isConnected = false let start = Moment.now() while Moment.now() - start < TestTimeout: - let req = RequestConnectionStatus.request(client.brokerCtx).valueOr: + let req = RequestConnectionStatus.request(client.waku.brokerCtx).valueOr: raiseAssert "RequestConnectionStatus failed" if req.connectionStatus == ConnectionStatus.PartiallyConnected or @@ -194,29 +194,30 @@ suite "LM API health checking": check isConnected == true asyncTest "EventConnectionStatusChange, detect connect and disconnect": - let connectFuture = - waitForConnectionStatus(client.brokerCtx, ConnectionStatus.PartiallyConnected) + let connectFuture = waitForConnectionStatus( + client.waku.brokerCtx, ConnectionStatus.PartiallyConnected + ) - await client.node.connectToNodes(@[servicePeerInfo]) + await client.waku.node.connectToNodes(@[servicePeerInfo]) await connectFuture let disconnectFuture = - waitForConnectionStatus(client.brokerCtx, ConnectionStatus.Disconnected) - await client.node.disconnectNode(servicePeerInfo) + waitForConnectionStatus(client.waku.brokerCtx, ConnectionStatus.Disconnected) + await client.waku.node.disconnectNode(servicePeerInfo) await disconnectFuture asyncTest "EventShardTopicHealthChange, detect health improvement": - client.node.wakuRelay.subscribe(DefaultShard, dummyHandler) + client.waku.node.wakuRelay.subscribe(DefaultShard, dummyHandler) - let healthEventFuture = waitForShardHealthy(client.brokerCtx) + let healthEventFuture = waitForShardHealthy(client.waku.brokerCtx) - await client.node.connectToNodes(@[servicePeerInfo]) + await client.waku.node.connectToNodes(@[servicePeerInfo]) let event = await healthEventFuture check event.topic == DefaultShard asyncTest "RequestHealthReport, check aggregate report": - let req = await RequestHealthReport.request(client.brokerCtx) + let req = await RequestHealthReport.request(client.waku.brokerCtx) check req.isOk() @@ -228,7 +229,8 @@ suite "LM API health checking": asyncTest "RequestContentTopicsHealth, smoke test": let fictionalTopic = ContentTopic("/waku/2/this-does-not-exist/proto") - let req = RequestContentTopicsHealth.request(client.brokerCtx, @[fictionalTopic]) + let req = + RequestContentTopicsHealth.request(client.waku.brokerCtx, @[fictionalTopic]) check req.isOk() @@ -241,20 +243,20 @@ suite "LM API health checking": let cTopic = ContentTopic("/waku/2/my-content-topic/proto") let shardReq = - RequestRelayShard.request(client.brokerCtx, none(PubsubTopic), cTopic) + RequestRelayShard.request(client.waku.brokerCtx, none(PubsubTopic), cTopic) check shardReq.isOk() let targetShard = $shardReq.get().relayShard - client.node.wakuRelay.subscribe(targetShard, dummyHandler) + client.waku.node.wakuRelay.subscribe(targetShard, dummyHandler) serviceNode.wakuRelay.subscribe(targetShard, dummyHandler) - await client.node.connectToNodes(@[servicePeerInfo]) + await client.waku.node.connectToNodes(@[servicePeerInfo]) var isHealthy = false let start = Moment.now() while Moment.now() - start < TestTimeout: - let req = RequestContentTopicsHealth.request(client.brokerCtx, @[cTopic]).valueOr: + let req = RequestContentTopicsHealth.request(client.waku.brokerCtx, @[cTopic]).valueOr: raiseAssert "Request failed" if req.contentTopicHealth.len > 0: @@ -268,7 +270,7 @@ suite "LM API health checking": check isHealthy == true asyncTest "RequestProtocolHealth, edge mode smoke test": - var edgeWaku: Waku + var edgeWaku: LogosDelivery lockNewGlobalBrokerContext: var edgeConf = defaultWakuNodeConf().valueOr: @@ -281,20 +283,18 @@ suite "LM API health checking": edgeConf.maxMessageSize = "150 KiB" edgeConf.rest = false - edgeWaku = (await createNode(edgeConf)).valueOr: + edgeWaku = (await LogosDelivery.new(edgeConf)).valueOr: raiseAssert "Failed to create edge node: " & error - edgeWaku.mountMessagingClient().isOkOr: - raiseAssert "Failed to mount edge messaging: " & error (await edgeWaku.start()).isOkOr: raiseAssert "Failed to start edge waku: " & error let relayReq = await RequestProtocolHealth.request( - edgeWaku.brokerCtx, WakuProtocol.RelayProtocol + edgeWaku.waku.brokerCtx, WakuProtocol.RelayProtocol ) check relayReq.isOk() check relayReq.get().healthStatus.health == HealthStatus.NOT_MOUNTED - check not edgeWaku.node.wakuFilterClient.isNil() + check not edgeWaku.waku.node.wakuFilterClient.isNil() discard await edgeWaku.stop() diff --git a/tests/api/test_api_receive.nim b/tests/api/test_api_receive.nim index 362ee1847..d9eb02427 100644 --- a/tests/api/test_api_receive.nim +++ b/tests/api/test_api_receive.nim @@ -98,7 +98,7 @@ proc createApiNodeConf(numShards: uint16 = 1): WakuNodeConf = type TestNetwork = ref object storeNode: WakuNode publisher: WakuNode - subscriber: Waku + subscriber: LogosDelivery storeNodePeerInfo: RemotePeerInfo missedPayload: seq[byte] @@ -158,12 +158,11 @@ proc setupNetwork(testTopic: ContentTopic): Future[TestNetwork] {.async.} = # subscriber: created before the publish so the message timestamp lands after # its RecvService startTimeToCheck watermark - var subscriber: Waku + var subscriber: LogosDelivery lockNewGlobalBrokerContext: - subscriber = (await createNode(createApiNodeConf(numShards))).expect( + subscriber = (await LogosDelivery.new(createApiNodeConf(numShards))).expect( "Failed to create subscriber" ) - subscriber.mountMessagingClient().expect("Failed to mount messaging") (await subscriber.start()).expect("Failed to start subscriber") # publish while the subscriber is offline: the message reaches the archive but @@ -188,7 +187,7 @@ proc setupNetwork(testTopic: ContentTopic): Future[TestNetwork] {.async.} = raiseAssert "Message was not archived in time" # subscribe to the content topic; with no peers yet the subscriber stays offline - (await subscriber.subscribe(testTopic)).expect("Failed to subscribe") + (await subscriber.waku.subscribe(testTopic)).expect("Failed to subscribe") return TestNetwork( storeNode: storeNode, @@ -217,11 +216,11 @@ suite "Messaging API, Receive Service (store recovery)": defer: await net.teardown() - let eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: await eventManager.teardown() - await net.subscriber.node.connectToNodes(@[net.storeNodePeerInfo]) + await net.subscriber.waku.node.connectToNodes(@[net.storeNodePeerInfo]) await net.subscriber.messagingClient.recvService.checkStore() check await eventManager.waitForEvents(TestTimeout) @@ -236,15 +235,15 @@ suite "Messaging API, Receive Service (store recovery)": defer: await net.teardown() - let eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: await eventManager.teardown() # sync on coming online (the transition that fires the backfill) before asserting let onlineFut = waitForConnectionStatus( - net.subscriber.brokerCtx, ConnectionStatus.PartiallyConnected + net.subscriber.waku.brokerCtx, ConnectionStatus.PartiallyConnected ) - await net.subscriber.node.connectToNodes(@[net.storeNodePeerInfo]) + await net.subscriber.waku.node.connectToNodes(@[net.storeNodePeerInfo]) await onlineFut check await eventManager.waitForEvents(TestTimeout) diff --git a/tests/api/test_api_send.nim b/tests/api/test_api_send.nim index e51473b17..5c75f2e4a 100644 --- a/tests/api/test_api_send.nim +++ b/tests/api/test_api_send.nim @@ -237,12 +237,10 @@ suite "Waku API - Send": ) asyncTest "Check API availability (unhealthy node)": - var node: Waku + var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await createNode(createApiNodeConf())).valueOr: + node = (await LogosDelivery.new(createApiNodeConf())).valueOr: raiseAssert error - node.mountMessagingClient().isOkOr: - raiseAssert "Failed to mount messaging: " & error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error # node is not connected ! @@ -251,7 +249,7 @@ suite "Waku API - Send": ContentTopic("/waku/2/default-content/proto"), "test payload" ) - let sendResult = await node.send(envelope) + let sendResult = await node.messagingClient.send(envelope) # TODO: The API is not enforcing a health check before the send, # so currently this test cannot successfully fail to send. @@ -261,20 +259,18 @@ suite "Waku API - Send": raiseAssert "Failed to stop node: " & error asyncTest "Send fully validated": - var node: Waku + var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await createNode(createApiNodeConf())).valueOr: + node = (await LogosDelivery.new(createApiNodeConf())).valueOr: raiseAssert error - node.mountMessagingClient().isOkOr: - raiseAssert "Failed to mount messaging: " & error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error - await node.node.connectToNodes( + await node.waku.node.connectToNodes( @[relayNode1PeerInfo, lightpushNodePeerInfo, storeNodePeerInfo] ) - let eventManager = newSendEventListenerManager(node.brokerCtx) + let eventManager = newSendEventListenerManager(node.waku.brokerCtx) defer: await eventManager.teardown() @@ -282,7 +278,7 @@ suite "Waku API - Send": ContentTopic("/waku/2/default-content/proto"), "test payload" ) - let requestId = (await node.send(envelope)).valueOr: + let requestId = (await node.messagingClient.send(envelope)).valueOr: raiseAssert error # Wait for events with timeout @@ -297,18 +293,16 @@ suite "Waku API - Send": raiseAssert "Failed to stop node: " & error asyncTest "Send only propagates": - var node: Waku + var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await createNode(createApiNodeConf())).valueOr: + node = (await LogosDelivery.new(createApiNodeConf())).valueOr: raiseAssert error - node.mountMessagingClient().isOkOr: - raiseAssert "Failed to mount messaging: " & error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error - await node.node.connectToNodes(@[relayNode1PeerInfo]) + await node.waku.node.connectToNodes(@[relayNode1PeerInfo]) - let eventManager = newSendEventListenerManager(node.brokerCtx) + let eventManager = newSendEventListenerManager(node.waku.brokerCtx) defer: await eventManager.teardown() @@ -316,7 +310,7 @@ suite "Waku API - Send": ContentTopic("/waku/2/default-content/proto"), "test payload" ) - let requestId = (await node.send(envelope)).valueOr: + let requestId = (await node.messagingClient.send(envelope)).valueOr: raiseAssert error # Wait for events with timeout @@ -329,18 +323,16 @@ suite "Waku API - Send": raiseAssert "Failed to stop node: " & error asyncTest "Send only propagates fallback to lightpush": - var node: Waku + var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await createNode(createApiNodeConf())).valueOr: + node = (await LogosDelivery.new(createApiNodeConf())).valueOr: raiseAssert error - node.mountMessagingClient().isOkOr: - raiseAssert "Failed to mount messaging: " & error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error - await node.node.connectToNodes(@[lightpushNodePeerInfo]) + await node.waku.node.connectToNodes(@[lightpushNodePeerInfo]) - let eventManager = newSendEventListenerManager(node.brokerCtx) + let eventManager = newSendEventListenerManager(node.waku.brokerCtx) defer: await eventManager.teardown() @@ -348,7 +340,7 @@ suite "Waku API - Send": ContentTopic("/waku/2/default-content/proto"), "test payload" ) - let requestId = (await node.send(envelope)).valueOr: + let requestId = (await node.messagingClient.send(envelope)).valueOr: raiseAssert error # Wait for events with timeout @@ -361,18 +353,16 @@ suite "Waku API - Send": raiseAssert "Failed to stop node: " & error asyncTest "Send fully validates fallback to lightpush": - var node: Waku + var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await createNode(createApiNodeConf())).valueOr: + node = (await LogosDelivery.new(createApiNodeConf())).valueOr: raiseAssert error - node.mountMessagingClient().isOkOr: - raiseAssert "Failed to mount messaging: " & error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error - await node.node.connectToNodes(@[lightpushNodePeerInfo, storeNodePeerInfo]) + await node.waku.node.connectToNodes(@[lightpushNodePeerInfo, storeNodePeerInfo]) - let eventManager = newSendEventListenerManager(node.brokerCtx) + let eventManager = newSendEventListenerManager(node.waku.brokerCtx) defer: await eventManager.teardown() @@ -380,7 +370,7 @@ suite "Waku API - Send": ContentTopic("/waku/2/default-content/proto"), "test payload" ) - let requestId = (await node.send(envelope)).valueOr: + let requestId = (await node.messagingClient.send(envelope)).valueOr: raiseAssert error # Wait for events with timeout @@ -417,18 +407,16 @@ suite "Waku API - Send": ).isOkOr: raiseAssert "Failed to subscribe fakeLightpushNode: " & error - var node: Waku + var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await createNode(createApiNodeConf(cli_args.WakuMode.Edge))).valueOr: + node = (await LogosDelivery.new(createApiNodeConf(cli_args.WakuMode.Edge))).valueOr: raiseAssert error - node.mountMessagingClient().isOkOr: - raiseAssert "Failed to mount messaging: " & error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error - await node.node.connectToNodes(@[fakeLightpushNodePeerInfo]) + await node.waku.node.connectToNodes(@[fakeLightpushNodePeerInfo]) - let eventManager = newSendEventListenerManager(node.brokerCtx) + let eventManager = newSendEventListenerManager(node.waku.brokerCtx) defer: await eventManager.teardown() @@ -436,7 +424,7 @@ suite "Waku API - Send": ContentTopic("/waku/2/default-content/proto"), "test payload" ) - let requestId = (await node.send(envelope)).valueOr: + let requestId = (await node.messagingClient.send(envelope)).valueOr: raiseAssert error echo "Sent message with requestId=", requestId diff --git a/tests/api/test_api_subscription.nim b/tests/api/test_api_subscription.nim index 2a6ce36ee..1169a7366 100644 --- a/tests/api/test_api_subscription.nim +++ b/tests/api/test_api_subscription.nim @@ -64,7 +64,7 @@ proc waitForEvents( type TestNetwork = ref object publisher: WakuNode # Relay node that publishes messages in tests. meshBuddy: WakuNode # Extra relay peer for publisher's mesh (Edge tests only). - subscriber: Waku + subscriber: LogosDelivery # The receiver node in tests. Edge node in edge tests, Core node in relay tests. publisherPeerInfo: RemotePeerInfo @@ -83,11 +83,10 @@ proc createApiNodeConf( conf.rest = false result = conf -proc setupSubscriberNode(conf: WakuNodeConf): Future[Waku] {.async.} = - var node: Waku +proc setupSubscriberNode(conf: WakuNodeConf): Future[LogosDelivery] {.async.} = + var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await createNode(conf)).expect("Failed to create subscriber node") - node.mountMessagingClient().expect("Failed to mount messaging") + node = (await LogosDelivery.new(conf)).expect("Failed to create subscriber node") (await node.start()).expect("Failed to start subscriber node") return node @@ -141,7 +140,7 @@ proc setupNetwork( net.subscriber = await setupSubscriberNode(createApiNodeConf(mode, numShards)) - await net.subscriber.node.connectToNodes(@[net.publisherPeerInfo]) + await net.subscriber.waku.node.connectToNodes(@[net.publisherPeerInfo]) return net @@ -171,28 +170,30 @@ proc waitForMesh(node: WakuNode, shard: PubsubTopic) {.async.} = await sleepAsync(100.milliseconds) raise newException(ValueError, "GossipSub Mesh failed to stabilize on " & shard) -proc waitForEdgeSubs(w: Waku, shard: PubsubTopic) {.async.} = +proc waitForEdgeSubs(w: LogosDelivery, shard: PubsubTopic) {.async.} = let deadline = Moment.now() + EdgeWaitTimeout while Moment.now() < deadline: - if w.node.subscriptionManager.edgeFilterPeerCount(shard) > 0: + if w.waku.node.subscriptionManager.edgeFilterPeerCount(shard) > 0: return await sleepAsync(100.milliseconds) raise newException(ValueError, "Edge filter subscription failed on " & shard) -proc edgePeersReached(w: Waku, shard: PubsubTopic, n: int): Future[bool] {.async.} = +proc edgePeersReached( + w: LogosDelivery, shard: PubsubTopic, n: int +): Future[bool] {.async.} = let deadline = Moment.now() + EdgeWaitTimeout while Moment.now() < deadline: - if w.node.subscriptionManager.edgeFilterPeerCount(shard) >= n: + if w.waku.node.subscriptionManager.edgeFilterPeerCount(shard) >= n: return true await sleepAsync(100.milliseconds) return false proc edgePeersDroppedBelow( - w: Waku, shard: PubsubTopic, n: int + w: LogosDelivery, shard: PubsubTopic, n: int ): Future[bool] {.async.} = let deadline = Moment.now() + EdgeWaitTimeout while Moment.now() < deadline: - if w.node.subscriptionManager.edgeFilterPeerCount(shard) < n: + if w.waku.node.subscriptionManager.edgeFilterPeerCount(shard) < n: return true await sleepAsync(100.milliseconds) return false @@ -201,7 +202,7 @@ proc publishToMesh( net: TestNetwork, contentTopic: ContentTopic, payload: seq[byte] ): Future[Result[int, string]] {.async.} = # Publishes a message from "publisher" via relay into the gossipsub mesh. - let shard = net.subscriber.node.getRelayShard(contentTopic) + let shard = net.subscriber.waku.node.getRelayShard(contentTopic) await waitForMesh(net.publisher, shard) let msg = WakuMessage( payload: payload, contentTopic: contentTopic, version: 0, timestamp: now() @@ -213,7 +214,7 @@ proc publishToMeshAfterEdgeReady( ): Future[Result[int, string]] {.async.} = # First, ensure "subscriber" node (an edge node) is subscribed and ready to receive. # Afterwards, "publisher" (relay node) sends the message in the gossipsub network. - let shard = net.subscriber.node.getRelayShard(contentTopic) + let shard = net.subscriber.waku.node.getRelayShard(contentTopic) await waitForEdgeSubs(net.subscriber, shard) return await net.publishToMesh(contentTopic, payload) @@ -224,11 +225,11 @@ suite "Messaging API, SubscriptionManager": await net.teardown() let testTopic = ContentTopic("/waku/2/test-content/proto") - (await net.subscriber.subscribe(testTopic)).expect( + (await net.subscriber.waku.subscribe(testTopic)).expect( "subscriberNode failed to subscribe" ) - let eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: await eventManager.teardown() @@ -247,9 +248,9 @@ suite "Messaging API, SubscriptionManager": let subbedTopic = ContentTopic("/waku/2/subbed-topic/proto") let ignoredTopic = ContentTopic("/waku/2/ignored-topic/proto") - (await net.subscriber.subscribe(subbedTopic)).expect("failed to subscribe") + (await net.subscriber.waku.subscribe(subbedTopic)).expect("failed to subscribe") - let eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: await eventManager.teardown() @@ -267,10 +268,10 @@ suite "Messaging API, SubscriptionManager": let testTopic = ContentTopic("/waku/2/unsub-test/proto") - (await net.subscriber.subscribe(testTopic)).expect("failed to subscribe") - net.subscriber.unsubscribe(testTopic).expect("failed to unsubscribe") + (await net.subscriber.waku.subscribe(testTopic)).expect("failed to subscribe") + net.subscriber.waku.unsubscribe(testTopic).expect("failed to unsubscribe") - let eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: await eventManager.teardown() @@ -288,14 +289,14 @@ suite "Messaging API, SubscriptionManager": let topicA = ContentTopic("/waku/2/topic-a/proto") let topicB = ContentTopic("/waku/2/topic-b/proto") - (await net.subscriber.subscribe(topicA)).expect("failed to sub A") - (await net.subscriber.subscribe(topicB)).expect("failed to sub B") + (await net.subscriber.waku.subscribe(topicA)).expect("failed to sub A") + (await net.subscriber.waku.subscribe(topicB)).expect("failed to sub B") - let eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: await eventManager.teardown() - net.subscriber.unsubscribe(topicA).expect("failed to unsub A") + net.subscriber.waku.unsubscribe(topicA).expect("failed to unsub A") discard (await net.publishToMesh(topicA, "Dropped Message".toBytes())).expect( "Publish A failed" @@ -314,11 +315,11 @@ suite "Messaging API, SubscriptionManager": let glitchTopic = ContentTopic("/waku/2/glitch/proto") - (await net.subscriber.subscribe(glitchTopic)).expect("failed to sub") - (await net.subscriber.subscribe(glitchTopic)).expect("failed to double sub") - net.subscriber.unsubscribe(glitchTopic).expect("failed to unsub") + (await net.subscriber.waku.subscribe(glitchTopic)).expect("failed to sub") + (await net.subscriber.waku.subscribe(glitchTopic)).expect("failed to double sub") + net.subscriber.waku.unsubscribe(glitchTopic).expect("failed to unsub") - let eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: await eventManager.teardown() @@ -337,9 +338,9 @@ suite "Messaging API, SubscriptionManager": let testTopic = ContentTopic("/waku/2/resub-test/proto") # Subscribe - (await net.subscriber.subscribe(testTopic)).expect("Initial sub failed") + (await net.subscriber.waku.subscribe(testTopic)).expect("Initial sub failed") - var eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + var eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) discard (await net.publishToMesh(testTopic, "Msg 1".toBytes())).expect("Pub 1 failed") @@ -347,8 +348,8 @@ suite "Messaging API, SubscriptionManager": await eventManager.teardown() # Unsubscribe and verify teardown - net.subscriber.unsubscribe(testTopic).expect("Unsub failed") - eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + net.subscriber.waku.unsubscribe(testTopic).expect("Unsub failed") + eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) discard (await net.publishToMesh(testTopic, "Ghost".toBytes())).expect("Ghost pub failed") @@ -357,8 +358,8 @@ suite "Messaging API, SubscriptionManager": await eventManager.teardown() # Resubscribe - (await net.subscriber.subscribe(testTopic)).expect("Resub failed") - eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + (await net.subscriber.waku.subscribe(testTopic)).expect("Resub failed") + eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) discard (await net.publishToMesh(testTopic, "Msg 2".toBytes())).expect("Pub 2 failed") @@ -376,15 +377,15 @@ suite "Messaging API, SubscriptionManager": # generate two content topics that land in two different shards var i = 0 - while net.subscriber.node.getRelayShard(topicA) == - net.subscriber.node.getRelayShard(topicB): + while net.subscriber.waku.node.getRelayShard(topicA) == + net.subscriber.waku.node.getRelayShard(topicB): topicB = ContentTopic("/appB" & $i & "/2/shard-test-b/proto") inc i - (await net.subscriber.subscribe(topicA)).expect("failed to sub A") - (await net.subscriber.subscribe(topicB)).expect("failed to sub B") + (await net.subscriber.waku.subscribe(topicA)).expect("failed to sub A") + (await net.subscriber.waku.subscribe(topicB)).expect("failed to sub B") - let eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 2) + let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 2) defer: await eventManager.teardown() @@ -411,7 +412,7 @@ suite "Messaging API, SubscriptionManager": proc verifyNetworkState(expected: seq[ContentTopic]) {.async.} = let eventManager = - newReceiveEventListenerManager(net.subscriber.brokerCtx, expected.len) + newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, expected.len) for topic in allTopics: discard (await net.publishToMesh(topic, "Stress Payload".toBytes())).expect( @@ -439,7 +440,7 @@ suite "Messaging API, SubscriptionManager": # subscribe to all content topics we generated for t in allTopics: - (await net.subscriber.subscribe(t)).expect("sub failed") + (await net.subscriber.waku.subscribe(t)).expect("sub failed") activeSubs.add(t) await verifyNetworkState(activeSubs) @@ -447,7 +448,7 @@ suite "Messaging API, SubscriptionManager": # unsubscribe from some content topics for i in 0 ..< 50: let t = allTopics[i] - net.subscriber.unsubscribe(t).expect("unsub failed") + net.subscriber.waku.unsubscribe(t).expect("unsub failed") let idx = activeSubs.find(t) if idx >= 0: @@ -458,7 +459,7 @@ suite "Messaging API, SubscriptionManager": # re-subscribe to some content topics for i in 0 ..< 25: let t = allTopics[i] - (await net.subscriber.subscribe(t)).expect("resub failed") + (await net.subscriber.waku.subscribe(t)).expect("resub failed") activeSubs.add(t) await verifyNetworkState(activeSubs) @@ -469,9 +470,9 @@ suite "Messaging API, SubscriptionManager": await net.teardown() let testTopic = ContentTopic("/waku/2/test-content/proto") - (await net.subscriber.subscribe(testTopic)).expect("failed to subscribe") + (await net.subscriber.waku.subscribe(testTopic)).expect("failed to subscribe") - let eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: await eventManager.teardown() @@ -490,9 +491,9 @@ suite "Messaging API, SubscriptionManager": let subbedTopic = ContentTopic("/waku/2/subbed-topic/proto") let ignoredTopic = ContentTopic("/waku/2/ignored-topic/proto") - (await net.subscriber.subscribe(subbedTopic)).expect("failed to subscribe") + (await net.subscriber.waku.subscribe(subbedTopic)).expect("failed to subscribe") - let eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: await eventManager.teardown() @@ -510,10 +511,10 @@ suite "Messaging API, SubscriptionManager": let testTopic = ContentTopic("/waku/2/unsub-test/proto") - (await net.subscriber.subscribe(testTopic)).expect("failed to subscribe") - net.subscriber.unsubscribe(testTopic).expect("failed to unsubscribe") + (await net.subscriber.waku.subscribe(testTopic)).expect("failed to subscribe") + net.subscriber.waku.unsubscribe(testTopic).expect("failed to unsubscribe") - let eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: await eventManager.teardown() @@ -531,17 +532,17 @@ suite "Messaging API, SubscriptionManager": let topicA = ContentTopic("/waku/2/topic-a/proto") let topicB = ContentTopic("/waku/2/topic-b/proto") - (await net.subscriber.subscribe(topicA)).expect("failed to sub A") - (await net.subscriber.subscribe(topicB)).expect("failed to sub B") + (await net.subscriber.waku.subscribe(topicA)).expect("failed to sub A") + (await net.subscriber.waku.subscribe(topicB)).expect("failed to sub B") - let shard = net.subscriber.node.getRelayShard(topicA) + let shard = net.subscriber.waku.node.getRelayShard(topicA) await waitForEdgeSubs(net.subscriber, shard) - let eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: await eventManager.teardown() - net.subscriber.unsubscribe(topicA).expect("failed to unsub A") + net.subscriber.waku.unsubscribe(topicA).expect("failed to unsub A") discard (await net.publishToMesh(topicA, "Dropped Message".toBytes())).expect( "Publish A failed" @@ -560,9 +561,9 @@ suite "Messaging API, SubscriptionManager": let testTopic = ContentTopic("/waku/2/resub-test/proto") - (await net.subscriber.subscribe(testTopic)).expect("Initial sub failed") + (await net.subscriber.waku.subscribe(testTopic)).expect("Initial sub failed") - var eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + var eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) discard (await net.publishToMeshAfterEdgeReady(testTopic, "Msg 1".toBytes())).expect( "Pub 1 failed" ) @@ -570,8 +571,8 @@ suite "Messaging API, SubscriptionManager": require await eventManager.waitForEvents(TestTimeout) await eventManager.teardown() - net.subscriber.unsubscribe(testTopic).expect("Unsub failed") - eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + net.subscriber.waku.unsubscribe(testTopic).expect("Unsub failed") + eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) discard (await net.publishToMesh(testTopic, "Ghost".toBytes())).expect("Ghost pub failed") @@ -579,8 +580,8 @@ suite "Messaging API, SubscriptionManager": check not await eventManager.waitForEvents(NegativeTestTimeout) await eventManager.teardown() - (await net.subscriber.subscribe(testTopic)).expect("Resub failed") - eventManager = newReceiveEventListenerManager(net.subscriber.brokerCtx, 1) + (await net.subscriber.waku.subscribe(testTopic)).expect("Resub failed") + eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) discard (await net.publishToMeshAfterEdgeReady(testTopic, "Msg 2".toBytes())).expect( "Pub 2 failed" @@ -640,19 +641,19 @@ suite "Messaging API, SubscriptionManager": await meshBuddy.connectToNodes(@[publisherPeerInfo]) let conf = createApiNodeConf(cli_args.WakuMode.Edge, numShards) - var subscriber: Waku + var subscriber: LogosDelivery lockNewGlobalBrokerContext: - subscriber = (await createNode(conf)).expect("Failed to create edge subscriber") - subscriber.mountMessagingClient().expect("Failed to mount messaging") + subscriber = + (await LogosDelivery.new(conf)).expect("Failed to create edge subscriber") (await subscriber.start()).expect("Failed to start edge subscriber") # Connect edge subscriber to both filter servers so selectPeers finds both - await subscriber.node.connectToNodes(@[publisherPeerInfo, meshBuddyPeerInfo]) + await subscriber.waku.node.connectToNodes(@[publisherPeerInfo, meshBuddyPeerInfo]) let testTopic = ContentTopic("/waku/2/failover-test/proto") - let shard = subscriber.node.getRelayShard(testTopic) + let shard = subscriber.waku.node.getRelayShard(testTopic) - (await subscriber.subscribe(testTopic)).expect("Failed to subscribe") + (await subscriber.waku.subscribe(testTopic)).expect("Failed to subscribe") # Wait for dialing both filter servers (HealthyThreshold = 2) check await edgePeersReached(subscriber, shard, 2) @@ -660,7 +661,7 @@ suite "Messaging API, SubscriptionManager": # Verify message delivery with both servers alive await waitForMesh(publisher, shard) - var eventManager = newReceiveEventListenerManager(subscriber.brokerCtx, 1) + var eventManager = newReceiveEventListenerManager(subscriber.waku.brokerCtx, 1) let msg1 = WakuMessage( payload: "Before failover".toBytes(), contentTopic: testTopic, @@ -674,14 +675,14 @@ suite "Messaging API, SubscriptionManager": await eventManager.teardown() # Disconnect meshBuddy from edge (keeps relay mesh alive for publishing) - await subscriber.node.disconnectNode(meshBuddyPeerInfo) + await subscriber.waku.node.disconnectNode(meshBuddyPeerInfo) # Wait for the dead peer to be pruned check await edgePeersDroppedBelow(subscriber, shard, 2) - check subscriber.node.subscriptionManager.edgeFilterPeerCount(shard) >= 1 + check subscriber.waku.node.subscriptionManager.edgeFilterPeerCount(shard) >= 1 # Verify messages still arrive through the surviving filter server (publisher) - eventManager = newReceiveEventListenerManager(subscriber.brokerCtx, 1) + eventManager = newReceiveEventListenerManager(subscriber.waku.brokerCtx, 1) let msg2 = WakuMessage( payload: "After failover".toBytes(), contentTopic: testTopic, @@ -769,33 +770,33 @@ suite "Messaging API, SubscriptionManager": await sparePeer.connectToNodes(@[publisherPeerInfo]) let conf = createApiNodeConf(cli_args.WakuMode.Edge, numShards) - var subscriber: Waku + var subscriber: LogosDelivery lockNewGlobalBrokerContext: - subscriber = (await createNode(conf)).expect("Failed to create edge subscriber") - subscriber.mountMessagingClient().expect("Failed to mount messaging") + subscriber = + (await LogosDelivery.new(conf)).expect("Failed to create edge subscriber") (await subscriber.start()).expect("Failed to start edge subscriber") - await subscriber.node.connectToNodes( + await subscriber.waku.node.connectToNodes( @[publisherPeerInfo, meshBuddyPeerInfo, sparePeerInfo] ) let testTopic = ContentTopic("/waku/2/replacement-test/proto") - let shard = subscriber.node.getRelayShard(testTopic) + let shard = subscriber.waku.node.getRelayShard(testTopic) - (await subscriber.subscribe(testTopic)).expect("Failed to subscribe") + (await subscriber.waku.subscribe(testTopic)).expect("Failed to subscribe") # Wait for 2 confirmed peers (HealthyThreshold). The 3rd is available but not dialed. check await edgePeersReached(subscriber, shard, 2) - require subscriber.node.subscriptionManager.edgeFilterPeerCount(shard) == 2 + require subscriber.waku.node.subscriptionManager.edgeFilterPeerCount(shard) == 2 - await subscriber.node.disconnectNode(meshBuddyPeerInfo) + await subscriber.waku.node.disconnectNode(meshBuddyPeerInfo) # Wait for the sub loop to detect the loss and dial a replacement check await edgePeersReached(subscriber, shard, 2) await waitForMesh(publisher, shard) - var eventManager = newReceiveEventListenerManager(subscriber.brokerCtx, 1) + var eventManager = newReceiveEventListenerManager(subscriber.waku.brokerCtx, 1) let msg = WakuMessage( payload: "After replacement".toBytes(), contentTopic: testTopic, diff --git a/tests/channels/test_reliable_channel_send_receive.nim b/tests/channels/test_reliable_channel_send_receive.nim index ca6177e9d..881c22b3a 100644 --- a/tests/channels/test_reliable_channel_send_receive.nim +++ b/tests/channels/test_reliable_channel_send_receive.nim @@ -53,14 +53,12 @@ suite "Reliable Channel - ingress": contentTopic = ContentTopic("/reliable-channel/test/proto") let appPayload = "hello reliable channel".toBytes() - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager ## Noop encryption providers so the Encrypt/Decrypt brokers have @@ -121,14 +119,12 @@ suite "Reliable Channel - ingress": contentTopic = ContentTopic("/reliable-channel/test/proto") let appPayload = "foreign payload".toBytes() - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -180,14 +176,12 @@ suite "Reliable Channel - send state machine": contentTopic = ContentTopic("/reliable-channel/test/sm-success") fakeMsgReqId = RequestId("fake-msg-req-1") - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -247,14 +241,12 @@ suite "Reliable Channel - send state machine": channelId = ChannelId("sm-multi-channel") contentTopic = ContentTopic("/reliable-channel/test/sm-multi") - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -350,14 +342,12 @@ suite "Reliable Channel - send state machine": channelId = ChannelId("sm-race-channel") contentTopic = ContentTopic("/reliable-channel/test/sm-race") - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -459,12 +449,10 @@ suite "Reliable Channel - SDS persistence": Persistency.reset() removeDir(root) - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager lockNewGlobalBrokerContext: - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -529,14 +517,12 @@ suite "Reliable Channel - SDS lifecycle": let payload1 = "first message".toBytes() let payload2 = "second message".toBytes() - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -602,14 +588,12 @@ suite "Reliable Channel - SDS lifecycle": contentTopic = ContentTopic("/reliable-channel/test/dup") let appPayload = "deliver once".toBytes() - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -661,14 +645,12 @@ suite "Reliable Channel - SDS lifecycle": channelId = ChannelId("sds-foreign-channel") contentTopic = ContentTopic("/reliable-channel/test/foreign") - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -723,14 +705,12 @@ suite "Reliable Channel - SDS lifecycle": Persistency.reset() removeDir(root) - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -807,14 +787,12 @@ suite "Reliable Channel - SDS protocol semantics": channelId = ChannelId("sds-semantics-channel") contentTopic = ContentTopic("/reliable-channel/test/semantics") - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -877,14 +855,12 @@ suite "Reliable Channel - SDS protocol semantics": Persistency.reset() removeDir(root) - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -967,14 +943,12 @@ suite "Reliable Channel - SDS protocol semantics": let payloads = @["chain first".toBytes(), "chain second".toBytes(), "chain third".toBytes()] - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -1044,14 +1018,12 @@ suite "Reliable Channel - SDS protocol semantics": contentTopic = ContentTopic("/reliable-channel/test/sync") let appPayload = "real message".toBytes() - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -1119,14 +1091,12 @@ suite "Reliable Channel - SDS protocol semantics": contentTopic = ContentTopic("/reliable-channel/test/unique-id") let appPayload = "ok".toBytes() - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager setNoopEncryption() @@ -1189,12 +1159,10 @@ suite "Reliable Channel - SDS protocol semantics": (await waku.stop()).expect("stop") asyncTest "manager rejects operations on unknown channels": - var waku: Waku + var waku: LogosDelivery var manager: ReliableChannelManager lockNewGlobalBrokerContext: - waku = (await createNode(createApiNodeConf())).expect("createNode") - waku.mountMessagingClient().expect("mountMessagingClient") - waku.mountReliableChannelManager().expect("mountReliableChannelManager") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") manager = waku.reliableChannelManager check (await manager.send(ChannelId("no-such-channel"), "x".toBytes())).isErr() diff --git a/tests/node/test_wakunode_health_monitor.nim b/tests/node/test_wakunode_health_monitor.nim index 480cb235c..04a39455a 100644 --- a/tests/node/test_wakunode_health_monitor.nim +++ b/tests/node/test_wakunode_health_monitor.nim @@ -228,8 +228,9 @@ suite "Health Monitor - events": nodeA.mountMetadata(1, @[0'u16]).expect("Node A failed to mount metadata") await nodeA.start() - let ds = - MessagingClient.new(false, nodeA).expect("Failed to create MessagingClient") + let ds = MessagingClient + .new(MessagingClientConf(useP2PReliability: false), nodeA) + .expect("Failed to create MessagingClient") ds.start().expect("Failed to start MessagingClient") let monitorA = NodeHealthMonitor.new(nodeA) @@ -332,8 +333,9 @@ suite "Health Monitor - events": nodeA.mountMetadata(1, @[0'u16]).expect("Node A failed to mount metadata") await nodeA.start() - let ds = - MessagingClient.new(false, nodeA).expect("Failed to create MessagingClient") + let ds = MessagingClient + .new(MessagingClientConf(useP2PReliability: false), nodeA) + .expect("Failed to create MessagingClient") ds.start().expect("Failed to start MessagingClient") let subMgr = nodeA.subscriptionManager From 5309ce294b1986ad19b975b3882daef52d57db13 Mon Sep 17 00:00:00 2001 From: Ivan FB <128452529+Ivansete-status@users.noreply.github.com> Date: Tue, 23 Jun 2026 11:39:35 +0200 Subject: [PATCH 04/33] fix: retry send tasks stuck in Entry state --- .../send_service/send_processor.nim | 7 +++- tests/api/test_api_send.nim | 37 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/logos_delivery/messaging/delivery_service/send_service/send_processor.nim b/logos_delivery/messaging/delivery_service/send_service/send_processor.nim index 3782b9d4e..ae6d7141d 100644 --- a/logos_delivery/messaging/delivery_service/send_service/send_processor.nim +++ b/logos_delivery/messaging/delivery_service/send_service/send_processor.nim @@ -32,5 +32,10 @@ method process*( currentProcessor = currentProcessor.fallbackProcessor keepTrying = task.state == DeliveryState.FallbackRetry - if task.state == DeliveryState.FallbackRetry: + # A task still in `FallbackRetry` exhausted the chain without delivering, and + # one still in `Entry` was never attempted because no processor had a usable + # peer yet (e.g. a lightpush peer that finishes registering right after the + # first send). Both must be queued for the next round so the service loop + # retries them; otherwise the task would sit untouched until it ages out. + if task.state == DeliveryState.FallbackRetry or task.state == DeliveryState.Entry: task.state = DeliveryState.NextRoundRetry diff --git a/tests/api/test_api_send.nim b/tests/api/test_api_send.nim index 5c75f2e4a..0585998b2 100644 --- a/tests/api/test_api_send.nim +++ b/tests/api/test_api_send.nim @@ -352,6 +352,43 @@ suite "Waku API - Send": (await node.stop()).isOkOr: raiseAssert "Failed to stop node: " & error + asyncTest "Edge sender delivers via lightpush (no relay)": + ## Reproduces issue #3847: an Edge node (no relay mounted) that is only + ## connected to a lightpush-capable peer must deliver through lightpush. + var node: Waku + lockNewGlobalBrokerContext: + node = (await createNode(createApiNodeConf(cli_args.WakuMode.Edge))).valueOr: + raiseAssert error + node.mountMessagingClient().isOkOr: + raiseAssert "Failed to mount messaging: " & error + (await node.start()).isOkOr: + raiseAssert "Failed to start Waku node: " & error + + # Edge node has no relay; its only path to the network is the + # lightpush peer it is connected to. + await node.node.connectToNodes(@[lightpushNodePeerInfo]) + + check node.node.wakuRelay.isNil() + + let eventManager = newSendEventListenerManager(node.brokerCtx) + defer: + await eventManager.teardown() + + let envelope = MessageEnvelope.init( + ContentTopic("/waku/2/default-content/proto"), "test payload" + ) + + let requestId = (await node.send(envelope)).valueOr: + raiseAssert error + + const eventTimeout = 10.seconds + discard await eventManager.waitForEvents(eventTimeout) + + eventManager.validate({SendEventOutcome.Propagated}, requestId) + + (await node.stop()).isOkOr: + raiseAssert "Failed to stop node: " & error + asyncTest "Send fully validates fallback to lightpush": var node: LogosDelivery lockNewGlobalBrokerContext: From 8501d051a102f5844803c376fa19951b9cb2cffa Mon Sep 17 00:00:00 2001 From: NagyZoltanPeter <113987313+NagyZoltanPeter@users.noreply.github.com> Date: Tue, 23 Jun 2026 12:22:16 +0200 Subject: [PATCH 05/33] fix - Cap store checks on propagated messages by MessagingClient (#3965) * Frame send service store checks and cap to 1 min per task * Add test for store not verify case --- .../send_service/delivery_task.nim | 11 ++++ .../send_service/lightpush_processor.nim | 2 + .../send_service/relay_processor.nim | 2 + .../send_service/send_service.nim | 39 ++++++++++++- tests/api/test_api_send.nim | 57 +++++++++++++++++++ 5 files changed, 108 insertions(+), 3 deletions(-) diff --git a/logos_delivery/messaging/delivery_service/send_service/delivery_task.nim b/logos_delivery/messaging/delivery_service/send_service/delivery_task.nim index 1e38fcee1..685f9afce 100644 --- a/logos_delivery/messaging/delivery_service/send_service/delivery_task.nim +++ b/logos_delivery/messaging/delivery_service/send_service/delivery_task.nim @@ -24,6 +24,9 @@ type DeliveryTask* = ref object tryCount*: int state*: DeliveryState deliveryTime*: Moment + firstPropagatedTime*: Option[Moment] + ## Set once on the first successful propagation; never reset on re-publish. + ## Anchors the store-validation time cap (see propagationAge). propagateEventEmitted*: bool errorDesc*: string @@ -74,5 +77,13 @@ proc deliveryAge*(self: DeliveryTask): timer.Duration = else: ZeroDuration +proc propagationAge*(self: DeliveryTask): timer.Duration = + ## Time elapsed since the message was first successfully propagated. + ## Stable across re-publishes; ZeroDuration until first propagation. + if self.firstPropagatedTime.isSome(): + timer.Moment.now() - self.firstPropagatedTime.get() + else: + ZeroDuration + proc isEphemeral*(self: DeliveryTask): bool = return self.msg.ephemeral diff --git a/logos_delivery/messaging/delivery_service/send_service/lightpush_processor.nim b/logos_delivery/messaging/delivery_service/send_service/lightpush_processor.nim index 4105ba48d..ae5e775aa 100644 --- a/logos_delivery/messaging/delivery_service/send_service/lightpush_processor.nim +++ b/logos_delivery/messaging/delivery_service/send_service/lightpush_processor.nim @@ -71,6 +71,8 @@ method sendImpl*( requestId = task.requestId, msgHash = task.msgHash.to0xHex() task.state = DeliveryState.SuccessfullyPropagated task.deliveryTime = Moment.now() + if task.firstPropagatedTime.isNone(): + task.firstPropagatedTime = some(Moment.now()) # TODO: with a simple retry processor it might be more accurate to say `Sent` else: # Controversial state, publish says ok but no peer. It should not happen. diff --git a/logos_delivery/messaging/delivery_service/send_service/relay_processor.nim b/logos_delivery/messaging/delivery_service/send_service/relay_processor.nim index 23b5b846c..3acc44bb4 100644 --- a/logos_delivery/messaging/delivery_service/send_service/relay_processor.nim +++ b/logos_delivery/messaging/delivery_service/send_service/relay_processor.nim @@ -76,6 +76,8 @@ method sendImpl*(self: RelaySendProcessor, task: DeliveryTask) {.async.} = noOfPeers = noOfPublishedPeers task.state = DeliveryState.SuccessfullyPropagated task.deliveryTime = Moment.now() + if task.firstPropagatedTime.isNone(): + task.firstPropagatedTime = some(Moment.now()) else: # It shall not happen, but still covering it task.state = self.fallbackStateToSet diff --git a/logos_delivery/messaging/delivery_service/send_service/send_service.nim b/logos_delivery/messaging/delivery_service/send_service/send_service.nim index 92026be5d..00f2ff672 100644 --- a/logos_delivery/messaging/delivery_service/send_service/send_service.nim +++ b/logos_delivery/messaging/delivery_service/send_service/send_service.nim @@ -59,6 +59,7 @@ type SendService* = ref object of RootObj node: WakuNode checkStoreForMessages: bool + lastStoreCheckTime: Moment ## throttles store validation queries to ArchiveTime cadence proc setupSendProcessorChain( peerManager: PeerManager, @@ -117,6 +118,7 @@ proc new*( sendProcessor: sendProcessorChain, node: w, checkStoreForMessages: checkStoreForMessages, + lastStoreCheckTime: Moment.now(), ) return ok(sendService) @@ -163,11 +165,20 @@ proc checkStoredMessages(self: SendService) {.async.} = if not self.checkStoreForMessages: return + # Throttle store queries so they run at most every ArchiveTime (3s), regardless + # of the 1s service loop cadence. + if Moment.now() - self.lastStoreCheckTime < ArchiveTime: + return + let tasksToValidate = self.taskCache.filterIt( - it.state == DeliveryState.SuccessfullyPropagated and it.deliveryAge() > ArchiveTime and - not it.isEphemeral() + it.state == DeliveryState.SuccessfullyPropagated and + it.propagationAge() > ArchiveTime and not it.isEphemeral() ) + if tasksToValidate.len() == 0: + return + + self.lastStoreCheckTime = Moment.now() await self.checkMsgsInStore(tasksToValidate) proc reportTaskResult(self: SendService, task: DeliveryTask) = @@ -200,7 +211,10 @@ proc reportTaskResult(self: SendService, task: DeliveryTask) = # rest of the states are intermediate and does not translate to event discard - if task.messageAge() > MaxTimeInCache: + # Only tasks that never propagated are reported as hard send failures here. + # Propagated-but-not-store-validated tasks are handled (warn + drop, no event) + # in evaluateAndCleanUp. + if task.firstPropagatedTime.isNone() and task.messageAge() > MaxTimeInCache: error "Failed to send message", requestId = task.requestId, msgHash = task.msgHash.to0xHex(), @@ -229,6 +243,25 @@ proc evaluateAndCleanUp(self: SendService) = ) ) + # Store validation timed out: the message was propagated but never confirmed in a + # store node within MaxTimeInCache (measured from first propagation). Warn and drop + # without emitting an app event. + for task in self.taskCache: + if task.firstPropagatedTime.isSome() and + task.state != DeliveryState.SuccessfullyValidated and + task.propagationAge() > MaxTimeInCache: + warn "Message propagated but not validated by a store node within time window; stop trying.", + requestId = task.requestId, + msgHash = task.msgHash.to0xHex(), + propagationAge = task.propagationAge() + + self.taskCache.keepItIf( + not ( + it.firstPropagatedTime.isSome() and it.state != DeliveryState.SuccessfullyValidated and + it.propagationAge() > MaxTimeInCache + ) + ) + proc trySendMessages(self: SendService) {.async.} = let tasksToSend = self.taskCache.filterIt(it.state == DeliveryState.NextRoundRetry) diff --git a/tests/api/test_api_send.nim b/tests/api/test_api_send.nim index 0585998b2..13ec57c83 100644 --- a/tests/api/test_api_send.nim +++ b/tests/api/test_api_send.nim @@ -472,3 +472,60 @@ suite "Waku API - Send": eventManager.validate({SendEventOutcome.Error}, requestId) (await node.stop()).isOkOr: raiseAssert "Failed to stop node: " & error + + asyncTest "Store validation times out without event": + ## The message propagates successfully, but the only reachable store peer never + ## receives/archives it (it is outside the relay propagation path), so store + ## validation never confirms. After MaxTimeInCache the task must be dropped with a + ## warn log and NO app event: Propagated fires, but neither Sent nor Error - the + ## missing Sent event is the signal that delivery could not be validated. + var isolatedStoreNode: WakuNode + lockNewGlobalBrokerContext: + isolatedStoreNode = + newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + isolatedStoreNode.mountMetadata(3, @[0'u16]).isOkOr: + raiseAssert "Failed to mount metadata: " & error + (await isolatedStoreNode.mountRelay()).isOkOr: + raiseAssert "Failed to mount relay" + let archiveDriver = newSqliteArchiveDriver() + isolatedStoreNode.mountArchive(archiveDriver).isOkOr: + raiseAssert "Failed to mount archive: " & error + await isolatedStoreNode.mountStore() + await isolatedStoreNode.mountLibp2pPing() + await isolatedStoreNode.start() + # Deliberately NOT subscribed to the topic and NOT wired into the relay mesh, so + # it can answer store queries but never holds the published message. + let isolatedStoreNodePeerInfo = isolatedStoreNode.peerInfo.toRemotePeerInfo() + + var node: Waku + lockNewGlobalBrokerContext: + node = (await createNode(createApiNodeConf())).valueOr: + raiseAssert error + node.mountMessagingClient().isOkOr: + raiseAssert "Failed to mount messaging: " & error + (await node.start()).isOkOr: + raiseAssert "Failed to start Waku node: " & error + + # Propagate via relayNode1; store queries can only reach the isolated store node. + await node.node.connectToNodes(@[relayNode1PeerInfo, isolatedStoreNodePeerInfo]) + + let eventManager = newSendEventListenerManager(node.brokerCtx) + defer: + await eventManager.teardown() + + let envelope = MessageEnvelope.init( + ContentTopic("/waku/2/default-content/proto"), "test payload" + ) + + let requestId = (await node.send(envelope)).valueOr: + raiseAssert error + + # Must outlive MaxTimeInCache (1 min) so the store-validation timeout drop fires. + const eventTimeout = 65.seconds + discard await eventManager.waitForEvents(eventTimeout) + + eventManager.validate({SendEventOutcome.Propagated}, requestId) + + await isolatedStoreNode.stop() + (await node.stop()).isOkOr: + raiseAssert "Failed to stop node: " & error From b2795332149a1fc395b850790961cce9b17f45d4 Mon Sep 17 00:00:00 2001 From: Tanya S <120410716+stubbsta@users.noreply.github.com> Date: Wed, 24 Jun 2026 10:43:16 +0200 Subject: [PATCH 06/33] Apply LogosDelivery Orchestrator changes to api_send tests (#3976) --- tests/api/test_api_send.nim | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/tests/api/test_api_send.nim b/tests/api/test_api_send.nim index 13ec57c83..f64715592 100644 --- a/tests/api/test_api_send.nim +++ b/tests/api/test_api_send.nim @@ -355,22 +355,20 @@ suite "Waku API - Send": asyncTest "Edge sender delivers via lightpush (no relay)": ## Reproduces issue #3847: an Edge node (no relay mounted) that is only ## connected to a lightpush-capable peer must deliver through lightpush. - var node: Waku + var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await createNode(createApiNodeConf(cli_args.WakuMode.Edge))).valueOr: + node = (await LogosDelivery.new(createApiNodeConf(cli_args.WakuMode.Edge))).valueOr: raiseAssert error - node.mountMessagingClient().isOkOr: - raiseAssert "Failed to mount messaging: " & error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error # Edge node has no relay; its only path to the network is the # lightpush peer it is connected to. - await node.node.connectToNodes(@[lightpushNodePeerInfo]) + await node.waku.node.connectToNodes(@[lightpushNodePeerInfo]) - check node.node.wakuRelay.isNil() + check node.waku.node.wakuRelay.isNil() - let eventManager = newSendEventListenerManager(node.brokerCtx) + let eventManager = newSendEventListenerManager(node.waku.brokerCtx) defer: await eventManager.teardown() @@ -378,7 +376,7 @@ suite "Waku API - Send": ContentTopic("/waku/2/default-content/proto"), "test payload" ) - let requestId = (await node.send(envelope)).valueOr: + let requestId = (await node.messagingClient.send(envelope)).valueOr: raiseAssert error const eventTimeout = 10.seconds @@ -497,19 +495,19 @@ suite "Waku API - Send": # it can answer store queries but never holds the published message. let isolatedStoreNodePeerInfo = isolatedStoreNode.peerInfo.toRemotePeerInfo() - var node: Waku + var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await createNode(createApiNodeConf())).valueOr: + node = (await LogosDelivery.new(createApiNodeConf())).valueOr: raiseAssert error - node.mountMessagingClient().isOkOr: - raiseAssert "Failed to mount messaging: " & error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error # Propagate via relayNode1; store queries can only reach the isolated store node. - await node.node.connectToNodes(@[relayNode1PeerInfo, isolatedStoreNodePeerInfo]) + await node.waku.node.connectToNodes( + @[relayNode1PeerInfo, isolatedStoreNodePeerInfo] + ) - let eventManager = newSendEventListenerManager(node.brokerCtx) + let eventManager = newSendEventListenerManager(node.waku.brokerCtx) defer: await eventManager.teardown() @@ -517,7 +515,7 @@ suite "Waku API - Send": ContentTopic("/waku/2/default-content/proto"), "test payload" ) - let requestId = (await node.send(envelope)).valueOr: + let requestId = (await node.messagingClient.send(envelope)).valueOr: raiseAssert error # Must outlive MaxTimeInCache (1 min) so the store-validation timeout drop fires. From 5c20f48ab323d7212c07f3ed6c1440e2368de528 Mon Sep 17 00:00:00 2001 From: Ivan FB <128452529+Ivansete-status@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:44:44 +0200 Subject: [PATCH 07/33] chore: build C/C++ examples on macOS via POSIX getopt (#3982) --- examples/cbindings/waku_example.c | 77 +++++++++++-------------------- examples/cpp/waku.cpp | 77 +++++++++++-------------------- 2 files changed, 56 insertions(+), 98 deletions(-) diff --git a/examples/cbindings/waku_example.c b/examples/cbindings/waku_example.c index 4b7d32247..5c821e18e 100644 --- a/examples/cbindings/waku_example.c +++ b/examples/cbindings/waku_example.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -64,51 +64,35 @@ void *ctx; // For the case of C language we don't need to store a particular userData void *userData = NULL; -// Arguments parsing -static char doc[] = "\nC example that shows how to use the waku library."; -static char args_doc[] = ""; - -static struct argp_option options[] = { - {"host", 'h', "HOST", 0, "IP to listen for for LibP2P traffic. (default: \"0.0.0.0\")"}, - {"port", 'p', "PORT", 0, "TCP listening port. (default: \"60000\")"}, - {"key", 'k', "KEY", 0, "P2P node private key as 64 char hex string."}, - {"relay", 'r', "RELAY", 0, "Enable relay protocol: 1 or 0. (default: 1)"}, - {"peers", 'a', "PEERS", 0, "Comma-separated list of peer-multiaddress to connect\ - to. (default: \"\") e.g. \"/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmVFXtAfSj4EiR7mL2KvL4EE2wztuQgUSBoj2Jx2KeXFLN\""}, - {0}}; - -static error_t parse_opt(int key, char *arg, struct argp_state *state) +// Arguments parsing. Uses POSIX getopt so the example builds on glibc and on +// macOS/BSD alike (argp is a GNU libc extension not available everywhere). +static void parse_args(int argc, char **argv, struct ConfigNode *cfgNode) { - - struct ConfigNode *cfgNode = state->input; - switch (key) + int opt; + while ((opt = getopt(argc, argv, "h:p:k:r:a:")) != -1) { - case 'h': - snprintf(cfgNode->host, 128, "%s", arg); - break; - case 'p': - cfgNode->port = atoi(arg); - break; - case 'k': - snprintf(cfgNode->key, 128, "%s", arg); - break; - case 'r': - cfgNode->relay = atoi(arg); - break; - case 'a': - snprintf(cfgNode->peers, 2048, "%s", arg); - break; - case ARGP_KEY_ARG: - if (state->arg_num >= 1) /* Too many arguments. */ - argp_usage(state); - break; - case ARGP_KEY_END: - break; - default: - return ARGP_ERR_UNKNOWN; + switch (opt) + { + case 'h': + snprintf(cfgNode->host, 128, "%s", optarg); + break; + case 'p': + cfgNode->port = atoi(optarg); + break; + case 'k': + snprintf(cfgNode->key, 128, "%s", optarg); + break; + case 'r': + cfgNode->relay = atoi(optarg); + break; + case 'a': + snprintf(cfgNode->peers, 2048, "%s", optarg); + break; + default: + printf("Wrong parameters\n"); + exit(1); + } } - - return 0; } void signal_cond() @@ -119,8 +103,6 @@ void signal_cond() pthread_mutex_unlock(&mutex); } -static struct argp argp = {options, parse_opt, args_doc, doc, 0, 0, 0}; - void event_handler(int callerRet, const char *msg, size_t len, void *userData) { if (callerRet == RET_ERR) @@ -315,10 +297,7 @@ int main(int argc, char **argv) cfgNode.storeDbMigration = 0; cfgNode.storeMaxNumDbConnections = 30; - if (argp_parse(&argp, argc, argv, 0, 0, &cfgNode) == ARGP_ERR_UNKNOWN) - { - show_help_and_exit(); - } + parse_args(argc, argv, &cfgNode); char jsonConfig[5000]; snprintf(jsonConfig, 5000, "{ \ diff --git a/examples/cpp/waku.cpp b/examples/cpp/waku.cpp index b5a36cd49..0f91cbc14 100644 --- a/examples/cpp/waku.cpp +++ b/examples/cpp/waku.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -60,51 +60,35 @@ struct ConfigNode char peers[2048]; }; -// Arguments parsing -static char doc[] = "\nC example that shows how to use the waku library."; -static char args_doc[] = ""; - -static struct argp_option options[] = { - {"host", 'h', "HOST", 0, "IP to listen for for LibP2P traffic. (default: \"0.0.0.0\")"}, - {"port", 'p', "PORT", 0, "TCP listening port. (default: \"60000\")"}, - {"key", 'k', "KEY", 0, "P2P node private key as 64 char hex string."}, - {"relay", 'r', "RELAY", 0, "Enable relay protocol: 1 or 0. (default: 1)"}, - {"peers", 'a', "PEERS", 0, "Comma-separated list of peer-multiaddress to connect\ - to. (default: \"\") e.g. \"/ip4/127.0.0.1/tcp/60001/p2p/16Uiu2HAmVFXtAfSj4EiR7mL2KvL4EE2wztuQgUSBoj2Jx2KeXFLN\""}, - {0}}; - -static error_t parse_opt(int key, char *arg, struct argp_state *state) +// Arguments parsing. Uses POSIX getopt so the example builds on glibc and on +// macOS/BSD alike (argp is a GNU libc extension not available everywhere). +static void parse_args(int argc, char **argv, struct ConfigNode *cfgNode) { - - struct ConfigNode *cfgNode = (ConfigNode *)state->input; - switch (key) + int opt; + while ((opt = getopt(argc, argv, "h:p:k:r:a:")) != -1) { - case 'h': - snprintf(cfgNode->host, 128, "%s", arg); - break; - case 'p': - cfgNode->port = atoi(arg); - break; - case 'k': - snprintf(cfgNode->key, 128, "%s", arg); - break; - case 'r': - cfgNode->relay = atoi(arg); - break; - case 'a': - snprintf(cfgNode->peers, 2048, "%s", arg); - break; - case ARGP_KEY_ARG: - if (state->arg_num >= 1) /* Too many arguments. */ - argp_usage(state); - break; - case ARGP_KEY_END: - break; - default: - return ARGP_ERR_UNKNOWN; + switch (opt) + { + case 'h': + snprintf(cfgNode->host, 128, "%s", optarg); + break; + case 'p': + cfgNode->port = atoi(optarg); + break; + case 'k': + snprintf(cfgNode->key, 128, "%s", optarg); + break; + case 'r': + cfgNode->relay = atoi(optarg); + break; + case 'a': + snprintf(cfgNode->peers, 2048, "%s", optarg); + break; + default: + printf("Wrong parameters\n"); + exit(1); + } } - - return 0; } void event_handler(const char *msg, size_t len) @@ -129,8 +113,6 @@ auto cify(F &&f) }; } -static struct argp argp = {options, parse_opt, args_doc, doc, 0, 0, 0}; - // Beginning of UI program logic enum PROGRAM_STATE @@ -254,10 +236,7 @@ int main(int argc, char **argv) cfgNode.port = 60000; cfgNode.relay = 1; - if (argp_parse(&argp, argc, argv, 0, 0, &cfgNode) == ARGP_ERR_UNKNOWN) - { - show_help_and_exit(); - } + parse_args(argc, argv, &cfgNode); char jsonConfig[2048]; snprintf(jsonConfig, 2048, "{ \ From 56b92cfd71a8256c52e724fcbc930babc21b4424 Mon Sep 17 00:00:00 2001 From: Fabiana Cecin Date: Thu, 25 Jun 2026 04:11:52 -0300 Subject: [PATCH 08/33] chore: Fix .md docs (fix broken links, add QUIC notes) (#3988) --- README.md | 7 +++++-- docs/operators/how-to/configure-quic.md | 20 ++++++++++++++++++++ docs/operators/how-to/configure.md | 5 +++-- docs/operators/how-to/run.md | 6 ++++++ logos_delivery/waku/README.md | 16 ++++++++++++++++ 5 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 docs/operators/how-to/configure-quic.md diff --git a/README.md b/README.md index f227ea483..b94f5d491 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ private communications. - Examples. - Various tests of above. -For more details see the [source code](waku/README.md) +For more details see the [source code](logos_delivery/waku/README.md) ## How to Build & Run ( Linux, MacOS & WSL ) @@ -48,11 +48,14 @@ 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](https://github.com/logos-messaging/logos-delivery/blob/master/waku/README.md) for more information. +Please refer to the [Waku README](https://github.com/logos-messaging/logos-delivery/blob/master/logos_delivery/waku/README.md) for more information. For more on how to run `wakunode2`, refer to: - [Run using binaries](https://docs.waku.org/run-node/build-source) diff --git a/docs/operators/how-to/configure-quic.md b/docs/operators/how-to/configure-quic.md new file mode 100644 index 000000000..188433f8b --- /dev/null +++ b/docs/operators/how-to/configure-quic.md @@ -0,0 +1,20 @@ +# Configure QUIC transport + +QUIC is a UDP-based transport. Enabling it allows peers to connect to your node over QUIC, in addition to the default TCP transport. + +To enable QUIC, use the `--quic-support` option. +Note, the default port for QUIC is 60000. + +```shell +wakunode2 --quic-support=true +``` + +To listen on a different UDP port, use `--quic-port`: + +```shell +wakunode2 --quic-support=true --quic-port= +``` + +QUIC runs alongside the existing TCP transport. The node keeps listening on TCP and announces a `/udp//quic-v1` address in its ENR, so peers that support QUIC can connect over it while others continue to use TCP. + +If you restrict the node's announced addresses with `--ext-multiaddr-only`, the QUIC address is no longer announced automatically. In that case, include the QUIC multiaddr in `--ext-multiaddr` yourself, for example `/ip4//udp//quic-v1`. diff --git a/docs/operators/how-to/configure.md b/docs/operators/how-to/configure.md index f052b22be..b7ef2a57b 100644 --- a/docs/operators/how-to/configure.md +++ b/docs/operators/how-to/configure.md @@ -132,5 +132,6 @@ This is an index of tutorials explaining how to configure your nwaku node for di 4. [Configure store protocol and message store](./configure-store.md) 5. [Generate and configure a node key](./configure-key.md) 6. [Configure websocket transport](./configure-websocket.md) -7. [Run nwaku with rate limiting enabled](./run-with-rln.md) -8. [Configure a REST API node](./configure-rest-api.md) +7. [Configure QUIC transport](./configure-quic.md) +8. [Run nwaku with rate limiting enabled](./run-with-rln.md) +9. [Configure a REST API node](./configure-rest-api.md) diff --git a/docs/operators/how-to/run.md b/docs/operators/how-to/run.md index bc9039479..b6801f311 100644 --- a/docs/operators/how-to/run.md +++ b/docs/operators/how-to/run.md @@ -56,6 +56,12 @@ and websocket address /ip4/0.0.0.0/tcp/8000/ws/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H ``` +If your node is running with QUIC enabled, the log also includes a QUIC address + +``` +/ip4/0.0.0.0/udp/60000/quic-v1/p2p/16Uiu2HAkzjwwgEAXfeGNMKFPSpc6vGBRqCdTLG5q3Gmk2v4pQw7H +``` + You can also query a running node for its listening addresses using the REST API. diff --git a/logos_delivery/waku/README.md b/logos_delivery/waku/README.md index d9f160cb5..73af43fe4 100644 --- a/logos_delivery/waku/README.md +++ b/logos_delivery/waku/README.md @@ -232,4 +232,20 @@ However, they can be used for local testing purposes: mkdir -p ./ssl_dir/ openssl req -x509 -newkey rsa:4096 -keyout ./ssl_dir/key.pem -out ./ssl_dir/cert.pem -sha256 -nodes wakunode2 --websocket-secure-support=true --websocket-secure-key-path="./ssl_dir/key.pem" --websocket-secure-cert-path="./ssl_dir/cert.pem" +``` + +## Enabling QUIC + +QUIC is a UDP-based transport that peers can use to connect to your node. + +The default port for QUIC is 60000. + +```shell +wakunode2 --quic-support=true +``` + +To listen on a different UDP port, use `--quic-port`: + +```shell +wakunode2 --quic-support=true --quic-port= ``` \ No newline at end of file From 6d35800fce92ea5952b0bda654fa9ff027d8b462 Mon Sep 17 00:00:00 2001 From: NagyZoltanPeter <113987313+NagyZoltanPeter@users.noreply.github.com> Date: Thu, 25 Jun 2026 09:27:01 +0200 Subject: [PATCH 09/33] Chore: api shape phase2 (#3974) * Move waku.nim from waku/factory to under waku/ * remove unused * Realize Kernel API in scope of Waku class * Refactor waku/api into messaging_client, waku/api/types and api_conf into logos_delivery/api * Make liblogosdelivery and wakunode2 compile, remove waku/api.nim as it was just a import orchestrator * make test compile and run * Reconcile master's new send tests to LogosDelivery API after rebase master commits #3965/#3669-followup added two test cases (Edge lightpush delivery #3847, store-validation timeout) written against the removed waku/api.nim createNode helper. Rewrite them to the LogosDelivery shape: createNode -> LogosDelivery.new, node.node -> node.waku.node, node.brokerCtx -> node.waku.brokerCtx, node.send -> node.messagingClient.send, and drop the now-implicit mountMessagingClient calls (LogosDelivery.new mounts the client internally). --- .../liteprotocoltester/liteprotocoltester.nim | 2 +- apps/wakunode2/wakunode2.nim | 2 +- examples/wakustealthcommitments/node_spec.nim | 2 +- .../json_connection_status_change_event.nim | 2 +- library/kernel_api/debug_node_api.nim | 2 +- library/kernel_api/discovery_api.nim | 2 +- library/kernel_api/node_lifecycle_api.nim | 2 +- library/kernel_api/peer_manager_api.nim | 2 +- library/kernel_api/ping_api.nim | 4 +- library/kernel_api/protocols/filter_api.nim | 2 +- .../kernel_api/protocols/lightpush_api.nim | 2 +- library/kernel_api/protocols/relay_api.nim | 3 +- library/kernel_api/protocols/store_api.nim | 2 +- library/liblogosdelivery.nim | 2 +- library/logos_delivery_api/messaging_api.nim | 8 +- library/logos_delivery_api/node_api.nim | 3 +- logos_delivery/{waku => }/api/api_conf.nim | 0 logos_delivery/{waku => }/api/types.nim | 3 +- logos_delivery/channels/reliable_channel.nim | 4 +- .../channels/reliable_channel_manager.nim | 2 +- logos_delivery/channels/types.nim | 2 +- logos_delivery/logos_delivery.nim | 19 +- .../send_service/delivery_task.nim | 2 +- .../send_service/relay_processor.nim | 2 +- logos_delivery/messaging/messaging_client.nim | 22 +- logos_delivery/waku/api.nim | 5 - logos_delivery/waku/api/api.nim | 48 -- logos_delivery/waku/api/send_api.md | 46 -- logos_delivery/waku/events/health_events.nim | 2 +- logos_delivery/waku/events/message_events.nim | 3 +- .../node/health_monitor/connection_status.nim | 3 +- .../health_monitor/node_health_monitor.nim | 2 +- .../waku/requests/health_requests.nim | 2 +- .../waku/rest_api/endpoint/health/types.nim | 3 +- logos_delivery/waku/{factory => }/waku.nim | 431 +++++++++++++++++- tests/api/test_api_receive.nim | 2 +- tests/api/test_api_subscription.nim | 86 ++-- tests/api/test_node_conf.nim | 4 +- .../test_reliable_channel_send_receive.nim | 32 +- tests/test_waku.nim | 50 +- tests/waku_discv5/test_waku_discv5.nim | 2 +- tests/wakunode2/test_app.nim | 2 +- 42 files changed, 598 insertions(+), 223 deletions(-) rename logos_delivery/{waku => }/api/api_conf.nim (100%) rename logos_delivery/{waku => }/api/types.nim (98%) delete mode 100644 logos_delivery/waku/api.nim delete mode 100644 logos_delivery/waku/api/api.nim delete mode 100644 logos_delivery/waku/api/send_api.md rename logos_delivery/waku/{factory => }/waku.nim (59%) diff --git a/apps/liteprotocoltester/liteprotocoltester.nim b/apps/liteprotocoltester/liteprotocoltester.nim index dd3097e49..124d09173 100644 --- a/apps/liteprotocoltester/liteprotocoltester.nim +++ b/apps/liteprotocoltester/liteprotocoltester.nim @@ -14,7 +14,7 @@ import logos_delivery/waku/[ common/enr, common/logging, - factory/waku as waku_factory, + waku as waku_factory, waku_node, node/waku_metrics, node/peer_manager, diff --git a/apps/wakunode2/wakunode2.nim b/apps/wakunode2/wakunode2.nim index e61b76e9c..a75b472ec 100644 --- a/apps/wakunode2/wakunode2.nim +++ b/apps/wakunode2/wakunode2.nim @@ -11,7 +11,7 @@ import ../../tools/[rln_keystore_generator/rln_keystore_generator, confutils/cli_args], logos_delivery/waku/[ common/logging, - factory/waku, + waku, node/health_monitor, rest_api/endpoint/builder as rest_server_builder, waku_core/message/default_values, diff --git a/examples/wakustealthcommitments/node_spec.nim b/examples/wakustealthcommitments/node_spec.nim index 3f4deeb08..e8e0a2062 100644 --- a/examples/wakustealthcommitments/node_spec.nim +++ b/examples/wakustealthcommitments/node_spec.nim @@ -1,7 +1,7 @@ {.push raises: [].} import tools/confutils/cli_args -import logos_delivery/waku/[common/logging, factory/[waku, networks_config]] +import logos_delivery/waku/[common/logging, waku, factory/networks_config] import std/[options, strutils, os, sequtils], chronicles, diff --git a/library/events/json_connection_status_change_event.nim b/library/events/json_connection_status_change_event.nim index 9b8a0f9e6..f5af78f24 100644 --- a/library/events/json_connection_status_change_event.nim +++ b/library/events/json_connection_status_change_event.nim @@ -2,7 +2,7 @@ import system, std/json import ./json_base_event -import ../../logos_delivery/waku/api/types +import ../../logos_delivery/api/types type JsonConnectionStatusChangeEvent* = ref object of JsonEvent status*: ConnectionStatus diff --git a/library/kernel_api/debug_node_api.nim b/library/kernel_api/debug_node_api.nim index b1229d660..44764a673 100644 --- a/library/kernel_api/debug_node_api.nim +++ b/library/kernel_api/debug_node_api.nim @@ -9,7 +9,7 @@ import metrics, ffi import - logos_delivery/waku/factory/waku, + logos_delivery/waku/waku, logos_delivery/waku/node/waku_node, logos_delivery/waku/node/health_monitor, library/declare_lib diff --git a/library/kernel_api/discovery_api.nim b/library/kernel_api/discovery_api.nim index 7ae7d1dea..158c3a925 100644 --- a/library/kernel_api/discovery_api.nim +++ b/library/kernel_api/discovery_api.nim @@ -2,7 +2,7 @@ import logos_delivery/waku/compat/option_valueor import std/json import chronos, chronicles, results, strutils, libp2p/multiaddress, ffi import - logos_delivery/waku/factory/waku, + logos_delivery/waku/waku, logos_delivery/waku/discovery/waku_dnsdisc, logos_delivery/waku/discovery/waku_discv5, logos_delivery/waku/waku_core/peers, diff --git a/library/kernel_api/node_lifecycle_api.nim b/library/kernel_api/node_lifecycle_api.nim index 279480031..dbf7ad9b1 100644 --- a/library/kernel_api/node_lifecycle_api.nim +++ b/library/kernel_api/node_lifecycle_api.nim @@ -5,7 +5,7 @@ import chronos, chronicles, results, confutils, confutils/std/net, ffi import logos_delivery/waku/node/peer_manager/peer_manager, tools/confutils/cli_args, - logos_delivery/waku/factory/waku, + logos_delivery/waku/waku, logos_delivery/waku/factory/node_factory, logos_delivery/waku/factory/app_callbacks, logos_delivery/waku/rest_api/endpoint/builder, diff --git a/library/kernel_api/peer_manager_api.nim b/library/kernel_api/peer_manager_api.nim index 38c15fc3d..eeea2c63f 100644 --- a/library/kernel_api/peer_manager_api.nim +++ b/library/kernel_api/peer_manager_api.nim @@ -2,7 +2,7 @@ import logos_delivery/waku/compat/option_valueor import std/[sequtils, strutils, tables] import chronicles, chronos, results, options, json, ffi import - logos_delivery/waku/factory/waku, + logos_delivery/waku/waku, logos_delivery/waku/node/waku_node, logos_delivery/waku/node/peer_manager, library/declare_lib diff --git a/library/kernel_api/ping_api.nim b/library/kernel_api/ping_api.nim index 8313004d3..e6ed69dd1 100644 --- a/library/kernel_api/ping_api.nim +++ b/library/kernel_api/ping_api.nim @@ -1,9 +1,7 @@ import std/[json, strutils] import chronos, results, ffi import libp2p/[protocols/ping, switch, multiaddress, multicodec] -import - logos_delivery/waku/[factory/waku, waku_core/peers, node/waku_node], - library/declare_lib +import logos_delivery/waku/[waku, waku_core/peers, node/waku_node], library/declare_lib proc waku_ping_peer( ctx: ptr FFIContext[LogosDelivery], diff --git a/library/kernel_api/protocols/filter_api.nim b/library/kernel_api/protocols/filter_api.nim index fb1c905ca..a070bd2c7 100644 --- a/library/kernel_api/protocols/filter_api.nim +++ b/library/kernel_api/protocols/filter_api.nim @@ -4,7 +4,7 @@ import chronicles, chronos, results, ffi import logos_delivery/waku/waku_filter_v2/client, logos_delivery/waku/waku_core/message/message, - logos_delivery/waku/factory/waku, + logos_delivery/waku/waku, logos_delivery/waku/waku_relay, logos_delivery/waku/waku_filter_v2/common, logos_delivery/waku/waku_core/subscription/push_handler, diff --git a/library/kernel_api/protocols/lightpush_api.nim b/library/kernel_api/protocols/lightpush_api.nim index 287e37aa5..eb0d1de09 100644 --- a/library/kernel_api/protocols/lightpush_api.nim +++ b/library/kernel_api/protocols/lightpush_api.nim @@ -4,7 +4,7 @@ import chronicles, chronos, results, ffi import logos_delivery/waku/waku_core/message/message, logos_delivery/waku/waku_core/codecs, - logos_delivery/waku/factory/waku, + logos_delivery/waku/waku, logos_delivery/waku/waku_core/message, logos_delivery/waku/waku_core/topics/pubsub_topic, logos_delivery/waku/waku_lightpush_legacy/client, diff --git a/library/kernel_api/protocols/relay_api.nim b/library/kernel_api/protocols/relay_api.nim index 2195cdffd..d580597ae 100644 --- a/library/kernel_api/protocols/relay_api.nim +++ b/library/kernel_api/protocols/relay_api.nim @@ -3,7 +3,8 @@ import std/[net, sequtils, strutils, json], strformat import chronicles, chronos, stew/byteutils, results, ffi import logos_delivery/waku/waku_core/message/message, - logos_delivery/waku/factory/[validator_signed, waku], + logos_delivery/waku/factory/validator_signed, + logos_delivery/waku/waku, tools/confutils/cli_args, logos_delivery/waku/waku_core/message, logos_delivery/waku/waku_core/topics/pubsub_topic, diff --git a/library/kernel_api/protocols/store_api.nim b/library/kernel_api/protocols/store_api.nim index 7083f93f2..c6356bf6a 100644 --- a/library/kernel_api/protocols/store_api.nim +++ b/library/kernel_api/protocols/store_api.nim @@ -2,7 +2,7 @@ import logos_delivery/waku/compat/option_valueor import std/[json, sugar, strutils, options] import chronos, chronicles, results, stew/byteutils, ffi import - logos_delivery/waku/factory/waku, + logos_delivery/waku/waku, library/utils, logos_delivery/waku/waku_core/peers, logos_delivery/waku/waku_core/message/digest, diff --git a/library/liblogosdelivery.nim b/library/liblogosdelivery.nim index 8dbcfac29..0ead080bc 100644 --- a/library/liblogosdelivery.nim +++ b/library/liblogosdelivery.nim @@ -6,7 +6,7 @@ import logos_delivery/waku/waku_core/topics/pubsub_topic, logos_delivery/waku/waku_relay, logos_delivery, - logos_delivery/waku/factory/waku, + logos_delivery/waku/waku, logos_delivery/waku/node/waku_node, logos_delivery/waku/node/health_monitor/health_status, ../logos_delivery/waku/factory/app_callbacks, diff --git a/library/logos_delivery_api/messaging_api.nim b/library/logos_delivery_api/messaging_api.nim index 3c86138b4..0e4dc7449 100644 --- a/library/logos_delivery_api/messaging_api.nim +++ b/library/logos_delivery_api/messaging_api.nim @@ -3,9 +3,9 @@ import chronos, results, ffi import stew/byteutils import logos_delivery/waku/common/base64, - logos_delivery/waku/factory/waku, + logos_delivery/waku/waku, logos_delivery/waku/waku_core/topics/content_topic, - logos_delivery/waku/api/[api, types], + logos_delivery/api/types, ../declare_lib proc logosdelivery_subscribe( @@ -20,7 +20,7 @@ proc logosdelivery_subscribe( # ContentTopic is just a string type alias let contentTopic = ContentTopic($contentTopicStr) - (await api.subscribe(ctx.myLib[].waku, contentTopic)).isOkOr: + (await ctx.myLib[].messagingClient.subscribe(contentTopic)).isOkOr: let errMsg = $error return err("Subscribe failed: " & errMsg) @@ -38,7 +38,7 @@ proc logosdelivery_unsubscribe( # ContentTopic is just a string type alias let contentTopic = ContentTopic($contentTopicStr) - api.unsubscribe(ctx.myLib[].waku, contentTopic).isOkOr: + ctx.myLib[].messagingClient.unsubscribe(contentTopic).isOkOr: let errMsg = $error return err("Unsubscribe failed: " & errMsg) diff --git a/library/logos_delivery_api/node_api.nim b/library/logos_delivery_api/node_api.nim index f6a0945ec..354ab3a98 100644 --- a/library/logos_delivery_api/node_api.nim +++ b/library/logos_delivery_api/node_api.nim @@ -3,7 +3,8 @@ import chronos, chronicles, results, ffi import logos_delivery, logos_delivery/waku/node/waku_node, - logos_delivery/waku/api/[api, types], + logos_delivery/waku/events/message_events, + logos_delivery/api/types, logos_delivery/waku/events/[message_events, health_events], tools/confutils/conf_from_json, ../declare_lib, diff --git a/logos_delivery/waku/api/api_conf.nim b/logos_delivery/api/api_conf.nim similarity index 100% rename from logos_delivery/waku/api/api_conf.nim rename to logos_delivery/api/api_conf.nim diff --git a/logos_delivery/waku/api/types.nim b/logos_delivery/api/types.nim similarity index 98% rename from logos_delivery/waku/api/types.nim rename to logos_delivery/api/types.nim index d51d46994..5757a8e82 100644 --- a/logos_delivery/waku/api/types.nim +++ b/logos_delivery/api/types.nim @@ -6,7 +6,8 @@ import bearssl/rand, std/times, chronos import stew/byteutils import logos_delivery/waku/utils/requests as request_utils import logos_delivery/waku/waku_core/[topics/content_topic, message/message, time] -import logos_delivery/waku/requests/requests + +export content_topic, message type MessageEnvelope* = object diff --git a/logos_delivery/channels/reliable_channel.nim b/logos_delivery/channels/reliable_channel.nim index 5a7ab24d4..307dc17a4 100644 --- a/logos_delivery/channels/reliable_channel.nim +++ b/logos_delivery/channels/reliable_channel.nim @@ -21,7 +21,7 @@ import bearssl/rand import stew/byteutils import libp2p/crypto/crypto as libp2p_crypto -import logos_delivery/waku/api/types +import logos_delivery/api/types import logos_delivery/messaging/delivery_service/send_service import logos_delivery/waku/waku_core/topics @@ -135,7 +135,7 @@ proc tryFinalizeChannelReq(self: ReliableChannel, channelReqId: RequestId) = ## and the total number of confirmed + failed segments equals the total expected segments. ## Therefore, the channel-level request is removed from `self.channelReqs` ## and the appropriate final event is emitted. - ## + ## let state = self.channelReqs.getOrDefault(channelReqId) if state.totalExpectedSegments == 0: ## Either already finalized (and removed) or never inserted. diff --git a/logos_delivery/channels/reliable_channel_manager.nim b/logos_delivery/channels/reliable_channel_manager.nim index 887444b19..29feab0b9 100644 --- a/logos_delivery/channels/reliable_channel_manager.nim +++ b/logos_delivery/channels/reliable_channel_manager.nim @@ -15,7 +15,7 @@ import brokers/broker_context import logos_delivery/waku/events/message_events as waku_message_events import logos_delivery/messaging/messaging_client -import logos_delivery/waku/api/types +import logos_delivery/api/types import logos_delivery/waku/waku_core/topics import logos_delivery/waku/persistency/sds_persistency diff --git a/logos_delivery/channels/types.nim b/logos_delivery/channels/types.nim index ec663cf7b..7730c5c58 100644 --- a/logos_delivery/channels/types.nim +++ b/logos_delivery/channels/types.nim @@ -1,7 +1,7 @@ ## Core identifier types for the Reliable Channel API. import std/hashes -import logos_delivery/waku/api/types as api_types +import logos_delivery/api/types as api_types import ./scalable_data_sync/scalable_data_sync diff --git a/logos_delivery/logos_delivery.nim b/logos_delivery/logos_delivery.nim index f01cf4207..f61202b9a 100644 --- a/logos_delivery/logos_delivery.nim +++ b/logos_delivery/logos_delivery.nim @@ -11,9 +11,7 @@ import results, chronos, chronicles -import logos_delivery/waku/api -export api -import logos_delivery/waku/factory/waku +import logos_delivery/waku/waku export waku import logos_delivery/messaging/messaging_client export messaging_client @@ -22,7 +20,8 @@ export reliable_channel_manager import logos_delivery/waku/factory/waku_conf import logos_delivery/waku/factory/app_callbacks -import logos_delivery/waku/api/[api_conf, types] +import tools/confutils/cli_args +import logos_delivery/waku/node/health_monitor/online_monitor logScope: topics = "logosdelivery" @@ -82,6 +81,13 @@ proc new*( proc start*(self: LogosDelivery): Future[Result[void, string]] {.async.} = ## Starts each layer bottom-up: transport first, then messaging, then channels. + if self.waku.isNil(): + return err("Waku node is not initialized") + if self.messagingClient.isNil(): + return err("MessagingClient is not initialized") + if self.reliableChannelManager.isNil(): + return err("ReliableChannelManager is not initialized") + (await self.waku.start()).isOkOr: return err("failed to start Waku: " & error) @@ -102,3 +108,8 @@ proc stop*(self: LogosDelivery): Future[Result[void, string]] {.async.} = return err("failed to stop Waku: " & error) return ok() + +proc isOnline*(self: LogosDelivery): Future[Result[bool, string]] {.async.} = + if self.waku.isNil(): + return err("Waku node is not initialized") + return ok(self.waku.healthMonitor.onlineMonitor.amIOnline()) diff --git a/logos_delivery/messaging/delivery_service/send_service/delivery_task.nim b/logos_delivery/messaging/delivery_service/send_service/delivery_task.nim index 685f9afce..1f76078fe 100644 --- a/logos_delivery/messaging/delivery_service/send_service/delivery_task.nim +++ b/logos_delivery/messaging/delivery_service/send_service/delivery_task.nim @@ -3,7 +3,7 @@ import std/[options, times], chronos import brokers/broker_context import logos_delivery/waku/waku_core, - logos_delivery/waku/api/types, + logos_delivery/api/types, logos_delivery/waku/requests/node_requests type DeliveryState* {.pure.} = enum diff --git a/logos_delivery/messaging/delivery_service/send_service/relay_processor.nim b/logos_delivery/messaging/delivery_service/send_service/relay_processor.nim index 3acc44bb4..dc40b797d 100644 --- a/logos_delivery/messaging/delivery_service/send_service/relay_processor.nim +++ b/logos_delivery/messaging/delivery_service/send_service/relay_processor.nim @@ -4,7 +4,7 @@ import chronos, chronicles import brokers/broker_context import logos_delivery/waku/[waku_core], logos_delivery/waku/waku_lightpush/[common, rpc] import logos_delivery/waku/requests/health_requests -import logos_delivery/waku/api/types +import logos_delivery/api/types import ./[delivery_task, send_processor] logScope: diff --git a/logos_delivery/messaging/messaging_client.nim b/logos_delivery/messaging/messaging_client.nim index 652671e46..96cd13eb1 100644 --- a/logos_delivery/messaging/messaging_client.nim +++ b/logos_delivery/messaging/messaging_client.nim @@ -1,7 +1,7 @@ import results, chronos import chronicles import - logos_delivery/waku/api/types, + logos_delivery/api/types, logos_delivery/waku/node/[waku_node, subscription_manager], logos_delivery/messaging/delivery_service/[recv_service, send_service], logos_delivery/messaging/delivery_service/send_service/delivery_task @@ -43,6 +43,26 @@ proc stop*(self: MessagingClient) {.async.} = await self.recvService.stopRecvService() self.started = false +proc checkApiAvailability(self: MessagingClient): Result[void, string] = + if self.isNil(): + return err("MessagingClient is not initialized") + + return ok() + +proc subscribe*( + self: MessagingClient, contentTopic: ContentTopic +): Future[Result[void, string]] {.async.} = + ?checkApiAvailability(self) + + return self.node.subscriptionManager.subscribe(contentTopic) + +proc unsubscribe*( + self: MessagingClient, contentTopic: ContentTopic +): Result[void, string] = + ?checkApiAvailability(self) + + return self.node.subscriptionManager.unsubscribe(contentTopic) + proc send*( self: MessagingClient, envelope: MessageEnvelope ): Future[Result[RequestId, string]] {.async.} = diff --git a/logos_delivery/waku/api.nim b/logos_delivery/waku/api.nim deleted file mode 100644 index a977a062a..000000000 --- a/logos_delivery/waku/api.nim +++ /dev/null @@ -1,5 +0,0 @@ -import ./api/[api, api_conf] -import ./events/message_events -import tools/confutils/entry_nodes - -export api, api_conf, entry_nodes, message_events diff --git a/logos_delivery/waku/api/api.nim b/logos_delivery/waku/api/api.nim deleted file mode 100644 index 0ffc38dc8..000000000 --- a/logos_delivery/waku/api/api.nim +++ /dev/null @@ -1,48 +0,0 @@ -import logos_delivery/waku/compat/option_valueor -import std/[net, options] - -import chronicles, chronos, libp2p/peerid, results - -import logos_delivery/waku/factory/waku -import logos_delivery/waku/[requests/health_requests, waku_core, waku_node] -import logos_delivery/waku/node/subscription_manager -import libp2p/peerid -import tools/confutils/cli_args -import ./[api_conf, types] - -export cli_args - -logScope: - topics = "api" - -proc createNode*(conf: WakuNodeConf): Future[Result[Waku, string]] {.async.} = - let wakuConf = conf.toWakuConf().valueOr: - return err("Failed to handle the configuration: " & error) - - ## We are not defining app callbacks at node creation - let wakuRes = (await Waku.new(wakuConf)).valueOr: - error "waku initialization failed", error = error - return err("Failed setting up Waku: " & $error) - - return ok(wakuRes) - -proc checkApiAvailability(w: Waku): Result[void, string] = - if w.isNil(): - return err("Waku node is not initialized") - - # TODO: Conciliate request-bouncing health checks here with unit testing. - # (For now, better to just allow all sends and rely on retries.) - - return ok() - -proc subscribe*( - w: Waku, contentTopic: ContentTopic -): Future[Result[void, string]] {.async.} = - ?checkApiAvailability(w) - - return w.node.subscriptionManager.subscribe(contentTopic) - -proc unsubscribe*(w: Waku, contentTopic: ContentTopic): Result[void, string] = - ?checkApiAvailability(w) - - return w.node.subscriptionManager.unsubscribe(contentTopic) diff --git a/logos_delivery/waku/api/send_api.md b/logos_delivery/waku/api/send_api.md deleted file mode 100644 index 2a5a2f8a4..000000000 --- a/logos_delivery/waku/api/send_api.md +++ /dev/null @@ -1,46 +0,0 @@ -# SEND API - -**THIS IS TO BE REMOVED BEFORE PR MERGE** - -This document collects logic and todo's around the Send API. - -## Overview - -Send api hides the complex logic of using raw protocols for reliable message delivery. -The delivery method is chosen based on the node configuration and actual availabilities of peers. - -## Delivery task - -Each message send request is bundled into a task that not just holds the composed message but also the state of the delivery. - -## Delivery methods - -Depending on the configuration and the availability of store client protocol + actual configured and/or discovered store nodes: -- P2PReliability validation - checking network store node whether the message is reached at least a store node. -- Simple retry until message is propagated to the network - - Relay says >0 peers as publish result - - LightpushClient returns with success - -Depending on node config: -- Relay -- Lightpush - -These methods are used in combination to achieve the best reliability. -Fallback mechanism is used to switch between methods if the current one fails. - -Relay+StoreCheck -> Relay+simple retry -> Lightpush+StoreCheck -> Lightpush simple retry -> Error - -Combination is dynamically chosen on node configuration. Levels can be skipped depending on actual connectivity. -Actual connectivity is checked: -- Relay's topic health check - at least dLow peers in the mesh for the topic -- Store nodes availability - at least one store service node is available in peer manager -- Lightpush client availability - at least one lightpush service node is available in peer manager - -## Delivery processing - -At every send request, each task is tried to be delivered right away. -Any further retries and store check is done as a background task in a loop with predefined intervals. -Each task is set for a maximum number of retries and/or maximum time to live. - -In each round of store check and retry send tasks are selected based on their state. -The state is updated based on the result of the delivery method. diff --git a/logos_delivery/waku/events/health_events.nim b/logos_delivery/waku/events/health_events.nim index 4ff6f0c6c..d19de776b 100644 --- a/logos_delivery/waku/events/health_events.nim +++ b/logos_delivery/waku/events/health_events.nim @@ -1,6 +1,6 @@ import brokers/event_broker -import logos_delivery/waku/api/types +import logos_delivery/api/types import logos_delivery/waku/node/health_monitor/[protocol_health, topic_health] import logos_delivery/waku/waku_core/topics diff --git a/logos_delivery/waku/events/message_events.nim b/logos_delivery/waku/events/message_events.nim index 9338fda67..2e4bece80 100644 --- a/logos_delivery/waku/events/message_events.nim +++ b/logos_delivery/waku/events/message_events.nim @@ -1,5 +1,6 @@ import brokers/event_broker -import logos_delivery/waku/[api/types, waku_core/message, waku_core/topics] +import logos_delivery/api/types +import logos_delivery/waku/[waku_core/message, waku_core/topics] export types EventBroker: diff --git a/logos_delivery/waku/node/health_monitor/connection_status.nim b/logos_delivery/waku/node/health_monitor/connection_status.nim index 68ec9d4be..fd0328fb7 100644 --- a/logos_delivery/waku/node/health_monitor/connection_status.nim +++ b/logos_delivery/waku/node/health_monitor/connection_status.nim @@ -1,4 +1,5 @@ -import chronos, results, std/strutils, ../../api/types +import chronos, results, std/strutils +from logos_delivery/api/types import ConnectionStatus export ConnectionStatus diff --git a/logos_delivery/waku/node/health_monitor/node_health_monitor.nim b/logos_delivery/waku/node/health_monitor/node_health_monitor.nim index 6e77816f0..465598794 100644 --- a/logos_delivery/waku/node/health_monitor/node_health_monitor.nim +++ b/logos_delivery/waku/node/health_monitor/node_health_monitor.nim @@ -8,10 +8,10 @@ import libp2p/protocols/rendezvous, libp2p/protocols/pubsub, libp2p/protocols/pubsub/rpc/messages, + logos_delivery/api/types, logos_delivery/waku/[ waku_relay, waku_rln_relay, - api/types, events/health_events, events/peer_events, node/waku_node, diff --git a/logos_delivery/waku/requests/health_requests.nim b/logos_delivery/waku/requests/health_requests.nim index 1c9ed4d70..366cdf875 100644 --- a/logos_delivery/waku/requests/health_requests.nim +++ b/logos_delivery/waku/requests/health_requests.nim @@ -1,6 +1,6 @@ import brokers/request_broker -import logos_delivery/waku/api/types +import logos_delivery/api/types import logos_delivery/waku/node/health_monitor/[protocol_health, topic_health, health_report] import logos_delivery/waku/waku_core/topics diff --git a/logos_delivery/waku/rest_api/endpoint/health/types.nim b/logos_delivery/waku/rest_api/endpoint/health/types.nim index 331460e7a..c32a0c778 100644 --- a/logos_delivery/waku/rest_api/endpoint/health/types.nim +++ b/logos_delivery/waku/rest_api/endpoint/health/types.nim @@ -4,7 +4,8 @@ import logos_delivery/waku/compat/option_valueor import results import chronicles, json_serialization, json_serialization/std/options import ../serdes -import logos_delivery/waku/[waku_node, api/types, node/health_monitor] +import logos_delivery/api/types +import logos_delivery/waku/[waku_node, node/health_monitor] #### Serialization and deserialization diff --git a/logos_delivery/waku/factory/waku.nim b/logos_delivery/waku/waku.nim similarity index 59% rename from logos_delivery/waku/factory/waku.nim rename to logos_delivery/waku/waku.nim index a797b8294..2c18c5f63 100644 --- a/logos_delivery/waku/factory/waku.nim +++ b/logos_delivery/waku/waku.nim @@ -11,6 +11,7 @@ import libp2p/wire, libp2p/crypto/crypto, libp2p/protocols/pubsub/gossipsub, + libp2p/protocols/ping, libp2p/services/autorelayservice, libp2p/services/hpservice, libp2p/peerid, @@ -20,6 +21,7 @@ import metrics, metrics/chronos_httpserver, brokers/broker_context, + logos_delivery/api/types, logos_delivery/waku/[ waku_core, waku_node, @@ -30,7 +32,6 @@ import waku_relay/protocol, waku_enr/sharding, waku_enr/multiaddr, - api/types, common/logging, node/peer_manager, node/health_monitor, @@ -48,9 +49,13 @@ import factory/internal_config, factory/app_callbacks, persistency/persistency, + factory/validator_signed, + waku_lightpush/client, + waku_lightpush_legacy/client, + waku_store/client, ], - ./waku_conf, - ./waku_state_info + ./factory/waku_conf, + ./factory/waku_state_info logScope: topics = "wakunode waku" @@ -58,6 +63,8 @@ logScope: # Git version in git describe format (defined at compile time) const git_version* {.strdefine.} = "n/a" +const FilterOpTimeout = 5.seconds + type Waku* = ref object stateInfo*: WakuStateInfo conf*: WakuConf @@ -567,12 +574,418 @@ proc stop*(waku: Waku): Future[Result[void, string]] {.async: (raises: []).} = return ok() -proc isModeCoreAvailable*(waku: Waku): bool = - return not waku.node.wakuRelay.isNil() +## Kernel API realization +## +# --- topic construction --- +proc buildContentTopic*( + self: Waku, appName: string, appVersion: uint32, name: string, encoding: string +): Future[Result[ContentTopic, string]] {.async.} = + try: + return ok(ContentTopic(fmt"/{appName}/{appVersion}/{name}/{encoding}")) + except CatchableError as e: + return err(e.msg) -proc isModeEdgeAvailable*(waku: Waku): bool = - return - waku.node.wakuRelay.isNil() and not waku.node.wakuStoreClient.isNil() and - not waku.node.wakuFilterClient.isNil() and not waku.node.wakuLightPushClient.isNil() +proc buildPubsubTopic*( + self: Waku, topicName: string +): Future[Result[PubsubTopic, string]] {.async.} = + try: + return ok(PubsubTopic(fmt"/waku/2/{topicName}")) + except CatchableError as e: + return err(e.msg) + +proc defaultPubsubTopic*(self: Waku): Future[Result[PubsubTopic, string]] {.async.} = + return ok(DefaultPubsubTopic) + +# --- relay --- +proc relayPublish*( + self: Waku, pubsubTopic: PubsubTopic, message: WakuMessage, timeoutMs: uint32 +): Future[Result[int, string]] {.async.} = + try: + if self.node.wakuRelay.isNil(): + return err("relayPublish: WakuRelay not mounted") + + let numPeers = (await self.node.wakuRelay.publish(pubsubTopic, message)).valueOr: + return err($error) + + return ok(numPeers) + except CatchableError as e: + return err(e.msg) + +proc relaySubscribe*( + self: Waku, pubsubTopic: PubsubTopic +): Future[Result[bool, string]] {.async.} = + try: + if self.node.wakuRelay.isNil(): + return err("relaySubscribe: WakuRelay not mounted") + + self.node.subscribe( + (kind: SubscriptionKind.PubsubSub, topic: pubsubTopic), WakuRelayHandler(nil) + ).isOkOr: + return err($error) + + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc relayUnsubscribe*( + self: Waku, pubsubTopic: PubsubTopic +): Future[Result[bool, string]] {.async.} = + try: + if self.node.wakuRelay.isNil(): + return err("relayUnsubscribe: WakuRelay not mounted") + + self.node.unsubscribe((kind: SubscriptionKind.PubsubSub, topic: pubsubTopic)).isOkOr: + return err($error) + + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc relayAddProtectedShard*( + self: Waku, clusterId: uint16, shardId: uint16, publicKey: string +): Future[Result[bool, string]] {.async.} = + try: + if self.node.wakuRelay.isNil(): + return err("relayAddProtectedShard: WakuRelay not mounted") + + let pubKey = SkPublicKey.fromHex(publicKey).valueOr: + return err("relayAddProtectedShard: invalid public key: " & $error) + + let protectedShard = ProtectedShard(shard: shardId, key: pubKey) + self.node.wakuRelay.addSignedShardsValidator(@[protectedShard], clusterId) + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc relayConnectedPeers*( + self: Waku, pubsubTopic: PubsubTopic +): Future[Result[seq[string], string]] {.async.} = + try: + if self.node.wakuRelay.isNil(): + return err("relayConnectedPeers: WakuRelay not mounted") + + let connPeers = self.node.wakuRelay.getConnectedPeers(pubsubTopic).valueOr: + return err($error) + + return ok(connPeers.mapIt($it)) + except CatchableError as e: + return err(e.msg) + +proc relayPeersInMesh*( + self: Waku, pubsubTopic: PubsubTopic +): Future[Result[seq[string], string]] {.async.} = + try: + if self.node.wakuRelay.isNil(): + return err("relayPeersInMesh: WakuRelay not mounted") + + let meshPeers = self.node.wakuRelay.getPeersInMesh(pubsubTopic).valueOr: + return err($error) + + return ok(meshPeers.mapIt($it)) + except CatchableError as e: + return err(e.msg) + +# --- filter --- +proc filterSubscribe*( + self: Waku, + pubsubTopic: Option[PubsubTopic], + contentTopics: seq[ContentTopic], + peer: string, +): Future[Result[bool, string]] {.async.} = + try: + if self.node.wakuFilterClient.isNil(): + return err("wakuFilterClient is not mounted") + + let subFut = self.node.filterSubscribe(pubsubTopic, contentTopics, peer) + if not await subFut.withTimeout(FilterOpTimeout): + return err("filter subscription timed out") + subFut.read().isOkOr: + return err($error) + + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc filterUnsubscribe*( + self: Waku, + pubsubTopic: Option[PubsubTopic], + contentTopics: seq[ContentTopic], + peer: string, +): Future[Result[bool, string]] {.async.} = + try: + if self.node.wakuFilterClient.isNil(): + return err("wakuFilterClient is not mounted") + + let unsubFut = self.node.filterUnsubscribe(pubsubTopic, contentTopics, peer) + if not await unsubFut.withTimeout(FilterOpTimeout): + return err("filter un-subscription timed out") + unsubFut.read().isOkOr: + return err($error) + + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc filterUnsubscribeAll*( + self: Waku, peer: string +): Future[Result[bool, string]] {.async.} = + try: + if self.node.wakuFilterClient.isNil(): + return err("wakuFilterClient is not mounted") + + let unsubFut = self.node.filterUnsubscribeAll(peer) + if not await unsubFut.withTimeout(FilterOpTimeout): + return err("filter un-subscription all timed out") + unsubFut.read().isOkOr: + return err($error) + + return ok(true) + except CatchableError as e: + return err(e.msg) + +# --- lightpush --- +proc lightpushPublish*( + self: Waku, pubsubTopic: PubsubTopic, message: WakuMessage, peer: string +): Future[Result[string, string]] {.async.} = + try: + if self.node.wakuLegacyLightpushClient.isNil(): + return err("wakuLegacyLightpushClient is not mounted") + + let remotePeer = parsePeerInfo(peer).valueOr: + return err("lightpushPublish failed to parse peer addr: " & $error) + + let msgHashHex = ( + await self.node.wakuLegacyLightpushClient.publish( + pubsubTopic, message, remotePeer + ) + ).valueOr: + return err($error) + + return ok(msgHashHex) + except CatchableError as e: + return err(e.msg) + +# --- store --- +proc storeQuery*( + self: Waku, request: StoreQueryRequest, peer: string, timeoutMs: int +): Future[Result[StoreQueryResponse, string]] {.async.} = + try: + if self.node.wakuStoreClient.isNil(): + return err("wakuStoreClient is not mounted") + + let remotePeer = parsePeerInfo(peer).valueOr: + return err("storeQuery failed to parse peer addr: " & $error) + + let queryFut = self.node.wakuStoreClient.query(request, remotePeer) + if not await queryFut.withTimeout(timeoutMs.milliseconds): + return err("storeQuery timed out") + + let queryResponse = queryFut.read().valueOr: + return err("storeQuery failed: " & $error) + + return ok(queryResponse) + except CatchableError as e: + return err(e.msg) + +# --- peer management --- +proc connect*( + self: Waku, peers: seq[string], timeoutMs: uint32 +): Future[Result[bool, string]] {.async.} = + try: + await self.node.connectToNodes(peers.mapIt(strip(it)), source = "static") + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc disconnectPeerById*( + self: Waku, peerId: string +): Future[Result[bool, string]] {.async.} = + try: + let pId = PeerId.init(peerId).valueOr: + return err($error) + await self.node.peerManager.disconnectNode(pId) + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc disconnectAllPeers*(self: Waku): Future[Result[bool, string]] {.async.} = + try: + await self.node.peerManager.disconnectAllPeers() + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc dialPeer*( + self: Waku, peerAddr: string, protocol: string, timeoutMs: int +): Future[Result[bool, string]] {.async.} = + try: + let remotePeerInfo = parsePeerInfo(peerAddr).valueOr: + return err($error) + let conn = await self.node.peerManager.dialPeer(remotePeerInfo, protocol) + if conn.isNone(): + return err("failed dialing peer") + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc dialPeerById*( + self: Waku, peerId: string, protocol: string, timeoutMs: int +): Future[Result[bool, string]] {.async.} = + try: + let pId = PeerId.init(peerId).valueOr: + return err($error) + let conn = await self.node.peerManager.dialPeer(pId, protocol) + if conn.isNone(): + return err("failed dialing peer") + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc peerIdsFromPeerstore*(self: Waku): Future[Result[seq[string], string]] {.async.} = + try: + return ok(self.node.peerManager.switch.peerStore.peers().mapIt($it.peerId)) + except CatchableError as e: + return err(e.msg) + +proc connectedPeersInfo*(self: Waku): Future[Result[seq[string], string]] {.async.} = + try: + return ok( + self.node.peerManager.switch.peerStore + .peers() + .filterIt(it.connectedness == Connected) + .mapIt($it.peerId) + ) + except CatchableError as e: + return err(e.msg) + +proc connectedPeers*(self: Waku): Future[Result[seq[string], string]] {.async.} = + try: + let (inPeerIds, outPeerIds) = self.node.peerManager.connectedPeers() + return ok(concat(inPeerIds, outPeerIds).mapIt($it)) + except CatchableError as e: + return err(e.msg) + +proc peerIdsByProtocol*( + self: Waku, protocol: string +): Future[Result[seq[string], string]] {.async.} = + try: + return ok( + self.node.peerManager.switch.peerStore + .peers(protocol) + .filterIt(it.connectedness == Connected) + .mapIt($it.peerId) + ) + except CatchableError as e: + return err(e.msg) + +# --- discovery --- +proc dnsDiscovery*( + self: Waku, enrTreeUrl: string, nameServer: string, timeoutMs: int +): Future[Result[seq[string], string]] {.async.} = + try: + let dnsNameServers = @[parseIpAddress(nameServer)] + let discoveredPeers = ( + await retrieveDynamicBootstrapNodes(enrTreeUrl, dnsNameServers) + ).valueOr: + return err("failed discovering peers from DNS: " & $error) + + var multiAddresses = newSeq[string]() + for discPeer in discoveredPeers: + for address in discPeer.addrs: + multiAddresses.add($address & "/p2p/" & $discPeer) + + return ok(multiAddresses) + except CatchableError as e: + return err(e.msg) + +proc discv5UpdateBootnodes*( + self: Waku, bootnodes: seq[string] +): Future[Result[bool, string]] {.async.} = + try: + if self.wakuDiscv5.isNil(): + return err("discv5 not started") + let jsonArray = "[" & bootnodes.mapIt("\"" & it & "\"").join(",") & "]" + self.wakuDiscv5.updateBootstrapRecords(jsonArray).isOkOr: + return err("error in discv5UpdateBootnodes: " & $error) + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc startDiscv5*(self: Waku): Future[Result[bool, string]] {.async.} = + try: + if self.wakuDiscv5.isNil(): + return err("discv5 not started") + (await self.wakuDiscv5.start()).isOkOr: + return err("error starting discv5: " & $error) + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc stopDiscv5*(self: Waku): Future[Result[bool, string]] {.async.} = + try: + if self.wakuDiscv5.isNil(): + return err("discv5 not started") + await self.wakuDiscv5.stop() + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc peerExchangeRequest*( + self: Waku, numPeers: uint64 +): Future[Result[int, string]] {.async.} = + try: + let numPeersRecv = (await self.node.fetchPeerExchangePeers(numPeers)).valueOr: + return err("failed peer exchange: " & $error) + return ok(numPeersRecv) + except CatchableError as e: + return err(e.msg) + +# --- debug / info --- +proc version*(self: Waku): Future[Result[string, string]] {.async.} = + return ok(WakuNodeVersionString) + +proc listenAddresses*(self: Waku): Future[Result[seq[string], string]] {.async.} = + try: + return ok(self.node.info().listenAddresses) + except CatchableError as e: + return err(e.msg) + +proc myEnr*(self: Waku): Future[Result[string, string]] {.async.} = + try: + return ok(self.node.enr.toURI()) + except CatchableError as e: + return err(e.msg) + +proc myPeerId*(self: Waku): Future[Result[string, string]] {.async.} = + try: + return ok($self.node.peerId()) + except CatchableError as e: + return err(e.msg) + +proc metrics*(self: Waku): Future[Result[string, string]] {.async.} = + {.gcsafe.}: + try: + return ok(defaultRegistry.toText()) + except CatchableError as e: + return err(e.msg) + +proc pingPeer*( + self: Waku, peerAddr: string, timeoutMs: int +): Future[Result[int64, string]] {.async.} = + try: + let peerInfo = parsePeerInfo(peerAddr).valueOr: + return err("pingPeer failed to parse peer addr: " & $error) + + let conn = await self.node.switch.dial(peerInfo.peerId, peerInfo.addrs, PingCodec) + defer: + await conn.close() + let pingRTT = await self.node.libp2pPing.ping(conn) + + if pingRTT == 0.nanos: + return err("could not ping peer: rtt-0") + + return ok(pingRTT.nanos) + except CatchableError as e: + return err(e.msg) {.pop.} diff --git a/tests/api/test_api_receive.nim b/tests/api/test_api_receive.nim index d9eb02427..41c0f0477 100644 --- a/tests/api/test_api_receive.nim +++ b/tests/api/test_api_receive.nim @@ -187,7 +187,7 @@ proc setupNetwork(testTopic: ContentTopic): Future[TestNetwork] {.async.} = raiseAssert "Message was not archived in time" # subscribe to the content topic; with no peers yet the subscriber stays offline - (await subscriber.waku.subscribe(testTopic)).expect("Failed to subscribe") + (await subscriber.messagingClient.subscribe(testTopic)).expect("Failed to subscribe") return TestNetwork( storeNode: storeNode, diff --git a/tests/api/test_api_subscription.nim b/tests/api/test_api_subscription.nim index 1169a7366..bf2851b02 100644 --- a/tests/api/test_api_subscription.nim +++ b/tests/api/test_api_subscription.nim @@ -225,7 +225,7 @@ suite "Messaging API, SubscriptionManager": await net.teardown() let testTopic = ContentTopic("/waku/2/test-content/proto") - (await net.subscriber.waku.subscribe(testTopic)).expect( + (await net.subscriber.messagingClient.subscribe(testTopic)).expect( "subscriberNode failed to subscribe" ) @@ -248,7 +248,9 @@ suite "Messaging API, SubscriptionManager": let subbedTopic = ContentTopic("/waku/2/subbed-topic/proto") let ignoredTopic = ContentTopic("/waku/2/ignored-topic/proto") - (await net.subscriber.waku.subscribe(subbedTopic)).expect("failed to subscribe") + (await net.subscriber.messagingClient.subscribe(subbedTopic)).expect( + "failed to subscribe" + ) let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: @@ -268,8 +270,12 @@ suite "Messaging API, SubscriptionManager": let testTopic = ContentTopic("/waku/2/unsub-test/proto") - (await net.subscriber.waku.subscribe(testTopic)).expect("failed to subscribe") - net.subscriber.waku.unsubscribe(testTopic).expect("failed to unsubscribe") + (await net.subscriber.messagingClient.subscribe(testTopic)).expect( + "failed to subscribe" + ) + net.subscriber.messagingClient.unsubscribe(testTopic).expect( + "failed to unsubscribe" + ) let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: @@ -289,14 +295,14 @@ suite "Messaging API, SubscriptionManager": let topicA = ContentTopic("/waku/2/topic-a/proto") let topicB = ContentTopic("/waku/2/topic-b/proto") - (await net.subscriber.waku.subscribe(topicA)).expect("failed to sub A") - (await net.subscriber.waku.subscribe(topicB)).expect("failed to sub B") + (await net.subscriber.messagingClient.subscribe(topicA)).expect("failed to sub A") + (await net.subscriber.messagingClient.subscribe(topicB)).expect("failed to sub B") let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: await eventManager.teardown() - net.subscriber.waku.unsubscribe(topicA).expect("failed to unsub A") + net.subscriber.messagingClient.unsubscribe(topicA).expect("failed to unsub A") discard (await net.publishToMesh(topicA, "Dropped Message".toBytes())).expect( "Publish A failed" @@ -315,9 +321,13 @@ suite "Messaging API, SubscriptionManager": let glitchTopic = ContentTopic("/waku/2/glitch/proto") - (await net.subscriber.waku.subscribe(glitchTopic)).expect("failed to sub") - (await net.subscriber.waku.subscribe(glitchTopic)).expect("failed to double sub") - net.subscriber.waku.unsubscribe(glitchTopic).expect("failed to unsub") + (await net.subscriber.messagingClient.subscribe(glitchTopic)).expect( + "failed to sub" + ) + (await net.subscriber.messagingClient.subscribe(glitchTopic)).expect( + "failed to double sub" + ) + net.subscriber.messagingClient.unsubscribe(glitchTopic).expect("failed to unsub") let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: @@ -338,7 +348,9 @@ suite "Messaging API, SubscriptionManager": let testTopic = ContentTopic("/waku/2/resub-test/proto") # Subscribe - (await net.subscriber.waku.subscribe(testTopic)).expect("Initial sub failed") + (await net.subscriber.messagingClient.subscribe(testTopic)).expect( + "Initial sub failed" + ) var eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) discard @@ -348,7 +360,7 @@ suite "Messaging API, SubscriptionManager": await eventManager.teardown() # Unsubscribe and verify teardown - net.subscriber.waku.unsubscribe(testTopic).expect("Unsub failed") + net.subscriber.messagingClient.unsubscribe(testTopic).expect("Unsub failed") eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) discard @@ -358,7 +370,7 @@ suite "Messaging API, SubscriptionManager": await eventManager.teardown() # Resubscribe - (await net.subscriber.waku.subscribe(testTopic)).expect("Resub failed") + (await net.subscriber.messagingClient.subscribe(testTopic)).expect("Resub failed") eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) discard @@ -382,8 +394,8 @@ suite "Messaging API, SubscriptionManager": topicB = ContentTopic("/appB" & $i & "/2/shard-test-b/proto") inc i - (await net.subscriber.waku.subscribe(topicA)).expect("failed to sub A") - (await net.subscriber.waku.subscribe(topicB)).expect("failed to sub B") + (await net.subscriber.messagingClient.subscribe(topicA)).expect("failed to sub A") + (await net.subscriber.messagingClient.subscribe(topicB)).expect("failed to sub B") let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 2) defer: @@ -440,7 +452,7 @@ suite "Messaging API, SubscriptionManager": # subscribe to all content topics we generated for t in allTopics: - (await net.subscriber.waku.subscribe(t)).expect("sub failed") + (await net.subscriber.messagingClient.subscribe(t)).expect("sub failed") activeSubs.add(t) await verifyNetworkState(activeSubs) @@ -448,7 +460,7 @@ suite "Messaging API, SubscriptionManager": # unsubscribe from some content topics for i in 0 ..< 50: let t = allTopics[i] - net.subscriber.waku.unsubscribe(t).expect("unsub failed") + net.subscriber.messagingClient.unsubscribe(t).expect("unsub failed") let idx = activeSubs.find(t) if idx >= 0: @@ -459,7 +471,7 @@ suite "Messaging API, SubscriptionManager": # re-subscribe to some content topics for i in 0 ..< 25: let t = allTopics[i] - (await net.subscriber.waku.subscribe(t)).expect("resub failed") + (await net.subscriber.messagingClient.subscribe(t)).expect("resub failed") activeSubs.add(t) await verifyNetworkState(activeSubs) @@ -470,7 +482,9 @@ suite "Messaging API, SubscriptionManager": await net.teardown() let testTopic = ContentTopic("/waku/2/test-content/proto") - (await net.subscriber.waku.subscribe(testTopic)).expect("failed to subscribe") + (await net.subscriber.messagingClient.subscribe(testTopic)).expect( + "failed to subscribe" + ) let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: @@ -491,7 +505,9 @@ suite "Messaging API, SubscriptionManager": let subbedTopic = ContentTopic("/waku/2/subbed-topic/proto") let ignoredTopic = ContentTopic("/waku/2/ignored-topic/proto") - (await net.subscriber.waku.subscribe(subbedTopic)).expect("failed to subscribe") + (await net.subscriber.messagingClient.subscribe(subbedTopic)).expect( + "failed to subscribe" + ) let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: @@ -511,8 +527,12 @@ suite "Messaging API, SubscriptionManager": let testTopic = ContentTopic("/waku/2/unsub-test/proto") - (await net.subscriber.waku.subscribe(testTopic)).expect("failed to subscribe") - net.subscriber.waku.unsubscribe(testTopic).expect("failed to unsubscribe") + (await net.subscriber.messagingClient.subscribe(testTopic)).expect( + "failed to subscribe" + ) + net.subscriber.messagingClient.unsubscribe(testTopic).expect( + "failed to unsubscribe" + ) let eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) defer: @@ -532,8 +552,8 @@ suite "Messaging API, SubscriptionManager": let topicA = ContentTopic("/waku/2/topic-a/proto") let topicB = ContentTopic("/waku/2/topic-b/proto") - (await net.subscriber.waku.subscribe(topicA)).expect("failed to sub A") - (await net.subscriber.waku.subscribe(topicB)).expect("failed to sub B") + (await net.subscriber.messagingClient.subscribe(topicA)).expect("failed to sub A") + (await net.subscriber.messagingClient.subscribe(topicB)).expect("failed to sub B") let shard = net.subscriber.waku.node.getRelayShard(topicA) await waitForEdgeSubs(net.subscriber, shard) @@ -542,7 +562,7 @@ suite "Messaging API, SubscriptionManager": defer: await eventManager.teardown() - net.subscriber.waku.unsubscribe(topicA).expect("failed to unsub A") + net.subscriber.messagingClient.unsubscribe(topicA).expect("failed to unsub A") discard (await net.publishToMesh(topicA, "Dropped Message".toBytes())).expect( "Publish A failed" @@ -561,7 +581,9 @@ suite "Messaging API, SubscriptionManager": let testTopic = ContentTopic("/waku/2/resub-test/proto") - (await net.subscriber.waku.subscribe(testTopic)).expect("Initial sub failed") + (await net.subscriber.messagingClient.subscribe(testTopic)).expect( + "Initial sub failed" + ) var eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) discard (await net.publishToMeshAfterEdgeReady(testTopic, "Msg 1".toBytes())).expect( @@ -571,7 +593,7 @@ suite "Messaging API, SubscriptionManager": require await eventManager.waitForEvents(TestTimeout) await eventManager.teardown() - net.subscriber.waku.unsubscribe(testTopic).expect("Unsub failed") + net.subscriber.messagingClient.unsubscribe(testTopic).expect("Unsub failed") eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) discard @@ -580,7 +602,7 @@ suite "Messaging API, SubscriptionManager": check not await eventManager.waitForEvents(NegativeTestTimeout) await eventManager.teardown() - (await net.subscriber.waku.subscribe(testTopic)).expect("Resub failed") + (await net.subscriber.messagingClient.subscribe(testTopic)).expect("Resub failed") eventManager = newReceiveEventListenerManager(net.subscriber.waku.brokerCtx, 1) discard (await net.publishToMeshAfterEdgeReady(testTopic, "Msg 2".toBytes())).expect( @@ -653,7 +675,9 @@ suite "Messaging API, SubscriptionManager": let testTopic = ContentTopic("/waku/2/failover-test/proto") let shard = subscriber.waku.node.getRelayShard(testTopic) - (await subscriber.waku.subscribe(testTopic)).expect("Failed to subscribe") + (await subscriber.messagingClient.subscribe(testTopic)).expect( + "Failed to subscribe" + ) # Wait for dialing both filter servers (HealthyThreshold = 2) check await edgePeersReached(subscriber, shard, 2) @@ -783,7 +807,9 @@ suite "Messaging API, SubscriptionManager": let testTopic = ContentTopic("/waku/2/replacement-test/proto") let shard = subscriber.waku.node.getRelayShard(testTopic) - (await subscriber.waku.subscribe(testTopic)).expect("Failed to subscribe") + (await subscriber.messagingClient.subscribe(testTopic)).expect( + "Failed to subscribe" + ) # Wait for 2 confirmed peers (HealthyThreshold). The 3rd is available but not dialed. check await edgePeersReached(subscriber, shard, 2) diff --git a/tests/api/test_node_conf.nim b/tests/api/test_node_conf.nim index a5bff3906..df5b18887 100644 --- a/tests/api/test_node_conf.nim +++ b/tests/api/test_node_conf.nim @@ -5,7 +5,7 @@ import json_serialization, confutils, confutils/std/net import tools/confutils/cli_args, tools/confutils/conf_from_json, - logos_delivery/waku/api/api_conf, + logos_delivery/api/api_conf, logos_delivery/waku/factory/waku_conf, logos_delivery/waku/factory/networks_config, logos_delivery/waku/factory/conf_builder/conf_builder, @@ -350,7 +350,7 @@ suite "WakuNodeConf JSON -> WakuConf integration": {.push warning[Deprecated]: off.} -import logos_delivery/waku/api/api_conf +import logos_delivery/api/api_conf suite "NodeConfig (deprecated) - toWakuConf": test "Minimal configuration": diff --git a/tests/channels/test_reliable_channel_send_receive.nim b/tests/channels/test_reliable_channel_send_receive.nim index 881c22b3a..b3d9a8f00 100644 --- a/tests/channels/test_reliable_channel_send_receive.nim +++ b/tests/channels/test_reliable_channel_send_receive.nim @@ -58,7 +58,7 @@ suite "Reliable Channel - ingress": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager ## Noop encryption providers so the Encrypt/Decrypt brokers have @@ -124,7 +124,7 @@ suite "Reliable Channel - ingress": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -181,7 +181,7 @@ suite "Reliable Channel - send state machine": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -246,7 +246,7 @@ suite "Reliable Channel - send state machine": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -347,7 +347,7 @@ suite "Reliable Channel - send state machine": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -452,7 +452,7 @@ suite "Reliable Channel - SDS persistence": var waku: LogosDelivery var manager: ReliableChannelManager lockNewGlobalBrokerContext: - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -522,7 +522,7 @@ suite "Reliable Channel - SDS lifecycle": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -593,7 +593,7 @@ suite "Reliable Channel - SDS lifecycle": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -650,7 +650,7 @@ suite "Reliable Channel - SDS lifecycle": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -710,7 +710,7 @@ suite "Reliable Channel - SDS lifecycle": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -792,7 +792,7 @@ suite "Reliable Channel - SDS protocol semantics": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -860,7 +860,7 @@ suite "Reliable Channel - SDS protocol semantics": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -948,7 +948,7 @@ suite "Reliable Channel - SDS protocol semantics": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -1023,7 +1023,7 @@ suite "Reliable Channel - SDS protocol semantics": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -1096,7 +1096,7 @@ suite "Reliable Channel - SDS protocol semantics": var brokerCtx: BrokerContext lockNewGlobalBrokerContext: brokerCtx = globalBrokerContext() - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager setNoopEncryption() @@ -1162,7 +1162,7 @@ suite "Reliable Channel - SDS protocol semantics": var waku: LogosDelivery var manager: ReliableChannelManager lockNewGlobalBrokerContext: - waku = (await LogosDelivery.new(createApiNodeConf())).expect("createNode") + waku = (await LogosDelivery.new(createApiNodeConf())).expect("LogosDelivery.new") manager = waku.reliableChannelManager check (await manager.send(ChannelId("no-such-channel"), "x".toBytes())).isErr() diff --git a/tests/test_waku.nim b/tests/test_waku.nim index ff009c3ba..faf1575eb 100644 --- a/tests/test_waku.nim +++ b/tests/test_waku.nim @@ -9,7 +9,7 @@ import tools/confutils/cli_args import logos_delivery/waku/factory/networks_config import logos_delivery/waku/factory/conf_builder/conf_builder -suite "Waku API - Create node": +suite "LogosDelivery API - Create node": asyncTest "Create node with minimal configuration": ## Given var nodeConf = defaultWakuNodeConf().valueOr: @@ -21,14 +21,14 @@ suite "Waku API - Create node": # This is the actual minimal config but as the node auto-start, it is not suitable for tests ## When - let node = (await createNode(nodeConf)).valueOr: - raiseAssert "createNode (minimal config) failed: " & error + let ld = (await LogosDelivery.new(nodeConf)).valueOr: + raiseAssert "LogosDelivery.new (minimal config) failed: " & error ## Then check: - not node.isNil() - node.conf.clusterId == 3 - node.conf.relay == true + not ld.isNil() + ld.waku.conf.clusterId == 3 + ld.waku.conf.relay == true asyncTest "Create node with full configuration": ## Given @@ -47,20 +47,20 @@ suite "Waku API - Create node": ] ## When - let node = (await createNode(nodeConf)).valueOr: - raiseAssert "createNode (full config) failed: " & error + let ld = (await LogosDelivery.new(nodeConf)).valueOr: + raiseAssert "LogosDelivery.new (full config) failed: " & error ## Then check: - not node.isNil() - node.conf.clusterId == 99 - node.conf.shardingConf.numShardsInCluster == 16 - node.conf.maxMessageSizeBytes == 1024'u64 * 1024'u64 - node.conf.staticNodes.len == 1 - node.conf.relay == true - node.conf.lightPush == true - node.conf.peerExchangeService == true - node.conf.rendezvous == true + not ld.isNil() + ld.waku.conf.clusterId == 99 + ld.waku.conf.shardingConf.numShardsInCluster == 16 + ld.waku.conf.maxMessageSizeBytes == 1024'u64 * 1024'u64 + ld.waku.conf.staticNodes.len == 1 + ld.waku.conf.relay == true + ld.waku.conf.lightPush == true + ld.waku.conf.peerExchangeService == true + ld.waku.conf.rendezvous == true asyncTest "Create node with mixed entry nodes (enrtree, multiaddr)": ## Given @@ -75,18 +75,18 @@ suite "Waku API - Create node": ] ## When - let node = (await createNode(nodeConf)).valueOr: - raiseAssert "createNode (mixed entry nodes) failed: " & error + let ld = (await LogosDelivery.new(nodeConf)).valueOr: + raiseAssert "LogosDelivery.new (mixed entry nodes) failed: " & error ## Then check: - not node.isNil() - node.conf.clusterId == 42 + not ld.isNil() + ld.waku.conf.clusterId == 42 # ENRTree should go to DNS discovery - node.conf.dnsDiscoveryConf.isSome() - node.conf.dnsDiscoveryConf.get().enrTreeUrl == + ld.waku.conf.dnsDiscoveryConf.isSome() + ld.waku.conf.dnsDiscoveryConf.get().enrTreeUrl == "enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im" # Multiaddr should go to static nodes - node.conf.staticNodes.len == 1 - node.conf.staticNodes[0] == + ld.waku.conf.staticNodes.len == 1 + ld.waku.conf.staticNodes[0] == "/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc" diff --git a/tests/waku_discv5/test_waku_discv5.nim b/tests/waku_discv5/test_waku_discv5.nim index b5d34486f..b794ec918 100644 --- a/tests/waku_discv5/test_waku_discv5.nim +++ b/tests/waku_discv5/test_waku_discv5.nim @@ -21,7 +21,7 @@ import discovery/waku_discv5, waku_enr/capabilities, factory/conf_builder/conf_builder, - factory/waku, + waku, waku_node, node/peer_manager, ], diff --git a/tests/wakunode2/test_app.nim b/tests/wakunode2/test_app.nim index eefa866db..1a312a6c6 100644 --- a/tests/wakunode2/test_app.nim +++ b/tests/wakunode2/test_app.nim @@ -10,7 +10,7 @@ import tests/testlib/[wakucore, wakunode], logos_delivery/waku/factory/conf_builder/conf_builder -include logos_delivery/waku/factory/waku, logos_delivery/waku/common/enr/typed_record +include logos_delivery/waku/waku, logos_delivery/waku/common/enr/typed_record suite "Wakunode2 - Waku": test "compilation version should be reported": From fc15430e5280d2eeec919b57649b42f4695f37e2 Mon Sep 17 00:00:00 2001 From: Tanya S <120410716+stubbsta@users.noreply.github.com> Date: Thu, 25 Jun 2026 17:13:38 +0200 Subject: [PATCH 10/33] Temporarily extend ci test job timeout (#3997) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a407e37f4..fe275a60a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,7 +125,7 @@ jobs: matrix: os: [ubuntu-22.04, macos-15] runs-on: ${{ matrix.os }} - timeout-minutes: 45 + timeout-minutes: 60 name: test-${{ matrix.os }} steps: From 3eebc7288b76fdc4ba40c3bfd838e325668fd2d1 Mon Sep 17 00:00:00 2001 From: Darshan <35736874+darshankabariya@users.noreply.github.com> Date: Fri, 26 Jun 2026 01:23:57 +0530 Subject: [PATCH 11/33] test: add S16 lightpush dynamic-recovery unittest (#3995) --- tests/api/test_api_send.nim | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/api/test_api_send.nim b/tests/api/test_api_send.nim index f64715592..0ad1cea4a 100644 --- a/tests/api/test_api_send.nim +++ b/tests/api/test_api_send.nim @@ -387,6 +387,46 @@ suite "Waku API - Send": (await node.stop()).isOkOr: raiseAssert "Failed to stop node: " & error + asyncTest "S16 - isolated sender recovers when lightpush peer appears later": + ## Edge sender starts with no peer; a lightpush peer appears mid-send and a + ## later retry must deliver the queued message. + var node: LogosDelivery + lockNewGlobalBrokerContext: + node = (await LogosDelivery.new(createApiNodeConf(cli_args.WakuMode.Edge))).valueOr: + raiseAssert error + (await node.start()).isOkOr: + raiseAssert "Failed to start Waku node: " & error + # No connectToNodes: the sender has no reachable peer at T0. + + check node.waku.node.wakuRelay.isNil() + + let eventManager = newSendEventListenerManager(node.waku.brokerCtx) + defer: + await eventManager.teardown() + + let envelope = MessageEnvelope.init( + ContentTopic("/waku/2/default-content/proto"), "test payload" + ) + + # send() with no peer must still succeed; the message is queued and retried. + let requestId = (await node.messagingClient.send(envelope)).valueOr: + raiseAssert error + + # Nothing should propagate while isolated (retry interval is 1s). + await sleepAsync(5.seconds) + check eventManager.propagatedCount == 0 + + # The lightpush peer appears; a later retry must now deliver. + await node.waku.node.connectToNodes(@[lightpushNodePeerInfo]) + + const eventTimeout = 10.seconds + discard await eventManager.waitForEvents(eventTimeout) + + eventManager.validate({SendEventOutcome.Propagated}, requestId) + + (await node.stop()).isOkOr: + raiseAssert "Failed to stop node: " & error + asyncTest "Send fully validates fallback to lightpush": var node: LogosDelivery lockNewGlobalBrokerContext: From 4ba5710a15271f2dc3a45b2dd66fb06d57a9e3d2 Mon Sep 17 00:00:00 2001 From: Tanya S <120410716+stubbsta@users.noreply.github.com> Date: Fri, 26 Jun 2026 14:43:40 +0200 Subject: [PATCH 12/33] chore: phase 1 - extract generateRlnValidator into temp adapter dir (#3984) * extract generateRlnValidator into temp adapter dir * fix linting --- logos_delivery/waku/node/waku_node/relay.nim | 1 + .../waku/waku_rln_relay/adapters/relay.nim | 78 +++++++++++++++++++ .../waku/waku_rln_relay/rln_relay.nim | 69 +--------------- 3 files changed, 81 insertions(+), 67 deletions(-) create mode 100644 logos_delivery/waku/waku_rln_relay/adapters/relay.nim diff --git a/logos_delivery/waku/node/waku_node/relay.nim b/logos_delivery/waku/node/waku_node/relay.nim index 57904dc94..c1e79d44e 100644 --- a/logos_delivery/waku/node/waku_node/relay.nim +++ b/logos_delivery/waku/node/waku_node/relay.nim @@ -29,6 +29,7 @@ import waku_archive, waku_store_sync, waku_rln_relay, + waku_rln_relay/adapters/relay as waku_rln_relay_adapter, node/waku_node, node/subscription_manager, node/peer_manager, diff --git a/logos_delivery/waku/waku_rln_relay/adapters/relay.nim b/logos_delivery/waku/waku_rln_relay/adapters/relay.nim new file mode 100644 index 000000000..206839aab --- /dev/null +++ b/logos_delivery/waku/waku_rln_relay/adapters/relay.nim @@ -0,0 +1,78 @@ +{.push raises: [].} + +import + std/options, + chronicles, + chronos, + results, + stew/byteutils, + libp2p/protocols/pubsub/pubsub + +import ../rln_relay, ../protocol_types, ../protocol_metrics, ../conversion_utils + +import logos_delivery/waku/[waku_relay, waku_core] + +logScope: + topics = "waku rln_relay adapter" + +proc generateRlnValidator*( + wakuRlnRelay: WakuRLNRelay, spamHandler = none(SpamHandler) +): WakuValidatorHandler = + ## Bridges RLN's protocol-agnostic message validation into a relay + ## (gossipsub) validator. The core decision is made by + ## `validateMessageAndUpdateLog`; this adapter maps the result to + ## `pubsub.ValidationResult` so the validator can be installed on + ## WakuRelay's validator chain. + ## Validation logic follows https://rfc.vac.dev/spec/17/ + proc validator( + topic: string, message: WakuMessage + ): Future[pubsub.ValidationResult] {.async.} = + trace "rln-relay topic validator is called" + wakuRlnRelay.clearNullifierLog() + + let msgProof = RateLimitProof.init(message.proof).valueOr: + trace "generateRlnValidator reject", error = error + return pubsub.ValidationResult.Reject + + # validate the message and update log + let validationRes = await wakuRlnRelay.validateMessageAndUpdateLog(message) + + let + proof = byteutils.toHex(msgProof.proof) + epoch = fromEpoch(msgProof.epoch) + root = inHex(msgProof.merkleRoot) + shareX = inHex(msgProof.shareX) + shareY = inHex(msgProof.shareY) + nullifier = inHex(msgProof.nullifier) + payload = string.fromBytes(message.payload) + case validationRes + of Valid: + trace "message validity is verified, relaying", + proof = proof, + root = root, + shareX = shareX, + shareY = shareY, + nullifier = nullifier + waku_rln_valid_messages_total.inc(labelValues = [topic]) + return pubsub.ValidationResult.Accept + of Invalid: + trace "message validity could not be verified, discarding", + proof = proof, + root = root, + shareX = shareX, + shareY = shareY, + nullifier = nullifier + return pubsub.ValidationResult.Reject + of Spam: + trace "A spam message is found! yay! discarding:", + proof = proof, + root = root, + shareX = shareX, + shareY = shareY, + nullifier = nullifier + if spamHandler.isSome(): + let handler = spamHandler.get() + handler(message) + return pubsub.ValidationResult.Reject + + return validator diff --git a/logos_delivery/waku/waku_rln_relay/rln_relay.nim b/logos_delivery/waku/waku_rln_relay/rln_relay.nim index 7c325d3d5..5ae7a2cff 100644 --- a/logos_delivery/waku/waku_rln_relay/rln_relay.nim +++ b/logos_delivery/waku/waku_rln_relay/rln_relay.nim @@ -12,7 +12,6 @@ import web3/eth_api_types, eth/keys, libp2p/protocols/pubsub/rpc/messages, - libp2p/protocols/pubsub/pubsub, results, stew/[byteutils, arrayops], brokers/broker_context @@ -27,13 +26,8 @@ import ./nonce_manager import - logos_delivery/waku/[ - common/error_handling, - waku_relay, # for WakuRelayHandler - waku_core, - requests/rln_requests, - waku_keystore, - ] + logos_delivery/waku/ + [common/error_handling, waku_core, requests/rln_requests, waku_keystore] logScope: topics = "waku rln_relay" @@ -321,65 +315,6 @@ proc clearNullifierLog*(rlnPeer: WakuRlnRelay) = currentEpoch = currentEpoch, cleanedEpoch = fromEpoch(epochRemove) rlnPeer.nullifierLog.del(epochRemove) -proc generateRlnValidator*( - wakuRlnRelay: WakuRLNRelay, spamHandler = none(SpamHandler) -): WakuValidatorHandler = - ## this procedure is a thin wrapper for the pubsub addValidator method - ## it sets a validator for waku messages, acting in the registered pubsub topic - ## the message validation logic is according to https://rfc.vac.dev/spec/17/ - proc validator( - topic: string, message: WakuMessage - ): Future[pubsub.ValidationResult] {.async.} = - trace "rln-relay topic validator is called" - wakuRlnRelay.clearNullifierLog() - - let msgProof = RateLimitProof.init(message.proof).valueOr: - trace "generateRlnValidator reject", error = error - return pubsub.ValidationResult.Reject - - # validate the message and update log - let validationRes = await wakuRlnRelay.validateMessageAndUpdateLog(message) - - let - proof = byteutils.toHex(msgProof.proof) - epoch = fromEpoch(msgProof.epoch) - root = inHex(msgProof.merkleRoot) - shareX = inHex(msgProof.shareX) - shareY = inHex(msgProof.shareY) - nullifier = inHex(msgProof.nullifier) - payload = string.fromBytes(message.payload) - case validationRes - of Valid: - trace "message validity is verified, relaying", - proof = proof, - root = root, - shareX = shareX, - shareY = shareY, - nullifier = nullifier - waku_rln_valid_messages_total.inc(labelValues = [topic]) - return pubsub.ValidationResult.Accept - of Invalid: - trace "message validity could not be verified, discarding", - proof = proof, - root = root, - shareX = shareX, - shareY = shareY, - nullifier = nullifier - return pubsub.ValidationResult.Reject - of Spam: - trace "A spam message is found! yay! discarding:", - proof = proof, - root = root, - shareX = shareX, - shareY = shareY, - nullifier = nullifier - if spamHandler.isSome(): - let handler = spamHandler.get() - handler(message) - return pubsub.ValidationResult.Reject - - return validator - proc monitorEpochs(wakuRlnRelay: WakuRLNRelay) {.async.} = while true: try: From 26098d76cf7a30f00c76dc5a01d0d74614674628 Mon Sep 17 00:00:00 2001 From: AYAHASSAN287 <49167455+AYAHASSAN287@users.noreply.github.com> Date: Fri, 26 Jun 2026 19:36:06 +0300 Subject: [PATCH 13/33] Add send API e2e tests to the PR job (#3983) * Add send API e2e tests to the PR job * Update yml reference * ci: pin interop smoke tests to SMOKE_TEST_2026.06.25 tag * ci: retrigger CI * ci: retrigger CI --- .github/workflows/ci.yml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe275a60a..af7f256fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,6 +110,17 @@ jobs: # job, which already forces -j1. run: make V=1 -j1 all + # Publish the freshly built shared library so the interop-tests workflow + # can run the wrapper suite against it without rebuilding. Only the Linux + # build is uploaded since the interop runners are ubuntu-latest. + - name: Upload liblogosdelivery.so + if: matrix.os == 'ubuntu-22.04' + uses: actions/upload-artifact@v4 + with: + name: liblogosdelivery + path: build/liblogosdelivery.so + retention-days: 1 + build-windows: needs: changes if: ${{ needs.changes.outputs.v2 == 'true' || needs.changes.outputs.common == 'true' }} @@ -184,14 +195,24 @@ jobs: uses: ./.github/workflows/container-image.yml secrets: inherit + # Docker-based interop smoke tests. Gated only on the docker image so they + # still run on docker-only PRs (no liblogosdelivery build required). nwaku-nwaku-interop-tests: needs: build-docker-image - uses: logos-messaging/logos-delivery-interop-tests/.github/workflows/nim_waku_PR.yml@SMOKE_TEST_STABLE + uses: logos-messaging/logos-delivery-interop-tests/.github/workflows/nim_waku_PR.yml@SMOKE_TEST_2026.06.25 with: node_nwaku: ${{ needs.build-docker-image.outputs.image }} secrets: inherit + # Send API E2E tests run the wrapper suite against the liblogosdelivery.so + # built by the `build` job (downloaded as the `liblogosdelivery` artifact). + # Gated on `build`, so it is skipped on docker-only PRs where no lib is built. + send-api-e2e-tests: + needs: build + uses: logos-messaging/logos-delivery-interop-tests/.github/workflows/send_api_e2e_PR.yml@SMOKE_TEST_2026.06.25 + secrets: inherit + lint: name: "Lint" runs-on: ubuntu-22.04 From 57ff24760fee77c711acaaea56ff9b9e150f6a27 Mon Sep 17 00:00:00 2001 From: Tanya S <120410716+stubbsta@users.noreply.github.com> Date: Sat, 27 Jun 2026 12:28:59 +0200 Subject: [PATCH 14/33] =?UTF-8?q?chore:=20phase=202=20=E2=80=94=20rename/r?= =?UTF-8?q?estructure=20waku=5Frln=5Frelay=20=E2=86=92=20waku=5Frln=20(#39?= =?UTF-8?q?78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * move rln specific procs and types * Renaming and restructuring RLN module * Rename waku_rln_relay to waku_rln * rename rln-relay types to rln * Fix linting * update logScope topics to match new rln module name * Rename waku_rln module to rln * Rename WakuRln type to Rln, rename FFI raw type to RlnRaw to avoid collision * Apply nph formatting * Rename local rlnRelay to rln in mountRlnRelay --- apps/benchmarks/benchmarks.nim | 10 +- apps/chat2/chat2.nim | 24 +- apps/chat2mix/chat2mix.nim | 2 +- apps/networkmonitor/networkmonitor.nim | 4 +- .../stealth_commitment_protocol.nim | 6 +- .../send_service/send_service.nim | 10 +- .../conf_builder/rln_relay_conf_builder.nim | 36 +-- .../conf_builder/waku_conf_builder.nim | 4 +- logos_delivery/waku/factory/node_factory.nim | 2 +- logos_delivery/waku/factory/waku_conf.nim | 6 +- .../health_monitor/node_health_monitor.nim | 6 +- logos_delivery/waku/node/waku_metrics.nim | 2 +- logos_delivery/waku/node/waku_node.nim | 12 +- .../waku/node/waku_node/lightpush.nim | 14 +- logos_delivery/waku/node/waku_node/relay.nim | 16 +- .../waku/rest_api/endpoint/relay/handlers.nim | 15 +- logos_delivery/waku/rln.nim | 3 + .../adapters/relay.nim | 10 +- .../rln.nim => rln/bindings.nim} | 0 logos_delivery/waku/rln/config.nim | 26 +++ .../{waku_rln_relay => rln}/constants.nim | 2 +- .../waku/{waku_rln_relay => rln}/contract.nim | 0 .../conversion_utils.nim | 2 +- .../{waku_rln_relay => rln}/group_manager.nim | 0 .../group_manager/group_manager_base.nim | 2 +- .../group_manager/on_chain.nim | 0 .../group_manager/on_chain/group_manager.nim | 4 +- .../group_manager/on_chain/retry_wrapper.nim | 0 .../group_manager/on_chain/rpc_wrapper.nim | 2 +- .../{waku_rln_relay => rln}/nonce_manager.nim | 0 logos_delivery/waku/rln/nullifier_log.nim | 75 +++++++ logos_delivery/waku/rln/proof.nim | 65 ++++++ .../protocol_metrics.nim | 2 +- .../protocol_types.nim | 10 +- .../rln_relay.nim => rln/rln.nim} | 211 +++--------------- .../rln/rln_interface.nim | 24 +- .../{waku_rln_relay => rln}/rln/wrappers.nim | 46 ++-- logos_delivery/waku/rln/types.nim | 22 ++ logos_delivery/waku/waku.nim | 2 +- .../waku/waku_keystore/protocol_types.nim | 2 +- .../waku/waku_lightpush/callbacks.nim | 11 +- .../waku/waku_lightpush_legacy/callbacks.nim | 8 +- logos_delivery/waku/waku_rln_relay.nim | 7 - tests/node/test_wakunode_legacy_lightpush.nim | 4 +- tests/node/test_wakunode_lightpush.nim | 5 +- tests/node/test_wakunode_relay_rln.nim | 62 +++-- tests/waku_keystore/utils.nim | 4 +- tests/waku_relay/utils.nim | 11 +- .../waku_rln_relay/rln/test_rln_interface.nim | 4 +- tests/waku_rln_relay/rln/test_wrappers.nim | 4 +- .../rln/waku_rln_relay_utils.nim | 14 +- .../test_rln_contract_deployment.nim | 7 +- .../test_rln_group_manager_onchain.nim | 14 +- .../waku_rln_relay/test_rln_nonce_manager.nim | 2 +- tests/waku_rln_relay/test_waku_rln_relay.nim | 93 ++++---- .../test_wakunode_rln_relay.nim | 94 ++++---- tests/waku_rln_relay/utils_offchain.nim | 8 +- tests/waku_rln_relay/utils_onchain.nim | 9 +- tests/wakunode_rest/test_rest_health.nim | 2 +- tests/wakunode_rest/test_rest_relay.nim | 12 +- .../rln_keystore_generator.nim | 8 +- 61 files changed, 523 insertions(+), 539 deletions(-) create mode 100644 logos_delivery/waku/rln.nim rename logos_delivery/waku/{waku_rln_relay => rln}/adapters/relay.nim (88%) rename logos_delivery/waku/{waku_rln_relay/rln.nim => rln/bindings.nim} (100%) create mode 100644 logos_delivery/waku/rln/config.nim rename logos_delivery/waku/{waku_rln_relay => rln}/constants.nim (98%) rename logos_delivery/waku/{waku_rln_relay => rln}/contract.nim (100%) rename logos_delivery/waku/{waku_rln_relay => rln}/conversion_utils.nim (98%) rename logos_delivery/waku/{waku_rln_relay => rln}/group_manager.nim (100%) rename logos_delivery/waku/{waku_rln_relay => rln}/group_manager/group_manager_base.nim (99%) rename logos_delivery/waku/{waku_rln_relay => rln}/group_manager/on_chain.nim (100%) rename logos_delivery/waku/{waku_rln_relay => rln}/group_manager/on_chain/group_manager.nim (99%) rename logos_delivery/waku/{waku_rln_relay => rln}/group_manager/on_chain/retry_wrapper.nim (100%) rename logos_delivery/waku/{waku_rln_relay => rln}/group_manager/on_chain/rpc_wrapper.nim (98%) rename logos_delivery/waku/{waku_rln_relay => rln}/nonce_manager.nim (100%) create mode 100644 logos_delivery/waku/rln/nullifier_log.nim create mode 100644 logos_delivery/waku/rln/proof.nim rename logos_delivery/waku/{waku_rln_relay => rln}/protocol_metrics.nim (99%) rename logos_delivery/waku/{waku_rln_relay => rln}/protocol_types.nim (94%) rename logos_delivery/waku/{waku_rln_relay/rln_relay.nim => rln/rln.nim} (55%) rename logos_delivery/waku/{waku_rln_relay => rln}/rln/rln_interface.nim (94%) rename logos_delivery/waku/{waku_rln_relay => rln}/rln/wrappers.nim (91%) create mode 100644 logos_delivery/waku/rln/types.nim delete mode 100644 logos_delivery/waku/waku_rln_relay.nim diff --git a/apps/benchmarks/benchmarks.nim b/apps/benchmarks/benchmarks.nim index 705beeacf..eb1efe98d 100644 --- a/apps/benchmarks/benchmarks.nim +++ b/apps/benchmarks/benchmarks.nim @@ -3,11 +3,11 @@ import import logos_delivery/waku/[ - waku_rln_relay/protocol_types, - waku_rln_relay/rln, - waku_rln_relay, - waku_rln_relay/conversion_utils, - waku_rln_relay/group_manager/on_chain/group_manager, + rln/protocol_types, + rln/bindings, + rln, + rln/conversion_utils, + rln/group_manager/on_chain/group_manager, ], tests/waku_rln_relay/utils_onchain diff --git a/apps/chat2/chat2.nim b/apps/chat2/chat2.nim index 968d4582a..aa36300fc 100644 --- a/apps/chat2/chat2.nim +++ b/apps/chat2/chat2.nim @@ -48,7 +48,7 @@ import ./config_chat2 import libp2p/protocols/pubsub/rpc/messages, libp2p/protocols/pubsub/pubsub -import ../../logos_delivery/waku/waku_rln_relay +import ../../logos_delivery/waku/rln const Help = """ Commands: /[?|help|connect|nick|exit] @@ -189,11 +189,11 @@ proc publish(c: Chat, line: string) = timestamp: getNanosecondTime(time), ) - if not isNil(c.node.wakuRlnRelay): + if not isNil(c.node.rln): # for future version when we support more than one rln protected content topic, # we should check the message content topic as well let proofRes = - waitFor c.node.wakuRlnRelay.generateRLNProof(message.toRLNSignal(), float64(time)) + waitFor c.node.rln.generateRLNProof(message.toRLNSignal(), float64(time)) if proofRes.isErr(): info "could not append rate limit proof to the message" else: @@ -204,13 +204,13 @@ proc publish(c: Chat, line: string) = return # TODO move it to log after dogfooding let msgEpoch = fromEpoch(proof.epoch) - if fromEpoch(c.node.wakuRlnRelay.lastEpoch) == msgEpoch: + if fromEpoch(c.node.rln.lastEpoch) == msgEpoch: echo "--rln epoch: ", msgEpoch, " ⚠️ message rate violation! you are spamming the network!" else: echo "--rln epoch: ", msgEpoch # update the last epoch - c.node.wakuRlnRelay.lastEpoch = proof.epoch + c.node.rln.lastEpoch = proof.epoch try: if not c.node.wakuLegacyLightPush.isNil(): @@ -521,7 +521,7 @@ proc processInput(rfd: AsyncFD, rng: crypto.Rng) {.async.} = topic = DefaultPubsubTopic, error = error if conf.rlnRelay: - info "WakuRLNRelay is enabled" + info "Rln is enabled" proc spamHandler(wakuMessage: WakuMessage) {.gcsafe, closure.} = info "spam handler is called" @@ -538,9 +538,7 @@ proc processInput(rfd: AsyncFD, rng: crypto.Rng) {.async.} = chainId: UInt256.fromBytesBE(conf.rlnRelayChainId.toBytesBE()), ethClientUrls: conf.ethClientUrls.mapIt(string(it)), creds: some( - RlnRelayCreds( - path: conf.rlnRelayCredPath, password: conf.rlnRelayCredPassword - ) + RlnCreds(path: conf.rlnRelayCredPath, password: conf.rlnRelayCredPassword) ), userMessageLimit: conf.rlnRelayUserMessageLimit, epochSizeSec: conf.rlnEpochSizeSec, @@ -548,14 +546,14 @@ proc processInput(rfd: AsyncFD, rng: crypto.Rng) {.async.} = waitFor node.mountRlnRelay(rlnConf, spamHandler = some(spamHandler)) - let membershipIndex = node.wakuRlnRelay.groupManager.membershipIndex.get() - let identityCredential = node.wakuRlnRelay.groupManager.idCredentials.get() + let membershipIndex = node.rln.groupManager.membershipIndex.get() + let identityCredential = node.rln.groupManager.idCredentials.get() echo "your membership index is: ", membershipIndex echo "your rln identity commitment key is: ", identityCredential.idCommitment.inHex() else: - info "WakuRLNRelay is disabled" - echo "WakuRLNRelay is disabled, please enable it by passing in the --rln-relay flag" + info "Rln is disabled" + echo "Rln is disabled, please enable it by passing in the --rln-relay flag" if conf.metricsLogging: startMetricsLog() diff --git a/apps/chat2mix/chat2mix.nim b/apps/chat2mix/chat2mix.nim index 155f1e855..f5d864a8f 100644 --- a/apps/chat2mix/chat2mix.nim +++ b/apps/chat2mix/chat2mix.nim @@ -56,7 +56,7 @@ import ./config_chat2mix import libp2p/protocols/pubsub/rpc/messages, libp2p/protocols/pubsub/pubsub -import ../../logos_delivery/waku/waku_rln_relay +import ../../logos_delivery/waku/rln logScope: topics = "chat2 mix" diff --git a/apps/networkmonitor/networkmonitor.nim b/apps/networkmonitor/networkmonitor.nim index 4c2d047ae..2c0a5ca89 100644 --- a/apps/networkmonitor/networkmonitor.nim +++ b/apps/networkmonitor/networkmonitor.nim @@ -25,7 +25,7 @@ import discovery/waku_discv5, discovery/waku_dnsdisc, waku_relay, - waku_rln_relay, + rln, factory/builder, factory/networks_config, ], @@ -614,7 +614,7 @@ when isMainModule: ethContractAddress: conf.rlnRelayEthContractAddress, ethClientUrls: conf.ethClientUrls.mapIt(string(it)), epochSizeSec: conf.rlnEpochSizeSec, - creds: none(RlnRelayCreds), + creds: none(RlnCreds), onFatalErrorAction: onFatalErrorAction, ) diff --git a/examples/wakustealthcommitments/stealth_commitment_protocol.nim b/examples/wakustealthcommitments/stealth_commitment_protocol.nim index f7b34e26a..097994126 100644 --- a/examples/wakustealthcommitments/stealth_commitment_protocol.nim +++ b/examples/wakustealthcommitments/stealth_commitment_protocol.nim @@ -2,7 +2,7 @@ import results, - logos_delivery/waku/[common/logging, waku_node, waku_rln_relay], + logos_delivery/waku/[common/logging, waku_node, rln], ./erc_5564_interface as StealthCommitmentFFI, ./node_spec, ./wire_spec @@ -47,9 +47,7 @@ proc sendThruWaku*( ) message.proof = ( - await self.waku.node.wakuRlnRelay.generateRLNProof( - message.toRLNSignal(), float64(time) - ) + await self.waku.node.rln.generateRLNProof(message.toRLNSignal(), float64(time)) ).valueOr: return err("could not append rate limit proof to the message: " & error) diff --git a/logos_delivery/messaging/delivery_service/send_service/send_service.nim b/logos_delivery/messaging/delivery_service/send_service/send_service.nim index 00f2ff672..67f8de95f 100644 --- a/logos_delivery/messaging/delivery_service/send_service/send_service.nim +++ b/logos_delivery/messaging/delivery_service/send_service/send_service.nim @@ -15,7 +15,7 @@ import waku_store/client, waku_store/common, waku_relay/protocol, - waku_rln_relay/rln_relay, + rln/rln, waku_lightpush/client, waku_lightpush/callbacks, events/message_events, @@ -65,7 +65,7 @@ proc setupSendProcessorChain( peerManager: PeerManager, lightpushClient: WakuLightPushClient, relay: WakuRelay, - rlnRelay: WakuRLNRelay, + rlnRelay: Rln, brokerCtx: BrokerContext, ): Result[BaseSendProcessor, string] = let isRelayAvail = not relay.isNil() @@ -77,9 +77,9 @@ proc setupSendProcessorChain( var processors = newSeq[BaseSendProcessor]() if isRelayAvail: - let rln: Option[WakuRLNRelay] = + let rln: Option[Rln] = if rlnRelay.isNil(): - none[WakuRLNRelay]() + none[Rln]() else: some(rlnRelay) let publishProc = getRelayPushHandler(relay, rln) @@ -107,7 +107,7 @@ proc new*( let checkStoreForMessages = preferP2PReliability and not w.wakuStoreClient.isNil() let sendProcessorChain = setupSendProcessorChain( - w.peerManager, w.wakuLightPushClient, w.wakuRelay, w.wakuRlnRelay, w.brokerCtx + w.peerManager, w.wakuLightPushClient, w.wakuRelay, w.rln, w.brokerCtx ).valueOr: return err("failed to setup SendProcessorChain: " & $error) diff --git a/logos_delivery/waku/factory/conf_builder/rln_relay_conf_builder.nim b/logos_delivery/waku/factory/conf_builder/rln_relay_conf_builder.nim index f4e70767e..8835f4c66 100644 --- a/logos_delivery/waku/factory/conf_builder/rln_relay_conf_builder.nim +++ b/logos_delivery/waku/factory/conf_builder/rln_relay_conf_builder.nim @@ -12,7 +12,7 @@ const ############################## ## RLN Relay Config Builder ## ############################## -type RlnRelayConfBuilder* = object +type RlnConfBuilder* = object enabled*: Option[bool] chainId*: Option[UInt256] ethClientUrls*: Option[seq[string]] @@ -24,58 +24,58 @@ type RlnRelayConfBuilder* = object epochSizeSec*: Option[uint64] userMessageLimit*: Option[uint64] -proc init*(T: type RlnRelayConfBuilder): RlnRelayConfBuilder = - RlnRelayConfBuilder() +proc init*(T: type RlnConfBuilder): RlnConfBuilder = + RlnConfBuilder() -proc withEnabled*(b: var RlnRelayConfBuilder, enabled: bool) = +proc withEnabled*(b: var RlnConfBuilder, enabled: bool) = b.enabled = some(enabled) -proc withChainId*(b: var RlnRelayConfBuilder, chainId: uint | UInt256) = +proc withChainId*(b: var RlnConfBuilder, chainId: uint | UInt256) = when chainId is uint: b.chainId = some(UInt256.fromBytesBE(chainId.toBytesBE())) else: b.chainId = some(chainId) -proc withCredIndex*(b: var RlnRelayConfBuilder, credIndex: uint) = +proc withCredIndex*(b: var RlnConfBuilder, credIndex: uint) = b.credIndex = some(credIndex) -proc withCredPassword*(b: var RlnRelayConfBuilder, credPassword: string) = +proc withCredPassword*(b: var RlnConfBuilder, credPassword: string) = b.credPassword = some(credPassword) -proc withCredPath*(b: var RlnRelayConfBuilder, credPath: string) = +proc withCredPath*(b: var RlnConfBuilder, credPath: string) = b.credPath = some(credPath) -proc withDynamic*(b: var RlnRelayConfBuilder, dynamic: bool) = +proc withDynamic*(b: var RlnConfBuilder, dynamic: bool) = b.dynamic = some(dynamic) -proc withEthClientUrls*(b: var RlnRelayConfBuilder, ethClientUrls: seq[string]) = +proc withEthClientUrls*(b: var RlnConfBuilder, ethClientUrls: seq[string]) = b.ethClientUrls = some(ethClientUrls) -proc withEthContractAddress*(b: var RlnRelayConfBuilder, ethContractAddress: string) = +proc withEthContractAddress*(b: var RlnConfBuilder, ethContractAddress: string) = b.ethContractAddress = some(ethContractAddress) -proc withEpochSizeSec*(b: var RlnRelayConfBuilder, epochSizeSec: uint64) = +proc withEpochSizeSec*(b: var RlnConfBuilder, epochSizeSec: uint64) = b.epochSizeSec = some(epochSizeSec) -proc withUserMessageLimit*(b: var RlnRelayConfBuilder, userMessageLimit: uint64) = +proc withUserMessageLimit*(b: var RlnConfBuilder, userMessageLimit: uint64) = b.userMessageLimit = some(userMessageLimit) -proc build*(b: RlnRelayConfBuilder): Result[Option[RlnRelayConf], string] = +proc build*(b: RlnConfBuilder): Result[Option[RlnConf], string] = if not b.enabled.get(DefaultRlnRelayEnabled): - return ok(none(RlnRelayConf)) + return ok(none(RlnConf)) if b.chainId.isNone(): return err("RLN Relay Chain Id is not specified") let creds = if b.credPath.isSome() and b.credPassword.isSome(): - some(RlnRelayCreds(path: b.credPath.get(), password: b.credPassword.get())) + some(RlnCreds(path: b.credPath.get(), password: b.credPassword.get())) elif b.credPath.isSome() and b.credPassword.isNone(): return err("RLN Relay Credential Password is not specified but path is") elif b.credPath.isNone() and b.credPassword.isSome(): return err("RLN Relay Credential Path is not specified but password is") else: - none(RlnRelayCreds) + none(RlnCreds) if b.dynamic.isNone(): return err("rlnRelay.dynamic is not specified") @@ -85,7 +85,7 @@ proc build*(b: RlnRelayConfBuilder): Result[Option[RlnRelayConf], string] = return err("rlnRelay.ethContractAddress is not specified") return ok( some( - RlnRelayConf( + RlnConf( chainId: b.chainId.get(), credIndex: b.credIndex, creds: creds, diff --git a/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim b/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim index dc17fb4ff..9fbd23f54 100644 --- a/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim +++ b/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim @@ -116,7 +116,7 @@ type WakuConfBuilder* = object filterServiceConf*: FilterServiceConfBuilder metricsServerConf*: MetricsServerConfBuilder restServerConf*: RestServerConfBuilder - rlnRelayConf*: RlnRelayConfBuilder + rlnRelayConf*: RlnConfBuilder storeServiceConf*: StoreServiceConfBuilder mixConf*: MixConfBuilder webSocketConf*: WebSocketConfBuilder @@ -181,7 +181,7 @@ proc init*(T: type WakuConfBuilder): WakuConfBuilder = filterServiceConf: FilterServiceConfBuilder.init(), metricsServerConf: MetricsServerConfBuilder.init(), restServerConf: RestServerConfBuilder.init(), - rlnRelayConf: RlnRelayConfBuilder.init(), + rlnRelayConf: RlnConfBuilder.init(), storeServiceConf: StoreServiceConfBuilder.init(), webSocketConf: WebSocketConfBuilder.init(), quicConf: QuicConfBuilder.init(), diff --git a/logos_delivery/waku/factory/node_factory.nim b/logos_delivery/waku/factory/node_factory.nim index 30e37850a..48a21aeb0 100644 --- a/logos_delivery/waku/factory/node_factory.nim +++ b/logos_delivery/waku/factory/node_factory.nim @@ -23,7 +23,7 @@ import ../net/net_config, ../waku_core, ../waku_core/codecs, - ../waku_rln_relay, + ../rln, ../discovery/waku_dnsdisc, ../waku_archive/retention_policy as policy, ../waku_archive/retention_policy/builder as policy_builder, diff --git a/logos_delivery/waku/factory/waku_conf.nim b/logos_delivery/waku/factory/waku_conf.nim index 9de3d3bc8..0ffe803ae 100644 --- a/logos_delivery/waku/factory/waku_conf.nim +++ b/logos_delivery/waku/factory/waku_conf.nim @@ -13,7 +13,7 @@ import results import - ../waku_rln_relay/rln_relay, + ../rln/rln, ../rest_api/endpoint/builder, ../discovery/waku_discv5, ../discovery/waku_kademlia, @@ -25,7 +25,7 @@ import ../waku_mix, ./conf_builder/kademlia_discovery_conf_builder -export RlnRelayConf, RlnRelayCreds, RestServerConf, Discv5Conf, MetricsServerConf +export RlnConf, RlnCreds, RestServerConf, Discv5Conf, MetricsServerConf logScope: topics = "waku conf" @@ -113,7 +113,7 @@ type WakuConf* {.requiresInit.} = ref object dnsDiscoveryConf*: Option[DnsDiscoveryConf] filterServiceConf*: Option[FilterServiceConf] storeServiceConf*: Option[StoreServiceConf] - rlnRelayConf*: Option[RlnRelayConf] + rlnRelayConf*: Option[RlnConf] restServerConf*: Option[RestServerConf] metricsServerConf*: Option[MetricsServerConf] webSocketConf*: Option[WebSocketConf] diff --git a/logos_delivery/waku/node/health_monitor/node_health_monitor.nim b/logos_delivery/waku/node/health_monitor/node_health_monitor.nim index 465598794..d75a793ac 100644 --- a/logos_delivery/waku/node/health_monitor/node_health_monitor.nim +++ b/logos_delivery/waku/node/health_monitor/node_health_monitor.nim @@ -11,7 +11,7 @@ import logos_delivery/api/types, logos_delivery/waku/[ waku_relay, - waku_rln_relay, + rln, events/health_events, events/peer_events, node/waku_node, @@ -89,12 +89,12 @@ proc getRelayHealth(hm: NodeHealthMonitor): ProtocolHealth = proc getRlnRelayHealth(hm: NodeHealthMonitor): Future[ProtocolHealth] {.async.} = var p = ProtocolHealth.init(WakuProtocol.RlnRelayProtocol) - if isNil(hm.node.wakuRlnRelay): + if isNil(hm.node.rln): return p.notMounted() const FutIsReadyTimout = 5.seconds - let isReadyStateFut = hm.node.wakuRlnRelay.isReady() + let isReadyStateFut = hm.node.rln.isReady() if not await isReadyStateFut.withTimeout(FutIsReadyTimout): return p.notReady("Ready state check timed out") diff --git a/logos_delivery/waku/node/waku_metrics.nim b/logos_delivery/waku/node/waku_metrics.nim index 08eecbe5c..7058af755 100644 --- a/logos_delivery/waku/node/waku_metrics.nim +++ b/logos_delivery/waku/node/waku_metrics.nim @@ -4,7 +4,7 @@ import logos_delivery/waku/compat/option_valueor import chronicles, chronos, metrics, metrics/chronos_httpserver import logos_delivery/waku/ - [net/auto_port, waku_rln_relay/protocol_metrics as rln_metrics, utils/collector], + [net/auto_port, rln/protocol_metrics as rln_metrics, utils/collector], ./peer_manager, ./node_telemetry, ./waku_node diff --git a/logos_delivery/waku/node/waku_node.nim b/logos_delivery/waku/node/waku_node.nim index 2ad7dc601..665f0ba98 100644 --- a/logos_delivery/waku/node/waku_node.nim +++ b/logos_delivery/waku/node/waku_node.nim @@ -52,7 +52,7 @@ import waku_lightpush as lightpush_protocol, waku_enr, waku_peer_exchange, - waku_rln_relay, + rln, common/rate_limit/setting, common/callbacks, common/nimchronos, @@ -110,7 +110,7 @@ type wakuStoreTransfer*: SyncTransfer wakuFilter*: waku_filter_v2.WakuFilter wakuFilterClient*: filter_client.WakuFilterClient - wakuRlnRelay*: WakuRLNRelay + rln*: Rln wakuLegacyLightPush*: WakuLegacyLightPush wakuLegacyLightpushClient*: WakuLegacyLightPushClient wakuLightPush*: WakuLightPush @@ -662,9 +662,9 @@ proc stop*(node: WakuNode) {.async.} = node.peerManager.stop() - if not node.wakuRlnRelay.isNil(): + if not node.rln.isNil(): try: - await node.wakuRlnRelay.stop() ## this can raise an exception + await node.rln.stop() ## this can raise an exception except Exception: error "exception stopping the node", error = getCurrentExceptionMsg() @@ -684,7 +684,7 @@ proc stop*(node: WakuNode) {.async.} = node.started = false proc isReady*(node: WakuNode): Future[bool] {.async: (raises: [Exception]).} = - if node.wakuRlnRelay == nil: + if node.rln == nil: return true - return await node.wakuRlnRelay.isReady() + return await node.rln.isReady() ## TODO: add other protocol `isReady` checks diff --git a/logos_delivery/waku/node/waku_node/lightpush.nim b/logos_delivery/waku/node/waku_node/lightpush.nim index 10f27e32f..6b9d816fd 100644 --- a/logos_delivery/waku/node/waku_node/lightpush.nim +++ b/logos_delivery/waku/node/waku_node/lightpush.nim @@ -30,7 +30,7 @@ import ../../waku_lightpush as lightpush_protocol, ../peer_manager, ../../common/rate_limit/setting, - ../../waku_rln_relay + ../../rln logScope: topics = "waku node lightpush api" @@ -48,12 +48,12 @@ proc mountLegacyLightPush*( info "mounting legacy lightpush with relay" let rlnPeer = - if node.wakuRlnRelay.isNil(): + if node.rln.isNil(): info "mounting legacy lightpush without rln-relay" - none(WakuRLNRelay) + none(Rln) else: info "mounting legacy lightpush with rln-relay" - some(node.wakuRlnRelay) + some(node.rln) let pushHandler = legacy_lightpush_protocol.getRelayPushHandler(node.wakuRelay, rlnPeer) @@ -159,12 +159,12 @@ proc mountLightPush*( info "mounting lightpush with relay" let rlnPeer = - if node.wakuRlnRelay.isNil(): + if node.rln.isNil(): info "mounting lightpush without rln-relay" - none(WakuRLNRelay) + none(Rln) else: info "mounting lightpush with rln-relay" - some(node.wakuRlnRelay) + some(node.rln) let pushHandler = lightpush_protocol.getRelayPushHandler(node.wakuRelay, rlnPeer) node.wakuLightPush = WakuLightPush.new( diff --git a/logos_delivery/waku/node/waku_node/relay.nim b/logos_delivery/waku/node/waku_node/relay.nim index c1e79d44e..b1a2f9287 100644 --- a/logos_delivery/waku/node/waku_node/relay.nim +++ b/logos_delivery/waku/node/waku_node/relay.nim @@ -28,8 +28,8 @@ import waku_filter_v2, waku_archive, waku_store_sync, - waku_rln_relay, - waku_rln_relay/adapters/relay as waku_rln_relay_adapter, + rln, + rln/adapters/relay as waku_rln_adapter, node/waku_node, node/subscription_manager, node/peer_manager, @@ -195,17 +195,17 @@ proc mountRlnRelay*( if node.wakuRelay.isNil(): raise newException( - CatchableError, "WakuRelay protocol is not mounted, cannot mount WakuRlnRelay" + CatchableError, "WakuRelay protocol is not mounted, cannot mount Rln" ) - let rlnRelay = (await WakuRlnRelay.new(rlnConf, registrationHandler)).valueOr: - raise newException(CatchableError, "failed to mount WakuRlnRelay: " & error) - if (rlnConf.userMessageLimit > rlnRelay.groupManager.rlnRelayMaxMessageLimit): + let rln = (await Rln.new(rlnConf, registrationHandler)).valueOr: + raise newException(CatchableError, "failed to mount Rln: " & error) + if (rlnConf.userMessageLimit > rln.groupManager.rlnRelayMaxMessageLimit): error "rln-relay-user-message-limit can't exceed the MAX_MESSAGE_LIMIT in the rln contract" - let validator = generateRlnValidator(rlnRelay, spamHandler) + let validator = generateRlnValidator(rln, spamHandler) # register rln validator as default validator info "Registering RLN validator" node.wakuRelay.addValidator(validator, "RLN validation failed") - node.wakuRlnRelay = rlnRelay + node.rln = rln diff --git a/logos_delivery/waku/rest_api/endpoint/relay/handlers.nim b/logos_delivery/waku/rest_api/endpoint/relay/handlers.nim index 9075c61df..6b1fdcc69 100644 --- a/logos_delivery/waku/rest_api/endpoint/relay/handlers.nim +++ b/logos_delivery/waku/rest_api/endpoint/relay/handlers.nim @@ -13,7 +13,7 @@ import import ../../../waku_node, ../../../waku_relay/protocol, - ../../../waku_rln_relay, + ../../../rln, ../../../node/waku_node, ../../message_cache, ../../handlers, @@ -167,11 +167,11 @@ proc installRelayApiHandlers*( return RestApiResponse.badRequest($error) # if RLN is mounted, append the proof to the message - if not node.wakuRlnRelay.isNil(): + if not node.rln.isNil(): # append the proof to the message message.proof = ( - await node.wakuRlnRelay.generateRLNProof( + await node.rln.generateRLNProof( message.toRLNSignal(), float64(getTime().toUnix()) ) ).valueOr: @@ -186,8 +186,7 @@ proc installRelayApiHandlers*( logMessageInfo(node.wakuRelay, "rest", pubsubTopic, "none", message, onRecv = true) # if we reach here its either a non-RLN message or a RLN message with a valid proof - info "Publishing message", - pubSubTopic = pubSubTopic, rln = not node.wakuRlnRelay.isNil() + info "Publishing message", pubSubTopic = pubSubTopic, rln = not node.rln.isNil() if not (waitFor node.publish(some(pubSubTopic), message).withTimeout(futTimeout)): error "Failed to publish message to topic", pubSubTopic = pubSubTopic return RestApiResponse.internalServerError("Failed to publish: timedout") @@ -299,9 +298,9 @@ proc installRelayApiHandlers*( return RestApiResponse.badRequest("Failed to publish. " & msg) # if RLN is mounted, append the proof to the message - if not node.wakuRlnRelay.isNil(): + if not node.rln.isNil(): message.proof = ( - await node.wakuRlnRelay.generateRLNProof( + await node.rln.generateRLNProof( message.toRLNSignal(), float64(getTime().toUnix()) ) ).valueOr: @@ -317,7 +316,7 @@ proc installRelayApiHandlers*( # if we reach here its either a non-RLN message or a RLN message with a valid proof info "Publishing message", - contentTopic = message.contentTopic, rln = not node.wakuRlnRelay.isNil() + contentTopic = message.contentTopic, rln = not node.rln.isNil() var publishFut = node.publish(some($pubsubTopic), message) if not await publishFut.withTimeout(futTimeout): diff --git a/logos_delivery/waku/rln.nim b/logos_delivery/waku/rln.nim new file mode 100644 index 000000000..c8aa98857 --- /dev/null +++ b/logos_delivery/waku/rln.nim @@ -0,0 +1,3 @@ +import ./rln/group_manager, ./rln/conversion_utils, ./rln/rln, ./rln/contract + +export group_manager, conversion_utils, rln, contract diff --git a/logos_delivery/waku/waku_rln_relay/adapters/relay.nim b/logos_delivery/waku/rln/adapters/relay.nim similarity index 88% rename from logos_delivery/waku/waku_rln_relay/adapters/relay.nim rename to logos_delivery/waku/rln/adapters/relay.nim index 206839aab..cce1d6bbc 100644 --- a/logos_delivery/waku/waku_rln_relay/adapters/relay.nim +++ b/logos_delivery/waku/rln/adapters/relay.nim @@ -8,15 +8,15 @@ import stew/byteutils, libp2p/protocols/pubsub/pubsub -import ../rln_relay, ../protocol_types, ../protocol_metrics, ../conversion_utils +import ../rln, ../protocol_types, ../protocol_metrics, ../conversion_utils import logos_delivery/waku/[waku_relay, waku_core] logScope: - topics = "waku rln_relay adapter" + topics = "waku rln adapter" proc generateRlnValidator*( - wakuRlnRelay: WakuRLNRelay, spamHandler = none(SpamHandler) + rln: Rln, spamHandler = none(SpamHandler) ): WakuValidatorHandler = ## Bridges RLN's protocol-agnostic message validation into a relay ## (gossipsub) validator. The core decision is made by @@ -28,14 +28,14 @@ proc generateRlnValidator*( topic: string, message: WakuMessage ): Future[pubsub.ValidationResult] {.async.} = trace "rln-relay topic validator is called" - wakuRlnRelay.clearNullifierLog() + rln.clearNullifierLog() let msgProof = RateLimitProof.init(message.proof).valueOr: trace "generateRlnValidator reject", error = error return pubsub.ValidationResult.Reject # validate the message and update log - let validationRes = await wakuRlnRelay.validateMessageAndUpdateLog(message) + let validationRes = await rln.validateMessageAndUpdateLog(message) let proof = byteutils.toHex(msgProof.proof) diff --git a/logos_delivery/waku/waku_rln_relay/rln.nim b/logos_delivery/waku/rln/bindings.nim similarity index 100% rename from logos_delivery/waku/waku_rln_relay/rln.nim rename to logos_delivery/waku/rln/bindings.nim diff --git a/logos_delivery/waku/rln/config.nim b/logos_delivery/waku/rln/config.nim new file mode 100644 index 000000000..4d449f392 --- /dev/null +++ b/logos_delivery/waku/rln/config.nim @@ -0,0 +1,26 @@ +{.push raises: [].} + +import std/options, stint + +import logos_delivery/waku/common/error_handling + +type RlnCreds* {.requiresInit.} = object + path*: string + password*: string + +type RlnConf* = object of RootObj + # TODO: severals parameters are only needed when it's dynamic + # change the config to either nest or use enum/type variant so it's obvious + # and then it can be set to `requiresInit` + dynamic*: bool + credIndex*: Option[uint] + ethContractAddress*: string + ethClientUrls*: seq[string] + chainId*: UInt256 + creds*: Option[RlnCreds] + epochSizeSec*: uint64 + userMessageLimit*: uint64 + ethPrivateKey*: Option[string] + +type WakuRlnConfig* = object of RlnConf + onFatalErrorAction*: OnFatalErrorHandler diff --git a/logos_delivery/waku/waku_rln_relay/constants.nim b/logos_delivery/waku/rln/constants.nim similarity index 98% rename from logos_delivery/waku/waku_rln_relay/constants.nim rename to logos_delivery/waku/rln/constants.nim index 7ede78d43..22454b22b 100644 --- a/logos_delivery/waku/waku_rln_relay/constants.nim +++ b/logos_delivery/waku/rln/constants.nim @@ -23,7 +23,7 @@ const PathCheckMinInterval* = 30.seconds const RlnCredentialsFilename* = "rlnCredentials.txt" # inputs of the membership contract constructor -# TODO may be able to make these constants private and put them inside the waku_rln_relay_utils +# TODO may be able to make these constants private and put them inside the waku_rln_utils const # in wei MembershipFee* = 0.u256 diff --git a/logos_delivery/waku/waku_rln_relay/contract.nim b/logos_delivery/waku/rln/contract.nim similarity index 100% rename from logos_delivery/waku/waku_rln_relay/contract.nim rename to logos_delivery/waku/rln/contract.nim diff --git a/logos_delivery/waku/waku_rln_relay/conversion_utils.nim b/logos_delivery/waku/rln/conversion_utils.nim similarity index 98% rename from logos_delivery/waku/waku_rln_relay/conversion_utils.nim rename to logos_delivery/waku/rln/conversion_utils.nim index fc130621b..2b2ad626f 100644 --- a/logos_delivery/waku/waku_rln_relay/conversion_utils.nim +++ b/logos_delivery/waku/rln/conversion_utils.nim @@ -12,7 +12,7 @@ import ../waku_keystore export web3, chronicles, stint, constants, endians2 logScope: - topics = "waku rln_relay conversion_utils" + topics = "waku rln conversion_utils" proc inHex*( value: diff --git a/logos_delivery/waku/waku_rln_relay/group_manager.nim b/logos_delivery/waku/rln/group_manager.nim similarity index 100% rename from logos_delivery/waku/waku_rln_relay/group_manager.nim rename to logos_delivery/waku/rln/group_manager.nim diff --git a/logos_delivery/waku/waku_rln_relay/group_manager/group_manager_base.nim b/logos_delivery/waku/rln/group_manager/group_manager_base.nim similarity index 99% rename from logos_delivery/waku/waku_rln_relay/group_manager/group_manager_base.nim rename to logos_delivery/waku/rln/group_manager/group_manager_base.nim index 365227a14..21571aa52 100644 --- a/logos_delivery/waku/waku_rln_relay/group_manager/group_manager_base.nim +++ b/logos_delivery/waku/rln/group_manager/group_manager_base.nim @@ -23,7 +23,7 @@ type GroupManager* = ref object of RootObj membershipIndex*: Option[MembershipIndex] registerCb*: Option[OnRegisterCallback] withdrawCb*: Option[OnWithdrawCallback] - rlnInstance*: ptr RLN + rlnInstance*: ptr RlnRaw initialized*: bool latestIndex*: MembershipIndex validRoots*: Deque[MerkleNode] diff --git a/logos_delivery/waku/waku_rln_relay/group_manager/on_chain.nim b/logos_delivery/waku/rln/group_manager/on_chain.nim similarity index 100% rename from logos_delivery/waku/waku_rln_relay/group_manager/on_chain.nim rename to logos_delivery/waku/rln/group_manager/on_chain.nim diff --git a/logos_delivery/waku/waku_rln_relay/group_manager/on_chain/group_manager.nim b/logos_delivery/waku/rln/group_manager/on_chain/group_manager.nim similarity index 99% rename from logos_delivery/waku/waku_rln_relay/group_manager/on_chain/group_manager.nim rename to logos_delivery/waku/rln/group_manager/on_chain/group_manager.nim index 66edcf93d..e316de85b 100644 --- a/logos_delivery/waku/waku_rln_relay/group_manager/on_chain/group_manager.nim +++ b/logos_delivery/waku/rln/group_manager/on_chain/group_manager.nim @@ -17,7 +17,7 @@ import import ../../../waku_keystore, - ../../rln, + ../../bindings, ../../rln/rln_interface, ../../conversion_utils, ../group_manager_base, @@ -27,7 +27,7 @@ import export group_manager_base logScope: - topics = "waku rln_relay onchain_group_manager" + topics = "waku rln onchain_group_manager" type WakuRlnContractWithSender = Sender[WakuRlnContract] diff --git a/logos_delivery/waku/waku_rln_relay/group_manager/on_chain/retry_wrapper.nim b/logos_delivery/waku/rln/group_manager/on_chain/retry_wrapper.nim similarity index 100% rename from logos_delivery/waku/waku_rln_relay/group_manager/on_chain/retry_wrapper.nim rename to logos_delivery/waku/rln/group_manager/on_chain/retry_wrapper.nim diff --git a/logos_delivery/waku/waku_rln_relay/group_manager/on_chain/rpc_wrapper.nim b/logos_delivery/waku/rln/group_manager/on_chain/rpc_wrapper.nim similarity index 98% rename from logos_delivery/waku/waku_rln_relay/group_manager/on_chain/rpc_wrapper.nim rename to logos_delivery/waku/rln/group_manager/on_chain/rpc_wrapper.nim index a82356af8..9cb7f1ce3 100644 --- a/logos_delivery/waku/waku_rln_relay/group_manager/on_chain/rpc_wrapper.nim +++ b/logos_delivery/waku/rln/group_manager/on_chain/rpc_wrapper.nim @@ -18,7 +18,7 @@ import ../group_manager_base logScope: - topics = "waku rln_relay onchain rpc_wrapper" + topics = "waku rln onchain rpc_wrapper" # using the when predicate does not work within the contract macro, hence need to dupe contract(WakuRlnContract): diff --git a/logos_delivery/waku/waku_rln_relay/nonce_manager.nim b/logos_delivery/waku/rln/nonce_manager.nim similarity index 100% rename from logos_delivery/waku/waku_rln_relay/nonce_manager.nim rename to logos_delivery/waku/rln/nonce_manager.nim diff --git a/logos_delivery/waku/rln/nullifier_log.nim b/logos_delivery/waku/rln/nullifier_log.nim new file mode 100644 index 000000000..372433c72 --- /dev/null +++ b/logos_delivery/waku/rln/nullifier_log.nim @@ -0,0 +1,75 @@ +{.push raises: [].} + +import std/tables +import chronicles, results + +import ./types, ./protocol_types, ./conversion_utils, ./proof + +logScope: + topics = "waku rln nullifier_log" + +proc hasDuplicate*( + rlnPeer: Rln, epoch: Epoch, proofMetadata: ProofMetadata +): RlnResult[bool] = + ## returns true if there is another message in the `nullifierLog` of the `rlnPeer` with the same + ## epoch and nullifier as `proofMetadata`'s epoch and nullifier + ## otherwise, returns false + ## Returns an error if it cannot check for duplicates + + # check if the epoch exists + let nullifier = proofMetadata.nullifier + if not rlnPeer.nullifierLog.hasKey(epoch): + return ok(false) + try: + if rlnPeer.nullifierLog[epoch].hasKey(nullifier): + # there is an identical record, mark it as spam + return ok(true) + + # there is no duplicate + return ok(false) + except KeyError: + return err("the epoch was not found: " & getCurrentExceptionMsg()) + +proc updateLog*( + rlnPeer: Rln, epoch: Epoch, proofMetadata: ProofMetadata +): RlnResult[void] = + ## saves supplied proofMetadata `proofMetadata` + ## in the `nullifierLog` of the `rlnPeer` + ## Returns an error if it cannot update the log + + # check if the epoch exists + if not rlnPeer.nullifierLog.hasKeyOrPut( + epoch, {proofMetadata.nullifier: proofMetadata}.toTable() + ): + return ok() + + try: + # check if an identical record exists + if rlnPeer.nullifierLog[epoch].hasKeyOrPut(proofMetadata.nullifier, proofMetadata): + # the above condition could be `discarded` but it is kept for clarity, that slashing will + # be implemented here + # TODO: slashing logic + return ok() + return ok() + except KeyError: + return + err("the epoch was not found: " & getCurrentExceptionMsg()) # should never happen + +proc clearNullifierLog*(rlnPeer: Rln) = + # clear the first MaxEpochGap epochs of the nullifer log + # if more than MaxEpochGap epochs are in the log + let currentEpoch = fromEpoch(rlnPeer.getCurrentEpoch()) + + var epochsToRemove: seq[Epoch] = @[] + for epoch in rlnPeer.nullifierLog.keys(): + let epochInt = fromEpoch(epoch) + + # clean all epochs that are +- rlnMaxEpochGap from the current epoch + if (currentEpoch + rlnPeer.rlnMaxEpochGap) <= epochInt or + epochInt <= (currentEpoch - rlnPeer.rlnMaxEpochGap): + epochsToRemove.add(epoch) + + for epochRemove in epochsToRemove: + trace "clearing epochs from the nullifier log", + currentEpoch = currentEpoch, cleanedEpoch = fromEpoch(epochRemove) + rlnPeer.nullifierLog.del(epochRemove) diff --git a/logos_delivery/waku/rln/proof.nim b/logos_delivery/waku/rln/proof.nim new file mode 100644 index 000000000..8f1a63517 --- /dev/null +++ b/logos_delivery/waku/rln/proof.nim @@ -0,0 +1,65 @@ +{.push raises: [].} + +import std/[times, sequtils] +import chronos, results, stew/byteutils + +import ./types, ./protocol_types, ./conversion_utils, ./group_manager, ./nonce_manager + +import logos_delivery/waku/waku_core + +proc calcEpoch*(rlnPeer: Rln, t: float64): Epoch = + ## gets time `t` as `flaot64` with subseconds resolution in the fractional part + ## and returns its corresponding rln `Epoch` value + + let e = uint64(t / rlnPeer.rlnEpochSizeSec.float64) + return toEpoch(e) + +proc nextEpoch*(rlnPeer: Rln, time: float64): float64 = + let + currentEpoch = uint64(time / rlnPeer.rlnEpochSizeSec.float64) + nextEpochTime = float64(currentEpoch + 1) * rlnPeer.rlnEpochSizeSec.float64 + currentTime = epochTime() + + # Ensure we always return a future time + if nextEpochTime > currentTime: + return nextEpochTime + else: + return epochTime() + +proc getCurrentEpoch*(rlnPeer: Rln): Epoch = + return rlnPeer.calcEpoch(epochTime()) + +proc absDiff*(e1, e2: Epoch): uint64 = + ## returns the absolute difference between the two rln `Epoch`s `e1` and `e2` + ## i.e., e1 - e2 + + # convert epochs to their corresponding unsigned numerical values + let + epoch1 = fromEpoch(e1) + epoch2 = fromEpoch(e2) + + # Manually perform an `abs` calculation + if epoch1 > epoch2: + return epoch1 - epoch2 + else: + return epoch2 - epoch1 + +proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] = + ## it is a utility proc that prepares the `data` parameter of the proof generation procedure i.e., `proofGen` that resides in the current module + ## it extracts the `contentTopic`, `timestamp` and the `payload` of the supplied `wakumessage` and serializes them into a byte sequence + + let + contentTopicBytes = toBytes(wakumessage.contentTopic) + timestampBytes = toBytes(wakumessage.timestamp.uint64) + output = concat(wakumessage.payload, contentTopicBytes, @(timestampBytes)) + return output + +proc generateRLNProof*( + rlnPeer: Rln, input: seq[byte], senderEpochTime: float64 +): Future[RlnResult[seq[byte]]] {.async.} = + let epoch = rlnPeer.calcEpoch(senderEpochTime) + let nonce = rlnPeer.nonceManager.getNonce().valueOr: + return err("could not get new message id to generate an rln proof: " & $error) + let proof = (await rlnPeer.groupManager.generateProof(input, epoch, nonce)).valueOr: + return err("could not generate rln-v2 proof: " & $error) + return ok(proof.encode().buffer) diff --git a/logos_delivery/waku/waku_rln_relay/protocol_metrics.nim b/logos_delivery/waku/rln/protocol_metrics.nim similarity index 99% rename from logos_delivery/waku/waku_rln_relay/protocol_metrics.nim rename to logos_delivery/waku/rln/protocol_metrics.nim index 2cea329fe..fa034aa8e 100644 --- a/logos_delivery/waku/waku_rln_relay/protocol_metrics.nim +++ b/logos_delivery/waku/rln/protocol_metrics.nim @@ -5,7 +5,7 @@ import chronicles, metrics, metrics/chronos_httpserver, ../utils/collector export metrics logScope: - topics = "waku rln_relay" + topics = "waku rln" func generateBucketsForHistogram*(length: int): seq[float64] = ## Generate a custom set of 5 buckets for a given length diff --git a/logos_delivery/waku/waku_rln_relay/protocol_types.nim b/logos_delivery/waku/rln/protocol_types.nim similarity index 94% rename from logos_delivery/waku/waku_rln_relay/protocol_types.nim rename to logos_delivery/waku/rln/protocol_types.nim index 867878886..29ee97083 100644 --- a/logos_delivery/waku/waku_rln_relay/protocol_types.nim +++ b/logos_delivery/waku/rln/protocol_types.nim @@ -5,11 +5,11 @@ import ../waku_core, ../waku_keystore, ../common/protobuf export waku_keystore, waku_core -type RlnRelayResult*[T] = Result[T, string] +type RlnResult*[T] = Result[T, string] ## RLN is a Nim wrapper for the data types used in zerokit RLN -type RLN* {.incompleteStruct.} = object -type RLNResult* = RlnRelayResult[ptr RLN] +type RlnRaw* {.incompleteStruct.} = object +type RlnInstanceResult* = RlnResult[ptr RlnRaw] type MerkleNode* = array[32, byte] @@ -78,8 +78,8 @@ type Invalid Spam - MerkleNodeResult* = RlnRelayResult[MerkleNode] - RateLimitProofResult* = RlnRelayResult[RateLimitProof] + MerkleNodeResult* = RlnResult[MerkleNode] + RateLimitProofResult* = RlnResult[RateLimitProof] # Protobufs enc and init proc init*(T: type RateLimitProof, buffer: seq[byte]): ProtoResult[T] = diff --git a/logos_delivery/waku/waku_rln_relay/rln_relay.nim b/logos_delivery/waku/rln/rln.nim similarity index 55% rename from logos_delivery/waku/waku_rln_relay/rln_relay.nim rename to logos_delivery/waku/rln/rln.nim index 5ae7a2cff..77b72e48f 100644 --- a/logos_delivery/waku/waku_rln_relay/rln_relay.nim +++ b/logos_delivery/waku/rln/rln.nim @@ -18,75 +18,30 @@ import import ./group_manager, - ./rln, + ./bindings, ./conversion_utils, ./constants, ./protocol_types, ./protocol_metrics, - ./nonce_manager + ./nonce_manager, + ./types, + ./config, + ./proof, + ./nullifier_log import logos_delivery/waku/ [common/error_handling, waku_core, requests/rln_requests, waku_keystore] +# Re-export the submodules so existing `import rln` +# (and `import rln/rln`) callers see the moved symbols +# (Rln, WakuRlnConfig, generateRLNProof, etc.). +export types, config, proof, nullifier_log + logScope: - topics = "waku rln_relay" + topics = "waku rln" -type RlnRelayCreds* {.requiresInit.} = object - path*: string - password*: string - -type RlnRelayConf* = object of RootObj - # TODO: severals parameters are only needed when it's dynamic - # change the config to either nest or use enum/type variant so it's obvious - # and then it can be set to `requiresInit` - dynamic*: bool - credIndex*: Option[uint] - ethContractAddress*: string - ethClientUrls*: seq[string] - chainId*: UInt256 - creds*: Option[RlnRelayCreds] - epochSizeSec*: uint64 - userMessageLimit*: uint64 - ethPrivateKey*: Option[string] - -type WakuRlnConfig* = object of RlnRelayConf - onFatalErrorAction*: OnFatalErrorHandler - -type WakuRLNRelay* = ref object of RootObj - # the log of nullifiers and Shamir shares of the past messages grouped per epoch - nullifierLog*: OrderedTable[Epoch, Table[Nullifier, ProofMetadata]] - lastEpoch*: Epoch # the epoch of the last published rln message - rlnEpochSizeSec*: uint64 - rlnMaxTimestampGap*: uint64 - rlnMaxEpochGap*: uint64 - groupManager*: GroupManager - onFatalErrorAction*: OnFatalErrorHandler - nonceManager*: NonceManager - epochMonitorFuture*: Future[void] - rootChangesFuture*: Future[Result[void, string]] - brokerCtx*: BrokerContext - -proc calcEpoch*(rlnPeer: WakuRLNRelay, t: float64): Epoch = - ## gets time `t` as `flaot64` with subseconds resolution in the fractional part - ## and returns its corresponding rln `Epoch` value - - let e = uint64(t / rlnPeer.rlnEpochSizeSec.float64) - return toEpoch(e) - -proc nextEpoch*(rlnPeer: WakuRLNRelay, time: float64): float64 = - let - currentEpoch = uint64(time / rlnPeer.rlnEpochSizeSec.float64) - nextEpochTime = float64(currentEpoch + 1) * rlnPeer.rlnEpochSizeSec.float64 - currentTime = epochTime() - - # Ensure we always return a future time - if nextEpochTime > currentTime: - return nextEpochTime - else: - return epochTime() - -proc stop*(rlnPeer: WakuRLNRelay) {.async: (raises: [Exception]).} = +proc stop*(rlnPeer: Rln) {.async: (raises: [Exception]).} = ## stops the rln-relay protocol ## Throws an error if it cannot stop the rln-relay protocol @@ -95,83 +50,8 @@ proc stop*(rlnPeer: WakuRLNRelay) {.async: (raises: [Exception]).} = RequestGenerateRlnProof.clearProvider(rlnPeer.brokerCtx) await rlnPeer.groupManager.stop() -proc hasDuplicate*( - rlnPeer: WakuRLNRelay, epoch: Epoch, proofMetadata: ProofMetadata -): RlnRelayResult[bool] = - ## returns true if there is another message in the `nullifierLog` of the `rlnPeer` with the same - ## epoch and nullifier as `proofMetadata`'s epoch and nullifier - ## otherwise, returns false - ## Returns an error if it cannot check for duplicates - - # check if the epoch exists - let nullifier = proofMetadata.nullifier - if not rlnPeer.nullifierLog.hasKey(epoch): - return ok(false) - try: - if rlnPeer.nullifierLog[epoch].hasKey(nullifier): - # there is an identical record, mark it as spam - return ok(true) - - # there is no duplicate - return ok(false) - except KeyError: - return err("the epoch was not found: " & getCurrentExceptionMsg()) - -proc updateLog*( - rlnPeer: WakuRLNRelay, epoch: Epoch, proofMetadata: ProofMetadata -): RlnRelayResult[void] = - ## saves supplied proofMetadata `proofMetadata` - ## in the `nullifierLog` of the `rlnPeer` - ## Returns an error if it cannot update the log - - # check if the epoch exists - if not rlnPeer.nullifierLog.hasKeyOrPut( - epoch, {proofMetadata.nullifier: proofMetadata}.toTable() - ): - return ok() - - try: - # check if an identical record exists - if rlnPeer.nullifierLog[epoch].hasKeyOrPut(proofMetadata.nullifier, proofMetadata): - # the above condition could be `discarded` but it is kept for clarity, that slashing will - # be implemented here - # TODO: slashing logic - return ok() - return ok() - except KeyError: - return - err("the epoch was not found: " & getCurrentExceptionMsg()) # should never happen - -proc getCurrentEpoch*(rlnPeer: WakuRLNRelay): Epoch = - return rlnPeer.calcEpoch(epochTime()) - -proc absDiff*(e1, e2: Epoch): uint64 = - ## returns the absolute difference between the two rln `Epoch`s `e1` and `e2` - ## i.e., e1 - e2 - - # convert epochs to their corresponding unsigned numerical values - let - epoch1 = fromEpoch(e1) - epoch2 = fromEpoch(e2) - - # Manually perform an `abs` calculation - if epoch1 > epoch2: - return epoch1 - epoch2 - else: - return epoch2 - epoch1 - -proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] = - ## it is a utility proc that prepares the `data` parameter of the proof generation procedure i.e., `proofGen` that resides in the current module - ## it extracts the `contentTopic`, `timestamp` and the `payload` of the supplied `wakumessage` and serializes them into a byte sequence - - let - contentTopicBytes = toBytes(wakumessage.contentTopic) - timestampBytes = toBytes(wakumessage.timestamp.uint64) - output = concat(wakumessage.payload, contentTopicBytes, @(timestampBytes)) - return output - proc validateMessage*( - rlnPeer: WakuRLNRelay, msg: WakuMessage + rlnPeer: Rln, msg: WakuMessage ): Future[MessageValidationResult] {.async.} = ## validate the supplied `msg` based on the waku-rln-relay routing protocol i.e., ## the `msg`'s epoch is within MaxEpochGap of the current epoch @@ -266,7 +146,7 @@ proc validateMessage*( return MessageValidationResult.Valid proc validateMessageAndUpdateLog*( - rlnPeer: WakuRLNRelay, msg: WakuMessage + rlnPeer: Rln, msg: WakuMessage ): Future[MessageValidationResult] {.async.} = ## validates the message and updates the log to prevent double messaging ## in future messages @@ -286,57 +166,28 @@ proc validateMessageAndUpdateLog*( return isValidMessage -proc generateRLNProof*( - rlnPeer: WakuRLNRelay, input: seq[byte], senderEpochTime: float64 -): Future[RlnRelayResult[seq[byte]]] {.async.} = - let epoch = rlnPeer.calcEpoch(senderEpochTime) - let nonce = rlnPeer.nonceManager.getNonce().valueOr: - return err("could not get new message id to generate an rln proof: " & $error) - let proof = (await rlnPeer.groupManager.generateProof(input, epoch, nonce)).valueOr: - return err("could not generate rln-v2 proof: " & $error) - return ok(proof.encode().buffer) - -proc clearNullifierLog*(rlnPeer: WakuRlnRelay) = - # clear the first MaxEpochGap epochs of the nullifer log - # if more than MaxEpochGap epochs are in the log - let currentEpoch = fromEpoch(rlnPeer.getCurrentEpoch()) - - var epochsToRemove: seq[Epoch] = @[] - for epoch in rlnPeer.nullifierLog.keys(): - let epochInt = fromEpoch(epoch) - - # clean all epochs that are +- rlnMaxEpochGap from the current epoch - if (currentEpoch + rlnPeer.rlnMaxEpochGap) <= epochInt or - epochInt <= (currentEpoch - rlnPeer.rlnMaxEpochGap): - epochsToRemove.add(epoch) - - for epochRemove in epochsToRemove: - trace "clearing epochs from the nullifier log", - currentEpoch = currentEpoch, cleanedEpoch = fromEpoch(epochRemove) - rlnPeer.nullifierLog.del(epochRemove) - -proc monitorEpochs(wakuRlnRelay: WakuRLNRelay) {.async.} = +proc monitorEpochs(rln: Rln) {.async.} = while true: try: - if wakuRlnRelay.groupManager.userMessageLimit.isSome(): + if rln.groupManager.userMessageLimit.isSome(): waku_rln_remaining_proofs_per_epoch.set( - wakuRlnRelay.groupManager.userMessageLimit.get().float64 + rln.groupManager.userMessageLimit.get().float64 ) else: error "userMessageLimit is not set in monitorEpochs" except CatchableError: error "Error in epoch monitoring", error = getCurrentExceptionMsg() - let nextEpochTime = wakuRlnRelay.nextEpoch(epochTime()) + let nextEpochTime = rln.nextEpoch(epochTime()) let sleepDuration = int((nextEpochTime - epochTime()) * 1000) await sleepAsync(sleepDuration) proc mount( conf: WakuRlnConfig, registrationHandler = none(RegistrationHandler) -): Future[RlnRelayResult[WakuRlnRelay]] {.async.} = +): Future[RlnResult[Rln]] {.async.} = var groupManager: GroupManager - wakuRlnRelay: WakuRLNRelay + rln: Rln # create an RLN instance let rlnInstance = createRLNInstance().valueOr: return err("could not create RLN instance: " & $error) @@ -365,7 +216,7 @@ proc mount( (await groupManager.init()).isOkOr: return err("could not initialize the group manager: " & $error) - wakuRlnRelay = WakuRLNRelay( + rln = Rln( groupManager: groupManager, nonceManager: NonceManager.init(conf.userMessageLimit, conf.epochSizeSec.float), rlnEpochSizeSec: conf.epochSizeSec, @@ -376,25 +227,23 @@ proc mount( ) # Start epoch monitoring in the background - wakuRlnRelay.epochMonitorFuture = monitorEpochs(wakuRlnRelay) + rln.epochMonitorFuture = monitorEpochs(rln) RequestGenerateRlnProof.setProvider( - wakuRlnRelay.brokerCtx, + rln.brokerCtx, proc( msg: WakuMessage, senderEpochTime: float64 ): Future[Result[RequestGenerateRlnProof, string]] {.async.} = - let proof = ( - await wakuRlnRelay.generateRLNProof(msg.toRLNSignal(), senderEpochTime) - ).valueOr: + let proof = (await rln.generateRLNProof(msg.toRLNSignal(), senderEpochTime)).valueOr: return err("Could not create RLN proof: " & error) return ok(RequestGenerateRlnProof(proof: proof)), ).isOkOr: return err("Proof generator provider cannot be set: " & $error) - return ok(wakuRlnRelay) + return ok(rln) -proc isReady*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} = +proc isReady*(rlnPeer: Rln): Future[bool] {.async.} = ## returns true if the rln-relay protocol is ready to relay messages ## returns false otherwise @@ -409,10 +258,8 @@ proc isReady*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} = return false proc new*( - T: type WakuRlnRelay, - conf: WakuRlnConfig, - registrationHandler = none(RegistrationHandler), -): Future[RlnRelayResult[WakuRlnRelay]] {.async.} = + T: type Rln, conf: WakuRlnConfig, registrationHandler = none(RegistrationHandler) +): Future[RlnResult[Rln]] {.async.} = ## Mounts the rln-relay protocol on the node. ## The rln-relay protocol can be mounted in two modes: on-chain and off-chain. ## Returns an error if the rln-relay protocol could not be mounted. diff --git a/logos_delivery/waku/waku_rln_relay/rln/rln_interface.nim b/logos_delivery/waku/rln/rln/rln_interface.nim similarity index 94% rename from logos_delivery/waku/waku_rln_relay/rln/rln_interface.nim rename to logos_delivery/waku/rln/rln/rln_interface.nim index 00d1fc0ba..60272ea3e 100644 --- a/logos_delivery/waku/waku_rln_relay/rln/rln_interface.nim +++ b/logos_delivery/waku/rln/rln/rln_interface.nim @@ -51,7 +51,7 @@ type err*: Vec_uint8 CResultRLNPtrVecU8* = object - ok*: ptr RLN + ok*: ptr RlnRaw err*: Vec_uint8 CResultCFrPtrVecU8* = object @@ -99,7 +99,7 @@ proc ffi_rln_new_with_params*( zkey_data: ptr Vec_uint8, graph_data: ptr Vec_uint8 ): CResultRLNPtrVecU8 {.importc: "ffi_rln_new_with_params", cdecl.} -proc ffi_rln_free*(rln: ptr RLN) {.importc: "ffi_rln_free", cdecl.} +proc ffi_rln_free*(rln: ptr RlnRaw) {.importc: "ffi_rln_free", cdecl.} # --- Keygen --------------------------------------------------------------- @@ -143,15 +143,15 @@ proc ffi_rln_partial_witness_input_free*( # pass `addr handle` where `handle` is `ptr T`. proc ffi_generate_rln_proof*( - rln: ptr ptr RLN, witness: ptr ptr FFI_RLNWitnessInput + rln: ptr ptr RlnRaw, witness: ptr ptr FFI_RLNWitnessInput ): CResultProofPtrVecU8 {.importc: "ffi_generate_rln_proof", cdecl.} proc ffi_generate_partial_zk_proof*( - rln: ptr ptr RLN, partial_witness: ptr ptr FFI_RLNPartialWitnessInput + rln: ptr ptr RlnRaw, partial_witness: ptr ptr FFI_RLNPartialWitnessInput ): CResultPartialProofPtrVecU8 {.importc: "ffi_generate_partial_zk_proof", cdecl.} proc ffi_finish_rln_proof*( - rln: ptr ptr RLN, + rln: ptr ptr RlnRaw, partial_proof: ptr ptr FFI_RLNPartialProof, witness: ptr ptr FFI_RLNWitnessInput, ): CResultProofPtrVecU8 {.importc: "ffi_finish_rln_proof", cdecl.} @@ -159,7 +159,7 @@ proc ffi_finish_rln_proof*( # --- Verification --------------------------------------------------------- proc ffi_verify_with_roots*( - rln: ptr ptr RLN, proof: ptr ptr FFI_RLNProof, roots: ptr Vec_CFr, x: ptr CFr + rln: ptr ptr RlnRaw, proof: ptr ptr FFI_RLNProof, roots: ptr Vec_CFr, x: ptr CFr ): CBoolResult {.importc: "ffi_verify_with_roots", cdecl.} # --- Proof serialization -------------------------------------------------- @@ -318,14 +318,14 @@ proc vecToSeq*(data: Vec_uint8): seq[byte] = if result.len > 0: copyMem(addr result[0], data.dataPtr, result.len) -proc seqToFixed32*(data: openArray[byte]): RlnRelayResult[array[32, byte]] = +proc seqToFixed32*(data: openArray[byte]): RlnResult[array[32, byte]] = if data.len != FieldElementSize: return err("Expected 32 bytes, got " & $data.len) var output: array[32, byte] copyMem(addr output[0], unsafeAddr data[0], FieldElementSize) ok(output) -proc cfrToBytesLe*(cfr: ptr CFr): RlnRelayResult[array[32, byte]] = +proc cfrToBytesLe*(cfr: ptr CFr): RlnResult[array[32, byte]] = let bytes = ffi_cfr_to_bytes_le(cfr) defer: ffi_vec_u8_free(bytes) @@ -333,7 +333,7 @@ proc cfrToBytesLe*(cfr: ptr CFr): RlnRelayResult[array[32, byte]] = return err("Invalid field byte length: " & $bytes.len) seqToFixed32(vecToSeq(bytes)) -proc bytesToCfrLe*(data: openArray[byte]): RlnRelayResult[ptr CFr] = +proc bytesToCfrLe*(data: openArray[byte]): RlnResult[ptr CFr] = ## Allocate a ptr CFr from raw bytes. Caller MUST ffi_cfr_free(x). var vec = toVecUint8(data) let res = ffi_bytes_le_to_cfr(addr vec) @@ -343,7 +343,7 @@ proc bytesToCfrLe*(data: openArray[byte]): RlnRelayResult[ptr CFr] = proc cfrResultToBytes*( res: CResultCFrPtrVecU8, prefix: string -): RlnRelayResult[array[32, byte]] = +): RlnResult[array[32, byte]] = ## Consume a CResultCFrPtrVecU8: read bytes if ok, free the CFr, or ## propagate the error (also freeing the error string). if res.ok.isNil: @@ -352,7 +352,7 @@ proc cfrResultToBytes*( ffi_cfr_free(res.ok) cfrToBytesLe(res.ok) -proc hashToFieldLe*(data: openArray[byte]): RlnRelayResult[ptr CFr] = +proc hashToFieldLe*(data: openArray[byte]): RlnResult[ptr CFr] = ## Caller MUST ffi_cfr_free the returned ptr. var vec = toVecUint8(data) let cfr = ffi_hash_to_field_le(addr vec) @@ -360,7 +360,7 @@ proc hashToFieldLe*(data: openArray[byte]): RlnRelayResult[ptr CFr] = return err("Failed to hash to field") ok(cfr) -proc poseidonPairLe*(a, b: openArray[byte]): RlnRelayResult[array[32, byte]] = +proc poseidonPairLe*(a, b: openArray[byte]): RlnResult[array[32, byte]] = ## Poseidon hash of exactly two 32-byte field elements (little-endian). ## zerokit v2 FFI only exposes pair-input Poseidon; unary is not supported. let aPtr = bytesToCfrLe(a).valueOr: diff --git a/logos_delivery/waku/waku_rln_relay/rln/wrappers.nim b/logos_delivery/waku/rln/rln/wrappers.nim similarity index 91% rename from logos_delivery/waku/waku_rln_relay/rln/wrappers.nim rename to logos_delivery/waku/rln/rln/wrappers.nim index 611deeb5d..5a39aedf0 100644 --- a/logos_delivery/waku/waku_rln_relay/rln/wrappers.nim +++ b/logos_delivery/waku/rln/rln/wrappers.nim @@ -7,14 +7,14 @@ import ../../waku_core, ../../waku_keystore {.push raises: [], gcsafe.} logScope: - topics = "waku rln_relay ffi" + topics = "waku rln ffi" # Forward decl; body defined below. proc generateExternalNullifier*( epoch: Epoch, rlnIdentifier: RlnIdentifier -): RlnRelayResult[ExternalNullifier] +): RlnResult[ExternalNullifier] -proc toRootVec(validRoots: seq[MerkleNode]): RlnRelayResult[Vec_CFr] = +proc toRootVec(validRoots: seq[MerkleNode]): RlnResult[Vec_CFr] = ## Caller MUST ffi_vec_cfr_free the returned Vec_CFr. var roots = ffi_vec_cfr_new(csize_t(validRoots.len)) for root in validRoots: @@ -27,7 +27,7 @@ proc toRootVec(validRoots: seq[MerkleNode]): RlnRelayResult[Vec_CFr] = proc proofPtrToRateLimitProof( proofPtr: ptr FFI_RLNProof, epoch: Epoch, rlnIdentifier: RlnIdentifier -): RlnRelayResult[RateLimitProof] = +): RlnResult[RateLimitProof] = var proofHandle = proofPtr let proofBytesRes = ffi_rln_proof_to_bytes_le(addr proofHandle) if hasError(proofBytesRes.err): @@ -93,7 +93,7 @@ proc proofPtrToRateLimitProof( ok(output) -proc parseCredentialVec(vec: var Vec_CFr): RlnRelayResult[IdentityCredential] = +proc parseCredentialVec(vec: var Vec_CFr): RlnResult[IdentityCredential] = ## Vec_CFr order: idTrapdoor, idNullifier, idSecretHash, idCommitment. if int(ffi_vec_cfr_len(addr vec)) != 4: return err("Unexpected credential element count") @@ -120,13 +120,13 @@ proc parseCredentialVec(vec: var Vec_CFr): RlnRelayResult[IdentityCredential] = ) ) -proc membershipKeyGen*(): RlnRelayResult[IdentityCredential] = +proc membershipKeyGen*(): RlnResult[IdentityCredential] = var vec = ffi_extended_key_gen() defer: ffi_vec_cfr_free(vec) parseCredentialVec(vec) -proc createRLNInstanceLocal(): RLNResult = +proc createRLNInstanceLocal(): RlnInstanceResult = ## Creates a stateless RLN instance (no local Merkle tree). let res = ffi_rln_new() if res.ok.isNil(): @@ -135,18 +135,18 @@ proc createRLNInstanceLocal(): RLNResult = return err(msg) ok(res.ok) -proc createRLNInstance*(): RLNResult = +proc createRLNInstance*(): RlnInstanceResult = ## Wraps createRLNInstanceLocal with metrics timing. - var res: RLNResult + var res: RlnInstanceResult waku_rln_instance_creation_duration_seconds.nanosecondTime: res = createRLNInstanceLocal() return res -proc poseidon*(left, right: seq[byte]): RlnRelayResult[array[32, byte]] = +proc poseidon*(left, right: seq[byte]): RlnResult[array[32, byte]] = ## Poseidon hash of exactly 2 inputs; zerokit v2 FFI only exposes the pair variant. poseidonPairLe(left, right) -proc toLeaf*(rateCommitment: RateCommitment): RlnRelayResult[seq[byte]] = +proc toLeaf*(rateCommitment: RateCommitment): RlnResult[seq[byte]] = let idCommitment = rateCommitment.idCommitment var userMessageLimit: array[32, byte] try: @@ -164,7 +164,7 @@ proc toLeaf*(rateCommitment: RateCommitment): RlnRelayResult[seq[byte]] = retLeaf[i] = leaf[i] return ok(retLeaf) -proc toLeaves*(rateCommitments: seq[RateCommitment]): RlnRelayResult[seq[seq[byte]]] = +proc toLeaves*(rateCommitments: seq[RateCommitment]): RlnResult[seq[seq[byte]]] = var leaves = newSeq[seq[byte]]() for rateCommitment in rateCommitments: let leaf = toLeaf(rateCommitment).valueOr: @@ -174,7 +174,7 @@ proc toLeaves*(rateCommitments: seq[RateCommitment]): RlnRelayResult[seq[seq[byt proc generateExternalNullifier*( epoch: Epoch, rlnIdentifier: RlnIdentifier -): RlnRelayResult[ExternalNullifier] = +): RlnResult[ExternalNullifier] = ## externalNullifier = Poseidon(H(epoch), H(rlnIdentifier)); H = ffi_hash_to_field_le. let epochFr = hashToFieldLe(@epoch).valueOr: return err("Failed to hash epoch to field: " & error) @@ -194,7 +194,7 @@ proc generateExternalNullifier*( "Failed to serialize external nullifier: " & e ) -proc extractMetadata*(proof: RateLimitProof): RlnRelayResult[ProofMetadata] = +proc extractMetadata*(proof: RateLimitProof): RlnResult[ProofMetadata] = let externalNullifier = generateExternalNullifier(proof.epoch, proof.rlnIdentifier).valueOr: return err("Failed to compute external nullifier: " & error) return ok( @@ -206,9 +206,7 @@ proc extractMetadata*(proof: RateLimitProof): RlnRelayResult[ProofMetadata] = ) ) -proc buildPathElementsVec( - pathElements: seq[byte], depth: int -): RlnRelayResult[Vec_CFr] = +proc buildPathElementsVec(pathElements: seq[byte], depth: int): RlnResult[Vec_CFr] = ## Caller MUST ffi_vec_cfr_free the returned Vec_CFr. var vec = ffi_vec_cfr_new(csize_t(depth)) for i in 0 ..< depth: @@ -224,9 +222,7 @@ proc buildPathElementsVec( ffi_cfr_free(element) ok(vec) -proc buildWitnessInput( - witness: RLNWitnessInput -): RlnRelayResult[ptr FFI_RLNWitnessInput] = +proc buildWitnessInput(witness: RLNWitnessInput): RlnResult[ptr FFI_RLNWitnessInput] = ## ffi_rln_witness_input_new copies all inputs, so the intermediate CFrs/vecs ## are freed here. Caller MUST ffi_rln_witness_input_free the returned handle. let depth = witness.identity_path_index.len @@ -287,11 +283,11 @@ proc buildWitnessInput( return ok(witnessRes.ok) proc generateRlnProofWithWitness*( - rlnInstance: ptr RLN, + rlnInstance: ptr RlnRaw, witness: RLNWitnessInput, epoch: Epoch, rlnIdentifier: RlnIdentifier, -): RlnRelayResult[RateLimitProof] = +): RlnResult[RateLimitProof] = let witnessHandle = buildWitnessInput(witness).valueOr: return err("failed call to buildWitnessInput in generateRlnProofWithWitness: " & error) @@ -310,7 +306,7 @@ proc generateRlnProofWithWitness*( proc buildRlnProof( proof: RateLimitProof, externalNullifier: ExternalNullifier -): RlnRelayResult[ptr FFI_RLNProof] = +): RlnResult[ptr FFI_RLNProof] = ## ffi_rln_proof_new copies all inputs, so the intermediate CFrs are freed ## here. Caller MUST ffi_rln_proof_free the returned handle. var groth16Vec = toVecUint8(proof.proof) @@ -345,11 +341,11 @@ proc buildRlnProof( return ok(proofRes.ok) proc verifyRlnProof*( - rlnInstance: ptr RLN, + rlnInstance: ptr RlnRaw, proof: RateLimitProof, signal: openArray[byte], validRoots: seq[MerkleNode], -): RlnRelayResult[bool] = +): RlnResult[bool] = if validRoots.len == 0: return err("verifyRlnProof requires at least one valid root (stateless mode)") diff --git a/logos_delivery/waku/rln/types.nim b/logos_delivery/waku/rln/types.nim new file mode 100644 index 000000000..f89d10c76 --- /dev/null +++ b/logos_delivery/waku/rln/types.nim @@ -0,0 +1,22 @@ +{.push raises: [].} + +import std/tables, chronos, results +import brokers/broker_context + +import ./group_manager, ./nonce_manager, ./protocol_types + +import logos_delivery/waku/common/error_handling + +type Rln* = ref object of RootObj + # the log of nullifiers and Shamir shares of the past messages grouped per epoch + nullifierLog*: OrderedTable[Epoch, Table[Nullifier, ProofMetadata]] + lastEpoch*: Epoch # the epoch of the last published rln message + rlnEpochSizeSec*: uint64 + rlnMaxTimestampGap*: uint64 + rlnMaxEpochGap*: uint64 + groupManager*: GroupManager + onFatalErrorAction*: OnFatalErrorHandler + nonceManager*: NonceManager + epochMonitorFuture*: Future[void] + rootChangesFuture*: Future[Result[void, string]] + brokerCtx*: BrokerContext diff --git a/logos_delivery/waku/waku.nim b/logos_delivery/waku/waku.nim index 2c18c5f63..067b5b6ec 100644 --- a/logos_delivery/waku/waku.nim +++ b/logos_delivery/waku/waku.nim @@ -26,7 +26,7 @@ import waku_core, waku_node, waku_archive, - waku_rln_relay, + rln, waku_store, waku_filter_v2, waku_relay/protocol, diff --git a/logos_delivery/waku/waku_keystore/protocol_types.nim b/logos_delivery/waku/waku_keystore/protocol_types.nim index 0f50c66ee..3bf748ef9 100644 --- a/logos_delivery/waku/waku_keystore/protocol_types.nim +++ b/logos_delivery/waku/waku_keystore/protocol_types.nim @@ -77,7 +77,7 @@ proc toIdentityCredentials*( proc serialize*(idComms: seq[IDCommitment]): seq[byte] = ## serializes a seq of IDCommitments to a byte seq - ## the serialization is based on https://github.com/status-im/nwaku/blob/37bd29fbc37ce5cf636734e7dd410b1ed27b88c8/waku/v2/protocol/waku_rln_relay/rln.nim#L142 + ## the serialization is based on https://github.com/status-im/nwaku/blob/37bd29fbc37ce5cf636734e7dd410b1ed27b88c8/waku/v2/protocol/waku_rln/rln.nim#L142 ## the order of serialization is |id_commitment_len<8>|id_commitment| var idCommsBytes = newSeq[byte]() diff --git a/logos_delivery/waku/waku_lightpush/callbacks.nim b/logos_delivery/waku/waku_lightpush/callbacks.nim index dd910326e..68af190f1 100644 --- a/logos_delivery/waku/waku_lightpush/callbacks.nim +++ b/logos_delivery/waku/waku_lightpush/callbacks.nim @@ -3,17 +3,12 @@ import logos_delivery/waku/compat/option_valueor import results -import - ../waku_core, - ../waku_relay, - ./common, - ../waku_rln_relay, - ../waku_rln_relay/protocol_types +import ../waku_core, ../waku_relay, ./common, ../rln, ../rln/protocol_types import std/times, libp2p/peerid, stew/byteutils proc checkAndGenerateRLNProof*( - rlnPeer: Option[WakuRLNRelay], message: WakuMessage + rlnPeer: Option[Rln], message: WakuMessage ): Future[Result[WakuMessage, string]] {.async.} = # check if the message already has RLN proof if message.proof.len > 0: @@ -40,7 +35,7 @@ proc getNilPushHandler*(): PushMessageHandler = return lightpushResultInternalError("no waku relay found") proc getRelayPushHandler*( - wakuRelay: WakuRelay, rlnPeer: Option[WakuRLNRelay] = none[WakuRLNRelay]() + wakuRelay: WakuRelay, rlnPeer: Option[Rln] = none[Rln]() ): PushMessageHandler = return proc( pubsubTopic: string, message: WakuMessage diff --git a/logos_delivery/waku/waku_lightpush_legacy/callbacks.nim b/logos_delivery/waku/waku_lightpush_legacy/callbacks.nim index af31ecf7d..4fc92d516 100644 --- a/logos_delivery/waku/waku_lightpush_legacy/callbacks.nim +++ b/logos_delivery/waku/waku_lightpush_legacy/callbacks.nim @@ -5,13 +5,13 @@ import ../waku_relay, ./common, ./protocol_metrics, - ../waku_rln_relay, - ../waku_rln_relay/protocol_types + ../rln, + ../rln/protocol_types import std/times, libp2p/peerid, stew/byteutils proc checkAndGenerateRLNProof*( - rlnPeer: Option[WakuRLNRelay], message: WakuMessage + rlnPeer: Option[Rln], message: WakuMessage ): Future[Result[WakuMessage, string]] {.async.} = # check if the message already has RLN proof if message.proof.len > 0: @@ -38,7 +38,7 @@ proc getNilPushHandler*(): PushMessageHandler = return err("no waku relay found") proc getRelayPushHandler*( - wakuRelay: WakuRelay, rlnPeer: Option[WakuRLNRelay] = none[WakuRLNRelay]() + wakuRelay: WakuRelay, rlnPeer: Option[Rln] = none[Rln]() ): PushMessageHandler = return proc( pubsubTopic: string, message: WakuMessage diff --git a/logos_delivery/waku/waku_rln_relay.nim b/logos_delivery/waku/waku_rln_relay.nim deleted file mode 100644 index ce79f98d8..000000000 --- a/logos_delivery/waku/waku_rln_relay.nim +++ /dev/null @@ -1,7 +0,0 @@ -import - ./waku_rln_relay/group_manager, - ./waku_rln_relay/conversion_utils, - ./waku_rln_relay/rln_relay, - ./waku_rln_relay/contract - -export group_manager, conversion_utils, rln_relay, contract diff --git a/tests/node/test_wakunode_legacy_lightpush.nim b/tests/node/test_wakunode_legacy_lightpush.nim index 4dfe21e9c..91be200f8 100644 --- a/tests/node/test_wakunode_legacy_lightpush.nim +++ b/tests/node/test_wakunode_legacy_lightpush.nim @@ -15,7 +15,7 @@ import waku_lightpush_legacy, waku_lightpush_legacy/common, waku_lightpush_legacy/protocol_metrics, - waku_rln_relay, + rln, ], ../testlib/[wakucore, wakunode, testasync, futures, testutils], ../resources/payloads, @@ -132,7 +132,7 @@ suite "RLN Proofs as a Lightpush Service": check (await server.mountLegacyLightPush()).isOk() client.mountLegacyLightPushClient() - let manager1 = cast[OnchainGroupManager](server.wakuRlnRelay.groupManager) + let manager1 = cast[OnchainGroupManager](server.rln.groupManager) let idCredentials1 = generateCredentials() (waitFor manager1.register(idCredentials1, UserMessageLimit(20))).isOkOr: diff --git a/tests/node/test_wakunode_lightpush.nim b/tests/node/test_wakunode_lightpush.nim index df6c7f8e0..f5f8bdf45 100644 --- a/tests/node/test_wakunode_lightpush.nim +++ b/tests/node/test_wakunode_lightpush.nim @@ -8,8 +8,7 @@ import libp2p/crypto/crypto import - logos_delivery/waku/ - [waku_core, node/peer_manager, waku_node, waku_lightpush, waku_rln_relay], + logos_delivery/waku/[waku_core, node/peer_manager, waku_node, waku_lightpush, rln], ../testlib/[wakucore, wakunode, testasync, futures], ../resources/payloads, ../waku_rln_relay/[rln/waku_rln_relay_utils, utils_onchain] @@ -130,7 +129,7 @@ suite "RLN Proofs as a Lightpush Service": check (await server.mountLightPush()).isOk() client.mountLightPushClient() - let manager1 = cast[OnchainGroupManager](server.wakuRlnRelay.groupManager) + let manager1 = cast[OnchainGroupManager](server.rln.groupManager) let idCredentials1 = generateCredentials() (waitFor manager1.register(idCredentials1, UserMessageLimit(20))).isOkOr: diff --git a/tests/node/test_wakunode_relay_rln.nim b/tests/node/test_wakunode_relay_rln.nim index c53b3d571..c8aba49ee 100644 --- a/tests/node/test_wakunode_relay_rln.nim +++ b/tests/node/test_wakunode_relay_rln.nim @@ -17,9 +17,9 @@ import waku_core, waku_node, common/error_handling, - waku_rln_relay, - waku_rln_relay/rln, - waku_rln_relay/protocol_types, + rln, + rln/bindings, + rln/protocol_types, waku_keystore/keystore, ], ../waku_store/store_utils, @@ -85,8 +85,8 @@ proc getWakuRlnConfigOnChain*( ethClientAddress: ethClientAddress.get(EthClient), epochSizeSec: 1, onFatalErrorAction: fatalErrorHandler.get(fatalErrorVoidHandler), - # If these are used, initialisation fails with "failed to mount WakuRlnRelay: could not initialize the group manager: the commitment does not have a membership" - creds: some(RlnRelayCreds(path: keystorePath, password: password)), + # If these are used, initialisation fails with "failed to mount Rln: could not initialize the group manager: the commitment does not have a membership" + creds: some(RlnCreds(path: keystorePath, password: password)), ) proc setupRelayWithOnChainRln*( @@ -132,7 +132,7 @@ suite "Waku RlnRelay - End to End - Static": # Given Relay and RLN are not mounted check: server.wakuRelay == nil - server.wakuRlnRelay == nil + server.rln == nil # When RlnRelay is mounted let catchRes = catch: @@ -141,9 +141,8 @@ suite "Waku RlnRelay - End to End - Static": # Then Relay and RLN are not mounted,and the process fails check: server.wakuRelay == nil - server.wakuRlnRelay == nil - catchRes.error()[].msg == - "WakuRelay protocol is not mounted, cannot mount WakuRlnRelay" + server.rln == nil + catchRes.error()[].msg == "WakuRelay protocol is not mounted, cannot mount Rln" asyncTest "Pubsub topics subscribed before mounting RlnRelay are added to it": # Given the node enables Relay and Rln while subscribing to a pubsub topic @@ -151,9 +150,9 @@ suite "Waku RlnRelay - End to End - Static": await client.setupRelayWithStaticRln(2.uint, @[pubsubTopic]) check: server.wakuRelay != nil - server.wakuRlnRelay != nil + server.rln != nil client.wakuRelay != nil - client.wakuRlnRelay != nil + client.rln != nil # And the nodes are connected await client.connectToNodes(@[serverRemotePeerInfo]) @@ -236,7 +235,7 @@ suite "Waku RlnRelay - End to End - Static": await node.mountRlnRelay(wakuRlnConfig) except CatchableError as e: check e.msg == - "failed to mount WakuRlnRelay: rln-relay-user-message-limit can't exceed the MAX_MESSAGE_LIMIT in the rln contract" + "failed to mount Rln: rln-relay-user-message-limit can't exceed the MAX_MESSAGE_LIMIT in the rln contract" suite "Analysis of Bandwith Limitations": asyncTest "Valid Payload Sizes": @@ -278,30 +277,27 @@ suite "Waku RlnRelay - End to End - Static": WakuMessage(payload: @payload150kibPlus, contentTopic: contentTopic) message1b.proof = ( - await client.wakuRlnRelay.generateRLNProof( - message1b.toRLNSignal(), - epoch + float64(client.wakuRlnRelay.rlnEpochSizeSec * 0), + await client.rln.generateRLNProof( + message1b.toRLNSignal(), epoch + float64(client.rln.rlnEpochSizeSec * 0) ) ).valueOr: raiseAssert "generateRLNProof failed: " & error message1kib.proof = ( - await client.wakuRlnRelay.generateRLNProof( - message1kib.toRLNSignal(), - epoch + float64(client.wakuRlnRelay.rlnEpochSizeSec * 1), + await client.rln.generateRLNProof( + message1kib.toRLNSignal(), epoch + float64(client.rln.rlnEpochSizeSec * 1) ) ).valueOr: raiseAssert "generateRLNProof failed: " & error message150kib.proof = ( - await client.wakuRlnRelay.generateRLNProof( - message150kib.toRLNSignal(), - epoch + float64(client.wakuRlnRelay.rlnEpochSizeSec * 2), + await client.rln.generateRLNProof( + message150kib.toRLNSignal(), epoch + float64(client.rln.rlnEpochSizeSec * 2) ) ).valueOr: raiseAssert "generateRLNProof failed: " & error message151kibPlus.proof = ( - await client.wakuRlnRelay.generateRLNProof( + await client.rln.generateRLNProof( message151kibPlus.toRLNSignal(), - epoch + float64(client.wakuRlnRelay.rlnEpochSizeSec * 3), + epoch + float64(client.rln.rlnEpochSizeSec * 3), ) ).valueOr: raiseAssert "generateRLNProof failed: " & error @@ -367,9 +363,9 @@ suite "Waku RlnRelay - End to End - Static": WakuMessage(payload: @payload150kibPlus, contentTopic: contentTopic) message151kibPlus.proof = ( - await client.wakuRlnRelay.generateRLNProof( + await client.rln.generateRLNProof( message151kibPlus.toRLNSignal(), - epoch + float64(client.wakuRlnRelay.rlnEpochSizeSec * 3), + epoch + float64(client.rln.rlnEpochSizeSec * 3), ) ).valueOr: raiseAssert "generateRLNProof failed: " & error @@ -576,14 +572,14 @@ suite "Waku RlnRelay - End to End - OnChain": await client.setupRelayWithOnChainRln(@[pubsubTopic], wakuRlnConfig2) try: - (await server.wakuRlnRelay.groupManager.startGroupSync()).isOkOr: + (await server.rln.groupManager.startGroupSync()).isOkOr: raiseAssert $error - (await client.wakuRlnRelay.groupManager.startGroupSync()).isOkOr: + (await client.rln.groupManager.startGroupSync()).isOkOr: raiseAssert $error # Test Hack: Monkeypatch the idCredentials into the groupManager - server.wakuRlnRelay.groupManager.idCredentials = some(idCredential1) - client.wakuRlnRelay.groupManager.idCredentials = some(idCredential2) + server.rln.groupManager.idCredentials = some(idCredential1) + client.rln.groupManager.idCredentials = some(idCredential2) except Exception, CatchableError: assert false, "exception raised: " & getCurrentExceptionMsg() @@ -657,14 +653,14 @@ suite "Waku RlnRelay - End to End - OnChain": await client.setupRelayWithOnChainRln(@[pubsubTopic], wakuRlnConfig2) try: - (await server.wakuRlnRelay.groupManager.startGroupSync()).isOkOr: + (await server.rln.groupManager.startGroupSync()).isOkOr: raiseAssert $error - (await client.wakuRlnRelay.groupManager.startGroupSync()).isOkOr: + (await client.rln.groupManager.startGroupSync()).isOkOr: raiseAssert $error # Test Hack: Monkeypatch the idCredentials into the groupManager - echo server.wakuRlnRelay.groupManager.idCredentials - echo client.wakuRlnRelay.groupManager.idCredentials + echo server.rln.groupManager.idCredentials + echo client.rln.groupManager.idCredentials except Exception, CatchableError: assert false, "exception raised: " & getCurrentExceptionMsg() diff --git a/tests/waku_keystore/utils.nim b/tests/waku_keystore/utils.nim index 9d86df9bb..61fc524ce 100644 --- a/tests/waku_keystore/utils.nim +++ b/tests/waku_keystore/utils.nim @@ -3,9 +3,7 @@ import stint -import - logos_delivery/waku/ - [waku_keystore/protocol_types, waku_rln_relay, waku_rln_relay/protocol_types] +import logos_delivery/waku/[waku_keystore/protocol_types, rln, rln/protocol_types] func fromStrToBytesLe*(v: string): seq[byte] = try: diff --git a/tests/waku_relay/utils.nim b/tests/waku_relay/utils.nim index 0391d47bc..8c5a6a184 100644 --- a/tests/waku_relay/utils.nim +++ b/tests/waku_relay/utils.nim @@ -13,9 +13,8 @@ import brokers/broker_context from std/times import epochTime import - logos_delivery/waku/[ - waku_relay, node/waku_node, node/peer_manager, waku_core, waku_node, waku_rln_relay - ], + logos_delivery/waku/ + [waku_relay, node/waku_node, node/peer_manager, waku_core, waku_node, rln], ../waku_store/store_utils, ../waku_archive/archive_utils, ../testlib/[wakucore, futures] @@ -78,7 +77,7 @@ proc sendRlnMessage*( ): Future[bool] {.async.} = var message = WakuMessage(payload: payload, contentTopic: contentTopic) message.proof = ( - await client.wakuRlnRelay.generateRLNProof(message.toRLNSignal(), epochTime()) + await client.rln.generateRLNProof(message.toRLNSignal(), epochTime()) ).valueOr: raiseAssert "generateRLNProof failed: " & error discard await client.publish(some(pubsubTopic), message) @@ -93,10 +92,10 @@ proc sendRlnMessageWithInvalidProof*( payload: seq[byte] = "Hello".toBytes(), ): Future[bool] {.async.} = let extraBytes: seq[byte] = @[byte(1), 2, 3] - let rateLimitProofRes = await client.wakuRlnRelay.groupManager.generateProof( + let rateLimitProofRes = await client.rln.groupManager.generateProof( concat(payload, extraBytes), # we add extra bytes to invalidate proof verification against original payload - client.wakuRlnRelay.getCurrentEpoch(), + client.rln.getCurrentEpoch(), messageId = MessageId(0), ) let diff --git a/tests/waku_rln_relay/rln/test_rln_interface.nim b/tests/waku_rln_relay/rln/test_rln_interface.nim index 5ace0c8bb..7d714c2dd 100644 --- a/tests/waku_rln_relay/rln/test_rln_interface.nim +++ b/tests/waku_rln_relay/rln/test_rln_interface.nim @@ -1,7 +1,7 @@ import testutils/unittests, results -import logos_delivery/waku/waku_rln_relay/rln/rln_interface -import logos_delivery/waku/waku_rln_relay/rln/wrappers +import logos_delivery/waku/rln/rln/rln_interface +import logos_delivery/waku/rln/rln/wrappers suite "Vec_uint8": suite "toVecUint8": diff --git a/tests/waku_rln_relay/rln/test_wrappers.nim b/tests/waku_rln_relay/rln/test_wrappers.nim index 8d969a31b..9b239dbf0 100644 --- a/tests/waku_rln_relay/rln/test_wrappers.nim +++ b/tests/waku_rln_relay/rln/test_wrappers.nim @@ -1,8 +1,8 @@ import testutils/unittests, results import - logos_delivery/waku/waku_rln_relay/rln, - logos_delivery/waku/waku_rln_relay/rln/wrappers, + logos_delivery/waku/rln/bindings, + logos_delivery/waku/rln/rln/wrappers, ./waku_rln_relay_utils suite "membershipKeyGen": diff --git a/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim b/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim index 81032eab9..15647a5ef 100644 --- a/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim +++ b/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim @@ -2,18 +2,18 @@ import std/options import std/tempfiles import - logos_delivery/waku/waku_rln_relay, - logos_delivery/waku/waku_rln_relay/[ - group_manager, rln, conversion_utils, constants, protocol_types, protocol_metrics, - nonce_manager, + logos_delivery/waku/rln, + logos_delivery/waku/rln/[ + group_manager, bindings, conversion_utils, constants, protocol_types, + protocol_metrics, nonce_manager, ] -proc createRLNInstanceWrapper*(): RLNResult = +proc createRLNInstanceWrapper*(): RlnInstanceResult = return createRlnInstance() proc unsafeAppendRLNProof*( - rlnPeer: WakuRLNRelay, msg: var WakuMessage, epoch: Epoch, messageId: MessageId -): RlnRelayResult[void] = + rlnPeer: Rln, msg: var WakuMessage, epoch: Epoch, messageId: MessageId +): RlnResult[void] = ## Test helper derived from the publish-path proof flow. ## - Skips nonce validation to intentionally allow generating "bad" message IDs for tests. ## - Forces a real-time on-chain Merkle root refresh via `updateRoots()` and fetches Merkle diff --git a/tests/waku_rln_relay/test_rln_contract_deployment.nim b/tests/waku_rln_relay/test_rln_contract_deployment.nim index f903a2382..e5600e829 100644 --- a/tests/waku_rln_relay/test_rln_contract_deployment.nim +++ b/tests/waku_rln_relay/test_rln_contract_deployment.nim @@ -5,11 +5,8 @@ import std/[options, os], results, testutils/unittests, chronos, web3 import - logos_delivery/waku/[ - waku_rln_relay, - waku_rln_relay/conversion_utils, - waku_rln_relay/group_manager/on_chain/group_manager, - ], + logos_delivery/waku/ + [rln, rln/conversion_utils, rln/group_manager/on_chain/group_manager], ./utils_onchain suite "Token and RLN Contract Deployment": diff --git a/tests/waku_rln_relay/test_rln_group_manager_onchain.nim b/tests/waku_rln_relay/test_rln_group_manager_onchain.nim index fa406eda3..491da4274 100644 --- a/tests/waku_rln_relay/test_rln_group_manager_onchain.nim +++ b/tests/waku_rln_relay/test_rln_group_manager_onchain.nim @@ -18,13 +18,13 @@ import import logos_delivery/waku/[ - waku_rln_relay, - waku_rln_relay/protocol_types, - waku_rln_relay/protocol_metrics, - waku_rln_relay/constants, - waku_rln_relay/rln, - waku_rln_relay/conversion_utils, - waku_rln_relay/group_manager/on_chain/group_manager, + rln, + rln/protocol_types, + rln/protocol_metrics, + rln/constants, + rln/bindings, + rln/conversion_utils, + rln/group_manager/on_chain/group_manager, ], ../testlib/wakucore, ./utils_onchain diff --git a/tests/waku_rln_relay/test_rln_nonce_manager.nim b/tests/waku_rln_relay/test_rln_nonce_manager.nim index fb24f0e29..f9e541e1a 100644 --- a/tests/waku_rln_relay/test_rln_nonce_manager.nim +++ b/tests/waku_rln_relay/test_rln_nonce_manager.nim @@ -1,7 +1,7 @@ {.used.} import testutils/unittests, chronos, os -import logos_delivery/waku/waku_rln_relay/nonce_manager +import logos_delivery/waku/rln/nonce_manager suite "Nonce manager": test "should initialize successfully": diff --git a/tests/waku_rln_relay/test_waku_rln_relay.nim b/tests/waku_rln_relay/test_waku_rln_relay.nim index 28557c1b7..10de8d4d6 100644 --- a/tests/waku_rln_relay/test_waku_rln_relay.nim +++ b/tests/waku_rln_relay/test_waku_rln_relay.nim @@ -12,13 +12,8 @@ import import brokers/broker_context import - logos_delivery/waku/[ - waku_core, - waku_rln_relay, - waku_rln_relay/rln, - waku_rln_relay/protocol_metrics, - waku_keystore, - ], + logos_delivery/waku/ + [waku_core, rln, rln/bindings, rln/protocol_metrics, waku_keystore], ./rln/waku_rln_relay_utils, ./utils_onchain, ../testlib/[wakucore, futures, wakunode, testutils] @@ -159,8 +154,8 @@ suite "Waku rln relay": test "updateLog and hasDuplicate tests": let - wakuRlnRelay = WakuRLNRelay() - epoch = wakuRlnRelay.getCurrentEpoch() + rln = Rln() + epoch = rln.getCurrentEpoch() # create some dummy nullifiers and secret shares var nullifier1: Nullifier @@ -205,25 +200,23 @@ suite "Waku rln relay": # check whether hasDuplicate correctly finds records with the same nullifiers but different secret shares # no duplicate for proof1 should be found, since the log is empty let proofMetadata1 = proof1.extractMetadata().tryGet() - let isDuplicate1 = wakuRlnRelay.hasDuplicate(epoch, proofMetadata1).valueOr: + let isDuplicate1 = rln.hasDuplicate(epoch, proofMetadata1).valueOr: raiseAssert $error assert isDuplicate1 == false, "no duplicate should be found" # add it to the log - discard wakuRlnRelay.updateLog(epoch, proofMetadata1) + discard rln.updateLog(epoch, proofMetadata1) # no duplicate for proof2 should be found, its nullifier differs from proof1 let proofMetadata2 = proof2.extractMetadata().tryGet() - let isDuplicate2 = wakuRlnRelay.hasDuplicate(epoch, proofMetadata2).valueOr: + let isDuplicate2 = rln.hasDuplicate(epoch, proofMetadata2).valueOr: raiseAssert $error # no duplicate is found assert isDuplicate2 == false, "no duplicate should be found" # add it to the log - discard wakuRlnRelay.updateLog(epoch, proofMetadata2) + discard rln.updateLog(epoch, proofMetadata2) # proof3 has the same nullifier as proof1 but different secret shares, it should be detected as duplicate - let isDuplicate3 = wakuRlnRelay.hasDuplicate( - epoch, proof3.extractMetadata().tryGet() - ).valueOr: + let isDuplicate3 = rln.hasDuplicate(epoch, proof3.extractMetadata().tryGet()).valueOr: raiseAssert $error # it is a duplicate assert isDuplicate3, "duplicate should be found" @@ -232,18 +225,18 @@ suite "Waku rln relay": let index = MembershipIndex(5) let wakuRlnConfig = getWakuRlnConfig(manager = manager, index = index) - var wakuRlnRelay: WakuRlnRelay + var rln: Rln lockNewGlobalBrokerContext: - wakuRlnRelay = (await WakuRlnRelay.new(wakuRlnConfig)).valueOr: + rln = (await Rln.new(wakuRlnConfig)).valueOr: raiseAssert $error - let manager = cast[OnchainGroupManager](wakuRlnRelay.groupManager) + let manager = cast[OnchainGroupManager](rln.groupManager) let idCredentials = generateCredentials() (waitFor manager.register(idCredentials, UserMessageLimit(20))).isOkOr: assert false, "error returned when calling register: " & error - let epoch1 = wakuRlnRelay.getCurrentEpoch() + let epoch1 = rln.getCurrentEpoch() # Create messages from the same peer and append RLN proof to them (except wm4) var @@ -252,7 +245,7 @@ suite "Waku rln relay": wm2 = WakuMessage(payload: "Spam message".toBytes(), timestamp: now()) await sleepAsync(1.seconds) - let epoch2 = wakuRlnRelay.getCurrentEpoch() + let epoch2 = rln.getCurrentEpoch() var # wm3 points to the next epoch due to the sleep @@ -260,22 +253,22 @@ suite "Waku rln relay": wm4 = WakuMessage(payload: "Invalid message".toBytes(), timestamp: now()) # Append RLN proofs - wakuRlnRelay.unsafeAppendRLNProof(wm1, epoch1, MessageId(1)).isOkOr: + rln.unsafeAppendRLNProof(wm1, epoch1, MessageId(1)).isOkOr: raiseAssert $error - wakuRlnRelay.unsafeAppendRLNProof(wm2, epoch1, MessageId(1)).isOkOr: + rln.unsafeAppendRLNProof(wm2, epoch1, MessageId(1)).isOkOr: raiseAssert $error - wakuRlnRelay.unsafeAppendRLNProof(wm3, epoch2, MessageId(3)).isOkOr: + rln.unsafeAppendRLNProof(wm3, epoch2, MessageId(3)).isOkOr: raiseAssert $error # Validate messages let - msgValidate1 = await wakuRlnRelay.validateMessageAndUpdateLog(wm1) + msgValidate1 = await rln.validateMessageAndUpdateLog(wm1) # wm2 is within the same epoch as wm1 → should be spam - msgValidate2 = await wakuRlnRelay.validateMessageAndUpdateLog(wm2) + msgValidate2 = await rln.validateMessageAndUpdateLog(wm2) # wm3 is in the next epoch → should be valid - msgValidate3 = await wakuRlnRelay.validateMessageAndUpdateLog(wm3) + msgValidate3 = await rln.validateMessageAndUpdateLog(wm3) # wm4 has no RLN proof → should be invalid - msgValidate4 = await wakuRlnRelay.validateMessageAndUpdateLog(wm4) + msgValidate4 = await rln.validateMessageAndUpdateLog(wm4) check: msgValidate1 == MessageValidationResult.Valid @@ -288,21 +281,21 @@ suite "Waku rln relay": let wakuRlnConfig = getWakuRlnConfig(manager = manager, index = index) - var wakuRlnRelay: WakuRlnRelay + var rln: Rln lockNewGlobalBrokerContext: - wakuRlnRelay = (await WakuRlnRelay.new(wakuRlnConfig)).valueOr: + rln = (await Rln.new(wakuRlnConfig)).valueOr: raiseAssert $error - let manager = cast[OnchainGroupManager](wakuRlnRelay.groupManager) + let manager = cast[OnchainGroupManager](rln.groupManager) let idCredentials = generateCredentials() (waitFor manager.register(idCredentials, UserMessageLimit(20))).isOkOr: assert false, "error returned when calling register: " & error # usually it's 20 seconds but we set it to 1 for testing purposes which make the test faster - wakuRlnRelay.rlnMaxTimestampGap = 1 + rln.rlnMaxTimestampGap = 1 - var epoch = wakuRlnRelay.getCurrentEpoch() + var epoch = rln.getCurrentEpoch() var wm1 = WakuMessage( @@ -316,19 +309,19 @@ suite "Waku rln relay": timestamp: now(), ) - wakuRlnRelay.unsafeAppendRLNProof(wm1, epoch, MessageId(1)).isOkOr: + rln.unsafeAppendRLNProof(wm1, epoch, MessageId(1)).isOkOr: raiseAssert $error - wakuRlnRelay.unsafeAppendRLNProof(wm2, epoch, MessageId(2)).isOkOr: + rln.unsafeAppendRLNProof(wm2, epoch, MessageId(2)).isOkOr: raiseAssert $error # validate the first message because it's timestamp is the same as the generated timestamp - let msgValidate1 = await wakuRlnRelay.validateMessageAndUpdateLog(wm1) + let msgValidate1 = await rln.validateMessageAndUpdateLog(wm1) # wait for 2 seconds to make the timestamp different from generated timestamp await sleepAsync(2.seconds) - let msgValidate2 = await wakuRlnRelay.validateMessageAndUpdateLog(wm2) + let msgValidate2 = await rln.validateMessageAndUpdateLog(wm2) check: msgValidate1 == MessageValidationResult.Valid @@ -337,9 +330,9 @@ suite "Waku rln relay": asyncTest "multiple senders with same external nullifier": let index1 = MembershipIndex(5) let rlnConf1 = getWakuRlnConfig(manager = manager, index = index1) - var wakuRlnRelay1: WakuRlnRelay + var wakuRlnRelay1: Rln lockNewGlobalBrokerContext: - wakuRlnRelay1 = (await WakuRlnRelay.new(rlnConf1)).valueOr: + wakuRlnRelay1 = (await Rln.new(rlnConf1)).valueOr: raiseAssert "failed to create waku rln relay: " & $error let manager1 = cast[OnchainGroupManager](wakuRlnRelay1.groupManager) @@ -350,9 +343,9 @@ suite "Waku rln relay": let index2 = MembershipIndex(6) let rlnConf2 = getWakuRlnConfig(manager = manager, index = index2) - var wakuRlnRelay2: WakuRlnRelay + var wakuRlnRelay2: Rln lockNewGlobalBrokerContext: - wakuRlnRelay2 = (await WakuRlnRelay.new(rlnConf2)).valueOr: + wakuRlnRelay2 = (await Rln.new(rlnConf2)).valueOr: raiseAssert "failed to create waku rln relay: " & $error let manager2 = cast[OnchainGroupManager](wakuRlnRelay2.groupManager) @@ -481,31 +474,31 @@ suite "Waku rln relay": let wakuRlnConfig = getWakuRlnConfig( manager = manager, index = index, epochSizeSec = rlnEpochSizeSec.uint64 ) - var wakuRlnRelay: WakuRlnRelay + var rln: Rln lockNewGlobalBrokerContext: - wakuRlnRelay = (await WakuRlnRelay.new(wakuRlnConfig)).valueOr: + rln = (await Rln.new(wakuRlnConfig)).valueOr: raiseAssert $error - let rlnMaxEpochGap = wakuRlnRelay.rlnMaxEpochGap + let rlnMaxEpochGap = rln.rlnMaxEpochGap let testProofMetadata = default(ProofMetadata) let testProofMetadataTable = {testProofMetadata.nullifier: testProofMetadata}.toTable() for i in 0 .. rlnMaxEpochGap: # we add epochs to the nullifierLog - let testEpoch = wakuRlnRelay.calcEpoch(epochTime() + float(rlnEpochSizeSec * i)) - wakuRlnRelay.nullifierLog[testEpoch] = testProofMetadataTable + let testEpoch = rln.calcEpoch(epochTime() + float(rlnEpochSizeSec * i)) + rln.nullifierLog[testEpoch] = testProofMetadataTable check: - wakuRlnRelay.nullifierLog.len().uint == i + 1 + rln.nullifierLog.len().uint == i + 1 check: - wakuRlnRelay.nullifierLog.len().uint == rlnMaxEpochGap + 1 + rln.nullifierLog.len().uint == rlnMaxEpochGap + 1 # clearing it now will remove 1 epoch - wakuRlnRelay.clearNullifierLog() + rln.clearNullifierLog() check: - wakuRlnRelay.nullifierLog.len().uint == rlnMaxEpochGap + rln.nullifierLog.len().uint == rlnMaxEpochGap var testEpochSizes: seq[uint] = @[1, 5, 10, 30, 60, 600] for i in testEpochSizes: diff --git a/tests/waku_rln_relay/test_wakunode_rln_relay.nim b/tests/waku_rln_relay/test_wakunode_rln_relay.nim index 0056e575e..487d4a674 100644 --- a/tests/waku_rln_relay/test_wakunode_rln_relay.nim +++ b/tests/waku_rln_relay/test_wakunode_rln_relay.nim @@ -11,7 +11,7 @@ import brokers/broker_context import - logos_delivery/waku/[waku_core, waku_node, waku_rln_relay], + logos_delivery/waku/[waku_core, waku_node, rln], ../testlib/[wakucore, futures, wakunode, testutils], ./utils_onchain, ./rln/waku_rln_relay_utils @@ -21,7 +21,7 @@ from std/times import epochTime proc waitForNullifierLog(node: WakuNode, expectedLen: int): Future[bool] {.async.} = ## Helper function for i in 0 .. 100: # Try for up to 50 seconds (100 * 500ms) - if node.wakuRlnRelay.nullifierLog.len() == expectedLen: + if node.rln.nullifierLog.len() == expectedLen: return true await sleepAsync(500.millis) return false @@ -56,7 +56,7 @@ procSuite "WakuNode - RLN relay": await node1.start() # Registration is mandatory before sending messages with rln-relay - let manager1 = cast[OnchainGroupManager](node1.wakuRlnRelay.groupManager) + let manager1 = cast[OnchainGroupManager](node1.rln.groupManager) let idCredentials1 = generateCredentials() (waitFor manager1.register(idCredentials1, UserMessageLimit(20))).isOkOr: @@ -79,7 +79,7 @@ procSuite "WakuNode - RLN relay": await node2.mountRlnRelay(wakuRlnConfig2) await node2.start() - let manager2 = cast[OnchainGroupManager](node2.wakuRlnRelay.groupManager) + let manager2 = cast[OnchainGroupManager](node2.rln.groupManager) let rootUpdated2 = waitFor manager2.updateRoots() info "Updated root for node2", rootUpdated2 @@ -97,7 +97,7 @@ procSuite "WakuNode - RLN relay": await node3.mountRlnRelay(wakuRlnConfig3) await node3.start() - let manager3 = cast[OnchainGroupManager](node3.wakuRlnRelay.groupManager) + let manager3 = cast[OnchainGroupManager](node3.rln.groupManager) let rootUpdated3 = waitFor manager3.updateRoots() info "Updated root for node3", rootUpdated3 @@ -135,10 +135,8 @@ procSuite "WakuNode - RLN relay": var message = WakuMessage(payload: @payload, contentTopic: contentTopic, timestamp: now()) doAssert( - node1.wakuRlnRelay - .unsafeAppendRLNProof( - message, node1.wakuRlnRelay.getCurrentEpoch(), MessageId(0) - ) + node1.rln + .unsafeAppendRLNProof(message, node1.rln.getCurrentEpoch(), MessageId(0)) .isOk() ) @@ -169,7 +167,7 @@ procSuite "WakuNode - RLN relay": getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) await node1.mountRlnRelay(wakuRlnConfig1) await node1.start() - let manager1 = cast[OnchainGroupManager](node1.wakuRlnRelay.groupManager) + let manager1 = cast[OnchainGroupManager](node1.rln.groupManager) let idCredentials1 = generateCredentials() (waitFor manager1.register(idCredentials1, UserMessageLimit(20))).isOkOr: @@ -186,7 +184,7 @@ procSuite "WakuNode - RLN relay": getWakuRlnConfig(manager = manager, index = MembershipIndex(2)) await node2.mountRlnRelay(wakuRlnConfig2) await node2.start() - let manager2 = cast[OnchainGroupManager](node2.wakuRlnRelay.groupManager) + let manager2 = cast[OnchainGroupManager](node2.rln.groupManager) let idCredentials2 = generateCredentials() (waitFor manager2.register(idCredentials2, UserMessageLimit(20))).isOkOr: @@ -203,7 +201,7 @@ procSuite "WakuNode - RLN relay": getWakuRlnConfig(manager = manager, index = MembershipIndex(3)) await node3.mountRlnRelay(wakuRlnConfig3) await node3.start() - let manager3 = cast[OnchainGroupManager](node3.wakuRlnRelay.groupManager) + let manager3 = cast[OnchainGroupManager](node3.rln.groupManager) let idCredentials3 = generateCredentials() (waitFor manager3.register(idCredentials3, UserMessageLimit(20))).isOkOr: @@ -264,8 +262,8 @@ procSuite "WakuNode - RLN relay": contentTopic: contentTopics[0], ) - node1.wakuRlnRelay.unsafeAppendRLNProof( - message, node1.wakuRlnRelay.getCurrentEpoch(), MessageId(i.uint8) + node1.rln.unsafeAppendRLNProof( + message, node1.rln.getCurrentEpoch(), MessageId(i.uint8) ).isOkOr: raiseAssert $error messages1.add(message) @@ -277,8 +275,8 @@ procSuite "WakuNode - RLN relay": contentTopic: contentTopics[1], ) - node2.wakuRlnRelay.unsafeAppendRLNProof( - message, node2.wakuRlnRelay.getCurrentEpoch(), MessageId(i.uint8) + node2.rln.unsafeAppendRLNProof( + message, node2.rln.getCurrentEpoch(), MessageId(i.uint8) ).isOkOr: raiseAssert $error messages2.add(message) @@ -320,7 +318,7 @@ procSuite "WakuNode - RLN relay": await node1.mountRlnRelay(wakuRlnConfig1) await node1.start() - let manager1 = cast[OnchainGroupManager](node1.wakuRlnRelay.groupManager) + let manager1 = cast[OnchainGroupManager](node1.rln.groupManager) let idCredentials1 = generateCredentials() (waitFor manager1.register(idCredentials1, UserMessageLimit(20))).isOkOr: @@ -341,7 +339,7 @@ procSuite "WakuNode - RLN relay": await node2.mountRlnRelay(wakuRlnConfig2) await node2.start() - let manager2 = cast[OnchainGroupManager](node2.wakuRlnRelay.groupManager) + let manager2 = cast[OnchainGroupManager](node2.rln.groupManager) let rootUpdated2 = waitFor manager2.updateRoots() info "Updated root for node2", rootUpdated2 lockNewGlobalBrokerContext: @@ -357,7 +355,7 @@ procSuite "WakuNode - RLN relay": await node3.mountRlnRelay(wakuRlnConfig3) await node3.start() - let manager3 = cast[OnchainGroupManager](node3.wakuRlnRelay.groupManager) + let manager3 = cast[OnchainGroupManager](node3.rln.groupManager) let rootUpdated3 = waitFor manager3.updateRoots() info "Updated root for node3", rootUpdated3 @@ -392,12 +390,12 @@ procSuite "WakuNode - RLN relay": # prepare the message payload let payload = "valid".toBytes() # prepare the epoch - let epoch = node1.wakuRlnRelay.getCurrentEpoch() + let epoch = node1.rln.getCurrentEpoch() var message = WakuMessage(payload: @payload, contentTopic: DefaultPubsubTopic, timestamp: now()) - node1.wakuRlnRelay.unsafeAppendRLNProof(message, epoch, MessageId(0)).isOkOr: + node1.rln.unsafeAppendRLNProof(message, epoch, MessageId(0)).isOkOr: assert false, "Failed to append rln proof: " & $error # message.payload = "Invalid".toBytes() @@ -432,7 +430,7 @@ procSuite "WakuNode - RLN relay": await node1.start() # Registration is mandatory before sending messages with rln-relay - let manager1 = cast[OnchainGroupManager](node1.wakuRlnRelay.groupManager) + let manager1 = cast[OnchainGroupManager](node1.rln.groupManager) let idCredentials1 = generateCredentials() (waitFor manager1.register(idCredentials1, UserMessageLimit(20))).isOkOr: @@ -455,7 +453,7 @@ procSuite "WakuNode - RLN relay": await node2.start() # Registration is mandatory before sending messages with rln-relay - let manager2 = cast[OnchainGroupManager](node2.wakuRlnRelay.groupManager) + let manager2 = cast[OnchainGroupManager](node2.rln.groupManager) let rootUpdated2 = waitFor manager2.updateRoots() info "Updated root for node2", rootUpdated2 lockNewGlobalBrokerContext: @@ -473,7 +471,7 @@ procSuite "WakuNode - RLN relay": await node3.start() # Registration is mandatory before sending messages with rln-relay - let manager3 = cast[OnchainGroupManager](node3.wakuRlnRelay.groupManager) + let manager3 = cast[OnchainGroupManager](node3.rln.groupManager) let rootUpdated3 = waitFor manager3.updateRoots() info "Updated root for node3", rootUpdated3 @@ -482,7 +480,7 @@ procSuite "WakuNode - RLN relay": await node3.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()]) # get the current epoch time - let epoch_1 = node1.wakuRlnRelay.getCurrentEpoch() + let epoch_1 = node1.rln.getCurrentEpoch() # create some messages with rate limit proofs var @@ -500,7 +498,7 @@ procSuite "WakuNode - RLN relay": # wm3 points to the next epoch await sleepAsync(1000.millis) - let epoch_2 = node1.wakuRlnRelay.getCurrentEpoch() + let epoch_2 = node1.rln.getCurrentEpoch() var wm3 = WakuMessage( @@ -514,12 +512,12 @@ procSuite "WakuNode - RLN relay": contentTopic: DefaultPubsubTopic, ) - node1.wakuRlnRelay.unsafeAppendRLNProof(wm1, epoch_1, MessageId(0)).isOkOr: + node1.rln.unsafeAppendRLNProof(wm1, epoch_1, MessageId(0)).isOkOr: raiseAssert $error - node1.wakuRlnRelay.unsafeAppendRLNProof(wm2, epoch_1, MessageId(0)).isOkOr: + node1.rln.unsafeAppendRLNProof(wm2, epoch_1, MessageId(0)).isOkOr: raiseAssert $error - node1.wakuRlnRelay.unsafeAppendRLNProof(wm3, epoch_2, MessageId(2)).isOkOr: + node1.rln.unsafeAppendRLNProof(wm3, epoch_2, MessageId(2)).isOkOr: raiseAssert $error # relay handler for node3 @@ -601,7 +599,7 @@ procSuite "WakuNode - RLN relay": await node1.start() # Registration is mandatory before sending messages with rln-relay - let manager1 = cast[OnchainGroupManager](node1.wakuRlnRelay.groupManager) + let manager1 = cast[OnchainGroupManager](node1.rln.groupManager) let idCredentials1 = generateCredentials() (waitFor manager1.register(idCredentials1, UserMessageLimit(20))).isOkOr: @@ -620,7 +618,7 @@ procSuite "WakuNode - RLN relay": await node2.start() # Registration is mandatory before sending messages with rln-relay - let manager2 = cast[OnchainGroupManager](node2.wakuRlnRelay.groupManager) + let manager2 = cast[OnchainGroupManager](node2.rln.groupManager) let rootUpdated2 = waitFor manager2.updateRoots() info "Updated root for node2", rootUpdated2 @@ -673,26 +671,22 @@ procSuite "WakuNode - RLN relay": # Given all messages have an rln proof and are published by the node 1 let publishSleepDuration: Duration = 5000.millis - let epoch_1 = node1.wakuRlnRelay.calcEpoch(epochTime().float64) - let epoch_2 = node1.wakuRlnRelay.calcEpoch( - epochTime().float64 + node1.wakuRlnRelay.rlnEpochSizeSec.float64 * 1 - ) - let epoch_3 = node1.wakuRlnRelay.calcEpoch( - epochTime().float64 + node1.wakuRlnRelay.rlnEpochSizeSec.float64 * 2 - ) - let epoch_4 = node1.wakuRlnRelay.calcEpoch( - epochTime().float64 + node1.wakuRlnRelay.rlnEpochSizeSec.float64 * 3 - ) - let epoch_5 = node1.wakuRlnRelay.calcEpoch( - epochTime().float64 + node1.wakuRlnRelay.rlnEpochSizeSec.float64 * 4 - ) + let epoch_1 = node1.rln.calcEpoch(epochTime().float64) + let epoch_2 = + node1.rln.calcEpoch(epochTime().float64 + node1.rln.rlnEpochSizeSec.float64 * 1) + let epoch_3 = + node1.rln.calcEpoch(epochTime().float64 + node1.rln.rlnEpochSizeSec.float64 * 2) + let epoch_4 = + node1.rln.calcEpoch(epochTime().float64 + node1.rln.rlnEpochSizeSec.float64 * 3) + let epoch_5 = + node1.rln.calcEpoch(epochTime().float64 + node1.rln.rlnEpochSizeSec.float64 * 4) # Epoch 1 - node1.wakuRlnRelay.unsafeAppendRLNProof(wm1, epoch_1, MessageId(0)).isOkOr: + node1.rln.unsafeAppendRLNProof(wm1, epoch_1, MessageId(0)).isOkOr: raiseAssert $error # Message wm2 is published in the same epoch as wm1, so it'll be considered spam - node1.wakuRlnRelay.unsafeAppendRLNProof(wm2, epoch_1, MessageId(0)).isOkOr: + node1.rln.unsafeAppendRLNProof(wm2, epoch_1, MessageId(0)).isOkOr: raiseAssert $error discard await node1.publish(some(DefaultPubsubTopic), wm1) @@ -704,7 +698,7 @@ procSuite "WakuNode - RLN relay": # Epoch 2 - node1.wakuRlnRelay.unsafeAppendRLNProof(wm3, epoch_2, MessageId(0)).isOkOr: + node1.rln.unsafeAppendRLNProof(wm3, epoch_2, MessageId(0)).isOkOr: raiseAssert $error discard await node1.publish(some(DefaultPubsubTopic), wm3) @@ -716,7 +710,7 @@ procSuite "WakuNode - RLN relay": await node2.waitForNullifierLog(2) # Epoch 3 - node1.wakuRlnRelay.unsafeAppendRLNProof(wm4, epoch_3, MessageId(0)).isOkOr: + node1.rln.unsafeAppendRLNProof(wm4, epoch_3, MessageId(0)).isOkOr: raiseAssert $error discard await node1.publish(some(DefaultPubsubTopic), wm4) @@ -726,7 +720,7 @@ procSuite "WakuNode - RLN relay": await node2.waitForNullifierLog(3) # Epoch 4 - node1.wakuRlnRelay.unsafeAppendRLNProof(wm5, epoch_4, MessageId(0)).isOkOr: + node1.rln.unsafeAppendRLNProof(wm5, epoch_4, MessageId(0)).isOkOr: raiseAssert $error discard await node1.publish(some(DefaultPubsubTopic), wm5) @@ -736,7 +730,7 @@ procSuite "WakuNode - RLN relay": await node2.waitForNullifierLog(4) # Epoch 5 - node1.wakuRlnRelay.unsafeAppendRLNProof(wm6, epoch_5, MessageId(0)).isOkOr: + node1.rln.unsafeAppendRLNProof(wm6, epoch_5, MessageId(0)).isOkOr: raiseAssert $error discard await node1.publish(some(DefaultPubsubTopic), wm6) diff --git a/tests/waku_rln_relay/utils_offchain.nim b/tests/waku_rln_relay/utils_offchain.nim index 593f31c3f..625b4ea2a 100644 --- a/tests/waku_rln_relay/utils_offchain.nim +++ b/tests/waku_rln_relay/utils_offchain.nim @@ -12,7 +12,7 @@ from std/times import epochTime import ../../../logos_delivery/waku/ - [node/waku_node, node/peer_manager, waku_core, waku_node, waku_rln_relay], + [node/waku_node, node/peer_manager, waku_core, waku_node, rln], ../waku_store/store_utils, ../waku_archive/archive_utils, ../testlib/[wakucore, futures, assertions] @@ -55,7 +55,7 @@ proc sendRlnMessage*( ): Future[bool] {.async.} = var message = WakuMessage(payload: payload, contentTopic: contentTopic) message.proof = ( - await client.wakuRlnRelay.generateRLNProof(message.toRLNSignal(), epochTime()) + await client.rln.generateRLNProof(message.toRLNSignal(), epochTime()) ).valueOr: raiseAssert "generateRLNProof failed: " & error discard await client.publish(some(pubsubTopic), message) @@ -70,10 +70,10 @@ proc sendRlnMessageWithInvalidProof*( payload: seq[byte] = "Hello".toBytes(), ): Future[bool] {.async.} = let extraBytes: seq[byte] = @[byte(1), 2, 3] - let rateLimitProofRes = await client.wakuRlnRelay.groupManager.generateProof( + let rateLimitProofRes = await client.rln.groupManager.generateProof( concat(payload, extraBytes), # we add extra bytes to invalidate proof verification against original payload - client.wakuRlnRelay.getCurrentEpoch(), + client.rln.getCurrentEpoch(), ) let rateLimitProof = rateLimitProofRes.get().encode().buffer diff --git a/tests/waku_rln_relay/utils_onchain.nim b/tests/waku_rln_relay/utils_onchain.nim index 828b273a2..422b8c351 100644 --- a/tests/waku_rln_relay/utils_onchain.nim +++ b/tests/waku_rln_relay/utils_onchain.nim @@ -20,12 +20,7 @@ import results import - logos_delivery/waku/[ - waku_rln_relay, - waku_rln_relay/protocol_types, - waku_rln_relay/constants, - waku_rln_relay/rln, - ], + logos_delivery/waku/[rln, rln/protocol_types, rln/constants, rln/bindings], ../testlib/common const CHAIN_ID* = 1234'u256 @@ -42,7 +37,7 @@ proc generateCredentials*(): IdentityCredential = proc getRateCommitment*( idCredential: IdentityCredential, userMessageLimit: UserMessageLimit -): RlnRelayResult[RawRateCommitment] = +): RlnResult[RawRateCommitment] = return RateCommitment( idCommitment: idCredential.idCommitment, userMessageLimit: userMessageLimit ).toLeaf() diff --git a/tests/wakunode_rest/test_rest_health.nim b/tests/wakunode_rest/test_rest_health.nim index f2d94f6e5..18a5abc4c 100644 --- a/tests/wakunode_rest/test_rest_health.nim +++ b/tests/wakunode_rest/test_rest_health.nim @@ -19,7 +19,7 @@ import rest_api/endpoint/responses, rest_api/endpoint/health/handlers as health_rest_interface, rest_api/endpoint/health/client as health_rest_client, - waku_rln_relay, + rln, node/health_monitor, ], ../testlib/common, diff --git a/tests/wakunode_rest/test_rest_relay.nim b/tests/wakunode_rest/test_rest_relay.nim index e90ccb31d..d32ba3373 100644 --- a/tests/wakunode_rest/test_rest_relay.nim +++ b/tests/wakunode_rest/test_rest_relay.nim @@ -21,7 +21,7 @@ import rest_api/endpoint/relay/handlers as relay_rest_interface, rest_api/endpoint/relay/client as relay_rest_client, waku_relay, - waku_rln_relay, + rln, ], ../testlib/wakucore, ../testlib/wakunode, @@ -264,7 +264,7 @@ suite "Waku v2 Rest API - Relay": await node.mountRlnRelay(wakuRlnConfig) await node.start() # Registration is mandatory before sending messages with rln-relay - let manager = cast[OnchainGroupManager](node.wakuRlnRelay.groupManager) + let manager = cast[OnchainGroupManager](node.rln.groupManager) let idCredentials = generateCredentials() (waitFor manager.register(idCredentials, UserMessageLimit(20))).isOkOr: @@ -535,7 +535,7 @@ suite "Waku v2 Rest API - Relay": await node.connectToNodes(@[meshNode.peerInfo.toRemotePeerInfo()]) # Registration is mandatory before sending messages with rln-relay - let manager = cast[OnchainGroupManager](node.wakuRlnRelay.groupManager) + let manager = cast[OnchainGroupManager](node.rln.groupManager) let idCredentials = generateCredentials() (waitFor manager.register(idCredentials, UserMessageLimit(20))).isOkOr: @@ -604,7 +604,7 @@ suite "Waku v2 Rest API - Relay": await node.start() # Registration is mandatory before sending messages with rln-relay - let manager = cast[OnchainGroupManager](node.wakuRlnRelay.groupManager) + let manager = cast[OnchainGroupManager](node.rln.groupManager) let idCredentials = generateCredentials() (waitFor manager.register(idCredentials, UserMessageLimit(20))).isOkOr: @@ -663,7 +663,7 @@ suite "Waku v2 Rest API - Relay": await node.start() # Registration is mandatory before sending messages with rln-relay - let manager = cast[OnchainGroupManager](node.wakuRlnRelay.groupManager) + let manager = cast[OnchainGroupManager](node.rln.groupManager) let idCredentials = generateCredentials() (waitFor manager.register(idCredentials, UserMessageLimit(20))).isOkOr: @@ -735,7 +735,7 @@ suite "Waku v2 Rest API - Relay": await node.start() # Registration is mandatory before sending messages with rln-relay - let manager = cast[OnchainGroupManager](node.wakuRlnRelay.groupManager) + let manager = cast[OnchainGroupManager](node.rln.groupManager) let idCredentials = generateCredentials() (waitFor manager.register(idCredentials, UserMessageLimit(20))).isOkOr: diff --git a/tools/rln_keystore_generator/rln_keystore_generator.nim b/tools/rln_keystore_generator/rln_keystore_generator.nim index 16508178b..9643f039f 100644 --- a/tools/rln_keystore_generator/rln_keystore_generator.nim +++ b/tools/rln_keystore_generator/rln_keystore_generator.nim @@ -6,12 +6,8 @@ else: import chronicles, results, std/[tempfiles, sequtils] import - logos_delivery/waku/[ - waku_keystore, - waku_rln_relay/rln, - waku_rln_relay/conversion_utils, - waku_rln_relay/group_manager/on_chain, - ] + logos_delivery/waku/ + [waku_keystore, rln/bindings, rln/conversion_utils, rln/group_manager/on_chain] logScope: topics = "rln_keystore_generator" From ec36e09baeedd8c7df9d39530176224c79f69832 Mon Sep 17 00:00:00 2001 From: Tanya S <120410716+stubbsta@users.noreply.github.com> Date: Mon, 29 Jun 2026 13:01:04 +0200 Subject: [PATCH 15/33] =?UTF-8?q?chore:=20phase=203=20=E2=80=94=20inline?= =?UTF-8?q?=20RLN=20validator,=20allow=20mountRlnRelay=20without=20wakuRel?= =?UTF-8?q?ay=20(#3985)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add rln validator to mountRlnRelay * remove requirement for relay when mounting RLN * Rename mountRlnRelay to setRlnValidator * Clean up setRlnValidator: update log wording, drop unused let bindings * Fix botched setRlnValidator callsites from rename * Drop obsolete test: setRlnValidator no longer requires relay * Remove unused code in test utils * remove unused import for rln --- apps/chat2/chat2.nim | 2 +- apps/networkmonitor/networkmonitor.nim | 2 +- logos_delivery/waku/factory/node_factory.nim | 2 +- logos_delivery/waku/node/waku_node/relay.nim | 79 ++++++++++++++++--- logos_delivery/waku/rln/adapters/relay.nim | 78 ------------------ logos_delivery/waku/rln/rln.nim | 1 - tests/node/test_wakunode_legacy_lightpush.nim | 2 +- tests/node/test_wakunode_lightpush.nim | 2 +- tests/node/test_wakunode_relay_rln.nim | 20 +---- tests/waku_relay/utils.nim | 5 -- .../test_wakunode_rln_relay.nim | 28 +++---- tests/waku_rln_relay/utils_offchain.nim | 2 +- tests/wakunode_rest/test_rest_health.nim | 2 +- tests/wakunode_rest/test_rest_relay.nim | 12 +-- 14 files changed, 95 insertions(+), 142 deletions(-) delete mode 100644 logos_delivery/waku/rln/adapters/relay.nim diff --git a/apps/chat2/chat2.nim b/apps/chat2/chat2.nim index aa36300fc..647667b0c 100644 --- a/apps/chat2/chat2.nim +++ b/apps/chat2/chat2.nim @@ -544,7 +544,7 @@ proc processInput(rfd: AsyncFD, rng: crypto.Rng) {.async.} = epochSizeSec: conf.rlnEpochSizeSec, ) - waitFor node.mountRlnRelay(rlnConf, spamHandler = some(spamHandler)) + waitFor node.setRlnValidator(rlnConf, spamHandler = some(spamHandler)) let membershipIndex = node.rln.groupManager.membershipIndex.get() let identityCredential = node.rln.groupManager.idCredentials.get() diff --git a/apps/networkmonitor/networkmonitor.nim b/apps/networkmonitor/networkmonitor.nim index 2c0a5ca89..a23c778ef 100644 --- a/apps/networkmonitor/networkmonitor.nim +++ b/apps/networkmonitor/networkmonitor.nim @@ -619,7 +619,7 @@ when isMainModule: ) try: - waitFor node.mountRlnRelay(rlnConf) + waitFor node.setRlnValidator(rlnConf) except CatchableError: error "failed to setup RLN", error = getCurrentExceptionMsg() quit(QuitFailure) diff --git a/logos_delivery/waku/factory/node_factory.nim b/logos_delivery/waku/factory/node_factory.nim index 48a21aeb0..658a49e38 100644 --- a/logos_delivery/waku/factory/node_factory.nim +++ b/logos_delivery/waku/factory/node_factory.nim @@ -337,7 +337,7 @@ proc setupProtocols( ) try: - await node.mountRlnRelay(rlnConf) + await node.setRlnValidator(rlnConf) except CatchableError: return err("failed to mount waku RLN relay protocol: " & getCurrentExceptionMsg()) diff --git a/logos_delivery/waku/node/waku_node/relay.nim b/logos_delivery/waku/node/waku_node/relay.nim index b1a2f9287..3446b4b73 100644 --- a/logos_delivery/waku/node/waku_node/relay.nim +++ b/logos_delivery/waku/node/waku_node/relay.nim @@ -13,6 +13,7 @@ import libp2p/crypto/crypto, libp2p/protocols/ping, libp2p/protocols/pubsub/gossipsub, + libp2p/protocols/pubsub/pubsub, libp2p/protocols/pubsub/rpc/messages, libp2p/builders, libp2p/transports/tcptransport, @@ -29,7 +30,6 @@ import waku_archive, waku_store_sync, rln, - rln/adapters/relay as waku_rln_adapter, node/waku_node, node/subscription_manager, node/peer_manager, @@ -185,27 +185,80 @@ proc mountRelay*( ## Waku RLN Relay -proc mountRlnRelay*( +proc setRlnValidator*( node: WakuNode, rlnConf: WakuRlnConfig, spamHandler = none(SpamHandler), registrationHandler = none(RegistrationHandler), ) {.async.} = - info "mounting rln relay" - - if node.wakuRelay.isNil(): - raise newException( - CatchableError, "WakuRelay protocol is not mounted, cannot mount Rln" - ) + info "setting rln validator" let rln = (await Rln.new(rlnConf, registrationHandler)).valueOr: - raise newException(CatchableError, "failed to mount Rln: " & error) + raise newException(CatchableError, "failed to set rln validator: " & error) if (rlnConf.userMessageLimit > rln.groupManager.rlnRelayMaxMessageLimit): - error "rln-relay-user-message-limit can't exceed the MAX_MESSAGE_LIMIT in the rln contract" - let validator = generateRlnValidator(rln, spamHandler) + error "rln-user-message-limit can't exceed the MAX_MESSAGE_LIMIT in the rln contract" + + node.rln = rln + + if node.wakuRelay.isNil(): + info "WakuRelay not mounted; RLN validator not set" + return + + ## Bridges RLN's protocol-agnostic message validation into a relay + ## (gossipsub) validator. The core decision is made by + ## `validateMessageAndUpdateLog`; this maps the result to + ## `pubsub.ValidationResult` so the validator can be installed on + ## WakuRelay's validator chain. + proc validator( + topic: string, message: WakuMessage + ): Future[pubsub.ValidationResult] {.async.} = + trace "rln-relay topic validator is called" + rln.clearNullifierLog() + + let msgProof = RateLimitProof.init(message.proof).valueOr: + trace "rln validator reject", error = error + return pubsub.ValidationResult.Reject + + # validate the message and update log + let validationRes = await rln.validateMessageAndUpdateLog(message) + + let + proof = byteutils.toHex(msgProof.proof) + root = inHex(msgProof.merkleRoot) + shareX = inHex(msgProof.shareX) + shareY = inHex(msgProof.shareY) + nullifier = inHex(msgProof.nullifier) + + case validationRes + of Valid: + trace "message validity is verified, relaying", + proof = proof, + root = root, + shareX = shareX, + shareY = shareY, + nullifier = nullifier + waku_rln_valid_messages_total.inc(labelValues = [topic]) + return pubsub.ValidationResult.Accept + of Invalid: + trace "message validity could not be verified, discarding", + proof = proof, + root = root, + shareX = shareX, + shareY = shareY, + nullifier = nullifier + return pubsub.ValidationResult.Reject + of Spam: + trace "A spam message is found! yay! discarding:", + proof = proof, + root = root, + shareX = shareX, + shareY = shareY, + nullifier = nullifier + if spamHandler.isSome(): + let handler = spamHandler.get() + handler(message) + return pubsub.ValidationResult.Reject # register rln validator as default validator info "Registering RLN validator" node.wakuRelay.addValidator(validator, "RLN validation failed") - - node.rln = rln diff --git a/logos_delivery/waku/rln/adapters/relay.nim b/logos_delivery/waku/rln/adapters/relay.nim deleted file mode 100644 index cce1d6bbc..000000000 --- a/logos_delivery/waku/rln/adapters/relay.nim +++ /dev/null @@ -1,78 +0,0 @@ -{.push raises: [].} - -import - std/options, - chronicles, - chronos, - results, - stew/byteutils, - libp2p/protocols/pubsub/pubsub - -import ../rln, ../protocol_types, ../protocol_metrics, ../conversion_utils - -import logos_delivery/waku/[waku_relay, waku_core] - -logScope: - topics = "waku rln adapter" - -proc generateRlnValidator*( - rln: Rln, spamHandler = none(SpamHandler) -): WakuValidatorHandler = - ## Bridges RLN's protocol-agnostic message validation into a relay - ## (gossipsub) validator. The core decision is made by - ## `validateMessageAndUpdateLog`; this adapter maps the result to - ## `pubsub.ValidationResult` so the validator can be installed on - ## WakuRelay's validator chain. - ## Validation logic follows https://rfc.vac.dev/spec/17/ - proc validator( - topic: string, message: WakuMessage - ): Future[pubsub.ValidationResult] {.async.} = - trace "rln-relay topic validator is called" - rln.clearNullifierLog() - - let msgProof = RateLimitProof.init(message.proof).valueOr: - trace "generateRlnValidator reject", error = error - return pubsub.ValidationResult.Reject - - # validate the message and update log - let validationRes = await rln.validateMessageAndUpdateLog(message) - - let - proof = byteutils.toHex(msgProof.proof) - epoch = fromEpoch(msgProof.epoch) - root = inHex(msgProof.merkleRoot) - shareX = inHex(msgProof.shareX) - shareY = inHex(msgProof.shareY) - nullifier = inHex(msgProof.nullifier) - payload = string.fromBytes(message.payload) - case validationRes - of Valid: - trace "message validity is verified, relaying", - proof = proof, - root = root, - shareX = shareX, - shareY = shareY, - nullifier = nullifier - waku_rln_valid_messages_total.inc(labelValues = [topic]) - return pubsub.ValidationResult.Accept - of Invalid: - trace "message validity could not be verified, discarding", - proof = proof, - root = root, - shareX = shareX, - shareY = shareY, - nullifier = nullifier - return pubsub.ValidationResult.Reject - of Spam: - trace "A spam message is found! yay! discarding:", - proof = proof, - root = root, - shareX = shareX, - shareY = shareY, - nullifier = nullifier - if spamHandler.isSome(): - let handler = spamHandler.get() - handler(message) - return pubsub.ValidationResult.Reject - - return validator diff --git a/logos_delivery/waku/rln/rln.nim b/logos_delivery/waku/rln/rln.nim index 77b72e48f..dab5ec3ef 100644 --- a/logos_delivery/waku/rln/rln.nim +++ b/logos_delivery/waku/rln/rln.nim @@ -11,7 +11,6 @@ import json, web3/eth_api_types, eth/keys, - libp2p/protocols/pubsub/rpc/messages, results, stew/[byteutils, arrayops], brokers/broker_context diff --git a/tests/node/test_wakunode_legacy_lightpush.nim b/tests/node/test_wakunode_legacy_lightpush.nim index 91be200f8..54cc0b6f6 100644 --- a/tests/node/test_wakunode_legacy_lightpush.nim +++ b/tests/node/test_wakunode_legacy_lightpush.nim @@ -128,7 +128,7 @@ suite "RLN Proofs as a Lightpush Service": (await server.mountRelay()).isOkOr: assert false, "Failed to mount relay" - await server.mountRlnRelay(wakuRlnConfig) + await server.setRlnValidator(wakuRlnConfig) check (await server.mountLegacyLightPush()).isOk() client.mountLegacyLightPushClient() diff --git a/tests/node/test_wakunode_lightpush.nim b/tests/node/test_wakunode_lightpush.nim index f5f8bdf45..6c28f5083 100644 --- a/tests/node/test_wakunode_lightpush.nim +++ b/tests/node/test_wakunode_lightpush.nim @@ -125,7 +125,7 @@ suite "RLN Proofs as a Lightpush Service": (await server.mountRelay()).isOkOr: assert false, "Failed to mount relay" - await server.mountRlnRelay(wakuRlnConfig) + await server.setRlnValidator(wakuRlnConfig) check (await server.mountLightPush()).isOk() client.mountLightPushClient() diff --git a/tests/node/test_wakunode_relay_rln.nim b/tests/node/test_wakunode_relay_rln.nim index c8aba49ee..823fde4b5 100644 --- a/tests/node/test_wakunode_relay_rln.nim +++ b/tests/node/test_wakunode_relay_rln.nim @@ -93,7 +93,7 @@ proc setupRelayWithOnChainRln*( node: WakuNode, shards: seq[RelayShard], wakuRlnConfig: WakuRlnConfig ) {.async.} = await node.mountRelay(shards) - await node.mountRlnRelay(wakuRlnConfig) + await node.setRlnValidator(wakuRlnConfig) suite "Waku RlnRelay - End to End - Static": var @@ -128,22 +128,6 @@ suite "Waku RlnRelay - End to End - Static": await allFutures(client.stop(), server.stop()) suite "Mount": - asyncTest "Can't mount if relay is not mounted": - # Given Relay and RLN are not mounted - check: - server.wakuRelay == nil - server.rln == nil - - # When RlnRelay is mounted - let catchRes = catch: - await server.setupStaticRln(1) - - # Then Relay and RLN are not mounted,and the process fails - check: - server.wakuRelay == nil - server.rln == nil - catchRes.error()[].msg == "WakuRelay protocol is not mounted, cannot mount Rln" - asyncTest "Pubsub topics subscribed before mounting RlnRelay are added to it": # Given the node enables Relay and Rln while subscribing to a pubsub topic await server.setupRelayWithStaticRln(1.uint, @[pubsubTopic]) @@ -232,7 +216,7 @@ suite "Waku RlnRelay - End to End - Static": ) try: - await node.mountRlnRelay(wakuRlnConfig) + await node.setRlnValidator(wakuRlnConfig) except CatchableError as e: check e.msg == "failed to mount Rln: rln-relay-user-message-limit can't exceed the MAX_MESSAGE_LIMIT in the rln contract" diff --git a/tests/waku_relay/utils.nim b/tests/waku_relay/utils.nim index 8c5a6a184..663d06c18 100644 --- a/tests/waku_relay/utils.nim +++ b/tests/waku_relay/utils.nim @@ -35,11 +35,6 @@ proc newTestWakuRelay*(switch = newTestSwitch()): Future[WakuRelay] {.async.} = return proto -proc setupRln*(node: WakuNode, identifier: uint) {.async.} = - await node.mountRlnRelay( - WakuRlnConfig(dynamic: false, credIndex: some(identifier), epochSizeSec: 1) - ) - proc subscribeToContentTopicWithHandler*( node: WakuNode, contentTopic: string ): Future[bool] = diff --git a/tests/waku_rln_relay/test_wakunode_rln_relay.nim b/tests/waku_rln_relay/test_wakunode_rln_relay.nim index 487d4a674..9da693119 100644 --- a/tests/waku_rln_relay/test_wakunode_rln_relay.nim +++ b/tests/waku_rln_relay/test_wakunode_rln_relay.nim @@ -52,7 +52,7 @@ procSuite "WakuNode - RLN relay": let wakuRlnConfig1 = getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) - await node1.mountRlnRelay(wakuRlnConfig1) + await node1.setRlnValidator(wakuRlnConfig1) await node1.start() # Registration is mandatory before sending messages with rln-relay @@ -76,7 +76,7 @@ procSuite "WakuNode - RLN relay": let wakuRlnConfig2 = getWakuRlnConfig(manager = manager, index = MembershipIndex(2)) - await node2.mountRlnRelay(wakuRlnConfig2) + await node2.setRlnValidator(wakuRlnConfig2) await node2.start() let manager2 = cast[OnchainGroupManager](node2.rln.groupManager) @@ -94,7 +94,7 @@ procSuite "WakuNode - RLN relay": let wakuRlnConfig3 = getWakuRlnConfig(manager = manager, index = MembershipIndex(3)) - await node3.mountRlnRelay(wakuRlnConfig3) + await node3.setRlnValidator(wakuRlnConfig3) await node3.start() let manager3 = cast[OnchainGroupManager](node3.rln.groupManager) @@ -165,7 +165,7 @@ procSuite "WakuNode - RLN relay": assert false, "Failed to mount relay" let wakuRlnConfig1 = getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) - await node1.mountRlnRelay(wakuRlnConfig1) + await node1.setRlnValidator(wakuRlnConfig1) await node1.start() let manager1 = cast[OnchainGroupManager](node1.rln.groupManager) let idCredentials1 = generateCredentials() @@ -182,7 +182,7 @@ procSuite "WakuNode - RLN relay": assert false, "Failed to mount relay" let wakuRlnConfig2 = getWakuRlnConfig(manager = manager, index = MembershipIndex(2)) - await node2.mountRlnRelay(wakuRlnConfig2) + await node2.setRlnValidator(wakuRlnConfig2) await node2.start() let manager2 = cast[OnchainGroupManager](node2.rln.groupManager) let idCredentials2 = generateCredentials() @@ -199,7 +199,7 @@ procSuite "WakuNode - RLN relay": assert false, "Failed to mount relay" let wakuRlnConfig3 = getWakuRlnConfig(manager = manager, index = MembershipIndex(3)) - await node3.mountRlnRelay(wakuRlnConfig3) + await node3.setRlnValidator(wakuRlnConfig3) await node3.start() let manager3 = cast[OnchainGroupManager](node3.rln.groupManager) let idCredentials3 = generateCredentials() @@ -315,7 +315,7 @@ procSuite "WakuNode - RLN relay": let wakuRlnConfig1 = getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) - await node1.mountRlnRelay(wakuRlnConfig1) + await node1.setRlnValidator(wakuRlnConfig1) await node1.start() let manager1 = cast[OnchainGroupManager](node1.rln.groupManager) @@ -336,7 +336,7 @@ procSuite "WakuNode - RLN relay": let wakuRlnConfig2 = getWakuRlnConfig(manager = manager, index = MembershipIndex(2)) - await node2.mountRlnRelay(wakuRlnConfig2) + await node2.setRlnValidator(wakuRlnConfig2) await node2.start() let manager2 = cast[OnchainGroupManager](node2.rln.groupManager) @@ -352,7 +352,7 @@ procSuite "WakuNode - RLN relay": let wakuRlnConfig3 = getWakuRlnConfig(manager = manager, index = MembershipIndex(3)) - await node3.mountRlnRelay(wakuRlnConfig3) + await node3.setRlnValidator(wakuRlnConfig3) await node3.start() let manager3 = cast[OnchainGroupManager](node3.rln.groupManager) @@ -426,7 +426,7 @@ procSuite "WakuNode - RLN relay": let wakuRlnConfig1 = getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) - await node1.mountRlnRelay(wakuRlnConfig1) + await node1.setRlnValidator(wakuRlnConfig1) await node1.start() # Registration is mandatory before sending messages with rln-relay @@ -449,7 +449,7 @@ procSuite "WakuNode - RLN relay": let wakuRlnConfig2 = getWakuRlnConfig(manager = manager, index = MembershipIndex(2)) - await node2.mountRlnRelay(wakuRlnConfig2) + await node2.setRlnValidator(wakuRlnConfig2) await node2.start() # Registration is mandatory before sending messages with rln-relay @@ -467,7 +467,7 @@ procSuite "WakuNode - RLN relay": let wakuRlnConfig3 = getWakuRlnConfig(manager = manager, index = MembershipIndex(3)) - await node3.mountRlnRelay(wakuRlnConfig3) + await node3.setRlnValidator(wakuRlnConfig3) await node3.start() # Registration is mandatory before sending messages with rln-relay @@ -595,7 +595,7 @@ procSuite "WakuNode - RLN relay": assert false, "Failed to mount relay" let wakuRlnConfig1 = getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) - await node1.mountRlnRelay(wakuRlnConfig1) + await node1.setRlnValidator(wakuRlnConfig1) await node1.start() # Registration is mandatory before sending messages with rln-relay @@ -614,7 +614,7 @@ procSuite "WakuNode - RLN relay": assert false, "Failed to mount relay" let wakuRlnConfig2 = getWakuRlnConfig(manager = manager, index = MembershipIndex(2)) - await node2.mountRlnRelay(wakuRlnConfig2) + await node2.setRlnValidator(wakuRlnConfig2) await node2.start() # Registration is mandatory before sending messages with rln-relay diff --git a/tests/waku_rln_relay/utils_offchain.nim b/tests/waku_rln_relay/utils_offchain.nim index 625b4ea2a..9976ec783 100644 --- a/tests/waku_rln_relay/utils_offchain.nim +++ b/tests/waku_rln_relay/utils_offchain.nim @@ -22,7 +22,7 @@ proc setupStaticRln*( identifier: uint, rlnRelayEthContractAddress: Option[string] = none(string), ) {.async.} = - await node.mountRlnRelay( + await node.setRlnValidator( WakuRlnConfig(dynamic: false, credIndex: some(identifier), epochSizeSec: 1) ) diff --git a/tests/wakunode_rest/test_rest_health.nim b/tests/wakunode_rest/test_rest_health.nim index 18a5abc4c..4a2fa0250 100644 --- a/tests/wakunode_rest/test_rest_health.nim +++ b/tests/wakunode_rest/test_rest_health.nim @@ -67,7 +67,7 @@ suite "Waku v2 REST API - health": let client = newRestHttpClient(initTAddress(restAddress, restPort)) # kick in rln (currently the only check for health) - await node.mountRlnRelay( + await node.setRlnValidator( getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) ) diff --git a/tests/wakunode_rest/test_rest_relay.nim b/tests/wakunode_rest/test_rest_relay.nim index d32ba3373..ccf59b4fd 100644 --- a/tests/wakunode_rest/test_rest_relay.nim +++ b/tests/wakunode_rest/test_rest_relay.nim @@ -261,7 +261,7 @@ suite "Waku v2 Rest API - Relay": assert false, "Failed to mount relay" let wakuRlnConfig = getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) - await node.mountRlnRelay(wakuRlnConfig) + await node.setRlnValidator(wakuRlnConfig) await node.start() # Registration is mandatory before sending messages with rln-relay let manager = cast[OnchainGroupManager](node.rln.groupManager) @@ -509,7 +509,7 @@ suite "Waku v2 Rest API - Relay": let wakuRlnConfig = getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) - await meshNode.mountRlnRelay(wakuRlnConfig) + await meshNode.setRlnValidator(wakuRlnConfig) await meshNode.start() const testPubsubTopic = PubsubTopic("/waku/2/rs/1/0") proc dummyHandler( @@ -530,7 +530,7 @@ suite "Waku v2 Rest API - Relay": let wakuRlnConfig = getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) - await node.mountRlnRelay(wakuRlnConfig) + await node.setRlnValidator(wakuRlnConfig) await node.start() await node.connectToNodes(@[meshNode.peerInfo.toRemotePeerInfo()]) @@ -600,7 +600,7 @@ suite "Waku v2 Rest API - Relay": require node.mountAutoSharding(1, 8).isOk let wakuRlnConfig = getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) - await node.mountRlnRelay(wakuRlnConfig) + await node.setRlnValidator(wakuRlnConfig) await node.start() # Registration is mandatory before sending messages with rln-relay @@ -659,7 +659,7 @@ suite "Waku v2 Rest API - Relay": (await node.mountRelay()).isOkOr: assert false, "Failed to mount relay" let wakuRlnConfig = getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) - await node.mountRlnRelay(wakuRlnConfig) + await node.setRlnValidator(wakuRlnConfig) await node.start() # Registration is mandatory before sending messages with rln-relay @@ -731,7 +731,7 @@ suite "Waku v2 Rest API - Relay": require node.mountAutoSharding(1, 8).isOk let wakuRlnConfig = getWakuRlnConfig(manager = manager, index = MembershipIndex(1)) - await node.mountRlnRelay(wakuRlnConfig) + await node.setRlnValidator(wakuRlnConfig) await node.start() # Registration is mandatory before sending messages with rln-relay From a4cd261fc7c7bd36aa181c5914b512b4d9640d76 Mon Sep 17 00:00:00 2001 From: Darshan <35736874+darshankabariya@users.noreply.github.com> Date: Tue, 30 Jun 2026 15:10:11 +0530 Subject: [PATCH 16/33] fix: start_node() after stop_node() re-opens the listener (#4007) --- logos_delivery/waku/node/waku_node.nim | 10 ++++-- logos_delivery/waku/waku.nim | 4 ++- tests/node/test_wakunode_restart.nim | 42 ++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 tests/node/test_wakunode_restart.nim diff --git a/logos_delivery/waku/node/waku_node.nim b/logos_delivery/waku/node/waku_node.nim index 665f0ba98..febc66731 100644 --- a/logos_delivery/waku/node/waku_node.nim +++ b/logos_delivery/waku/node/waku_node.nim @@ -136,6 +136,7 @@ type wakuMix*: WakuMix wakuKademlia*: WakuKademlia ports*: BoundPorts + relayReconnectFut*: Future[void] SubscriptionManager* = ref object of RootObj node*: WakuNode @@ -620,8 +621,9 @@ proc start*(node: WakuNode) {.async.} = ## NOTE: This will dispatch gossipsub start to the WakuRelay.start method override await node.switch.start() - # After switch.start, run custom Logos Delivery relay start logic - await node.reconnectRelayPeers() + # Reconnect to known relay peers in the background; it waits a prune backoff + # and must not block startup. + node.relayReconnectFut = node.reconnectRelayPeers() node.started = true @@ -650,6 +652,10 @@ proc start*(node: WakuNode) {.async.} = proc stop*(node: WakuNode) {.async.} = ## By stopping the switch we are stopping all the underlying mounted protocols + # Cancel the background relay reconnection (may still be in its backoff wait). + if not node.relayReconnectFut.isNil(): + await node.relayReconnectFut.cancelAndWait() + await node.subscriptionManager.stop() node.stopProvidersAndListeners() diff --git a/logos_delivery/waku/waku.nim b/logos_delivery/waku/waku.nim index 067b5b6ec..fa0562148 100644 --- a/logos_delivery/waku/waku.nim +++ b/logos_delivery/waku/waku.nim @@ -563,8 +563,10 @@ proc stop*(waku: Waku): Future[Result[void, string]] {.async: (raises: []).} = if not waku.healthMonitor.isNil(): await waku.healthMonitor.stopHealthMonitor() - ## Clear RequestConnectionStatus provider + ## Clear all providers registered in start() so a later start() can re-set them. RequestConnectionStatus.clearProvider(waku.brokerCtx) + RequestProtocolHealth.clearProvider(waku.brokerCtx) + RequestHealthReport.clearProvider(waku.brokerCtx) if not waku.restServer.isNil(): await waku.restServer.stop() diff --git a/tests/node/test_wakunode_restart.nim b/tests/node/test_wakunode_restart.nim new file mode 100644 index 000000000..8d58f5b9d --- /dev/null +++ b/tests/node/test_wakunode_restart.nim @@ -0,0 +1,42 @@ +{.used.} + +import std/options +import testutils/unittests, chronos, chronicles +import libp2p/switch + +import logos_delivery/waku/[waku_node, waku_core, node/peer_manager] +import ../testlib/[wakucore, wakunode, testasync] + +suite "WakuNode - restart (#3979)": + asyncTest "start -> stop -> start re-opens the listener promptly": + ## A restart must not block on the relay-reconnect backoff. + let + node1 = + newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + node2 = + newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + + (await node1.mountRelay()).isOkOr: + raiseAssert "mountRelay node1: " & error + (await node2.mountRelay()).isOkOr: + raiseAssert "mountRelay node2: " & error + + await allFutures(node1.start(), node2.start()) + + # node1 learns node2 as a relay peer, so a restart triggers reconnectRelayPeers. + await node1.connectToNodes(@[node2.peerInfo.toRemotePeerInfo()]) + + await node1.stop() + + # The restart must complete promptly and yield a usable, listening node. + let startFut = node1.start() + let restarted = await startFut.withTimeout(20.seconds) + if not restarted: + await startFut.cancelAndWait() + + check: + restarted + node1.started + node1.switch.peerInfo.listenAddrs.len > 0 + + await allFutures(node1.stop(), node2.stop()) From a7f893555d1fc88202b951cbb652f691f8ac5d2d Mon Sep 17 00:00:00 2001 From: NagyZoltanPeter <113987313+NagyZoltanPeter@users.noreply.github.com> Date: Tue, 30 Jun 2026 11:51:22 +0200 Subject: [PATCH 17/33] Integrate api-shape phase2 (#3989) + api interfaces (#3975) (#3999) * Reshape per-layer API into api/ folders and thin the FFI over them Each layer now separates its constructible core from its public surface: - core module (waku.nim / messaging_client.nim / reliable_channel_manager.nim): the type plus new/start/stop and the private construction helpers. - api/ folder: one module per differentiated set of operations (waku: topics/relay/filter/lightpush/store/peer_manager/discovery/ debug/health) plus an events surface. The waku api is reshaped to be the complete operation surface the C bindings need, so the library no longer reaches into node internals: relayPublish returns the message hash, relaySubscribe takes an optional handler, filter/lightpush auto-select the service peer, connectedPeersInfo returns structured data, pingPeer honours the timeout, plus relayNumPeersInMesh / relayNumConnectedPeers / isOnline. library/ is now a thin C-ABI shim: each {.ffi.} proc only marshals cstring/JSON/callbacks and delegates to ctx.myLib[].waku. (or messagingClient.). app_callbacks re-exports the modules defining its handler types, which the included FFI files previously relied on by leakage. Events move next to the surface that owns them, with each dependency kept pointing the right way: - waku/events/ relocated under waku/api/events/. - channel events live in channels/api/events.nim. - the four messaging-level message events move to messaging/api/events; MessageSeenEvent stays in waku because it is emitted by waku core, so moving it would make waku depend on the messaging layer. - delivery_events renamed to filter_subscribe_events to match the OnFilterSubscribe/Unsubscribe events it actually declares. Co-Authored-By: Claude Opus 4.8 * Add reliable-channel FFI ops + events (nim-ffi v0.1.3) Expose the reliable-channel layer through the v0.1.3 FFI: - channel_create / channel_send / channel_close call the ReliableChannelManager api (createReliableChannel / send / closeChannel), marshalling channel id + base64 payload + ephemeral by hand - channel message received / sent / errored are surfaced by listening to the channel-layer broker events in start_node and forwarding them through callEventCallback (received payload base64-encoded), dropped in stop_node Stays on nim-ffi v0.1.3 (no typed/CBOR rewrite). Co-Authored-By: Claude Opus 4.8 * Expose reliable-channel ops in the stable C header (#3851) The library already ships as a single .so with a tiered header surface (liblogosdelivery.h = stable Messaging/Reliable-Channels, liblogosdelivery_kernel.h = advanced Kernel). Per that tiering, the reliable-channel ops belong on the stable surface, so declare channel_create / channel_send / channel_close in liblogosdelivery.h and document the channel lifecycle events delivered through the event callback. Co-Authored-By: Claude Opus 4.8 * Graft PR#3975 interface layer onto decomposed foundation (events deduped) Add IKernel/IMessagingClient/IReliableChannelManager/ILogosDelivery interface classes under logos_delivery/api/. The EventBroker types PR#3975 hoisted into these files already exist in PR#3989's decomposed */api/events/ modules, so the interface files re-export those modules instead of redefining the types (avoids 8 duplicate EventBroker definitions). api/types.nim kept at the foundation version (ChannelId stays in channels/types.nim, which the decomposed modules import). Co-Authored-By: Claude Opus 4.8 * Wire impl classes to interfaces (inherit; relocate SendHandler) - Waku : IKernel, MessagingClient : IMessagingClient, ReliableChannelManager : IReliableChannelManager. - The operation procs already live in PR#3989's decomposed */api/ modules and stay as plain procs (nothing dispatches through the interface types, so no method-ization is needed). - SendHandler now lives in reliable_channel_manager_api.nim (its PR#3975 home); removed the duplicate from reliable_channel.nim, which re-exports the interface module so channels/api/{channel_lifecycle,send} still see it. Co-Authored-By: Claude Opus 4.8 * Wire LogosDelivery to ILogosDelivery orchestrator interface LogosDelivery : ILogosDelivery; start/stop/isOnline become method overrides. Peripheral PR#3975 edits (lightpush/store clients, self_req_handlers, statistics) are import-reorg artifacts of deleting waku/utils/requests.nim, which the decomposed structure keeps -- so they are intentionally not ported. Co-Authored-By: Claude Opus 4.8 * Dedup EventConnectionStatusChange (re-export from health_events) 9th duplicate EventBroker type: defined in both logos_delivery_api.nim and the decomposed waku/api/events/health_events.nim. The interface file now re-exports it. liblogosdelivery builds clean. Co-Authored-By: Claude Opus 4.8 * Move events back into interface-class source files (restore #3975 placement) Reverses the earlier dedup-by-re-export: event TYPE definitions now live in the interface classes, and the emptied decomposed event files are removed. - MessageSeenEvent -> logos_delivery/api/kernel_api.nim - Message{Sent,Error,Propagated,Received}Event -> api/messaging_client_api.nim - ChannelMessage{Received,Sent,Error}Event -> api/reliable_channel_manager_api.nim - EventConnectionStatusChange -> api/logos_delivery_api.nim Deleted (became empty after the move): - logos_delivery/waku/api/events/message_events.nim - logos_delivery/messaging/api/events.nim - logos_delivery/channels/api/events.nim health_events.nim keeps its two remaining events (content/shard topic health). Rewiring: each layer re-exports its interface module (waku->kernel_api, messaging_client->messaging_client_api, reliable_channel->reliable_channel_manager_api, which also re-exports messaging_client_api). Deep emitters/listeners (subscription_manager, waku_node, waku_node/relay, node_health_monitor, recv_service, send_service) import the owning interface module directly. kernel_api stays below node level (types/topics/message/store-common) so the node->kernel_api imports are acyclic. liblogosdelivery builds. Co-Authored-By: Claude Opus 4.8 * nph formatting --------- Co-authored-by: Ivan FB Co-authored-by: Claude Opus 4.8 --- library/channels_api/channel_api.nim | 76 ++++ library/kernel_api/debug_node_api.nim | 49 +- library/kernel_api/discovery_api.nim | 69 +-- library/kernel_api/peer_manager_api.nim | 91 ++-- library/kernel_api/ping_api.nim | 39 +- library/kernel_api/protocols/filter_api.nim | 96 +--- .../kernel_api/protocols/lightpush_api.nim | 26 +- library/kernel_api/protocols/relay_api.nim | 108 ++--- library/kernel_api/protocols/store_api.nim | 16 +- library/liblogosdelivery.h | 30 ++ library/liblogosdelivery.nim | 3 +- library/logos_delivery_api/node_api.nim | 41 +- logos_delivery/api/kernel_api.nim | 203 +++++++++ logos_delivery/api/logos_delivery_api.nim | 33 ++ logos_delivery/api/messaging_client_api.nim | 49 ++ .../api/reliable_channel_manager_api.nim | 70 +++ .../channels/api/channel_lifecycle.nim | 89 ++++ logos_delivery/channels/api/send.nim | 21 + logos_delivery/channels/events.nim | 39 -- logos_delivery/channels/reliable_channel.nim | 14 +- .../channels/reliable_channel_manager.nim | 108 +---- logos_delivery/logos_delivery.nim | 42 +- logos_delivery/messaging/api/send.nim | 34 ++ logos_delivery/messaging/api/subscription.nim | 18 + .../recv_service/recv_service.nim | 6 +- .../send_service/send_service.nim | 2 +- logos_delivery/messaging/messaging_client.nim | 59 +-- logos_delivery/waku/api/debug.nim | 36 ++ logos_delivery/waku/api/discovery.nim | 76 ++++ .../{ => api}/events/discovery_events.nim | 0 logos_delivery/waku/api/events/events.nim | 3 + .../events/filter_subscribe_events.nim} | 0 .../waku/{ => api}/events/health_events.nim | 6 +- .../waku/{ => api}/events/peer_events.nim | 0 logos_delivery/waku/api/filter.nim | 88 ++++ logos_delivery/waku/api/health.nim | 13 + logos_delivery/waku/api/lightpush.nim | 37 ++ logos_delivery/waku/api/peer_manager.nim | 112 +++++ logos_delivery/waku/api/ping.nim | 45 ++ logos_delivery/waku/api/relay.nim | 132 ++++++ logos_delivery/waku/api/store.nim | 29 ++ logos_delivery/waku/api/topics.nim | 27 ++ .../waku/discovery/waku_kademlia.nim | 2 +- logos_delivery/waku/events/events.nim | 9 - logos_delivery/waku/events/message_events.nim | 35 -- logos_delivery/waku/factory/app_callbacks.nim | 4 + logos_delivery/waku/factory/node_factory.nim | 2 +- .../health_monitor/node_health_monitor.nim | 5 +- .../waku/node/peer_manager/peer_manager.nim | 2 +- .../waku/node/subscription_manager.nim | 6 +- logos_delivery/waku/node/waku_node.nim | 6 +- logos_delivery/waku/node/waku_node/relay.nim | 2 +- logos_delivery/waku/waku.nim | 423 +----------------- logos_delivery/waku/waku_filter_v2/client.nim | 2 +- logos_delivery/waku/waku_relay/protocol.nim | 4 +- tests/api/test_api_health.nim | 2 +- tests/api/test_api_receive.nim | 3 +- tests/api/test_api_subscription.nim | 1 - .../test_reliable_channel_send_receive.nim | 2 +- tests/node/test_wakunode_health_monitor.nim | 4 +- 60 files changed, 1512 insertions(+), 1037 deletions(-) create mode 100644 library/channels_api/channel_api.nim create mode 100644 logos_delivery/api/kernel_api.nim create mode 100644 logos_delivery/api/logos_delivery_api.nim create mode 100644 logos_delivery/api/messaging_client_api.nim create mode 100644 logos_delivery/api/reliable_channel_manager_api.nim create mode 100644 logos_delivery/channels/api/channel_lifecycle.nim create mode 100644 logos_delivery/channels/api/send.nim delete mode 100644 logos_delivery/channels/events.nim create mode 100644 logos_delivery/messaging/api/send.nim create mode 100644 logos_delivery/messaging/api/subscription.nim create mode 100644 logos_delivery/waku/api/debug.nim create mode 100644 logos_delivery/waku/api/discovery.nim rename logos_delivery/waku/{ => api}/events/discovery_events.nim (100%) create mode 100644 logos_delivery/waku/api/events/events.nim rename logos_delivery/waku/{events/delivery_events.nim => api/events/filter_subscribe_events.nim} (100%) rename logos_delivery/waku/{ => api}/events/health_events.nim (82%) rename logos_delivery/waku/{ => api}/events/peer_events.nim (100%) create mode 100644 logos_delivery/waku/api/filter.nim create mode 100644 logos_delivery/waku/api/health.nim create mode 100644 logos_delivery/waku/api/lightpush.nim create mode 100644 logos_delivery/waku/api/peer_manager.nim create mode 100644 logos_delivery/waku/api/ping.nim create mode 100644 logos_delivery/waku/api/relay.nim create mode 100644 logos_delivery/waku/api/store.nim create mode 100644 logos_delivery/waku/api/topics.nim delete mode 100644 logos_delivery/waku/events/events.nim delete mode 100644 logos_delivery/waku/events/message_events.nim diff --git a/library/channels_api/channel_api.nim b/library/channels_api/channel_api.nim new file mode 100644 index 000000000..b25ad78ed --- /dev/null +++ b/library/channels_api/channel_api.nim @@ -0,0 +1,76 @@ +import std/json +import chronos, results, ffi +import + logos_delivery/waku/common/base64, + logos_delivery, + logos_delivery/waku/waku_core/topics/content_topic, + logos_delivery/api/types, + ../declare_lib + +proc logosdelivery_channel_create( + ctx: ptr FFIContext[LogosDelivery], + callback: FFICallBack, + userData: pointer, + channelIdStr: cstring, + contentTopicStr: cstring, + senderIdStr: cstring, +) {.ffi.} = + requireInitializedNode(ctx, "ChannelCreate"): + return err(errMsg) + + let id = ctx.myLib[].reliableChannelManager.createReliableChannel( + ChannelId($channelIdStr), + ContentTopic($contentTopicStr), + SdsParticipantID($senderIdStr), + ).valueOr: + return err("ChannelCreate failed: " & $error) + + return ok(string(id)) + +proc logosdelivery_channel_send( + ctx: ptr FFIContext[LogosDelivery], + callback: FFICallBack, + userData: pointer, + channelIdStr: cstring, + messageJson: cstring, +) {.ffi.} = + ## `messageJson` carries `{ "payload": , "ephemeral": }`. + requireInitializedNode(ctx, "ChannelSend"): + return err(errMsg) + + var jsonNode: JsonNode + try: + jsonNode = parseJson($messageJson) + except Exception as e: + return err("Failed to parse channel message JSON: " & e.msg) + + if not jsonNode.hasKey("payload"): + return err("Missing payload field") + + let payload = base64.decode(Base64String(jsonNode["payload"].getStr())).valueOr: + return err("invalid payload format: " & error) + + let ephemeral = jsonNode.getOrDefault("ephemeral").getBool(false) + + let requestId = ( + await ctx.myLib[].reliableChannelManager.send( + ChannelId($channelIdStr), payload, ephemeral + ) + ).valueOr: + return err("ChannelSend failed: " & $error) + + return ok($requestId) + +proc logosdelivery_channel_close( + ctx: ptr FFIContext[LogosDelivery], + callback: FFICallBack, + userData: pointer, + channelIdStr: cstring, +) {.ffi.} = + requireInitializedNode(ctx, "ChannelClose"): + return err(errMsg) + + (await ctx.myLib[].reliableChannelManager.closeChannel(ChannelId($channelIdStr))).isOkOr: + return err("ChannelClose failed: " & $error) + + return ok("") diff --git a/library/kernel_api/debug_node_api.nim b/library/kernel_api/debug_node_api.nim index 44764a673..7d39935c6 100644 --- a/library/kernel_api/debug_node_api.nim +++ b/library/kernel_api/debug_node_api.nim @@ -1,53 +1,46 @@ -import std/json -import - chronicles, - chronos, - results, - eth/p2p/discoveryv5/enr, - strutils, - libp2p/peerid, - metrics, - ffi -import - logos_delivery/waku/waku, - logos_delivery/waku/node/waku_node, - logos_delivery/waku/node/health_monitor, - library/declare_lib - -proc getMultiaddresses(node: WakuNode): seq[string] = - return node.info().listenAddresses - -proc getMetrics(): string = - {.gcsafe.}: - return defaultRegistry.toText() ## defaultRegistry is {.global.} in metrics module +import std/strutils +import chronos, results, ffi +import logos_delivery, library/declare_lib proc waku_version( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - return ok(WakuNodeVersionString) + let v = (await ctx.myLib[].waku.version()).valueOr: + return err(error) + return ok(v) proc waku_listen_addresses( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = ## returns a comma-separated string of the listen addresses - return ok(ctx.myLib[].waku.node.getMultiaddresses().join(",")) + let addrs = (await ctx.myLib[].waku.listenAddresses()).valueOr: + return err(error) + return ok(addrs.join(",")) proc waku_get_my_enr( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - return ok(ctx.myLib[].waku.node.enr.toURI()) + let enrUri = (await ctx.myLib[].waku.myEnr()).valueOr: + return err(error) + return ok(enrUri) proc waku_get_my_peerid( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - return ok($ctx.myLib[].waku.node.peerId()) + let peerId = (await ctx.myLib[].waku.myPeerId()).valueOr: + return err(error) + return ok(peerId) proc waku_get_metrics( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - return ok(getMetrics()) + let m = (await ctx.myLib[].waku.metrics()).valueOr: + return err(error) + return ok(m) proc waku_is_online( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - return ok($ctx.myLib[].waku.healthMonitor.onlineMonitor.amIOnline()) + let online = (await ctx.myLib[].waku.isOnline()).valueOr: + return err(error) + return ok($online) diff --git a/library/kernel_api/discovery_api.nim b/library/kernel_api/discovery_api.nim index 158c3a925..98c83e42d 100644 --- a/library/kernel_api/discovery_api.nim +++ b/library/kernel_api/discovery_api.nim @@ -1,42 +1,6 @@ -import logos_delivery/waku/compat/option_valueor -import std/json -import chronos, chronicles, results, strutils, libp2p/multiaddress, ffi -import - logos_delivery/waku/waku, - logos_delivery/waku/discovery/waku_dnsdisc, - logos_delivery/waku/discovery/waku_discv5, - logos_delivery/waku/waku_core/peers, - logos_delivery/waku/waku_node, - library/declare_lib - -proc retrieveBootstrapNodes( - enrTreeUrl: string, ipDnsServer: string -): Future[Result[seq[string], string]] {.async.} = - let dnsNameServers = @[parseIpAddress(ipDnsServer)] - let discoveredPeers: seq[RemotePeerInfo] = ( - await retrieveDynamicBootstrapNodes(enrTreeUrl, dnsNameServers) - ).valueOr: - return err("failed discovering peers from DNS: " & $error) - - var multiAddresses = newSeq[string]() - - for discPeer in discoveredPeers: - for address in discPeer.addrs: - multiAddresses.add($address & "/p2p/" & $discPeer) - - return ok(multiAddresses) - -proc updateDiscv5BootstrapNodes(nodes: string, waku: Waku): Result[void, string] = - waku.wakuDiscv5.updateBootstrapRecords(nodes).isOkOr: - return err("error in updateDiscv5BootstrapNodes: " & $error) - return ok() - -proc performPeerExchangeRequestTo*( - numPeers: uint64, waku: Waku -): Future[Result[int, string]] {.async.} = - let numPeersRecv = (await waku.node.fetchPeerExchangePeers(numPeers)).valueOr: - return err($error) - return ok(numPeersRecv) +import std/strutils +import chronos, chronicles, results, ffi +import logos_delivery, library/declare_lib proc waku_discv5_update_bootnodes( ctx: ptr FFIContext[LogosDelivery], @@ -46,11 +10,9 @@ proc waku_discv5_update_bootnodes( ) {.ffi.} = ## Updates the bootnode list used for discovering new peers via DiscoveryV5 ## bootnodes - JSON array containing the bootnode ENRs i.e. `["enr:...", "enr:..."]` - - updateDiscv5BootstrapNodes($bootnodes, ctx.myLib[].waku).isOkOr: + (await ctx.myLib[].waku.discv5UpdateBootnodes($bootnodes)).isOkOr: error "UPDATE_DISCV5_BOOTSTRAP_NODES failed", error = error - return err($error) - + return err(error) return ok("discovery request processed correctly") proc waku_dns_discovery( @@ -61,26 +23,28 @@ proc waku_dns_discovery( nameDnsServer: cstring, timeoutMs: cint, ) {.ffi.} = - let nodes = (await retrieveBootstrapNodes($enrTreeUrl, $nameDnsServer)).valueOr: + let nodes = ( + await ctx.myLib[].waku.dnsDiscovery($enrTreeUrl, $nameDnsServer, int(timeoutMs)) + ).valueOr: error "GET_BOOTSTRAP_NODES failed", error = error - return err($error) - + return err(error) ## returns a comma-separated string of bootstrap nodes' multiaddresses return ok(nodes.join(",")) proc waku_start_discv5( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - (await ctx.myLib[].waku.wakuDiscv5.start()).isOkOr: + (await ctx.myLib[].waku.startDiscv5()).isOkOr: error "START_DISCV5 failed", error = error - return err("error starting discv5: " & $error) - + return err(error) return ok("discv5 started correctly") proc waku_stop_discv5( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - await ctx.myLib[].waku.wakuDiscv5.stop() + (await ctx.myLib[].waku.stopDiscv5()).isOkOr: + error "STOP_DISCV5 failed", error = error + return err(error) return ok("discv5 stopped correctly") proc waku_peer_exchange_request( @@ -89,8 +53,7 @@ proc waku_peer_exchange_request( userData: pointer, numPeers: uint64, ) {.ffi.} = - let numValidPeers = (await performPeerExchangeRequestTo(numPeers, ctx.myLib[].waku)).valueOr: + let numValidPeers = (await ctx.myLib[].waku.peerExchangeRequest(numPeers)).valueOr: error "waku_peer_exchange_request failed", error = error - return err("failed peer exchange: " & $error) - + return err(error) return ok($numValidPeers) diff --git a/library/kernel_api/peer_manager_api.nim b/library/kernel_api/peer_manager_api.nim index eeea2c63f..e14b8b2c9 100644 --- a/library/kernel_api/peer_manager_api.nim +++ b/library/kernel_api/peer_manager_api.nim @@ -1,11 +1,6 @@ -import logos_delivery/waku/compat/option_valueor -import std/[sequtils, strutils, tables] -import chronicles, chronos, results, options, json, ffi -import - logos_delivery/waku/waku, - logos_delivery/waku/node/waku_node, - logos_delivery/waku/node/peer_manager, - library/declare_lib +import std/[strutils, tables, json] +import chronicles, chronos, results, ffi +import logos_delivery, library/declare_lib type PeerInfo = object protocols: seq[string] @@ -15,11 +10,9 @@ proc waku_get_peerids_from_peerstore( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = ## returns a comma-separated string of peerIDs - let peerIDs = ctx.myLib[].waku.node.peerManager.switch.peerStore - .peers() - .mapIt($it.peerId) - .join(",") - return ok(peerIDs) + let peerIds = (await ctx.myLib[].waku.peerIdsFromPeerstore()).valueOr: + return err(error) + return ok(peerIds.join(",")) proc waku_connect( ctx: ptr FFIContext[LogosDelivery], @@ -28,8 +21,9 @@ proc waku_connect( peerMultiAddr: cstring, timeoutMs: cuint, ) {.ffi.} = - let peers = ($peerMultiAddr).split(",").mapIt(strip(it)) - await ctx.myLib[].waku.node.connectToNodes(peers, source = "static") + let peers = ($peerMultiAddr).split(",") + (await ctx.myLib[].waku.connect(peers, uint32(timeoutMs))).isOkOr: + return err(error) return ok("") proc waku_disconnect_peer_by_id( @@ -38,16 +32,16 @@ proc waku_disconnect_peer_by_id( userData: pointer, peerId: cstring, ) {.ffi.} = - let pId = PeerId.init($peerId).valueOr: - error "DISCONNECT_PEER_BY_ID failed", error = $error - return err($error) - await ctx.myLib[].waku.node.peerManager.disconnectNode(pId) + (await ctx.myLib[].waku.disconnectPeerById($peerId)).isOkOr: + error "DISCONNECT_PEER_BY_ID failed", error = error + return err(error) return ok("") proc waku_disconnect_all_peers( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - await ctx.myLib[].waku.node.peerManager.disconnectAllPeers() + (await ctx.myLib[].waku.disconnectAllPeers()).isOkOr: + return err(error) return ok("") proc waku_dial_peer( @@ -58,14 +52,9 @@ proc waku_dial_peer( protocol: cstring, timeoutMs: cuint, ) {.ffi.} = - let remotePeerInfo = parsePeerInfo($peerMultiAddr).valueOr: - error "DIAL_PEER failed", error = $error - return err($error) - let conn = await ctx.myLib[].waku.node.peerManager.dialPeer(remotePeerInfo, $protocol) - if conn.isNone(): - let msg = "failed dialing peer" - error "DIAL_PEER failed", error = msg, peerId = $remotePeerInfo.peerId - return err(msg) + (await ctx.myLib[].waku.dialPeer($peerMultiAddr, $protocol, int(timeoutMs))).isOkOr: + error "DIAL_PEER failed", error = error + return err(error) return ok("") proc waku_dial_peer_by_id( @@ -76,47 +65,32 @@ proc waku_dial_peer_by_id( protocol: cstring, timeoutMs: cuint, ) {.ffi.} = - let pId = PeerId.init($peerId).valueOr: - error "DIAL_PEER_BY_ID failed", error = $error - return err($error) - let conn = await ctx.myLib[].waku.node.peerManager.dialPeer(pId, $protocol) - if conn.isNone(): - let msg = "failed dialing peer" - error "DIAL_PEER_BY_ID failed", error = msg, peerId = $peerId - return err(msg) - + (await ctx.myLib[].waku.dialPeerById($peerId, $protocol, int(timeoutMs))).isOkOr: + error "DIAL_PEER_BY_ID failed", error = error + return err(error) return ok("") proc waku_get_connected_peers_info( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = ## returns a JSON string mapping peerIDs to objects with protocols and addresses + let peers = (await ctx.myLib[].waku.connectedPeersInfo()).valueOr: + return err(error) var peersMap = initTable[string, PeerInfo]() - let peers = ctx.myLib[].waku.node.peerManager.switch.peerStore.peers().filterIt( - it.connectedness == Connected - ) - - # Build a map of peer IDs to peer info objects for peer in peers: - let peerIdStr = $peer.peerId - peersMap[peerIdStr] = - PeerInfo(protocols: peer.protocols, addresses: peer.addrs.mapIt($it)) + peersMap[peer.peerId] = + PeerInfo(protocols: peer.protocols, addresses: peer.addresses) - # Convert the map to JSON string - let jsonObj = %*peersMap - let jsonStr = $jsonObj - return ok(jsonStr) + return ok($(%*peersMap)) proc waku_get_connected_peers( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = ## returns a comma-separated string of peerIDs - let - (inPeerIds, outPeerIds) = ctx.myLib[].waku.node.peerManager.connectedPeers() - connectedPeerids = concat(inPeerIds, outPeerIds) - - return ok(connectedPeerids.mapIt($it).join(",")) + let peerIds = (await ctx.myLib[].waku.connectedPeers()).valueOr: + return err(error) + return ok(peerIds.join(",")) proc waku_get_peerids_by_protocol( ctx: ptr FFIContext[LogosDelivery], @@ -125,9 +99,6 @@ proc waku_get_peerids_by_protocol( protocol: cstring, ) {.ffi.} = ## returns a comma-separated string of peerIDs that mount the given protocol - let connectedPeers = ctx.myLib[].waku.node.peerManager.switch.peerStore - .peers($protocol) - .filterIt(it.connectedness == Connected) - .mapIt($it.peerId) - .join(",") - return ok(connectedPeers) + let peerIds = (await ctx.myLib[].waku.peerIdsByProtocol($protocol)).valueOr: + return err(error) + return ok(peerIds.join(",")) diff --git a/library/kernel_api/ping_api.nim b/library/kernel_api/ping_api.nim index e6ed69dd1..6570fffd5 100644 --- a/library/kernel_api/ping_api.nim +++ b/library/kernel_api/ping_api.nim @@ -1,7 +1,5 @@ -import std/[json, strutils] import chronos, results, ffi -import libp2p/[protocols/ping, switch, multiaddress, multicodec] -import logos_delivery/waku/[waku, waku_core/peers, node/waku_node], library/declare_lib +import logos_delivery, library/declare_lib proc waku_ping_peer( ctx: ptr FFIContext[LogosDelivery], @@ -10,35 +8,6 @@ proc waku_ping_peer( peerAddr: cstring, timeoutMs: cuint, ) {.ffi.} = - let peerInfo = peers.parsePeerInfo(($peerAddr).split(",")).valueOr: - return err("PingRequest failed to parse peer addr: " & $error) - - let timeout = chronos.milliseconds(timeoutMs) - proc ping(): Future[Result[Duration, string]] {.async, gcsafe.} = - try: - let conn = await ctx.myLib[].waku.node.switch.dial( - peerInfo.peerId, peerInfo.addrs, PingCodec - ) - defer: - await conn.close() - - let pingRTT = await ctx.myLib[].waku.node.libp2pPing.ping(conn) - if pingRTT == 0.nanos: - return err("could not ping peer: rtt-0") - return ok(pingRTT) - except CatchableError as exc: - return err("could not ping peer: " & exc.msg) - - let pingFuture = ping() - let pingRTT: Duration = - if timeout == chronos.milliseconds(0): # No timeout expected - (await pingFuture).valueOr: - return err("ping failed, no timeout expected: " & error) - else: - let timedOut = not (await pingFuture.withTimeout(timeout)) - if timedOut: - return err("ping timed out") - pingFuture.read().valueOr: - return err("failed to read ping future: " & error) - - return ok($(pingRTT.nanos)) + let rttNanos = (await ctx.myLib[].waku.pingPeer($peerAddr, int(timeoutMs))).valueOr: + return err(error) + return ok($rttNanos) diff --git a/library/kernel_api/protocols/filter_api.nim b/library/kernel_api/protocols/filter_api.nim index a070bd2c7..cd613c1e0 100644 --- a/library/kernel_api/protocols/filter_api.nim +++ b/library/kernel_api/protocols/filter_api.nim @@ -1,29 +1,14 @@ -import logos_delivery/waku/compat/option_valueor -import options, std/[strutils, sequtils] +import std/[strutils, sequtils] import chronicles, chronos, results, ffi import - logos_delivery/waku/waku_filter_v2/client, + logos_delivery, logos_delivery/waku/waku_core/message/message, - logos_delivery/waku/waku, - logos_delivery/waku/waku_relay, - logos_delivery/waku/waku_filter_v2/common, logos_delivery/waku/waku_core/subscription/push_handler, - logos_delivery/waku/node/peer_manager/peer_manager, - logos_delivery/waku/waku_node, logos_delivery/waku/waku_core/topics/pubsub_topic, logos_delivery/waku/waku_core/topics/content_topic, library/events/json_message_event, library/declare_lib -const FilterOpTimeout = 5.seconds - -proc checkFilterClientMounted(waku: Waku): Result[string, string] = - if waku.node.wakuFilterClient.isNil(): - let errorMsg = "wakuFilterClient is not mounted" - error "fail filter process", error = errorMsg - return err(errorMsg) - return ok("") - proc waku_filter_subscribe( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, @@ -31,33 +16,20 @@ proc waku_filter_subscribe( pubSubTopic: cstring, contentTopics: cstring, ) {.ffi.} = - proc onReceivedMessage(ctx: ptr FFIContext): WakuRelayHandler = + proc onReceivedMessage(ctx: ptr FFIContext[LogosDelivery]): FilterPushHandler = return proc(pubsubTopic: PubsubTopic, msg: WakuMessage) {.async.} = callEventCallback(ctx, "onReceivedMessage"): $JsonMessageEvent.new(pubsubTopic, msg) - checkFilterClientMounted(ctx.myLib[].waku).isOkOr: - return err($error) - - var filterPushEventCallback = FilterPushHandler(onReceivedMessage(ctx)) - ctx.myLib[].waku.node.wakuFilterClient.registerPushHandler(filterPushEventCallback) - - let peer = ctx.myLib[].waku.node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: - let errorMsg = "could not find peer with WakuFilterSubscribeCodec when subscribing" - error "fail filter subscribe", error = errorMsg - return err(errorMsg) - - let subFut = ctx.myLib[].waku.node.filterSubscribe( - some(PubsubTopic($pubsubTopic)), - ($contentTopics).split(",").mapIt(ContentTopic(it)), - peer, - ) - if not await subFut.withTimeout(FilterOpTimeout): - let errorMsg = "filter subscription timed out" - error "fail filter unsubscribe", error = errorMsg - - return err(errorMsg) - + ( + await ctx.myLib[].waku.filterSubscribe( + PubsubTopic($pubSubTopic), + ($contentTopics).split(",").mapIt(ContentTopic(it)), + FilterPushHandler(onReceivedMessage(ctx)), + ) + ).isOkOr: + error "fail filter subscribe", error = error + return err(error) return ok("") proc waku_filter_unsubscribe( @@ -67,43 +39,19 @@ proc waku_filter_unsubscribe( pubSubTopic: cstring, contentTopics: cstring, ) {.ffi.} = - checkFilterClientMounted(ctx.myLib[].waku).isOkOr: - return err($error) - - let peer = ctx.myLib[].waku.node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: - let errorMsg = - "could not find peer with WakuFilterSubscribeCodec when unsubscribing" - error "fail filter process", error = errorMsg - return err(errorMsg) - - let subFut = ctx.myLib[].waku.node.filterUnsubscribe( - some(PubsubTopic($pubsubTopic)), - ($contentTopics).split(",").mapIt(ContentTopic(it)), - peer, - ) - if not await subFut.withTimeout(FilterOpTimeout): - let errorMsg = "filter un-subscription timed out" - error "fail filter unsubscribe", error = errorMsg - return err(errorMsg) + ( + await ctx.myLib[].waku.filterUnsubscribe( + PubsubTopic($pubSubTopic), ($contentTopics).split(",").mapIt(ContentTopic(it)) + ) + ).isOkOr: + error "fail filter unsubscribe", error = error + return err(error) return ok("") proc waku_filter_unsubscribe_all( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - checkFilterClientMounted(ctx.myLib[].waku).isOkOr: - return err($error) - - let peer = ctx.myLib[].waku.node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: - let errorMsg = - "could not find peer with WakuFilterSubscribeCodec when unsubscribing all" - error "fail filter unsubscribe all", error = errorMsg - return err(errorMsg) - - let unsubFut = ctx.myLib[].waku.node.filterUnsubscribeAll(peer) - - if not await unsubFut.withTimeout(FilterOpTimeout): - let errorMsg = "filter un-subscription all timed out" - error "fail filter unsubscribe all", error = errorMsg - - return err(errorMsg) + (await ctx.myLib[].waku.filterUnsubscribeAll()).isOkOr: + error "fail filter unsubscribe all", error = error + return err(error) return ok("") diff --git a/library/kernel_api/protocols/lightpush_api.nim b/library/kernel_api/protocols/lightpush_api.nim index eb0d1de09..c7bc8169d 100644 --- a/library/kernel_api/protocols/lightpush_api.nim +++ b/library/kernel_api/protocols/lightpush_api.nim @@ -1,14 +1,9 @@ -import logos_delivery/waku/compat/option_valueor -import options, std/[json, strformat] +import std/[json, strformat] import chronicles, chronos, results, ffi import - logos_delivery/waku/waku_core/message/message, - logos_delivery/waku/waku_core/codecs, - logos_delivery/waku/waku, + logos_delivery, logos_delivery/waku/waku_core/message, logos_delivery/waku/waku_core/topics/pubsub_topic, - logos_delivery/waku/waku_lightpush_legacy/client, - logos_delivery/waku/node/peer_manager/peer_manager, library/events/json_message_event, library/declare_lib @@ -19,11 +14,6 @@ proc waku_lightpush_publish( pubSubTopic: cstring, jsonWakuMessage: cstring, ) {.ffi.} = - if ctx.myLib[].waku.node.wakuLightpushClient.isNil(): - let errorMsg = "LightpushRequest waku.node.wakuLightpushClient is nil" - error "PUBLISH failed", error = errorMsg - return err(errorMsg) - var jsonMessage: JsonMessage try: let jsonContent = parseJson($jsonWakuMessage) @@ -35,18 +25,10 @@ proc waku_lightpush_publish( let msg = json_message_event.toWakuMessage(jsonMessage).valueOr: return err("Problem building the WakuMessage: " & $error) - let peerOpt = ctx.myLib[].waku.node.peerManager.selectPeer(WakuLightPushCodec) - if peerOpt.isNone(): - let errorMsg = "failed to lightpublish message, no suitable remote peers" - error "PUBLISH failed", error = errorMsg - return err(errorMsg) - let msgHashHex = ( - await ctx.myLib[].waku.node.wakuLegacyLightpushClient.publish( - $pubsubTopic, msg, peer = peerOpt.get() - ) + await ctx.myLib[].waku.lightpushPublish(PubsubTopic($pubSubTopic), msg) ).valueOr: error "PUBLISH failed", error = error - return err($error) + return err(error) return ok(msgHashHex) diff --git a/library/kernel_api/protocols/relay_api.nim b/library/kernel_api/protocols/relay_api.nim index d580597ae..7a1fe446f 100644 --- a/library/kernel_api/protocols/relay_api.nim +++ b/library/kernel_api/protocols/relay_api.nim @@ -1,17 +1,10 @@ -import logos_delivery/waku/compat/option_valueor -import std/[net, sequtils, strutils, json], strformat -import chronicles, chronos, stew/byteutils, results, ffi +import std/[strutils, json] +import chronicles, chronos, results, ffi import - logos_delivery/waku/waku_core/message/message, - logos_delivery/waku/factory/validator_signed, - logos_delivery/waku/waku, - tools/confutils/cli_args, - logos_delivery/waku/waku_core/message, + logos_delivery, logos_delivery/waku/waku_core/topics/pubsub_topic, - logos_delivery/waku/waku_core/topics, - logos_delivery/waku/node/waku_node/relay, + logos_delivery/waku/waku_core/message, logos_delivery/waku/waku_relay/protocol, - logos_delivery/waku/node/peer_manager, library/events/json_message_event, library/declare_lib @@ -21,11 +14,11 @@ proc waku_relay_get_peers_in_mesh( userData: pointer, pubSubTopic: cstring, ) {.ffi.} = - let meshPeers = ctx.myLib[].waku.node.wakuRelay.getPeersInMesh($pubsubTopic).valueOr: + let peers = (await ctx.myLib[].waku.relayPeersInMesh(PubsubTopic($pubSubTopic))).valueOr: error "LIST_MESH_PEERS failed", error = error - return err($error) + return err(error) ## returns a comma-separated string of peerIDs - return ok(meshPeers.mapIt($it).join(",")) + return ok(peers.join(",")) proc waku_relay_get_num_peers_in_mesh( ctx: ptr FFIContext[LogosDelivery], @@ -33,10 +26,10 @@ proc waku_relay_get_num_peers_in_mesh( userData: pointer, pubSubTopic: cstring, ) {.ffi.} = - let numPeersInMesh = ctx.myLib[].waku.node.wakuRelay.getNumPeersInMesh($pubsubTopic).valueOr: + let n = (await ctx.myLib[].waku.relayNumPeersInMesh(PubsubTopic($pubSubTopic))).valueOr: error "NUM_MESH_PEERS failed", error = error - return err($error) - return ok($numPeersInMesh) + return err(error) + return ok($n) proc waku_relay_get_connected_peers( ctx: ptr FFIContext[LogosDelivery], @@ -45,11 +38,10 @@ proc waku_relay_get_connected_peers( pubSubTopic: cstring, ) {.ffi.} = ## Returns the list of all connected peers to an specific pubsub topic - let connPeers = ctx.myLib[].waku.node.wakuRelay.getConnectedPeers($pubsubTopic).valueOr: + let peers = (await ctx.myLib[].waku.relayConnectedPeers(PubsubTopic($pubSubTopic))).valueOr: error "LIST_CONNECTED_PEERS failed", error = error - return err($error) - ## returns a comma-separated string of peerIDs - return ok(connPeers.mapIt($it).join(",")) + return err(error) + return ok(peers.join(",")) proc waku_relay_get_num_connected_peers( ctx: ptr FFIContext[LogosDelivery], @@ -57,10 +49,10 @@ proc waku_relay_get_num_connected_peers( userData: pointer, pubSubTopic: cstring, ) {.ffi.} = - let numConnPeers = ctx.myLib[].waku.node.wakuRelay.getNumConnectedPeers($pubsubTopic).valueOr: + let n = (await ctx.myLib[].waku.relayNumConnectedPeers(PubsubTopic($pubSubTopic))).valueOr: error "NUM_CONNECTED_PEERS failed", error = error - return err($error) - return ok($numConnPeers) + return err(error) + return ok($n) proc waku_relay_add_protected_shard( ctx: ptr FFIContext[LogosDelivery], @@ -71,15 +63,12 @@ proc waku_relay_add_protected_shard( publicKey: cstring, ) {.ffi.} = ## Protects a shard with a public key - try: - let relayShard = RelayShard(clusterId: uint16(clusterId), shardId: uint16(shardId)) - let protectedShard = ProtectedShard.parseCmdArg($relayShard & ":" & $publicKey) - ctx.myLib[].waku.node.wakuRelay.addSignedShardsValidator( - @[protectedShard], uint16(clusterId) + ( + await ctx.myLib[].waku.relayAddProtectedShard( + uint16(clusterId), uint16(shardId), $publicKey ) - except ValueError as exc: - return err("ERROR in waku_relay_add_protected_shard: " & exc.msg) - + ).isOkOr: + return err(error) return ok("") proc waku_relay_subscribe( @@ -88,20 +77,18 @@ proc waku_relay_subscribe( userData: pointer, pubSubTopic: cstring, ) {.ffi.} = - echo "Subscribing to topic: " & $pubSubTopic & " ..." proc onReceivedMessage(ctx: ptr FFIContext[LogosDelivery]): WakuRelayHandler = return proc(pubsubTopic: PubsubTopic, msg: WakuMessage) {.async.} = callEventCallback(ctx, "onReceivedMessage"): $JsonMessageEvent.new(pubsubTopic, msg) - var cb = onReceivedMessage(ctx) - - ctx.myLib[].waku.node.subscribe( - (kind: SubscriptionKind.PubsubSub, topic: $pubsubTopic), - handler = WakuRelayHandler(cb), + ( + await ctx.myLib[].waku.relaySubscribe( + PubsubTopic($pubSubTopic), WakuRelayHandler(onReceivedMessage(ctx)) + ) ).isOkOr: error "SUBSCRIBE failed", error = error - return err($error) + return err(error) return ok("") proc waku_relay_unsubscribe( @@ -110,12 +97,9 @@ proc waku_relay_unsubscribe( userData: pointer, pubSubTopic: cstring, ) {.ffi.} = - ctx.myLib[].waku.node.unsubscribe( - (kind: SubscriptionKind.PubsubSub, topic: $pubsubTopic) - ).isOkOr: + (await ctx.myLib[].waku.relayUnsubscribe(PubsubTopic($pubSubTopic))).isOkOr: error "UNSUBSCRIBE failed", error = error - return err($error) - + return err(error) return ok("") proc waku_relay_publish( @@ -126,31 +110,32 @@ proc waku_relay_publish( jsonWakuMessage: cstring, timeoutMs: cuint, ) {.ffi.} = - var - # https://rfc.vac.dev/spec/36/#extern-char-waku_relay_publishchar-messagejson-char-pubsubtopic-int-timeoutms - jsonMessage: JsonMessage + var jsonMessage: JsonMessage try: let jsonContent = parseJson($jsonWakuMessage) jsonMessage = JsonMessage.fromJsonNode(jsonContent).valueOr: raise newException(JsonParsingError, $error) except JsonParsingError as exc: - return err(fmt"Error parsing json message: {exc.msg}") + return err("Error parsing json message: " & exc.msg) let msg = json_message_event.toWakuMessage(jsonMessage).valueOr: return err("Problem building the WakuMessage: " & $error) - (await ctx.myLib[].waku.node.wakuRelay.publish($pubsubTopic, msg)).isOkOr: + let msgHash = ( + await ctx.myLib[].waku.relayPublish( + PubsubTopic($pubSubTopic), msg, uint32(timeoutMs) + ) + ).valueOr: error "PUBLISH failed", error = error - return err($error) - - let msgHash = computeMessageHash($pubSubTopic, msg).to0xHex + return err(error) return ok(msgHash) proc waku_default_pubsub_topic( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.ffi.} = - # https://rfc.vac.dev/spec/36/#extern-char-waku_default_pubsub_topic - return ok(DefaultPubsubTopic) + let topic = (await ctx.myLib[].waku.defaultPubsubTopic()).valueOr: + return err(error) + return ok(string(topic)) proc waku_content_topic( ctx: ptr FFIContext[LogosDelivery], @@ -161,9 +146,13 @@ proc waku_content_topic( contentTopicName: cstring, encoding: cstring, ) {.ffi.} = - # https://rfc.vac.dev/spec/36/#extern-char-waku_content_topicchar-applicationname-unsigned-int-applicationversion-char-contenttopicname-char-encoding - - return ok(fmt"/{$appName}/{$appVersion}/{$contentTopicName}/{$encoding}") + let topic = ( + await ctx.myLib[].waku.buildContentTopic( + $appName, uint32(appVersion), $contentTopicName, $encoding + ) + ).valueOr: + return err(error) + return ok(string(topic)) proc waku_pubsub_topic( ctx: ptr FFIContext[LogosDelivery], @@ -171,5 +160,6 @@ proc waku_pubsub_topic( userData: pointer, topicName: cstring, ) {.ffi.} = - # https://rfc.vac.dev/spec/36/#extern-char-waku_pubsub_topicchar-name-char-encoding - return ok(fmt"/waku/2/{$topicName}") + let topic = (await ctx.myLib[].waku.buildPubsubTopic($topicName)).valueOr: + return err(error) + return ok(string(topic)) diff --git a/library/kernel_api/protocols/store_api.nim b/library/kernel_api/protocols/store_api.nim index c6356bf6a..75d43fee1 100644 --- a/library/kernel_api/protocols/store_api.nim +++ b/library/kernel_api/protocols/store_api.nim @@ -1,13 +1,10 @@ -import logos_delivery/waku/compat/option_valueor -import std/[json, sugar, strutils, options] -import chronos, chronicles, results, stew/byteutils, ffi +import std/[json, sugar, options] +import chronos, chronicles, results, ffi import - logos_delivery/waku/waku, + logos_delivery, library/utils, - logos_delivery/waku/waku_core/peers, logos_delivery/waku/waku_core/message/digest, logos_delivery/waku/waku_store/common, - logos_delivery/waku/waku_store/client, logos_delivery/waku/common/paging, library/declare_lib @@ -83,13 +80,10 @@ proc waku_store_query( let storeQueryRequest = ?fromJsonNode(jsonContentRes.get()) - let peer = peers.parsePeerInfo(($peerAddr).split(",")).valueOr: - return err("StoreRequest failed to parse peer addr: " & $error) - let queryResponse = ( - await ctx.myLib[].waku.node.wakuStoreClient.query(storeQueryRequest, peer) + await ctx.myLib[].waku.storeQuery(storeQueryRequest, $peerAddr, int(timeoutMs)) ).valueOr: - return err("StoreRequest failed store query: " & $error) + return err("StoreRequest failed store query: " & error) let res = $(%*(queryResponse.toHex())) return ok(res) ## returning the response in json format diff --git a/library/liblogosdelivery.h b/library/liblogosdelivery.h index 3965ef013..7b4bbd5ab 100644 --- a/library/liblogosdelivery.h +++ b/library/liblogosdelivery.h @@ -71,6 +71,36 @@ extern "C" void *userData, const char *messageJson); + // --- Reliable Channels API (stable surface) --- + + // Create a reliable channel. Returns the channel id. + int logosdelivery_channel_create(void *ctx, + FFICallBack callback, + void *userData, + const char *channelId, + const char *contentTopic, + const char *senderId); + + // Send a message on a reliable channel. + // messageJson: { "payload": "base64-encoded-payload", "ephemeral": false } + // Returns a request ID that can be used to track delivery. + int logosdelivery_channel_send(void *ctx, + FFICallBack callback, + void *userData, + const char *channelId, + const char *messageJson); + + // Close a reliable channel: stops its SDS loops; persisted state survives, so + // re-creating the channel restores it. + int logosdelivery_channel_close(void *ctx, + FFICallBack callback, + void *userData, + const char *channelId); + + // Channel lifecycle events are delivered through the event callback set via + // logosdelivery_set_event_callback: "onChannelMessageReceived" (payload + // base64-encoded), "onChannelMessageSent", "onChannelMessageError". + // Sets a callback that will be invoked whenever an event occurs. // It is crucial that the passed callback is fast, non-blocking and potentially thread-safe. void logosdelivery_set_event_callback(void *ctx, diff --git a/library/liblogosdelivery.nim b/library/liblogosdelivery.nim index 0ead080bc..755503e7c 100644 --- a/library/liblogosdelivery.nim +++ b/library/liblogosdelivery.nim @@ -31,7 +31,8 @@ include ./kernel_api/protocols/relay_api, ./kernel_api/protocols/store_api, ./kernel_api/protocols/lightpush_api, - ./kernel_api/protocols/filter_api + ./kernel_api/protocols/filter_api, + ./channels_api/channel_api ################################################################################ ### Exported procs (former libwaku API) diff --git a/library/logos_delivery_api/node_api.nim b/library/logos_delivery_api/node_api.nim index 354ab3a98..cfee2f5e2 100644 --- a/library/logos_delivery_api/node_api.nim +++ b/library/logos_delivery_api/node_api.nim @@ -1,11 +1,11 @@ import std/json import chronos, chronicles, results, ffi +import logos_delivery/waku/common/base64 import logos_delivery, logos_delivery/waku/node/waku_node, - logos_delivery/waku/events/message_events, logos_delivery/api/types, - logos_delivery/waku/events/[message_events, health_events], + logos_delivery/waku/api/events/health_events, tools/confutils/conf_from_json, ../declare_lib, ../json_event @@ -125,6 +125,40 @@ proc logosdelivery_start_node( chronicles.error "ConnectionStatusChange.listen failed", err = $error return err("ConnectionStatusChange.listen failed: " & $error) + let channelReceivedListener = ChannelMessageReceivedEvent.listen( + ctx.myLib[].waku.brokerCtx, + proc(event: ChannelMessageReceivedEvent) {.async: (raises: []).} = + callEventCallback(ctx, "onChannelMessageReceived"): + $( + %*{ + "eventType": "channel_message_received", + "channelId": string(event.channelId), + "senderId": $event.senderId, + "payload": string(base64.encode(event.payload)), + } + ), + ).valueOr: + chronicles.error "ChannelMessageReceivedEvent.listen failed", err = $error + return err("ChannelMessageReceivedEvent.listen failed: " & $error) + + let channelSentListener = ChannelMessageSentEvent.listen( + ctx.myLib[].waku.brokerCtx, + proc(event: ChannelMessageSentEvent) {.async: (raises: []).} = + callEventCallback(ctx, "onChannelMessageSent"): + $newJsonEvent("channel_message_sent", event), + ).valueOr: + chronicles.error "ChannelMessageSentEvent.listen failed", err = $error + return err("ChannelMessageSentEvent.listen failed: " & $error) + + let channelErrorListener = ChannelMessageErrorEvent.listen( + ctx.myLib[].waku.brokerCtx, + proc(event: ChannelMessageErrorEvent) {.async: (raises: []).} = + callEventCallback(ctx, "onChannelMessageError"): + $newJsonEvent("channel_message_error", event), + ).valueOr: + chronicles.error "ChannelMessageErrorEvent.listen failed", err = $error + return err("ChannelMessageErrorEvent.listen failed: " & $error) + (await ctx.myLib[].start()).isOkOr: let errMsg = $error chronicles.error "START_NODE failed", err = errMsg @@ -142,6 +176,9 @@ proc logosdelivery_stop_node( await MessagePropagatedEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) await MessageReceivedEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) await EventConnectionStatusChange.dropAllListeners(ctx.myLib[].waku.brokerCtx) + await ChannelMessageReceivedEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) + await ChannelMessageSentEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) + await ChannelMessageErrorEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) (await ctx.myLib[].stop()).isOkOr: let errMsg = $error diff --git a/logos_delivery/api/kernel_api.nim b/logos_delivery/api/kernel_api.nim new file mode 100644 index 000000000..35cf7251f --- /dev/null +++ b/logos_delivery/api/kernel_api.nim @@ -0,0 +1,203 @@ +import std/options +import chronos, results +import brokers/event_broker + +import logos_delivery/api/types as api_types +import logos_delivery/waku/waku_core/topics/pubsub_topic +import logos_delivery/waku/waku_core/message +import logos_delivery/waku/waku_store/common as store_types + +export event_broker +export api_types, pubsub_topic, store_types + +type IKernel* = ref object of RootObj + +EventBroker: + # Internal event emitted when a message arrives from the network via any protocol + type MessageSeenEvent* = object + topic*: PubsubTopic + message*: WakuMessage + +# --- topic construction --- +method buildContentTopic*( + self: IKernel, appName: string, appVersion: uint32, name: string, encoding: string +): Future[Result[ContentTopic, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.buildContentTopic not implemented") + +method buildPubsubTopic*( + self: IKernel, topicName: string +): Future[Result[PubsubTopic, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.buildPubsubTopic not implemented") + +method defaultPubsubTopic*( + self: IKernel +): Future[Result[PubsubTopic, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.defaultPubsubTopic not implemented") + +# --- relay --- +method relayPublish*( + self: IKernel, pubsubTopic: PubsubTopic, message: WakuMessage, timeoutMs: uint32 +): Future[Result[int, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.relayPublish not implemented") + +method relaySubscribe*( + self: IKernel, pubsubTopic: PubsubTopic +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.relaySubscribe not implemented") + +method relayUnsubscribe*( + self: IKernel, pubsubTopic: PubsubTopic +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.relayUnsubscribe not implemented") + +method relayAddProtectedShard*( + self: IKernel, clusterId: uint16, shardId: uint16, publicKey: string +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.relayAddProtectedShard not implemented") + +method relayConnectedPeers*( + self: IKernel, pubsubTopic: PubsubTopic +): Future[Result[seq[string], string]] {.async: (raises: []), base.} = + return err("Interface IKernel.relayConnectedPeers not implemented") + +method relayPeersInMesh*( + self: IKernel, pubsubTopic: PubsubTopic +): Future[Result[seq[string], string]] {.async: (raises: []), base.} = + return err("Interface IKernel.relayPeersInMesh not implemented") + +# --- filter --- +method filterSubscribe*( + self: IKernel, + pubsubTopic: Option[PubsubTopic], + contentTopics: seq[ContentTopic], + peer: string, +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.filterSubscribe not implemented") + +method filterUnsubscribe*( + self: IKernel, + pubsubTopic: Option[PubsubTopic], + contentTopics: seq[ContentTopic], + peer: string, +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.filterUnsubscribe not implemented") + +method filterUnsubscribeAll*( + self: IKernel, peer: string +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.filterUnsubscribeAll not implemented") + +# --- lightpush --- +method lightpushPublish*( + self: IKernel, pubsubTopic: PubsubTopic, message: WakuMessage, peer: string +): Future[Result[string, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.lightpushPublish not implemented") + +# --- store --- +method storeQuery*( + self: IKernel, request: StoreQueryRequest, peer: string, timeoutMs: int +): Future[Result[StoreQueryResponse, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.storeQuery not implemented") + +# --- peer management --- +method connect*( + self: IKernel, peers: seq[string], timeoutMs: uint32 +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.connect not implemented") + +method disconnectPeerById*( + self: IKernel, peerId: string +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.disconnectPeerById not implemented") + +method disconnectAllPeers*( + self: IKernel +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.disconnectAllPeers not implemented") + +method dialPeer*( + self: IKernel, peerAddr: string, protocol: string, timeoutMs: int +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.dialPeer not implemented") + +method dialPeerById*( + self: IKernel, peerId: string, protocol: string, timeoutMs: int +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.dialPeerById not implemented") + +method peerIdsFromPeerstore*( + self: IKernel +): Future[Result[seq[string], string]] {.async: (raises: []), base.} = + return err("Interface IKernel.peerIdsFromPeerstore not implemented") + +method connectedPeersInfo*( + self: IKernel +): Future[Result[seq[string], string]] {.async: (raises: []), base.} = + return err("Interface IKernel.connectedPeersInfo not implemented") + +method connectedPeers*( + self: IKernel +): Future[Result[seq[string], string]] {.async: (raises: []), base.} = + return err("Interface IKernel.connectedPeers not implemented") + +method peerIdsByProtocol*( + self: IKernel, protocol: string +): Future[Result[seq[string], string]] {.async: (raises: []), base.} = + return err("Interface IKernel.peerIdsByProtocol not implemented") + +# --- discovery --- +method dnsDiscovery*( + self: IKernel, enrTreeUrl: string, nameServer: string, timeoutMs: int +): Future[Result[seq[string], string]] {.async: (raises: []), base.} = + return err("Interface IKernel.dnsDiscovery not implemented") + +method discv5UpdateBootnodes*( + self: IKernel, bootnodes: seq[string] +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.discv5UpdateBootnodes not implemented") + +method startDiscv5*( + self: IKernel +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.startDiscv5 not implemented") + +method stopDiscv5*( + self: IKernel +): Future[Result[bool, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.stopDiscv5 not implemented") + +method peerExchangeRequest*( + self: IKernel, numPeers: uint64 +): Future[Result[int, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.peerExchangeRequest not implemented") + +# --- debug / info --- +method version*( + self: IKernel +): Future[Result[string, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.version not implemented") + +method listenAddresses*( + self: IKernel +): Future[Result[seq[string], string]] {.async: (raises: []), base.} = + return err("Interface IKernel.listenAddresses not implemented") + +method myEnr*( + self: IKernel +): Future[Result[string, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.myEnr not implemented") + +method myPeerId*( + self: IKernel +): Future[Result[string, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.myPeerId not implemented") + +method metrics*( + self: IKernel +): Future[Result[string, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.metrics not implemented") + +method pingPeer*( + self: IKernel, peerAddr: string, timeoutMs: int +): Future[Result[int64, string]] {.async: (raises: []), base.} = + return err("Interface IKernel.pingPeer not implemented") diff --git a/logos_delivery/api/logos_delivery_api.nim b/logos_delivery/api/logos_delivery_api.nim new file mode 100644 index 000000000..162159d8e --- /dev/null +++ b/logos_delivery/api/logos_delivery_api.nim @@ -0,0 +1,33 @@ +## `LogosDelivery` is the project entry point. It is a pure concentrator: it +## owns exactly one instance of each API layer +## +## Waku <- MessagingClient <- ReliableChannelManager +## +## and chains them together (each layer drives the one below it). Every layer +## keeps its own, separate public API — `LogosDelivery` only wires them up and +## drives the shared `new` / `start` / `stop` lifecycle. + +{.push raises: [].} + +import results, chronos +import brokers/event_broker +import types as api_types + +export api_types, event_broker + +type + ## Entry point. Holds one instance of each API layer. + ILogosDelivery* = ref object of RootObj + +EventBroker: + type EventConnectionStatusChange* = object + connectionStatus*: ConnectionStatus + +method start*(self: ILogosDelivery): Future[Result[void, string]] {.async, base.} = + return err("ILogosDelivery.start not implemented") + +method stop*(self: ILogosDelivery): Future[Result[void, string]] {.async, base.} = + return err("ILogosDelivery.stop not implemented") + +method isOnline*(self: ILogosDelivery): Future[Result[bool, string]] {.async, base.} = + return err("ILogosDelivery.isOnline not implemented") diff --git a/logos_delivery/api/messaging_client_api.nim b/logos_delivery/api/messaging_client_api.nim new file mode 100644 index 000000000..f29a5cb41 --- /dev/null +++ b/logos_delivery/api/messaging_client_api.nim @@ -0,0 +1,49 @@ +import chronos, results +import brokers/event_broker + +import logos_delivery/api/types as api_types +import logos_delivery/waku/waku_core/message + +export event_broker, api_types + +type IMessagingClient* = ref object of RootObj + +EventBroker: + # Event emitted when a message is sent to the network + type MessageSentEvent* = object + requestId*: RequestId + messageHash*: string + +EventBroker: + # Event emitted when a message send operation fails + type MessageErrorEvent* = object + requestId*: RequestId + messageHash*: string + error*: string + +EventBroker: + # Confirmation that a message has been correctly delivered to some neighbouring nodes. + type MessagePropagatedEvent* = object + requestId*: RequestId + messageHash*: string + +EventBroker: + # Event emitted when a message is received via Waku + type MessageReceivedEvent* = object + messageHash*: string + message*: WakuMessage + +method subscribe*( + self: IMessagingClient, contentTopic: ContentTopic +): Future[Result[void, string]] {.async: (raises: []), base.} = + return err("Interface IMessagingClient.subscribe not implemented") + +method unsubscribe*( + self: IMessagingClient, contentTopic: ContentTopic +): Result[void, string] {.base, raises: [].} = + return err("Interface IMessagingClient.unsubscribe not implemented") + +method send*( + self: IMessagingClient, envelope: MessageEnvelope +): Future[Result[RequestId, string]] {.async: (raises: []), base.} = + return err("Interface IMessagingClient.send not implemented") diff --git a/logos_delivery/api/reliable_channel_manager_api.nim b/logos_delivery/api/reliable_channel_manager_api.nim new file mode 100644 index 000000000..c5266972a --- /dev/null +++ b/logos_delivery/api/reliable_channel_manager_api.nim @@ -0,0 +1,70 @@ +import chronos, results + +import brokers/event_broker + +import logos_delivery/api/types as api_types +import logos_delivery/channels/types as channel_types + +# The channel layer re-uses the messaging-layer message events (the `requestId` +# is shared across layers), so it re-exports the messaging interface's event +# surface and only adds the channel-level events that have no lower-layer +# analogue (reassembled payload / senderId / channelId). +import logos_delivery/api/messaging_client_api + +export event_broker, api_types +export channel_types, messaging_client_api + +type + IReliableChannelManager* = ref object of RootObj + + SendHandler* = proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {. + async: (raises: [CatchableError]), gcsafe + .} + ## Egress dispatch boundary. Typically wraps `MessagingClient.send`; + ## tests inject a fake that records calls and returns canned + ## `RequestId`s so the send state machine can be exercised end-to-end + ## without a network. + +EventBroker: + type ChannelMessageReceivedEvent* = object + channelId*: ChannelId + senderId*: SdsParticipantID + payload*: seq[byte] + +EventBroker: + ## Emitted when every segment of a channel-level `send()` reached + ## `Confirmed`. Channel-level analogue of `MessageSentEvent`; the + ## `requestId` is the channel-layer parent returned by `send()`. + type ChannelMessageSentEvent* = object + channelId*: ChannelId + requestId*: RequestId + +EventBroker: + ## Emitted when a channel-level `send()` finalises with at least one + ## segment in `Failed`. Channel-level analogue of `MessageErrorEvent`. + type ChannelMessageErrorEvent* = object + channelId*: ChannelId + requestId*: RequestId + error*: string + +method createReliableChannel*( + self: IReliableChannelManager, + channelId: ChannelId, + contentTopic: ContentTopic, + senderId: SdsParticipantID, + sendHandler: SendHandler = nil, +): Result[ChannelId, string] {.base.} = + return err("Interface IReliableChannelManager.createReliableChannel not implemented") + +method closeChannel*( + self: IReliableChannelManager, channelId: ChannelId +): Future[Result[void, string]] {.async: (raises: []), base.} = + return err("Interface IReliableChannelManager.closeChannel not implemented") + +method send*( + self: IReliableChannelManager, + channelId: ChannelId, + appPayload: seq[byte], + ephemeral: bool = false, +): Future[Result[RequestId, string]] {.async: (raises: []), base.} = + return err("Interface IReliableChannelManager.send not implemented") diff --git a/logos_delivery/channels/api/channel_lifecycle.nim b/logos_delivery/channels/api/channel_lifecycle.nim new file mode 100644 index 000000000..b50ba1b63 --- /dev/null +++ b/logos_delivery/channels/api/channel_lifecycle.nim @@ -0,0 +1,89 @@ +## Reliable Channel layer API — channel lifecycle +## (createReliableChannel / closeChannel). +import std/[options, tables] +import results, chronos, chronicles + +import logos_delivery/api/types +import logos_delivery/channels/reliable_channel_manager +import logos_delivery/channels/reliable_channel +import logos_delivery/waku/persistency/sds_persistency + +# ReliableChannel, SendHandler, config and wire-version markers. +export reliable_channel + +const SdsJobId = "sds" + ## One persistency job shared by every channel's SDS state; rows are + ## keyed by channelId. + +proc sdsPersistence(): Option[Persistence] = + ## SDS backend from the Persistency singleton; memory-only fallback when + ## it is unavailable (e.g. unit tests). + let p = Persistency.instance().valueOr: + info "SDS persistence disabled, running memory-only", reason = $error + return none(Persistence) + let job = p.openJob(SdsJobId).valueOr: + warn "SDS persistence disabled, could not open persistency job", + jobId = SdsJobId, reason = $error + return none(Persistence) + return some(newSdsPersistence(job)) + +proc createReliableChannel*( + self: ReliableChannelManager, + channelId: ChannelId, + contentTopic: ContentTopic, + senderId: SdsParticipantID, + sendHandler: SendHandler = nil, +): Result[ChannelId, string] = + ## Spec entry point. The `sendHandler` and `rng` the channel needs are + ## sourced from the owning `ReliableChannelManager` rather than passed + ## per call. Encryption is wired up through the `Encrypt`/`Decrypt` + ## request brokers — the application installs its own providers + ## (or `setNoopEncryption()`) before traffic flows. + ## + ## `sendHandler` defaults to the manager's default (constructed at mount + ## from `MessagingClient.send`); tests pass a fake to bypass the network. + if self.channels.hasKey(channelId): + return err("channel already exists: " & channelId) + + let segConfig = SegmentationConfig( + segmentSizeBytes: DefaultSegmentSizeBytes, + enableReedSolomon: false, + persistence: nil, + ) + let sdsConfig = SdsConfig( + acknowledgementTimeoutMs: DefaultAcknowledgementTimeoutMs, + maxRetransmissions: DefaultMaxRetransmissions, + causalHistorySize: DefaultCausalHistorySize, + persistence: sdsPersistence(), + ) + let rateConfig = RateLimitConfig( + epochPeriodSec: DefaultEpochPeriodSec, messagesPerEpoch: DefaultMessagesPerEpoch + ) + + let effectiveSendHandler = if sendHandler.isNil(): self.sendHandler else: sendHandler + + let chn = ReliableChannel.new( + sendHandler = effectiveSendHandler, + channelId = channelId, + contentTopic = contentTopic, + senderId = senderId, + segConfig = segConfig, + sdsConfig = sdsConfig, + rateConfig = rateConfig, + brokerCtx = self.brokerCtx, + ) + + self.channels[channelId] = chn + return ok(channelId) + +proc closeChannel*( + self: ReliableChannelManager, channelId: ChannelId +): Future[Result[void, string]] {.async: (raises: []).} = + ## Stops the channel's SDS loops and releases the channel. Persisted SDS + ## state survives, so re-creating the channel restores it. + let chn = self.channels.getOrDefault(channelId) + if chn.isNil(): + return err("unknown channel: " & channelId) + self.channels.del(channelId) + await chn.stop() + return ok() diff --git a/logos_delivery/channels/api/send.nim b/logos_delivery/channels/api/send.nim new file mode 100644 index 000000000..23c41ac13 --- /dev/null +++ b/logos_delivery/channels/api/send.nim @@ -0,0 +1,21 @@ +## Reliable Channel layer API — channel send operation. +import std/tables +import results, chronos + +import logos_delivery/api/types +import logos_delivery/channels/reliable_channel_manager +import logos_delivery/channels/reliable_channel + +proc send*( + self: ReliableChannelManager, + channelId: ChannelId, + appPayload: seq[byte], + ephemeral: bool = false, +): Future[Result[RequestId, string]] {.async: (raises: []).} = + ## Spec-level entry point. Looks the channel up by id and delegates + ## to `ReliableChannel.send`, which exposes the visible pipeline + ## segmentation -> sds -> rate_limit_manager -> encryption. + let chn = self.channels.getOrDefault(channelId) + if chn.isNil(): + return err("unknown channel: " & channelId) + return await chn.send(appPayload, ephemeral) diff --git a/logos_delivery/channels/events.nim b/logos_delivery/channels/events.nim deleted file mode 100644 index 5f69095e4..000000000 --- a/logos_delivery/channels/events.nim +++ /dev/null @@ -1,39 +0,0 @@ -## Reliable Channel event types emitted to API consumers. -## -## Lifecycle events for individual segments (sent / propagated / errored) -## are the same as the network-level ones the MessagingClient already -## emits — `requestId` is shared across layers — so we just re-export -## `waku/events/message_events` and avoid declaring duplicates. -## -## Only the channel-level `MessageReceivedEvent` carries data that has -## no analogue in the lower layer (reassembled application payload, -## senderId, channelId), so it lives here. - -import logos_delivery/waku/events/message_events as waku_message_events -import brokers/event_broker - -import ./types as channel_types - -export waku_message_events, channel_types, event_broker - -EventBroker: - type ChannelMessageReceivedEvent* = object - channelId*: ChannelId - senderId*: SdsParticipantID - payload*: seq[byte] - -EventBroker: - ## Emitted when every segment of a channel-level `send()` reached - ## `Confirmed`. Channel-level analogue of `MessageSentEvent`; the - ## `requestId` is the channel-layer parent returned by `send()`. - type ChannelMessageSentEvent* = object - channelId*: ChannelId - requestId*: RequestId - -EventBroker: - ## Emitted when a channel-level `send()` finalises with at least one - ## segment in `Failed`. Channel-level analogue of `MessageErrorEvent`. - type ChannelMessageErrorEvent* = object - channelId*: ChannelId - requestId*: RequestId - error*: string diff --git a/logos_delivery/channels/reliable_channel.nim b/logos_delivery/channels/reliable_channel.nim index 307dc17a4..d06f74e36 100644 --- a/logos_delivery/channels/reliable_channel.nim +++ b/logos_delivery/channels/reliable_channel.nim @@ -22,18 +22,18 @@ import stew/byteutils import libp2p/crypto/crypto as libp2p_crypto import logos_delivery/api/types +import logos_delivery/api/reliable_channel_manager_api import logos_delivery/messaging/delivery_service/send_service import logos_delivery/waku/waku_core/topics -import ./events import ./segmentation/segmentation import ./scalable_data_sync/scalable_data_sync import ./rate_limit_manager/rate_limit_manager import ./encryption/encryption export - types, send_service, events, segmentation, scalable_data_sync, rate_limit_manager, - encryption + types, reliable_channel_manager_api, send_service, segmentation, scalable_data_sync, + rate_limit_manager, encryption const LipWireReliableChannelVersion* = "RELIABLE-CHANNEL-API/1" ## Wire-format spec marker for the Reliable Channel layer, as defined @@ -44,14 +44,6 @@ const LipWireReliableChannelVersion* = "RELIABLE-CHANNEL-API/1" ## on breaking on-the-wire changes; implementations pin one version. type - SendHandler* = proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {. - async: (raises: [CatchableError]), gcsafe - .} - ## Egress dispatch boundary. Typically wraps `MessagingClient.send`; - ## tests inject a fake that records calls and returns canned - ## `RequestId`s so the send state machine can be exercised end-to-end - ## without a network. - MessagePersistence {.pure.} = enum Persistent Ephemeral diff --git a/logos_delivery/channels/reliable_channel_manager.nim b/logos_delivery/channels/reliable_channel_manager.nim index 29feab0b9..d3f671c2b 100644 --- a/logos_delivery/channels/reliable_channel_manager.nim +++ b/logos_delivery/channels/reliable_channel_manager.nim @@ -13,35 +13,29 @@ import stew/byteutils import brokers/broker_context -import logos_delivery/waku/events/message_events as waku_message_events import logos_delivery/messaging/messaging_client +import logos_delivery/messaging/api/send import logos_delivery/api/types -import logos_delivery/waku/waku_core/topics -import logos_delivery/waku/persistency/sds_persistency +import logos_delivery/api/reliable_channel_manager_api import ./reliable_channel -import ./encryption/noop_encryption export reliable_channel -const SdsJobId = "sds" - ## One persistency job shared by every channel's SDS state; rows are - ## keyed by channelId. - type ReliableChannelManagerConf* = object ## Per-layer config object for the reliable ## channel API. Placeholder for now (segmentation / SDS / rate-limit defaults ## will move here in a follow-up PR); kept so each layer owns its own config. - ReliableChannelManager* = ref object - channels: Table[ChannelId, ReliableChannel] + ReliableChannelManager* = ref object of IReliableChannelManager + channels*: Table[ChannelId, ReliableChannel] ## read by `channels/api.nim` messagingClient: MessagingClient ## The channel layer chains onto messaging. - sendHandler: SendHandler + sendHandler*: SendHandler ## Default egress dispatch for channels created through this manager. ## Built in `new` as a closure over `MessagingClient.send` so the channel ## layer itself stays callable-only. - brokerCtx: BrokerContext + brokerCtx*: BrokerContext proc new*( T: type ReliableChannelManager, @@ -82,96 +76,6 @@ proc stop*(self: ReliableChannelManager) {.async.} = await chn.stop() self.channels.clear() -proc sdsPersistence(): Option[Persistence] = - ## SDS backend from the Persistency singleton; memory-only fallback when - ## it is unavailable (e.g. unit tests). - let p = Persistency.instance().valueOr: - info "SDS persistence disabled, running memory-only", reason = $error - return none(Persistence) - let job = p.openJob(SdsJobId).valueOr: - warn "SDS persistence disabled, could not open persistency job", - jobId = SdsJobId, reason = $error - return none(Persistence) - return some(newSdsPersistence(job)) - -proc createReliableChannel*( - self: ReliableChannelManager, - channelId: ChannelId, - contentTopic: ContentTopic, - senderId: SdsParticipantID, - sendHandler: SendHandler = nil, -): Result[ChannelId, string] = - ## Spec entry point. The `sendHandler` and `rng` the channel needs are - ## sourced from the owning `ReliableChannelManager` rather than passed - ## per call. Encryption is wired up through the `Encrypt`/`Decrypt` - ## request brokers — the application installs its own providers - ## (or `setNoopEncryption()`) before traffic flows. - ## - ## Segmentation, SDS and rate-limit configs will eventually be read - ## from the node's `NodeConfig`. Defaults for now. - ## - ## `sendHandler` defaults to the manager's default (constructed at mount - ## from `MessagingClient.send`); tests pass a fake to bypass the network. - if self.channels.hasKey(channelId): - return err("channel already exists: " & channelId) - - let segConfig = SegmentationConfig( - segmentSizeBytes: DefaultSegmentSizeBytes, - enableReedSolomon: false, - persistence: nil, - ) - let sdsConfig = SdsConfig( - acknowledgementTimeoutMs: DefaultAcknowledgementTimeoutMs, - maxRetransmissions: DefaultMaxRetransmissions, - causalHistorySize: DefaultCausalHistorySize, - persistence: sdsPersistence(), - ) - let rateConfig = RateLimitConfig( - epochPeriodSec: DefaultEpochPeriodSec, messagesPerEpoch: DefaultMessagesPerEpoch - ) - - let effectiveSendHandler = if sendHandler.isNil(): self.sendHandler else: sendHandler - - let chn = ReliableChannel.new( - sendHandler = effectiveSendHandler, - channelId = channelId, - contentTopic = contentTopic, - senderId = senderId, - segConfig = segConfig, - sdsConfig = sdsConfig, - rateConfig = rateConfig, - brokerCtx = self.brokerCtx, - ) - - self.channels[channelId] = chn - return ok(channelId) - -proc closeChannel*( - self: ReliableChannelManager, channelId: ChannelId -): Future[Result[void, string]] {.async: (raises: []).} = - ## Stops the channel's SDS loops and releases the channel. Persisted SDS - ## state survives, so re-creating the channel restores it. - let chn = self.channels.getOrDefault(channelId) - if chn.isNil(): - return err("unknown channel: " & channelId) - self.channels.del(channelId) - await chn.stop() - return ok() - -proc send*( - self: ReliableChannelManager, - channelId: ChannelId, - appPayload: seq[byte], - ephemeral: bool = false, -): Future[Result[RequestId, string]] {.async: (raises: []).} = - ## Spec-level entry point. Looks the channel up by id and delegates - ## to `ReliableChannel.send`, which exposes the visible pipeline - ## segmentation -> sds -> rate_limit_manager -> encryption. - let chn = self.channels.getOrDefault(channelId) - if chn.isNil(): - return err("unknown channel: " & channelId) - return await chn.send(appPayload, ephemeral) - ## Inbound messages are not handed to the manager by direct call. Each ## `ReliableChannel` installs its own `MessageReceivedEvent` listener ## in `ReliableChannel.new`, filters by spec marker and `contentTopic`, diff --git a/logos_delivery/logos_delivery.nim b/logos_delivery/logos_delivery.nim index f61202b9a..435bb968d 100644 --- a/logos_delivery/logos_delivery.nim +++ b/logos_delivery/logos_delivery.nim @@ -11,12 +11,45 @@ import results, chronos, chronicles +import logos_delivery/api/logos_delivery_api +export logos_delivery_api + +# Each layer has a core module (type + new/start/stop) and an api/ folder whose +# modules each implement a differentiated set of operations, plus an events +# surface. The concentrator re-exports them so library consumers get the full +# surface from `import logos_delivery`. (The per-layer `events` modules share a +# stem, so they are imported under aliases.) + +# Waku layer import logos_delivery/waku/waku export waku +import + logos_delivery/waku/api/[ + topics, relay, filter, lightpush, store, peer_manager, discovery, debug, health, + ping, + ] +export + topics, relay, filter, lightpush, store, peer_manager, discovery, debug, health, ping +# `MessageSeenEvent` is surfaced via `export waku` (Kernel interface); the +# remaining waku health events live here. +import logos_delivery/waku/api/events/health_events +export health_events + +# Messaging layer import logos_delivery/messaging/messaging_client export messaging_client +import logos_delivery/messaging/api/[subscription, send] +export subscription, send +# Message* events are surfaced via `export messaging_client` (messaging interface). + +# Reliable Channel layer import logos_delivery/channels/reliable_channel_manager export reliable_channel_manager +import logos_delivery/channels/api/channel_lifecycle +export channel_lifecycle +import logos_delivery/channels/api/send as channel_send +export channel_send +# ChannelMessage* events are surfaced via `export reliable_channel_manager`. import logos_delivery/waku/factory/waku_conf import logos_delivery/waku/factory/app_callbacks @@ -35,7 +68,8 @@ type messaging*: MessagingClientConf reliableChannel*: ReliableChannelManagerConf - LogosDelivery* = ref object ## Entry point. Holds one instance of each API layer. + LogosDelivery* = ref object of ILogosDelivery + ## Entry point. Holds one instance of each API layer. waku*: Waku messagingClient*: MessagingClient reliableChannelManager*: ReliableChannelManager @@ -79,7 +113,7 @@ proc new*( ) ) -proc start*(self: LogosDelivery): Future[Result[void, string]] {.async.} = +method start*(self: LogosDelivery): Future[Result[void, string]] {.async.} = ## Starts each layer bottom-up: transport first, then messaging, then channels. if self.waku.isNil(): return err("Waku node is not initialized") @@ -99,7 +133,7 @@ proc start*(self: LogosDelivery): Future[Result[void, string]] {.async.} = return ok() -proc stop*(self: LogosDelivery): Future[Result[void, string]] {.async.} = +method stop*(self: LogosDelivery): Future[Result[void, string]] {.async.} = ## Stops in reverse order so higher layers drain before their dependencies. await self.reliableChannelManager.stop() await self.messagingClient.stop() @@ -109,7 +143,7 @@ proc stop*(self: LogosDelivery): Future[Result[void, string]] {.async.} = return ok() -proc isOnline*(self: LogosDelivery): Future[Result[bool, string]] {.async.} = +method isOnline*(self: LogosDelivery): Future[Result[bool, string]] {.async.} = if self.waku.isNil(): return err("Waku node is not initialized") return ok(self.waku.healthMonitor.onlineMonitor.amIOnline()) diff --git a/logos_delivery/messaging/api/send.nim b/logos_delivery/messaging/api/send.nim new file mode 100644 index 000000000..cc6312471 --- /dev/null +++ b/logos_delivery/messaging/api/send.nim @@ -0,0 +1,34 @@ +## Messaging layer API — send operation. +import results, chronos, chronicles + +import logos_delivery/api/types +import logos_delivery/messaging/messaging_client +import logos_delivery/waku/node/[waku_node, subscription_manager] +import logos_delivery/messaging/delivery_service/send_service +import logos_delivery/messaging/delivery_service/send_service/delivery_task + +proc send*( + self: MessagingClient, envelope: MessageEnvelope +): Future[Result[RequestId, string]] {.async.} = + ## High-level messaging API send. Auto-subscribes to the content topic + ## (so the local node sees its own gossipsub broadcast), builds a + ## `DeliveryTask`, and hands it to the send service. Returns the request + ## id the caller can correlate with `MessageSentEvent` / `MessageErrorEvent`. + ?self.checkApiAvailability() + + let isSubbed = + self.node.subscriptionManager.isSubscribed(envelope.contentTopic).valueOr(false) + if not isSubbed: + info "Auto-subscribing to topic on send", contentTopic = envelope.contentTopic + self.node.subscriptionManager.subscribe(envelope.contentTopic).isOkOr: + warn "Failed to auto-subscribe", error = error + return err("Failed to auto-subscribe before sending: " & error) + + let requestId = RequestId.new(self.node.rng) + + let deliveryTask = DeliveryTask.new(requestId, envelope, self.node.brokerCtx).valueOr: + return err("MessagingClient.send: Failed to create delivery task: " & error) + + asyncSpawn self.sendService.send(deliveryTask) + + return ok(requestId) diff --git a/logos_delivery/messaging/api/subscription.nim b/logos_delivery/messaging/api/subscription.nim new file mode 100644 index 000000000..35b8c7e53 --- /dev/null +++ b/logos_delivery/messaging/api/subscription.nim @@ -0,0 +1,18 @@ +## Messaging layer API — subscription operations. +import results, chronos + +import logos_delivery/api/types +import logos_delivery/messaging/messaging_client +import logos_delivery/waku/node/[waku_node, subscription_manager] + +proc subscribe*( + self: MessagingClient, contentTopic: ContentTopic +): Future[Result[void, string]] {.async.} = + ?self.checkApiAvailability() + return self.node.subscriptionManager.subscribe(contentTopic) + +proc unsubscribe*( + self: MessagingClient, contentTopic: ContentTopic +): Result[void, string] = + ?self.checkApiAvailability() + return self.node.subscriptionManager.unsubscribe(contentTopic) diff --git a/logos_delivery/messaging/delivery_service/recv_service/recv_service.nim b/logos_delivery/messaging/delivery_service/recv_service/recv_service.nim index 90bdb0839..25b35222d 100644 --- a/logos_delivery/messaging/delivery_service/recv_service/recv_service.nim +++ b/logos_delivery/messaging/delivery_service/recv_service/recv_service.nim @@ -13,11 +13,13 @@ import waku_store/client, waku_store/common, waku_filter_v2/client, - events/message_events, - events/health_events, waku_node, node/subscription_manager, ] +import + logos_delivery/api/kernel_api, # MessageSeenEvent + logos_delivery/api/messaging_client_api, # MessageReceivedEvent + logos_delivery/api/logos_delivery_api # EventConnectionStatusChange const MaxMessageLife = chronos.minutes(7) ## Max time we will keep track of rx messages diff --git a/logos_delivery/messaging/delivery_service/send_service/send_service.nim b/logos_delivery/messaging/delivery_service/send_service/send_service.nim index 67f8de95f..f5d1f302b 100644 --- a/logos_delivery/messaging/delivery_service/send_service/send_service.nim +++ b/logos_delivery/messaging/delivery_service/send_service/send_service.nim @@ -18,8 +18,8 @@ import rln/rln, waku_lightpush/client, waku_lightpush/callbacks, - events/message_events, ] +import logos_delivery/api/messaging_client_api logScope: topics = "send service" diff --git a/logos_delivery/messaging/messaging_client.nim b/logos_delivery/messaging/messaging_client.nim index 96cd13eb1..69c6c520b 100644 --- a/logos_delivery/messaging/messaging_client.nim +++ b/logos_delivery/messaging/messaging_client.nim @@ -1,10 +1,14 @@ +## Messaging layer core: the `MessagingClient` type plus its construction and +## lifecycle. The public operations (subscribe / unsubscribe / send) live in +## `messaging/api.nim`. import results, chronos -import chronicles import - logos_delivery/api/types, - logos_delivery/waku/node/[waku_node, subscription_manager], - logos_delivery/messaging/delivery_service/[recv_service, send_service], - logos_delivery/messaging/delivery_service/send_service/delivery_task + logos_delivery/api/messaging_client_api, + logos_delivery/waku/node/waku_node, + logos_delivery/messaging/delivery_service/[recv_service, send_service] + +# Surfaces the messaging API interface (and its Message* events) to consumers. +export messaging_client_api type MessagingClientConf* = object @@ -13,8 +17,8 @@ type ## follow-up PR. Today it only carries the p2p reliability toggle. useP2PReliability*: bool - MessagingClient* = ref object - node: WakuNode + MessagingClient* = ref object of IMessagingClient + node*: WakuNode ## Waku core driven by this layer; read by `messaging/api.nim`. sendService*: SendService recvService*: RecvService started: bool @@ -43,46 +47,9 @@ proc stop*(self: MessagingClient) {.async.} = await self.recvService.stopRecvService() self.started = false -proc checkApiAvailability(self: MessagingClient): Result[void, string] = +proc checkApiAvailability*(self: MessagingClient): Result[void, string] = + ## Shared guard for the api operation module. if self.isNil(): return err("MessagingClient is not initialized") return ok() - -proc subscribe*( - self: MessagingClient, contentTopic: ContentTopic -): Future[Result[void, string]] {.async.} = - ?checkApiAvailability(self) - - return self.node.subscriptionManager.subscribe(contentTopic) - -proc unsubscribe*( - self: MessagingClient, contentTopic: ContentTopic -): Result[void, string] = - ?checkApiAvailability(self) - - return self.node.subscriptionManager.unsubscribe(contentTopic) - -proc send*( - self: MessagingClient, envelope: MessageEnvelope -): Future[Result[RequestId, string]] {.async.} = - ## High-level messaging API send. Auto-subscribes to the content topic - ## (so the local node sees its own gossipsub broadcast), builds a - ## `DeliveryTask`, and hands it to the send service. Returns the request - ## id the caller can correlate with `MessageSentEvent` / `MessageErrorEvent`. - let isSubbed = - self.node.subscriptionManager.isSubscribed(envelope.contentTopic).valueOr(false) - if not isSubbed: - info "Auto-subscribing to topic on send", contentTopic = envelope.contentTopic - self.node.subscriptionManager.subscribe(envelope.contentTopic).isOkOr: - warn "Failed to auto-subscribe", error = error - return err("Failed to auto-subscribe before sending: " & error) - - let requestId = RequestId.new(self.node.rng) - - let deliveryTask = DeliveryTask.new(requestId, envelope, self.node.brokerCtx).valueOr: - return err("MessagingClient.send: Failed to create delivery task: " & error) - - asyncSpawn self.sendService.send(deliveryTask) - - return ok(requestId) diff --git a/logos_delivery/waku/api/debug.nim b/logos_delivery/waku/api/debug.nim new file mode 100644 index 000000000..10582316b --- /dev/null +++ b/logos_delivery/waku/api/debug.nim @@ -0,0 +1,36 @@ +## Waku layer API — debug / info operations. +{.push raises: [].} + +import results, chronos, chronicles, metrics +import eth/p2p/discoveryv5/enr + +import logos_delivery/waku/waku +import logos_delivery/waku/[waku_core, node/waku_node] + +proc version*(self: Waku): Future[Result[string, string]] {.async.} = + return ok(WakuNodeVersionString) + +proc listenAddresses*(self: Waku): Future[Result[seq[string], string]] {.async.} = + try: + return ok(self.node.info().listenAddresses) + except CatchableError as e: + return err(e.msg) + +proc myEnr*(self: Waku): Future[Result[string, string]] {.async.} = + try: + return ok(self.node.enr.toURI()) + except CatchableError as e: + return err(e.msg) + +proc myPeerId*(self: Waku): Future[Result[string, string]] {.async.} = + try: + return ok($self.node.peerId()) + except CatchableError as e: + return err(e.msg) + +proc metrics*(self: Waku): Future[Result[string, string]] {.async.} = + {.gcsafe.}: + try: + return ok(defaultRegistry.toText()) + except CatchableError as e: + return err(e.msg) diff --git a/logos_delivery/waku/api/discovery.nim b/logos_delivery/waku/api/discovery.nim new file mode 100644 index 000000000..fefeb3d5d --- /dev/null +++ b/logos_delivery/waku/api/discovery.nim @@ -0,0 +1,76 @@ +## Waku layer API — discovery operations (DNS, discv5, peer exchange). +{.push raises: [].} + +import std/[net, sequtils] +import results, chronos, chronicles + +import logos_delivery/waku/waku +import + logos_delivery/waku/[ + waku_core, + node/waku_node, + node/waku_node/peer_exchange, + discovery/waku_dnsdisc, + discovery/waku_discv5, + ] + +proc dnsDiscovery*( + self: Waku, enrTreeUrl: string, nameServer: string, timeoutMs: int +): Future[Result[seq[string], string]] {.async.} = + try: + let dnsNameServers = @[parseIpAddress(nameServer)] + let discoveredPeers = ( + await retrieveDynamicBootstrapNodes(enrTreeUrl, dnsNameServers) + ).valueOr: + return err("failed discovering peers from DNS: " & $error) + + var multiAddresses = newSeq[string]() + for discPeer in discoveredPeers: + for address in discPeer.addrs: + multiAddresses.add($address & "/p2p/" & $discPeer) + + return ok(multiAddresses) + except CatchableError as e: + return err(e.msg) + +proc discv5UpdateBootnodes*( + self: Waku, bootnodes: string +): Future[Result[bool, string]] {.async.} = + ## `bootnodes` is a JSON array of ENRs, e.g. `["enr:...", "enr:..."]`. + try: + if self.wakuDiscv5.isNil(): + return err("discv5 not started") + self.wakuDiscv5.updateBootstrapRecords(bootnodes).isOkOr: + return err("error in discv5UpdateBootnodes: " & $error) + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc startDiscv5*(self: Waku): Future[Result[bool, string]] {.async.} = + try: + if self.wakuDiscv5.isNil(): + return err("discv5 not started") + (await self.wakuDiscv5.start()).isOkOr: + return err("error starting discv5: " & $error) + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc stopDiscv5*(self: Waku): Future[Result[bool, string]] {.async.} = + try: + if self.wakuDiscv5.isNil(): + return err("discv5 not started") + await self.wakuDiscv5.stop() + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc peerExchangeRequest*( + self: Waku, numPeers: uint64 +): Future[Result[int, string]] {.async.} = + try: + let numPeersRecv = (await self.node.fetchPeerExchangePeers(numPeers)).valueOr: + return err("failed peer exchange: " & $error) + return ok(numPeersRecv) + except CatchableError as e: + return err(e.msg) diff --git a/logos_delivery/waku/events/discovery_events.nim b/logos_delivery/waku/api/events/discovery_events.nim similarity index 100% rename from logos_delivery/waku/events/discovery_events.nim rename to logos_delivery/waku/api/events/discovery_events.nim diff --git a/logos_delivery/waku/api/events/events.nim b/logos_delivery/waku/api/events/events.nim new file mode 100644 index 000000000..e9f175e65 --- /dev/null +++ b/logos_delivery/waku/api/events/events.nim @@ -0,0 +1,3 @@ +import ./[filter_subscribe_events, health_events, peer_events, discovery_events] + +export filter_subscribe_events, health_events, peer_events, discovery_events diff --git a/logos_delivery/waku/events/delivery_events.nim b/logos_delivery/waku/api/events/filter_subscribe_events.nim similarity index 100% rename from logos_delivery/waku/events/delivery_events.nim rename to logos_delivery/waku/api/events/filter_subscribe_events.nim diff --git a/logos_delivery/waku/events/health_events.nim b/logos_delivery/waku/api/events/health_events.nim similarity index 82% rename from logos_delivery/waku/events/health_events.nim rename to logos_delivery/waku/api/events/health_events.nim index d19de776b..fbf00debb 100644 --- a/logos_delivery/waku/events/health_events.nim +++ b/logos_delivery/waku/api/events/health_events.nim @@ -6,10 +6,8 @@ import logos_delivery/waku/waku_core/topics export protocol_health, topic_health -# Notify health changes to node connectivity -EventBroker: - type EventConnectionStatusChange* = object - connectionStatus*: ConnectionStatus +# Note: `EventConnectionStatusChange` lives in `logos_delivery/api/logos_delivery_api` +# (the top-level orchestrator interface owns the node-connectivity event). # Notify health changes to a subscribed topic # TODO: emit content topic health change events when subscribe/unsubscribe diff --git a/logos_delivery/waku/events/peer_events.nim b/logos_delivery/waku/api/events/peer_events.nim similarity index 100% rename from logos_delivery/waku/events/peer_events.nim rename to logos_delivery/waku/api/events/peer_events.nim diff --git a/logos_delivery/waku/api/filter.nim b/logos_delivery/waku/api/filter.nim new file mode 100644 index 000000000..06c78213f --- /dev/null +++ b/logos_delivery/waku/api/filter.nim @@ -0,0 +1,88 @@ +## Waku layer API — filter (light client) operations. +import logos_delivery/waku/compat/option_valueor +{.push raises: [].} + +import std/options +import results, chronos, chronicles + +import logos_delivery/waku/waku +import + logos_delivery/waku/[ + waku_core, + waku_core/subscription/push_handler, + node/waku_node, + node/waku_node/filter, + node/peer_manager, + waku_filter_v2/client, + waku_filter_v2/common, + ] + +const FilterOpTimeout = 5.seconds + +proc filterSubscribe*( + self: Waku, + pubsubTopic: PubsubTopic, + contentTopics: seq[ContentTopic], + pushHandler: FilterPushHandler, +): Future[Result[bool, string]] {.async.} = + ## Registers `pushHandler` for incoming filtered messages, selects a filter + ## service peer, and subscribes. + try: + if self.node.wakuFilterClient.isNil(): + return err("wakuFilterClient is not mounted") + + self.node.wakuFilterClient.registerPushHandler(pushHandler) + + let peer = self.node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: + return err("could not find peer with WakuFilterSubscribeCodec when subscribing") + + let subFut = self.node.filterSubscribe(some(pubsubTopic), contentTopics, peer) + if not await subFut.withTimeout(FilterOpTimeout): + return err("filter subscription timed out") + subFut.read().isOkOr: + return err($error) + + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc filterUnsubscribe*( + self: Waku, pubsubTopic: PubsubTopic, contentTopics: seq[ContentTopic] +): Future[Result[bool, string]] {.async.} = + ## Selects a filter service peer and unsubscribes the given content topics. + try: + if self.node.wakuFilterClient.isNil(): + return err("wakuFilterClient is not mounted") + + let peer = self.node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: + return err("could not find peer with WakuFilterSubscribeCodec when unsubscribing") + + let unsubFut = self.node.filterUnsubscribe(some(pubsubTopic), contentTopics, peer) + if not await unsubFut.withTimeout(FilterOpTimeout): + return err("filter un-subscription timed out") + unsubFut.read().isOkOr: + return err($error) + + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc filterUnsubscribeAll*(self: Waku): Future[Result[bool, string]] {.async.} = + ## Selects a filter service peer and unsubscribes from everything. + try: + if self.node.wakuFilterClient.isNil(): + return err("wakuFilterClient is not mounted") + + let peer = self.node.peerManager.selectPeer(WakuFilterSubscribeCodec).valueOr: + return + err("could not find peer with WakuFilterSubscribeCodec when unsubscribing all") + + let unsubFut = self.node.filterUnsubscribeAll(peer) + if not await unsubFut.withTimeout(FilterOpTimeout): + return err("filter un-subscription all timed out") + unsubFut.read().isOkOr: + return err($error) + + return ok(true) + except CatchableError as e: + return err(e.msg) diff --git a/logos_delivery/waku/api/health.nim b/logos_delivery/waku/api/health.nim new file mode 100644 index 000000000..7faeebe4d --- /dev/null +++ b/logos_delivery/waku/api/health.nim @@ -0,0 +1,13 @@ +## Waku layer API — health / connectivity. +{.push raises: [].} + +import results, chronos, chronicles + +import logos_delivery/waku/waku +import logos_delivery/waku/[node/health_monitor, node/health_monitor/online_monitor] + +proc isOnline*(self: Waku): Future[Result[bool, string]] {.async.} = + try: + return ok(self.healthMonitor.onlineMonitor.amIOnline()) + except CatchableError as e: + return err(e.msg) diff --git a/logos_delivery/waku/api/lightpush.nim b/logos_delivery/waku/api/lightpush.nim new file mode 100644 index 000000000..6208a2643 --- /dev/null +++ b/logos_delivery/waku/api/lightpush.nim @@ -0,0 +1,37 @@ +## Waku layer API — lightpush (light client publish) operations. +import logos_delivery/waku/compat/option_valueor +{.push raises: [].} + +import results, chronos, chronicles + +import logos_delivery/waku/waku +import + logos_delivery/waku/[ + waku_core, + waku_core/codecs, + node/waku_node, + node/peer_manager, + waku_lightpush_legacy/client, + ] + +proc lightpushPublish*( + self: Waku, pubsubTopic: PubsubTopic, message: WakuMessage +): Future[Result[string, string]] {.async.} = + ## Selects a lightpush service peer and publishes; returns the message hash. + try: + if self.node.wakuLegacyLightpushClient.isNil(): + return err("wakuLegacyLightpushClient is not mounted") + + let remotePeer = self.node.peerManager.selectPeer(WakuLightPushCodec).valueOr: + return err("failed to lightpublish message, no suitable remote peers") + + let msgHashHex = ( + await self.node.wakuLegacyLightpushClient.publish( + pubsubTopic, message, remotePeer + ) + ).valueOr: + return err($error) + + return ok(msgHashHex) + except CatchableError as e: + return err(e.msg) diff --git a/logos_delivery/waku/api/peer_manager.nim b/logos_delivery/waku/api/peer_manager.nim new file mode 100644 index 000000000..26de46594 --- /dev/null +++ b/logos_delivery/waku/api/peer_manager.nim @@ -0,0 +1,112 @@ +## Waku layer API — peer management operations. +{.push raises: [].} + +import std/[options, sequtils, strutils] +import results, chronos, chronicles +import libp2p/[peerid, peerstore] + +import logos_delivery/waku/waku +import logos_delivery/waku/[waku_core, node/waku_node, node/peer_manager] + +type PeerConnInfo* = object ## structured connected-peer info for the api boundary + peerId*: string + protocols*: seq[string] + addresses*: seq[string] + +proc connect*( + self: Waku, peers: seq[string], timeoutMs: uint32 +): Future[Result[bool, string]] {.async.} = + try: + await self.node.connectToNodes(peers.mapIt(strip(it)), source = "static") + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc disconnectPeerById*( + self: Waku, peerId: string +): Future[Result[bool, string]] {.async.} = + try: + let pId = PeerId.init(peerId).valueOr: + return err($error) + await self.node.peerManager.disconnectNode(pId) + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc disconnectAllPeers*(self: Waku): Future[Result[bool, string]] {.async.} = + try: + await self.node.peerManager.disconnectAllPeers() + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc dialPeer*( + self: Waku, peerAddr: string, protocol: string, timeoutMs: int +): Future[Result[bool, string]] {.async.} = + try: + let remotePeerInfo = parsePeerInfo(peerAddr).valueOr: + return err($error) + let conn = await self.node.peerManager.dialPeer(remotePeerInfo, protocol) + if conn.isNone(): + return err("failed dialing peer") + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc dialPeerById*( + self: Waku, peerId: string, protocol: string, timeoutMs: int +): Future[Result[bool, string]] {.async.} = + try: + let pId = PeerId.init(peerId).valueOr: + return err($error) + let conn = await self.node.peerManager.dialPeer(pId, protocol) + if conn.isNone(): + return err("failed dialing peer") + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc peerIdsFromPeerstore*(self: Waku): Future[Result[seq[string], string]] {.async.} = + try: + return ok(self.node.peerManager.switch.peerStore.peers().mapIt($it.peerId)) + except CatchableError as e: + return err(e.msg) + +proc connectedPeersInfo*( + self: Waku +): Future[Result[seq[PeerConnInfo], string]] {.async.} = + ## Structured info (protocols, addresses) for every connected peer. + try: + var infos: seq[PeerConnInfo] + for peer in self.node.peerManager.switch.peerStore.peers(): + if peer.connectedness == Connected: + infos.add( + PeerConnInfo( + peerId: $peer.peerId, + protocols: peer.protocols, + addresses: peer.addrs.mapIt($it), + ) + ) + return ok(infos) + except CatchableError as e: + return err(e.msg) + +proc connectedPeers*(self: Waku): Future[Result[seq[string], string]] {.async.} = + try: + let (inPeerIds, outPeerIds) = self.node.peerManager.connectedPeers() + return ok(concat(inPeerIds, outPeerIds).mapIt($it)) + except CatchableError as e: + return err(e.msg) + +proc peerIdsByProtocol*( + self: Waku, protocol: string +): Future[Result[seq[string], string]] {.async.} = + try: + return ok( + self.node.peerManager.switch.peerStore + .peers(protocol) + .filterIt(it.connectedness == Connected) + .mapIt($it.peerId) + ) + except CatchableError as e: + return err(e.msg) diff --git a/logos_delivery/waku/api/ping.nim b/logos_delivery/waku/api/ping.nim new file mode 100644 index 000000000..9cf2dfc33 --- /dev/null +++ b/logos_delivery/waku/api/ping.nim @@ -0,0 +1,45 @@ +## Waku layer API — ping operation. +{.push raises: [].} + +import results, chronos, chronicles +import libp2p/protocols/ping +import libp2p/switch + +import logos_delivery/waku/waku +import logos_delivery/waku/[waku_core, node/waku_node, node/waku_node/ping] + +proc pingPeer*( + self: Waku, peerAddr: string, timeoutMs: int +): Future[Result[int64, string]] {.async.} = + ## Pings the peer; `timeoutMs <= 0` means no timeout. Returns RTT in nanos. + try: + let peerInfo = parsePeerInfo(peerAddr).valueOr: + return err("pingPeer failed to parse peer addr: " & $error) + + proc doPing(): Future[Result[Duration, string]] {.async.} = + try: + let conn = + await self.node.switch.dial(peerInfo.peerId, peerInfo.addrs, PingCodec) + defer: + await conn.close() + let rtt = await self.node.libp2pPing.ping(conn) + if rtt == 0.nanos: + return err("could not ping peer: rtt-0") + return ok(rtt) + except CatchableError as e: + return err("could not ping peer: " & e.msg) + + let pingFut = doPing() + let rtt: Duration = + if timeoutMs <= 0: + (await pingFut).valueOr: + return err(error) + else: + if not await pingFut.withTimeout(chronos.milliseconds(timeoutMs)): + return err("ping timed out") + pingFut.read().valueOr: + return err(error) + + return ok(rtt.nanos) + except CatchableError as e: + return err(e.msg) diff --git a/logos_delivery/waku/api/relay.nim b/logos_delivery/waku/api/relay.nim new file mode 100644 index 000000000..930221300 --- /dev/null +++ b/logos_delivery/waku/api/relay.nim @@ -0,0 +1,132 @@ +## Waku layer API — relay (gossipsub) operations. +{.push raises: [].} + +import std/sequtils +import results, chronos, chronicles, secp256k1, stew/byteutils + +import logos_delivery/waku/waku +import + logos_delivery/waku/[ + waku_core, + node/waku_node, + node/waku_node/relay, + node/subscription_manager, + waku_relay/protocol, + factory/waku_conf, + factory/validator_signed, + ] + +proc relayPublish*( + self: Waku, pubsubTopic: PubsubTopic, message: WakuMessage, timeoutMs: uint32 +): Future[Result[string, string]] {.async.} = + ## Publishes `message` and returns its message hash (0x-hex). + try: + if self.node.wakuRelay.isNil(): + return err("relayPublish: WakuRelay not mounted") + + (await self.node.wakuRelay.publish(pubsubTopic, message)).isOkOr: + return err($error) + + return ok(computeMessageHash(pubsubTopic, message).to0xHex) + except CatchableError as e: + return err(e.msg) + +proc relaySubscribe*( + self: Waku, + pubsubTopic: PubsubTopic, + handler: WakuRelayHandler = WakuRelayHandler(nil), +): Future[Result[bool, string]] {.async.} = + ## Subscribes to `pubsubTopic`. `handler` (optional) is invoked per message; + ## pass nil to subscribe without a message callback. + try: + if self.node.wakuRelay.isNil(): + return err("relaySubscribe: WakuRelay not mounted") + + self.node.subscribe((kind: SubscriptionKind.PubsubSub, topic: pubsubTopic), handler).isOkOr: + return err($error) + + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc relayUnsubscribe*( + self: Waku, pubsubTopic: PubsubTopic +): Future[Result[bool, string]] {.async.} = + try: + if self.node.wakuRelay.isNil(): + return err("relayUnsubscribe: WakuRelay not mounted") + + self.node.unsubscribe((kind: SubscriptionKind.PubsubSub, topic: pubsubTopic)).isOkOr: + return err($error) + + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc relayAddProtectedShard*( + self: Waku, clusterId: uint16, shardId: uint16, publicKey: string +): Future[Result[bool, string]] {.async.} = + try: + if self.node.wakuRelay.isNil(): + return err("relayAddProtectedShard: WakuRelay not mounted") + + let pubKey = SkPublicKey.fromHex(publicKey).valueOr: + return err("relayAddProtectedShard: invalid public key: " & $error) + + let protectedShard = ProtectedShard(shard: shardId, key: pubKey) + self.node.wakuRelay.addSignedShardsValidator(@[protectedShard], clusterId) + return ok(true) + except CatchableError as e: + return err(e.msg) + +proc relayConnectedPeers*( + self: Waku, pubsubTopic: PubsubTopic +): Future[Result[seq[string], string]] {.async.} = + try: + if self.node.wakuRelay.isNil(): + return err("relayConnectedPeers: WakuRelay not mounted") + + let connPeers = self.node.wakuRelay.getConnectedPeers(pubsubTopic).valueOr: + return err($error) + + return ok(connPeers.mapIt($it)) + except CatchableError as e: + return err(e.msg) + +proc relayPeersInMesh*( + self: Waku, pubsubTopic: PubsubTopic +): Future[Result[seq[string], string]] {.async.} = + try: + if self.node.wakuRelay.isNil(): + return err("relayPeersInMesh: WakuRelay not mounted") + + let meshPeers = self.node.wakuRelay.getPeersInMesh(pubsubTopic).valueOr: + return err($error) + + return ok(meshPeers.mapIt($it)) + except CatchableError as e: + return err(e.msg) + +proc relayNumPeersInMesh*( + self: Waku, pubsubTopic: PubsubTopic +): Future[Result[int, string]] {.async.} = + try: + if self.node.wakuRelay.isNil(): + return err("relayNumPeersInMesh: WakuRelay not mounted") + let n = self.node.wakuRelay.getNumPeersInMesh(pubsubTopic).valueOr: + return err($error) + return ok(n) + except CatchableError as e: + return err(e.msg) + +proc relayNumConnectedPeers*( + self: Waku, pubsubTopic: PubsubTopic +): Future[Result[int, string]] {.async.} = + try: + if self.node.wakuRelay.isNil(): + return err("relayNumConnectedPeers: WakuRelay not mounted") + let n = self.node.wakuRelay.getNumConnectedPeers(pubsubTopic).valueOr: + return err($error) + return ok(n) + except CatchableError as e: + return err(e.msg) diff --git a/logos_delivery/waku/api/store.nim b/logos_delivery/waku/api/store.nim new file mode 100644 index 000000000..5095dbb0d --- /dev/null +++ b/logos_delivery/waku/api/store.nim @@ -0,0 +1,29 @@ +## Waku layer API — store (historical query) operations. +{.push raises: [].} + +import results, chronos, chronicles + +import logos_delivery/waku/waku +import + logos_delivery/waku/[waku_core, node/waku_node, waku_store/common, waku_store/client] + +proc storeQuery*( + self: Waku, request: StoreQueryRequest, peer: string, timeoutMs: int +): Future[Result[StoreQueryResponse, string]] {.async.} = + try: + if self.node.wakuStoreClient.isNil(): + return err("wakuStoreClient is not mounted") + + let remotePeer = parsePeerInfo(peer).valueOr: + return err("storeQuery failed to parse peer addr: " & $error) + + let queryFut = self.node.wakuStoreClient.query(request, remotePeer) + if not await queryFut.withTimeout(timeoutMs.milliseconds): + return err("storeQuery timed out") + + let queryResponse = queryFut.read().valueOr: + return err("storeQuery failed: " & $error) + + return ok(queryResponse) + except CatchableError as e: + return err(e.msg) diff --git a/logos_delivery/waku/api/topics.nim b/logos_delivery/waku/api/topics.nim new file mode 100644 index 000000000..223291169 --- /dev/null +++ b/logos_delivery/waku/api/topics.nim @@ -0,0 +1,27 @@ +## Waku layer API — topic construction. +{.push raises: [].} + +import std/strformat +import results, chronos + +import logos_delivery/waku/waku +import logos_delivery/waku/waku_core + +proc buildContentTopic*( + self: Waku, appName: string, appVersion: uint32, name: string, encoding: string +): Future[Result[ContentTopic, string]] {.async.} = + try: + return ok(ContentTopic(fmt"/{appName}/{appVersion}/{name}/{encoding}")) + except CatchableError as e: + return err(e.msg) + +proc buildPubsubTopic*( + self: Waku, topicName: string +): Future[Result[PubsubTopic, string]] {.async.} = + try: + return ok(PubsubTopic(fmt"/waku/2/{topicName}")) + except CatchableError as e: + return err(e.msg) + +proc defaultPubsubTopic*(self: Waku): Future[Result[PubsubTopic, string]] {.async.} = + return ok(DefaultPubsubTopic) diff --git a/logos_delivery/waku/discovery/waku_kademlia.nim b/logos_delivery/waku/discovery/waku_kademlia.nim index 9b0e43abc..e6775530c 100644 --- a/logos_delivery/waku/discovery/waku_kademlia.nim +++ b/logos_delivery/waku/discovery/waku_kademlia.nim @@ -21,7 +21,7 @@ import import logos_delivery/waku/waku_core, logos_delivery/waku/node/peer_manager, - logos_delivery/waku/events/discovery_events + logos_delivery/waku/api/events/discovery_events logScope: topics = "waku service discovery" diff --git a/logos_delivery/waku/events/events.nim b/logos_delivery/waku/events/events.nim deleted file mode 100644 index 130d7c018..000000000 --- a/logos_delivery/waku/events/events.nim +++ /dev/null @@ -1,9 +0,0 @@ -import - ./[ - message_events, delivery_events, health_events, peer_events, lifecycle_events, - discovery_events, - ] - -export - message_events, delivery_events, health_events, peer_events, lifecycle_events, - discovery_events diff --git a/logos_delivery/waku/events/message_events.nim b/logos_delivery/waku/events/message_events.nim deleted file mode 100644 index 2e4bece80..000000000 --- a/logos_delivery/waku/events/message_events.nim +++ /dev/null @@ -1,35 +0,0 @@ -import brokers/event_broker -import logos_delivery/api/types -import logos_delivery/waku/[waku_core/message, waku_core/topics] -export types - -EventBroker: - # Event emitted when a message is sent to the network - type MessageSentEvent* = object - requestId*: RequestId - messageHash*: string - -EventBroker: - # Event emitted when a message send operation fails - type MessageErrorEvent* = object - requestId*: RequestId - messageHash*: string - error*: string - -EventBroker: - # Confirmation that a message has been correctly delivered to some neighbouring nodes. - type MessagePropagatedEvent* = object - requestId*: RequestId - messageHash*: string - -EventBroker: - # Event emitted when a message is received via Waku - type MessageReceivedEvent* = object - messageHash*: string - message*: WakuMessage - -EventBroker: - # Internal event emitted when a message arrives from the network via any protocol - type MessageSeenEvent* = object - topic*: PubsubTopic - message*: WakuMessage diff --git a/logos_delivery/waku/factory/app_callbacks.nim b/logos_delivery/waku/factory/app_callbacks.nim index f1d3369be..0945b56bf 100644 --- a/logos_delivery/waku/factory/app_callbacks.nim +++ b/logos_delivery/waku/factory/app_callbacks.nim @@ -1,5 +1,9 @@ import ../waku_relay, ../node/peer_manager, ../node/health_monitor/connection_status +# Re-export the modules that define the handler types below, so that consumers +# of `AppCallbacks` (e.g. the FFI library) can construct the handlers. +export waku_relay, peer_manager, connection_status + type AppCallbacks* = ref object relayHandler*: WakuRelayHandler topicHealthChangeHandler*: TopicHealthChangeHandler diff --git a/logos_delivery/waku/factory/node_factory.nim b/logos_delivery/waku/factory/node_factory.nim index 658a49e38..af19f0afd 100644 --- a/logos_delivery/waku/factory/node_factory.nim +++ b/logos_delivery/waku/factory/node_factory.nim @@ -39,7 +39,7 @@ import ../node/peer_manager/peer_store/migrations as peer_store_sqlite_migrations, ../waku_lightpush_legacy/common, ../common/rate_limit/setting, - ../events/discovery_events + ../api/events/discovery_events ## Peer persistence diff --git a/logos_delivery/waku/node/health_monitor/node_health_monitor.nim b/logos_delivery/waku/node/health_monitor/node_health_monitor.nim index d75a793ac..f59e0d7e6 100644 --- a/logos_delivery/waku/node/health_monitor/node_health_monitor.nim +++ b/logos_delivery/waku/node/health_monitor/node_health_monitor.nim @@ -9,11 +9,12 @@ import libp2p/protocols/pubsub, libp2p/protocols/pubsub/rpc/messages, logos_delivery/api/types, + logos_delivery/api/logos_delivery_api, # EventConnectionStatusChange logos_delivery/waku/[ waku_relay, + api/events/health_events, + api/events/peer_events, rln, - events/health_events, - events/peer_events, node/waku_node, node/node_telemetry, node/peer_manager, diff --git a/logos_delivery/waku/node/peer_manager/peer_manager.nim b/logos_delivery/waku/node/peer_manager/peer_manager.nim index 60bd2acb1..31c6133ab 100644 --- a/logos_delivery/waku/node/peer_manager/peer_manager.nim +++ b/logos_delivery/waku/node/peer_manager/peer_manager.nim @@ -20,7 +20,7 @@ import waku_relay/protocol, waku_enr/sharding, waku_enr/capabilities, - events/peer_events, + api/events/peer_events, common/nimchronos, common/enr, common/callbacks, diff --git a/logos_delivery/waku/node/subscription_manager.nim b/logos_delivery/waku/node/subscription_manager.nim index 15b582ea6..d0468749d 100644 --- a/logos_delivery/waku/node/subscription_manager.nim +++ b/logos_delivery/waku/node/subscription_manager.nim @@ -15,14 +15,14 @@ import waku_filter_v2/common as filter_common, waku_filter_v2/client as filter_client, waku_filter_v2/protocol as filter_protocol, - events/health_events, - events/message_events, - events/peer_events, + api/events/health_events, + api/events/peer_events, requests/health_requests, node/peer_manager, node/health_monitor/topic_health, node/health_monitor/connection_status, ] +import logos_delivery/api/kernel_api # MessageSeenEvent {.push raises: [].} diff --git a/logos_delivery/waku/node/waku_node.nim b/logos_delivery/waku/node/waku_node.nim index febc66731..3c2e41d8a 100644 --- a/logos_delivery/waku/node/waku_node.nim +++ b/logos_delivery/waku/node/waku_node.nim @@ -59,10 +59,10 @@ import waku_mix, requests/node_requests, requests/health_requests, - events/health_events, - events/message_events, - events/peer_events, + api/events/health_events, + api/events/peer_events, ], + logos_delivery/api/kernel_api, # MessageSeenEvent logos_delivery/waku/discovery/waku_kademlia, logos_delivery/waku/net/[bound_ports, net_config], ./peer_manager, diff --git a/logos_delivery/waku/node/waku_node/relay.nim b/logos_delivery/waku/node/waku_node/relay.nim index 3446b4b73..ed039fb49 100644 --- a/logos_delivery/waku/node/waku_node/relay.nim +++ b/logos_delivery/waku/node/waku_node/relay.nim @@ -33,8 +33,8 @@ import node/waku_node, node/subscription_manager, node/peer_manager, - events/message_events, ] +import logos_delivery/api/kernel_api # MessageSeenEvent export waku_relay.WakuRelayHandler diff --git a/logos_delivery/waku/waku.nim b/logos_delivery/waku/waku.nim index fa0562148..8b9fa9226 100644 --- a/logos_delivery/waku/waku.nim +++ b/logos_delivery/waku/waku.nim @@ -22,6 +22,7 @@ import metrics/chronos_httpserver, brokers/broker_context, logos_delivery/api/types, + logos_delivery/api/kernel_api, logos_delivery/waku/[ waku_core, waku_node, @@ -57,15 +58,17 @@ import ./factory/waku_conf, ./factory/waku_state_info +# Surfaces the Kernel API interface (and its `MessageSeenEvent`) to consumers +# of the Waku layer. +export kernel_api + logScope: topics = "wakunode waku" # Git version in git describe format (defined at compile time) const git_version* {.strdefine.} = "n/a" -const FilterOpTimeout = 5.seconds - -type Waku* = ref object +type Waku* = ref object of IKernel stateInfo*: WakuStateInfo conf*: WakuConf rng*: crypto.Rng @@ -576,418 +579,4 @@ proc stop*(waku: Waku): Future[Result[void, string]] {.async: (raises: []).} = return ok() -## Kernel API realization -## -# --- topic construction --- -proc buildContentTopic*( - self: Waku, appName: string, appVersion: uint32, name: string, encoding: string -): Future[Result[ContentTopic, string]] {.async.} = - try: - return ok(ContentTopic(fmt"/{appName}/{appVersion}/{name}/{encoding}")) - except CatchableError as e: - return err(e.msg) - -proc buildPubsubTopic*( - self: Waku, topicName: string -): Future[Result[PubsubTopic, string]] {.async.} = - try: - return ok(PubsubTopic(fmt"/waku/2/{topicName}")) - except CatchableError as e: - return err(e.msg) - -proc defaultPubsubTopic*(self: Waku): Future[Result[PubsubTopic, string]] {.async.} = - return ok(DefaultPubsubTopic) - -# --- relay --- -proc relayPublish*( - self: Waku, pubsubTopic: PubsubTopic, message: WakuMessage, timeoutMs: uint32 -): Future[Result[int, string]] {.async.} = - try: - if self.node.wakuRelay.isNil(): - return err("relayPublish: WakuRelay not mounted") - - let numPeers = (await self.node.wakuRelay.publish(pubsubTopic, message)).valueOr: - return err($error) - - return ok(numPeers) - except CatchableError as e: - return err(e.msg) - -proc relaySubscribe*( - self: Waku, pubsubTopic: PubsubTopic -): Future[Result[bool, string]] {.async.} = - try: - if self.node.wakuRelay.isNil(): - return err("relaySubscribe: WakuRelay not mounted") - - self.node.subscribe( - (kind: SubscriptionKind.PubsubSub, topic: pubsubTopic), WakuRelayHandler(nil) - ).isOkOr: - return err($error) - - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc relayUnsubscribe*( - self: Waku, pubsubTopic: PubsubTopic -): Future[Result[bool, string]] {.async.} = - try: - if self.node.wakuRelay.isNil(): - return err("relayUnsubscribe: WakuRelay not mounted") - - self.node.unsubscribe((kind: SubscriptionKind.PubsubSub, topic: pubsubTopic)).isOkOr: - return err($error) - - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc relayAddProtectedShard*( - self: Waku, clusterId: uint16, shardId: uint16, publicKey: string -): Future[Result[bool, string]] {.async.} = - try: - if self.node.wakuRelay.isNil(): - return err("relayAddProtectedShard: WakuRelay not mounted") - - let pubKey = SkPublicKey.fromHex(publicKey).valueOr: - return err("relayAddProtectedShard: invalid public key: " & $error) - - let protectedShard = ProtectedShard(shard: shardId, key: pubKey) - self.node.wakuRelay.addSignedShardsValidator(@[protectedShard], clusterId) - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc relayConnectedPeers*( - self: Waku, pubsubTopic: PubsubTopic -): Future[Result[seq[string], string]] {.async.} = - try: - if self.node.wakuRelay.isNil(): - return err("relayConnectedPeers: WakuRelay not mounted") - - let connPeers = self.node.wakuRelay.getConnectedPeers(pubsubTopic).valueOr: - return err($error) - - return ok(connPeers.mapIt($it)) - except CatchableError as e: - return err(e.msg) - -proc relayPeersInMesh*( - self: Waku, pubsubTopic: PubsubTopic -): Future[Result[seq[string], string]] {.async.} = - try: - if self.node.wakuRelay.isNil(): - return err("relayPeersInMesh: WakuRelay not mounted") - - let meshPeers = self.node.wakuRelay.getPeersInMesh(pubsubTopic).valueOr: - return err($error) - - return ok(meshPeers.mapIt($it)) - except CatchableError as e: - return err(e.msg) - -# --- filter --- -proc filterSubscribe*( - self: Waku, - pubsubTopic: Option[PubsubTopic], - contentTopics: seq[ContentTopic], - peer: string, -): Future[Result[bool, string]] {.async.} = - try: - if self.node.wakuFilterClient.isNil(): - return err("wakuFilterClient is not mounted") - - let subFut = self.node.filterSubscribe(pubsubTopic, contentTopics, peer) - if not await subFut.withTimeout(FilterOpTimeout): - return err("filter subscription timed out") - subFut.read().isOkOr: - return err($error) - - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc filterUnsubscribe*( - self: Waku, - pubsubTopic: Option[PubsubTopic], - contentTopics: seq[ContentTopic], - peer: string, -): Future[Result[bool, string]] {.async.} = - try: - if self.node.wakuFilterClient.isNil(): - return err("wakuFilterClient is not mounted") - - let unsubFut = self.node.filterUnsubscribe(pubsubTopic, contentTopics, peer) - if not await unsubFut.withTimeout(FilterOpTimeout): - return err("filter un-subscription timed out") - unsubFut.read().isOkOr: - return err($error) - - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc filterUnsubscribeAll*( - self: Waku, peer: string -): Future[Result[bool, string]] {.async.} = - try: - if self.node.wakuFilterClient.isNil(): - return err("wakuFilterClient is not mounted") - - let unsubFut = self.node.filterUnsubscribeAll(peer) - if not await unsubFut.withTimeout(FilterOpTimeout): - return err("filter un-subscription all timed out") - unsubFut.read().isOkOr: - return err($error) - - return ok(true) - except CatchableError as e: - return err(e.msg) - -# --- lightpush --- -proc lightpushPublish*( - self: Waku, pubsubTopic: PubsubTopic, message: WakuMessage, peer: string -): Future[Result[string, string]] {.async.} = - try: - if self.node.wakuLegacyLightpushClient.isNil(): - return err("wakuLegacyLightpushClient is not mounted") - - let remotePeer = parsePeerInfo(peer).valueOr: - return err("lightpushPublish failed to parse peer addr: " & $error) - - let msgHashHex = ( - await self.node.wakuLegacyLightpushClient.publish( - pubsubTopic, message, remotePeer - ) - ).valueOr: - return err($error) - - return ok(msgHashHex) - except CatchableError as e: - return err(e.msg) - -# --- store --- -proc storeQuery*( - self: Waku, request: StoreQueryRequest, peer: string, timeoutMs: int -): Future[Result[StoreQueryResponse, string]] {.async.} = - try: - if self.node.wakuStoreClient.isNil(): - return err("wakuStoreClient is not mounted") - - let remotePeer = parsePeerInfo(peer).valueOr: - return err("storeQuery failed to parse peer addr: " & $error) - - let queryFut = self.node.wakuStoreClient.query(request, remotePeer) - if not await queryFut.withTimeout(timeoutMs.milliseconds): - return err("storeQuery timed out") - - let queryResponse = queryFut.read().valueOr: - return err("storeQuery failed: " & $error) - - return ok(queryResponse) - except CatchableError as e: - return err(e.msg) - -# --- peer management --- -proc connect*( - self: Waku, peers: seq[string], timeoutMs: uint32 -): Future[Result[bool, string]] {.async.} = - try: - await self.node.connectToNodes(peers.mapIt(strip(it)), source = "static") - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc disconnectPeerById*( - self: Waku, peerId: string -): Future[Result[bool, string]] {.async.} = - try: - let pId = PeerId.init(peerId).valueOr: - return err($error) - await self.node.peerManager.disconnectNode(pId) - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc disconnectAllPeers*(self: Waku): Future[Result[bool, string]] {.async.} = - try: - await self.node.peerManager.disconnectAllPeers() - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc dialPeer*( - self: Waku, peerAddr: string, protocol: string, timeoutMs: int -): Future[Result[bool, string]] {.async.} = - try: - let remotePeerInfo = parsePeerInfo(peerAddr).valueOr: - return err($error) - let conn = await self.node.peerManager.dialPeer(remotePeerInfo, protocol) - if conn.isNone(): - return err("failed dialing peer") - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc dialPeerById*( - self: Waku, peerId: string, protocol: string, timeoutMs: int -): Future[Result[bool, string]] {.async.} = - try: - let pId = PeerId.init(peerId).valueOr: - return err($error) - let conn = await self.node.peerManager.dialPeer(pId, protocol) - if conn.isNone(): - return err("failed dialing peer") - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc peerIdsFromPeerstore*(self: Waku): Future[Result[seq[string], string]] {.async.} = - try: - return ok(self.node.peerManager.switch.peerStore.peers().mapIt($it.peerId)) - except CatchableError as e: - return err(e.msg) - -proc connectedPeersInfo*(self: Waku): Future[Result[seq[string], string]] {.async.} = - try: - return ok( - self.node.peerManager.switch.peerStore - .peers() - .filterIt(it.connectedness == Connected) - .mapIt($it.peerId) - ) - except CatchableError as e: - return err(e.msg) - -proc connectedPeers*(self: Waku): Future[Result[seq[string], string]] {.async.} = - try: - let (inPeerIds, outPeerIds) = self.node.peerManager.connectedPeers() - return ok(concat(inPeerIds, outPeerIds).mapIt($it)) - except CatchableError as e: - return err(e.msg) - -proc peerIdsByProtocol*( - self: Waku, protocol: string -): Future[Result[seq[string], string]] {.async.} = - try: - return ok( - self.node.peerManager.switch.peerStore - .peers(protocol) - .filterIt(it.connectedness == Connected) - .mapIt($it.peerId) - ) - except CatchableError as e: - return err(e.msg) - -# --- discovery --- -proc dnsDiscovery*( - self: Waku, enrTreeUrl: string, nameServer: string, timeoutMs: int -): Future[Result[seq[string], string]] {.async.} = - try: - let dnsNameServers = @[parseIpAddress(nameServer)] - let discoveredPeers = ( - await retrieveDynamicBootstrapNodes(enrTreeUrl, dnsNameServers) - ).valueOr: - return err("failed discovering peers from DNS: " & $error) - - var multiAddresses = newSeq[string]() - for discPeer in discoveredPeers: - for address in discPeer.addrs: - multiAddresses.add($address & "/p2p/" & $discPeer) - - return ok(multiAddresses) - except CatchableError as e: - return err(e.msg) - -proc discv5UpdateBootnodes*( - self: Waku, bootnodes: seq[string] -): Future[Result[bool, string]] {.async.} = - try: - if self.wakuDiscv5.isNil(): - return err("discv5 not started") - let jsonArray = "[" & bootnodes.mapIt("\"" & it & "\"").join(",") & "]" - self.wakuDiscv5.updateBootstrapRecords(jsonArray).isOkOr: - return err("error in discv5UpdateBootnodes: " & $error) - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc startDiscv5*(self: Waku): Future[Result[bool, string]] {.async.} = - try: - if self.wakuDiscv5.isNil(): - return err("discv5 not started") - (await self.wakuDiscv5.start()).isOkOr: - return err("error starting discv5: " & $error) - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc stopDiscv5*(self: Waku): Future[Result[bool, string]] {.async.} = - try: - if self.wakuDiscv5.isNil(): - return err("discv5 not started") - await self.wakuDiscv5.stop() - return ok(true) - except CatchableError as e: - return err(e.msg) - -proc peerExchangeRequest*( - self: Waku, numPeers: uint64 -): Future[Result[int, string]] {.async.} = - try: - let numPeersRecv = (await self.node.fetchPeerExchangePeers(numPeers)).valueOr: - return err("failed peer exchange: " & $error) - return ok(numPeersRecv) - except CatchableError as e: - return err(e.msg) - -# --- debug / info --- -proc version*(self: Waku): Future[Result[string, string]] {.async.} = - return ok(WakuNodeVersionString) - -proc listenAddresses*(self: Waku): Future[Result[seq[string], string]] {.async.} = - try: - return ok(self.node.info().listenAddresses) - except CatchableError as e: - return err(e.msg) - -proc myEnr*(self: Waku): Future[Result[string, string]] {.async.} = - try: - return ok(self.node.enr.toURI()) - except CatchableError as e: - return err(e.msg) - -proc myPeerId*(self: Waku): Future[Result[string, string]] {.async.} = - try: - return ok($self.node.peerId()) - except CatchableError as e: - return err(e.msg) - -proc metrics*(self: Waku): Future[Result[string, string]] {.async.} = - {.gcsafe.}: - try: - return ok(defaultRegistry.toText()) - except CatchableError as e: - return err(e.msg) - -proc pingPeer*( - self: Waku, peerAddr: string, timeoutMs: int -): Future[Result[int64, string]] {.async.} = - try: - let peerInfo = parsePeerInfo(peerAddr).valueOr: - return err("pingPeer failed to parse peer addr: " & $error) - - let conn = await self.node.switch.dial(peerInfo.peerId, peerInfo.addrs, PingCodec) - defer: - await conn.close() - let pingRTT = await self.node.libp2pPing.ping(conn) - - if pingRTT == 0.nanos: - return err("could not ping peer: rtt-0") - - return ok(pingRTT.nanos) - except CatchableError as e: - return err(e.msg) - {.pop.} diff --git a/logos_delivery/waku/waku_filter_v2/client.nim b/logos_delivery/waku/waku_filter_v2/client.nim index ddae363a3..51ed18af3 100644 --- a/logos_delivery/waku/waku_filter_v2/client.nim +++ b/logos_delivery/waku/waku_filter_v2/client.nim @@ -14,7 +14,7 @@ import brokers/broker_context import - logos_delivery/waku/[node/peer_manager, waku_core, events/delivery_events], + logos_delivery/waku/[node/peer_manager, waku_core, api/events/filter_subscribe_events], ./common, ./protocol_metrics, ./rpc_codec, diff --git a/logos_delivery/waku/waku_relay/protocol.nim b/logos_delivery/waku/waku_relay/protocol.nim index e677ec5a0..409b02929 100644 --- a/logos_delivery/waku/waku_relay/protocol.nim +++ b/logos_delivery/waku/waku_relay/protocol.nim @@ -24,9 +24,9 @@ import logos_delivery/waku/waku_core, logos_delivery/waku/node/health_monitor/topic_health, logos_delivery/waku/requests/health_requests, - logos_delivery/waku/events/health_events, + logos_delivery/waku/api/events/health_events, ./message_id, - logos_delivery/waku/events/peer_events + logos_delivery/waku/api/events/peer_events from logos_delivery/waku/waku_core/codecs import WakuRelayCodec export WakuRelayCodec diff --git a/tests/api/test_api_health.nim b/tests/api/test_api_health.nim index 83efedd18..27030d432 100644 --- a/tests/api/test_api_health.nim +++ b/tests/api/test_api_health.nim @@ -12,7 +12,7 @@ import [topic_health, health_status, protocol_health, health_report], logos_delivery/waku/requests/health_requests, logos_delivery/waku/requests/node_requests, - logos_delivery/waku/events/health_events, + logos_delivery/waku/api/events/health_events, logos_delivery/waku/common/waku_protocol, logos_delivery/waku/factory/waku_conf import tools/confutils/cli_args diff --git a/tests/api/test_api_receive.nim b/tests/api/test_api_receive.nim index 41c0f0477..b1e7635d8 100644 --- a/tests/api/test_api_receive.nim +++ b/tests/api/test_api_receive.nim @@ -14,8 +14,7 @@ import logos_delivery/waku/[ waku_node, waku_core, - events/message_events, - events/health_events, + api/events/health_events, waku_relay/protocol, waku_archive, waku_archive/common as archive_common, diff --git a/tests/api/test_api_subscription.nim b/tests/api/test_api_subscription.nim index bf2851b02..ca9d2c9b3 100644 --- a/tests/api/test_api_subscription.nim +++ b/tests/api/test_api_subscription.nim @@ -12,7 +12,6 @@ import logos_delivery/waku/[ waku_node, waku_core, - events/message_events, waku_relay/protocol, node/waku_node/filter, node/subscription_manager, diff --git a/tests/channels/test_reliable_channel_send_receive.nim b/tests/channels/test_reliable_channel_send_receive.nim index b3d9a8f00..3a0a48c06 100644 --- a/tests/channels/test_reliable_channel_send_receive.nim +++ b/tests/channels/test_reliable_channel_send_receive.nim @@ -10,7 +10,7 @@ import ../testlib/[common, wakucore, wakunode, testasync] import logos_delivery import logos_delivery/waku/[waku_node, waku_core] import logos_delivery/waku/factory/waku_conf -import logos_delivery/waku/events/message_events as waku_message_events +import logos_delivery/api/messaging_client_api as waku_message_events import tools/confutils/cli_args import logos_delivery/channels/reliable_channel_manager diff --git a/tests/node/test_wakunode_health_monitor.nim b/tests/node/test_wakunode_health_monitor.nim index 04a39455a..acf0bc09a 100644 --- a/tests/node/test_wakunode_health_monitor.nim +++ b/tests/node/test_wakunode_health_monitor.nim @@ -19,8 +19,8 @@ import node/waku_node/store, node/waku_node/lightpush, node/waku_node/filter, - events/health_events, - events/peer_events, + api/events/health_events, + api/events/peer_events, waku_archive, ] From a763a59ac9b8d8b1e2d3a83f3bf9575ada34876c Mon Sep 17 00:00:00 2001 From: Fabiana Cecin Date: Tue, 30 Jun 2026 11:30:41 -0300 Subject: [PATCH 18/33] fix: fix portsShift feature (#4006) * Fold portsShift into each port in WakuConfBuilder.build() * Drop portsShift field from WakuConf * Drop portsShift param from networkConfiguration, discv5 setup, rest/metrics start * Keep Port(0) auto-assign sentinel (also fixes port-0 + shift) * Add regression test: announced port == bound port under portsShift * Fix lint --- logos_delivery/waku/discovery/waku_discv5.nim | 6 +-- .../conf_builder/waku_conf_builder.nim | 34 ++++++++++++---- .../waku/factory/internal_config.nim | 9 ++--- logos_delivery/waku/factory/node_factory.nim | 2 +- logos_delivery/waku/factory/waku_conf.nim | 1 - logos_delivery/waku/node/waku_metrics.nim | 8 +--- .../waku/rest_api/endpoint/builder.nim | 4 +- logos_delivery/waku/waku.nim | 11 ++---- tests/factory/test_node_factory.nim | 6 +-- tests/waku_discv5/test_waku_discv5.nim | 2 - tests/wakunode2/test_app.nim | 39 +++++++++++++++++++ 11 files changed, 83 insertions(+), 39 deletions(-) diff --git a/logos_delivery/waku/discovery/waku_discv5.nim b/logos_delivery/waku/discovery/waku_discv5.nim index ade874733..5f831730d 100644 --- a/logos_delivery/waku/discovery/waku_discv5.nim +++ b/logos_delivery/waku/discovery/waku_discv5.nim @@ -413,7 +413,6 @@ proc setupDiscoveryV5*( rng: crypto.Rng, key: crypto.PrivateKey, p2pListenAddress: IpAddress, - portsShift: uint16, ): Result[WakuDiscoveryV5, string] = ## Public only for testing. Callers should use `setupAndStartDiscv5`, which ## additionally handles `udpPort == 0` via auto-port retry. @@ -443,7 +442,7 @@ proc setupDiscoveryV5*( let discv5Config = DiscoveryConfig.init(conf.tableIpLimit, conf.bucketIpLimit, conf.bitsPerHop) - let discv5UdpPort = Port(uint16(conf.udpPort) + portsShift) + let discv5UdpPort = conf.udpPort let discv5Conf = WakuDiscoveryV5Config( discv5Config: some(discv5Config), @@ -469,7 +468,6 @@ proc setupAndStartDiscv5*( rng: crypto.Rng, key: crypto.PrivateKey, p2pListenAddress: IpAddress, - portsShift: uint16, ): Future[Result[WakuDiscoveryV5, string]] {.async: (raises: []).} = ## Construct and start a `WakuDiscoveryV5` instance, handling auto-port ## retry when the caller asks for `udpPort == 0`. @@ -480,7 +478,7 @@ proc setupAndStartDiscv5*( c.udpPort = port let wd = setupDiscoveryV5( myENR, nodePeerManager, nodeTopicSubscriptionQueue, c, dynamicBootstrapNodes, rng, - key, p2pListenAddress, portsShift, + key, p2pListenAddress, ).valueOr: return err(error) let startRes = await wd.start() diff --git a/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim b/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim index 9fbd23f54..ffffceb5f 100644 --- a/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim +++ b/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim @@ -529,6 +529,13 @@ proc enforceSecurityConstraints(builder: WakuConfBuilder): Result[void, string] ok() +func resolvePortsShift(configured: Port, portsShift: uint16): Port = + ## Fold portsShift into a configured port. Port(0) (auto-assign) is left as-is. + if configured == Port(0): + configured + else: + Port(configured.uint16 + portsShift) + proc build*( builder: var WakuConfBuilder, rng: crypto.Rng = crypto.newRng() ): Result[WakuConf, string] = @@ -624,7 +631,7 @@ proc build*( let contentTopics = builder.contentTopics.get(@[]) # Build sub-configs - let discv5Conf = builder.discv5Conf.build().valueOr: + var discv5Conf = builder.discv5Conf.build().valueOr: return err("Discv5 Conf building failed: " & $error) let dnsDiscoveryConf = builder.dnsDiscoveryConf.build().valueOr: @@ -633,10 +640,10 @@ proc build*( let filterServiceConf = builder.filterServiceConf.build().valueOr: return err("Filter Service Conf building failed: " & $error) - let metricsServerConf = builder.metricsServerConf.build().valueOr: + var metricsServerConf = builder.metricsServerConf.build().valueOr: return err("Metrics Server Conf building failed: " & $error) - let restServerConf = builder.restServerConf.build().valueOr: + var restServerConf = builder.restServerConf.build().valueOr: return err("REST Server Conf building failed: " & $error) let rlnRelayConf = builder.rlnRelayConf.build().valueOr: @@ -648,10 +655,10 @@ proc build*( let mixConf = builder.mixConf.build().valueOr: return err("Mix Conf building failed: " & $error) - let webSocketConf = builder.webSocketConf.build().valueOr: + var webSocketConf = builder.webSocketConf.build().valueOr: return err("WebSocket Conf building failed: " & $error) - let quicConf = builder.quicConf.build().valueOr: + var quicConf = builder.quicConf.build().valueOr: return err("QUIC Conf building failed: " & $error) let rateLimit = builder.rateLimitConf.build().valueOr: @@ -683,7 +690,7 @@ proc build*( warn "Nat Strategy is not specified, defaulting to none" DefaultNatStrategy - let p2pTcpPort = builder.p2pTcpPort.get(DefaultP2pTcpPort) + var p2pTcpPort = builder.p2pTcpPort.get(DefaultP2pTcpPort) let p2pListenAddress = if builder.p2pListenAddress.isSome(): @@ -765,6 +772,20 @@ proc build*( mix = mix, ) + # portsShift is consumed here, WakuConf carries final bind ports. + p2pTcpPort = resolvePortsShift(p2pTcpPort, portsShift) + if webSocketConf.isSome(): + webSocketConf.get().port = resolvePortsShift(webSocketConf.get().port, portsShift) + if quicConf.isSome(): + quicConf.get().port = resolvePortsShift(quicConf.get().port, portsShift) + if discv5Conf.isSome(): + discv5Conf.get().udpPort = resolvePortsShift(discv5Conf.get().udpPort, portsShift) + if restServerConf.isSome(): + restServerConf.get().port = resolvePortsShift(restServerConf.get().port, portsShift) + if metricsServerConf.isSome(): + metricsServerConf.get().httpPort = + resolvePortsShift(metricsServerConf.get().httpPort, portsShift) + let wakuConf = WakuConf( # confs storeServiceConf: storeServiceConf, @@ -806,7 +827,6 @@ proc build*( extMultiAddrs: extMultiAddrs, extMultiAddrsOnly: extMultiAddrsOnly, ), - portsShift: portsShift, webSocketConf: webSocketConf, quicConf: quicConf, dnsAddrsNameServers: dnsAddrsNameServers, diff --git a/logos_delivery/waku/factory/internal_config.nim b/logos_delivery/waku/factory/internal_config.nim index 08f6e2568..45d7f0b4c 100644 --- a/logos_delivery/waku/factory/internal_config.nim +++ b/logos_delivery/waku/factory/internal_config.nim @@ -90,15 +90,14 @@ proc networkConfiguration*( quicConf: Option[QuicConf], wakuFlags: CapabilitiesBitfield, dnsAddrsNameServers: seq[IpAddress], - portsShift: uint16, clientId: string, ): Future[NetConfigResult] {.async.} = - let tcpBindPort = Port(uint16(conf.p2pTcpPort) + portsShift) + let tcpBindPort = conf.p2pTcpPort let (quicEnabled, quicBindPort) = if quicConf.isSome(): let qConf = quicConf.get() - (true, some(Port(qConf.port.uint16 + portsShift))) + (true, some(qConf.port)) else: (false, none(Port)) @@ -111,7 +110,7 @@ proc networkConfiguration*( let discv5UdpPort = if discv5Conf.isSome(): - some(Port(uint16(discv5Conf.get().udpPort) + portsShift)) + some(discv5Conf.get().udpPort) else: none(Port) @@ -145,7 +144,7 @@ proc networkConfiguration*( let (wsEnabled, wsBindPort, wssEnabled) = if webSocketConf.isSome: let wsConf = webSocketConf.get() - (true, some(Port(wsConf.port.uint16 + portsShift)), wsConf.secureConf.isSome) + (true, some(wsConf.port), wsConf.secureConf.isSome) else: (false, none(Port), false) diff --git a/logos_delivery/waku/factory/node_factory.nim b/logos_delivery/waku/factory/node_factory.nim index af19f0afd..daa2e5fd0 100644 --- a/logos_delivery/waku/factory/node_factory.nim +++ b/logos_delivery/waku/factory/node_factory.nim @@ -462,7 +462,7 @@ proc setupNode*( await networkConfiguration( wakuConf.clusterId, wakuConf.endpointConf, wakuConf.discv5Conf, wakuConf.webSocketConf, wakuConf.quicConf, wakuConf.wakuFlags, - wakuConf.dnsAddrsNameServers, wakuConf.portsShift, clientId, + wakuConf.dnsAddrsNameServers, clientId, ) ).valueOr: error "failed to create internal config", error = error diff --git a/logos_delivery/waku/factory/waku_conf.nim b/logos_delivery/waku/factory/waku_conf.nim index 0ffe803ae..1ceb9ed09 100644 --- a/logos_delivery/waku/factory/waku_conf.nim +++ b/logos_delivery/waku/factory/waku_conf.nim @@ -121,7 +121,6 @@ type WakuConf* {.requiresInit.} = ref object mixConf*: Option[MixConf] kademliaDiscoveryConf*: Option[KademliaDiscoveryConf] - portsShift*: uint16 dnsAddrsNameServers*: seq[IpAddress] endpointConf*: EndpointConf wakuFlags*: CapabilitiesBitfield diff --git a/logos_delivery/waku/node/waku_metrics.nim b/logos_delivery/waku/node/waku_metrics.nim index 7058af755..30e5e6a2a 100644 --- a/logos_delivery/waku/node/waku_metrics.nim +++ b/logos_delivery/waku/node/waku_metrics.nim @@ -86,13 +86,9 @@ proc startMetricsServer( return ok(started) proc startMetricsServerAndLogging*( - conf: MetricsServerConf, portsShift: uint16 + conf: MetricsServerConf ): Future[Result[StartedMetricsServer, string]] {.async.} = - let started = ( - await ( - startMetricsServer(conf.httpAddress, Port(conf.httpPort.uint16 + portsShift)) - ) - ).valueOr: + let started = (await (startMetricsServer(conf.httpAddress, conf.httpPort))).valueOr: return err("Starting metrics server failed. Continuing in current state:" & $error) if conf.logging: diff --git a/logos_delivery/waku/rest_api/endpoint/builder.nim b/logos_delivery/waku/rest_api/endpoint/builder.nim index 4610720ee..f9bc1da89 100644 --- a/logos_delivery/waku/rest_api/endpoint/builder.nim +++ b/logos_delivery/waku/rest_api/endpoint/builder.nim @@ -41,7 +41,7 @@ type RestServerConf* = object relayCacheCapacity*: uint32 proc startRestServerEssentials*( - nodeHealthMonitor: NodeHealthMonitor, conf: RestServerConf, portsShift: uint16 + nodeHealthMonitor: NodeHealthMonitor, conf: RestServerConf ): Result[WakuRestServerRef, string] = if restServerNotInstalledTab.isNil: restServerNotInstalledTab = newTable[string, string]() @@ -87,7 +87,7 @@ proc startRestServerEssentials*( none(string) let address = conf.listenAddress - let port = Port(conf.port.uint16 + portsShift) + let port = conf.port let server = ?newRestHttpServer( address, port, diff --git a/logos_delivery/waku/waku.nim b/logos_delivery/waku/waku.nim index 8b9fa9226..6e0e933e0 100644 --- a/logos_delivery/waku/waku.nim +++ b/logos_delivery/waku/waku.nim @@ -214,7 +214,7 @@ proc new*( let restServer: WakuRestServerRef = if wakuConf.restServerConf.isSome(): let restServer = startRestServerEssentials( - healthMonitor, wakuConf.restServerConf.get(), wakuConf.portsShift + healthMonitor, wakuConf.restServerConf.get() ).valueOr: error "Starting essential REST server failed", error = $error return err("Failed to start essential REST server in Waku.new: " & $error) @@ -290,11 +290,11 @@ proc getRunningNetConfig(waku: Waku): Future[Result[NetConfig, string]] {.async. if quicPort.isSome() and conf.quicConf.isSome(): conf.quicConf.get().port = quicPort.get() - # Rebuild NetConfig with bound port values + # Rebuild NetConfig from the bound ports already read back into `conf`. let netConf = ( await networkConfiguration( conf.clusterId, conf.endpointConf, conf.discv5Conf, conf.webSocketConf, - conf.quicConf, conf.wakuFlags, conf.dnsAddrsNameServers, conf.portsShift, clientId, + conf.quicConf, conf.wakuFlags, conf.dnsAddrsNameServers, clientId, ) ).valueOr: return err("Could not update NetConfig: " & error) @@ -443,7 +443,6 @@ proc start*(waku: Waku): Future[Result[void, string]] {.async: (raises: []).} = waku.rng, conf.nodeKey, conf.endpointConf.p2pListenAddress, - conf.portsShift, ) ).valueOr: return err("failed to start waku discovery v5: " & error) @@ -525,9 +524,7 @@ proc start*(waku: Waku): Future[Result[void, string]] {.async: (raises: []).} = if conf.metricsServerConf.isSome(): try: let (server, port) = ( - await waku_metrics.startMetricsServerAndLogging( - conf.metricsServerConf.get(), conf.portsShift - ) + await waku_metrics.startMetricsServerAndLogging(conf.metricsServerConf.get()) ).valueOr: return err("Starting monitoring and external interfaces failed: " & error) waku.metricsServer = server diff --git a/tests/factory/test_node_factory.nim b/tests/factory/test_node_factory.nim index 6a08ccfd1..b0b8bc198 100644 --- a/tests/factory/test_node_factory.nim +++ b/tests/factory/test_node_factory.nim @@ -156,10 +156,10 @@ suite "Auto-port retry": b.withHttpPort(port) b.build().value.get() - let failRes = await startMetricsServerAndLogging(buildMetricsConf(takenPort), 0'u16) + let failRes = await startMetricsServerAndLogging(buildMetricsConf(takenPort)) check failRes.isErr() - let okRes = await startMetricsServerAndLogging(buildMetricsConf(freePort), 0'u16) + let okRes = await startMetricsServerAndLogging(buildMetricsConf(freePort)) check okRes.isOk() if okRes.isOk(): await okRes.get().server.close() @@ -204,7 +204,6 @@ suite "Auto-port retry": node.rng, nodeKey, parseIpAddress("0.0.0.0"), - 0'u16, ) check failRes.isErr() @@ -217,7 +216,6 @@ suite "Auto-port retry": node.rng, nodeKey, parseIpAddress("0.0.0.0"), - 0'u16, ) check okRes.isOk() if okRes.isOk(): diff --git a/tests/waku_discv5/test_waku_discv5.nim b/tests/waku_discv5/test_waku_discv5.nim index b794ec918..9cb295877 100644 --- a/tests/waku_discv5/test_waku_discv5.nim +++ b/tests/waku_discv5/test_waku_discv5.nim @@ -505,7 +505,6 @@ suite "Waku Discovery v5": waku.rng, waku.conf.nodeKey, waku.conf.endpointConf.p2pListenAddress, - waku.conf.portsShift, ).valueOr: raiseAssert "failed setup discv5 in test: " & $error @@ -537,7 +536,6 @@ suite "Waku Discovery v5": waku.rng, waku.conf.nodeKey, waku.conf.endpointConf.p2pListenAddress, - waku.conf.portsShift, ).valueOr: raiseAssert "failed setup discv5 in test: " & $error diff --git a/tests/wakunode2/test_app.nim b/tests/wakunode2/test_app.nim index 1a312a6c6..879dcd7b5 100644 --- a/tests/wakunode2/test_app.nim +++ b/tests/wakunode2/test_app.nim @@ -170,3 +170,42 @@ suite "Wakunode2 - Waku initialization": check: quicAddrs.len >= 1 quicAddrs.allIt("/udp/0/quic-v1" notin $it) + + test "portsShift is applied exactly once": + # The announced port must equal the bound port, not bound + portsShift. + const shift = 5'u16 + + # Reserve a free port, then set base = port - shift so base + shift binds onto it. + let boundTarget = block: + let sock = newSocket() + defer: + sock.close() + sock.bindAddr(Port(0), "127.0.0.1") + sock.getLocalAddr()[1] + doAssert boundTarget.uint16 > shift, "ephemeral port unexpectedly low" + + var builder = defaultTestWakuConfBuilder() + builder.withP2pListenAddress(parseIpAddress("127.0.0.1")) + builder.withP2pTcpPort(Port(boundTarget.uint16 - shift)) + builder.withPortsShift(shift) + + let conf = builder.build().valueOr: + raiseAssert error + + var waku = (waitFor Waku.new(conf)).valueOr: + raiseAssert error + defer: + (waitFor waku.stop()).isOkOr: + raiseAssert error + + (waitFor waku.start()).isOkOr: + raiseAssert error + + let typedEnr = waku.node.enr.toTyped().valueOr: + raiseAssert $error + let announcedTcp = typedEnr.tcp() + + check: + announcedTcp.isSome() + waku.node.ports.tcp == boundTarget.uint16 + announcedTcp.get() == waku.node.ports.tcp From a45b785141630bbf040a6160a383972c1a4acbeb Mon Sep 17 00:00:00 2001 From: Ivan FB <128452529+Ivansete-status@users.noreply.github.com> Date: Tue, 30 Jun 2026 18:00:12 +0200 Subject: [PATCH 19/33] messaging: depend on the Waku kernel, not the raw WakuNode (#4000) --- logos_delivery/logos_delivery.nim | 9 +-- logos_delivery/messaging/api/send.nim | 12 ++-- logos_delivery/messaging/api/subscription.nim | 7 +- .../recv_service/recv_service.nim | 32 ++++------ .../send_service/lightpush_processor.nim | 32 ++-------- .../send_service/send_service.nim | 57 ++++++----------- logos_delivery/messaging/messaging_client.nim | 16 ++--- logos_delivery/waku/api/publish.nim | 64 +++++++++++++++++++ logos_delivery/waku/api/store.nim | 27 +++++++- logos_delivery/waku/api/subscriptions.nim | 37 +++++++++++ tests/node/test_wakunode_health_monitor.nim | 18 +++++- 11 files changed, 203 insertions(+), 108 deletions(-) create mode 100644 logos_delivery/waku/api/publish.nim create mode 100644 logos_delivery/waku/api/subscriptions.nim diff --git a/logos_delivery/logos_delivery.nim b/logos_delivery/logos_delivery.nim index 435bb968d..d0fb33b90 100644 --- a/logos_delivery/logos_delivery.nim +++ b/logos_delivery/logos_delivery.nim @@ -25,11 +25,12 @@ import logos_delivery/waku/waku export waku import logos_delivery/waku/api/[ - topics, relay, filter, lightpush, store, peer_manager, discovery, debug, health, - ping, + topics, relay, subscriptions, filter, lightpush, store, peer_manager, discovery, + debug, health, ping, ] export - topics, relay, filter, lightpush, store, peer_manager, discovery, debug, health, ping + topics, relay, subscriptions, filter, lightpush, store, peer_manager, discovery, + debug, health, ping # `MessageSeenEvent` is surfaced via `export waku` (Kernel interface); the # remaining waku health events live here. import logos_delivery/waku/api/events/health_events @@ -97,7 +98,7 @@ proc new*( let waku = (await Waku.new(layerConf.waku, appCallbacks)).valueOr: return err("failed to create Waku: " & error) - let messagingClient = MessagingClient.new(layerConf.messaging, waku.node).valueOr: + let messagingClient = MessagingClient.new(layerConf.messaging, waku).valueOr: return err("failed to create MessagingClient: " & error) let reliableChannelManager = ReliableChannelManager.new( diff --git a/logos_delivery/messaging/api/send.nim b/logos_delivery/messaging/api/send.nim index cc6312471..27c86d0ab 100644 --- a/logos_delivery/messaging/api/send.nim +++ b/logos_delivery/messaging/api/send.nim @@ -3,7 +3,8 @@ import results, chronos, chronicles import logos_delivery/api/types import logos_delivery/messaging/messaging_client -import logos_delivery/waku/node/[waku_node, subscription_manager] +import logos_delivery/waku/waku +import logos_delivery/waku/api/subscriptions import logos_delivery/messaging/delivery_service/send_service import logos_delivery/messaging/delivery_service/send_service/delivery_task @@ -16,17 +17,16 @@ proc send*( ## id the caller can correlate with `MessageSentEvent` / `MessageErrorEvent`. ?self.checkApiAvailability() - let isSubbed = - self.node.subscriptionManager.isSubscribed(envelope.contentTopic).valueOr(false) + let isSubbed = self.waku.isSubscribed(envelope.contentTopic).valueOr(false) if not isSubbed: info "Auto-subscribing to topic on send", contentTopic = envelope.contentTopic - self.node.subscriptionManager.subscribe(envelope.contentTopic).isOkOr: + self.waku.subscribe(envelope.contentTopic).isOkOr: warn "Failed to auto-subscribe", error = error return err("Failed to auto-subscribe before sending: " & error) - let requestId = RequestId.new(self.node.rng) + let requestId = RequestId.new(self.waku.rng) - let deliveryTask = DeliveryTask.new(requestId, envelope, self.node.brokerCtx).valueOr: + let deliveryTask = DeliveryTask.new(requestId, envelope, self.waku.brokerCtx).valueOr: return err("MessagingClient.send: Failed to create delivery task: " & error) asyncSpawn self.sendService.send(deliveryTask) diff --git a/logos_delivery/messaging/api/subscription.nim b/logos_delivery/messaging/api/subscription.nim index 35b8c7e53..8893af7f1 100644 --- a/logos_delivery/messaging/api/subscription.nim +++ b/logos_delivery/messaging/api/subscription.nim @@ -3,16 +3,17 @@ import results, chronos import logos_delivery/api/types import logos_delivery/messaging/messaging_client -import logos_delivery/waku/node/[waku_node, subscription_manager] +import logos_delivery/waku/waku +import logos_delivery/waku/api/subscriptions proc subscribe*( self: MessagingClient, contentTopic: ContentTopic ): Future[Result[void, string]] {.async.} = ?self.checkApiAvailability() - return self.node.subscriptionManager.subscribe(contentTopic) + return self.waku.subscribe(contentTopic) proc unsubscribe*( self: MessagingClient, contentTopic: ContentTopic ): Result[void, string] = ?self.checkApiAvailability() - return self.node.subscriptionManager.unsubscribe(contentTopic) + return self.waku.unsubscribe(contentTopic) diff --git a/logos_delivery/messaging/delivery_service/recv_service/recv_service.nim b/logos_delivery/messaging/delivery_service/recv_service/recv_service.nim index 25b35222d..1090be223 100644 --- a/logos_delivery/messaging/delivery_service/recv_service/recv_service.nim +++ b/logos_delivery/messaging/delivery_service/recv_service/recv_service.nim @@ -7,15 +7,9 @@ import std/[tables, sequtils, options, sets] import chronos, chronicles, libp2p/utility import brokers/broker_context import - logos_delivery/waku/[ - waku_core, - waku_core/topics, - waku_store/client, - waku_store/common, - waku_filter_v2/client, - waku_node, - node/subscription_manager, - ] + logos_delivery/waku/[waku_core, waku_core/topics, waku_store/common], + logos_delivery/waku/waku, + logos_delivery/waku/api/[store, subscriptions] import logos_delivery/api/kernel_api, # MessageSeenEvent logos_delivery/api/messaging_client_api, # MessageReceivedEvent @@ -38,7 +32,7 @@ type RecvMessage = object type RecvService* = ref object of RootObj brokerCtx: BrokerContext - node: WakuNode + waku: Waku seenMsgListener: MessageSeenEventListener connStatusListener: EventConnectionStatusChangeListener @@ -60,7 +54,7 @@ proc getMissingMsgsFromStore( self: RecvService, msgHashes: seq[WakuMessageHash] ): Future[Result[seq[TupleHashAndMsg], string]] {.async.} = let storeResp: StoreQueryResponse = ( - await self.node.wakuStoreClient.queryToAny( + await self.waku.storeQueryToAny( StoreQueryRequest(includeData: true, messageHashes: msgHashes) ) ).valueOr: @@ -85,9 +79,7 @@ proc processIncomingMessage( ## or if the message is a duplicate (recently-seen). Otherwise, save it as ## recently-seen, emit a MessageReceivedEvent, and return true. - if not self.node.subscriptionManager.isContentSubscribed( - pubsubTopic, message.contentTopic - ): + if not self.waku.isContentSubscribed(pubsubTopic, message.contentTopic): trace "skipping message as I am not subscribed", shard = pubsubTopic, contentTopic = message.contentTopic return false @@ -108,16 +100,16 @@ proc processIncomingMessage( proc checkStore*(self: RecvService) {.async.} = ## Checks the store for messages that were not received directly and ## delivers them via MessageReceivedEvent. - if self.node.wakuStoreClient.isNil(): + if not self.waku.isStoreMounted(): error "recv service has no store client mounted, skipping store check" return self.endTimeToCheck = getNowInNanosecondTime() ## query store and deliver new recovered messages per subscribed topic - for pubsubTopic, contentTopics in self.node.subscriptionManager.subscribedContentTopics: + for (pubsubTopic, contentTopics) in self.waku.subscribedContentTopics(): let storeResp: StoreQueryResponse = ( - await self.node.wakuStoreClient.queryToAny( + await self.waku.storeQueryToAny( StoreQueryRequest( includeData: false, pubsubTopic: some(pubsubTopic), @@ -171,14 +163,14 @@ proc onConnectionStatusChange(self: RecvService, status: ConnectionStatus) = info "recv service backfilling missed messages after coming back online" self.backfillHandler = self.checkStore() -proc new*(T: typedesc[RecvService], node: WakuNode): T = +proc new*(T: typedesc[RecvService], waku: Waku): T = ## The storeClient will help to acquire any possible missed messages let now = getNowInNanosecondTime() var recvService = RecvService( - node: node, + waku: waku, startTimeToCheck: now, - brokerCtx: node.brokerCtx, + brokerCtx: waku.brokerCtx, recentReceivedMsgs: @[], ) diff --git a/logos_delivery/messaging/delivery_service/send_service/lightpush_processor.nim b/logos_delivery/messaging/delivery_service/send_service/lightpush_processor.nim index ae5e775aa..fa199774b 100644 --- a/logos_delivery/messaging/delivery_service/send_service/lightpush_processor.nim +++ b/logos_delivery/messaging/delivery_service/send_service/lightpush_processor.nim @@ -2,10 +2,8 @@ import logos_delivery/waku/compat/option_valueor import chronicles, chronos, results import std/options import brokers/broker_context -import - logos_delivery/waku/node/peer_manager, - logos_delivery/waku/waku_core, - logos_delivery/waku/waku_lightpush/[common, client, rpc] +import logos_delivery/waku/waku_core, logos_delivery/waku/waku +import logos_delivery/waku/api/publish import ./[delivery_task, send_processor] @@ -13,27 +11,17 @@ logScope: topics = "send service lightpush processor" type LightpushSendProcessor* = ref object of BaseSendProcessor - peerManager: PeerManager - lightpushClient: WakuLightPushClient + waku: Waku proc new*( - T: typedesc[LightpushSendProcessor], - peerManager: PeerManager, - lightpushClient: WakuLightPushClient, - brokerCtx: BrokerContext, + T: typedesc[LightpushSendProcessor], waku: Waku, brokerCtx: BrokerContext ): T = - return - T(peerManager: peerManager, lightpushClient: lightpushClient, brokerCtx: brokerCtx) - -proc isLightpushPeerAvailable( - self: LightpushSendProcessor, pubsubTopic: PubsubTopic -): bool = - return self.peerManager.selectPeer(WakuLightPushCodec, some(pubsubTopic)).isSome() + return T(waku: waku, brokerCtx: brokerCtx) method isValidProcessor*( self: LightpushSendProcessor, task: DeliveryTask ): bool {.gcsafe.} = - return self.isLightpushPeerAvailable(task.pubsubTopic) + return self.waku.lightpushPeerAvailable(task.pubsubTopic) method sendImpl*( self: LightpushSendProcessor, task: DeliveryTask @@ -44,14 +32,8 @@ method sendImpl*( msgHash = task.msgHash.to0xHex(), tryCount = task.tryCount - let peer = self.peerManager.selectPeer(WakuLightPushCodec, some(task.pubsubTopic)).valueOr: - debug "No peer available for Lightpush, request pushed back for next round", - requestId = task.requestId - task.state = DeliveryState.NextRoundRetry - return - let numLightpushServers = ( - await self.lightpushClient.publish(some(task.pubsubTopic), task.msg, peer) + await self.waku.lightpushPublishToAny(task.pubsubTopic, task.msg) ).valueOr: error "LightpushSendProcessor.sendImpl failed", error = error.desc.get($error.code) case error.code diff --git a/logos_delivery/messaging/delivery_service/send_service/send_service.nim b/logos_delivery/messaging/delivery_service/send_service/send_service.nim index f5d1f302b..f482a2aff 100644 --- a/logos_delivery/messaging/delivery_service/send_service/send_service.nim +++ b/logos_delivery/messaging/delivery_service/send_service/send_service.nim @@ -7,18 +7,9 @@ import chronos, chronicles, libp2p/utility import brokers/broker_context import ./[send_processor, relay_processor, lightpush_processor, delivery_task], - logos_delivery/waku/[ - waku_core, - node/waku_node, - node/subscription_manager, - node/peer_manager, - waku_store/client, - waku_store/common, - waku_relay/protocol, - rln/rln, - waku_lightpush/client, - waku_lightpush/callbacks, - ] + logos_delivery/waku/[waku_core, waku_store/common], + logos_delivery/waku/waku, + logos_delivery/waku/api/[store, subscriptions, publish] import logos_delivery/api/messaging_client_api logScope: @@ -57,19 +48,15 @@ type SendService* = ref object of RootObj serviceLoopHandle: Future[void] ## handle that allows to stop the async task sendProcessor: BaseSendProcessor - node: WakuNode + waku: Waku checkStoreForMessages: bool lastStoreCheckTime: Moment ## throttles store validation queries to ArchiveTime cadence proc setupSendProcessorChain( - peerManager: PeerManager, - lightpushClient: WakuLightPushClient, - relay: WakuRelay, - rlnRelay: Rln, - brokerCtx: BrokerContext, + waku: Waku, brokerCtx: BrokerContext ): Result[BaseSendProcessor, string] = - let isRelayAvail = not relay.isNil() - let isLightPushAvail = not lightpushClient.isNil() + let isRelayAvail = waku.hasRelay() + let isLightPushAvail = waku.hasLightpush() if not isRelayAvail and not isLightPushAvail: return err("No valid send processor found for the delivery task") @@ -77,16 +64,10 @@ proc setupSendProcessorChain( var processors = newSeq[BaseSendProcessor]() if isRelayAvail: - let rln: Option[Rln] = - if rlnRelay.isNil(): - none[Rln]() - else: - some(rlnRelay) - let publishProc = getRelayPushHandler(relay, rln) - + let publishProc = waku.relayPushHandler() processors.add(RelaySendProcessor.new(isLightPushAvail, publishProc, brokerCtx)) if isLightPushAvail: - processors.add(LightpushSendProcessor.new(peerManager, lightpushClient, brokerCtx)) + processors.add(LightpushSendProcessor.new(waku, brokerCtx)) var currentProcessor: BaseSendProcessor = processors[0] for i in 1 ..< processors.len: @@ -97,26 +78,24 @@ proc setupSendProcessorChain( return ok(processors[0]) proc new*( - T: typedesc[SendService], preferP2PReliability: bool, w: WakuNode + T: typedesc[SendService], preferP2PReliability: bool, waku: Waku ): Result[T, string] = - if w.wakuRelay.isNil() and w.wakuLightpushClient.isNil(): + if not waku.hasRelay() and not waku.hasLightpush(): return err( "Could not create SendService. wakuRelay or wakuLightpushClient should be set" ) - let checkStoreForMessages = preferP2PReliability and not w.wakuStoreClient.isNil() + let checkStoreForMessages = preferP2PReliability and waku.isStoreMounted() - let sendProcessorChain = setupSendProcessorChain( - w.peerManager, w.wakuLightPushClient, w.wakuRelay, w.rln, w.brokerCtx - ).valueOr: + let sendProcessorChain = setupSendProcessorChain(waku, waku.brokerCtx).valueOr: return err("failed to setup SendProcessorChain: " & $error) let sendService = SendService( - brokerCtx: w.brokerCtx, + brokerCtx: waku.brokerCtx, taskCache: newSeq[DeliveryTask](), serviceLoopHandle: nil, sendProcessor: sendProcessorChain, - node: w, + waku: waku, checkStoreForMessages: checkStoreForMessages, lastStoreCheckTime: Moment.now(), ) @@ -127,7 +106,7 @@ proc addTask(self: SendService, task: DeliveryTask) = self.taskCache.addUnique(task) proc isStorePeerAvailable*(sendService: SendService): bool = - return sendService.node.peerManager.selectPeer(WakuStoreCodec).isSome() + return sendService.waku.hasStorePeer() proc checkMsgsInStore(self: SendService, tasksToValidate: seq[DeliveryTask]) {.async.} = if tasksToValidate.len() == 0: @@ -142,7 +121,7 @@ proc checkMsgsInStore(self: SendService, tasksToValidate: seq[DeliveryTask]) {.a # TODO: confirm hash format for store query!!! let storeResp: StoreQueryResponse = ( - await self.node.wakuStoreClient.queryToAny( + await self.waku.storeQueryToAny( StoreQueryRequest(includeData: false, messageHashes: hashesToValidate) ) ).valueOr: @@ -292,7 +271,7 @@ proc send*(self: SendService, task: DeliveryTask) {.async.} = info "SendService.send: processing delivery task", requestId = task.requestId, msgHash = task.msgHash.to0xHex() - self.node.subscriptionManager.subscribe(task.msg.contentTopic).isOkOr: + self.waku.subscribe(task.msg.contentTopic).isOkOr: error "SendService.send: failed to subscribe to content topic", contentTopic = task.msg.contentTopic, error = error diff --git a/logos_delivery/messaging/messaging_client.nim b/logos_delivery/messaging/messaging_client.nim index 69c6c520b..4e02c7a82 100644 --- a/logos_delivery/messaging/messaging_client.nim +++ b/logos_delivery/messaging/messaging_client.nim @@ -4,7 +4,7 @@ import results, chronos import logos_delivery/api/messaging_client_api, - logos_delivery/waku/node/waku_node, + logos_delivery/waku/waku, logos_delivery/messaging/delivery_service/[recv_service, send_service] # Surfaces the messaging API interface (and its Message* events) to consumers. @@ -18,19 +18,19 @@ type useP2PReliability*: bool MessagingClient* = ref object of IMessagingClient - node*: WakuNode ## Waku core driven by this layer; read by `messaging/api.nim`. + waku*: Waku ## The Waku kernel this layer drives; read by `messaging/api/*`. sendService*: SendService recvService*: RecvService started: bool proc new*( - T: type MessagingClient, conf: MessagingClientConf, node: WakuNode + T: type MessagingClient, conf: MessagingClientConf, waku: Waku ): Result[T, string] = - ## The messaging layer chains onto Waku: it drives the underlying - ## `WakuNode` (Waku's core) for transport while exposing its own send/recv API. - let sendService = ?SendService.new(conf.useP2PReliability, node) - let recvService = RecvService.new(node) - ok(T(node: node, sendService: sendService, recvService: recvService)) + ## The messaging layer chains onto Waku: it drives the underlying Waku kernel + ## for transport while exposing its own send/recv API. + let sendService = ?SendService.new(conf.useP2PReliability, waku) + let recvService = RecvService.new(waku) + return ok(T(waku: waku, sendService: sendService, recvService: recvService)) proc start*(self: MessagingClient): Result[void, string] = if self.started: diff --git a/logos_delivery/waku/api/publish.nim b/logos_delivery/waku/api/publish.nim new file mode 100644 index 000000000..51259504e --- /dev/null +++ b/logos_delivery/waku/api/publish.nim @@ -0,0 +1,64 @@ +## Waku layer API — message publish primitives used by the messaging send +## pipeline. +## +## Unlike `relay.nim`/`lightpush.nim`, these preserve the rich +## `WakuLightPushResult` (status code + description) that the send processors +## branch on for their retry decisions, and expose relay/lightpush availability +## so the messaging layer never inspects `waku.node` directly. +import logos_delivery/waku/compat/option_valueor +{.push raises: [].} + +import std/options +import results, chronos + +import logos_delivery/waku/waku +import + logos_delivery/waku/[ + waku_core, + node/waku_node, + node/peer_manager, + waku_relay/protocol, + rln, + waku_lightpush/common, + waku_lightpush/rpc, + waku_lightpush/client, + waku_lightpush/callbacks, + ] + +# WakuLightPushResult, PushMessageHandler, LightPushErrorCode (common) plus the +# LightPushStatusCode `$`/`==` the send processors branch on (rpc). +export common, rpc + +proc hasRelay*(self: Waku): bool = + ## True if relay (gossipsub publishing) is mounted. + return not self.node.wakuRelay.isNil() + +proc hasLightpush*(self: Waku): bool = + ## True if a lightpush client is mounted. + return not self.node.wakuLightpushClient.isNil() + +proc relayPushHandler*(self: Waku): PushMessageHandler = + ## Builds the relay publish handler (appending an RLN proof when RLN is + ## mounted) used by the send pipeline. Caller ensures relay is mounted. + let rln = + if self.node.rln.isNil(): + none[Rln]() + else: + some(self.node.rln) + return getRelayPushHandler(self.node.wakuRelay, rln) + +proc lightpushPeerAvailable*(self: Waku, shard: PubsubTopic): bool = + ## True if a lightpush service peer is available for `shard`. + return self.node.peerManager.selectPeer(WakuLightPushCodec, some(shard)).isSome() + +proc lightpushPublishToAny*( + self: Waku, shard: PubsubTopic, message: WakuMessage +): Future[WakuLightPushResult] {.async.} = + ## Selects a lightpush service peer for `shard` and publishes `message`. + ## Returns SERVICE_NOT_AVAILABLE when no peer is available. + let peer = self.node.peerManager.selectPeer(WakuLightPushCodec, some(shard)).valueOr: + return lightpushResultServiceUnavailable("no lightpush peer available for shard") + try: + return await self.node.wakuLightpushClient.publish(some(shard), message, peer) + except CatchableError as e: + return lightpushResultInternalError(e.msg) diff --git a/logos_delivery/waku/api/store.nim b/logos_delivery/waku/api/store.nim index 5095dbb0d..a54ebe1f3 100644 --- a/logos_delivery/waku/api/store.nim +++ b/logos_delivery/waku/api/store.nim @@ -1,11 +1,36 @@ ## Waku layer API — store (historical query) operations. {.push raises: [].} +import std/options import results, chronos, chronicles import logos_delivery/waku/waku import - logos_delivery/waku/[waku_core, node/waku_node, waku_store/common, waku_store/client] + logos_delivery/waku/ + [waku_core, node/waku_node, node/peer_manager, waku_store/common, waku_store/client] + +proc isStoreMounted*(self: Waku): bool = + ## True if a store client is mounted (the node can run store queries). + return not self.node.wakuStoreClient.isNil() + +proc hasStorePeer*(self: Waku): bool = + ## True if at least one store service peer is available to query. + return self.node.peerManager.selectPeer(WakuStoreCodec).isSome() + +proc storeQueryToAny*( + self: Waku, request: StoreQueryRequest +): Future[Result[StoreQueryResponse, string]] {.async.} = + ## Runs a store query against any available store peer (retries across peers). + try: + if self.node.wakuStoreClient.isNil(): + return err("wakuStoreClient is not mounted") + + let queryResponse = (await self.node.wakuStoreClient.queryToAny(request)).valueOr: + return err($error) + + return ok(queryResponse) + except CatchableError as e: + return err(e.msg) proc storeQuery*( self: Waku, request: StoreQueryRequest, peer: string, timeoutMs: int diff --git a/logos_delivery/waku/api/subscriptions.nim b/logos_delivery/waku/api/subscriptions.nim new file mode 100644 index 000000000..7638abf3e --- /dev/null +++ b/logos_delivery/waku/api/subscriptions.nim @@ -0,0 +1,37 @@ +## Waku layer API — content-topic subscription operations. +## +## These wrap the node's `SubscriptionManager`, which resolves each content +## topic to its autosharding shard. They give the layers above (messaging) a +## kernel-level entry point so they never reach into `waku.node` internals. +{.push raises: [].} + +import std/sets +import results + +import logos_delivery/waku/waku +import logos_delivery/waku/[waku_core, node/waku_node, node/subscription_manager] + +proc subscribe*(self: Waku, contentTopic: ContentTopic): Result[void, string] = + ## Subscribes to `contentTopic`, resolving its shard via autosharding. + return self.node.subscriptionManager.subscribe(contentTopic) + +proc unsubscribe*(self: Waku, contentTopic: ContentTopic): Result[void, string] = + ## Unsubscribes from `contentTopic`, resolving its shard via autosharding. + return self.node.subscriptionManager.unsubscribe(contentTopic) + +proc isSubscribed*(self: Waku, contentTopic: ContentTopic): Result[bool, string] = + ## True if the node already subscribes to `contentTopic`. + return self.node.subscriptionManager.isSubscribed(contentTopic) + +proc isContentSubscribed*( + self: Waku, shard: PubsubTopic, contentTopic: ContentTopic +): bool = + ## True if `contentTopic` is subscribed on the given `shard` (pubsub topic). + return self.node.subscriptionManager.isContentSubscribed(shard, contentTopic) + +proc subscribedContentTopics*(self: Waku): seq[(PubsubTopic, HashSet[ContentTopic])] = + ## Snapshot of every shard with its non-empty content-topic set. + var res: seq[(PubsubTopic, HashSet[ContentTopic])] + for shard, contentTopics in self.node.subscriptionManager.subscribedContentTopics: + res.add((shard, contentTopics)) + return res diff --git a/tests/node/test_wakunode_health_monitor.nim b/tests/node/test_wakunode_health_monitor.nim index acf0bc09a..148c93330 100644 --- a/tests/node/test_wakunode_health_monitor.nim +++ b/tests/node/test_wakunode_health_monitor.nim @@ -26,6 +26,8 @@ import import ../testlib/[wakunode, wakucore], ../waku_archive/archive_utils import logos_delivery/waku/node/subscription_manager +import logos_delivery/waku/waku +import logos_delivery/waku/factory/waku_state_info import logos_delivery/messaging/messaging_client const MockDLow = 4 # Mocked GossipSub DLow value @@ -228,8 +230,14 @@ suite "Health Monitor - events": nodeA.mountMetadata(1, @[0'u16]).expect("Node A failed to mount metadata") await nodeA.start() + # MessagingClient now depends on the Waku kernel, not the raw node. Only + # `waku.node` is read on the messaging path; `conf`/`stateInfo` are supplied + # solely to satisfy Waku's {.requiresInit.} fields. + let waku = Waku( + node: nodeA, conf: defaultTestWakuConf(), stateInfo: WakuStateInfo.init(nodeA) + ) let ds = MessagingClient - .new(MessagingClientConf(useP2PReliability: false), nodeA) + .new(MessagingClientConf(useP2PReliability: false), waku) .expect("Failed to create MessagingClient") ds.start().expect("Failed to start MessagingClient") @@ -333,8 +341,14 @@ suite "Health Monitor - events": nodeA.mountMetadata(1, @[0'u16]).expect("Node A failed to mount metadata") await nodeA.start() + # MessagingClient now depends on the Waku kernel, not the raw node. Only + # `waku.node` is read on the messaging path; `conf`/`stateInfo` are supplied + # solely to satisfy Waku's {.requiresInit.} fields. + let waku = Waku( + node: nodeA, conf: defaultTestWakuConf(), stateInfo: WakuStateInfo.init(nodeA) + ) let ds = MessagingClient - .new(MessagingClientConf(useP2PReliability: false), nodeA) + .new(MessagingClientConf(useP2PReliability: false), waku) .expect("Failed to create MessagingClient") ds.start().expect("Failed to start MessagingClient") let subMgr = nodeA.subscriptionManager From 7b6d5d542c24ebc64cfd2f372d4ddee5adcbbeeb Mon Sep 17 00:00:00 2001 From: Fabiana Cecin Date: Tue, 30 Jun 2026 14:59:27 -0300 Subject: [PATCH 20/33] fix: remove hardcoded ports in tests (#3998) * Remove hardcoded ports in tests * Promote getPorts to a public helper in net/net_config.nim * Make 0.0.0.0:0 a default argument for newTestWakuNode --- logos_delivery/waku/net/auto_port.nim | 2 +- logos_delivery/waku/net/net_config.nim | 30 ++- logos_delivery/waku/waku.nim | 24 --- tests/api/test_api_health.nim | 3 +- tests/api/test_api_receive.nim | 6 +- tests/api/test_api_send.nim | 18 +- tests/api/test_api_subscription.nim | 21 +-- tests/factory/test_node_factory.nim | 2 +- tests/node/test_all.nim | 3 +- tests/node/test_wakunode_filter.nim | 29 +-- tests/node/test_wakunode_legacy_lightpush.nim | 22 +-- tests/node/test_wakunode_lightpush.nim | 22 +-- tests/node/test_wakunode_peer_exchange.nim | 74 ++------ tests/node/test_wakunode_peer_manager.nim | 4 +- tests/node/test_wakunode_relay_rln.nim | 10 +- tests/node/test_wakunode_restart.nim | 6 +- tests/node/test_wakunode_store.nim | 31 ++-- tests/test_peer_manager.nim | 137 +++++--------- tests/test_waku_dnsdisc.nim | 22 ++- tests/test_waku_keepalive.nim | 4 +- tests/test_waku_metadata.nim | 24 +-- tests/test_waku_rendezvous.nim | 31 +--- tests/test_wakunode.nim | 54 ++---- tests/testlib/wakunode.nim | 11 +- tests/waku_core/test_published_address.nim | 3 +- tests/waku_discv5/test_waku_discv5.nim | 175 +++++++++--------- tests/waku_filter_v2/test_waku_client.nim | 9 +- tests/waku_peer_exchange/test_protocol.nim | 134 ++++---------- tests/waku_peer_exchange/utils.nim | 31 +++- tests/waku_relay/test_wakunode_relay.nim | 69 ++----- .../test_wakunode_rln_relay.nim | 28 +-- tests/waku_store/test_wakunode_store.nim | 31 ++-- tests/wakunode2/test_validators.nim | 12 +- 33 files changed, 412 insertions(+), 670 deletions(-) diff --git a/logos_delivery/waku/net/auto_port.nim b/logos_delivery/waku/net/auto_port.nim index 38176d27d..3191b25a6 100644 --- a/logos_delivery/waku/net/auto_port.nim +++ b/logos_delivery/waku/net/auto_port.nim @@ -9,7 +9,7 @@ const AutoPortMax = 59000'u16 AutoPortAttemptTimeout = chronos.seconds(30) -proc getAutoPort*(): uint16 = +proc getAutoPort(): uint16 = var rng = initRand() uint16(rng.rand(AutoPortMin.int .. AutoPortMax.int)) diff --git a/logos_delivery/waku/net/net_config.nim b/logos_delivery/waku/net/net_config.nim index b5eda9d6c..7d1fca9ff 100644 --- a/logos_delivery/waku/net/net_config.nim +++ b/logos_delivery/waku/net/net_config.nim @@ -1,7 +1,9 @@ {.push raises: [].} import - std/[options, sequtils, strutils, net], results, libp2p/[multiaddress, multicodec] + std/[options, sequtils, strutils, net], + results, + libp2p/[multiaddress, multicodec, wire] import ../../waku/waku_core/peers import ../waku_enr @@ -74,6 +76,32 @@ proc isWsAddress*(ma: MultiAddress): bool = proc isQuicAddress*(ma: MultiAddress): bool = return ma.hasProtocol("quic-v1") +proc isP2pTcpAddress*(ma: MultiAddress): bool = + return ma.hasProtocol("tcp") and not ma.isWsAddress() + +proc getPorts*( + listenAddrs: seq[MultiAddress] +): Result[tuple[tcpPort, websocketPort, quicPort: Option[Port]], string] = + var tcpPort, websocketPort, quicPort = none(Port) + + for a in listenAddrs: + if a.isWsAddress(): + if websocketPort.isNone(): + let wsAddress = initTAddress(a).valueOr: + return err("getPorts wsAddr error:" & $error) + websocketPort = some(wsAddress.port) + elif a.isQuicAddress(): + if quicPort.isNone(): + let quicAddress = initTAddress(a).valueOr: + return err("getPorts quicAddr error:" & $error) + quicPort = some(quicAddress.port) + elif tcpPort.isNone(): + let tcpAddress = initTAddress(a).valueOr: + return err("getPorts tcpAddr error:" & $error) + tcpPort = some(tcpAddress.port) + + return ok((tcpPort: tcpPort, websocketPort: websocketPort, quicPort: quicPort)) + proc containsWsAddress(extMultiAddrs: seq[MultiAddress]): bool = return extMultiAddrs.filterIt(it.isWsAddress()).len > 0 diff --git a/logos_delivery/waku/waku.nim b/logos_delivery/waku/waku.nim index 6e0e933e0..1dba0f633 100644 --- a/logos_delivery/waku/waku.nim +++ b/logos_delivery/waku/waku.nim @@ -8,7 +8,6 @@ import chronos, libp2p/protocols/connectivity/relay/relay, libp2p/protocols/connectivity/relay/client, - libp2p/wire, libp2p/crypto/crypto, libp2p/protocols/pubsub/gossipsub, libp2p/protocols/ping, @@ -251,29 +250,6 @@ proc new*( ok(waku) -proc getPorts( - listenAddrs: seq[MultiAddress] -): Result[tuple[tcpPort, websocketPort, quicPort: Option[Port]], string] = - var tcpPort, websocketPort, quicPort = none(Port) - - for a in listenAddrs: - if a.isWsAddress(): - if websocketPort.isNone(): - let wsAddress = initTAddress(a).valueOr: - return err("getPorts wsAddr error:" & $error) - websocketPort = some(wsAddress.port) - elif a.isQuicAddress(): - if quicPort.isNone(): - let quicAddress = initTAddress(a).valueOr: - return err("getPorts quicAddr error:" & $error) - quicPort = some(quicAddress.port) - elif tcpPort.isNone(): - let tcpAddress = initTAddress(a).valueOr: - return err("getPorts tcpAddr error:" & $error) - tcpPort = some(tcpAddress.port) - - return ok((tcpPort: tcpPort, websocketPort: websocketPort, quicPort: quicPort)) - proc getRunningNetConfig(waku: Waku): Future[Result[NetConfig, string]] {.async.} = let conf = waku.conf let (tcpPort, websocketPort, quicPort) = getPorts( diff --git a/tests/api/test_api_health.nim b/tests/api/test_api_health.nim index 27030d432..a972d5db6 100644 --- a/tests/api/test_api_health.nim +++ b/tests/api/test_api_health.nim @@ -79,8 +79,7 @@ suite "LM API health checking": asyncSetup: lockNewGlobalBrokerContext: - serviceNode = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + serviceNode = newTestWakuNode(generateSecp256k1Key()) (await serviceNode.mountRelay()).isOkOr: raiseAssert error serviceNode.mountMetadata(3, @[0'u16]).isOkOr: diff --git a/tests/api/test_api_receive.nim b/tests/api/test_api_receive.nim index b1e7635d8..807f36014 100644 --- a/tests/api/test_api_receive.nim +++ b/tests/api/test_api_receive.nim @@ -113,8 +113,7 @@ proc setupNetwork(testTopic: ContentTopic): Future[TestNetwork] {.async.} = # store node: archive + store + relay, subscribed to the shard var storeNode: WakuNode lockNewGlobalBrokerContext: - storeNode = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + storeNode = newTestWakuNode(generateSecp256k1Key()) storeNode.mountMetadata(3, toSeq(0'u16 ..< numShards)).expect( "Failed to mount metadata on storeNode" ) @@ -132,8 +131,7 @@ proc setupNetwork(testTopic: ContentTopic): Future[TestNetwork] {.async.} = # publisher: relay, connected to the store so its messages get archived var publisher: WakuNode lockNewGlobalBrokerContext: - publisher = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + publisher = newTestWakuNode(generateSecp256k1Key()) publisher.mountMetadata(3, toSeq(0'u16 ..< numShards)).expect( "Failed to mount metadata on publisher" ) diff --git a/tests/api/test_api_send.nim b/tests/api/test_api_send.nim index 0ad1cea4a..df1522564 100644 --- a/tests/api/test_api_send.nim +++ b/tests/api/test_api_send.nim @@ -152,8 +152,7 @@ suite "Waku API - Send": asyncSetup: lockNewGlobalBrokerContext: - relayNode1 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + relayNode1 = newTestWakuNode(generateSecp256k1Key()) relayNode1.mountMetadata(3, @[0'u16]).isOkOr: raiseAssert "Failed to mount metadata: " & error (await relayNode1.mountRelay()).isOkOr: @@ -162,8 +161,7 @@ suite "Waku API - Send": await relayNode1.start() lockNewGlobalBrokerContext: - relayNode2 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + relayNode2 = newTestWakuNode(generateSecp256k1Key()) relayNode2.mountMetadata(3, @[0'u16]).isOkOr: raiseAssert "Failed to mount metadata: " & error (await relayNode2.mountRelay()).isOkOr: @@ -172,8 +170,7 @@ suite "Waku API - Send": await relayNode2.start() lockNewGlobalBrokerContext: - lightpushNode = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + lightpushNode = newTestWakuNode(generateSecp256k1Key()) lightpushNode.mountMetadata(3, @[0'u16]).isOkOr: raiseAssert "Failed to mount metadata: " & error (await lightpushNode.mountRelay()).isOkOr: @@ -184,8 +181,7 @@ suite "Waku API - Send": await lightpushNode.start() lockNewGlobalBrokerContext: - storeNode = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + storeNode = newTestWakuNode(generateSecp256k1Key()) storeNode.mountMetadata(3, @[0'u16]).isOkOr: raiseAssert "Failed to mount metadata: " & error (await storeNode.mountRelay()).isOkOr: @@ -461,8 +457,7 @@ suite "Waku API - Send": asyncTest "Send fails with event": var fakeLightpushNode: WakuNode lockNewGlobalBrokerContext: - fakeLightpushNode = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + fakeLightpushNode = newTestWakuNode(generateSecp256k1Key()) fakeLightpushNode.mountMetadata(3, @[0'u16]).isOkOr: raiseAssert "Failed to mount metadata: " & error (await fakeLightpushNode.mountRelay()).isOkOr: @@ -519,8 +514,7 @@ suite "Waku API - Send": ## missing Sent event is the signal that delivery could not be validated. var isolatedStoreNode: WakuNode lockNewGlobalBrokerContext: - isolatedStoreNode = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + isolatedStoreNode = newTestWakuNode(generateSecp256k1Key()) isolatedStoreNode.mountMetadata(3, @[0'u16]).isOkOr: raiseAssert "Failed to mount metadata: " & error (await isolatedStoreNode.mountRelay()).isOkOr: diff --git a/tests/api/test_api_subscription.nim b/tests/api/test_api_subscription.nim index ca9d2c9b3..359e55fbc 100644 --- a/tests/api/test_api_subscription.nim +++ b/tests/api/test_api_subscription.nim @@ -95,8 +95,7 @@ proc setupNetwork( var net = TestNetwork() lockNewGlobalBrokerContext: - net.publisher = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + net.publisher = newTestWakuNode(generateSecp256k1Key()) net.publisher.mountMetadata(3, toSeq(0'u16 ..< numShards)).expect( "Failed to mount metadata" ) @@ -122,8 +121,7 @@ proc setupNetwork( if mode == cli_args.WakuMode.Edge: lockNewGlobalBrokerContext: - net.meshBuddy = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + net.meshBuddy = newTestWakuNode(generateSecp256k1Key()) net.meshBuddy.mountMetadata(3, toSeq(0'u16 ..< numShards)).expect( "Failed to mount metadata on meshBuddy" ) @@ -623,8 +621,7 @@ suite "Messaging API, SubscriptionManager": var publisher: WakuNode lockNewGlobalBrokerContext: - publisher = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + publisher = newTestWakuNode(generateSecp256k1Key()) publisher.mountMetadata(3, toSeq(0'u16 ..< numShards)).expect( "Failed to mount metadata on publisher" ) @@ -642,8 +639,7 @@ suite "Messaging API, SubscriptionManager": var meshBuddy: WakuNode lockNewGlobalBrokerContext: - meshBuddy = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + meshBuddy = newTestWakuNode(generateSecp256k1Key()) meshBuddy.mountMetadata(3, toSeq(0'u16 ..< numShards)).expect( "Failed to mount metadata on meshBuddy" ) @@ -734,8 +730,7 @@ suite "Messaging API, SubscriptionManager": var publisher: WakuNode lockNewGlobalBrokerContext: - publisher = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + publisher = newTestWakuNode(generateSecp256k1Key()) publisher.mountMetadata(3, toSeq(0'u16 ..< numShards)).expect( "Failed to mount metadata on publisher" ) @@ -753,8 +748,7 @@ suite "Messaging API, SubscriptionManager": var meshBuddy: WakuNode lockNewGlobalBrokerContext: - meshBuddy = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + meshBuddy = newTestWakuNode(generateSecp256k1Key()) meshBuddy.mountMetadata(3, toSeq(0'u16 ..< numShards)).expect( "Failed to mount metadata on meshBuddy" ) @@ -772,8 +766,7 @@ suite "Messaging API, SubscriptionManager": var sparePeer: WakuNode lockNewGlobalBrokerContext: - sparePeer = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + sparePeer = newTestWakuNode(generateSecp256k1Key()) sparePeer.mountMetadata(3, toSeq(0'u16 ..< numShards)).expect( "Failed to mount metadata on sparePeer" ) diff --git a/tests/factory/test_node_factory.nim b/tests/factory/test_node_factory.nim index b0b8bc198..b4e8429a3 100644 --- a/tests/factory/test_node_factory.nim +++ b/tests/factory/test_node_factory.nim @@ -171,7 +171,7 @@ suite "Auto-port retry": discard let nodeKey = generateSecp256k1Key() - let node = newTestWakuNode(nodeKey, parseIpAddress("0.0.0.0"), Port(0)) + let node = newTestWakuNode(nodeKey) await node.start() defer: await node.stop() diff --git a/tests/node/test_all.nim b/tests/node/test_all.nim index 2846fdb7f..a53a83086 100644 --- a/tests/node/test_all.nim +++ b/tests/node/test_all.nim @@ -7,4 +7,5 @@ import ./test_wakunode_peer_exchange, ./test_wakunode_store, ./test_wakunode_peer_manager, - ./test_wakunode_health_monitor + ./test_wakunode_health_monitor, + ./test_wakunode_restart diff --git a/tests/node/test_wakunode_filter.nim b/tests/node/test_wakunode_filter.nim index 65c162c0a..dd92c0c37 100644 --- a/tests/node/test_wakunode_filter.nim +++ b/tests/node/test_wakunode_filter.nim @@ -68,12 +68,9 @@ suite "Waku Filter - End to End": serverKey = generateSecp256k1Key() clientKey = generateSecp256k1Key() - server = newTestWakuNode( - serverKey, parseIpAddress("0.0.0.0"), Port(23450), maxConnections = 300 - ) - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(23451)) - clientClone = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(23451)) - # Used for testing client restarts + server = newTestWakuNode(serverKey, maxConnections = 300) + client = newTestWakuNode(clientKey) + clientClone = newTestWakuNode(clientKey) # Used for testing client restarts await allFutures(server.start(), client.start()) @@ -157,7 +154,7 @@ suite "Waku Filter - End to End": # Given a server node with Relay without Filter let serverKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) await server.start() (await server.mountRelay()).isOkOr: @@ -192,7 +189,7 @@ suite "Waku Filter - End to End": ## This line above causes the test to fail. I think ConnManager ## is not prepare for restarts and maybe we don't need that restart feature. - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(23451)) + client = newTestWakuNode(clientKey) await client.start() # Mimic restart by starting the clone # pushHandlerFuture = newFuture[(string, WakuMessage)]() @@ -587,9 +584,7 @@ suite "Waku Filter - End to End": var peers = newSeq[WakuNode](MaxFilterPeers) for index in 0 ..< MaxFilterPeers: - peers[index] = newTestWakuNode( - generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(23551 + index) - ) + peers[index] = newTestWakuNode(generateSecp256k1Key()) await peers[index].start() await peers[index].mountFilterClient() @@ -740,15 +735,9 @@ suite "Waku Filter - End to End": peerManager = server.peerManager let - client1 = newTestWakuNode( - generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(23552) - ) - client2 = newTestWakuNode( - generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(23553) - ) - client3 = newTestWakuNode( - generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(23554) - ) + client1 = newTestWakuNode(generateSecp256k1Key()) + client2 = newTestWakuNode(generateSecp256k1Key()) + client3 = newTestWakuNode(generateSecp256k1Key()) filterSubscribeRequest = createRequest( filterSubscribeType = FilterSubscribeType.SUBSCRIBE, pubsubTopic = some(DefaultPubsubTopic), diff --git a/tests/node/test_wakunode_legacy_lightpush.nim b/tests/node/test_wakunode_legacy_lightpush.nim index 54cc0b6f6..c2e901d41 100644 --- a/tests/node/test_wakunode_legacy_lightpush.nim +++ b/tests/node/test_wakunode_legacy_lightpush.nim @@ -36,8 +36,8 @@ suite "Waku Legacy Lightpush - End To End": serverKey = generateSecp256k1Key() clientKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) + client = newTestWakuNode(clientKey) await allFutures(server.start(), client.start()) await server.start() @@ -59,8 +59,7 @@ suite "Waku Legacy Lightpush - End To End": suite "Assessment of Message Relaying Mechanisms": asyncTest "Via 11/WAKU2-RELAY from Relay/Full Node": # Given a light lightpush client - let lightpushClient = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + let lightpushClient = newTestWakuNode(generateSecp256k1Key()) lightpushClient.mountLegacyLightpushClient() # When the client publishes a message @@ -111,8 +110,8 @@ suite "RLN Proofs as a Lightpush Service": serverKey = generateSecp256k1Key() clientKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) + client = newTestWakuNode(clientKey) anvilProc = runAnvil(stateFile = some(DEFAULT_ANVIL_STATE_PATH)) manager = waitFor setupOnchainGroupManager(deployContracts = false) @@ -159,8 +158,7 @@ suite "RLN Proofs as a Lightpush Service": suite "Lightpush attaching RLN proofs": asyncTest "Message is published when RLN enabled": # Given a light lightpush client - let lightpushClient = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + let lightpushClient = newTestWakuNode(generateSecp256k1Key()) lightpushClient.mountLegacyLightPushClient() # When the client publishes a message @@ -180,11 +178,11 @@ suite "Waku Legacy Lightpush message delivery": ## Setup let lightNodeKey = generateSecp256k1Key() - lightNode = newTestWakuNode(lightNodeKey, parseIpAddress("0.0.0.0"), Port(0)) + lightNode = newTestWakuNode(lightNodeKey) bridgeNodeKey = generateSecp256k1Key() - bridgeNode = newTestWakuNode(bridgeNodeKey, parseIpAddress("0.0.0.0"), Port(0)) + bridgeNode = newTestWakuNode(bridgeNodeKey) destNodeKey = generateSecp256k1Key() - destNode = newTestWakuNode(destNodeKey, parseIpAddress("0.0.0.0"), Port(0)) + destNode = newTestWakuNode(destNodeKey) await allFutures(destNode.start(), bridgeNode.start(), lightNode.start()) @@ -234,7 +232,7 @@ suite "Waku Legacy Lightpush mounting behavior": ## Given a node without Relay mounted let key = generateSecp256k1Key() - node = newTestWakuNode(key, parseIpAddress("0.0.0.0"), Port(0)) + node = newTestWakuNode(key) # Do not mount Relay on purpose check node.wakuRelay.isNil() diff --git a/tests/node/test_wakunode_lightpush.nim b/tests/node/test_wakunode_lightpush.nim index 6c28f5083..cc82cb7a3 100644 --- a/tests/node/test_wakunode_lightpush.nim +++ b/tests/node/test_wakunode_lightpush.nim @@ -33,8 +33,8 @@ suite "Waku Lightpush - End To End": serverKey = generateSecp256k1Key() clientKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) + client = newTestWakuNode(clientKey) await allFutures(server.start(), client.start()) await server.start() @@ -55,8 +55,7 @@ suite "Waku Lightpush - End To End": suite "Assessment of Message Relaying Mechanisms": asyncTest "Via 11/WAKU2-RELAY from Relay/Full Node": # Given a light lightpush client - let lightpushClient = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + let lightpushClient = newTestWakuNode(generateSecp256k1Key()) lightpushClient.mountLightpushClient() # When the client publishes a message @@ -108,8 +107,8 @@ suite "RLN Proofs as a Lightpush Service": serverKey = generateSecp256k1Key() clientKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) + client = newTestWakuNode(clientKey) anvilProc = runAnvil(stateFile = some(DEFAULT_ANVIL_STATE_PATH)) manager = waitFor setupOnchainGroupManager(deployContracts = false) @@ -156,8 +155,7 @@ suite "RLN Proofs as a Lightpush Service": suite "Lightpush attaching RLN proofs": asyncTest "Message is published when RLN enabled": # Given a light lightpush client - let lightpushClient = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + let lightpushClient = newTestWakuNode(generateSecp256k1Key()) lightpushClient.mountLightPushClient() # When the client publishes a message @@ -177,11 +175,11 @@ suite "Waku Lightpush message delivery": ## Setup let lightNodeKey = generateSecp256k1Key() - lightNode = newTestWakuNode(lightNodeKey, parseIpAddress("0.0.0.0"), Port(0)) + lightNode = newTestWakuNode(lightNodeKey) bridgeNodeKey = generateSecp256k1Key() - bridgeNode = newTestWakuNode(bridgeNodeKey, parseIpAddress("0.0.0.0"), Port(0)) + bridgeNode = newTestWakuNode(bridgeNodeKey) destNodeKey = generateSecp256k1Key() - destNode = newTestWakuNode(destNodeKey, parseIpAddress("0.0.0.0"), Port(0)) + destNode = newTestWakuNode(destNodeKey) await allFutures(destNode.start(), bridgeNode.start(), lightNode.start()) @@ -233,7 +231,7 @@ suite "Waku Lightpush mounting behavior": ## Given a node without Relay mounted let key = generateSecp256k1Key() - node = newTestWakuNode(key, parseIpAddress("0.0.0.0"), Port(0)) + node = newTestWakuNode(key) # Do not mount Relay on purpose check node.wakuRelay.isNil() diff --git a/tests/node/test_wakunode_peer_exchange.nim b/tests/node/test_wakunode_peer_exchange.nim index 47bd5b8af..e70abadc9 100644 --- a/tests/node/test_wakunode_peer_exchange.nim +++ b/tests/node/test_wakunode_peer_exchange.nim @@ -187,74 +187,28 @@ suite "Waku Peer Exchange with discv5": lightpush = false, filter = false, store = false, relay = true ) bindIp = parseIpAddress("0.0.0.0") - extIp = parseIpAddress("127.0.0.1") nodeKey1 = generateSecp256k1Key() - nodeTcpPort1 = Port(64010) - nodeUdpPort1 = Port(9000) - node1 = newTestWakuNode( - nodeKey1, - bindIp, - nodeTcpPort1, - some(extIp), - wakuFlags = some(flags), - discv5UdpPort = some(nodeUdpPort1), - ) + node1 = newTestWakuNode(nodeKey1, bindIp, Port(0), wakuFlags = some(flags)) nodeKey2 = generateSecp256k1Key() - nodeTcpPort2 = Port(64012) - nodeUdpPort2 = Port(9002) - node2 = newTestWakuNode( - nodeKey2, - bindIp, - nodeTcpPort2, - some(extIp), - wakuFlags = some(flags), - discv5UdpPort = some(nodeUdpPort2), - ) + node2 = newTestWakuNode(nodeKey2, bindIp, Port(0), wakuFlags = some(flags)) nodeKey3 = generateSecp256k1Key() - nodeTcpPort3 = Port(64014) - nodeUdpPort3 = Port(9004) - node3 = newTestWakuNode( - nodeKey3, - bindIp, - nodeTcpPort3, - some(extIp), - wakuFlags = some(flags), - discv5UdpPort = some(nodeUdpPort3), - ) - - # discv5 - let conf1 = WakuDiscoveryV5Config( - discv5Config: none(DiscoveryConfig), - address: bindIp, - port: nodeUdpPort1, - privateKey: keys.PrivateKey(nodeKey1.skkey), - bootstrapRecords: @[], - autoupdateRecord: true, - ) - - let disc1 = - WakuDiscoveryV5.new(node1.rng, conf1, some(node1.enr), some(node1.peerManager)) - - let conf2 = WakuDiscoveryV5Config( - discv5Config: none(DiscoveryConfig), - address: bindIp, - port: nodeUdpPort2, - privateKey: keys.PrivateKey(nodeKey2.skkey), - bootstrapRecords: @[disc1.protocol.getRecord()], - autoupdateRecord: true, - ) - - let disc2 = - WakuDiscoveryV5.new(node2.rng, conf2, some(node2.enr), some(node2.peerManager)) + node3 = newTestWakuNode(nodeKey3, bindIp, Port(0), wakuFlags = some(flags)) await allFutures(node1.start(), node2.start(), node3.start()) - let resultDisc1StartRes = await disc1.start() - assert resultDisc1StartRes.isOk(), resultDisc1StartRes.error - let resultDisc2StartRes = await disc2.start() - assert resultDisc2StartRes.isOk(), resultDisc2StartRes.error + + let disc1 = ( + await startDiscv5WithAutoPort(node1, keys.PrivateKey(nodeKey1.skkey), bindIp) + ).valueOr: + raiseAssert "disc1: " & error + let disc2 = ( + await startDiscv5WithAutoPort( + node2, keys.PrivateKey(nodeKey2.skkey), bindIp, @[disc1.protocol.getRecord()] + ) + ).valueOr: + raiseAssert "disc2: " & error ## When var attempts = 10 diff --git a/tests/node/test_wakunode_peer_manager.nim b/tests/node/test_wakunode_peer_manager.nim index a43f76d97..0f85d61fe 100644 --- a/tests/node/test_wakunode_peer_manager.nim +++ b/tests/node/test_wakunode_peer_manager.nim @@ -63,9 +63,9 @@ suite "Peer Manager": serverKey = generateSecp256k1Key() clientKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, listenIp, Port(3000)) + server = newTestWakuNode(serverKey, listenIp, Port(0)) serverPeerStore = server.peerManager.switch.peerStore - client = newTestWakuNode(clientKey, listenIp, Port(3001)) + client = newTestWakuNode(clientKey, listenIp, Port(0)) clientPeerStore = client.peerManager.switch.peerStore await allFutures(server.start(), client.start()) diff --git a/tests/node/test_wakunode_relay_rln.nim b/tests/node/test_wakunode_relay_rln.nim index 823fde4b5..76074bb6d 100644 --- a/tests/node/test_wakunode_relay_rln.nim +++ b/tests/node/test_wakunode_relay_rln.nim @@ -116,8 +116,8 @@ suite "Waku RlnRelay - End to End - Static": serverKey = generateSecp256k1Key() clientKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) + client = newTestWakuNode(clientKey) await allFutures(server.start(), client.start()) @@ -198,7 +198,7 @@ suite "Waku RlnRelay - End to End - Static": asyncTest "rln-relay-max-message-limit testing": let nodekey = generateSecp256k1Key() - node = newTestWakuNode(nodekey, parseIpAddress("0.0.0.0"), Port(0)) + node = newTestWakuNode(nodekey) await node.mountRelay(@[DefaultRelayShard]) @@ -384,8 +384,8 @@ suite "Waku RlnRelay - End to End - OnChain": serverKey = generateSecp256k1Key() clientKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) + client = newTestWakuNode(clientKey) await allFutures(server.start(), client.start()) diff --git a/tests/node/test_wakunode_restart.nim b/tests/node/test_wakunode_restart.nim index 8d58f5b9d..8d05ce131 100644 --- a/tests/node/test_wakunode_restart.nim +++ b/tests/node/test_wakunode_restart.nim @@ -11,10 +11,8 @@ suite "WakuNode - restart (#3979)": asyncTest "start -> stop -> start re-opens the listener promptly": ## A restart must not block on the relay-reconnect backoff. let - node1 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - node2 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(generateSecp256k1Key()) + node2 = newTestWakuNode(generateSecp256k1Key()) (await node1.mountRelay()).isOkOr: raiseAssert "mountRelay node1: " & error diff --git a/tests/node/test_wakunode_store.nim b/tests/node/test_wakunode_store.nim index 3d62a17c7..b0dc3fc28 100644 --- a/tests/node/test_wakunode_store.nim +++ b/tests/node/test_wakunode_store.nim @@ -69,8 +69,8 @@ suite "Waku Store - End to End - Sorted Archive": serverKey = generateSecp256k1Key() clientKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) + client = newTestWakuNode(clientKey) archiveDriver = newArchiveDriverWithMessages(pubsubTopic, messages) let mountArchiveResult = server.mountArchive(archiveDriver) @@ -473,8 +473,7 @@ suite "Waku Store - End to End - Sorted Archive": pubsubTopic, archiveMessages.mapIt(it.message.get()) ) otherServerKey = generateSecp256k1Key() - otherServer = - newTestWakuNode(otherServerKey, parseIpAddress("0.0.0.0"), Port(0)) + otherServer = newTestWakuNode(otherServerKey) mountOtherArchiveResult = otherServer.mountArchive(otherArchiveDriverWithMessages) assert mountOtherArchiveResult.isOk() @@ -564,8 +563,8 @@ suite "Waku Store - End to End - Unsorted Archive": serverKey = generateSecp256k1Key() clientKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) + client = newTestWakuNode(clientKey) let unsortedArchiveDriverWithMessages = @@ -781,8 +780,8 @@ suite "Waku Store - End to End - Unsorted Archive without provided Timestamp": serverKey = generateSecp256k1Key() clientKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) + client = newTestWakuNode(clientKey) let unsortedArchiveDriverWithMessages = @@ -930,8 +929,8 @@ suite "Waku Store - End to End - Archive with Multiple Topics": serverKey = generateSecp256k1Key() clientKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) + client = newTestWakuNode(clientKey) let archiveDriver = newSqliteArchiveDriver().put(pubsubTopic, messages[0 ..< 6]).put( pubsubTopicB, messages[6 ..< 10] @@ -1179,8 +1178,7 @@ suite "Waku Store - End to End - Archive with Multiple Topics": # And a server node with the ephemeral archive let ephemeralServerKey = generateSecp256k1Key() - ephemeralServer = - newTestWakuNode(ephemeralServerKey, parseIpAddress("0.0.0.0"), Port(0)) + ephemeralServer = newTestWakuNode(ephemeralServerKey) mountEphemeralArchiveResult = ephemeralServer.mountArchive(ephemeralArchiveDriver) assert mountEphemeralArchiveResult.isOk() @@ -1219,8 +1217,7 @@ suite "Waku Store - End to End - Archive with Multiple Topics": # And a server node with the mixed archive let mixedServerKey = generateSecp256k1Key() - mixedServer = - newTestWakuNode(mixedServerKey, parseIpAddress("0.0.0.0"), Port(0)) + mixedServer = newTestWakuNode(mixedServerKey) mountMixedArchiveResult = mixedServer.mountArchive(mixedArchiveDriver) assert mountMixedArchiveResult.isOk() @@ -1246,8 +1243,7 @@ suite "Waku Store - End to End - Archive with Multiple Topics": # And a server node with the empty archive let emptyServerKey = generateSecp256k1Key() - emptyServer = - newTestWakuNode(emptyServerKey, parseIpAddress("0.0.0.0"), Port(0)) + emptyServer = newTestWakuNode(emptyServerKey) mountEmptyArchiveResult = emptyServer.mountArchive(emptyArchiveDriver) assert mountEmptyArchiveResult.isOk() @@ -1286,8 +1282,7 @@ suite "Waku Store - End to End - Archive with Multiple Topics": # And a server node with the voluminous archive let voluminousServerKey = generateSecp256k1Key() - voluminousServer = - newTestWakuNode(voluminousServerKey, parseIpAddress("0.0.0.0"), Port(0)) + voluminousServer = newTestWakuNode(voluminousServerKey) mountVoluminousArchiveResult = voluminousServer.mountArchive(voluminousArchiveDriverWithMessages) assert mountVoluminousArchiveResult.isOk() diff --git a/tests/test_peer_manager.nim b/tests/test_peer_manager.nim index d4c2af5b5..b364fc8c3 100644 --- a/tests/test_peer_manager.nim +++ b/tests/test_peer_manager.nim @@ -2,11 +2,13 @@ import std/[sequtils, times, sugar, net, options], + results, testutils/unittests, chronos, json_rpc/rpcserver, json_rpc/rpcclient, eth/keys, + eth/p2p/discoveryv5/enr, eth/common/eth_types, libp2p/[builders, switch, multiaddress], libp2p/protobuf/minprotobuf, @@ -34,12 +36,17 @@ import ./testlib/wakucore, ./testlib/wakunode +proc syncToBoundPort(node: WakuNode, key: keys.PrivateKey) = + let addrs = node.switch.peerInfo.listenAddrs + node.switch.peerInfo.addrs = addrs + node.announcedAddresses = addrs + node.enr.update(key, tcpPort = Opt.some(node.boundTcpPort())).isOkOr: + raiseAssert "failed to sync node ENR to bound tcp port: " & $error + procSuite "Peer Manager": asyncTest "connectPeer() works": # Create 2 nodes - let nodes = toSeq(0 ..< 2).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 2).mapIt(newTestWakuNode(generateSecp256k1Key())) await allFutures(nodes.mapIt(it.start())) let connOk = @@ -55,9 +62,7 @@ procSuite "Peer Manager": Connectedness.Connected asyncTest "Peer manager tracks active store request state": - let nodes = toSeq(0 ..< 2).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 2).mapIt(newTestWakuNode(generateSecp256k1Key())) await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.mountRelay())) @@ -94,9 +99,7 @@ procSuite "Peer Manager": asyncTest "dialPeer() works": # Create 2 nodes - let nodes = toSeq(0 ..< 2).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 2).mapIt(newTestWakuNode(generateSecp256k1Key())) await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.mountRelay())) @@ -129,9 +132,7 @@ procSuite "Peer Manager": asyncTest "dialPeer() fails gracefully": # Create 2 nodes and start them - let nodes = toSeq(0 ..< 2).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 2).mapIt(newTestWakuNode(generateSecp256k1Key())) await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.mountRelay())) @@ -158,7 +159,7 @@ procSuite "Peer Manager": asyncTest "Adding, selecting and filtering peers work": let - node = newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + node = newTestWakuNode(generateSecp256k1Key()) # Create filter peer filterLoc = MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet() @@ -190,9 +191,7 @@ procSuite "Peer Manager": asyncTest "Peer manager keeps track of connections": # Create 2 nodes - let nodes = toSeq(0 ..< 2).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 2).mapIt(newTestWakuNode(generateSecp256k1Key())) await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.mountRelay())) @@ -243,9 +242,7 @@ procSuite "Peer Manager": asyncTest "Peer manager updates failed peers correctly": # Create 2 nodes - let nodes = toSeq(0 ..< 2).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 2).mapIt(newTestWakuNode(generateSecp256k1Key())) await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.mountRelay())) @@ -311,13 +308,13 @@ procSuite "Peer Manager": node1 = newTestWakuNode( generateSecp256k1Key(), getPrimaryIPAddr(), - Port(44048), + Port(0), peerStorage = storage, quicEnabled = false, ) - node2 = newTestWakuNode( - generateSecp256k1Key(), getPrimaryIPAddr(), Port(34023), quicEnabled = false - ) + node2Key = generateSecp256k1Key() + node2 = + newTestWakuNode(node2Key, getPrimaryIPAddr(), Port(0), quicEnabled = false) node1.mountMetadata(0, @[0'u16]).expect("Mounted Waku Metadata") node2.mountMetadata(0, @[0'u16]).expect("Mounted Waku Metadata") @@ -330,6 +327,8 @@ procSuite "Peer Manager": (await node2.mountRelay()).isOkOr: assert false, "Failed to mount relay" + node2.syncToBoundPort(keys.PrivateKey(node2Key.skkey)) + let peerInfo2 = node2.switch.peerInfo var remotePeerInfo2 = peerInfo2.toRemotePeerInfo() remotePeerInfo2.enr = some(node2.enr) @@ -354,7 +353,7 @@ procSuite "Peer Manager": let node3 = newTestWakuNode( generateSecp256k1Key(), parseIpAddress("127.0.0.1"), - Port(56037), + Port(0), peerStorage = storage, quicEnabled = false, ) @@ -392,13 +391,13 @@ procSuite "Peer Manager": node1 = newTestWakuNode( generateSecp256k1Key(), getPrimaryIPAddr(), - Port(44048), + Port(0), peerStorage = storage, quicEnabled = false, ) - node2 = newTestWakuNode( - generateSecp256k1Key(), getPrimaryIPAddr(), Port(34023), quicEnabled = false - ) + node2Key = generateSecp256k1Key() + node2 = + newTestWakuNode(node2Key, getPrimaryIPAddr(), Port(0), quicEnabled = false) node1.mountMetadata(0, @[0'u16]).expect("Mounted Waku Metadata") node2.mountMetadata(0, @[0'u16]).expect("Mounted Waku Metadata") @@ -411,6 +410,8 @@ procSuite "Peer Manager": (await node2.mountRelay()).isOkOr: assert false, "Failed to mount relay" + node2.syncToBoundPort(keys.PrivateKey(node2Key.skkey)) + let peerInfo2 = node2.switch.peerInfo var remotePeerInfo2 = peerInfo2.toRemotePeerInfo() remotePeerInfo2.enr = some(node2.enr) @@ -435,7 +436,7 @@ procSuite "Peer Manager": let node3 = newTestWakuNode( generateSecp256k1Key(), parseIpAddress("127.0.0.1"), - Port(56037), + Port(0), peerStorage = storage, quicEnabled = false, ) @@ -525,14 +526,8 @@ procSuite "Peer Manager": let database = SqliteDatabase.new(":memory:")[] storage = WakuPeerStorage.new(database)[] - node1 = newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - peerStorage = storage, - ) - node2 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(generateSecp256k1Key(), peerStorage = storage) + node2 = newTestWakuNode(generateSecp256k1Key()) peerInfo2 = node2.switch.peerInfo betaCodec = "/vac/waku/relay/2.0.0-beta2" stableCodec = "/vac/waku/relay/2.0.0" @@ -559,9 +554,7 @@ procSuite "Peer Manager": node1.peerManager.switch.peerStore.connectedness(peerInfo2.peerId) == Connected # Simulate restart by initialising a new node using the same storage - let node3 = newTestWakuNode( - generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0), peerStorage = storage - ) + let node3 = newTestWakuNode(generateSecp256k1Key(), peerStorage = storage) (await node3.mountRelay()).isOkOr: assert false, "Failed to mount relay" @@ -595,8 +588,6 @@ procSuite "Peer Manager": let nodes = toSeq(0 ..< 4).mapIt( newTestWakuNode( nodeKey = generateSecp256k1Key(), - bindIp = parseIpAddress("0.0.0.0"), - bindPort = Port(0), wakuFlags = some(CapabilitiesBitfield.init(@[Relay])), ) ) @@ -668,8 +659,6 @@ procSuite "Peer Manager": let nodes = toSeq(0 ..< 4).mapIt( newTestWakuNode( nodeKey = generateSecp256k1Key(), - bindIp = parseIpAddress("0.0.0.0"), - bindPort = Port(0), wakuFlags = some(CapabilitiesBitfield.init(@[Relay])), ) ) @@ -747,9 +736,7 @@ procSuite "Peer Manager": asyncTest "Peer store keeps track of incoming connections": # Create 4 nodes - let nodes = toSeq(0 ..< 4).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 4).mapIt(newTestWakuNode(generateSecp256k1Key())) # Start them await allFutures(nodes.mapIt(it.start())) @@ -842,7 +829,7 @@ procSuite "Peer Manager": let basePeerId = "16Uiu2HAm7QGEZKujdSbbo1aaQyfDPQ6Bw3ybQnj6fruH5Dxwd7D" let - node = newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + node = newTestWakuNode(generateSecp256k1Key()) peers = toSeq(1 .. 4) .mapIt(parsePeerInfo("/ip4/0.0.0.0/tcp/30300/p2p/" & basePeerId & $it)) .filterIt(it.isOk()) @@ -880,9 +867,7 @@ procSuite "Peer Manager": asyncTest "connectedPeers() returns expected number of connections per protocol": # Create 4 nodes - let nodes = toSeq(0 ..< 4).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 4).mapIt(newTestWakuNode(generateSecp256k1Key())) # Start them with relay + filter await allFutures(nodes.mapIt(it.start())) @@ -935,9 +920,7 @@ procSuite "Peer Manager": asyncTest "getNumStreams() returns expected number of connections per protocol": # Create 2 nodes - let nodes = toSeq(0 ..< 2).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 2).mapIt(newTestWakuNode(generateSecp256k1Key())) # Start them with relay + filter await allFutures(nodes.mapIt(it.start())) @@ -1210,9 +1193,7 @@ procSuite "Peer Manager": asyncTest "colocationLimit is enforced by pruneConnsByIp()": # Create 5 nodes - let nodes = toSeq(0 ..< 5).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 5).mapIt(newTestWakuNode(generateSecp256k1Key())) # Start them with relay + filter await allFutures(nodes.mapIt(it.start())) @@ -1275,25 +1256,13 @@ procSuite "Peer Manager": # Create 3 nodes with different shards let nodes = @[ newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = clusterId, - subscribeShards = @[shardId0], + generateSecp256k1Key(), clusterId = clusterId, subscribeShards = @[shardId0] ), newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = clusterId, - subscribeShards = @[shardId1], + generateSecp256k1Key(), clusterId = clusterId, subscribeShards = @[shardId1] ), newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = clusterId, - subscribeShards = @[shardId0], + generateSecp256k1Key(), clusterId = clusterId, subscribeShards = @[shardId0] ), ] @@ -1399,18 +1368,12 @@ procSuite "Peer Manager": asyncTest "selectPeer() handles invalid pubsub topic gracefully": ## Given: A peer manager with valid peers - let node = newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = 0, - subscribeShards = @[0'u16], - ) + let node = + newTestWakuNode(generateSecp256k1Key(), clusterId = 0, subscribeShards = @[0'u16]) await node.start() # Add a peer - let peer = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + let peer = newTestWakuNode(generateSecp256k1Key()) await peer.start() discard await peer.mountRelay() @@ -1443,22 +1406,14 @@ procSuite "Peer Manager": shardId1 = 1.uint16 let node = newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = clusterId, - subscribeShards = @[shardId0], + generateSecp256k1Key(), clusterId = clusterId, subscribeShards = @[shardId0] ) await node.start() discard await node.mountRelay() # Create peer with ENR containing shard 0 let peer = newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = clusterId, - subscribeShards = @[shardId0], + generateSecp256k1Key(), clusterId = clusterId, subscribeShards = @[shardId0] ) await peer.start() discard await peer.mountRelay() diff --git a/tests/test_waku_dnsdisc.nim b/tests/test_waku_dnsdisc.nim index d0430adb0..13b5ef6c8 100644 --- a/tests/test_waku_dnsdisc.nim +++ b/tests/test_waku_dnsdisc.nim @@ -17,25 +17,22 @@ import logos_delivery/waku/discovery/waku_dnsdisc, ./testlib/common, ./testlib/wakucore, - ./testlib/wakunode + ./testlib/wakunode, + ./waku_enr/utils suite "Waku DNS Discovery": asyncTest "Waku DNS Discovery end-to-end": ## Tests integrated DNS discovery, from building ## the tree to connecting to discovered nodes - # Create nodes and ENR. These will be added to the discoverable list let - bindIp = parseIpAddress("0.0.0.0") + bindIp = parseIpAddress("127.0.0.1") nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode(nodeKey1, bindIp, Port(63500)) - enr1 = node1.enr + node1 = newTestWakuNode(nodeKey1, bindIp, Port(0)) nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, bindIp, Port(63502)) - enr2 = node2.enr + node2 = newTestWakuNode(nodeKey2, bindIp, Port(0)) nodeKey3 = generateSecp256k1Key() - node3 = newTestWakuNode(nodeKey3, bindIp, Port(63503)) - enr3 = node3.enr + node3 = newTestWakuNode(nodeKey3, bindIp, Port(0)) (await node1.mountRelay()).isOkOr: assert false, "Failed to mount relay" @@ -45,6 +42,11 @@ suite "Waku DNS Discovery": assert false, "Failed to mount relay" await allFutures([node1.start(), node2.start(), node3.start()]) + let + enr1 = newTestEnrRecord(nodeKey1, $bindIp, uint16(node1.boundTcpPort()), 0) + enr2 = newTestEnrRecord(nodeKey2, $bindIp, uint16(node2.boundTcpPort()), 0) + enr3 = newTestEnrRecord(nodeKey3, $bindIp, uint16(node3.boundTcpPort()), 0) + # Build and sign tree var tree = buildTree( 1, # Seq no @@ -76,7 +78,7 @@ suite "Waku DNS Discovery": let nodeKey4 = generateSecp256k1Key() - node4 = newTestWakuNode(nodeKey4, bindIp, Port(63504)) + node4 = newTestWakuNode(nodeKey4, bindIp, Port(0)) (await node4.mountRelay()).isOkOr: assert false, "Failed to mount relay" diff --git a/tests/test_waku_keepalive.nim b/tests/test_waku_keepalive.nim index d8412f2c5..f628de069 100644 --- a/tests/test_waku_keepalive.nim +++ b/tests/test_waku_keepalive.nim @@ -20,9 +20,9 @@ suite "Waku Keepalive": asyncTest "handle ping keepalives": let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(nodeKey1) nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(0)) + node2 = newTestWakuNode(nodeKey2) var completionFut = newFuture[bool]() diff --git a/tests/test_waku_metadata.nim b/tests/test_waku_metadata.nim index 0252d3f77..1ae4ce7b7 100644 --- a/tests/test_waku_metadata.nim +++ b/tests/test_waku_metadata.nim @@ -29,18 +29,8 @@ procSuite "Waku Metadata Protocol": asyncTest "request() returns the supported metadata of the peer": let clusterId = 10.uint16 let - node1 = newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = clusterId, - ) - node2 = newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = clusterId, - ) + node1 = newTestWakuNode(generateSecp256k1Key(), clusterId = clusterId) + node2 = newTestWakuNode(generateSecp256k1Key(), clusterId = clusterId) # Mount metadata protocol on both nodes before starting discard node1.mountMetadata(clusterId, @[]) @@ -89,15 +79,9 @@ procSuite "Waku Metadata Protocol": configuredShards = @[uint16(0), uint16(1)] let node1 = newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = clusterId, - subscribeShards = configuredShards, - ) - let node2 = newTestWakuNode( - generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0), clusterId = clusterId + generateSecp256k1Key(), clusterId = clusterId, subscribeShards = configuredShards ) + let node2 = newTestWakuNode(generateSecp256k1Key(), clusterId = clusterId) # Mount metadata with configured shards on node1 discard node1.mountMetadata(clusterId, configuredShards) diff --git a/tests/test_waku_rendezvous.nim b/tests/test_waku_rendezvous.nim index 0ebe648fa..4bdb90630 100644 --- a/tests/test_waku_rendezvous.nim +++ b/tests/test_waku_rendezvous.nim @@ -22,24 +22,9 @@ procSuite "Waku Rendezvous": asyncTest "Simple remote test": let clusterId = 10.uint16 - node1 = newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = clusterId, - ) - node2 = newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = clusterId, - ) - node3 = newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = clusterId, - ) + node1 = newTestWakuNode(generateSecp256k1Key(), clusterId = clusterId) + node2 = newTestWakuNode(generateSecp256k1Key(), clusterId = clusterId) + node3 = newTestWakuNode(generateSecp256k1Key(), clusterId = clusterId) await allFutures( [ @@ -90,11 +75,7 @@ procSuite "Waku Rendezvous": configuredShards = @[RelayShard(clusterId: clusterId, shardId: 0)] let node = newTestWakuNode( - generateSecp256k1Key(), - parseIpAddress("0.0.0.0"), - Port(0), - clusterId = clusterId, - subscribeShards = @[0'u16], + generateSecp256k1Key(), clusterId = clusterId, subscribeShards = @[0'u16] ) ## When: Node mounts rendezvous with configured shards (before relay) @@ -122,9 +103,7 @@ procSuite "Waku Rendezvous": RelayShard(clusterId: clusterId, shardId: 1), ] - let lightClient = newTestWakuNode( - generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0), clusterId = clusterId - ) + let lightClient = newTestWakuNode(generateSecp256k1Key(), clusterId = clusterId) ## When: Node mounts rendezvous with configured shards (no relay mounted) await lightClient.mountRendezvous(clusterId, configuredShards) diff --git a/tests/test_wakunode.nim b/tests/test_wakunode.nim index 902ca7192..4279d9066 100644 --- a/tests/test_wakunode.nim +++ b/tests/test_wakunode.nim @@ -27,9 +27,9 @@ suite "WakuNode": asyncTest "Protocol matcher works as expected": let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(61000)) + node1 = newTestWakuNode(nodeKey1) nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(61002)) + node2 = newTestWakuNode(nodeKey2) shard = DefaultRelayShard contentTopic = ContentTopic("/waku/2/default-content/proto") payload = "hello world".toBytes() @@ -94,16 +94,9 @@ suite "WakuNode": let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode( - nodeKey1, parseIpAddress("0.0.0.0"), Port(61020), nameResolver = resolver - ) + node1 = newTestWakuNode(nodeKey1, nameResolver = resolver) nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(61022)) - - # Construct DNS multiaddr for node2 - let - node2PeerId = $(node2.switch.peerInfo.peerId) - node2Dns4Addr = "/dns4/localhost/tcp/61022/p2p/" & node2PeerId + node2 = newTestWakuNode(nodeKey2) (await node1.mountRelay()).isOkOr: assert false, "Failed to mount relay" @@ -112,6 +105,12 @@ suite "WakuNode": await allFutures([node1.start(), node2.start()]) + # Construct DNS multiaddr for node2 + let + node2PeerId = $(node2.switch.peerInfo.peerId) + node2Dns4Addr = + "/dns4/localhost/tcp/" & $node2.boundTcpPort() & "/p2p/" & node2PeerId + await node1.connectToNodes(@[node2Dns4Addr]) check: @@ -171,8 +170,6 @@ suite "WakuNode": # gibberish discard newTestWakuNode( nodeKey1, - parseIpAddress("0.0.0.0"), - bindPort = Port(61004), wsBindPort = Port(8000), wssEnabled = true, secureKey = "../../waku/node/key_dummy.txt", @@ -221,7 +218,7 @@ suite "WakuNode": let nodeKey = generateSecp256k1Key() bindIp = parseIpAddress("0.0.0.0") - bindPort = Port(61006) + bindPort = Port(0) extIp = some(getPrimaryIPAddr()) extPort = some(Port(61008)) node = @@ -247,8 +244,7 @@ suite "WakuNode": test "toRemotePeerInfo sorts quic-v1 addresses first": let nodeKey = generateSecp256k1Key() - node = - newTestWakuNode(nodeKey, parseIpAddress("0.0.0.0"), Port(0), quicEnabled = true) + node = newTestWakuNode(nodeKey, quicEnabled = true) # peerinfo path let fromPeerInfo = node.switch.peerInfo.toRemotePeerInfo() @@ -266,13 +262,9 @@ suite "WakuNode": asyncTest "Dual-stack nodes connect over QUIC": let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode( - nodeKey1, parseIpAddress("0.0.0.0"), Port(0), quicEnabled = true - ) + node1 = newTestWakuNode(nodeKey1, quicEnabled = true) nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode( - nodeKey2, parseIpAddress("0.0.0.0"), Port(0), quicEnabled = true - ) + node2 = newTestWakuNode(nodeKey2, quicEnabled = true) await allFutures(node1.start(), node2.start()) @@ -374,16 +366,11 @@ suite "WakuNode": let # node with custom agent string nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode( - nodeKey1, - parseIpAddress("0.0.0.0"), - Port(61014), - agentString = some(expectedAgentString1), - ) + node1 = newTestWakuNode(nodeKey1, agentString = some(expectedAgentString1)) # node with default agent string from libp2p nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(61016)) + node2 = newTestWakuNode(nodeKey2) await node1.start() (await node1.mountRelay()).isOkOr: @@ -417,16 +404,11 @@ suite "WakuNode": let # node with custom multiaddress nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode( - nodeKey1, - parseIpAddress("0.0.0.0"), - Port(61018), - extMultiAddrs = @[expectedMultiaddress1], - ) + node1 = newTestWakuNode(nodeKey1, extMultiAddrs = @[expectedMultiaddress1]) # node with default multiaddress nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(61020)) + node2 = newTestWakuNode(nodeKey2) await node1.start() (await node1.mountRelay()).isOkOr: diff --git a/tests/testlib/wakunode.nim b/tests/testlib/wakunode.nim index 65b165a08..f269f6314 100644 --- a/tests/testlib/wakunode.nim +++ b/tests/testlib/wakunode.nim @@ -51,8 +51,8 @@ proc defaultTestWakuConf*(): WakuConf = proc newTestWakuNode*( nodeKey: crypto.PrivateKey, - bindIp: IpAddress, - bindPort: Port, + bindIp: IpAddress = parseIpAddress("0.0.0.0"), + bindPort: Port = Port(0), extIp = none(IpAddress), extPort = none(Port), extMultiAddrs = newSeq[MultiAddress](), @@ -170,3 +170,10 @@ proc newTestWakuNode*( ) return builder.build().get() + +proc boundTcpPort*(node: WakuNode): Port = + let ports = getPorts(node.switch.peerInfo.listenAddrs).valueOr: + raiseAssert "getPorts failed: " & error + if ports.tcpPort.isNone(): + raiseAssert "no tcp listen address in " & $node.switch.peerInfo.listenAddrs + ports.tcpPort.get() diff --git a/tests/waku_core/test_published_address.nim b/tests/waku_core/test_published_address.nim index 9d6201a77..5b65c7804 100644 --- a/tests/waku_core/test_published_address.nim +++ b/tests/waku_core/test_published_address.nim @@ -5,8 +5,7 @@ import ../testlib/wakucore, ../testlib/wakunode suite "Waku Core - Published Address": test "Test IP 0.0.0.0": - let node = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + let node = newTestWakuNode(generateSecp256k1Key()) check: ($node.announcedAddresses).contains("127.0.0.1") diff --git a/tests/waku_discv5/test_waku_discv5.nim b/tests/waku_discv5/test_waku_discv5.nim index 9cb295877..7f42e3b80 100644 --- a/tests/waku_discv5/test_waku_discv5.nim +++ b/tests/waku_discv5/test_waku_discv5.nim @@ -19,6 +19,7 @@ import waku_core/codecs, waku_enr, discovery/waku_discv5, + net/auto_port, waku_enr/capabilities, factory/conf_builder/conf_builder, waku, @@ -179,51 +180,57 @@ suite "Waku Discovery v5": suite "findRandomPeers": proc buildNode( tcpPort: uint16, - udpPort: uint16, bindIp: string = "0.0.0.0", extIp: string = "127.0.0.1", indices: seq[uint64] = @[], recordFlags: Option[CapabilitiesBitfield] = none(CapabilitiesBitfield), bootstrapRecords: seq[waku_enr.Record] = @[], - ): (WakuDiscoveryV5, Record) {.raises: [ValueError, LPError].} = - let - privKey = generateSecp256k1Key() - record = newTestEnrRecord( - privKey = privKey, - extIp = extIp, - tcpPort = tcpPort, - udpPort = udpPort, - indices = indices, - flags = recordFlags, - ) - node = discv5_utils.newTestDiscv5( - privKey = privKey, - bindIp = bindIp, - tcpPort = tcpPort, - udpPort = udpPort, - record = record, - bootstrapRecords = bootstrapRecords, - ) + ): Future[Result[(WakuDiscoveryV5, waku_enr.Record), string]] {.async.} = + let privKey = generateSecp256k1Key() + proc attempt( + p: Port + ): Future[Result[(WakuDiscoveryV5, waku_enr.Record), string]] {. + async: (raises: []) + .} = + let (node, record) = + try: + let record = newTestEnrRecord( + privKey = privKey, + extIp = extIp, + tcpPort = tcpPort, + udpPort = uint16(p), + indices = indices, + flags = recordFlags, + ) + ( + discv5_utils.newTestDiscv5( + privKey = privKey, + bindIp = bindIp, + tcpPort = tcpPort, + udpPort = uint16(p), + record = record, + bootstrapRecords = bootstrapRecords, + ), + record, + ) + except CatchableError as e: + return err("could not build discv5 node: " & e.msg) + (await node.start()).isOkOr: + return err(error) + ok((node, record)) - (node, record) + return await tryWithAutoPort[(WakuDiscoveryV5, waku_enr.Record)](Port(0), attempt) asyncTest "find random peers without predicate": # Given 3 nodes - let - (node1, record1) = buildNode(tcpPort = 61500u16, udpPort = 9000u16) - (node2, record2) = buildNode(tcpPort = 61502u16, udpPort = 9002u16) - (node3, record3) = buildNode( - tcpPort = 61504u16, udpPort = 9004u16, bootstrapRecords = @[record1, record2] - ) - - let res1 = await node1.start() - assertResultOk res1 - - let res2 = await node2.start() - assertResultOk res2 - - let res3 = await node3.start() - assertResultOk res3 + let (node1, record1) = (await buildNode(tcpPort = 61500u16)).valueOr: + raiseAssert "node1: " & error + let (node2, record2) = (await buildNode(tcpPort = 61502u16)).valueOr: + raiseAssert "node2: " & error + let (node3, record3) = ( + await buildNode(tcpPort = 61504u16, bootstrapRecords = @[record1, record2]) + ).valueOr: + raiseAssert "node3: " & error await sleepAsync(FUTURE_TIMEOUT) @@ -254,45 +261,39 @@ suite "Waku Discovery v5": return capabilities.get().supportsCapability(Capabilities.Store) # Given 4 nodes - let - (node3, record3) = buildNode( + let (node3, record3) = ( + await buildNode( tcpPort = 61504u16, - udpPort = 9004u16, recordFlags = some(CapabilitiesBitfield.init(Capabilities.Relay, Capabilities.Filter)), ) - (node4, record4) = buildNode( + ).valueOr: + raiseAssert "node3: " & error + let (node4, record4) = ( + await buildNode( tcpPort = 61506u16, - udpPort = 9006u16, recordFlags = some(CapabilitiesBitfield.init(Capabilities.Relay, Capabilities.Store)), ) - (node2, record2) = buildNode( + ).valueOr: + raiseAssert "node4: " & error + let (node2, record2) = ( + await buildNode( tcpPort = 61502u16, - udpPort = 9002u16, recordFlags = some(CapabilitiesBitfield.init(Capabilities.Relay, Capabilities.Store)), bootstrapRecords = @[record3, record4], ) - (node1, record1) = buildNode( + ).valueOr: + raiseAssert "node2: " & error + let (node1, record1) = ( + await buildNode( tcpPort = 61500u16, - udpPort = 9000u16, recordFlags = some(CapabilitiesBitfield.init(Capabilities.Relay)), bootstrapRecords = @[record2], ) - - # Start nodes' discoveryV5 protocols - let res1 = await node1.start() - assertResultOk res1 - - let res2 = await node2.start() - assertResultOk res2 - - let res3 = await node3.start() - assertResultOk res3 - - let res4 = await node4.start() - assertResultOk res4 + ).valueOr: + raiseAssert "node1: " & error await sleepAsync(FUTURE_TIMEOUT) @@ -312,46 +313,40 @@ suite "Waku Discovery v5": ## Setup # Records - let - (node3, record3) = buildNode( + let (node3, record3) = ( + await buildNode( tcpPort = 61504u16, - udpPort = 9004u16, recordFlags = some(CapabilitiesBitfield.init(Capabilities.Relay, Capabilities.Filter)), ) - (node4, record4) = buildNode( + ).valueOr: + raiseAssert "node3: " & error + let (node4, record4) = ( + await buildNode( tcpPort = 61506u16, - udpPort = 9006u16, recordFlags = some(CapabilitiesBitfield.init(Capabilities.Relay, Capabilities.Store)), ) - (node2, record2) = buildNode( + ).valueOr: + raiseAssert "node4: " & error + let (node2, record2) = ( + await buildNode( tcpPort = 61502u16, - udpPort = 9002u16, recordFlags = some(CapabilitiesBitfield.init(Capabilities.Relay, Capabilities.Store)), bootstrapRecords = @[record3, record4], ) - let (node1, record1) = buildNode( - tcpPort = 61500u16, - udpPort = 9000u16, - recordFlags = some(CapabilitiesBitfield.init(Capabilities.Relay)), - indices = @[0u64, 0u64, 1u64, 0u64, 0u64], - bootstrapRecords = @[record2], - ) - - # Start nodes' discoveryV5 protocols - let res1 = await node1.start() - assertResultOk res1 - - let res2 = await node2.start() - assertResultOk res2 - - let res3 = await node3.start() - assertResultOk res3 - - let res4 = await node4.start() - assertResultOk res4 + ).valueOr: + raiseAssert "node2: " & error + let (node1, record1) = ( + await buildNode( + tcpPort = 61500u16, + recordFlags = some(CapabilitiesBitfield.init(Capabilities.Relay)), + indices = @[0u64, 0u64, 1u64, 0u64, 0u64], + bootstrapRecords = @[record2], + ) + ).valueOr: + raiseAssert "node1: " & error ## leave some time for discv5 to act await sleepAsync(chronos.seconds(10)) @@ -425,7 +420,7 @@ suite "Waku Discovery v5": confBuilder.withNodeKey(libp2p_keys.PrivateKey.random(Secp256k1, myRng)[]) confBuilder.discv5Conf.withEnabled(true) - confBuilder.discv5Conf.withUdpPort(9000.Port) + confBuilder.discv5Conf.withUdpPort(Port(0)) let conf = confBuilder.build().valueOr: raiseAssert error @@ -437,8 +432,8 @@ suite "Waku Discovery v5": confBuilder.withNodeKey(crypto.PrivateKey.random(Secp256k1, myRng)[]) confBuilder.discv5Conf.withBootstrapNodes(@[waku0.node.enr.toURI()]) confBuilder.discv5Conf.withEnabled(true) - confBuilder.discv5Conf.withUdpPort(9001.Port) - confBuilder.withP2pTcpPort(60001.Port) + confBuilder.discv5Conf.withUdpPort(Port(0)) + confBuilder.withP2pTcpPort(Port(0)) let conf1 = confBuilder.build().valueOr: raiseAssert error @@ -452,8 +447,8 @@ suite "Waku Discovery v5": await waku1.node.mountRendezvous(conf.clusterId) confBuilder.discv5Conf.withBootstrapNodes(@[waku1.node.enr.toURI()]) - confBuilder.withP2pTcpPort(60003.Port) - confBuilder.discv5Conf.withUdpPort(9003.Port) + confBuilder.withP2pTcpPort(Port(0)) + confBuilder.discv5Conf.withUdpPort(Port(0)) confBuilder.withNodeKey(crypto.PrivateKey.random(Secp256k1, myRng)[]) let conf2 = confBuilder.build().valueOr: diff --git a/tests/waku_filter_v2/test_waku_client.nim b/tests/waku_filter_v2/test_waku_client.nim index c5bd3c558..8a802d911 100644 --- a/tests/waku_filter_v2/test_waku_client.nim +++ b/tests/waku_filter_v2/test_waku_client.nim @@ -2257,12 +2257,9 @@ suite "Waku Filter - End to End": contentTopic = DefaultContentTopic contentTopicSeq = @[contentTopic] - client = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(23450)) - server = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(23451)) - client2nd = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(23452)) + client = newTestWakuNode(generateSecp256k1Key()) + server = newTestWakuNode(generateSecp256k1Key()) + client2nd = newTestWakuNode(generateSecp256k1Key()) await allFutures(server.start(), client.start(), client2nd.start()) diff --git a/tests/waku_peer_exchange/test_protocol.nim b/tests/waku_peer_exchange/test_protocol.nim index 93c522c06..d8083158b 100644 --- a/tests/waku_peer_exchange/test_protocol.nim +++ b/tests/waku_peer_exchange/test_protocol.nim @@ -40,76 +40,31 @@ suite "Waku Peer Exchange": lightpush = false, filter = false, store = false, relay = true ) bindIp = parseIpAddress("0.0.0.0") - extIp = parseIpAddress("127.0.0.1") nodeKey1 = generateSecp256k1Key() - nodeTcpPort1 = Port(64010) - nodeUdpPort1 = Port(9000) - node1 = newTestWakuNode( - nodeKey1, - bindIp, - nodeTcpPort1, - some(extIp), - wakuFlags = some(flags), - discv5UdpPort = some(nodeUdpPort1), - ) + node1 = newTestWakuNode(nodeKey1, bindIp, Port(0), wakuFlags = some(flags)) nodeKey2 = generateSecp256k1Key() - nodeTcpPort2 = Port(64012) - nodeUdpPort2 = Port(9002) - node2 = newTestWakuNode( - nodeKey2, - bindIp, - nodeTcpPort2, - some(extIp), - wakuFlags = some(flags), - discv5UdpPort = some(nodeUdpPort2), - ) + node2 = newTestWakuNode(nodeKey2, bindIp, Port(0), wakuFlags = some(flags)) nodeKey3 = generateSecp256k1Key() - nodeTcpPort3 = Port(64014) - nodeUdpPort3 = Port(9004) - node3 = newTestWakuNode( - nodeKey3, - bindIp, - nodeTcpPort3, - some(extIp), - wakuFlags = some(flags), - discv5UdpPort = some(nodeUdpPort3), - ) - - # discv5 - let conf1 = WakuDiscoveryV5Config( - discv5Config: none(DiscoveryConfig), - address: bindIp, - port: nodeUdpPort1, - privateKey: keys.PrivateKey(nodeKey1.skkey), - bootstrapRecords: @[], - autoupdateRecord: true, - ) - - let disc1 = WakuDiscoveryV5.new( - node1.rng, conf1, some(node1.enr), some(node1.peerManager) - ) - - let conf2 = WakuDiscoveryV5Config( - discv5Config: none(DiscoveryConfig), - address: bindIp, - port: nodeUdpPort2, - privateKey: keys.PrivateKey(nodeKey2.skkey), - bootstrapRecords: @[disc1.protocol.getRecord()], - autoupdateRecord: true, - ) - - let disc2 = WakuDiscoveryV5.new( - node2.rng, conf2, some(node2.enr), some(node2.peerManager) - ) + node3 = newTestWakuNode(nodeKey3, bindIp, Port(0), wakuFlags = some(flags)) await allFutures(node1.start(), node2.start(), node3.start()) - let resultDisc1StartRes = await disc1.start() - assert resultDisc1StartRes.isOk(), resultDisc1StartRes.error - let resultDisc2StartRes = await disc2.start() - assert resultDisc2StartRes.isOk(), resultDisc2StartRes.error + + let disc1 = ( + await startDiscv5WithAutoPort(node1, keys.PrivateKey(nodeKey1.skkey), bindIp) + ).valueOr: + raiseAssert "disc1: " & error + let disc2 = ( + await startDiscv5WithAutoPort( + node2, + keys.PrivateKey(nodeKey2.skkey), + bindIp, + @[disc1.protocol.getRecord()], + ) + ).valueOr: + raiseAssert "disc2: " & error ## When var attempts = 10 @@ -142,14 +97,10 @@ suite "Waku Peer Exchange": asyncTest "Request returns some discovered peers": let - node1 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - node2 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - node3 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - node4 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(generateSecp256k1Key()) + node2 = newTestWakuNode(generateSecp256k1Key()) + node3 = newTestWakuNode(generateSecp256k1Key()) + node4 = newTestWakuNode(generateSecp256k1Key()) # Start and mount peer exchange await allFutures([node1.start(), node2.start(), node3.start(), node4.start()]) @@ -199,10 +150,8 @@ suite "Waku Peer Exchange": asyncTest "Request fails gracefully": let - node1 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - node2 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(generateSecp256k1Key()) + node2 = newTestWakuNode(generateSecp256k1Key()) # Start and mount peer exchange await allFutures([node1.start(), node2.start()]) @@ -276,12 +225,9 @@ suite "Waku Peer Exchange": asyncTest "Request 0 peers, with 1 peer in PeerExchange": # Given two valid nodes with PeerExchange let - node1 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - node2 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - node3 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(generateSecp256k1Key()) + node2 = newTestWakuNode(generateSecp256k1Key()) + node3 = newTestWakuNode(generateSecp256k1Key()) # Start and mount peer exchange await allFutures([node1.start(), node2.start(), node3.start()]) @@ -308,10 +254,8 @@ suite "Waku Peer Exchange": asyncTest "Request with invalid peer info": # Given two valid nodes with PeerExchange let - node1 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - node2 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(generateSecp256k1Key()) + node2 = newTestWakuNode(generateSecp256k1Key()) # Start and mount peer exchange await allFutures([node1.start(), node2.start()]) @@ -329,9 +273,7 @@ suite "Waku Peer Exchange": asyncTest "Connections are closed after response is sent": # Create 3 nodes - let nodes = toSeq(0 ..< 3).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 3).mapIt(newTestWakuNode(generateSecp256k1Key())) await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.mountPeerExchange())) @@ -356,12 +298,9 @@ suite "Waku Peer Exchange": suite "Protocol Handler": asyncTest "Works as expected": let - node1 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - node2 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - node3 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(generateSecp256k1Key()) + node2 = newTestWakuNode(generateSecp256k1Key()) + node3 = newTestWakuNode(generateSecp256k1Key()) # Start and mount peer exchange await allFutures([node1.start(), node2.start(), node3.start()]) @@ -398,12 +337,9 @@ suite "Waku Peer Exchange": asyncTest "RateLimit as expected": let - node1 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - node2 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - node3 = - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(generateSecp256k1Key()) + node2 = newTestWakuNode(generateSecp256k1Key()) + node3 = newTestWakuNode(generateSecp256k1Key()) # Start and mount peer exchange await allFutures([node1.start(), node2.start(), node3.start()]) diff --git a/tests/waku_peer_exchange/utils.nim b/tests/waku_peer_exchange/utils.nim index c72405c51..f762541f8 100644 --- a/tests/waku_peer_exchange/utils.nim +++ b/tests/waku_peer_exchange/utils.nim @@ -1,7 +1,8 @@ {.used.} import - std/options, + std/[options, net], + results, testutils/unittests, chronos, libp2p/switch, @@ -14,6 +15,7 @@ import logos_delivery/waku/[ waku_node, discovery/waku_discv5, + net/auto_port, waku_peer_exchange, waku_peer_exchange/rpc, waku_peer_exchange/protocol, @@ -23,6 +25,33 @@ import ], ../testlib/[futures, wakucore, assertions] +proc startDiscv5WithAutoPort*( + node: WakuNode, + key: keys.PrivateKey, + bindIp: IpAddress, + bootstrapRecords: seq[enr.Record] = @[], +): Future[Result[WakuDiscoveryV5, string]] {.async.} = + proc attempt( + p: Port + ): Future[Result[WakuDiscoveryV5, string]] {.async: (raises: []).} = + var record = node.enr + record.update(key, udpPort = Opt.some(p)).isOkOr: + return err("could not set discv5 udp port in enr: " & $error) + let conf = WakuDiscoveryV5Config( + discv5Config: none(DiscoveryConfig), + address: bindIp, + port: p, + privateKey: key, + bootstrapRecords: bootstrapRecords, + autoupdateRecord: true, + ) + let wd = WakuDiscoveryV5.new(node.rng, conf, some(record), some(node.peerManager)) + (await wd.start()).isOkOr: + return err(error) + return ok(wd) + + return await tryWithAutoPort[WakuDiscoveryV5](Port(0), attempt) + proc dialForPeerExchange*( client: WakuNode, peerInfo: RemotePeerInfo, diff --git a/tests/waku_relay/test_wakunode_relay.nim b/tests/waku_relay/test_wakunode_relay.nim index 311454e22..6ec622f2a 100644 --- a/tests/waku_relay/test_wakunode_relay.nim +++ b/tests/waku_relay/test_wakunode_relay.nim @@ -24,7 +24,7 @@ suite "WakuNode - Relay": asyncTest "Relay protocol is started correctly": let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(nodeKey1) # Relay protocol starts if mounted after node start @@ -39,7 +39,7 @@ suite "WakuNode - Relay": let nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(0)) + node2 = newTestWakuNode(nodeKey2) (await node2.mountRelay()).isOkOr: assert false, "Failed to mount relay" @@ -59,11 +59,11 @@ suite "WakuNode - Relay": asyncTest "Messages are correctly relayed": let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(nodeKey1) nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(0)) + node2 = newTestWakuNode(nodeKey2) nodeKey3 = generateSecp256k1Key() - node3 = newTestWakuNode(nodeKey3, parseIpAddress("0.0.0.0"), Port(0)) + node3 = newTestWakuNode(nodeKey3) shard = DefaultRelayShard contentTopic = ContentTopic("/waku/2/default-content/proto") payload = "hello world".toBytes() @@ -134,13 +134,13 @@ suite "WakuNode - Relay": let # publisher node nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(nodeKey1) # Relay node nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(0)) + node2 = newTestWakuNode(nodeKey2) # Subscriber nodeKey3 = generateSecp256k1Key() - node3 = newTestWakuNode(nodeKey3, parseIpAddress("0.0.0.0"), Port(0)) + node3 = newTestWakuNode(nodeKey3) shard = DefaultRelayShard contentTopic1 = ContentTopic("/waku/2/default-content/proto") @@ -238,9 +238,7 @@ suite "WakuNode - Relay": # TODO: Add a function to validate the WakuMessage integrity xasyncTest "Stats of peer sending wrong WakuMessages are updated": # Create 2 nodes - let nodes = toSeq(0 .. 1).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 .. 1).mapIt(newTestWakuNode(generateSecp256k1Key())) # Start all the nodes and mount relay with await allFutures(nodes.mapIt(it.start())) @@ -278,21 +276,9 @@ suite "WakuNode - Relay": asyncTest "Messages are relayed between two websocket nodes": let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode( - nodeKey1, - parseIpAddress("0.0.0.0"), - bindPort = Port(0), - wsBindPort = Port(0), - wsEnabled = true, - ) + node1 = newTestWakuNode(nodeKey1, wsBindPort = Port(0), wsEnabled = true) nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode( - nodeKey2, - parseIpAddress("0.0.0.0"), - bindPort = Port(0), - wsBindPort = Port(0), - wsEnabled = true, - ) + node2 = newTestWakuNode(nodeKey2, wsBindPort = Port(0), wsEnabled = true) shard = DefaultRelayShard contentTopic = ContentTopic("/waku/2/default-content/proto") payload = "hello world".toBytes() @@ -342,13 +328,7 @@ suite "WakuNode - Relay": asyncTest "Messages are relayed between nodes with multiple transports (TCP and Websockets)": let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode( - nodeKey1, - parseIpAddress("0.0.0.0"), - bindPort = Port(0), - wsBindPort = Port(0), - wsEnabled = true, - ) + node1 = newTestWakuNode(nodeKey1, wsBindPort = Port(0), wsEnabled = true) nodeKey2 = generateSecp256k1Key() node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), bindPort = Port(0)) shard = DefaultRelayShard @@ -406,12 +386,7 @@ suite "WakuNode - Relay": ) nodeKey2 = generateSecp256k1Key() node2 = newTestWakuNode( - nodeKey2, - parseIpAddress("0.0.0.0"), - bindPort = Port(0), - wsBindPort = Port(0), - wsEnabled = true, - quicEnabled = false, + nodeKey2, wsBindPort = Port(0), wsEnabled = true, quicEnabled = false ) shard = DefaultRelayShard contentTopic = ContentTopic("/waku/2/default-content/proto") @@ -469,8 +444,6 @@ suite "WakuNode - Relay": nodeKey1 = generateSecp256k1Key() node1 = newTestWakuNode( nodeKey1, - parseIpAddress("0.0.0.0"), - bindPort = Port(0), wsBindPort = Port(0), wssEnabled = true, secureKey = KEY_PATH, @@ -529,21 +502,13 @@ suite "WakuNode - Relay": nodeKey1 = generateSecp256k1Key() node1 = newTestWakuNode( nodeKey1, - parseIpAddress("0.0.0.0"), - bindPort = Port(0), wsBindPort = Port(0), wssEnabled = true, secureKey = KEY_PATH, secureCert = CERT_PATH, ) nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode( - nodeKey2, - parseIpAddress("0.0.0.0"), - bindPort = Port(0), - wsBindPort = Port(0), - wsEnabled = true, - ) + node2 = newTestWakuNode(nodeKey2, wsBindPort = Port(0), wsEnabled = true) let shard = DefaultRelayShard @@ -594,9 +559,7 @@ suite "WakuNode - Relay": asyncTest "Bad peers with low reputation are disconnected": # Create 5 nodes - let nodes = toSeq(0 ..< 5).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 5).mapIt(newTestWakuNode(generateSecp256k1Key())) await allFutures(nodes.mapIt(it.start())) await allFutures(nodes.mapIt(it.mountRelay())) @@ -657,7 +620,7 @@ suite "WakuNode - Relay": ## Setup let nodeKey = generateSecp256k1Key() - node = newTestWakuNode(nodeKey, parseIpAddress("0.0.0.0"), Port(0)) + node = newTestWakuNode(nodeKey) await node.start() (await node.mountRelay()).isOkOr: diff --git a/tests/waku_rln_relay/test_wakunode_rln_relay.nim b/tests/waku_rln_relay/test_wakunode_rln_relay.nim index 9da693119..7954317e7 100644 --- a/tests/waku_rln_relay/test_wakunode_rln_relay.nim +++ b/tests/waku_rln_relay/test_wakunode_rln_relay.nim @@ -44,7 +44,7 @@ procSuite "WakuNode - RLN relay": # set up three nodes lockNewGlobalBrokerContext: let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(nodeKey1) (await node1.mountRelay()).isOkOr: assert false, "Failed to mount relay" @@ -68,7 +68,7 @@ procSuite "WakuNode - RLN relay": lockNewGlobalBrokerContext: # Relay node let nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(0)) + node2 = newTestWakuNode(nodeKey2) (await node2.mountRelay()).isOkOr: assert false, "Failed to mount relay" @@ -86,7 +86,7 @@ procSuite "WakuNode - RLN relay": lockNewGlobalBrokerContext: # Subscriber let nodeKey3 = generateSecp256k1Key() - node3 = newTestWakuNode(nodeKey3, parseIpAddress("0.0.0.0"), Port(0)) + node3 = newTestWakuNode(nodeKey3) (await node3.mountRelay()).isOkOr: assert false, "Failed to mount relay" @@ -160,7 +160,7 @@ procSuite "WakuNode - RLN relay": var node1, node2, node3: WakuNode lockNewGlobalBrokerContext: let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(nodeKey1) (await node1.mountRelay()).isOkOr: assert false, "Failed to mount relay" let wakuRlnConfig1 = @@ -177,7 +177,7 @@ procSuite "WakuNode - RLN relay": info "Updated root for node", node = 1, rootUpdated = rootUpdated1 lockNewGlobalBrokerContext: let nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(0)) + node2 = newTestWakuNode(nodeKey2) (await node2.mountRelay()).isOkOr: assert false, "Failed to mount relay" let wakuRlnConfig2 = @@ -194,7 +194,7 @@ procSuite "WakuNode - RLN relay": info "Updated root for node", node = 2, rootUpdated = rootUpdated2 lockNewGlobalBrokerContext: let nodeKey3 = generateSecp256k1Key() - node3 = newTestWakuNode(nodeKey3, parseIpAddress("0.0.0.0"), Port(0)) + node3 = newTestWakuNode(nodeKey3) (await node3.mountRelay()).isOkOr: assert false, "Failed to mount relay" let wakuRlnConfig3 = @@ -307,7 +307,7 @@ procSuite "WakuNode - RLN relay": lockNewGlobalBrokerContext: # publisher node let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(nodeKey1) (await node1.mountRelay()).isOkOr: assert false, "Failed to mount relay" @@ -329,7 +329,7 @@ procSuite "WakuNode - RLN relay": lockNewGlobalBrokerContext: # Relay node let nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(0)) + node2 = newTestWakuNode(nodeKey2) (await node2.mountRelay()).isOkOr: assert false, "Failed to mount relay" # mount rlnrelay in off-chain mode @@ -345,7 +345,7 @@ procSuite "WakuNode - RLN relay": lockNewGlobalBrokerContext: # Subscriber let nodeKey3 = generateSecp256k1Key() - node3 = newTestWakuNode(nodeKey3, parseIpAddress("0.0.0.0"), Port(0)) + node3 = newTestWakuNode(nodeKey3) (await node3.mountRelay()).isOkOr: assert false, "Failed to mount relay" @@ -418,7 +418,7 @@ procSuite "WakuNode - RLN relay": lockNewGlobalBrokerContext: # publisher node let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(nodeKey1) (await node1.mountRelay()).isOkOr: assert false, "Failed to mount relay" @@ -441,7 +441,7 @@ procSuite "WakuNode - RLN relay": lockNewGlobalBrokerContext: # Relay node let nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(0)) + node2 = newTestWakuNode(nodeKey2) (await node2.mountRelay()).isOkOr: assert false, "Failed to mount relay" @@ -459,7 +459,7 @@ procSuite "WakuNode - RLN relay": lockNewGlobalBrokerContext: # Subscriber let nodeKey3 = generateSecp256k1Key() - node3 = newTestWakuNode(nodeKey3, parseIpAddress("0.0.0.0"), Port(0)) + node3 = newTestWakuNode(nodeKey3) (await node3.mountRelay()).isOkOr: assert false, "Failed to mount relay" @@ -590,7 +590,7 @@ procSuite "WakuNode - RLN relay": epochSizeSec: uint64 = 5 # This means rlnMaxEpochGap = 4 lockNewGlobalBrokerContext: let nodeKey1 = generateSecp256k1Key() - node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), Port(0)) + node1 = newTestWakuNode(nodeKey1) (await node1.mountRelay()).isOkOr: assert false, "Failed to mount relay" let wakuRlnConfig1 = @@ -609,7 +609,7 @@ procSuite "WakuNode - RLN relay": info "Updated root for node1", rootUpdated1 lockNewGlobalBrokerContext: let nodeKey2 = generateSecp256k1Key() - node2 = newTestWakuNode(nodeKey2, parseIpAddress("0.0.0.0"), Port(0)) + node2 = newTestWakuNode(nodeKey2) (await node2.mountRelay()).isOkOr: assert false, "Failed to mount relay" let wakuRlnConfig2 = diff --git a/tests/waku_store/test_wakunode_store.nim b/tests/waku_store/test_wakunode_store.nim index 0447bcdfe..9c067488a 100644 --- a/tests/waku_store/test_wakunode_store.nim +++ b/tests/waku_store/test_wakunode_store.nim @@ -66,9 +66,9 @@ procSuite "WakuNode - Store": ## Setup let serverKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) clientKey = generateSecp256k1Key() - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + client = newTestWakuNode(clientKey) waitFor allFutures(client.start(), server.start()) @@ -101,9 +101,9 @@ procSuite "WakuNode - Store": ## Setup let serverKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) clientKey = generateSecp256k1Key() - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + client = newTestWakuNode(clientKey) waitFor allFutures(client.start(), server.start()) @@ -157,9 +157,9 @@ procSuite "WakuNode - Store": ## Setup let serverKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) clientKey = generateSecp256k1Key() - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + client = newTestWakuNode(clientKey) waitFor allFutures(client.start(), server.start()) @@ -214,12 +214,11 @@ procSuite "WakuNode - Store": ## Setup let filterSourceKey = generateSecp256k1Key() - filterSource = - newTestWakuNode(filterSourceKey, parseIpAddress("0.0.0.0"), Port(0)) + filterSource = newTestWakuNode(filterSourceKey) serverKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) clientKey = generateSecp256k1Key() - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + client = newTestWakuNode(clientKey) waitFor allFutures(client.start(), server.start(), filterSource.start()) @@ -289,9 +288,9 @@ procSuite "WakuNode - Store": ## Setup let serverKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) clientKey = generateSecp256k1Key() - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + client = newTestWakuNode(clientKey) waitFor allFutures(client.start(), server.start()) @@ -332,9 +331,9 @@ procSuite "WakuNode - Store": ## Setup let serverKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) clientKey = generateSecp256k1Key() - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + client = newTestWakuNode(clientKey) waitFor allFutures(client.start(), server.start()) @@ -382,9 +381,9 @@ procSuite "WakuNode - Store": ## Setup let serverKey = generateSecp256k1Key() - server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0)) + server = newTestWakuNode(serverKey) clientKey = generateSecp256k1Key() - client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0)) + client = newTestWakuNode(clientKey) waitFor allFutures(client.start(), server.start()) diff --git a/tests/wakunode2/test_validators.nim b/tests/wakunode2/test_validators.nim index 7a84c365a..fc4c2cf57 100644 --- a/tests/wakunode2/test_validators.nim +++ b/tests/wakunode2/test_validators.nim @@ -21,9 +21,7 @@ import suite "WakuNode2 - Validators": asyncTest "Spam protected topic accepts signed messages": # Create 5 nodes - let nodes = toSeq(0 ..< 5).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 5).mapIt(newTestWakuNode(generateSecp256k1Key())) # Protected shard and key to sign let spamProtectedShard = RelayShard(clusterId: 0, shardId: 7) @@ -108,9 +106,7 @@ suite "WakuNode2 - Validators": asyncTest "Spam protected topic rejects non-signed/wrongly-signed/no-timestamp messages": # Create 5 nodes - let nodes = toSeq(0 ..< 5).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 5).mapIt(newTestWakuNode(generateSecp256k1Key())) # Protected shard and key to sign let spamProtectedShard = RelayShard(clusterId: 0, shardId: 7) @@ -260,9 +256,7 @@ suite "WakuNode2 - Validators": asyncTest "Spam protected topic rejects a spammer node": # Create 5 nodes - let nodes = toSeq(0 ..< 5).mapIt( - newTestWakuNode(generateSecp256k1Key(), parseIpAddress("0.0.0.0"), Port(0)) - ) + let nodes = toSeq(0 ..< 5).mapIt(newTestWakuNode(generateSecp256k1Key())) # Protected shard and key to sign let spamProtectedShard = RelayShard(clusterId: 0, shardId: 7) From 82dcada1b5a0d352e74f4cdb9d7583eda8b9bab8 Mon Sep 17 00:00:00 2001 From: Ivan FB <128452529+Ivansete-status@users.noreply.github.com> Date: Tue, 30 Jun 2026 22:15:04 +0200 Subject: [PATCH 21/33] api: define layer contracts with concept, not RootObj inheritance (#4001) --- logos_delivery/api/events/kernel_events.nim | 18 ++ .../api/events/messaging_client_events.nim | 30 +++ .../reliable_channel_manager_events.nim | 27 ++ logos_delivery/api/kernel_api.nim | 252 +++++------------- logos_delivery/api/logos_delivery_api.nim | 40 +-- logos_delivery/api/messaging_client_api.nim | 50 +--- .../api/reliable_channel_manager_api.nim | 79 ++---- logos_delivery/api/types.nim | 5 + logos_delivery/channels/reliable_channel.nim | 2 + .../channels/reliable_channel_manager.nim | 2 +- logos_delivery/logos_delivery.nim | 34 ++- logos_delivery/messaging/api/send.nim | 1 + .../recv_service/recv_service.nim | 6 +- .../send_service/send_service.nim | 2 +- logos_delivery/messaging/messaging_client.nim | 2 +- .../waku/api/events/health_events.nim | 5 +- logos_delivery/waku/api/peer_manager.nim | 5 +- .../health_monitor/node_health_monitor.nim | 2 +- .../waku/node/subscription_manager.nim | 2 +- logos_delivery/waku/node/waku_node.nim | 2 +- logos_delivery/waku/node/waku_node/relay.nim | 2 +- logos_delivery/waku/waku.nim | 6 +- .../test_reliable_channel_send_receive.nim | 2 +- 23 files changed, 216 insertions(+), 360 deletions(-) create mode 100644 logos_delivery/api/events/kernel_events.nim create mode 100644 logos_delivery/api/events/messaging_client_events.nim create mode 100644 logos_delivery/api/events/reliable_channel_manager_events.nim diff --git a/logos_delivery/api/events/kernel_events.nim b/logos_delivery/api/events/kernel_events.nim new file mode 100644 index 000000000..d8dd46627 --- /dev/null +++ b/logos_delivery/api/events/kernel_events.nim @@ -0,0 +1,18 @@ +import brokers/event_broker + +import logos_delivery/api/types +import logos_delivery/waku/waku_core/topics/pubsub_topic +import logos_delivery/waku/waku_core/message + +export event_broker, pubsub_topic, message + +EventBroker: + # Internal event emitted when a message arrives from the network via any protocol + type MessageSeenEvent* = object + topic*: PubsubTopic + message*: WakuMessage + +# Emitted by the health monitor when overall node connectivity changes. +EventBroker: + type EventConnectionStatusChange* = object + connectionStatus*: ConnectionStatus diff --git a/logos_delivery/api/events/messaging_client_events.nim b/logos_delivery/api/events/messaging_client_events.nim new file mode 100644 index 000000000..b3b7a46f6 --- /dev/null +++ b/logos_delivery/api/events/messaging_client_events.nim @@ -0,0 +1,30 @@ +import brokers/event_broker + +import logos_delivery/api/types as api_types + +export event_broker, api_types + +EventBroker: + # Event emitted when a message is sent to the network + type MessageSentEvent* = object + requestId*: RequestId + messageHash*: string + +EventBroker: + # Event emitted when a message send operation fails + type MessageErrorEvent* = object + requestId*: RequestId + messageHash*: string + error*: string + +EventBroker: + # Confirmation that a message has been correctly delivered to some neighbouring nodes. + type MessagePropagatedEvent* = object + requestId*: RequestId + messageHash*: string + +EventBroker: + # Event emitted when a message is received via Waku + type MessageReceivedEvent* = object + messageHash*: string + message*: WakuMessage diff --git a/logos_delivery/api/events/reliable_channel_manager_events.nim b/logos_delivery/api/events/reliable_channel_manager_events.nim new file mode 100644 index 000000000..0f3b4e1ec --- /dev/null +++ b/logos_delivery/api/events/reliable_channel_manager_events.nim @@ -0,0 +1,27 @@ +import brokers/event_broker + +import logos_delivery/channels/types as channel_types + +export event_broker, channel_types + +EventBroker: + type ChannelMessageReceivedEvent* = object + channelId*: ChannelId + senderId*: SdsParticipantID + payload*: seq[byte] + +EventBroker: + ## Emitted when every segment of a channel-level `send()` reached + ## `Confirmed`. Channel-level analogue of `MessageSentEvent`; the + ## `requestId` is the channel-layer parent returned by `send()`. + type ChannelMessageSentEvent* = object + channelId*: ChannelId + requestId*: RequestId + +EventBroker: + ## Emitted when a channel-level `send()` finalises with at least one + ## segment in `Failed`. Channel-level analogue of `MessageErrorEvent`. + type ChannelMessageErrorEvent* = object + channelId*: ChannelId + requestId*: RequestId + error*: string diff --git a/logos_delivery/api/kernel_api.nim b/logos_delivery/api/kernel_api.nim index 35cf7251f..01b0c30c0 100644 --- a/logos_delivery/api/kernel_api.nim +++ b/logos_delivery/api/kernel_api.nim @@ -1,203 +1,81 @@ -import std/options import chronos, results -import brokers/event_broker import logos_delivery/api/types as api_types import logos_delivery/waku/waku_core/topics/pubsub_topic import logos_delivery/waku/waku_core/message +import logos_delivery/waku/waku_core/subscription/push_handler import logos_delivery/waku/waku_store/common as store_types -export event_broker export api_types, pubsub_topic, store_types -type IKernel* = ref object of RootObj +# Structural API contract for the Kernel surface, implemented by `Waku` +# (ops in `waku/api/*`). +type KernelApi* = concept w + # --- topic construction --- + buildContentTopic( + w, appName = string, appVersion = uint32, name = string, encoding = string + ) is Future[Result[ContentTopic, string]] + buildPubsubTopic(w, topicName = string) is Future[Result[PubsubTopic, string]] + defaultPubsubTopic(w) is Future[Result[PubsubTopic, string]] -EventBroker: - # Internal event emitted when a message arrives from the network via any protocol - type MessageSeenEvent* = object - topic*: PubsubTopic - message*: WakuMessage + # --- relay --- + relayPublish(w, pubsubTopic = PubsubTopic, message = WakuMessage, timeoutMs = uint32) is + Future[Result[string, string]] + relaySubscribe(w, pubsubTopic = PubsubTopic) is Future[Result[bool, string]] + relayUnsubscribe(w, pubsubTopic = PubsubTopic) is Future[Result[bool, string]] + relayAddProtectedShard(w, clusterId = uint16, shardId = uint16, publicKey = string) is + Future[Result[bool, string]] + relayConnectedPeers(w, pubsubTopic = PubsubTopic) is + Future[Result[seq[string], string]] + relayPeersInMesh(w, pubsubTopic = PubsubTopic) is Future[Result[seq[string], string]] + relayNumPeersInMesh(w, pubsubTopic = PubsubTopic) is Future[Result[int, string]] + relayNumConnectedPeers(w, pubsubTopic = PubsubTopic) is Future[Result[int, string]] -# --- topic construction --- -method buildContentTopic*( - self: IKernel, appName: string, appVersion: uint32, name: string, encoding: string -): Future[Result[ContentTopic, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.buildContentTopic not implemented") + # --- filter --- + filterSubscribe( + w, + pubsubTopic = PubsubTopic, + contentTopics = seq[ContentTopic], + pushHandler = FilterPushHandler, + ) is Future[Result[bool, string]] + filterUnsubscribe(w, pubsubTopic = PubsubTopic, contentTopics = seq[ContentTopic]) is + Future[Result[bool, string]] + filterUnsubscribeAll(w) is Future[Result[bool, string]] -method buildPubsubTopic*( - self: IKernel, topicName: string -): Future[Result[PubsubTopic, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.buildPubsubTopic not implemented") + # --- lightpush --- + lightpushPublish(w, pubsubTopic = PubsubTopic, message = WakuMessage) is + Future[Result[string, string]] -method defaultPubsubTopic*( - self: IKernel -): Future[Result[PubsubTopic, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.defaultPubsubTopic not implemented") + # --- store --- + storeQuery(w, request = StoreQueryRequest, peer = string, timeoutMs = int) is + Future[Result[StoreQueryResponse, string]] -# --- relay --- -method relayPublish*( - self: IKernel, pubsubTopic: PubsubTopic, message: WakuMessage, timeoutMs: uint32 -): Future[Result[int, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.relayPublish not implemented") + # --- peer management --- + connect(w, peers = seq[string], timeoutMs = uint32) is Future[Result[bool, string]] + disconnectPeerById(w, peerId = string) is Future[Result[bool, string]] + disconnectAllPeers(w) is Future[Result[bool, string]] + dialPeer(w, peerAddr = string, protocol = string, timeoutMs = int) is + Future[Result[bool, string]] + dialPeerById(w, peerId = string, protocol = string, timeoutMs = int) is + Future[Result[bool, string]] + peerIdsFromPeerstore(w) is Future[Result[seq[string], string]] + connectedPeersInfo(w) is Future[Result[seq[PeerConnInfo], string]] + connectedPeers(w) is Future[Result[seq[string], string]] + peerIdsByProtocol(w, protocol = string) is Future[Result[seq[string], string]] -method relaySubscribe*( - self: IKernel, pubsubTopic: PubsubTopic -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.relaySubscribe not implemented") + # --- discovery --- + dnsDiscovery(w, enrTreeUrl = string, nameServer = string, timeoutMs = int) is + Future[Result[seq[string], string]] + discv5UpdateBootnodes(w, bootnodes = string) is Future[Result[bool, string]] + startDiscv5(w) is Future[Result[bool, string]] + stopDiscv5(w) is Future[Result[bool, string]] + peerExchangeRequest(w, numPeers = uint64) is Future[Result[int, string]] -method relayUnsubscribe*( - self: IKernel, pubsubTopic: PubsubTopic -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.relayUnsubscribe not implemented") - -method relayAddProtectedShard*( - self: IKernel, clusterId: uint16, shardId: uint16, publicKey: string -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.relayAddProtectedShard not implemented") - -method relayConnectedPeers*( - self: IKernel, pubsubTopic: PubsubTopic -): Future[Result[seq[string], string]] {.async: (raises: []), base.} = - return err("Interface IKernel.relayConnectedPeers not implemented") - -method relayPeersInMesh*( - self: IKernel, pubsubTopic: PubsubTopic -): Future[Result[seq[string], string]] {.async: (raises: []), base.} = - return err("Interface IKernel.relayPeersInMesh not implemented") - -# --- filter --- -method filterSubscribe*( - self: IKernel, - pubsubTopic: Option[PubsubTopic], - contentTopics: seq[ContentTopic], - peer: string, -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.filterSubscribe not implemented") - -method filterUnsubscribe*( - self: IKernel, - pubsubTopic: Option[PubsubTopic], - contentTopics: seq[ContentTopic], - peer: string, -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.filterUnsubscribe not implemented") - -method filterUnsubscribeAll*( - self: IKernel, peer: string -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.filterUnsubscribeAll not implemented") - -# --- lightpush --- -method lightpushPublish*( - self: IKernel, pubsubTopic: PubsubTopic, message: WakuMessage, peer: string -): Future[Result[string, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.lightpushPublish not implemented") - -# --- store --- -method storeQuery*( - self: IKernel, request: StoreQueryRequest, peer: string, timeoutMs: int -): Future[Result[StoreQueryResponse, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.storeQuery not implemented") - -# --- peer management --- -method connect*( - self: IKernel, peers: seq[string], timeoutMs: uint32 -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.connect not implemented") - -method disconnectPeerById*( - self: IKernel, peerId: string -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.disconnectPeerById not implemented") - -method disconnectAllPeers*( - self: IKernel -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.disconnectAllPeers not implemented") - -method dialPeer*( - self: IKernel, peerAddr: string, protocol: string, timeoutMs: int -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.dialPeer not implemented") - -method dialPeerById*( - self: IKernel, peerId: string, protocol: string, timeoutMs: int -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.dialPeerById not implemented") - -method peerIdsFromPeerstore*( - self: IKernel -): Future[Result[seq[string], string]] {.async: (raises: []), base.} = - return err("Interface IKernel.peerIdsFromPeerstore not implemented") - -method connectedPeersInfo*( - self: IKernel -): Future[Result[seq[string], string]] {.async: (raises: []), base.} = - return err("Interface IKernel.connectedPeersInfo not implemented") - -method connectedPeers*( - self: IKernel -): Future[Result[seq[string], string]] {.async: (raises: []), base.} = - return err("Interface IKernel.connectedPeers not implemented") - -method peerIdsByProtocol*( - self: IKernel, protocol: string -): Future[Result[seq[string], string]] {.async: (raises: []), base.} = - return err("Interface IKernel.peerIdsByProtocol not implemented") - -# --- discovery --- -method dnsDiscovery*( - self: IKernel, enrTreeUrl: string, nameServer: string, timeoutMs: int -): Future[Result[seq[string], string]] {.async: (raises: []), base.} = - return err("Interface IKernel.dnsDiscovery not implemented") - -method discv5UpdateBootnodes*( - self: IKernel, bootnodes: seq[string] -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.discv5UpdateBootnodes not implemented") - -method startDiscv5*( - self: IKernel -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.startDiscv5 not implemented") - -method stopDiscv5*( - self: IKernel -): Future[Result[bool, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.stopDiscv5 not implemented") - -method peerExchangeRequest*( - self: IKernel, numPeers: uint64 -): Future[Result[int, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.peerExchangeRequest not implemented") - -# --- debug / info --- -method version*( - self: IKernel -): Future[Result[string, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.version not implemented") - -method listenAddresses*( - self: IKernel -): Future[Result[seq[string], string]] {.async: (raises: []), base.} = - return err("Interface IKernel.listenAddresses not implemented") - -method myEnr*( - self: IKernel -): Future[Result[string, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.myEnr not implemented") - -method myPeerId*( - self: IKernel -): Future[Result[string, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.myPeerId not implemented") - -method metrics*( - self: IKernel -): Future[Result[string, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.metrics not implemented") - -method pingPeer*( - self: IKernel, peerAddr: string, timeoutMs: int -): Future[Result[int64, string]] {.async: (raises: []), base.} = - return err("Interface IKernel.pingPeer not implemented") + # --- debug / info --- + version(w) is Future[Result[string, string]] + listenAddresses(w) is Future[Result[seq[string], string]] + myEnr(w) is Future[Result[string, string]] + myPeerId(w) is Future[Result[string, string]] + metrics(w) is Future[Result[string, string]] + isOnline(w) is Future[Result[bool, string]] + pingPeer(w, peerAddr = string, timeoutMs = int) is Future[Result[int64, string]] diff --git a/logos_delivery/api/logos_delivery_api.nim b/logos_delivery/api/logos_delivery_api.nim index 162159d8e..708484553 100644 --- a/logos_delivery/api/logos_delivery_api.nim +++ b/logos_delivery/api/logos_delivery_api.nim @@ -1,33 +1,11 @@ -## `LogosDelivery` is the project entry point. It is a pure concentrator: it -## owns exactly one instance of each API layer -## -## Waku <- MessagingClient <- ReliableChannelManager -## -## and chains them together (each layer drives the one below it). Every layer -## keeps its own, separate public API — `LogosDelivery` only wires them up and -## drives the shared `new` / `start` / `stop` lifecycle. +import chronos, results -{.push raises: [].} +# Structural API contract for the top-level entry point, implemented by +# `LogosDelivery` (the aggregate owning one instance of each API layer). +type LogosDeliveryApi* = concept ld + # --- lifecycle --- + start(ld) is Future[Result[void, string]] + stop(ld) is Future[Result[void, string]] -import results, chronos -import brokers/event_broker -import types as api_types - -export api_types, event_broker - -type - ## Entry point. Holds one instance of each API layer. - ILogosDelivery* = ref object of RootObj - -EventBroker: - type EventConnectionStatusChange* = object - connectionStatus*: ConnectionStatus - -method start*(self: ILogosDelivery): Future[Result[void, string]] {.async, base.} = - return err("ILogosDelivery.start not implemented") - -method stop*(self: ILogosDelivery): Future[Result[void, string]] {.async, base.} = - return err("ILogosDelivery.stop not implemented") - -method isOnline*(self: ILogosDelivery): Future[Result[bool, string]] {.async, base.} = - return err("ILogosDelivery.isOnline not implemented") + # --- health --- + isOnline(ld) is Future[Result[bool, string]] diff --git a/logos_delivery/api/messaging_client_api.nim b/logos_delivery/api/messaging_client_api.nim index f29a5cb41..536ce94fa 100644 --- a/logos_delivery/api/messaging_client_api.nim +++ b/logos_delivery/api/messaging_client_api.nim @@ -1,49 +1,11 @@ import chronos, results -import brokers/event_broker import logos_delivery/api/types as api_types -import logos_delivery/waku/waku_core/message -export event_broker, api_types +export api_types -type IMessagingClient* = ref object of RootObj - -EventBroker: - # Event emitted when a message is sent to the network - type MessageSentEvent* = object - requestId*: RequestId - messageHash*: string - -EventBroker: - # Event emitted when a message send operation fails - type MessageErrorEvent* = object - requestId*: RequestId - messageHash*: string - error*: string - -EventBroker: - # Confirmation that a message has been correctly delivered to some neighbouring nodes. - type MessagePropagatedEvent* = object - requestId*: RequestId - messageHash*: string - -EventBroker: - # Event emitted when a message is received via Waku - type MessageReceivedEvent* = object - messageHash*: string - message*: WakuMessage - -method subscribe*( - self: IMessagingClient, contentTopic: ContentTopic -): Future[Result[void, string]] {.async: (raises: []), base.} = - return err("Interface IMessagingClient.subscribe not implemented") - -method unsubscribe*( - self: IMessagingClient, contentTopic: ContentTopic -): Result[void, string] {.base, raises: [].} = - return err("Interface IMessagingClient.unsubscribe not implemented") - -method send*( - self: IMessagingClient, envelope: MessageEnvelope -): Future[Result[RequestId, string]] {.async: (raises: []), base.} = - return err("Interface IMessagingClient.send not implemented") +# Structural API contract for a messaging client (ops in `messaging/api/*`). +type MessagingApi* = concept c + subscribe(c, contentTopic = ContentTopic) is Future[Result[void, string]] + unsubscribe(c, contentTopic = ContentTopic) is Result[void, string] + send(c, envelope = MessageEnvelope) is Future[Result[RequestId, string]] diff --git a/logos_delivery/api/reliable_channel_manager_api.nim b/logos_delivery/api/reliable_channel_manager_api.nim index c5266972a..d50b57288 100644 --- a/logos_delivery/api/reliable_channel_manager_api.nim +++ b/logos_delivery/api/reliable_channel_manager_api.nim @@ -1,70 +1,23 @@ import chronos, results -import brokers/event_broker - import logos_delivery/api/types as api_types import logos_delivery/channels/types as channel_types -# The channel layer re-uses the messaging-layer message events (the `requestId` -# is shared across layers), so it re-exports the messaging interface's event -# surface and only adds the channel-level events that have no lower-layer -# analogue (reassembled payload / senderId / channelId). -import logos_delivery/api/messaging_client_api +export api_types, channel_types -export event_broker, api_types -export channel_types, messaging_client_api +type SendHandler* = proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {. + async: (raises: [CatchableError]), gcsafe +.} + ## Egress dispatch boundary. Typically wraps `MessagingClient.send`; + ## tests inject a fake that records calls and returns canned + ## `RequestId`s so the send state machine can be exercised end-to-end + ## without a network. -type - IReliableChannelManager* = ref object of RootObj - - SendHandler* = proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {. - async: (raises: [CatchableError]), gcsafe - .} - ## Egress dispatch boundary. Typically wraps `MessagingClient.send`; - ## tests inject a fake that records calls and returns canned - ## `RequestId`s so the send state machine can be exercised end-to-end - ## without a network. - -EventBroker: - type ChannelMessageReceivedEvent* = object - channelId*: ChannelId - senderId*: SdsParticipantID - payload*: seq[byte] - -EventBroker: - ## Emitted when every segment of a channel-level `send()` reached - ## `Confirmed`. Channel-level analogue of `MessageSentEvent`; the - ## `requestId` is the channel-layer parent returned by `send()`. - type ChannelMessageSentEvent* = object - channelId*: ChannelId - requestId*: RequestId - -EventBroker: - ## Emitted when a channel-level `send()` finalises with at least one - ## segment in `Failed`. Channel-level analogue of `MessageErrorEvent`. - type ChannelMessageErrorEvent* = object - channelId*: ChannelId - requestId*: RequestId - error*: string - -method createReliableChannel*( - self: IReliableChannelManager, - channelId: ChannelId, - contentTopic: ContentTopic, - senderId: SdsParticipantID, - sendHandler: SendHandler = nil, -): Result[ChannelId, string] {.base.} = - return err("Interface IReliableChannelManager.createReliableChannel not implemented") - -method closeChannel*( - self: IReliableChannelManager, channelId: ChannelId -): Future[Result[void, string]] {.async: (raises: []), base.} = - return err("Interface IReliableChannelManager.closeChannel not implemented") - -method send*( - self: IReliableChannelManager, - channelId: ChannelId, - appPayload: seq[byte], - ephemeral: bool = false, -): Future[Result[RequestId, string]] {.async: (raises: []), base.} = - return err("Interface IReliableChannelManager.send not implemented") +# Structural API contract for the reliable-channel surface (ops in `channels/api/*`). +type ReliableChannelApi* = concept c + createReliableChannel( + c, channelId = ChannelId, contentTopic = ContentTopic, senderId = SdsParticipantID + ) is Result[ChannelId, string] + closeChannel(c, channelId = ChannelId) is Future[Result[void, string]] + send(c, channelId = ChannelId, appPayload = seq[byte]) is + Future[Result[RequestId, string]] diff --git a/logos_delivery/api/types.nim b/logos_delivery/api/types.nim index 5757a8e82..97e7a11c9 100644 --- a/logos_delivery/api/types.nim +++ b/logos_delivery/api/types.nim @@ -26,6 +26,11 @@ type PartiallyConnected Connected + PeerConnInfo* = object ## structured connected-peer info for the api boundary + peerId*: string + protocols*: seq[string] + addresses*: seq[string] + proc new*(T: typedesc[RequestId], rng: crypto.Rng): T = ## Generate a new RequestId using the provided RNG. RequestId(request_utils.generateRequestId(rng)) diff --git a/logos_delivery/channels/reliable_channel.nim b/logos_delivery/channels/reliable_channel.nim index d06f74e36..06648a8f7 100644 --- a/logos_delivery/channels/reliable_channel.nim +++ b/logos_delivery/channels/reliable_channel.nim @@ -23,6 +23,8 @@ import libp2p/crypto/crypto as libp2p_crypto import logos_delivery/api/types import logos_delivery/api/reliable_channel_manager_api +import logos_delivery/api/events/messaging_client_events +import logos_delivery/api/events/reliable_channel_manager_events import logos_delivery/messaging/delivery_service/send_service import logos_delivery/waku/waku_core/topics diff --git a/logos_delivery/channels/reliable_channel_manager.nim b/logos_delivery/channels/reliable_channel_manager.nim index d3f671c2b..e8d579326 100644 --- a/logos_delivery/channels/reliable_channel_manager.nim +++ b/logos_delivery/channels/reliable_channel_manager.nim @@ -28,7 +28,7 @@ type ## channel API. Placeholder for now (segmentation / SDS / rate-limit defaults ## will move here in a follow-up PR); kept so each layer owns its own config. - ReliableChannelManager* = ref object of IReliableChannelManager + ReliableChannelManager* = ref object ## Implements `ReliableChannelApi`. channels*: Table[ChannelId, ReliableChannel] ## read by `channels/api.nim` messagingClient: MessagingClient ## The channel layer chains onto messaging. sendHandler*: SendHandler diff --git a/logos_delivery/logos_delivery.nim b/logos_delivery/logos_delivery.nim index d0fb33b90..0b161d6f7 100644 --- a/logos_delivery/logos_delivery.nim +++ b/logos_delivery/logos_delivery.nim @@ -11,9 +11,6 @@ import results, chronos, chronicles -import logos_delivery/api/logos_delivery_api -export logos_delivery_api - # Each layer has a core module (type + new/start/stop) and an api/ folder whose # modules each implement a differentiated set of operations, plus an events # surface. The concentrator re-exports them so library consumers get the full @@ -31,8 +28,9 @@ import export topics, relay, subscriptions, filter, lightpush, store, peer_manager, discovery, debug, health, ping -# `MessageSeenEvent` is surfaced via `export waku` (Kernel interface); the -# remaining waku health events live here. +# Kernel event surface (`MessageSeenEvent`) plus the remaining waku health events. +import logos_delivery/api/events/kernel_events +export kernel_events import logos_delivery/waku/api/events/health_events export health_events @@ -41,7 +39,8 @@ import logos_delivery/messaging/messaging_client export messaging_client import logos_delivery/messaging/api/[subscription, send] export subscription, send -# Message* events are surfaced via `export messaging_client` (messaging interface). +import logos_delivery/api/events/messaging_client_events +export messaging_client_events # Reliable Channel layer import logos_delivery/channels/reliable_channel_manager @@ -50,8 +49,11 @@ import logos_delivery/channels/api/channel_lifecycle export channel_lifecycle import logos_delivery/channels/api/send as channel_send export channel_send -# ChannelMessage* events are surfaced via `export reliable_channel_manager`. +import logos_delivery/api/events/reliable_channel_manager_events +export reliable_channel_manager_events +import logos_delivery/api/logos_delivery_api +export logos_delivery_api import logos_delivery/waku/factory/waku_conf import logos_delivery/waku/factory/app_callbacks import tools/confutils/cli_args @@ -69,8 +71,7 @@ type messaging*: MessagingClientConf reliableChannel*: ReliableChannelManagerConf - LogosDelivery* = ref object of ILogosDelivery - ## Entry point. Holds one instance of each API layer. + LogosDelivery* = ref object ## Entry point. Holds one instance of each API layer. waku*: Waku messagingClient*: MessagingClient reliableChannelManager*: ReliableChannelManager @@ -114,7 +115,7 @@ proc new*( ) ) -method start*(self: LogosDelivery): Future[Result[void, string]] {.async.} = +proc start*(self: LogosDelivery): Future[Result[void, string]] {.async.} = ## Starts each layer bottom-up: transport first, then messaging, then channels. if self.waku.isNil(): return err("Waku node is not initialized") @@ -134,7 +135,7 @@ method start*(self: LogosDelivery): Future[Result[void, string]] {.async.} = return ok() -method stop*(self: LogosDelivery): Future[Result[void, string]] {.async.} = +proc stop*(self: LogosDelivery): Future[Result[void, string]] {.async.} = ## Stops in reverse order so higher layers drain before their dependencies. await self.reliableChannelManager.stop() await self.messagingClient.stop() @@ -144,7 +145,14 @@ method stop*(self: LogosDelivery): Future[Result[void, string]] {.async.} = return ok() -method isOnline*(self: LogosDelivery): Future[Result[bool, string]] {.async.} = +proc isOnline*(self: LogosDelivery): Future[Result[bool, string]] {.async.} = if self.waku.isNil(): return err("Waku node is not initialized") - return ok(self.waku.healthMonitor.onlineMonitor.amIOnline()) + return await self.waku.isOnline() + +# Compile-time check that each concrete type satisfies its API concept. +static: + doAssert Waku is KernelApi + doAssert MessagingClient is MessagingApi + doAssert ReliableChannelManager is ReliableChannelApi + doAssert LogosDelivery is LogosDeliveryApi diff --git a/logos_delivery/messaging/api/send.nim b/logos_delivery/messaging/api/send.nim index 27c86d0ab..9a01c5f35 100644 --- a/logos_delivery/messaging/api/send.nim +++ b/logos_delivery/messaging/api/send.nim @@ -2,6 +2,7 @@ import results, chronos, chronicles import logos_delivery/api/types +import logos_delivery/api/events/messaging_client_events import logos_delivery/messaging/messaging_client import logos_delivery/waku/waku import logos_delivery/waku/api/subscriptions diff --git a/logos_delivery/messaging/delivery_service/recv_service/recv_service.nim b/logos_delivery/messaging/delivery_service/recv_service/recv_service.nim index 1090be223..d2c369d46 100644 --- a/logos_delivery/messaging/delivery_service/recv_service/recv_service.nim +++ b/logos_delivery/messaging/delivery_service/recv_service/recv_service.nim @@ -11,9 +11,9 @@ import logos_delivery/waku/waku, logos_delivery/waku/api/[store, subscriptions] import - logos_delivery/api/kernel_api, # MessageSeenEvent - logos_delivery/api/messaging_client_api, # MessageReceivedEvent - logos_delivery/api/logos_delivery_api # EventConnectionStatusChange + logos_delivery/api/events/kernel_events, + # MessageSeenEvent + EventConnectionStatusChange + logos_delivery/api/events/messaging_client_events # MessageReceivedEvent const MaxMessageLife = chronos.minutes(7) ## Max time we will keep track of rx messages diff --git a/logos_delivery/messaging/delivery_service/send_service/send_service.nim b/logos_delivery/messaging/delivery_service/send_service/send_service.nim index f482a2aff..037ad879b 100644 --- a/logos_delivery/messaging/delivery_service/send_service/send_service.nim +++ b/logos_delivery/messaging/delivery_service/send_service/send_service.nim @@ -10,7 +10,7 @@ import logos_delivery/waku/[waku_core, waku_store/common], logos_delivery/waku/waku, logos_delivery/waku/api/[store, subscriptions, publish] -import logos_delivery/api/messaging_client_api +import logos_delivery/api/events/messaging_client_events logScope: topics = "send service" diff --git a/logos_delivery/messaging/messaging_client.nim b/logos_delivery/messaging/messaging_client.nim index 4e02c7a82..e82c6d6fd 100644 --- a/logos_delivery/messaging/messaging_client.nim +++ b/logos_delivery/messaging/messaging_client.nim @@ -17,7 +17,7 @@ type ## follow-up PR. Today it only carries the p2p reliability toggle. useP2PReliability*: bool - MessagingClient* = ref object of IMessagingClient + MessagingClient* = ref object ## Implements `MessagingApi`. waku*: Waku ## The Waku kernel this layer drives; read by `messaging/api/*`. sendService*: SendService recvService*: RecvService diff --git a/logos_delivery/waku/api/events/health_events.nim b/logos_delivery/waku/api/events/health_events.nim index fbf00debb..9c49402f7 100644 --- a/logos_delivery/waku/api/events/health_events.nim +++ b/logos_delivery/waku/api/events/health_events.nim @@ -4,10 +4,7 @@ import logos_delivery/api/types import logos_delivery/waku/node/health_monitor/[protocol_health, topic_health] import logos_delivery/waku/waku_core/topics -export protocol_health, topic_health - -# Note: `EventConnectionStatusChange` lives in `logos_delivery/api/logos_delivery_api` -# (the top-level orchestrator interface owns the node-connectivity event). +export event_broker, protocol_health, topic_health # Notify health changes to a subscribed topic # TODO: emit content topic health change events when subscribe/unsubscribe diff --git a/logos_delivery/waku/api/peer_manager.nim b/logos_delivery/waku/api/peer_manager.nim index 26de46594..3a73051b8 100644 --- a/logos_delivery/waku/api/peer_manager.nim +++ b/logos_delivery/waku/api/peer_manager.nim @@ -8,10 +8,7 @@ import libp2p/[peerid, peerstore] import logos_delivery/waku/waku import logos_delivery/waku/[waku_core, node/waku_node, node/peer_manager] -type PeerConnInfo* = object ## structured connected-peer info for the api boundary - peerId*: string - protocols*: seq[string] - addresses*: seq[string] +# `PeerConnInfo` is defined in `api/types` (surfaced here via `import waku`). proc connect*( self: Waku, peers: seq[string], timeoutMs: uint32 diff --git a/logos_delivery/waku/node/health_monitor/node_health_monitor.nim b/logos_delivery/waku/node/health_monitor/node_health_monitor.nim index f59e0d7e6..8a4aadb4b 100644 --- a/logos_delivery/waku/node/health_monitor/node_health_monitor.nim +++ b/logos_delivery/waku/node/health_monitor/node_health_monitor.nim @@ -9,7 +9,7 @@ import libp2p/protocols/pubsub, libp2p/protocols/pubsub/rpc/messages, logos_delivery/api/types, - logos_delivery/api/logos_delivery_api, # EventConnectionStatusChange + logos_delivery/api/events/kernel_events, # EventConnectionStatusChange logos_delivery/waku/[ waku_relay, api/events/health_events, diff --git a/logos_delivery/waku/node/subscription_manager.nim b/logos_delivery/waku/node/subscription_manager.nim index d0468749d..2c18a7b5e 100644 --- a/logos_delivery/waku/node/subscription_manager.nim +++ b/logos_delivery/waku/node/subscription_manager.nim @@ -22,7 +22,7 @@ import node/health_monitor/topic_health, node/health_monitor/connection_status, ] -import logos_delivery/api/kernel_api # MessageSeenEvent +import logos_delivery/api/events/kernel_events # MessageSeenEvent {.push raises: [].} diff --git a/logos_delivery/waku/node/waku_node.nim b/logos_delivery/waku/node/waku_node.nim index 3c2e41d8a..db90ab47c 100644 --- a/logos_delivery/waku/node/waku_node.nim +++ b/logos_delivery/waku/node/waku_node.nim @@ -62,7 +62,7 @@ import api/events/health_events, api/events/peer_events, ], - logos_delivery/api/kernel_api, # MessageSeenEvent + logos_delivery/api/events/kernel_events, # MessageSeenEvent logos_delivery/waku/discovery/waku_kademlia, logos_delivery/waku/net/[bound_ports, net_config], ./peer_manager, diff --git a/logos_delivery/waku/node/waku_node/relay.nim b/logos_delivery/waku/node/waku_node/relay.nim index ed039fb49..18a3f0f59 100644 --- a/logos_delivery/waku/node/waku_node/relay.nim +++ b/logos_delivery/waku/node/waku_node/relay.nim @@ -34,7 +34,7 @@ import node/subscription_manager, node/peer_manager, ] -import logos_delivery/api/kernel_api # MessageSeenEvent +import logos_delivery/api/events/kernel_events # MessageSeenEvent export waku_relay.WakuRelayHandler diff --git a/logos_delivery/waku/waku.nim b/logos_delivery/waku/waku.nim index 1dba0f633..29290483f 100644 --- a/logos_delivery/waku/waku.nim +++ b/logos_delivery/waku/waku.nim @@ -57,8 +57,8 @@ import ./factory/waku_conf, ./factory/waku_state_info -# Surfaces the Kernel API interface (and its `MessageSeenEvent`) to consumers -# of the Waku layer. +# Surfaces the Kernel API interface to consumers of the Waku layer. +# `MessageSeenEvent` now lives in `events/kernel_events` (surfaced by the concentrator). export kernel_api logScope: @@ -67,7 +67,7 @@ logScope: # Git version in git describe format (defined at compile time) const git_version* {.strdefine.} = "n/a" -type Waku* = ref object of IKernel +type Waku* = ref object ## Implements `KernelApi` (ops in `waku/api/*`). stateInfo*: WakuStateInfo conf*: WakuConf rng*: crypto.Rng diff --git a/tests/channels/test_reliable_channel_send_receive.nim b/tests/channels/test_reliable_channel_send_receive.nim index 3a0a48c06..c20f0d0b0 100644 --- a/tests/channels/test_reliable_channel_send_receive.nim +++ b/tests/channels/test_reliable_channel_send_receive.nim @@ -10,7 +10,7 @@ import ../testlib/[common, wakucore, wakunode, testasync] import logos_delivery import logos_delivery/waku/[waku_node, waku_core] import logos_delivery/waku/factory/waku_conf -import logos_delivery/api/messaging_client_api as waku_message_events +import logos_delivery/api/events/messaging_client_events as waku_message_events import tools/confutils/cli_args import logos_delivery/channels/reliable_channel_manager From 440bd01cd88779b6e369f47d2f3c471fb1813c4b Mon Sep 17 00:00:00 2001 From: Darshan <35736874+darshankabariya@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:48:32 +0530 Subject: [PATCH 22/33] fix: handle on-chain RPC errors gracefully during RLN setup (#4008) --- .../group_manager/on_chain/rpc_wrapper.nim | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/logos_delivery/waku/rln/group_manager/on_chain/rpc_wrapper.nim b/logos_delivery/waku/rln/group_manager/on_chain/rpc_wrapper.nim index 9cb7f1ce3..c5fbc4a01 100644 --- a/logos_delivery/waku/rln/group_manager/on_chain/rpc_wrapper.nim +++ b/logos_delivery/waku/rln/group_manager/on_chain/rpc_wrapper.nim @@ -68,9 +68,22 @@ proc sendEthCallWithoutParams*( tx.data = Opt.some(byteutils.hexToSeqByte(dataSignature)) tx.chainId = Opt.some(chainId) - let resultBytes = await ethRpc.provider.eth_call(tx, "latest") + # A revert / rate-limit / dropped connection raises here; return err so RLN + # setup fails gracefully instead of crashing the node. + let resultBytes = + try: + await ethRpc.provider.eth_call(tx, "latest") + except CatchableError as e: + return err("eth_call failed for " & functionSignature & ": " & e.msg) + if resultBytes.len == 0: return err("No result returned for function call: " & functionSignature) + # UInt256.fromBytesBE panics (Defect) on < 32 bytes; reject short responses. + if resultBytes.len != 32: + return err( + "Unexpected result length (" & $resultBytes.len & " bytes) for function call: " & + functionSignature + ) return ok(UInt256.fromBytesBE(resultBytes)) proc sendEthCallWithParams*( @@ -94,5 +107,10 @@ proc sendEthCallWithParams*( tx.data = Opt.some(callData) tx.chainId = Opt.some(chainId) - let resultBytes = await ethRpc.provider.eth_call(tx, "latest") + let resultBytes = + try: + await ethRpc.provider.eth_call(tx, "latest") + except CatchableError as e: + return err("eth_call failed for " & functionSignature & ": " & e.msg) + return ok(resultBytes) From cbb601ec9f544b8a832ec6b9de6c872b07fb31a5 Mon Sep 17 00:00:00 2001 From: Tanya S <120410716+stubbsta@users.noreply.github.com> Date: Wed, 1 Jul 2026 14:02:07 +0200 Subject: [PATCH 23/33] =?UTF-8?q?chore:=20phase=204=20=E2=80=94=20remove?= =?UTF-8?q?=20RLN=20proof=20generation=20from=20lightpush=20server=20to=20?= =?UTF-8?q?client=20(#4009)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * remove RLN proof generation from lightpush server to client * set timestamp before generating lightpush msg proof * Add explicit error message for checkAndGenerateRLNProof and oneline for ensureTimestampSet * Change ensureTimestampSet from func to proc --- logos_delivery/waku/api/publish.nim | 12 ++--- .../waku/node/waku_node/lightpush.nim | 52 ++++++++++++------- logos_delivery/waku/rln/proof.nim | 46 +++++++++++----- .../waku/waku_core/message/message.nim | 5 ++ .../waku/waku_lightpush/callbacks.nim | 33 ++---------- logos_delivery/waku/waku_lightpush/client.nim | 7 +-- .../waku/waku_lightpush_legacy/callbacks.nim | 32 ++---------- .../waku/waku_lightpush_legacy/client.nim | 4 +- tests/node/test_wakunode_legacy_lightpush.nim | 11 +++- tests/node/test_wakunode_lightpush.nim | 11 +++- 10 files changed, 100 insertions(+), 113 deletions(-) diff --git a/logos_delivery/waku/api/publish.nim b/logos_delivery/waku/api/publish.nim index 51259504e..e45eff942 100644 --- a/logos_delivery/waku/api/publish.nim +++ b/logos_delivery/waku/api/publish.nim @@ -38,14 +38,10 @@ proc hasLightpush*(self: Waku): bool = return not self.node.wakuLightpushClient.isNil() proc relayPushHandler*(self: Waku): PushMessageHandler = - ## Builds the relay publish handler (appending an RLN proof when RLN is - ## mounted) used by the send pipeline. Caller ensures relay is mounted. - let rln = - if self.node.rln.isNil(): - none[Rln]() - else: - some(self.node.rln) - return getRelayPushHandler(self.node.wakuRelay, rln) + ## Builds the relay publish handler used by the send pipeline. Caller + ## ensures relay is mounted. RLN proof generation is handled client-side + ## in (legacy)lightpushPublish; this handler only validates and republishes. + return getRelayPushHandler(self.node.wakuRelay) proc lightpushPeerAvailable*(self: Waku, shard: PubsubTopic): bool = ## True if a lightpush service peer is available for `shard`. diff --git a/logos_delivery/waku/node/waku_node/lightpush.nim b/logos_delivery/waku/node/waku_node/lightpush.nim index 6b9d816fd..1bb4ddcbb 100644 --- a/logos_delivery/waku/node/waku_node/lightpush.nim +++ b/logos_delivery/waku/node/waku_node/lightpush.nim @@ -47,15 +47,7 @@ proc mountLegacyLightPush*( return err(MountWithoutRelayError) info "mounting legacy lightpush with relay" - let rlnPeer = - if node.rln.isNil(): - info "mounting legacy lightpush without rln-relay" - none(Rln) - else: - info "mounting legacy lightpush with rln-relay" - some(node.rln) - let pushHandler = - legacy_lightpush_protocol.getRelayPushHandler(node.wakuRelay, rlnPeer) + let pushHandler = legacy_lightpush_protocol.getRelayPushHandler(node.wakuRelay) node.wakuLegacyLightPush = WakuLegacyLightPush.new(node.peerManager, node.rng, pushHandler, some(rateLimit)) @@ -90,6 +82,19 @@ proc legacyLightpushPublish*( error "failed to publish message as legacy lightpush not available" return err("Waku lightpush not available") + # toRLNSignal includes the timestamp in the proof input, so the timestamp + # must be fixed before proof generation. The downstream ensureTimestampSet + # in the client publish becomes an idempotent no-op safety net. + let message = ensureTimestampSet(message) + + let rln = + if node.rln.isNil(): + none(Rln) + else: + some(node.rln) + let msgWithProof = (await checkAndGenerateRLNProof(rln, message)).valueOr: + return err("failed call checkAndGenerateRLNProof from lightpush: " & error) + let internalPublish = proc( node: WakuNode, pubsubTopic: PubsubTopic, @@ -115,7 +120,7 @@ proc legacyLightpushPublish*( await node.wakuLegacyLightPush.handleSelfLightPushRequest(pubsubTopic, message) try: if pubsubTopic.isSome(): - return await internalPublish(node, pubsubTopic.get(), message, peer) + return await internalPublish(node, pubsubTopic.get(), msgWithProof, peer) if node.wakuAutoSharding.isNone(): return err("Pubsub topic must be specified when static sharding is enabled") @@ -123,7 +128,7 @@ proc legacyLightpushPublish*( ?node.wakuAutoSharding.get().getShardsFromContentTopics(message.contentTopic) for pubsub, _ in topicMap.pairs: # There's only one pair anyway - return await internalPublish(node, $pubsub, message, peer) + return await internalPublish(node, $pubsub, msgWithProof, peer) except CatchableError: return err(getCurrentExceptionMsg()) @@ -158,14 +163,7 @@ proc mountLightPush*( return err(MountWithoutRelayError) info "mounting lightpush with relay" - let rlnPeer = - if node.rln.isNil(): - info "mounting lightpush without rln-relay" - none(Rln) - else: - info "mounting lightpush with rln-relay" - some(node.rln) - let pushHandler = lightpush_protocol.getRelayPushHandler(node.wakuRelay, rlnPeer) + let pushHandler = lightpush_protocol.getRelayPushHandler(node.wakuRelay) node.wakuLightPush = WakuLightPush.new( node.peerManager, node.rng, pushHandler, node.wakuAutoSharding, some(rateLimit) @@ -282,4 +280,18 @@ proc lightpushPublish*( error "lightpush publish error", error = msg return lighpushErrorResult(LightPushErrorCode.INTERNAL_SERVER_ERROR, msg) - return await lightpushPublishHandler(node, pubsubForPublish, message, toPeer, mixify) + # toRLNSignal includes the timestamp in the proof input, so the timestamp + # must be fixed before proof generation. The downstream ensureTimestampSet + # in the client publish becomes an idempotent no-op safety net. + let message = ensureTimestampSet(message) + + let rln = + if node.rln.isNil(): + none(Rln) + else: + some(node.rln) + let msgWithProof = (await checkAndGenerateRLNProof(rln, message)).valueOr: + return lighpushErrorResult(LightPushErrorCode.OUT_OF_RLN_PROOF, error) + + return + await lightpushPublishHandler(node, pubsubForPublish, msgWithProof, toPeer, mixify) diff --git a/logos_delivery/waku/rln/proof.nim b/logos_delivery/waku/rln/proof.nim index 8f1a63517..4a0c332c1 100644 --- a/logos_delivery/waku/rln/proof.nim +++ b/logos_delivery/waku/rln/proof.nim @@ -1,23 +1,23 @@ {.push raises: [].} -import std/[times, sequtils] -import chronos, results, stew/byteutils +import std/[options, times, sequtils] +import chronos, chronicles, results, stew/byteutils import ./types, ./protocol_types, ./conversion_utils, ./group_manager, ./nonce_manager import logos_delivery/waku/waku_core -proc calcEpoch*(rlnPeer: Rln, t: float64): Epoch = +proc calcEpoch*(rln: Rln, t: float64): Epoch = ## gets time `t` as `flaot64` with subseconds resolution in the fractional part ## and returns its corresponding rln `Epoch` value - let e = uint64(t / rlnPeer.rlnEpochSizeSec.float64) + let e = uint64(t / rln.rlnEpochSizeSec.float64) return toEpoch(e) -proc nextEpoch*(rlnPeer: Rln, time: float64): float64 = +proc nextEpoch*(rln: Rln, time: float64): float64 = let - currentEpoch = uint64(time / rlnPeer.rlnEpochSizeSec.float64) - nextEpochTime = float64(currentEpoch + 1) * rlnPeer.rlnEpochSizeSec.float64 + currentEpoch = uint64(time / rln.rlnEpochSizeSec.float64) + nextEpochTime = float64(currentEpoch + 1) * rln.rlnEpochSizeSec.float64 currentTime = epochTime() # Ensure we always return a future time @@ -26,8 +26,8 @@ proc nextEpoch*(rlnPeer: Rln, time: float64): float64 = else: return epochTime() -proc getCurrentEpoch*(rlnPeer: Rln): Epoch = - return rlnPeer.calcEpoch(epochTime()) +proc getCurrentEpoch*(rln: Rln): Epoch = + return rln.calcEpoch(epochTime()) proc absDiff*(e1, e2: Epoch): uint64 = ## returns the absolute difference between the two rln `Epoch`s `e1` and `e2` @@ -55,11 +55,31 @@ proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] = return output proc generateRLNProof*( - rlnPeer: Rln, input: seq[byte], senderEpochTime: float64 + rln: Rln, input: seq[byte], senderEpochTime: float64 ): Future[RlnResult[seq[byte]]] {.async.} = - let epoch = rlnPeer.calcEpoch(senderEpochTime) - let nonce = rlnPeer.nonceManager.getNonce().valueOr: + let epoch = rln.calcEpoch(senderEpochTime) + let nonce = rln.nonceManager.getNonce().valueOr: return err("could not get new message id to generate an rln proof: " & $error) - let proof = (await rlnPeer.groupManager.generateProof(input, epoch, nonce)).valueOr: + let proof = (await rln.groupManager.generateProof(input, epoch, nonce)).valueOr: return err("could not generate rln-v2 proof: " & $error) return ok(proof.encode().buffer) + +proc checkAndGenerateRLNProof*( + rln: Option[Rln], message: WakuMessage +): Future[Result[WakuMessage, string]] {.async.} = + if message.proof.len > 0: + return ok(message) + + if rln.isNone(): + notice "Publishing message without RLN proof" + return ok(message) + + let + time = getTime().toUnix() + senderEpochTime = float64(time) + var msgWithProof = message + msgWithProof.proof = ( + await rln.get().generateRLNProof(msgWithProof.toRLNSignal(), senderEpochTime) + ).valueOr: + return err("error in checkAndGenerateRLNProof: " & $error) + return ok(msgWithProof) diff --git a/logos_delivery/waku/waku_core/message/message.nim b/logos_delivery/waku/waku_core/message/message.nim index acd7055a0..f6e6c9336 100644 --- a/logos_delivery/waku/waku_core/message/message.nim +++ b/logos_delivery/waku/waku_core/message/message.nim @@ -27,3 +27,8 @@ type WakuMessage* = object # Data payload transmitted. # The proof attribute indicates that the message is not spam. This # attribute will be used in the rln-relay protocol. proof*: seq[byte] + +proc ensureTimestampSet*(message: WakuMessage): WakuMessage = + result = message + if result.timestamp == 0: + result.timestamp = getNowInNanosecondTime() diff --git a/logos_delivery/waku/waku_lightpush/callbacks.nim b/logos_delivery/waku/waku_lightpush/callbacks.nim index 68af190f1..80d9c2439 100644 --- a/logos_delivery/waku/waku_lightpush/callbacks.nim +++ b/logos_delivery/waku/waku_lightpush/callbacks.nim @@ -7,47 +7,20 @@ import ../waku_core, ../waku_relay, ./common, ../rln, ../rln/protocol_types import std/times, libp2p/peerid, stew/byteutils -proc checkAndGenerateRLNProof*( - rlnPeer: Option[Rln], message: WakuMessage -): Future[Result[WakuMessage, string]] {.async.} = - # check if the message already has RLN proof - if message.proof.len > 0: - return ok(message) - - if rlnPeer.isNone(): - notice "Publishing message without RLN proof" - return ok(message) - # generate and append RLN proof - let - time = getTime().toUnix() - senderEpochTime = float64(time) - var msgWithProof = message - msgWithProof.proof = ( - await rlnPeer.get().generateRLNProof(msgWithProof.toRLNSignal, senderEpochTime) - ).valueOr: - return err($error) - return ok(msgWithProof) - proc getNilPushHandler*(): PushMessageHandler = return proc( pubsubTopic: string, message: WakuMessage ): Future[WakuLightPushResult] {.async.} = return lightpushResultInternalError("no waku relay found") -proc getRelayPushHandler*( - wakuRelay: WakuRelay, rlnPeer: Option[Rln] = none[Rln]() -): PushMessageHandler = +proc getRelayPushHandler*(wakuRelay: WakuRelay): PushMessageHandler = return proc( pubsubTopic: string, message: WakuMessage ): Future[WakuLightPushResult] {.async.} = - # append RLN proof - let msgWithProof = (await checkAndGenerateRLNProof(rlnPeer, message)).valueOr: - return lighpushErrorResult(LightPushErrorCode.OUT_OF_RLN_PROOF, error) - - (await wakuRelay.validateMessage(pubSubTopic, msgWithProof)).isOkOr: + (await wakuRelay.validateMessage(pubSubTopic, message)).isOkOr: return lighpushErrorResult(LightPushErrorCode.INVALID_MESSAGE, $error) - let publishedResult = (await wakuRelay.publish(pubsubTopic, msgWithProof)).valueOr: + let publishedResult = (await wakuRelay.publish(pubsubTopic, message)).valueOr: let msgHash = computeMessageHash(pubsubTopic, message).to0xHex() notice "Lightpush request has not been published to any peers", msg_hash = msgHash, reason = $error diff --git a/logos_delivery/waku/waku_lightpush/client.nim b/logos_delivery/waku/waku_lightpush/client.nim index 680970a51..9572b9323 100644 --- a/logos_delivery/waku/waku_lightpush/client.nim +++ b/logos_delivery/waku/waku_lightpush/client.nim @@ -24,10 +24,6 @@ type WakuLightPushClient* = ref object proc new*(T: type WakuLightPushClient, peerManager: PeerManager, rng: crypto.Rng): T = WakuLightPushClient(peerManager: peerManager, rng: rng) -proc ensureTimestampSet(message: var WakuMessage) = - if message.timestamp == 0: - message.timestamp = getNowInNanosecondTime() - ## Short log string for peer identifiers (overloads for convenience) func shortPeerId(peer: PeerId): string = shortLog(peer) @@ -82,8 +78,7 @@ proc publish*( wakuMessage: WakuMessage, dest: Connection | PeerId | RemotePeerInfo, ): Future[WakuLightPushResult] {.async, gcsafe.} = - var message = wakuMessage - ensureTimestampSet(message) + let message = ensureTimestampSet(wakuMessage) let msgHash = computeMessageHash(pubSubTopic.get(""), message).to0xHex() diff --git a/logos_delivery/waku/waku_lightpush_legacy/callbacks.nim b/logos_delivery/waku/waku_lightpush_legacy/callbacks.nim index 4fc92d516..fa6ec92d4 100644 --- a/logos_delivery/waku/waku_lightpush_legacy/callbacks.nim +++ b/logos_delivery/waku/waku_lightpush_legacy/callbacks.nim @@ -10,45 +10,19 @@ import import std/times, libp2p/peerid, stew/byteutils -proc checkAndGenerateRLNProof*( - rlnPeer: Option[Rln], message: WakuMessage -): Future[Result[WakuMessage, string]] {.async.} = - # check if the message already has RLN proof - if message.proof.len > 0: - return ok(message) - - if rlnPeer.isNone(): - notice "Publishing message without RLN proof" - return ok(message) - # generate and append RLN proof - let - time = getTime().toUnix() - senderEpochTime = float64(time) - var msgWithProof = message - msgWithProof.proof = ( - await rlnPeer.get().generateRLNProof(msgWithProof.toRLNSignal(), senderEpochTime) - ).valueOr: - return err($error) - return ok(msgWithProof) - proc getNilPushHandler*(): PushMessageHandler = return proc( pubsubTopic: string, message: WakuMessage ): Future[WakuLightPushResult[void]] {.async.} = return err("no waku relay found") -proc getRelayPushHandler*( - wakuRelay: WakuRelay, rlnPeer: Option[Rln] = none[Rln]() -): PushMessageHandler = +proc getRelayPushHandler*(wakuRelay: WakuRelay): PushMessageHandler = return proc( pubsubTopic: string, message: WakuMessage ): Future[WakuLightPushResult[void]] {.async.} = - # append RLN proof - let msgWithProof = ?(await checkAndGenerateRLNProof(rlnPeer, message)) + ?(await wakuRelay.validateMessage(pubSubTopic, message)) - ?(await wakuRelay.validateMessage(pubSubTopic, msgWithProof)) - - (await wakuRelay.publish(pubsubTopic, msgWithProof)).isOkOr: + (await wakuRelay.publish(pubsubTopic, message)).isOkOr: ## Agreed change expected to the lightpush protocol to better handle such case. https://github.com/waku-org/pm/issues/93 let msgHash = computeMessageHash(pubsubTopic, message).to0xHex() notice "Lightpush request has not been published to any peers", diff --git a/logos_delivery/waku/waku_lightpush_legacy/client.nim b/logos_delivery/waku/waku_lightpush_legacy/client.nim index 511c3f543..0ed5ca823 100644 --- a/logos_delivery/waku/waku_lightpush_legacy/client.nim +++ b/logos_delivery/waku/waku_lightpush_legacy/client.nim @@ -73,9 +73,7 @@ proc publish*( ): Future[WakuLightPushResult[string]] {.async, gcsafe.} = ## On success, returns the msg_hash of the published message - var message = wakuMessage - if message.timestamp == 0: - message.timestamp = getNowInNanosecondTime() + let message = ensureTimestampSet(wakuMessage) let msg_hash_hex_str = computeMessageHash(pubsubTopic, message).to0xHex() let pushRequest = PushRequest(pubSubTopic: pubSubTopic, message: message) diff --git a/tests/node/test_wakunode_legacy_lightpush.nim b/tests/node/test_wakunode_legacy_lightpush.nim index c2e901d41..9301e9e01 100644 --- a/tests/node/test_wakunode_legacy_lightpush.nim +++ b/tests/node/test_wakunode_legacy_lightpush.nim @@ -99,6 +99,7 @@ suite "RLN Proofs as a Lightpush Service": client {.threadvar.}: WakuNode anvilProc {.threadvar.}: Process manager {.threadvar.}: OnchainGroupManager + wakuRlnConfig {.threadvar.}: WakuRlnConfig serverRemotePeerInfo {.threadvar.}: RemotePeerInfo pubsubTopic {.threadvar.}: PubsubTopic @@ -118,7 +119,7 @@ suite "RLN Proofs as a Lightpush Service": # mount rln-relay # match prod epoch window to reduce test flake - let wakuRlnConfig = getWakuRlnConfig( + wakuRlnConfig = getWakuRlnConfig( manager = manager, index = MembershipIndex(1), epochSizeSec = 600 ) @@ -161,9 +162,15 @@ suite "RLN Proofs as a Lightpush Service": let lightpushClient = newTestWakuNode(generateSecp256k1Key()) lightpushClient.mountLegacyLightPushClient() + # Attach the RLN proof. In production the client mounts RLN and generates the + # proof in legacyLightpushPublish; here we generate it using the server's RLN + # instance since both ends share group state via the in-memory manager. + let msgWithProof = + (await checkAndGenerateRLNProof(some(server.rln), message)).get() + # When the client publishes a message let publishResponse = await lightpushClient.legacyLightpushPublish( - some(pubsubTopic), message, serverRemotePeerInfo + some(pubsubTopic), msgWithProof, serverRemotePeerInfo ) if not publishResponse.isOk(): diff --git a/tests/node/test_wakunode_lightpush.nim b/tests/node/test_wakunode_lightpush.nim index cc82cb7a3..6e87ab237 100644 --- a/tests/node/test_wakunode_lightpush.nim +++ b/tests/node/test_wakunode_lightpush.nim @@ -96,6 +96,7 @@ suite "RLN Proofs as a Lightpush Service": client {.threadvar.}: WakuNode anvilProc {.threadvar.}: Process manager {.threadvar.}: OnchainGroupManager + wakuRlnConfig {.threadvar.}: WakuRlnConfig serverRemotePeerInfo {.threadvar.}: RemotePeerInfo pubsubTopic {.threadvar.}: PubsubTopic @@ -115,7 +116,7 @@ suite "RLN Proofs as a Lightpush Service": # mount rln-relay # match prod epoch window to reduce test flake - let wakuRlnConfig = getWakuRlnConfig( + wakuRlnConfig = getWakuRlnConfig( manager = manager, index = MembershipIndex(1), epochSizeSec = 600 ) @@ -158,9 +159,15 @@ suite "RLN Proofs as a Lightpush Service": let lightpushClient = newTestWakuNode(generateSecp256k1Key()) lightpushClient.mountLightPushClient() + # Attach the RLN proof. In production the client mounts RLN and generates the + # proof in lightpushPublish; here we generate it using the server's RLN instance + # since both ends share group state via the in-memory manager. + let msgWithProof = + (await checkAndGenerateRLNProof(some(server.rln), message)).get() + # When the client publishes a message let publishResponse = await lightpushClient.lightpushPublish( - some(pubsubTopic), message, some(serverRemotePeerInfo) + some(pubsubTopic), msgWithProof, some(serverRemotePeerInfo) ) if not publishResponse.isOk(): From ad175416194d91c0eafa1dfc15ab8b17ad69b5ab Mon Sep 17 00:00:00 2001 From: Darshan <35736874+darshankabariya@users.noreply.github.com> Date: Thu, 2 Jul 2026 18:46:46 +0530 Subject: [PATCH 24/33] fix: handle peer-exchange stream-closed errors gracefully (#4010) --- .../waku/waku_peer_exchange/common.nim | 1 + .../waku/waku_peer_exchange/protocol.nim | 81 ++++++++----------- 2 files changed, 34 insertions(+), 48 deletions(-) diff --git a/logos_delivery/waku/waku_peer_exchange/common.nim b/logos_delivery/waku/waku_peer_exchange/common.nim index 85d7398b1..91f1ac647 100644 --- a/logos_delivery/waku/waku_peer_exchange/common.nim +++ b/logos_delivery/waku/waku_peer_exchange/common.nim @@ -17,5 +17,6 @@ const decodeRpcFailure* = "decode_rpc_failure" retrievePeersDiscv5Error* = "retrieve_peers_discv5_failure" pxFailure* = "px_failure" + streamClosedFailure* = "stream_closed_failure" type WakuPeerExchangeResult*[T] = Result[T, PeerExchangeResponseStatus] diff --git a/logos_delivery/waku/waku_peer_exchange/protocol.nim b/logos_delivery/waku/waku_peer_exchange/protocol.nim index a389938f8..ce1f0cd04 100644 --- a/logos_delivery/waku/waku_peer_exchange/protocol.nim +++ b/logos_delivery/waku/waku_peer_exchange/protocol.nim @@ -6,6 +6,7 @@ import chronos, metrics, libp2p/protocols/protocol, + libp2p/stream/connection, libp2p/crypto/crypto, eth/p2p/discoveryv5/enr import @@ -38,18 +39,20 @@ type WakuPeerExchange* = ref object of LPProtocol proc respond( wpx: WakuPeerExchange, enrs: seq[enr.Record], conn: Connection -): Future[WakuPeerExchangeResult[void]] {.async, gcsafe.} = +): Future[WakuPeerExchangeResult[void]] {.async: (raises: [CancelledError]), gcsafe.} = let rpc = PeerExchangeRpc.makeResponse(enrs.mapIt(PeerExchangePeerInfo(enr: it.raw))) try: await conn.writeLP(rpc.encode().buffer) - except CatchableError as exc: - error "exception when trying to send a respond", error = getCurrentExceptionMsg() - waku_px_errors.inc(labelValues = [exc.msg]) + except LPStreamError as exc: + # Remote closed the stream before we responded - expected during peer churn. + debug "peer exchange response not delivered: stream closed", + peerId = conn.peerId, error = exc.msg + waku_px_errors.inc(labelValues = [streamClosedFailure]) return err( ( status_code: PeerExchangeResponseStatusCode.DIAL_FAILURE, - status_desc: some("exception dialing peer: " & exc.msg), + status_desc: some("stream closed before response: " & exc.msg), ) ) @@ -60,18 +63,20 @@ proc respondError( status_code: PeerExchangeResponseStatusCode, status_desc: Option[string], conn: Connection, -): Future[WakuPeerExchangeResult[void]] {.async, gcsafe.} = +): Future[WakuPeerExchangeResult[void]] {.async: (raises: [CancelledError]), gcsafe.} = let rpc = PeerExchangeRpc.makeErrorResponse(status_code, status_desc) try: await conn.writeLP(rpc.encode().buffer) - except CatchableError as exc: - error "exception when trying to send a respond", error = getCurrentExceptionMsg() - waku_px_errors.inc(labelValues = [exc.msg]) + except LPStreamError as exc: + # Remote closed the stream before we responded - expected during peer churn. + debug "peer exchange error response not delivered: stream closed", + peerId = conn.peerId, error = exc.msg + waku_px_errors.inc(labelValues = [streamClosedFailure]) return err( ( status_code: PeerExchangeResponseStatusCode.SERVICE_UNAVAILABLE, - status_desc: some("exception dialing peer: " & exc.msg), + status_desc: some("stream closed before response: " & exc.msg), ) ) @@ -128,20 +133,12 @@ proc initProtocolHandler(wpx: WakuPeerExchange) = wpx.requestRateLimiter.checkUsageLimit(WakuPeerExchangeCodec, conn): try: buffer = await conn.readLp(DefaultMaxRpcSize.int) - except CatchableError as exc: - error "exception when handling px request", error = getCurrentExceptionMsg() - waku_px_errors.inc(labelValues = [exc.msg]) - - ( - try: - await wpx.respondError( - PeerExchangeResponseStatusCode.BAD_REQUEST, some(exc.msg), conn - ) - except CatchableError: - error "could not send error response", error = getCurrentExceptionMsg() - return - ).isOkOr: - error "Failed to respond with BAD_REQUEST:", error = $error + except LPStreamError as exc: + # Remote disconnected before sending a full request - expected churn; + # no point responding on a dead stream. + debug "peer exchange request not received: stream closed", + peerId = conn.peerId, error = exc.msg + waku_px_errors.inc(labelValues = [streamClosedFailure]) return let decBuf = PeerExchangeRpc.decode(buffer).valueOr: @@ -149,42 +146,30 @@ proc initProtocolHandler(wpx: WakuPeerExchange) = error "Failed to decode PeerExchange request", error = $error ( - try: - await wpx.respondError( - PeerExchangeResponseStatusCode.BAD_REQUEST, some($error), conn - ) - except CatchableError: - error "could not send error response decode", - error = getCurrentExceptionMsg() - return + await wpx.respondError( + PeerExchangeResponseStatusCode.BAD_REQUEST, some($error), conn + ) ).isOkOr: - error "Failed to respond with BAD_REQUEST:", error = $error + error "Failed to respond with BAD_REQUEST", error = $error return let enrs = wpx.getEnrsFromStore(decBuf.request.numPeers) info "peer exchange request received" trace "px enrs to respond", enrs = $enrs - try: - (await wpx.respond(enrs, conn)).isErrOr: - waku_px_peers_sent.inc(enrs.len().int64()) - except CatchableError: - error "could not send response", error = getCurrentExceptionMsg() + (await wpx.respond(enrs, conn)).isErrOr: + waku_px_peers_sent.inc(enrs.len().int64()) do: defer: # close, no data is expected await conn.closeWithEof() - try: - ( - await wpx.respondError( - PeerExchangeResponseStatusCode.TOO_MANY_REQUESTS, none(string), conn - ) - ).isOkOr: - error "Failed to respond with TOO_MANY_REQUESTS:", error = $error - except CatchableError: - error "could not send error response", error = getCurrentExceptionMsg() - return + ( + await wpx.respondError( + PeerExchangeResponseStatusCode.TOO_MANY_REQUESTS, none(string), conn + ) + ).isOkOr: + error "Failed to respond with TOO_MANY_REQUESTS", error = $error wpx.handler = handler wpx.codec = WakuPeerExchangeCodec From 42ccb5c67310fe5d341abf11ad354cfc54ba2dea Mon Sep 17 00:00:00 2001 From: NagyZoltanPeter <113987313+NagyZoltanPeter@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:46:05 +0200 Subject: [PATCH 25/33] chore: replace ReliableChannel sendhandler hack (#3994) * squash original commits and rebase to master: removed sendHandler and replaced by MessagingSend BrokerRequest * Needed to adapt to new messaging source structure to avoid cyclic imports * Relocate MessagingSend request broker and eliminate api/requests * fix ci fails --- logos_delivery.nimble | 2 +- logos_delivery/api/messaging_client_api.nim | 11 +- .../api/reliable_channel_manager_api.nim | 8 - .../channels/api/channel_lifecycle.nim | 12 -- logos_delivery/channels/reliable_channel.nim | 30 +--- .../channels/reliable_channel_manager.nim | 26 +--- logos_delivery/logos_delivery.nim | 4 +- logos_delivery/messaging/messaging_client.nim | 30 ++-- .../messaging/messaging_client_lifecycle.nim | 37 +++++ .../waku/persistency/backend_thread.nim | 6 +- .../waku/persistency/persistency.nim | 2 +- nimble.lock | 6 +- nix/deps.nix | 5 +- .../test_reliable_channel_send_receive.nim | 140 +++++++++--------- tests/node/test_wakunode_health_monitor.nim | 1 + tests/persistency/test_lifecycle.nim | 10 +- 16 files changed, 154 insertions(+), 176 deletions(-) create mode 100644 logos_delivery/messaging/messaging_client_lifecycle.nim diff --git a/logos_delivery.nimble b/logos_delivery.nimble index efe118595..8c4d32cb5 100644 --- a/logos_delivery.nimble +++ b/logos_delivery.nimble @@ -65,7 +65,7 @@ requires "https://github.com/logos-messaging/nim-ffi#v0.1.3" requires "https://github.com/logos-messaging/nim-sds.git#b12f5ee07c5b764303b51fb948b32a4ade1de3b5" -requires "https://github.com/NagyZoltanPeter/nim-brokers.git#v3.1.1" +requires "https://github.com/NagyZoltanPeter/nim-brokers.git#v3.1.4" requires "https://github.com/vacp2p/nim-lsquic.git#v0.5.1" requires "https://github.com/vacp2p/nim-jwt.git#057ec95eb5af0eea9c49bfe9025b3312c95dc5f2" diff --git a/logos_delivery/api/messaging_client_api.nim b/logos_delivery/api/messaging_client_api.nim index 536ce94fa..5906ab564 100644 --- a/logos_delivery/api/messaging_client_api.nim +++ b/logos_delivery/api/messaging_client_api.nim @@ -1,11 +1,18 @@ import chronos, results - +import brokers/[request_broker, broker_context] import logos_delivery/api/types as api_types -export api_types +export api_types, request_broker, broker_context # Structural API contract for a messaging client (ops in `messaging/api/*`). type MessagingApi* = concept c subscribe(c, contentTopic = ContentTopic) is Future[Result[void, string]] unsubscribe(c, contentTopic = ContentTopic) is Result[void, string] send(c, envelope = MessageEnvelope) is Future[Result[RequestId, string]] + +# Semi detached MessagingClient send interface. +# Can be used without MessagingClient, under the same context of MessagingClient instance. +RequestBroker: + proc MessagingSend( + envelope: MessageEnvelope + ): Future[Result[RequestId, string]] {.async.} diff --git a/logos_delivery/api/reliable_channel_manager_api.nim b/logos_delivery/api/reliable_channel_manager_api.nim index d50b57288..8d3f63ff3 100644 --- a/logos_delivery/api/reliable_channel_manager_api.nim +++ b/logos_delivery/api/reliable_channel_manager_api.nim @@ -5,14 +5,6 @@ import logos_delivery/channels/types as channel_types export api_types, channel_types -type SendHandler* = proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {. - async: (raises: [CatchableError]), gcsafe -.} - ## Egress dispatch boundary. Typically wraps `MessagingClient.send`; - ## tests inject a fake that records calls and returns canned - ## `RequestId`s so the send state machine can be exercised end-to-end - ## without a network. - # Structural API contract for the reliable-channel surface (ops in `channels/api/*`). type ReliableChannelApi* = concept c createReliableChannel( diff --git a/logos_delivery/channels/api/channel_lifecycle.nim b/logos_delivery/channels/api/channel_lifecycle.nim index b50ba1b63..4f891c61f 100644 --- a/logos_delivery/channels/api/channel_lifecycle.nim +++ b/logos_delivery/channels/api/channel_lifecycle.nim @@ -32,16 +32,7 @@ proc createReliableChannel*( channelId: ChannelId, contentTopic: ContentTopic, senderId: SdsParticipantID, - sendHandler: SendHandler = nil, ): Result[ChannelId, string] = - ## Spec entry point. The `sendHandler` and `rng` the channel needs are - ## sourced from the owning `ReliableChannelManager` rather than passed - ## per call. Encryption is wired up through the `Encrypt`/`Decrypt` - ## request brokers — the application installs its own providers - ## (or `setNoopEncryption()`) before traffic flows. - ## - ## `sendHandler` defaults to the manager's default (constructed at mount - ## from `MessagingClient.send`); tests pass a fake to bypass the network. if self.channels.hasKey(channelId): return err("channel already exists: " & channelId) @@ -60,10 +51,7 @@ proc createReliableChannel*( epochPeriodSec: DefaultEpochPeriodSec, messagesPerEpoch: DefaultMessagesPerEpoch ) - let effectiveSendHandler = if sendHandler.isNil(): self.sendHandler else: sendHandler - let chn = ReliableChannel.new( - sendHandler = effectiveSendHandler, channelId = channelId, contentTopic = contentTopic, senderId = senderId, diff --git a/logos_delivery/channels/reliable_channel.nim b/logos_delivery/channels/reliable_channel.nim index 06648a8f7..4daf8a1e2 100644 --- a/logos_delivery/channels/reliable_channel.nim +++ b/logos_delivery/channels/reliable_channel.nim @@ -24,8 +24,9 @@ import libp2p/crypto/crypto as libp2p_crypto import logos_delivery/api/types import logos_delivery/api/reliable_channel_manager_api import logos_delivery/api/events/messaging_client_events +import logos_delivery/api/messaging_client_api import logos_delivery/api/events/reliable_channel_manager_events -import logos_delivery/messaging/delivery_service/send_service +import logos_delivery/messaging/messaging_client import logos_delivery/waku/waku_core/topics import ./segmentation/segmentation @@ -34,7 +35,7 @@ import ./rate_limit_manager/rate_limit_manager import ./encryption/encryption export - types, reliable_channel_manager_api, send_service, segmentation, scalable_data_sync, + types, reliable_channel_manager_api, segmentation, scalable_data_sync, rate_limit_manager, encryption const LipWireReliableChannelVersion* = "RELIABLE-CHANNEL-API/1" @@ -84,7 +85,6 @@ type ## Spec-defined public type. Fields are private so callers cannot ## mutate internals and break invariants. Getters are added below ## for the few values consumers may need. - sendHandler: SendHandler channelId: ChannelId contentTopic: ContentTopic senderId: SdsParticipantID @@ -263,14 +263,7 @@ proc onReadyToSend( meta: LipWireReliableChannelVersion.toBytes(), ) - ## `sendHandler` is not annotated `(raises: [])`, but this listener is. - ## Convert any raise to a Result error so the state machine handles - ## both failure modes (Result.err and exception) through one path. - let sendRes = - try: - await self.sendHandler(envelope) - except CatchableError as e: - Result[RequestId, string].err("messaging send raised: " & e.msg) + let sendRes = await MessagingSend.request(self.brokerCtx, envelope) let messagingReqId = sendRes.valueOr: MessageErrorEvent.emit( @@ -360,14 +353,9 @@ proc dispatchRepair(self: ReliableChannel, wire: seq[byte]) {.async: (raises: [] meta: LipWireReliableChannelVersion.toBytes(), ) - let sendRes = - try: - await self.sendHandler(envelope) - except CatchableError as e: - Result[RequestId, string].err("messaging send raised: " & e.msg) - if sendRes.isErr(): + (await MessagingSend.request(self.brokerCtx, envelope)).isOkOr: debug "SDS repair rebroadcast dropped: dispatch failed", - channelId = self.channelId, error = sendRes.error + channelId = self.channelId, error = error proc onMessageReceived( self: ReliableChannel, messageHash: string, payload: seq[byte] @@ -415,7 +403,6 @@ proc onMessageReceived( proc new*( T: type ReliableChannel, - sendHandler: SendHandler, channelId: ChannelId, contentTopic: ContentTopic, senderId: SdsParticipantID, @@ -430,12 +417,7 @@ proc new*( ## should be wiring up. Encryption is delegated to the `Encrypt`/ ## `Decrypt` request brokers, so the channel keeps no per-instance ## encryption state either. - ## - ## `sendHandler` is the egress dispatch. The owning `ReliableChannelManager` - ## typically constructs it as a closure over `MessagingClient.send`. Tests - ## pass a fake to drive the send state machine without touching the network. let chn = T( - sendHandler: sendHandler, channelId: channelId, contentTopic: contentTopic, senderId: senderId, diff --git a/logos_delivery/channels/reliable_channel_manager.nim b/logos_delivery/channels/reliable_channel_manager.nim index e8d579326..8ff176e9a 100644 --- a/logos_delivery/channels/reliable_channel_manager.nim +++ b/logos_delivery/channels/reliable_channel_manager.nim @@ -30,38 +30,14 @@ type ReliableChannelManager* = ref object ## Implements `ReliableChannelApi`. channels*: Table[ChannelId, ReliableChannel] ## read by `channels/api.nim` - messagingClient: MessagingClient ## The channel layer chains onto messaging. - sendHandler*: SendHandler - ## Default egress dispatch for channels created through this manager. - ## Built in `new` as a closure over `MessagingClient.send` so the channel - ## layer itself stays callable-only. brokerCtx*: BrokerContext proc new*( T: type ReliableChannelManager, conf: ReliableChannelManagerConf, - messagingClient: MessagingClient, brokerCtx: BrokerContext = globalBrokerContext(), ): Result[T, string] = - ## The reliable channel layer chains onto the messaging layer: its default - ## egress is `MessagingClient.send`, wrapped here so callers never wire the - ## handler themselves. - if messagingClient.isNil(): - return err("messaging client is required") - - let defaultSendHandler: SendHandler = proc( - envelope: MessageEnvelope - ): Future[Result[RequestId, string]] {.async: (raises: [CatchableError]), gcsafe.} = - return await messagingClient.send(envelope) - - return ok( - T( - channels: initTable[ChannelId, ReliableChannel](), - messagingClient: messagingClient, - sendHandler: defaultSendHandler, - brokerCtx: brokerCtx, - ) - ) + return ok(T(channels: initTable[ChannelId, ReliableChannel](), brokerCtx: brokerCtx)) proc start*(self: ReliableChannelManager): Result[void, string] = ## Placeholder: per-channel listeners are installed in `ReliableChannel.new`, diff --git a/logos_delivery/logos_delivery.nim b/logos_delivery/logos_delivery.nim index 0b161d6f7..6a6587ab0 100644 --- a/logos_delivery/logos_delivery.nim +++ b/logos_delivery/logos_delivery.nim @@ -35,7 +35,7 @@ import logos_delivery/waku/api/events/health_events export health_events # Messaging layer -import logos_delivery/messaging/messaging_client +import logos_delivery/messaging/[messaging_client, messaging_client_lifecycle] export messaging_client import logos_delivery/messaging/api/[subscription, send] export subscription, send @@ -103,7 +103,7 @@ proc new*( return err("failed to create MessagingClient: " & error) let reliableChannelManager = ReliableChannelManager.new( - layerConf.reliableChannel, messagingClient, waku.brokerCtx + layerConf.reliableChannel, waku.brokerCtx ).valueOr: return err("failed to create ReliableChannelManager: " & error) diff --git a/logos_delivery/messaging/messaging_client.nim b/logos_delivery/messaging/messaging_client.nim index e82c6d6fd..32f5439e5 100644 --- a/logos_delivery/messaging/messaging_client.nim +++ b/logos_delivery/messaging/messaging_client.nim @@ -2,6 +2,7 @@ ## lifecycle. The public operations (subscribe / unsubscribe / send) live in ## `messaging/api.nim`. import results, chronos +import chronicles import logos_delivery/api/messaging_client_api, logos_delivery/waku/waku, @@ -17,11 +18,12 @@ type ## follow-up PR. Today it only carries the p2p reliability toggle. useP2PReliability*: bool - MessagingClient* = ref object ## Implements `MessagingApi`. + MessagingClient* = ref object + brokerCtx*: BrokerContext waku*: Waku ## The Waku kernel this layer drives; read by `messaging/api/*`. sendService*: SendService recvService*: RecvService - started: bool + started*: bool proc new*( T: type MessagingClient, conf: MessagingClientConf, waku: Waku @@ -30,22 +32,14 @@ proc new*( ## for transport while exposing its own send/recv API. let sendService = ?SendService.new(conf.useP2PReliability, waku) let recvService = RecvService.new(waku) - return ok(T(waku: waku, sendService: sendService, recvService: recvService)) - -proc start*(self: MessagingClient): Result[void, string] = - if self.started: - return ok() - self.recvService.startRecvService() - self.sendService.startSendService() - self.started = true - ok() - -proc stop*(self: MessagingClient) {.async.} = - if not self.started: - return - await self.sendService.stopSendService() - await self.recvService.stopRecvService() - self.started = false + return ok( + T( + waku: waku, + sendService: sendService, + recvService: recvService, + brokerCtx: waku.brokerCtx, + ) + ) proc checkApiAvailability*(self: MessagingClient): Result[void, string] = ## Shared guard for the api operation module. diff --git a/logos_delivery/messaging/messaging_client_lifecycle.nim b/logos_delivery/messaging/messaging_client_lifecycle.nim new file mode 100644 index 000000000..52ea295aa --- /dev/null +++ b/logos_delivery/messaging/messaging_client_lifecycle.nim @@ -0,0 +1,37 @@ +## Messaging layer core: the `MessagingClient` type plus its construction and +## lifecycle. The public operations (subscribe / unsubscribe / send) live in +## `messaging/api.nim`. +import results, chronos +import chronicles +import + logos_delivery/messaging/messaging_client, + logos_delivery/messaging/api/send, + logos_delivery/api/messaging_client_api, + logos_delivery/waku/waku, + logos_delivery/messaging/delivery_service/[recv_service, send_service] + +# Surfaces the messaging API interface (and its Message* events) to consumers. +export messaging_client + +proc start*(self: MessagingClient): Result[void, string] = + if self.started: + return ok() + self.recvService.startRecvService() + self.sendService.startSendService() + + ?MessagingSend.setProvider( + self.brokerCtx, + proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {.async.} = + return await self.send(envelope), + ) + + self.started = true + ok() + +proc stop*(self: MessagingClient) {.async.} = + if not self.started: + return + MessagingSend.clearProvider(self.brokerCtx) + await self.sendService.stopSendService() + await self.recvService.stopRecvService() + self.started = false diff --git a/logos_delivery/waku/persistency/backend_thread.nim b/logos_delivery/waku/persistency/backend_thread.nim index e32e5c209..28f328729 100644 --- a/logos_delivery/waku/persistency/backend_thread.nim +++ b/logos_delivery/waku/persistency/backend_thread.nim @@ -162,13 +162,13 @@ proc registerProviders(backend: KvBackend, ctx: BrokerContext): Result[void, str return ok() -proc clearProviders(ctx: BrokerContext) = +proc clearProviders(ctx: BrokerContext) {.async.} = KvGet.clearProvider(ctx) KvExists.clearProvider(ctx) KvScan.clearProvider(ctx) KvCount.clearProvider(ctx) KvDelete.clearProvider(ctx) - PersistEvent.dropAllListeners(ctx) + await PersistEvent.dropAllListeners(ctx) # ── thread proc ───────────────────────────────────────────────────────── @@ -217,7 +217,7 @@ proc storageThreadMain(arg: ptr StorageThreadArg) {.thread.} = except CatchableError as e: error "storage thread loop crashed", err = e.msg - clearProviders(arg.ctx) + waitFor clearProviders(arg.ctx) backend.close() # ── lifecycle ─────────────────────────────────────────────────────────── diff --git a/logos_delivery/waku/persistency/persistency.nim b/logos_delivery/waku/persistency/persistency.nim index fca6ad34d..d6ac28a1e 100644 --- a/logos_delivery/waku/persistency/persistency.nim +++ b/logos_delivery/waku/persistency/persistency.nim @@ -272,7 +272,7 @@ proc hasJob*(p: Persistency, jobId: string): bool {.inline.} = proc persist*(t: Job, ops: seq[TxOp]): Future[void] {.async.} = ## Emit a batched persist event. The handler treats >1 ops as a single ## BEGIN IMMEDIATE/COMMIT transaction (see backend_sqlite.applyOps). - await PersistEvent.emit(t.context, PersistEvent(ops: ops)) + PersistEvent.emit(t.context, PersistEvent(ops: ops)) proc persist*(t: Job, op: TxOp): Future[void] {.async.} = await persist(t, @[op]) diff --git a/nimble.lock b/nimble.lock index 18ebde258..03bb7a434 100644 --- a/nimble.lock +++ b/nimble.lock @@ -328,8 +328,8 @@ } }, "brokers": { - "version": "#v3.1.1", - "vcsRevision": "a7316a35f1b62e3497ae8ee0fc1aace74df0beb2", + "version": "#v3.1.4", + "vcsRevision": "8ae9e963b0b4478c93e6f888be6a46654da787de", "url": "https://github.com/NagyZoltanPeter/nim-brokers.git", "downloadMethod": "git", "dependencies": [ @@ -341,7 +341,7 @@ "cbor_serialization" ], "checksums": { - "sha1": "4447d7c1f9da14ae439afb23aee45116ce2ecb40" + "sha1": "2949398033c1b3a2586f0afd7e92f46b9a0a412e" } }, "stint": { diff --git a/nix/deps.nix b/nix/deps.nix index 00dea27c2..63b01f8af 100644 --- a/nix/deps.nix +++ b/nix/deps.nix @@ -159,8 +159,8 @@ brokers = pkgs.fetchgit { url = "https://github.com/NagyZoltanPeter/nim-brokers.git"; - rev = "a7316a35f1b62e3497ae8ee0fc1aace74df0beb2"; - sha256 = "1990270n88jm0i48g07zr4vq2nn79g7gymf28f3g5ak42g33l7rm"; + rev = "8ae9e963b0b4478c93e6f888be6a46654da787de"; + sha256 = "0r3vrc6h9jiwhd3p4sy3j8gx3k5174s77qcp2wrswclvfvbskw5y"; fetchSubmodules = true; }; @@ -277,7 +277,6 @@ }; sds = pkgs.fetchgit { - # Keep in sync with the nim-sds pin in nimble.lock. url = "https://github.com/logos-messaging/nim-sds.git"; rev = "b12f5ee07c5b764303b51fb948b32a4ade1de3b5"; sha256 = "1z8f0v1ww7y6zssdacjxfs6s4862dwckw25df3yn1v0qnz40rpc8"; diff --git a/tests/channels/test_reliable_channel_send_receive.nim b/tests/channels/test_reliable_channel_send_receive.nim index c20f0d0b0..70a8e2702 100644 --- a/tests/channels/test_reliable_channel_send_receive.nim +++ b/tests/channels/test_reliable_channel_send_receive.nim @@ -11,6 +11,7 @@ import logos_delivery import logos_delivery/waku/[waku_node, waku_core] import logos_delivery/waku/factory/waku_conf import logos_delivery/api/events/messaging_client_events as waku_message_events +import logos_delivery/api/messaging_client_api import tools/confutils/cli_args import logos_delivery/channels/reliable_channel_manager @@ -187,16 +188,16 @@ suite "Reliable Channel - send state machine": setNoopEncryption() var sendCalls = 0 - let fakeSend: SendHandler = proc( - env: MessageEnvelope - ): Future[Result[RequestId, string]] {.async: (raises: [CatchableError]), gcsafe.} = - sendCalls.inc - return ok(fakeMsgReqId) + MessagingSend.replaceProvider( + brokerCtx, + proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {.async.} = + sendCalls.inc + return ok(fakeMsgReqId), + ).isOkOr: + raiseAssert "replaceProvider failed: " & error discard manager - .createReliableChannel( - channelId, contentTopic, SdsParticipantID("local"), sendHandler = fakeSend - ) + .createReliableChannel(channelId, contentTopic, SdsParticipantID("local")) .expect("createReliableChannel") let sentFut = newFuture[RequestId]("channel-sent") @@ -252,17 +253,17 @@ suite "Reliable Channel - send state machine": setNoopEncryption() var msgReqIds: seq[RequestId] - let fakeSend: SendHandler = proc( - env: MessageEnvelope - ): Future[Result[RequestId, string]] {.async: (raises: [CatchableError]), gcsafe.} = - let id = RequestId("fake-msg-req-" & $(msgReqIds.len + 1)) - msgReqIds.add(id) - return ok(id) + MessagingSend.replaceProvider( + brokerCtx, + proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {.async.} = + let id = RequestId("fake-msg-req-" & $(msgReqIds.len + 1)) + msgReqIds.add(id) + return ok(id), + ).isOkOr: + raiseAssert "replaceProvider failed: " & error discard manager - .createReliableChannel( - channelId, contentTopic, SdsParticipantID("local"), sendHandler = fakeSend - ) + .createReliableChannel(channelId, contentTopic, SdsParticipantID("local")) .expect("createReliableChannel") let sentFut = newFuture[RequestId]("channel-sent") @@ -354,28 +355,27 @@ suite "Reliable Channel - send state machine": var msgReqIds: seq[RequestId] var sendsReturned = 0 - let fakeSend: SendHandler = proc( - env: MessageEnvelope - ): Future[Result[RequestId, string]] {.async: (raises: [CatchableError]), gcsafe.} = - ## Call 2 fires the first segment's terminal event and then - ## yields, so the listener task runs while the second segment - ## is still mid-`await` in `onReadyToSend` — the exact race - ## window the regression test targets. - let id = RequestId("race-msg-req-" & $(msgReqIds.len + 1)) - msgReqIds.add(id) - if msgReqIds.len == 2: - waku_message_events.MessageSentEvent.emit( - brokerCtx, - waku_message_events.MessageSentEvent(requestId: msgReqIds[0], messageHash: ""), - ) - await sleepAsync(50.milliseconds) - sendsReturned.inc() - return ok(id) + MessagingSend.replaceProvider( + brokerCtx, + proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {.async.} = + ## Call 2 fires the first segment's terminal event and then + ## yields, so the listener task runs while the second segment + ## is still mid-`await` in `onReadyToSend` — the exact race + ## window the regression test targets. + let id = RequestId("race-msg-req-" & $(msgReqIds.len + 1)) + msgReqIds.add(id) + if msgReqIds.len == 2: + MessageSentEvent.emit( + brokerCtx, MessageSentEvent(requestId: msgReqIds[0], messageHash: "") + ) + await sleepAsync(50.milliseconds) + sendsReturned.inc() + return ok(id), + ).isOkOr: + raiseAssert "replaceProvider failed: " & error discard manager - .createReliableChannel( - channelId, contentTopic, SdsParticipantID("local"), sendHandler = fakeSend - ) + .createReliableChannel(channelId, contentTopic, SdsParticipantID("local")) .expect("createReliableChannel") var finalisedReqIds: seq[RequestId] @@ -457,15 +457,15 @@ suite "Reliable Channel - SDS persistence": setNoopEncryption() - let fakeSend: SendHandler = proc( - env: MessageEnvelope - ): Future[Result[RequestId, string]] {.async: (raises: [CatchableError]), gcsafe.} = - return ok(RequestId("persist-msg-req-1")) + MessagingSend.replaceProvider( + globalBrokerContext(), + proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {.async.} = + return ok(RequestId("persist-msg-req-1")), + ).isOkOr: + raiseAssert "replaceProvider failed: " & error discard manager - .createReliableChannel( - channelId, contentTopic, SdsParticipantID("local"), sendHandler = fakeSend - ) + .createReliableChannel(channelId, contentTopic, SdsParticipantID("local")) .expect("createReliableChannel") discard (await manager.send(channelId, "persist me".toBytes())).expect("send") @@ -798,17 +798,17 @@ suite "Reliable Channel - SDS protocol semantics": setNoopEncryption() var capturedWires: seq[seq[byte]] - let fakeSend: SendHandler = proc( - env: MessageEnvelope - ): Future[Result[RequestId, string]] {.async: (raises: [CatchableError]), gcsafe.} = - ## Noop encryption is identity, so the envelope payload IS the SDS wire. - capturedWires.add(env.payload) - return ok(RequestId("semantics-req-" & $capturedWires.len)) + MessagingSend.replaceProvider( + brokerCtx, + proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {.async.} = + ## Noop encryption is identity, so the envelope payload IS the SDS wire. + capturedWires.add(envelope.payload) + return ok(RequestId("semantics-req-" & $capturedWires.len)), + ).isOkOr: + raiseAssert "replaceProvider failed: " & error discard manager - .createReliableChannel( - channelId, contentTopic, SdsParticipantID("local"), sendHandler = fakeSend - ) + .createReliableChannel(channelId, contentTopic, SdsParticipantID("local")) .expect("createReliableChannel") let remotePeer = @@ -866,16 +866,17 @@ suite "Reliable Channel - SDS protocol semantics": setNoopEncryption() var capturedWires: seq[seq[byte]] - let fakeSend: SendHandler = proc( - env: MessageEnvelope - ): Future[Result[RequestId, string]] {.async: (raises: [CatchableError]), gcsafe.} = - capturedWires.add(env.payload) - return ok(RequestId("ack-req-" & $capturedWires.len)) + MessagingSend.replaceProvider( + brokerCtx, + proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {.async.} = + ## Noop encryption is identity, so the envelope payload IS the SDS wire. + capturedWires.add(envelope.payload) + return ok(RequestId("ack-req-" & $capturedWires.len)), + ).isOkOr: + raiseAssert "replaceProvider failed: " & error discard manager - .createReliableChannel( - channelId, contentTopic, SdsParticipantID("local"), sendHandler = fakeSend - ) + .createReliableChannel(channelId, contentTopic, SdsParticipantID("local")) .expect("createReliableChannel") discard (await manager.send(channelId, "needs ack".toBytes())).expect("send") @@ -1102,16 +1103,17 @@ suite "Reliable Channel - SDS protocol semantics": setNoopEncryption() var capturedWires: seq[seq[byte]] - let fakeSend: SendHandler = proc( - env: MessageEnvelope - ): Future[Result[RequestId, string]] {.async: (raises: [CatchableError]), gcsafe.} = - capturedWires.add(env.payload) - return ok(RequestId("unique-req-" & $capturedWires.len)) + MessagingSend.replaceProvider( + brokerCtx, + proc(envelope: MessageEnvelope): Future[Result[RequestId, string]] {.async.} = + ## Noop encryption is identity, so the envelope payload IS the SDS wire. + capturedWires.add(envelope.payload) + return ok(RequestId("unique-req-" & $capturedWires.len)), + ).isOkOr: + raiseAssert "replaceProvider failed: " & error discard manager - .createReliableChannel( - channelId, contentTopic, SdsParticipantID("local"), sendHandler = fakeSend - ) + .createReliableChannel(channelId, contentTopic, SdsParticipantID("local")) .expect("createReliableChannel") var deliveries: seq[seq[byte]] diff --git a/tests/node/test_wakunode_health_monitor.nim b/tests/node/test_wakunode_health_monitor.nim index 148c93330..c6f9d34e6 100644 --- a/tests/node/test_wakunode_health_monitor.nim +++ b/tests/node/test_wakunode_health_monitor.nim @@ -29,6 +29,7 @@ import logos_delivery/waku/node/subscription_manager import logos_delivery/waku/waku import logos_delivery/waku/factory/waku_state_info import logos_delivery/messaging/messaging_client +import logos_delivery/messaging/messaging_client_lifecycle const MockDLow = 4 # Mocked GossipSub DLow value diff --git a/tests/persistency/test_lifecycle.nim b/tests/persistency/test_lifecycle.nim index 2626cb01b..3006672da 100644 --- a/tests/persistency/test_lifecycle.nim +++ b/tests/persistency/test_lifecycle.nim @@ -156,7 +156,7 @@ suite "Persistency lifecycle": let ev = PersistEvent( ops: @[TxOp(category: "msg", key: k, kind: txPut, payload: payloadBytes("hello"))] ) - await PersistEvent.emit(t.context, ev) + PersistEvent.emit(t.context, ev) let ckOk2 = await t.pollExists("msg", k) check ckOk2 @@ -178,7 +178,7 @@ suite "Persistency lifecycle": check a.context != b.context let k = key("shared", 1'i64) - await PersistEvent.emit( + PersistEvent.emit( a.context, PersistEvent( ops: @[ @@ -188,7 +188,7 @@ suite "Persistency lifecycle": ] ), ) - await PersistEvent.emit( + PersistEvent.emit( b.context, PersistEvent( ops: @[ @@ -255,7 +255,7 @@ suite "Persistency lifecycle": ops.add( TxOp(category: "msg", key: key("c", i), kind: txPut, payload: payloadBytes($i)) ) - await PersistEvent.emit(t.context, PersistEvent(ops: ops)) + PersistEvent.emit(t.context, PersistEvent(ops: ops)) # Wait for the last insert to land. let ckOk5 = await t.pollExists("msg", key("c", 5'i64)) check ckOk5 @@ -285,7 +285,7 @@ suite "Persistency lifecycle": let r1 = aw7.get() check r1.existed == false - await PersistEvent.emit( + PersistEvent.emit( t.context, PersistEvent( ops: @[TxOp(category: "msg", key: k, kind: txPut, payload: payloadBytes("v"))] From 7a3a064b52742434b3e40260e98e94abf006442b Mon Sep 17 00:00:00 2001 From: Igor Sirotin Date: Fri, 3 Jul 2026 13:04:57 +0100 Subject: [PATCH 26/33] feat: add status.prod network preset (cluster 16, RLN off) (#3962) Add a `status.prod` network preset usable from the Messaging API and CLI (`--preset=status.prod`). It targets the live status.prod network that Status runs on: - cluster-id 16 - RLN disabled - static sharding (fleet shards 1, 32, 64, 128, 256; Status uses 32 & 64) - max message size 1024KiB - bootstrap via the status.prod DNS-discovery enrtree and the three fleet boot nodes Config sourced from https://fleets.waku.org/ and each host's /config.toml. Closes #3906 Co-authored-by: Claude Opus 4.8 (1M context) --- library/README.md | 13 ++++++-- .../waku/factory/networks_config.nim | 33 +++++++++++++++++++ tests/api/test_node_conf.nim | 19 +++++++++++ tools/confutils/cli_args.nim | 4 ++- 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/library/README.md b/library/README.md index e8352c611..e6ddcd4d2 100644 --- a/library/README.md +++ b/library/README.md @@ -40,8 +40,17 @@ void *logosdelivery_create_node( ``` Configuration uses flat field names matching `WakuNodeConf` in `tools/confutils/cli_args.nim`. -Use `"preset"` to select a network preset (e.g., `"twn"`, `"logos.dev"`) which auto-configures -entry nodes, cluster ID, sharding, and other network-specific settings. +Use `"preset"` to select a network preset (e.g., `"twn"`, `"logos.dev"`, `"status.prod"`) which +auto-configures entry nodes, cluster ID, sharding, and other network-specific settings. + +Available presets: + +| Preset | Cluster ID | RLN | Sharding | Network | +| --- | --- | --- | --- | --- | +| `twn` | 1 | on | auto (8 shards) | The Waku Network | +| `logos.dev` | 2 | off | auto (8 shards) | Logos Dev Network | +| `logos.test` | 2 | off | auto (8 shards) | Logos Test Network | +| `status.prod` | 16 | off | auto (1 shard) | Status Production Network | #### `logosdelivery_start_node` Starts the node. diff --git a/logos_delivery/waku/factory/networks_config.nim b/logos_delivery/waku/factory/networks_config.nim index 3d1e296fe..339922c02 100644 --- a/logos_delivery/waku/factory/networks_config.nim +++ b/logos_delivery/waku/factory/networks_config.nim @@ -123,6 +123,39 @@ proc LogosTestConf*(T: type NetworkPresetConf): NetworkPresetConf = ], ) +# cluster-id=16 (Status Production Network) +# Cluster configuration for the `status.prod` network that Status runs on. +# RLN is disabled. Starting from the logos-delivery integration, status.prod +# defaults to auto-sharding with a single shard (numShardsInCluster = 1). +# Bootstrap is done through the status.prod DNS discovery enrtree plus the +# fleet boot nodes. +# Source: https://fleets.waku.org/ and each host's `/config.toml`. +proc StatusProdConf*(T: type NetworkPresetConf): NetworkPresetConf = + const ZeroChainId = 0'u256 + return NetworkPresetConf( + maxMessageSize: "1024KiB", + clusterId: 16, + rlnRelay: false, + rlnRelayEthContractAddress: "", + rlnRelayDynamic: false, + rlnRelayChainId: ZeroChainId, + rlnEpochSizeSec: 0, + rlnRelayUserMessageLimit: 0, + shardingConf: ShardingConf(kind: AutoSharding, numShardsInCluster: 1), + enableKadDiscovery: false, + kadBootstrapNodes: @[], + mix: false, + p2pReliability: false, + discv5Discovery: true, + discv5BootstrapNodes: @[], + entryNodes: @[ + "enrtree://AMOJVZX4V6EXP7NTJPMAYJYST2QP6AJXYW76IU6VGJS7UVSNDYZG4@boot.prod.status.nodes.status.im", + "/dns4/boot-01.do-ams3.status.prod.status.im/tcp/30303/p2p/16Uiu2HAmAR24Mbb6VuzoyUiGx42UenDkshENVDj4qnmmbabLvo31", + "/dns4/boot-01.gc-us-central1-a.status.prod.status.im/tcp/30303/p2p/16Uiu2HAm8mUZ18tBWPXDQsaF7PbCKYA35z7WB2xNZH2EVq1qS8LJ", + "/dns4/boot-01.ac-cn-hongkong-c.status.prod.status.im/tcp/30303/p2p/16Uiu2HAmGwcE8v7gmJNEWFtZtojYpPMTHy2jBLL6xRk33qgDxFWX", + ], + ) + proc validateShards*( shardingConf: ShardingConf, shards: seq[uint16] ): Result[void, string] = diff --git a/tests/api/test_node_conf.nim b/tests/api/test_node_conf.nim index df5b18887..2716ffed1 100644 --- a/tests/api/test_node_conf.nim +++ b/tests/api/test_node_conf.nim @@ -241,6 +241,25 @@ suite "WakuNodeConf - preset integration": check: wakuConf.clusterId == 2 + test "StatusProd preset applies StatusProdConf": + ## Given + var conf = defaultWakuNodeConf().valueOr: + raiseAssert error + conf.preset = "status.prod" + + ## When + let wakuConfRes = conf.toWakuConf() + + ## Then + require wakuConfRes.isOk() + let wakuConf = wakuConfRes.get() + require wakuConf.validate().isOk() + check: + wakuConf.clusterId == 16 + wakuConf.shardingConf.kind == AutoSharding + wakuConf.shardingConf.numShardsInCluster == 1 + wakuConf.rlnRelayConf.isNone() + test "Invalid preset returns error": ## Given var conf = defaultWakuNodeConf().valueOr: diff --git a/tools/confutils/cli_args.nim b/tools/confutils/cli_args.nim index 5228dfff7..f781133af 100644 --- a/tools/confutils/cli_args.nim +++ b/tools/confutils/cli_args.nim @@ -175,7 +175,7 @@ type WakuNodeConf* = object preset* {. desc: - "Network preset to use. 'twn' is The RLN-protected Waku Network (cluster 1). 'logos.dev' is the Logos Dev Network (cluster 2). 'logos.test' is the Logos Test Network (cluster 2). Overrides other values.", + "Network preset to use. 'twn' is The RLN-protected Waku Network (cluster 1). 'logos.dev' is the Logos Dev Network (cluster 2). 'logos.test' is the Logos Test Network (cluster 2). 'status.prod' is the Status Production Network (cluster 16, RLN off, auto-sharding with 1 shard). Overrides other values.", defaultValue: "", name: "preset" .}: string @@ -1001,6 +1001,8 @@ proc toNetworkPresetConf( ok(some(NetworkPresetConf.LogosDevConf())) of "logos.test", "logostest": ok(some(NetworkPresetConf.LogosTestConf())) + of "status.prod", "statusprod": + ok(some(NetworkPresetConf.StatusProdConf())) else: err("Invalid --preset value passed: " & lcPreset) From 21569ef88484efdb1c9280262a474795ac864280 Mon Sep 17 00:00:00 2001 From: Ivan FB <128452529+Ivansete-status@users.noreply.github.com> Date: Mon, 6 Jul 2026 16:13:48 +0200 Subject: [PATCH 27/33] ffi: unify node lifecycle on logosdelivery_* and drop legacy waku_* surface (#4012) --- examples/cbindings/waku_example.c | 4 +- examples/cpp/waku.cpp | 6 +- examples/golang/waku.go | 8 +- examples/ios/WakuExample/WakuNode.swift | 12 +-- .../src/main/java/com/mobile/WakuModule.kt | 8 +- .../android/app/src/main/jni/waku_ffi.c | 8 +- examples/nodejs/waku_addon.c | 4 +- examples/python/waku.py | 16 ++-- examples/qt/waku_handler.h | 6 +- examples/rust/src/main.rs | 12 +-- library/kernel_api/node_lifecycle_api.nim | 82 ----------------- library/liblogosdelivery.nim | 87 +------------------ library/liblogosdelivery_kernel.h | 25 ++---- library/logos_delivery_api/node_api.nim | 33 +++++++ 14 files changed, 84 insertions(+), 227 deletions(-) delete mode 100644 library/kernel_api/node_lifecycle_api.nim diff --git a/examples/cbindings/waku_example.c b/examples/cbindings/waku_example.c index 5c821e18e..55feb225d 100644 --- a/examples/cbindings/waku_example.c +++ b/examples/cbindings/waku_example.c @@ -327,7 +327,7 @@ int main(int argc, char **argv) cfgNode.storeRetentionPolicy, cfgNode.storeMaxNumDbConnections); - ctx = waku_new(jsonConfig, event_handler, userData); + ctx = logosdelivery_create_node(jsonConfig, event_handler, userData); waitForCallback(); WAKU_CALL(waku_default_pubsub_topic(ctx, print_default_pubsub_topic, userData)); @@ -338,7 +338,7 @@ int main(int argc, char **argv) logosdelivery_set_event_callback(ctx, on_event_received, userData); - waku_start(ctx, event_handler, userData); + logosdelivery_start_node(ctx, event_handler, userData); waitForCallback(); WAKU_CALL(waku_listen_addresses(ctx, event_handler, userData)); diff --git a/examples/cpp/waku.cpp b/examples/cpp/waku.cpp index 0f91cbc14..7b2a9b5f5 100644 --- a/examples/cpp/waku.cpp +++ b/examples/cpp/waku.cpp @@ -257,9 +257,9 @@ int main(int argc, char **argv) cfgNode.port); void *ctx = - waku_new(jsonConfig, + logosdelivery_create_node(jsonConfig, cify([](const char *msg, size_t len) - { std::cout << "waku_new feedback: " << msg << std::endl; }), + { std::cout << "logosdelivery_create_node feedback: " << msg << std::endl; }), nullptr); waitForCallback(); @@ -296,7 +296,7 @@ int main(int argc, char **argv) { event_handler(msg, len); }), nullptr); - WAKU_CALL(waku_start(ctx, + WAKU_CALL(logosdelivery_start_node(ctx, cify([&](const char *msg, size_t len) { event_handler(msg, len); }), nullptr)); diff --git a/examples/golang/waku.go b/examples/golang/waku.go index 968151475..1f99003bd 100644 --- a/examples/golang/waku.go +++ b/examples/golang/waku.go @@ -71,20 +71,20 @@ package main static void* cGoWakuNew(const char* configJson, void* resp) { // We pass NULL because we are not interested in retrieving data from this callback - void* ret = waku_new(configJson, (FFICallBack) callback, resp); + void* ret = logosdelivery_create_node(configJson, (FFICallBack) callback, resp); return ret; } static void cGoWakuStart(void* wakuCtx, void* resp) { - WAKU_CALL(waku_start(wakuCtx, (FFICallBack) callback, resp)); + WAKU_CALL(logosdelivery_start_node(wakuCtx, (FFICallBack) callback, resp)); } static void cGoWakuStop(void* wakuCtx, void* resp) { - WAKU_CALL(waku_stop(wakuCtx, (FFICallBack) callback, resp)); + WAKU_CALL(logosdelivery_stop_node(wakuCtx, (FFICallBack) callback, resp)); } static void cGoWakuDestroy(void* wakuCtx, void* resp) { - WAKU_CALL(waku_destroy(wakuCtx, (FFICallBack) callback, resp)); + WAKU_CALL(logosdelivery_destroy(wakuCtx, (FFICallBack) callback, resp)); } static void cGoWakuStartDiscV5(void* wakuCtx, void* resp) { diff --git a/examples/ios/WakuExample/WakuNode.swift b/examples/ios/WakuExample/WakuNode.swift index fa4e02a3b..9cc252546 100644 --- a/examples/ios/WakuExample/WakuNode.swift +++ b/examples/ios/WakuExample/WakuNode.swift @@ -240,11 +240,11 @@ actor WakuActor { print("[WakuActor] Unsubscribed from filter") // Stop - _ = await self.callWakuSync { waku_stop(ctxToStop, WakuActor.syncCallback, $0) } + _ = await self.callWakuSync { logosdelivery_stop_node(ctxToStop, WakuActor.syncCallback, $0) } print("[WakuActor] Node stopped") // Destroy - _ = await self.callWakuSync { waku_destroy(ctxToStop, WakuActor.syncCallback, $0) } + _ = await self.callWakuSync { logosdelivery_destroy(ctxToStop, WakuActor.syncCallback, $0) } print("[WakuActor] Node destroyed") } } @@ -319,13 +319,13 @@ actor WakuActor { } """ - // Create node - waku_new is special, it returns the context directly + // Create node - logosdelivery_create_node is special, it returns the context directly let createResult = await withCheckedContinuation { (continuation: CheckedContinuation<(ctx: UnsafeMutableRawPointer?, success: Bool, result: String?), Never>) in let callbackCtx = CallbackContext() let userDataPtr = Unmanaged.passRetained(callbackCtx).toOpaque() - // Set up a simple callback for waku_new - let newCtx = waku_new(config, { ret, msg, len, userData in + // Set up a simple callback for logosdelivery_create_node + let newCtx = logosdelivery_create_node(config, { ret, msg, len, userData in guard let userData = userData else { return } let context = Unmanaged.fromOpaque(userData).takeUnretainedValue() context.success = (ret == RET_OK) @@ -354,7 +354,7 @@ actor WakuActor { // Start node let startResult = await callWakuSync { userData in - waku_start(self.ctx, WakuActor.syncCallback, userData) + logosdelivery_start_node(self.ctx, WakuActor.syncCallback, userData) } guard startResult.success else { diff --git a/examples/mobile/android/app/src/main/java/com/mobile/WakuModule.kt b/examples/mobile/android/app/src/main/java/com/mobile/WakuModule.kt index 4c1c0209e..aff8a9ca8 100644 --- a/examples/mobile/android/app/src/main/java/com/mobile/WakuModule.kt +++ b/examples/mobile/android/app/src/main/java/com/mobile/WakuModule.kt @@ -123,7 +123,7 @@ class WakuModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo val configStr = stringifyReadableMap(config) val response = wakuNew(configStr) if (response.error) { - promise.reject("waku_new", response.errorMessage) + promise.reject("logosdelivery_create_node", response.errorMessage) } else { // With this we just indicate to waku_ffi that we have registered a // closure, for this wakuPtr. Later once a message is received the @@ -140,7 +140,7 @@ class WakuModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo val wakuPtr = BigInteger(ctx).toLong() val response = wakuStart(wakuPtr) if (response.error) { - promise.reject("waku_start", response.message) + promise.reject("logosdelivery_start_node", response.message) } else { promise.resolve(null) } @@ -162,7 +162,7 @@ class WakuModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo val wakuPtr = BigInteger(ctx).toLong() val response = wakuStop(wakuPtr) if (response.error) { - promise.reject("waku_stop", response.message) + promise.reject("logosdelivery_stop_node", response.message) } else { promise.resolve(null) } @@ -173,7 +173,7 @@ class WakuModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo val wakuPtr = BigInteger(ctx).toLong() val response = wakuDestroy(wakuPtr) if (response.error) { - promise.reject("waku_destroy", response.message) + promise.reject("logosdelivery_destroy", response.message) } else { promise.resolve(null) } diff --git a/examples/mobile/android/app/src/main/jni/waku_ffi.c b/examples/mobile/android/app/src/main/jni/waku_ffi.c index 9dfeadaee..6a15ed6d9 100644 --- a/examples/mobile/android/app/src/main/jni/waku_ffi.c +++ b/examples/mobile/android/app/src/main/jni/waku_ffi.c @@ -184,7 +184,7 @@ jobject Java_com_mobile_WakuModule_wakuNew(JNIEnv *env, jobject thiz, jstring configJson) { const char *config = (*env)->GetStringUTFChars(env, configJson, 0); cb_result *result = NULL; - void *wakuPtr = waku_new(config, on_response, (void *)&result); + void *wakuPtr = logosdelivery_create_node(config, on_response, (void *)&result); jobject response = to_jni_ptr(env, result, wakuPtr); (*env)->ReleaseStringUTFChars(env, configJson, config); free_cb_result(result); @@ -194,7 +194,7 @@ jobject Java_com_mobile_WakuModule_wakuNew(JNIEnv *env, jobject thiz, jobject Java_com_mobile_WakuModule_wakuStart(JNIEnv *env, jobject thiz, jlong wakuPtr) { cb_result *result = NULL; - waku_start((void *)wakuPtr, on_response, &result); + logosdelivery_start_node((void *)wakuPtr, on_response, &result); jobject response = to_jni_result(env, result); free_cb_result(result); return response; @@ -212,7 +212,7 @@ jobject Java_com_mobile_WakuModule_wakuVersion(JNIEnv *env, jobject thiz, jobject Java_com_mobile_WakuModule_wakuStop(JNIEnv *env, jobject thiz, jlong wakuPtr) { cb_result *result = NULL; - waku_stop((void *)wakuPtr, on_response, &result); + logosdelivery_stop_node((void *)wakuPtr, on_response, &result); jobject response = to_jni_result(env, result); free_cb_result(result); return response; @@ -221,7 +221,7 @@ jobject Java_com_mobile_WakuModule_wakuStop(JNIEnv *env, jobject thiz, jobject Java_com_mobile_WakuModule_wakuDestroy(JNIEnv *env, jobject thiz, jlong wakuPtr) { cb_result *result = NULL; - waku_destroy((void *)wakuPtr, on_response, &result); + logosdelivery_destroy((void *)wakuPtr, on_response, &result); jobject response = to_jni_result(env, result); free_cb_result(result); return response; diff --git a/examples/nodejs/waku_addon.c b/examples/nodejs/waku_addon.c index 2fa312573..3bf570abe 100644 --- a/examples/nodejs/waku_addon.c +++ b/examples/nodejs/waku_addon.c @@ -200,7 +200,7 @@ static napi_value WakuNew(napi_env env, napi_callback_info info) { str_size = str_size + 1; napi_get_value_string_utf8(env, args[0], jsonConfig, str_size, &str_size_read); - ctx = waku_new(jsonConfig, event_handler, userData); + ctx = logosdelivery_create_node(jsonConfig, event_handler, userData); free(jsonConfig); @@ -290,7 +290,7 @@ static napi_value WakuSetEventCallback(napi_env env, napi_callback_info info) { } static napi_value WakuStart(napi_env env, napi_callback_info info) { - waku_start(ctx, event_handler, userData); + logosdelivery_start_node(ctx, event_handler, userData); return NULL; } diff --git a/examples/python/waku.py b/examples/python/waku.py index d038e4351..5df72695d 100644 --- a/examples/python/waku.py +++ b/examples/python/waku.py @@ -53,7 +53,7 @@ parser.add_argument('--peer', dest='peer', default="", args = parser.parse_args() -# The next 'json_config' is the item passed to the 'waku_new'. +# The next 'json_config' is the item passed to the 'logosdelivery_create_node'. json_config = "{ \ \"host\": \"%s\", \ \"port\": %d, \ @@ -68,16 +68,16 @@ json_config = "{ \ callback_type = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p, ctypes.c_size_t) # Node creation -libwaku.waku_new.restype = ctypes.c_void_p -libwaku.waku_new.argtypes = [ctypes.c_char_p, +libwaku.logosdelivery_create_node.restype = ctypes.c_void_p +libwaku.logosdelivery_create_node.argtypes = [ctypes.c_char_p, callback_type, ctypes.c_void_p] -ctx = libwaku.waku_new(bytes(json_config, 'utf-8'), +ctx = libwaku.logosdelivery_create_node(bytes(json_config, 'utf-8'), callback_type( #onErrCb lambda ret, msg, len: - print("Error calling waku_new: %s", + print("Error calling logosdelivery_create_node: %s", msg.decode('utf-8')) ), ctypes.c_void_p(0)) @@ -115,12 +115,12 @@ libwaku.logosdelivery_set_event_callback.argtypes = [callback_type, ctypes.c_voi libwaku.logosdelivery_set_event_callback(callback, ctypes.c_void_p(0)) # Start the node -libwaku.waku_start.argtypes = [ctypes.c_void_p, +libwaku.logosdelivery_start_node.argtypes = [ctypes.c_void_p, callback_type, ctypes.c_void_p] -libwaku.waku_start(ctx, +libwaku.logosdelivery_start_node(ctx, callback_type(lambda ret, msg, len: - print("Error in waku_start: %s" % + print("Error in logosdelivery_start_node: %s" % msg.decode('utf-8'))), ctypes.c_void_p(0)) diff --git a/examples/qt/waku_handler.h b/examples/qt/waku_handler.h index baee785a3..ea44416d5 100644 --- a/examples/qt/waku_handler.h +++ b/examples/qt/waku_handler.h @@ -25,7 +25,7 @@ public: WakuHandler() : QObject(), ctx(nullptr) {} void initialize(const QString& jsonConfig, WakuCallBack event_handler, void* userData) { - ctx = waku_new(jsonConfig.toUtf8().constData(), WakuCallBack(event_handler), userData); + ctx = logosdelivery_create_node(jsonConfig.toUtf8().constData(), WakuCallBack(event_handler), userData); logosdelivery_set_event_callback(ctx, on_event_received, userData); qDebug() << "Waku context initialized, ready to start."; @@ -33,7 +33,7 @@ public: Q_INVOKABLE void start() { if (ctx) { - waku_start(ctx, event_handler, nullptr); + logosdelivery_start_node(ctx, event_handler, nullptr); qDebug() << "Waku start called with event_handler and userData."; } else { qDebug() << "Context is not initialized in start."; @@ -42,7 +42,7 @@ public: Q_INVOKABLE void stop() { if (ctx) { - waku_stop(ctx, event_handler, nullptr); + logosdelivery_stop_node(ctx, event_handler, nullptr); qDebug() << "Waku stop called with event_handler and userData."; } else { qDebug() << "Context is not initialized in stop."; diff --git a/examples/rust/src/main.rs b/examples/rust/src/main.rs index d26e9627e..16f31bb63 100644 --- a/examples/rust/src/main.rs +++ b/examples/rust/src/main.rs @@ -6,7 +6,7 @@ use std::{slice, thread, time}; pub type FFICallBack = unsafe extern "C" fn(c_int, *const c_char, usize, *const c_void); extern "C" { - pub fn waku_new( + pub fn logosdelivery_create_node( config_json: *const u8, cb: FFICallBack, user_data: *const c_void, @@ -14,7 +14,7 @@ extern "C" { pub fn waku_version(ctx: *const c_void, cb: FFICallBack, user_data: *const c_void) -> c_int; - pub fn waku_start(ctx: *const c_void, cb: FFICallBack, user_data: *const c_void) -> c_int; + pub fn logosdelivery_start_node(ctx: *const c_void, cb: FFICallBack, user_data: *const c_void) -> c_int; pub fn waku_default_pubsub_topic( ctx: *mut c_void, @@ -60,11 +60,11 @@ fn main() { unsafe { // Create the waku node let closure = |ret: i32, data: &str| { - println!("Ret {ret}. waku_new closure called {data}"); + println!("Ret {ret}. logosdelivery_create_node closure called {data}"); }; let cb = get_trampoline(&closure); let config_json_str = CString::new(config_json).unwrap(); - let ctx = waku_new( + let ctx = logosdelivery_create_node( config_json_str.as_ptr() as *const u8, cb, &closure as *const _ as *const c_void, @@ -99,10 +99,10 @@ fn main() { // Start the Waku node let closure = |ret: i32, data: &str| { - println!("Ret {ret}. waku_start closure called {data}"); + println!("Ret {ret}. logosdelivery_start_node closure called {data}"); }; let cb = get_trampoline(&closure); - let _ret = waku_start(ctx, cb, &closure as *const _ as *const c_void); + let _ret = logosdelivery_start_node(ctx, cb, &closure as *const _ as *const c_void); } loop { diff --git a/library/kernel_api/node_lifecycle_api.nim b/library/kernel_api/node_lifecycle_api.nim deleted file mode 100644 index dbf7ad9b1..000000000 --- a/library/kernel_api/node_lifecycle_api.nim +++ /dev/null @@ -1,82 +0,0 @@ -import logos_delivery/waku/compat/option_valueor -import std/[options, json, strutils, net] -import chronos, chronicles, results, confutils, confutils/std/net, ffi - -import - logos_delivery/waku/node/peer_manager/peer_manager, - tools/confutils/cli_args, - logos_delivery/waku/waku, - logos_delivery/waku/factory/node_factory, - logos_delivery/waku/factory/app_callbacks, - logos_delivery/waku/rest_api/endpoint/builder, - library/declare_lib - -proc createWaku( - configJson: cstring, appCallbacks: AppCallbacks = nil -): Future[Result[LogosDelivery, string]] {.async.} = - var conf = defaultWakuNodeConf().valueOr: - return err("Failed creating node: " & error) - - var errorResp: string - - var jsonNode: JsonNode - try: - jsonNode = parseJson($configJson) - except Exception: - return err( - "exception in createWaku when calling parseJson: " & getCurrentExceptionMsg() & - " configJson string: " & $configJson - ) - - for confField, confValue in fieldPairs(conf): - if jsonNode.contains(confField): - # Make sure string doesn't contain the leading or trailing " character - let formattedString = ($jsonNode[confField]).strip(chars = {'\"'}) - # Override conf field with the value set in the json-string - try: - confValue = parseCmdArg(typeof(confValue), formattedString) - except Exception: - return err( - "exception in createWaku when parsing configuration. exc: " & - getCurrentExceptionMsg() & ". string that could not be parsed: " & - formattedString & ". expected type: " & $typeof(confValue) - ) - - # Don't send relay app callbacks if relay is disabled - if not conf.relay and not appCallbacks.isNil(): - appCallbacks.relayHandler = nil - appCallbacks.topicHealthChangeHandler = nil - - conf.rest = false ## libwaku never runs the REST server - - let logosRes = (await LogosDelivery.new(conf, appCallbacks)).valueOr: - error "LogosDelivery initialization failed", error = error - return err("Failed setting up LogosDelivery: " & $error) - - return ok(logosRes) - -registerReqFFI(CreateNodeWithCallbacksRequest, ctx: ptr FFIContext[LogosDelivery]): - proc( - configJson: cstring, appCallbacks: AppCallbacks - ): Future[Result[string, string]] {.async.} = - ctx.myLib[] = (await createWaku(configJson, cast[AppCallbacks](appCallbacks))).valueOr: - error "CreateNodeWithCallbacksRequest failed", error = error - return err($error) - - return ok("") - -proc waku_start( - ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer -) {.ffi.} = - (await ctx.myLib[].start()).isOkOr: - error "START_NODE failed", error = error - return err("failed to start: " & $error) - return ok("") - -proc waku_stop( - ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer -) {.ffi.} = - (await ctx.myLib[].stop()).isOkOr: - error "STOP_NODE failed", error = error - return err("failed to stop: " & $error) - return ok("") diff --git a/library/liblogosdelivery.nim b/library/liblogosdelivery.nim index 755503e7c..d3753da63 100644 --- a/library/liblogosdelivery.nim +++ b/library/liblogosdelivery.nim @@ -9,11 +9,7 @@ import logos_delivery/waku/waku, logos_delivery/waku/node/waku_node, logos_delivery/waku/node/health_monitor/health_status, - ../logos_delivery/waku/factory/app_callbacks, ./events/json_message_event, - ./events/json_topic_health_change_event, - ./events/json_connection_change_event, - ./events/json_connection_status_change_event, ./declare_lib ################################################################################ @@ -25,7 +21,6 @@ include ./logos_delivery_api/debug_api, ./kernel_api/peer_manager_api, ./kernel_api/discovery_api, - ./kernel_api/node_lifecycle_api, ./kernel_api/debug_node_api, ./kernel_api/ping_api, ./kernel_api/protocols/relay_api, @@ -34,81 +29,7 @@ include ./kernel_api/protocols/filter_api, ./channels_api/channel_api -################################################################################ -### Exported procs (former libwaku API) - -proc waku_new( - configJson: cstring, callback: FFICallback, userData: pointer -): pointer {.dynlib, exportc, cdecl.} = - initializeLibrary() - - ## Creates a new instance of the WakuNode. - if isNil(callback): - echo "error: missing callback in waku_new" - return nil - - ## Create the Waku thread that will keep waiting for req from the main thread. - var ctx = ffi.createFFIContext[LogosDelivery]().valueOr: - let msg = "Error in createFFIContext: " & $error - callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) - return nil - - ctx.userData = userData - - proc onReceivedMessage(ctx: ptr FFIContext): WakuRelayHandler = - return proc(pubsubTopic: PubsubTopic, msg: WakuMessage) {.async.} = - callEventCallback(ctx, "onReceivedMessage"): - $JsonMessageEvent.new(pubsubTopic, msg) - - proc onTopicHealthChange(ctx: ptr FFIContext): TopicHealthChangeHandler = - return proc(pubsubTopic: PubsubTopic, topicHealth: TopicHealth) {.async.} = - callEventCallback(ctx, "onTopicHealthChange"): - $JsonTopicHealthChangeEvent.new(pubsubTopic, topicHealth) - - proc onConnectionChange(ctx: ptr FFIContext): ConnectionChangeHandler = - return proc(peerId: PeerId, peerEvent: PeerEventKind) {.async.} = - callEventCallback(ctx, "onConnectionChange"): - $JsonConnectionChangeEvent.new($peerId, peerEvent) - - proc onConnectionStatusChange(ctx: ptr FFIContext): ConnectionStatusChangeHandler = - return proc(status: ConnectionStatus) {.async.} = - callEventCallback(ctx, "onConnectionStatusChange"): - $JsonConnectionStatusChangeEvent.new(status) - - let appCallbacks = AppCallbacks( - relayHandler: onReceivedMessage(ctx), - topicHealthChangeHandler: onTopicHealthChange(ctx), - connectionChangeHandler: onConnectionChange(ctx), - connectionStatusChangeHandler: onConnectionStatusChange(ctx), - ) - - ffi.sendRequestToFFIThread( - ctx, - CreateNodeWithCallbacksRequest.ffiNewReq( - callback, userData, configJson, appCallbacks - ), - ).isOkOr: - let msg = "error in sendRequestToFFIThread: " & $error - callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) - return nil - - return ctx - -proc waku_destroy( - ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer -): cint {.dynlib, exportc, cdecl.} = - initializeLibrary() - checkParams(ctx, callback, userData) - - ffi.destroyFFIContext(ctx).isOkOr: - let msg = "libwaku error: " & $error - callback(RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), userData) - return RET_ERR - - ## always need to invoke the callback although we don't retrieve value to the caller - callback(RET_OK, nil, 0, userData) - - return RET_OK - -# ### End of exported procs -# ################################################################################ +# Node lifecycle (create / start / stop / destroy) is unified under the stable +# logosdelivery_* surface in ./logos_delivery_api/node_api. The former +# waku_new / waku_start / waku_stop / waku_destroy entry points were removed to +# avoid maintaining two parallel node-lifecycle APIs. diff --git a/library/liblogosdelivery_kernel.h b/library/liblogosdelivery_kernel.h index 0b502327b..58c67b712 100644 --- a/library/liblogosdelivery_kernel.h +++ b/library/liblogosdelivery_kernel.h @@ -26,26 +26,11 @@ extern "C" { #endif - // Creates a new instance of the waku node. - // Sets up the waku node from the given configuration. - // Returns a pointer to the Context needed by the rest of the API functions. - void *waku_new( - const char *configJson, - FFICallBack callback, - void *userData); - - int waku_start(void *ctx, - FFICallBack callback, - void *userData); - - int waku_stop(void *ctx, - FFICallBack callback, - void *userData); - - // Destroys an instance of a waku node created with waku_new - int waku_destroy(void *ctx, - FFICallBack callback, - void *userData); + // NOTE: node lifecycle (create / start / stop / destroy) is unified and lives + // only in the stable header. Use logosdelivery_create_node, + // logosdelivery_start_node, logosdelivery_stop_node and logosdelivery_destroy + // (declared in liblogosdelivery.h, included above) regardless of whether you + // drive the node through the messaging surface or this kernel API. int waku_version(void *ctx, FFICallBack callback, diff --git a/library/logos_delivery_api/node_api.nim b/library/logos_delivery_api/node_api.nim index cfee2f5e2..1130ab181 100644 --- a/library/logos_delivery_api/node_api.nim +++ b/library/logos_delivery_api/node_api.nim @@ -6,6 +6,7 @@ import logos_delivery/waku/node/waku_node, logos_delivery/api/types, logos_delivery/waku/api/events/health_events, + logos_delivery/waku/api/events/peer_events, tools/confutils/conf_from_json, ../declare_lib, ../json_event @@ -125,6 +126,36 @@ proc logosdelivery_start_node( chronicles.error "ConnectionStatusChange.listen failed", err = $error return err("ConnectionStatusChange.listen failed: " & $error) + let shardTopicHealthListener = EventShardTopicHealthChange.listen( + ctx.myLib[].waku.brokerCtx, + proc(event: EventShardTopicHealthChange) {.async: (raises: []).} = + callEventCallback(ctx, "onTopicHealthChange"): + $( + %*{ + "eventType": "relay_topic_health_change", + "pubsubTopic": $event.topic, + "topicHealth": $event.health, + } + ), + ).valueOr: + chronicles.error "EventShardTopicHealthChange.listen failed", err = $error + return err("EventShardTopicHealthChange.listen failed: " & $error) + + let peerEventListener = WakuPeerEvent.listen( + ctx.myLib[].waku.brokerCtx, + proc(event: WakuPeerEvent) {.async: (raises: []).} = + callEventCallback(ctx, "onConnectionChange"): + $( + %*{ + "eventType": "connection_change", + "peerId": $event.peerId, + "peerEvent": $event.kind, + } + ), + ).valueOr: + chronicles.error "WakuPeerEvent.listen failed", err = $error + return err("WakuPeerEvent.listen failed: " & $error) + let channelReceivedListener = ChannelMessageReceivedEvent.listen( ctx.myLib[].waku.brokerCtx, proc(event: ChannelMessageReceivedEvent) {.async: (raises: []).} = @@ -176,6 +207,8 @@ proc logosdelivery_stop_node( await MessagePropagatedEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) await MessageReceivedEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) await EventConnectionStatusChange.dropAllListeners(ctx.myLib[].waku.brokerCtx) + await EventShardTopicHealthChange.dropAllListeners(ctx.myLib[].waku.brokerCtx) + await WakuPeerEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) await ChannelMessageReceivedEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) await ChannelMessageSentEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) await ChannelMessageErrorEvent.dropAllListeners(ctx.myLib[].waku.brokerCtx) From 0a1700e239b926b32798cb918bf5ac6a25da2c19 Mon Sep 17 00:00:00 2001 From: Tanya S <120410716+stubbsta@users.noreply.github.com> Date: Mon, 6 Jul 2026 17:49:20 +0200 Subject: [PATCH 28/33] replace RLN specific Result with generic Result (#4011) --- logos_delivery/waku/rln/nullifier_log.nim | 4 +- logos_delivery/waku/rln/proof.nim | 2 +- logos_delivery/waku/rln/protocol_types.nim | 15 ++----- logos_delivery/waku/rln/rln.nim | 4 +- logos_delivery/waku/rln/rln/rln_interface.nim | 12 +++--- logos_delivery/waku/rln/rln/wrappers.nim | 40 ++++++++++--------- .../rln/waku_rln_relay_utils.nim | 5 ++- tests/waku_rln_relay/utils_onchain.nim | 2 +- 8 files changed, 41 insertions(+), 43 deletions(-) diff --git a/logos_delivery/waku/rln/nullifier_log.nim b/logos_delivery/waku/rln/nullifier_log.nim index 372433c72..8a1a3cd6c 100644 --- a/logos_delivery/waku/rln/nullifier_log.nim +++ b/logos_delivery/waku/rln/nullifier_log.nim @@ -10,7 +10,7 @@ logScope: proc hasDuplicate*( rlnPeer: Rln, epoch: Epoch, proofMetadata: ProofMetadata -): RlnResult[bool] = +): Result[bool, string] = ## returns true if there is another message in the `nullifierLog` of the `rlnPeer` with the same ## epoch and nullifier as `proofMetadata`'s epoch and nullifier ## otherwise, returns false @@ -32,7 +32,7 @@ proc hasDuplicate*( proc updateLog*( rlnPeer: Rln, epoch: Epoch, proofMetadata: ProofMetadata -): RlnResult[void] = +): Result[void, string] = ## saves supplied proofMetadata `proofMetadata` ## in the `nullifierLog` of the `rlnPeer` ## Returns an error if it cannot update the log diff --git a/logos_delivery/waku/rln/proof.nim b/logos_delivery/waku/rln/proof.nim index 4a0c332c1..fbbe2292e 100644 --- a/logos_delivery/waku/rln/proof.nim +++ b/logos_delivery/waku/rln/proof.nim @@ -56,7 +56,7 @@ proc toRLNSignal*(wakumessage: WakuMessage): seq[byte] = proc generateRLNProof*( rln: Rln, input: seq[byte], senderEpochTime: float64 -): Future[RlnResult[seq[byte]]] {.async.} = +): Future[Result[seq[byte], string]] {.async.} = let epoch = rln.calcEpoch(senderEpochTime) let nonce = rln.nonceManager.getNonce().valueOr: return err("could not get new message id to generate an rln proof: " & $error) diff --git a/logos_delivery/waku/rln/protocol_types.nim b/logos_delivery/waku/rln/protocol_types.nim index 29ee97083..66aa72f53 100644 --- a/logos_delivery/waku/rln/protocol_types.nim +++ b/logos_delivery/waku/rln/protocol_types.nim @@ -5,11 +5,8 @@ import ../waku_core, ../waku_keystore, ../common/protobuf export waku_keystore, waku_core -type RlnResult*[T] = Result[T, string] - ## RLN is a Nim wrapper for the data types used in zerokit RLN type RlnRaw* {.incompleteStruct.} = object -type RlnInstanceResult* = RlnResult[ptr RlnRaw] type MerkleNode* = array[32, byte] @@ -72,14 +69,10 @@ type ProofMetadata* = object shareY*: MerkleNode externalNullifier*: Nullifier -type - MessageValidationResult* {.pure.} = enum - Valid - Invalid - Spam - - MerkleNodeResult* = RlnResult[MerkleNode] - RateLimitProofResult* = RlnResult[RateLimitProof] +type MessageValidationResult* {.pure.} = enum + Valid + Invalid + Spam # Protobufs enc and init proc init*(T: type RateLimitProof, buffer: seq[byte]): ProtoResult[T] = diff --git a/logos_delivery/waku/rln/rln.nim b/logos_delivery/waku/rln/rln.nim index dab5ec3ef..ec20cf0c8 100644 --- a/logos_delivery/waku/rln/rln.nim +++ b/logos_delivery/waku/rln/rln.nim @@ -183,7 +183,7 @@ proc monitorEpochs(rln: Rln) {.async.} = proc mount( conf: WakuRlnConfig, registrationHandler = none(RegistrationHandler) -): Future[RlnResult[Rln]] {.async.} = +): Future[Result[Rln, string]] {.async.} = var groupManager: GroupManager rln: Rln @@ -258,7 +258,7 @@ proc isReady*(rlnPeer: Rln): Future[bool] {.async.} = proc new*( T: type Rln, conf: WakuRlnConfig, registrationHandler = none(RegistrationHandler) -): Future[RlnResult[Rln]] {.async.} = +): Future[Result[Rln, string]] {.async.} = ## Mounts the rln-relay protocol on the node. ## The rln-relay protocol can be mounted in two modes: on-chain and off-chain. ## Returns an error if the rln-relay protocol could not be mounted. diff --git a/logos_delivery/waku/rln/rln/rln_interface.nim b/logos_delivery/waku/rln/rln/rln_interface.nim index 60272ea3e..d9b2fc903 100644 --- a/logos_delivery/waku/rln/rln/rln_interface.nim +++ b/logos_delivery/waku/rln/rln/rln_interface.nim @@ -318,14 +318,14 @@ proc vecToSeq*(data: Vec_uint8): seq[byte] = if result.len > 0: copyMem(addr result[0], data.dataPtr, result.len) -proc seqToFixed32*(data: openArray[byte]): RlnResult[array[32, byte]] = +proc seqToFixed32*(data: openArray[byte]): Result[array[32, byte], string] = if data.len != FieldElementSize: return err("Expected 32 bytes, got " & $data.len) var output: array[32, byte] copyMem(addr output[0], unsafeAddr data[0], FieldElementSize) ok(output) -proc cfrToBytesLe*(cfr: ptr CFr): RlnResult[array[32, byte]] = +proc cfrToBytesLe*(cfr: ptr CFr): Result[array[32, byte], string] = let bytes = ffi_cfr_to_bytes_le(cfr) defer: ffi_vec_u8_free(bytes) @@ -333,7 +333,7 @@ proc cfrToBytesLe*(cfr: ptr CFr): RlnResult[array[32, byte]] = return err("Invalid field byte length: " & $bytes.len) seqToFixed32(vecToSeq(bytes)) -proc bytesToCfrLe*(data: openArray[byte]): RlnResult[ptr CFr] = +proc bytesToCfrLe*(data: openArray[byte]): Result[ptr CFr, string] = ## Allocate a ptr CFr from raw bytes. Caller MUST ffi_cfr_free(x). var vec = toVecUint8(data) let res = ffi_bytes_le_to_cfr(addr vec) @@ -343,7 +343,7 @@ proc bytesToCfrLe*(data: openArray[byte]): RlnResult[ptr CFr] = proc cfrResultToBytes*( res: CResultCFrPtrVecU8, prefix: string -): RlnResult[array[32, byte]] = +): Result[array[32, byte], string] = ## Consume a CResultCFrPtrVecU8: read bytes if ok, free the CFr, or ## propagate the error (also freeing the error string). if res.ok.isNil: @@ -352,7 +352,7 @@ proc cfrResultToBytes*( ffi_cfr_free(res.ok) cfrToBytesLe(res.ok) -proc hashToFieldLe*(data: openArray[byte]): RlnResult[ptr CFr] = +proc hashToFieldLe*(data: openArray[byte]): Result[ptr CFr, string] = ## Caller MUST ffi_cfr_free the returned ptr. var vec = toVecUint8(data) let cfr = ffi_hash_to_field_le(addr vec) @@ -360,7 +360,7 @@ proc hashToFieldLe*(data: openArray[byte]): RlnResult[ptr CFr] = return err("Failed to hash to field") ok(cfr) -proc poseidonPairLe*(a, b: openArray[byte]): RlnResult[array[32, byte]] = +proc poseidonPairLe*(a, b: openArray[byte]): Result[array[32, byte], string] = ## Poseidon hash of exactly two 32-byte field elements (little-endian). ## zerokit v2 FFI only exposes pair-input Poseidon; unary is not supported. let aPtr = bytesToCfrLe(a).valueOr: diff --git a/logos_delivery/waku/rln/rln/wrappers.nim b/logos_delivery/waku/rln/rln/wrappers.nim index 5a39aedf0..4f46fc164 100644 --- a/logos_delivery/waku/rln/rln/wrappers.nim +++ b/logos_delivery/waku/rln/rln/wrappers.nim @@ -12,9 +12,9 @@ logScope: # Forward decl; body defined below. proc generateExternalNullifier*( epoch: Epoch, rlnIdentifier: RlnIdentifier -): RlnResult[ExternalNullifier] +): Result[ExternalNullifier, string] -proc toRootVec(validRoots: seq[MerkleNode]): RlnResult[Vec_CFr] = +proc toRootVec(validRoots: seq[MerkleNode]): Result[Vec_CFr, string] = ## Caller MUST ffi_vec_cfr_free the returned Vec_CFr. var roots = ffi_vec_cfr_new(csize_t(validRoots.len)) for root in validRoots: @@ -27,7 +27,7 @@ proc toRootVec(validRoots: seq[MerkleNode]): RlnResult[Vec_CFr] = proc proofPtrToRateLimitProof( proofPtr: ptr FFI_RLNProof, epoch: Epoch, rlnIdentifier: RlnIdentifier -): RlnResult[RateLimitProof] = +): Result[RateLimitProof, string] = var proofHandle = proofPtr let proofBytesRes = ffi_rln_proof_to_bytes_le(addr proofHandle) if hasError(proofBytesRes.err): @@ -93,7 +93,7 @@ proc proofPtrToRateLimitProof( ok(output) -proc parseCredentialVec(vec: var Vec_CFr): RlnResult[IdentityCredential] = +proc parseCredentialVec(vec: var Vec_CFr): Result[IdentityCredential, string] = ## Vec_CFr order: idTrapdoor, idNullifier, idSecretHash, idCommitment. if int(ffi_vec_cfr_len(addr vec)) != 4: return err("Unexpected credential element count") @@ -120,13 +120,13 @@ proc parseCredentialVec(vec: var Vec_CFr): RlnResult[IdentityCredential] = ) ) -proc membershipKeyGen*(): RlnResult[IdentityCredential] = +proc membershipKeyGen*(): Result[IdentityCredential, string] = var vec = ffi_extended_key_gen() defer: ffi_vec_cfr_free(vec) parseCredentialVec(vec) -proc createRLNInstanceLocal(): RlnInstanceResult = +proc createRLNInstanceLocal(): Result[ptr RlnRaw, string] = ## Creates a stateless RLN instance (no local Merkle tree). let res = ffi_rln_new() if res.ok.isNil(): @@ -135,18 +135,18 @@ proc createRLNInstanceLocal(): RlnInstanceResult = return err(msg) ok(res.ok) -proc createRLNInstance*(): RlnInstanceResult = +proc createRLNInstance*(): Result[ptr RlnRaw, string] = ## Wraps createRLNInstanceLocal with metrics timing. - var res: RlnInstanceResult + var res: Result[ptr RlnRaw, string] waku_rln_instance_creation_duration_seconds.nanosecondTime: res = createRLNInstanceLocal() return res -proc poseidon*(left, right: seq[byte]): RlnResult[array[32, byte]] = +proc poseidon*(left, right: seq[byte]): Result[array[32, byte], string] = ## Poseidon hash of exactly 2 inputs; zerokit v2 FFI only exposes the pair variant. poseidonPairLe(left, right) -proc toLeaf*(rateCommitment: RateCommitment): RlnResult[seq[byte]] = +proc toLeaf*(rateCommitment: RateCommitment): Result[seq[byte], string] = let idCommitment = rateCommitment.idCommitment var userMessageLimit: array[32, byte] try: @@ -164,7 +164,7 @@ proc toLeaf*(rateCommitment: RateCommitment): RlnResult[seq[byte]] = retLeaf[i] = leaf[i] return ok(retLeaf) -proc toLeaves*(rateCommitments: seq[RateCommitment]): RlnResult[seq[seq[byte]]] = +proc toLeaves*(rateCommitments: seq[RateCommitment]): Result[seq[seq[byte]], string] = var leaves = newSeq[seq[byte]]() for rateCommitment in rateCommitments: let leaf = toLeaf(rateCommitment).valueOr: @@ -174,7 +174,7 @@ proc toLeaves*(rateCommitments: seq[RateCommitment]): RlnResult[seq[seq[byte]]] proc generateExternalNullifier*( epoch: Epoch, rlnIdentifier: RlnIdentifier -): RlnResult[ExternalNullifier] = +): Result[ExternalNullifier, string] = ## externalNullifier = Poseidon(H(epoch), H(rlnIdentifier)); H = ffi_hash_to_field_le. let epochFr = hashToFieldLe(@epoch).valueOr: return err("Failed to hash epoch to field: " & error) @@ -194,7 +194,7 @@ proc generateExternalNullifier*( "Failed to serialize external nullifier: " & e ) -proc extractMetadata*(proof: RateLimitProof): RlnResult[ProofMetadata] = +proc extractMetadata*(proof: RateLimitProof): Result[ProofMetadata, string] = let externalNullifier = generateExternalNullifier(proof.epoch, proof.rlnIdentifier).valueOr: return err("Failed to compute external nullifier: " & error) return ok( @@ -206,7 +206,9 @@ proc extractMetadata*(proof: RateLimitProof): RlnResult[ProofMetadata] = ) ) -proc buildPathElementsVec(pathElements: seq[byte], depth: int): RlnResult[Vec_CFr] = +proc buildPathElementsVec( + pathElements: seq[byte], depth: int +): Result[Vec_CFr, string] = ## Caller MUST ffi_vec_cfr_free the returned Vec_CFr. var vec = ffi_vec_cfr_new(csize_t(depth)) for i in 0 ..< depth: @@ -222,7 +224,9 @@ proc buildPathElementsVec(pathElements: seq[byte], depth: int): RlnResult[Vec_CF ffi_cfr_free(element) ok(vec) -proc buildWitnessInput(witness: RLNWitnessInput): RlnResult[ptr FFI_RLNWitnessInput] = +proc buildWitnessInput( + witness: RLNWitnessInput +): Result[ptr FFI_RLNWitnessInput, string] = ## ffi_rln_witness_input_new copies all inputs, so the intermediate CFrs/vecs ## are freed here. Caller MUST ffi_rln_witness_input_free the returned handle. let depth = witness.identity_path_index.len @@ -287,7 +291,7 @@ proc generateRlnProofWithWitness*( witness: RLNWitnessInput, epoch: Epoch, rlnIdentifier: RlnIdentifier, -): RlnResult[RateLimitProof] = +): Result[RateLimitProof, string] = let witnessHandle = buildWitnessInput(witness).valueOr: return err("failed call to buildWitnessInput in generateRlnProofWithWitness: " & error) @@ -306,7 +310,7 @@ proc generateRlnProofWithWitness*( proc buildRlnProof( proof: RateLimitProof, externalNullifier: ExternalNullifier -): RlnResult[ptr FFI_RLNProof] = +): Result[ptr FFI_RLNProof, string] = ## ffi_rln_proof_new copies all inputs, so the intermediate CFrs are freed ## here. Caller MUST ffi_rln_proof_free the returned handle. var groth16Vec = toVecUint8(proof.proof) @@ -345,7 +349,7 @@ proc verifyRlnProof*( proof: RateLimitProof, signal: openArray[byte], validRoots: seq[MerkleNode], -): RlnResult[bool] = +): Result[bool, string] = if validRoots.len == 0: return err("verifyRlnProof requires at least one valid root (stateless mode)") diff --git a/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim b/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim index 15647a5ef..d7577b88a 100644 --- a/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim +++ b/tests/waku_rln_relay/rln/waku_rln_relay_utils.nim @@ -1,5 +1,6 @@ import std/options import std/tempfiles +import results import logos_delivery/waku/rln, @@ -8,12 +9,12 @@ import protocol_metrics, nonce_manager, ] -proc createRLNInstanceWrapper*(): RlnInstanceResult = +proc createRLNInstanceWrapper*(): Result[ptr RlnRaw, string] = return createRlnInstance() proc unsafeAppendRLNProof*( rlnPeer: Rln, msg: var WakuMessage, epoch: Epoch, messageId: MessageId -): RlnResult[void] = +): Result[void, string] = ## Test helper derived from the publish-path proof flow. ## - Skips nonce validation to intentionally allow generating "bad" message IDs for tests. ## - Forces a real-time on-chain Merkle root refresh via `updateRoots()` and fetches Merkle diff --git a/tests/waku_rln_relay/utils_onchain.nim b/tests/waku_rln_relay/utils_onchain.nim index 422b8c351..4955d8d64 100644 --- a/tests/waku_rln_relay/utils_onchain.nim +++ b/tests/waku_rln_relay/utils_onchain.nim @@ -37,7 +37,7 @@ proc generateCredentials*(): IdentityCredential = proc getRateCommitment*( idCredential: IdentityCredential, userMessageLimit: UserMessageLimit -): RlnResult[RawRateCommitment] = +): Result[RawRateCommitment, string] = return RateCommitment( idCommitment: idCredential.idCommitment, userMessageLimit: userMessageLimit ).toLeaf() From 90fa5fa91f14aa43a66c7ef2f3fd027248b40dbc Mon Sep 17 00:00:00 2001 From: Fabiana Cecin Date: Thu, 9 Jul 2026 12:21:41 -0300 Subject: [PATCH 29/33] feat: improve config v3 (#4015) * remove --mode from the CLI * move WakuMode to the messaging layer * expose store backend (db url, max connections) and a remote store node on the messaging surface * wakunode2 with no flags now runs as a full service node (store still opt-in) * add rateLimitMessagesPerEpoch * channel rate-limiting auto-enables if epochPeriodSec or messagesPerEpoch is set * fix JSON conf parser to be generic (works over all config types) * messaging config = mode + preset + messagingOverrides + channelsOverrides * add full messaging plus selective kernel config options to MessagingClientConf * mode (Core/Edge) expands to kernel protocol flags in the messaging layer * create_node parses the messaging config, drops the flat WakuNodeConf JSON entrypoint * wire channelsOverrides (segmentation/SDS/rate-limit) into channel creation * fix liblogosdelivery.h comments and README for the new config shape * messaging conf tests: switch names, reject-unknown, set-twice, field->kernel * add kernel log-level, log-format, nodekey to the messaging surface * Port 0 (ephemeral) default for messaging entry points * KernelConf alias for WakuNodeConf * rewrite the FFI examples to the new config shape * C/C++ examples use preset status.prod * drop operator-only confs from the examples * remove duplicate tests & misc test fixes * Delete p2pReliability from Kernel (Waku) resolver and config (keep preset definition) * Delete NodeConfig API (deprecation completed by p2pReliability removal from kernel) * Rename test_messaging_conf.nim to test_conf.nim (tests Logos Delivery config in general) * Rename messaging_conf_json.nim to logos_delivery_conf_json.nim * Add logos_delivery_conf.nim (defines LogosDeliveryConf aggregate) * misc docs/comments cleanups --- examples/cbindings/waku_example.c | 44 +- examples/cpp/waku.cpp | 20 +- examples/golang/waku.go | 26 +- examples/ios/WakuExample/WakuNode.swift | 27 +- examples/mobile/App.tsx | 11 +- examples/nodejs/waku.js | 12 +- examples/python/waku.py | 15 +- examples/qt/main_qt.cpp | 22 +- examples/rust/src/main.rs | 12 +- library/README.md | 19 +- library/examples/logosdelivery_example.c | 10 +- library/liblogosdelivery.h | 11 +- library/logos_delivery_api/node_api.nim | 8 +- logos_delivery/api/api_conf.nim | 534 ------------------ logos_delivery/api/kernel_conf.nim | 4 + logos_delivery/api/logos_delivery_conf.nim | 31 + .../api/logos_delivery_conf_json.nim | 86 +++ logos_delivery/api/messaging_conf.nim | 95 ++++ .../channels/api/channel_lifecycle.nim | 24 +- .../channels/reliable_channel_manager.nim | 26 +- logos_delivery/logos_delivery.nim | 88 +-- logos_delivery/messaging/messaging_client.nim | 52 +- .../conf_builder/waku_conf_builder.nim | 9 - logos_delivery/waku/factory/waku_conf.nim | 2 - tests/api/test_all.nim | 1 + tests/api/test_api_health.nim | 10 +- tests/api/test_api_receive.nim | 7 +- tests/api/test_api_send.nim | 17 +- tests/api/test_api_subscription.nim | 29 +- tests/api/test_conf.nim | 261 +++++++++ tests/api/test_node_conf.nim | 408 +------------ .../test_reliable_channel_send_receive.nim | 13 +- tests/node/test_wakunode_health_monitor.nim | 4 +- tests/test_waku.nim | 15 +- tools/confutils/cli_args.nim | 64 +-- tools/confutils/conf_from_json.nim | 163 +++--- 36 files changed, 893 insertions(+), 1287 deletions(-) delete mode 100644 logos_delivery/api/api_conf.nim create mode 100644 logos_delivery/api/kernel_conf.nim create mode 100644 logos_delivery/api/logos_delivery_conf.nim create mode 100644 logos_delivery/api/logos_delivery_conf_json.nim create mode 100644 logos_delivery/api/messaging_conf.nim create mode 100644 tests/api/test_conf.nim diff --git a/examples/cbindings/waku_example.c b/examples/cbindings/waku_example.c index 55feb225d..614d54d69 100644 --- a/examples/cbindings/waku_example.c +++ b/examples/cbindings/waku_example.c @@ -50,12 +50,6 @@ struct ConfigNode int relay; char peers[2048]; int store; - char storeNode[2048]; - char storeRetentionPolicy[64]; - char storeDbUrl[256]; - int storeVacuum; - int storeDbMigration; - int storeMaxNumDbConnections; }; // libwaku Context @@ -290,42 +284,24 @@ int main(int argc, char **argv) cfgNode.relay = 1; cfgNode.store = 0; - snprintf(cfgNode.storeNode, 2048, ""); - snprintf(cfgNode.storeRetentionPolicy, 64, "time:6000000"); - snprintf(cfgNode.storeDbUrl, 256, "postgres://postgres:test123@localhost:5432/postgres"); - cfgNode.storeVacuum = 0; - cfgNode.storeDbMigration = 0; - cfgNode.storeMaxNumDbConnections = 30; parse_args(argc, argv, &cfgNode); char jsonConfig[5000]; snprintf(jsonConfig, 5000, "{ \ - \"clusterId\": 16, \ - \"shards\": [ 1, 32, 64, 128, 256 ], \ - \"numShardsInNetwork\": 257, \ - \"listenAddress\": \"%s\", \ - \"tcpPort\": %d, \ - \"relay\": %s, \ - \"store\": %s, \ - \"storeMessageDbUrl\": \"%s\", \ - \"storeMessageRetentionPolicy\": \"%s\", \ - \"storeMaxNumDbConnections\": %d , \ - \"logLevel\": \"DEBUG\", \ - \"discv5Discovery\": true, \ - \"discv5BootstrapNodes\": \ - [\"enr:-QEKuED9AJm2HGgrRpVaJY2nj68ao_QiPeUT43sK-aRM7sMJ6R4G11OSDOwnvVacgN1sTw-K7soC5dzHDFZgZkHU0u-XAYJpZIJ2NIJpcISnYxMvim11bHRpYWRkcnO4WgAqNiVib290LTAxLmRvLWFtczMuc3RhdHVzLnByb2Quc3RhdHVzLmltBnZfACw2JWJvb3QtMDEuZG8tYW1zMy5zdGF0dXMucHJvZC5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEC3rRtFQSgc24uWewzXaxTY8hDAHB8sgnxr9k8Rjb5GeSDdGNwgnZfg3VkcIIjKIV3YWt1Mg0\", \"enr:-QEcuED7ww5vo2rKc1pyBp7fubBUH-8STHEZHo7InjVjLblEVyDGkjdTI9VdqmYQOn95vuQH-Htku17WSTzEufx-Wg4mAYJpZIJ2NIJpcIQihw1Xim11bHRpYWRkcnO4bAAzNi5ib290LTAxLmdjLXVzLWNlbnRyYWwxLWEuc3RhdHVzLnByb2Quc3RhdHVzLmltBnZfADU2LmJvb3QtMDEuZ2MtdXMtY2VudHJhbDEtYS5zdGF0dXMucHJvZC5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaECxjqgDQ0WyRSOilYU32DA5k_XNlDis3m1VdXkK9xM6kODdGNwgnZfg3VkcIIjKIV3YWt1Mg0\", \"enr:-QEcuEAoShWGyN66wwusE3Ri8hXBaIkoHZHybUB8cCPv5v3ypEf9OCg4cfslJxZFANl90s-jmMOugLUyBx4EfOBNJ6_VAYJpZIJ2NIJpcIQI2hdMim11bHRpYWRkcnO4bAAzNi5ib290LTAxLmFjLWNuLWhvbmdrb25nLWMuc3RhdHVzLnByb2Quc3RhdHVzLmltBnZfADU2LmJvb3QtMDEuYWMtY24taG9uZ2tvbmctYy5zdGF0dXMucHJvZC5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDP7CbRk-YKJwOFFM4Z9ney0GPc7WPJaCwGkpNRyla7mCDdGNwgnZfg3VkcIIjKIV3YWt1Mg0\"], \ - \"discv5UdpPort\": 9999, \ - \"dnsDiscoveryUrl\": \"enrtree://AMOJVZX4V6EXP7NTJPMAYJYST2QP6AJXYW76IU6VGJS7UVSNDYZG4@boot.prod.status.nodes.status.im\", \ - \"dnsDiscoveryNameServers\": [\"8.8.8.8\", \"1.0.0.1\"] \ + \"mode\": \"Core\", \ + \"preset\": \"status.prod\", \ + \"messagingOverrides\": { \ + \"listen-address\": \"%s\", \ + \"tcp-port\": %d, \ + \"store\": %s, \ + \"log-level\": \"DEBUG\", \ + \"discv5-udp-port\": 9999 \ + } \ }", cfgNode.host, cfgNode.port, - cfgNode.relay ? "true" : "false", - cfgNode.store ? "true" : "false", - cfgNode.storeDbUrl, - cfgNode.storeRetentionPolicy, - cfgNode.storeMaxNumDbConnections); + cfgNode.store ? "true" : "false"); ctx = logosdelivery_create_node(jsonConfig, event_handler, userData); waitForCallback(); diff --git a/examples/cpp/waku.cpp b/examples/cpp/waku.cpp index 7b2a9b5f5..d2808537f 100644 --- a/examples/cpp/waku.cpp +++ b/examples/cpp/waku.cpp @@ -240,18 +240,14 @@ int main(int argc, char **argv) char jsonConfig[2048]; snprintf(jsonConfig, 2048, "{ \ - \"host\": \"%s\", \ - \"port\": %d, \ - \"relay\": true, \ - \"clusterId\": 16, \ - \"shards\": [ 1, 32, 64, 128, 256 ], \ - \"logLevel\": \"FATAL\", \ - \"discv5Discovery\": true, \ - \"discv5BootstrapNodes\": \ - [\"enr:-QESuEB4Dchgjn7gfAvwB00CxTA-nGiyk-aALI-H4dYSZD3rUk7bZHmP8d2U6xDiQ2vZffpo45Jp7zKNdnwDUx6g4o6XAYJpZIJ2NIJpcIRA4VDAim11bHRpYWRkcnO4XAArNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwAtNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQOvD3S3jUNICsrOILlmhENiWAMmMVlAl6-Q8wRB7hidY4N0Y3CCdl-DdWRwgiMohXdha3UyDw\", \"enr:-QEkuEBIkb8q8_mrorHndoXH9t5N6ZfD-jehQCrYeoJDPHqT0l0wyaONa2-piRQsi3oVKAzDShDVeoQhy0uwN1xbZfPZAYJpZIJ2NIJpcIQiQlleim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwA2Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQKnGt-GSgqPSf3IAPM7bFgTlpczpMZZLF3geeoNNsxzSoN0Y3CCdl-DdWRwgiMohXdha3UyDw\"], \ - \"discv5UdpPort\": 9999, \ - \"dnsDiscoveryUrl\": \"enrtree://AMOJVZX4V6EXP7NTJPMAYJYST2QP6AJXYW76IU6VGJS7UVSNDYZG4@boot.prod.status.nodes.status.im\", \ - \"dnsDiscoveryNameServers\": [\"8.8.8.8\", \"1.0.0.1\"] \ + \"mode\": \"Core\", \ + \"preset\": \"status.prod\", \ + \"messagingOverrides\": { \ + \"listen-address\": \"%s\", \ + \"tcp-port\": %d, \ + \"log-level\": \"FATAL\", \ + \"discv5-udp-port\": 9999 \ + } \ }", cfgNode.host, cfgNode.port); diff --git a/examples/golang/waku.go b/examples/golang/waku.go index 1f99003bd..1d09bfb5e 100644 --- a/examples/golang/waku.go +++ b/examples/golang/waku.go @@ -293,12 +293,16 @@ type WakuMessageHash = string type WakuPubsubTopic = string type WakuContentTopic = string +type MessagingOverrides struct { + ListenAddress string `json:"listen-address,omitempty"` + Port int `json:"tcp-port,omitempty"` + NodeKey string `json:"nodekey,omitempty"` + LogLevel string `json:"log-level,omitempty"` +} + type WakuConfig struct { - Host string `json:"host,omitempty"` - Port int `json:"port,omitempty"` - NodeKey string `json:"key,omitempty"` - EnableRelay bool `json:"relay"` - LogLevel string `json:"logLevel"` + Mode string `json:"mode"` + MessagingOverrides MessagingOverrides `json:"messagingOverrides"` } type WakuNode struct { @@ -563,11 +567,13 @@ func (self *WakuNode) WakuGetMyENR() (string, error) { func main() { config := WakuConfig{ - Host: "0.0.0.0", - Port: 30304, - NodeKey: "11d0dcea28e86f81937a3bd1163473c7fbc0a0db54fd72914849bc47bdf78710", - EnableRelay: true, - LogLevel: "DEBUG", + Mode: "Core", + MessagingOverrides: MessagingOverrides{ + ListenAddress: "0.0.0.0", + Port: 30304, + NodeKey: "11d0dcea28e86f81937a3bd1163473c7fbc0a0db54fd72914849bc47bdf78710", + LogLevel: "DEBUG", + }, } node, err := WakuNew(config) diff --git a/examples/ios/WakuExample/WakuNode.swift b/examples/ios/WakuExample/WakuNode.swift index 9cc252546..611f50c50 100644 --- a/examples/ios/WakuExample/WakuNode.swift +++ b/examples/ios/WakuExample/WakuNode.swift @@ -300,22 +300,17 @@ actor WakuActor { private func initializeNode() async -> Bool { let config = """ { - "tcpPort": 60000, - "clusterId": 1, - "shards": [0], - "relay": false, - "lightpush": true, - "filter": true, - "logLevel": "DEBUG", - "discv5Discovery": true, - "discv5BootstrapNodes": [ - "enr:-QESuEB4Dchgjn7gfAvwB00CxTA-nGiyk-aALI-H4dYSZD3rUk7bZHmP8d2U6xDiQ2vZffpo45Jp7zKNdnwDUx6g4o6XAYJpZIJ2NIJpcIRA4VDAim11bHRpYWRkcnO4XAArNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwAtNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQOvD3S3jUNICsrOILlmhENiWAMmMVlAl6-Q8wRB7hidY4N0Y3CCdl-DdWRwgiMohXdha3UyDw", - "enr:-QEkuEBIkb8q8_mrorHndoXH9t5N6ZfD-jehQCrYeoJDPHqT0l0wyaONa2-piRQsi3oVKAzDShDVeoQhy0uwN1xbZfPZAYJpZIJ2NIJpcIQiQlleim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwA2Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQKnGt-GSgqPSf3IAPM7bFgTlpczpMZZLF3geeoNNsxzSoN0Y3CCdl-DdWRwgiMohXdha3UyDw" - ], - "discv5UdpPort": 9999, - "dnsDiscovery": true, - "dnsDiscoveryUrl": "enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im", - "dnsDiscoveryNameServers": ["8.8.8.8", "1.0.0.1"] + "mode": "Edge", + "messagingOverrides": { + "tcp-port": 60000, + "cluster-id": 1, + "log-level": "DEBUG", + "entry-node": [ + "enr:-QESuEB4Dchgjn7gfAvwB00CxTA-nGiyk-aALI-H4dYSZD3rUk7bZHmP8d2U6xDiQ2vZffpo45Jp7zKNdnwDUx6g4o6XAYJpZIJ2NIJpcIRA4VDAim11bHRpYWRkcnO4XAArNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwAtNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQOvD3S3jUNICsrOILlmhENiWAMmMVlAl6-Q8wRB7hidY4N0Y3CCdl-DdWRwgiMohXdha3UyDw", + "enr:-QEkuEBIkb8q8_mrorHndoXH9t5N6ZfD-jehQCrYeoJDPHqT0l0wyaONa2-piRQsi3oVKAzDShDVeoQhy0uwN1xbZfPZAYJpZIJ2NIJpcIQiQlleim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwA2Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQKnGt-GSgqPSf3IAPM7bFgTlpczpMZZLF3geeoNNsxzSoN0Y3CCdl-DdWRwgiMohXdha3UyDw" + ], + "discv5-udp-port": 9999 + } } """ diff --git a/examples/mobile/App.tsx b/examples/mobile/App.tsx index bf01de3a5..ff77fea5e 100644 --- a/examples/mobile/App.tsx +++ b/examples/mobile/App.tsx @@ -162,10 +162,13 @@ function App(): React.JSX.Element { const onClickNew = async () => { const config = { - host: '0.0.0.0', - port: 42342, - key: '1122334455667788990011223344556677889900112233445566778899000022', - relay: true, + mode: 'Core', + messagingOverrides: { + 'listen-address': '0.0.0.0', + 'tcp-port': 42342, + nodekey: + '1122334455667788990011223344556677889900112233445566778899000022', + }, }; waku = await WakuFactory.createInstance(config); }; diff --git a/examples/nodejs/waku.js b/examples/nodejs/waku.js index 258e34849..13005dc62 100644 --- a/examples/nodejs/waku.js +++ b/examples/nodejs/waku.js @@ -17,11 +17,13 @@ function create_random_string(length) { var wakuMod = require('bindings')('waku'); var cfg = `{ - "host": "0.0.0.0", - "port": 60001, - "key": "364d111d729a6eb6d3e6113e163f017b5ef03a6f94c9b5b7bb1bb36fa5cb07a9", - "relay": true - "logLevel": "DEBUG" + "mode": "Core", + "messagingOverrides": { + "listen-address": "0.0.0.0", + "tcp-port": 60001, + "nodekey": "364d111d729a6eb6d3e6113e163f017b5ef03a6f94c9b5b7bb1bb36fa5cb07a9", + "log-level": "DEBUG" + } }` function event_handler(event) { diff --git a/examples/python/waku.py b/examples/python/waku.py index 5df72695d..b61d5ff36 100644 --- a/examples/python/waku.py +++ b/examples/python/waku.py @@ -55,15 +55,16 @@ args = parser.parse_args() # The next 'json_config' is the item passed to the 'logosdelivery_create_node'. json_config = "{ \ - \"host\": \"%s\", \ - \"port\": %d, \ - \"key\": \"%s\", \ - \"relay\": %s ,\ - \"logLevel\": \"DEBUG\" \ + \"mode\": \"Core\", \ + \"messagingOverrides\": { \ + \"listen-address\": \"%s\", \ + \"tcp-port\": %d, \ + \"nodekey\": \"%s\", \ + \"log-level\": \"DEBUG\" \ + } \ }" % (args.host, int(args.port), - args.key, - "true" if args.relay else "false") + args.key) callback_type = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p, ctypes.c_size_t) diff --git a/examples/qt/main_qt.cpp b/examples/qt/main_qt.cpp index f16660c17..db922f062 100644 --- a/examples/qt/main_qt.cpp +++ b/examples/qt/main_qt.cpp @@ -17,18 +17,16 @@ int main(int argc, char *argv[]) { QString jsonConfig = R"( { - "tcpPort": 60000, - "relay": true, - "logLevel": "TRACE", - "discv5Discovery": true, - "discv5BootstrapNodes": [ - "enr:-QESuEB4Dchgjn7gfAvwB00CxTA-nGiyk-aALI-H4dYSZD3rUk7bZHmP8d2U6xDiQ2vZffpo45Jp7zKNdnwDUx6g4o6XAYJpZIJ2NIJpcIRA4VDAim11bHRpYWRkcnO4XAArNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwAtNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQOvD3S3jUNICsrOILlmhENiWAMmMVlAl6-Q8wRB7hidY4N0Y3CCdl-DdWRwgiMohXdha3UyDw", - "enr:-QEkuEBIkb8q8_mrorHndoXH9t5N6ZfD-jehQCrYeoJDPHqT0l0wyaONa2-piRQsi3oVKAzDShDVeoQhy0uwN1xbZfPZAYJpZIJ2NIJpcIQiQlleim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwA2Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQKnGt-GSgqPSf3IAPM7bFgTlpczpMZZLF3geeoNNsxzSoN0Y3CCdl-DdWRwgiMohXdha3UyDw" - ], - "discv5UdpPort": 9999, - "dnsDiscovery": true, - "dnsDiscoveryUrl": "enrtree://AOGYWMBYOUIMOENHXCHILPKY3ZRFEULMFI4DOM442QSZ73TT2A7VI@test.waku.nodes.status.im", - "dnsDiscoveryNameServers": ["8.8.8.8", "1.0.0.1"] + "mode": "Core", + "messagingOverrides": { + "tcp-port": 60000, + "log-level": "TRACE", + "entry-node": [ + "enr:-QESuEB4Dchgjn7gfAvwB00CxTA-nGiyk-aALI-H4dYSZD3rUk7bZHmP8d2U6xDiQ2vZffpo45Jp7zKNdnwDUx6g4o6XAYJpZIJ2NIJpcIRA4VDAim11bHRpYWRkcnO4XAArNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwAtNiZub2RlLTAxLmRvLWFtczMud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQOvD3S3jUNICsrOILlmhENiWAMmMVlAl6-Q8wRB7hidY4N0Y3CCdl-DdWRwgiMohXdha3UyDw", + "enr:-QEkuEBIkb8q8_mrorHndoXH9t5N6ZfD-jehQCrYeoJDPHqT0l0wyaONa2-piRQsi3oVKAzDShDVeoQhy0uwN1xbZfPZAYJpZIJ2NIJpcIQiQlleim11bHRpYWRkcnO4bgA0Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQZ2XwA2Ni9ub2RlLTAxLmdjLXVzLWNlbnRyYWwxLWEud2FrdS5zYW5kYm94LnN0YXR1cy5pbQYfQN4DgnJzkwABCAAAAAEAAgADAAQABQAGAAeJc2VjcDI1NmsxoQKnGt-GSgqPSf3IAPM7bFgTlpczpMZZLF3geeoNNsxzSoN0Y3CCdl-DdWRwgiMohXdha3UyDw" + ], + "discv5-udp-port": 9999 + } } )"; diff --git a/examples/rust/src/main.rs b/examples/rust/src/main.rs index 16f31bb63..bf142bd1b 100644 --- a/examples/rust/src/main.rs +++ b/examples/rust/src/main.rs @@ -50,11 +50,13 @@ where fn main() { let config_json = "\ { \ - \"host\": \"127.0.0.1\",\ - \"port\": 60000, \ - \"key\": \"0d714a1fada214dead6dc9c7274581ec20ff292451866e7d6d677dc818e8ccd2\", \ - \"relay\": true ,\ - \"logLevel\": \"DEBUG\" + \"mode\": \"Core\",\ + \"messagingOverrides\": { \ + \"listen-address\": \"127.0.0.1\",\ + \"tcp-port\": 60000, \ + \"nodekey\": \"0d714a1fada214dead6dc9c7274581ec20ff292451866e7d6d677dc818e8ccd2\", \ + \"log-level\": \"DEBUG\" + } }"; unsafe { diff --git a/library/README.md b/library/README.md index e6ddcd4d2..53f174759 100644 --- a/library/README.md +++ b/library/README.md @@ -33,15 +33,22 @@ void *logosdelivery_create_node( { "mode": "Core", "preset": "logos.dev", - "listenAddress": "0.0.0.0", - "tcpPort": 60000, - "discv5UdpPort": 9000 + "messagingOverrides": { + "listen-address": "0.0.0.0", + "tcp-port": 60000, + "discv5-udp-port": 9000 + } } ``` -Configuration uses flat field names matching `WakuNodeConf` in `tools/confutils/cli_args.nim`. -Use `"preset"` to select a network preset (e.g., `"twn"`, `"logos.dev"`, `"status.prod"`) which -auto-configures entry nodes, cluster ID, sharding, and other network-specific settings. +The configuration object has four optional top-level keys: `mode` (`"Core"` or +`"Edge"`, defaults to `"Core"`), `preset`, `messagingOverrides` (per-field node +config overrides), and `channelsOverrides` (reliable-channel overrides). +Override keys accept the config field name or its CLI switch name (e.g. +`"clusterId"` or `"cluster-id"`); unknown keys are rejected. +Use `"preset"` to select a network preset (e.g., `"twn"`, `"logos.dev"`, +`"status.prod"`) which auto-configures entry nodes, cluster ID, sharding, and +other network-specific settings. Available presets: diff --git a/library/examples/logosdelivery_example.c b/library/examples/logosdelivery_example.c index 729f7f0dc..bccdc7d25 100644 --- a/library/examples/logosdelivery_example.c +++ b/library/examples/logosdelivery_example.c @@ -129,12 +129,14 @@ void simple_callback(int ret, const char *msg, size_t len, void *userData) { int main() { printf("=== Logos Messaging API (LMAPI) Example ===\n\n"); - // Configuration JSON using WakuNodeConf field names (flat structure). - // Field names match Nim identifiers from WakuNodeConf in tools/confutils/cli_args.nim. + // Layered messaging config: {mode, preset, messagingOverrides, channelsOverrides}. + // Override keys are MessagingClientConf field or CLI switch names. const char *config = "{" - "\"logLevel\": \"INFO\"," "\"mode\": \"Core\"," - "\"preset\": \"logos.dev\"" + "\"preset\": \"logos.dev\"," + "\"messagingOverrides\": {" + "\"log-level\": \"INFO\"" + "}" "}"; printf("1. Creating node...\n"); diff --git a/library/liblogosdelivery.h b/library/liblogosdelivery.h index 7b4bbd5ab..21d8890d2 100644 --- a/library/liblogosdelivery.h +++ b/library/liblogosdelivery.h @@ -22,9 +22,14 @@ extern "C" // Creates a new instance of the node from the given configuration JSON. // Returns a pointer to the Context needed by the rest of the API functions. - // Configuration should be in JSON format using WakuNodeConf field names. - // Field names match Nim identifiers from WakuNodeConf (camelCase). - // Example: {"mode": "Core", "clusterId": 42, "relay": true} + // The configuration is a JSON object with these optional keys: + // "mode": "Core" | "Edge" (messaging role; defaults to "Core") + // "preset": "" (e.g. "twn") + // "messagingOverrides": { ... } (per-field messaging config overrides) + // "channelsOverrides": { ... } (per-field reliable-channel overrides) + // Override keys accept the config field name or its CLI switch name (e.g. + // "clusterId" or "cluster-id"). Unknown keys are rejected. + // Example: {"mode":"Core","messagingOverrides":{"cluster-id":42,"log-level":"INFO"}} void *logosdelivery_create_node( const char *configJson, FFICallBack callback, diff --git a/library/logos_delivery_api/node_api.nim b/library/logos_delivery_api/node_api.nim index 1130ab181..bed900683 100644 --- a/library/logos_delivery_api/node_api.nim +++ b/library/logos_delivery_api/node_api.nim @@ -7,7 +7,7 @@ import logos_delivery/api/types, logos_delivery/waku/api/events/health_events, logos_delivery/waku/api/events/peer_events, - tools/confutils/conf_from_json, + logos_delivery/api/logos_delivery_conf_json, ../declare_lib, ../json_event @@ -17,10 +17,10 @@ proc `%`*(id: RequestId): JsonNode = registerReqFFI(CreateNodeRequest, ctx: ptr FFIContext[LogosDelivery]): proc(configJson: cstring): Future[Result[string, string]] {.async.} = - let conf = parseNodeConfFromJson($configJson).valueOr: - error "Failed to assemble WakuNodeConf from JSON", + let conf = parseLogosDeliveryConf($configJson).valueOr: + error "Failed to parse Logos Delivery configuration JSON", error = error, configJson = $configJson - return err("failed parseNodeConfFromJson " & error) + return err("failed parseLogosDeliveryConf " & error) ctx.myLib[] = (await LogosDelivery.new(conf)).valueOr: let errMsg = $error diff --git a/logos_delivery/api/api_conf.nim b/logos_delivery/api/api_conf.nim deleted file mode 100644 index 5810b1eb2..000000000 --- a/logos_delivery/api/api_conf.nim +++ /dev/null @@ -1,534 +0,0 @@ -import logos_delivery/waku/compat/option_valueor -import std/[net, options] - -import results -import json_serialization, json_serialization/std/options as json_options - -import - logos_delivery/waku/common/utils/parse_size_units, - logos_delivery/waku/common/logging, - logos_delivery/waku/factory/waku_conf, - logos_delivery/waku/factory/conf_builder/conf_builder, - logos_delivery/waku/factory/networks_config, - tools/confutils/entry_nodes - -export json_serialization, json_options - -type AutoShardingConfig* = object - numShardsInCluster*: uint16 - -type RlnConfig* = object - contractAddress*: string - chainId*: uint - epochSizeSec*: uint64 - -type NetworkingConfig* = object - listenIpv4*: string - p2pTcpPort*: uint16 - discv5UdpPort*: uint16 - -type MessageValidation* = object - maxMessageSize*: string # Accepts formats like "150 KiB", "1500 B" - rlnConfig*: Option[RlnConfig] - -type ProtocolsConfig* = object - entryNodes: seq[string] - staticStoreNodes: seq[string] - clusterId: uint16 - autoShardingConfig: AutoShardingConfig - messageValidation: MessageValidation - -const DefaultNetworkingConfig* = - NetworkingConfig(listenIpv4: "0.0.0.0", p2pTcpPort: 60000, discv5UdpPort: 9000) - -const DefaultAutoShardingConfig* = AutoShardingConfig(numShardsInCluster: 1) - -const DefaultMessageValidation* = - MessageValidation(maxMessageSize: "150 KiB", rlnConfig: none(RlnConfig)) - -proc init*( - T: typedesc[ProtocolsConfig], - entryNodes: seq[string], - staticStoreNodes: seq[string] = @[], - clusterId: uint16, - autoShardingConfig: AutoShardingConfig = DefaultAutoShardingConfig, - messageValidation: MessageValidation = DefaultMessageValidation, -): T = - return T( - entryNodes: entryNodes, - staticStoreNodes: staticStoreNodes, - clusterId: clusterId, - autoShardingConfig: autoShardingConfig, - messageValidation: messageValidation, - ) - -const TheWakuNetworkPreset* = ProtocolsConfig( - entryNodes: @[ - "enrtree://AIRVQ5DDA4FFWLRBCHJWUWOO6X6S4ZTZ5B667LQ6AJU6PEYDLRD5O@sandbox.waku.nodes.status.im" - ], - staticStoreNodes: @[], - clusterId: 1, - autoShardingConfig: AutoShardingConfig(numShardsInCluster: 8), - messageValidation: MessageValidation( - maxMessageSize: "150 KiB", - rlnConfig: some( - RlnConfig( - contractAddress: "0xB9cd878C90E49F797B4431fBF4fb333108CB90e6", - chainId: 59141, - epochSizeSec: 600, # 10 minutes - ) - ), - ), -) - -type WakuMode* {.pure.} = enum - Edge - Core - -type NodeConfig* {. - requiresInit, deprecated: "Use WakuNodeConf from tools/confutils/cli_args instead" -.} = object - mode: WakuMode - protocolsConfig: ProtocolsConfig - networkingConfig: NetworkingConfig - ethRpcEndpoints: seq[string] - p2pReliability: bool - logLevel: LogLevel - logFormat: LogFormat - -proc init*( - T: typedesc[NodeConfig], - mode: WakuMode = WakuMode.Core, - protocolsConfig: ProtocolsConfig = TheWakuNetworkPreset, - networkingConfig: NetworkingConfig = DefaultNetworkingConfig, - ethRpcEndpoints: seq[string] = @[], - p2pReliability: bool = false, - logLevel: LogLevel = LogLevel.INFO, - logFormat: LogFormat = LogFormat.TEXT, -): T = - return T( - mode: mode, - protocolsConfig: protocolsConfig, - networkingConfig: networkingConfig, - ethRpcEndpoints: ethRpcEndpoints, - p2pReliability: p2pReliability, - logLevel: logLevel, - logFormat: logFormat, - ) - -# -- Getters for ProtocolsConfig (private fields) - used for testing -- - -proc entryNodes*(c: ProtocolsConfig): seq[string] = - c.entryNodes - -proc staticStoreNodes*(c: ProtocolsConfig): seq[string] = - c.staticStoreNodes - -proc clusterId*(c: ProtocolsConfig): uint16 = - c.clusterId - -proc autoShardingConfig*(c: ProtocolsConfig): AutoShardingConfig = - c.autoShardingConfig - -proc messageValidation*(c: ProtocolsConfig): MessageValidation = - c.messageValidation - -# -- Getters for NodeConfig (private fields) - used for testing -- - -proc mode*(c: NodeConfig): WakuMode = - c.mode - -proc protocolsConfig*(c: NodeConfig): ProtocolsConfig = - c.protocolsConfig - -proc networkingConfig*(c: NodeConfig): NetworkingConfig = - c.networkingConfig - -proc ethRpcEndpoints*(c: NodeConfig): seq[string] = - c.ethRpcEndpoints - -proc p2pReliability*(c: NodeConfig): bool = - c.p2pReliability - -proc logLevel*(c: NodeConfig): LogLevel = - c.logLevel - -proc logFormat*(c: NodeConfig): LogFormat = - c.logFormat - -proc toWakuConf*( - nodeConfig: NodeConfig -): Result[WakuConf, string] {.deprecated: "Use WakuNodeConf.toWakuConf instead".} = - var b = WakuConfBuilder.init() - - # Apply log configuration - b.withLogLevel(nodeConfig.logLevel) - b.withLogFormat(nodeConfig.logFormat) - - # Apply networking configuration - let networkingConfig = nodeConfig.networkingConfig - let ip = parseIpAddress(networkingConfig.listenIpv4) - - b.withP2pListenAddress(ip) - b.withP2pTcpPort(networkingConfig.p2pTcpPort) - b.discv5Conf.withUdpPort(networkingConfig.discv5UdpPort) - - case nodeConfig.mode - of Core: - b.withRelay(true) - - # Metadata is always mounted - - b.filterServiceConf.withEnabled(true) - b.filterServiceConf.withMaxPeersToServe(20) - - b.withLightPush(true) - - b.discv5Conf.withEnabled(true) - b.withPeerExchange(true) - b.withRendezvous(true) - - # TODO: fix store as client usage - - b.rateLimitConf.withRateLimits(@["filter:100/1s", "lightpush:5/1s", "px:5/1s"]) - of Edge: - # All client side protocols are mounted by default - # Peer exchange client is always enabled and start_node will start the px loop - # Metadata is always mounted - b.withPeerExchange(true) - # switch off all service side protocols and relay - b.withRelay(false) - b.filterServiceConf.withEnabled(false) - b.withLightPush(false) - b.storeServiceConf.withEnabled(false) - # Leave discv5 and rendezvous for user choice - - ## Network Conf - let protocolsConfig = nodeConfig.protocolsConfig - - # Set cluster ID - b.withClusterId(protocolsConfig.clusterId) - - # Set sharding configuration - b.withShardingConf(ShardingConfKind.AutoSharding) - let autoShardingConfig = protocolsConfig.autoShardingConfig - b.withNumShardsInCluster(autoShardingConfig.numShardsInCluster) - - # Process entry nodes - supports enrtree:, enr:, and multiaddress formats - if protocolsConfig.entryNodes.len > 0: - let (enrTreeUrls, bootstrapEnrs, staticNodesFromEntry) = processEntryNodes( - protocolsConfig.entryNodes - ).valueOr: - return err("Failed to process entry nodes: " & error) - - # Set ENRTree URLs for DNS discovery - if enrTreeUrls.len > 0: - for url in enrTreeUrls: - b.dnsDiscoveryConf.withEnrTreeUrl(url) - b.dnsDiscoveryconf.withNameServers( - @[parseIpAddress("1.1.1.1"), parseIpAddress("1.0.0.1")] - ) - - # Set ENR records as bootstrap nodes for discv5 - if bootstrapEnrs.len > 0: - b.discv5Conf.withBootstrapNodes(bootstrapEnrs) - - # Add static nodes (multiaddrs and those extracted from ENR entries) - if staticNodesFromEntry.len > 0: - b.withStaticNodes(staticNodesFromEntry) - - # TODO: verify behaviour - # Set static store nodes - if protocolsConfig.staticStoreNodes.len > 0: - b.withStaticNodes(protocolsConfig.staticStoreNodes) - - # Set message validation - let msgValidation = protocolsConfig.messageValidation - let maxSizeBytes = parseMsgSize(msgValidation.maxMessageSize).valueOr: - return err("Failed to parse max message size: " & error) - b.withMaxMessageSize(maxSizeBytes) - - # Set RLN config if provided - if msgValidation.rlnConfig.isSome(): - let rlnConfig = msgValidation.rlnConfig.get() - b.rlnRelayConf.withEnabled(true) - b.rlnRelayConf.withEthContractAddress(rlnConfig.contractAddress) - b.rlnRelayConf.withChainId(rlnConfig.chainId) - b.rlnRelayConf.withEpochSizeSec(rlnConfig.epochSizeSec) - b.rlnRelayConf.withDynamic(true) - b.rlnRelayConf.withEthClientUrls(nodeConfig.ethRpcEndpoints) - - # TODO: we should get rid of those two - b.rlnRelayconf.withUserMessageLimit(100) - - ## Various configurations - b.withNatStrategy("any") - b.withP2PReliability(nodeConfig.p2pReliability) - - let wakuConf = b.build().valueOr: - return err("Failed to build configuration: " & error) - - wakuConf.validate().isOkOr: - return err("Failed to validate configuration: " & error) - - return ok(wakuConf) - -# ---- JSON serialization (writeValue / readValue) ---- -# ---------- AutoShardingConfig ---------- - -proc writeValue*(w: var JsonWriter, val: AutoShardingConfig) {.raises: [IOError].} = - w.beginRecord() - w.writeField("numShardsInCluster", val.numShardsInCluster) - w.endRecord() - -proc readValue*( - r: var JsonReader, val: var AutoShardingConfig -) {.raises: [SerializationError, IOError].} = - var numShardsInCluster: Option[uint16] - - for fieldName in readObjectFields(r): - case fieldName - of "numShardsInCluster": - numShardsInCluster = some(r.readValue(uint16)) - else: - r.raiseUnexpectedField(fieldName, "AutoShardingConfig") - - if numShardsInCluster.isNone(): - r.raiseUnexpectedValue("Missing required field 'numShardsInCluster'") - - val = AutoShardingConfig(numShardsInCluster: numShardsInCluster.get()) - -# ---------- RlnConfig ---------- - -proc writeValue*(w: var JsonWriter, val: RlnConfig) {.raises: [IOError].} = - w.beginRecord() - w.writeField("contractAddress", val.contractAddress) - w.writeField("chainId", val.chainId) - w.writeField("epochSizeSec", val.epochSizeSec) - w.endRecord() - -proc readValue*( - r: var JsonReader, val: var RlnConfig -) {.raises: [SerializationError, IOError].} = - var - contractAddress: Option[string] - chainId: Option[uint] - epochSizeSec: Option[uint64] - - for fieldName in readObjectFields(r): - case fieldName - of "contractAddress": - contractAddress = some(r.readValue(string)) - of "chainId": - chainId = some(r.readValue(uint)) - of "epochSizeSec": - epochSizeSec = some(r.readValue(uint64)) - else: - r.raiseUnexpectedField(fieldName, "RlnConfig") - - if contractAddress.isNone(): - r.raiseUnexpectedValue("Missing required field 'contractAddress'") - if chainId.isNone(): - r.raiseUnexpectedValue("Missing required field 'chainId'") - if epochSizeSec.isNone(): - r.raiseUnexpectedValue("Missing required field 'epochSizeSec'") - - val = RlnConfig( - contractAddress: contractAddress.get(), - chainId: chainId.get(), - epochSizeSec: epochSizeSec.get(), - ) - -# ---------- NetworkingConfig ---------- - -proc writeValue*(w: var JsonWriter, val: NetworkingConfig) {.raises: [IOError].} = - w.beginRecord() - w.writeField("listenIpv4", val.listenIpv4) - w.writeField("p2pTcpPort", val.p2pTcpPort) - w.writeField("discv5UdpPort", val.discv5UdpPort) - w.endRecord() - -proc readValue*( - r: var JsonReader, val: var NetworkingConfig -) {.raises: [SerializationError, IOError].} = - var - listenIpv4: Option[string] - p2pTcpPort: Option[uint16] - discv5UdpPort: Option[uint16] - - for fieldName in readObjectFields(r): - case fieldName - of "listenIpv4": - listenIpv4 = some(r.readValue(string)) - of "p2pTcpPort": - p2pTcpPort = some(r.readValue(uint16)) - of "discv5UdpPort": - discv5UdpPort = some(r.readValue(uint16)) - else: - r.raiseUnexpectedField(fieldName, "NetworkingConfig") - - if listenIpv4.isNone(): - r.raiseUnexpectedValue("Missing required field 'listenIpv4'") - if p2pTcpPort.isNone(): - r.raiseUnexpectedValue("Missing required field 'p2pTcpPort'") - if discv5UdpPort.isNone(): - r.raiseUnexpectedValue("Missing required field 'discv5UdpPort'") - - val = NetworkingConfig( - listenIpv4: listenIpv4.get(), - p2pTcpPort: p2pTcpPort.get(), - discv5UdpPort: discv5UdpPort.get(), - ) - -# ---------- MessageValidation ---------- - -proc writeValue*(w: var JsonWriter, val: MessageValidation) {.raises: [IOError].} = - w.beginRecord() - w.writeField("maxMessageSize", val.maxMessageSize) - w.writeField("rlnConfig", val.rlnConfig) - w.endRecord() - -proc readValue*( - r: var JsonReader, val: var MessageValidation -) {.raises: [SerializationError, IOError].} = - var - maxMessageSize: Option[string] - rlnConfig: Option[Option[RlnConfig]] - - for fieldName in readObjectFields(r): - case fieldName - of "maxMessageSize": - maxMessageSize = some(r.readValue(string)) - of "rlnConfig": - rlnConfig = some(r.readValue(Option[RlnConfig])) - else: - r.raiseUnexpectedField(fieldName, "MessageValidation") - - if maxMessageSize.isNone(): - r.raiseUnexpectedValue("Missing required field 'maxMessageSize'") - - val = MessageValidation( - maxMessageSize: maxMessageSize.get(), rlnConfig: rlnConfig.get(none(RlnConfig)) - ) - -# ---------- ProtocolsConfig ---------- - -proc writeValue*(w: var JsonWriter, val: ProtocolsConfig) {.raises: [IOError].} = - w.beginRecord() - w.writeField("entryNodes", val.entryNodes) - w.writeField("staticStoreNodes", val.staticStoreNodes) - w.writeField("clusterId", val.clusterId) - w.writeField("autoShardingConfig", val.autoShardingConfig) - w.writeField("messageValidation", val.messageValidation) - w.endRecord() - -proc readValue*( - r: var JsonReader, val: var ProtocolsConfig -) {.raises: [SerializationError, IOError].} = - var - entryNodes: Option[seq[string]] - staticStoreNodes: Option[seq[string]] - clusterId: Option[uint16] - autoShardingConfig: Option[AutoShardingConfig] - messageValidation: Option[MessageValidation] - - for fieldName in readObjectFields(r): - case fieldName - of "entryNodes": - entryNodes = some(r.readValue(seq[string])) - of "staticStoreNodes": - staticStoreNodes = some(r.readValue(seq[string])) - of "clusterId": - clusterId = some(r.readValue(uint16)) - of "autoShardingConfig": - autoShardingConfig = some(r.readValue(AutoShardingConfig)) - of "messageValidation": - messageValidation = some(r.readValue(MessageValidation)) - else: - r.raiseUnexpectedField(fieldName, "ProtocolsConfig") - - if entryNodes.isNone(): - r.raiseUnexpectedValue("Missing required field 'entryNodes'") - if clusterId.isNone(): - r.raiseUnexpectedValue("Missing required field 'clusterId'") - - val = ProtocolsConfig.init( - entryNodes = entryNodes.get(), - staticStoreNodes = staticStoreNodes.get(@[]), - clusterId = clusterId.get(), - autoShardingConfig = autoShardingConfig.get(DefaultAutoShardingConfig), - messageValidation = messageValidation.get(DefaultMessageValidation), - ) - -# ---------- NodeConfig ---------- - -proc writeValue*(w: var JsonWriter, val: NodeConfig) {.raises: [IOError].} = - w.beginRecord() - w.writeField("mode", val.mode) - w.writeField("protocolsConfig", val.protocolsConfig) - w.writeField("networkingConfig", val.networkingConfig) - w.writeField("ethRpcEndpoints", val.ethRpcEndpoints) - w.writeField("p2pReliability", val.p2pReliability) - w.writeField("logLevel", val.logLevel) - w.writeField("logFormat", val.logFormat) - w.endRecord() - -proc readValue*( - r: var JsonReader, val: var NodeConfig -) {.raises: [SerializationError, IOError].} = - var - mode: Option[WakuMode] - protocolsConfig: Option[ProtocolsConfig] - networkingConfig: Option[NetworkingConfig] - ethRpcEndpoints: Option[seq[string]] - p2pReliability: Option[bool] - logLevel: Option[LogLevel] - logFormat: Option[LogFormat] - - for fieldName in readObjectFields(r): - case fieldName - of "mode": - mode = some(r.readValue(WakuMode)) - of "protocolsConfig": - protocolsConfig = some(r.readValue(ProtocolsConfig)) - of "networkingConfig": - networkingConfig = some(r.readValue(NetworkingConfig)) - of "ethRpcEndpoints": - ethRpcEndpoints = some(r.readValue(seq[string])) - of "p2pReliability": - p2pReliability = some(r.readValue(bool)) - of "logLevel": - logLevel = some(r.readValue(LogLevel)) - of "logFormat": - logFormat = some(r.readValue(LogFormat)) - else: - r.raiseUnexpectedField(fieldName, "NodeConfig") - - val = NodeConfig.init( - mode = mode.get(WakuMode.Core), - protocolsConfig = protocolsConfig.get(TheWakuNetworkPreset), - networkingConfig = networkingConfig.get(DefaultNetworkingConfig), - ethRpcEndpoints = ethRpcEndpoints.get(@[]), - p2pReliability = p2pReliability.get(false), - logLevel = logLevel.get(LogLevel.INFO), - logFormat = logFormat.get(LogFormat.TEXT), - ) - -# ---------- Decode helper ---------- -# Json.decode returns T via `result`, which conflicts with {.requiresInit.} -# on Nim 2.x. This helper avoids the issue by using readValue into a var. - -proc decodeNodeConfigFromJson*( - jsonStr: string -): NodeConfig {. - raises: [SerializationError], - deprecated: "Use WakuNodeConf with fieldPairs-based JSON parsing instead" -.} = - var val = NodeConfig.init() # default-initialized - try: - var stream = unsafeMemoryInput(jsonStr) - var reader = (JsonReader[DefaultFlavor].init(stream)) - reader.readValue(val) - except IOError as err: - raise (ref SerializationError)(msg: err.msg) - return val diff --git a/logos_delivery/api/kernel_conf.nim b/logos_delivery/api/kernel_conf.nim new file mode 100644 index 000000000..2c66e2d51 --- /dev/null +++ b/logos_delivery/api/kernel_conf.nim @@ -0,0 +1,4 @@ +import tools/confutils/cli_args +export cli_args + +type KernelConf* = WakuNodeConf ## Alias of the CLI-facing `WakuNodeConf`. diff --git a/logos_delivery/api/logos_delivery_conf.nim b/logos_delivery/api/logos_delivery_conf.nim new file mode 100644 index 000000000..124a8c5fb --- /dev/null +++ b/logos_delivery/api/logos_delivery_conf.nim @@ -0,0 +1,31 @@ +{.push raises: [].} + +import results + +import logos_delivery/api/messaging_conf +import logos_delivery/channels/reliable_channel_manager + +export messaging_conf, reliable_channel_manager + +type LogosDeliveryConf* = object ## Aggregates the per-layer config objects. + kernelConf*: KernelConf + messagingConf*: MessagingClientConf + channelsConf*: ReliableChannelManagerConf + +proc init*( + T: type LogosDeliveryConf, + mode: WakuMode, + preset: string, + messagingOverrides: MessagingClientConf, + channelsOverrides: ReliableChannelManagerConf, +): ConfResult[LogosDeliveryConf] = + let merged = merge(?resolvePreset(preset), messagingOverrides) + var kernelConf = ?toKernelConf(merged, mode) + kernelConf.preset = preset + return ok( + LogosDeliveryConf( + kernelConf: kernelConf, messagingConf: merged, channelsConf: channelsOverrides + ) + ) + +{.pop.} diff --git a/logos_delivery/api/logos_delivery_conf_json.nim b/logos_delivery/api/logos_delivery_conf_json.nim new file mode 100644 index 000000000..2416104bd --- /dev/null +++ b/logos_delivery/api/logos_delivery_conf_json.nim @@ -0,0 +1,86 @@ +{.push raises: [].} + +import std/[json, options, strutils, tables] +import results + +import tools/confutils/conf_from_json +import logos_delivery/api/logos_delivery_conf + +const + # Lowercased, since `collectJsonFields` keys the object case-insensitively. + KeyMode = "mode" + KeyPreset = "preset" + KeyMessagingOverrides = "messagingoverrides" + KeyChannelsOverrides = "channelsoverrides" + +proc parseMode(s: string): Result[WakuMode, string] = + case s.strip().toLowerAscii() + of "core": + return ok(WakuMode.Core) + of "edge": + return ok(WakuMode.Edge) + else: + return err("invalid mode: '" & s & "' (expected 'Core' or 'Edge')") + +proc parseOverrides[T](node: JsonNode, label: string): Result[T, string] = + if node.kind != JObject: + return err(label & " must be a JSON object") + var fields = ?collectJsonFields(node) + var conf = T() + ?applyJsonFieldsToConf( + conf, + fields, + "Failed to parse " & label & " field", + "Unrecognized " & label & " option(s) found", + ) + return ok(conf) + +proc parseLogosDeliveryConf*(jsonStr: string): ConfResult[LogosDeliveryConf] = + var node: JsonNode + try: + node = parseJson(jsonStr) + except CatchableError as e: + return err("invalid JSON: " & e.msg) + if node.kind != JObject: + return err("configuration JSON must be an object") + + var top = ?collectJsonFields(node) + var mode = WakuMode.Core + var preset = "" + var messagingOverrides = MessagingClientConf() + var channelsOverrides = ReliableChannelManagerConf() + + if top.hasKey(KeyMode): + let (_, v) = top.getOrDefault(KeyMode) + if v.kind != JString: + return err("mode must be a string") + mode = ?parseMode(v.getStr()) + top.del(KeyMode) + + if top.hasKey(KeyPreset): + let (_, v) = top.getOrDefault(KeyPreset) + if v.kind != JString: + return err("preset must be a string") + preset = v.getStr().strip() + top.del(KeyPreset) + + if top.hasKey(KeyMessagingOverrides): + let (_, v) = top.getOrDefault(KeyMessagingOverrides) + messagingOverrides = ?parseOverrides[MessagingClientConf](v, "messagingOverrides") + top.del(KeyMessagingOverrides) + + if top.hasKey(KeyChannelsOverrides): + let (_, v) = top.getOrDefault(KeyChannelsOverrides) + channelsOverrides = + ?parseOverrides[ReliableChannelManagerConf](v, "channelsOverrides") + top.del(KeyChannelsOverrides) + + if top.len > 0: + var keys: seq[string] + for _, (k, _) in pairs(top): + keys.add(k) + return err("Unrecognized configuration option(s) found: " & keys.join(", ")) + + return LogosDeliveryConf.init(mode, preset, messagingOverrides, channelsOverrides) + +{.pop.} diff --git a/logos_delivery/api/messaging_conf.nim b/logos_delivery/api/messaging_conf.nim new file mode 100644 index 000000000..9ba642cd8 --- /dev/null +++ b/logos_delivery/api/messaging_conf.nim @@ -0,0 +1,95 @@ +import std/options +import std/net +import results + +import logos_delivery/api/kernel_conf +import logos_delivery/messaging/messaging_client +import logos_delivery/waku/factory/networks_config + +export kernel_conf, messaging_client + +type WakuMode* {.pure.} = enum + Edge # client-only node + Core # full service node + +proc toKernelConf*(self: MessagingClientConf, mode: WakuMode): ConfResult[KernelConf] = + ## Mode sets the protocol flags; set fields map to their kernel counterpart. + var conf = ?defaultWakuNodeConf() + + case mode + of WakuMode.Core: + conf.relay = true + conf.filter = true + conf.lightpush = true + conf.discv5Discovery = some(true) + conf.peerExchange = true + conf.rendezvous = true + of WakuMode.Edge: + conf.peerExchange = true + conf.relay = false + conf.filter = false + conf.lightpush = false + conf.store = false + + if self.store.isSome(): + conf.store = self.store.get() + if self.storeMessageDbUrl.isSome(): + conf.storeMessageDbUrl = self.storeMessageDbUrl.get() + if self.storeMessageRetentionPolicy.isSome(): + conf.storeMessageRetentionPolicy = self.storeMessageRetentionPolicy.get() + if self.storeMaxNumDbConnections.isSome(): + conf.storeMaxNumDbConnections = self.storeMaxNumDbConnections.get() + if self.storenode.isSome(): + conf.storenode = self.storenode.get() + + if self.clusterId.isSome(): + conf.clusterId = self.clusterId + if self.numShardsInCluster.isSome(): + conf.numShardsInNetwork = self.numShardsInCluster.get() + if self.listenIpv4.isSome(): + conf.listenAddress = self.listenIpv4.get() + if self.maxMessageSize.isSome(): + conf.maxMessageSize = self.maxMessageSize.get() + if self.entryNodes.isSome(): + conf.entryNodes = self.entryNodes.get() + if self.ethRpcEndpoints.isSome(): + conf.ethClientUrls = self.ethRpcEndpoints.get() + if self.rlnContractAddress.isSome(): + conf.rlnRelayEthContractAddress = self.rlnContractAddress.get() + conf.rlnRelay = some(true) + if self.rlnChainId.isSome(): + conf.rlnRelayChainId = self.rlnChainId.get() + if self.rlnEpochSizeSec.isSome(): + conf.rlnEpochSizeSec = some(self.rlnEpochSizeSec.get().uint64) + if self.logLevel.isSome(): + conf.logLevel = self.logLevel.get() + if self.logFormat.isSome(): + conf.logFormat = self.logFormat.get() + if self.nodeKey.isSome(): + conf.nodekey = self.nodeKey + + conf.tcpPort = self.p2pTcpPort.get(Port(0)) + conf.discv5UdpPort = self.discv5UdpPort.get(Port(0)) + conf.websocketPort = self.websocketPort.get(Port(0)) + conf.quicPort = self.quicPort.get(Port(0)) + conf.websocketSupport = self.websocketSupport.get(false) + conf.quicSupport = self.quicSupport.get(true) + + return ok(conf) + +proc merge*(base, overrides: MessagingClientConf): MessagingClientConf = + var m = base + for _, mField, oField in fieldPairs(m, overrides): + when oField is Option: + if oField.isSome(): + mField = oField + return m + +proc resolvePreset*(preset: string): ConfResult[MessagingClientConf] = + ## Preset to messaging-only fields. Kernel-mirrored fields stay unset; the + ## kernel resolves those from `conf.preset`. + let npcOpt = ?toNetworkPresetConf(preset, none(uint16)) + if npcOpt.isNone(): + return ok(MessagingClientConf()) + let npc = npcOpt.get() + return ok(MessagingClientConf(reliabilityEnabled: some(npc.p2pReliability))) diff --git a/logos_delivery/channels/api/channel_lifecycle.nim b/logos_delivery/channels/api/channel_lifecycle.nim index 4f891c61f..d110ceb72 100644 --- a/logos_delivery/channels/api/channel_lifecycle.nim +++ b/logos_delivery/channels/api/channel_lifecycle.nim @@ -8,7 +8,7 @@ import logos_delivery/channels/reliable_channel_manager import logos_delivery/channels/reliable_channel import logos_delivery/waku/persistency/sds_persistency -# ReliableChannel, SendHandler, config and wire-version markers. +# ReliableChannel, config and wire-version markers. export reliable_channel const SdsJobId = "sds" @@ -33,22 +33,32 @@ proc createReliableChannel*( contentTopic: ContentTopic, senderId: SdsParticipantID, ): Result[ChannelId, string] = + ## Encryption and egress providers must be installed (or `setNoopEncryption()`) + ## before traffic flows on the channel. if self.channels.hasKey(channelId): return err("channel already exists: " & channelId) + let cc = self.conf let segConfig = SegmentationConfig( - segmentSizeBytes: DefaultSegmentSizeBytes, - enableReedSolomon: false, + segmentSizeBytes: cc.segmentationSegmentSizeBytes.get(DefaultSegmentSizeBytes), + enableReedSolomon: cc.segmentationEnableReedSolomon.get(false), persistence: nil, ) let sdsConfig = SdsConfig( - acknowledgementTimeoutMs: DefaultAcknowledgementTimeoutMs, - maxRetransmissions: DefaultMaxRetransmissions, - causalHistorySize: DefaultCausalHistorySize, + acknowledgementTimeoutMs: + cc.sdsAcknowledgementTimeoutMs.get(DefaultAcknowledgementTimeoutMs), + maxRetransmissions: cc.sdsMaxRetransmissions.get(DefaultMaxRetransmissions), + causalHistorySize: cc.sdsCausalHistorySize.get(DefaultCausalHistorySize), persistence: sdsPersistence(), ) let rateConfig = RateLimitConfig( - epochPeriodSec: DefaultEpochPeriodSec, messagesPerEpoch: DefaultMessagesPerEpoch + # Setting a rate-limit parameter implies enabling; an explicit + # rateLimitEnabled still wins. + enabled: cc.rateLimitEnabled.get( + cc.rateLimitEpochPeriodSec.isSome() or cc.rateLimitMessagesPerEpoch.isSome() + ), + epochPeriodSec: cc.rateLimitEpochPeriodSec.get(DefaultEpochPeriodSec), + messagesPerEpoch: cc.rateLimitMessagesPerEpoch.get(DefaultMessagesPerEpoch), ) let chn = ReliableChannel.new( diff --git a/logos_delivery/channels/reliable_channel_manager.nim b/logos_delivery/channels/reliable_channel_manager.nim index 8ff176e9a..46e3c3912 100644 --- a/logos_delivery/channels/reliable_channel_manager.nim +++ b/logos_delivery/channels/reliable_channel_manager.nim @@ -13,8 +13,6 @@ import stew/byteutils import brokers/broker_context -import logos_delivery/messaging/messaging_client -import logos_delivery/messaging/api/send import logos_delivery/api/types import logos_delivery/api/reliable_channel_manager_api @@ -24,12 +22,22 @@ export reliable_channel type ReliableChannelManagerConf* = object - ## Per-layer config object for the reliable - ## channel API. Placeholder for now (segmentation / SDS / rate-limit defaults - ## will move here in a follow-up PR); kept so each layer owns its own config. + ## All-`Option` partial; unset fields fall back to `createReliableChannel` defaults. + segmentationEnableReedSolomon*: Option[bool] + ## Add Reed-Solomon parity segments for recovery of lost segments. + segmentationSegmentSizeBytes*: Option[int] ## Maximum segment size in bytes. + sdsAcknowledgementTimeoutMs*: Option[int] + ## Time to wait before retransmitting an unacknowledged message. + sdsMaxRetransmissions*: Option[int] + ## Maximum retransmission attempts before delivery fails. + sdsCausalHistorySize*: Option[int] ## Number of message ids kept in causal history. + rateLimitEnabled*: Option[bool] ## Enable rate limiting. + rateLimitEpochPeriodSec*: Option[int] ## Rate-limit epoch length in seconds. + rateLimitMessagesPerEpoch*: Option[int] ## Messages allowed per rate-limit epoch. ReliableChannelManager* = ref object ## Implements `ReliableChannelApi`. channels*: Table[ChannelId, ReliableChannel] ## read by `channels/api.nim` + conf*: ReliableChannelManagerConf brokerCtx*: BrokerContext proc new*( @@ -37,7 +45,13 @@ proc new*( conf: ReliableChannelManagerConf, brokerCtx: BrokerContext = globalBrokerContext(), ): Result[T, string] = - return ok(T(channels: initTable[ChannelId, ReliableChannel](), brokerCtx: brokerCtx)) + return ok( + T( + channels: initTable[ChannelId, ReliableChannel](), + conf: conf, + brokerCtx: brokerCtx, + ) + ) proc start*(self: ReliableChannelManager): Result[void, string] = ## Placeholder: per-channel listeners are installed in `ReliableChannel.new`, diff --git a/logos_delivery/logos_delivery.nim b/logos_delivery/logos_delivery.nim index 6a6587ab0..1e34b6a6a 100644 --- a/logos_delivery/logos_delivery.nim +++ b/logos_delivery/logos_delivery.nim @@ -9,6 +9,7 @@ {.push raises: [].} +import std/options import results, chronos, chronicles # Each layer has a core module (type + new/start/stop) and an api/ folder whose @@ -41,6 +42,8 @@ import logos_delivery/messaging/api/[subscription, send] export subscription, send import logos_delivery/api/events/messaging_client_events export messaging_client_events +import logos_delivery/api/messaging_conf +export messaging_conf # Reliable Channel layer import logos_delivery/channels/reliable_channel_manager @@ -58,63 +61,68 @@ import logos_delivery/waku/factory/waku_conf import logos_delivery/waku/factory/app_callbacks import tools/confutils/cli_args import logos_delivery/waku/node/health_monitor/online_monitor +import logos_delivery/api/logos_delivery_conf +export logos_delivery_conf logScope: topics = "logosdelivery" -type - LogosDeliveryConf* = object - ## Aggregates the per-layer config objects. For now - ## the sub-configs are derived from `WakuConf`; richer per-layer configuration - ## (and how it is sourced) lands in a follow-up PR. - waku*: WakuConf - messaging*: MessagingClientConf - reliableChannel*: ReliableChannelManagerConf - - LogosDelivery* = ref object ## Entry point. Holds one instance of each API layer. - waku*: Waku - messagingClient*: MessagingClient - reliableChannelManager*: ReliableChannelManager - -proc init*(T: type LogosDeliveryConf, wakuConf: WakuConf): LogosDeliveryConf = - ## Builds the aggregated config from a `WakuConf`. The messaging / reliable - ## channel layers carry trivial config today; this is the seam where their - ## dedicated config will be threaded through later. - LogosDeliveryConf( - waku: wakuConf, - messaging: MessagingClientConf(useP2PReliability: wakuConf.p2pReliability), - reliableChannel: ReliableChannelManagerConf(), - ) +type LogosDelivery* = ref object ## Entry point. Holds one instance of each API layer. + waku*: Waku + messagingClient*: MessagingClient + reliableChannelManager*: ReliableChannelManager proc new*( - T: type LogosDelivery, conf: WakuNodeConf, appCallbacks: AppCallbacks = nil + T: type LogosDelivery, conf: LogosDeliveryConf, appCallbacks: AppCallbacks = nil ): Future[Result[LogosDelivery, string]] {.async.} = - ## Single entry point, from the CLI configuration type. Derives the aggregated - ## per-layer config, then creates the full stack bottom-up so each layer can - ## chain onto the one below. - let wakuConf = conf.toWakuConf().valueOr: + ## Builds the stack bottom-up from a resolved per-layer config. + let wakuConf = conf.kernelConf.toWakuConf().valueOr: return err("failed to handle the configuration: " & error) - let layerConf = LogosDeliveryConf.init(wakuConf) - - let waku = (await Waku.new(layerConf.waku, appCallbacks)).valueOr: + let waku = (await Waku.new(wakuConf, appCallbacks)).valueOr: return err("failed to create Waku: " & error) - let messagingClient = MessagingClient.new(layerConf.messaging, waku).valueOr: + let messagingClient = MessagingClient.new(conf.messagingConf, waku).valueOr: return err("failed to create MessagingClient: " & error) let reliableChannelManager = ReliableChannelManager.new( - layerConf.reliableChannel, waku.brokerCtx + conf.channelsConf, waku.brokerCtx ).valueOr: return err("failed to create ReliableChannelManager: " & error) return ok( - T( + LogosDelivery( waku: waku, messagingClient: messagingClient, reliableChannelManager: reliableChannelManager, ) ) +proc new*( + T: type LogosDelivery, conf: WakuNodeConf, appCallbacks: AppCallbacks = nil +): Future[Result[LogosDelivery, string]] {.async.} = + ## Builds the full stack from a kernel `WakuNodeConf`. + return await LogosDelivery.new( + LogosDeliveryConf( + kernelConf: conf, + messagingConf: MessagingClientConf(), + channelsConf: ReliableChannelManagerConf(), + ), + appCallbacks, + ) + +proc new*( + T: type LogosDelivery, + mode: WakuMode = WakuMode.Core, + preset: string = "", + messagingOverrides: MessagingClientConf = MessagingClientConf(), + channelsOverrides: ReliableChannelManagerConf = ReliableChannelManagerConf(), + appCallbacks: AppCallbacks = nil, +): Future[Result[LogosDelivery, string]] {.async.} = + ## Messaging entry point (app dev). Builds the full stack from preset, mode and overrides. + let conf = LogosDeliveryConf.init(mode, preset, messagingOverrides, channelsOverrides).valueOr: + return err("failed to synthesize configuration: " & error) + return await LogosDelivery.new(conf, appCallbacks) + proc start*(self: LogosDelivery): Future[Result[void, string]] {.async.} = ## Starts each layer bottom-up: transport first, then messaging, then channels. if self.waku.isNil(): @@ -137,11 +145,13 @@ proc start*(self: LogosDelivery): Future[Result[void, string]] {.async.} = proc stop*(self: LogosDelivery): Future[Result[void, string]] {.async.} = ## Stops in reverse order so higher layers drain before their dependencies. - await self.reliableChannelManager.stop() - await self.messagingClient.stop() - - (await self.waku.stop()).isOkOr: - return err("failed to stop Waku: " & error) + if not self.reliableChannelManager.isNil(): + await self.reliableChannelManager.stop() + if not self.messagingClient.isNil(): + await self.messagingClient.stop() + if not self.waku.isNil(): + (await self.waku.stop()).isOkOr: + return err("failed to stop Waku: " & error) return ok() diff --git a/logos_delivery/messaging/messaging_client.nim b/logos_delivery/messaging/messaging_client.nim index 32f5439e5..ef69c2d43 100644 --- a/logos_delivery/messaging/messaging_client.nim +++ b/logos_delivery/messaging/messaging_client.nim @@ -1,11 +1,17 @@ ## Messaging layer core: the `MessagingClient` type plus its construction and ## lifecycle. The public operations (subscribe / unsubscribe / send) live in ## `messaging/api.nim`. +import std/[options, net] import results, chronos +import confutils/defs +import libp2p/crypto/crypto +import logos_delivery/waku/common/logging +import logos_delivery/api/kernel_conf import chronicles import logos_delivery/api/messaging_client_api, logos_delivery/waku/waku, + logos_delivery/waku/factory/conf_builder/waku_conf_builder, logos_delivery/messaging/delivery_service/[recv_service, send_service] # Surfaces the messaging API interface (and its Message* events) to consumers. @@ -13,10 +19,45 @@ export messaging_client_api type MessagingClientConf* = object - ## Per-layer config object for the messaging API. - ## Kept intentionally minimal for now; the full config surface lands in a - ## follow-up PR. Today it only carries the p2p reliability toggle. - useP2PReliability*: bool + clusterId* {.name: "cluster-id".}: Option[uint16] ## Network cluster id. + numShardsInCluster* {.name: "num-shards-in-network".}: Option[uint16] + ## Number of shards in the cluster. + p2pTcpPort* {.name: "tcp-port".}: Option[Port] ## TCP listening port. + discv5UdpPort* {.name: "discv5-udp-port".}: Option[Port] ## discv5 UDP port. + websocketSupport* {.name: "websocket-support".}: Option[bool] + ## Enable the websocket transport. + websocketPort* {.name: "websocket-port".}: Option[Port] ## Websocket listening port. + quicSupport* {.name: "quic-support".}: Option[bool] ## Enable the QUIC transport. + quicPort* {.name: "quic-port".}: Option[Port] ## QUIC (UDP) listening port. + listenIpv4* {.name: "listen-address".}: Option[IpAddress] ## Inbound bind address. + maxMessageSize* {.name: "max-msg-size".}: Option[string] + ## Maximum accepted message size (e.g. "150 KiB"). + entryNodes* {.name: "entry-node".}: Option[seq[string]] + ## Bootstrap / connectivity nodes (enrtree or multiaddr). + ethRpcEndpoints* {.name: "rln-relay-eth-client-address".}: Option[seq[EthRpcUrl]] + ## Ethereum RPC endpoints (required for RLN validation); multiple for fail-over. + rlnContractAddress* {.name: "rln-relay-eth-contract-address".}: Option[string] + ## RLN contract address; when set, RLN validation is enabled. + rlnChainId* {.name: "rln-relay-chain-id".}: Option[uint] + ## Chain id the RLN contract is deployed on. + rlnEpochSizeSec* {.name: "rln-relay-epoch-sec".}: Option[uint] + ## RLN epoch size, in seconds. + reliabilityEnabled* {.name: "reliability".}: Option[bool] + ## Enable store-based send reliability. + store*: Option[bool] ## Enable the store protocol. + storenode* {.name: "storenode".}: Option[string] + storeMessageDbUrl* {.name: "store-message-db-url".}: Option[string] + ## Database connection URL for the store service's persistent storage. + storeMessageRetentionPolicy* {.name: "store-message-retention-policy".}: + Option[string] ## Store retention policy (e.g. "time:3600;size:1GB"). + storeMaxNumDbConnections* {.name: "store-max-num-db-connections".}: Option[int] + ## Maximum number of simultaneous store database connections. + logLevel* {.name: "log-level".}: Option[logging.LogLevel] + ## Process log level (TRACE..FATAL); applied by the kernel on node creation. + logFormat* {.name: "log-format".}: Option[logging.LogFormat] + ## Process log format (TEXT or JSON); applied by the kernel on node creation. + nodeKey* {.name: "nodekey".}: Option[crypto.PrivateKey] + ## P2P node private key (64-char hex): stable identity / peerId across restarts. MessagingClient* = ref object brokerCtx*: BrokerContext @@ -30,7 +71,8 @@ proc new*( ): Result[T, string] = ## The messaging layer chains onto Waku: it drives the underlying Waku kernel ## for transport while exposing its own send/recv API. - let sendService = ?SendService.new(conf.useP2PReliability, waku) + let reliability = conf.reliabilityEnabled.get(DefaultP2pReliability) + let sendService = ?SendService.new(reliability, waku) let recvService = RecvService.new(waku) return ok( T( diff --git a/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim b/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim index ffffceb5f..d312f620e 100644 --- a/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim +++ b/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim @@ -170,7 +170,6 @@ type WakuConfBuilder* = object relayShardedPeerManagement: Option[bool] relayServiceRatio: Option[string] circuitRelayClient: Option[bool] - p2pReliability: Option[bool] localStoragePath: Option[string] @@ -310,9 +309,6 @@ proc withRelayShardedPeerManagement*( ) = b.relayShardedPeerManagement = some(relayShardedPeerManagement) -proc withP2pReliability*(b: var WakuConfBuilder, p2pReliability: bool) = - b.p2pReliability = some(p2pReliability) - proc withLocalStoragePath*(b: var WakuConfBuilder, localStoragePath: string) = b.localStoragePath = some(localStoragePath) @@ -462,10 +458,6 @@ proc applyNetworkPresetConf(builder: var WakuConfBuilder) = checkSetPresetValueToField( builder.mix, networkPresetConf.mix, "Mix was provided alongside a network conf" ) - checkSetPresetValueToField( - builder.p2pReliability, networkPresetConf.p2pReliability, - "P2P Reliability was provided alongside a network conf", - ) # Process entry nodes from network config - classify and distribute if networkPresetConf.entryNodes.len > 0: @@ -841,7 +833,6 @@ proc build*( circuitRelayClient: builder.circuitRelayClient.get(DefaultCircuitRelayClient), staticNodes: builder.staticNodes, relayShardedPeerManagement: relayShardedPeerManagement, - p2pReliability: builder.p2pReliability.get(DefaultP2pReliability), wakuFlags: wakuFlags, localStoragePath: builder.localStoragePath.get(DefaultStoragePath), ) diff --git a/logos_delivery/waku/factory/waku_conf.nim b/logos_delivery/waku/factory/waku_conf.nim index 1ceb9ed09..db1a8aa28 100644 --- a/logos_delivery/waku/factory/waku_conf.nim +++ b/logos_delivery/waku/factory/waku_conf.nim @@ -155,8 +155,6 @@ type WakuConf* {.requiresInit.} = ref object # TODO: use proper type relayServiceRatio*: string - p2pReliability*: bool - localStoragePath*: string proc logConf*(conf: WakuConf) = diff --git a/tests/api/test_all.nim b/tests/api/test_all.nim index 56be19c27..108551bc2 100644 --- a/tests/api/test_all.nim +++ b/tests/api/test_all.nim @@ -3,6 +3,7 @@ import ./test_entry_nodes, ./test_node_conf, + ./test_conf, ./test_api_send, ./test_api_subscription, ./test_api_receive, diff --git a/tests/api/test_api_health.nim b/tests/api/test_api_health.nim index a972d5db6..e0d344958 100644 --- a/tests/api/test_api_health.nim +++ b/tests/api/test_api_health.nim @@ -91,9 +91,8 @@ suite "LM API health checking": serviceNode.wakuRelay.subscribe(DefaultShard, dummyHandler) lockNewGlobalBrokerContext: - var conf = defaultWakuNodeConf().valueOr: - raiseAssert error - conf.mode = Core + var conf = MessagingClientConf().toKernelConf(Core).valueOr: + raiseAssert error conf.listenAddress = parseIpAddress("0.0.0.0") conf.tcpPort = Port(0) conf.discv5UdpPort = Port(0) @@ -272,9 +271,8 @@ suite "LM API health checking": var edgeWaku: LogosDelivery lockNewGlobalBrokerContext: - var edgeConf = defaultWakuNodeConf().valueOr: - raiseAssert error - edgeConf.mode = Edge + var edgeConf = MessagingClientConf().toKernelConf(Edge).valueOr: + raiseAssert error edgeConf.listenAddress = parseIpAddress("0.0.0.0") edgeConf.tcpPort = Port(0) edgeConf.discv5UdpPort = Port(0) diff --git a/tests/api/test_api_receive.nim b/tests/api/test_api_receive.nim index 807f36014..51c2438a8 100644 --- a/tests/api/test_api_receive.nim +++ b/tests/api/test_api_receive.nim @@ -21,6 +21,7 @@ import ] import logos_delivery/waku/factory/waku_conf import tools/confutils/cli_args +import logos_delivery/api/messaging_conf const TestTimeout = chronos.seconds(60) @@ -82,15 +83,13 @@ proc waitForConnectionStatus( await EventConnectionStatusChange.dropListener(brokerCtx, handle) proc createApiNodeConf(numShards: uint16 = 1): WakuNodeConf = - var conf = defaultWakuNodeConf().valueOr: - raiseAssert error - conf.mode = cli_args.WakuMode.Core + var conf = MessagingClientConf().toKernelConf(messaging_conf.WakuMode.Core).valueOr: + raiseAssert error conf.listenAddress = parseIpAddress("0.0.0.0") conf.tcpPort = Port(0) conf.discv5UdpPort = Port(0) conf.clusterId = some(3'u16) conf.numShardsInNetwork = numShards - conf.reliabilityEnabled = some(true) conf.rest = false result = conf diff --git a/tests/api/test_api_send.nim b/tests/api/test_api_send.nim index df1522564..29921e7aa 100644 --- a/tests/api/test_api_send.nim +++ b/tests/api/test_api_send.nim @@ -8,6 +8,7 @@ import ../waku_archive/archive_utils import logos_delivery, logos_delivery/waku/[waku_node, waku_core, waku_relay/protocol] import logos_delivery/waku/factory/waku_conf import tools/confutils/cli_args +import logos_delivery/api/messaging_conf type SendEventOutcome {.pure.} = enum Sent @@ -119,16 +120,16 @@ proc validate( for requestId in manager.errorRequestIds: check requestId == expectedRequestId -proc createApiNodeConf(mode: cli_args.WakuMode = cli_args.WakuMode.Core): WakuNodeConf = - var conf = defaultWakuNodeConf().valueOr: - raiseAssert error - conf.mode = mode +proc createApiNodeConf( + mode: messaging_conf.WakuMode = messaging_conf.WakuMode.Core +): WakuNodeConf = + var conf = MessagingClientConf().toKernelConf(mode).valueOr: + raiseAssert error conf.listenAddress = parseIpAddress("0.0.0.0") conf.tcpPort = Port(0) conf.discv5UdpPort = Port(0) conf.clusterId = some(3'u16) conf.numShardsInNetwork = 1 - conf.reliabilityEnabled = some(true) conf.rest = false result = conf @@ -353,7 +354,7 @@ suite "Waku API - Send": ## connected to a lightpush-capable peer must deliver through lightpush. var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await LogosDelivery.new(createApiNodeConf(cli_args.WakuMode.Edge))).valueOr: + node = (await LogosDelivery.new(createApiNodeConf(messaging_conf.WakuMode.Edge))).valueOr: raiseAssert error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error @@ -388,7 +389,7 @@ suite "Waku API - Send": ## later retry must deliver the queued message. var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await LogosDelivery.new(createApiNodeConf(cli_args.WakuMode.Edge))).valueOr: + node = (await LogosDelivery.new(createApiNodeConf(messaging_conf.WakuMode.Edge))).valueOr: raiseAssert error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error @@ -479,7 +480,7 @@ suite "Waku API - Send": var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await LogosDelivery.new(createApiNodeConf(cli_args.WakuMode.Edge))).valueOr: + node = (await LogosDelivery.new(createApiNodeConf(messaging_conf.WakuMode.Edge))).valueOr: raiseAssert error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error diff --git a/tests/api/test_api_subscription.nim b/tests/api/test_api_subscription.nim index 359e55fbc..37566539a 100644 --- a/tests/api/test_api_subscription.nim +++ b/tests/api/test_api_subscription.nim @@ -18,6 +18,7 @@ import ] import logos_delivery/waku/factory/waku_conf import tools/confutils/cli_args +import logos_delivery/api/messaging_conf const TestTimeout = chronos.seconds(10) const NegativeTestTimeout = chronos.seconds(2) @@ -68,17 +69,15 @@ type TestNetwork = ref object publisherPeerInfo: RemotePeerInfo proc createApiNodeConf( - mode: cli_args.WakuMode = cli_args.WakuMode.Core, numShards: uint16 = 1 + mode: messaging_conf.WakuMode = messaging_conf.WakuMode.Core, numShards: uint16 = 1 ): WakuNodeConf = - var conf = defaultWakuNodeConf().valueOr: - raiseAssert error - conf.mode = mode + var conf = MessagingClientConf().toKernelConf(mode).valueOr: + raiseAssert error conf.listenAddress = parseIpAddress("0.0.0.0") conf.tcpPort = Port(0) conf.discv5UdpPort = Port(0) conf.clusterId = some(3'u16) conf.numShardsInNetwork = numShards - conf.reliabilityEnabled = some(true) conf.rest = false result = conf @@ -90,7 +89,7 @@ proc setupSubscriberNode(conf: WakuNodeConf): Future[LogosDelivery] {.async.} = return node proc setupNetwork( - numShards: uint16 = 1, mode: cli_args.WakuMode = cli_args.WakuMode.Core + numShards: uint16 = 1, mode: messaging_conf.WakuMode = messaging_conf.WakuMode.Core ): Future[TestNetwork] {.async.} = var net = TestNetwork() @@ -100,7 +99,7 @@ proc setupNetwork( "Failed to mount metadata" ) (await net.publisher.mountRelay()).expect("Failed to mount relay") - if mode == cli_args.WakuMode.Edge: + if mode == messaging_conf.WakuMode.Edge: await net.publisher.mountFilter() await net.publisher.mountLibp2pPing() await net.publisher.start() @@ -119,7 +118,7 @@ proc setupNetwork( "Failed to sub publisher" ) - if mode == cli_args.WakuMode.Edge: + if mode == messaging_conf.WakuMode.Edge: lockNewGlobalBrokerContext: net.meshBuddy = newTestWakuNode(generateSecp256k1Key()) net.meshBuddy.mountMetadata(3, toSeq(0'u16 ..< numShards)).expect( @@ -474,7 +473,7 @@ suite "Messaging API, SubscriptionManager": await verifyNetworkState(activeSubs) asyncTest "Subscription API, edge node subscribe and receive message": - let net = await setupNetwork(1, cli_args.WakuMode.Edge) + let net = await setupNetwork(1, messaging_conf.WakuMode.Edge) defer: await net.teardown() @@ -496,7 +495,7 @@ suite "Messaging API, SubscriptionManager": check eventManager.receivedMessages[0].contentTopic == testTopic asyncTest "Subscription API, edge node ignores unsubscribed content topics": - let net = await setupNetwork(1, cli_args.WakuMode.Edge) + let net = await setupNetwork(1, messaging_conf.WakuMode.Edge) defer: await net.teardown() @@ -518,7 +517,7 @@ suite "Messaging API, SubscriptionManager": check eventManager.receivedMessages.len == 0 asyncTest "Subscription API, edge node unsubscribe stops message receipt": - let net = await setupNetwork(1, cli_args.WakuMode.Edge) + let net = await setupNetwork(1, messaging_conf.WakuMode.Edge) defer: await net.teardown() @@ -543,7 +542,7 @@ suite "Messaging API, SubscriptionManager": check eventManager.receivedMessages.len == 0 asyncTest "Subscription API, edge node overlapping topics isolation": - let net = await setupNetwork(1, cli_args.WakuMode.Edge) + let net = await setupNetwork(1, messaging_conf.WakuMode.Edge) defer: await net.teardown() @@ -572,7 +571,7 @@ suite "Messaging API, SubscriptionManager": check eventManager.receivedMessages[0].contentTopic == topicB asyncTest "Subscription API, edge node resubscribe after unsubscribe": - let net = await setupNetwork(1, cli_args.WakuMode.Edge) + let net = await setupNetwork(1, messaging_conf.WakuMode.Edge) defer: await net.teardown() @@ -657,7 +656,7 @@ suite "Messaging API, SubscriptionManager": await meshBuddy.connectToNodes(@[publisherPeerInfo]) - let conf = createApiNodeConf(cli_args.WakuMode.Edge, numShards) + let conf = createApiNodeConf(messaging_conf.WakuMode.Edge, numShards) var subscriber: LogosDelivery lockNewGlobalBrokerContext: subscriber = @@ -785,7 +784,7 @@ suite "Messaging API, SubscriptionManager": await meshBuddy.connectToNodes(@[publisherPeerInfo]) await sparePeer.connectToNodes(@[publisherPeerInfo]) - let conf = createApiNodeConf(cli_args.WakuMode.Edge, numShards) + let conf = createApiNodeConf(messaging_conf.WakuMode.Edge, numShards) var subscriber: LogosDelivery lockNewGlobalBrokerContext: subscriber = diff --git a/tests/api/test_conf.nim b/tests/api/test_conf.nim new file mode 100644 index 000000000..dd90477d4 --- /dev/null +++ b/tests/api/test_conf.nim @@ -0,0 +1,261 @@ +{.used.} + +import std/[options, net], results, chronos, testutils/unittests +import brokers/broker_context +import logos_delivery +import logos_delivery/api/logos_delivery_conf_json +import logos_delivery/waku/factory/[waku_conf, networks_config] +import logos_delivery/waku/common/logging + +suite "MessagingClientConf - mode expansion (toKernelConf)": + test "Core mode enables relay + service protocols": + let kc = MessagingClientConf().toKernelConf(WakuMode.Core).valueOr: + raiseAssert error + check: + kc.relay == true + kc.filter == true + kc.lightpush == true + kc.discv5Discovery == some(true) + kc.peerExchange == true + kc.rendezvous == true + + test "Edge mode is client-only (no relay/filter/lightpush/store)": + let kc = MessagingClientConf().toKernelConf(WakuMode.Edge).valueOr: + raiseAssert error + check: + kc.relay == false + kc.filter == false + kc.lightpush == false + kc.store == false + kc.peerExchange == true + kc.discv5Discovery == some(true) # discovery stays on; mode does not force it off + +suite "MessagingClientConf - field mapping + transport policy": + test "set fields are written to their kernel counterparts": + let mc = MessagingClientConf( + clusterId: some(3'u16), + numShardsInCluster: some(4'u16), + maxMessageSize: some("150KiB"), + ) + let kc = mc.toKernelConf(WakuMode.Core).valueOr: + raiseAssert error + check: + kc.clusterId == some(3'u16) + kc.numShardsInNetwork == 4 + kc.maxMessageSize == "150KiB" + + test "messaging transport defaults: ephemeral ports, websocket off, quic on": + let kc = MessagingClientConf().toKernelConf(WakuMode.Core).valueOr: + raiseAssert error + check: + kc.tcpPort == Port(0) + kc.discv5UdpPort == Port(0) + kc.websocketSupport == false + kc.quicSupport == true + + test "explicit transport overrides win": + let mc = MessagingClientConf( + p2pTcpPort: some(Port(1234)), + websocketSupport: some(true), + quicSupport: some(false), + ) + let kc = mc.toKernelConf(WakuMode.Core).valueOr: + raiseAssert error + check: + kc.tcpPort == Port(1234) + kc.websocketSupport == true + kc.quicSupport == false + +suite "MessagingClientConf - preset resolution": + test "resolvePreset lifts only messaging-exclusive fields, not kernel-mirrored ones": + let mc = resolvePreset("twn").valueOr: + raiseAssert error + check: + mc.reliabilityEnabled.isSome() + mc.clusterId.isNone() + mc.maxMessageSize.isNone() + + test "resolvePreset does not lift entryNodes": + let mc = resolvePreset("logostest").valueOr: + raiseAssert error + check mc.entryNodes.isNone() + + test "empty preset resolves to an empty config": + let mc = resolvePreset("").valueOr: + raiseAssert error + check: + mc.clusterId.isNone() + mc.maxMessageSize.isNone() + + test "a messaging override of a kernel-mirrored field wins over the preset": + let presetConf = resolvePreset("logos.dev").valueOr: + raiseAssert error + let merged = merge(presetConf, MessagingClientConf(numShardsInCluster: some(1'u16))) + var kernelConf = toKernelConf(merged, WakuMode.Core).valueOr: + raiseAssert error + kernelConf.preset = "logos.dev" + let wakuConf = kernelConf.toWakuConf().valueOr: + raiseAssert error + check: + wakuConf.shardingConf.kind == AutoSharding + wakuConf.shardingConf.numShardsInCluster == 1 + +suite "MessagingClientConf - merge (override wins)": + test "a set override field wins; unset keeps the base": + let base = MessagingClientConf(clusterId: some(1'u16), maxMessageSize: some("1MB")) + let overrides = MessagingClientConf(clusterId: some(2'u16)) + let mc = merge(base, overrides) + check: + mc.clusterId == some(2'u16) # override wins + mc.maxMessageSize == some("1MB") # base preserved + +suite "parseLogosDeliveryConf - JSON parsing": + test "empty object -> Core (default), empty preset, empty overrides": + let lc = parseLogosDeliveryConf("{}").valueOr: + raiseAssert error + check: + lc.kernelConf.relay == true # Core enables relay + lc.kernelConf.preset == "" + lc.messagingConf.clusterId.isNone() + + test "mode + preset are parsed": + let lc = parseLogosDeliveryConf("""{"mode": "Edge", "preset": "logostest"}""").valueOr: + raiseAssert error + check: + lc.kernelConf.preset == "logostest" + lc.kernelConf.relay == false # Edge disables relay + + test "messagingOverrides parsed into the partial": + let lc = parseLogosDeliveryConf( + """{"mode": "Core", "messagingOverrides": {"clusterId": 7, "reliabilityEnabled": true}}""" + ).valueOr: + raiseAssert error + check: + lc.messagingConf.clusterId == some(7'u16) + lc.messagingConf.reliabilityEnabled == some(true) + + test "channelsOverrides parsed into the partial": + let lc = parseLogosDeliveryConf( + """{"channelsOverrides": {"rateLimitEnabled": true, "sdsMaxRetransmissions": 9}}""" + ).valueOr: + raiseAssert error + check: + lc.channelsConf.rateLimitEnabled == some(true) + lc.channelsConf.sdsMaxRetransmissions == some(9) + + test "invalid mode is rejected (Core or Edge only)": + check parseLogosDeliveryConf("""{"mode": "bogus"}""").isErr() + check parseLogosDeliveryConf("""{"mode": "noMode"}""").isErr() + check parseLogosDeliveryConf("""{"mode": ""}""").isErr() + + test "invalid JSON is rejected": + check parseLogosDeliveryConf("{ not json }").isErr() + + test "unknown top-level keys are rejected": + check parseLogosDeliveryConf("""{"logLevel": "INFO", "mode": "Core"}""").isErr() + + test "override keys accept CLI switch names": + let lc = parseLogosDeliveryConf( + """{"messagingOverrides": {"cluster-id": 7, "reliability": true, "tcp-port": 1234}}""" + ).valueOr: + raiseAssert error + check: + lc.messagingConf.clusterId == some(7'u16) + lc.messagingConf.reliabilityEnabled == some(true) + lc.messagingConf.p2pTcpPort == some(Port(1234)) + + test "unknown keys inside an overrides body are rejected": + check parseLogosDeliveryConf("""{"messagingOverrides": {"bogusKey": 1}}""").isErr() + check parseLogosDeliveryConf("""{"channelsOverrides": {"bogusKey": 1}}""").isErr() + + test "a field set via both its name and its switch name is rejected": + check parseLogosDeliveryConf( + """{"messagingOverrides": {"clusterId": 5, "cluster-id": 5}}""" + ) + .isErr() + + test "logLevel and nodeKey parse via switch names and map to the kernel": + let lc = parseLogosDeliveryConf( + """{"messagingOverrides": {"log-level": "DEBUG", "log-format": "JSON", "nodekey": "0d714a1fada214dead6dc9c7274581ec20ff292451866e7d6d677dc818e8ccd2"}}""" + ).valueOr: + raiseAssert error + check: + lc.kernelConf.logLevel == logging.LogLevel.DEBUG + lc.kernelConf.logFormat == logging.LogFormat.JSON + lc.kernelConf.nodekey.isSome() + + test "a null value leaves the field unset": + let lc = parseLogosDeliveryConf( + """{"messagingOverrides": {"store": null, "clusterId": 7}}""" + ).valueOr: + raiseAssert error + check: + lc.messagingConf.store.isNone() + lc.messagingConf.clusterId == some(7'u16) + + test "an invalid Ethereum RPC URL is rejected at parse time": + check parseLogosDeliveryConf( + """{"messagingOverrides": {"rln-relay-eth-client-address": ["ws://node:8546"]}}""" + ) + .isErr() + let lc = parseLogosDeliveryConf( + """{"messagingOverrides": {"rln-relay-eth-client-address": ["http://localhost:8540/"]}}""" + ).valueOr: + raiseAssert error + check lc.kernelConf.ethClientUrls.len == 1 + + test "store backend fields parse and map to the kernel": + let lc = parseLogosDeliveryConf( + """{"messagingOverrides": {"store": true, "store-message-db-url": "sqlite://test.db", "store-message-retention-policy": "time:3600", "store-max-num-db-connections": 7}}""" + ).valueOr: + raiseAssert error + check: + lc.kernelConf.store == true + lc.kernelConf.storeMessageDbUrl == "sqlite://test.db" + lc.kernelConf.storeMessageRetentionPolicy == "time:3600" + lc.kernelConf.storeMaxNumDbConnections == 7 + +suite "LogosDelivery.new - construction (the app-dev entry)": + asyncTest "builds the full messaging stack from mode + overrides": + var node: LogosDelivery + lockNewGlobalBrokerContext: + node = ( + await LogosDelivery.new( + WakuMode.Core, + "", + MessagingClientConf( + clusterId: some(3'u16), + numShardsInCluster: some(1'u16), + listenIpv4: some(parseIpAddress("0.0.0.0")), + reliabilityEnabled: some(true), + ), + ) + ).valueOr: + raiseAssert error + check: + not node.waku.isNil() + not node.messagingClient.isNil() + not node.reliableChannelManager.isNil() + (await node.stop()).expect("stop") + + asyncTest "builds from a network preset": + var node: LogosDelivery + lockNewGlobalBrokerContext: + node = ( + await LogosDelivery.new( + WakuMode.Core, + "logostest", + MessagingClientConf(listenIpv4: some(parseIpAddress("0.0.0.0"))), + ) + ).valueOr: + raiseAssert error + check not node.waku.isNil() + (await node.stop()).expect("stop") + +suite "MessagingClientConf - store override": + test "store opt-in overrides the mode default; protocol flags follow the mode": + let kc = MessagingClientConf(store: some(true)).toKernelConf(WakuMode.Edge).valueOr: + raiseAssert error + check: + kc.store == true # Edge defaults store off; the explicit opt-in wins + kc.relay == false # protocols are owned by the mode, not overridable diff --git a/tests/api/test_node_conf.nim b/tests/api/test_node_conf.nim index 2716ffed1..6048a5951 100644 --- a/tests/api/test_node_conf.nim +++ b/tests/api/test_node_conf.nim @@ -1,196 +1,10 @@ {.used.} -import std/[options, strutils], results, stint, testutils/unittests -import json_serialization, confutils, confutils/std/net +import std/options, results, testutils/unittests import tools/confutils/cli_args, - tools/confutils/conf_from_json, - logos_delivery/api/api_conf, logos_delivery/waku/factory/waku_conf, - logos_delivery/waku/factory/networks_config, - logos_delivery/waku/factory/conf_builder/conf_builder, - logos_delivery/waku/common/logging - -suite "WakuNodeConf - mode-driven toWakuConf": - test "Core mode enables service protocols": - ## Given - var conf = defaultWakuNodeConf().valueOr: - raiseAssert error - conf.mode = Core - conf.clusterId = some(1'u16) - - ## When - let wakuConfRes = conf.toWakuConf() - - ## Then - require wakuConfRes.isOk() - let wakuConf = wakuConfRes.get() - require wakuConf.validate().isOk() - check: - wakuConf.relay == true - wakuConf.lightPush == true - wakuConf.peerExchangeService == true - wakuConf.rendezvous == true - wakuConf.clusterId == 1 - - test "Edge mode disables service protocols": - ## Given - var conf = defaultWakuNodeConf().valueOr: - raiseAssert error - conf.mode = Edge - conf.clusterId = some(1'u16) - - ## When - let wakuConfRes = conf.toWakuConf() - - ## Then - require wakuConfRes.isOk() - let wakuConf = wakuConfRes.get() - require wakuConf.validate().isOk() - check: - wakuConf.relay == false - wakuConf.lightPush == false - wakuConf.filterServiceConf.isSome() == false - wakuConf.storeServiceConf.isSome() == false - wakuConf.peerExchangeService == true - - test "noMode uses explicit CLI flags as-is": - ## Given - var conf = defaultWakuNodeConf().valueOr: - raiseAssert error - conf.mode = cli_args.WakuMode.noMode - conf.relay = true - conf.lightpush = false - conf.clusterId = some(5'u16) - - ## When - let wakuConfRes = conf.toWakuConf() - - ## Then - require wakuConfRes.isOk() - let wakuConf = wakuConfRes.get() - require wakuConf.validate().isOk() - check: - wakuConf.relay == true - wakuConf.lightPush == false - wakuConf.clusterId == 5 - - test "Core mode overrides individual protocol flags": - ## Given - user sets relay=false but mode=Core should override - var conf = defaultWakuNodeConf().valueOr: - raiseAssert error - conf.mode = Core - conf.relay = false # will be overridden by Core mode - - ## When - let wakuConfRes = conf.toWakuConf() - - ## Then - require wakuConfRes.isOk() - let wakuConf = wakuConfRes.get() - require wakuConf.validate().isOk() - check: - wakuConf.relay == true # mode overrides - -suite "WakuNodeConf - JSON parsing with fieldPairs": - test "Empty JSON produces valid default conf": - ## Given / When - let confRes = parseNodeConfFromJson("{}") - - ## Then - require confRes.isOk() - let conf = confRes.get() - check: - conf.mode == cli_args.WakuMode.noMode - conf.clusterId.isNone() - conf.logLevel == logging.LogLevel.INFO - - test "JSON with mode and clusterId": - ## Given / When - let confRes = parseNodeConfFromJson("""{"mode": "Core", "clusterId": 42}""") - - ## Then - require confRes.isOk() - let conf = confRes.get() - check: - conf.mode == Core - conf.clusterId == some(42'u16) - - test "JSON with Edge mode": - ## Given / When - let confRes = parseNodeConfFromJson("""{"mode": "Edge"}""") - - ## Then - require confRes.isOk() - let conf = confRes.get() - check: - conf.mode == Edge - - test "JSON with logLevel": - ## Given / When - let confRes = parseNodeConfFromJson("""{"logLevel": "DEBUG"}""") - - ## Then - require confRes.isOk() - let conf = confRes.get() - check: - conf.logLevel == logging.LogLevel.DEBUG - - test "JSON with sharding config": - ## Given / When - let confRes = - parseNodeConfFromJson("""{"clusterId": 99, "numShardsInNetwork": 16}""") - - ## Then - require confRes.isOk() - let conf = confRes.get() - check: - conf.clusterId == some(99'u16) - conf.numShardsInNetwork == 16 - - test "JSON with unknown fields is rejected": - ## Given / When - let confRes = parseNodeConfFromJson("""{"unknownField": true, "clusterId": 5}""") - - ## Then - the parser rejects unrecognized config keys - check confRes.isErr() - - test "JSON config keys accepts cli_args pragma names": - ## Given / When - let confRes = parseNodeConfFromJson( - """{"cluster-id": 7, "reliability": true, "staticnode": ["/ip4/127.0.0.1/tcp/60000"]}""" - ) - - ## Then - require confRes.isOk() - let conf = confRes.get() - check: - conf.clusterId == some(7'u16) - conf.reliabilityEnabled == some(true) - conf.staticNodes == @["/ip4/127.0.0.1/tcp/60000"] - - test "JSON config accepts field-name form": - ## Given / When - let confRes = parseNodeConfFromJson("""{"reliabilityEnabled": true}""") - - ## Then - require confRes.isOk() - check confRes.get().reliabilityEnabled == some(true) - - test "JSON config rejects option set via both field name and cli name": - ## Given / When - let confRes = parseNodeConfFromJson("""{"clusterId": 5, "cluster-id": 5}""") - - ## Then - require confRes.isErr() - check "set twice" in confRes.error - - test "Invalid JSON syntax returns error": - ## Given / When - let confRes = parseNodeConfFromJson("{ not valid json }") - - ## Then - check confRes.isErr() + logos_delivery/waku/factory/networks_config suite "WakuNodeConf - preset integration": test "TWN preset applies TheWakuNetworkConf": @@ -260,6 +74,25 @@ suite "WakuNodeConf - preset integration": wakuConf.shardingConf.numShardsInCluster == 1 wakuConf.rlnRelayConf.isNone() + test "Invalid preset returns error": + ## Given + var conf = defaultWakuNodeConf().valueOr: + raiseAssert error + conf.preset = "status.prod" + + ## When + let wakuConfRes = conf.toWakuConf() + + ## Then + require wakuConfRes.isOk() + let wakuConf = wakuConfRes.get() + require wakuConf.validate().isOk() + check: + wakuConf.clusterId == 16 + wakuConf.shardingConf.kind == AutoSharding + wakuConf.shardingConf.numShardsInCluster == 1 + wakuConf.rlnRelayConf.isNone() + test "Invalid preset returns error": ## Given var conf = defaultWakuNodeConf().valueOr: @@ -271,202 +104,3 @@ suite "WakuNodeConf - preset integration": ## Then check wakuConfRes.isErr() - -suite "WakuNodeConf JSON -> WakuConf integration": - test "Core mode JSON config produces valid WakuConf": - ## Given - let confRes = parseNodeConfFromJson( - """{"mode": "Core", "clusterId": 55, "numShardsInNetwork": 6}""" - ) - require confRes.isOk() - let conf = confRes.get() - - ## When - let wakuConfRes = conf.toWakuConf() - - ## Then - require wakuConfRes.isOk() - let wakuConf = wakuConfRes.get() - require wakuConf.validate().isOk() - check: - wakuConf.relay == true - wakuConf.lightPush == true - wakuConf.peerExchangeService == true - wakuConf.clusterId == 55 - wakuConf.shardingConf.numShardsInCluster == 6 - - test "Edge mode JSON config produces valid WakuConf": - ## Given - let confRes = parseNodeConfFromJson("""{"mode": "Edge", "clusterId": 1}""") - require confRes.isOk() - let conf = confRes.get() - - ## When - let wakuConfRes = conf.toWakuConf() - - ## Then - require wakuConfRes.isOk() - let wakuConf = wakuConfRes.get() - require wakuConf.validate().isOk() - check: - wakuConf.relay == false - wakuConf.lightPush == false - wakuConf.peerExchangeService == true - - test "JSON with preset produces valid WakuConf": - ## Given - let confRes = parseNodeConfFromJson("""{"mode": "Core", "preset": "logosdev"}""") - require confRes.isOk() - let conf = confRes.get() - - ## When - let wakuConfRes = conf.toWakuConf() - - ## Then - require wakuConfRes.isOk() - let wakuConf = wakuConfRes.get() - require wakuConf.validate().isOk() - check: - wakuConf.clusterId == 2 - wakuConf.relay == true - - test "JSON with static nodes": - ## Given - let confRes = parseNodeConfFromJson( - """{"mode": "Core", "clusterId": 42, "staticnodes": ["/ip4/127.0.0.1/tcp/60000/p2p/16Uuu2HBmAcHvhLqQKwSSbX6BG5JLWUDRcaLVrehUVqpw7fz1hbYc"]}""" - ) - require confRes.isOk() - let conf = confRes.get() - - ## When - let wakuConfRes = conf.toWakuConf() - - ## Then - require wakuConfRes.isOk() - let wakuConf = wakuConfRes.get() - require wakuConf.validate().isOk() - check: - wakuConf.staticNodes.len == 1 - - test "JSON with max message size": - ## Given - let confRes = - parseNodeConfFromJson("""{"clusterId": 42, "maxMessageSize": "100KiB"}""") - require confRes.isOk() - let conf = confRes.get() - - ## When - let wakuConfRes = conf.toWakuConf() - - ## Then - require wakuConfRes.isOk() - let wakuConf = wakuConfRes.get() - require wakuConf.validate().isOk() - check: - wakuConf.maxMessageSizeBytes == 100'u64 * 1024'u64 - -# ---- Deprecated NodeConfig tests (kept for backward compatibility) ---- - -{.push warning[Deprecated]: off.} - -import logos_delivery/api/api_conf - -suite "NodeConfig (deprecated) - toWakuConf": - test "Minimal configuration": - let nodeConfig = NodeConfig.init(ethRpcEndpoints = @["http://someaddress"]) - let wakuConfRes = api_conf.toWakuConf(nodeConfig) - let wakuConf = wakuConfRes.valueOr: - raiseAssert error - wakuConf.validate().isOkOr: - raiseAssert error - check: - wakuConf.clusterId == 1 - wakuConf.shardingConf.numShardsInCluster == 8 - wakuConf.staticNodes.len == 0 - - test "Edge mode configuration": - let protocolsConfig = ProtocolsConfig.init(entryNodes = @[], clusterId = 1) - let nodeConfig = - NodeConfig.init(mode = api_conf.WakuMode.Edge, protocolsConfig = protocolsConfig) - let wakuConfRes = api_conf.toWakuConf(nodeConfig) - require wakuConfRes.isOk() - let wakuConf = wakuConfRes.get() - require wakuConf.validate().isOk() - check: - wakuConf.relay == false - wakuConf.lightPush == false - wakuConf.peerExchangeService == true - - test "Core mode configuration": - let protocolsConfig = ProtocolsConfig.init(entryNodes = @[], clusterId = 1) - let nodeConfig = - NodeConfig.init(mode = api_conf.WakuMode.Core, protocolsConfig = protocolsConfig) - let wakuConfRes = api_conf.toWakuConf(nodeConfig) - require wakuConfRes.isOk() - let wakuConf = wakuConfRes.get() - require wakuConf.validate().isOk() - check: - wakuConf.relay == true - wakuConf.lightPush == true - wakuConf.peerExchangeService == true - -{.pop.} - -suite "WakuConfBuilder - store retention policies": - test "Multiple retention policies": - ## Given - var b = WakuConfBuilder.init() - b.withP2pTcpPort(0'u16) - b.storeServiceConf.withEnabled(true) - b.storeServiceConf.withDbUrl("sqlite://test.db") - b.storeServiceConf.withRetentionPolicies( - "time:86400 ; capacity:10000; size : 50GB" - ) - - ## When - let wakuConf = b.build().valueOr: - raiseAssert error - - ## Then - require wakuConf.storeServiceConf.isSome() - let storeConf = wakuConf.storeServiceConf.get() - check storeConf.retentionPolicies == @["time:86400", "capacity:10000", "size:50GB"] - - test "Duplicated retention policies returns error": - ## Given - var b = WakuConfBuilder.init() - b.storeServiceConf.withEnabled(true) - b.storeServiceConf.withDbUrl("sqlite://test.db") - b.storeServiceConf.withRetentionPolicies("time:86400;time:800;capacity:10000") - - ## When - let wakuConfRes = b.build() - check wakuConfRes.isErr() - check wakuConfRes.error.contains("duplicated retention policy type") - - test "Incorrect retention policy type returns error": - ## Given - var b = WakuConfBuilder.init() - b.storeServiceConf.withEnabled(true) - b.storeServiceConf.withDbUrl("sqlite://test.db") - b.storeServiceConf.withRetentionPolicies("capaity:10000") - - ## When - let wakuConfRes = b.build() - - ## Then - check wakuConfRes.isErr() - check wakuConfRes.error.contains("unknown retention policy type") - - test "Store disabled - no retention policy applied": - ## Given - var b = WakuConfBuilder.init() - b.withP2pTcpPort(0'u16) - # storeServiceConf not enabled - - ## When - let wakuConf = b.build().valueOr: - raiseAssert error - - ## Then - check wakuConf.storeServiceConf.isNone() diff --git a/tests/channels/test_reliable_channel_send_receive.nim b/tests/channels/test_reliable_channel_send_receive.nim index 70a8e2702..a56cb0522 100644 --- a/tests/channels/test_reliable_channel_send_receive.nim +++ b/tests/channels/test_reliable_channel_send_receive.nim @@ -13,6 +13,7 @@ import logos_delivery/waku/factory/waku_conf import logos_delivery/api/events/messaging_client_events as waku_message_events import logos_delivery/api/messaging_client_api import tools/confutils/cli_args +import logos_delivery/api/messaging_conf import logos_delivery/channels/reliable_channel_manager import logos_delivery/channels/encryption/noop_encryption @@ -27,15 +28,13 @@ import snapshot_codec const TestTimeout = chronos.seconds(15) proc createApiNodeConf(): WakuNodeConf = - var conf = defaultWakuNodeConf().valueOr: - raiseAssert error - conf.mode = cli_args.WakuMode.Core + var conf = MessagingClientConf().toKernelConf(messaging_conf.WakuMode.Core).valueOr: + raiseAssert error conf.listenAddress = parseIpAddress("0.0.0.0") conf.tcpPort = Port(0) conf.discv5UdpPort = Port(0) conf.clusterId = some(3'u16) conf.numShardsInNetwork = 1 - conf.reliabilityEnabled = some(true) conf.rest = false return conf @@ -166,7 +165,7 @@ suite "Reliable Channel - ingress": suite "Reliable Channel - send state machine": asyncTest "MessageSentEvent finalises the channelReqId as Sent": ## Drives the real send pipeline (`send` -> segmentation -> SDS -> - ## rate_limit -> encrypt -> dispatch) via a fake `SendHandler` that + ## rate_limit -> encrypt -> dispatch) via a fake `MessagingSend` provider that ## returns a canned `RequestId` instead of hitting the network. ## Emitting the delivery-layer `MessageSentEvent` must drive the ## channel-level state machine through `Confirmed` and produce a @@ -234,7 +233,7 @@ suite "Reliable Channel - send state machine": ## Two `send()` calls -> two independent `channelReqId`s, each with ## one segment under the current segmentation skeleton ## (`performSegmentation` always emits exactly one segment). The - ## fake `SendHandler` returns distinct `messagingReqId`s; finalising + ## fake `MessagingSend` provider returns distinct `messagingReqId`s; finalising ## the first emits `ChannelMessageSentEvent` for its `channelReqId`, ## finalising the second as a failure emits `ChannelMessageErrorEvent` ## for the other. @@ -329,7 +328,7 @@ suite "Reliable Channel - send state machine": ## `channelReqId` cannot be produced through the real pipeline. ## Implement once segmentation does real chunking: send a payload ## larger than `DefaultSegmentSizeBytes`, capture the N - ## `messagingReqId`s from a fake `SendHandler`, finalise some, and + ## `messagingReqId`s from a fake `MessagingSend` provider, finalise some, and ## assert prune only fires once every sibling is final. skip() diff --git a/tests/node/test_wakunode_health_monitor.nim b/tests/node/test_wakunode_health_monitor.nim index c6f9d34e6..af90a9be0 100644 --- a/tests/node/test_wakunode_health_monitor.nim +++ b/tests/node/test_wakunode_health_monitor.nim @@ -238,7 +238,7 @@ suite "Health Monitor - events": node: nodeA, conf: defaultTestWakuConf(), stateInfo: WakuStateInfo.init(nodeA) ) let ds = MessagingClient - .new(MessagingClientConf(useP2PReliability: false), waku) + .new(MessagingClientConf(reliabilityEnabled: some(false)), waku) .expect("Failed to create MessagingClient") ds.start().expect("Failed to start MessagingClient") @@ -349,7 +349,7 @@ suite "Health Monitor - events": node: nodeA, conf: defaultTestWakuConf(), stateInfo: WakuStateInfo.init(nodeA) ) let ds = MessagingClient - .new(MessagingClientConf(useP2PReliability: false), waku) + .new(MessagingClientConf(reliabilityEnabled: some(false)), waku) .expect("Failed to create MessagingClient") ds.start().expect("Failed to start MessagingClient") let subMgr = nodeA.subscriptionManager diff --git a/tests/test_waku.nim b/tests/test_waku.nim index faf1575eb..772387674 100644 --- a/tests/test_waku.nim +++ b/tests/test_waku.nim @@ -12,9 +12,8 @@ import logos_delivery/waku/factory/conf_builder/conf_builder suite "LogosDelivery API - Create node": asyncTest "Create node with minimal configuration": ## Given - var nodeConf = defaultWakuNodeConf().valueOr: - raiseAssert "defaultWakuNodeConf failed: " & error - nodeConf.mode = Core + var nodeConf = MessagingClientConf().toKernelConf(Core).valueOr: + raiseAssert "toKernelConf failed: " & error nodeConf.clusterId = some(3'u16) nodeConf.rest = false @@ -32,9 +31,8 @@ suite "LogosDelivery API - Create node": asyncTest "Create node with full configuration": ## Given - var nodeConf = defaultWakuNodeConf().valueOr: - raiseAssert "defaultWakuNodeConf failed: " & error - nodeConf.mode = Core + var nodeConf = MessagingClientConf().toKernelConf(Core).valueOr: + raiseAssert "toKernelConf failed: " & error nodeConf.clusterId = some(99'u16) nodeConf.rest = false nodeConf.numShardsInNetwork = 16 @@ -64,9 +62,8 @@ suite "LogosDelivery API - Create node": asyncTest "Create node with mixed entry nodes (enrtree, multiaddr)": ## Given - var nodeConf = defaultWakuNodeConf().valueOr: - raiseAssert "defaultWakuNodeConf failed: " & error - nodeConf.mode = Core + var nodeConf = MessagingClientConf().toKernelConf(Core).valueOr: + raiseAssert "toKernelConf failed: " & error nodeConf.clusterId = some(42'u16) nodeConf.rest = false nodeConf.entryNodes = @[ diff --git a/tools/confutils/cli_args.nim b/tools/confutils/cli_args.nim index f781133af..aa7b6cb1b 100644 --- a/tools/confutils/cli_args.nim +++ b/tools/confutils/cli_args.nim @@ -60,11 +60,6 @@ type StartUpCommand* = enum noCommand # default, runs waku generateRlnKeystore # generates a new RLN keystore -type WakuMode* {.pure.} = enum - noMode # default - use explicit CLI flags as-is - Core # full service node - Edge # client-only node - type WakuNodeConf* = object configFile* {. desc: "Loads configuration from a TOML file (cmd-line parameters take precedence)", @@ -166,13 +161,6 @@ type WakuNodeConf* = object .}: seq[ProtectedShard] ## General node config - mode* {. - desc: - "Node operation mode. 'Core' enables relay+service protocols. 'Edge' enables client-only protocols. Default: explicit CLI flags used.", - defaultValue: WakuMode.noMode, - name: "mode" - .}: WakuMode - preset* {. desc: "Network preset to use. 'twn' is The RLN-protected Waku Network (cluster 1). 'logos.dev' is the Logos Dev Network (cluster 2). 'logos.test' is the Logos Test Network (cluster 2). 'status.prod' is the Status Production Network (cluster 16, RLN off, auto-sharding with 1 shard). Overrides other values.", @@ -442,7 +430,7 @@ hence would have reachability issues.""", ## Filter config filter* {. - desc: "Enable filter protocol: true|false", defaultValue: false, name: "filter" + desc: "Enable filter protocol: true|false", defaultValue: true, name: "filter" .}: bool filternode* {. @@ -474,7 +462,7 @@ hence would have reachability issues.""", ## Lightpush config lightpush* {. desc: "Enable lightpush protocol: true|false", - defaultValue: false, + defaultValue: true, name: "lightpush" .}: bool @@ -484,16 +472,6 @@ hence would have reachability issues.""", name: "lightpushnode" .}: string - ## Reliability config - # Option-typed; desc states the default since the CLI can't auto-show it for none(). - reliabilityEnabled* {. - desc: - """Adds an extra effort in the delivery/reception of messages by leveraging store-v3 requests, with the drawback of consuming some more bandwidth. Default is """ & - $DefaultP2pReliability & ".", - defaultValue: none(bool), - name: "reliability" - .}: Option[bool] - ## REST HTTP config rest* {. desc: "Enable Waku REST HTTP server: true|false", @@ -577,12 +555,9 @@ hence would have reachability issues.""", .}: string ## Discovery v5 config - # Option-typed; desc states the default since the CLI can't auto-show it for none(). discv5Discovery* {. - desc: - "Enable discovering nodes via Node Discovery v5. Default is " & - $DefaultDiscv5Enabled & ".", - defaultValue: none(bool), + desc: "Enable discovering nodes via Node Discovery v5. Default is true.", + defaultValue: some(true), name: "discv5-discovery" .}: Option[bool] @@ -977,7 +952,7 @@ proc toKeystoreGeneratorConf*(n: WakuNodeConf): RlnKeystoreGeneratorConf = credPassword: n.rlnRelayCredPassword, ) -proc toNetworkPresetConf( +proc toNetworkPresetConf*( preset: string, clusterId: Option[uint16] ): ConfResult[Option[NetworkPresetConf]] = var lcPreset = toLowerAscii(preset) @@ -1146,8 +1121,6 @@ proc toWakuConf*(n: WakuNodeConf): ConfResult[WakuConf] = b.filterServiceConf.withMaxCriteria(n.filterMaxCriteria) b.withLightPush(n.lightpush) - if n.reliabilityEnabled.isSome(): - b.withP2pReliability(n.reliabilityEnabled.get()) b.restServerConf.withEnabled(n.rest) b.restServerConf.withListenAddress(n.restAddress) @@ -1190,6 +1163,12 @@ proc toWakuConf*(n: WakuNodeConf): ConfResult[WakuConf] = if n.rateLimits.len > 0: b.rateLimitConf.withRateLimits(n.rateLimits) + else: + # The default protocol posture serves filter/lightpush, so default service + # rate limits must apply unless the operator sets their own. + b.rateLimitConf.withRateLimitsIfNotAssigned( + @["filter:100/1s", "lightpush:5/1s", "px:5/1s"] + ) b.withLocalStoragePath(n.localStoragePath) @@ -1206,25 +1185,4 @@ proc toWakuConf*(n: WakuNodeConf): ConfResult[WakuConf] = chronos.seconds(n.kadServiceLookupIntervalSec.int64) ) - # Mode-driven configuration overrides - case n.mode - of WakuMode.Core: - b.withRelay(true) - b.filterServiceConf.withEnabled(true) - b.withLightPush(true) - b.discv5Conf.withEnabled(true) - b.withPeerExchange(true) - b.withRendezvous(true) - b.rateLimitConf.withRateLimitsIfNotAssigned( - @["filter:100/1s", "lightpush:5/1s", "px:5/1s"] - ) - of WakuMode.Edge: - b.withPeerExchange(true) - b.withRelay(false) - b.filterServiceConf.withEnabled(false) - b.withLightPush(false) - b.storeServiceConf.withEnabled(false) - of WakuMode.noMode: - discard # use explicit CLI flags as-is - return b.build() diff --git a/tools/confutils/conf_from_json.nim b/tools/confutils/conf_from_json.nim index 69cca0ce7..9afb145f9 100644 --- a/tools/confutils/conf_from_json.nim +++ b/tools/confutils/conf_from_json.nim @@ -1,6 +1,9 @@ -import std/[json, macros, strutils, tables] +import std/[json, macros, options, strutils, tables] import confutils, confutils/defs, confutils/std/net, results -import ./cli_args + +# The shared JSON walker is `raises: []` so the messaging FFI parser (also +# `raises: []`) can build on it. +{.push raises: [].} proc collectJsonFields*( jsonNode: JsonNode @@ -12,7 +15,7 @@ proc collectJsonFields*( for key, value in jsonNode: let lowerKey = key.toLowerAscii() if jsonFields.hasKey(lowerKey): - let firstKey = jsonFields[lowerKey][0] + let firstKey = jsonFields.getOrDefault(lowerKey)[0] return err( "Duplicate configuration option (case-insensitive): '" & firstKey & "' and '" & key & "'" @@ -27,7 +30,7 @@ proc unknownKeysError( var keys = newSeq[string]() for _, (jsonKey, _) in pairs(jsonFields): keys.add(jsonKey) - return prefix & ": " & $keys + return prefix & ": " & keys.join(", ") proc jsonScalarToString(node: JsonNode): Result[string, string] = ## Convert a scalar JSON value to its string form. @@ -40,20 +43,55 @@ proc jsonScalarToString(node: JsonNode): Result[string, string] = return ok($node.getFloat()) of JBool: return ok($node.getBool()) - of JNull: - return ok("") else: return err("expected scalar JSON value, got " & $node.kind) -proc applyJsonFieldsToConf( - conf: var WakuNodeConf, +proc parseScalarInto[U]( + jsonValue: JsonNode, confField, jsonKey, prefix: string +): Result[U, string] = + ## Parse a scalar JSON value into `U` via confutils `parseCmdArg`, same as the CLI. + let s = jsonScalarToString(jsonValue).valueOr: + return + err(prefix & " '" & confField & "' from JSON key '" & jsonKey & "': " & error) + try: + ok(parseCmdArg(U, s)) + except CatchableError as e: + err( + prefix & " '" & confField & "' from JSON key '" & jsonKey & "': " & e.msg & + ". Value: " & s + ) + +proc parseSeqInto[U]( + jsonValue: JsonNode, confField, jsonKey, prefix: string +): Result[seq[U], string] = + ## Parse a JSON array into `seq[U]`, each element via `parseCmdArg`. + if jsonValue.kind != JArray: + return err( + prefix & " '" & confField & "' from JSON key '" & jsonKey & + "' must be a JSON array" + ) + var res: seq[U] + for item in jsonValue: + let s = jsonScalarToString(item).valueOr: + return + err(prefix & " '" & confField & "' from JSON key '" & jsonKey & "': " & error) + try: + res.add(parseCmdArg(U, s)) + except CatchableError as e: + return err( + prefix & " '" & confField & "' from JSON key '" & jsonKey & "': " & e.msg & + ". Value: " & s + ) + ok(res) + +proc applyJsonFieldsToConf*[T]( + conf: var T, jsonFields: var Table[string, (string, JsonNode)], parseErrPrefix: string, unknownErrPrefix: string, ): Result[void, string] = - ## Walk `conf`'s fields and write each one matched (case-insensitive) by - ## `jsonFields`. seq fields take a JArray (full replace); scalar fields - ## take any scalar JSON kind. Errors on leftover unknown keys. + ## Write each conf field matched (case-insensitive) by name or CLI `name:` pragma. + ## JSON `null` leaves the field unset; unknown or non-`parseCmdArg` keys error. for confField, confValue in fieldPairs(conf): # Match a field by its name or by its CLI name: pragma; case-insensitive. var matchKey = "" @@ -66,71 +104,52 @@ proc applyJsonFieldsToConf( if matchKey != "": # field-name form already present: set twice return err( "config option '" & confField & "' was set twice, via '" & - jsonFields[matchKey][0] & "' and '" & jsonFields[lowerCliName][0] & "'" + jsonFields.getOrDefault(matchKey)[0] & "' and '" & + jsonFields.getOrDefault(lowerCliName)[0] & "'" ) matchKey = lowerCliName if matchKey != "": - let (jsonKey, jsonValue) = jsonFields[matchKey] - when confValue is seq: - if jsonValue.kind != JArray: - return err( - parseErrPrefix & " '" & confField & "' from JSON key '" & jsonKey & - "' must be a JSON array" - ) - var newSeq: typeof(confValue) = @[] - for item in jsonValue: - let formattedItem = jsonScalarToString(item).valueOr: - return err( - parseErrPrefix & " '" & confField & "' from JSON key '" & jsonKey & "': " & - error - ) - try: - type ElemType = typeof(confValue[0]) - newSeq.add(parseCmdArg(ElemType, formattedItem)) - except CatchableError as e: - return err( - parseErrPrefix & " '" & confField & "' from JSON key '" & jsonKey & "': " & - e.msg & ". Value: " & formattedItem - ) - confValue = newSeq + let (jsonKey, jsonValue) = jsonFields.getOrDefault(matchKey) + if jsonValue.kind == JNull: + # JSON null leaves the field unset; it keeps its default. + jsonFields.del(matchKey) else: - let formattedString = jsonScalarToString(jsonValue).valueOr: - return err( - parseErrPrefix & " '" & confField & "' from JSON key '" & jsonKey & "': " & - error - ) - try: - confValue = parseCmdArg(typeof(confValue), formattedString) - except CatchableError as e: - return err( - parseErrPrefix & " '" & confField & "' from JSON key '" & jsonKey & "': " & - e.msg & ". Value: " & formattedString - ) - jsonFields.del(matchKey) + when confValue is Option: + type Inner = typeof(confValue.get()) + when Inner is seq: + type Elem = typeof(confValue.get()[0]) + when compiles(parseCmdArg(Elem, "")): + confValue = + some(?parseSeqInto[Elem](jsonValue, confField, jsonKey, parseErrPrefix)) + jsonFields.del(matchKey) + else: + return err("config option '" & jsonKey & "' cannot be set via JSON") + else: + when compiles(parseCmdArg(Inner, "")): + confValue = some( + ?parseScalarInto[Inner](jsonValue, confField, jsonKey, parseErrPrefix) + ) + jsonFields.del(matchKey) + else: + return err("config option '" & jsonKey & "' cannot be set via JSON") + elif confValue is seq: + type Elem = typeof(confValue[0]) + when compiles(parseCmdArg(Elem, "")): + confValue = + ?parseSeqInto[Elem](jsonValue, confField, jsonKey, parseErrPrefix) + jsonFields.del(matchKey) + else: + return err("config option '" & jsonKey & "' cannot be set via JSON") + else: + when compiles(parseCmdArg(typeof(confValue), "")): + confValue = ?parseScalarInto[typeof(confValue)]( + jsonValue, confField, jsonKey, parseErrPrefix + ) + jsonFields.del(matchKey) + else: + return err("config option '" & jsonKey & "' cannot be set via JSON") if jsonFields.len > 0: return err(unknownKeysError(jsonFields, unknownErrPrefix)) - return ok() + ok() -proc assembleFullConf*( - jsonFields: Table[string, (string, JsonNode)] -): Result[WakuNodeConf, string] = - ## Build a WakuNodeConf from a flat JSON object whose keys are WakuNodeConf field - ## names or their CLI `name:` pragma equivalents. - var conf = ?defaultWakuNodeConf() - var fields = jsonFields - ?applyJsonFieldsToConf( - conf, fields, "Failed to parse field", "Unrecognized configuration option(s) found" - ) - return ok(conf) - -proc parseNodeConfFromJson*(jsonStr: string): Result[WakuNodeConf, string] = - ## Parse a flat JSON config whose keys are WakuNodeConf field names or their CLI - ## `name:` pragma equivalents. - var jsonNode: JsonNode - try: - jsonNode = parseJson(jsonStr) - except CatchableError as e: - return err("Failed to parse config JSON: " & e.msg) - - let jsonFields = ?collectJsonFields(jsonNode) - return assembleFullConf(jsonFields) +{.pop.} From d83900aa9b9467eadd7f2420c5c9045d219a8e92 Mon Sep 17 00:00:00 2001 From: Fabiana Cecin Date: Thu, 9 Jul 2026 15:57:16 -0300 Subject: [PATCH 30/33] feat: add fleet mode (#4019) * Rename WakuMode to LogosDeliveryMode, add Fleet mode * KernelConf (which starts only kernel) is now a distinct WakuNodeConf (starts messaging, for tests) * Add LogosDelivery.new(kernelConf) (kernel-only) * Add LogosDelivery.new(kernelConf, messagingOverrides, channelsOverrides) (full stack) * LogosDeliveryConf.messagingOverrides/channelsOverrides are Option and mount each layer if present * Add kernel-only LogosDeliveryConf.init(kernelConf) * Parse {"mode": "fleet", "kernelConf": {...}} in parseLogosDeliveryConf * Generalize parseOverrides to parse over caller-supplied defaults * Add requireMessaging/requireChannels FFI guards and apply them * Make start/stop skip layers that were not mounted (no config) * Rename toKernelConf to toWakuNodeConf * Add tests to test_conf.nim * Misc additions/refactors --- library/channels_api/channel_api.nim | 9 ++ library/declare_lib.nim | 19 +++ library/logos_delivery_api/messaging_api.nim | 9 ++ logos_delivery/api/kernel_conf.nim | 4 +- logos_delivery/api/logos_delivery_conf.nim | 22 ++- .../api/logos_delivery_conf_json.nim | 46 ++++-- logos_delivery/api/messaging_conf.nim | 14 +- logos_delivery/logos_delivery.nim | 86 +++++++--- tests/api/test_api_health.nim | 4 +- tests/api/test_api_receive.nim | 3 +- tests/api/test_api_send.nim | 22 ++- tests/api/test_api_subscription.nim | 26 +-- tests/api/test_conf.nim | 151 +++++++++++++----- .../test_reliable_channel_send_receive.nim | 3 +- tests/test_waku.nim | 12 +- tools/confutils/conf_from_json.nim | 2 +- 16 files changed, 317 insertions(+), 115 deletions(-) diff --git a/library/channels_api/channel_api.nim b/library/channels_api/channel_api.nim index b25ad78ed..c9b96312f 100644 --- a/library/channels_api/channel_api.nim +++ b/library/channels_api/channel_api.nim @@ -18,6 +18,9 @@ proc logosdelivery_channel_create( requireInitializedNode(ctx, "ChannelCreate"): return err(errMsg) + requireChannels(ctx, "ChannelCreate"): + return err(errMsg) + let id = ctx.myLib[].reliableChannelManager.createReliableChannel( ChannelId($channelIdStr), ContentTopic($contentTopicStr), @@ -38,6 +41,9 @@ proc logosdelivery_channel_send( requireInitializedNode(ctx, "ChannelSend"): return err(errMsg) + requireChannels(ctx, "ChannelSend"): + return err(errMsg) + var jsonNode: JsonNode try: jsonNode = parseJson($messageJson) @@ -70,6 +76,9 @@ proc logosdelivery_channel_close( requireInitializedNode(ctx, "ChannelClose"): return err(errMsg) + requireChannels(ctx, "ChannelClose"): + return err(errMsg) + (await ctx.myLib[].reliableChannelManager.closeChannel(ChannelId($channelIdStr))).isOkOr: return err("ChannelClose failed: " & $error) diff --git a/library/declare_lib.nim b/library/declare_lib.nim index eaf8f6315..869c6f71e 100644 --- a/library/declare_lib.nim +++ b/library/declare_lib.nim @@ -1,5 +1,6 @@ import ffi import std/locks +import results import logos_delivery declareLibrary("logosdelivery") @@ -17,6 +18,24 @@ template requireInitializedNode*( let errMsg {.inject.} = opName & " failed: node is not initialized" onError +template requireMessaging*( + ctx: ptr FFIContext[LogosDelivery], opName: string, onError: untyped +) = + ## Use after `requireInitializedNode`. Fails if the node has no messaging client + ## (a kernel-only / fleet node). + ctx.myLib[].ensureMessaging().isOkOr: + let errMsg {.inject.} = opName & " failed: " & error + onError + +template requireChannels*( + ctx: ptr FFIContext[LogosDelivery], opName: string, onError: untyped +) = + ## Use after `requireInitializedNode`. Fails if the node has no reliable channel + ## manager (a kernel-only / fleet node). + ctx.myLib[].ensureChannels().isOkOr: + let errMsg {.inject.} = opName & " failed: " & error + onError + proc logosdelivery_set_event_callback( ctx: ptr FFIContext[LogosDelivery], callback: FFICallBack, userData: pointer ) {.dynlib, exportc, cdecl.} = diff --git a/library/logos_delivery_api/messaging_api.nim b/library/logos_delivery_api/messaging_api.nim index 0e4dc7449..4cab9bd93 100644 --- a/library/logos_delivery_api/messaging_api.nim +++ b/library/logos_delivery_api/messaging_api.nim @@ -17,6 +17,9 @@ proc logosdelivery_subscribe( requireInitializedNode(ctx, "Subscribe"): return err(errMsg) + requireMessaging(ctx, "Subscribe"): + return err(errMsg) + # ContentTopic is just a string type alias let contentTopic = ContentTopic($contentTopicStr) @@ -35,6 +38,9 @@ proc logosdelivery_unsubscribe( requireInitializedNode(ctx, "Unsubscribe"): return err(errMsg) + requireMessaging(ctx, "Unsubscribe"): + return err(errMsg) + # ContentTopic is just a string type alias let contentTopic = ContentTopic($contentTopicStr) @@ -53,6 +59,9 @@ proc logosdelivery_send( requireInitializedNode(ctx, "Send"): return err(errMsg) + requireMessaging(ctx, "Send"): + return err(errMsg) + ## Parse the message JSON and send the message var jsonNode: JsonNode try: diff --git a/logos_delivery/api/kernel_conf.nim b/logos_delivery/api/kernel_conf.nim index 2c66e2d51..d0131b561 100644 --- a/logos_delivery/api/kernel_conf.nim +++ b/logos_delivery/api/kernel_conf.nim @@ -1,4 +1,6 @@ import tools/confutils/cli_args export cli_args -type KernelConf* = WakuNodeConf ## Alias of the CLI-facing `WakuNodeConf`. +type KernelConf* = distinct WakuNodeConf + ## Raw kernel config; distinct so `new(KernelConf)` doesn't collide with the + ## full-stack `new(WakuNodeConf)`. diff --git a/logos_delivery/api/logos_delivery_conf.nim b/logos_delivery/api/logos_delivery_conf.nim index 124a8c5fb..66acaa8fc 100644 --- a/logos_delivery/api/logos_delivery_conf.nim +++ b/logos_delivery/api/logos_delivery_conf.nim @@ -1,30 +1,38 @@ {.push raises: [].} +import std/options import results import logos_delivery/api/messaging_conf import logos_delivery/channels/reliable_channel_manager -export messaging_conf, reliable_channel_manager +export options, messaging_conf, reliable_channel_manager -type LogosDeliveryConf* = object ## Aggregates the per-layer config objects. +type LogosDeliveryConf* = object + ## Aggregates the per-layer config objects. A layer is mounted iff its config + ## is present. kernelConf*: KernelConf - messagingConf*: MessagingClientConf - channelsConf*: ReliableChannelManagerConf + messagingConf*: Option[MessagingClientConf] + channelsConf*: Option[ReliableChannelManagerConf] + +proc init*(T: type LogosDeliveryConf, kernelConf: KernelConf): LogosDeliveryConf = + return LogosDeliveryConf(kernelConf: kernelConf) proc init*( T: type LogosDeliveryConf, - mode: WakuMode, + mode: LogosDeliveryMode, preset: string, messagingOverrides: MessagingClientConf, channelsOverrides: ReliableChannelManagerConf, ): ConfResult[LogosDeliveryConf] = let merged = merge(?resolvePreset(preset), messagingOverrides) - var kernelConf = ?toKernelConf(merged, mode) + var kernelConf = ?toWakuNodeConf(merged, mode) kernelConf.preset = preset return ok( LogosDeliveryConf( - kernelConf: kernelConf, messagingConf: merged, channelsConf: channelsOverrides + kernelConf: KernelConf(kernelConf), + messagingConf: some(merged), + channelsConf: some(channelsOverrides), ) ) diff --git a/logos_delivery/api/logos_delivery_conf_json.nim b/logos_delivery/api/logos_delivery_conf_json.nim index 2416104bd..18e9ad485 100644 --- a/logos_delivery/api/logos_delivery_conf_json.nim +++ b/logos_delivery/api/logos_delivery_conf_json.nim @@ -10,23 +10,27 @@ const # Lowercased, since `collectJsonFields` keys the object case-insensitively. KeyMode = "mode" KeyPreset = "preset" + KeyKernelConf = "kernelconf" KeyMessagingOverrides = "messagingoverrides" KeyChannelsOverrides = "channelsoverrides" -proc parseMode(s: string): Result[WakuMode, string] = +proc parseMode(s: string): Result[LogosDeliveryMode, string] = case s.strip().toLowerAscii() of "core": - return ok(WakuMode.Core) + return ok(LogosDeliveryMode.Core) of "edge": - return ok(WakuMode.Edge) + return ok(LogosDeliveryMode.Edge) + of "fleet": + return ok(LogosDeliveryMode.Fleet) else: - return err("invalid mode: '" & s & "' (expected 'Core' or 'Edge')") + return err("invalid mode: '" & s & "' (expected 'Core', 'Edge' or 'Fleet')") -proc parseOverrides[T](node: JsonNode, label: string): Result[T, string] = +proc parseOverrides[T](defaults: T, node: JsonNode, label: string): Result[T, string] = + ## Parse the JSON object `node` as overrides on top of `defaults`. if node.kind != JObject: return err(label & " must be a JSON object") var fields = ?collectJsonFields(node) - var conf = T() + var conf = defaults ?applyJsonFieldsToConf( conf, fields, @@ -45,11 +49,8 @@ proc parseLogosDeliveryConf*(jsonStr: string): ConfResult[LogosDeliveryConf] = return err("configuration JSON must be an object") var top = ?collectJsonFields(node) - var mode = WakuMode.Core - var preset = "" - var messagingOverrides = MessagingClientConf() - var channelsOverrides = ReliableChannelManagerConf() + var mode = LogosDeliveryMode.Core if top.hasKey(KeyMode): let (_, v) = top.getOrDefault(KeyMode) if v.kind != JString: @@ -57,6 +58,22 @@ proc parseLogosDeliveryConf*(jsonStr: string): ConfResult[LogosDeliveryConf] = mode = ?parseMode(v.getStr()) top.del(KeyMode) + if mode == LogosDeliveryMode.Fleet: + # Kernel-only: a raw kernelConf and no upper layers. + if not top.hasKey(KeyKernelConf): + return err("fleet mode requires a 'kernelConf' object") + let (_, v) = top.getOrDefault(KeyKernelConf) + let kernel = ?parseOverrides(?defaultWakuNodeConf(), v, "kernelConf") + top.del(KeyKernelConf) + if top.len > 0: + return + err(unknownKeysError(top, "fleet mode takes only 'kernelConf'; unexpected")) + return ok(LogosDeliveryConf.init(KernelConf(kernel))) + + var preset = "" + var messagingOverrides = MessagingClientConf() + var channelsOverrides = ReliableChannelManagerConf() + if top.hasKey(KeyPreset): let (_, v) = top.getOrDefault(KeyPreset) if v.kind != JString: @@ -66,20 +83,17 @@ proc parseLogosDeliveryConf*(jsonStr: string): ConfResult[LogosDeliveryConf] = if top.hasKey(KeyMessagingOverrides): let (_, v) = top.getOrDefault(KeyMessagingOverrides) - messagingOverrides = ?parseOverrides[MessagingClientConf](v, "messagingOverrides") + messagingOverrides = ?parseOverrides(MessagingClientConf(), v, "messagingOverrides") top.del(KeyMessagingOverrides) if top.hasKey(KeyChannelsOverrides): let (_, v) = top.getOrDefault(KeyChannelsOverrides) channelsOverrides = - ?parseOverrides[ReliableChannelManagerConf](v, "channelsOverrides") + ?parseOverrides(ReliableChannelManagerConf(), v, "channelsOverrides") top.del(KeyChannelsOverrides) if top.len > 0: - var keys: seq[string] - for _, (k, _) in pairs(top): - keys.add(k) - return err("Unrecognized configuration option(s) found: " & keys.join(", ")) + return err(unknownKeysError(top, "Unrecognized configuration option(s) found")) return LogosDeliveryConf.init(mode, preset, messagingOverrides, channelsOverrides) diff --git a/logos_delivery/api/messaging_conf.nim b/logos_delivery/api/messaging_conf.nim index 9ba642cd8..1907e3540 100644 --- a/logos_delivery/api/messaging_conf.nim +++ b/logos_delivery/api/messaging_conf.nim @@ -8,28 +8,34 @@ import logos_delivery/waku/factory/networks_config export kernel_conf, messaging_client -type WakuMode* {.pure.} = enum +type LogosDeliveryMode* {.pure.} = enum Edge # client-only node Core # full service node + Fleet # kernel-only node from a raw kernel config -proc toKernelConf*(self: MessagingClientConf, mode: WakuMode): ConfResult[KernelConf] = +proc toWakuNodeConf*( + self: MessagingClientConf, mode: LogosDeliveryMode +): ConfResult[WakuNodeConf] = ## Mode sets the protocol flags; set fields map to their kernel counterpart. var conf = ?defaultWakuNodeConf() case mode - of WakuMode.Core: + of LogosDeliveryMode.Core: conf.relay = true conf.filter = true conf.lightpush = true conf.discv5Discovery = some(true) conf.peerExchange = true conf.rendezvous = true - of WakuMode.Edge: + of LogosDeliveryMode.Edge: conf.peerExchange = true conf.relay = false conf.filter = false conf.lightpush = false conf.store = false + of LogosDeliveryMode.Fleet: + return + err("fleet mode takes a raw kernel config; use LogosDelivery.new(kernelConf)") if self.store.isSome(): conf.store = self.store.get() diff --git a/logos_delivery/logos_delivery.nim b/logos_delivery/logos_delivery.nim index 1e34b6a6a..9ce568740 100644 --- a/logos_delivery/logos_delivery.nim +++ b/logos_delivery/logos_delivery.nim @@ -75,19 +75,26 @@ type LogosDelivery* = ref object ## Entry point. Holds one instance of each API proc new*( T: type LogosDelivery, conf: LogosDeliveryConf, appCallbacks: AppCallbacks = nil ): Future[Result[LogosDelivery, string]] {.async.} = - ## Builds the stack bottom-up from a resolved per-layer config. - let wakuConf = conf.kernelConf.toWakuConf().valueOr: - return err("failed to handle the configuration: " & error) + ## Builds the stack bottom-up from a resolved per-layer config; each layer is + ## mounted iff its config is present. + let wakuConf = WakuNodeConf(conf.kernelConf).toWakuConf().valueOr: + return err("failed to handle the configuration: " & error) let waku = (await Waku.new(wakuConf, appCallbacks)).valueOr: return err("failed to create Waku: " & error) - let messagingClient = MessagingClient.new(conf.messagingConf, waku).valueOr: - return err("failed to create MessagingClient: " & error) + let messagingClient = + if conf.messagingConf.isSome(): + MessagingClient.new(conf.messagingConf.get(), waku).valueOr: + return err("failed to create MessagingClient: " & error) + else: + nil - let reliableChannelManager = ReliableChannelManager.new( - conf.channelsConf, waku.brokerCtx - ).valueOr: - return err("failed to create ReliableChannelManager: " & error) + let reliableChannelManager = + if conf.channelsConf.isSome(): + ReliableChannelManager.new(conf.channelsConf.get(), waku.brokerCtx).valueOr: + return err("failed to create ReliableChannelManager: " & error) + else: + nil return ok( LogosDelivery( @@ -103,16 +110,41 @@ proc new*( ## Builds the full stack from a kernel `WakuNodeConf`. return await LogosDelivery.new( LogosDeliveryConf( - kernelConf: conf, - messagingConf: MessagingClientConf(), - channelsConf: ReliableChannelManagerConf(), + kernelConf: KernelConf(conf), + messagingConf: some(MessagingClientConf()), + channelsConf: some(ReliableChannelManagerConf()), + ), + appCallbacks, + ) + +proc new*( + T: type LogosDelivery, kernelConf: KernelConf, appCallbacks: AppCallbacks = nil +): Future[Result[LogosDelivery, string]] {.async.} = + ## Fleet mode: mounts the kernel only from a raw `KernelConf`; no messaging client, + ## no channel manager. + return await LogosDelivery.new(LogosDeliveryConf.init(kernelConf), appCallbacks) + +proc new*( + T: type LogosDelivery, + kernelConf: KernelConf, + messagingOverrides: MessagingClientConf, + channelsOverrides: ReliableChannelManagerConf, + appCallbacks: AppCallbacks = nil, +): Future[Result[LogosDelivery, string]] {.async.} = + ## Full stack: kernel + messaging + channels. Messaging is never skipped; a + ## kernel-only node uses `new(kernelConf)` instead. + return await LogosDelivery.new( + LogosDeliveryConf( + kernelConf: kernelConf, + messagingConf: some(messagingOverrides), + channelsConf: some(channelsOverrides), ), appCallbacks, ) proc new*( T: type LogosDelivery, - mode: WakuMode = WakuMode.Core, + mode: LogosDeliveryMode = LogosDeliveryMode.Core, preset: string = "", messagingOverrides: MessagingClientConf = MessagingClientConf(), channelsOverrides: ReliableChannelManagerConf = ReliableChannelManagerConf(), @@ -124,22 +156,20 @@ proc new*( return await LogosDelivery.new(conf, appCallbacks) proc start*(self: LogosDelivery): Future[Result[void, string]] {.async.} = - ## Starts each layer bottom-up: transport first, then messaging, then channels. + ## Starts each present layer bottom-up: transport, then messaging, then channels. if self.waku.isNil(): return err("Waku node is not initialized") - if self.messagingClient.isNil(): - return err("MessagingClient is not initialized") - if self.reliableChannelManager.isNil(): - return err("ReliableChannelManager is not initialized") (await self.waku.start()).isOkOr: return err("failed to start Waku: " & error) - self.messagingClient.start().isOkOr: - return err("failed to start MessagingClient: " & error) + if not self.messagingClient.isNil(): + self.messagingClient.start().isOkOr: + return err("failed to start MessagingClient: " & error) - self.reliableChannelManager.start().isOkOr: - return err("failed to start ReliableChannelManager: " & error) + if not self.reliableChannelManager.isNil(): + self.reliableChannelManager.start().isOkOr: + return err("failed to start ReliableChannelManager: " & error) return ok() @@ -160,6 +190,18 @@ proc isOnline*(self: LogosDelivery): Future[Result[bool, string]] {.async.} = return err("Waku node is not initialized") return await self.waku.isOnline() +proc ensureMessaging*(self: LogosDelivery): Result[void, string] = + ## Fails if the node has no messaging client (a kernel-only / fleet node). + if self.isNil() or self.messagingClient.isNil(): + return err("node has no messaging client (kernel-only/fleet node)") + ok() + +proc ensureChannels*(self: LogosDelivery): Result[void, string] = + ## Fails if the node has no reliable channel manager (a kernel-only / fleet node). + if self.isNil() or self.reliableChannelManager.isNil(): + return err("node has no reliable channel manager (kernel-only/fleet node)") + ok() + # Compile-time check that each concrete type satisfies its API concept. static: doAssert Waku is KernelApi diff --git a/tests/api/test_api_health.nim b/tests/api/test_api_health.nim index e0d344958..7b493964a 100644 --- a/tests/api/test_api_health.nim +++ b/tests/api/test_api_health.nim @@ -91,7 +91,7 @@ suite "LM API health checking": serviceNode.wakuRelay.subscribe(DefaultShard, dummyHandler) lockNewGlobalBrokerContext: - var conf = MessagingClientConf().toKernelConf(Core).valueOr: + var conf = MessagingClientConf().toWakuNodeConf(Core).valueOr: raiseAssert error conf.listenAddress = parseIpAddress("0.0.0.0") conf.tcpPort = Port(0) @@ -271,7 +271,7 @@ suite "LM API health checking": var edgeWaku: LogosDelivery lockNewGlobalBrokerContext: - var edgeConf = MessagingClientConf().toKernelConf(Edge).valueOr: + var edgeConf = MessagingClientConf().toWakuNodeConf(Edge).valueOr: raiseAssert error edgeConf.listenAddress = parseIpAddress("0.0.0.0") edgeConf.tcpPort = Port(0) diff --git a/tests/api/test_api_receive.nim b/tests/api/test_api_receive.nim index 51c2438a8..744cc5e52 100644 --- a/tests/api/test_api_receive.nim +++ b/tests/api/test_api_receive.nim @@ -83,7 +83,8 @@ proc waitForConnectionStatus( await EventConnectionStatusChange.dropListener(brokerCtx, handle) proc createApiNodeConf(numShards: uint16 = 1): WakuNodeConf = - var conf = MessagingClientConf().toKernelConf(messaging_conf.WakuMode.Core).valueOr: + var conf = MessagingClientConf() + .toWakuNodeConf(messaging_conf.LogosDeliveryMode.Core).valueOr: raiseAssert error conf.listenAddress = parseIpAddress("0.0.0.0") conf.tcpPort = Port(0) diff --git a/tests/api/test_api_send.nim b/tests/api/test_api_send.nim index 29921e7aa..e913d76a0 100644 --- a/tests/api/test_api_send.nim +++ b/tests/api/test_api_send.nim @@ -121,9 +121,9 @@ proc validate( check requestId == expectedRequestId proc createApiNodeConf( - mode: messaging_conf.WakuMode = messaging_conf.WakuMode.Core + mode: messaging_conf.LogosDeliveryMode = messaging_conf.LogosDeliveryMode.Core ): WakuNodeConf = - var conf = MessagingClientConf().toKernelConf(mode).valueOr: + var conf = MessagingClientConf().toWakuNodeConf(mode).valueOr: raiseAssert error conf.listenAddress = parseIpAddress("0.0.0.0") conf.tcpPort = Port(0) @@ -354,7 +354,11 @@ suite "Waku API - Send": ## connected to a lightpush-capable peer must deliver through lightpush. var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await LogosDelivery.new(createApiNodeConf(messaging_conf.WakuMode.Edge))).valueOr: + node = ( + await LogosDelivery.new( + createApiNodeConf(messaging_conf.LogosDeliveryMode.Edge) + ) + ).valueOr: raiseAssert error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error @@ -389,7 +393,11 @@ suite "Waku API - Send": ## later retry must deliver the queued message. var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await LogosDelivery.new(createApiNodeConf(messaging_conf.WakuMode.Edge))).valueOr: + node = ( + await LogosDelivery.new( + createApiNodeConf(messaging_conf.LogosDeliveryMode.Edge) + ) + ).valueOr: raiseAssert error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error @@ -480,7 +488,11 @@ suite "Waku API - Send": var node: LogosDelivery lockNewGlobalBrokerContext: - node = (await LogosDelivery.new(createApiNodeConf(messaging_conf.WakuMode.Edge))).valueOr: + node = ( + await LogosDelivery.new( + createApiNodeConf(messaging_conf.LogosDeliveryMode.Edge) + ) + ).valueOr: raiseAssert error (await node.start()).isOkOr: raiseAssert "Failed to start Waku node: " & error diff --git a/tests/api/test_api_subscription.nim b/tests/api/test_api_subscription.nim index 37566539a..7ba3b2bf5 100644 --- a/tests/api/test_api_subscription.nim +++ b/tests/api/test_api_subscription.nim @@ -69,9 +69,10 @@ type TestNetwork = ref object publisherPeerInfo: RemotePeerInfo proc createApiNodeConf( - mode: messaging_conf.WakuMode = messaging_conf.WakuMode.Core, numShards: uint16 = 1 + mode: messaging_conf.LogosDeliveryMode = messaging_conf.LogosDeliveryMode.Core, + numShards: uint16 = 1, ): WakuNodeConf = - var conf = MessagingClientConf().toKernelConf(mode).valueOr: + var conf = MessagingClientConf().toWakuNodeConf(mode).valueOr: raiseAssert error conf.listenAddress = parseIpAddress("0.0.0.0") conf.tcpPort = Port(0) @@ -89,7 +90,8 @@ proc setupSubscriberNode(conf: WakuNodeConf): Future[LogosDelivery] {.async.} = return node proc setupNetwork( - numShards: uint16 = 1, mode: messaging_conf.WakuMode = messaging_conf.WakuMode.Core + numShards: uint16 = 1, + mode: messaging_conf.LogosDeliveryMode = messaging_conf.LogosDeliveryMode.Core, ): Future[TestNetwork] {.async.} = var net = TestNetwork() @@ -99,7 +101,7 @@ proc setupNetwork( "Failed to mount metadata" ) (await net.publisher.mountRelay()).expect("Failed to mount relay") - if mode == messaging_conf.WakuMode.Edge: + if mode == messaging_conf.LogosDeliveryMode.Edge: await net.publisher.mountFilter() await net.publisher.mountLibp2pPing() await net.publisher.start() @@ -118,7 +120,7 @@ proc setupNetwork( "Failed to sub publisher" ) - if mode == messaging_conf.WakuMode.Edge: + if mode == messaging_conf.LogosDeliveryMode.Edge: lockNewGlobalBrokerContext: net.meshBuddy = newTestWakuNode(generateSecp256k1Key()) net.meshBuddy.mountMetadata(3, toSeq(0'u16 ..< numShards)).expect( @@ -473,7 +475,7 @@ suite "Messaging API, SubscriptionManager": await verifyNetworkState(activeSubs) asyncTest "Subscription API, edge node subscribe and receive message": - let net = await setupNetwork(1, messaging_conf.WakuMode.Edge) + let net = await setupNetwork(1, messaging_conf.LogosDeliveryMode.Edge) defer: await net.teardown() @@ -495,7 +497,7 @@ suite "Messaging API, SubscriptionManager": check eventManager.receivedMessages[0].contentTopic == testTopic asyncTest "Subscription API, edge node ignores unsubscribed content topics": - let net = await setupNetwork(1, messaging_conf.WakuMode.Edge) + let net = await setupNetwork(1, messaging_conf.LogosDeliveryMode.Edge) defer: await net.teardown() @@ -517,7 +519,7 @@ suite "Messaging API, SubscriptionManager": check eventManager.receivedMessages.len == 0 asyncTest "Subscription API, edge node unsubscribe stops message receipt": - let net = await setupNetwork(1, messaging_conf.WakuMode.Edge) + let net = await setupNetwork(1, messaging_conf.LogosDeliveryMode.Edge) defer: await net.teardown() @@ -542,7 +544,7 @@ suite "Messaging API, SubscriptionManager": check eventManager.receivedMessages.len == 0 asyncTest "Subscription API, edge node overlapping topics isolation": - let net = await setupNetwork(1, messaging_conf.WakuMode.Edge) + let net = await setupNetwork(1, messaging_conf.LogosDeliveryMode.Edge) defer: await net.teardown() @@ -571,7 +573,7 @@ suite "Messaging API, SubscriptionManager": check eventManager.receivedMessages[0].contentTopic == topicB asyncTest "Subscription API, edge node resubscribe after unsubscribe": - let net = await setupNetwork(1, messaging_conf.WakuMode.Edge) + let net = await setupNetwork(1, messaging_conf.LogosDeliveryMode.Edge) defer: await net.teardown() @@ -656,7 +658,7 @@ suite "Messaging API, SubscriptionManager": await meshBuddy.connectToNodes(@[publisherPeerInfo]) - let conf = createApiNodeConf(messaging_conf.WakuMode.Edge, numShards) + let conf = createApiNodeConf(messaging_conf.LogosDeliveryMode.Edge, numShards) var subscriber: LogosDelivery lockNewGlobalBrokerContext: subscriber = @@ -784,7 +786,7 @@ suite "Messaging API, SubscriptionManager": await meshBuddy.connectToNodes(@[publisherPeerInfo]) await sparePeer.connectToNodes(@[publisherPeerInfo]) - let conf = createApiNodeConf(messaging_conf.WakuMode.Edge, numShards) + let conf = createApiNodeConf(messaging_conf.LogosDeliveryMode.Edge, numShards) var subscriber: LogosDelivery lockNewGlobalBrokerContext: subscriber = diff --git a/tests/api/test_conf.nim b/tests/api/test_conf.nim index dd90477d4..4017d006b 100644 --- a/tests/api/test_conf.nim +++ b/tests/api/test_conf.nim @@ -7,9 +7,9 @@ import logos_delivery/api/logos_delivery_conf_json import logos_delivery/waku/factory/[waku_conf, networks_config] import logos_delivery/waku/common/logging -suite "MessagingClientConf - mode expansion (toKernelConf)": +suite "MessagingClientConf - mode expansion (toWakuNodeConf)": test "Core mode enables relay + service protocols": - let kc = MessagingClientConf().toKernelConf(WakuMode.Core).valueOr: + let kc = MessagingClientConf().toWakuNodeConf(LogosDeliveryMode.Core).valueOr: raiseAssert error check: kc.relay == true @@ -20,7 +20,7 @@ suite "MessagingClientConf - mode expansion (toKernelConf)": kc.rendezvous == true test "Edge mode is client-only (no relay/filter/lightpush/store)": - let kc = MessagingClientConf().toKernelConf(WakuMode.Edge).valueOr: + let kc = MessagingClientConf().toWakuNodeConf(LogosDeliveryMode.Edge).valueOr: raiseAssert error check: kc.relay == false @@ -37,7 +37,7 @@ suite "MessagingClientConf - field mapping + transport policy": numShardsInCluster: some(4'u16), maxMessageSize: some("150KiB"), ) - let kc = mc.toKernelConf(WakuMode.Core).valueOr: + let kc = mc.toWakuNodeConf(LogosDeliveryMode.Core).valueOr: raiseAssert error check: kc.clusterId == some(3'u16) @@ -45,7 +45,7 @@ suite "MessagingClientConf - field mapping + transport policy": kc.maxMessageSize == "150KiB" test "messaging transport defaults: ephemeral ports, websocket off, quic on": - let kc = MessagingClientConf().toKernelConf(WakuMode.Core).valueOr: + let kc = MessagingClientConf().toWakuNodeConf(LogosDeliveryMode.Core).valueOr: raiseAssert error check: kc.tcpPort == Port(0) @@ -59,7 +59,7 @@ suite "MessagingClientConf - field mapping + transport policy": websocketSupport: some(true), quicSupport: some(false), ) - let kc = mc.toKernelConf(WakuMode.Core).valueOr: + let kc = mc.toWakuNodeConf(LogosDeliveryMode.Core).valueOr: raiseAssert error check: kc.tcpPort == Port(1234) @@ -91,7 +91,7 @@ suite "MessagingClientConf - preset resolution": let presetConf = resolvePreset("logos.dev").valueOr: raiseAssert error let merged = merge(presetConf, MessagingClientConf(numShardsInCluster: some(1'u16))) - var kernelConf = toKernelConf(merged, WakuMode.Core).valueOr: + var kernelConf = toWakuNodeConf(merged, LogosDeliveryMode.Core).valueOr: raiseAssert error kernelConf.preset = "logos.dev" let wakuConf = kernelConf.toWakuConf().valueOr: @@ -110,38 +110,59 @@ suite "MessagingClientConf - merge (override wins)": mc.maxMessageSize == some("1MB") # base preserved suite "parseLogosDeliveryConf - JSON parsing": - test "empty object -> Core (default), empty preset, empty overrides": + test "empty object resolves to a full Core node conf": let lc = parseLogosDeliveryConf("{}").valueOr: raiseAssert error check: - lc.kernelConf.relay == true # Core enables relay - lc.kernelConf.preset == "" - lc.messagingConf.clusterId.isNone() + lc.messagingConf.isSome() # full node: messaging + channels mounted + lc.channelsConf.isSome() + WakuNodeConf(lc.kernelConf).relay == true # Core enables relay + WakuNodeConf(lc.kernelConf).clusterId.isNone() # no cluster set - test "mode + preset are parsed": + test "mode + preset fold into the kernel conf": let lc = parseLogosDeliveryConf("""{"mode": "Edge", "preset": "logostest"}""").valueOr: raiseAssert error + let kc = WakuNodeConf(lc.kernelConf) check: - lc.kernelConf.preset == "logostest" - lc.kernelConf.relay == false # Edge disables relay + kc.preset == "logostest" + kc.relay == false # Edge disables relay - test "messagingOverrides parsed into the partial": + test "messaging overrides split: kernel fields to the kernel, reliability to the record": let lc = parseLogosDeliveryConf( """{"mode": "Core", "messagingOverrides": {"clusterId": 7, "reliabilityEnabled": true}}""" ).valueOr: raiseAssert error + require lc.messagingConf.isSome() check: - lc.messagingConf.clusterId == some(7'u16) - lc.messagingConf.reliabilityEnabled == some(true) + WakuNodeConf(lc.kernelConf).clusterId == some(7'u16) # kernel field + lc.messagingConf.get().reliabilityEnabled == some(true) # messaging-only - test "channelsOverrides parsed into the partial": + test "messaging overrides are recorded verbatim, unset fields left none": + let lc = parseLogosDeliveryConf("""{"messagingOverrides": {"clusterId": 7}}""").valueOr: + raiseAssert error + require lc.messagingConf.isSome() + let overrides = lc.messagingConf.get() + check: + overrides.clusterId == some(7'u16) # the user's override, kept as given + overrides.reliabilityEnabled.isNone() # user didn't set it, so it stays none + + test "a preset resolves reliability into the messaging record": + let lc = parseLogosDeliveryConf("""{"preset": "twn"}""").valueOr: + raiseAssert error + # the user set no reliability override, but the preset supplies one + require lc.messagingConf.isSome() + check lc.messagingConf.get().reliabilityEnabled.isSome() + + test "channelsOverrides fold into the channel conf": let lc = parseLogosDeliveryConf( """{"channelsOverrides": {"rateLimitEnabled": true, "sdsMaxRetransmissions": 9}}""" ).valueOr: raiseAssert error + require lc.channelsConf.isSome() + let channels = lc.channelsConf.get() check: - lc.channelsConf.rateLimitEnabled == some(true) - lc.channelsConf.sdsMaxRetransmissions == some(9) + channels.rateLimitEnabled == some(true) + channels.sdsMaxRetransmissions == some(9) test "invalid mode is rejected (Core or Edge only)": check parseLogosDeliveryConf("""{"mode": "bogus"}""").isErr() @@ -159,10 +180,12 @@ suite "parseLogosDeliveryConf - JSON parsing": """{"messagingOverrides": {"cluster-id": 7, "reliability": true, "tcp-port": 1234}}""" ).valueOr: raiseAssert error + let kc = WakuNodeConf(lc.kernelConf) + require lc.messagingConf.isSome() check: - lc.messagingConf.clusterId == some(7'u16) - lc.messagingConf.reliabilityEnabled == some(true) - lc.messagingConf.p2pTcpPort == some(Port(1234)) + kc.clusterId == some(7'u16) + lc.messagingConf.get().reliabilityEnabled == some(true) # messaging-only + kc.tcpPort == Port(1234) test "unknown keys inside an overrides body are rejected": check parseLogosDeliveryConf("""{"messagingOverrides": {"bogusKey": 1}}""").isErr() @@ -179,19 +202,21 @@ suite "parseLogosDeliveryConf - JSON parsing": """{"messagingOverrides": {"log-level": "DEBUG", "log-format": "JSON", "nodekey": "0d714a1fada214dead6dc9c7274581ec20ff292451866e7d6d677dc818e8ccd2"}}""" ).valueOr: raiseAssert error + let kc = WakuNodeConf(lc.kernelConf) check: - lc.kernelConf.logLevel == logging.LogLevel.DEBUG - lc.kernelConf.logFormat == logging.LogFormat.JSON - lc.kernelConf.nodekey.isSome() + kc.logLevel == logging.LogLevel.DEBUG + kc.logFormat == logging.LogFormat.JSON + kc.nodekey.isSome() test "a null value leaves the field unset": let lc = parseLogosDeliveryConf( """{"messagingOverrides": {"store": null, "clusterId": 7}}""" ).valueOr: raiseAssert error + let kc = WakuNodeConf(lc.kernelConf) check: - lc.messagingConf.store.isNone() - lc.messagingConf.clusterId == some(7'u16) + kc.clusterId == some(7'u16) + kc.store == false # null left store unset; Core does not enable it test "an invalid Ethereum RPC URL is rejected at parse time": check parseLogosDeliveryConf( @@ -202,18 +227,50 @@ suite "parseLogosDeliveryConf - JSON parsing": """{"messagingOverrides": {"rln-relay-eth-client-address": ["http://localhost:8540/"]}}""" ).valueOr: raiseAssert error - check lc.kernelConf.ethClientUrls.len == 1 + check WakuNodeConf(lc.kernelConf).ethClientUrls.len == 1 - test "store backend fields parse and map to the kernel": + test "store backend fields fold into the kernel conf": let lc = parseLogosDeliveryConf( """{"messagingOverrides": {"store": true, "store-message-db-url": "sqlite://test.db", "store-message-retention-policy": "time:3600", "store-max-num-db-connections": 7}}""" ).valueOr: raiseAssert error + let kc = WakuNodeConf(lc.kernelConf) check: - lc.kernelConf.store == true - lc.kernelConf.storeMessageDbUrl == "sqlite://test.db" - lc.kernelConf.storeMessageRetentionPolicy == "time:3600" - lc.kernelConf.storeMaxNumDbConnections == 7 + kc.store == true + kc.storeMessageDbUrl == "sqlite://test.db" + kc.storeMessageRetentionPolicy == "time:3600" + kc.storeMaxNumDbConnections == 7 + + test "fleet mode parses a raw kernelConf": + let lc = parseLogosDeliveryConf( + """{"mode": "fleet", "kernelConf": {"relay": false, "maxMessageSize": "150KiB"}}""" + ).valueOr: + raiseAssert error + check: + lc.messagingConf.isNone() # kernel-only: no upper layers + lc.channelsConf.isNone() + let kc = WakuNodeConf(lc.kernelConf) + check: + kc.relay == false + kc.maxMessageSize == "150KiB" + + test "fleet mode requires a kernelConf": + check parseLogosDeliveryConf("""{"mode": "fleet"}""").isErr() + + test "fleet mode rejects anything besides kernelConf": + check parseLogosDeliveryConf( + """{"mode": "fleet", "kernelConf": {}, "messagingOverrides": {"clusterId": 1}}""" + ) + .isErr() + # a preset for a fleet node goes inside kernelConf (WakuNodeConf.preset); fleet + # treats kernelConf as a finished object and overlays nothing onto it + check parseLogosDeliveryConf( + """{"mode": "fleet", "kernelConf": {}, "preset": "twn"}""" + ) + .isErr() + + test "kernelConf is rejected outside fleet mode": + check parseLogosDeliveryConf("""{"mode": "core", "kernelConf": {}}""").isErr() suite "LogosDelivery.new - construction (the app-dev entry)": asyncTest "builds the full messaging stack from mode + overrides": @@ -221,7 +278,7 @@ suite "LogosDelivery.new - construction (the app-dev entry)": lockNewGlobalBrokerContext: node = ( await LogosDelivery.new( - WakuMode.Core, + LogosDeliveryMode.Core, "", MessagingClientConf( clusterId: some(3'u16), @@ -243,7 +300,7 @@ suite "LogosDelivery.new - construction (the app-dev entry)": lockNewGlobalBrokerContext: node = ( await LogosDelivery.new( - WakuMode.Core, + LogosDeliveryMode.Core, "logostest", MessagingClientConf(listenIpv4: some(parseIpAddress("0.0.0.0"))), ) @@ -254,8 +311,28 @@ suite "LogosDelivery.new - construction (the app-dev entry)": suite "MessagingClientConf - store override": test "store opt-in overrides the mode default; protocol flags follow the mode": - let kc = MessagingClientConf(store: some(true)).toKernelConf(WakuMode.Edge).valueOr: + let kc = MessagingClientConf(store: some(true)).toWakuNodeConf( + LogosDeliveryMode.Edge + ).valueOr: raiseAssert error check: kc.store == true # Edge defaults store off; the explicit opt-in wins kc.relay == false # protocols are owned by the mode, not overridable + +suite "LogosDelivery.new - raw kernel construction": + asyncTest "a fleet node mounts the kernel only; start/stop tolerate the nil layers": + let kernel = MessagingClientConf(listenIpv4: some(parseIpAddress("0.0.0.0"))).toWakuNodeConf( + LogosDeliveryMode.Core + ).valueOr: + raiseAssert error + var node: LogosDelivery + lockNewGlobalBrokerContext: + node = (await LogosDelivery.new(KernelConf(kernel))).valueOr: + raiseAssert error + check: + not node.waku.isNil() + node.messagingClient.isNil() # no messaging client on a fleet node + node.reliableChannelManager.isNil() # no channel manager either + # start()/stop() must skip the nil messaging + channel layers, not deref them + (await node.start()).expect("start") + (await node.stop()).expect("stop") diff --git a/tests/channels/test_reliable_channel_send_receive.nim b/tests/channels/test_reliable_channel_send_receive.nim index a56cb0522..0a7ad8e20 100644 --- a/tests/channels/test_reliable_channel_send_receive.nim +++ b/tests/channels/test_reliable_channel_send_receive.nim @@ -28,7 +28,8 @@ import snapshot_codec const TestTimeout = chronos.seconds(15) proc createApiNodeConf(): WakuNodeConf = - var conf = MessagingClientConf().toKernelConf(messaging_conf.WakuMode.Core).valueOr: + var conf = MessagingClientConf() + .toWakuNodeConf(messaging_conf.LogosDeliveryMode.Core).valueOr: raiseAssert error conf.listenAddress = parseIpAddress("0.0.0.0") conf.tcpPort = Port(0) diff --git a/tests/test_waku.nim b/tests/test_waku.nim index 772387674..7ddde8ad9 100644 --- a/tests/test_waku.nim +++ b/tests/test_waku.nim @@ -12,8 +12,8 @@ import logos_delivery/waku/factory/conf_builder/conf_builder suite "LogosDelivery API - Create node": asyncTest "Create node with minimal configuration": ## Given - var nodeConf = MessagingClientConf().toKernelConf(Core).valueOr: - raiseAssert "toKernelConf failed: " & error + var nodeConf = MessagingClientConf().toWakuNodeConf(Core).valueOr: + raiseAssert "toWakuNodeConf failed: " & error nodeConf.clusterId = some(3'u16) nodeConf.rest = false @@ -31,8 +31,8 @@ suite "LogosDelivery API - Create node": asyncTest "Create node with full configuration": ## Given - var nodeConf = MessagingClientConf().toKernelConf(Core).valueOr: - raiseAssert "toKernelConf failed: " & error + var nodeConf = MessagingClientConf().toWakuNodeConf(Core).valueOr: + raiseAssert "toWakuNodeConf failed: " & error nodeConf.clusterId = some(99'u16) nodeConf.rest = false nodeConf.numShardsInNetwork = 16 @@ -62,8 +62,8 @@ suite "LogosDelivery API - Create node": asyncTest "Create node with mixed entry nodes (enrtree, multiaddr)": ## Given - var nodeConf = MessagingClientConf().toKernelConf(Core).valueOr: - raiseAssert "toKernelConf failed: " & error + var nodeConf = MessagingClientConf().toWakuNodeConf(Core).valueOr: + raiseAssert "toWakuNodeConf failed: " & error nodeConf.clusterId = some(42'u16) nodeConf.rest = false nodeConf.entryNodes = @[ diff --git a/tools/confutils/conf_from_json.nim b/tools/confutils/conf_from_json.nim index 9afb145f9..68a833a45 100644 --- a/tools/confutils/conf_from_json.nim +++ b/tools/confutils/conf_from_json.nim @@ -23,7 +23,7 @@ proc collectJsonFields*( jsonFields[lowerKey] = (key, value) return ok(jsonFields) -proc unknownKeysError( +proc unknownKeysError*( jsonFields: Table[string, (string, JsonNode)], prefix: string ): string = ## Format leftover JSON keys as an error message. From 28956856bb9eafa23aef14bd5108ff54df82cbc2 Mon Sep 17 00:00:00 2001 From: Fabiana Cecin Date: Thu, 9 Jul 2026 17:23:34 -0300 Subject: [PATCH 31/33] Fix interop tests (#4020) * logosdelivery_create_node takes a flat WakuNodeConf JSON conf (auto-detect) * Fix tools/confutils/conf_from_json.nim not accepting Port(0) * Fix: don't mount lightpush without relay * Add tests --- .../api/logos_delivery_conf_json.nim | 63 +++++++++++++ logos_delivery/api/messaging_conf.nim | 16 ++-- .../conf_builder/waku_conf_builder.nim | 2 +- tests/api/test_conf.nim | 88 ++++++++++++++++++- tools/confutils/conf_from_json.nim | 7 ++ 5 files changed, 167 insertions(+), 9 deletions(-) diff --git a/logos_delivery/api/logos_delivery_conf_json.nim b/logos_delivery/api/logos_delivery_conf_json.nim index 18e9ad485..260f4f2ae 100644 --- a/logos_delivery/api/logos_delivery_conf_json.nim +++ b/logos_delivery/api/logos_delivery_conf_json.nim @@ -13,6 +13,10 @@ const KeyKernelConf = "kernelconf" KeyMessagingOverrides = "messagingoverrides" KeyChannelsOverrides = "channelsoverrides" + # [Legacy flat JSON config] Keys that left the kernel and so must be lifted out of + # a flat blob before the WakuNodeConf walker sees them (it would reject them). + KeyReliabilityEnabled = "reliabilityenabled" + KeyReliability = "reliability" proc parseMode(s: string): Result[LogosDeliveryMode, string] = case s.strip().toLowerAscii() @@ -39,6 +43,50 @@ proc parseOverrides[T](defaults: T, node: JsonNode, label: string): Result[T, st ) return ok(conf) +proc parseFlatConf( + mode: LogosDeliveryMode, topJsonNode: var Table[string, (string, JsonNode)] +): ConfResult[LogosDeliveryConf] = + ## [Legacy flat JSON config] Flat shape: a blob of `WakuNodeConf` fields. `mode` + ## expands to protocol flags over raw kernel defaults, `reliabilityEnabled` routes + ## to the messaging conf (it left the kernel), and the rest parses as a + ## `WakuNodeConf`. Full stack. Delete this proc and its call site to drop support. + var messaging = MessagingClientConf() + var reliabilityFields: Table[string, (string, JsonNode)] + for key in [KeyReliabilityEnabled, KeyReliability]: + if topJsonNode.hasKey(key): + reliabilityFields[key] = topJsonNode.getOrDefault(key) + topJsonNode.del(key) + if reliabilityFields.len > 0: + ?applyJsonFieldsToConf( + messaging, reliabilityFields, "Failed to parse reliability field", + "Unrecognized reliability option(s) found", + ) + + # [Legacy flat JSON config] The blob is a raw WakuNodeConf, exactly as the + # pre-refactor flat create_node parsed it: start from the kernel defaults and apply + # the mode's protocol flags (the kernel no longer owns `mode`, so we expand it here, + # like the old kernel builder did), then let explicit fields override. + var kernel = ?defaultWakuNodeConf() + ?applyMode(kernel, mode) + ?applyJsonFieldsToConf( + kernel, topJsonNode, "Failed to parse config field", + "Unrecognized configuration option(s) found", + ) + + # [Legacy flat JSON config] Reliability is resolved from the preset by the messaging + # layer (the kernel no longer carries it), so a flat blob's `preset` must lift it + # here to stay faithful to master. An explicit reliability in the blob still wins. + if kernel.preset.len > 0: + messaging = merge(?resolvePreset(kernel.preset), messaging) + + return ok( + LogosDeliveryConf( + kernelConf: KernelConf(kernel), + messagingConf: some(messaging), + channelsConf: some(ReliableChannelManagerConf()), + ) + ) + proc parseLogosDeliveryConf*(jsonStr: string): ConfResult[LogosDeliveryConf] = var node: JsonNode try: @@ -70,6 +118,21 @@ proc parseLogosDeliveryConf*(jsonStr: string): ConfResult[LogosDeliveryConf] = err(unknownKeysError(top, "fleet mode takes only 'kernelConf'; unexpected")) return ok(LogosDeliveryConf.init(KernelConf(kernel))) + # [Legacy flat JSON config] A wrapper key marks our structured shape. Otherwise any + # leftover top-level key besides `preset` (mode is already consumed) is a bare + # kernel field -> flat blob. Delete this block to drop flat-shape support. + let hasWrapper = + top.hasKey(KeyMessagingOverrides) or top.hasKey(KeyChannelsOverrides) or + top.hasKey(KeyKernelConf) + if not hasWrapper: + var bareField = false + for k in top.keys: + if k != KeyPreset: + bareField = true + break + if bareField: + return parseFlatConf(mode, top) + var preset = "" var messagingOverrides = MessagingClientConf() var channelsOverrides = ReliableChannelManagerConf() diff --git a/logos_delivery/api/messaging_conf.nim b/logos_delivery/api/messaging_conf.nim index 1907e3540..da0d7c641 100644 --- a/logos_delivery/api/messaging_conf.nim +++ b/logos_delivery/api/messaging_conf.nim @@ -13,12 +13,8 @@ type LogosDeliveryMode* {.pure.} = enum Core # full service node Fleet # kernel-only node from a raw kernel config -proc toWakuNodeConf*( - self: MessagingClientConf, mode: LogosDeliveryMode -): ConfResult[WakuNodeConf] = - ## Mode sets the protocol flags; set fields map to their kernel counterpart. - var conf = ?defaultWakuNodeConf() - +proc applyMode*(conf: var WakuNodeConf, mode: LogosDeliveryMode): ConfResult[void] = + ## Sets the protocol flags implied by the mode. case mode of LogosDeliveryMode.Core: conf.relay = true @@ -36,6 +32,14 @@ proc toWakuNodeConf*( of LogosDeliveryMode.Fleet: return err("fleet mode takes a raw kernel config; use LogosDelivery.new(kernelConf)") + return ok() + +proc toWakuNodeConf*( + self: MessagingClientConf, mode: LogosDeliveryMode +): ConfResult[WakuNodeConf] = + ## Mode sets the protocol flags; set fields map to their kernel counterpart. + var conf = ?defaultWakuNodeConf() + ?applyMode(conf, mode) if self.store.isSome(): conf.store = self.store.get() diff --git a/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim b/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim index d312f620e..0eaec4934 100644 --- a/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim +++ b/logos_delivery/waku/factory/conf_builder/waku_conf_builder.nim @@ -797,7 +797,7 @@ proc build*( subscribeShards: subscribeShards, protectedShards: protectedShards, relay: relay, - lightPush: lightPush, + lightPush: lightPush and relay, # can't mount lightpush without relay peerExchangeService: peerExchange, rendezvous: rendezvous, peerExchangeDiscovery: true, diff --git a/tests/api/test_conf.nim b/tests/api/test_conf.nim index 4017d006b..280068516 100644 --- a/tests/api/test_conf.nim +++ b/tests/api/test_conf.nim @@ -172,8 +172,10 @@ suite "parseLogosDeliveryConf - JSON parsing": test "invalid JSON is rejected": check parseLogosDeliveryConf("{ not json }").isErr() - test "unknown top-level keys are rejected": - check parseLogosDeliveryConf("""{"logLevel": "INFO", "mode": "Core"}""").isErr() + test "a truly unknown top-level key is rejected (flat walker rejects the field)": + # NB: a *known* WakuNodeConf field at top level (e.g. logLevel) is now accepted + # as a flat blob; only a field that is not a WakuNodeConf field is rejected. + check parseLogosDeliveryConf("""{"totallyBogusField": "x", "mode": "Core"}""").isErr() test "override keys accept CLI switch names": let lc = parseLogosDeliveryConf( @@ -270,6 +272,8 @@ suite "parseLogosDeliveryConf - JSON parsing": .isErr() test "kernelConf is rejected outside fleet mode": + # kernelConf is a fleet-only wrapper. Under Core/Edge it is neither consumed by the + # structured path nor a flat kernel field, so it must surface as an unknown key. check parseLogosDeliveryConf("""{"mode": "core", "kernelConf": {}}""").isErr() suite "LogosDelivery.new - construction (the app-dev entry)": @@ -336,3 +340,83 @@ suite "LogosDelivery.new - raw kernel construction": # start()/stop() must skip the nil messaging + channel layers, not deref them (await node.start()).expect("start") (await node.stop()).expect("stop") + +# [Legacy flat JSON config] These suites cover the legacy flat shape; delete them +# together with parseFlatConf when flat-shape support is dropped. +suite "parseLogosDeliveryConf - flat WakuNodeConf shape (interop compatibility)": + test "a flat blob of kernel fields is detected and parsed as a full stack": + let lc = parseLogosDeliveryConf( + """{"relay": true, "clusterId": 7, "store": true, "filter": false}""" + ).valueOr: + raiseAssert error + check: + WakuNodeConf(lc.kernelConf).relay == true + WakuNodeConf(lc.kernelConf).clusterId == some(7'u16) + lc.messagingConf.isSome() # full stack + lc.channelsConf.isSome() + + test "flat blob carrying mode: mode expands to flags, explicit flags override": + let lc = parseLogosDeliveryConf( + """{"mode": "Edge", "relay": true, "clusterId": 7}""" + ).valueOr: + raiseAssert error + check: + WakuNodeConf(lc.kernelConf).relay == true + # explicit flat field overrides the Edge default + WakuNodeConf(lc.kernelConf).filter == false # Edge default, not set explicitly + WakuNodeConf(lc.kernelConf).clusterId == some(7'u16) + + test "flat blob's reliabilityEnabled routes to the messaging conf, not the kernel": + let lc = parseLogosDeliveryConf("""{"relay": true, "reliabilityEnabled": true}""").valueOr: + raiseAssert error + check: + lc.messagingConf.get().reliabilityEnabled == some(true) + + test "an unknown key in a flat blob is rejected": + check parseLogosDeliveryConf("""{"relay": true, "bogusKey": 1}""").isErr() + + test "a flat blob's preset lifts reliability into the messaging record": + # twn defines a p2pReliability; the flat path must resolve it into the messaging + # conf (the kernel no longer carries reliability), matching master. + let lc = parseLogosDeliveryConf("""{"preset": "twn", "relay": true}""").valueOr: + raiseAssert error + let fromPreset = resolvePreset("twn").valueOr: + raiseAssert error + check lc.messagingConf.get().reliabilityEnabled == fromPreset.reliabilityEnabled + + test "port 0 (auto-allocate) is accepted in a flat blob": + let lc = parseLogosDeliveryConf( + """{"relay": true, "tcpPort": 0, "discv5UdpPort": 0}""" + ).valueOr: + raiseAssert error + check: + WakuNodeConf(lc.kernelConf).tcpPort == Port(0) + WakuNodeConf(lc.kernelConf).discv5UdpPort == Port(0) + + test "port 0 is accepted in structured messagingOverrides": + let lc = parseLogosDeliveryConf("""{"messagingOverrides": {"tcp-port": 0}}""").valueOr: + raiseAssert error + check lc.messagingConf.get().p2pTcpPort == some(Port(0)) + + test "mode/preset with no bare field stays structured; a bare field flips it to flat": + # No bare kernel field -> structured: mode owns relay, no per-flag override. + let structured = parseLogosDeliveryConf( + """{"mode": "Edge", "preset": "logostest"}""" + ).valueOr: + raiseAssert error + check WakuNodeConf(structured.kernelConf).relay == false # Edge, structured + # Adding a bare kernel field (relay) flips to flat, where the explicit flag wins; + # relay == true is only reachable via the flat path, so it proves the routing. + let flat = parseLogosDeliveryConf( + """{"mode": "Edge", "preset": "logostest", "relay": true}""" + ).valueOr: + raiseAssert error + check: + WakuNodeConf(flat.kernelConf).relay == true + WakuNodeConf(flat.kernelConf).preset == "logostest" + + test "a wrapper key alongside a bare kernel field is rejected, not split": + # A wrapper key (messagingOverrides) marks the structured shape, so the blob does + # not flip to flat; the leftover bare field (relay) then has no structured home and + # is rejected. Guards against the discriminator silently splitting a mixed object. + check parseLogosDeliveryConf("""{"messagingOverrides": {}, "relay": true}""").isErr() diff --git a/tools/confutils/conf_from_json.nim b/tools/confutils/conf_from_json.nim index 68a833a45..7e78fd300 100644 --- a/tools/confutils/conf_from_json.nim +++ b/tools/confutils/conf_from_json.nim @@ -53,6 +53,13 @@ proc parseScalarInto[U]( let s = jsonScalarToString(jsonValue).valueOr: return err(prefix & " '" & confField & "' from JSON key '" & jsonKey & "': " & error) + when U is Port: + # `Port(0)` means "auto-allocate an ephemeral port" and is a valid value (see + # PR #3828). confutils' `parseCmdArg(Port)` enforces the CLI range 1-65535 and + # rejects "0", but the JSON config API must accept it, so handle 0 explicitly and + # delegate every other value to confutils. + if s == "0": + return ok(Port(0)) try: ok(parseCmdArg(U, s)) except CatchableError as e: From 9f2a1c89ffa2da2fdde4826e33326929cfd5712f Mon Sep 17 00:00:00 2001 From: Fabiana Cecin Date: Thu, 9 Jul 2026 18:14:37 -0300 Subject: [PATCH 32/33] Move api config modules to api/conf/ (#4022) Move api config modules to api/conf/ --- library/logos_delivery_api/node_api.nim | 2 +- logos_delivery/api/{ => conf}/kernel_conf.nim | 0 logos_delivery/api/{ => conf}/logos_delivery_conf.nim | 2 +- logos_delivery/api/{ => conf}/logos_delivery_conf_json.nim | 2 +- logos_delivery/api/{ => conf}/messaging_conf.nim | 2 +- logos_delivery/logos_delivery.nim | 4 ++-- logos_delivery/messaging/messaging_client.nim | 2 +- tests/api/test_api_receive.nim | 2 +- tests/api/test_api_send.nim | 2 +- tests/api/test_api_subscription.nim | 2 +- tests/api/test_conf.nim | 2 +- tests/channels/test_reliable_channel_send_receive.nim | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) rename logos_delivery/api/{ => conf}/kernel_conf.nim (100%) rename logos_delivery/api/{ => conf}/logos_delivery_conf.nim (96%) rename logos_delivery/api/{ => conf}/logos_delivery_conf_json.nim (99%) rename logos_delivery/api/{ => conf}/messaging_conf.nim (98%) diff --git a/library/logos_delivery_api/node_api.nim b/library/logos_delivery_api/node_api.nim index bed900683..d70c34252 100644 --- a/library/logos_delivery_api/node_api.nim +++ b/library/logos_delivery_api/node_api.nim @@ -7,7 +7,7 @@ import logos_delivery/api/types, logos_delivery/waku/api/events/health_events, logos_delivery/waku/api/events/peer_events, - logos_delivery/api/logos_delivery_conf_json, + logos_delivery/api/conf/logos_delivery_conf_json, ../declare_lib, ../json_event diff --git a/logos_delivery/api/kernel_conf.nim b/logos_delivery/api/conf/kernel_conf.nim similarity index 100% rename from logos_delivery/api/kernel_conf.nim rename to logos_delivery/api/conf/kernel_conf.nim diff --git a/logos_delivery/api/logos_delivery_conf.nim b/logos_delivery/api/conf/logos_delivery_conf.nim similarity index 96% rename from logos_delivery/api/logos_delivery_conf.nim rename to logos_delivery/api/conf/logos_delivery_conf.nim index 66acaa8fc..ae069b2df 100644 --- a/logos_delivery/api/logos_delivery_conf.nim +++ b/logos_delivery/api/conf/logos_delivery_conf.nim @@ -3,7 +3,7 @@ import std/options import results -import logos_delivery/api/messaging_conf +import logos_delivery/api/conf/messaging_conf import logos_delivery/channels/reliable_channel_manager export options, messaging_conf, reliable_channel_manager diff --git a/logos_delivery/api/logos_delivery_conf_json.nim b/logos_delivery/api/conf/logos_delivery_conf_json.nim similarity index 99% rename from logos_delivery/api/logos_delivery_conf_json.nim rename to logos_delivery/api/conf/logos_delivery_conf_json.nim index 260f4f2ae..e5df174e4 100644 --- a/logos_delivery/api/logos_delivery_conf_json.nim +++ b/logos_delivery/api/conf/logos_delivery_conf_json.nim @@ -4,7 +4,7 @@ import std/[json, options, strutils, tables] import results import tools/confutils/conf_from_json -import logos_delivery/api/logos_delivery_conf +import logos_delivery/api/conf/logos_delivery_conf const # Lowercased, since `collectJsonFields` keys the object case-insensitively. diff --git a/logos_delivery/api/messaging_conf.nim b/logos_delivery/api/conf/messaging_conf.nim similarity index 98% rename from logos_delivery/api/messaging_conf.nim rename to logos_delivery/api/conf/messaging_conf.nim index da0d7c641..bf6a81695 100644 --- a/logos_delivery/api/messaging_conf.nim +++ b/logos_delivery/api/conf/messaging_conf.nim @@ -2,7 +2,7 @@ import std/options import std/net import results -import logos_delivery/api/kernel_conf +import logos_delivery/api/conf/kernel_conf import logos_delivery/messaging/messaging_client import logos_delivery/waku/factory/networks_config diff --git a/logos_delivery/logos_delivery.nim b/logos_delivery/logos_delivery.nim index 9ce568740..0e1f77104 100644 --- a/logos_delivery/logos_delivery.nim +++ b/logos_delivery/logos_delivery.nim @@ -42,7 +42,7 @@ import logos_delivery/messaging/api/[subscription, send] export subscription, send import logos_delivery/api/events/messaging_client_events export messaging_client_events -import logos_delivery/api/messaging_conf +import logos_delivery/api/conf/messaging_conf export messaging_conf # Reliable Channel layer @@ -61,7 +61,7 @@ import logos_delivery/waku/factory/waku_conf import logos_delivery/waku/factory/app_callbacks import tools/confutils/cli_args import logos_delivery/waku/node/health_monitor/online_monitor -import logos_delivery/api/logos_delivery_conf +import logos_delivery/api/conf/logos_delivery_conf export logos_delivery_conf logScope: diff --git a/logos_delivery/messaging/messaging_client.nim b/logos_delivery/messaging/messaging_client.nim index ef69c2d43..acb5dcd35 100644 --- a/logos_delivery/messaging/messaging_client.nim +++ b/logos_delivery/messaging/messaging_client.nim @@ -6,7 +6,7 @@ import results, chronos import confutils/defs import libp2p/crypto/crypto import logos_delivery/waku/common/logging -import logos_delivery/api/kernel_conf +import logos_delivery/api/conf/kernel_conf import chronicles import logos_delivery/api/messaging_client_api, diff --git a/tests/api/test_api_receive.nim b/tests/api/test_api_receive.nim index 744cc5e52..870f85860 100644 --- a/tests/api/test_api_receive.nim +++ b/tests/api/test_api_receive.nim @@ -21,7 +21,7 @@ import ] import logos_delivery/waku/factory/waku_conf import tools/confutils/cli_args -import logos_delivery/api/messaging_conf +import logos_delivery/api/conf/messaging_conf const TestTimeout = chronos.seconds(60) diff --git a/tests/api/test_api_send.nim b/tests/api/test_api_send.nim index e913d76a0..8df564b0d 100644 --- a/tests/api/test_api_send.nim +++ b/tests/api/test_api_send.nim @@ -8,7 +8,7 @@ import ../waku_archive/archive_utils import logos_delivery, logos_delivery/waku/[waku_node, waku_core, waku_relay/protocol] import logos_delivery/waku/factory/waku_conf import tools/confutils/cli_args -import logos_delivery/api/messaging_conf +import logos_delivery/api/conf/messaging_conf type SendEventOutcome {.pure.} = enum Sent diff --git a/tests/api/test_api_subscription.nim b/tests/api/test_api_subscription.nim index 7ba3b2bf5..90d160bc3 100644 --- a/tests/api/test_api_subscription.nim +++ b/tests/api/test_api_subscription.nim @@ -18,7 +18,7 @@ import ] import logos_delivery/waku/factory/waku_conf import tools/confutils/cli_args -import logos_delivery/api/messaging_conf +import logos_delivery/api/conf/messaging_conf const TestTimeout = chronos.seconds(10) const NegativeTestTimeout = chronos.seconds(2) diff --git a/tests/api/test_conf.nim b/tests/api/test_conf.nim index 280068516..705c00825 100644 --- a/tests/api/test_conf.nim +++ b/tests/api/test_conf.nim @@ -3,7 +3,7 @@ import std/[options, net], results, chronos, testutils/unittests import brokers/broker_context import logos_delivery -import logos_delivery/api/logos_delivery_conf_json +import logos_delivery/api/conf/logos_delivery_conf_json import logos_delivery/waku/factory/[waku_conf, networks_config] import logos_delivery/waku/common/logging diff --git a/tests/channels/test_reliable_channel_send_receive.nim b/tests/channels/test_reliable_channel_send_receive.nim index 0a7ad8e20..b76851006 100644 --- a/tests/channels/test_reliable_channel_send_receive.nim +++ b/tests/channels/test_reliable_channel_send_receive.nim @@ -13,7 +13,7 @@ import logos_delivery/waku/factory/waku_conf import logos_delivery/api/events/messaging_client_events as waku_message_events import logos_delivery/api/messaging_client_api import tools/confutils/cli_args -import logos_delivery/api/messaging_conf +import logos_delivery/api/conf/messaging_conf import logos_delivery/channels/reliable_channel_manager import logos_delivery/channels/encryption/noop_encryption From 53c084dfdb2c4366bbcb8e8ee967c13f4b9f294c Mon Sep 17 00:00:00 2001 From: Fabiana Cecin Date: Fri, 10 Jul 2026 09:33:28 -0300 Subject: [PATCH 33/33] 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 --- logos_delivery/api/conf/channels_conf.nim | 15 +++++ .../api/conf/logos_delivery_conf.nim | 4 +- logos_delivery/api/conf/messaging_conf.nim | 46 ++++++++++++- .../channels/reliable_channel_manager.nim | 28 ++------ logos_delivery/messaging/messaging_client.nim | 67 +++---------------- 5 files changed, 78 insertions(+), 82 deletions(-) create mode 100644 logos_delivery/api/conf/channels_conf.nim diff --git a/logos_delivery/api/conf/channels_conf.nim b/logos_delivery/api/conf/channels_conf.nim new file mode 100644 index 000000000..5d19e802a --- /dev/null +++ b/logos_delivery/api/conf/channels_conf.nim @@ -0,0 +1,15 @@ +import std/options + +type ReliableChannelManagerConf* = object + ## All-`Option` partial; unset fields fall back to `createReliableChannel` defaults. + segmentationEnableReedSolomon*: Option[bool] + ## Add Reed-Solomon parity segments for recovery of lost segments. + segmentationSegmentSizeBytes*: Option[int] ## Maximum segment size in bytes. + sdsAcknowledgementTimeoutMs*: Option[int] + ## Time to wait before retransmitting an unacknowledged message. + sdsMaxRetransmissions*: Option[int] + ## Maximum retransmission attempts before delivery fails. + sdsCausalHistorySize*: Option[int] ## Number of message ids kept in causal history. + rateLimitEnabled*: Option[bool] ## Enable rate limiting. + rateLimitEpochPeriodSec*: Option[int] ## Rate-limit epoch length in seconds. + rateLimitMessagesPerEpoch*: Option[int] ## Messages allowed per rate-limit epoch. diff --git a/logos_delivery/api/conf/logos_delivery_conf.nim b/logos_delivery/api/conf/logos_delivery_conf.nim index ae069b2df..e5ac7432a 100644 --- a/logos_delivery/api/conf/logos_delivery_conf.nim +++ b/logos_delivery/api/conf/logos_delivery_conf.nim @@ -4,9 +4,9 @@ import std/options import results import logos_delivery/api/conf/messaging_conf -import logos_delivery/channels/reliable_channel_manager +import logos_delivery/api/conf/channels_conf -export options, messaging_conf, reliable_channel_manager +export options, messaging_conf, channels_conf type LogosDeliveryConf* = object ## Aggregates the per-layer config objects. A layer is mounted iff its config diff --git a/logos_delivery/api/conf/messaging_conf.nim b/logos_delivery/api/conf/messaging_conf.nim index bf6a81695..86a73575b 100644 --- a/logos_delivery/api/conf/messaging_conf.nim +++ b/logos_delivery/api/conf/messaging_conf.nim @@ -1,18 +1,60 @@ import std/options import std/net import results +import libp2p/crypto/crypto import logos_delivery/api/conf/kernel_conf -import logos_delivery/messaging/messaging_client +import logos_delivery/waku/common/logging import logos_delivery/waku/factory/networks_config -export kernel_conf, messaging_client +export kernel_conf type LogosDeliveryMode* {.pure.} = enum Edge # client-only node Core # full service node Fleet # kernel-only node from a raw kernel config +type MessagingClientConf* = object + clusterId* {.name: "cluster-id".}: Option[uint16] ## Network cluster id. + numShardsInCluster* {.name: "num-shards-in-network".}: Option[uint16] + ## Number of shards in the cluster. + p2pTcpPort* {.name: "tcp-port".}: Option[Port] ## TCP listening port. + discv5UdpPort* {.name: "discv5-udp-port".}: Option[Port] ## discv5 UDP port. + websocketSupport* {.name: "websocket-support".}: Option[bool] + ## Enable the websocket transport. + websocketPort* {.name: "websocket-port".}: Option[Port] ## Websocket listening port. + quicSupport* {.name: "quic-support".}: Option[bool] ## Enable the QUIC transport. + quicPort* {.name: "quic-port".}: Option[Port] ## QUIC (UDP) listening port. + listenIpv4* {.name: "listen-address".}: Option[IpAddress] ## Inbound bind address. + maxMessageSize* {.name: "max-msg-size".}: Option[string] + ## Maximum accepted message size (e.g. "150 KiB"). + entryNodes* {.name: "entry-node".}: Option[seq[string]] + ## Bootstrap / connectivity nodes (enrtree or multiaddr). + ethRpcEndpoints* {.name: "rln-relay-eth-client-address".}: Option[seq[EthRpcUrl]] + ## Ethereum RPC endpoints (required for RLN validation); multiple for fail-over. + rlnContractAddress* {.name: "rln-relay-eth-contract-address".}: Option[string] + ## RLN contract address; when set, RLN validation is enabled. + rlnChainId* {.name: "rln-relay-chain-id".}: Option[uint] + ## Chain id the RLN contract is deployed on. + rlnEpochSizeSec* {.name: "rln-relay-epoch-sec".}: Option[uint] + ## RLN epoch size, in seconds. + reliabilityEnabled* {.name: "reliability".}: Option[bool] + ## Enable store-based send reliability. + store*: Option[bool] ## Enable the store protocol. + storenode* {.name: "storenode".}: Option[string] + storeMessageDbUrl* {.name: "store-message-db-url".}: Option[string] + ## Database connection URL for the store service's persistent storage. + storeMessageRetentionPolicy* {.name: "store-message-retention-policy".}: + Option[string] ## Store retention policy (e.g. "time:3600;size:1GB"). + storeMaxNumDbConnections* {.name: "store-max-num-db-connections".}: Option[int] + ## Maximum number of simultaneous store database connections. + logLevel* {.name: "log-level".}: Option[logging.LogLevel] + ## Process log level (TRACE..FATAL); applied by the kernel on node creation. + logFormat* {.name: "log-format".}: Option[logging.LogFormat] + ## Process log format (TEXT or JSON); applied by the kernel on node creation. + nodeKey* {.name: "nodekey".}: Option[crypto.PrivateKey] + ## P2P node private key (64-char hex): stable identity / peerId across restarts. + proc applyMode*(conf: var WakuNodeConf, mode: LogosDeliveryMode): ConfResult[void] = ## Sets the protocol flags implied by the mode. case mode diff --git a/logos_delivery/channels/reliable_channel_manager.nim b/logos_delivery/channels/reliable_channel_manager.nim index 46e3c3912..6f8ca14dc 100644 --- a/logos_delivery/channels/reliable_channel_manager.nim +++ b/logos_delivery/channels/reliable_channel_manager.nim @@ -5,7 +5,7 @@ ## ## See: https://lip.logos.co/messaging/raw/reliable-channel-api.html -import std/[options, tables] +import std/tables import results import chronos import chronicles @@ -15,30 +15,16 @@ import brokers/broker_context import logos_delivery/api/types import logos_delivery/api/reliable_channel_manager_api +import logos_delivery/api/conf/channels_conf import ./reliable_channel -export reliable_channel +export reliable_channel, channels_conf -type - ReliableChannelManagerConf* = object - ## All-`Option` partial; unset fields fall back to `createReliableChannel` defaults. - segmentationEnableReedSolomon*: Option[bool] - ## Add Reed-Solomon parity segments for recovery of lost segments. - segmentationSegmentSizeBytes*: Option[int] ## Maximum segment size in bytes. - sdsAcknowledgementTimeoutMs*: Option[int] - ## Time to wait before retransmitting an unacknowledged message. - sdsMaxRetransmissions*: Option[int] - ## Maximum retransmission attempts before delivery fails. - sdsCausalHistorySize*: Option[int] ## Number of message ids kept in causal history. - rateLimitEnabled*: Option[bool] ## Enable rate limiting. - rateLimitEpochPeriodSec*: Option[int] ## Rate-limit epoch length in seconds. - rateLimitMessagesPerEpoch*: Option[int] ## Messages allowed per rate-limit epoch. - - ReliableChannelManager* = ref object ## Implements `ReliableChannelApi`. - channels*: Table[ChannelId, ReliableChannel] ## read by `channels/api.nim` - conf*: ReliableChannelManagerConf - brokerCtx*: BrokerContext +type ReliableChannelManager* = ref object ## Implements `ReliableChannelApi`. + channels*: Table[ChannelId, ReliableChannel] ## read by `channels/api.nim` + conf*: ReliableChannelManagerConf + brokerCtx*: BrokerContext proc new*( T: type ReliableChannelManager, diff --git a/logos_delivery/messaging/messaging_client.nim b/logos_delivery/messaging/messaging_client.nim index acb5dcd35..1044122d3 100644 --- a/logos_delivery/messaging/messaging_client.nim +++ b/logos_delivery/messaging/messaging_client.nim @@ -1,70 +1,23 @@ ## Messaging layer core: the `MessagingClient` type plus its construction and ## lifecycle. The public operations (subscribe / unsubscribe / send) live in ## `messaging/api.nim`. -import std/[options, net] -import results, chronos -import confutils/defs -import libp2p/crypto/crypto -import logos_delivery/waku/common/logging -import logos_delivery/api/conf/kernel_conf -import chronicles +import std/options +import results, chronos, chronicles import + logos_delivery/api/conf/messaging_conf, logos_delivery/api/messaging_client_api, logos_delivery/waku/waku, logos_delivery/waku/factory/conf_builder/waku_conf_builder, logos_delivery/messaging/delivery_service/[recv_service, send_service] -# Surfaces the messaging API interface (and its Message* events) to consumers. -export messaging_client_api +export messaging_client_api, messaging_conf -type - MessagingClientConf* = object - clusterId* {.name: "cluster-id".}: Option[uint16] ## Network cluster id. - numShardsInCluster* {.name: "num-shards-in-network".}: Option[uint16] - ## Number of shards in the cluster. - p2pTcpPort* {.name: "tcp-port".}: Option[Port] ## TCP listening port. - discv5UdpPort* {.name: "discv5-udp-port".}: Option[Port] ## discv5 UDP port. - websocketSupport* {.name: "websocket-support".}: Option[bool] - ## Enable the websocket transport. - websocketPort* {.name: "websocket-port".}: Option[Port] ## Websocket listening port. - quicSupport* {.name: "quic-support".}: Option[bool] ## Enable the QUIC transport. - quicPort* {.name: "quic-port".}: Option[Port] ## QUIC (UDP) listening port. - listenIpv4* {.name: "listen-address".}: Option[IpAddress] ## Inbound bind address. - maxMessageSize* {.name: "max-msg-size".}: Option[string] - ## Maximum accepted message size (e.g. "150 KiB"). - entryNodes* {.name: "entry-node".}: Option[seq[string]] - ## Bootstrap / connectivity nodes (enrtree or multiaddr). - ethRpcEndpoints* {.name: "rln-relay-eth-client-address".}: Option[seq[EthRpcUrl]] - ## Ethereum RPC endpoints (required for RLN validation); multiple for fail-over. - rlnContractAddress* {.name: "rln-relay-eth-contract-address".}: Option[string] - ## RLN contract address; when set, RLN validation is enabled. - rlnChainId* {.name: "rln-relay-chain-id".}: Option[uint] - ## Chain id the RLN contract is deployed on. - rlnEpochSizeSec* {.name: "rln-relay-epoch-sec".}: Option[uint] - ## RLN epoch size, in seconds. - reliabilityEnabled* {.name: "reliability".}: Option[bool] - ## Enable store-based send reliability. - store*: Option[bool] ## Enable the store protocol. - storenode* {.name: "storenode".}: Option[string] - storeMessageDbUrl* {.name: "store-message-db-url".}: Option[string] - ## Database connection URL for the store service's persistent storage. - storeMessageRetentionPolicy* {.name: "store-message-retention-policy".}: - Option[string] ## Store retention policy (e.g. "time:3600;size:1GB"). - storeMaxNumDbConnections* {.name: "store-max-num-db-connections".}: Option[int] - ## Maximum number of simultaneous store database connections. - logLevel* {.name: "log-level".}: Option[logging.LogLevel] - ## Process log level (TRACE..FATAL); applied by the kernel on node creation. - logFormat* {.name: "log-format".}: Option[logging.LogFormat] - ## Process log format (TEXT or JSON); applied by the kernel on node creation. - nodeKey* {.name: "nodekey".}: Option[crypto.PrivateKey] - ## P2P node private key (64-char hex): stable identity / peerId across restarts. - - MessagingClient* = ref object - brokerCtx*: BrokerContext - waku*: Waku ## The Waku kernel this layer drives; read by `messaging/api/*`. - sendService*: SendService - recvService*: RecvService - started*: bool +type MessagingClient* = ref object + brokerCtx*: BrokerContext + waku*: Waku ## The Waku kernel this layer drives; read by `messaging/api/*`. + sendService*: SendService + recvService*: RecvService + started*: bool proc new*( T: type MessagingClient, conf: MessagingClientConf, waku: Waku