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