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