linera_execution/
lib.rs

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