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,
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)]
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("Admin operations are only allowed on the admin chain.")]
349    AdminOperationOnNonAdminChain,
350    #[error("Failed to create new committee: expected {expected}, but got {provided}")]
351    InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
352    #[error("Failed to remove committee")]
353    InvalidCommitteeRemoval,
354    #[error("No recorded response for oracle query")]
355    MissingOracleResponse,
356    #[error("process_streams was not called for all stream updates")]
357    UnprocessedStreams,
358    #[error("Internal error: {0}")]
359    InternalError(&'static str),
360    #[error("UpdateStreams is outdated")]
361    OutdatedUpdateStreams,
362}
363
364impl ExecutionError {
365    /// Returns whether this error is caused by an issue in the local node.
366    ///
367    /// Returns `false` whenever the error could be caused by a bad message from a peer.
368    pub fn is_local(&self) -> bool {
369        match self {
370            ExecutionError::ArithmeticError(_)
371            | ExecutionError::UserError(_)
372            | ExecutionError::DecompressionError(_)
373            | ExecutionError::InvalidPromise
374            | ExecutionError::CrossApplicationCallInFinalize { .. }
375            | ExecutionError::ReentrantCall(_)
376            | ExecutionError::ApplicationBytecodeNotFound(_)
377            | ExecutionError::UnsupportedDynamicApplicationLoad(_)
378            | ExecutionError::ExcessiveRead
379            | ExecutionError::ExcessiveWrite
380            | ExecutionError::MaximumFuelExceeded(_)
381            | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
382            | ExecutionError::ServiceOracleResponseTooLarge
383            | ExecutionError::BlockTooLarge
384            | ExecutionError::HttpResponseSizeLimitExceeded { .. }
385            | ExecutionError::UnauthorizedApplication(_)
386            | ExecutionError::UnexpectedOracleResponse
387            | ExecutionError::JsonError(_)
388            | ExecutionError::BcsError(_)
389            | ExecutionError::OracleResponseMismatch
390            | ExecutionError::ServiceOracleQueryOperations(_)
391            | ExecutionError::AssertBefore { .. }
392            | ExecutionError::StreamNameTooLong
393            | ExecutionError::BlobTooLarge
394            | ExecutionError::BytecodeTooLarge
395            | ExecutionError::UnauthorizedHttpRequest(_)
396            | ExecutionError::InvalidUrlForHttpRequest(_)
397            | ExecutionError::InactiveChain(_)
398            | ExecutionError::BlobsNotFound(_)
399            | ExecutionError::EventsNotFound(_)
400            | ExecutionError::InvalidHeaderName(_)
401            | ExecutionError::InvalidHeaderValue(_)
402            | ExecutionError::InvalidEpoch { .. }
403            | ExecutionError::IncorrectTransferAmount
404            | ExecutionError::UnauthenticatedTransferOwner
405            | ExecutionError::InsufficientBalance { .. }
406            | ExecutionError::FeesExceedFunding { .. }
407            | ExecutionError::IncorrectClaimAmount
408            | ExecutionError::UnauthenticatedClaimOwner
409            | ExecutionError::AdminOperationOnNonAdminChain
410            | ExecutionError::InvalidCommitteeEpoch { .. }
411            | ExecutionError::InvalidCommitteeRemoval
412            | ExecutionError::MissingOracleResponse
413            | ExecutionError::UnprocessedStreams
414            | ExecutionError::OutdatedUpdateStreams
415            | ExecutionError::ViewError(ViewError::NotFound(_)) => false,
416            #[cfg(with_wasm_runtime)]
417            ExecutionError::WasmError(_) => false,
418            #[cfg(with_revm)]
419            ExecutionError::EvmError(..) => false,
420            ExecutionError::MissingRuntimeResponse
421            | ExecutionError::ViewError(_)
422            | ExecutionError::ReqwestError(_)
423            | ExecutionError::Thread(_)
424            | ExecutionError::NoNetworkDescriptionFound
425            | ExecutionError::InternalError(_)
426            | ExecutionError::IoError(_) => true,
427        }
428    }
429
430    /// Returns whether this error is caused by a per-block limit being exceeded.
431    ///
432    /// These are errors that might succeed in a later block if the limit was only exceeded
433    /// due to accumulated transactions. Per-transaction or per-call limits are not included.
434    pub fn is_limit_error(&self) -> bool {
435        matches!(
436            self,
437            ExecutionError::ExcessiveRead
438                | ExecutionError::ExcessiveWrite
439                | ExecutionError::MaximumFuelExceeded(_)
440                | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
441                | ExecutionError::BlockTooLarge
442        )
443    }
444}
445
446/// The public entry points provided by the contract part of an application.
447pub trait UserContract {
448    /// Instantiate the application state on the chain that owns the application.
449    fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
450
451    /// Applies an operation from the current block.
452    fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
453
454    /// Applies a message originating from a cross-chain message.
455    fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
456
457    /// Reacts to new events on streams this application subscribes to.
458    fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
459
460    /// Finishes execution of the current transaction.
461    fn finalize(&mut self) -> Result<(), ExecutionError>;
462}
463
464/// The public entry points provided by the service part of an application.
465pub trait UserService {
466    /// Executes unmetered read-only queries on the state of this application.
467    fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
468}
469
470/// Configuration options for the execution runtime available to applications.
471#[derive(Clone, Copy)]
472pub struct ExecutionRuntimeConfig {
473    /// Whether contract log messages should be output.
474    /// This is typically enabled for clients but disabled for validators.
475    pub allow_application_logs: bool,
476}
477
478impl Default for ExecutionRuntimeConfig {
479    fn default() -> Self {
480        Self {
481            allow_application_logs: true,
482        }
483    }
484}
485
486/// Requirements for the `extra` field in our state views (and notably the
487/// [`ExecutionStateView`]).
488#[cfg_attr(not(web), async_trait)]
489#[cfg_attr(web, async_trait(?Send))]
490pub trait ExecutionRuntimeContext {
491    fn chain_id(&self) -> ChainId;
492
493    fn thread_pool(&self) -> &Arc<ThreadPool>;
494
495    fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
496
497    fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>>;
498
499    fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>>;
500
501    async fn get_user_contract(
502        &self,
503        description: &ApplicationDescription,
504        txn_tracker: &TransactionTracker,
505    ) -> Result<UserContractCode, ExecutionError>;
506
507    async fn get_user_service(
508        &self,
509        description: &ApplicationDescription,
510        txn_tracker: &TransactionTracker,
511    ) -> Result<UserServiceCode, ExecutionError>;
512
513    async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError>;
514
515    async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError>;
516
517    async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
518
519    /// Returns the committees for the epochs in the given range.
520    async fn get_committees(
521        &self,
522        epoch_range: RangeInclusive<Epoch>,
523    ) -> Result<BTreeMap<Epoch, Committee>, ExecutionError> {
524        let net_description = self
525            .get_network_description()
526            .await?
527            .ok_or(ExecutionError::NoNetworkDescriptionFound)?;
528        let committee_hashes = futures::future::join_all(
529            (epoch_range.start().0..=epoch_range.end().0).map(|epoch| async move {
530                if epoch == 0 {
531                    // Genesis epoch is stored in NetworkDescription.
532                    Ok((epoch, net_description.genesis_committee_blob_hash))
533                } else {
534                    let event_id = EventId {
535                        chain_id: net_description.admin_chain_id,
536                        stream_id: StreamId::system(EPOCH_STREAM_NAME),
537                        index: epoch,
538                    };
539                    let event = self
540                        .get_event(event_id.clone())
541                        .await?
542                        .ok_or_else(|| ExecutionError::EventsNotFound(vec![event_id]))?;
543                    let event_data: EpochEventData = bcs::from_bytes(&event)?;
544                    Ok((epoch, event_data.blob_hash))
545                }
546            }),
547        )
548        .await;
549        let missing_events = committee_hashes
550            .iter()
551            .filter_map(|result| {
552                if let Err(ExecutionError::EventsNotFound(event_ids)) = result {
553                    return Some(event_ids);
554                }
555                None
556            })
557            .flatten()
558            .cloned()
559            .collect::<Vec<_>>();
560        ensure!(
561            missing_events.is_empty(),
562            ExecutionError::EventsNotFound(missing_events)
563        );
564        let committee_hashes = committee_hashes
565            .into_iter()
566            .collect::<Result<Vec<_>, _>>()?;
567        let committees = futures::future::join_all(committee_hashes.into_iter().map(
568            |(epoch, committee_hash)| async move {
569                let blob_id = BlobId::new(committee_hash, BlobType::Committee);
570                let committee_blob = self
571                    .get_blob(blob_id)
572                    .await?
573                    .ok_or_else(|| ExecutionError::BlobsNotFound(vec![blob_id]))?;
574                Ok((Epoch(epoch), bcs::from_bytes(committee_blob.bytes())?))
575            },
576        ))
577        .await;
578        let missing_blobs = committees
579            .iter()
580            .filter_map(|result| {
581                if let Err(ExecutionError::BlobsNotFound(blob_ids)) = result {
582                    return Some(blob_ids);
583                }
584                None
585            })
586            .flatten()
587            .cloned()
588            .collect::<Vec<_>>();
589        ensure!(
590            missing_blobs.is_empty(),
591            ExecutionError::BlobsNotFound(missing_blobs)
592        );
593        committees.into_iter().collect()
594    }
595
596    async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
597
598    async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
599
600    #[cfg(with_testing)]
601    async fn add_blobs(
602        &self,
603        blobs: impl IntoIterator<Item = Blob> + Send,
604    ) -> Result<(), ViewError>;
605
606    #[cfg(with_testing)]
607    async fn add_events(
608        &self,
609        events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
610    ) -> Result<(), ViewError>;
611}
612
613#[derive(Clone, Copy, Debug)]
614pub struct OperationContext {
615    /// The current chain ID.
616    pub chain_id: ChainId,
617    /// The authenticated owner of the operation, if any.
618    #[debug(skip_if = Option::is_none)]
619    pub authenticated_owner: Option<AccountOwner>,
620    /// The current block height.
621    pub height: BlockHeight,
622    /// The consensus round number, if this is a block that gets validated in a multi-leader round.
623    pub round: Option<u32>,
624    /// The timestamp of the block containing the operation.
625    pub timestamp: Timestamp,
626}
627
628#[derive(Clone, Copy, Debug)]
629pub struct MessageContext {
630    /// The current chain ID.
631    pub chain_id: ChainId,
632    /// The chain ID where the message originated from.
633    pub origin: ChainId,
634    /// Whether the message was rejected by the original receiver and is now bouncing back.
635    pub is_bouncing: bool,
636    /// The authenticated owner of the operation that created the message, if any.
637    #[debug(skip_if = Option::is_none)]
638    pub authenticated_owner: Option<AccountOwner>,
639    /// Where to send a refund for the unused part of each grant after execution, if any.
640    #[debug(skip_if = Option::is_none)]
641    pub refund_grant_to: Option<Account>,
642    /// The current block height.
643    pub height: BlockHeight,
644    /// The consensus round number, if this is a block that gets validated in a multi-leader round.
645    pub round: Option<u32>,
646    /// The timestamp of the block executing the message.
647    pub timestamp: Timestamp,
648}
649
650#[derive(Clone, Copy, Debug)]
651pub struct ProcessStreamsContext {
652    /// The current chain ID.
653    pub chain_id: ChainId,
654    /// The current block height.
655    pub height: BlockHeight,
656    /// The consensus round number, if this is a block that gets validated in a multi-leader round.
657    pub round: Option<u32>,
658    /// The timestamp of the current block.
659    pub timestamp: Timestamp,
660}
661
662impl From<MessageContext> for ProcessStreamsContext {
663    fn from(context: MessageContext) -> Self {
664        Self {
665            chain_id: context.chain_id,
666            height: context.height,
667            round: context.round,
668            timestamp: context.timestamp,
669        }
670    }
671}
672
673impl From<OperationContext> for ProcessStreamsContext {
674    fn from(context: OperationContext) -> Self {
675        Self {
676            chain_id: context.chain_id,
677            height: context.height,
678            round: context.round,
679            timestamp: context.timestamp,
680        }
681    }
682}
683
684#[derive(Clone, Copy, Debug)]
685pub struct FinalizeContext {
686    /// The current chain ID.
687    pub chain_id: ChainId,
688    /// The authenticated owner of the operation, if any.
689    #[debug(skip_if = Option::is_none)]
690    pub authenticated_owner: Option<AccountOwner>,
691    /// The current block height.
692    pub height: BlockHeight,
693    /// The consensus round number, if this is a block that gets validated in a multi-leader round.
694    pub round: Option<u32>,
695}
696
697#[derive(Clone, Copy, Debug, Eq, PartialEq)]
698pub struct QueryContext {
699    /// The current chain ID.
700    pub chain_id: ChainId,
701    /// The height of the next block on this chain.
702    pub next_block_height: BlockHeight,
703    /// The local time in the node executing the query.
704    pub local_time: Timestamp,
705}
706
707pub trait BaseRuntime {
708    type Read: fmt::Debug + Send + Sync;
709    type ContainsKey: fmt::Debug + Send + Sync;
710    type ContainsKeys: fmt::Debug + Send + Sync;
711    type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
712    type ReadValueBytes: fmt::Debug + Send + Sync;
713    type FindKeysByPrefix: fmt::Debug + Send + Sync;
714    type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
715
716    /// The current chain ID.
717    fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
718
719    /// The current block height.
720    fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
721
722    /// The current application ID.
723    fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
724
725    /// The current application creator's chain ID.
726    fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
727
728    /// The current application parameters.
729    fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
730
731    /// Reads the system timestamp.
732    fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
733
734    /// Reads the balance of the chain.
735    fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
736
737    /// Reads the owner balance.
738    fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
739
740    /// Reads the balances from all owners.
741    fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
742
743    /// Reads balance owners.
744    fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
745
746    /// Reads the current ownership configuration for this chain.
747    fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
748
749    /// Reads the current application permissions for this chain.
750    fn application_permissions(&mut self) -> Result<ApplicationPermissions, ExecutionError>;
751
752    /// Tests whether a key exists in the key-value store
753    #[cfg(feature = "test")]
754    fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
755        let promise = self.contains_key_new(key)?;
756        self.contains_key_wait(&promise)
757    }
758
759    /// Creates the promise to test whether a key exists in the key-value store
760    fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
761
762    /// Resolves the promise to test whether a key exists in the key-value store
763    fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
764
765    /// Tests whether multiple keys exist in the key-value store
766    #[cfg(feature = "test")]
767    fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
768        let promise = self.contains_keys_new(keys)?;
769        self.contains_keys_wait(&promise)
770    }
771
772    /// Creates the promise to test whether multiple keys exist in the key-value store
773    fn contains_keys_new(
774        &mut self,
775        keys: Vec<Vec<u8>>,
776    ) -> Result<Self::ContainsKeys, ExecutionError>;
777
778    /// Resolves the promise to test whether multiple keys exist in the key-value store
779    fn contains_keys_wait(
780        &mut self,
781        promise: &Self::ContainsKeys,
782    ) -> Result<Vec<bool>, ExecutionError>;
783
784    /// Reads several keys from the key-value store
785    #[cfg(feature = "test")]
786    fn read_multi_values_bytes(
787        &mut self,
788        keys: Vec<Vec<u8>>,
789    ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
790        let promise = self.read_multi_values_bytes_new(keys)?;
791        self.read_multi_values_bytes_wait(&promise)
792    }
793
794    /// Creates the promise to access several keys from the key-value store
795    fn read_multi_values_bytes_new(
796        &mut self,
797        keys: Vec<Vec<u8>>,
798    ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
799
800    /// Resolves the promise to access several keys from the key-value store
801    fn read_multi_values_bytes_wait(
802        &mut self,
803        promise: &Self::ReadMultiValuesBytes,
804    ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
805
806    /// Reads the key from the key-value store
807    #[cfg(feature = "test")]
808    fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
809        let promise = self.read_value_bytes_new(key)?;
810        self.read_value_bytes_wait(&promise)
811    }
812
813    /// Creates the promise to access a key from the key-value store
814    fn read_value_bytes_new(
815        &mut self,
816        key: Vec<u8>,
817    ) -> Result<Self::ReadValueBytes, ExecutionError>;
818
819    /// Resolves the promise to access a key from the key-value store
820    fn read_value_bytes_wait(
821        &mut self,
822        promise: &Self::ReadValueBytes,
823    ) -> Result<Option<Vec<u8>>, ExecutionError>;
824
825    /// Creates the promise to access keys having a specific prefix
826    fn find_keys_by_prefix_new(
827        &mut self,
828        key_prefix: Vec<u8>,
829    ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
830
831    /// Resolves the promise to access keys having a specific prefix
832    fn find_keys_by_prefix_wait(
833        &mut self,
834        promise: &Self::FindKeysByPrefix,
835    ) -> Result<Vec<Vec<u8>>, ExecutionError>;
836
837    /// Reads the data from the key/values having a specific prefix.
838    #[cfg(feature = "test")]
839    #[expect(clippy::type_complexity)]
840    fn find_key_values_by_prefix(
841        &mut self,
842        key_prefix: Vec<u8>,
843    ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
844        let promise = self.find_key_values_by_prefix_new(key_prefix)?;
845        self.find_key_values_by_prefix_wait(&promise)
846    }
847
848    /// Creates the promise to access key/values having a specific prefix
849    fn find_key_values_by_prefix_new(
850        &mut self,
851        key_prefix: Vec<u8>,
852    ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
853
854    /// Resolves the promise to access key/values having a specific prefix
855    #[expect(clippy::type_complexity)]
856    fn find_key_values_by_prefix_wait(
857        &mut self,
858        promise: &Self::FindKeyValuesByPrefix,
859    ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
860
861    /// Makes an HTTP request to the given URL and returns the answer, if any.
862    fn perform_http_request(
863        &mut self,
864        request: http::Request,
865    ) -> Result<http::Response, ExecutionError>;
866
867    /// Ensures that the current time at block validation is `< timestamp`. Note that block
868    /// validation happens at or after the block timestamp, but isn't necessarily the same.
869    ///
870    /// Cannot be used in fast blocks: A block using this call should be proposed by a regular
871    /// owner, not a super owner.
872    fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
873
874    /// Reads a data blob specified by a given hash.
875    fn read_data_blob(&mut self, hash: DataBlobHash) -> Result<Vec<u8>, ExecutionError>;
876
877    /// Asserts the existence of a data blob with the given hash.
878    fn assert_data_blob_exists(&mut self, hash: DataBlobHash) -> Result<(), ExecutionError>;
879
880    /// Returns true if the corresponding contract uses a zero amount of storage.
881    fn has_empty_storage(&mut self, application: ApplicationId) -> Result<bool, ExecutionError>;
882
883    /// Returns the maximum blob size from the `ResourceControlPolicy`.
884    fn maximum_blob_size(&mut self) -> Result<u64, ExecutionError>;
885
886    /// Returns whether contract log messages should be output.
887    /// This is typically enabled for clients but disabled for validators.
888    fn allow_application_logs(&mut self) -> Result<bool, ExecutionError>;
889
890    /// Sends a log message (used for forwarding logs from web workers to the main thread).
891    /// This is a fire-and-forget operation - errors are silently ignored.
892    #[cfg(web)]
893    fn send_log(&mut self, message: String, level: tracing::log::Level);
894}
895
896pub trait ServiceRuntime: BaseRuntime {
897    /// Queries another application.
898    fn try_query_application(
899        &mut self,
900        queried_id: ApplicationId,
901        argument: Vec<u8>,
902    ) -> Result<Vec<u8>, ExecutionError>;
903
904    /// Schedules an operation to be included in the block proposed after execution.
905    fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
906
907    /// Checks if the service has exceeded its execution time limit.
908    fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
909}
910
911pub trait ContractRuntime: BaseRuntime {
912    /// The authenticated owner for this execution, if there is one.
913    fn authenticated_owner(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
914
915    /// If the current message (if there is one) was rejected by its destination and is now
916    /// bouncing back.
917    fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
918
919    /// The chain ID where the current message originated from, if there is one.
920    fn message_origin_chain_id(&mut self) -> Result<Option<ChainId>, ExecutionError>;
921
922    /// The optional authenticated caller application ID, if it was provided and if there is one
923    /// based on the execution context.
924    fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
925
926    /// Returns the maximum gas fuel per block.
927    fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
928
929    /// Returns the amount of execution fuel remaining before execution is aborted.
930    fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
931
932    /// Consumes some of the execution fuel.
933    fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
934
935    /// Schedules a message to be sent.
936    fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
937
938    /// Transfers amount from source to destination.
939    fn transfer(
940        &mut self,
941        source: AccountOwner,
942        destination: Account,
943        amount: Amount,
944    ) -> Result<(), ExecutionError>;
945
946    /// Claims amount from source to destination.
947    fn claim(
948        &mut self,
949        source: Account,
950        destination: Account,
951        amount: Amount,
952    ) -> Result<(), ExecutionError>;
953
954    /// Calls another application. Forwarded sessions will now be visible to
955    /// `callee_id` (but not to the caller any more).
956    fn try_call_application(
957        &mut self,
958        authenticated: bool,
959        callee_id: ApplicationId,
960        argument: Vec<u8>,
961    ) -> Result<Vec<u8>, ExecutionError>;
962
963    /// Adds a new item to an event stream. Returns the new event's index in the stream.
964    fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
965
966    /// Reads an event from a stream. Returns the event's value.
967    ///
968    /// Returns an error if the event doesn't exist.
969    fn read_event(
970        &mut self,
971        chain_id: ChainId,
972        stream_name: StreamName,
973        index: u32,
974    ) -> Result<Vec<u8>, ExecutionError>;
975
976    /// Subscribes this application to an event stream.
977    fn subscribe_to_events(
978        &mut self,
979        chain_id: ChainId,
980        application_id: ApplicationId,
981        stream_name: StreamName,
982    ) -> Result<(), ExecutionError>;
983
984    /// Unsubscribes this application from an event stream.
985    fn unsubscribe_from_events(
986        &mut self,
987        chain_id: ChainId,
988        application_id: ApplicationId,
989        stream_name: StreamName,
990    ) -> Result<(), ExecutionError>;
991
992    /// Queries a service.
993    fn query_service(
994        &mut self,
995        application_id: ApplicationId,
996        query: Vec<u8>,
997    ) -> Result<Vec<u8>, ExecutionError>;
998
999    /// Opens a new chain.
1000    fn open_chain(
1001        &mut self,
1002        ownership: ChainOwnership,
1003        application_permissions: ApplicationPermissions,
1004        balance: Amount,
1005    ) -> Result<ChainId, ExecutionError>;
1006
1007    /// Closes the current chain.
1008    fn close_chain(&mut self) -> Result<(), ExecutionError>;
1009
1010    /// Changes the ownership of the current chain.
1011    fn change_ownership(&mut self, ownership: ChainOwnership) -> Result<(), ExecutionError>;
1012
1013    /// Changes the application permissions on the current chain.
1014    fn change_application_permissions(
1015        &mut self,
1016        application_permissions: ApplicationPermissions,
1017    ) -> Result<(), ExecutionError>;
1018
1019    /// Creates a new application on chain.
1020    fn create_application(
1021        &mut self,
1022        module_id: ModuleId,
1023        parameters: Vec<u8>,
1024        argument: Vec<u8>,
1025        required_application_ids: Vec<ApplicationId>,
1026    ) -> Result<ApplicationId, ExecutionError>;
1027
1028    /// Returns the next application index, which is equal to the number of
1029    /// new applications created so far in this block.
1030    fn peek_application_index(&mut self) -> Result<u32, ExecutionError>;
1031
1032    /// Creates a new data blob and returns its hash.
1033    fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<DataBlobHash, ExecutionError>;
1034
1035    /// Publishes a module with contract and service bytecode and returns the module ID.
1036    fn publish_module(
1037        &mut self,
1038        contract: Bytecode,
1039        service: Bytecode,
1040        vm_runtime: VmRuntime,
1041    ) -> Result<ModuleId, ExecutionError>;
1042
1043    /// Returns the multi-leader round in which this block was validated.
1044    fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
1045
1046    /// Writes a batch of changes.
1047    fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
1048}
1049
1050/// An operation to be executed in a block.
1051#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1052pub enum Operation {
1053    /// A system operation.
1054    System(Box<SystemOperation>),
1055    /// A user operation (in serialized form).
1056    User {
1057        application_id: ApplicationId,
1058        #[serde(with = "serde_bytes")]
1059        #[debug(with = "hex_debug")]
1060        bytes: Vec<u8>,
1061    },
1062}
1063
1064impl BcsHashable<'_> for Operation {}
1065
1066/// A message to be sent and possibly executed in the receiver's block.
1067#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1068pub enum Message {
1069    /// A system message.
1070    System(SystemMessage),
1071    /// A user message (in serialized form).
1072    User {
1073        application_id: ApplicationId,
1074        #[serde(with = "serde_bytes")]
1075        #[debug(with = "hex_debug")]
1076        bytes: Vec<u8>,
1077    },
1078}
1079
1080/// An query to be sent and possibly executed in the receiver's block.
1081#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1082pub enum Query {
1083    /// A system query.
1084    System(SystemQuery),
1085    /// A user query (in serialized form).
1086    User {
1087        application_id: ApplicationId,
1088        #[serde(with = "serde_bytes")]
1089        #[debug(with = "hex_debug")]
1090        bytes: Vec<u8>,
1091    },
1092}
1093
1094/// The outcome of the execution of a query.
1095#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1096pub struct QueryOutcome<Response = QueryResponse> {
1097    pub response: Response,
1098    pub operations: Vec<Operation>,
1099}
1100
1101impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
1102    fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
1103        let QueryOutcome {
1104            response,
1105            operations,
1106        } = system_outcome;
1107
1108        QueryOutcome {
1109            response: QueryResponse::System(response),
1110            operations,
1111        }
1112    }
1113}
1114
1115impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
1116    fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
1117        let QueryOutcome {
1118            response,
1119            operations,
1120        } = user_service_outcome;
1121
1122        QueryOutcome {
1123            response: QueryResponse::User(response),
1124            operations,
1125        }
1126    }
1127}
1128
1129/// The response to a query.
1130#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1131pub enum QueryResponse {
1132    /// A system response.
1133    System(SystemResponse),
1134    /// A user response (in serialized form).
1135    User(
1136        #[serde(with = "serde_bytes")]
1137        #[debug(with = "hex_debug")]
1138        Vec<u8>,
1139    ),
1140}
1141
1142/// The kind of outgoing message being sent.
1143#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy, Allocative)]
1144pub enum MessageKind {
1145    /// The message can be skipped or rejected. No receipt is requested.
1146    Simple,
1147    /// The message cannot be skipped nor rejected. No receipt is requested.
1148    /// This only concerns certain system messages that cannot fail.
1149    Protected,
1150    /// The message cannot be skipped but can be rejected. A receipt must be sent
1151    /// when the message is rejected in a block of the receiver.
1152    Tracked,
1153    /// This message is a receipt automatically created when the original message was rejected.
1154    Bouncing,
1155}
1156
1157impl Display for MessageKind {
1158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1159        match self {
1160            MessageKind::Simple => write!(f, "Simple"),
1161            MessageKind::Protected => write!(f, "Protected"),
1162            MessageKind::Tracked => write!(f, "Tracked"),
1163            MessageKind::Bouncing => write!(f, "Bouncing"),
1164        }
1165    }
1166}
1167
1168/// A posted message together with routing information.
1169#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1170pub struct OutgoingMessage {
1171    /// The destination of the message.
1172    pub destination: ChainId,
1173    /// The user authentication carried by the message, if any.
1174    #[debug(skip_if = Option::is_none)]
1175    pub authenticated_owner: Option<AccountOwner>,
1176    /// A grant to pay for the message execution.
1177    #[debug(skip_if = Amount::is_zero)]
1178    pub grant: Amount,
1179    /// Where to send a refund for the unused part of the grant after execution, if any.
1180    #[debug(skip_if = Option::is_none)]
1181    pub refund_grant_to: Option<Account>,
1182    /// The kind of message being sent.
1183    pub kind: MessageKind,
1184    /// The message itself.
1185    pub message: Message,
1186}
1187
1188impl BcsHashable<'_> for OutgoingMessage {}
1189
1190impl OutgoingMessage {
1191    /// Creates a new simple outgoing message with no grant and no authenticated owner.
1192    pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
1193        OutgoingMessage {
1194            destination: recipient,
1195            authenticated_owner: None,
1196            grant: Amount::ZERO,
1197            refund_grant_to: None,
1198            kind: MessageKind::Simple,
1199            message: message.into(),
1200        }
1201    }
1202
1203    /// Returns the same message, with the specified kind.
1204    pub fn with_kind(mut self, kind: MessageKind) -> Self {
1205        self.kind = kind;
1206        self
1207    }
1208
1209    /// Returns the same message, with the specified authenticated owner.
1210    pub fn with_authenticated_owner(mut self, authenticated_owner: Option<AccountOwner>) -> Self {
1211        self.authenticated_owner = authenticated_owner;
1212        self
1213    }
1214}
1215
1216impl OperationContext {
1217    /// Returns an account for the refund.
1218    /// Returns `None` if there is no authenticated owner of the [`OperationContext`].
1219    fn refund_grant_to(&self) -> Option<Account> {
1220        self.authenticated_owner.map(|owner| Account {
1221            chain_id: self.chain_id,
1222            owner,
1223        })
1224    }
1225}
1226
1227#[cfg(with_testing)]
1228#[derive(Clone)]
1229pub struct TestExecutionRuntimeContext {
1230    chain_id: ChainId,
1231    thread_pool: Arc<ThreadPool>,
1232    execution_runtime_config: ExecutionRuntimeConfig,
1233    user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
1234    user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
1235    blobs: Arc<papaya::HashMap<BlobId, Blob>>,
1236    events: Arc<papaya::HashMap<EventId, Vec<u8>>>,
1237}
1238
1239#[cfg(with_testing)]
1240impl TestExecutionRuntimeContext {
1241    pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1242        Self {
1243            chain_id,
1244            thread_pool: Arc::new(ThreadPool::new(20)),
1245            execution_runtime_config,
1246            user_contracts: Arc::default(),
1247            user_services: Arc::default(),
1248            blobs: Arc::default(),
1249            events: Arc::default(),
1250        }
1251    }
1252}
1253
1254#[cfg(with_testing)]
1255#[cfg_attr(not(web), async_trait)]
1256#[cfg_attr(web, async_trait(?Send))]
1257impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1258    fn chain_id(&self) -> ChainId {
1259        self.chain_id
1260    }
1261
1262    fn thread_pool(&self) -> &Arc<ThreadPool> {
1263        &self.thread_pool
1264    }
1265
1266    fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1267        self.execution_runtime_config
1268    }
1269
1270    fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>> {
1271        &self.user_contracts
1272    }
1273
1274    fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>> {
1275        &self.user_services
1276    }
1277
1278    async fn get_user_contract(
1279        &self,
1280        description: &ApplicationDescription,
1281        _txn_tracker: &TransactionTracker,
1282    ) -> Result<UserContractCode, ExecutionError> {
1283        let application_id: ApplicationId = description.into();
1284        let pinned = self.user_contracts().pin();
1285        Ok(pinned
1286            .get(&application_id)
1287            .ok_or_else(|| {
1288                ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1289            })?
1290            .clone())
1291    }
1292
1293    async fn get_user_service(
1294        &self,
1295        description: &ApplicationDescription,
1296        _txn_tracker: &TransactionTracker,
1297    ) -> Result<UserServiceCode, ExecutionError> {
1298        let application_id: ApplicationId = description.into();
1299        let pinned = self.user_services().pin();
1300        Ok(pinned
1301            .get(&application_id)
1302            .ok_or_else(|| {
1303                ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1304            })?
1305            .clone())
1306    }
1307
1308    async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError> {
1309        Ok(self.blobs.pin().get(&blob_id).cloned())
1310    }
1311
1312    async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError> {
1313        Ok(self.events.pin().get(&event_id).cloned())
1314    }
1315
1316    async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1317        let pinned = self.blobs.pin();
1318        let genesis_committee_blob_hash = pinned
1319            .iter()
1320            .find(|(_, blob)| blob.content().blob_type() == BlobType::Committee)
1321            .map_or_else(
1322                || CryptoHash::test_hash("genesis committee"),
1323                |(_, blob)| blob.id().hash,
1324            );
1325        Ok(Some(NetworkDescription {
1326            admin_chain_id: dummy_chain_description(0).id(),
1327            genesis_config_hash: CryptoHash::test_hash("genesis config"),
1328            genesis_timestamp: Timestamp::from(0),
1329            genesis_committee_blob_hash,
1330            name: "dummy network description".to_string(),
1331        }))
1332    }
1333
1334    async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1335        Ok(self.blobs.pin().contains_key(&blob_id))
1336    }
1337
1338    async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1339        Ok(self.events.pin().contains_key(&event_id))
1340    }
1341
1342    #[cfg(with_testing)]
1343    async fn add_blobs(
1344        &self,
1345        blobs: impl IntoIterator<Item = Blob> + Send,
1346    ) -> Result<(), ViewError> {
1347        let pinned = self.blobs.pin();
1348        for blob in blobs {
1349            pinned.insert(blob.id(), blob);
1350        }
1351
1352        Ok(())
1353    }
1354
1355    #[cfg(with_testing)]
1356    async fn add_events(
1357        &self,
1358        events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1359    ) -> Result<(), ViewError> {
1360        let pinned = self.events.pin();
1361        for (event_id, bytes) in events {
1362            pinned.insert(event_id, bytes);
1363        }
1364
1365        Ok(())
1366    }
1367}
1368
1369impl From<SystemOperation> for Operation {
1370    fn from(operation: SystemOperation) -> Self {
1371        Operation::System(Box::new(operation))
1372    }
1373}
1374
1375impl Operation {
1376    pub fn system(operation: SystemOperation) -> Self {
1377        Operation::System(Box::new(operation))
1378    }
1379
1380    /// Creates a new user application operation following the `application_id`'s [`Abi`].
1381    #[cfg(with_testing)]
1382    pub fn user<A: Abi>(
1383        application_id: ApplicationId<A>,
1384        operation: &A::Operation,
1385    ) -> Result<Self, bcs::Error> {
1386        Self::user_without_abi(application_id.forget_abi(), operation)
1387    }
1388
1389    /// Creates a new user application operation assuming that the `operation` is valid for the
1390    /// `application_id`.
1391    #[cfg(with_testing)]
1392    pub fn user_without_abi(
1393        application_id: ApplicationId,
1394        operation: &impl Serialize,
1395    ) -> Result<Self, bcs::Error> {
1396        Ok(Operation::User {
1397            application_id,
1398            bytes: bcs::to_bytes(&operation)?,
1399        })
1400    }
1401
1402    /// Returns a reference to the [`SystemOperation`] in this [`Operation`], if this [`Operation`]
1403    /// is for the system application.
1404    pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1405        match self {
1406            Operation::System(system_operation) => Some(system_operation),
1407            Operation::User { .. } => None,
1408        }
1409    }
1410
1411    pub fn application_id(&self) -> GenericApplicationId {
1412        match self {
1413            Self::System(_) => GenericApplicationId::System,
1414            Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1415        }
1416    }
1417
1418    /// Returns the IDs of all blobs published in this operation.
1419    pub fn published_blob_ids(&self) -> Vec<BlobId> {
1420        match self.as_system_operation() {
1421            Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1422                vec![BlobId::new(*blob_hash, BlobType::Data)]
1423            }
1424            Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1425                vec![BlobId::new(*blob_hash, BlobType::Committee)]
1426            }
1427            Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1428            _ => vec![],
1429        }
1430    }
1431
1432    /// Returns whether this operation is allowed regardless of application permissions.
1433    pub fn is_exempt_from_permissions(&self) -> bool {
1434        let Operation::System(system_op) = self else {
1435            return false;
1436        };
1437        matches!(
1438            **system_op,
1439            SystemOperation::ProcessNewEpoch(_)
1440                | SystemOperation::ProcessRemovedEpoch(_)
1441                | SystemOperation::UpdateStreams(_)
1442        )
1443    }
1444}
1445
1446impl From<SystemMessage> for Message {
1447    fn from(message: SystemMessage) -> Self {
1448        Message::System(message)
1449    }
1450}
1451
1452impl Message {
1453    pub fn system(message: SystemMessage) -> Self {
1454        Message::System(message)
1455    }
1456
1457    /// Creates a new user application message assuming that the `message` is valid for the
1458    /// `application_id`.
1459    pub fn user<A, M: Serialize>(
1460        application_id: ApplicationId<A>,
1461        message: &M,
1462    ) -> Result<Self, bcs::Error> {
1463        let application_id = application_id.forget_abi();
1464        let bytes = bcs::to_bytes(&message)?;
1465        Ok(Message::User {
1466            application_id,
1467            bytes,
1468        })
1469    }
1470
1471    pub fn application_id(&self) -> GenericApplicationId {
1472        match self {
1473            Self::System(_) => GenericApplicationId::System,
1474            Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1475        }
1476    }
1477}
1478
1479impl From<SystemQuery> for Query {
1480    fn from(query: SystemQuery) -> Self {
1481        Query::System(query)
1482    }
1483}
1484
1485impl Query {
1486    pub fn system(query: SystemQuery) -> Self {
1487        Query::System(query)
1488    }
1489
1490    /// Creates a new user application query following the `application_id`'s [`Abi`].
1491    pub fn user<A: Abi>(
1492        application_id: ApplicationId<A>,
1493        query: &A::Query,
1494    ) -> Result<Self, serde_json::Error> {
1495        Self::user_without_abi(application_id.forget_abi(), query)
1496    }
1497
1498    /// Creates a new user application query assuming that the `query` is valid for the
1499    /// `application_id`.
1500    pub fn user_without_abi(
1501        application_id: ApplicationId,
1502        query: &impl Serialize,
1503    ) -> Result<Self, serde_json::Error> {
1504        Ok(Query::User {
1505            application_id,
1506            bytes: serde_json::to_vec(&query)?,
1507        })
1508    }
1509
1510    pub fn application_id(&self) -> GenericApplicationId {
1511        match self {
1512            Self::System(_) => GenericApplicationId::System,
1513            Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1514        }
1515    }
1516}
1517
1518impl From<SystemResponse> for QueryResponse {
1519    fn from(response: SystemResponse) -> Self {
1520        QueryResponse::System(response)
1521    }
1522}
1523
1524impl From<Vec<u8>> for QueryResponse {
1525    fn from(response: Vec<u8>) -> Self {
1526        QueryResponse::User(response)
1527    }
1528}
1529
1530/// The state of a blob of binary data.
1531#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1532pub struct BlobState {
1533    /// Hash of the last `Certificate` that published or used this blob. If empty, the
1534    /// blob is known to be published by a confirmed certificate but we may not have fully
1535    /// processed this certificate just yet.
1536    pub last_used_by: Option<CryptoHash>,
1537    /// The `ChainId` of the chain that published the change
1538    pub chain_id: ChainId,
1539    /// The `BlockHeight` of the chain that published the change
1540    pub block_height: BlockHeight,
1541    /// Epoch of the `last_used_by` certificate (if any).
1542    pub epoch: Option<Epoch>,
1543}
1544
1545/// The runtime to use for running the application.
1546#[derive(Clone, Copy, Display)]
1547#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1548pub enum WasmRuntime {
1549    #[cfg(with_wasmer)]
1550    #[default]
1551    #[display("wasmer")]
1552    Wasmer,
1553    #[cfg(with_wasmtime)]
1554    #[cfg_attr(not(with_wasmer), default)]
1555    #[display("wasmtime")]
1556    Wasmtime,
1557}
1558
1559#[derive(Clone, Copy, Display)]
1560#[cfg_attr(with_revm, derive(Debug, Default))]
1561pub enum EvmRuntime {
1562    #[cfg(with_revm)]
1563    #[default]
1564    #[display("revm")]
1565    Revm,
1566}
1567
1568/// Trait used to select a default `WasmRuntime`, if one is available.
1569pub trait WithWasmDefault {
1570    fn with_wasm_default(self) -> Self;
1571}
1572
1573impl WithWasmDefault for Option<WasmRuntime> {
1574    fn with_wasm_default(self) -> Self {
1575        #[cfg(with_wasm_runtime)]
1576        {
1577            Some(self.unwrap_or_default())
1578        }
1579        #[cfg(not(with_wasm_runtime))]
1580        {
1581            None
1582        }
1583    }
1584}
1585
1586impl FromStr for WasmRuntime {
1587    type Err = InvalidWasmRuntime;
1588
1589    fn from_str(string: &str) -> Result<Self, Self::Err> {
1590        match string {
1591            #[cfg(with_wasmer)]
1592            "wasmer" => Ok(WasmRuntime::Wasmer),
1593            #[cfg(with_wasmtime)]
1594            "wasmtime" => Ok(WasmRuntime::Wasmtime),
1595            unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1596        }
1597    }
1598}
1599
1600/// Attempts to create an invalid [`WasmRuntime`] instance from a string.
1601#[derive(Clone, Debug, Error)]
1602#[error("{0:?} is not a valid WebAssembly runtime")]
1603pub struct InvalidWasmRuntime(String);
1604
1605doc_scalar!(Operation, "An operation to be executed in a block");
1606doc_scalar!(
1607    Message,
1608    "A message to be sent and possibly executed in the receiver's block."
1609);
1610doc_scalar!(MessageKind, "The kind of outgoing message being sent");