linera_execution/
lib.rs

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