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