Skip to main content

miden_node_utils/
limiter.rs

1//! Limits for RPC and store parameters and payload sizes.
2//!
3//! # Rationale
4//! - Parameter limits are kept across all multi-value RPC parameters. This caps worst-case SQL `IN`
5//!   clauses and keeps responses comfortably under the 4 MiB payload budget enforced in the store.
6//! - Limits are enforced both at the RPC boundary and inside the store to prevent bypasses and to
7//!   avoid expensive queries even if validation is skipped earlier in the stack.
8//! - `MAX_PAGINATED_PAYLOAD_BYTES` is set to 4 MiB (e.g. 1000 nullifier rows at ~36 B each, 1000
9//!   transactions summaries streamed in chunks).
10//!
11//! Add new limits here so callers share the same values and rationale.
12
13/// Basic request limit.
14pub const GENERAL_REQUEST_LIMIT: usize = 1000;
15
16#[expect(missing_docs)]
17#[derive(Debug, thiserror::Error)]
18#[error("parameter {which} exceeded limit {limit}: {size}")]
19pub struct QueryLimitError {
20    which: &'static str,
21    size: usize,
22    limit: usize,
23}
24
25/// Checks limits against the desired query parameters, per query parameter and bails if they exceed
26/// a defined value.
27pub trait QueryParamLimiter {
28    /// Name of the parameter to mention in the error.
29    const PARAM_NAME: &'static str;
30    /// Limit that causes a bail if exceeded.
31    const LIMIT: usize;
32    /// Do the actual check.
33    fn check(size: usize) -> Result<(), QueryLimitError> {
34        if size > Self::LIMIT {
35            Err(QueryLimitError {
36                which: Self::PARAM_NAME,
37                size,
38                limit: Self::LIMIT,
39            })?;
40        }
41        Ok(())
42    }
43}
44
45/// Maximum payload size (in bytes) for paginated responses returned by the store.
46pub const MAX_RESPONSE_PAYLOAD_BYTES: usize = 4 * 1024 * 1024;
47
48/// Used for the following RPC endpoints:
49/// * `sync_transactions`
50///
51/// Capped at 1000 account IDs to keep SQL `IN` clauses bounded and response payloads under the
52/// 4 MB budget.
53pub struct QueryParamAccountIdLimit;
54impl QueryParamLimiter for QueryParamAccountIdLimit {
55    const PARAM_NAME: &str = "account_id";
56    const LIMIT: usize = GENERAL_REQUEST_LIMIT;
57}
58
59/// Used for the following RPC endpoints:
60/// * `sync_nullifiers`
61///
62/// Capped at 1000 prefixes to keep queries and responses comfortably within the 4 MB payload
63/// budget and to avoid unbounded prefix scans.
64pub struct QueryParamNullifierPrefixLimit;
65impl QueryParamLimiter for QueryParamNullifierPrefixLimit {
66    const PARAM_NAME: &str = "nullifier_prefix";
67    const LIMIT: usize = GENERAL_REQUEST_LIMIT;
68}
69
70/// Used for the following RPC endpoints
71/// * `get_note_sync_multi`
72///
73/// Capped at 1000 tags so note sync responses remain within the 4 MB payload budget.
74pub struct QueryParamNoteTagLimit;
75impl QueryParamLimiter for QueryParamNoteTagLimit {
76    const PARAM_NAME: &str = "note_tag";
77    const LIMIT: usize = GENERAL_REQUEST_LIMIT;
78}
79
80/// Used for the following RPC endpoints
81/// `select_notes_by_id`
82///
83/// The limit is set to 100 notes to keep responses within the 4 MiB payload cap because individual
84/// notes are bounded to roughly 32 KiB.
85pub struct QueryParamNoteIdLimit;
86impl QueryParamLimiter for QueryParamNoteIdLimit {
87    const PARAM_NAME: &str = "note_id";
88    const LIMIT: usize = 100;
89}
90
91/// Used for internal queries retrieving note inclusion proofs by commitment.
92///
93/// Capped at 1000 commitments to keep internal proof lookups bounded and responses under the 4 MB
94/// payload cap.
95pub struct QueryParamNoteCommitmentLimit;
96impl QueryParamLimiter for QueryParamNoteCommitmentLimit {
97    const PARAM_NAME: &str = "note_commitment";
98    const LIMIT: usize = GENERAL_REQUEST_LIMIT;
99}
100
101/// Only used internally, not exposed via public RPC.
102///
103/// Capped at 1000 block headers to bound internal batch operations and keep payloads below the
104/// 4 MB limit.
105pub struct QueryParamBlockLimit;
106impl QueryParamLimiter for QueryParamBlockLimit {
107    const PARAM_NAME: &str = "block_header";
108    const LIMIT: usize = GENERAL_REQUEST_LIMIT;
109}
110
111/// Used for the following RPC endpoints
112/// * `get_account`
113///
114/// Capped at 64 total storage map keys across all slots to limit the number of SMT proofs
115/// returned.
116pub struct QueryParamStorageMapKeyTotalLimit;
117impl QueryParamLimiter for QueryParamStorageMapKeyTotalLimit {
118    const PARAM_NAME: &str = "storage_map_key";
119    const LIMIT: usize = 64;
120}