agglayer_config/
rate_limiting.rs1use std::{collections::BTreeMap, time::Duration};
2
3use serde::{Deserialize, Serialize};
4use serde_with::DisplayFromStr;
5
6pub type NetworkId = u32;
7
8#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
10#[serde(rename_all = "kebab-case")]
11pub enum TimeRateLimit {
12 Unlimited,
15
16 #[serde(untagged, rename_all = "kebab-case")]
18 Limited {
19 max_per_interval: u32,
20
21 #[serde(with = "crate::with::HumanDuration")]
22 time_interval: Duration,
23 },
24}
25
26impl TimeRateLimit {
27 pub const fn send_tx_default() -> Self {
29 Self::Unlimited
30 }
31
32 pub const fn limited(max_per_interval: u32, time_interval: Duration) -> Self {
34 Self::Limited {
35 max_per_interval,
36 time_interval,
37 }
38 }
39}
40
41#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
43#[serde(rename_all = "kebab-case")]
44struct RateLimitOverride {
45 send_tx: Option<TimeRateLimit>,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct NetworkRateLimitingConfig<'a> {
51 pub send_tx: &'a TimeRateLimit,
53}
54
55#[serde_with::serde_as]
58#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
59#[serde(rename_all = "kebab-case")]
60pub struct RateLimitingConfig {
61 #[serde(default = "TimeRateLimit::send_tx_default")]
63 send_tx: TimeRateLimit,
64
65 #[serde(default)]
67 #[serde_as(as = "BTreeMap<DisplayFromStr, _>")]
68 network: BTreeMap<NetworkId, RateLimitOverride>,
69}
70
71impl RateLimitingConfig {
72 pub const DEFAULT: Self = Self::new(TimeRateLimit::send_tx_default());
74
75 pub const fn new(send_tx: TimeRateLimit) -> Self {
77 let network = BTreeMap::new();
78 Self { send_tx, network }
79 }
80
81 pub fn with_send_tx_override(mut self, nid: NetworkId, limit: TimeRateLimit) -> Self {
83 self.network.entry(nid).or_default().send_tx = Some(limit);
84 self
85 }
86
87 pub fn config_for(&self, network_id: NetworkId) -> NetworkRateLimitingConfig<'_> {
89 let overrides = self.override_for(network_id);
90 let send_tx = overrides
91 .and_then(|l| l.send_tx.as_ref())
92 .unwrap_or(&self.send_tx);
93 NetworkRateLimitingConfig { send_tx }
94 }
95
96 fn override_for(&self, nid: NetworkId) -> Option<&RateLimitOverride> {
97 self.network.get(&nid)
98 }
99}
100
101impl Default for RateLimitingConfig {
102 fn default() -> Self {
103 Self::DEFAULT
104 }
105}
106
107#[cfg(test)]
108mod test {
109 use super::*;
110
111 #[test]
112 fn default_config() {
113 let config_str = "send-tx = \"unlimited\"";
114 let parsed_default_config: RateLimitingConfig = toml::from_str(config_str).unwrap();
115 assert_eq!(parsed_default_config, RateLimitingConfig::DEFAULT);
116
117 let empty_config: RateLimitingConfig = toml::from_str("").unwrap();
118 assert_eq!(empty_config, RateLimitingConfig::DEFAULT);
119 }
120
121 #[test]
122 fn unlimited() {
123 let config_str = "send-tx = \"unlimited\"";
124 let config: RateLimitingConfig = toml::from_str(config_str).unwrap();
125 let expected = RateLimitingConfig::new(TimeRateLimit::Unlimited);
126 assert_eq!(config, expected);
127 }
128
129 #[rstest::rstest]
130 #[case(4, "1h 20min", 80 * 60)]
131 #[case(2, "30min", 1800)]
132 #[case(50, "90s", 90)]
133 #[case(0, "2min", 120)]
134 fn basic(#[case] limit: u32, #[case] interval_str: String, #[case] interval_secs: u64) {
135 #[rustfmt::skip]
136 let config_str = format!(
137 "[send-tx]\n\
138 max-per-interval = {limit}\n\
139 time-interval = {interval_str:?}\n"
140 );
141 let config: RateLimitingConfig = toml::from_str(&config_str).unwrap();
142 let expected = RateLimitingConfig::new(TimeRateLimit::Limited {
143 max_per_interval: limit,
144 time_interval: Duration::from_secs(interval_secs),
145 });
146 assert_eq!(config, expected);
147 }
148
149 #[test]
150 fn top_level_and_override() {
151 #[rustfmt::skip]
152 let config_str = "[send-tx]\n\
153 max-per-interval = 3\n\
154 time-interval = \"30min\"\n\
155 [network.1.send-tx]\n\
156 max-per-interval = 4\n\
157 time-interval = \"40min\"\n";
158 let config: RateLimitingConfig = toml::from_str(config_str).unwrap();
159
160 let default_send_tx_limit = TimeRateLimit::limited(3, Duration::from_secs(1800));
161 let network_1_send_tx_limit = TimeRateLimit::limited(4, Duration::from_secs(2400));
162 let network_1_override = RateLimitOverride {
163 send_tx: Some(network_1_send_tx_limit.clone()),
164 };
165
166 let expected = RateLimitingConfig {
167 send_tx: default_send_tx_limit.clone(),
168 network: BTreeMap::from_iter([(1, network_1_override)]),
169 };
170
171 assert_eq!(config, expected);
172 assert_eq!(config.config_for(1).send_tx, &network_1_send_tx_limit);
173 assert_eq!(config.config_for(2).send_tx, &default_send_tx_limit);
174 assert_eq!(config.config_for(1337).send_tx, &default_send_tx_limit);
175 }
176}