1use std::{marker::PhantomData, sync::Arc};
3
4use anyhow::{anyhow, bail, Result};
5
6use crate::{
7 context::RuntimeBatchContext,
8 core::{
9 consensus::{
10 roothash::Header,
11 state::{
12 beacon::ImmutableState as BeaconState, registry::ImmutableState as RegistryState,
13 roothash::ImmutableState as RoothashState,
14 },
15 verifier::Verifier,
16 },
17 enclave_rpc::{
18 dispatcher::{
19 Dispatcher as RpcDispatcher, Method as RpcMethod,
20 MethodDescriptor as RpcMethodDescriptor,
21 },
22 types::Kind as RpcKind,
23 Context as RpcContext,
24 },
25 future::block_on,
26 protocol::{HostInfo, Protocol},
27 storage::mkvs,
28 },
29 dispatcher,
30 keymanager::KeyManagerClient,
31 module::MethodHandler,
32 state::{self, CurrentState},
33 storage::HostStore,
34 Runtime,
35};
36
37pub const METHOD_QUERY: &str = "runtime-sdk/query";
39
40#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
42pub struct QueryRequest {
43 pub round: u64,
44 pub method: String,
45 pub args: Vec<u8>,
46}
47
48pub(crate) struct Wrapper<R: Runtime> {
50 host_info: HostInfo,
51 host: Arc<Protocol>,
52 key_manager: Option<Arc<KeyManagerClient>>,
53 consensus_verifier: Arc<dyn Verifier>,
54 _runtime: PhantomData<R>,
55}
56
57impl<R> Wrapper<R>
58where
59 R: Runtime + Send + Sync + 'static,
60{
61 pub(crate) fn wrap(
62 rpc: &mut RpcDispatcher,
63 host: Arc<Protocol>,
64 host_info: HostInfo,
65 key_manager: Option<Arc<KeyManagerClient>>,
66 consensus_verifier: Arc<dyn Verifier>,
67 ) {
68 let wrapper = Box::leak(Box::new(Self {
69 host_info,
70 host,
71 key_manager,
72 consensus_verifier,
73 _runtime: PhantomData,
74 }));
75 rpc.add_methods(wrapper.methods());
76 }
77
78 fn methods(&'static self) -> Vec<RpcMethod> {
79 vec![RpcMethod::new(
80 RpcMethodDescriptor {
81 name: METHOD_QUERY.to_string(),
82 kind: RpcKind::NoiseSession,
83 },
84 move |ctx: &_, req: &_| self.rpc_query(ctx, req),
85 )]
86 }
87
88 fn ensure_session_endorsed(&self, ctx: &RpcContext) -> Result<()> {
89 let endorsed_by = ctx
90 .session_info
91 .as_ref()
92 .ok_or(anyhow!("not authorized"))?
93 .endorsed_by
94 .ok_or(anyhow!("not endorsed by host"))?;
95 let host_identity = self
96 .host
97 .get_identity()
98 .ok_or(anyhow!("local identity not available"))?
99 .node_identity()
100 .ok_or(anyhow!("node identity not available"))?;
101 if endorsed_by != host_identity {
102 bail!("not endorsed by host");
103 }
104 Ok(())
105 }
106
107 fn rpc_query(&self, ctx: &RpcContext, req: &QueryRequest) -> Result<Vec<u8>> {
108 self.ensure_session_endorsed(ctx)?;
109
110 let is_confidential_allowed = R::Modules::is_allowed_private_km_query(&req.method)
113 && R::is_allowed_private_km_query(&req.method);
114 let key_manager = self.key_manager.as_ref().map(|mgr| {
115 if is_confidential_allowed {
116 mgr.with_private_context()
117 } else {
118 mgr.with_context()
119 }
120 });
121
122 let state = block_on(self.consensus_verifier.latest_state())?;
124 let roothash = RoothashState::new(&state);
125 let roots = roothash
126 .round_roots(self.host_info.runtime_id, req.round)?
127 .ok_or(anyhow!("root not found"))?;
128 let beacon = BeaconState::new(&state);
129 let epoch = beacon.epoch()?;
130 let registry = RegistryState::new(&state);
131 let runtime = registry
132 .runtime(&self.host_info.runtime_id)?
133 .ok_or(anyhow!("runtime not found"))?;
134
135 let history = self.consensus_verifier.clone();
137 let root = HostStore::new(
138 self.host.clone(),
139 mkvs::Root {
140 namespace: self.host_info.runtime_id,
141 version: req.round,
142 root_type: mkvs::RootType::State,
143 hash: roots.state_root,
144 },
145 );
146 let header = Header {
151 namespace: self.host_info.runtime_id,
152 round: req.round,
153 io_root: roots.io_root,
154 state_root: roots.state_root,
155 ..Default::default()
156 };
157 let round_results = Default::default();
158 let max_messages = runtime.executor.max_messages;
159
160 let ctx = RuntimeBatchContext::<'_, R>::new(
161 &self.host_info,
162 key_manager,
163 &header,
164 &round_results,
165 &state,
166 &history,
167 epoch,
168 max_messages,
169 );
170
171 CurrentState::enter_opts(
172 state::Options::new()
173 .with_mode(state::Mode::Check)
174 .with_rng_local_entropy(), root,
176 || dispatcher::Dispatcher::<R>::dispatch_query(&ctx, &req.method, req.args.clone()),
177 )
178 .map_err(Into::into)
179 }
180}