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/// Notification registration options.
56#[derive(Clone, Default, Debug)]
57pub struct RegisterNotifyOpts {
58    /// Subscribe to runtime block notifications.
59    pub runtime_block: bool,
60    /// Subscribe to runtime event notifications.
61    pub runtime_event: Vec<Vec<u8>>,
62}
63
64/// Interface to the (untrusted) host node.
65#[async_trait]
66pub trait Host: Send + Sync {
67    /// Returns the identity of the host node.
68    async fn identity(&self) -> Result<PublicKey, Error>;
69
70    /// Submit a transaction.
71    async fn submit_tx(&self, data: Vec<u8>, opts: SubmitTxOpts)
72        -> Result<Option<TxResult>, Error>;
73
74    /// Register for receiving notifications.
75    async fn register_notify(&self, opts: RegisterNotifyOpts) -> Result<(), Error>;
76
77    /// Bundle manager interface.
78    fn bundle_manager(&self) -> &dyn bundle_manager::BundleManager;
79
80    /// Volume manager interface.
81    fn volume_manager(&self) -> &dyn volume_manager::VolumeManager;
82
83    /// Log manager interface.
84    fn log_manager(&self) -> &dyn log_manager::LogManager;
85
86    /// Attestation interface.
87    fn attestation(&self) -> &dyn attestation::Attestation;
88}
89
90#[async_trait]
91impl Host for Protocol {
92    async fn identity(&self) -> Result<PublicKey, Error> {
93        match self.call_host_async(Body::HostIdentityRequest {}).await? {
94            Body::HostIdentityResponse { node_id } => Ok(node_id),
95            _ => Err(Error::BadResponse),
96        }
97    }
98
99    async fn submit_tx(
100        &self,
101        data: Vec<u8>,
102        opts: SubmitTxOpts,
103    ) -> Result<Option<TxResult>, Error> {
104        match self
105            .call_host_async(Body::HostSubmitTxRequest {
106                runtime_id: opts.runtime_id.unwrap_or_else(|| self.get_runtime_id()),
107                data,
108                wait: opts.wait,
109                prove: opts.prove,
110            })
111            .await?
112        {
113            Body::HostSubmitTxResponse {
114                output,
115                round,
116                batch_order,
117                proof,
118            } => {
119                if opts.wait {
120                    Ok(Some(TxResult {
121                        output,
122                        round,
123                        batch_order,
124                        proof,
125                    }))
126                } else {
127                    // If we didn't wait for inclusion then there is no result.
128                    Ok(None)
129                }
130            }
131            _ => Err(Error::BadResponse),
132        }
133    }
134
135    async fn register_notify(&self, opts: RegisterNotifyOpts) -> Result<(), Error> {
136        match self
137            .call_host_async(Body::HostRegisterNotifyRequest {
138                runtime_block: opts.runtime_block,
139                runtime_event: match opts.runtime_event {
140                    tags if tags.is_empty() => None,
141                    tags => Some(types::RegisterNotifyRuntimeEvent { tags }),
142                },
143            })
144            .await?
145        {
146            Body::Empty {} => Ok(()),
147            _ => Err(Error::BadResponse),
148        }
149    }
150
151    fn bundle_manager(&self) -> &dyn bundle_manager::BundleManager {
152        self
153    }
154
155    fn volume_manager(&self) -> &dyn volume_manager::VolumeManager {
156        self
157    }
158
159    fn log_manager(&self) -> &dyn log_manager::LogManager {
160        self
161    }
162
163    fn attestation(&self) -> &dyn attestation::Attestation {
164        self
165    }
166}
167
168/// Wrapper to call the host via local RPC.
169pub(super) async fn host_rpc_call<Rq: cbor::Encode, Rs: cbor::Decode>(
170    protocol: &Protocol,
171    endpoint: &str,
172    method: &str,
173    args: Rq,
174) -> Result<Rs, Error> {
175    match protocol
176        .call_host_async(Body::HostRPCCallRequest {
177            endpoint: endpoint.to_string(),
178            request_id: 0,
179            request: cbor::to_vec(enclave_rpc::types::Request {
180                method: method.to_string(),
181                args: cbor::to_value(args),
182            }),
183            kind: enclave_rpc::types::Kind::LocalQuery,
184            nodes: vec![],
185        })
186        .await?
187    {
188        Body::HostRPCCallResponse { response, .. } => Ok(cbor::from_slice(&response)?),
189        _ => Err(Error::BadResponse),
190    }
191}