anvil_zksync_common/
resolver.rs1use super::sh_warn;
3use once_cell::sync::Lazy;
4use serde::Deserialize;
5use std::{
6 collections::HashMap,
7 sync::atomic::{AtomicBool, AtomicUsize, Ordering},
8};
9
10static SELECTOR_DATABASE_URL: &str = "https://api.openchain.xyz/signature-database/v1/lookup";
11
12const MAX_TIMEDOUT_REQ: usize = 4usize;
14
15#[derive(Debug, Default)]
19pub struct SignEthClient {
20 spurious_connection: AtomicBool,
22 timedout_requests: AtomicUsize,
24}
25
26#[derive(Deserialize)]
27struct KnownAbi {
28 abi: String,
29 name: String,
30}
31
32static KNOWN_SIGNATURES: Lazy<HashMap<String, String>> = Lazy::new(|| {
33 let json_value = serde_json::from_slice(include_bytes!("data/abi_map.json")).unwrap();
34 let pairs: Vec<KnownAbi> = serde_json::from_value(json_value).unwrap();
35
36 pairs
37 .into_iter()
38 .map(|entry| (entry.abi, entry.name))
39 .collect()
40});
41
42impl SignEthClient {
43 pub fn new() -> Self {
45 Self::default()
46 }
47
48 async fn get(&self, url: &str) -> eyre::Result<String> {
50 let resp = reqwest::get(url).await.inspect_err(|e| {
51 self.on_reqwest_err(e);
52 })?;
53 let text = resp.text().await.inspect_err(|e| {
54 self.on_reqwest_err(e);
55 })?;
56 Ok(text)
57 }
58
59 fn on_reqwest_err(&self, err: &reqwest::Error) {
60 fn is_connectivity_err(err: &reqwest::Error) -> bool {
61 if err.is_timeout() || err.is_connect() {
62 return true;
63 }
64 if let Some(status) = err.status() {
66 let code = status.as_u16();
67 if (500..600).contains(&code) {
68 return true;
69 }
70 }
71 false
72 }
73
74 if is_connectivity_err(err) {
75 sh_warn!("spurious network detected for api.openchain.xyz");
76 let previous = self.timedout_requests.fetch_add(1, Ordering::Relaxed);
77 if previous >= MAX_TIMEDOUT_REQ {
78 self.set_spurious();
79 }
80 }
81 }
82
83 fn is_spurious(&self) -> bool {
85 self.spurious_connection.load(Ordering::Relaxed)
86 }
87
88 fn set_spurious(&self) {
90 self.spurious_connection.store(true, Ordering::Relaxed);
91 tracing::warn!(
92 "Connection to {SELECTOR_DATABASE_URL} is spurious, further requests will fail."
93 );
94 }
95
96 pub async fn decode_selector(
98 &self,
99 selector: &str,
100 selector_type: SelectorType,
101 ) -> eyre::Result<Option<String>> {
102 eyre::ensure!(!self.is_spurious(), "Spurious connection detected");
104
105 #[derive(Deserialize)]
106 struct Decoded {
107 name: String,
108 filtered: bool,
109 }
110
111 #[derive(Deserialize)]
112 struct ApiResult {
113 event: HashMap<String, Option<Vec<Decoded>>>,
114 function: HashMap<String, Option<Vec<Decoded>>>,
115 }
116
117 #[derive(Deserialize)]
118 struct ApiResponse {
119 ok: bool,
120 result: ApiResult,
121 }
122
123 let url = match selector_type {
126 SelectorType::Function | SelectorType::Error => {
127 format!("{SELECTOR_DATABASE_URL}?function={selector}&filter=true")
128 }
129 SelectorType::Event => format!("{SELECTOR_DATABASE_URL}?event={selector}&filter=true"),
130 };
131
132 let res = self.get(&url).await?;
133 let api_response = match serde_json::from_str::<ApiResponse>(&res) {
134 Ok(inner) => inner,
135 Err(err) => {
136 eyre::bail!("Could not decode response:\n {res}.\nError: {err}")
137 }
138 };
139
140 if !api_response.ok {
141 eyre::bail!("Failed to decode:\n {res}")
142 }
143
144 let decoded = match selector_type {
145 SelectorType::Function | SelectorType::Error => api_response.result.function,
146 SelectorType::Event => api_response.result.event,
147 };
148
149 let default_decoded = vec![Decoded {
151 name: selector.to_string(),
152 filtered: false,
153 }];
154
155 Ok(decoded
156 .get(selector)
157 .ok_or(eyre::eyre!("No signature found"))?
158 .as_ref()
159 .unwrap_or(&default_decoded)
160 .iter()
161 .filter(|d| !d.filtered)
162 .map(|d| d.name.clone())
163 .collect::<Vec<String>>()
164 .first()
165 .cloned())
166 }
167
168 pub async fn decode_selectors(
170 &self,
171 selector_type: SelectorType,
172 selectors: impl IntoIterator<Item = impl Into<String>>,
173 ) -> eyre::Result<Vec<Option<Vec<String>>>> {
174 let selectors: Vec<String> = selectors
175 .into_iter()
176 .map(Into::into)
177 .map(|s| s.to_lowercase())
178 .map(|s| {
179 if s.starts_with("0x") {
180 s
181 } else {
182 format!("0x{s}")
183 }
184 })
185 .collect();
186
187 if selectors.is_empty() {
188 return Ok(vec![]);
189 }
190
191 tracing::debug!(len = selectors.len(), "decoding selectors");
192 tracing::trace!(?selectors, "decoding selectors");
193
194 eyre::ensure!(!self.is_spurious(), "Spurious connection detected");
196
197 let expected_len = match selector_type {
198 SelectorType::Function | SelectorType::Error => 10, SelectorType::Event => 66, };
201 if let Some(s) = selectors.iter().find(|s| s.len() != expected_len) {
202 eyre::bail!(
203 "Invalid selector {s}: expected {expected_len} characters (including 0x prefix)."
204 )
205 }
206
207 #[derive(Deserialize)]
208 struct Decoded {
209 name: String,
210 }
211
212 #[derive(Deserialize)]
213 struct ApiResult {
214 event: HashMap<String, Option<Vec<Decoded>>>,
215 function: HashMap<String, Option<Vec<Decoded>>>,
216 }
217
218 #[derive(Deserialize)]
219 struct ApiResponse {
220 ok: bool,
221 result: ApiResult,
222 }
223
224 let url = format!(
225 "{SELECTOR_DATABASE_URL}?{ltype}={selectors_str}",
226 ltype = match selector_type {
227 SelectorType::Function | SelectorType::Error => "function",
228 SelectorType::Event => "event",
229 },
230 selectors_str = selectors.join(",")
231 );
232
233 let res = self.get(&url).await?;
234 let api_response = match serde_json::from_str::<ApiResponse>(&res) {
235 Ok(inner) => inner,
236 Err(err) => {
237 eyre::bail!("Could not decode response:\n {res}.\nError: {err}")
238 }
239 };
240
241 if !api_response.ok {
242 eyre::bail!("Failed to decode:\n {res}")
243 }
244
245 let decoded = match selector_type {
246 SelectorType::Function | SelectorType::Error => api_response.result.function,
247 SelectorType::Event => api_response.result.event,
248 };
249
250 Ok(selectors
251 .into_iter()
252 .map(|selector| match decoded.get(&selector) {
253 Some(Some(r)) => Some(r.iter().map(|d| d.name.clone()).collect()),
254 _ => None,
255 })
256 .collect())
257 }
258
259 pub async fn decode_function_selector(&self, selector: &str) -> eyre::Result<Option<String>> {
261 let prefixed_selector = format!("0x{}", selector.strip_prefix("0x").unwrap_or(selector));
262 if prefixed_selector.len() != 10 {
263 eyre::bail!("Invalid selector: expected 8 characters (excluding 0x prefix), got {} characters (including 0x prefix).", prefixed_selector.len())
264 }
265
266 if let Some(r) = KNOWN_SIGNATURES.get(&prefixed_selector) {
267 return Ok(Some(r.clone()));
268 }
269
270 self.decode_selector(&prefixed_selector[..10], SelectorType::Function)
271 .await
272 }
273}
274
275#[derive(Clone, Copy)]
276pub enum SelectorType {
277 Function,
278 Event,
279 Error,
280}