state: create a new wrapper service
The new `StateService` type wraps a `SledState` and a `MemoryState`. This will allow the sled-related code and the in-memory code to be kept separate, with the top-level `StateService` making method calls to one or the other, as appropriate. This commit removes the existing Service impl for the SledService. This saves time in refactoring, and the code needs to be rewritten anyways so there's no loss to deleting it now.
This commit is contained in:
parent
303b02d10a
commit
2ac8b5b9d4
|
|
@ -8,8 +8,10 @@
|
|||
|
||||
mod config;
|
||||
mod constants;
|
||||
mod memory_state;
|
||||
mod request;
|
||||
mod response;
|
||||
mod service;
|
||||
mod sled_state;
|
||||
mod util;
|
||||
|
||||
|
|
@ -17,7 +19,13 @@ mod util;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use memory_state::MemoryState;
|
||||
use sled_state::SledState;
|
||||
|
||||
pub use config::Config;
|
||||
pub use request::Request;
|
||||
pub use response::Response;
|
||||
pub use sled_state::init;
|
||||
pub use service::init;
|
||||
|
||||
/// A boxed `std::error::Error`.
|
||||
pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
pub struct MemoryState {
|
||||
// TODO
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower::{buffer::Buffer, util::BoxService, Service};
|
||||
use zebra_chain::parameters::Network;
|
||||
|
||||
use crate::{BoxError, Config, MemoryState, Request, Response, SledState};
|
||||
|
||||
struct StateService {
|
||||
/// Holds data relating to finalized chain state.
|
||||
sled: SledState,
|
||||
/// Holds data relating to non-finalized chain state.
|
||||
mem: MemoryState,
|
||||
}
|
||||
|
||||
impl StateService {
|
||||
pub fn new(config: Config, network: Network) -> Self {
|
||||
let sled = SledState::new(&config, network);
|
||||
let mem = MemoryState {};
|
||||
Self { sled, mem }
|
||||
}
|
||||
}
|
||||
|
||||
impl Service<Request> for StateService {
|
||||
type Response = Response;
|
||||
type Error = BoxError;
|
||||
type Future =
|
||||
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request) -> Self::Future {
|
||||
match req {
|
||||
Request::AddBlock { block } => unimplemented!(),
|
||||
Request::GetBlock { hash } => unimplemented!(),
|
||||
Request::GetTip => unimplemented!(),
|
||||
Request::GetDepth { hash } => unimplemented!(),
|
||||
Request::GetBlockLocator { genesis } => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize a state service from the provided `config`.
|
||||
///
|
||||
/// Each `network` has its own separate sled database.
|
||||
///
|
||||
/// The resulting service is clonable, to provide shared access to a common chain
|
||||
/// state. It's possible to construct multiple state services in the same
|
||||
/// application (as long as they, e.g., use different storage locations), but
|
||||
/// doing so is probably not what you want.
|
||||
pub fn init(
|
||||
config: Config,
|
||||
network: Network,
|
||||
) -> Buffer<BoxService<Request, Response, BoxError>, Request> {
|
||||
Buffer::new(BoxService::new(StateService::new(config, network)), 1)
|
||||
}
|
||||
|
|
@ -1,15 +1,7 @@
|
|||
//! The primary implementation of the `zebra_state::Service` built upon sled
|
||||
use super::{Request, Response};
|
||||
use crate::Config;
|
||||
use futures::prelude::*;
|
||||
use std::error;
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
error,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower::{buffer::Buffer, util::BoxService, Service};
|
||||
use tracing::instrument;
|
||||
use zebra_chain::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize};
|
||||
use zebra_chain::{
|
||||
|
|
@ -17,11 +9,8 @@ use zebra_chain::{
|
|||
parameters::Network,
|
||||
};
|
||||
|
||||
/// Type alias of our wrapped service
|
||||
pub type StateService = Buffer<BoxService<Request, Response, Error>, Request>;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SledState {
|
||||
pub struct SledState {
|
||||
storage: sled::Db,
|
||||
}
|
||||
|
||||
|
|
@ -153,112 +142,6 @@ impl SledState {
|
|||
}
|
||||
}
|
||||
|
||||
impl Service<Request> for SledState {
|
||||
type Response = Response;
|
||||
type Error = Error;
|
||||
type Future =
|
||||
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request) -> Self::Future {
|
||||
match req {
|
||||
Request::AddBlock { block } => {
|
||||
let mut storage = self.clone();
|
||||
|
||||
// Make sure writes to the state are serialised, by performing
|
||||
// them in the state's call.
|
||||
// (See the state design RFC #0005 for details.)
|
||||
let result = storage.insert(block).map(|hash| Response::Added { hash });
|
||||
|
||||
async { result }.boxed()
|
||||
}
|
||||
Request::GetBlock { hash } => {
|
||||
let storage = self.clone();
|
||||
async move {
|
||||
storage
|
||||
.get(hash)?
|
||||
.map(|block| Response::Block { block })
|
||||
.ok_or_else(|| "block could not be found".into())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
Request::GetTip => {
|
||||
let storage = self.clone();
|
||||
async move {
|
||||
storage
|
||||
.get_tip()?
|
||||
.map(|hash| Response::Tip { hash })
|
||||
.ok_or_else(|| "zebra-state contains no blocks".into())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
Request::GetDepth { hash } => {
|
||||
let storage = self.clone();
|
||||
|
||||
async move {
|
||||
if !storage.contains(&hash)? {
|
||||
return Ok(Response::Depth(None));
|
||||
}
|
||||
|
||||
let block = storage
|
||||
.get(hash)?
|
||||
.expect("block must be present if contains returned true");
|
||||
let tip_hash = storage
|
||||
.get_tip()?
|
||||
.expect("storage must have a tip if it contains the previous block");
|
||||
let tip = storage
|
||||
.get(tip_hash)?
|
||||
.expect("block must be present if contains returned true");
|
||||
|
||||
let depth =
|
||||
tip.coinbase_height().unwrap().0 - block.coinbase_height().unwrap().0;
|
||||
|
||||
Ok(Response::Depth(Some(depth)))
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
Request::GetBlockLocator { genesis } => {
|
||||
let storage = self.clone();
|
||||
|
||||
async move {
|
||||
let tip_hash = match storage.get_tip()? {
|
||||
Some(tip) => tip,
|
||||
None => {
|
||||
return Ok(Response::BlockLocator {
|
||||
block_locator: vec![genesis],
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let tip = storage
|
||||
.get(tip_hash)?
|
||||
.expect("block must be present if contains returned true");
|
||||
|
||||
let tip_height = tip
|
||||
.coinbase_height()
|
||||
.expect("tip of the current chain will have a coinbase height");
|
||||
|
||||
let heights = crate::util::block_locator_heights(tip_height);
|
||||
|
||||
let block_locator = heights
|
||||
.map(|height| {
|
||||
storage.get_main_chain_at(height).map(|hash| {
|
||||
hash.expect("there should be no holes in the current chain")
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(Response::BlockLocator { block_locator })
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An alternate repr for `block::Height` that implements `AsRef<[u8]>` for usage
|
||||
/// with sled
|
||||
struct BytesHeight(u32, [u8; 4]);
|
||||
|
|
@ -336,10 +219,3 @@ impl Into<BoxError> for Error {
|
|||
BoxError::from(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) -> StateService {
|
||||
Buffer::new(BoxService::new(SledState::new(&config, network)), 1)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue