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