oasis_core_runtime/host/
mod.rs

1//! Host interface.
2use async_trait::async_trait;
3use thiserror::Error;
4
5use crate::{
6    common::{crypto::signature::PublicKey, namespace::Namespace},
7    enclave_rpc,
8    protocol::Protocol,
9    storage::mkvs::sync,
10    types::{self, Body},
11};
12
13pub mod bundle_manager;
14pub mod volume_manager;
15
16/// Errors.
17#[derive(Error, Debug)]
18pub enum Error {
19    #[error("bad response from host")]
20    BadResponse,
21
22    #[error("{0}")]
23    Host(#[from] types::Error),
24
25    #[error("{0}")]
26    Decode(#[from] cbor::DecodeError),
27}
28
29/// Transaction submission options.
30#[derive(Clone, Default, Debug)]
31pub struct SubmitTxOpts {
32    /// Target runtime identifier. If not specified, own runtime identifier is used.
33    pub runtime_id: Option<Namespace>,
34    /// Whether the call should wait until the transaction is included in a block.
35    pub wait: bool,
36    /// Whether the response should include a proof of transaction being included in a block.
37    pub prove: bool,
38}
39
40/// Transaction submission result.
41#[derive(Clone, Default, Debug)]
42pub struct TxResult {
43    /// Transaction output.
44    pub output: Vec<u8>,
45    /// Round in which the transaction was executed.
46    pub round: u64,
47    /// Order of the transaction in the execution batch.
48    pub batch_order: u32,
49    /// Optional inclusion proof.
50    pub proof: Option<sync::Proof>,
51}
52
53/// Notification registration options.
54#[derive(Clone, Default, Debug)]
55pub struct RegisterNotifyOpts {
56    /// Subscribe to runtime block notifications.
57    pub runtime_block: bool,
58    /// Subscribe to runtime event notifications.
59    pub runtime_event: Vec<Vec<u8>>,
60}
61
62/// Interface to the (untrusted) host node.
63#[async_trait]
64pub trait Host: Send + Sync {
65    /// Returns the identity of the host node.
66    async fn identity(&self) -> Result<PublicKey, Error>;
67
68    /// Submit a transaction.
69    async fn submit_tx(&self, data: Vec<u8>, opts: SubmitTxOpts)
70        -> Result<Option<TxResult>, Error>;
71
72    /// Register for receiving notifications.
73    async fn register_notify(&self, opts: RegisterNotifyOpts) -> Result<(), Error>;
74
75    /// Bundle manager interface.
76    fn bundle_manager(&self) -> &dyn bundle_manager::BundleManager;
77
78    /// Volume manager interface.
79    fn volume_manager(&self) -> &dyn volume_manager::VolumeManager;
80}
81
82#[async_trait]
83impl Host for Protocol {
84    async fn identity(&self) -> Result<PublicKey, Error> {
85        match self.call_host_async(Body::HostIdentityRequest {}).await? {
86            Body::HostIdentityResponse { node_id } => Ok(node_id),
87            _ => Err(Error::BadResponse),
88        }
89    }
90
91    async fn submit_tx(
92        &self,
93        data: Vec<u8>,
94        opts: SubmitTxOpts,
95    ) -> Result<Option<TxResult>, Error> {
96        match self
97            .call_host_async(Body::HostSubmitTxRequest {
98                runtime_id: opts.runtime_id.unwrap_or_else(|| self.get_runtime_id()),
99                data,
100                wait: opts.wait,
101                prove: opts.prove,
102            })
103            .await?
104        {
105            Body::HostSubmitTxResponse {
106                output,
107                round,
108                batch_order,
109                proof,
110            } => {
111                if opts.wait {
112                    Ok(Some(TxResult {
113                        output,
114                        round,
115                        batch_order,
116                        proof,
117                    }))
118                } else {
119                    // If we didn't wait for inclusion then there is no result.
120                    Ok(None)
121                }
122            }
123            _ => Err(Error::BadResponse),
124        }
125    }
126
127    async fn register_notify(&self, opts: RegisterNotifyOpts) -> Result<(), Error> {
128        match self
129            .call_host_async(Body::HostRegisterNotifyRequest {
130                runtime_block: opts.runtime_block,
131                runtime_event: match opts.runtime_event {
132                    tags if tags.is_empty() => None,
133                    tags => Some(types::RegisterNotifyRuntimeEvent { tags }),
134                },
135            })
136            .await?
137        {
138            Body::Empty {} => Ok(()),
139            _ => Err(Error::BadResponse),
140        }
141    }
142
143    fn bundle_manager(&self) -> &dyn bundle_manager::BundleManager {
144        self
145    }
146
147    fn volume_manager(&self) -> &dyn volume_manager::VolumeManager {
148        self
149    }
150}
151
152/// Wrapper to call the host via local RPC.
153pub(super) async fn host_rpc_call<Rq: cbor::Encode, Rs: cbor::Decode>(
154    protocol: &Protocol,
155    endpoint: &str,
156    method: &str,
157    args: Rq,
158) -> Result<Rs, Error> {
159    match protocol
160        .call_host_async(Body::HostRPCCallRequest {
161            endpoint: endpoint.to_string(),
162            request_id: 0,
163            request: cbor::to_vec(enclave_rpc::types::Request {
164                method: method.to_string(),
165                args: cbor::to_value(args),
166            }),
167            kind: enclave_rpc::types::Kind::LocalQuery,
168            nodes: vec![],
169        })
170        .await?
171    {
172        Body::HostRPCCallResponse { response, .. } => Ok(cbor::from_slice(&response)?),
173        _ => Err(Error::BadResponse),
174    }
175}