1#![deny(clippy::large_futures)]
8
9pub mod committee;
10mod execution;
11mod execution_state_actor;
12mod graphql;
13mod policy;
14mod resources;
15#[cfg(with_revm)]
16pub mod revm;
17mod runtime;
18pub mod system;
19#[cfg(with_testing)]
20pub mod test_utils;
21mod transaction_tracker;
22mod util;
23mod wasm;
24
25use std::{any::Any, fmt, str::FromStr, sync::Arc};
26
27use async_graphql::SimpleObject;
28use async_trait::async_trait;
29use committee::Epoch;
30use custom_debug_derive::Debug;
31use dashmap::DashMap;
32use derive_more::Display;
33#[cfg(web)]
34use js_sys::wasm_bindgen::JsValue;
35use linera_base::{
36 abi::Abi,
37 crypto::{BcsHashable, CryptoHash},
38 data_types::{
39 Amount, ApplicationDescription, ApplicationPermissions, ArithmeticError, Blob, BlockHeight,
40 DecompressionError, Resources, SendMessageRequest, Timestamp,
41 },
42 doc_scalar, hex_debug, http,
43 identifiers::{
44 Account, AccountOwner, ApplicationId, BlobId, BlobType, ChainId, ChannelName, Destination,
45 EventId, GenericApplicationId, MessageId, ModuleId, StreamName,
46 },
47 ownership::ChainOwnership,
48 task,
49};
50use linera_views::{batch::Batch, views::ViewError};
51use serde::{Deserialize, Serialize};
52use system::{AdminOperation, OpenChainConfig};
53use thiserror::Error;
54
55#[cfg(with_revm)]
56use crate::revm::EvmExecutionError;
57use crate::runtime::ContractSyncRuntime;
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 execution::{ExecutionStateView, ServiceRuntimeEndpoint},
67 execution_state_actor::ExecutionRequest,
68 policy::ResourceControlPolicy,
69 resources::{ResourceController, ResourceTracker},
70 runtime::{
71 ContractSyncRuntimeHandle, ServiceRuntimeRequest, ServiceSyncRuntime,
72 ServiceSyncRuntimeHandle,
73 },
74 system::{
75 SystemExecutionStateView, SystemMessage, SystemOperation, SystemQuery, SystemResponse,
76 },
77 transaction_tracker::{TransactionOutcome, TransactionTracker},
78};
79
80const MAX_STREAM_NAME_LEN: usize = 64;
82
83#[derive(Clone)]
85pub struct UserContractCode(Box<dyn UserContractModule>);
86
87#[derive(Clone)]
89pub struct UserServiceCode(Box<dyn UserServiceModule>);
90
91pub type UserContractInstance = Box<dyn UserContract>;
93
94pub type UserServiceInstance = Box<dyn UserService>;
96
97pub trait UserContractModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
99 fn instantiate(
100 &self,
101 runtime: ContractSyncRuntimeHandle,
102 ) -> Result<UserContractInstance, ExecutionError>;
103}
104
105impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
106 fn from(module: T) -> Self {
107 Self(Box::new(module))
108 }
109}
110
111dyn_clone::clone_trait_object!(UserContractModule);
112
113pub trait UserServiceModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
115 fn instantiate(
116 &self,
117 runtime: ServiceSyncRuntimeHandle,
118 ) -> Result<UserServiceInstance, ExecutionError>;
119}
120
121impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
122 fn from(module: T) -> Self {
123 Self(Box::new(module))
124 }
125}
126
127dyn_clone::clone_trait_object!(UserServiceModule);
128
129impl UserServiceCode {
130 fn instantiate(
131 &self,
132 runtime: ServiceSyncRuntimeHandle,
133 ) -> Result<UserServiceInstance, ExecutionError> {
134 self.0.instantiate(runtime)
135 }
136}
137
138impl UserContractCode {
139 fn instantiate(
140 &self,
141 runtime: ContractSyncRuntimeHandle,
142 ) -> Result<UserContractInstance, ExecutionError> {
143 self.0.instantiate(runtime)
144 }
145}
146
147#[cfg(web)]
148const _: () = {
149 impl From<UserContractCode> for JsValue {
153 fn from(code: UserContractCode) -> JsValue {
154 let module: WasmContractModule = *(code.0 as Box<dyn Any>)
155 .downcast()
156 .expect("we only support Wasm modules on the Web for now");
157 module.into()
158 }
159 }
160
161 impl From<UserServiceCode> for JsValue {
162 fn from(code: UserServiceCode) -> JsValue {
163 let module: WasmServiceModule = *(code.0 as Box<dyn Any>)
164 .downcast()
165 .expect("we only support Wasm modules on the Web for now");
166 module.into()
167 }
168 }
169
170 impl TryFrom<JsValue> for UserContractCode {
171 type Error = JsValue;
172 fn try_from(value: JsValue) -> Result<Self, JsValue> {
173 WasmContractModule::try_from(value).map(Into::into)
174 }
175 }
176
177 impl TryFrom<JsValue> for UserServiceCode {
178 type Error = JsValue;
179 fn try_from(value: JsValue) -> Result<Self, JsValue> {
180 WasmServiceModule::try_from(value).map(Into::into)
181 }
182 }
183};
184
185#[derive(Error, Debug)]
187pub enum ExecutionError {
188 #[error(transparent)]
189 ViewError(ViewError),
190 #[error(transparent)]
191 ArithmeticError(#[from] ArithmeticError),
192 #[error("User application reported an error: {0}")]
193 UserError(String),
194 #[cfg(with_wasm_runtime)]
195 #[error(transparent)]
196 WasmError(#[from] WasmExecutionError),
197 #[cfg(with_revm)]
198 #[error(transparent)]
199 EvmError(#[from] EvmExecutionError),
200 #[error(transparent)]
201 DecompressionError(#[from] DecompressionError),
202 #[error("The given promise is invalid or was polled once already")]
203 InvalidPromise,
204
205 #[error("Attempted to perform a reentrant call to application {0}")]
206 ReentrantCall(ApplicationId),
207 #[error(
208 "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
209 from `finalize`"
210 )]
211 CrossApplicationCallInFinalize {
212 caller_id: Box<ApplicationId>,
213 callee_id: Box<ApplicationId>,
214 },
215 #[error("Attempt to write to storage from a contract")]
216 ServiceWriteAttempt,
217 #[error("Failed to load bytecode from storage {0:?}")]
218 ApplicationBytecodeNotFound(Box<ApplicationDescription>),
219 #[error("Unsupported dynamic application load: {0:?}")]
221 UnsupportedDynamicApplicationLoad(Box<ApplicationId>),
222
223 #[error("Excessive number of bytes read from storage")]
224 ExcessiveRead,
225 #[error("Excessive number of bytes written to storage")]
226 ExcessiveWrite,
227 #[error("Block execution required too much fuel")]
228 MaximumFuelExceeded,
229 #[error("Services running as oracles in block took longer than allowed")]
230 MaximumServiceOracleExecutionTimeExceeded,
231 #[error("Service running as an oracle produced a response that's too large")]
232 ServiceOracleResponseTooLarge,
233 #[error("Serialized size of the block exceeds limit")]
234 BlockTooLarge,
235 #[error("HTTP response exceeds the size limit of {limit} bytes, having at least {size} bytes")]
236 HttpResponseSizeLimitExceeded { limit: u64, size: u64 },
237 #[error("Runtime failed to respond to application")]
238 MissingRuntimeResponse,
239 #[error("Module ID {0:?} is invalid")]
240 InvalidModuleId(ModuleId),
241 #[error("AccountOwner is None")]
242 OwnerIsNone,
243 #[error("Application is not authorized to perform system operations on this chain: {0:}")]
244 UnauthorizedApplication(ApplicationId),
245 #[error("Failed to make network reqwest: {0}")]
246 ReqwestError(#[from] reqwest::Error),
247 #[error("Encountered I/O error: {0}")]
248 IoError(#[from] std::io::Error),
249 #[error("More recorded oracle responses than expected")]
250 UnexpectedOracleResponse,
251 #[error("Invalid JSON: {0}")]
252 JsonError(#[from] serde_json::Error),
253 #[error(transparent)]
254 BcsError(#[from] bcs::Error),
255 #[error("Recorded response for oracle query has the wrong type")]
256 OracleResponseMismatch,
257 #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
258 AssertBefore {
259 timestamp: Timestamp,
260 local_time: Timestamp,
261 },
262
263 #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
264 StreamNameTooLong,
265 #[error("Blob exceeds size limit")]
266 BlobTooLarge,
267 #[error("Bytecode exceeds size limit")]
268 BytecodeTooLarge,
269 #[error("Attempt to perform an HTTP request to an unauthorized host: {0:?}")]
270 UnauthorizedHttpRequest(reqwest::Url),
271 #[error("Attempt to perform an HTTP request to an invalid URL")]
272 InvalidUrlForHttpRequest(#[from] url::ParseError),
273 #[error("Failed to send contract code to worker thread: {0:?}")]
274 ContractModuleSend(#[from] linera_base::task::SendError<UserContractCode>),
275 #[error("Failed to send service code to worker thread: {0:?}")]
276 ServiceModuleSend(#[from] linera_base::task::SendError<UserServiceCode>),
277 #[error("Blobs not found: {0:?}")]
278 BlobsNotFound(Vec<BlobId>),
279
280 #[error("Invalid HTTP header name used for HTTP request")]
281 InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
282 #[error("Invalid HTTP header value used for HTTP request")]
283 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
284
285 #[error("Invalid admin ID in new chain: {0}")]
286 InvalidNewChainAdminId(ChainId),
287 #[error("Invalid committees")]
288 InvalidCommittees,
289 #[error("{epoch:?} is not recognized by chain {chain_id:}")]
290 InvalidEpoch { chain_id: ChainId, epoch: Epoch },
291 #[error("Transfer must have positive amount")]
292 IncorrectTransferAmount,
293 #[error("Transfer from owned account must be authenticated by the right signer")]
294 UnauthenticatedTransferOwner,
295 #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
296 InsufficientFunding {
297 balance: Amount,
298 account: AccountOwner,
299 },
300 #[error("Required execution fees exceeded the total funding available: {balance}")]
301 InsufficientFundingForFees { balance: Amount },
302 #[error("Claim must have positive amount")]
303 IncorrectClaimAmount,
304 #[error("Claim must be authenticated by the right signer")]
305 UnauthenticatedClaimOwner,
306 #[error("Admin operations are only allowed on the admin chain.")]
307 AdminOperationOnNonAdminChain,
308 #[error("Failed to create new committee: expected {expected}, but got {provided}")]
309 InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
310 #[error("Failed to remove committee")]
311 InvalidCommitteeRemoval,
312 #[error("Amount overflow")]
313 AmountOverflow,
314 #[error("Amount underflow")]
315 AmountUnderflow,
316 #[error("Chain balance overflow")]
317 BalanceOverflow,
318 #[error("Chain balance underflow")]
319 BalanceUnderflow,
320 #[error("Cannot decrease the chain's timestamp")]
321 TicksOutOfOrder,
322 #[error("Application {0:?} is not registered by the chain")]
323 UnknownApplicationId(Box<ApplicationId>),
324 #[error("Chain is not active yet.")]
325 InactiveChain,
326 #[error("No recorded response for oracle query")]
327 MissingOracleResponse,
328 #[error("Internal error: {0}")]
329 InternalError(&'static str),
330}
331
332impl From<ViewError> for ExecutionError {
333 fn from(error: ViewError) -> Self {
334 match error {
335 ViewError::BlobsNotFound(blob_ids) => ExecutionError::BlobsNotFound(blob_ids),
336 error => ExecutionError::ViewError(error),
337 }
338 }
339}
340
341pub trait UserContract {
343 fn instantiate(
345 &mut self,
346 context: OperationContext,
347 argument: Vec<u8>,
348 ) -> Result<(), ExecutionError>;
349
350 fn execute_operation(
352 &mut self,
353 context: OperationContext,
354 operation: Vec<u8>,
355 ) -> Result<Vec<u8>, ExecutionError>;
356
357 fn execute_message(
359 &mut self,
360 context: MessageContext,
361 message: Vec<u8>,
362 ) -> Result<(), ExecutionError>;
363
364 fn finalize(&mut self, context: FinalizeContext) -> Result<(), ExecutionError>;
366}
367
368pub trait UserService {
370 fn handle_query(
372 &mut self,
373 context: QueryContext,
374 argument: Vec<u8>,
375 ) -> Result<Vec<u8>, ExecutionError>;
376}
377
378#[derive(Clone, Copy, Default)]
380pub struct ExecutionRuntimeConfig {}
381
382#[cfg_attr(not(web), async_trait)]
385#[cfg_attr(web, async_trait(?Send))]
386pub trait ExecutionRuntimeContext {
387 fn chain_id(&self) -> ChainId;
388
389 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
390
391 fn user_contracts(&self) -> &Arc<DashMap<ApplicationId, UserContractCode>>;
392
393 fn user_services(&self) -> &Arc<DashMap<ApplicationId, UserServiceCode>>;
394
395 async fn get_user_contract(
396 &self,
397 description: &ApplicationDescription,
398 ) -> Result<UserContractCode, ExecutionError>;
399
400 async fn get_user_service(
401 &self,
402 description: &ApplicationDescription,
403 ) -> Result<UserServiceCode, ExecutionError>;
404
405 async fn get_blob(&self, blob_id: BlobId) -> Result<Blob, ViewError>;
406
407 async fn get_event(&self, event_id: EventId) -> Result<Vec<u8>, ViewError>;
408
409 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
410
411 #[cfg(with_testing)]
412 async fn add_blobs(
413 &self,
414 blobs: impl IntoIterator<Item = Blob> + Send,
415 ) -> Result<(), ViewError>;
416
417 #[cfg(with_testing)]
418 async fn add_events(
419 &self,
420 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
421 ) -> Result<(), ViewError>;
422}
423
424#[derive(Clone, Copy, Debug)]
425pub struct OperationContext {
426 pub chain_id: ChainId,
428 #[debug(skip_if = Option::is_none)]
430 pub authenticated_signer: Option<AccountOwner>,
431 #[debug(skip_if = Option::is_none)]
434 pub authenticated_caller_id: Option<ApplicationId>,
435 pub height: BlockHeight,
437 pub round: Option<u32>,
439 #[debug(skip_if = Option::is_none)]
441 pub index: Option<u32>,
442}
443
444#[derive(Clone, Copy, Debug)]
445pub struct MessageContext {
446 pub chain_id: ChainId,
448 pub is_bouncing: bool,
450 #[debug(skip_if = Option::is_none)]
452 pub authenticated_signer: Option<AccountOwner>,
453 #[debug(skip_if = Option::is_none)]
455 pub refund_grant_to: Option<Account>,
456 pub height: BlockHeight,
458 pub round: Option<u32>,
460 pub certificate_hash: CryptoHash,
462 pub message_id: MessageId,
465}
466
467#[derive(Clone, Copy, Debug)]
468pub struct FinalizeContext {
469 pub chain_id: ChainId,
471 #[debug(skip_if = Option::is_none)]
473 pub authenticated_signer: Option<AccountOwner>,
474 pub height: BlockHeight,
476 pub round: Option<u32>,
478}
479
480#[derive(Clone, Copy, Debug, Eq, PartialEq)]
481pub struct QueryContext {
482 pub chain_id: ChainId,
484 pub next_block_height: BlockHeight,
486 pub local_time: Timestamp,
488}
489
490pub trait BaseRuntime {
491 type Read: fmt::Debug + Send + Sync;
492 type ContainsKey: fmt::Debug + Send + Sync;
493 type ContainsKeys: fmt::Debug + Send + Sync;
494 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
495 type ReadValueBytes: fmt::Debug + Send + Sync;
496 type FindKeysByPrefix: fmt::Debug + Send + Sync;
497 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
498
499 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
501
502 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
504
505 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
507
508 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
510
511 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
513
514 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
516
517 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
519
520 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
522
523 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
525
526 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
528
529 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
531
532 #[cfg(feature = "test")]
534 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
535 let promise = self.contains_key_new(key)?;
536 self.contains_key_wait(&promise)
537 }
538
539 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
541
542 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
544
545 #[cfg(feature = "test")]
547 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
548 let promise = self.contains_keys_new(keys)?;
549 self.contains_keys_wait(&promise)
550 }
551
552 fn contains_keys_new(
554 &mut self,
555 keys: Vec<Vec<u8>>,
556 ) -> Result<Self::ContainsKeys, ExecutionError>;
557
558 fn contains_keys_wait(
560 &mut self,
561 promise: &Self::ContainsKeys,
562 ) -> Result<Vec<bool>, ExecutionError>;
563
564 #[cfg(feature = "test")]
566 fn read_multi_values_bytes(
567 &mut self,
568 keys: Vec<Vec<u8>>,
569 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
570 let promise = self.read_multi_values_bytes_new(keys)?;
571 self.read_multi_values_bytes_wait(&promise)
572 }
573
574 fn read_multi_values_bytes_new(
576 &mut self,
577 keys: Vec<Vec<u8>>,
578 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
579
580 fn read_multi_values_bytes_wait(
582 &mut self,
583 promise: &Self::ReadMultiValuesBytes,
584 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
585
586 #[cfg(feature = "test")]
588 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
589 let promise = self.read_value_bytes_new(key)?;
590 self.read_value_bytes_wait(&promise)
591 }
592
593 fn read_value_bytes_new(
595 &mut self,
596 key: Vec<u8>,
597 ) -> Result<Self::ReadValueBytes, ExecutionError>;
598
599 fn read_value_bytes_wait(
601 &mut self,
602 promise: &Self::ReadValueBytes,
603 ) -> Result<Option<Vec<u8>>, ExecutionError>;
604
605 fn find_keys_by_prefix_new(
607 &mut self,
608 key_prefix: Vec<u8>,
609 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
610
611 fn find_keys_by_prefix_wait(
613 &mut self,
614 promise: &Self::FindKeysByPrefix,
615 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
616
617 #[cfg(feature = "test")]
619 #[expect(clippy::type_complexity)]
620 fn find_key_values_by_prefix(
621 &mut self,
622 key_prefix: Vec<u8>,
623 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
624 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
625 self.find_key_values_by_prefix_wait(&promise)
626 }
627
628 fn find_key_values_by_prefix_new(
630 &mut self,
631 key_prefix: Vec<u8>,
632 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
633
634 #[expect(clippy::type_complexity)]
636 fn find_key_values_by_prefix_wait(
637 &mut self,
638 promise: &Self::FindKeyValuesByPrefix,
639 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
640
641 fn perform_http_request(
643 &mut self,
644 request: http::Request,
645 ) -> Result<http::Response, ExecutionError>;
646
647 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
653
654 fn read_data_blob(&mut self, hash: &CryptoHash) -> Result<Vec<u8>, ExecutionError>;
656
657 fn assert_data_blob_exists(&mut self, hash: &CryptoHash) -> Result<(), ExecutionError>;
659}
660
661pub trait ServiceRuntime: BaseRuntime {
662 fn try_query_application(
664 &mut self,
665 queried_id: ApplicationId,
666 argument: Vec<u8>,
667 ) -> Result<Vec<u8>, ExecutionError>;
668
669 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
671
672 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
674}
675
676pub trait ContractRuntime: BaseRuntime {
677 fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
679
680 fn message_id(&mut self) -> Result<Option<MessageId>, ExecutionError>;
682
683 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
686
687 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
690
691 fn remaining_fuel(&mut self) -> Result<u64, ExecutionError>;
693
694 fn consume_fuel(&mut self, fuel: u64) -> Result<(), ExecutionError>;
696
697 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
699
700 fn subscribe(&mut self, chain: ChainId, channel: ChannelName) -> Result<(), ExecutionError>;
702
703 fn unsubscribe(&mut self, chain: ChainId, channel: ChannelName) -> Result<(), ExecutionError>;
705
706 fn transfer(
708 &mut self,
709 source: AccountOwner,
710 destination: Account,
711 amount: Amount,
712 ) -> Result<(), ExecutionError>;
713
714 fn claim(
716 &mut self,
717 source: Account,
718 destination: Account,
719 amount: Amount,
720 ) -> Result<(), ExecutionError>;
721
722 fn try_call_application(
725 &mut self,
726 authenticated: bool,
727 callee_id: ApplicationId,
728 argument: Vec<u8>,
729 ) -> Result<Vec<u8>, ExecutionError>;
730
731 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
733
734 fn read_event(
738 &mut self,
739 chain_id: ChainId,
740 stream_name: StreamName,
741 index: u32,
742 ) -> Result<Vec<u8>, ExecutionError>;
743
744 fn subscribe_to_events(
746 &mut self,
747 chain_id: ChainId,
748 application_id: ApplicationId,
749 stream_name: StreamName,
750 ) -> Result<(), ExecutionError>;
751
752 fn unsubscribe_from_events(
754 &mut self,
755 chain_id: ChainId,
756 application_id: ApplicationId,
757 stream_name: StreamName,
758 ) -> Result<(), ExecutionError>;
759
760 fn query_service(
762 &mut self,
763 application_id: ApplicationId,
764 query: Vec<u8>,
765 ) -> Result<Vec<u8>, ExecutionError>;
766
767 fn open_chain(
769 &mut self,
770 ownership: ChainOwnership,
771 application_permissions: ApplicationPermissions,
772 balance: Amount,
773 ) -> Result<(MessageId, ChainId), ExecutionError>;
774
775 fn close_chain(&mut self) -> Result<(), ExecutionError>;
777
778 fn change_application_permissions(
780 &mut self,
781 application_permissions: ApplicationPermissions,
782 ) -> Result<(), ExecutionError>;
783
784 fn create_application(
786 &mut self,
787 module_id: ModuleId,
788 parameters: Vec<u8>,
789 argument: Vec<u8>,
790 required_application_ids: Vec<ApplicationId>,
791 ) -> Result<ApplicationId, ExecutionError>;
792
793 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
795
796 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
798}
799
800#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
802pub enum Operation {
803 System(Box<SystemOperation>),
805 User {
807 application_id: ApplicationId,
808 #[serde(with = "serde_bytes")]
809 #[debug(with = "hex_debug")]
810 bytes: Vec<u8>,
811 },
812}
813
814impl BcsHashable<'_> for Operation {}
815
816#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
818pub enum Message {
819 System(SystemMessage),
821 User {
823 application_id: ApplicationId,
824 #[serde(with = "serde_bytes")]
825 #[debug(with = "hex_debug")]
826 bytes: Vec<u8>,
827 },
828}
829
830#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
832pub enum Query {
833 System(SystemQuery),
835 User {
837 application_id: ApplicationId,
838 #[serde(with = "serde_bytes")]
839 #[debug(with = "hex_debug")]
840 bytes: Vec<u8>,
841 },
842}
843
844#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
846pub struct QueryOutcome<Response = QueryResponse> {
847 pub response: Response,
848 pub operations: Vec<Operation>,
849}
850
851impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
852 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
853 let QueryOutcome {
854 response,
855 operations,
856 } = system_outcome;
857
858 QueryOutcome {
859 response: QueryResponse::System(response),
860 operations,
861 }
862 }
863}
864
865impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
866 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
867 let QueryOutcome {
868 response,
869 operations,
870 } = user_service_outcome;
871
872 QueryOutcome {
873 response: QueryResponse::User(response),
874 operations,
875 }
876 }
877}
878
879#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
881pub enum QueryResponse {
882 System(SystemResponse),
884 User(
886 #[serde(with = "serde_bytes")]
887 #[debug(with = "hex_debug")]
888 Vec<u8>,
889 ),
890}
891
892#[derive(Clone, Debug)]
894#[cfg_attr(with_testing, derive(Eq, PartialEq))]
895pub struct RawOutgoingMessage<Message, Grant> {
896 pub destination: Destination,
898 pub authenticated: bool,
900 pub grant: Grant,
902 pub kind: MessageKind,
904 pub message: Message,
906}
907
908impl<Message> From<SendMessageRequest<Message>> for RawOutgoingMessage<Message, Resources> {
909 fn from(request: SendMessageRequest<Message>) -> Self {
910 let SendMessageRequest {
911 destination,
912 authenticated,
913 grant,
914 is_tracked,
915 message,
916 } = request;
917
918 let kind = if is_tracked {
919 MessageKind::Tracked
920 } else {
921 MessageKind::Simple
922 };
923
924 RawOutgoingMessage {
925 destination,
926 authenticated,
927 grant,
928 kind,
929 message,
930 }
931 }
932}
933
934#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy)]
936pub enum MessageKind {
937 Simple,
939 Protected,
942 Tracked,
945 Bouncing,
947}
948
949#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
951pub struct OutgoingMessage {
952 pub destination: Destination,
954 #[debug(skip_if = Option::is_none)]
956 pub authenticated_signer: Option<AccountOwner>,
957 #[debug(skip_if = Amount::is_zero)]
959 pub grant: Amount,
960 #[debug(skip_if = Option::is_none)]
962 pub refund_grant_to: Option<Account>,
963 pub kind: MessageKind,
965 pub message: Message,
967}
968
969impl BcsHashable<'_> for OutgoingMessage {}
970
971impl OutgoingMessage {
972 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
974 OutgoingMessage {
975 destination: Destination::Recipient(recipient),
976 authenticated_signer: None,
977 grant: Amount::ZERO,
978 refund_grant_to: None,
979 kind: MessageKind::Simple,
980 message: message.into(),
981 }
982 }
983
984 pub fn with_kind(mut self, kind: MessageKind) -> Self {
986 self.kind = kind;
987 self
988 }
989
990 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
992 self.authenticated_signer = authenticated_signer;
993 self
994 }
995}
996
997impl OperationContext {
998 fn refund_grant_to(&self) -> Option<Account> {
1001 self.authenticated_signer.map(|owner| Account {
1002 chain_id: self.chain_id,
1003 owner,
1004 })
1005 }
1006
1007 fn next_message_id(&self, next_message_index: u32) -> MessageId {
1008 MessageId {
1009 chain_id: self.chain_id,
1010 height: self.height,
1011 index: next_message_index,
1012 }
1013 }
1014}
1015
1016#[cfg(with_testing)]
1017#[derive(Clone)]
1018pub struct TestExecutionRuntimeContext {
1019 chain_id: ChainId,
1020 execution_runtime_config: ExecutionRuntimeConfig,
1021 user_contracts: Arc<DashMap<ApplicationId, UserContractCode>>,
1022 user_services: Arc<DashMap<ApplicationId, UserServiceCode>>,
1023 blobs: Arc<DashMap<BlobId, Blob>>,
1024 events: Arc<DashMap<EventId, Vec<u8>>>,
1025}
1026
1027#[cfg(with_testing)]
1028impl TestExecutionRuntimeContext {
1029 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1030 Self {
1031 chain_id,
1032 execution_runtime_config,
1033 user_contracts: Arc::default(),
1034 user_services: Arc::default(),
1035 blobs: Arc::default(),
1036 events: Arc::default(),
1037 }
1038 }
1039}
1040
1041#[cfg(with_testing)]
1042#[cfg_attr(not(web), async_trait)]
1043#[cfg_attr(web, async_trait(?Send))]
1044impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1045 fn chain_id(&self) -> ChainId {
1046 self.chain_id
1047 }
1048
1049 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1050 self.execution_runtime_config
1051 }
1052
1053 fn user_contracts(&self) -> &Arc<DashMap<ApplicationId, UserContractCode>> {
1054 &self.user_contracts
1055 }
1056
1057 fn user_services(&self) -> &Arc<DashMap<ApplicationId, UserServiceCode>> {
1058 &self.user_services
1059 }
1060
1061 async fn get_user_contract(
1062 &self,
1063 description: &ApplicationDescription,
1064 ) -> Result<UserContractCode, ExecutionError> {
1065 let application_id = description.into();
1066 Ok(self
1067 .user_contracts()
1068 .get(&application_id)
1069 .ok_or_else(|| {
1070 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1071 })?
1072 .clone())
1073 }
1074
1075 async fn get_user_service(
1076 &self,
1077 description: &ApplicationDescription,
1078 ) -> Result<UserServiceCode, ExecutionError> {
1079 let application_id = description.into();
1080 Ok(self
1081 .user_services()
1082 .get(&application_id)
1083 .ok_or_else(|| {
1084 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1085 })?
1086 .clone())
1087 }
1088
1089 async fn get_blob(&self, blob_id: BlobId) -> Result<Blob, ViewError> {
1090 Ok(self
1091 .blobs
1092 .get(&blob_id)
1093 .ok_or_else(|| ViewError::BlobsNotFound(vec![blob_id]))?
1094 .clone())
1095 }
1096
1097 async fn get_event(&self, event_id: EventId) -> Result<Vec<u8>, ViewError> {
1098 Ok(self
1099 .events
1100 .get(&event_id)
1101 .ok_or_else(|| ViewError::EventsNotFound(vec![event_id]))?
1102 .clone())
1103 }
1104
1105 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1106 Ok(self.blobs.contains_key(&blob_id))
1107 }
1108
1109 #[cfg(with_testing)]
1110 async fn add_blobs(
1111 &self,
1112 blobs: impl IntoIterator<Item = Blob> + Send,
1113 ) -> Result<(), ViewError> {
1114 for blob in blobs {
1115 self.blobs.insert(blob.id(), blob);
1116 }
1117
1118 Ok(())
1119 }
1120
1121 #[cfg(with_testing)]
1122 async fn add_events(
1123 &self,
1124 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1125 ) -> Result<(), ViewError> {
1126 for (event_id, bytes) in events {
1127 self.events.insert(event_id, bytes);
1128 }
1129
1130 Ok(())
1131 }
1132}
1133
1134impl From<SystemOperation> for Operation {
1135 fn from(operation: SystemOperation) -> Self {
1136 Operation::System(Box::new(operation))
1137 }
1138}
1139
1140impl Operation {
1141 pub fn system(operation: SystemOperation) -> Self {
1142 Operation::System(Box::new(operation))
1143 }
1144
1145 #[cfg(with_testing)]
1147 pub fn user<A: Abi>(
1148 application_id: ApplicationId<A>,
1149 operation: &A::Operation,
1150 ) -> Result<Self, bcs::Error> {
1151 Self::user_without_abi(application_id.forget_abi(), operation)
1152 }
1153
1154 #[cfg(with_testing)]
1157 pub fn user_without_abi(
1158 application_id: ApplicationId,
1159 operation: &impl Serialize,
1160 ) -> Result<Self, bcs::Error> {
1161 Ok(Operation::User {
1162 application_id,
1163 bytes: bcs::to_bytes(&operation)?,
1164 })
1165 }
1166
1167 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1170 match self {
1171 Operation::System(system_operation) => Some(system_operation),
1172 Operation::User { .. } => None,
1173 }
1174 }
1175
1176 pub fn application_id(&self) -> GenericApplicationId {
1177 match self {
1178 Self::System(_) => GenericApplicationId::System,
1179 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1180 }
1181 }
1182
1183 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1185 match self.as_system_operation() {
1186 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1187 vec![BlobId::new(*blob_hash, BlobType::Data)]
1188 }
1189 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1190 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1191 }
1192 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1193 _ => vec![],
1194 }
1195 }
1196}
1197
1198impl From<SystemMessage> for Message {
1199 fn from(message: SystemMessage) -> Self {
1200 Message::System(message)
1201 }
1202}
1203
1204impl Message {
1205 pub fn system(message: SystemMessage) -> Self {
1206 Message::System(message)
1207 }
1208
1209 pub fn user<A, M: Serialize>(
1212 application_id: ApplicationId<A>,
1213 message: &M,
1214 ) -> Result<Self, bcs::Error> {
1215 let application_id = application_id.forget_abi();
1216 let bytes = bcs::to_bytes(&message)?;
1217 Ok(Message::User {
1218 application_id,
1219 bytes,
1220 })
1221 }
1222
1223 pub fn application_id(&self) -> GenericApplicationId {
1224 match self {
1225 Self::System(_) => GenericApplicationId::System,
1226 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1227 }
1228 }
1229
1230 pub fn matches_open_chain(&self) -> Option<&OpenChainConfig> {
1231 match self {
1232 Message::System(SystemMessage::OpenChain(config)) => Some(config),
1233 _ => None,
1234 }
1235 }
1236}
1237
1238impl From<SystemQuery> for Query {
1239 fn from(query: SystemQuery) -> Self {
1240 Query::System(query)
1241 }
1242}
1243
1244impl Query {
1245 pub fn system(query: SystemQuery) -> Self {
1246 Query::System(query)
1247 }
1248
1249 pub fn user<A: Abi>(
1251 application_id: ApplicationId<A>,
1252 query: &A::Query,
1253 ) -> Result<Self, serde_json::Error> {
1254 Self::user_without_abi(application_id.forget_abi(), query)
1255 }
1256
1257 pub fn user_without_abi(
1260 application_id: ApplicationId,
1261 query: &impl Serialize,
1262 ) -> Result<Self, serde_json::Error> {
1263 Ok(Query::User {
1264 application_id,
1265 bytes: serde_json::to_vec(&query)?,
1266 })
1267 }
1268
1269 pub fn application_id(&self) -> GenericApplicationId {
1270 match self {
1271 Self::System(_) => GenericApplicationId::System,
1272 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1273 }
1274 }
1275}
1276
1277impl From<SystemResponse> for QueryResponse {
1278 fn from(response: SystemResponse) -> Self {
1279 QueryResponse::System(response)
1280 }
1281}
1282
1283impl From<Vec<u8>> for QueryResponse {
1284 fn from(response: Vec<u8>) -> Self {
1285 QueryResponse::User(response)
1286 }
1287}
1288
1289#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1291pub struct BlobState {
1292 pub last_used_by: CryptoHash,
1294 pub chain_id: ChainId,
1296 pub block_height: BlockHeight,
1298 pub epoch: Epoch,
1300}
1301
1302#[derive(Clone, Copy, Display)]
1304#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1305pub enum WasmRuntime {
1306 #[cfg(with_wasmer)]
1307 #[default]
1308 #[display("wasmer")]
1309 Wasmer,
1310 #[cfg(with_wasmtime)]
1311 #[cfg_attr(not(with_wasmer), default)]
1312 #[display("wasmtime")]
1313 Wasmtime,
1314}
1315
1316#[derive(Clone, Copy, Display)]
1317#[cfg_attr(with_revm, derive(Debug, Default))]
1318pub enum EvmRuntime {
1319 #[cfg(with_revm)]
1320 #[default]
1321 #[display("revm")]
1322 Revm,
1323}
1324
1325pub trait WithWasmDefault {
1327 fn with_wasm_default(self) -> Self;
1328}
1329
1330impl WithWasmDefault for Option<WasmRuntime> {
1331 fn with_wasm_default(self) -> Self {
1332 #[cfg(with_wasm_runtime)]
1333 {
1334 Some(self.unwrap_or_default())
1335 }
1336 #[cfg(not(with_wasm_runtime))]
1337 {
1338 None
1339 }
1340 }
1341}
1342
1343impl FromStr for WasmRuntime {
1344 type Err = InvalidWasmRuntime;
1345
1346 fn from_str(string: &str) -> Result<Self, Self::Err> {
1347 match string {
1348 #[cfg(with_wasmer)]
1349 "wasmer" => Ok(WasmRuntime::Wasmer),
1350 #[cfg(with_wasmtime)]
1351 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1352 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1353 }
1354 }
1355}
1356
1357#[derive(Clone, Debug, Error)]
1359#[error("{0:?} is not a valid WebAssembly runtime")]
1360pub struct InvalidWasmRuntime(String);
1361
1362doc_scalar!(Operation, "An operation to be executed in a block");
1363doc_scalar!(
1364 Message,
1365 "A message to be sent and possibly executed in the receiver's block."
1366);
1367doc_scalar!(MessageKind, "The kind of outgoing message being sent");