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