logos-delivery/logos_delivery/messaging/messaging_client.nim
stubbsta e18e944113
feat: source the rate limit manager's epoch and budget from RLN
Wires the quota seam to its producer, so enforcement tracks RLN rather
than only the wall clock.

- Waku.currentRlnEpochQuota (waku/api/publish) reads RLN's current epoch
  index and the epoch's user message limit together, returning none when
  RLN is not mounted (or its limit is unset).
- MessagingClient.new builds a QuotaProvider closure over that accessor
  and hands it to the RateLimitManager. The closure is late-binding: it
  queries the kernel on each admission, so a node whose RLN mounts after
  construction upgrades from the wall-clock fallback to RLN's epoch and
  limit automatically, with no reconstruction.

With this, admit() rolls its window on RLN's epoch and clamps the
configured cap to RLN's user message limit; without RLN it still falls
back to the absolute wall-clock window and the configured limit.

Also switches the quota seam from std/options to results `Opt`, matching
the kernel surface it now bridges (`groupManager.userMessageLimit` is
`Opt`) and the rest of the messaging layer post-Opt migration.

Tests: currentRlnEpochQuota is none unmounted and reports epoch + the
configured userMessageLimit when mounted (anvil-backed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 10:17:43 +02:00

62 lines
2.2 KiB
Nim

## 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, chronicles
import
logos_delivery/api/conf/messaging_conf,
logos_delivery/api/messaging_client_api,
logos_delivery/waku/waku,
logos_delivery/waku/api/publish,
logos_delivery/waku/factory/conf_builder/waku_conf_builder,
logos_delivery/messaging/delivery_service/[recv_service, send_service],
logos_delivery/messaging/rate_limit_manager/rate_limit_manager
export messaging_client_api, messaging_conf
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 rlnQuotaProvider(waku: Waku): QuotaProvider =
## Sources the rate limit manager's epoch + limit from RLN. Late-binding: the
## closure queries `waku` on each admission, so a node whose RLN mounts after
## this client is constructed upgrades from the wall-clock fallback to RLN's
## epoch and user message limit automatically.
return proc(): Opt[EpochQuota] {.gcsafe, raises: [].} =
let q = waku.currentRlnEpochQuota().valueOr:
return Opt.none(EpochQuota)
Opt.some(EpochQuota(epochIndex: q.epochIndex, userMessageLimit: q.messageLimit))
proc new*(
T: type MessagingClient, conf: MessagingClientConf, waku: Waku
): 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 reliability = conf.reliabilityEnabled.get(DefaultP2pReliability)
let sendService = ?SendService.new(
reliability,
waku,
RateLimitManager.new(
conf.rateLimit.get(DefaultRateLimitConfig), rlnQuotaProvider(waku)
),
)
let recvService = RecvService.new(waku)
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.
if self.isNil():
return err("MessagingClient is not initialized")
return ok()