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