Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/cli/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,21 @@ pub fn execute_and_print(payload: &serde_json::Value, opts: TraceOpts) -> Result
palette,
);

let warnings: Vec<String> = [
selector_resolver.take_warning(),
contract_resolver.take_warning(),
]
.into_iter()
.flatten()
.collect();

if !warnings.is_empty() {
eprintln!();
for w in warnings {
eprintln!("{}", palette.yellow(&format!("warning: {w}")));
}
}

Ok(())
}

Expand Down
32 changes: 22 additions & 10 deletions src/utils/contract_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub struct ContractResolver {
base_url: String,
chain_id: Option<String>,
enabled: bool,
warning: Option<String>,
}

impl ContractResolver {
Expand All @@ -25,9 +26,14 @@ impl ContractResolver {
base_url,
chain_id,
enabled,
warning: None,
}
}

pub fn take_warning(&mut self) -> Option<String> {
self.warning.take()
}

/// Resolve a contract address to its name via Sourcify's v2 contract lookup.
pub fn resolve(&mut self, address: &str) -> Option<String> {
if !self.enabled {
Expand All @@ -52,19 +58,25 @@ impl ContractResolver {
self.base_url
);

let Some(resp) = self
.client
.get(&url)
.send()
.ok()
.and_then(|r| r.error_for_status().ok())
else {
self.disk_cache.insert_miss(cache_key);
return None;
let resp = match self.client.get(&url).send() {
Ok(r) if r.status().is_success() => r,
Ok(r) if r.status() == reqwest::StatusCode::NOT_FOUND => {
self.disk_cache.insert_miss(cache_key);
return None;
}
Ok(_) | Err(_) => {
if self.warning.is_none() {
self.warning = Some(format!(
"sourcify contract lookup failed for {address}, results may be incomplete"
));
}
self.disk_cache.insert_transient_miss(cache_key);
return None;
}
};

let Some(body) = resp.json::<serde_json::Value>().ok() else {
self.disk_cache.insert_miss(cache_key);
self.disk_cache.insert_transient_miss(cache_key);
return None;
};

Expand Down
22 changes: 21 additions & 1 deletion src/utils/disk_cache.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::{
collections::HashMap,
collections::{HashMap, HashSet},
fs,
path::{Path, PathBuf},
};
Expand Down Expand Up @@ -35,6 +35,7 @@ pub enum CacheError {
/// used for serialization, keeping backward compatibility with existing cache files.
pub struct DiskCache {
entries: HashMap<String, String>,
transient_misses: HashSet<String>,
kind: String,
dirty: bool,
disabled: bool,
Expand All @@ -57,6 +58,7 @@ impl DiskCache {
let disabled = std::env::var("TORGE_DISABLE_CACHE").is_ok();
let empty = Self {
entries: HashMap::new(),
transient_misses: HashSet::new(),
kind: kind.to_owned(),
dirty: false,
disabled,
Expand All @@ -74,6 +76,7 @@ impl DiskCache {

Self {
entries,
transient_misses: HashSet::new(),
kind: kind.to_owned(),
dirty: false,
disabled: false,
Expand Down Expand Up @@ -124,6 +127,9 @@ impl DiskCache {

/// Look up a key, distinguishing between a resolved hit, a cached miss, and not-in-cache.
pub fn lookup(&self, key: &str) -> CacheLookup<'_> {
if self.transient_misses.contains(key) {
return CacheLookup::Miss;
}
match self.entries.get(key) {
Some(v) if v == CACHE_MISS_MARKER => CacheLookup::Miss,
Some(v) => CacheLookup::Hit(v),
Expand All @@ -140,6 +146,11 @@ impl DiskCache {
self.insert(key, CACHE_MISS_MARKER.to_owned());
}

/// Record a miss that should not be persisted to disk (e.g. transient network errors).
pub fn insert_transient_miss(&mut self, key: String) {
self.transient_misses.insert(key);
}

/// Remove all unknown (unresolved) entries from the persisted cache.
/// Returns `(kept, removed)` counts.
pub fn remove_unknown(kind: &str) -> Result<(usize, usize), CacheError> {
Expand Down Expand Up @@ -179,6 +190,7 @@ mod tests {
fn empty_cache() -> DiskCache {
DiskCache {
entries: HashMap::new(),
transient_misses: HashSet::new(),
kind: "test".to_owned(),
dirty: false,
disabled: true,
Expand Down Expand Up @@ -206,4 +218,12 @@ mod tests {
cache.insert_miss("0xdeadbeef".to_owned());
assert!(matches!(cache.lookup("0xdeadbeef"), CacheLookup::Miss));
}

#[test]
fn test_transient_miss() {
let mut cache = empty_cache();
cache.insert_transient_miss("0xdeadbeef".to_owned());
assert!(matches!(cache.lookup("0xdeadbeef"), CacheLookup::Miss));
assert!(!cache.entries.contains_key("0xdeadbeef"));
}
}
28 changes: 18 additions & 10 deletions src/utils/selector_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct SelectorResolver {
disk_cache: DiskCache,
base_url: String,
enabled: bool,
warning: Option<String>,
}

impl SelectorResolver {
Expand All @@ -27,6 +28,7 @@ impl SelectorResolver {
disk_cache: DiskCache::load("selectors"),
base_url,
enabled,
warning: None,
}
}

Expand All @@ -35,6 +37,10 @@ impl SelectorResolver {
self.enabled
}

pub fn take_warning(&mut self) -> Option<String> {
self.warning.take()
}

/// Resolve a 4-byte function selector to a text signature.
pub fn resolve(&mut self, selector: &str, calldata: Option<&str>) -> Option<String> {
self.lookup(selector, "function", 10, calldata)
Expand Down Expand Up @@ -68,19 +74,21 @@ impl SelectorResolver {
self.base_url
);

let Some(resp) = self
.client
.get(&url)
.send()
.ok()
.and_then(|r| r.error_for_status().ok())
else {
self.disk_cache.insert_miss(key.to_owned());
return None;
let resp = match self.client.get(&url).send() {
Ok(r) if r.status().is_success() => r,
Ok(_) | Err(_) => {
if self.warning.is_none() {
self.warning = Some(format!(
"sourcify selector lookup failed for {key}, results may be incomplete"
));
}
self.disk_cache.insert_transient_miss(key.to_owned());
return None;
}
};

let Some(body) = resp.json::<serde_json::Value>().ok() else {
self.disk_cache.insert_miss(key.to_owned());
self.disk_cache.insert_transient_miss(key.to_owned());
return None;
};

Expand Down