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