2020-06-15 14:41:26 -07:00
|
|
|
//! The primary implementation of the `zebra_state::Service` built upon sled
|
|
|
|
use super::{Request, Response};
|
|
|
|
use crate::Config;
|
|
|
|
use futures::prelude::*;
|
|
|
|
use std::sync::Arc;
|
|
|
|
use std::{
|
|
|
|
error,
|
|
|
|
future::Future,
|
|
|
|
pin::Pin,
|
|
|
|
task::{Context, Poll},
|
|
|
|
};
|
|
|
|
use tower::{buffer::Buffer, Service};
|
|
|
|
use zebra_chain::serialization::{ZcashDeserialize, ZcashSerialize};
|
|
|
|
use zebra_chain::{
|
|
|
|
block::{Block, BlockHeaderHash},
|
|
|
|
types::BlockHeight,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
struct SledState {
|
|
|
|
storage: sled::Db,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SledState {
|
|
|
|
pub(crate) fn new(config: &Config) -> Self {
|
|
|
|
let config = config.sled_config();
|
|
|
|
|
|
|
|
Self {
|
|
|
|
storage: config.open().unwrap(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn insert(
|
|
|
|
&mut self,
|
|
|
|
block: impl Into<Arc<Block>>,
|
|
|
|
) -> Result<BlockHeaderHash, Error> {
|
|
|
|
let block = block.into();
|
|
|
|
let hash: BlockHeaderHash = block.as_ref().into();
|
|
|
|
let height = block.coinbase_height().unwrap();
|
|
|
|
|
|
|
|
let by_height = self.storage.open_tree(b"by_height")?;
|
|
|
|
let by_hash = self.storage.open_tree(b"by_hash")?;
|
|
|
|
|
|
|
|
let mut bytes = Vec::new();
|
|
|
|
block.zcash_serialize(&mut bytes)?;
|
|
|
|
|
|
|
|
// TODO(jlusby): make this transactional
|
|
|
|
by_height.insert(&height.0.to_be_bytes(), bytes.as_slice())?;
|
|
|
|
by_hash.insert(&hash.0, bytes)?;
|
|
|
|
|
|
|
|
Ok(hash)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn get(&self, query: impl Into<BlockQuery>) -> Result<Option<Arc<Block>>, Error> {
|
|
|
|
let query = query.into();
|
|
|
|
let value = match query {
|
|
|
|
BlockQuery::ByHash(hash) => {
|
|
|
|
let by_hash = self.storage.open_tree(b"by_hash")?;
|
|
|
|
let key = &hash.0;
|
|
|
|
by_hash.get(key)?
|
|
|
|
}
|
|
|
|
BlockQuery::ByHeight(height) => {
|
|
|
|
let by_height = self.storage.open_tree(b"by_height")?;
|
|
|
|
let key = height.0.to_be_bytes();
|
|
|
|
by_height.get(key)?
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(bytes) = value {
|
|
|
|
let bytes = bytes.as_ref();
|
|
|
|
let block = ZcashDeserialize::zcash_deserialize(bytes)?;
|
|
|
|
Ok(Some(block))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-22 19:24:53 -07:00
|
|
|
pub(super) fn get_tip(&self) -> Result<Option<Arc<Block>>, Error> {
|
2020-06-15 14:41:26 -07:00
|
|
|
let tree = self.storage.open_tree(b"by_height")?;
|
|
|
|
let last_entry = tree.iter().values().next_back();
|
|
|
|
|
|
|
|
match last_entry {
|
2020-06-22 19:24:53 -07:00
|
|
|
Some(Ok(bytes)) => Ok(Some(ZcashDeserialize::zcash_deserialize(bytes.as_ref())?)),
|
2020-06-15 14:41:26 -07:00
|
|
|
Some(Err(e)) => Err(e)?,
|
|
|
|
None => Ok(None),
|
|
|
|
}
|
|
|
|
}
|
2020-06-22 19:24:53 -07:00
|
|
|
|
|
|
|
fn contains(&self, hash: &BlockHeaderHash) -> Result<bool, Error> {
|
|
|
|
let by_hash = self.storage.open_tree(b"by_hash")?;
|
|
|
|
let key = &hash.0;
|
|
|
|
|
|
|
|
Ok(by_hash.contains_key(key)?)
|
|
|
|
}
|
2020-06-15 14:41:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
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();
|
|
|
|
|
|
|
|
async move { storage.insert(block).map(|hash| Response::Added { hash }) }.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()?
|
2020-06-22 19:24:53 -07:00
|
|
|
.map(|block| block.as_ref().into())
|
2020-06-15 14:41:26 -07:00
|
|
|
.map(|hash| Response::Tip { hash })
|
|
|
|
.ok_or_else(|| "zebra-state contains no blocks".into())
|
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
2020-06-22 19:24:53 -07:00
|
|
|
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 = storage
|
|
|
|
.get_tip()?
|
|
|
|
.expect("storage must have a tip if it contains the previous block");
|
|
|
|
|
|
|
|
let depth =
|
|
|
|
tip.coinbase_height().unwrap().0 - block.coinbase_height().unwrap().0;
|
|
|
|
|
|
|
|
Ok(Response::Depth(Some(depth)))
|
|
|
|
}
|
|
|
|
.boxed()
|
|
|
|
}
|
2020-06-15 14:41:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-22 19:24:53 -07:00
|
|
|
/// An alternate repr for `BlockHeight` that implements `AsRef<[u8]>` for usage
|
|
|
|
/// with sled
|
|
|
|
struct BytesHeight(u32, [u8; 4]);
|
|
|
|
|
|
|
|
impl From<BlockHeight> for BytesHeight {
|
|
|
|
fn from(height: BlockHeight) -> Self {
|
|
|
|
let bytes = height.0.to_be_bytes();
|
|
|
|
Self(height.0, bytes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AsRef<[u8]> for BytesHeight {
|
|
|
|
fn as_ref(&self) -> &[u8] {
|
|
|
|
&self.1[..]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-15 14:41:26 -07:00
|
|
|
pub(super) enum BlockQuery {
|
|
|
|
ByHash(BlockHeaderHash),
|
|
|
|
ByHeight(BlockHeight),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<BlockHeaderHash> for BlockQuery {
|
|
|
|
fn from(hash: BlockHeaderHash) -> Self {
|
|
|
|
Self::ByHash(hash)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<BlockHeight> for BlockQuery {
|
|
|
|
fn from(height: BlockHeight) -> Self {
|
|
|
|
Self::ByHeight(height)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return's a type that implement's the `zebra_state::Service` using `sled`
|
|
|
|
pub fn init(
|
|
|
|
config: Config,
|
|
|
|
) -> impl Service<
|
|
|
|
Request,
|
|
|
|
Response = Response,
|
|
|
|
Error = Error,
|
|
|
|
Future = impl Future<Output = Result<Response, Error>>,
|
|
|
|
> + Send
|
|
|
|
+ Clone
|
|
|
|
+ 'static {
|
|
|
|
Buffer::new(SledState::new(&config), 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
type Error = Box<dyn error::Error + Send + Sync + 'static>;
|