diff --git a/tower-batch/tests/ed25519.rs b/tower-batch/tests/ed25519.rs index 7c835a5f1..a5fb52888 100644 --- a/tower-batch/tests/ed25519.rs +++ b/tower-batch/tests/ed25519.rs @@ -123,33 +123,35 @@ where } #[tokio::test] -async fn batch_flushes_on_max_items() { +async fn batch_flushes_on_max_items() -> Result<(), Report> { use tokio::time::timeout; zebra_test::init(); // Use a very long max_latency and a short timeout to check that // flushing is happening based on hitting max_items. let verifier = Batch::new(Ed25519Verifier::new(), 10, Duration::from_secs(1000)); - assert!( - timeout(Duration::from_secs(1), sign_and_verify(verifier, 100, None)) - .await - .is_ok() - ); + timeout(Duration::from_secs(1), sign_and_verify(verifier, 100, None)) + .await + .map_err(|e| eyre!(e))? + .map_err(|e| eyre!(e))?; + + Ok(()) } #[tokio::test] -async fn batch_flushes_on_max_latency() { +async fn batch_flushes_on_max_latency() -> Result<(), Report> { use tokio::time::timeout; zebra_test::init(); // Use a very high max_items and a short timeout to check that // flushing is happening based on hitting max_latency. let verifier = Batch::new(Ed25519Verifier::new(), 100, Duration::from_millis(500)); - assert!( - timeout(Duration::from_secs(1), sign_and_verify(verifier, 10, None)) - .await - .is_ok() - ); + timeout(Duration::from_secs(1), sign_and_verify(verifier, 10, None)) + .await + .map_err(|e| eyre!(e))? + .map_err(|e| eyre!(e))?; + + Ok(()) } #[tokio::test] diff --git a/zebra-state/src/lib.rs b/zebra-state/src/lib.rs index 6b1e8081c..16b34ada5 100644 --- a/zebra-state/src/lib.rs +++ b/zebra-state/src/lib.rs @@ -69,6 +69,13 @@ pub struct Config { /// The maximum number of bytes to use caching data in memory. pub memory_cache_bytes: u64, + + /// Whether to use an ephemeral database. + /// + /// Ephemeral databases are stored in memory on Linux, and in a temporary directory on other OSes. + /// + /// Set to `false` by default. If this is set to `true`, [`cache_dir`] is ignored. + pub ephemeral: bool, } impl Config { @@ -79,12 +86,16 @@ impl Config { Network::Mainnet => "mainnet", Network::Testnet => "testnet", }; - let path = self.cache_dir.join(net_dir).join("state"); - sled::Config::default() - .path(path) + let config = sled::Config::default() .cache_capacity(self.memory_cache_bytes) - .mode(sled::Mode::LowSpace) + .mode(sled::Mode::LowSpace); + if self.ephemeral { + config.temporary(self.ephemeral) + } else { + let path = self.cache_dir.join(net_dir).join("state"); + config.path(path) + } } } @@ -96,6 +107,7 @@ impl Default for Config { Self { cache_dir, memory_cache_bytes: 512 * 1024 * 1024, + ephemeral: false, } } } diff --git a/zebrad/tests/acceptance.rs b/zebrad/tests/acceptance.rs index 2f4e840f4..7445c5753 100644 --- a/zebrad/tests/acceptance.rs +++ b/zebrad/tests/acceptance.rs @@ -11,15 +11,25 @@ use tempdir::TempDir; use zebra_test::prelude::*; use zebrad::config::ZebradConfig; -pub fn tempdir(create_config: bool) -> Result<(PathBuf, impl Drop)> { +#[derive(PartialEq)] +enum ConfigMode { + NoConfig, + Ephemeral, + Persistent, +} + +fn tempdir(config_mode: ConfigMode) -> Result<(PathBuf, impl Drop)> { let dir = TempDir::new("zebrad_tests")?; - if create_config { - let cache_dir = dir.path().join("state"); - fs::create_dir(&cache_dir)?; - + if config_mode != ConfigMode::NoConfig { let mut config = ZebradConfig::default(); - config.state.cache_dir = cache_dir; + if config_mode == ConfigMode::Ephemeral { + config.state.ephemeral = true; + } else { + let cache_dir = dir.path().join("state"); + fs::create_dir(&cache_dir)?; + config.state.cache_dir = cache_dir; + } config.state.memory_cache_bytes = 256000000; config.network.listen_addr = "127.0.0.1:0".parse()?; @@ -44,7 +54,7 @@ pub fn get_child(args: &[&str], tempdir: &PathBuf) -> Result { #[test] fn generate_no_args() -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(true)?; + let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?; let child = get_child(&["generate"], &tempdir)?; let output = child.wait_with_output()?; @@ -59,7 +69,7 @@ fn generate_no_args() -> Result<()> { #[test] fn generate_args() -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(false)?; + let (tempdir, _guard) = tempdir(ConfigMode::NoConfig)?; // unexpected free argument `argument` let child = get_child(&["generate", "argument"], &tempdir)?; @@ -101,7 +111,7 @@ fn generate_args() -> Result<()> { #[test] fn help_no_args() -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(true)?; + let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?; let child = get_child(&["help"], &tempdir)?; let output = child.wait_with_output()?; @@ -119,7 +129,7 @@ fn help_no_args() -> Result<()> { #[test] fn help_args() -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(true)?; + let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?; // The subcommand "argument" wasn't recognized. let child = get_child(&["help", "argument"], &tempdir)?; @@ -137,7 +147,7 @@ fn help_args() -> Result<()> { #[test] fn revhex_args() -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(true)?; + let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?; // Valid let child = get_child(&["revhex", "33eeff55"], &tempdir)?; @@ -152,7 +162,7 @@ fn revhex_args() -> Result<()> { #[test] fn seed_no_args() -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(true)?; + let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?; let mut child = get_child(&["-v", "seed"], &tempdir)?; @@ -174,7 +184,7 @@ fn seed_no_args() -> Result<()> { #[test] fn seed_args() -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(true)?; + let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?; // unexpected free argument `argument` let child = get_child(&["seed", "argument"], &tempdir)?; @@ -197,7 +207,8 @@ fn seed_args() -> Result<()> { #[test] fn start_no_args() -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(true)?; + // start caches state, so run one of the start tests with persistent state + let (tempdir, _guard) = tempdir(ConfigMode::Persistent)?; let mut child = get_child(&["-v", "start"], &tempdir)?; @@ -221,7 +232,7 @@ fn start_no_args() -> Result<()> { #[test] fn start_args() -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(true)?; + let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?; // Any free argument is valid let mut child = get_child(&["start", "argument"], &tempdir)?; @@ -243,10 +254,90 @@ fn start_args() -> Result<()> { Ok(()) } +#[test] +fn persistent_mode() -> Result<()> { + zebra_test::init(); + let (tempdir, _guard) = tempdir(ConfigMode::Persistent)?; + + let mut child = get_child(&["-v", "start"], &tempdir)?; + + // Run the program and kill it at 1 second + std::thread::sleep(Duration::from_secs(1)); + child.kill()?; + let output = child.wait_with_output()?; + + // Make sure the command was killed + assert!(output.was_killed()); + + // Check that we have persistent sled database + let cache_dir = tempdir.join("state"); + assert!(cache_dir.read_dir()?.count() > 0); + + Ok(()) +} + +#[test] +fn ephemeral_mode() -> Result<()> { + zebra_test::init(); + let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?; + + // Any free argument is valid + let mut child = get_child(&["start", "argument"], &tempdir)?; + // Run the program and kill it at 1 second + std::thread::sleep(Duration::from_secs(1)); + child.kill()?; + let output = child.wait_with_output()?; + + // Make sure the command was killed + assert!(output.was_killed()); + + let cache_dir = tempdir.join("state"); + assert!(!cache_dir.exists()); + + Ok(()) +} + +#[test] +fn misconfigured_ephemeral_mode() -> Result<()> { + zebra_test::init(); + + let dir = TempDir::new("zebrad_tests")?; + let cache_dir = dir.path().join("state"); + + // Write a configuration that has both cache_dir and ephemeral options set + let mut config = ZebradConfig::default(); + config.state.ephemeral = true; + // Although cache_dir has a default value, we set it a new temp directory + // to test that it is empty later. + fs::create_dir(&cache_dir)?; + config.state.cache_dir = cache_dir.clone(); + config.state.memory_cache_bytes = 256000000; + config.network.listen_addr = "127.0.0.1:0".parse()?; + fs::File::create(dir.path().join("zebrad.toml"))? + .write_all(toml::to_string(&config)?.as_bytes())?; + + let tempdir = dir.path().to_path_buf(); + + // Any free argument is valid + let mut child = get_child(&["start", "argument"], &tempdir)?; + // Run the program and kill it at 1 second + std::thread::sleep(Duration::from_secs(1)); + child.kill()?; + let output = child.wait_with_output()?; + + // Make sure the command was killed + assert!(output.was_killed()); + + // Check that ephemeral takes precedence over cache_dir + assert_eq!(cache_dir.read_dir()?.count(), 0); + + Ok(()) +} + #[test] fn app_no_args() -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(true)?; + let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?; let child = get_child(&[], &tempdir)?; let output = child.wait_with_output()?; @@ -260,7 +351,7 @@ fn app_no_args() -> Result<()> { #[test] fn version_no_args() -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(true)?; + let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?; let child = get_child(&["version"], &tempdir)?; let output = child.wait_with_output()?; @@ -274,7 +365,7 @@ fn version_no_args() -> Result<()> { #[test] fn version_args() -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(true)?; + let (tempdir, _guard) = tempdir(ConfigMode::Ephemeral)?; // unexpected free argument `argument` let child = get_child(&["version", "argument"], &tempdir)?; @@ -302,7 +393,7 @@ fn valid_generated_config_test() -> Result<()> { fn valid_generated_config(command: &str, expected_output: &str) -> Result<()> { zebra_test::init(); - let (tempdir, _guard) = tempdir(false)?; + let (tempdir, _guard) = tempdir(ConfigMode::NoConfig)?; // Add a config file name to tempdir path let mut generated_config_path = tempdir.clone();