Skip to content

Commit 9a73821

Browse files
committed
refactor(evm): clean up corpus.rs
- Unified CorpusEntry constructors with internal _new method taking Option<PathBuf> to eliminate code duplication between new and from_tx_seq. - Extracted common helper methods in WorkerCorpus: - can_replay_tx: static helper to check if tx can be replayed, replacing 2 duplicate closures. - file_extension: returns appropriate file extension based on gzip config, replacing 3 duplicated patterns. - Simplified directory initialization using functional map pattern instead of nested if-let, and removed unnecessary is_dir checks before create_dir_all calls. - Improved parse_corpus_filename by replacing rsplitn + collect with rsplit_once for better efficiency and added proper error message for invalid format. - Removed unused uuid field from CorpusDirEntry struct, fixing clippy dead_code warning. - Reordered early returns in export method to check master worker first for more logical flow.
1 parent bca649d commit 9a73821

File tree

1 file changed

+50
-73
lines changed

1 file changed

+50
-73
lines changed

crates/evm/evm/src/executors/corpus.rs

Lines changed: 50 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,31 @@ pub struct WorkerCorpus {
208208
}
209209

210210
impl WorkerCorpus {
211+
/// Helper to check if a tx can be replayed.
212+
fn can_replay_tx(
213+
tx: &BasicTxDetails,
214+
fuzzed_function: Option<&Function>,
215+
fuzzed_contracts: Option<&FuzzRunIdentifiedContracts>,
216+
) -> bool {
217+
fuzzed_contracts.is_some_and(|contracts| contracts.targets.lock().can_replay(tx))
218+
|| fuzzed_function.is_some_and(|function| {
219+
tx.call_details
220+
.calldata
221+
.get(..4)
222+
.is_some_and(|selector| function.selector() == selector)
223+
})
224+
}
225+
226+
/// Returns the file extension based on gzip config.
227+
fn file_extension(&self) -> &str {
228+
if self.config.corpus_gzip { ".json.gz" } else { JSON_EXTENSION }
229+
}
230+
211231
pub fn new(
212232
id: u32,
213233
config: FuzzCorpusConfig,
214234
tx_generator: BoxedStrategy<BasicTxDetails>,
215-
// Only required by master worker (id = 0) to replay existing corpus
235+
// Only required by master worker (id = 0) to replay existing corpus.
216236
executor: Option<&Executor>,
217237
fuzzed_function: Option<&Function>,
218238
fuzzed_contracts: Option<&FuzzRunIdentifiedContracts>,
@@ -227,24 +247,17 @@ impl WorkerCorpus {
227247
]
228248
.boxed();
229249

230-
let worker_dir = if let Some(corpus_dir) = &config.corpus_dir {
231-
// Create the necessary directories for the worker
250+
let worker_dir = config.corpus_dir.as_ref().map(|corpus_dir| {
232251
let worker_dir = corpus_dir.join(format!("{WORKER}{id}"));
233-
let worker_corpus = &worker_dir.join(CORPUS_DIR);
234-
let sync_dir = &worker_dir.join(SYNC_DIR);
235-
236-
if !worker_corpus.is_dir() {
237-
foundry_common::fs::create_dir_all(worker_corpus)?;
238-
}
252+
let worker_corpus = worker_dir.join(CORPUS_DIR);
253+
let sync_dir = worker_dir.join(SYNC_DIR);
239254

240-
if !sync_dir.is_dir() {
241-
foundry_common::fs::create_dir_all(sync_dir)?;
242-
}
255+
// Create the necessary directories for the worker.
256+
let _ = foundry_common::fs::create_dir_all(&worker_corpus);
257+
let _ = foundry_common::fs::create_dir_all(&sync_dir);
243258

244-
Some(worker_dir)
245-
} else {
246-
None
247-
};
259+
worker_dir
260+
});
248261

249262
let mut in_memory_corpus = vec![];
250263
let mut history_map = vec![0u8; COVERAGE_MAP_SIZE];
@@ -256,16 +269,6 @@ impl WorkerCorpus {
256269
{
257270
// Master worker loads the initial corpus, if it exists.
258271
// Then, [distribute]s it to workers.
259-
let can_replay_tx = |tx: &BasicTxDetails| -> bool {
260-
fuzzed_contracts.is_some_and(|contracts| contracts.targets.lock().can_replay(tx))
261-
|| fuzzed_function.is_some_and(|function| {
262-
tx.call_details
263-
.calldata
264-
.get(..4)
265-
.is_some_and(|selector| function.selector() == selector)
266-
})
267-
};
268-
269272
let executor = executor.expect("Executor required for master worker");
270273
'corpus_replay: for entry in read_corpus_dir(corpus_dir) {
271274
if entry.is_metadata() {
@@ -278,7 +281,7 @@ impl WorkerCorpus {
278281
// Warm up history map from loaded sequences.
279282
let mut executor = executor.clone();
280283
for tx in &tx_seq {
281-
if can_replay_tx(tx) {
284+
if Self::can_replay_tx(tx, fuzzed_function, fuzzed_contracts) {
282285
let mut call_result = execute_tx(&mut executor, tx)?;
283286
let (new_coverage, is_edge) =
284287
call_result.merge_edge_coverage(&mut history_map);
@@ -373,19 +376,14 @@ impl WorkerCorpus {
373376
let corpus = CorpusEntry::from_tx_seq(inputs);
374377
let corpus_uuid = corpus.uuid;
375378
let timestamp = corpus.timestamp;
379+
let ext = self.file_extension();
380+
let file_path = worker_corpus.join(format!("{corpus_uuid}-{timestamp}{ext}"));
381+
376382
// Persist to disk if corpus dir is configured.
377383
let write_result = if self.config.corpus_gzip {
378-
foundry_common::fs::write_json_gzip_file(
379-
worker_corpus
380-
.join(format!("{corpus_uuid}-{timestamp}{JSON_EXTENSION}.gz"))
381-
.as_path(),
382-
&corpus.tx_seq,
383-
)
384+
foundry_common::fs::write_json_gzip_file(&file_path, &corpus.tx_seq)
384385
} else {
385-
foundry_common::fs::write_json_file(
386-
worker_corpus.join(format!("{corpus_uuid}-{timestamp}{JSON_EXTENSION}")).as_path(),
387-
&corpus.tx_seq,
388-
)
386+
foundry_common::fs::write_json_file(&file_path, &corpus.tx_seq)
389387
};
390388

391389
if let Err(err) = write_result {
@@ -707,18 +705,17 @@ impl WorkerCorpus {
707705
/// Exports the new corpus entries to the master worker's (id = 0) sync dir.
708706
#[tracing::instrument(skip_all, fields(worker_id = self.id))]
709707
fn export(&self) -> eyre::Result<()> {
710-
// Early return if no new entries or corpus dir not configured
711-
if self.new_entry_indices.is_empty() || self.worker_dir.is_none() {
708+
// Master doesn't export (it only receives from others).
709+
if self.id == 0 {
712710
return Ok(());
713711
}
714712

715-
let worker_dir = self.worker_dir.as_ref().unwrap();
716-
717-
// Master doesn't export (it only receives from others)
718-
if self.id == 0 {
713+
// Early return if no new entries or corpus dir not configured.
714+
if self.new_entry_indices.is_empty() || self.worker_dir.is_none() {
719715
return Ok(());
720716
}
721717

718+
let worker_dir = self.worker_dir.as_ref().unwrap();
722719
let Some(master_sync_dir) = self
723720
.config
724721
.corpus_dir
@@ -731,14 +728,9 @@ impl WorkerCorpus {
731728
let mut exported = 0;
732729
let corpus_dir = worker_dir.join(CORPUS_DIR);
733730

731+
let ext = self.file_extension();
734732
for &index in &self.new_entry_indices {
735733
if let Some(entry) = self.in_memory_corpus.get(index) {
736-
// TODO(dani): with_added_extension
737-
let ext = if self.config.corpus_gzip {
738-
&format!("{JSON_EXTENSION}.gz")
739-
} else {
740-
JSON_EXTENSION
741-
};
742734
let file_name = format!("{}-{}{ext}", entry.uuid, entry.timestamp);
743735
let file_path = corpus_dir.join(&file_name);
744736
let sync_path = master_sync_dir.join(&file_name);
@@ -798,17 +790,6 @@ impl WorkerCorpus {
798790
return Ok(());
799791
};
800792

801-
// Helper to check if tx can be replayed
802-
let can_replay_tx = |tx: &BasicTxDetails| -> bool {
803-
fuzzed_contracts.is_some_and(|contracts| contracts.targets.lock().can_replay(tx))
804-
|| fuzzed_function.is_some_and(|function| {
805-
tx.call_details
806-
.calldata
807-
.get(..4)
808-
.is_some_and(|selector| function.selector() == selector)
809-
})
810-
};
811-
812793
let sync_dir = worker_dir.join(SYNC_DIR);
813794
let corpus_dir = worker_dir.join(CORPUS_DIR);
814795

@@ -817,7 +798,7 @@ impl WorkerCorpus {
817798
if !tx_seq.is_empty() {
818799
let mut new_coverage_on_sync = false;
819800
for tx in &tx_seq {
820-
if can_replay_tx(tx) {
801+
if Self::can_replay_tx(tx, fuzzed_function, fuzzed_contracts) {
821802
let mut call_result = execute_tx(&mut executor, tx)?;
822803

823804
// Check if this provides new coverage
@@ -845,12 +826,7 @@ impl WorkerCorpus {
845826

846827
if new_coverage_on_sync {
847828
let corpus_entry = CorpusEntry::from_tx_seq(&tx_seq);
848-
let ext = self
849-
.config
850-
.corpus_gzip
851-
.then_some(format!("{JSON_EXTENSION}.gz"))
852-
.unwrap_or(JSON_EXTENSION.to_string());
853-
829+
let ext = self.file_extension();
854830
let file_name =
855831
format!("{}-{}{ext}", corpus_entry.uuid, corpus_entry.timestamp);
856832

@@ -1032,8 +1008,8 @@ fn read_corpus_dir(path: &Path) -> impl Iterator<Item = CorpusDirEntry> {
10321008
return None;
10331009
};
10341010

1035-
if let Ok((uuid, timestamp)) = parse_corpus_filename(name) {
1036-
Some(CorpusDirEntry { path, uuid, timestamp })
1011+
if let Ok((_, timestamp)) = parse_corpus_filename(name) {
1012+
Some(CorpusDirEntry { path, timestamp })
10371013
} else {
10381014
trace!(target: "corpus", ?path, "failed to parse corpus filename");
10391015
None
@@ -1050,7 +1026,6 @@ fn read_corpus_dir(path: &Path) -> impl Iterator<Item = CorpusDirEntry> {
10501026

10511027
struct CorpusDirEntry {
10521028
path: PathBuf,
1053-
uuid: Uuid,
10541029
timestamp: u64,
10551030
}
10561031

@@ -1077,9 +1052,11 @@ impl CorpusDirEntry {
10771052
fn parse_corpus_filename(name: &str) -> eyre::Result<(Uuid, u64)> {
10781053
let name = name.trim_end_matches(".gz").trim_end_matches(".json").trim_end_matches(".metadata");
10791054

1080-
let parts = name.rsplitn(2, "-").collect::<Vec<_>>();
1081-
let uuid = Uuid::parse_str(parts[0])?;
1082-
let timestamp = parts[1].parse()?;
1055+
let (uuid_str, timestamp_str) =
1056+
name.rsplit_once('-').ok_or_else(|| eyre!("invalid corpus filename format: {name}"))?;
1057+
1058+
let uuid = Uuid::parse_str(uuid_str)?;
1059+
let timestamp = timestamp_str.parse()?;
10831060

10841061
Ok((uuid, timestamp))
10851062
}

0 commit comments

Comments
 (0)