Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to clear etherscan cache #1807

Merged
merged 4 commits into from Jun 4, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/src/cmd/cast/run.rs
Expand Up @@ -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),
);

Expand Down
25 changes: 20 additions & 5 deletions cli/src/cmd/forge/cache.rs
Expand Up @@ -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(
Expand All @@ -54,9 +55,13 @@ 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<u64>,

#[clap(long, group = "etherscan-blocks")]
etherscan: bool,
}

#[derive(Debug, Parser)]
Expand Down Expand Up @@ -84,12 +89,18 @@ impl Cmd for CleanArgs {
type Output = ();

fn run(self) -> Result<Self::Output> {
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()?
}
}
}
}

Expand All @@ -114,9 +125,13 @@ impl Cmd for LsArgs {
}
}

fn clean_chain_cache(chain: Chain, blocks: Vec<u64>) -> Result<()> {
fn clean_chain_cache(chain: Chain, blocks: Vec<u64>, 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 {
Expand Down
2 changes: 1 addition & 1 deletion cli/src/cmd/forge/script/mod.rs
Expand Up @@ -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),
);

Expand Down
2 changes: 1 addition & 1 deletion cli/src/cmd/forge/test.rs
Expand Up @@ -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,
);

Expand Down
112 changes: 109 additions & 3 deletions cli/tests/it/cmd.rs
Expand Up @@ -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::<Vec<_>>();
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| {
Expand All @@ -49,36 +82,109 @@ 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 <chain>` 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 <chain> --blocks 100,101` can be invoked and cleans the chain block
// caches this test is not isolated and modifies ~ so it is ignored
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 <chain> --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
Expand Down
66 changes: 64 additions & 2 deletions config/src/cache.rs
Expand Up @@ -193,14 +193,24 @@ 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::<u64>() as f32) {
match NumberPrefix::decimal(
chain.block_explorer as f32 + chain.blocks.iter().map(|x| x.1).sum::<u64>() as f32,
) {
NumberPrefix::Standalone(size) => {
writeln!(f, "-️ {} ({:.1} B)", chain.name, size)?;
}
NumberPrefix::Prefixed(prefix, size) => {
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)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note the additional newline - it was cleaner to my eye to add an additional line to separate the etherscan data and the block data

}
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) => {
Expand All @@ -216,18 +226,23 @@ 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
pub name: String,

/// 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,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this could be block_explorer_size. Could also be named etherscan_size to keep it consistent with the flag --etherscan. It's always difficult to determine whether to keep names specific to ethereum or generalize for all EVM chains.

}

#[cfg(test)]
mod tests {
use pretty_assertions::assert_str_eq;

use super::*;

#[test]
Expand Down Expand Up @@ -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);
}
}