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")]
#![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<PathBuf>,
}
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);
}
}

View File

@ -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<Request> for SledState {
type Response = Response;
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(
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<dyn error::Error + Send + Sync + 'static>;

View File

@ -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<Vec<(Request, Response)>> = 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?;

View File

@ -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