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