linera_execution/
lib.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module manages the execution of the system application and the user applications in a
5//! Linera chain.
6
7#![deny(clippy::large_futures)]
8
9mod applications;
10pub mod committee;
11mod execution;
12mod execution_state_actor;
13mod graphql;
14mod policy;
15mod resources;
16mod runtime;
17pub mod system;
18#[cfg(with_testing)]
19pub mod test_utils;
20mod transaction_tracker;
21mod util;
22mod wasm;
23
24use std::{fmt, str::FromStr, sync::Arc};
25
26use async_graphql::SimpleObject;
27use async_trait::async_trait;
28use committee::Epoch;
29use custom_debug_derive::Debug;
30use dashmap::DashMap;
31use derive_more::Display;
32use linera_base::{
33    abi::Abi,
34    crypto::CryptoHash,
35    data_types::{
36        Amount, ApplicationPermissions, ArithmeticError, Blob, BlockHeight, DecompressionError,
37        Resources, SendMessageRequest, Timestamp, UserApplicationDescription,
38    },
39    doc_scalar, hex_debug,
40    identifiers::{
41        Account, ApplicationId, BlobId, BytecodeId, ChainId, ChannelName, Destination,
42        GenericApplicationId, MessageId, Owner, StreamName, UserApplicationId,
43    },
44    ownership::ChainOwnership,
45};
46use linera_views::{batch::Batch, views::ViewError};
47use serde::{Deserialize, Serialize};
48use system::OpenChainConfig;
49use thiserror::Error;
50
51#[cfg(with_testing)]
52pub use crate::applications::ApplicationRegistry;
53use crate::runtime::ContractSyncRuntime;
54#[cfg(all(with_testing, with_wasm_runtime))]
55pub use crate::wasm::test as wasm_test;
56#[cfg(with_wasm_runtime)]
57pub use crate::wasm::{
58    ContractEntrypoints, ContractSystemApi, ServiceEntrypoints, ServiceSystemApi, SystemApiData,
59    ViewSystemApi, WasmContractModule, WasmExecutionError, WasmServiceModule,
60};
61pub use crate::{
62    applications::ApplicationRegistryView,
63    execution::{ExecutionStateView, ServiceRuntimeEndpoint},
64    execution_state_actor::ExecutionRequest,
65    policy::ResourceControlPolicy,
66    resources::{ResourceController, ResourceTracker},
67    runtime::{
68        ContractSyncRuntimeHandle, ServiceRuntimeRequest, ServiceSyncRuntime,
69        ServiceSyncRuntimeHandle,
70    },
71    system::{
72        SystemExecutionError, SystemExecutionStateView, SystemMessage, SystemOperation,
73        SystemQuery, SystemResponse,
74    },
75    transaction_tracker::TransactionTracker,
76};
77
78/// The maximum length of an event key in bytes.
79const MAX_EVENT_KEY_LEN: usize = 64;
80/// The maximum length of a stream name.
81const MAX_STREAM_NAME_LEN: usize = 64;
82
83/// An implementation of [`UserContractModule`].
84pub type UserContractCode = Box<dyn UserContractModule + Send + Sync>;
85
86/// An implementation of [`UserServiceModule`].
87pub type UserServiceCode = Box<dyn UserServiceModule + Send + Sync>;
88
89/// An implementation of [`UserContract`].
90pub type UserContractInstance = Box<dyn UserContract>;
91
92/// An implementation of [`UserService`].
93pub type UserServiceInstance = Box<dyn UserService>;
94
95/// A factory trait to obtain a [`UserContract`] from a [`UserContractModule`]
96pub trait UserContractModule: dyn_clone::DynClone {
97    fn instantiate(
98        &self,
99        runtime: ContractSyncRuntimeHandle,
100    ) -> Result<UserContractInstance, ExecutionError>;
101}
102
103impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
104    fn from(module: T) -> Self {
105        Box::new(module)
106    }
107}
108
109dyn_clone::clone_trait_object!(UserContractModule);
110
111/// A factory trait to obtain a [`UserService`] from a [`UserServiceModule`]
112pub trait UserServiceModule: dyn_clone::DynClone {
113    fn instantiate(
114        &self,
115        runtime: ServiceSyncRuntimeHandle,
116    ) -> Result<UserServiceInstance, ExecutionError>;
117}
118
119impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
120    fn from(module: T) -> Self {
121        Box::new(module)
122    }
123}
124
125dyn_clone::clone_trait_object!(UserServiceModule);
126
127/// A type for errors happening during execution.
128#[derive(Error, Debug)]
129pub enum ExecutionError {
130    #[error(transparent)]
131    ViewError(#[from] ViewError),
132    #[error(transparent)]
133    ArithmeticError(#[from] ArithmeticError),
134    #[error(transparent)]
135    SystemError(#[from] SystemExecutionError),
136    #[error("User application reported an error: {0}")]
137    UserError(String),
138    #[cfg(any(with_wasmer, with_wasmtime))]
139    #[error(transparent)]
140    WasmError(#[from] WasmExecutionError),
141    #[error(transparent)]
142    JoinError(#[from] linera_base::task::Error),
143    #[error(transparent)]
144    DecompressionError(#[from] DecompressionError),
145    #[error("The given promise is invalid or was polled once already")]
146    InvalidPromise,
147
148    #[error("Attempted to perform a reentrant call to application {0}")]
149    ReentrantCall(UserApplicationId),
150    #[error(
151        "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
152        from `finalize`"
153    )]
154    CrossApplicationCallInFinalize {
155        caller_id: Box<UserApplicationId>,
156        callee_id: Box<UserApplicationId>,
157    },
158    #[error("Attempt to write to storage from a contract")]
159    ServiceWriteAttempt,
160    #[error("Failed to load bytecode from storage {0:?}")]
161    ApplicationBytecodeNotFound(Box<UserApplicationDescription>),
162
163    #[error("Excessive number of bytes read from storage")]
164    ExcessiveRead,
165    #[error("Excessive number of bytes written to storage")]
166    ExcessiveWrite,
167    #[error("Block execution required too much fuel")]
168    MaximumFuelExceeded,
169    #[error("Serialized size of the executed block exceeds limit")]
170    ExecutedBlockTooLarge,
171    #[error("Runtime failed to respond to application")]
172    MissingRuntimeResponse,
173    #[error("Bytecode ID {0:?} is invalid")]
174    InvalidBytecodeId(BytecodeId),
175    #[error("Owner is None")]
176    OwnerIsNone,
177    #[error("Application is not authorized to perform system operations on this chain: {0:}")]
178    UnauthorizedApplication(UserApplicationId),
179    #[error("Failed to make network reqwest")]
180    ReqwestError(#[from] reqwest::Error),
181    #[error("Encountered IO error")]
182    IoError(#[from] std::io::Error),
183    #[error("More recorded oracle responses than expected")]
184    UnexpectedOracleResponse,
185    #[error("Invalid JSON: {}", .0)]
186    Json(#[from] serde_json::Error),
187    #[error(transparent)]
188    Bcs(#[from] bcs::Error),
189    #[error("Recorded response for oracle query has the wrong type")]
190    OracleResponseMismatch,
191    #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
192    AssertBefore {
193        timestamp: Timestamp,
194        local_time: Timestamp,
195    },
196
197    #[error("Event keys can be at most {MAX_EVENT_KEY_LEN} bytes.")]
198    EventKeyTooLong,
199    #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
200    StreamNameTooLong,
201    // TODO(#2127): Remove this error and the unstable-oracles feature once there are fees
202    // and enforced limits for all oracles.
203    #[error("Unstable oracles are disabled on this network.")]
204    UnstableOracle,
205}
206
207/// The public entry points provided by the contract part of an application.
208pub trait UserContract {
209    /// Instantiate the application state on the chain that owns the application.
210    fn instantiate(
211        &mut self,
212        context: OperationContext,
213        argument: Vec<u8>,
214    ) -> Result<(), ExecutionError>;
215
216    /// Applies an operation from the current block.
217    fn execute_operation(
218        &mut self,
219        context: OperationContext,
220        operation: Vec<u8>,
221    ) -> Result<Vec<u8>, ExecutionError>;
222
223    /// Applies a message originating from a cross-chain message.
224    fn execute_message(
225        &mut self,
226        context: MessageContext,
227        message: Vec<u8>,
228    ) -> Result<(), ExecutionError>;
229
230    /// Finishes execution of the current transaction.
231    fn finalize(&mut self, context: FinalizeContext) -> Result<(), ExecutionError>;
232}
233
234/// The public entry points provided by the service part of an application.
235pub trait UserService {
236    /// Executes unmetered read-only queries on the state of this application.
237    fn handle_query(
238        &mut self,
239        context: QueryContext,
240        argument: Vec<u8>,
241    ) -> Result<Vec<u8>, ExecutionError>;
242}
243
244/// The result of calling into a user application.
245#[derive(Default)]
246pub struct ApplicationCallOutcome {
247    /// The return value.
248    pub value: Vec<u8>,
249    /// The externally-visible result.
250    pub execution_outcome: RawExecutionOutcome<Vec<u8>>,
251}
252
253impl ApplicationCallOutcome {
254    /// Adds a `message` to this [`ApplicationCallOutcome`].
255    pub fn with_message(mut self, message: RawOutgoingMessage<Vec<u8>>) -> Self {
256        self.execution_outcome.messages.push(message);
257        self
258    }
259}
260
261/// Configuration options for the execution runtime available to applications.
262#[derive(Clone, Copy, Default)]
263pub struct ExecutionRuntimeConfig {}
264
265/// Requirements for the `extra` field in our state views (and notably the
266/// [`ExecutionStateView`]).
267#[async_trait]
268pub trait ExecutionRuntimeContext {
269    fn chain_id(&self) -> ChainId;
270
271    fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
272
273    fn user_contracts(&self) -> &Arc<DashMap<UserApplicationId, UserContractCode>>;
274
275    fn user_services(&self) -> &Arc<DashMap<UserApplicationId, UserServiceCode>>;
276
277    async fn get_user_contract(
278        &self,
279        description: &UserApplicationDescription,
280    ) -> Result<UserContractCode, ExecutionError>;
281
282    async fn get_user_service(
283        &self,
284        description: &UserApplicationDescription,
285    ) -> Result<UserServiceCode, ExecutionError>;
286
287    async fn get_blob(&self, blob_id: BlobId) -> Result<Blob, ExecutionError>;
288
289    async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
290}
291
292#[derive(Clone, Copy, Debug)]
293pub struct OperationContext {
294    /// The current chain ID.
295    pub chain_id: ChainId,
296    /// The authenticated signer of the operation, if any.
297    pub authenticated_signer: Option<Owner>,
298    /// `None` if this is the transaction entrypoint or the caller doesn't want this particular
299    /// call to be authenticated (e.g. for safety reasons).
300    pub authenticated_caller_id: Option<UserApplicationId>,
301    /// The current block height.
302    pub height: BlockHeight,
303    /// The current index of the operation.
304    pub index: Option<u32>,
305}
306
307#[derive(Clone, Copy, Debug)]
308pub struct MessageContext {
309    /// The current chain ID.
310    pub chain_id: ChainId,
311    /// Whether the message was rejected by the original receiver and is now bouncing back.
312    pub is_bouncing: bool,
313    /// The authenticated signer of the operation that created the message, if any.
314    pub authenticated_signer: Option<Owner>,
315    /// Where to send a refund for the unused part of each grant after execution, if any.
316    pub refund_grant_to: Option<Account>,
317    /// The current block height.
318    pub height: BlockHeight,
319    /// The hash of the remote certificate that created the message.
320    pub certificate_hash: CryptoHash,
321    /// The ID of the message (based on the operation height and index in the remote
322    /// certificate).
323    pub message_id: MessageId,
324}
325
326#[derive(Clone, Copy, Debug)]
327pub struct FinalizeContext {
328    /// The current chain ID.
329    pub chain_id: ChainId,
330    /// The authenticated signer of the operation, if any.
331    pub authenticated_signer: Option<Owner>,
332    /// The current block height.
333    pub height: BlockHeight,
334}
335
336#[derive(Clone, Copy, Debug, Eq, PartialEq)]
337pub struct QueryContext {
338    /// The current chain ID.
339    pub chain_id: ChainId,
340    /// The height of the next block on this chain.
341    pub next_block_height: BlockHeight,
342    /// The local time in the node executing the query.
343    pub local_time: Timestamp,
344}
345
346pub trait BaseRuntime {
347    type Read: fmt::Debug + Send + Sync;
348    type ContainsKey: fmt::Debug + Send + Sync;
349    type ContainsKeys: fmt::Debug + Send + Sync;
350    type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
351    type ReadValueBytes: fmt::Debug + Send + Sync;
352    type FindKeysByPrefix: fmt::Debug + Send + Sync;
353    type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
354
355    /// The current chain ID.
356    fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
357
358    /// The current block height.
359    fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
360
361    /// The current application ID.
362    fn application_id(&mut self) -> Result<UserApplicationId, ExecutionError>;
363
364    /// The current application creator's chain ID.
365    fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
366
367    /// The current application parameters.
368    fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
369
370    /// Reads the system timestamp.
371    fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
372
373    /// Reads the balance of the chain.
374    fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
375
376    /// Reads the owner balance.
377    fn read_owner_balance(&mut self, owner: Owner) -> Result<Amount, ExecutionError>;
378
379    /// Reads the balances from all owners.
380    fn read_owner_balances(&mut self) -> Result<Vec<(Owner, Amount)>, ExecutionError>;
381
382    /// Reads balance owners.
383    fn read_balance_owners(&mut self) -> Result<Vec<Owner>, ExecutionError>;
384
385    /// Reads the current ownership configuration for this chain.
386    fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
387
388    /// Tests whether a key exists in the key-value store
389    #[cfg(feature = "test")]
390    fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
391        let promise = self.contains_key_new(key)?;
392        self.contains_key_wait(&promise)
393    }
394
395    /// Creates the promise to test whether a key exists in the key-value store
396    fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
397
398    /// Resolves the promise to test whether a key exists in the key-value store
399    fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
400
401    /// Tests whether multiple keys exist in the key-value store
402    #[cfg(feature = "test")]
403    fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
404        let promise = self.contains_keys_new(keys)?;
405        self.contains_keys_wait(&promise)
406    }
407
408    /// Creates the promise to test whether multiple keys exist in the key-value store
409    fn contains_keys_new(
410        &mut self,
411        keys: Vec<Vec<u8>>,
412    ) -> Result<Self::ContainsKeys, ExecutionError>;
413
414    /// Resolves the promise to test whether multiple keys exist in the key-value store
415    fn contains_keys_wait(
416        &mut self,
417        promise: &Self::ContainsKeys,
418    ) -> Result<Vec<bool>, ExecutionError>;
419
420    /// Reads several keys from the key-value store
421    #[cfg(feature = "test")]
422    fn read_multi_values_bytes(
423        &mut self,
424        keys: Vec<Vec<u8>>,
425    ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
426        let promise = self.read_multi_values_bytes_new(keys)?;
427        self.read_multi_values_bytes_wait(&promise)
428    }
429
430    /// Creates the promise to access several keys from the key-value store
431    fn read_multi_values_bytes_new(
432        &mut self,
433        keys: Vec<Vec<u8>>,
434    ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
435
436    /// Resolves the promise to access several keys from the key-value store
437    fn read_multi_values_bytes_wait(
438        &mut self,
439        promise: &Self::ReadMultiValuesBytes,
440    ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
441
442    /// Reads the key from the key-value store
443    #[cfg(feature = "test")]
444    fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
445        let promise = self.read_value_bytes_new(key)?;
446        self.read_value_bytes_wait(&promise)
447    }
448
449    /// Creates the promise to access a key from the key-value store
450    fn read_value_bytes_new(
451        &mut self,
452        key: Vec<u8>,
453    ) -> Result<Self::ReadValueBytes, ExecutionError>;
454
455    /// Resolves the promise to access a key from the key-value store
456    fn read_value_bytes_wait(
457        &mut self,
458        promise: &Self::ReadValueBytes,
459    ) -> Result<Option<Vec<u8>>, ExecutionError>;
460
461    /// Creates the promise to access keys having a specific prefix
462    fn find_keys_by_prefix_new(
463        &mut self,
464        key_prefix: Vec<u8>,
465    ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
466
467    /// Resolves the promise to access keys having a specific prefix
468    fn find_keys_by_prefix_wait(
469        &mut self,
470        promise: &Self::FindKeysByPrefix,
471    ) -> Result<Vec<Vec<u8>>, ExecutionError>;
472
473    /// Reads the data from the key/values having a specific prefix.
474    #[cfg(feature = "test")]
475    #[expect(clippy::type_complexity)]
476    fn find_key_values_by_prefix(
477        &mut self,
478        key_prefix: Vec<u8>,
479    ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
480        let promise = self.find_key_values_by_prefix_new(key_prefix)?;
481        self.find_key_values_by_prefix_wait(&promise)
482    }
483
484    /// Creates the promise to access key/values having a specific prefix
485    fn find_key_values_by_prefix_new(
486        &mut self,
487        key_prefix: Vec<u8>,
488    ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
489
490    /// Resolves the promise to access key/values having a specific prefix
491    #[expect(clippy::type_complexity)]
492    fn find_key_values_by_prefix_wait(
493        &mut self,
494        promise: &Self::FindKeyValuesByPrefix,
495    ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
496
497    /// Queries a service.
498    fn query_service(
499        &mut self,
500        application_id: ApplicationId,
501        query: Vec<u8>,
502    ) -> Result<Vec<u8>, ExecutionError>;
503
504    /// Makes a POST request to the given URL and returns the answer, if any.
505    fn http_post(
506        &mut self,
507        url: &str,
508        content_type: String,
509        payload: Vec<u8>,
510    ) -> Result<Vec<u8>, ExecutionError>;
511
512    /// Ensures that the current time at block validation is `< timestamp`. Note that block
513    /// validation happens at or after the block timestamp, but isn't necessarily the same.
514    ///
515    /// Cannot be used in fast blocks: A block using this call should be proposed by a regular
516    /// owner, not a super owner.
517    fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
518
519    /// Reads a data blob content specified by a given hash.
520    fn read_data_blob(&mut self, hash: &CryptoHash) -> Result<Vec<u8>, ExecutionError>;
521
522    /// Asserts the existence of a data blob with the given hash.
523    fn assert_data_blob_exists(&mut self, hash: &CryptoHash) -> Result<(), ExecutionError>;
524}
525
526pub trait ServiceRuntime: BaseRuntime {
527    /// Queries another application.
528    fn try_query_application(
529        &mut self,
530        queried_id: UserApplicationId,
531        argument: Vec<u8>,
532    ) -> Result<Vec<u8>, ExecutionError>;
533
534    /// Fetches blob of bytes from an arbitrary URL.
535    fn fetch_url(&mut self, url: &str) -> Result<Vec<u8>, ExecutionError>;
536}
537
538pub trait ContractRuntime: BaseRuntime {
539    /// The authenticated signer for this execution, if there is one.
540    fn authenticated_signer(&mut self) -> Result<Option<Owner>, ExecutionError>;
541
542    /// The current message ID, if there is one.
543    fn message_id(&mut self) -> Result<Option<MessageId>, ExecutionError>;
544
545    /// If the current message (if there is one) was rejected by its destination and is now
546    /// bouncing back.
547    fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
548
549    /// The optional authenticated caller application ID, if it was provided and if there is one
550    /// based on the execution context.
551    fn authenticated_caller_id(&mut self) -> Result<Option<UserApplicationId>, ExecutionError>;
552
553    /// Returns the amount of execution fuel remaining before execution is aborted.
554    fn remaining_fuel(&mut self) -> Result<u64, ExecutionError>;
555
556    /// Consumes some of the execution fuel.
557    fn consume_fuel(&mut self, fuel: u64) -> Result<(), ExecutionError>;
558
559    /// Schedules a message to be sent.
560    fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
561
562    /// Schedules to subscribe to some `channel` on a `chain`.
563    fn subscribe(&mut self, chain: ChainId, channel: ChannelName) -> Result<(), ExecutionError>;
564
565    /// Schedules to unsubscribe to some `channel` on a `chain`.
566    fn unsubscribe(&mut self, chain: ChainId, channel: ChannelName) -> Result<(), ExecutionError>;
567
568    /// Transfers amount from source to destination.
569    fn transfer(
570        &mut self,
571        source: Option<Owner>,
572        destination: Account,
573        amount: Amount,
574    ) -> Result<(), ExecutionError>;
575
576    /// Claims amount from source to destination.
577    fn claim(
578        &mut self,
579        source: Account,
580        destination: Account,
581        amount: Amount,
582    ) -> Result<(), ExecutionError>;
583
584    /// Calls another application. Forwarded sessions will now be visible to
585    /// `callee_id` (but not to the caller any more).
586    fn try_call_application(
587        &mut self,
588        authenticated: bool,
589        callee_id: UserApplicationId,
590        argument: Vec<u8>,
591    ) -> Result<Vec<u8>, ExecutionError>;
592
593    /// Adds a new item to an event stream.
594    fn emit(
595        &mut self,
596        name: StreamName,
597        key: Vec<u8>,
598        value: Vec<u8>,
599    ) -> Result<(), ExecutionError>;
600
601    /// Opens a new chain.
602    fn open_chain(
603        &mut self,
604        ownership: ChainOwnership,
605        application_permissions: ApplicationPermissions,
606        balance: Amount,
607    ) -> Result<(MessageId, ChainId), ExecutionError>;
608
609    /// Closes the current chain.
610    fn close_chain(&mut self) -> Result<(), ExecutionError>;
611
612    /// Writes a batch of changes.
613    fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
614}
615
616/// An operation to be executed in a block.
617#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
618pub enum Operation {
619    /// A system operation.
620    System(SystemOperation),
621    /// A user operation (in serialized form).
622    User {
623        application_id: UserApplicationId,
624        #[serde(with = "serde_bytes")]
625        #[debug(with = "hex_debug")]
626        bytes: Vec<u8>,
627    },
628}
629
630/// A message to be sent and possibly executed in the receiver's block.
631#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
632pub enum Message {
633    /// A system message.
634    System(SystemMessage),
635    /// A user message (in serialized form).
636    User {
637        application_id: UserApplicationId,
638        #[serde(with = "serde_bytes")]
639        #[debug(with = "hex_debug")]
640        bytes: Vec<u8>,
641    },
642}
643
644/// An query to be sent and possibly executed in the receiver's block.
645#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
646pub enum Query {
647    /// A system query.
648    System(SystemQuery),
649    /// A user query (in serialized form).
650    User {
651        application_id: UserApplicationId,
652        #[serde(with = "serde_bytes")]
653        #[debug(with = "hex_debug")]
654        bytes: Vec<u8>,
655    },
656}
657
658/// The response to a query.
659#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
660pub enum Response {
661    /// A system response.
662    System(SystemResponse),
663    /// A user response (in serialized form).
664    User(
665        #[serde(with = "serde_bytes")]
666        #[debug(with = "hex_debug")]
667        Vec<u8>,
668    ),
669}
670
671/// A message together with routing information.
672#[derive(Clone, Debug)]
673#[cfg_attr(with_testing, derive(Eq, PartialEq))]
674pub struct RawOutgoingMessage<Message, Grant = Resources> {
675    /// The destination of the message.
676    pub destination: Destination,
677    /// Whether the message is authenticated.
678    pub authenticated: bool,
679    /// The grant needed for message execution, typically specified as an `Amount` or as `Resources`.
680    pub grant: Grant,
681    /// The kind of outgoing message being sent.
682    pub kind: MessageKind,
683    /// The message itself.
684    pub message: Message,
685}
686
687impl<Message> From<SendMessageRequest<Message>> for RawOutgoingMessage<Message, Resources> {
688    fn from(request: SendMessageRequest<Message>) -> Self {
689        let SendMessageRequest {
690            destination,
691            authenticated,
692            grant,
693            is_tracked,
694            message,
695        } = request;
696
697        let kind = if is_tracked {
698            MessageKind::Tracked
699        } else {
700            MessageKind::Simple
701        };
702
703        RawOutgoingMessage {
704            destination,
705            authenticated,
706            grant,
707            kind,
708            message,
709        }
710    }
711}
712
713/// The kind of outgoing message being sent.
714#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy)]
715pub enum MessageKind {
716    /// The message can be skipped or rejected. No receipt is requested.
717    Simple,
718    /// The message cannot be skipped nor rejected. No receipt is requested.
719    /// This only concerns certain system messages that cannot fail.
720    Protected,
721    /// The message cannot be skipped but can be rejected. A receipt must be sent
722    /// when the message is rejected in a block of the receiver.
723    Tracked,
724    /// This message is a receipt automatically created when the original message was rejected.
725    Bouncing,
726}
727
728/// Externally visible results of an execution. These results are meant in the context of
729/// the application that created them.
730#[derive(Debug)]
731#[cfg_attr(with_testing, derive(Eq, PartialEq))]
732pub struct RawExecutionOutcome<Message, Grant = Resources> {
733    /// The signer who created the messages.
734    pub authenticated_signer: Option<Owner>,
735    /// Where to send a refund for the unused part of each grant after execution, if any.
736    pub refund_grant_to: Option<Account>,
737    /// Sends messages to the given destinations, possibly forwarding the authenticated
738    /// signer and including grant with the refund policy described above.
739    pub messages: Vec<RawOutgoingMessage<Message, Grant>>,
740    /// Events recorded by contracts' `emit` calls.
741    pub events: Vec<(StreamName, Vec<u8>, Vec<u8>)>,
742    /// Subscribe chains to channels.
743    pub subscribe: Vec<(ChannelName, ChainId)>,
744    /// Unsubscribe chains to channels.
745    pub unsubscribe: Vec<(ChannelName, ChainId)>,
746}
747
748/// The identifier of a channel, relative to a particular application.
749#[derive(
750    Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash, Serialize, Deserialize, SimpleObject,
751)]
752pub struct ChannelSubscription {
753    /// The chain ID broadcasting on this channel.
754    pub chain_id: ChainId,
755    /// The name of the channel.
756    pub name: ChannelName,
757}
758
759/// Externally visible results of an execution, tagged by their application.
760#[derive(Debug)]
761#[cfg_attr(with_testing, derive(Eq, PartialEq))]
762#[expect(clippy::large_enum_variant)]
763pub enum ExecutionOutcome {
764    System(RawExecutionOutcome<SystemMessage, Amount>),
765    User(UserApplicationId, RawExecutionOutcome<Vec<u8>, Amount>),
766}
767
768impl ExecutionOutcome {
769    pub fn application_id(&self) -> GenericApplicationId {
770        match self {
771            ExecutionOutcome::System(_) => GenericApplicationId::System,
772            ExecutionOutcome::User(app_id, _) => GenericApplicationId::User(*app_id),
773        }
774    }
775
776    pub fn message_count(&self) -> usize {
777        match self {
778            ExecutionOutcome::System(outcome) => outcome.messages.len(),
779            ExecutionOutcome::User(_, outcome) => outcome.messages.len(),
780        }
781    }
782}
783
784impl<Message, Grant> RawExecutionOutcome<Message, Grant> {
785    pub fn with_authenticated_signer(mut self, authenticated_signer: Option<Owner>) -> Self {
786        self.authenticated_signer = authenticated_signer;
787        self
788    }
789
790    pub fn with_refund_grant_to(mut self, refund_grant_to: Option<Account>) -> Self {
791        self.refund_grant_to = refund_grant_to;
792        self
793    }
794
795    /// Adds a `message` to this [`RawExecutionOutcome`].
796    pub fn with_message(mut self, message: RawOutgoingMessage<Message, Grant>) -> Self {
797        self.messages.push(message);
798        self
799    }
800}
801
802impl<Message, Grant> Default for RawExecutionOutcome<Message, Grant> {
803    fn default() -> Self {
804        Self {
805            authenticated_signer: None,
806            refund_grant_to: None,
807            messages: Vec::new(),
808            events: Vec::new(),
809            subscribe: Vec::new(),
810            unsubscribe: Vec::new(),
811        }
812    }
813}
814
815impl<Message> RawOutgoingMessage<Message, Resources> {
816    pub fn into_priced(
817        self,
818        policy: &ResourceControlPolicy,
819    ) -> Result<RawOutgoingMessage<Message, Amount>, ArithmeticError> {
820        let RawOutgoingMessage {
821            destination,
822            authenticated,
823            grant,
824            kind,
825            message,
826        } = self;
827        Ok(RawOutgoingMessage {
828            destination,
829            authenticated,
830            grant: policy.total_price(&grant)?,
831            kind,
832            message,
833        })
834    }
835}
836
837impl<Message> RawExecutionOutcome<Message, Resources> {
838    pub fn into_priced(
839        self,
840        policy: &ResourceControlPolicy,
841    ) -> Result<RawExecutionOutcome<Message, Amount>, ArithmeticError> {
842        let RawExecutionOutcome {
843            authenticated_signer,
844            refund_grant_to,
845            messages,
846            events,
847            subscribe,
848            unsubscribe,
849        } = self;
850        let messages = messages
851            .into_iter()
852            .map(|message| message.into_priced(policy))
853            .collect::<Result<_, _>>()?;
854        Ok(RawExecutionOutcome {
855            authenticated_signer,
856            refund_grant_to,
857            messages,
858            events,
859            subscribe,
860            unsubscribe,
861        })
862    }
863}
864
865impl OperationContext {
866    fn refund_grant_to(&self) -> Option<Account> {
867        Some(Account {
868            chain_id: self.chain_id,
869            owner: self.authenticated_signer,
870        })
871    }
872
873    fn next_message_id(&self, next_message_index: u32) -> MessageId {
874        MessageId {
875            chain_id: self.chain_id,
876            height: self.height,
877            index: next_message_index,
878        }
879    }
880}
881
882#[cfg(with_testing)]
883#[derive(Clone)]
884pub struct TestExecutionRuntimeContext {
885    chain_id: ChainId,
886    execution_runtime_config: ExecutionRuntimeConfig,
887    user_contracts: Arc<DashMap<UserApplicationId, UserContractCode>>,
888    user_services: Arc<DashMap<UserApplicationId, UserServiceCode>>,
889    blobs: Arc<DashMap<BlobId, Blob>>,
890}
891
892#[cfg(with_testing)]
893impl TestExecutionRuntimeContext {
894    pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
895        Self {
896            chain_id,
897            execution_runtime_config,
898            user_contracts: Arc::default(),
899            user_services: Arc::default(),
900            blobs: Arc::default(),
901        }
902    }
903
904    pub fn add_blobs(&self, blobs: Vec<Blob>) {
905        for blob in blobs {
906            self.blobs.insert(blob.id(), blob);
907        }
908    }
909}
910
911#[cfg(with_testing)]
912#[async_trait]
913impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
914    fn chain_id(&self) -> ChainId {
915        self.chain_id
916    }
917
918    fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
919        self.execution_runtime_config
920    }
921
922    fn user_contracts(&self) -> &Arc<DashMap<UserApplicationId, UserContractCode>> {
923        &self.user_contracts
924    }
925
926    fn user_services(&self) -> &Arc<DashMap<UserApplicationId, UserServiceCode>> {
927        &self.user_services
928    }
929
930    async fn get_user_contract(
931        &self,
932        description: &UserApplicationDescription,
933    ) -> Result<UserContractCode, ExecutionError> {
934        let application_id = description.into();
935        Ok(self
936            .user_contracts()
937            .get(&application_id)
938            .ok_or_else(|| {
939                ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
940            })?
941            .clone())
942    }
943
944    async fn get_user_service(
945        &self,
946        description: &UserApplicationDescription,
947    ) -> Result<UserServiceCode, ExecutionError> {
948        let application_id = description.into();
949        Ok(self
950            .user_services()
951            .get(&application_id)
952            .ok_or_else(|| {
953                ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
954            })?
955            .clone())
956    }
957
958    async fn get_blob(&self, blob_id: BlobId) -> Result<Blob, ExecutionError> {
959        Ok(self
960            .blobs
961            .get(&blob_id)
962            .ok_or_else(|| SystemExecutionError::BlobNotFoundOnRead(blob_id))?
963            .clone())
964    }
965
966    async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
967        Ok(self.blobs.contains_key(&blob_id))
968    }
969}
970
971impl From<SystemOperation> for Operation {
972    fn from(operation: SystemOperation) -> Self {
973        Operation::System(operation)
974    }
975}
976
977impl Operation {
978    pub fn system(operation: SystemOperation) -> Self {
979        Operation::System(operation)
980    }
981
982    pub fn user<A: Abi>(
983        application_id: UserApplicationId<A>,
984        operation: &A::Operation,
985    ) -> Result<Self, bcs::Error> {
986        let application_id = application_id.forget_abi();
987        let bytes = bcs::to_bytes(&operation)?;
988        Ok(Operation::User {
989            application_id,
990            bytes,
991        })
992    }
993
994    pub fn application_id(&self) -> GenericApplicationId {
995        match self {
996            Self::System(_) => GenericApplicationId::System,
997            Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
998        }
999    }
1000}
1001
1002impl From<SystemMessage> for Message {
1003    fn from(message: SystemMessage) -> Self {
1004        Message::System(message)
1005    }
1006}
1007
1008impl Message {
1009    pub fn system(message: SystemMessage) -> Self {
1010        Message::System(message)
1011    }
1012
1013    pub fn user<A: Abi, M: Serialize>(
1014        application_id: UserApplicationId<A>,
1015        message: &M,
1016    ) -> Result<Self, bcs::Error> {
1017        let application_id = application_id.forget_abi();
1018        let bytes = bcs::to_bytes(&message)?;
1019        Ok(Message::User {
1020            application_id,
1021            bytes,
1022        })
1023    }
1024
1025    pub fn application_id(&self) -> GenericApplicationId {
1026        match self {
1027            Self::System(_) => GenericApplicationId::System,
1028            Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1029        }
1030    }
1031
1032    /// Returns whether this message must be added to the inbox.
1033    pub fn goes_to_inbox(&self) -> bool {
1034        !matches!(
1035            self,
1036            Message::System(SystemMessage::Subscribe { .. } | SystemMessage::Unsubscribe { .. })
1037        )
1038    }
1039
1040    pub fn matches_subscribe(&self) -> Option<(&ChainId, &ChannelSubscription)> {
1041        match self {
1042            Message::System(SystemMessage::Subscribe { id, subscription }) => {
1043                Some((id, subscription))
1044            }
1045            _ => None,
1046        }
1047    }
1048
1049    pub fn matches_unsubscribe(&self) -> Option<(&ChainId, &ChannelSubscription)> {
1050        match self {
1051            Message::System(SystemMessage::Unsubscribe { id, subscription }) => {
1052                Some((id, subscription))
1053            }
1054            _ => None,
1055        }
1056    }
1057
1058    pub fn matches_open_chain(&self) -> Option<&OpenChainConfig> {
1059        match self {
1060            Message::System(SystemMessage::OpenChain(config)) => Some(config),
1061            _ => None,
1062        }
1063    }
1064}
1065
1066impl From<SystemQuery> for Query {
1067    fn from(query: SystemQuery) -> Self {
1068        Query::System(query)
1069    }
1070}
1071
1072impl Query {
1073    pub fn system(query: SystemQuery) -> Self {
1074        Query::System(query)
1075    }
1076
1077    pub fn user<A: Abi>(
1078        application_id: UserApplicationId<A>,
1079        query: &A::Query,
1080    ) -> Result<Self, serde_json::Error> {
1081        let application_id = application_id.forget_abi();
1082        let bytes = serde_json::to_vec(&query)?;
1083        Ok(Query::User {
1084            application_id,
1085            bytes,
1086        })
1087    }
1088
1089    pub fn application_id(&self) -> GenericApplicationId {
1090        match self {
1091            Self::System(_) => GenericApplicationId::System,
1092            Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1093        }
1094    }
1095}
1096
1097impl From<SystemResponse> for Response {
1098    fn from(response: SystemResponse) -> Self {
1099        Response::System(response)
1100    }
1101}
1102
1103impl From<Vec<u8>> for Response {
1104    fn from(response: Vec<u8>) -> Self {
1105        Response::User(response)
1106    }
1107}
1108
1109/// The state of a blob of binary data.
1110#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1111pub struct BlobState {
1112    /// Hash of the last `Certificate` that published or used this blob.
1113    pub last_used_by: CryptoHash,
1114    /// Epoch of the `last_used_by` certificate.
1115    pub epoch: Epoch,
1116}
1117
1118/// The runtime to use for running the application.
1119#[derive(Clone, Copy, Display)]
1120#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1121pub enum WasmRuntime {
1122    #[cfg(with_wasmer)]
1123    #[default]
1124    #[display("wasmer")]
1125    Wasmer,
1126    #[cfg(with_wasmtime)]
1127    #[cfg_attr(not(with_wasmer), default)]
1128    #[display("wasmtime")]
1129    Wasmtime,
1130    #[cfg(with_wasmer)]
1131    WasmerWithSanitizer,
1132    #[cfg(with_wasmtime)]
1133    WasmtimeWithSanitizer,
1134}
1135
1136/// Trait used to select a default WasmRuntime, if one is available.
1137pub trait WithWasmDefault {
1138    fn with_wasm_default(self) -> Self;
1139}
1140
1141impl WasmRuntime {
1142    #[cfg(with_wasm_runtime)]
1143    pub fn default_with_sanitizer() -> Self {
1144        cfg_if::cfg_if! {
1145            if #[cfg(with_wasmer)] {
1146                WasmRuntime::WasmerWithSanitizer
1147            } else if #[cfg(with_wasmtime)] {
1148                WasmRuntime::WasmtimeWithSanitizer
1149            } else {
1150                compile_error!("BUG: Wasm runtime unhandled in `WasmRuntime::default_with_sanitizer`")
1151            }
1152        }
1153    }
1154
1155    pub fn needs_sanitizer(self) -> bool {
1156        match self {
1157            #[cfg(with_wasmer)]
1158            WasmRuntime::WasmerWithSanitizer => true,
1159            #[cfg(with_wasmtime)]
1160            WasmRuntime::WasmtimeWithSanitizer => true,
1161            #[cfg(with_wasm_runtime)]
1162            _ => false,
1163        }
1164    }
1165}
1166
1167impl WithWasmDefault for Option<WasmRuntime> {
1168    fn with_wasm_default(self) -> Self {
1169        #[cfg(with_wasm_runtime)]
1170        {
1171            Some(self.unwrap_or_default())
1172        }
1173        #[cfg(not(with_wasm_runtime))]
1174        {
1175            None
1176        }
1177    }
1178}
1179
1180impl FromStr for WasmRuntime {
1181    type Err = InvalidWasmRuntime;
1182
1183    fn from_str(string: &str) -> Result<Self, Self::Err> {
1184        match string {
1185            #[cfg(with_wasmer)]
1186            "wasmer" => Ok(WasmRuntime::Wasmer),
1187            #[cfg(with_wasmtime)]
1188            "wasmtime" => Ok(WasmRuntime::Wasmtime),
1189            unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1190        }
1191    }
1192}
1193
1194/// Attempts to create an invalid [`WasmRuntime`] instance from a string.
1195#[derive(Clone, Debug, Error)]
1196#[error("{0:?} is not a valid WebAssembly runtime")]
1197pub struct InvalidWasmRuntime(String);
1198
1199doc_scalar!(Operation, "An operation to be executed in a block");
1200doc_scalar!(
1201    Message,
1202    "An message to be sent and possibly executed in the receiver's block."
1203);
1204doc_scalar!(MessageKind, "The kind of outgoing message being sent");