oasis_runtime_sdk/
runtime.rs

1//! Runtime.
2use std::sync::Arc;
3
4use oasis_core_runtime::{
5    common::{sgx, version},
6    config::Config,
7    consensus::verifier::TrustRoot,
8    dispatcher::{PostInitState, PreInitState},
9    enclave_rpc::session,
10    start_runtime,
11    types::{FeatureScheduleControl, Features},
12};
13
14use crate::{
15    config,
16    context::Context,
17    crypto, dispatcher,
18    keymanager::{KeyManagerClient, TrustedSigners},
19    module::{
20        BlockHandler, FeeProxyHandler, InvariantHandler, MethodHandler, MigrationHandler,
21        ModuleInfoHandler, TransactionHandler,
22    },
23    modules,
24    state::CurrentState,
25    storage::{self},
26};
27
28/// A runtime.
29pub trait Runtime {
30    /// Runtime version.
31    const VERSION: version::Version;
32    /// State version.
33    const STATE_VERSION: u32 = 0;
34
35    /// Prefetch limit. To enable prefetch set it to a non-zero value.
36    const PREFETCH_LIMIT: u16 = 0;
37
38    /// Runtime schedule control configuration.
39    const SCHEDULE_CONTROL: config::ScheduleControl = config::ScheduleControl::default();
40
41    /// Module that provides the core API.
42    type Core: modules::core::API;
43    /// Module that provides the accounts API.
44    type Accounts: modules::accounts::API;
45    /// Handler for proxy fee payments.
46    type FeeProxy: FeeProxyHandler = ();
47
48    /// Supported modules.
49    type Modules: TransactionHandler
50        + MigrationHandler
51        + MethodHandler
52        + BlockHandler
53        + InvariantHandler
54        + ModuleInfoHandler;
55
56    /// Return the trusted signers for this runtime; if `None`, a key manager connection will not be
57    /// established on startup.
58    fn trusted_signers() -> Option<TrustedSigners> {
59        None
60    }
61
62    /// Return the consensus layer trust root for this runtime; if `None`, consensus layer integrity
63    /// verification will not be performed.
64    fn consensus_trust_root() -> Option<TrustRoot> {
65        None
66    }
67
68    /// Genesis state for the runtime.
69    fn genesis_state() -> <Self::Modules as MigrationHandler>::Genesis;
70
71    /// Perform runtime-specific state migration. This method is only called when the recorded
72    /// state version does not match `STATE_VERSION`.
73    fn migrate_state<C: Context>(_ctx: &C) {
74        // Default implementation doesn't perform any migration.
75    }
76
77    /// Whether a given query method is allowed to be invoked.
78    fn is_allowed_query(_method: &str) -> bool {
79        true
80    }
81
82    /// Whether a given query method is allowed to access private key manager state.
83    ///
84    /// Note that even if this returns `true` for a method, the method also needs to be tagged as
85    /// being allowed to access private key manager state (e.g. with `allow_private_km`).
86    fn is_allowed_private_km_query(_method: &str) -> bool {
87        true
88    }
89
90    /// Whether a given call is allowed to be invoked interactively.
91    ///
92    /// Note that even if this returns `true` for a method, the method also needs to be tagged as
93    /// being allowed to be executed interactively (e.g. with `allow_interactive`)
94    fn is_allowed_interactive_call(_method: &str) -> bool {
95        true
96    }
97
98    /// Perform state migrations if required.
99    fn migrate<C: Context>(ctx: &C) {
100        let mut metadata = CurrentState::with_store(|store| {
101            let store = storage::TypedStore::new(storage::PrefixStore::new(
102                store,
103                &modules::core::MODULE_NAME,
104            ));
105            let metadata: modules::core::types::Metadata = store
106                .get(modules::core::state::METADATA)
107                .unwrap_or_default();
108
109            metadata
110        });
111
112        // Perform state migrations/initialization on all modules.
113        let mut has_changes =
114            Self::Modules::init_or_migrate(ctx, &mut metadata, Self::genesis_state());
115
116        // Check if we need to also apply any global state updates.
117        let global_version = metadata
118            .versions
119            .get(modules::core::types::VERSION_GLOBAL_KEY)
120            .copied()
121            .unwrap_or_default();
122        if global_version != Self::STATE_VERSION
123            && !CurrentState::with_env(|env| env.is_check_only())
124        {
125            assert!(
126                // There should either be no state, or it should be the previous version.
127                global_version == 0 || global_version == Self::STATE_VERSION - 1,
128                "inconsistent existing state version (expected: {} got: {})",
129                Self::STATE_VERSION - 1,
130                global_version
131            );
132
133            Self::migrate_state(ctx);
134
135            // Update metadata.
136            metadata.versions.insert(
137                modules::core::types::VERSION_GLOBAL_KEY.to_string(),
138                Self::STATE_VERSION,
139            );
140            has_changes = true;
141        }
142
143        // If there are any changes, update metadata.
144        if has_changes {
145            CurrentState::with_store(|store| {
146                let mut store = storage::TypedStore::new(storage::PrefixStore::new(
147                    store,
148                    &modules::core::MODULE_NAME,
149                ));
150                store.insert(modules::core::state::METADATA, metadata);
151            });
152        }
153    }
154
155    /// Start the runtime.
156    fn start()
157    where
158        Self: Sized + Send + Sync + 'static,
159    {
160        // Initializer.
161        let init = |state: PreInitState<'_>| -> PostInitState {
162            // Fetch host information and configure domain separation context.
163            let hi = state.protocol.get_host_info();
164            crypto::signature::context::set_chain_context(
165                hi.runtime_id,
166                &hi.consensus_chain_context,
167            );
168
169            // Cobble together a keymanager client.
170            let key_manager = Self::trusted_signers().map(|signers| {
171                Arc::new(KeyManagerClient::new(
172                    hi.runtime_id,
173                    state.protocol.clone(),
174                    state.consensus_verifier.clone(),
175                    state.identity.clone(),
176                    state.rpc_dispatcher,
177                    4096,
178                    signers,
179                ))
180            });
181
182            // Create the transaction dispatcher.
183            let dispatcher = dispatcher::Dispatcher::<Self>::new(
184                state.protocol.clone(),
185                key_manager,
186                state.consensus_verifier.clone(),
187            );
188            // Register EnclaveRPC methods.
189            dispatcher.register_enclaverpc(state.rpc_dispatcher);
190            // Make sure we allow a wide amount of valid quotes for EnclaveRPC because our clients
191            // are other runtime components and we should accept them all.
192            let session_builder = session::Builder::default()
193                .local_identity(state.identity.clone())
194                .quote_policy(Some(Arc::new(sgx::QuotePolicy {
195                    ias: Some(sgx::ias::QuotePolicy {
196                        disabled: true, // Disable legacy EPID attestation.
197                        ..Default::default()
198                    }),
199                    pcs: Some(sgx::pcs::QuotePolicy {
200                        // Allow TDX since that is not part of the default policy.
201                        tdx: Some(sgx::pcs::TdxQuotePolicy {
202                            allowed_tdx_modules: vec![],
203                        }),
204                        ..Default::default()
205                    }),
206                })));
207            state.rpc_demux.set_session_builder(session_builder);
208
209            PostInitState {
210                txn_dispatcher: Some(Box::new(dispatcher)),
211                app: None,
212            }
213        };
214
215        // Configure the runtime features.
216        let features = Features {
217            schedule_control: Some(FeatureScheduleControl {
218                initial_batch_size: Self::SCHEDULE_CONTROL.initial_batch_size,
219            }),
220            ..Default::default()
221        };
222
223        // Start the runtime.
224        start_runtime(
225            Box::new(init),
226            Config {
227                version: Self::VERSION,
228                trust_root: Self::consensus_trust_root(),
229                features,
230                persist_check_tx_state: false,
231                ..Default::default()
232            },
233        );
234    }
235}