use async_trait::async_trait;
use thiserror::Error;
use crate::{
common::{crypto::signature::PublicKey, namespace::Namespace},
enclave_rpc,
protocol::Protocol,
storage::mkvs::sync,
types::{self, Body},
};
pub mod bundle_manager;
pub mod volume_manager;
#[derive(Error, Debug)]
pub enum Error {
#[error("bad response from host")]
BadResponse,
#[error("{0}")]
Host(#[from] types::Error),
#[error("{0}")]
Decode(#[from] cbor::DecodeError),
}
#[derive(Clone, Default, Debug)]
pub struct SubmitTxOpts {
pub runtime_id: Option<Namespace>,
pub wait: bool,
pub prove: bool,
}
#[derive(Clone, Default, Debug)]
pub struct TxResult {
pub output: Vec<u8>,
pub round: u64,
pub batch_order: u32,
pub proof: Option<sync::Proof>,
}
#[derive(Clone, Default, Debug)]
pub struct RegisterNotifyOpts {
pub runtime_block: bool,
pub runtime_event: Vec<Vec<u8>>,
}
#[async_trait]
pub trait Host: Send + Sync {
async fn identity(&self) -> Result<PublicKey, Error>;
async fn submit_tx(&self, data: Vec<u8>, opts: SubmitTxOpts)
-> Result<Option<TxResult>, Error>;
async fn register_notify(&self, opts: RegisterNotifyOpts) -> Result<(), Error>;
fn bundle_manager(&self) -> &dyn bundle_manager::BundleManager;
fn volume_manager(&self) -> &dyn volume_manager::VolumeManager;
}
#[async_trait]
impl Host for Protocol {
async fn identity(&self) -> Result<PublicKey, Error> {
match self.call_host_async(Body::HostIdentityRequest {}).await? {
Body::HostIdentityResponse { node_id } => Ok(node_id),
_ => Err(Error::BadResponse),
}
}
async fn submit_tx(
&self,
data: Vec<u8>,
opts: SubmitTxOpts,
) -> Result<Option<TxResult>, Error> {
match self
.call_host_async(Body::HostSubmitTxRequest {
runtime_id: opts.runtime_id.unwrap_or_else(|| self.get_runtime_id()),
data,
wait: opts.wait,
prove: opts.prove,
})
.await?
{
Body::HostSubmitTxResponse {
output,
round,
batch_order,
proof,
} => {
if opts.wait {
Ok(Some(TxResult {
output,
round,
batch_order,
proof,
}))
} else {
Ok(None)
}
}
_ => Err(Error::BadResponse),
}
}
async fn register_notify(&self, opts: RegisterNotifyOpts) -> Result<(), Error> {
match self
.call_host_async(Body::HostRegisterNotifyRequest {
runtime_block: opts.runtime_block,
runtime_event: match opts.runtime_event {
tags if tags.is_empty() => None,
tags => Some(types::RegisterNotifyRuntimeEvent { tags }),
},
})
.await?
{
Body::Empty {} => Ok(()),
_ => Err(Error::BadResponse),
}
}
fn bundle_manager(&self) -> &dyn bundle_manager::BundleManager {
self
}
fn volume_manager(&self) -> &dyn volume_manager::VolumeManager {
self
}
}
pub(super) async fn host_rpc_call<Rq: cbor::Encode, Rs: cbor::Decode>(
protocol: &Protocol,
endpoint: &str,
method: &str,
args: Rq,
) -> Result<Rs, Error> {
match protocol
.call_host_async(Body::HostRPCCallRequest {
endpoint: endpoint.to_string(),
request_id: 0,
request: cbor::to_vec(enclave_rpc::types::Request {
method: method.to_string(),
args: cbor::to_value(args),
}),
kind: enclave_rpc::types::Kind::LocalQuery,
nodes: vec![],
})
.await?
{
Body::HostRPCCallResponse { response, .. } => Ok(cbor::from_slice(&response)?),
_ => Err(Error::BadResponse),
}
}