108 Commits

Author SHA1 Message Date
Fabiana Cecin
ce918b0819
chore: replace Option with Opt (#4035)
* change all usage of std.options.Option[T] to results.Opt[T]
* fix broken apps and examples (to validate refactor)
* removed all std/options code added for libp2p v2 migration
* add broker Opt codec to persistency/backend_comm.nim
* add a readValue overload for Opt[T] in tools/confutils/cli_args.nim
* keep Option where required (Presto, confutils' config_file.nim)
* Change generateRlnProof error handling
* Fix imports
2026-07-16 14:02:17 -03:00
Tanya S
77cb8a6c7a
refresh merkle proof cache reactively on msg publish rejection (#4013)
* refresh merkle proof cache reactively on lightpush rejection

* Add tests for lightpush force refresh regenerates proof

* Fix linting

* Add nonce rollback for lightpush publish retry

* Fix nonce rollback race: make rollbackNonce CAS-conditional

Two bugs in the previous blind-decrement rollbackNonce():

1. Concurrent draw: another proof generated between attempt 1 and the retry
   advances nextNonce, so decrementing steals a nonce already claimed by
   another live message — nullifier collision, on-chain slashable.

2. Pre-attached proof: if the incoming message already carries a proof,
   checkAndGenerateRLNProof short-circuits and draws no nonce, yet the retry
   still called rollback, stomping on whatever nonce the preceding message
   legitimately drew.

Fix: rollbackNonce now takes the specific nonce as a parameter and only
decrements when nextNonce == nonce + 1. checkAndGenerateRLNProof returns
(msg, messageId) so callers can thread the drawn id through to the retry
as reuseMessageId; generateRLNProof no longer performs an implicit rollback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Extend reactive Merkle proof refresh to REST relay and broker paths

With PathCheckMinInterval removed, a non-empty merkleProofCache is trusted
forever unless force=true is passed. Previously only the two lightpush retry
paths ever passed force=true; the REST relay publish handlers (static and
auto-sharding) and the broker proof provider had no rejection feedback loop,
so a sliding root window would permanently reject their messages until restart.

- REST relay handlers: run validateMessage first; if it returns
  RlnValidatorErrorMsg, force-refresh the cached path and retry validation once
  before publishing — mirroring the lightpush reactive pattern.
- Broker provider (rln.nim): decode the generated proof bytes, call
  validateRoot on the embedded Merkle root, and force-refresh + regenerate
  when the root is outside the acceptance window — since the broker has no
  external rejection signal to react to.

Tests added for all three new paths, using the corrupted-cache technique
(all-zero merkleProofCache → garbage root → rejection → retry).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Gate lightpush Merkle refresh on RlnValidatorErrorMsg for 420 responses

420 INVALID_MESSAGE is returned for any validateMessage failure, not just
RLN ones (e.g. oversized messages), so a bare status-code check triggers
an unbounded on-chain fetchMerkleProofElements RPC per rejected message.
Match the legacy-lightpush path: require the error description to contain
RlnValidatorErrorMsg before refreshing; 504 OUT_OF_RLN_PROOF remains
unconditional as it is unambiguously RLN-specific.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix merge conflict residue: replace RlnResult with Result in proof.nim

RlnResult[T] was removed as a type alias in commit 0a1700e23 (replace RLN
specific Result with generic Result). The merge left one stale reference at
proof.nim:62; replace with the equivalent Result[seq[byte], string].

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix ResultDefect on success and stale test stubs in lightpush retry path

Two bugs left by a bad merge:

1. lightpush.nim: isRlnRelatedFailure was computed before firstResult.isOk()
   was checked, accessing firstResult.error unconditionally. When publish
   succeeds, this raises a ResultDefect. Reorder the guard so isOk()/isNone()
   short-circuits before the error fields are touched.

2. test_wakunode_lightpush.nim: the 420-retry stubs used descriptions
   ("simulated stale merkle path", "still stale") that don't contain
   RlnValidatorErrorMsg. The code gates 420 retries on that substring to
   distinguish RLN rejections from unrelated INVALID_MESSAGE responses
   (e.g. oversized messages). Update stubs to emit RlnValidatorErrorMsg so
   the retry path fires as the tests expect.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix linting

* Dedupe checkAndGenerateRLNProof force-refresh test

Same test existed in both test_wakunode_lightpush.nim and
test_wakunode_legacy_lightpush.nim. It exercises the checkAndGenerateRLNProof
primitive (short-circuit override on force=true + nonce rollback +
cache refetch), not lightpush-specific behavior. The modern lightpush copy
provides full coverage; the legacy copy paid the anvil/on-chain setup cost
for zero incremental coverage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Bound lightpush RLN refresh-retry with a 5s timeout

The reactive refresh + retry path calls into the RLN contract (eth_call for
getMerkleProof) then republishes over libp2p — neither has an outer bound,
so a hanging RPC endpoint could stall the caller indefinitely. Wrap the
retry in withTimeout(RlnRefreshRetryTimeout); on timeout, log and return
the original error rather than hang. Applied to both the legacy and modern
lightpushPublish retry paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Extract broker RLN proof provider into generateRLNProofWithRootRefresh

The broker proof provider's inline logic — generate proof, decode it,
self-validate the root against the acceptable-root window, and on stale
root force-refresh + regenerate — is a reusable primitive on its own.
Lift it into rln/proof.nim next to the existing generateRLNProof so the
provider closure in mount() collapses to a single call, and any future
non-broker caller can share the same flow.

Pure refactor; behavior identical.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Extract REST relay handlers' RLN attach+validate+retry into a helper

Both the static and auto POST handlers duplicated the same block: attach an
RLN proof, run wakuRelay.validateMessage, and on RlnValidatorErrorMsg
substring force-refresh the merkle path and revalidate. Lift the block into
attachRlnProofValidateWithRetry alongside a small RlnPublishError variant
so the handlers can keep dispatching 500 for proof-gen failures and 400
for validator rejections. Each handler is now ~15 lines instead of ~35 and
the retry contract lives in one place.

Pure refactor; behavior unchanged. Verified against
tests/wakunode_rest/test_rest_relay.nim (13/13) including the two stale-
RLN-proof retry tests added earlier on this branch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Simplify legacy lightpush autosharding path to a direct getShard call

The single-content-topic path went through getShardsFromContentTopics,
iterated the resulting table under the assumption it was 1-entry, and
had a defensive fallthrough (`resolved = false` → bare `return`) for
the empty-table case. That case is unreachable: on success the table
always has exactly one entry, and the fallthrough returned a default-
initialized Result — not behavior worth preserving.

Replace with the same two-step pattern the modern lightpushPublish uses
(NsContentTopic.parse → sharding.getShard) so both handlers share a
shape and the (now-explicit) failure paths surface useful error strings.

Verified against tests/node/test_wakunode_legacy_lightpush.nim (9/9).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Break up legacyLightpushPublish into single-purpose helpers

legacyLightpushPublish had grown to ~100 lines with three concerns
interleaved: choosing between client/self-request publish, resolving the
effective pubsub topic, and running the RLN refresh-retry with timeout.
Lift each into a private top-level proc so the main proc reads top-to-
bottom in ~35 lines: precondition → prepare message + proof → resolve
topic → publish once → RLN-refresh retry if applicable.

The new helpers are:
  - internalLegacyLightpushPublish — client vs self-request dispatch
    (was the inline closure)
  - resolveLegacyPubsubTopic       — explicit param or autosharding
  - runRlnRefreshRetry             — force-refresh proof + retry with
    RlnRefreshRetryTimeout bound; returns caller's fallback on timeout

Nonce now appears in a top-level signature, so import it directly from
rln/nonce_manager rather than re-exporting through rln/proof.nim (the
latter would leak nonce_manager's bulk chronos/times exports and clash
with waku_rendezvous overload resolution).

Pure refactor; behavior unchanged. Verified against
tests/node/test_wakunode_legacy_lightpush.nim (9/9) and
tests/node/test_wakunode_lightpush.nim (11/11).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Replace forceMerkleProofRefresh flag with invalidateMerkleProofCache

The forceMerkleProofRefresh bool was threaded through five procs
(checkAndGenerateRLNProof → generateRLNProof → generateProof base + on-chain
→ ensureFreshMerkleProofPath) purely so a caller could ask the merkle path
to be refetched from chain on a stale-RLN retry. Reading any one of those
signatures required a mental stack trace back up to the retry caller to
know what "force" meant.

Flip the contract: expose invalidateMerkleProofCache on the group manager;
callers invoke it on the retry path to empty the cache, and the normal
proof-gen flow then sees an empty cache and refetches on its own. No flag
crosses procs.

Removed forceMerkleProofRefresh from:
  - group_manager_base.generateProof (+ on_chain override)
  - ensureFreshMerkleProofPath (its `force` param)
  - generateRLNProof

Renamed on checkAndGenerateRLNProof: forceMerkleProofRefresh → regenerate.
It now controls one thing — bypassing the "message already has a proof"
short-circuit — and never propagates downstream. Callers pair it with
invalidateMerkleProofCache when they need the refetch semantics too.

Retry callers updated in lockstep: generateRLNProofWithRootRefresh, REST
attachRlnProofValidateWithRetry, and both legacy + modern lightpush retry
paths now call groupManager.invalidateMerkleProofCache() before regenerating.
The primitive test in test_wakunode_lightpush.nim now exercises the pair
directly, and the onchain ensureFreshMerkleProofPath test is rewritten to
exercise invalidate → ensureFresh as one flow (retiring its `force = true`
variant).

Pure refactor; behavior unchanged. Verified against:
  tests/waku_rln_relay/test_rln_group_manager_onchain.nim   (29/29)
  tests/wakunode_rest/test_rest_relay.nim                   (13/13)
  tests/waku_rln_relay/test_wakunode_rln_relay.nim          (5/5, 1 skip)
  tests/node/test_wakunode_lightpush.nim                    (11/11)
  tests/node/test_wakunode_legacy_lightpush.nim             (9/9)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Reject empty merkle proof cache in on-chain generateProof

An empty cache passes the existing mod-32 length check and silently
produces a witness with no path elements, yielding a proof no validator
will accept. Catch the case explicitly so the failure surfaces at the
callsite rather than after a wasted publish round-trip.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Drop nonce reuse on RLN retry; consume a fresh nonce instead

The retry path threaded a rejected attempt's message id back so
NonceManager.rollbackNonce could CAS the counter and reclaim the slot.
This bought at most one nonce per retry (a rare event) at the cost of
an Option[Nonce] param, a tuple return, and a concurrent-draw race the
CAS had to defend against.

Simpler to optimistically waste the nonce: checkAndGenerateRLNProof
returns Result[WakuMessage, string] and callers drop the tuple
destructuring. NonceManager.rollbackNonce and its tests, now dead,
are removed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Split checkAndGenerateRLNProof: extract attachRLNProof

The `regenerate: bool` param was control coupling: every caller passing
`regenerate = true` was a retry path that had already established
`rln.isSome()` (it just called `invalidateMerkleProofCache` on the
group manager) and wanted an unconditional fresh proof, not a "check
and maybe generate".

Extract that into `attachRLNProof(r: Rln, message)`, which always draws
a fresh nonce and rebuilds the proof, replacing any existing one.
`checkAndGenerateRLNProof(rln: Option[Rln], message)` keeps only its
"check" behaviour — short-circuit on an existing proof, pass through
when RLN is not configured — and delegates to `attachRLNProof`.

Taking a concrete `Rln` deletes the previously unreachable
`regenerate = true` + `rln.isNone()` branch (which would have
republished the stale rejected proof) at the type level, and removes
the duplicated nonce-draw/generateProof/encode body that
checkAndGenerateRLNProof carried alongside generateRLNProof.

The two lightpush retry sites and the invalidate+regenerate test now
call attachRLNProof directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Bound only the proof refresh on lightpush RLN retry, not the republish

RlnRefreshRetryTimeout previously wrapped the whole retry leg: merkle
path refetch + proof regeneration + the second publish. Sharing one
clock meant a slow (but successful) refetch could leave no budget for
the publish, cancelling it mid-flight — burning the freshly drawn nonce
and, worse, reporting the original RLN rejection while the retried
request may already have reached the service node and been relayed.

Move the withTimeout to cover only attachRLNProof (the on-chain
eth_call + proof regeneration — the one step with an unbounded external
dependency). The retried publish runs unbounded after it, matching the
first attempt's semantics, so slow transports (e.g. an optional mix
route) cannot be cancelled by the refresh budget and the caller always
receives the publish's actual result.

On refresh timeout the retry publish never starts and the original
rejection is returned, so a "failed" answer now guarantees no second
request is in flight.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Shield the shared merkle path refetch from caller cancellation

ensureFreshMerkleProofPath coalesces concurrent callers onto a single
in-flight refetch future and awaited it directly. Chronos propagates
cancellation into the awaited future, so one caller being cancelled
(e.g. a publish retry whose RlnRefreshRetryTimeout fired mid-refetch)
cancelled the shared future under every coalesced caller — and threw
away an eth_call that may have been about to complete.

Await the in-flight future via join() instead: cancelling a caller only
detaches its callback. The refetch always runs to completion, coalesced
callers keep waiting, and even when every caller times out the cache
still gets populated in the background so the next publish fast-paths
instead of paying the RPC round-trip again.

Detaching is safe because doRefresh cannot fail with an unhandled
exception: the eth_call wrappers catch CatchableError and return
Result, and failures surface to callers through fetchOk / an empty
cache exactly as before.

Adds a test cancelling the initiating caller mid-refetch and asserting
the shared future survives and the coalesced caller completes with a
populated cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Move message publish retries out of the kernel on stale RLN proof

On a stale-merkle-proof rejection the kernel and REST layers no longer
republish internally. They schedule a detached merkle proof refresh and
fail fast with a distinguishable, retry-worthy error, leaving the retry
decision to the caller.

- add GroupManager.scheduleMerkleProofRefresh: drops the cached path and
  asyncSpawns the refetch (join-shielded, so a cancelled caller never
  cancels the shared refetch); base is a no-op.
- v3 lightpush (node.lightpushPublish): on an RLN-related 420/504, call
  scheduleMerkleProofRefresh and return immediately, normalized to 504
  OUT_OF_RLN_PROOF with RlnProofRefreshScheduledMsg. The internal
  republish and its bounded timeout are gone.
- send service: lightpushPublishToAny now routes through
  node.lightpushPublish so every NextRoundRetry round regenerates the
  proof against the current cache; the existing OUT_OF_RLN_PROOF ->
  NextRoundRetry mapping is the retry.
- REST lightpush: pass the 504 through unchanged (no internal retry).
- REST relay: attachRlnProofValidateWithRetry -> attachRlnProofAndValidate;
  validate once, on RLN-invalid schedule the refresh and return 503 with
  the marker instead of regenerating and revalidating in-request.
- rename RlnRefreshRetryTimeout -> RlnMerkleProofRefreshTimeout; it now
  bounds only the legacy lightpush refresh, which keeps its one-shot
  internal retry (no wire error taxonomy to delegate to callers).

Not touched: legacy lightpush retry behavior, the proof.nim
attachRLNProof/checkAndGenerateRLNProof split, and the merkle path
refetch/coalescing internals of ensureFreshMerkleProofPath.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Tighten RLN proof-refresh docstrings and comments

Condense the verbose comments introduced with the reactive merkle
proof-refresh work. No code changes.

- group_manager_base: base methods carry the contract; the
  OnchainGroupManager overrides keep only their concrete behavior
  instead of repeating the rationale.
- proof.nim, lightpush.nim, constants.nim: trim multi-line docstrings
  and inline comments to their load-bearing facts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Make the merkle proof path refresh safe against concurrent invalidation

ensureFreshMerkleProofPath returns the fetched path instead of leaving
callers to re-read merkleProofCache. generateProof uses the returned
value, so an invalidate landing during the refresh's updateMemberCache
suspension can no longer empty the cache under an in-flight proof-gen
and trip its empty-cache guard. The cache field is now only a hint for
the next call.

invalidateMerkleProofCache also bumps merkleProofCacheGeneration. The
refresh stamps the generation before each fetchMerkleProofElements and
only caches a result whose generation is unchanged, otherwise it fetches
again, bounded by MerkleProofRefetchMaxAttempts. A rejection-driven
invalidate is therefore never absorbed by a fetch that predates it, so
the path a caller receives always postdates the invalidate.

refreshRoots awaits rootsRefreshInFlightFut via join() at both await
sites, giving the roots refresh the same cancellation shield the proof
path refetch already had: a cancelled validateRoot caller no longer
cancels the refresh its coalesced peers are waiting on.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 11:27:37 +02:00
Tanya S
ec36e09bae
chore: phase 3 — inline RLN validator, allow mountRlnRelay without wakuRelay (#3985)
* 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
2026-06-29 13:01:04 +02:00
Tanya S
57ff24760f
chore: phase 2 — rename/restructure waku_rln_relay → waku_rln (#3978)
* 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
2026-06-27 12:28:59 +02:00
Fabiana Cecin
c3090fb62f
feat: QUIC transport support (#3951)
* additive quic transport, off by default (--quic-support)
* add QuicConf + QuicConfBuilder, --quic-support / --quic-port flags
* net_config announces quic-v1 host/ext/dns4 addrs, adds quic-v1 to enr multiaddrs
* newWakuSwitch mounts quic transport when a quic addr is set
* toRemotePeerInfo: quic from enr multiaddrs ext, sorted quic-first
* BoundPorts.quic, read back the bound quic port at start (handles --quic-port=0)
* skip auto quic addr when operator supplies one via --ext-multiaddr
* tests: nodes dual-stack by default, tcp-only where single transport asserted
* tests: drop hardcoded ephemeral ports (port 0) to fix quic-churn bind flakes
* use setupNat to discover NAT-mapped UDP port when QUIC enabled
2026-06-17 07:55:45 -03:00
Fabiana Cecin
6837ae0c1f
feat: bump nim-libp2p to v2.0.0 (#3929)
* bump nim-libp2p pin to v2.0.0 tag
* bump json_rpc to v0.6.1, lsquic to v0.5.1, boringssl to v0.0.8 (latest tags)
* add libp2p_mix dep; repoint libp2p/protocols/mix -> libp2p_mix
* pin nimble.lock: websock / protobuf_serialization / npeg / jwt
* Makefile: add -d:libp2p_quic_support
* regenerate nix/deps.nix (adds libp2p_mix, refreshes pins)
* migrate rng ref HmacDrbgContext -> libp2p Rng across prod/channels/tests (interface-only; same DRBG)
* waku_switch: TransportConfig factory; unified 2.0.0 connection limits (withMaxInOut, withMaxConnections); local MaxConnections
* waku_relay/rendezvous/discv5/kademlia: v2.0.0 API (rng, config, ServiceDiscovery rename)
* call Service.setup() on post-build switch services (2.0.0 split setup/start)
* drop libp2p/utils/semaphore -> chronos AsyncSemaphore
* add logos_delivery/waku/compat/option_valueor shim (Option[T] valueOr/withValue, dropped upstream)
* add std/options where a transitive re-export was removed
* add newStandardSwitch shim (libp2p removed it in 2.0.0); mounts yamux+mplex to match prod muxer
* PeerId.random(rng); common.rng()/crypto.newRng(); hoist shared rng (instantiation cleanup)
* update expectations for 2.0.0 defaults: DEFAULT_PROTOCOLS += /ipfs/id/push/1.0.0; agent "nim-libp2p"
* drop relay reboot/reconnect test (asserted a Switch restart capability that is simply not supported)
* fix up a few tests that were flaking on MacOS (libp2p upgrade may have exposed these)
2026-06-15 09:56:15 -03:00
Ivan FB
3b03ca29b1
refactor: introduce proper logos_delivery layers folder structure (#3935)
Co-authored-by: NagyZoltanPeter <113987313+NagyZoltanPeter@users.noreply.github.com>
2026-06-08 13:37:53 +02:00
NagyZoltanPeter
42e0aa43d1
feat: persistency (#3880)
* persistency: per-job SQLite-backed storage layer (singleton, brokered)

Adds a backend-neutral CRUD library at waku/persistency/, plus the
nim-brokers dependency swap that enables it.

Architecture (ports-and-adapters):
  * Persistency: process-wide singleton, one root directory.
  * Job: one tenant, one DB file, one worker thread, one BrokerContext.
  * Backend: SQLite via waku/common/databases/db_sqlite. Uniform schema
    kv(category BLOB, key BLOB, payload BLOB) PRIMARY KEY (category, key)
    WITHOUT ROWID, WAL mode.
  * Writes are fire-and-forget via EventBroker(mt) PersistEvent.
  * Reads are async via five RequestBroker(mt) shapes (KvGet, KvExists,
    KvScan, KvCount, KvDelete). Reads return Result[T, PersistencyError].
  * One storage thread per job; tenants isolated by BrokerContext.

Public surface (waku/persistency/persistency.nim):
  Persistency.instance(rootDir) / Persistency.instance() / Persistency.reset()
  p.openJob(id) / p.closeJob(id) / p.dropJob(id) / p.close()
  p.job(id) / p[id] / p.hasJob(id)
  Writes (Job form & string-id form, fire-and-forget):
    persist / persistPut / persistDelete / persistEncoded
  Reads (Job form & string-id form, async Result):
    get / exists / scan / scanPrefix / count / deleteAcked

Key & payload encoding (keys.nim, payload.nim):
  * encodePart family + variadic key(...) / payload(...) macros +
    single-value toKey / toPayload.
  * Primitives: string and openArray[byte] are 2-byte BE length + bytes;
    int{8..64} are sign-flipped 8-byte BE; uint{16..64} are 8-byte BE;
    bool/byte/char are 1 byte; enums are int64(ord(v)).
  * Generic encodePart[T: tuple | object] recurses through fields() so
    any composite Nim type is encodable without ceremony.
  * Stable across Nim/C compiler upgrades: no sizeof, no memcpy, no
    cast on pointers, no host-endianness dependency.
  * `rawKey(bytes)` + `persistPut(..., openArray[byte])` let callers
    bypass the built-in encoder with their own format (CBOR, protobuf...).

Lifecycle:
  * Persistency.new is private; Persistency.instance is the only public
    constructor. Same rootDir is idempotent; conflicting rootDir is
    peInvalidArgument. Persistency.reset for test/restart paths.
  * openJob opens-or-creates the per-job SQLite file; an existing file
    is reused with its data preserved.
  * Teardown integration: Persistency.instance registers a Teardown
    MultiRequestBroker provider that closes all jobs and clears the
    singleton slot when Waku.stop() issues Teardown.request.

Internal layering:
  types.nim          pure value types (Key, KeyRange, KvRow, TxOp,
                     PersistencyError)
  keys.nim           encodePart primitives + key(...) macro
  payload.nim        toPayload + payload(...) macro
  schema.nim         CREATE TABLE + connection pragmas + user_version
  backend_sqlite.nim KvBackend, applyOps (single source of write SQL),
                     getOne/existsOne/deleteOne, scanRange (asc/desc,
                     half-open ranges, open-ended stop), countRange
  backend_comm.nim   EventBroker(mt) PersistEvent + 5 RequestBroker(mt)
                     declarations; encodeErr/decodeErr boundary helpers
  backend_thread.nim startStorageThread / stopStorageThread (shared
                     allocShared0 arg, cstring dbPath, atomic
                     ready/shutdown flags); per-thread provider
                     registration
  persistency.nim    Persistency + Job types, singleton state, public
                     facade
  ../requests/lifecycle_requests.nim
                     Teardown MultiRequestBroker

Tests (69 cases, all passing):
  test_keys.nim          sort-order invariants (length-prefix strings,
                         sign-flipped ints, composite tuples, prefix
                         range)
  test_backend.nim       round-trip / replace / delete-return-value /
                         batched atomicity / asc-desc-half-open-open-
                         ended scans / category isolation / batch
                         txDelete
  test_lifecycle.nim     open-or-create rootDir / non-dir collision /
                         reopen across sessions / idempotent openJob /
                         two-tenant parallel isolation / closeJob joins
                         worker / dropJob removes file / acked delete
  test_facade.nim        put-then-get / atomic batch / scanPrefix
                         asc/desc / deleteAcked hit-miss /
                         fire-and-forget delete / two-tenant facade
                         isolation
  test_encoding.nim      tuple/named-tuple/object keys, embedded Key,
                         enum encoding, field-major composite sort,
                         payload struct encoding, end-to-end struct
                         round-trip through SQLite
  test_string_lookup.nim peJobNotFound semantics / hasJob / subscript /
                         persistPut+get via id / reads short-circuit /
                         writes drop+warn / persistEncoded via id /
                         scan parity Job-ref vs id
  test_singleton.nim     idempotent same-rootDir / different-rootDir
                         rejection / no-arg instance lifecycle / reset
                         retargets / reset idempotence / Teardown.request
                         end-to-end

Prerequisite delivered in the same series: replace the in-tree broker
implementation with the external nim-brokers package; update all
broker call-sites (waku_filter_v2, waku_relay, waku_rln_relay,
delivery_service, peer_manager, requests/*, factory/*, api tests, etc.)
to the new package API; chat2 made to compile again.

Note: SDS adapter (Phase 5 of the design) is deferred -- nim-sds is
still developed side-by-side and the persistency layer is intentionally
SDS-agnostic.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* persistency: pin nim-brokers by URL+commit (workaround for stale registry)

The bare `brokers >= 2.0.1` form cannot resolve on machines where the
local nimble SAT solver enumerates only the registry-recorded 0.1.0 for
brokers. The nim-lang/packages entry for `brokers` carries no per-tag
metadata (only the URL), so until that registry entry is refreshed the
SAT solver clamps the available-versions list to 0.1.0 and rejects the
>= 2.0.1 constraint -- even though pkgs2 and pkgcache both have v2.0.1
cloned locally.

Pinning by URL+commit bypasses the registry path entirely. Inline
comment in waku.nimble documents the situation and the path back to
the bare form once nim-lang/packages is updated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* persistency: nph format pass

Run `nph` on all 57 Nim files touched by this PR. Pure formatting:
17 files re-styled, no semantic change. Suite still 69/69.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Fix build, add local-storage-path config, lazy init of Persistency from Waku start

* fix: fix nix deps

* fixes for nix build, regenerate deps

* reverting accidental dependency changes

* Fixing deps

* Apply suggestions from code review

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

* persistency tests: migrate to suite / asyncTest / await

Match the in-tree test convention (procSuite -> suite, sync test +
waitFor -> asyncTest + await):

- procSuite "X": -> suite "X":
- For tests doing async work: test -> asyncTest, waitFor -> await.
- Poll helpers (proc waitFor(t: Job, ...) in test_lifecycle.nim,
  proc waitUntilExists(...) in test_facade.nim and
  test_string_lookup.nim) -> Future[bool] {.async.}, internal
  `waitFor X` -> `await X`, internal `sleep(N)` ->
  `await sleepAsync(chronos.milliseconds(N))`.
- Renamed test_lifecycle.nim's helper proc from `waitFor(t: Job, ...)`
  -> `pollExists(t: Job, ...)`; the previous name shadowed
  chronos.waitFor in the chronos macro expansion.
- `chronos.milliseconds(N)` explicitly qualified because `std/times`
  also exports `milliseconds` (returning TimeInterval, not Duration).
- `check await x` -> `let okN = await x; check okN` to dodge chronos's
  "yield in expr not lowered" with await-as-macro-argument.
- `(await x).foo()` -> `let awN = await x; ... awN.foo() ...` for the
  same reason.

waku/persistency/persistency.nim: nph also pulled the proc signatures
across multiple lines; restored explicit `Future[void] {.async.}`
return types after the colon (an intermediate nph pass had elided them).

Suite: 71 / 71 OK against the new async write surface.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* use idiomatic valueOr instead of ifs

* Reworked persistency shutdown, remove not necessary teardown mechanism

* Use const for DefaultStoragePath

* format to follow coding guidelines - no use of result and explicit returns - no functional change

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>
2026-05-16 00:09:07 +02:00
Fabiana Cecin
71a369ffad
feat: allow a port value of zero for service ports (auto-assign port) (#3828)
* any port set to 0 on conf results in a random port bound
* Debug API MyBoundPorts reports actually bound ports for all services, reports 0 if disabled
* write back bound values to both WakuConf and WakuNode.ports
* setupDiscoveryV5 returns Result and errors out on port 0
* rename setupAndStartDiscv5WithAutoPort to setupAndStartDiscv5
* updateWaku ENR rebuild now runs after discv5 startup
* Add DefaultP2pTcpPort, DefaultDiscv5UdpPort, DefaultWebSocketPort, DefaultRestPort, DefaultMetricsHttpPort
* add tests
2026-05-11 15:22:22 -03:00
Ivan FB
0623c10635
completely remove storev2 (#3781) 2026-03-30 00:08:08 +02:00
Fabiana Cecin
614f171626
nim nph 0.7.0 formatting (#3759) 2026-03-17 14:15:35 +01:00
Tanya S
bc9454db5e
Chore: Simplify on chain group manager error handling (#3678) 2026-03-13 01:57:50 +05:30
Fabiana Cecin
1fb4d1eab0
feat: implement Waku API Health spec (#3689)
* Fix protocol strength metric to consider connected peers only
* Remove polling loop; event-driven node connection health updates
* Remove 10s WakuRelay topic health polling loop; now event-driven
* Change NodeHealthStatus to ConnectionStatus
* Change new nodeState (rest API /health) field to connectionStatus
* Add getSyncProtocolHealthInfo and getSyncNodeHealthReport
* Add ConnectionStatusChangeEvent
* Add RequestHealthReport
* Refactor sync/async protocol health queries in the health monitor
* Add EventRelayTopicHealthChange
* Add EventWakuPeer emitted by PeerManager
* Add Edge support for topics health requests and events
* Rename "RelayTopic" -> "Topic"
* Add RequestContentTopicsHealth sync request
* Add EventContentTopicHealthChange
* Rename RequestTopicsHealth -> RequestShardTopicsHealth
* Remove health check gating from checkApiAvailability
* Add basic health smoke tests
* Other misc improvements, refactors, fixes

Co-authored-by: NagyZoltanPeter <113987313+NagyZoltanPeter@users.noreply.github.com>
Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>
2026-02-12 14:52:39 -03:00
Fabiana Cecin
a8bdbca98a
Simplify NodeHealthMonitor creation (#3716)
Simplify NodeHealthMonitor creation

* Force NodeHealthMonitor.new() to set up a WakuNode
* Remove all checks for isNil(node) in NodeHealthMonitor
* Fix tests to use the new NodeHealthMonitor.new()

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>
2026-02-11 10:36:37 -03:00
NagyZoltanPeter
1fd25355e0
feat: waku api send (#3669)
* Introduce api/send
Added events and requests for support.
Reworked delivery_monitor into a featured devlivery_service, that
- supports relay publish and lightpush depending on configuration but with fallback options
- if available and configured it utilizes store api to confirm message delivery
- emits message delivery events accordingly

prepare for use in api_example

* Fix edge mode config and test added
* Fix some import issues, start and stop waku shall not throw exception but return with result properly
* Utlize sync RequestBroker, adapt to non-async broker usage and gcsafe where appropriate, removed leftover
* add api_example app to examples2
* Adapt after merge from master
* Adapt code for using broker context
* Fix brokerCtx settings for all usedbrokers, cover locked node init
* Various fixes upon test failures. Added initial of subscribe API and auto-subscribe for send api
* More test added
* Fix multi propagate event emit, fix fail send test case
* Fix rebase
* Fix PushMessageHandlers in tests
* adapt libwaku to api changes
* Fix relay test by adapting publish return error in case NoPeersToPublish
* Addressing all remaining review findings. Removed leftovers. Fixed loggings and typos
* Fix rln relay broker, missed brokerCtx
* Fix rest relay test failed, due to publish will fail if no peer avail
* ignore anvil test state file
* Make terst_wakunode_rln_relay broker context aware to fix
* Fix waku rln tests by having them broker context aware
* fix typo in test_app.nim
2026-01-30 01:06:00 +01:00
Sergei Tikhomirov
7d1c6abaac
chore: do not mount lightpush without relay (fixes #2808) (#3540)
* chore: do not mount lightpush without relay (fixes #2808)

- Change mountLightPush signature to return Result[void, string]
- Return error when relay is not mounted
- Update all call sites to handle Result return type
- Add test verifying mounting fails without relay
- Only advertise lightpush capability when relay is enabled

* chore: don't mount legacy lightpush without relay
2025-12-11 10:51:47 +01:00
Tanya S
2cf4fe559a
Chore: bump waku-rlnv2-contract-repo commit (#3651)
* Bump commit for vendor wakurlnv2contract

* Update RLN registration proc for contract updates

* add option to runAnvil for state dump or load with optional contract deployment on setup

* Code clean up

* Upodate rln relay tests to use cached anvil state

* Minor updates to utils and new test for anvil state dump

* stopAnvil needs to wait for graceful shutdown

* configure runAnvil to use load state in other tests

* reduce ci timeout

* Allow for RunAnvil load state file to be compressed

* Fix linting

* Change return type of sendMintCall to Futre[void]

* Update naming of ci path for interop tests
2025-12-08 08:29:48 +02:00
Ivan FB
e54851d9d6
fix: admin API peer shards field from metadata protocol (#3594)
* fix: admin API peer shards field from metadata protocol
   Store and return peer shard info from metadata protocol exchange instead of only checking ENR records.
* peer_manager set shard info and extend rest test to validate it

Co-authored-by: MorganaFuture <andrewmochalskyi@gmail.com>
2025-11-20 13:12:16 +01:00
NagyZoltanPeter
1762548741
chore: clarify api folders (#3637)
* Rename waku_api to rest_api and underlying rest to endpoint for clearity
* Rename node/api to node/kernel_api to suggest that it is an internal accessor to node interface + make everything compile after renaming
* make waku api a top level import
* fix use of relative path imports and use default to root rather in case of waku and tools modules
2025-11-15 23:31:09 +01:00
Fabiana Cecin
7b580dbf39
chore(refactoring): replace some isErr usage with better alternatives (#3615)
* Closes apply isOkOr || valueOr approach (#1969)
2025-10-27 14:07:06 -03:00
Darshan K
7a009c8b27
bump libp2p ( v1.14.0 ) (#3627) 2025-10-17 11:49:28 +02:00
Darshan K
deebee45d7
feat: stateless RLN ( bump v0.9.0 ) (#3621) 2025-10-15 19:08:46 +05:30
Ivan FB
7e5041d5e1
Move log level from debug to info (#3622)
* convert all debug logs to info log level
* waku_relay protocol mv notice spammy logs to debug
2025-10-15 10:49:36 +02:00
Prem Chaitanya Prathi
797370ec80
remove mixPubKey from ENR and provide config param to pass mix nodes statically (#3587) 2025-10-08 10:18:54 +05:30
Darshan K
3c9b355879 feat: deprecate tree_path and rlnDB (#3577) 2025-09-26 14:47:15 +05:30
Darshan K
9bba8b0f9c fix: refact rln-relay and post sync test (#3434) 2025-09-10 16:18:51 +05:30
NagyZoltanPeter
a36601ab0d fix: Do not allow invalid pubsub topic subscription via relay REST api (#3559)
* Check input pubsub topics for REST /relay/v1/subscriptions endpoint
2025-09-09 14:04:10 +02:00
Prem Chaitanya Prathi
e4358c9718 chore: remove metadata protocol dependency on enr, relax check when nwaku is edge node (#3519)
* remove metadata protocol dep on enr, do not disconnect peers based on shards mismatch
2025-08-13 10:48:56 +05:30
fryorcraken
994d485b49 chore!: make sharding configuration explicit (#3468)
* Reserve `networkconfig` name to waku network related settings

* Rename cluster conf to network conf

 A `NetworkConf` is a Waku network configuration.

# Conflicts:
#	tests/factory/test_waku_conf.nim

# Conflicts:
#	tests/factory/test_waku_conf.nim

* Improve sharding configuration

A smarter data types simplifies the logic.

* Fixing tests

* fixup! rename to endpointConf

* wip: autosharding is a specific configuration state and treat it like
it

# Conflicts:
#	waku/factory/external_config.nim

* refactor lightpush handler

some metrics error reporting were missing

# Conflicts:
#	waku/waku_lightpush/protocol.nim

* test_node_factory tests pass

* remove warnings

* fix tests

* Revert eager previous replace-all command

* fix up build tools compilation

* metadata is used to store cluster id

* Mount relay routes in static sharding

* Rename activeRelayShards to subscribeShards

To make it clearer that these are the shards the node will subscribe to.

* Remove unused msg var

* Improve error handling

* Set autosharding as default, with 1 shard in network

Also makes shards to subscribe to all shards in auto sharding, none in
static sharding.
2025-07-04 17:10:53 +10:00
fryorcraken
4b186a4b28 fix: deprecate --dns-discovery (#3485)
* fix: deprecate `--dns-discovery`

Properly deprecates `--dns-discovery` CLI arg.
DNS Discovery is enabled if a non-empty DNS Discovery URL is passed.

* test: add test_all for factory

add and use test_all for some tests.
2025-07-03 11:56:43 +10:00
Ivan FB
478925a389 chore: refactor to unify online and health monitors (#3456) 2025-06-16 18:44:21 +02:00
Ivan FB
336fbf8b64 fix: relay unsubscribe (#3422)
* waku_relay protocol fix unsubscribe and remove topic validator
* simplify subscription and avoid unneeded code
* tests adaptations
* call wakuRelay.subscribe only in one place within waku_node
2025-06-02 22:02:49 +02:00
NagyZoltanPeter
a39bcff6dc feat: Extend node /health REST endpoint with all protocol's state (#3419)
* Extend ndoe /health REST endpoint with all protocol's state

* Added check for Rendezvous peers availability

* Fine tune filter, added client protocols to health report

* Fix /health endpoint test

* Add explanatory description for state NOT_READY

* Fix formattings

* Apply suggestions from code review

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

* Apply code style changes and extended test

* Fix formatting

---------

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>
2025-06-02 17:21:09 +02:00
Ivan FB
9f68c83fed chore: bump dependencies for v0.36 (#3410)
* properly pass userMessageLimit to OnchainGroupManager
* waku.nimble 2.2.4 Nim compiler
* rm stew/shims/net import
* change ValidIpAddress.init with parseIpAddress
* fix serialize for zerokit
* group_manager: separate if statements
* protocol_types: add encode UInt32 with zeros up to 32 bytes
* windows build: skip libunwind build and rm libunwind.a inlcusion step
* bump nph to overcome the compilation issues with 2.2.x
* bump nim-libp2p to v1.10.1
2025-05-26 21:58:02 +02:00
Darshan K
39e65dea28 fix: timestamp based validation (#3406) 2025-05-26 17:56:29 +05:30
fryorcraken
cc66c7fe78 chore!: separate internal and CLI configurations (#3357)
Split `WakuNodeConfig` object for better separation of concerns and to introduce a tree-like structure to configuration.

* fix: ensure twn cluster conf is still applied when clusterId=1
* test: remove usage of `WakuNodeConf`
* Remove macro, split builder files, remove wakunodeconf from tests
* rm network_conf_builder module as it is not used

---------

Co-authored-by: NagyZoltanPeter <113987313+NagyZoltanPeter@users.noreply.github.com>
Co-authored-by: Ivan Folgueira Bande <ivansete@status.im>
2025-05-07 23:05:35 +02:00
Ivan FB
6bc05efc02 chore: Avoid double relay subscription (#3396)
* make sure subscribe once to every topic in waku_node
* start suggest use of removeValidator in waku_relay/protocol. Commented until libp2p updated.
2025-05-05 22:57:20 +02:00
NagyZoltanPeter
98c3979119 chore: return all peers from rest admin (#3395)
* Updated version of getting peers by /admin endpoints
2025-04-25 15:36:41 +02:00
NagyZoltanPeter
8394c15a1a fix: bad HttpCode conversion, add missing lightpush v3 rest api tests (#3389)
* Fix bad HttpCode conversion, add missing lightpush v3 rest api tests
2025-04-24 08:36:30 +02:00
NagyZoltanPeter
ab8a30d3d6 chore: extended /admin/v1 RESP API with different option to look at current connected/relay/mesh state of the node (#3382)
* Extended /admin/v1 RESP API with different option to look at current connected/relay/mesh state of the node
* Added score information for peer info retrievals
2025-04-24 08:36:02 +02:00
Miran
ed0474ade3 chore: fix unused and deprecated imports (#3368) 2025-04-11 18:20:23 +03:00
NagyZoltanPeter
fe8327627e Make /debug endpoints /version and /info available as root (#3333) 2025-03-27 11:56:44 +01:00
NagyZoltanPeter
dcf09dd365 feat: lightpush v3 (#3279)
* Separate new lightpush protocol
New RPC defined
Rename al occurence of old lightpush to legacy lightpush, fix rest tests of lightpush
New lightpush protocol added back
Setup new lightpush protocol, mounting and rest api for it

	modified:   apps/chat2/chat2.nim
	modified:   tests/node/test_wakunode_lightpush.nim
	modified:   tests/node/test_wakunode_sharding.nim
	modified:   tests/test_peer_manager.nim
	modified:   tests/test_wakunode_lightpush.nim
	renamed:    tests/waku_lightpush/lightpush_utils.nim -> tests/waku_lightpush_legacy/lightpush_utils.nim
	renamed:    tests/waku_lightpush/test_all.nim -> tests/waku_lightpush_legacy/test_all.nim
	renamed:    tests/waku_lightpush/test_client.nim -> tests/waku_lightpush_legacy/test_client.nim
	renamed:    tests/waku_lightpush/test_ratelimit.nim -> tests/waku_lightpush_legacy/test_ratelimit.nim
	modified:   tests/wakunode_rest/test_all.nim
	renamed:    tests/wakunode_rest/test_rest_lightpush.nim -> tests/wakunode_rest/test_rest_lightpush_legacy.nim
	modified:   waku/factory/node_factory.nim
	modified:   waku/node/waku_node.nim
	modified:   waku/waku_api/rest/admin/handlers.nim
	modified:   waku/waku_api/rest/builder.nim
	new file:   waku/waku_api/rest/legacy_lightpush/client.nim
	new file:   waku/waku_api/rest/legacy_lightpush/handlers.nim
	new file:   waku/waku_api/rest/legacy_lightpush/types.nim
	modified:   waku/waku_api/rest/lightpush/client.nim
	modified:   waku/waku_api/rest/lightpush/handlers.nim
	modified:   waku/waku_api/rest/lightpush/types.nim
	modified:   waku/waku_core/codecs.nim
	modified:   waku/waku_lightpush.nim
	modified:   waku/waku_lightpush/callbacks.nim
	modified:   waku/waku_lightpush/client.nim
	modified:   waku/waku_lightpush/common.nim
	modified:   waku/waku_lightpush/protocol.nim
	modified:   waku/waku_lightpush/rpc.nim
	modified:   waku/waku_lightpush/rpc_codec.nim
	modified:   waku/waku_lightpush/self_req_handler.nim
	new file:   waku/waku_lightpush_legacy.nim
	renamed:    waku/waku_lightpush/README.md -> waku/waku_lightpush_legacy/README.md
	new file:   waku/waku_lightpush_legacy/callbacks.nim
	new file:   waku/waku_lightpush_legacy/client.nim
	new file:   waku/waku_lightpush_legacy/common.nim
	new file:   waku/waku_lightpush_legacy/protocol.nim
	new file:   waku/waku_lightpush_legacy/protocol_metrics.nim
	new file:   waku/waku_lightpush_legacy/rpc.nim
	new file:   waku/waku_lightpush_legacy/rpc_codec.nim
	new file:   waku/waku_lightpush_legacy/self_req_handler.nim

Adapt to non-invasive libp2p observers

cherry pick latest lightpush (v1) changes into legacy lightpush code after rebase to latest master

Fix vendor dependencies from origin/master after failed rebase of them

Adjust examples, test to new lightpush - keep using of legacy

Fixup error code mappings

Fix REST admin interface with distinct legacy and new lightpush

Fix lightpush v2 tests

* Utilize new publishEx interface of pubsub libp2p

* Adapt to latest libp2p pubslih design changes. publish returns an outcome as Result error.

* Fix review findings

* Fix tests, re-added lost one

* Fix rebase

* Apply suggestions from code review

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

* Addressing review comments

* Fix incentivization tests

* Fix build failed on libwaku

* Change new lightpush endpoint version to 3 instead of 2. Noticed that old and new lightpush metrics can cause trouble in monitoring dashboards so decided to give new name as v3 for the new lightpush metrics and change legacy ones back - temporarly till old lightpush will be decommissioned

* Fixing flaky test with rate limit timing

* Fixing logscope of lightpush and legacy lightpush

---------

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>
2025-03-05 12:07:56 +01:00
Ivan FB
9bb567eb0e chore: better proof handling in REST (#3286)
* better proof handling in REST
2025-02-14 11:14:38 +01:00
gabrielmer
8867fd6fa9 chore: sending msg hash as string on libwaku message event (#3234) 2025-01-30 10:15:31 +02:00
gabrielmer
f3af7fa37e chore: naming connectPeer procedure (#3157) 2024-10-29 18:37:07 +02:00
Ivan FB
3786ce12e2 chore: Circuit relay (#3112)
* undo apt install libpcre (not circuit-relay related.)
* nat.nim: protect against possible exceptions when calling getExternalIP
* new external CLI argument, isRelayClient
* waku factory change to mount circuit hop proto by default
* waku_node: move autonat_service to a separate module
2024-10-28 09:17:46 +01:00
fryorcraken
723b009b20 chore: use submodule nph in CI to check lint (#3027) 2024-09-11 11:51:42 +10:00
gabrielmer
43bea3c476 chore: deprecating pubsub topic (#2997) 2024-09-10 15:07:12 -06:00
Sasha
1d850e43dc chore: return all connected peers from REST API (#2923)
* Remove the condition of gathering connected peers with relay and user req/resp protocols.
* Return PeerExchange protocol support of connected nodes with /admin/peers
* Added test for checking return of PeerExchange mounted protocol of connected peer by GET /admin/peers

---------

Co-authored-by: NagyZoltanPeter <113987313+NagyZoltanPeter@users.noreply.github.com>
2024-07-23 12:58:56 +02:00