#![warn(
missing_debug_implementations,
missing_docs,
unsafe_code,
bare_trait_objects
)]
#![warn(clippy::pedantic, clippy::nursery)]
#![allow(
// Next `cast_*` lints don't give alternatives.
clippy::cast_possible_wrap, clippy::cast_possible_truncation, clippy::cast_sign_loss,
// Next lints produce too much noise/false positives.
clippy::module_name_repetitions, clippy::similar_names, clippy::must_use_candidate,
clippy::pub_enum_variant_names,
// '... may panic' lints.
clippy::indexing_slicing,
// Too much work to fix.
clippy::missing_errors_doc, clippy::missing_const_for_fn,
// False positive: WebSocket
clippy::doc_markdown
)]
use exonum::{
merkledb::ObjectHash,
runtime::{ExecutionContext, ExecutionError, ExecutionFail},
};
use exonum_derive::*;
use exonum_rust_runtime::{api::ServiceApiBuilder, AfterCommitContext, DefaultInstance, Service};
pub mod api;
use crate::api::{websocket::SharedState, ExplorerApi};
#[derive(Debug, Clone, Copy, ExecutionFail)]
#[non_exhaustive]
pub enum Error {
DuplicateExplorer = 0,
}
#[derive(Debug, Default, ServiceDispatcher)]
pub struct ExplorerService {
shared_state: SharedState,
}
impl Service for ExplorerService {
fn initialize(
&self,
context: ExecutionContext<'_>,
_params: Vec<u8>,
) -> Result<(), ExecutionError> {
let instances = context.data().for_dispatcher().service_instances();
for instance in instances.values() {
if instance.spec.artifact.name == env!("CARGO_PKG_NAME") {
let msg = format!(
"An explorer service is already instantiated on the blockchain as {}",
instance.spec
);
return Err(Error::DuplicateExplorer.with_description(msg));
}
}
Ok(())
}
fn after_commit(&self, context: AfterCommitContext<'_>) {
let block_hash = context.data().for_core().last_block().object_hash();
self.shared_state.broadcast_block(block_hash);
}
fn wire_api(&self, builder: &mut ServiceApiBuilder) {
let blockchain = builder.blockchain().to_owned();
let scope = builder
.with_root_path(ExplorerFactory::INSTANCE_NAME)
.public_scope();
ExplorerApi::new(blockchain)
.wire_rest(scope)
.wire_ws(self.shared_state.get_ref(), scope);
}
}
#[derive(Debug, Clone, Copy, ServiceFactory)]
#[service_factory(service_constructor = "Self::new_instance")]
pub struct ExplorerFactory;
#[allow(clippy::unused_self)]
impl ExplorerFactory {
#[allow(clippy::trivially_copy_pass_by_ref)]
fn new_instance(&self) -> Box<dyn Service> {
Box::new(ExplorerService::default())
}
}
impl DefaultInstance for ExplorerFactory {
const INSTANCE_ID: u32 = 2;
const INSTANCE_NAME: &'static str = "explorer";
}