1use std::net::SocketAddr;
5use std::time::Duration;
6
7#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
8#[serde(rename_all = "kebab-case")]
9pub struct RpcConfig {
10 #[serde(skip_serializing_if = "Option::is_none")]
20 pub enable_indexing: Option<bool>,
21
22 #[serde(skip_serializing_if = "Option::is_none")]
36 pub use_experimental_rpc_store: Option<bool>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
42 pub https_address: Option<SocketAddr>,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
48 pub tls: Option<RpcTlsConfig>,
49
50 #[serde(skip_serializing_if = "Option::is_none")]
57 pub max_json_move_value_size: Option<usize>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
68 pub max_json_move_value_response_size: Option<usize>,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub index_initialization: Option<RpcIndexInitConfig>,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
84 pub ledger_history_indexing: Option<bool>,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
91 pub ledger_history: Option<LedgerHistoryConfig>,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub display: Option<DisplayConfig>,
96}
97
98impl RpcConfig {
99 pub fn enable_indexing(&self) -> bool {
100 self.enable_indexing.unwrap_or(false)
101 }
102
103 pub fn use_experimental_rpc_store(&self) -> bool {
104 self.use_experimental_rpc_store.unwrap_or(false)
105 }
106
107 pub fn https_address(&self) -> SocketAddr {
108 self.https_address
109 .unwrap_or_else(|| SocketAddr::from(([0, 0, 0, 0], 9443)))
110 }
111
112 pub fn tls_config(&self) -> Option<&RpcTlsConfig> {
113 self.tls.as_ref()
114 }
115
116 pub fn max_json_move_value_size(&self) -> usize {
117 self.max_json_move_value_size.unwrap_or(1024 * 1024)
118 }
119
120 pub fn max_json_move_value_response_size(&self) -> usize {
121 self.max_json_move_value_response_size
122 .unwrap_or(16 * 1024 * 1024)
123 }
124
125 pub fn index_initialization_config(&self) -> Option<&RpcIndexInitConfig> {
126 self.index_initialization.as_ref()
127 }
128
129 pub fn ledger_history_indexing(&self) -> bool {
130 self.ledger_history_indexing.unwrap_or(false)
131 }
132
133 pub fn ledger_history(&self) -> &LedgerHistoryConfig {
134 const DEFAULT_LEDGER_HISTORY_CONFIG: LedgerHistoryConfig = LedgerHistoryConfig {
135 list_transactions: None,
136 list_events: None,
137 list_checkpoints: None,
138 bitmap_bucket_scan_budget: None,
139 chunk_bucket_scan_budget: None,
140 max_bitmap_filter_literals: None,
141 };
142
143 self.ledger_history
144 .as_ref()
145 .unwrap_or(&DEFAULT_LEDGER_HISTORY_CONFIG)
146 }
147
148 pub fn validate(&self) -> anyhow::Result<()> {
151 self.ledger_history().validate()
152 }
153
154 pub fn display(&self) -> &DisplayConfig {
155 const DEFAULT_DISPLAY_CONFIG: DisplayConfig = DisplayConfig {
156 max_field_depth: None,
157 max_format_nodes: None,
158 max_object_loads: None,
159 max_move_value_depth: None,
160 max_output_size: None,
161 };
162
163 self.display.as_ref().unwrap_or(&DEFAULT_DISPLAY_CONFIG)
164 }
165}
166
167#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
168#[serde(rename_all = "kebab-case")]
169pub struct RpcTlsConfig {
170 cert: String,
172 key: String,
174}
175
176impl RpcTlsConfig {
177 pub fn cert(&self) -> &str {
178 &self.cert
179 }
180
181 pub fn key(&self) -> &str {
182 &self.key
183 }
184}
185
186#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
188#[serde(rename_all = "kebab-case")]
189pub struct RpcIndexInitConfig {
190 #[serde(skip_serializing_if = "Option::is_none")]
195 pub db_write_buffer_size: Option<usize>,
196
197 #[serde(skip_serializing_if = "Option::is_none")]
201 pub cf_write_buffer_size: Option<usize>,
202
203 #[serde(skip_serializing_if = "Option::is_none")]
208 pub cf_max_write_buffer_number: Option<i32>,
209
210 #[serde(skip_serializing_if = "Option::is_none")]
214 pub max_background_jobs: Option<i32>,
215
216 #[serde(skip_serializing_if = "Option::is_none")]
221 pub batch_size_limit: Option<usize>,
222}
223
224#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
225#[serde(rename_all = "kebab-case")]
226pub struct DisplayConfig {
227 #[serde(skip_serializing_if = "Option::is_none")]
232 max_field_depth: Option<usize>,
233
234 #[serde(skip_serializing_if = "Option::is_none")]
239 max_format_nodes: Option<usize>,
240
241 #[serde(skip_serializing_if = "Option::is_none")]
245 max_object_loads: Option<usize>,
246
247 #[serde(skip_serializing_if = "Option::is_none")]
251 max_move_value_depth: Option<usize>,
252
253 #[serde(skip_serializing_if = "Option::is_none")]
260 max_output_size: Option<usize>,
261}
262
263impl DisplayConfig {
264 pub fn max_field_depth(&self) -> usize {
265 self.max_field_depth.unwrap_or(32)
266 }
267
268 pub fn max_format_nodes(&self) -> usize {
269 self.max_format_nodes.unwrap_or(32768)
270 }
271
272 pub fn max_object_loads(&self) -> usize {
273 self.max_object_loads.unwrap_or(8)
274 }
275
276 pub fn max_move_value_depth(&self) -> usize {
277 self.max_move_value_depth.unwrap_or(32)
278 }
279
280 pub fn max_output_size(&self) -> usize {
281 self.max_output_size.unwrap_or(1024 * 1024)
282 }
283}
284
285const DEFAULT_LEDGER_HISTORY_METHOD_TIMEOUT_MS: u64 = 5_000;
286const DEFAULT_BITMAP_BUCKET_SCAN_BUDGET: usize = 1_024;
287const DEFAULT_CHUNK_BUCKET_SCAN_BUDGET: usize = 256;
288const DEFAULT_MAX_BITMAP_FILTER_LITERALS: usize = 10;
289const _: () = assert!(DEFAULT_CHUNK_BUCKET_SCAN_BUDGET <= DEFAULT_BITMAP_BUCKET_SCAN_BUDGET);
293
294struct LedgerHistoryMethodDefaults {
297 default_limit_items: u32,
298 max_limit_items: u32,
299 chunk_max: usize,
300}
301
302const LIST_TRANSACTIONS_DEFAULTS: LedgerHistoryMethodDefaults = LedgerHistoryMethodDefaults {
303 default_limit_items: 50,
304 max_limit_items: 500,
305 chunk_max: 32,
306};
307const LIST_EVENTS_DEFAULTS: LedgerHistoryMethodDefaults = LedgerHistoryMethodDefaults {
308 default_limit_items: 50,
309 max_limit_items: 1_000,
310 chunk_max: 32,
311};
312const LIST_CHECKPOINTS_DEFAULTS: LedgerHistoryMethodDefaults = LedgerHistoryMethodDefaults {
313 default_limit_items: 10,
314 max_limit_items: 100,
315 chunk_max: 16,
316};
317
318#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
321#[serde(rename_all = "kebab-case")]
322pub struct LedgerHistoryMethodConfig {
323 #[serde(skip_serializing_if = "Option::is_none")]
325 pub timeout_ms: Option<u64>,
326
327 #[serde(skip_serializing_if = "Option::is_none")]
329 pub default_limit_items: Option<u32>,
330
331 #[serde(skip_serializing_if = "Option::is_none")]
333 pub max_limit_items: Option<u32>,
334
335 #[serde(skip_serializing_if = "Option::is_none")]
337 pub chunk_max: Option<usize>,
338}
339
340#[derive(Clone, Copy, Debug)]
342pub struct ResolvedLedgerHistoryMethodConfig {
343 pub timeout: Duration,
344 pub default_limit_items: u32,
345 pub max_limit_items: u32,
346 pub chunk_max: usize,
347}
348
349impl LedgerHistoryMethodConfig {
350 fn resolve(
351 this: Option<&LedgerHistoryMethodConfig>,
352 defaults: LedgerHistoryMethodDefaults,
353 ) -> ResolvedLedgerHistoryMethodConfig {
354 ResolvedLedgerHistoryMethodConfig {
355 timeout: Duration::from_millis(
356 this.and_then(|c| c.timeout_ms)
357 .unwrap_or(DEFAULT_LEDGER_HISTORY_METHOD_TIMEOUT_MS),
358 ),
359 default_limit_items: this
360 .and_then(|c| c.default_limit_items)
361 .unwrap_or(defaults.default_limit_items),
362 max_limit_items: this
363 .and_then(|c| c.max_limit_items)
364 .unwrap_or(defaults.max_limit_items),
365 chunk_max: this.and_then(|c| c.chunk_max).unwrap_or(defaults.chunk_max),
366 }
367 }
368}
369
370#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
374#[serde(rename_all = "kebab-case")]
375pub struct LedgerHistoryConfig {
376 #[serde(skip_serializing_if = "Option::is_none")]
378 pub list_transactions: Option<LedgerHistoryMethodConfig>,
379
380 #[serde(skip_serializing_if = "Option::is_none")]
382 pub list_events: Option<LedgerHistoryMethodConfig>,
383
384 #[serde(skip_serializing_if = "Option::is_none")]
386 pub list_checkpoints: Option<LedgerHistoryMethodConfig>,
387
388 #[serde(skip_serializing_if = "Option::is_none")]
394 pub bitmap_bucket_scan_budget: Option<usize>,
395
396 #[serde(skip_serializing_if = "Option::is_none")]
403 pub chunk_bucket_scan_budget: Option<usize>,
404
405 #[serde(skip_serializing_if = "Option::is_none")]
412 pub max_bitmap_filter_literals: Option<usize>,
413}
414
415impl LedgerHistoryConfig {
416 pub fn list_transactions(&self) -> ResolvedLedgerHistoryMethodConfig {
417 LedgerHistoryMethodConfig::resolve(
418 self.list_transactions.as_ref(),
419 LIST_TRANSACTIONS_DEFAULTS,
420 )
421 }
422
423 pub fn list_events(&self) -> ResolvedLedgerHistoryMethodConfig {
424 LedgerHistoryMethodConfig::resolve(self.list_events.as_ref(), LIST_EVENTS_DEFAULTS)
425 }
426
427 pub fn list_checkpoints(&self) -> ResolvedLedgerHistoryMethodConfig {
428 LedgerHistoryMethodConfig::resolve(
429 self.list_checkpoints.as_ref(),
430 LIST_CHECKPOINTS_DEFAULTS,
431 )
432 }
433
434 pub fn bitmap_bucket_scan_budget(&self) -> usize {
435 self.bitmap_bucket_scan_budget
436 .unwrap_or(DEFAULT_BITMAP_BUCKET_SCAN_BUDGET)
437 }
438
439 pub fn chunk_bucket_scan_budget(&self) -> usize {
440 self.chunk_bucket_scan_budget
441 .unwrap_or(DEFAULT_CHUNK_BUCKET_SCAN_BUDGET)
442 .min(self.bitmap_bucket_scan_budget())
443 }
444
445 pub fn max_bitmap_filter_literals(&self) -> usize {
446 self.max_bitmap_filter_literals
447 .unwrap_or(DEFAULT_MAX_BITMAP_FILTER_LITERALS)
448 }
449
450 pub fn validate(&self) -> anyhow::Result<()> {
457 anyhow::ensure!(
458 self.bitmap_bucket_scan_budget() >= self.max_bitmap_filter_literals(),
459 "ledger_history.bitmap_bucket_scan_budget ({}) must be >= \
460 max_bitmap_filter_literals ({}) so every filter leaf gets at least one \
461 bucket before SCAN_LIMIT",
462 self.bitmap_bucket_scan_budget(),
463 self.max_bitmap_filter_literals(),
464 );
465 Ok(())
466 }
467}