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