2020-06-15 14:41:26 -07:00
|
|
|
//! The primary implementation of the `zebra_state::Service` built upon sled
|
|
|
|
use crate::Config;
|
2020-09-09 17:13:58 -07:00
|
|
|
use std::error;
|
2020-06-15 14:41:26 -07:00
|
|
|
use std::sync::Arc;
|
2020-07-30 16:21:54 -07:00
|
|
|
use tracing::instrument;
|
|
|
|
use zebra_chain::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize};
|
2020-06-15 14:41:26 -07:00
|
|
|
use zebra_chain::{
|
2020-08-16 11:42:02 -07:00
|
|
|
block::{self, Block},
|
2020-08-15 15:45:37 -07:00
|
|
|
parameters::Network,
|
2020-06-15 14:41:26 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
2020-09-09 17:13:58 -07:00
|
|
|
pub struct SledState {
|
2020-06-15 14:41:26 -07:00
|
|
|
storage: sled::Db,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SledState {
|
2020-07-30 16:21:54 -07:00
|
|
|
#[instrument]
|
2020-07-26 16:57:18 -07:00
|
|
|
pub(crate) fn new(config: &Config, network: Network) -> Self {
|
|
|
|
let config = config.sled_config(network);
|
2020-06-15 14:41:26 -07:00
|
|
|
|
|
|
|
Self {
|
|
|
|
storage: config.open().unwrap(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-30 16:21:54 -07:00
|
|
|
#[instrument(skip(self))]
|
2020-06-15 14:41:26 -07:00
|
|
|
pub(super) fn insert(
|
|
|
|
&mut self,
|
2020-07-30 16:21:54 -07:00
|
|
|
block: impl Into<Arc<Block>> + std::fmt::Debug,
|
2020-08-15 23:20:01 -07:00
|
|
|
) -> Result<block::Hash, Error> {
|
2020-08-21 12:36:00 -07:00
|
|
|
use sled::Transactional;
|
|
|
|
|
2020-06-15 14:41:26 -07:00
|
|
|
let block = block.into();
|
2020-08-15 23:20:01 -07:00
|
|
|
let hash = block.hash();
|
2020-09-04 02:34:40 -07:00
|
|
|
let height = block
|
|
|
|
.coinbase_height()
|
|
|
|
.expect("missing height: valid blocks must have a height");
|
|
|
|
|
|
|
|
// Make sure blocks are inserted in order, as a defence in depth.
|
|
|
|
// See the state design RFC #0005 for details.
|
|
|
|
//
|
|
|
|
// TODO: handle multiple chains
|
|
|
|
match self.get_tip()? {
|
|
|
|
None => {
|
|
|
|
// This is a defence in depth - there is no need to check the
|
|
|
|
// genesis hash or previous block hash here.
|
|
|
|
assert_eq!(
|
|
|
|
height,
|
|
|
|
block::Height(0),
|
|
|
|
"out of order block: the first block must be at the genesis height"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Some(tip_hash) => {
|
|
|
|
assert_eq!(
|
|
|
|
block.header.previous_block_hash, tip_hash,
|
|
|
|
"out of order block: the next block must be a child of the current tip"
|
|
|
|
);
|
|
|
|
let tip_block = self
|
|
|
|
.get(tip_hash)?
|
|
|
|
.expect("missing tip block: tip hashes must have a corresponding block");
|
|
|
|
let tip_height = tip_block
|
|
|
|
.coinbase_height()
|
|
|
|
.expect("missing height: valid blocks must have a height");
|
|
|
|
assert_eq!(
|
|
|
|
height,
|
|
|
|
block::Height(tip_height.0 + 1),
|
|
|
|
"out of order block: the next height must be 1 greater than the tip height"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
2020-06-15 14:41:26 -07:00
|
|
|
|
2020-07-30 16:21:54 -07:00
|
|
|
let height_map = self.storage.open_tree(b"height_map")?;
|
2020-06-15 14:41:26 -07:00
|
|
|
let by_hash = self.storage.open_tree(b"by_hash")?;
|
|
|
|
|
2020-07-31 23:15:26 -07:00
|
|
|
let bytes = block.zcash_serialize_to_vec()?;
|
2020-06-15 14:41:26 -07:00
|
|
|
|
2020-08-21 12:36:00 -07:00
|
|
|
(&height_map, &by_hash).transaction(|(height_map, by_hash)| {
|
|
|
|
height_map.insert(&height.0.to_be_bytes(), &hash.0)?;
|
|
|
|
by_hash.insert(&hash.0, bytes.clone())?;
|
|
|
|
Ok(())
|
|
|
|
})?;
|
2020-06-15 14:41:26 -07:00
|
|
|
|
2020-09-01 18:20:32 -07:00
|
|
|
tracing::trace!(?height, ?hash, "Committed block");
|
|
|
|
metrics::gauge!("state.committed.block.height", height.0 as _);
|
|
|
|
metrics::counter!("state.committed.block.count", 1);
|
|
|
|
|
2020-06-15 14:41:26 -07:00
|
|
|
Ok(hash)
|
|
|
|
}
|
|
|
|
|
2020-07-30 16:21:54 -07:00
|
|
|
#[instrument(skip(self))]
|
2020-08-15 23:20:01 -07:00
|
|
|
pub(super) fn get(&self, hash: block::Hash) -> Result<Option<Arc<Block>>, Error> {
|
2020-07-30 16:21:54 -07:00
|
|
|
let by_hash = self.storage.open_tree(b"by_hash")?;
|
|
|
|
let key = &hash.0;
|
|
|
|
let value = by_hash.get(key)?;
|
2020-06-15 14:41:26 -07:00
|
|
|
|
|
|
|
if let Some(bytes) = value {
|
|
|
|
let bytes = bytes.as_ref();
|
|
|
|
let block = ZcashDeserialize::zcash_deserialize(bytes)?;
|
|
|
|
Ok(Some(block))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-30 16:21:54 -07:00
|
|
|
#[instrument(skip(self))]
|
|
|
|
pub(super) fn get_main_chain_at(
|
|
|
|
&self,
|
2020-08-16 11:42:02 -07:00
|
|
|
height: block::Height,
|
2020-08-15 23:20:01 -07:00
|
|
|
) -> Result<Option<block::Hash>, Error> {
|
2020-07-30 16:21:54 -07:00
|
|
|
let height_map = self.storage.open_tree(b"height_map")?;
|
|
|
|
let key = height.0.to_be_bytes();
|
|
|
|
let value = height_map.get(key)?;
|
|
|
|
|
|
|
|
if let Some(bytes) = value {
|
|
|
|
let bytes = bytes.as_ref();
|
|
|
|
let hash = ZcashDeserialize::zcash_deserialize(bytes)?;
|
|
|
|
Ok(Some(hash))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[instrument(skip(self))]
|
2020-08-15 23:20:01 -07:00
|
|
|
pub(super) fn get_tip(&self) -> Result<Option<block::Hash>, Error> {
|
2020-07-30 16:21:54 -07:00
|
|
|
let tree = self.storage.open_tree(b"height_map")?;
|
2020-06-15 14:41:26 -07:00
|
|
|
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
|
|
|
|
2020-07-30 16:21:54 -07:00
|
|
|
#[instrument(skip(self))]
|
2020-08-15 23:20:01 -07:00
|
|
|
fn contains(&self, hash: &block::Hash) -> Result<bool, Error> {
|
2020-06-22 19:24:53 -07:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-08-16 11:42:02 -07:00
|
|
|
/// An alternate repr for `block::Height` that implements `AsRef<[u8]>` for usage
|
2020-06-22 19:24:53 -07:00
|
|
|
/// with sled
|
|
|
|
struct BytesHeight(u32, [u8; 4]);
|
|
|
|
|
2020-08-16 11:42:02 -07:00
|
|
|
impl From<block::Height> for BytesHeight {
|
|
|
|
fn from(height: block::Height) -> Self {
|
2020-06-22 19:24:53 -07:00
|
|
|
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 {
|
2020-08-15 23:20:01 -07:00
|
|
|
ByHash(block::Hash),
|
2020-08-16 11:42:02 -07:00
|
|
|
ByHeight(block::Height),
|
2020-06-15 14:41:26 -07:00
|
|
|
}
|
|
|
|
|
2020-08-15 23:20:01 -07:00
|
|
|
impl From<block::Hash> for BlockQuery {
|
|
|
|
fn from(hash: block::Hash) -> Self {
|
2020-06-15 14:41:26 -07:00
|
|
|
Self::ByHash(hash)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-16 11:42:02 -07:00
|
|
|
impl From<block::Height> for BlockQuery {
|
|
|
|
fn from(height: block::Height) -> Self {
|
2020-06-15 14:41:26 -07:00
|
|
|
Self::ByHeight(height)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-30 16:21:54 -07:00
|
|
|
type BoxError = Box<dyn error::Error + Send + Sync + 'static>;
|
|
|
|
|
|
|
|
// these hacks are necessary to capture spantraces that can be extracted again
|
|
|
|
// while still having a nice From impl.
|
|
|
|
//
|
|
|
|
// Please forgive me.
|
|
|
|
|
|
|
|
/// a type that can store any error and implements the Error trait at the cost of
|
|
|
|
/// not implementing From<E: Error>
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
#[error(transparent)]
|
|
|
|
struct BoxRealError(BoxError);
|
|
|
|
|
|
|
|
/// The TracedError wrapper on a type that implements Error
|
|
|
|
#[derive(Debug)]
|
2020-08-11 13:25:40 -07:00
|
|
|
pub struct Error(tracing_error::TracedError<BoxRealError>);
|
2020-07-30 16:21:54 -07:00
|
|
|
|
|
|
|
macro_rules! impl_from {
|
|
|
|
($($src:ty,)*) => {$(
|
|
|
|
impl From<$src> for Error {
|
|
|
|
fn from(source: $src) -> Self {
|
|
|
|
let source = BoxRealError(source.into());
|
|
|
|
Self(source.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The hoops we have to jump through to keep using this like a BoxError
|
|
|
|
impl_from! {
|
|
|
|
&str,
|
|
|
|
SerializationError,
|
|
|
|
std::io::Error,
|
|
|
|
sled::Error,
|
2020-08-21 12:36:00 -07:00
|
|
|
sled::transaction::TransactionError,
|
2020-07-30 16:21:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Into<BoxError> for Error {
|
|
|
|
fn into(self) -> BoxError {
|
|
|
|
BoxError::from(self.0)
|
|
|
|
}
|
|
|
|
}
|