Skip to main content

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