1use std::sync::Arc;
5
6use jsonrpsee::{
7 core::{client::ClientT, RpcResult},
8 proc_macros::rpc,
9 types::{error::INTERNAL_ERROR_CODE, ErrorObject},
10};
11use jsonrpsee_http_client::{HttpClient, HttpClientBuilder};
12use parking_lot::Mutex;
13use serde_json::{self as json, value::RawValue, Value as JsonValue};
14use std::time::Duration;
15
16fn forwarding_not_configured() -> ErrorObject<'static> {
17 ErrorObject::owned(
18 INTERNAL_ERROR_CODE,
19 "arbtrace calls forwarding not configured",
20 None::<()>,
21 )
22}
23
24fn block_unsupported_by_classic(block_num: i64, genesis: u64) -> ErrorObject<'static> {
25 ErrorObject::owned(
26 INTERNAL_ERROR_CODE,
27 format!("block number {block_num} is not supported by classic node (> genesis {genesis})"),
28 None::<()>,
29 )
30}
31
32fn http_error(e: impl std::fmt::Display) -> ErrorObject<'static> {
33 ErrorObject::owned(
34 INTERNAL_ERROR_CODE,
35 format!("arbtrace forwarding failed: {e}"),
36 None::<()>,
37 )
38}
39
40#[derive(Debug, Clone, Default)]
41pub struct ArbTraceConfig {
42 pub fallback_client_url: Option<String>,
44 pub fallback_client_timeout: Option<Duration>,
46 pub genesis_block_num: u64,
49}
50
51#[rpc(server, namespace = "arbtrace")]
52pub trait ArbTraceApi {
53 #[method(name = "call")]
54 async fn call(
55 &self,
56 call_args: Box<RawValue>,
57 trace_types: Box<RawValue>,
58 block_num_or_hash: Box<RawValue>,
59 ) -> RpcResult<JsonValue>;
60
61 #[method(name = "callMany")]
62 async fn call_many(
63 &self,
64 calls: Box<RawValue>,
65 block_num_or_hash: Box<RawValue>,
66 ) -> RpcResult<JsonValue>;
67
68 #[method(name = "replayBlockTransactions")]
69 async fn replay_block_transactions(
70 &self,
71 block_num_or_hash: Box<RawValue>,
72 trace_types: Box<RawValue>,
73 ) -> RpcResult<JsonValue>;
74
75 #[method(name = "replayTransaction")]
76 async fn replay_transaction(
77 &self,
78 tx_hash: Box<RawValue>,
79 trace_types: Box<RawValue>,
80 ) -> RpcResult<JsonValue>;
81
82 #[method(name = "transaction")]
83 async fn transaction(&self, tx_hash: Box<RawValue>) -> RpcResult<JsonValue>;
84
85 #[method(name = "get")]
86 async fn get(&self, tx_hash: Box<RawValue>, path: Box<RawValue>) -> RpcResult<JsonValue>;
87
88 #[method(name = "block")]
89 async fn block(&self, block_num_or_hash: Box<RawValue>) -> RpcResult<JsonValue>;
90
91 #[method(name = "filter")]
92 async fn filter(&self, filter: Box<RawValue>) -> RpcResult<JsonValue>;
93}
94
95pub struct ArbTraceHandler {
97 config: Arc<ArbTraceConfig>,
98 client: Mutex<Option<Arc<HttpClient>>>,
99}
100
101impl std::fmt::Debug for ArbTraceHandler {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.debug_struct("ArbTraceHandler")
104 .field("config", &self.config)
105 .finish_non_exhaustive()
106 }
107}
108
109impl Clone for ArbTraceHandler {
110 fn clone(&self) -> Self {
111 Self {
112 config: self.config.clone(),
113 client: Mutex::new(self.client.lock().clone()),
114 }
115 }
116}
117
118impl ArbTraceHandler {
119 pub fn new(config: ArbTraceConfig) -> Self {
120 Self {
121 config: Arc::new(config),
122 client: Mutex::new(None),
123 }
124 }
125
126 fn get_client(&self) -> Result<Arc<HttpClient>, ErrorObject<'static>> {
127 if let Some(c) = self.client.lock().as_ref() {
128 return Ok(c.clone());
129 }
130 let url = self
131 .config
132 .fallback_client_url
133 .as_ref()
134 .ok_or_else(forwarding_not_configured)?;
135 let mut builder = HttpClientBuilder::default();
136 if let Some(t) = self.config.fallback_client_timeout {
137 builder = builder.request_timeout(t);
138 }
139 let client = builder.build(url).map_err(http_error)?;
140 let arc = Arc::new(client);
141 *self.client.lock() = Some(arc.clone());
142 Ok(arc)
143 }
144
145 fn check_block_supported_by_classic(
146 &self,
147 block_num_or_hash: &RawValue,
148 ) -> Result<(), ErrorObject<'static>> {
149 let parsed: JsonValue = json::from_str(block_num_or_hash.get()).unwrap_or(JsonValue::Null);
150 if let Some(s) = parsed.as_str() {
151 if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
152 if let Ok(n) = i64::from_str_radix(hex, 16) {
153 if n < 0 || (n as u64) > self.config.genesis_block_num {
154 return Err(block_unsupported_by_classic(
155 n,
156 self.config.genesis_block_num,
157 ));
158 }
159 }
160 }
161 }
162 Ok(())
163 }
164
165 async fn forward(&self, method: &str, params: Vec<Box<RawValue>>) -> RpcResult<JsonValue> {
166 let client = self.get_client()?;
167 let mut array_params = jsonrpsee::core::params::ArrayParams::new();
168 for raw in params {
169 let v: JsonValue = serde_json::from_str(raw.get()).unwrap_or(JsonValue::Null);
170 array_params.insert(v).map_err(http_error)?;
171 }
172 let resp: JsonValue = client
173 .request(method, array_params)
174 .await
175 .map_err(http_error)?;
176 Ok(resp)
177 }
178}
179
180#[async_trait::async_trait]
181impl ArbTraceApiServer for ArbTraceHandler {
182 async fn call(
183 &self,
184 call_args: Box<RawValue>,
185 trace_types: Box<RawValue>,
186 block_num_or_hash: Box<RawValue>,
187 ) -> RpcResult<JsonValue> {
188 self.check_block_supported_by_classic(&block_num_or_hash)?;
189 self.forward(
190 "arbtrace_call",
191 vec![call_args, trace_types, block_num_or_hash],
192 )
193 .await
194 }
195
196 async fn call_many(
197 &self,
198 calls: Box<RawValue>,
199 block_num_or_hash: Box<RawValue>,
200 ) -> RpcResult<JsonValue> {
201 self.check_block_supported_by_classic(&block_num_or_hash)?;
202 self.forward("arbtrace_callMany", vec![calls, block_num_or_hash])
203 .await
204 }
205
206 async fn replay_block_transactions(
207 &self,
208 block_num_or_hash: Box<RawValue>,
209 trace_types: Box<RawValue>,
210 ) -> RpcResult<JsonValue> {
211 self.check_block_supported_by_classic(&block_num_or_hash)?;
212 self.forward(
213 "arbtrace_replayBlockTransactions",
214 vec![block_num_or_hash, trace_types],
215 )
216 .await
217 }
218
219 async fn replay_transaction(
220 &self,
221 tx_hash: Box<RawValue>,
222 trace_types: Box<RawValue>,
223 ) -> RpcResult<JsonValue> {
224 self.forward("arbtrace_replayTransaction", vec![tx_hash, trace_types])
225 .await
226 }
227
228 async fn transaction(&self, tx_hash: Box<RawValue>) -> RpcResult<JsonValue> {
229 self.forward("arbtrace_transaction", vec![tx_hash]).await
230 }
231
232 async fn get(&self, tx_hash: Box<RawValue>, path: Box<RawValue>) -> RpcResult<JsonValue> {
233 self.forward("arbtrace_get", vec![tx_hash, path]).await
234 }
235
236 async fn block(&self, block_num_or_hash: Box<RawValue>) -> RpcResult<JsonValue> {
237 self.check_block_supported_by_classic(&block_num_or_hash)?;
238 self.forward("arbtrace_block", vec![block_num_or_hash])
239 .await
240 }
241
242 async fn filter(&self, filter: Box<RawValue>) -> RpcResult<JsonValue> {
243 self.forward("arbtrace_filter", vec![filter]).await
244 }
245}