1pub 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 async_graphql::SimpleObject;
26use async_trait::async_trait;
27use custom_debug_derive::Debug;
28use dashmap::DashMap;
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, hex_debug, http,
41 identifiers::{
42 Account, AccountOwner, ApplicationId, BlobId, BlobType, ChainId, DataBlobHash, EventId,
43 GenericApplicationId, ModuleId, StreamName,
44 },
45 ownership::ChainOwnership,
46 task,
47 vm::VmRuntime,
48};
49use linera_views::{batch::Batch, ViewError};
50use serde::{Deserialize, Serialize};
51use system::AdminOperation;
52use thiserror::Error;
53
54#[cfg(with_revm)]
55use crate::evm::EvmExecutionError;
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
81pub const LINERA_SOL: &str = include_str!("../solidity/Linera.sol");
84pub const LINERA_TYPES_SOL: &str = include_str!("../solidity/LineraTypes.sol");
85
86const MAX_STREAM_NAME_LEN: usize = 64;
88
89#[derive(Clone)]
91pub struct UserContractCode(Box<dyn UserContractModule>);
92
93#[derive(Clone)]
95pub struct UserServiceCode(Box<dyn UserServiceModule>);
96
97pub type UserContractInstance = Box<dyn UserContract>;
99
100pub type UserServiceInstance = Box<dyn UserService>;
102
103pub trait UserContractModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
105 fn instantiate(
106 &self,
107 runtime: ContractSyncRuntimeHandle,
108 ) -> Result<UserContractInstance, ExecutionError>;
109}
110
111impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
112 fn from(module: T) -> Self {
113 Self(Box::new(module))
114 }
115}
116
117dyn_clone::clone_trait_object!(UserContractModule);
118
119pub trait UserServiceModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
121 fn instantiate(
122 &self,
123 runtime: ServiceSyncRuntimeHandle,
124 ) -> Result<UserServiceInstance, ExecutionError>;
125}
126
127impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
128 fn from(module: T) -> Self {
129 Self(Box::new(module))
130 }
131}
132
133dyn_clone::clone_trait_object!(UserServiceModule);
134
135impl UserServiceCode {
136 fn instantiate(
137 &self,
138 runtime: ServiceSyncRuntimeHandle,
139 ) -> Result<UserServiceInstance, ExecutionError> {
140 self.0.instantiate(runtime)
141 }
142}
143
144impl UserContractCode {
145 fn instantiate(
146 &self,
147 runtime: ContractSyncRuntimeHandle,
148 ) -> Result<UserContractInstance, ExecutionError> {
149 self.0.instantiate(runtime)
150 }
151}
152
153#[cfg(web)]
154const _: () = {
155 impl From<UserContractCode> for JsValue {
159 fn from(code: UserContractCode) -> JsValue {
160 let module: WasmContractModule = *(code.0 as Box<dyn Any>)
161 .downcast()
162 .expect("we only support Wasm modules on the Web for now");
163 module.into()
164 }
165 }
166
167 impl From<UserServiceCode> for JsValue {
168 fn from(code: UserServiceCode) -> JsValue {
169 let module: WasmServiceModule = *(code.0 as Box<dyn Any>)
170 .downcast()
171 .expect("we only support Wasm modules on the Web for now");
172 module.into()
173 }
174 }
175
176 impl TryFrom<JsValue> for UserContractCode {
177 type Error = JsValue;
178 fn try_from(value: JsValue) -> Result<Self, JsValue> {
179 WasmContractModule::try_from(value).map(Into::into)
180 }
181 }
182
183 impl TryFrom<JsValue> for UserServiceCode {
184 type Error = JsValue;
185 fn try_from(value: JsValue) -> Result<Self, JsValue> {
186 WasmServiceModule::try_from(value).map(Into::into)
187 }
188 }
189};
190
191#[derive(Error, Debug)]
193pub enum ExecutionError {
194 #[error(transparent)]
195 ViewError(#[from] ViewError),
196 #[error(transparent)]
197 ArithmeticError(#[from] ArithmeticError),
198 #[error("User application reported an error: {0}")]
199 UserError(String),
200 #[cfg(with_wasm_runtime)]
201 #[error(transparent)]
202 WasmError(#[from] WasmExecutionError),
203 #[cfg(with_revm)]
204 #[error(transparent)]
205 EvmError(#[from] EvmExecutionError),
206 #[error(transparent)]
207 DecompressionError(#[from] DecompressionError),
208 #[error("The given promise is invalid or was polled once already")]
209 InvalidPromise,
210
211 #[error("Attempted to perform a reentrant call to application {0}")]
212 ReentrantCall(ApplicationId),
213 #[error(
214 "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
215 from `finalize`"
216 )]
217 CrossApplicationCallInFinalize {
218 caller_id: Box<ApplicationId>,
219 callee_id: Box<ApplicationId>,
220 },
221 #[error("Failed to load bytecode from storage {0:?}")]
222 ApplicationBytecodeNotFound(Box<ApplicationDescription>),
223 #[error("Unsupported dynamic application load: {0:?}")]
225 UnsupportedDynamicApplicationLoad(Box<ApplicationId>),
226
227 #[error("Excessive number of bytes read from storage")]
228 ExcessiveRead,
229 #[error("Excessive number of bytes written to storage")]
230 ExcessiveWrite,
231 #[error("Block execution required too much fuel for VM {0}")]
232 MaximumFuelExceeded(VmRuntime),
233 #[error("Services running as oracles in block took longer than allowed")]
234 MaximumServiceOracleExecutionTimeExceeded,
235 #[error("Service running as an oracle produced a response that's too large")]
236 ServiceOracleResponseTooLarge,
237 #[error("Serialized size of the block exceeds limit")]
238 BlockTooLarge,
239 #[error("HTTP response exceeds the size limit of {limit} bytes, having at least {size} bytes")]
240 HttpResponseSizeLimitExceeded { limit: u64, size: u64 },
241 #[error("Runtime failed to respond to application")]
242 MissingRuntimeResponse,
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("Service oracle query tried to create operations: {0:?}")]
258 ServiceOracleQueryOperations(Vec<Operation>),
259 #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
260 AssertBefore {
261 timestamp: Timestamp,
262 local_time: Timestamp,
263 },
264
265 #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
266 StreamNameTooLong,
267 #[error("Blob exceeds size limit")]
268 BlobTooLarge,
269 #[error("Bytecode exceeds size limit")]
270 BytecodeTooLarge,
271 #[error("Attempt to perform an HTTP request to an unauthorized host: {0:?}")]
272 UnauthorizedHttpRequest(reqwest::Url),
273 #[error("Attempt to perform an HTTP request to an invalid URL")]
274 InvalidUrlForHttpRequest(#[from] url::ParseError),
275 #[error("Failed to send contract code to worker thread: {0:?}")]
276 ContractModuleSend(#[from] linera_base::task::SendError<UserContractCode>),
277 #[error("Failed to send service code to worker thread: {0:?}")]
278 ServiceModuleSend(#[from] linera_base::task::SendError<UserServiceCode>),
279 #[error("The chain being queried is not active {0}")]
280 InactiveChain(ChainId),
281 #[error("Blobs not found: {0:?}")]
282 BlobsNotFound(Vec<BlobId>),
283 #[error("Events not found: {0:?}")]
284 EventsNotFound(Vec<EventId>),
285
286 #[error("Invalid HTTP header name used for HTTP request")]
287 InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
288 #[error("Invalid HTTP header value used for HTTP request")]
289 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
290
291 #[error("No NetworkDescription found in storage")]
292 NoNetworkDescriptionFound,
293 #[error("{epoch:?} is not recognized by chain {chain_id:}")]
294 InvalidEpoch { chain_id: ChainId, epoch: Epoch },
295 #[error("Transfer must have positive amount")]
296 IncorrectTransferAmount,
297 #[error("Transfer from owned account must be authenticated by the right signer")]
298 UnauthenticatedTransferOwner,
299 #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
300 InsufficientBalance {
301 balance: Amount,
302 account: AccountOwner,
303 },
304 #[error("Required execution fees exceeded the total funding available. Fees {fees}, available balance: {balance}")]
305 FeesExceedFunding { fees: Amount, balance: Amount },
306 #[error("Claim must have positive amount")]
307 IncorrectClaimAmount,
308 #[error("Claim must be authenticated by the right signer")]
309 UnauthenticatedClaimOwner,
310 #[error("Admin operations are only allowed on the admin chain.")]
311 AdminOperationOnNonAdminChain,
312 #[error("Failed to create new committee: expected {expected}, but got {provided}")]
313 InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
314 #[error("Failed to remove committee")]
315 InvalidCommitteeRemoval,
316 #[error("No recorded response for oracle query")]
317 MissingOracleResponse,
318 #[error("process_streams was not called for all stream updates")]
319 UnprocessedStreams,
320 #[error("Internal error: {0}")]
321 InternalError(&'static str),
322 #[error("UpdateStreams is outdated")]
323 OutdatedUpdateStreams,
324}
325
326pub trait UserContract {
328 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
330
331 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
333
334 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
336
337 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
339
340 fn finalize(&mut self) -> Result<(), ExecutionError>;
342}
343
344pub trait UserService {
346 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
348}
349
350#[derive(Clone, Copy, Default)]
352pub struct ExecutionRuntimeConfig {}
353
354#[cfg_attr(not(web), async_trait)]
357#[cfg_attr(web, async_trait(?Send))]
358pub trait ExecutionRuntimeContext {
359 fn chain_id(&self) -> ChainId;
360
361 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
362
363 fn user_contracts(&self) -> &Arc<DashMap<ApplicationId, UserContractCode>>;
364
365 fn user_services(&self) -> &Arc<DashMap<ApplicationId, UserServiceCode>>;
366
367 async fn get_user_contract(
368 &self,
369 description: &ApplicationDescription,
370 txn_tracker: &TransactionTracker,
371 ) -> Result<UserContractCode, ExecutionError>;
372
373 async fn get_user_service(
374 &self,
375 description: &ApplicationDescription,
376 txn_tracker: &TransactionTracker,
377 ) -> Result<UserServiceCode, ExecutionError>;
378
379 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError>;
380
381 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError>;
382
383 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
384
385 async fn committees_for(
386 &self,
387 epoch_range: RangeInclusive<Epoch>,
388 ) -> Result<BTreeMap<Epoch, Committee>, ViewError>;
389
390 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
391
392 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
393
394 #[cfg(with_testing)]
395 async fn add_blobs(
396 &self,
397 blobs: impl IntoIterator<Item = Blob> + Send,
398 ) -> Result<(), ViewError>;
399
400 #[cfg(with_testing)]
401 async fn add_events(
402 &self,
403 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
404 ) -> Result<(), ViewError>;
405}
406
407#[derive(Clone, Copy, Debug)]
408pub struct OperationContext {
409 pub chain_id: ChainId,
411 #[debug(skip_if = Option::is_none)]
413 pub authenticated_signer: Option<AccountOwner>,
414 pub height: BlockHeight,
416 pub round: Option<u32>,
418 pub timestamp: Timestamp,
420}
421
422#[derive(Clone, Copy, Debug)]
423pub struct MessageContext {
424 pub chain_id: ChainId,
426 pub origin: ChainId,
428 pub is_bouncing: bool,
430 #[debug(skip_if = Option::is_none)]
432 pub authenticated_signer: Option<AccountOwner>,
433 #[debug(skip_if = Option::is_none)]
435 pub refund_grant_to: Option<Account>,
436 pub height: BlockHeight,
438 pub round: Option<u32>,
440 pub timestamp: Timestamp,
442}
443
444#[derive(Clone, Copy, Debug)]
445pub struct ProcessStreamsContext {
446 pub chain_id: ChainId,
448 pub height: BlockHeight,
450 pub round: Option<u32>,
452 pub timestamp: Timestamp,
454}
455
456impl From<MessageContext> for ProcessStreamsContext {
457 fn from(context: MessageContext) -> Self {
458 Self {
459 chain_id: context.chain_id,
460 height: context.height,
461 round: context.round,
462 timestamp: context.timestamp,
463 }
464 }
465}
466
467impl From<OperationContext> for ProcessStreamsContext {
468 fn from(context: OperationContext) -> Self {
469 Self {
470 chain_id: context.chain_id,
471 height: context.height,
472 round: context.round,
473 timestamp: context.timestamp,
474 }
475 }
476}
477
478#[derive(Clone, Copy, Debug)]
479pub struct FinalizeContext {
480 pub chain_id: ChainId,
482 #[debug(skip_if = Option::is_none)]
484 pub authenticated_signer: Option<AccountOwner>,
485 pub height: BlockHeight,
487 pub round: Option<u32>,
489}
490
491#[derive(Clone, Copy, Debug, Eq, PartialEq)]
492pub struct QueryContext {
493 pub chain_id: ChainId,
495 pub next_block_height: BlockHeight,
497 pub local_time: Timestamp,
499}
500
501pub trait BaseRuntime {
502 type Read: fmt::Debug + Send + Sync;
503 type ContainsKey: fmt::Debug + Send + Sync;
504 type ContainsKeys: fmt::Debug + Send + Sync;
505 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
506 type ReadValueBytes: fmt::Debug + Send + Sync;
507 type FindKeysByPrefix: fmt::Debug + Send + Sync;
508 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
509
510 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
512
513 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
515
516 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
518
519 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
521
522 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
524
525 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
527
528 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
530
531 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
533
534 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
536
537 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
539
540 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
542
543 #[cfg(feature = "test")]
545 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
546 let promise = self.contains_key_new(key)?;
547 self.contains_key_wait(&promise)
548 }
549
550 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
552
553 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
555
556 #[cfg(feature = "test")]
558 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
559 let promise = self.contains_keys_new(keys)?;
560 self.contains_keys_wait(&promise)
561 }
562
563 fn contains_keys_new(
565 &mut self,
566 keys: Vec<Vec<u8>>,
567 ) -> Result<Self::ContainsKeys, ExecutionError>;
568
569 fn contains_keys_wait(
571 &mut self,
572 promise: &Self::ContainsKeys,
573 ) -> Result<Vec<bool>, ExecutionError>;
574
575 #[cfg(feature = "test")]
577 fn read_multi_values_bytes(
578 &mut self,
579 keys: Vec<Vec<u8>>,
580 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
581 let promise = self.read_multi_values_bytes_new(keys)?;
582 self.read_multi_values_bytes_wait(&promise)
583 }
584
585 fn read_multi_values_bytes_new(
587 &mut self,
588 keys: Vec<Vec<u8>>,
589 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
590
591 fn read_multi_values_bytes_wait(
593 &mut self,
594 promise: &Self::ReadMultiValuesBytes,
595 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
596
597 #[cfg(feature = "test")]
599 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
600 let promise = self.read_value_bytes_new(key)?;
601 self.read_value_bytes_wait(&promise)
602 }
603
604 fn read_value_bytes_new(
606 &mut self,
607 key: Vec<u8>,
608 ) -> Result<Self::ReadValueBytes, ExecutionError>;
609
610 fn read_value_bytes_wait(
612 &mut self,
613 promise: &Self::ReadValueBytes,
614 ) -> Result<Option<Vec<u8>>, ExecutionError>;
615
616 fn find_keys_by_prefix_new(
618 &mut self,
619 key_prefix: Vec<u8>,
620 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
621
622 fn find_keys_by_prefix_wait(
624 &mut self,
625 promise: &Self::FindKeysByPrefix,
626 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
627
628 #[cfg(feature = "test")]
630 #[expect(clippy::type_complexity)]
631 fn find_key_values_by_prefix(
632 &mut self,
633 key_prefix: Vec<u8>,
634 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
635 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
636 self.find_key_values_by_prefix_wait(&promise)
637 }
638
639 fn find_key_values_by_prefix_new(
641 &mut self,
642 key_prefix: Vec<u8>,
643 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
644
645 #[expect(clippy::type_complexity)]
647 fn find_key_values_by_prefix_wait(
648 &mut self,
649 promise: &Self::FindKeyValuesByPrefix,
650 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
651
652 fn perform_http_request(
654 &mut self,
655 request: http::Request,
656 ) -> Result<http::Response, ExecutionError>;
657
658 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
664
665 fn read_data_blob(&mut self, hash: DataBlobHash) -> Result<Vec<u8>, ExecutionError>;
667
668 fn assert_data_blob_exists(&mut self, hash: DataBlobHash) -> Result<(), ExecutionError>;
670}
671
672pub trait ServiceRuntime: BaseRuntime {
673 fn try_query_application(
675 &mut self,
676 queried_id: ApplicationId,
677 argument: Vec<u8>,
678 ) -> Result<Vec<u8>, ExecutionError>;
679
680 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
682
683 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
685}
686
687pub trait ContractRuntime: BaseRuntime {
688 fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
690
691 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
694
695 fn message_origin_chain_id(&mut self) -> Result<Option<ChainId>, ExecutionError>;
697
698 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
701
702 fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
704
705 fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
707
708 fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
710
711 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
713
714 fn transfer(
716 &mut self,
717 source: AccountOwner,
718 destination: Account,
719 amount: Amount,
720 ) -> Result<(), ExecutionError>;
721
722 fn claim(
724 &mut self,
725 source: Account,
726 destination: Account,
727 amount: Amount,
728 ) -> Result<(), ExecutionError>;
729
730 fn try_call_application(
733 &mut self,
734 authenticated: bool,
735 callee_id: ApplicationId,
736 argument: Vec<u8>,
737 ) -> Result<Vec<u8>, ExecutionError>;
738
739 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
741
742 fn read_event(
746 &mut self,
747 chain_id: ChainId,
748 stream_name: StreamName,
749 index: u32,
750 ) -> Result<Vec<u8>, ExecutionError>;
751
752 fn subscribe_to_events(
754 &mut self,
755 chain_id: ChainId,
756 application_id: ApplicationId,
757 stream_name: StreamName,
758 ) -> Result<(), ExecutionError>;
759
760 fn unsubscribe_from_events(
762 &mut self,
763 chain_id: ChainId,
764 application_id: ApplicationId,
765 stream_name: StreamName,
766 ) -> Result<(), ExecutionError>;
767
768 fn query_service(
770 &mut self,
771 application_id: ApplicationId,
772 query: Vec<u8>,
773 ) -> Result<Vec<u8>, ExecutionError>;
774
775 fn open_chain(
777 &mut self,
778 ownership: ChainOwnership,
779 application_permissions: ApplicationPermissions,
780 balance: Amount,
781 ) -> Result<ChainId, ExecutionError>;
782
783 fn close_chain(&mut self) -> Result<(), ExecutionError>;
785
786 fn change_application_permissions(
788 &mut self,
789 application_permissions: ApplicationPermissions,
790 ) -> Result<(), ExecutionError>;
791
792 fn create_application(
794 &mut self,
795 module_id: ModuleId,
796 parameters: Vec<u8>,
797 argument: Vec<u8>,
798 required_application_ids: Vec<ApplicationId>,
799 ) -> Result<ApplicationId, ExecutionError>;
800
801 fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<DataBlobHash, ExecutionError>;
803
804 fn publish_module(
806 &mut self,
807 contract: Bytecode,
808 service: Bytecode,
809 vm_runtime: VmRuntime,
810 ) -> Result<ModuleId, ExecutionError>;
811
812 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
814
815 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
817}
818
819#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
821pub enum Operation {
822 System(Box<SystemOperation>),
824 User {
826 application_id: ApplicationId,
827 #[serde(with = "serde_bytes")]
828 #[debug(with = "hex_debug")]
829 bytes: Vec<u8>,
830 },
831}
832
833impl BcsHashable<'_> for Operation {}
834
835#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
837pub enum Message {
838 System(SystemMessage),
840 User {
842 application_id: ApplicationId,
843 #[serde(with = "serde_bytes")]
844 #[debug(with = "hex_debug")]
845 bytes: Vec<u8>,
846 },
847}
848
849#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
851pub enum Query {
852 System(SystemQuery),
854 User {
856 application_id: ApplicationId,
857 #[serde(with = "serde_bytes")]
858 #[debug(with = "hex_debug")]
859 bytes: Vec<u8>,
860 },
861}
862
863#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
865pub struct QueryOutcome<Response = QueryResponse> {
866 pub response: Response,
867 pub operations: Vec<Operation>,
868}
869
870impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
871 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
872 let QueryOutcome {
873 response,
874 operations,
875 } = system_outcome;
876
877 QueryOutcome {
878 response: QueryResponse::System(response),
879 operations,
880 }
881 }
882}
883
884impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
885 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
886 let QueryOutcome {
887 response,
888 operations,
889 } = user_service_outcome;
890
891 QueryOutcome {
892 response: QueryResponse::User(response),
893 operations,
894 }
895 }
896}
897
898#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
900pub enum QueryResponse {
901 System(SystemResponse),
903 User(
905 #[serde(with = "serde_bytes")]
906 #[debug(with = "hex_debug")]
907 Vec<u8>,
908 ),
909}
910
911#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy)]
913pub enum MessageKind {
914 Simple,
916 Protected,
919 Tracked,
922 Bouncing,
924}
925
926impl Display for MessageKind {
927 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
928 match self {
929 MessageKind::Simple => write!(f, "Simple"),
930 MessageKind::Protected => write!(f, "Protected"),
931 MessageKind::Tracked => write!(f, "Tracked"),
932 MessageKind::Bouncing => write!(f, "Bouncing"),
933 }
934 }
935}
936
937#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
939pub struct OutgoingMessage {
940 pub destination: ChainId,
942 #[debug(skip_if = Option::is_none)]
944 pub authenticated_signer: Option<AccountOwner>,
945 #[debug(skip_if = Amount::is_zero)]
947 pub grant: Amount,
948 #[debug(skip_if = Option::is_none)]
950 pub refund_grant_to: Option<Account>,
951 pub kind: MessageKind,
953 pub message: Message,
955}
956
957impl BcsHashable<'_> for OutgoingMessage {}
958
959impl OutgoingMessage {
960 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
962 OutgoingMessage {
963 destination: recipient,
964 authenticated_signer: None,
965 grant: Amount::ZERO,
966 refund_grant_to: None,
967 kind: MessageKind::Simple,
968 message: message.into(),
969 }
970 }
971
972 pub fn with_kind(mut self, kind: MessageKind) -> Self {
974 self.kind = kind;
975 self
976 }
977
978 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
980 self.authenticated_signer = authenticated_signer;
981 self
982 }
983}
984
985impl OperationContext {
986 fn refund_grant_to(&self) -> Option<Account> {
989 self.authenticated_signer.map(|owner| Account {
990 chain_id: self.chain_id,
991 owner,
992 })
993 }
994}
995
996#[cfg(with_testing)]
997#[derive(Clone)]
998pub struct TestExecutionRuntimeContext {
999 chain_id: ChainId,
1000 execution_runtime_config: ExecutionRuntimeConfig,
1001 user_contracts: Arc<DashMap<ApplicationId, UserContractCode>>,
1002 user_services: Arc<DashMap<ApplicationId, UserServiceCode>>,
1003 blobs: Arc<DashMap<BlobId, Blob>>,
1004 events: Arc<DashMap<EventId, Vec<u8>>>,
1005}
1006
1007#[cfg(with_testing)]
1008impl TestExecutionRuntimeContext {
1009 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1010 Self {
1011 chain_id,
1012 execution_runtime_config,
1013 user_contracts: Arc::default(),
1014 user_services: Arc::default(),
1015 blobs: Arc::default(),
1016 events: Arc::default(),
1017 }
1018 }
1019}
1020
1021#[cfg(with_testing)]
1022#[cfg_attr(not(web), async_trait)]
1023#[cfg_attr(web, async_trait(?Send))]
1024impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1025 fn chain_id(&self) -> ChainId {
1026 self.chain_id
1027 }
1028
1029 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1030 self.execution_runtime_config
1031 }
1032
1033 fn user_contracts(&self) -> &Arc<DashMap<ApplicationId, UserContractCode>> {
1034 &self.user_contracts
1035 }
1036
1037 fn user_services(&self) -> &Arc<DashMap<ApplicationId, UserServiceCode>> {
1038 &self.user_services
1039 }
1040
1041 async fn get_user_contract(
1042 &self,
1043 description: &ApplicationDescription,
1044 _txn_tracker: &TransactionTracker,
1045 ) -> Result<UserContractCode, ExecutionError> {
1046 let application_id = description.into();
1047 Ok(self
1048 .user_contracts()
1049 .get(&application_id)
1050 .ok_or_else(|| {
1051 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1052 })?
1053 .clone())
1054 }
1055
1056 async fn get_user_service(
1057 &self,
1058 description: &ApplicationDescription,
1059 _txn_tracker: &TransactionTracker,
1060 ) -> Result<UserServiceCode, ExecutionError> {
1061 let application_id = description.into();
1062 Ok(self
1063 .user_services()
1064 .get(&application_id)
1065 .ok_or_else(|| {
1066 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1067 })?
1068 .clone())
1069 }
1070
1071 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError> {
1072 match self.blobs.get(&blob_id) {
1073 None => Ok(None),
1074 Some(blob) => Ok(Some(blob.clone())),
1075 }
1076 }
1077
1078 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError> {
1079 match self.events.get(&event_id) {
1080 None => Ok(None),
1081 Some(event) => Ok(Some(event.clone())),
1082 }
1083 }
1084
1085 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1086 Ok(Some(NetworkDescription {
1087 admin_chain_id: dummy_chain_description(0).id(),
1088 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1089 genesis_timestamp: Timestamp::from(0),
1090 genesis_committee_blob_hash: CryptoHash::test_hash("genesis committee"),
1091 name: "dummy network description".to_string(),
1092 }))
1093 }
1094
1095 async fn committees_for(
1096 &self,
1097 epoch_range: RangeInclusive<Epoch>,
1098 ) -> Result<BTreeMap<Epoch, Committee>, ViewError> {
1099 let committee_blob_bytes = self
1100 .blobs
1101 .iter()
1102 .find(|item| item.key().blob_type == BlobType::Committee)
1103 .ok_or_else(|| ViewError::NotFound("committee not found".to_owned()))?
1104 .value()
1105 .bytes()
1106 .to_vec();
1107 let committee: Committee = bcs::from_bytes(&committee_blob_bytes)?;
1108 Ok((epoch_range.start().0..=epoch_range.end().0)
1112 .map(|epoch| (Epoch::from(epoch), committee.clone()))
1113 .collect())
1114 }
1115
1116 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1117 Ok(self.blobs.contains_key(&blob_id))
1118 }
1119
1120 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1121 Ok(self.events.contains_key(&event_id))
1122 }
1123
1124 #[cfg(with_testing)]
1125 async fn add_blobs(
1126 &self,
1127 blobs: impl IntoIterator<Item = Blob> + Send,
1128 ) -> Result<(), ViewError> {
1129 for blob in blobs {
1130 self.blobs.insert(blob.id(), blob);
1131 }
1132
1133 Ok(())
1134 }
1135
1136 #[cfg(with_testing)]
1137 async fn add_events(
1138 &self,
1139 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1140 ) -> Result<(), ViewError> {
1141 for (event_id, bytes) in events {
1142 self.events.insert(event_id, bytes);
1143 }
1144
1145 Ok(())
1146 }
1147}
1148
1149impl From<SystemOperation> for Operation {
1150 fn from(operation: SystemOperation) -> Self {
1151 Operation::System(Box::new(operation))
1152 }
1153}
1154
1155impl Operation {
1156 pub fn system(operation: SystemOperation) -> Self {
1157 Operation::System(Box::new(operation))
1158 }
1159
1160 #[cfg(with_testing)]
1162 pub fn user<A: Abi>(
1163 application_id: ApplicationId<A>,
1164 operation: &A::Operation,
1165 ) -> Result<Self, bcs::Error> {
1166 Self::user_without_abi(application_id.forget_abi(), operation)
1167 }
1168
1169 #[cfg(with_testing)]
1172 pub fn user_without_abi(
1173 application_id: ApplicationId,
1174 operation: &impl Serialize,
1175 ) -> Result<Self, bcs::Error> {
1176 Ok(Operation::User {
1177 application_id,
1178 bytes: bcs::to_bytes(&operation)?,
1179 })
1180 }
1181
1182 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1185 match self {
1186 Operation::System(system_operation) => Some(system_operation),
1187 Operation::User { .. } => None,
1188 }
1189 }
1190
1191 pub fn application_id(&self) -> GenericApplicationId {
1192 match self {
1193 Self::System(_) => GenericApplicationId::System,
1194 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1195 }
1196 }
1197
1198 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1200 match self.as_system_operation() {
1201 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1202 vec![BlobId::new(*blob_hash, BlobType::Data)]
1203 }
1204 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1205 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1206 }
1207 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1208 _ => vec![],
1209 }
1210 }
1211
1212 pub fn is_exempt_from_permissions(&self) -> bool {
1214 let Operation::System(system_op) = self else {
1215 return false;
1216 };
1217 matches!(
1218 **system_op,
1219 SystemOperation::ProcessNewEpoch(_)
1220 | SystemOperation::ProcessRemovedEpoch(_)
1221 | SystemOperation::UpdateStreams(_)
1222 )
1223 }
1224}
1225
1226impl From<SystemMessage> for Message {
1227 fn from(message: SystemMessage) -> Self {
1228 Message::System(message)
1229 }
1230}
1231
1232impl Message {
1233 pub fn system(message: SystemMessage) -> Self {
1234 Message::System(message)
1235 }
1236
1237 pub fn user<A, M: Serialize>(
1240 application_id: ApplicationId<A>,
1241 message: &M,
1242 ) -> Result<Self, bcs::Error> {
1243 let application_id = application_id.forget_abi();
1244 let bytes = bcs::to_bytes(&message)?;
1245 Ok(Message::User {
1246 application_id,
1247 bytes,
1248 })
1249 }
1250
1251 pub fn application_id(&self) -> GenericApplicationId {
1252 match self {
1253 Self::System(_) => GenericApplicationId::System,
1254 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1255 }
1256 }
1257}
1258
1259impl From<SystemQuery> for Query {
1260 fn from(query: SystemQuery) -> Self {
1261 Query::System(query)
1262 }
1263}
1264
1265impl Query {
1266 pub fn system(query: SystemQuery) -> Self {
1267 Query::System(query)
1268 }
1269
1270 pub fn user<A: Abi>(
1272 application_id: ApplicationId<A>,
1273 query: &A::Query,
1274 ) -> Result<Self, serde_json::Error> {
1275 Self::user_without_abi(application_id.forget_abi(), query)
1276 }
1277
1278 pub fn user_without_abi(
1281 application_id: ApplicationId,
1282 query: &impl Serialize,
1283 ) -> Result<Self, serde_json::Error> {
1284 Ok(Query::User {
1285 application_id,
1286 bytes: serde_json::to_vec(&query)?,
1287 })
1288 }
1289
1290 pub fn application_id(&self) -> GenericApplicationId {
1291 match self {
1292 Self::System(_) => GenericApplicationId::System,
1293 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1294 }
1295 }
1296}
1297
1298impl From<SystemResponse> for QueryResponse {
1299 fn from(response: SystemResponse) -> Self {
1300 QueryResponse::System(response)
1301 }
1302}
1303
1304impl From<Vec<u8>> for QueryResponse {
1305 fn from(response: Vec<u8>) -> Self {
1306 QueryResponse::User(response)
1307 }
1308}
1309
1310#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1312pub struct BlobState {
1313 pub last_used_by: Option<CryptoHash>,
1317 pub chain_id: ChainId,
1319 pub block_height: BlockHeight,
1321 pub epoch: Option<Epoch>,
1323}
1324
1325#[derive(Clone, Copy, Display)]
1327#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1328pub enum WasmRuntime {
1329 #[cfg(with_wasmer)]
1330 #[default]
1331 #[display("wasmer")]
1332 Wasmer,
1333 #[cfg(with_wasmtime)]
1334 #[cfg_attr(not(with_wasmer), default)]
1335 #[display("wasmtime")]
1336 Wasmtime,
1337}
1338
1339#[derive(Clone, Copy, Display)]
1340#[cfg_attr(with_revm, derive(Debug, Default))]
1341pub enum EvmRuntime {
1342 #[cfg(with_revm)]
1343 #[default]
1344 #[display("revm")]
1345 Revm,
1346}
1347
1348pub trait WithWasmDefault {
1350 fn with_wasm_default(self) -> Self;
1351}
1352
1353impl WithWasmDefault for Option<WasmRuntime> {
1354 fn with_wasm_default(self) -> Self {
1355 #[cfg(with_wasm_runtime)]
1356 {
1357 Some(self.unwrap_or_default())
1358 }
1359 #[cfg(not(with_wasm_runtime))]
1360 {
1361 None
1362 }
1363 }
1364}
1365
1366impl FromStr for WasmRuntime {
1367 type Err = InvalidWasmRuntime;
1368
1369 fn from_str(string: &str) -> Result<Self, Self::Err> {
1370 match string {
1371 #[cfg(with_wasmer)]
1372 "wasmer" => Ok(WasmRuntime::Wasmer),
1373 #[cfg(with_wasmtime)]
1374 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1375 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1376 }
1377 }
1378}
1379
1380#[derive(Clone, Debug, Error)]
1382#[error("{0:?} is not a valid WebAssembly runtime")]
1383pub struct InvalidWasmRuntime(String);
1384
1385doc_scalar!(Operation, "An operation to be executed in a block");
1386doc_scalar!(
1387 Message,
1388 "A message to be sent and possibly executed in the receiver's block."
1389);
1390doc_scalar!(MessageKind, "The kind of outgoing message being sent");