sui_indexer_alt_jsonrpc/
config.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use anyhow::Context as _;
5use jsonrpsee::http_client::{HeaderMap, HeaderValue, HttpClient, HttpClientBuilder};
6use sui_default_config::DefaultConfig;
7use sui_protocol_config::ProtocolConfig;
8use sui_types::base_types::{ObjectID, SuiAddress};
9
10pub use sui_name_service::NameServiceConfig;
11
12pub const CLIENT_SDK_TYPE_HEADER: &str = "client-sdk-type";
13
14#[derive(Debug)]
15pub struct RpcConfig {
16    /// Configuration for object-related RPC methods.
17    pub objects: ObjectsConfig,
18
19    /// Configuration for dynamic-field-related RPC methods.
20    pub dynamic_fields: DynamicFieldsConfig,
21
22    /// Configuration for transaction-related RPC methods.
23    pub transactions: TransactionsConfig,
24
25    /// Configuration for SuiNS related RPC methods.
26    pub name_service: NameServiceConfig,
27
28    /// Configuration for coin-related RPC methods.
29    pub coins: CoinsConfig,
30
31    /// Configuration for methods that require a fullnode RPC connection,
32    /// including transaction execution, dry-running, and delegation coin queries etc.
33    pub node: NodeConfig,
34
35    /// Configuring limits for the package resolver.
36    pub package_resolver: sui_package_resolver::Limits,
37}
38
39#[DefaultConfig]
40#[derive(Clone, Default, Debug)]
41#[serde(deny_unknown_fields)]
42pub struct RpcLayer {
43    pub objects: ObjectsLayer,
44    pub dynamic_fields: DynamicFieldsLayer,
45    pub transactions: TransactionsLayer,
46    pub name_service: NameServiceLayer,
47    pub coins: CoinsLayer,
48    pub node: NodeLayer,
49    pub package_resolver: PackageResolverLayer,
50}
51
52#[derive(Debug, Clone)]
53pub struct ObjectsConfig {
54    /// The maximum number of keys that can be queried in a single multi-get request.
55    pub max_multi_get_objects: usize,
56
57    /// The default page size limit when querying objects, if none is provided.
58    pub default_page_size: usize,
59
60    /// The largest acceptable page size when querying objects. Requesting a page larger than
61    /// this is a user error.
62    pub max_page_size: usize,
63
64    /// The maximum depth a Display format string is allowed to nest field accesses.
65    pub max_display_field_depth: usize,
66
67    /// The maximum number of bytes occupied by Display field names and values in the output.
68    pub max_display_output_size: usize,
69
70    /// The maximum nesting depth of an owned object filter.
71    pub max_filter_depth: usize,
72
73    /// The maximum number of type filters in an owned object filter.
74    pub max_type_filters: usize,
75
76    /// The number of owned objects to fetch in one go when fulfilling a compound owned object
77    /// filter.
78    pub filter_scan_size: usize,
79
80    /// The number of times to retry a kv get operation. Retry is needed when a version of the object
81    /// is not yet found in the kv store due to the kv being behind the pg table's checkpoint watermark.
82    pub obj_retry_count: usize,
83
84    /// The interval between kv retry attempts in milliseconds.
85    pub obj_retry_interval_ms: u64,
86}
87
88#[DefaultConfig]
89#[derive(Clone, Default, Debug)]
90#[serde(deny_unknown_fields)]
91pub struct ObjectsLayer {
92    pub max_multi_get_objects: Option<usize>,
93    pub default_page_size: Option<usize>,
94    pub max_page_size: Option<usize>,
95    pub max_display_field_depth: Option<usize>,
96    pub max_display_output_size: Option<usize>,
97    pub max_filter_depth: Option<usize>,
98    pub max_type_filters: Option<usize>,
99    pub filter_scan_size: Option<usize>,
100    pub obj_retry_count: Option<usize>,
101    pub obj_retry_interval_ms: Option<u64>,
102}
103
104#[derive(Debug, Clone)]
105pub struct DynamicFieldsConfig {
106    /// The default page size limit when querying dynamic fields, if none is provided.
107    pub default_page_size: usize,
108
109    /// The largest acceptable page size when querying dynamic fields. Requesting a page larger
110    /// than this is a user error.
111    pub max_page_size: usize,
112}
113
114#[DefaultConfig]
115#[derive(Clone, Default, Debug)]
116#[serde(deny_unknown_fields)]
117pub struct DynamicFieldsLayer {
118    pub default_page_size: Option<usize>,
119    pub max_page_size: Option<usize>,
120}
121
122#[derive(Debug, Clone)]
123pub struct TransactionsConfig {
124    /// The default page size limit when querying transactions, if none is provided.
125    pub default_page_size: usize,
126
127    /// The largest acceptable page size when querying transactions. Requesting a page larger than
128    /// this is a user error.
129    pub max_page_size: usize,
130
131    /// The number of times to retry a read from kv or pg transaction tables. Retry is needed when a tx digest
132    /// is not yet found in the table due to it being behind other transaction table's checkpoint watermark.
133    pub tx_retry_count: usize,
134
135    /// The interval between tx_digest retry attempts in milliseconds.
136    pub tx_retry_interval_ms: u64,
137}
138
139#[DefaultConfig]
140#[derive(Clone, Default, Debug)]
141#[serde(deny_unknown_fields)]
142pub struct TransactionsLayer {
143    pub default_page_size: Option<usize>,
144    pub max_page_size: Option<usize>,
145    pub tx_retry_count: Option<usize>,
146    pub tx_retry_interval_ms: Option<u64>,
147}
148
149#[DefaultConfig]
150#[derive(Clone, Default, Debug)]
151#[serde(deny_unknown_fields)]
152pub struct NameServiceLayer {
153    pub package_address: Option<SuiAddress>,
154    pub registry_id: Option<ObjectID>,
155    pub reverse_registry_id: Option<ObjectID>,
156}
157
158#[derive(Debug, Clone)]
159pub struct CoinsConfig {
160    /// The default page size limit when querying coins, if none is provided.
161    pub default_page_size: usize,
162
163    /// The largest acceptable page size when querying coins. Requesting a page larger than
164    /// this is a user error.
165    pub max_page_size: usize,
166}
167
168#[DefaultConfig]
169#[derive(Clone, Default, Debug)]
170#[serde(deny_unknown_fields)]
171pub struct CoinsLayer {
172    pub default_page_size: Option<usize>,
173    pub max_page_size: Option<usize>,
174}
175
176#[derive(Clone, Debug)]
177pub struct NodeConfig {
178    /// The value of the header to be sent to the fullnode RPC, used to distinguish between different instances.
179    pub header_value: String,
180    /// The maximum size of the request body allowed.
181    pub max_request_size: u32,
182}
183
184#[DefaultConfig]
185#[derive(Clone, Default, Debug)]
186#[serde(deny_unknown_fields)]
187pub struct NodeLayer {
188    pub header_value: Option<String>,
189    pub max_request_size: Option<u32>,
190}
191
192#[DefaultConfig]
193#[derive(Clone, Debug)]
194#[serde(deny_unknown_fields)]
195pub struct PackageResolverLayer {
196    pub max_type_argument_depth: usize,
197    pub max_type_argument_width: usize,
198    pub max_type_nodes: usize,
199    pub max_move_value_depth: usize,
200}
201
202impl RpcLayer {
203    /// Generate an example configuration, suitable for demonstrating the fields available to
204    /// configure.
205    pub fn example() -> Self {
206        Self {
207            objects: ObjectsConfig::default().into(),
208            dynamic_fields: DynamicFieldsConfig::default().into(),
209            transactions: TransactionsConfig::default().into(),
210            name_service: NameServiceConfig::default().into(),
211            coins: CoinsConfig::default().into(),
212            package_resolver: PackageResolverLayer::default(),
213            node: NodeConfig::default().into(),
214        }
215    }
216
217    pub fn finish(self) -> RpcConfig {
218        RpcConfig {
219            objects: self.objects.finish(ObjectsConfig::default()),
220            dynamic_fields: self.dynamic_fields.finish(DynamicFieldsConfig::default()),
221            transactions: self.transactions.finish(TransactionsConfig::default()),
222            name_service: self.name_service.finish(NameServiceConfig::default()),
223            coins: self.coins.finish(CoinsConfig::default()),
224            node: self.node.finish(NodeConfig::default()),
225            package_resolver: self.package_resolver.finish(),
226        }
227    }
228}
229
230impl ObjectsLayer {
231    pub fn finish(self, base: ObjectsConfig) -> ObjectsConfig {
232        ObjectsConfig {
233            max_multi_get_objects: self
234                .max_multi_get_objects
235                .unwrap_or(base.max_multi_get_objects),
236            default_page_size: self.default_page_size.unwrap_or(base.default_page_size),
237            max_page_size: self.max_page_size.unwrap_or(base.max_page_size),
238            max_display_field_depth: self
239                .max_display_field_depth
240                .unwrap_or(base.max_display_field_depth),
241            max_display_output_size: self
242                .max_display_output_size
243                .unwrap_or(base.max_display_output_size),
244            max_filter_depth: self.max_filter_depth.unwrap_or(base.max_filter_depth),
245            max_type_filters: self.max_type_filters.unwrap_or(base.max_type_filters),
246            filter_scan_size: self.filter_scan_size.unwrap_or(base.filter_scan_size),
247            obj_retry_count: self.obj_retry_count.unwrap_or(base.obj_retry_count),
248            obj_retry_interval_ms: self
249                .obj_retry_interval_ms
250                .unwrap_or(base.obj_retry_interval_ms),
251        }
252    }
253}
254
255impl DynamicFieldsLayer {
256    pub fn finish(self, base: DynamicFieldsConfig) -> DynamicFieldsConfig {
257        DynamicFieldsConfig {
258            default_page_size: self.default_page_size.unwrap_or(base.default_page_size),
259            max_page_size: self.max_page_size.unwrap_or(base.max_page_size),
260        }
261    }
262}
263
264impl TransactionsLayer {
265    pub fn finish(self, base: TransactionsConfig) -> TransactionsConfig {
266        TransactionsConfig {
267            default_page_size: self.default_page_size.unwrap_or(base.default_page_size),
268            max_page_size: self.max_page_size.unwrap_or(base.max_page_size),
269            tx_retry_count: self.tx_retry_count.unwrap_or(base.tx_retry_count),
270            tx_retry_interval_ms: self
271                .tx_retry_interval_ms
272                .unwrap_or(base.tx_retry_interval_ms),
273        }
274    }
275}
276
277impl NameServiceLayer {
278    pub fn finish(self, base: NameServiceConfig) -> NameServiceConfig {
279        NameServiceConfig {
280            package_address: self.package_address.unwrap_or(base.package_address),
281            registry_id: self.registry_id.unwrap_or(base.registry_id),
282            reverse_registry_id: self.reverse_registry_id.unwrap_or(base.reverse_registry_id),
283        }
284    }
285}
286
287impl CoinsLayer {
288    pub fn finish(self, base: CoinsConfig) -> CoinsConfig {
289        CoinsConfig {
290            default_page_size: self.default_page_size.unwrap_or(base.default_page_size),
291            max_page_size: self.max_page_size.unwrap_or(base.max_page_size),
292        }
293    }
294}
295
296impl NodeConfig {
297    pub fn client(&self, fullnode_rpc_url: url::Url) -> anyhow::Result<HttpClient> {
298        let mut headers = HeaderMap::new();
299        headers.insert(
300            CLIENT_SDK_TYPE_HEADER,
301            HeaderValue::from_str(&self.header_value)?,
302        );
303
304        HttpClientBuilder::default()
305            .max_request_size(self.max_request_size)
306            .set_headers(headers.clone())
307            .build(&fullnode_rpc_url)
308            .context("Failed to initialize fullnode RPC client")
309    }
310}
311
312impl NodeLayer {
313    pub fn finish(self, base: NodeConfig) -> NodeConfig {
314        NodeConfig {
315            header_value: self.header_value.unwrap_or(base.header_value),
316            max_request_size: self.max_request_size.unwrap_or(base.max_request_size),
317        }
318    }
319}
320
321impl PackageResolverLayer {
322    pub fn finish(self) -> sui_package_resolver::Limits {
323        sui_package_resolver::Limits {
324            max_type_argument_depth: self.max_type_argument_depth,
325            max_type_argument_width: self.max_type_argument_width,
326            max_type_nodes: self.max_type_nodes,
327            max_move_value_depth: self.max_move_value_depth,
328        }
329    }
330}
331
332impl Default for RpcConfig {
333    fn default() -> Self {
334        Self {
335            objects: ObjectsConfig::default(),
336            dynamic_fields: DynamicFieldsConfig::default(),
337            transactions: TransactionsConfig::default(),
338            name_service: NameServiceConfig::default(),
339            coins: CoinsConfig::default(),
340            node: NodeConfig::default(),
341            package_resolver: PackageResolverLayer::default().finish(),
342        }
343    }
344}
345
346impl Default for ObjectsConfig {
347    fn default() -> Self {
348        Self {
349            max_multi_get_objects: 50,
350            default_page_size: 50,
351            max_page_size: 100,
352            max_display_field_depth: 10,
353            max_display_output_size: 1024 * 1024,
354            max_filter_depth: 3,
355            max_type_filters: 10,
356            filter_scan_size: 200,
357            obj_retry_count: 5,
358            obj_retry_interval_ms: 100,
359        }
360    }
361}
362
363impl Default for DynamicFieldsConfig {
364    fn default() -> Self {
365        Self {
366            default_page_size: 50,
367            max_page_size: 100,
368        }
369    }
370}
371
372impl Default for TransactionsConfig {
373    fn default() -> Self {
374        Self {
375            default_page_size: 50,
376            max_page_size: 100,
377            tx_retry_count: 5,
378            tx_retry_interval_ms: 100,
379        }
380    }
381}
382
383impl Default for CoinsConfig {
384    fn default() -> Self {
385        Self {
386            default_page_size: 50,
387            max_page_size: 100,
388        }
389    }
390}
391
392impl Default for NodeConfig {
393    fn default() -> Self {
394        Self {
395            header_value: "sui-indexer-alt-jsonrpc".to_string(),
396            max_request_size: (10 * 2) << 20, // 10MB
397        }
398    }
399}
400
401impl Default for PackageResolverLayer {
402    fn default() -> Self {
403        // SAFETY: Accessing the max supported config by the binary (and disregarding specific
404        // chain state) is a safe operation for the RPC because we are only using this to set
405        // default values which can be overridden by configuration.
406        let config = ProtocolConfig::get_for_max_version_UNSAFE();
407
408        Self {
409            max_type_argument_depth: config.max_type_argument_depth() as usize,
410            max_type_argument_width: config.max_generic_instantiation_length() as usize,
411            max_type_nodes: config.max_type_nodes() as usize,
412            max_move_value_depth: config.max_move_value_depth() as usize,
413        }
414    }
415}
416
417impl From<ObjectsConfig> for ObjectsLayer {
418    fn from(config: ObjectsConfig) -> Self {
419        Self {
420            max_multi_get_objects: Some(config.max_multi_get_objects),
421            default_page_size: Some(config.default_page_size),
422            max_page_size: Some(config.max_page_size),
423            max_display_field_depth: Some(config.max_display_field_depth),
424            max_display_output_size: Some(config.max_display_output_size),
425            max_filter_depth: Some(config.max_filter_depth),
426            max_type_filters: Some(config.max_type_filters),
427            filter_scan_size: Some(config.filter_scan_size),
428            obj_retry_count: Some(config.obj_retry_count),
429            obj_retry_interval_ms: Some(config.obj_retry_interval_ms),
430        }
431    }
432}
433
434impl From<DynamicFieldsConfig> for DynamicFieldsLayer {
435    fn from(config: DynamicFieldsConfig) -> Self {
436        Self {
437            default_page_size: Some(config.default_page_size),
438            max_page_size: Some(config.max_page_size),
439        }
440    }
441}
442
443impl From<TransactionsConfig> for TransactionsLayer {
444    fn from(config: TransactionsConfig) -> Self {
445        Self {
446            default_page_size: Some(config.default_page_size),
447            max_page_size: Some(config.max_page_size),
448            tx_retry_count: Some(config.tx_retry_count),
449            tx_retry_interval_ms: Some(config.tx_retry_interval_ms),
450        }
451    }
452}
453
454impl From<NameServiceConfig> for NameServiceLayer {
455    fn from(config: NameServiceConfig) -> Self {
456        Self {
457            package_address: Some(config.package_address),
458            registry_id: Some(config.registry_id),
459            reverse_registry_id: Some(config.reverse_registry_id),
460        }
461    }
462}
463
464impl From<CoinsConfig> for CoinsLayer {
465    fn from(config: CoinsConfig) -> Self {
466        Self {
467            default_page_size: Some(config.default_page_size),
468            max_page_size: Some(config.max_page_size),
469        }
470    }
471}
472
473impl From<NodeConfig> for NodeLayer {
474    fn from(config: NodeConfig) -> Self {
475        Self {
476            header_value: Some(config.header_value),
477            max_request_size: Some(config.max_request_size),
478        }
479    }
480}