diff --git a/zebra-state/src/lib.rs b/zebra-state/src/lib.rs index 9708c849..39e8c6f9 100644 --- a/zebra-state/src/lib.rs +++ b/zebra-state/src/lib.rs @@ -14,6 +14,7 @@ #![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_state")] #![warn(missing_docs)] #![allow(clippy::try_err)] + use color_eyre::eyre::{eyre, Report}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -23,34 +24,44 @@ use tower::{Service, ServiceExt}; use zebra_chain::{ block::{Block, BlockHeaderHash}, types::BlockHeight, + Network, + Network::*, }; pub mod in_memory; pub mod on_disk; -/// Configuration for networking code. +/// Configuration for the state service. #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(deny_unknown_fields)] pub struct Config { - /// The root directory for the state storage + /// The root directory for storing cached data. + /// + /// Each network has a separate state, which is stored in "mainnet/state" + /// and "testnet/state" subdirectories. pub cache_dir: Option, } impl Config { - /// Generate the appropriate `sled::Config` based on the provided - /// `zebra_state::Config`. + /// Generate the appropriate `sled::Config` for `network`, based on the + /// provided `zebra_state::Config`. /// /// # Details /// /// This function should panic if the user of `zebra-state` doesn't configure /// a directory to store the state. - pub(crate) fn sled_config(&self) -> sled::Config { + pub(crate) fn sled_config(&self, network: Network) -> sled::Config { + let net_dir = match network { + Mainnet => "mainnet", + Testnet => "testnet", + }; let path = self .cache_dir .as_ref() .unwrap_or_else(|| { - todo!("create a nice user facing error explaining how to set the cache directory") + todo!("create a nice user facing error explaining how to set the cache directory in zebrad.toml:\n[state]\ncache_dir = '/path/to/cache-or-tmp'") }) + .join(net_dir) .join("state"); sled::Config::default().path(path) @@ -185,12 +196,45 @@ where mod tests { use super::*; + use std::ffi::OsStr; + + #[test] + fn test_path_mainnet() { + test_path(Mainnet); + } + + #[test] + fn test_path_testnet() { + test_path(Testnet); + } + + /// Check the sled path for `network`. + fn test_path(network: Network) { + zebra_test::init(); + + let config = Config::default(); + // we can't do many useful tests on this value, because it depends on the + // local environment and OS. + let sled_config = config.sled_config(network); + let mut path = sled_config.get_path(); + assert_eq!(path.file_name(), Some(OsStr::new("state"))); + assert!(path.pop()); + match network { + Mainnet => assert_eq!(path.file_name(), Some(OsStr::new("mainnet"))), + Testnet => assert_eq!(path.file_name(), Some(OsStr::new("testnet"))), + } + } + + /// Check what happens when the config is invalid. #[test] #[should_panic] fn test_no_path() { - zebra_test::init(); - + // We don't call `zebra_test::init` here, to silence the expected panic log + // TODO: + // - implement test log levels in #760 + // - call `zebra_test::init` + // - disable all log output from this test let bad_config = Config { cache_dir: None }; - let _unreachable = bad_config.sled_config(); + let _unreachable = bad_config.sled_config(Mainnet); } } diff --git a/zebra-state/src/on_disk.rs b/zebra-state/src/on_disk.rs index 1192882e..a68291d8 100644 --- a/zebra-state/src/on_disk.rs +++ b/zebra-state/src/on_disk.rs @@ -14,6 +14,7 @@ use zebra_chain::serialization::{ZcashDeserialize, ZcashSerialize}; use zebra_chain::{ block::{Block, BlockHeaderHash}, types::BlockHeight, + Network, }; #[derive(Clone)] @@ -22,8 +23,8 @@ struct SledState { } impl SledState { - pub(crate) fn new(config: &Config) -> Self { - let config = config.sled_config(); + pub(crate) fn new(config: &Config, network: Network) -> Self { + let config = config.sled_config(network); Self { storage: config.open().unwrap(), @@ -94,13 +95,6 @@ impl SledState { } } -impl Default for SledState { - fn default() -> Self { - let config = crate::Config::default(); - Self::new(&config) - } -} - impl Service for SledState { type Response = Response; type Error = Error; @@ -232,9 +226,12 @@ impl From for BlockQuery { } } -/// Return's a type that implement's the `zebra_state::Service` using `sled` +/// Returns a type that implements the `zebra_state::Service` using `sled`. +/// +/// Each `network` has its own separate sled database. pub fn init( config: Config, + network: Network, ) -> impl Service< Request, Response = Response, @@ -243,7 +240,7 @@ pub fn init( > + Send + Clone + 'static { - Buffer::new(SledState::new(&config), 1) + Buffer::new(SledState::new(&config, network), 1) } type Error = Box; diff --git a/zebra-state/tests/basic.rs b/zebra-state/tests/basic.rs index b5fa735b..29344044 100644 --- a/zebra-state/tests/basic.rs +++ b/zebra-state/tests/basic.rs @@ -2,7 +2,8 @@ use color_eyre::eyre::Report; use once_cell::sync::Lazy; use std::sync::Arc; use tempdir::TempDir; -use zebra_chain::{block::Block, serialization::ZcashDeserialize}; + +use zebra_chain::{block::Block, serialization::ZcashDeserialize, Network, Network::*}; use zebra_test::transcript::Transcript; use zebra_state::*; @@ -49,12 +50,17 @@ static GET_TIP_TRANSCRIPT: Lazy> = Lazy::new(|| { }); #[tokio::test] -async fn check_transcripts_test() -> Result<(), Report> { - check_transcripts().await +async fn check_transcripts_mainnet() -> Result<(), Report> { + check_transcripts(Mainnet).await +} + +#[tokio::test] +async fn check_transcripts_testnet() -> Result<(), Report> { + check_transcripts(Testnet).await } #[spandoc::spandoc] -async fn check_transcripts() -> Result<(), Report> { +async fn check_transcripts(network: Network) -> Result<(), Report> { zebra_test::init(); for transcript_data in &[&ADD_BLOCK_TRANSCRIPT, &GET_TIP_TRANSCRIPT] { @@ -64,9 +70,12 @@ async fn check_transcripts() -> Result<(), Report> { transcript.check(service).await?; let storage_guard = TempDir::new("")?; - let service = on_disk::init(Config { - cache_dir: Some(storage_guard.path().to_owned()), - }); + let service = on_disk::init( + Config { + cache_dir: Some(storage_guard.path().to_owned()), + }, + network, + ); let transcript = Transcript::from(transcript_data.iter().cloned()); /// SPANDOC: check the on disk service against the transcript transcript.check(service).await?; diff --git a/zebrad/src/commands/start.rs b/zebrad/src/commands/start.rs index 15fe410e..d5940890 100644 --- a/zebrad/src/commands/start.rs +++ b/zebrad/src/commands/start.rs @@ -41,7 +41,7 @@ impl StartCmd { info!(?self, "starting to connect to the network"); let config = app_config(); - let state = zebra_state::on_disk::init(config.state.clone()); + let state = zebra_state::on_disk::init(config.state.clone(), config.network.network); let verifier = zebra_consensus::chain::init(config.network.network, state.clone()).await; // The service that our node uses to respond to requests by peers