use exonum_merkledb::{
access::{AsReadonly, FromAccess, Prefixed, RawAccess},
generic::GenericRawAccess,
Snapshot, SystemSchema,
};
use super::{
versioning::{ArtifactReqError, RequireArtifact},
DispatcherSchema, InstanceQuery, InstanceSpec, InstanceState,
};
use crate::blockchain::{IndexProof, Schema as CoreSchema};
#[derive(Debug, Clone)]
pub struct BlockchainData<T> {
access: T,
instance_name: String,
}
impl<T: RawAccess + AsReadonly> BlockchainData<T> {
pub fn new(access: T, instance_name: impl Into<String>) -> Self {
Self {
access,
instance_name: instance_name.into(),
}
}
pub fn instance_name(&self) -> &str {
&self.instance_name
}
#[doc(hidden)]
pub fn unstructured_access(&self) -> T::Readonly {
self.access.as_readonly()
}
pub fn for_core(&self) -> CoreSchema<T::Readonly> {
CoreSchema::new(self.unstructured_access())
}
pub fn for_dispatcher(&self) -> DispatcherSchema<T::Readonly> {
DispatcherSchema::new(self.unstructured_access())
}
pub fn for_service<'q>(
&self,
id: impl Into<InstanceQuery<'q>>,
) -> Option<Prefixed<T::Readonly>> {
mount_point_for_service(self.access.as_readonly(), id).map(|(access, _)| access)
}
pub fn service_schema<'q, S, I>(&self, service_id: I) -> Result<S, ArtifactReqError>
where
S: RequireArtifact + FromAccess<Prefixed<T::Readonly>>,
I: Into<InstanceQuery<'q>>,
{
schema_for_service(self.access.as_readonly(), service_id)
}
pub fn for_executing_service(&self) -> Prefixed<T> {
Prefixed::new(&self.instance_name, self.access.clone())
}
}
impl BlockchainData<&dyn Snapshot> {
pub fn proof_for_service_index(&self, index_name: &str) -> Option<IndexProof> {
let full_index_name = [&self.instance_name, ".", index_name].concat();
self.access.proof_for_index(&full_index_name)
}
}
#[allow(clippy::use_self)] impl<'a, T> BlockchainData<T>
where
T: Into<GenericRawAccess<'a>>,
{
pub fn erase_access(self) -> BlockchainData<GenericRawAccess<'a>> {
BlockchainData::new(self.access.into(), self.instance_name)
}
}
fn mount_point_for_service<'q, T: RawAccess>(
access: T,
id: impl Into<InstanceQuery<'q>>,
) -> Option<(Prefixed<T>, InstanceSpec)> {
let state = DispatcherSchema::new(access.clone())
.get_instance(id)
.filter(InstanceState::is_readable)?;
Some((Prefixed::new(state.spec.name.clone(), access), state.spec))
}
fn schema_for_service<'q, T, S>(
access: T,
service_id: impl Into<InstanceQuery<'q>>,
) -> Result<S, ArtifactReqError>
where
T: RawAccess,
S: RequireArtifact + FromAccess<Prefixed<T>>,
{
let (access, spec) =
mount_point_for_service(access, service_id).ok_or(ArtifactReqError::NoService)?;
let artifact_req = S::required_artifact();
artifact_req.try_match(&spec.artifact)?;
Ok(S::from_root(access).unwrap())
}
pub trait SnapshotExt {
fn for_core(&self) -> CoreSchema<&'_ dyn Snapshot>;
fn for_dispatcher(&self) -> DispatcherSchema<&'_ dyn Snapshot>;
fn for_service<'q>(&self, id: impl Into<InstanceQuery<'q>>) -> Option<Prefixed<&dyn Snapshot>>;
#[doc(hidden)]
fn proof_for_index(&self, index_name: &str) -> Option<IndexProof>;
fn service_schema<'s, 'q, S, I>(&'s self, service_id: I) -> Result<S, ArtifactReqError>
where
S: RequireArtifact + FromAccess<Prefixed<&'s dyn Snapshot>>,
I: Into<InstanceQuery<'q>>;
}
impl SnapshotExt for dyn Snapshot {
fn for_core(&self) -> CoreSchema<&'_ dyn Snapshot> {
CoreSchema::new(self)
}
fn for_dispatcher(&self) -> DispatcherSchema<&'_ dyn Snapshot> {
DispatcherSchema::new(self)
}
fn for_service<'q>(&self, id: impl Into<InstanceQuery<'q>>) -> Option<Prefixed<&dyn Snapshot>> {
mount_point_for_service(self, id).map(|(access, _)| access)
}
fn proof_for_index(&self, index_name: &str) -> Option<IndexProof> {
let core_schema = self.for_core();
let height = core_schema.height();
let block_proof = core_schema.block_and_precommits(height).unwrap();
let aggregator = SystemSchema::new(self).state_aggregator();
aggregator.get(index_name)?;
let index_proof = aggregator.get_proof(index_name.to_string());
Some(IndexProof::new(block_proof, index_proof))
}
fn service_schema<'s, 'q, S, I>(&'s self, service_id: I) -> Result<S, ArtifactReqError>
where
S: RequireArtifact + FromAccess<Prefixed<&'s dyn Snapshot>>,
I: Into<InstanceQuery<'q>>,
{
schema_for_service(self, service_id)
}
}