1use std::{collections::HashMap, path::Path};
7
8use agglayer_primitives::Address;
9use outbound::OutboundConfig;
10use serde::{de::DeserializeSeed, Deserialize, Serialize};
11use serde_with::DisplayFromStr;
12use shutdown::ShutdownConfig;
13use url::Url;
14
15pub use self::telemetry::TelemetryConfig;
16
17pub(crate) const DEFAULT_IP: std::net::Ipv4Addr = std::net::Ipv4Addr::new(0, 0, 0, 0);
18
19pub(crate) mod auth;
20pub mod certificate_orchestrator;
21pub mod epoch;
22pub mod grpc;
23pub(crate) mod l1;
24pub(crate) mod l2;
25pub mod log;
26mod multiplier;
27pub mod outbound;
28mod port;
29pub mod rate_limiting;
30pub(crate) mod rpc;
31pub mod settlement_service;
32pub mod shutdown;
33pub mod storage;
34pub(crate) mod telemetry;
35mod with;
36
37pub use auth::{AuthConfig, GcpKmsConfig, LocalConfig, PrivateKey};
38pub use epoch::Epoch;
39pub use l1::L1;
40pub use l2::L2;
41pub use log::Log;
42pub use multiplier::Multiplier;
43use port::{Port, PortDefaults};
44pub use rate_limiting::RateLimitingConfig;
45pub use rpc::RpcConfig;
46
47#[serde_with::serde_as]
49#[derive(Default, Deserialize, Serialize, Debug, PartialEq, Eq)]
50#[serde(rename_all = "kebab-case")]
51#[serde(deny_unknown_fields)]
52#[serde(default)]
53pub struct Config {
54 #[serde_as(as = "HashMap<DisplayFromStr, _>")]
59 pub full_node_rpcs: HashMap<u32, Url>,
60
61 #[serde(default)]
62 pub l2: L2,
63
64 #[serde_as(as = "HashMap<DisplayFromStr, _>")]
65 #[serde(default)]
66 pub proof_signers: HashMap<u32, Address>,
67
68 #[serde(default)]
70 pub log: Log,
71
72 #[serde(default)]
74 pub rpc: RpcConfig,
75
76 #[serde(default)]
78 pub rate_limiting: RateLimitingConfig,
79
80 #[serde(default)]
82 pub outbound: OutboundConfig,
83
84 #[serde(default)]
86 pub l1: L1,
87
88 #[serde(default)]
90 pub auth: AuthConfig,
91
92 #[serde(default)]
94 pub telemetry: TelemetryConfig,
95
96 #[serde(default)]
98 pub epoch: Epoch,
99
100 #[serde(default)]
102 pub shutdown: ShutdownConfig,
103
104 #[serde(default)]
106 pub certificate_orchestrator: certificate_orchestrator::CertificateOrchestrator,
107
108 #[serde(default)]
110 pub storage: storage::StorageConfig,
111
112 #[serde(default)]
114 pub prover: prover_config::ProverType,
115
116 #[serde(default = "default_prover_buffer_size")]
117 pub prover_buffer_size: usize,
118
119 #[serde(default)]
120 #[serde(skip_serializing_if = "is_false")]
121 pub debug_mode: bool,
122
123 #[serde(default)]
124 #[serde(skip_serializing_if = "is_false")]
125 pub mock_verifier: bool,
126
127 #[serde(default)]
128 pub grpc: grpc::GrpcConfig,
129}
130
131impl Config {
132 pub fn try_load(path: &Path) -> Result<Self, ConfigurationError> {
133 let reader = std::fs::read_to_string(path).map_err(|source| {
134 ConfigurationError::UnableToReadConfigFile {
135 path: path.to_path_buf(),
136 source,
137 }
138 })?;
139
140 let path = path
141 .parent()
142 .ok_or_else(|| ConfigurationError::UnableToReadConfigFile {
143 path: path.to_path_buf(),
144 source: std::io::Error::other(
145 "Unable to determine the parent folder of the configuration file",
146 ),
147 })?;
148
149 let config_with_path = ConfigDeserializer { path };
150
151 let deserializer = toml::de::Deserializer::parse(&reader)
152 .map_err(ConfigurationError::DeserializationError)?;
153
154 config_with_path
155 .deserialize(deserializer)
156 .map_err(ConfigurationError::DeserializationError)
157 }
158}
159
160impl Config {
161 pub fn new(base_path: &Path) -> Self {
162 Self {
163 storage: storage::StorageConfig::new_from_path(base_path),
164 full_node_rpcs: Default::default(),
165 proof_signers: Default::default(),
166 log: Default::default(),
167 rpc: Default::default(),
168 rate_limiting: Default::default(),
169 outbound: Default::default(),
170 l1: Default::default(),
171 l2: Default::default(),
172 auth: Default::default(),
173 telemetry: Default::default(),
174 epoch: Default::default(),
175 shutdown: Default::default(),
176 certificate_orchestrator: Default::default(),
177 prover: prover_config::ProverType::NetworkProver(
178 prover_config::NetworkProverConfig::default(),
179 ),
180 prover_buffer_size: default_prover_buffer_size(),
181 debug_mode: false,
182 mock_verifier: false,
183 grpc: Default::default(),
184 }
185 }
186
187 pub fn readrpc_addr(&self) -> std::net::SocketAddr {
189 std::net::SocketAddr::from((self.rpc.host, self.rpc.readrpc_port.as_u16()))
190 }
191
192 pub fn public_grpc_addr(&self) -> std::net::SocketAddr {
194 std::net::SocketAddr::from((self.rpc.host, self.rpc.grpc_port.as_u16()))
195 }
196
197 pub fn admin_rpc_addr(&self) -> std::net::SocketAddr {
199 std::net::SocketAddr::from((self.rpc.host, self.rpc.admin_port.as_u16()))
200 }
201
202 pub fn path_contextualized(mut self, base_path: &Path) -> Self {
203 self.storage = self.storage.path_contextualized(base_path);
204
205 self
206 }
207
208 pub(crate) fn validate(self) -> Result<Self, ConfigurationError> {
209 Ok(self)
210 }
211}
212
213#[derive(Debug, thiserror::Error)]
214pub enum ConfigurationError {
215 #[error("Unable to read the configuration file: {source}")]
216 UnableToReadConfigFile {
217 path: std::path::PathBuf,
218 #[source]
219 source: std::io::Error,
220 },
221
222 #[error("Failed to deserialize the configuration: {0}")]
223 DeserializationError(#[from] toml::de::Error),
224}
225
226#[cfg(feature = "testutils")]
227impl Config {
228 pub fn new_for_test() -> Self {
229 Config::new(Path::new("/tmp/agglayer"))
230 }
231}
232
233struct ConfigDeserializer<'a> {
234 path: &'a Path,
235}
236
237impl<'de> DeserializeSeed<'de> for ConfigDeserializer<'_> {
238 type Value = Config;
239
240 fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
241 where
242 D: serde::Deserializer<'de>,
243 {
244 let mut config_candidate: Config = serde::Deserialize::deserialize(deserializer)?;
245
246 config_candidate.storage =
247 config_candidate
248 .storage
249 .path_contextualized(&self.path.canonicalize().map_err(|error| {
250 serde::de::Error::custom(format!(
251 "Unable to canonicalize the storage path: {error}"
252 ))
253 })?);
254
255 config_candidate
256 .validate()
257 .map_err(|e| serde::de::Error::custom(e.to_string()))
258 }
259}
260
261const fn default_prover_buffer_size() -> usize {
263 100
264}
265
266fn is_false(b: &bool) -> bool {
267 !*b
268}
269
270pub(crate) fn is_default<T: Default + PartialEq>(t: &T) -> bool {
271 *t == Default::default()
272}
273
274#[cfg(feature = "testutils")]
275pub fn redact_storage_path() -> insta::internals::Redaction {
276 use insta::internals::Content;
277 insta::dynamic_redaction(|value, path| {
278 if path.to_string() != "storage.db-path" {
279 if let Content::String(path) = value {
280 let cur_dir = Path::new("./").canonicalize().unwrap();
281 return Content::String(path.replace(cur_dir.to_str().unwrap(), "/tmp/agglayer"));
282 }
283 }
284
285 value
286 })
287}