1#![deny(clippy::large_futures)]
8
9pub mod committee;
10pub mod evm;
11mod execution;
12mod execution_state_actor;
13#[cfg(with_graphql)]
14mod graphql;
15mod policy;
16mod resources;
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, collections::BTreeMap, fmt, ops::RangeInclusive, str::FromStr, sync::Arc};
26
27use async_graphql::SimpleObject;
28use async_trait::async_trait;
29use custom_debug_derive::Debug;
30use dashmap::DashMap;
31use derive_more::Display;
32#[cfg(web)]
33use js_sys::wasm_bindgen::JsValue;
34use linera_base::{
35 abi::Abi,
36 crypto::{BcsHashable, CryptoHash},
37 data_types::{
38 Amount, ApplicationDescription, ApplicationPermissions, ArithmeticError, Blob, BlockHeight,
39 Bytecode, DecompressionError, Epoch, NetworkDescription, SendMessageRequest, StreamUpdate,
40 Timestamp,
41 },
42 doc_scalar, hex_debug, http,
43 identifiers::{
44 Account, AccountOwner, ApplicationId, BlobId, BlobType, ChainId, DataBlobHash, EventId,
45 GenericApplicationId, ModuleId, StreamName,
46 },
47 ownership::ChainOwnership,
48 task,
49 vm::VmRuntime,
50};
51use linera_views::{batch::Batch, ViewError};
52use serde::{Deserialize, Serialize};
53use system::AdminOperation;
54use thiserror::Error;
55
56#[cfg(with_revm)]
57use crate::evm::EvmExecutionError;
58use crate::runtime::ContractSyncRuntime;
59#[cfg(with_testing)]
60use crate::test_utils::dummy_chain_description;
61#[cfg(all(with_testing, with_wasm_runtime))]
62pub use crate::wasm::test as wasm_test;
63#[cfg(with_wasm_runtime)]
64pub use crate::wasm::{
65 BaseRuntimeApi, ContractEntrypoints, ContractRuntimeApi, RuntimeApiData, ServiceEntrypoints,
66 ServiceRuntimeApi, WasmContractModule, WasmExecutionError, WasmServiceModule,
67};
68pub use crate::{
69 committee::Committee,
70 execution::{ExecutionStateView, ServiceRuntimeEndpoint},
71 execution_state_actor::ExecutionRequest,
72 policy::ResourceControlPolicy,
73 resources::{BalanceHolder, ResourceController, ResourceTracker},
74 runtime::{
75 ContractSyncRuntimeHandle, ServiceRuntimeRequest, ServiceSyncRuntime,
76 ServiceSyncRuntimeHandle,
77 },
78 system::{
79 SystemExecutionStateView, SystemMessage, SystemOperation, SystemQuery, SystemResponse,
80 },
81 transaction_tracker::{TransactionOutcome, TransactionTracker},
82};
83
84pub const LINERA_SOL: &str = include_str!("../solidity/Linera.sol");
87pub const LINERA_TYPES_SOL: &str = include_str!("../solidity/LineraTypes.sol");
88
89const MAX_STREAM_NAME_LEN: usize = 64;
91
92#[derive(Clone)]
94pub struct UserContractCode(Box<dyn UserContractModule>);
95
96#[derive(Clone)]
98pub struct UserServiceCode(Box<dyn UserServiceModule>);
99
100pub type UserContractInstance = Box<dyn UserContract>;
102
103pub type UserServiceInstance = Box<dyn UserService>;
105
106pub trait UserContractModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
108 fn instantiate(
109 &self,
110 runtime: ContractSyncRuntimeHandle,
111 ) -> Result<UserContractInstance, ExecutionError>;
112}
113
114impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
115 fn from(module: T) -> Self {
116 Self(Box::new(module))
117 }
118}
119
120dyn_clone::clone_trait_object!(UserContractModule);
121
122pub trait UserServiceModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
124 fn instantiate(
125 &self,
126 runtime: ServiceSyncRuntimeHandle,
127 ) -> Result<UserServiceInstance, ExecutionError>;
128}
129
130impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
131 fn from(module: T) -> Self {
132 Self(Box::new(module))
133 }
134}
135
136dyn_clone::clone_trait_object!(UserServiceModule);
137
138impl UserServiceCode {
139 fn instantiate(
140 &self,
141 runtime: ServiceSyncRuntimeHandle,
142 ) -> Result<UserServiceInstance, ExecutionError> {
143 self.0.instantiate(runtime)
144 }
145}
146
147impl UserContractCode {
148 fn instantiate(
149 &self,
150 runtime: ContractSyncRuntimeHandle,
151 ) -> Result<UserContractInstance, ExecutionError> {
152 self.0.instantiate(runtime)
153 }
154}
155
156#[cfg(web)]
157const _: () = {
158 impl From<UserContractCode> for JsValue {
162 fn from(code: UserContractCode) -> JsValue {
163 let module: WasmContractModule = *(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 From<UserServiceCode> for JsValue {
171 fn from(code: UserServiceCode) -> JsValue {
172 let module: WasmServiceModule = *(code.0 as Box<dyn Any>)
173 .downcast()
174 .expect("we only support Wasm modules on the Web for now");
175 module.into()
176 }
177 }
178
179 impl TryFrom<JsValue> for UserContractCode {
180 type Error = JsValue;
181 fn try_from(value: JsValue) -> Result<Self, JsValue> {
182 WasmContractModule::try_from(value).map(Into::into)
183 }
184 }
185
186 impl TryFrom<JsValue> for UserServiceCode {
187 type Error = JsValue;
188 fn try_from(value: JsValue) -> Result<Self, JsValue> {
189 WasmServiceModule::try_from(value).map(Into::into)
190 }
191 }
192};
193
194#[derive(Error, Debug)]
196pub enum ExecutionError {
197 #[error(transparent)]
198 ViewError(#[from] ViewError),
199 #[error(transparent)]
200 ArithmeticError(#[from] ArithmeticError),
201 #[error("User application reported an error: {0}")]
202 UserError(String),
203 #[cfg(with_wasm_runtime)]
204 #[error(transparent)]
205 WasmError(#[from] WasmExecutionError),
206 #[cfg(with_revm)]
207 #[error(transparent)]
208 EvmError(#[from] EvmExecutionError),
209 #[error(transparent)]
210 DecompressionError(#[from] DecompressionError),
211 #[error("The given promise is invalid or was polled once already")]
212 InvalidPromise,
213
214 #[error("Attempted to perform a reentrant call to application {0}")]
215 ReentrantCall(ApplicationId),
216 #[error(
217 "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
218 from `finalize`"
219 )]
220 CrossApplicationCallInFinalize {
221 caller_id: Box<ApplicationId>,
222 callee_id: Box<ApplicationId>,
223 },
224 #[error("Failed to load bytecode from storage {0:?}")]
225 ApplicationBytecodeNotFound(Box<ApplicationDescription>),
226 #[error("Unsupported dynamic application load: {0:?}")]
228 UnsupportedDynamicApplicationLoad(Box<ApplicationId>),
229
230 #[error("Excessive number of bytes read from storage")]
231 ExcessiveRead,
232 #[error("Excessive number of bytes written to storage")]
233 ExcessiveWrite,
234 #[error("Block execution required too much fuel for VM {0}")]
235 MaximumFuelExceeded(VmRuntime),
236 #[error("Services running as oracles in block took longer than allowed")]
237 MaximumServiceOracleExecutionTimeExceeded,
238 #[error("Service running as an oracle produced a response that's too large")]
239 ServiceOracleResponseTooLarge,
240 #[error("Serialized size of the block exceeds limit")]
241 BlockTooLarge,
242 #[error("HTTP response exceeds the size limit of {limit} bytes, having at least {size} bytes")]
243 HttpResponseSizeLimitExceeded { limit: u64, size: u64 },
244 #[error("Runtime failed to respond to application")]
245 MissingRuntimeResponse,
246 #[error("Application is not authorized to perform system operations on this chain: {0:}")]
247 UnauthorizedApplication(ApplicationId),
248 #[error("Failed to make network reqwest: {0}")]
249 ReqwestError(#[from] reqwest::Error),
250 #[error("Encountered I/O error: {0}")]
251 IoError(#[from] std::io::Error),
252 #[error("More recorded oracle responses than expected")]
253 UnexpectedOracleResponse,
254 #[error("Invalid JSON: {0}")]
255 JsonError(#[from] serde_json::Error),
256 #[error(transparent)]
257 BcsError(#[from] bcs::Error),
258 #[error("Recorded response for oracle query has the wrong type")]
259 OracleResponseMismatch,
260 #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
261 AssertBefore {
262 timestamp: Timestamp,
263 local_time: Timestamp,
264 },
265
266 #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
267 StreamNameTooLong,
268 #[error("Blob exceeds size limit")]
269 BlobTooLarge,
270 #[error("Bytecode exceeds size limit")]
271 BytecodeTooLarge,
272 #[error("Attempt to perform an HTTP request to an unauthorized host: {0:?}")]
273 UnauthorizedHttpRequest(reqwest::Url),
274 #[error("Attempt to perform an HTTP request to an invalid URL")]
275 InvalidUrlForHttpRequest(#[from] url::ParseError),
276 #[error("Failed to send contract code to worker thread: {0:?}")]
277 ContractModuleSend(#[from] linera_base::task::SendError<UserContractCode>),
278 #[error("Failed to send service code to worker thread: {0:?}")]
279 ServiceModuleSend(#[from] linera_base::task::SendError<UserServiceCode>),
280 #[error("The chain being queried is not active {0}")]
281 InactiveChain(ChainId),
282 #[error("Blobs not found: {0:?}")]
283 BlobsNotFound(Vec<BlobId>),
284 #[error("Events not found: {0:?}")]
285 EventsNotFound(Vec<EventId>),
286
287 #[error("Invalid HTTP header name used for HTTP request")]
288 InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
289 #[error("Invalid HTTP header value used for HTTP request")]
290 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
291
292 #[error("No NetworkDescription found in storage")]
293 NoNetworkDescriptionFound,
294 #[error("{epoch:?} is not recognized by chain {chain_id:}")]
295 InvalidEpoch { chain_id: ChainId, epoch: Epoch },
296 #[error("Transfer must have positive amount")]
297 IncorrectTransferAmount,
298 #[error("Transfer from owned account must be authenticated by the right signer")]
299 UnauthenticatedTransferOwner,
300 #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
301 InsufficientBalance {
302 balance: Amount,
303 account: AccountOwner,
304 },
305 #[error("Required execution fees exceeded the total funding available. Fees {fees}, available balance: {balance}")]
306 FeesExceedFunding { fees: Amount, balance: Amount },
307 #[error("Claim must have positive amount")]
308 IncorrectClaimAmount,
309 #[error("Claim must be authenticated by the right signer")]
310 UnauthenticatedClaimOwner,
311 #[error("Admin operations are only allowed on the admin chain.")]
312 AdminOperationOnNonAdminChain,
313 #[error("Failed to create new committee: expected {expected}, but got {provided}")]
314 InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
315 #[error("Failed to remove committee")]
316 InvalidCommitteeRemoval,
317 #[error("No recorded response for oracle query")]
318 MissingOracleResponse,
319 #[error("process_streams was not called for all stream updates")]
320 UnprocessedStreams,
321 #[error("Internal error: {0}")]
322 InternalError(&'static str),
323 #[error("UpdateStreams is outdated")]
324 OutdatedUpdateStreams,
325}
326
327pub trait UserContract {
329 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
331
332 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
334
335 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
337
338 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
340
341 fn finalize(&mut self) -> Result<(), ExecutionError>;
343}
344
345pub trait UserService {
347 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
349}
350
351#[derive(Clone, Copy, Default)]
353pub struct ExecutionRuntimeConfig {}
354
355#[cfg_attr(not(web), async_trait)]
358#[cfg_attr(web, async_trait(?Send))]
359pub trait ExecutionRuntimeContext {
360 fn chain_id(&self) -> ChainId;
361
362 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
363
364 fn user_contracts(&self) -> &Arc<DashMap<ApplicationId, UserContractCode>>;
365
366 fn user_services(&self) -> &Arc<DashMap<ApplicationId, UserServiceCode>>;
367
368 async fn get_user_contract(
369 &self,
370 description: &ApplicationDescription,
371 txn_tracker: &TransactionTracker,
372 ) -> Result<UserContractCode, ExecutionError>;
373
374 async fn get_user_service(
375 &self,
376 description: &ApplicationDescription,
377 txn_tracker: &TransactionTracker,
378 ) -> Result<UserServiceCode, ExecutionError>;
379
380 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError>;
381
382 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError>;
383
384 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
385
386 async fn committees_for(
387 &self,
388 epoch_range: RangeInclusive<Epoch>,
389 ) -> Result<BTreeMap<Epoch, Committee>, ViewError>;
390
391 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
392
393 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
394
395 #[cfg(with_testing)]
396 async fn add_blobs(
397 &self,
398 blobs: impl IntoIterator<Item = Blob> + Send,
399 ) -> Result<(), ViewError>;
400
401 #[cfg(with_testing)]
402 async fn add_events(
403 &self,
404 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
405 ) -> Result<(), ViewError>;
406}
407
408#[derive(Clone, Copy, Debug)]
409pub struct OperationContext {
410 pub chain_id: ChainId,
412 #[debug(skip_if = Option::is_none)]
414 pub authenticated_signer: Option<AccountOwner>,
415 pub height: BlockHeight,
417 pub round: Option<u32>,
419 pub timestamp: Timestamp,
421}
422
423#[derive(Clone, Copy, Debug)]
424pub struct MessageContext {
425 pub chain_id: ChainId,
427 pub origin: ChainId,
429 pub is_bouncing: bool,
431 #[debug(skip_if = Option::is_none)]
433 pub authenticated_signer: Option<AccountOwner>,
434 #[debug(skip_if = Option::is_none)]
436 pub refund_grant_to: Option<Account>,
437 pub height: BlockHeight,
439 pub round: Option<u32>,
441 pub timestamp: Timestamp,
443}
444
445#[derive(Clone, Copy, Debug)]
446pub struct ProcessStreamsContext {
447 pub chain_id: ChainId,
449 pub height: BlockHeight,
451 pub round: Option<u32>,
453 pub timestamp: Timestamp,
455}
456
457impl From<MessageContext> for ProcessStreamsContext {
458 fn from(context: MessageContext) -> Self {
459 Self {
460 chain_id: context.chain_id,
461 height: context.height,
462 round: context.round,
463 timestamp: context.timestamp,
464 }
465 }
466}
467
468impl From<OperationContext> for ProcessStreamsContext {
469 fn from(context: OperationContext) -> Self {
470 Self {
471 chain_id: context.chain_id,
472 height: context.height,
473 round: context.round,
474 timestamp: context.timestamp,
475 }
476 }
477}
478
479#[derive(Clone, Copy, Debug)]
480pub struct FinalizeContext {
481 pub chain_id: ChainId,
483 #[debug(skip_if = Option::is_none)]
485 pub authenticated_signer: Option<AccountOwner>,
486 pub height: BlockHeight,
488 pub round: Option<u32>,
490}
491
492#[derive(Clone, Copy, Debug, Eq, PartialEq)]
493pub struct QueryContext {
494 pub chain_id: ChainId,
496 pub next_block_height: BlockHeight,
498 pub local_time: Timestamp,
500}
501
502pub trait BaseRuntime {
503 type Read: fmt::Debug + Send + Sync;
504 type ContainsKey: fmt::Debug + Send + Sync;
505 type ContainsKeys: fmt::Debug + Send + Sync;
506 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
507 type ReadValueBytes: fmt::Debug + Send + Sync;
508 type FindKeysByPrefix: fmt::Debug + Send + Sync;
509 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
510
511 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
513
514 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
516
517 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
519
520 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
522
523 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
525
526 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
528
529 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
531
532 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
534
535 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
537
538 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
540
541 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
543
544 #[cfg(feature = "test")]
546 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
547 let promise = self.contains_key_new(key)?;
548 self.contains_key_wait(&promise)
549 }
550
551 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
553
554 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
556
557 #[cfg(feature = "test")]
559 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
560 let promise = self.contains_keys_new(keys)?;
561 self.contains_keys_wait(&promise)
562 }
563
564 fn contains_keys_new(
566 &mut self,
567 keys: Vec<Vec<u8>>,
568 ) -> Result<Self::ContainsKeys, ExecutionError>;
569
570 fn contains_keys_wait(
572 &mut self,
573 promise: &Self::ContainsKeys,
574 ) -> Result<Vec<bool>, ExecutionError>;
575
576 #[cfg(feature = "test")]
578 fn read_multi_values_bytes(
579 &mut self,
580 keys: Vec<Vec<u8>>,
581 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
582 let promise = self.read_multi_values_bytes_new(keys)?;
583 self.read_multi_values_bytes_wait(&promise)
584 }
585
586 fn read_multi_values_bytes_new(
588 &mut self,
589 keys: Vec<Vec<u8>>,
590 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
591
592 fn read_multi_values_bytes_wait(
594 &mut self,
595 promise: &Self::ReadMultiValuesBytes,
596 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
597
598 #[cfg(feature = "test")]
600 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
601 let promise = self.read_value_bytes_new(key)?;
602 self.read_value_bytes_wait(&promise)
603 }
604
605 fn read_value_bytes_new(
607 &mut self,
608 key: Vec<u8>,
609 ) -> Result<Self::ReadValueBytes, ExecutionError>;
610
611 fn read_value_bytes_wait(
613 &mut self,
614 promise: &Self::ReadValueBytes,
615 ) -> Result<Option<Vec<u8>>, ExecutionError>;
616
617 fn find_keys_by_prefix_new(
619 &mut self,
620 key_prefix: Vec<u8>,
621 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
622
623 fn find_keys_by_prefix_wait(
625 &mut self,
626 promise: &Self::FindKeysByPrefix,
627 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
628
629 #[cfg(feature = "test")]
631 #[expect(clippy::type_complexity)]
632 fn find_key_values_by_prefix(
633 &mut self,
634 key_prefix: Vec<u8>,
635 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
636 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
637 self.find_key_values_by_prefix_wait(&promise)
638 }
639
640 fn find_key_values_by_prefix_new(
642 &mut self,
643 key_prefix: Vec<u8>,
644 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
645
646 #[expect(clippy::type_complexity)]
648 fn find_key_values_by_prefix_wait(
649 &mut self,
650 promise: &Self::FindKeyValuesByPrefix,
651 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
652
653 fn perform_http_request(
655 &mut self,
656 request: http::Request,
657 ) -> Result<http::Response, ExecutionError>;
658
659 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
665
666 fn read_data_blob(&mut self, hash: DataBlobHash) -> Result<Vec<u8>, ExecutionError>;
668
669 fn assert_data_blob_exists(&mut self, hash: DataBlobHash) -> Result<(), ExecutionError>;
671}
672
673pub trait ServiceRuntime: BaseRuntime {
674 fn try_query_application(
676 &mut self,
677 queried_id: ApplicationId,
678 argument: Vec<u8>,
679 ) -> Result<Vec<u8>, ExecutionError>;
680
681 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
683
684 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
686}
687
688pub trait ContractRuntime: BaseRuntime {
689 fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
691
692 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
695
696 fn message_origin_chain_id(&mut self) -> Result<Option<ChainId>, ExecutionError>;
698
699 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
702
703 fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
705
706 fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
708
709 fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
711
712 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
714
715 fn transfer(
717 &mut self,
718 source: AccountOwner,
719 destination: Account,
720 amount: Amount,
721 ) -> Result<(), ExecutionError>;
722
723 fn claim(
725 &mut self,
726 source: Account,
727 destination: Account,
728 amount: Amount,
729 ) -> Result<(), ExecutionError>;
730
731 fn try_call_application(
734 &mut self,
735 authenticated: bool,
736 callee_id: ApplicationId,
737 argument: Vec<u8>,
738 ) -> Result<Vec<u8>, ExecutionError>;
739
740 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
742
743 fn read_event(
747 &mut self,
748 chain_id: ChainId,
749 stream_name: StreamName,
750 index: u32,
751 ) -> Result<Vec<u8>, ExecutionError>;
752
753 fn subscribe_to_events(
755 &mut self,
756 chain_id: ChainId,
757 application_id: ApplicationId,
758 stream_name: StreamName,
759 ) -> Result<(), ExecutionError>;
760
761 fn unsubscribe_from_events(
763 &mut self,
764 chain_id: ChainId,
765 application_id: ApplicationId,
766 stream_name: StreamName,
767 ) -> Result<(), ExecutionError>;
768
769 fn query_service(
771 &mut self,
772 application_id: ApplicationId,
773 query: Vec<u8>,
774 ) -> Result<Vec<u8>, ExecutionError>;
775
776 fn open_chain(
778 &mut self,
779 ownership: ChainOwnership,
780 application_permissions: ApplicationPermissions,
781 balance: Amount,
782 ) -> Result<ChainId, ExecutionError>;
783
784 fn close_chain(&mut self) -> Result<(), ExecutionError>;
786
787 fn change_application_permissions(
789 &mut self,
790 application_permissions: ApplicationPermissions,
791 ) -> Result<(), ExecutionError>;
792
793 fn create_application(
795 &mut self,
796 module_id: ModuleId,
797 parameters: Vec<u8>,
798 argument: Vec<u8>,
799 required_application_ids: Vec<ApplicationId>,
800 ) -> Result<ApplicationId, ExecutionError>;
801
802 fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<DataBlobHash, ExecutionError>;
804
805 fn publish_module(
807 &mut self,
808 contract: Bytecode,
809 service: Bytecode,
810 vm_runtime: VmRuntime,
811 ) -> Result<ModuleId, ExecutionError>;
812
813 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
815
816 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
818}
819
820#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
822pub enum Operation {
823 System(Box<SystemOperation>),
825 User {
827 application_id: ApplicationId,
828 #[serde(with = "serde_bytes")]
829 #[debug(with = "hex_debug")]
830 bytes: Vec<u8>,
831 },
832}
833
834impl BcsHashable<'_> for Operation {}
835
836#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
838pub enum Message {
839 System(SystemMessage),
841 User {
843 application_id: ApplicationId,
844 #[serde(with = "serde_bytes")]
845 #[debug(with = "hex_debug")]
846 bytes: Vec<u8>,
847 },
848}
849
850#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
852pub enum Query {
853 System(SystemQuery),
855 User {
857 application_id: ApplicationId,
858 #[serde(with = "serde_bytes")]
859 #[debug(with = "hex_debug")]
860 bytes: Vec<u8>,
861 },
862}
863
864#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
866pub struct QueryOutcome<Response = QueryResponse> {
867 pub response: Response,
868 pub operations: Vec<Operation>,
869}
870
871impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
872 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
873 let QueryOutcome {
874 response,
875 operations,
876 } = system_outcome;
877
878 QueryOutcome {
879 response: QueryResponse::System(response),
880 operations,
881 }
882 }
883}
884
885impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
886 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
887 let QueryOutcome {
888 response,
889 operations,
890 } = user_service_outcome;
891
892 QueryOutcome {
893 response: QueryResponse::User(response),
894 operations,
895 }
896 }
897}
898
899#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
901pub enum QueryResponse {
902 System(SystemResponse),
904 User(
906 #[serde(with = "serde_bytes")]
907 #[debug(with = "hex_debug")]
908 Vec<u8>,
909 ),
910}
911
912#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy)]
914pub enum MessageKind {
915 Simple,
917 Protected,
920 Tracked,
923 Bouncing,
925}
926
927impl Display for MessageKind {
928 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
929 match self {
930 MessageKind::Simple => write!(f, "Simple"),
931 MessageKind::Protected => write!(f, "Protected"),
932 MessageKind::Tracked => write!(f, "Tracked"),
933 MessageKind::Bouncing => write!(f, "Bouncing"),
934 }
935 }
936}
937
938#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
940pub struct OutgoingMessage {
941 pub destination: ChainId,
943 #[debug(skip_if = Option::is_none)]
945 pub authenticated_signer: Option<AccountOwner>,
946 #[debug(skip_if = Amount::is_zero)]
948 pub grant: Amount,
949 #[debug(skip_if = Option::is_none)]
951 pub refund_grant_to: Option<Account>,
952 pub kind: MessageKind,
954 pub message: Message,
956}
957
958impl BcsHashable<'_> for OutgoingMessage {}
959
960impl OutgoingMessage {
961 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
963 OutgoingMessage {
964 destination: recipient,
965 authenticated_signer: None,
966 grant: Amount::ZERO,
967 refund_grant_to: None,
968 kind: MessageKind::Simple,
969 message: message.into(),
970 }
971 }
972
973 pub fn with_kind(mut self, kind: MessageKind) -> Self {
975 self.kind = kind;
976 self
977 }
978
979 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
981 self.authenticated_signer = authenticated_signer;
982 self
983 }
984}
985
986impl OperationContext {
987 fn refund_grant_to(&self) -> Option<Account> {
990 self.authenticated_signer.map(|owner| Account {
991 chain_id: self.chain_id,
992 owner,
993 })
994 }
995}
996
997#[cfg(with_testing)]
998#[derive(Clone)]
999pub struct TestExecutionRuntimeContext {
1000 chain_id: ChainId,
1001 execution_runtime_config: ExecutionRuntimeConfig,
1002 user_contracts: Arc<DashMap<ApplicationId, UserContractCode>>,
1003 user_services: Arc<DashMap<ApplicationId, UserServiceCode>>,
1004 blobs: Arc<DashMap<BlobId, Blob>>,
1005 events: Arc<DashMap<EventId, Vec<u8>>>,
1006}
1007
1008#[cfg(with_testing)]
1009impl TestExecutionRuntimeContext {
1010 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1011 Self {
1012 chain_id,
1013 execution_runtime_config,
1014 user_contracts: Arc::default(),
1015 user_services: Arc::default(),
1016 blobs: Arc::default(),
1017 events: Arc::default(),
1018 }
1019 }
1020}
1021
1022#[cfg(with_testing)]
1023#[cfg_attr(not(web), async_trait)]
1024#[cfg_attr(web, async_trait(?Send))]
1025impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1026 fn chain_id(&self) -> ChainId {
1027 self.chain_id
1028 }
1029
1030 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1031 self.execution_runtime_config
1032 }
1033
1034 fn user_contracts(&self) -> &Arc<DashMap<ApplicationId, UserContractCode>> {
1035 &self.user_contracts
1036 }
1037
1038 fn user_services(&self) -> &Arc<DashMap<ApplicationId, UserServiceCode>> {
1039 &self.user_services
1040 }
1041
1042 async fn get_user_contract(
1043 &self,
1044 description: &ApplicationDescription,
1045 _txn_tracker: &TransactionTracker,
1046 ) -> Result<UserContractCode, ExecutionError> {
1047 let application_id = description.into();
1048 Ok(self
1049 .user_contracts()
1050 .get(&application_id)
1051 .ok_or_else(|| {
1052 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1053 })?
1054 .clone())
1055 }
1056
1057 async fn get_user_service(
1058 &self,
1059 description: &ApplicationDescription,
1060 _txn_tracker: &TransactionTracker,
1061 ) -> Result<UserServiceCode, ExecutionError> {
1062 let application_id = description.into();
1063 Ok(self
1064 .user_services()
1065 .get(&application_id)
1066 .ok_or_else(|| {
1067 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1068 })?
1069 .clone())
1070 }
1071
1072 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError> {
1073 match self.blobs.get(&blob_id) {
1074 None => Ok(None),
1075 Some(blob) => Ok(Some(blob.clone())),
1076 }
1077 }
1078
1079 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError> {
1080 match self.events.get(&event_id) {
1081 None => Ok(None),
1082 Some(event) => Ok(Some(event.clone())),
1083 }
1084 }
1085
1086 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1087 Ok(Some(NetworkDescription {
1088 admin_chain_id: dummy_chain_description(0).id(),
1089 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1090 genesis_timestamp: Timestamp::from(0),
1091 genesis_committee_blob_hash: CryptoHash::test_hash("genesis committee"),
1092 name: "dummy network description".to_string(),
1093 }))
1094 }
1095
1096 async fn committees_for(
1097 &self,
1098 epoch_range: RangeInclusive<Epoch>,
1099 ) -> Result<BTreeMap<Epoch, Committee>, ViewError> {
1100 let committee_blob_bytes = self
1101 .blobs
1102 .iter()
1103 .find(|item| item.key().blob_type == BlobType::Committee)
1104 .ok_or_else(|| ViewError::NotFound("committee not found".to_owned()))?
1105 .value()
1106 .bytes()
1107 .to_vec();
1108 let committee: Committee = bcs::from_bytes(&committee_blob_bytes)?;
1109 Ok((epoch_range.start().0..=epoch_range.end().0)
1113 .map(|epoch| (Epoch::from(epoch), committee.clone()))
1114 .collect())
1115 }
1116
1117 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1118 Ok(self.blobs.contains_key(&blob_id))
1119 }
1120
1121 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1122 Ok(self.events.contains_key(&event_id))
1123 }
1124
1125 #[cfg(with_testing)]
1126 async fn add_blobs(
1127 &self,
1128 blobs: impl IntoIterator<Item = Blob> + Send,
1129 ) -> Result<(), ViewError> {
1130 for blob in blobs {
1131 self.blobs.insert(blob.id(), blob);
1132 }
1133
1134 Ok(())
1135 }
1136
1137 #[cfg(with_testing)]
1138 async fn add_events(
1139 &self,
1140 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1141 ) -> Result<(), ViewError> {
1142 for (event_id, bytes) in events {
1143 self.events.insert(event_id, bytes);
1144 }
1145
1146 Ok(())
1147 }
1148}
1149
1150impl From<SystemOperation> for Operation {
1151 fn from(operation: SystemOperation) -> Self {
1152 Operation::System(Box::new(operation))
1153 }
1154}
1155
1156impl Operation {
1157 pub fn system(operation: SystemOperation) -> Self {
1158 Operation::System(Box::new(operation))
1159 }
1160
1161 #[cfg(with_testing)]
1163 pub fn user<A: Abi>(
1164 application_id: ApplicationId<A>,
1165 operation: &A::Operation,
1166 ) -> Result<Self, bcs::Error> {
1167 Self::user_without_abi(application_id.forget_abi(), operation)
1168 }
1169
1170 #[cfg(with_testing)]
1173 pub fn user_without_abi(
1174 application_id: ApplicationId,
1175 operation: &impl Serialize,
1176 ) -> Result<Self, bcs::Error> {
1177 Ok(Operation::User {
1178 application_id,
1179 bytes: bcs::to_bytes(&operation)?,
1180 })
1181 }
1182
1183 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1186 match self {
1187 Operation::System(system_operation) => Some(system_operation),
1188 Operation::User { .. } => None,
1189 }
1190 }
1191
1192 pub fn application_id(&self) -> GenericApplicationId {
1193 match self {
1194 Self::System(_) => GenericApplicationId::System,
1195 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1196 }
1197 }
1198
1199 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1201 match self.as_system_operation() {
1202 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1203 vec![BlobId::new(*blob_hash, BlobType::Data)]
1204 }
1205 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1206 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1207 }
1208 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1209 _ => vec![],
1210 }
1211 }
1212
1213 pub fn is_exempt_from_permissions(&self) -> bool {
1215 let Operation::System(system_op) = self else {
1216 return false;
1217 };
1218 matches!(
1219 **system_op,
1220 SystemOperation::ProcessNewEpoch(_)
1221 | SystemOperation::ProcessRemovedEpoch(_)
1222 | SystemOperation::UpdateStreams(_)
1223 )
1224 }
1225}
1226
1227impl From<SystemMessage> for Message {
1228 fn from(message: SystemMessage) -> Self {
1229 Message::System(message)
1230 }
1231}
1232
1233impl Message {
1234 pub fn system(message: SystemMessage) -> Self {
1235 Message::System(message)
1236 }
1237
1238 pub fn user<A, M: Serialize>(
1241 application_id: ApplicationId<A>,
1242 message: &M,
1243 ) -> Result<Self, bcs::Error> {
1244 let application_id = application_id.forget_abi();
1245 let bytes = bcs::to_bytes(&message)?;
1246 Ok(Message::User {
1247 application_id,
1248 bytes,
1249 })
1250 }
1251
1252 pub fn application_id(&self) -> GenericApplicationId {
1253 match self {
1254 Self::System(_) => GenericApplicationId::System,
1255 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1256 }
1257 }
1258}
1259
1260impl From<SystemQuery> for Query {
1261 fn from(query: SystemQuery) -> Self {
1262 Query::System(query)
1263 }
1264}
1265
1266impl Query {
1267 pub fn system(query: SystemQuery) -> Self {
1268 Query::System(query)
1269 }
1270
1271 pub fn user<A: Abi>(
1273 application_id: ApplicationId<A>,
1274 query: &A::Query,
1275 ) -> Result<Self, serde_json::Error> {
1276 Self::user_without_abi(application_id.forget_abi(), query)
1277 }
1278
1279 pub fn user_without_abi(
1282 application_id: ApplicationId,
1283 query: &impl Serialize,
1284 ) -> Result<Self, serde_json::Error> {
1285 Ok(Query::User {
1286 application_id,
1287 bytes: serde_json::to_vec(&query)?,
1288 })
1289 }
1290
1291 pub fn application_id(&self) -> GenericApplicationId {
1292 match self {
1293 Self::System(_) => GenericApplicationId::System,
1294 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1295 }
1296 }
1297}
1298
1299impl From<SystemResponse> for QueryResponse {
1300 fn from(response: SystemResponse) -> Self {
1301 QueryResponse::System(response)
1302 }
1303}
1304
1305impl From<Vec<u8>> for QueryResponse {
1306 fn from(response: Vec<u8>) -> Self {
1307 QueryResponse::User(response)
1308 }
1309}
1310
1311#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1313pub struct BlobState {
1314 pub last_used_by: Option<CryptoHash>,
1318 pub chain_id: ChainId,
1320 pub block_height: BlockHeight,
1322 pub epoch: Option<Epoch>,
1324}
1325
1326#[derive(Clone, Copy, Display)]
1328#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1329pub enum WasmRuntime {
1330 #[cfg(with_wasmer)]
1331 #[default]
1332 #[display("wasmer")]
1333 Wasmer,
1334 #[cfg(with_wasmtime)]
1335 #[cfg_attr(not(with_wasmer), default)]
1336 #[display("wasmtime")]
1337 Wasmtime,
1338}
1339
1340#[derive(Clone, Copy, Display)]
1341#[cfg_attr(with_revm, derive(Debug, Default))]
1342pub enum EvmRuntime {
1343 #[cfg(with_revm)]
1344 #[default]
1345 #[display("revm")]
1346 Revm,
1347}
1348
1349pub trait WithWasmDefault {
1351 fn with_wasm_default(self) -> Self;
1352}
1353
1354impl WithWasmDefault for Option<WasmRuntime> {
1355 fn with_wasm_default(self) -> Self {
1356 #[cfg(with_wasm_runtime)]
1357 {
1358 Some(self.unwrap_or_default())
1359 }
1360 #[cfg(not(with_wasm_runtime))]
1361 {
1362 None
1363 }
1364 }
1365}
1366
1367impl FromStr for WasmRuntime {
1368 type Err = InvalidWasmRuntime;
1369
1370 fn from_str(string: &str) -> Result<Self, Self::Err> {
1371 match string {
1372 #[cfg(with_wasmer)]
1373 "wasmer" => Ok(WasmRuntime::Wasmer),
1374 #[cfg(with_wasmtime)]
1375 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1376 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1377 }
1378 }
1379}
1380
1381#[derive(Clone, Debug, Error)]
1383#[error("{0:?} is not a valid WebAssembly runtime")]
1384pub struct InvalidWasmRuntime(String);
1385
1386doc_scalar!(Operation, "An operation to be executed in a block");
1387doc_scalar!(
1388 Message,
1389 "A message to be sent and possibly executed in the receiver's block."
1390);
1391doc_scalar!(MessageKind, "The kind of outgoing message being sent");