Skip to main content

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