1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/// Default gas budget for a simulated invocation.
pub const DEFAULT_VIEW_GAS_LIMIT: u64 = 10_000_000;
/// Operator policy for one shared-snapshot request, not consensus parameters.
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct ViewBatchConfig {
/// Maximum number of independent calls in a group.
pub max_calls: u32,
/// Sum of calldata bytes across calls.
pub max_calldata_bytes: u32,
/// Sum of declared gas limits in each attempt.
pub max_gas: u64,
/// Encoded backend response, including events; the JSON response cap also applies.
pub max_response_bytes: u32,
}
impl Default for ViewBatchConfig {
fn default() -> Self {
Self {
max_calls: 16,
max_calldata_bytes: 65_536,
max_gas: 160_000_000,
max_response_bytes: 1_048_576,
}
}
}
/// Server-wide bounds on JSON-RPC requests. The shared dispatcher
/// rejects requests that exceed these before they reach a backend.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct RpcServerConfig {
/// Aggregate view-batch policy. Per-call view limits also apply.
pub view_batch: ViewBatchConfig,
/// Maximum shared-snapshot response wait, measured from request start.
pub view_timeout_ms: u64,
/// Optional end-to-end deadline for one public JSON-RPC method.
///
/// The deadline starts before shared validation and is passed explicitly to
/// backend planes. `None` preserves the embedding application's own policy.
pub request_timeout_ms: Option<u64>,
/// Per-page cap on `getEvents` responses; requests with `limit`
/// above this are rejected as `InvalidParam`.
pub max_events_per_page: u32,
/// Per-page cap on transaction-reference queries. The effective maximum
/// is at most the indexer's 200; larger request limits are invalid params.
pub max_transactions_per_page: u32,
/// Maximum length of the opaque pagination cursor on paginated
/// methods; longer cursors are rejected as `InvalidParam`.
pub max_cursor_bytes: usize,
/// Maximum simulated-invocation calldata byte length; larger
/// requests are rejected as `InvalidParam` before backend dispatch.
pub max_view_calldata_bytes: usize,
/// Gas budget assigned to a simulated invocation when the request names
/// no `gasLimit`, and the ceiling a requested `gasLimit` may not exceed.
///
/// The shared server validates request shape; concrete backends pass
/// this cap into the hogd view invocation. Keep the default bounded so a
/// simulation backend cannot expose an unbounded root invocation.
pub view_gas_limit: u64,
/// Upper bound on a single chain-tip probe issued on behalf of the
/// `sendTransaction` pre-checks, whether by a submit or by the
/// background refresh. The pre-checks are advisory, so a slow or
/// stalled backend must not delay submission past this bound.
pub tip_probe_timeout_ms: u64,
/// How often the background task refreshes the chain-tip reading the
/// `sendTransaction` pre-checks read. A reading stays usable for
/// this plus `tip_probe_timeout_ms`, so a refresh always lands first
/// and a submit never fetches on its own latency path. Zero disables
/// both the task and reuse. Staleness is safe: the tip only grows, and
/// the config it carries changes only at upgrade blocks.
pub tip_refresh_interval_ms: u64,
}
impl Default for RpcServerConfig {
fn default() -> Self {
Self {
view_batch: ViewBatchConfig::default(),
view_timeout_ms: 30_000,
request_timeout_ms: None,
max_events_per_page: 200,
max_transactions_per_page: 200,
max_cursor_bytes: 256,
max_view_calldata_bytes: 64 * 1024,
view_gas_limit: DEFAULT_VIEW_GAS_LIMIT,
tip_probe_timeout_ms: 2_000,
tip_refresh_interval_ms: 5_000,
}
}
}
impl RpcServerConfig {
/// Set the endpoint's aggregate shared-read policy.
#[must_use]
pub const fn with_view_batch(mut self, limits: ViewBatchConfig) -> Self {
self.view_batch = limits;
self
}
#[must_use]
pub const fn with_view_timeout_ms(mut self, timeout_ms: u64) -> Self {
self.view_timeout_ms = timeout_ms;
self
}
/// Set an end-to-end timeout for every public JSON-RPC request.
#[must_use]
pub fn with_request_timeout_ms(mut self, timeout_ms: u64) -> Self {
self.request_timeout_ms = Some(timeout_ms.max(1));
self
}
/// Override the per-page event limit.
#[must_use]
pub fn with_max_events_per_page(mut self, max: u32) -> Self {
self.max_events_per_page = max;
self
}
/// Override the per-page transaction-ref limit.
#[must_use]
pub fn with_max_transactions_per_page(mut self, max: u32) -> Self {
self.max_transactions_per_page = max;
self
}
/// Effective event page cap, bounded by the indexer's limit.
pub fn event_page_cap(&self) -> u32 {
self.max_events_per_page.min(200)
}
/// Effective transaction-reference page cap, bounded by the indexer's limit.
pub fn transaction_page_cap(&self) -> u32 {
self.max_transactions_per_page.min(200)
}
/// Override the cursor byte length cap.
#[must_use]
pub fn with_max_cursor_bytes(mut self, max: usize) -> Self {
self.max_cursor_bytes = max;
self
}
/// Override the view calldata byte length cap.
#[must_use]
pub fn with_max_view_calldata_bytes(mut self, max: usize) -> Self {
self.max_view_calldata_bytes = max;
self
}
/// Override the view execution gas limit.
#[must_use]
pub fn with_view_gas_limit(mut self, gas_limit: u64) -> Self {
self.view_gas_limit = gas_limit;
self
}
/// Override the pre-checks' per-probe timeout.
#[must_use]
pub fn with_tip_probe_timeout_ms(mut self, timeout_ms: u64) -> Self {
self.tip_probe_timeout_ms = timeout_ms;
self
}
/// Override how often the background task refreshes the tip reading.
/// The reuse window follows it, plus `tip_probe_timeout_ms`.
#[must_use]
pub fn with_tip_refresh_interval_ms(mut self, interval_ms: u64) -> Self {
self.tip_refresh_interval_ms = interval_ms;
self
}
}