feature: Separate Mainnet and Testnet state

This commit is contained in:
teor 2020-07-27 09:57:18 +10:00 committed by Deirdre Connolly
parent be054906ef
commit 11090dbf91
4 changed files with 78 additions and 28 deletions

View File

@ -14,6 +14,7 @@
#![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_state")] #![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_state")]
#![warn(missing_docs)] #![warn(missing_docs)]
#![allow(clippy::try_err)] #![allow(clippy::try_err)]
use color_eyre::eyre::{eyre, Report}; use color_eyre::eyre::{eyre, Report};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::PathBuf;
@ -23,34 +24,44 @@ use tower::{Service, ServiceExt};
use zebra_chain::{ use zebra_chain::{
block::{Block, BlockHeaderHash}, block::{Block, BlockHeaderHash},
types::BlockHeight, types::BlockHeight,
Network,
Network::*,
}; };
pub mod in_memory; pub mod in_memory;
pub mod on_disk; pub mod on_disk;
/// Configuration for networking code. /// Configuration for the state service.
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Config { 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<PathBuf>, pub cache_dir: Option<PathBuf>,
} }
impl Config { impl Config {
/// Generate the appropriate `sled::Config` based on the provided /// Generate the appropriate `sled::Config` for `network`, based on the
/// `zebra_state::Config`. /// provided `zebra_state::Config`.
/// ///
/// # Details /// # Details
/// ///
/// This function should panic if the user of `zebra-state` doesn't configure /// This function should panic if the user of `zebra-state` doesn't configure
/// a directory to store the state. /// 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 let path = self
.cache_dir .cache_dir
.as_ref() .as_ref()
.unwrap_or_else(|| { .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"); .join("state");
sled::Config::default().path(path) sled::Config::default().path(path)
@ -185,12 +196,45 @@ where
mod tests { mod tests {
use super::*; 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] #[test]
#[should_panic] #[should_panic]
fn test_no_path() { 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 bad_config = Config { cache_dir: None };
let _unreachable = bad_config.sled_config(); let _unreachable = bad_config.sled_config(Mainnet);
} }
} }

View File

@ -14,6 +14,7 @@ use zebra_chain::serialization::{ZcashDeserialize, ZcashSerialize};
use zebra_chain::{ use zebra_chain::{
block::{Block, BlockHeaderHash}, block::{Block, BlockHeaderHash},
types::BlockHeight, types::BlockHeight,
Network,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -22,8 +23,8 @@ struct SledState {
} }
impl SledState { impl SledState {
pub(crate) fn new(config: &Config) -> Self { pub(crate) fn new(config: &Config, network: Network) -> Self {
let config = config.sled_config(); let config = config.sled_config(network);
Self { Self {
storage: config.open().unwrap(), 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<Request> for SledState { impl Service<Request> for SledState {
type Response = Response; type Response = Response;
type Error = Error; type Error = Error;
@ -232,9 +226,12 @@ impl From<BlockHeight> 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( pub fn init(
config: Config, config: Config,
network: Network,
) -> impl Service< ) -> impl Service<
Request, Request,
Response = Response, Response = Response,
@ -243,7 +240,7 @@ pub fn init(
> + Send > + Send
+ Clone + Clone
+ 'static { + 'static {
Buffer::new(SledState::new(&config), 1) Buffer::new(SledState::new(&config, network), 1)
} }
type Error = Box<dyn error::Error + Send + Sync + 'static>; type Error = Box<dyn error::Error + Send + Sync + 'static>;

View File

@ -2,7 +2,8 @@ use color_eyre::eyre::Report;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::sync::Arc; use std::sync::Arc;
use tempdir::TempDir; 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_test::transcript::Transcript;
use zebra_state::*; use zebra_state::*;
@ -49,12 +50,17 @@ static GET_TIP_TRANSCRIPT: Lazy<Vec<(Request, Response)>> = Lazy::new(|| {
}); });
#[tokio::test] #[tokio::test]
async fn check_transcripts_test() -> Result<(), Report> { async fn check_transcripts_mainnet() -> Result<(), Report> {
check_transcripts().await check_transcripts(Mainnet).await
}
#[tokio::test]
async fn check_transcripts_testnet() -> Result<(), Report> {
check_transcripts(Testnet).await
} }
#[spandoc::spandoc] #[spandoc::spandoc]
async fn check_transcripts() -> Result<(), Report> { async fn check_transcripts(network: Network) -> Result<(), Report> {
zebra_test::init(); zebra_test::init();
for transcript_data in &[&ADD_BLOCK_TRANSCRIPT, &GET_TIP_TRANSCRIPT] { for transcript_data in &[&ADD_BLOCK_TRANSCRIPT, &GET_TIP_TRANSCRIPT] {
@ -64,9 +70,12 @@ async fn check_transcripts() -> Result<(), Report> {
transcript.check(service).await?; transcript.check(service).await?;
let storage_guard = TempDir::new("")?; let storage_guard = TempDir::new("")?;
let service = on_disk::init(Config { let service = on_disk::init(
cache_dir: Some(storage_guard.path().to_owned()), Config {
}); cache_dir: Some(storage_guard.path().to_owned()),
},
network,
);
let transcript = Transcript::from(transcript_data.iter().cloned()); let transcript = Transcript::from(transcript_data.iter().cloned());
/// SPANDOC: check the on disk service against the transcript /// SPANDOC: check the on disk service against the transcript
transcript.check(service).await?; transcript.check(service).await?;

View File

@ -41,7 +41,7 @@ impl StartCmd {
info!(?self, "starting to connect to the network"); info!(?self, "starting to connect to the network");
let config = app_config(); 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; let verifier = zebra_consensus::chain::init(config.network.network, state.clone()).await;
// The service that our node uses to respond to requests by peers // The service that our node uses to respond to requests by peers