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