From 0b19431176d5c7dd6163088f9586572992ae9bf4 Mon Sep 17 00:00:00 2001 From: Jonathan Easterman Date: Tue, 31 May 2022 20:24:49 +0100 Subject: [PATCH 1/4] Separate cache layout into "rpc" and "etherscan" subdirectories for block data and etherscan data, respectively. before: ~/.foundry/cache//block, ~/.foundry/cache//etherscan after: ~/.foundry/cache/rpc/, ~/.foundry/cache/etherscan/ --- cli/src/cmd/cast/run.rs | 2 +- cli/src/cmd/forge/script/mod.rs | 2 +- cli/src/cmd/forge/test.rs | 2 +- config/src/lib.rs | 26 ++++++++++++++++++-------- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/cli/src/cmd/cast/run.rs b/cli/src/cmd/cast/run.rs index a99e82d4963b..5a58eed61a71 100644 --- a/cli/src/cmd/cast/run.rs +++ b/cli/src/cmd/cast/run.rs @@ -138,7 +138,7 @@ impl RunArgs { let etherscan_identifier = EtherscanIdentifier::new( evm_opts.get_remote_chain_id(), config.etherscan_api_key, - Config::foundry_etherscan_cache_dir(evm_opts.get_chain_id()), + Config::foundry_etherscan_chain_cache_dir(evm_opts.get_chain_id()), Duration::from_secs(24 * 60 * 60), ); diff --git a/cli/src/cmd/forge/script/mod.rs b/cli/src/cmd/forge/script/mod.rs index 8c2fb8cd8f86..6f5809525fe9 100644 --- a/cli/src/cmd/forge/script/mod.rs +++ b/cli/src/cmd/forge/script/mod.rs @@ -124,7 +124,7 @@ impl ScriptArgs { let etherscan_identifier = EtherscanIdentifier::new( script_config.evm_opts.get_remote_chain_id(), script_config.config.etherscan_api_key.clone(), - Config::foundry_etherscan_cache_dir(script_config.evm_opts.get_chain_id()), + Config::foundry_etherscan_chain_cache_dir(script_config.evm_opts.get_chain_id()), Duration::from_secs(24 * 60 * 60), ); diff --git a/cli/src/cmd/forge/test.rs b/cli/src/cmd/forge/test.rs index 9d19749ef218..cf3417d68c1b 100644 --- a/cli/src/cmd/forge/test.rs +++ b/cli/src/cmd/forge/test.rs @@ -620,7 +620,7 @@ fn test( let etherscan_identifier = EtherscanIdentifier::new( remote_chain_id, config.etherscan_api_key, - remote_chain_id.and_then(Config::foundry_etherscan_cache_dir), + remote_chain_id.and_then(Config::foundry_etherscan_chain_cache_dir), cache_ttl, ); diff --git a/config/src/lib.rs b/config/src/lib.rs index 9892738367fd..4ab52c57ba9e 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -907,24 +907,34 @@ impl Config { Self::foundry_dir().map(|p| p.join("cache")) } - /// Returns the path to foundry chain's cache dir `~/.foundry/cache/` + /// Returns the path to foundry rpc cache dir `~/.foundry/cache/rpc` + pub fn foundry_rpc_cache_dir() -> Option { + Some(Self::foundry_cache_dir()?.join("rpc")) + } + /// Returns the path to foundry chain's cache dir `~/.foundry/cache/rpc/` pub fn foundry_chain_cache_dir(chain_id: impl Into) -> Option { - Some(Self::foundry_cache_dir()?.join(chain_id.into().to_string())) + Some(Self::foundry_rpc_cache_dir()?.join(chain_id.into().to_string())) + } + + /// Returns the path to foundry's etherscan cache dir `~/.foundry/cache/etherscan` + pub fn foundry_etherscan_cache_dir() -> Option { + Some(Self::foundry_cache_dir()?.join("etherscan")) } - /// Returns the path to foundry's etherscan cache dir `~/.foundry/cache//etherscan` - pub fn foundry_etherscan_cache_dir(chain_id: impl Into) -> Option { - Some(Self::foundry_chain_cache_dir(chain_id)?.join("etherscan")) + /// Returns the path to foundry's etherscan cache dir for `chain_id` + /// `~/.foundry/cache/etherscan/` + pub fn foundry_etherscan_chain_cache_dir(chain_id: impl Into) -> Option { + Some(Self::foundry_etherscan_cache_dir()?.join(chain_id.into().to_string())) } /// Returns the path to the cache dir of the `block` on the `chain` - /// `~/.foundry/cache// + /// `~/.foundry/cache/rpc// pub fn foundry_block_cache_dir(chain_id: impl Into, block: u64) -> Option { Some(Self::foundry_chain_cache_dir(chain_id)?.join(format!("{block}"))) } /// Returns the path to the cache file of the `block` on the `chain` - /// `~/.foundry/cache///storage.json` + /// `~/.foundry/cache/rpc///storage.json` pub fn foundry_block_cache_file(chain_id: impl Into, block: u64) -> Option { Some(Self::foundry_block_cache_dir(chain_id, block)?.join("storage.json")) } @@ -1008,7 +1018,7 @@ impl Config { /// List the data in the foundry cache pub fn list_foundry_cache() -> eyre::Result { - if let Some(cache_dir) = Config::foundry_cache_dir() { + if let Some(cache_dir) = Config::foundry_rpc_cache_dir() { let mut cache = Cache { chains: vec![] }; if !cache_dir.exists() { return Ok(cache) From ef4f1aa42e2c5ddf126596eb3ec2a2da4a901574 Mon Sep 17 00:00:00 2001 From: Jonathan Easterman Date: Tue, 31 May 2022 20:31:51 +0100 Subject: [PATCH 2/4] Add option --etherscan to "forge cache clean" which results in only deleting the etherscan cache data. e.g. forge cache clean --etherscan # deletes ~/.foundry/cache/etherscan forge cache clean mainnet --etherscan # deletes ~/.foundry/cache/etherscan/mainnet * test: Add tests for cache clean --etherscan --- cli/src/cmd/forge/cache.rs | 21 ++++++++-- cli/tests/it/cmd.rs | 79 ++++++++++++++++++++++++++++++++++++-- config/src/lib.rs | 24 ++++++++++++ 3 files changed, 117 insertions(+), 7 deletions(-) diff --git a/cli/src/cmd/forge/cache.rs b/cli/src/cmd/forge/cache.rs index 258dcd90df90..573e847bc3cd 100644 --- a/cli/src/cmd/forge/cache.rs +++ b/cli/src/cmd/forge/cache.rs @@ -57,6 +57,9 @@ pub struct CleanArgs { value_name = "BLOCKS" )] blocks: Vec, + + #[clap(long)] + etherscan: bool, } #[derive(Debug, Parser)] @@ -84,12 +87,18 @@ impl Cmd for CleanArgs { type Output = (); fn run(self) -> Result { - let CleanArgs { chains, blocks } = self; + let CleanArgs { chains, blocks, etherscan } = self; for chain_or_all in chains { match chain_or_all { - ChainOrAll::Chain(chain) => clean_chain_cache(chain, blocks.to_vec())?, - ChainOrAll::All => Config::clean_foundry_cache()?, + ChainOrAll::Chain(chain) => clean_chain_cache(chain, blocks.to_vec(), etherscan)?, + ChainOrAll::All => { + if etherscan { + Config::clean_foundry_etherscan_cache()?; + } else { + Config::clean_foundry_cache()? + } + } } } @@ -114,9 +123,13 @@ impl Cmd for LsArgs { } } -fn clean_chain_cache(chain: Chain, blocks: Vec) -> Result<()> { +fn clean_chain_cache(chain: Chain, blocks: Vec, etherscan: bool) -> Result<()> { if let Ok(foundry_chain) = FoundryConfigChain::try_from(chain) { if blocks.is_empty() { + Config::clean_foundry_etherscan_chain_cache(foundry_chain)?; + if etherscan { + return Ok(()) + } Config::clean_foundry_chain_cache(foundry_chain)?; } else { for block in blocks { diff --git a/cli/tests/it/cmd.rs b/cli/tests/it/cmd.rs index f61e931be12b..de9b48c845be 100644 --- a/cli/tests/it/cmd.rs +++ b/cli/tests/it/cmd.rs @@ -49,17 +49,58 @@ forgetest_ignore!(can_cache_clean, |_: TestProject, mut cmd: TestCommand| { assert!(!path.exists()); }); +// checks that `cache clean --etherscan` can be invoked and only cleans the foundry etherscan cache +// this test is not isolated and modifies ~ so it is ignored +forgetest_ignore!(can_cache_clean_etherscan, |_: TestProject, mut cmd: TestCommand| { + let cache_dir = Config::foundry_cache_dir().unwrap(); + let etherscan_cache_dir = Config::foundry_etherscan_cache_dir().unwrap(); + let path = cache_dir.as_path(); + let etherscan_path = etherscan_cache_dir.as_path(); + fs::create_dir_all(etherscan_path).unwrap(); + cmd.args(["cache", "clean", "--etherscan"]); + cmd.assert_empty_stdout(); + + assert!(path.exists()); + assert!(!etherscan_path.exists()); + + Config::clean_foundry_cache().unwrap(); +}); + +// checks that `cache clean all --etherscan` can be invoked and only cleans the foundry etherscan +// cache. This test is not isolated and modifies ~ so it is ignored +forgetest_ignore!(can_cache_clean_all_etherscan, |_: TestProject, mut cmd: TestCommand| { + let rpc_cache_dir = Config::foundry_rpc_cache_dir().unwrap(); + let etherscan_cache_dir = Config::foundry_etherscan_cache_dir().unwrap(); + let rpc_path = rpc_cache_dir.as_path(); + let etherscan_path = etherscan_cache_dir.as_path(); + fs::create_dir_all(rpc_path).unwrap(); + fs::create_dir_all(etherscan_path).unwrap(); + cmd.args(["cache", "clean", "all", "--etherscan"]); + cmd.assert_empty_stdout(); + + assert!(rpc_path.exists()); + assert!(!etherscan_path.exists()); + + Config::clean_foundry_cache().unwrap(); +}); + // checks that `cache clean ` can be invoked and cleans the chain cache // this test is not isolated and modifies ~ so it is ignored forgetest_ignore!(can_cache_clean_chain, |_: TestProject, mut cmd: TestCommand| { - let cache_dir = - Config::foundry_chain_cache_dir(Chain::Named(ethers::prelude::Chain::Mainnet)).unwrap(); + let chain = Chain::Named(ethers::prelude::Chain::Mainnet); + let cache_dir = Config::foundry_chain_cache_dir(chain).unwrap(); + let etherscan_cache_dir = Config::foundry_etherscan_chain_cache_dir(chain).unwrap(); let path = cache_dir.as_path(); + let etherscan_path = etherscan_cache_dir.as_path(); fs::create_dir_all(path).unwrap(); + fs::create_dir_all(etherscan_path).unwrap(); cmd.args(["cache", "clean", "mainnet"]); cmd.assert_empty_stdout(); assert!(!path.exists()); + assert!(!etherscan_path.exists()); + + Config::clean_foundry_cache().unwrap(); }); // checks that `cache clean --blocks 100,101` can be invoked and cleans the chain block @@ -67,18 +108,50 @@ forgetest_ignore!(can_cache_clean_chain, |_: TestProject, mut cmd: TestCommand| forgetest_ignore!(can_cache_clean_blocks, |_: TestProject, mut cmd: TestCommand| { let chain = Chain::Named(ethers::prelude::Chain::Mainnet); let block1 = 100; - let block2 = 102; + let block2 = 101; + let block3 = 102; let block1_cache_dir = Config::foundry_block_cache_dir(chain, block1).unwrap(); let block2_cache_dir = Config::foundry_block_cache_dir(chain, block2).unwrap(); + let block3_cache_dir = Config::foundry_block_cache_dir(chain, block3).unwrap(); + let etherscan_cache_dir = Config::foundry_etherscan_chain_cache_dir(chain).unwrap(); let block1_path = block1_cache_dir.as_path(); let block2_path = block2_cache_dir.as_path(); + let block3_path = block3_cache_dir.as_path(); + let etherscan_path = etherscan_cache_dir.as_path(); fs::create_dir_all(block1_path).unwrap(); fs::create_dir_all(block2_path).unwrap(); + fs::create_dir_all(block3_path).unwrap(); + fs::create_dir_all(etherscan_path).unwrap(); cmd.args(["cache", "clean", "mainnet", "--blocks", "100,101"]); cmd.assert_empty_stdout(); assert!(!block1_path.exists()); assert!(!block2_path.exists()); + assert!(block3_path.exists()); + assert!(etherscan_path.exists()); + + Config::clean_foundry_cache().unwrap(); +}); + +// checks that `cache clean --etherscan` can be invoked and cleans the etherscan chain cache +// this test is not isolated and modifies ~ so it is ignored +forgetest_ignore!(can_cache_clean_chain_etherscan, |_: TestProject, mut cmd: TestCommand| { + let cache_dir = + Config::foundry_chain_cache_dir(Chain::Named(ethers::prelude::Chain::Mainnet)).unwrap(); + let etherscan_cache_dir = + Config::foundry_etherscan_chain_cache_dir(Chain::Named(ethers::prelude::Chain::Mainnet)) + .unwrap(); + let path = cache_dir.as_path(); + let etherscan_path = etherscan_cache_dir.as_path(); + fs::create_dir_all(path).unwrap(); + fs::create_dir_all(etherscan_path).unwrap(); + cmd.args(["cache", "clean", "mainnet", "--etherscan"]); + cmd.assert_empty_stdout(); + + assert!(path.exists()); + assert!(!etherscan_path.exists()); + + Config::clean_foundry_cache().unwrap(); }); // checks that init works diff --git a/config/src/lib.rs b/config/src/lib.rs index 4ab52c57ba9e..5e592a6dff6a 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -1016,6 +1016,30 @@ impl Config { Ok(()) } + /// Clears the foundry etherscan cache + pub fn clean_foundry_etherscan_cache() -> eyre::Result<()> { + if let Some(cache_dir) = Config::foundry_etherscan_cache_dir() { + let path = cache_dir.as_path(); + let _ = fs::remove_dir_all(path); + } else { + eyre::bail!("failed to get foundry_etherscan_cache_dir"); + } + + Ok(()) + } + + /// Clears the foundry etherscan cache for `chain` + pub fn clean_foundry_etherscan_chain_cache(chain: Chain) -> eyre::Result<()> { + if let Some(cache_dir) = Config::foundry_etherscan_chain_cache_dir(chain) { + let path = cache_dir.as_path(); + let _ = fs::remove_dir_all(path); + } else { + eyre::bail!("failed to get foundry_etherscan_cache_dir for chain: {}", chain); + } + + Ok(()) + } + /// List the data in the foundry cache pub fn list_foundry_cache() -> eyre::Result { if let Some(cache_dir) = Config::foundry_rpc_cache_dir() { From 7b7e3e3b740e6dc2992c2b65dd5dd3a738193d5e Mon Sep 17 00:00:00 2001 From: Jonathan Easterman Date: Wed, 1 Jun 2022 17:34:28 +0100 Subject: [PATCH 3/4] Display the etherscan cache with 'forge cache ls' test: add integration test for 'forge cache ls' fix: calculate etherscan cache size based on all files in each subdirectory test: Unit test cache.to_string() used in 'forge cache ls' --- cli/tests/it/cmd.rs | 33 +++++++++++++++++++ config/src/cache.rs | 66 +++++++++++++++++++++++++++++++++++-- config/src/lib.rs | 80 +++++++++++++++++++++++++++++++++++++++------ 3 files changed, 167 insertions(+), 12 deletions(-) diff --git a/cli/tests/it/cmd.rs b/cli/tests/it/cmd.rs index de9b48c845be..b15363cfa550 100644 --- a/cli/tests/it/cmd.rs +++ b/cli/tests/it/cmd.rs @@ -37,6 +37,39 @@ forgetest!(can_clean_non_existing, |prj: TestProject, mut cmd: TestCommand| { prj.assert_cleaned(); }); +// checks that `cache ls` can be invoked and displays the foundry cache +forgetest_ignore!(can_cache_ls, |_: TestProject, mut cmd: TestCommand| { + let chain = Chain::Named(ethers::prelude::Chain::Mainnet); + let block1 = 100; + let block2 = 101; + + let block1_cache_dir = Config::foundry_block_cache_dir(chain, block1).unwrap(); + let block1_file = Config::foundry_block_cache_file(chain, block1).unwrap(); + let block2_cache_dir = Config::foundry_block_cache_dir(chain, block2).unwrap(); + let block2_file = Config::foundry_block_cache_file(chain, block2).unwrap(); + let etherscan_cache_dir = Config::foundry_etherscan_chain_cache_dir(chain).unwrap(); + fs::create_dir_all(block1_cache_dir).unwrap(); + fs::write(block1_file, "{}").unwrap(); + fs::create_dir_all(block2_cache_dir).unwrap(); + fs::write(block2_file, "{}").unwrap(); + fs::create_dir_all(etherscan_cache_dir).unwrap(); + + cmd.args(["cache", "ls"]); + let output_string = String::from_utf8_lossy(&cmd.output().stdout).to_string(); + let output_lines = output_string.split("\n").collect::>(); + println!("{output_string}"); + + assert_eq!(output_lines.len(), 6); + assert!(output_lines[0].starts_with("-️ mainnet (")); + assert!(output_lines[1].starts_with("\t-️ Block Explorer (")); + assert_eq!(output_lines[2], ""); + assert!(output_lines[3].starts_with("\t-️ Block 100 (")); + assert!(output_lines[4].starts_with("\t-️ Block 101 (")); + assert_eq!(output_lines[5], ""); + + Config::clean_foundry_cache().unwrap(); +}); + // checks that `cache clean` can be invoked and cleans the foundry cache // this test is not isolated and modifies ~ so it is ignored forgetest_ignore!(can_cache_clean, |_: TestProject, mut cmd: TestCommand| { diff --git a/config/src/cache.rs b/config/src/cache.rs index 9dcf2c137d34..f8337ab14f18 100644 --- a/config/src/cache.rs +++ b/config/src/cache.rs @@ -193,7 +193,9 @@ pub struct Cache { impl fmt::Display for Cache { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { for chain in &self.chains { - match NumberPrefix::decimal(chain.blocks.iter().map(|x| x.1).sum::() as f32) { + match NumberPrefix::decimal( + chain.block_explorer as f32 + chain.blocks.iter().map(|x| x.1).sum::() as f32, + ) { NumberPrefix::Standalone(size) => { writeln!(f, "-️ {} ({:.1} B)", chain.name, size)?; } @@ -201,6 +203,14 @@ impl fmt::Display for Cache { writeln!(f, "-️ {} ({:.1} {}B)", chain.name, size, prefix)?; } } + match NumberPrefix::decimal(chain.block_explorer as f32) { + NumberPrefix::Standalone(size) => { + writeln!(f, "\t-️ Block Explorer ({:.1} B)\n", size)?; + } + NumberPrefix::Prefixed(prefix, size) => { + writeln!(f, "\t-️ Block Explorer ({:.1} {}B)\n", size, prefix)?; + } + } for block in &chain.blocks { match NumberPrefix::decimal(block.1 as f32) { NumberPrefix::Standalone(size) => { @@ -216,7 +226,7 @@ impl fmt::Display for Cache { } } -/// A chain folder in the foundry cache +/// A representation of data for a given chain in the foundry cache #[derive(Debug)] pub struct ChainCache { /// The name of the chain @@ -224,10 +234,15 @@ pub struct ChainCache { /// A tuple containing block number and the block directory size in bytes pub blocks: Vec<(String, u64)>, + + /// The size of the block explorer directory in bytes + pub block_explorer: u64, } #[cfg(test)] mod tests { + use pretty_assertions::assert_str_eq; + use super::*; #[test] @@ -260,4 +275,51 @@ mod tests { } ) } + + #[test] + fn cache_to_string() { + let cache = Cache { + chains: vec![ + ChainCache { + name: "mainnet".to_string(), + blocks: vec![("1".to_string(), 1), ("2".to_string(), 2)], + block_explorer: 500, + }, + ChainCache { + name: "ropsten".to_string(), + blocks: vec![("1".to_string(), 1), ("2".to_string(), 2)], + block_explorer: 4567, + }, + ChainCache { + name: "rinkeby".to_string(), + blocks: vec![("1".to_string(), 1032), ("2".to_string(), 2000000)], + block_explorer: 4230000, + }, + ChainCache { + name: "mumbai".to_string(), + blocks: vec![("1".to_string(), 1), ("2".to_string(), 2)], + block_explorer: 0, + }, + ], + }; + + let expected = "\ + -️ mainnet (503.0 B)\n\t\ + -️ Block Explorer (500.0 B)\n\n\t\ + -️ Block 1 (1.0 B)\n\t\ + -️ Block 2 (2.0 B)\n\ + -️ ropsten (4.6 kB)\n\t\ + -️ Block Explorer (4.6 kB)\n\n\t\ + -️ Block 1 (1.0 B)\n\t\ + -️ Block 2 (2.0 B)\n\ + -️ rinkeby (6.2 MB)\n\t\ + -️ Block Explorer (4.2 MB)\n\n\t\ + -️ Block 1 (1.0 kB)\n\t\ + -️ Block 2 (2.0 MB)\n\ + -️ mumbai (3.0 B)\n\t\ + -️ Block Explorer (0.0 B)\n\n\t\ + -️ Block 1 (1.0 B)\n\t\ + -️ Block 2 (2.0 B)\n"; + assert_str_eq!(format!("{cache}"), expected); + } } diff --git a/config/src/lib.rs b/config/src/lib.rs index 5e592a6dff6a..3ba589771509 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -1049,10 +1049,10 @@ impl Config { } if let Ok(entries) = cache_dir.as_path().read_dir() { for entry in entries.flatten().filter(|x| x.path().is_dir()) { - cache.chains.push(ChainCache { - name: entry.file_name().to_string_lossy().into_owned(), - blocks: Self::get_cached_blocks(&entry.path())?, - }) + match Chain::from_str(&entry.file_name().to_string_lossy()) { + Ok(chain) => cache.chains.push(Self::list_foundry_chain_cache(chain)?), + Err(_) => continue, + } } Ok(cache) } else { @@ -1065,9 +1065,18 @@ impl Config { /// List the cached data for `chain` pub fn list_foundry_chain_cache(chain: Chain) -> eyre::Result { + let block_explorer_data_size = match Config::foundry_etherscan_chain_cache_dir(chain) { + Some(cache_dir) => Self::get_cached_block_explorer_data(&cache_dir)?, + None => eyre::bail!("failed to access foundry_etherscan_chain_cache_dir"), + }; + if let Some(cache_dir) = Config::foundry_chain_cache_dir(chain) { let blocks = Self::get_cached_blocks(&cache_dir)?; - Ok(ChainCache { name: chain.to_string(), blocks }) + Ok(ChainCache { + name: chain.to_string(), + blocks, + block_explorer: block_explorer_data_size, + }) } else { eyre::bail!("failed to get foundry_chain_cache_dir"); } @@ -1079,11 +1088,7 @@ impl Config { if !chain_path.exists() { return Ok(blocks) } - for block in chain_path - .read_dir()? - .flatten() - .filter(|x| x.file_type().unwrap().is_dir() && !x.file_name().eq("etherscan")) - { + for block in chain_path.read_dir()?.flatten().filter(|x| x.file_type().unwrap().is_dir()) { let filepath = block.path().join("storage.json"); blocks.push(( block.file_name().to_string_lossy().into_owned(), @@ -1092,6 +1097,26 @@ impl Config { } Ok(blocks) } + + //The path provided to this function should point to the etherscan cache for a chain + fn get_cached_block_explorer_data(chain_path: &Path) -> eyre::Result { + if !chain_path.exists() { + return Ok(0) + } + + fn dir_size_recursive(mut dir: fs::ReadDir) -> eyre::Result { + dir.try_fold(0, |acc, file| { + let file = file?; + let size = match file.metadata()? { + data if data.is_dir() => dir_size_recursive(fs::read_dir(file.path())?)?, + data => data.len(), + }; + Ok(acc + size) + }) + } + + dir_size_recursive(fs::read_dir(chain_path)?) + } } impl From for Figment { @@ -2944,4 +2969,39 @@ mod tests { chain_dir.close()?; Ok(()) } + + #[test] + fn list_etherscan_cache() -> eyre::Result<()> { + fn fake_etherscan_cache(chain_path: &Path, address: &str, size_bytes: usize) { + let metadata_path = chain_path.join("sources"); + let abi_path = chain_path.join("abi"); + match fs::create_dir(metadata_path.as_path()) { + _ => {} + } + match fs::create_dir(abi_path.as_path()) { + _ => {} + } + let metadata_file_path = metadata_path.join(address); + let mut metadata_file = File::create(metadata_file_path).unwrap(); + writeln!(metadata_file, "{}", vec![' '; size_bytes / 2 - 1].iter().collect::()) + .unwrap(); + + let abi_file_path = abi_path.join(address); + let mut abi_file = File::create(abi_file_path).unwrap(); + writeln!(abi_file, "{}", vec![' '; size_bytes / 2 - 1].iter().collect::()) + .unwrap(); + } + + let chain_dir = tempdir()?; + + fake_etherscan_cache(chain_dir.path(), "1", 100); + fake_etherscan_cache(chain_dir.path(), "2", 500); + + let result = Config::get_cached_block_explorer_data(chain_dir.path())?; + + assert_eq!(result, 600); + + chain_dir.close()?; + Ok(()) + } } From 89a6ec636953721f6974f9bef31474ad7a6bb4d6 Mon Sep 17 00:00:00 2001 From: Jonathan Easterman Date: Fri, 3 Jun 2022 14:56:24 +0100 Subject: [PATCH 4/4] --etherscan and --blocks options are mutually exclusive when running 'forge cache clean' --- cli/src/cmd/forge/cache.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cli/src/cmd/forge/cache.rs b/cli/src/cmd/forge/cache.rs index 573e847bc3cd..2d2869fe2b36 100644 --- a/cli/src/cmd/forge/cache.rs +++ b/cli/src/cmd/forge/cache.rs @@ -37,6 +37,7 @@ impl FromStr for ChainOrAll { } #[derive(Debug, Parser)] +#[clap(group = clap::ArgGroup::new("etherscan-blocks").multiple(false))] pub struct CleanArgs { // TODO refactor to dedup shared logic with ClapChain in opts/mod #[clap( @@ -54,11 +55,12 @@ pub struct CleanArgs { multiple_values(true), use_value_delimiter(true), require_value_delimiter(true), - value_name = "BLOCKS" + value_name = "BLOCKS", + group = "etherscan-blocks" )] blocks: Vec, - #[clap(long)] + #[clap(long, group = "etherscan-blocks")] etherscan: bool, }