|
| 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