Background
rate_limiting.rs implements a generic rate-limiting primitive and is already wired into notification.rs for notification creation. bridge.rs::create_proposal (the entrypoint that creates a cross-chain bridge proposal) performs no rate-limiting check at all before consuming a proposal slot and incrementing PROPOSAL_COUNTER.
errors.rs even documents the gap explicitly:
/// # TODO
/// - Add `BridgeError::RateLimitExceeded` (152) for per-user rate limiting
/// once the rate-limiting module is fully integrated.
This mirrors the pattern where a defensive module exists in the codebase but isn't enforced at the one entrypoint that actually needs it (an unbounded flow of proposals from a single caller), leaving create_proposal exposed to spam/DoS despite the primitive already being available.
Implementation Plan
- Add
BridgeError::RateLimitExceeded = 152 to errors.rs (next free code in the 148-151 range per the module's own numbering table).
- Call into
rate_limiting from bft_consensus::create_proposal before incrementing PROPOSAL_COUNTER, returning BridgeError::RateLimitExceeded when the caller is over budget.
- Make the rate limit window/threshold configurable via existing
config.rs patterns rather than hardcoded constants.
- Add tests confirming: a caller under the limit succeeds, a caller over the limit is rejected with the new error, and the limit resets after the window elapses.
Acceptance Criteria
create_proposal rejects excessive proposal creation from a single caller with BridgeError::RateLimitExceeded
- Rate limit window/threshold is configurable, not hardcoded
- Tests cover both the allowed and rejected paths
Background
rate_limiting.rsimplements a generic rate-limiting primitive and is already wired intonotification.rsfor notification creation.bridge.rs::create_proposal(the entrypoint that creates a cross-chain bridge proposal) performs no rate-limiting check at all before consuming a proposal slot and incrementingPROPOSAL_COUNTER.errors.rseven documents the gap explicitly:This mirrors the pattern where a defensive module exists in the codebase but isn't enforced at the one entrypoint that actually needs it (an unbounded flow of proposals from a single caller), leaving
create_proposalexposed to spam/DoS despite the primitive already being available.Implementation Plan
BridgeError::RateLimitExceeded = 152toerrors.rs(next free code in the 148-151 range per the module's own numbering table).rate_limitingfrombft_consensus::create_proposalbefore incrementingPROPOSAL_COUNTER, returningBridgeError::RateLimitExceededwhen the caller is over budget.config.rspatterns rather than hardcoded constants.Acceptance Criteria
create_proposalrejects excessive proposal creation from a single caller withBridgeError::RateLimitExceeded