Skip to content

Commit 74057c6

Browse files
start basic reliable channel folder (#3886)
1 parent 5e262ba commit 74057c6

16 files changed

Lines changed: 930 additions & 0 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ jobs:
4343
- 'tools/**'
4444
- 'tests/all_tests_v2.nim'
4545
- 'tests/**'
46+
- 'channels/**'
4647
docker:
4748
- 'docker/**'
4849

channels/encryption/encryption.nim

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
## Optional encryption hooks for the Reliable Channel API.
2+
##
3+
## Modelled as `RequestBroker`s: the broker pattern lets the channel
4+
## delegate work to a provider that may live in any module without
5+
## introducing a direct dependency. If no provider is registered the
6+
## broker returns an error, so installing the noop providers from
7+
## `noop_encryption` is required when the application does not want
8+
## actual encryption.
9+
##
10+
## Applied per-segment after SDS processing on outgoing, and before
11+
## SDS processing on incoming. No specific scheme is mandated.
12+
##
13+
## See: https://lip.logos.co/messaging/raw/reliable-channel-api.html
14+
15+
import brokers/request_broker
16+
17+
export request_broker
18+
19+
RequestBroker:
20+
type Encrypt* = seq[byte]
21+
proc signature*(payload: seq[byte]): Future[Result[Encrypt, string]] {.async.}
22+
23+
RequestBroker:
24+
type Decrypt* = seq[byte]
25+
proc signature*(payload: seq[byte]): Future[Result[Decrypt, string]] {.async.}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
## No-op encryption providers. Install these when the application does
2+
## not want actual encryption so the `Encrypt` / `Decrypt` brokers have
3+
## something to dispatch to.
4+
5+
import results
6+
import chronos
7+
import ./encryption
8+
9+
proc setNoopEncryption*() =
10+
discard Encrypt.setProvider(
11+
proc(payload: seq[byte]): Future[Result[Encrypt, string]] {.async.} =
12+
return ok(Encrypt(payload))
13+
)
14+
15+
discard Decrypt.setProvider(
16+
proc(payload: seq[byte]): Future[Result[Decrypt, string]] {.async.} =
17+
return ok(Decrypt(payload))
18+
)

channels/events.nim

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## Reliable Channel event types emitted to API consumers.
2+
##
3+
## Lifecycle events for individual segments (sent / propagated / errored)
4+
## are the same as the network-level ones the DeliveryService already
5+
## emits — `requestId` is shared across layers — so we just re-export
6+
## `waku/events/message_events` and avoid declaring duplicates.
7+
##
8+
## Only the channel-level `MessageReceivedEvent` carries data that has
9+
## no analogue in the lower layer (reassembled application payload,
10+
## senderId, channelId), so it lives here.
11+
12+
import waku/events/message_events as waku_message_events
13+
import brokers/event_broker
14+
15+
import ./types as channel_types
16+
17+
export waku_message_events, channel_types, event_broker
18+
19+
EventBroker:
20+
type ChannelMessageReceivedEvent* = object
21+
channelId*: ChannelId
22+
senderId*: SdsParticipantID
23+
payload*: seq[byte]
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
## Rate Limit Manager for the Reliable Channel API.
2+
##
3+
## Tracks messages sent per RLN epoch and delays dispatch when the
4+
## limit is approached, ensuring RLN compliance on enforcing relays.
5+
##
6+
## For the skeleton this is a pass-through: messages are immediately
7+
## released as ready-to-send. Real epoch budgeting will be added later.
8+
##
9+
## See: https://lip.logos.co/messaging/raw/reliable-channel-api.html
10+
11+
import std/times
12+
import message
13+
import brokers/event_broker
14+
import brokers/broker_context
15+
16+
export event_broker, broker_context
17+
export message.SdsChannelID
18+
19+
const
20+
DefaultEpochPeriodSec* = 600
21+
DefaultMessagesPerEpoch* = 1
22+
23+
EventBroker:
24+
## Emitted by `enqueueToSend` carrying the batch of opaque message
25+
## blobs that may now leave the rate limiter and continue down the
26+
## outgoing pipeline (encryption -> dispatch). Bytes only: the rate
27+
## limiter is intentionally agnostic of SDS, so anything serialisable
28+
## can flow through it.
29+
##
30+
## `channelId` lets listeners filter to their own channel, since all
31+
## reliable channels share the underlying Waku node's broker context.
32+
type ReadyToSendEvent* = object
33+
channelId*: SdsChannelID
34+
msgs*: seq[seq[byte]]
35+
36+
type
37+
RateLimitConfig* = object
38+
enabled*: bool ## spec: rate limiting opt-in; SHOULD be true when RLN active
39+
epochPeriodSec*: int
40+
messagesPerEpoch*: int
41+
42+
RateLimitManager* = ref object
43+
config*: RateLimitConfig
44+
queue*: seq[seq[byte]]
45+
currentEpochStart*: Time
46+
sentInCurrentEpoch*: int
47+
channelId*: SdsChannelID ## tag for the emitted `ReadyToSendEvent`
48+
brokerCtx: BrokerContext
49+
50+
proc new*(
51+
T: type RateLimitManager,
52+
config: RateLimitConfig,
53+
channelId: SdsChannelID,
54+
brokerCtx: BrokerContext = globalBrokerContext(),
55+
): T =
56+
return T(
57+
config: config,
58+
queue: @[],
59+
currentEpochStart: getTime(),
60+
sentInCurrentEpoch: 0,
61+
channelId: channelId,
62+
brokerCtx: brokerCtx,
63+
)
64+
65+
proc enqueueToSend*(self: RateLimitManager, msg: seq[byte]) =
66+
## Skeleton behaviour: enqueue and immediately release as a single
67+
## ready batch. Real per-epoch budgeting will park messages on
68+
## `self.queue` and emit only when the budget allows.
69+
ReadyToSendEvent.emit(
70+
self.brokerCtx, ReadyToSendEvent(channelId: self.channelId, msgs: @[msg])
71+
)
72+
73+
proc dequeueReady*(self: RateLimitManager): seq[seq[byte]] =
74+
## Returns the set of queued messages that may be dispatched now
75+
## without exceeding the configured rate limit.
76+
discard
77+
78+
proc resetEpoch*(self: RateLimitManager) =
79+
self.currentEpochStart = getTime()
80+
self.sentInCurrentEpoch = 0

0 commit comments

Comments
 (0)