linera_execution/
lib.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module manages the execution of the system application and the user applications in a
5//! Linera chain.
6
7#![deny(clippy::large_futures)]
8
9pub mod committee;
10pub mod evm;
11mod execution;
12mod execution_state_actor;
13#[cfg(with_graphql)]
14mod graphql;
15mod policy;
16mod resources;
17mod runtime;
18pub mod system;
19#[cfg(with_testing)]
20pub mod test_utils;
21mod transaction_tracker;
22mod util;
23mod wasm;
24
25use std::{any::Any, fmt, str::FromStr, sync::Arc};
26
27use async_graphql::SimpleObject;
28use async_trait::async_trait;
29use custom_debug_derive::Debug;
30use dashmap::DashMap;
31use derive_more::Display;
32#[cfg(web)]
33use js_sys::wasm_bindgen::JsValue;
34use linera_base::{
35    abi::Abi,
36    crypto::{BcsHashable, CryptoHash},
37    data_types::{
38        Amount, ApplicationDescription, ApplicationPermissions, ArithmeticError, Blob, BlockHeight,
39        DecompressionError, Epoch, NetworkDescription, SendMessageRequest, StreamUpdate, Timestamp,
40    },
41    doc_scalar, hex_debug, http,
42    identifiers::{
43        Account, AccountOwner, ApplicationId, BlobId, BlobType, ChainId, EventId,
44        GenericApplicationId, MessageId, ModuleId, StreamName,
45    },
46    ownership::ChainOwnership,
47    task,
48    vm::VmRuntime,
49};
50use linera_views::{batch::Batch, ViewError};
51use serde::{Deserialize, Serialize};
52use system::AdminOperation;
53use thiserror::Error;
54
55#[cfg(with_revm)]
56use crate::evm::EvmExecutionError;
57use crate::runtime::ContractSyncRuntime;
58#[cfg(with_testing)]
59use crate::test_utils::dummy_chain_description;
60#[cfg(all(with_testing, with_wasm_runtime))]
61pub use crate::wasm::test as wasm_test;
62#[cfg(with_wasm_runtime)]
63pub use crate::wasm::{
64    BaseRuntimeApi, ContractEntrypoints, ContractRuntimeApi, RuntimeApiData, ServiceEntrypoints,
65    ServiceRuntimeApi, WasmContractModule, WasmExecutionError, WasmServiceModule,
66};
67pub use crate::{
68    execution::{ExecutionStateView, ServiceRuntimeEndpoint},
69    execution_state_actor::ExecutionRequest,
70    policy::ResourceControlPolicy,
71    resources::{BalanceHolder, ResourceController, ResourceTracker},
72    runtime::{
73        ContractSyncRuntimeHandle, ServiceRuntimeRequest, ServiceSyncRuntime,
74        ServiceSyncRuntimeHandle,
75    },
76    system::{
77        SystemExecutionStateView, SystemMessage, SystemOperation, SystemQuery, SystemResponse,
78    },
79    transaction_tracker::{TransactionOutcome, TransactionTracker},
80};
81
82/// The `Linera.sol` library code to be included in solidity smart
83/// contracts using Linera features.
84pub const LINERA_SOL: &str = include_str!("../solidity/Linera.sol");
85pub const LINERA_TYPES_SOL: &str = include_str!("../solidity/LineraTypes.sol");
86
87/// The maximum length of a stream name.
88const MAX_STREAM_NAME_LEN: usize = 64;
89
90/// An implementation of [`UserContractModule`].
91#[derive(Clone)]
92pub struct UserContractCode(Box<dyn UserContractModule>);
93
94/// An implementation of [`UserServiceModule`].
95#[derive(Clone)]
96pub struct UserServiceCode(Box<dyn UserServiceModule>);
97
98/// An implementation of [`UserContract`].
99pub type UserContractInstance = Box<dyn UserContract>;
100
101/// An implementation of [`UserService`].
102pub type UserServiceInstance = Box<dyn UserService>;
103
104/// A factory trait to obtain a [`UserContract`] from a [`UserContractModule`]
105pub trait UserContractModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
106    fn instantiate(
107        &self,
108        runtime: ContractSyncRuntimeHandle,
109    ) -> Result<UserContractInstance, ExecutionError>;
110}
111
112impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
113    fn from(module: T) -> Self {
114        Self(Box::new(module))
115    }
116}
117
118dyn_clone::clone_trait_object!(UserContractModule);
119
120/// A factory trait to obtain a [`UserService`] from a [`UserServiceModule`]
121pub trait UserServiceModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
122    fn instantiate(
123        &self,
124        runtime: ServiceSyncRuntimeHandle,
125    ) -> Result<UserServiceInstance, ExecutionError>;
126}
127
128impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
129    fn from(module: T) -> Self {
130        Self(Box::new(module))
131    }
132}
133
134dyn_clone::clone_trait_object!(UserServiceModule);
135
136impl UserServiceCode {
137    fn instantiate(
138        &self,
139        runtime: ServiceSyncRuntimeHandle,
140    ) -> Result<UserServiceInstance, ExecutionError> {
141        self.0.instantiate(runtime)
142    }
143}
144
145impl UserContractCode {
146    fn instantiate(
147        &self,
148        runtime: ContractSyncRuntimeHandle,
149    ) -> Result<UserContractInstance, ExecutionError> {
150        self.0.instantiate(runtime)
151    }
152}
153
154#[cfg(web)]
155const _: () = {
156    // TODO(#2775): add a vtable pointer into the JsValue rather than assuming the
157    // implementor
158
159    impl From<UserContractCode> for JsValue {
160        fn from(code: UserContractCode) -> JsValue {
161            let module: WasmContractModule = *(code.0 as Box<dyn Any>)
162                .downcast()
163                .expect("we only support Wasm modules on the Web for now");
164            module.into()
165        }
166    }
167
168    impl From<UserServiceCode> for JsValue {
169        fn from(code: UserServiceCode) -> JsValue {
170            let module: WasmServiceModule = *(code.0 as Box<dyn Any>)
171                .downcast()
172                .expect("we only support Wasm modules on the Web for now");
173            module.into()
174        }
175    }
176
177    impl TryFrom<JsValue> for UserContractCode {
178        type Error = JsValue;
179        fn try_from(value: JsValue) -> Result<Self, JsValue> {
180            WasmContractModule::try_from(value).map(Into::into)
181        }
182    }
183
184    impl TryFrom<JsValue> for UserServiceCode {
185        type Error = JsValue;
186        fn try_from(value: JsValue) -> Result<Self, JsValue> {
187            WasmServiceModule::try_from(value).map(Into::into)
188        }
189    }
190};
191
192/// A type for errors happening during execution.
193#[derive(Error, Debug)]
194pub enum ExecutionError {
195    #[error(transparent)]
196    ViewError(#[from] ViewError),
197    #[error(transparent)]
198    ArithmeticError(#[from] ArithmeticError),
199    #[error("User application reported an error: {0}")]
200    UserError(String),
201    #[cfg(with_wasm_runtime)]
202    #[error(transparent)]
203    WasmError(#[from] WasmExecutionError),
204    #[cfg(with_revm)]
205    #[error(transparent)]
206    EvmError(#[from] EvmExecutionError),
207    #[error(transparent)]
208    DecompressionError(#[from] DecompressionError),
209    #[error("The given promise is invalid or was polled once already")]
210    InvalidPromise,
211
212    #[error("Attempted to perform a reentrant call to application {0}")]
213    ReentrantCall(ApplicationId),
214    #[error(
215        "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
216        from `finalize`"
217    )]
218    CrossApplicationCallInFinalize {
219        caller_id: Box<ApplicationId>,
220        callee_id: Box<ApplicationId>,
221    },
222    #[error("Attempt to write to storage from a contract")]
223    ServiceWriteAttempt,
224    #[error("Failed to load bytecode from storage {0:?}")]
225    ApplicationBytecodeNotFound(Box<ApplicationDescription>),
226    // TODO(#2927): support dynamic loading of modules on the Web
227    #[error("Unsupported dynamic application load: {0:?}")]
228    UnsupportedDynamicApplicationLoad(Box<ApplicationId>),
229
230    #[error("Excessive number of bytes read from storage")]
231    ExcessiveRead,
232    #[error("Excessive number of bytes written to storage")]
233    ExcessiveWrite,
234    #[error("Block execution required too much fuel for VM {0}")]
235    MaximumFuelExceeded(VmRuntime),
236    #[error("Services running as oracles in block took longer than allowed")]
237    MaximumServiceOracleExecutionTimeExceeded,
238    #[error("Service running as an oracle produced a response that's too large")]
239    ServiceOracleResponseTooLarge,
240    #[error("Serialized size of the block exceeds limit")]
241    BlockTooLarge,
242    #[error("HTTP response exceeds the size limit of {limit} bytes, having at least {size} bytes")]
243    HttpResponseSizeLimitExceeded { limit: u64, size: u64 },
244    #[error("Runtime failed to respond to application")]
245    MissingRuntimeResponse,
246    #[error("Module ID {0:?} is invalid")]
247    InvalidModuleId(ModuleId),
248    #[error("AccountOwner is None")]
249    OwnerIsNone,
250    #[error("Application is not authorized to perform system operations on this chain: {0:}")]
251    UnauthorizedApplication(ApplicationId),
252    #[error("Failed to make network reqwest: {0}")]
253    ReqwestError(#[from] reqwest::Error),
254    #[error("Encountered I/O error: {0}")]
255    IoError(#[from] std::io::Error),
256    #[error("More recorded oracle responses than expected")]
257    UnexpectedOracleResponse,
258    #[error("Invalid JSON: {0}")]
259    JsonError(#[from] serde_json::Error),
260    #[error(transparent)]
261    BcsError(#[from] bcs::Error),
262    #[error("Recorded response for oracle query has the wrong type")]
263    OracleResponseMismatch,
264    #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
265    AssertBefore {
266        timestamp: Timestamp,
267        local_time: Timestamp,
268    },
269
270    #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
271    StreamNameTooLong,
272    #[error("Blob exceeds size limit")]
273    BlobTooLarge,
274    #[error("Bytecode exceeds size limit")]
275    BytecodeTooLarge,
276    #[error("Attempt to perform an HTTP request to an unauthorized host: {0:?}")]
277    UnauthorizedHttpRequest(reqwest::Url),
278    #[error("Attempt to perform an HTTP request to an invalid URL")]
279    InvalidUrlForHttpRequest(#[from] url::ParseError),
280    #[error("Failed to send contract code to worker thread: {0:?}")]
281    ContractModuleSend(#[from] linera_base::task::SendError<UserContractCode>),
282    #[error("Failed to send service code to worker thread: {0:?}")]
283    ServiceModuleSend(#[from] linera_base::task::SendError<UserServiceCode>),
284    #[error("The chain being queried is not active {0}")]
285    InactiveChain(ChainId),
286    #[error("Blobs not found: {0:?}")]
287    BlobsNotFound(Vec<BlobId>),
288    #[error("Events not found: {0:?}")]
289    EventsNotFound(Vec<EventId>),
290
291    #[error("Invalid HTTP header name used for HTTP request")]
292    InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
293    #[error("Invalid HTTP header value used for HTTP request")]
294    InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
295
296    #[error("No NetworkDescription found in storage")]
297    NoNetworkDescriptionFound,
298    #[error("Invalid committees")]
299    InvalidCommittees,
300    #[error("{epoch:?} is not recognized by chain {chain_id:}")]
301    InvalidEpoch { chain_id: ChainId, epoch: Epoch },
302    #[error("Transfer must have positive amount")]
303    IncorrectTransferAmount,
304    #[error("Transfer from owned account must be authenticated by the right signer")]
305    UnauthenticatedTransferOwner,
306    #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
307    InsufficientBalance {
308        balance: Amount,
309        account: AccountOwner,
310    },
311    #[error("Required execution fees exceeded the total funding available. Fees {fees}, available balance: {balance}")]
312    FeesExceedFunding { fees: Amount, balance: Amount },
313    #[error("Claim must have positive amount")]
314    IncorrectClaimAmount,
315    #[error("Claim must be authenticated by the right signer")]
316    UnauthenticatedClaimOwner,
317    #[error("Admin operations are only allowed on the admin chain.")]
318    AdminOperationOnNonAdminChain,
319    #[error("Failed to create new committee: expected {expected}, but got {provided}")]
320    InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
321    #[error("Failed to remove committee")]
322    InvalidCommitteeRemoval,
323    #[error("Amount overflow")]
324    AmountOverflow,
325    #[error("Amount underflow")]
326    AmountUnderflow,
327    #[error("Chain balance overflow")]
328    BalanceOverflow,
329    #[error("Chain balance underflow")]
330    BalanceUnderflow,
331    #[error("Cannot decrease the chain's timestamp")]
332    TicksOutOfOrder,
333    #[error("Application {0:?} is not registered by the chain")]
334    UnknownApplicationId(Box<ApplicationId>),
335    #[error("No recorded response for oracle query")]
336    MissingOracleResponse,
337    #[error("process_streams was not called for all stream updates")]
338    UnprocessedStreams,
339    #[error("Internal error: {0}")]
340    InternalError(&'static str),
341    #[error("UpdateStreams contains an unknown event")]
342    EventNotFound(EventId),
343    #[error("UpdateStreams is outdated")]
344    OutdatedUpdateStreams,
345}
346
347/// The public entry points provided by the contract part of an application.
348pub trait UserContract {
349    /// Instantiate the application state on the chain that owns the application.
350    fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
351
352    /// Applies an operation from the current block.
353    fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
354
355    /// Applies a message originating from a cross-chain message.
356    fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
357
358    /// Reacts to new events on streams this application subscribes to.
359    fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
360
361    /// Finishes execution of the current transaction.
362    fn finalize(&mut self) -> Result<(), ExecutionError>;
363}
364
365/// The public entry points provided by the service part of an application.
366pub trait UserService {
367    /// Executes unmetered read-only queries on the state of this application.
368    fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
369}
370
371/// Configuration options for the execution runtime available to applications.
372#[derive(Clone, Copy, Default)]
373pub struct ExecutionRuntimeConfig {}
374
375/// Requirements for the `extra` field in our state views (and notably the
376/// [`ExecutionStateView`]).
377#[cfg_attr(not(web), async_trait)]
378#[cfg_attr(web, async_trait(?Send))]
379pub trait ExecutionRuntimeContext {
380    fn chain_id(&self) -> ChainId;
381
382    fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
383
384    fn user_contracts(&self) -> &Arc<DashMap<ApplicationId, UserContractCode>>;
385
386    fn user_services(&self) -> &Arc<DashMap<ApplicationId, UserServiceCode>>;
387
388    async fn get_user_contract(
389        &self,
390        description: &ApplicationDescription,
391    ) -> Result<UserContractCode, ExecutionError>;
392
393    async fn get_user_service(
394        &self,
395        description: &ApplicationDescription,
396    ) -> Result<UserServiceCode, ExecutionError>;
397
398    async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError>;
399
400    async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError>;
401
402    async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
403
404    async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
405
406    async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
407
408    #[cfg(with_testing)]
409    async fn add_blobs(
410        &self,
411        blobs: impl IntoIterator<Item = Blob> + Send,
412    ) -> Result<(), ViewError>;
413
414    #[cfg(with_testing)]
415    async fn add_events(
416        &self,
417        events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
418    ) -> Result<(), ViewError>;
419}
420
421#[derive(Clone, Copy, Debug)]
422pub struct OperationContext {
423    /// The current chain ID.
424    pub chain_id: ChainId,
425    /// The authenticated signer of the operation, if any.
426    #[debug(skip_if = Option::is_none)]
427    pub authenticated_signer: Option<AccountOwner>,
428    /// `None` if this is the transaction entrypoint or the caller doesn't want this particular
429    /// call to be authenticated (e.g. for safety reasons).
430    #[debug(skip_if = Option::is_none)]
431    pub authenticated_caller_id: Option<ApplicationId>,
432    /// The current block height.
433    pub height: BlockHeight,
434    /// The consensus round number, if this is a block that gets validated in a multi-leader round.
435    pub round: Option<u32>,
436    /// The timestamp of the block containing the operation.
437    pub timestamp: Timestamp,
438}
439
440#[derive(Clone, Copy, Debug)]
441pub struct MessageContext {
442    /// The current chain ID.
443    pub chain_id: ChainId,
444    /// Whether the message was rejected by the original receiver and is now bouncing back.
445    pub is_bouncing: bool,
446    /// The authenticated signer of the operation that created the message, if any.
447    #[debug(skip_if = Option::is_none)]
448    pub authenticated_signer: Option<AccountOwner>,
449    /// Where to send a refund for the unused part of each grant after execution, if any.
450    #[debug(skip_if = Option::is_none)]
451    pub refund_grant_to: Option<Account>,
452    /// The current block height.
453    pub height: BlockHeight,
454    /// The consensus round number, if this is a block that gets validated in a multi-leader round.
455    pub round: Option<u32>,
456    /// The timestamp of the block executing the message.
457    pub timestamp: Timestamp,
458    /// The ID of the message (based on the operation height and index in the remote
459    /// certificate).
460    pub message_id: MessageId,
461}
462
463#[derive(Clone, Copy, Debug)]
464pub struct ProcessStreamsContext {
465    /// The current chain ID.
466    pub chain_id: ChainId,
467    /// The current block height.
468    pub height: BlockHeight,
469    /// The consensus round number, if this is a block that gets validated in a multi-leader round.
470    pub round: Option<u32>,
471    /// The timestamp of the current block.
472    pub timestamp: Timestamp,
473}
474
475impl From<MessageContext> for ProcessStreamsContext {
476    fn from(context: MessageContext) -> Self {
477        Self {
478            chain_id: context.chain_id,
479            height: context.height,
480            round: context.round,
481            timestamp: context.timestamp,
482        }
483    }
484}
485
486impl From<OperationContext> for ProcessStreamsContext {
487    fn from(context: OperationContext) -> Self {
488        Self {
489            chain_id: context.chain_id,
490            height: context.height,
491            round: context.round,
492            timestamp: context.timestamp,
493        }
494    }
495}
496
497#[derive(Clone, Copy, Debug)]
498pub struct FinalizeContext {
499    /// The current chain ID.
500    pub chain_id: ChainId,
501    /// The authenticated signer of the operation, if any.
502    #[debug(skip_if = Option::is_none)]
503    pub authenticated_signer: Option<AccountOwner>,
504    /// The current block height.
505    pub height: BlockHeight,
506    /// The consensus round number, if this is a block that gets validated in a multi-leader round.
507    pub round: Option<u32>,
508}
509
510#[derive(Clone, Copy, Debug, Eq, PartialEq)]
511pub struct QueryContext {
512    /// The current chain ID.
513    pub chain_id: ChainId,
514    /// The height of the next block on this chain.
515    pub next_block_height: BlockHeight,
516    /// The local time in the node executing the query.
517    pub local_time: Timestamp,
518}
519
520pub trait BaseRuntime {
521    type Read: fmt::Debug + Send + Sync;
522    type ContainsKey: fmt::Debug + Send + Sync;
523    type ContainsKeys: fmt::Debug + Send + Sync;
524    type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
525    type ReadValueBytes: fmt::Debug + Send + Sync;
526    type FindKeysByPrefix: fmt::Debug + Send + Sync;
527    type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
528
529    /// The current chain ID.
530    fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
531
532    /// The current block height.
533    fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
534
535    /// The current application ID.
536    fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
537
538    /// The current application creator's chain ID.
539    fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
540
541    /// The current application parameters.
542    fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
543
544    /// Reads the system timestamp.
545    fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
546
547    /// Reads the balance of the chain.
548    fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
549
550    /// Reads the owner balance.
551    fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
552
553    /// Reads the balances from all owners.
554    fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
555
556    /// Reads balance owners.
557    fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
558
559    /// Reads the current ownership configuration for this chain.
560    fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
561
562    /// Tests whether a key exists in the key-value store
563    #[cfg(feature = "test")]
564    fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
565        let promise = self.contains_key_new(key)?;
566        self.contains_key_wait(&promise)
567    }
568
569    /// Creates the promise to test whether a key exists in the key-value store
570    fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
571
572    /// Resolves the promise to test whether a key exists in the key-value store
573    fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
574
575    /// Tests whether multiple keys exist in the key-value store
576    #[cfg(feature = "test")]
577    fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
578        let promise = self.contains_keys_new(keys)?;
579        self.contains_keys_wait(&promise)
580    }
581
582    /// Creates the promise to test whether multiple keys exist in the key-value store
583    fn contains_keys_new(
584        &mut self,
585        keys: Vec<Vec<u8>>,
586    ) -> Result<Self::ContainsKeys, ExecutionError>;
587
588    /// Resolves the promise to test whether multiple keys exist in the key-value store
589    fn contains_keys_wait(
590        &mut self,
591        promise: &Self::ContainsKeys,
592    ) -> Result<Vec<bool>, ExecutionError>;
593
594    /// Reads several keys from the key-value store
595    #[cfg(feature = "test")]
596    fn read_multi_values_bytes(
597        &mut self,
598        keys: Vec<Vec<u8>>,
599    ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
600        let promise = self.read_multi_values_bytes_new(keys)?;
601        self.read_multi_values_bytes_wait(&promise)
602    }
603
604    /// Creates the promise to access several keys from the key-value store
605    fn read_multi_values_bytes_new(
606        &mut self,
607        keys: Vec<Vec<u8>>,
608    ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
609
610    /// Resolves the promise to access several keys from the key-value store
611    fn read_multi_values_bytes_wait(
612        &mut self,
613        promise: &Self::ReadMultiValuesBytes,
614    ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
615
616    /// Reads the key from the key-value store
617    #[cfg(feature = "test")]
618    fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
619        let promise = self.read_value_bytes_new(key)?;
620        self.read_value_bytes_wait(&promise)
621    }
622
623    /// Creates the promise to access a key from the key-value store
624    fn read_value_bytes_new(
625        &mut self,
626        key: Vec<u8>,
627    ) -> Result<Self::ReadValueBytes, ExecutionError>;
628
629    /// Resolves the promise to access a key from the key-value store
630    fn read_value_bytes_wait(
631        &mut self,
632        promise: &Self::ReadValueBytes,
633    ) -> Result<Option<Vec<u8>>, ExecutionError>;
634
635    /// Creates the promise to access keys having a specific prefix
636    fn find_keys_by_prefix_new(
637        &mut self,
638        key_prefix: Vec<u8>,
639    ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
640
641    /// Resolves the promise to access keys having a specific prefix
642    fn find_keys_by_prefix_wait(
643        &mut self,
644        promise: &Self::FindKeysByPrefix,
645    ) -> Result<Vec<Vec<u8>>, ExecutionError>;
646
647    /// Reads the data from the key/values having a specific prefix.
648    #[cfg(feature = "test")]
649    #[expect(clippy::type_complexity)]
650    fn find_key_values_by_prefix(
651        &mut self,
652        key_prefix: Vec<u8>,
653    ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
654        let promise = self.find_key_values_by_prefix_new(key_prefix)?;
655        self.find_key_values_by_prefix_wait(&promise)
656    }
657
658    /// Creates the promise to access key/values having a specific prefix
659    fn find_key_values_by_prefix_new(
660        &mut self,
661        key_prefix: Vec<u8>,
662    ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
663
664    /// Resolves the promise to access key/values having a specific prefix
665    #[expect(clippy::type_complexity)]
666    fn find_key_values_by_prefix_wait(
667        &mut self,
668        promise: &Self::FindKeyValuesByPrefix,
669    ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
670
671    /// Makes an HTTP request to the given URL and returns the answer, if any.
672    fn perform_http_request(
673        &mut self,
674        request: http::Request,
675    ) -> Result<http::Response, ExecutionError>;
676
677    /// Ensures that the current time at block validation is `< timestamp`. Note that block
678    /// validation happens at or after the block timestamp, but isn't necessarily the same.
679    ///
680    /// Cannot be used in fast blocks: A block using this call should be proposed by a regular
681    /// owner, not a super owner.
682    fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
683
684    /// Reads a data blob specified by a given hash.
685    fn read_data_blob(&mut self, hash: &CryptoHash) -> Result<Vec<u8>, ExecutionError>;
686
687    /// Asserts the existence of a data blob with the given hash.
688    fn assert_data_blob_exists(&mut self, hash: &CryptoHash) -> Result<(), ExecutionError>;
689}
690
691pub trait ServiceRuntime: BaseRuntime {
692    /// Queries another application.
693    fn try_query_application(
694        &mut self,
695        queried_id: ApplicationId,
696        argument: Vec<u8>,
697    ) -> Result<Vec<u8>, ExecutionError>;
698
699    /// Schedules an operation to be included in the block proposed after execution.
700    fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
701
702    /// Checks if the service has exceeded its execution time limit.
703    fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
704}
705
706pub trait ContractRuntime: BaseRuntime {
707    /// The authenticated signer for this execution, if there is one.
708    fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
709
710    /// The current message ID, if there is one.
711    fn message_id(&mut self) -> Result<Option<MessageId>, ExecutionError>;
712
713    /// If the current message (if there is one) was rejected by its destination and is now
714    /// bouncing back.
715    fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
716
717    /// The optional authenticated caller application ID, if it was provided and if there is one
718    /// based on the execution context.
719    fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
720
721    /// Returns the maximum gas fuel per block.
722    fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
723
724    /// Returns the amount of execution fuel remaining before execution is aborted.
725    fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
726
727    /// Consumes some of the execution fuel.
728    fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
729
730    /// Schedules a message to be sent.
731    fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
732
733    /// Transfers amount from source to destination.
734    fn transfer(
735        &mut self,
736        source: AccountOwner,
737        destination: Account,
738        amount: Amount,
739    ) -> Result<(), ExecutionError>;
740
741    /// Claims amount from source to destination.
742    fn claim(
743        &mut self,
744        source: Account,
745        destination: Account,
746        amount: Amount,
747    ) -> Result<(), ExecutionError>;
748
749    /// Calls another application. Forwarded sessions will now be visible to
750    /// `callee_id` (but not to the caller any more).
751    fn try_call_application(
752        &mut self,
753        authenticated: bool,
754        callee_id: ApplicationId,
755        argument: Vec<u8>,
756    ) -> Result<Vec<u8>, ExecutionError>;
757
758    /// Adds a new item to an event stream. Returns the new event's index in the stream.
759    fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
760
761    /// Reads an event from a stream. Returns the event's value.
762    ///
763    /// Returns an error if the event doesn't exist.
764    fn read_event(
765        &mut self,
766        chain_id: ChainId,
767        stream_name: StreamName,
768        index: u32,
769    ) -> Result<Vec<u8>, ExecutionError>;
770
771    /// Subscribes this application to an event stream.
772    fn subscribe_to_events(
773        &mut self,
774        chain_id: ChainId,
775        application_id: ApplicationId,
776        stream_name: StreamName,
777    ) -> Result<(), ExecutionError>;
778
779    /// Unsubscribes this application from an event stream.
780    fn unsubscribe_from_events(
781        &mut self,
782        chain_id: ChainId,
783        application_id: ApplicationId,
784        stream_name: StreamName,
785    ) -> Result<(), ExecutionError>;
786
787    /// Queries a service.
788    fn query_service(
789        &mut self,
790        application_id: ApplicationId,
791        query: Vec<u8>,
792    ) -> Result<Vec<u8>, ExecutionError>;
793
794    /// Opens a new chain.
795    fn open_chain(
796        &mut self,
797        ownership: ChainOwnership,
798        application_permissions: ApplicationPermissions,
799        balance: Amount,
800    ) -> Result<ChainId, ExecutionError>;
801
802    /// Closes the current chain.
803    fn close_chain(&mut self) -> Result<(), ExecutionError>;
804
805    /// Changes the application permissions on the current chain.
806    fn change_application_permissions(
807        &mut self,
808        application_permissions: ApplicationPermissions,
809    ) -> Result<(), ExecutionError>;
810
811    /// Creates a new application on chain.
812    fn create_application(
813        &mut self,
814        module_id: ModuleId,
815        parameters: Vec<u8>,
816        argument: Vec<u8>,
817        required_application_ids: Vec<ApplicationId>,
818    ) -> Result<ApplicationId, ExecutionError>;
819
820    /// Returns the round in which this block was validated.
821    fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
822
823    /// Writes a batch of changes.
824    fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
825}
826
827/// An operation to be executed in a block.
828#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
829pub enum Operation {
830    /// A system operation.
831    System(Box<SystemOperation>),
832    /// A user operation (in serialized form).
833    User {
834        application_id: ApplicationId,
835        #[serde(with = "serde_bytes")]
836        #[debug(with = "hex_debug")]
837        bytes: Vec<u8>,
838    },
839}
840
841impl BcsHashable<'_> for Operation {}
842
843/// A message to be sent and possibly executed in the receiver's block.
844#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
845pub enum Message {
846    /// A system message.
847    System(SystemMessage),
848    /// A user message (in serialized form).
849    User {
850        application_id: ApplicationId,
851        #[serde(with = "serde_bytes")]
852        #[debug(with = "hex_debug")]
853        bytes: Vec<u8>,
854    },
855}
856
857/// An query to be sent and possibly executed in the receiver's block.
858#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
859pub enum Query {
860    /// A system query.
861    System(SystemQuery),
862    /// A user query (in serialized form).
863    User {
864        application_id: ApplicationId,
865        #[serde(with = "serde_bytes")]
866        #[debug(with = "hex_debug")]
867        bytes: Vec<u8>,
868    },
869}
870
871/// The outcome of the execution of a query.
872#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
873pub struct QueryOutcome<Response = QueryResponse> {
874    pub response: Response,
875    pub operations: Vec<Operation>,
876}
877
878impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
879    fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
880        let QueryOutcome {
881            response,
882            operations,
883        } = system_outcome;
884
885        QueryOutcome {
886            response: QueryResponse::System(response),
887            operations,
888        }
889    }
890}
891
892impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
893    fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
894        let QueryOutcome {
895            response,
896            operations,
897        } = user_service_outcome;
898
899        QueryOutcome {
900            response: QueryResponse::User(response),
901            operations,
902        }
903    }
904}
905
906/// The response to a query.
907#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
908pub enum QueryResponse {
909    /// A system response.
910    System(SystemResponse),
911    /// A user response (in serialized form).
912    User(
913        #[serde(with = "serde_bytes")]
914        #[debug(with = "hex_debug")]
915        Vec<u8>,
916    ),
917}
918
919/// The kind of outgoing message being sent.
920#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy)]
921pub enum MessageKind {
922    /// The message can be skipped or rejected. No receipt is requested.
923    Simple,
924    /// The message cannot be skipped nor rejected. No receipt is requested.
925    /// This only concerns certain system messages that cannot fail.
926    Protected,
927    /// The message cannot be skipped but can be rejected. A receipt must be sent
928    /// when the message is rejected in a block of the receiver.
929    Tracked,
930    /// This message is a receipt automatically created when the original message was rejected.
931    Bouncing,
932}
933
934/// A posted message together with routing information.
935#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
936pub struct OutgoingMessage {
937    /// The destination of the message.
938    pub destination: ChainId,
939    /// The user authentication carried by the message, if any.
940    #[debug(skip_if = Option::is_none)]
941    pub authenticated_signer: Option<AccountOwner>,
942    /// A grant to pay for the message execution.
943    #[debug(skip_if = Amount::is_zero)]
944    pub grant: Amount,
945    /// Where to send a refund for the unused part of the grant after execution, if any.
946    #[debug(skip_if = Option::is_none)]
947    pub refund_grant_to: Option<Account>,
948    /// The kind of message being sent.
949    pub kind: MessageKind,
950    /// The message itself.
951    pub message: Message,
952}
953
954impl BcsHashable<'_> for OutgoingMessage {}
955
956impl OutgoingMessage {
957    /// Creates a new simple outgoing message with no grant and no authenticated signer.
958    pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
959        OutgoingMessage {
960            destination: recipient,
961            authenticated_signer: None,
962            grant: Amount::ZERO,
963            refund_grant_to: None,
964            kind: MessageKind::Simple,
965            message: message.into(),
966        }
967    }
968
969    /// Returns the same message, with the specified kind.
970    pub fn with_kind(mut self, kind: MessageKind) -> Self {
971        self.kind = kind;
972        self
973    }
974
975    /// Returns the same message, with the specified authenticated signer.
976    pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
977        self.authenticated_signer = authenticated_signer;
978        self
979    }
980}
981
982impl OperationContext {
983    /// Returns an account for the refund.
984    /// Returns `None` if there is no authenticated signer of the [`OperationContext`].
985    fn refund_grant_to(&self) -> Option<Account> {
986        self.authenticated_signer.map(|owner| Account {
987            chain_id: self.chain_id,
988            owner,
989        })
990    }
991}
992
993#[cfg(with_testing)]
994#[derive(Clone)]
995pub struct TestExecutionRuntimeContext {
996    chain_id: ChainId,
997    execution_runtime_config: ExecutionRuntimeConfig,
998    user_contracts: Arc<DashMap<ApplicationId, UserContractCode>>,
999    user_services: Arc<DashMap<ApplicationId, UserServiceCode>>,
1000    blobs: Arc<DashMap<BlobId, Blob>>,
1001    events: Arc<DashMap<EventId, Vec<u8>>>,
1002}
1003
1004#[cfg(with_testing)]
1005impl TestExecutionRuntimeContext {
1006    pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1007        Self {
1008            chain_id,
1009            execution_runtime_config,
1010            user_contracts: Arc::default(),
1011            user_services: Arc::default(),
1012            blobs: Arc::default(),
1013            events: Arc::default(),
1014        }
1015    }
1016}
1017
1018#[cfg(with_testing)]
1019#[cfg_attr(not(web), async_trait)]
1020#[cfg_attr(web, async_trait(?Send))]
1021impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1022    fn chain_id(&self) -> ChainId {
1023        self.chain_id
1024    }
1025
1026    fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1027        self.execution_runtime_config
1028    }
1029
1030    fn user_contracts(&self) -> &Arc<DashMap<ApplicationId, UserContractCode>> {
1031        &self.user_contracts
1032    }
1033
1034    fn user_services(&self) -> &Arc<DashMap<ApplicationId, UserServiceCode>> {
1035        &self.user_services
1036    }
1037
1038    async fn get_user_contract(
1039        &self,
1040        description: &ApplicationDescription,
1041    ) -> Result<UserContractCode, ExecutionError> {
1042        let application_id = description.into();
1043        Ok(self
1044            .user_contracts()
1045            .get(&application_id)
1046            .ok_or_else(|| {
1047                ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1048            })?
1049            .clone())
1050    }
1051
1052    async fn get_user_service(
1053        &self,
1054        description: &ApplicationDescription,
1055    ) -> Result<UserServiceCode, ExecutionError> {
1056        let application_id = description.into();
1057        Ok(self
1058            .user_services()
1059            .get(&application_id)
1060            .ok_or_else(|| {
1061                ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1062            })?
1063            .clone())
1064    }
1065
1066    async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError> {
1067        match self.blobs.get(&blob_id) {
1068            None => Ok(None),
1069            Some(blob) => Ok(Some(blob.clone())),
1070        }
1071    }
1072
1073    async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError> {
1074        match self.events.get(&event_id) {
1075            None => Ok(None),
1076            Some(event) => Ok(Some(event.clone())),
1077        }
1078    }
1079
1080    async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1081        Ok(Some(NetworkDescription {
1082            admin_chain_id: dummy_chain_description(0).id(),
1083            genesis_config_hash: CryptoHash::test_hash("genesis config"),
1084            genesis_timestamp: Timestamp::from(0),
1085            name: "dummy network description".to_string(),
1086        }))
1087    }
1088
1089    async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1090        Ok(self.blobs.contains_key(&blob_id))
1091    }
1092
1093    async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1094        Ok(self.events.contains_key(&event_id))
1095    }
1096
1097    #[cfg(with_testing)]
1098    async fn add_blobs(
1099        &self,
1100        blobs: impl IntoIterator<Item = Blob> + Send,
1101    ) -> Result<(), ViewError> {
1102        for blob in blobs {
1103            self.blobs.insert(blob.id(), blob);
1104        }
1105
1106        Ok(())
1107    }
1108
1109    #[cfg(with_testing)]
1110    async fn add_events(
1111        &self,
1112        events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1113    ) -> Result<(), ViewError> {
1114        for (event_id, bytes) in events {
1115            self.events.insert(event_id, bytes);
1116        }
1117
1118        Ok(())
1119    }
1120}
1121
1122impl From<SystemOperation> for Operation {
1123    fn from(operation: SystemOperation) -> Self {
1124        Operation::System(Box::new(operation))
1125    }
1126}
1127
1128impl Operation {
1129    pub fn system(operation: SystemOperation) -> Self {
1130        Operation::System(Box::new(operation))
1131    }
1132
1133    /// Creates a new user application operation following the `application_id`'s [`Abi`].
1134    #[cfg(with_testing)]
1135    pub fn user<A: Abi>(
1136        application_id: ApplicationId<A>,
1137        operation: &A::Operation,
1138    ) -> Result<Self, bcs::Error> {
1139        Self::user_without_abi(application_id.forget_abi(), operation)
1140    }
1141
1142    /// Creates a new user application operation assuming that the `operation` is valid for the
1143    /// `application_id`.
1144    #[cfg(with_testing)]
1145    pub fn user_without_abi(
1146        application_id: ApplicationId,
1147        operation: &impl Serialize,
1148    ) -> Result<Self, bcs::Error> {
1149        Ok(Operation::User {
1150            application_id,
1151            bytes: bcs::to_bytes(&operation)?,
1152        })
1153    }
1154
1155    /// Returns a reference to the [`SystemOperation`] in this [`Operation`], if this [`Operation`]
1156    /// is for the system application.
1157    pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1158        match self {
1159            Operation::System(system_operation) => Some(system_operation),
1160            Operation::User { .. } => None,
1161        }
1162    }
1163
1164    pub fn application_id(&self) -> GenericApplicationId {
1165        match self {
1166            Self::System(_) => GenericApplicationId::System,
1167            Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1168        }
1169    }
1170
1171    /// Returns the IDs of all blobs published in this operation.
1172    pub fn published_blob_ids(&self) -> Vec<BlobId> {
1173        match self.as_system_operation() {
1174            Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1175                vec![BlobId::new(*blob_hash, BlobType::Data)]
1176            }
1177            Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1178                vec![BlobId::new(*blob_hash, BlobType::Committee)]
1179            }
1180            Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1181            _ => vec![],
1182        }
1183    }
1184
1185    /// Returns whether this operation is allowed regardless of application permissions.
1186    pub fn is_exempt_from_permissions(&self) -> bool {
1187        let Operation::System(system_op) = self else {
1188            return false;
1189        };
1190        matches!(
1191            **system_op,
1192            SystemOperation::ProcessNewEpoch(_)
1193                | SystemOperation::ProcessRemovedEpoch(_)
1194                | SystemOperation::UpdateStreams(_)
1195        )
1196    }
1197}
1198
1199impl From<SystemMessage> for Message {
1200    fn from(message: SystemMessage) -> Self {
1201        Message::System(message)
1202    }
1203}
1204
1205impl Message {
1206    pub fn system(message: SystemMessage) -> Self {
1207        Message::System(message)
1208    }
1209
1210    /// Creates a new user application message assuming that the `message` is valid for the
1211    /// `application_id`.
1212    pub fn user<A, M: Serialize>(
1213        application_id: ApplicationId<A>,
1214        message: &M,
1215    ) -> Result<Self, bcs::Error> {
1216        let application_id = application_id.forget_abi();
1217        let bytes = bcs::to_bytes(&message)?;
1218        Ok(Message::User {
1219            application_id,
1220            bytes,
1221        })
1222    }
1223
1224    pub fn application_id(&self) -> GenericApplicationId {
1225        match self {
1226            Self::System(_) => GenericApplicationId::System,
1227            Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1228        }
1229    }
1230}
1231
1232impl From<SystemQuery> for Query {
1233    fn from(query: SystemQuery) -> Self {
1234        Query::System(query)
1235    }
1236}
1237
1238impl Query {
1239    pub fn system(query: SystemQuery) -> Self {
1240        Query::System(query)
1241    }
1242
1243    /// Creates a new user application query following the `application_id`'s [`Abi`].
1244    pub fn user<A: Abi>(
1245        application_id: ApplicationId<A>,
1246        query: &A::Query,
1247    ) -> Result<Self, serde_json::Error> {
1248        Self::user_without_abi(application_id.forget_abi(), query)
1249    }
1250
1251    /// Creates a new user application query assuming that the `query` is valid for the
1252    /// `application_id`.
1253    pub fn user_without_abi(
1254        application_id: ApplicationId,
1255        query: &impl Serialize,
1256    ) -> Result<Self, serde_json::Error> {
1257        Ok(Query::User {
1258            application_id,
1259            bytes: serde_json::to_vec(&query)?,
1260        })
1261    }
1262
1263    pub fn application_id(&self) -> GenericApplicationId {
1264        match self {
1265            Self::System(_) => GenericApplicationId::System,
1266            Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1267        }
1268    }
1269}
1270
1271impl From<SystemResponse> for QueryResponse {
1272    fn from(response: SystemResponse) -> Self {
1273        QueryResponse::System(response)
1274    }
1275}
1276
1277impl From<Vec<u8>> for QueryResponse {
1278    fn from(response: Vec<u8>) -> Self {
1279        QueryResponse::User(response)
1280    }
1281}
1282
1283/// The state of a blob of binary data.
1284#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1285pub struct BlobState {
1286    /// Hash of the last `Certificate` that published or used this blob. If empty, the
1287    /// blob is known to be published by a confirmed certificate but we may not have fully
1288    /// processed this certificate just yet.
1289    pub last_used_by: Option<CryptoHash>,
1290    /// The `ChainId` of the chain that published the change
1291    pub chain_id: ChainId,
1292    /// The `BlockHeight` of the chain that published the change
1293    pub block_height: BlockHeight,
1294    /// Epoch of the `last_used_by` certificate (if any).
1295    pub epoch: Option<Epoch>,
1296}
1297
1298/// The runtime to use for running the application.
1299#[derive(Clone, Copy, Display)]
1300#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1301pub enum WasmRuntime {
1302    #[cfg(with_wasmer)]
1303    #[default]
1304    #[display("wasmer")]
1305    Wasmer,
1306    #[cfg(with_wasmtime)]
1307    #[cfg_attr(not(with_wasmer), default)]
1308    #[display("wasmtime")]
1309    Wasmtime,
1310}
1311
1312#[derive(Clone, Copy, Display)]
1313#[cfg_attr(with_revm, derive(Debug, Default))]
1314pub enum EvmRuntime {
1315    #[cfg(with_revm)]
1316    #[default]
1317    #[display("revm")]
1318    Revm,
1319}
1320
1321/// Trait used to select a default `WasmRuntime`, if one is available.
1322pub trait WithWasmDefault {
1323    fn with_wasm_default(self) -> Self;
1324}
1325
1326impl WithWasmDefault for Option<WasmRuntime> {
1327    fn with_wasm_default(self) -> Self {
1328        #[cfg(with_wasm_runtime)]
1329        {
1330            Some(self.unwrap_or_default())
1331        }
1332        #[cfg(not(with_wasm_runtime))]
1333        {
1334            None
1335        }
1336    }
1337}
1338
1339impl FromStr for WasmRuntime {
1340    type Err = InvalidWasmRuntime;
1341
1342    fn from_str(string: &str) -> Result<Self, Self::Err> {
1343        match string {
1344            #[cfg(with_wasmer)]
1345            "wasmer" => Ok(WasmRuntime::Wasmer),
1346            #[cfg(with_wasmtime)]
1347            "wasmtime" => Ok(WasmRuntime::Wasmtime),
1348            unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1349        }
1350    }
1351}
1352
1353/// Attempts to create an invalid [`WasmRuntime`] instance from a string.
1354#[derive(Clone, Debug, Error)]
1355#[error("{0:?} is not a valid WebAssembly runtime")]
1356pub struct InvalidWasmRuntime(String);
1357
1358doc_scalar!(Operation, "An operation to be executed in a block");
1359doc_scalar!(
1360    Message,
1361    "A message to be sent and possibly executed in the receiver's block."
1362);
1363doc_scalar!(MessageKind, "The kind of outgoing message being sent");