oasis_runtime_sdk/
context.rs

1//! Execution context.
2use std::{collections::btree_map::BTreeMap, marker::PhantomData};
3
4use slog::{self, o};
5
6use oasis_core_runtime::{
7    common::{logger::get_logger, namespace::Namespace},
8    consensus,
9    consensus::roothash,
10    protocol::HostInfo,
11};
12
13use crate::{
14    history,
15    keymanager::KeyManager,
16    module::MethodHandler as _,
17    runtime,
18    state::{self, CurrentState},
19};
20
21/// Local configuration key the value of which determines whether expensive queries should be
22/// allowed or not, and also whether smart contracts should be simulated for `core.EstimateGas`.
23/// DEPRECATED and superseded by LOCAL_CONFIG_ESTIMATE_GAS_BY_SIMULATING_CONTRACTS and LOCAL_CONFIG_ALLOWED_QUERIES.
24const LOCAL_CONFIG_ALLOW_EXPENSIVE_QUERIES: &str = "allow_expensive_queries";
25/// Local configuration key the value of which determines whether smart contracts should
26/// be simulated when estimating gas in `core.EstimateGas`.
27const LOCAL_CONFIG_ESTIMATE_GAS_BY_SIMULATING_CONTRACTS: &str =
28    "estimate_gas_by_simulating_contracts";
29/// Local configuration key the value of which determines the set of allowed queries.
30const LOCAL_CONFIG_ALLOWED_QUERIES: &str = "allowed_queries";
31/// Special key inside the `allowed_queries` list; represents the set of all queries.
32const LOCAL_CONFIG_ALLOWED_QUERIES_ALL: &str = "all";
33/// Special key inside the `allowed_queries` list; represents the set of all queries
34/// that are tagged `expensive`.
35const LOCAL_CONFIG_ALLOWED_QUERIES_ALL_EXPENSIVE: &str = "all_expensive";
36
37/// Runtime SDK context.
38pub trait Context {
39    /// Runtime that the context is being invoked in.
40    type Runtime: runtime::Runtime;
41
42    /// Clone this context.
43    fn clone(&self) -> Self;
44
45    /// Returns a logger.
46    fn get_logger(&self, module: &'static str) -> slog::Logger;
47
48    /// Whether smart contracts should be executed in this context.
49    fn should_execute_contracts(&self) -> bool {
50        match CurrentState::with_env(|env| env.mode()) {
51            // When actually executing a transaction, we always run contracts.
52            state::Mode::Execute => true,
53            state::Mode::Simulate => {
54                // Backwards compatibility for the deprecated `allow_expensive_queries`.
55                if let Some(allow_expensive_queries) =
56                    self.local_config::<bool>(LOCAL_CONFIG_ALLOW_EXPENSIVE_QUERIES)
57                {
58                    slog::warn!(
59                        self.get_logger("runtime-sdk"),
60                        "The {} config option is DEPRECATED since April 2022 and will be removed in a future release. Use {} and {} instead.",
61                        LOCAL_CONFIG_ALLOW_EXPENSIVE_QUERIES,
62                        LOCAL_CONFIG_ESTIMATE_GAS_BY_SIMULATING_CONTRACTS,
63                        LOCAL_CONFIG_ALLOWED_QUERIES
64                    );
65                    return allow_expensive_queries;
66                };
67
68                // The non-deprecated config option.
69                self.local_config(LOCAL_CONFIG_ESTIMATE_GAS_BY_SIMULATING_CONTRACTS)
70                    .unwrap_or_default()
71            }
72            // When just checking a transaction, we always want to be fast and skip contracts.
73            state::Mode::Check | state::Mode::PreSchedule => false,
74        }
75    }
76
77    /// Whether `method` is an allowed query per policy in the local config.
78    fn is_allowed_query<R: crate::runtime::Runtime>(&self, method: &str) -> bool {
79        let config: Vec<BTreeMap<String, bool>> = self
80            .local_config(LOCAL_CONFIG_ALLOWED_QUERIES)
81            .unwrap_or_default();
82        let is_expensive = R::Modules::is_expensive_query(method);
83
84        // Backwards compatibility for the deprecated `allow_expensive_queries`.
85        if let Some(allow_expensive_queries) =
86            self.local_config::<bool>(LOCAL_CONFIG_ALLOW_EXPENSIVE_QUERIES)
87        {
88            slog::warn!(
89                self.get_logger("runtime-sdk"),
90                "The {} config option is DEPRECATED since April 2022 and will be removed in a future release. Use {} and {} instead.",
91                LOCAL_CONFIG_ALLOW_EXPENSIVE_QUERIES,
92                LOCAL_CONFIG_ESTIMATE_GAS_BY_SIMULATING_CONTRACTS,
93                LOCAL_CONFIG_ALLOWED_QUERIES
94            );
95            return (!is_expensive) || allow_expensive_queries;
96        };
97
98        // The non-deprecated config option.
99        config
100            .iter()
101            .find_map(|item| {
102                item.get(method)
103                    .or_else(|| {
104                        if !is_expensive {
105                            return None;
106                        }
107                        item.get(LOCAL_CONFIG_ALLOWED_QUERIES_ALL_EXPENSIVE)
108                    })
109                    .or_else(|| item.get(LOCAL_CONFIG_ALLOWED_QUERIES_ALL))
110                    .copied()
111            })
112            // If no config entry matches, the default is to allow only non-expensive queries.
113            .unwrap_or(!is_expensive)
114    }
115
116    /// Returns node operator-provided local configuration.
117    ///
118    /// This method will always return `None` in `Mode::ExecuteTx` contexts.
119    fn local_config<T>(&self, key: &str) -> Option<T>
120    where
121        T: cbor::Decode,
122    {
123        if CurrentState::with_env(|env| env.is_execute()) {
124            return None;
125        }
126
127        self.host_info().local_config.get(key).and_then(|v| {
128            cbor::from_value(v.clone()).unwrap_or_else(|e| {
129                let msg = format!(
130                    "Cannot interpret the value of \"{}\" in runtime's local config as a {}: {:?}",
131                    key,
132                    std::any::type_name::<T>(),
133                    e
134                );
135                slog::error!(self.get_logger("runtime-sdk"), "{}", msg);
136                panic!("{}", msg);
137            })
138        })
139    }
140
141    /// Information about the host environment.
142    fn host_info(&self) -> &HostInfo;
143
144    /// Runtime ID.
145    fn runtime_id(&self) -> &Namespace {
146        &self.host_info().runtime_id
147    }
148
149    /// The key manager, if the runtime is confidential.
150    fn key_manager(&self) -> Option<&dyn KeyManager>;
151
152    /// Whether the context has a key manager available (e.g. the runtime is confidential).
153    fn is_confidential(&self) -> bool {
154        self.key_manager().is_some()
155    }
156
157    /// Last runtime block header.
158    fn runtime_header(&self) -> &roothash::Header;
159
160    /// Results of executing the last successful runtime round.
161    fn runtime_round_results(&self) -> &roothash::RoundResults;
162
163    /// Consensus state.
164    fn consensus_state(&self) -> &consensus::state::ConsensusState;
165
166    /// Historical state.
167    fn history(&self) -> &dyn history::HistoryHost;
168
169    /// Current epoch.
170    fn epoch(&self) -> consensus::beacon::EpochTime;
171
172    /// Maximum number of consensus messages that the runtime can emit in this block.
173    fn max_messages(&self) -> u32;
174
175    /// UNIX timestamp of the current block.
176    fn now(&self) -> u64 {
177        self.runtime_header().timestamp
178    }
179}
180
181/// Dispatch context for the whole batch.
182pub struct RuntimeBatchContext<'a, R: runtime::Runtime> {
183    host_info: &'a HostInfo,
184    key_manager: Option<Box<dyn KeyManager>>,
185    runtime_header: &'a roothash::Header,
186    runtime_round_results: &'a roothash::RoundResults,
187    consensus_state: &'a consensus::state::ConsensusState,
188    history: &'a dyn history::HistoryHost,
189    epoch: consensus::beacon::EpochTime,
190    logger: slog::Logger,
191
192    /// Maximum number of messages that can be emitted.
193    max_messages: u32,
194
195    _runtime: PhantomData<R>,
196}
197
198impl<'a, R: runtime::Runtime> RuntimeBatchContext<'a, R> {
199    /// Create a new dispatch context.
200    #[allow(clippy::too_many_arguments)]
201    pub fn new(
202        host_info: &'a HostInfo,
203        key_manager: Option<Box<dyn KeyManager>>,
204        runtime_header: &'a roothash::Header,
205        runtime_round_results: &'a roothash::RoundResults,
206        consensus_state: &'a consensus::state::ConsensusState,
207        history: &'a dyn history::HistoryHost,
208        epoch: consensus::beacon::EpochTime,
209        max_messages: u32,
210    ) -> Self {
211        Self {
212            host_info,
213            runtime_header,
214            runtime_round_results,
215            consensus_state,
216            history,
217            epoch,
218            key_manager,
219            logger: get_logger("runtime-sdk"),
220            max_messages,
221            _runtime: PhantomData,
222        }
223    }
224}
225
226impl<R: runtime::Runtime> Context for RuntimeBatchContext<'_, R> {
227    type Runtime = R;
228
229    fn clone(&self) -> Self {
230        Self {
231            host_info: self.host_info,
232            runtime_header: self.runtime_header,
233            runtime_round_results: self.runtime_round_results,
234            consensus_state: self.consensus_state,
235            history: self.history,
236            epoch: self.epoch,
237            key_manager: self.key_manager.clone(),
238            logger: get_logger("runtime-sdk"),
239            max_messages: self.max_messages,
240            _runtime: PhantomData,
241        }
242    }
243
244    fn get_logger(&self, module: &'static str) -> slog::Logger {
245        self.logger.new(o!("sdk_module" => module))
246    }
247
248    fn host_info(&self) -> &HostInfo {
249        self.host_info
250    }
251
252    fn key_manager(&self) -> Option<&dyn KeyManager> {
253        self.key_manager.as_ref().map(Box::as_ref)
254    }
255
256    fn runtime_header(&self) -> &roothash::Header {
257        self.runtime_header
258    }
259
260    fn runtime_round_results(&self) -> &roothash::RoundResults {
261        self.runtime_round_results
262    }
263
264    fn consensus_state(&self) -> &consensus::state::ConsensusState {
265        self.consensus_state
266    }
267
268    fn history(&self) -> &dyn history::HistoryHost {
269        self.history
270    }
271
272    fn epoch(&self) -> consensus::beacon::EpochTime {
273        self.epoch
274    }
275
276    fn max_messages(&self) -> u32 {
277        self.max_messages
278    }
279}