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, EventId,
45 GenericApplicationId, MessageId, 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("Attempt to write to storage from a contract")]
225 ServiceWriteAttempt,
226 #[error("Failed to load bytecode from storage {0:?}")]
227 ApplicationBytecodeNotFound(Box<ApplicationDescription>),
228 #[error("Unsupported dynamic application load: {0:?}")]
230 UnsupportedDynamicApplicationLoad(Box<ApplicationId>),
231
232 #[error("Excessive number of bytes read from storage")]
233 ExcessiveRead,
234 #[error("Excessive number of bytes written to storage")]
235 ExcessiveWrite,
236 #[error("Block execution required too much fuel for VM {0}")]
237 MaximumFuelExceeded(VmRuntime),
238 #[error("Services running as oracles in block took longer than allowed")]
239 MaximumServiceOracleExecutionTimeExceeded,
240 #[error("Service running as an oracle produced a response that's too large")]
241 ServiceOracleResponseTooLarge,
242 #[error("Serialized size of the block exceeds limit")]
243 BlockTooLarge,
244 #[error("HTTP response exceeds the size limit of {limit} bytes, having at least {size} bytes")]
245 HttpResponseSizeLimitExceeded { limit: u64, size: u64 },
246 #[error("Runtime failed to respond to application")]
247 MissingRuntimeResponse,
248 #[error("Module ID {0:?} is invalid")]
249 InvalidModuleId(ModuleId),
250 #[error("Application is not authorized to perform system operations on this chain: {0:}")]
251 UnauthorizedApplication(ApplicationId),
252 #[error("Failed to make network reqwest: {0}")]
253 ReqwestError(#[from] reqwest::Error),
254 #[error("Encountered I/O error: {0}")]
255 IoError(#[from] std::io::Error),
256 #[error("More recorded oracle responses than expected")]
257 UnexpectedOracleResponse,
258 #[error("Invalid JSON: {0}")]
259 JsonError(#[from] serde_json::Error),
260 #[error(transparent)]
261 BcsError(#[from] bcs::Error),
262 #[error("Recorded response for oracle query has the wrong type")]
263 OracleResponseMismatch,
264 #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
265 AssertBefore {
266 timestamp: Timestamp,
267 local_time: Timestamp,
268 },
269
270 #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
271 StreamNameTooLong,
272 #[error("Blob exceeds size limit")]
273 BlobTooLarge,
274 #[error("Bytecode exceeds size limit")]
275 BytecodeTooLarge,
276 #[error("Attempt to perform an HTTP request to an unauthorized host: {0:?}")]
277 UnauthorizedHttpRequest(reqwest::Url),
278 #[error("Attempt to perform an HTTP request to an invalid URL")]
279 InvalidUrlForHttpRequest(#[from] url::ParseError),
280 #[error("Failed to send contract code to worker thread: {0:?}")]
281 ContractModuleSend(#[from] linera_base::task::SendError<UserContractCode>),
282 #[error("Failed to send service code to worker thread: {0:?}")]
283 ServiceModuleSend(#[from] linera_base::task::SendError<UserServiceCode>),
284 #[error("The chain being queried is not active {0}")]
285 InactiveChain(ChainId),
286 #[error("Blobs not found: {0:?}")]
287 BlobsNotFound(Vec<BlobId>),
288 #[error("Events not found: {0:?}")]
289 EventsNotFound(Vec<EventId>),
290
291 #[error("Invalid HTTP header name used for HTTP request")]
292 InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
293 #[error("Invalid HTTP header value used for HTTP request")]
294 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
295
296 #[error("No NetworkDescription found in storage")]
297 NoNetworkDescriptionFound,
298 #[error("Invalid committees")]
299 InvalidCommittees,
300 #[error("{epoch:?} is not recognized by chain {chain_id:}")]
301 InvalidEpoch { chain_id: ChainId, epoch: Epoch },
302 #[error("Transfer must have positive amount")]
303 IncorrectTransferAmount,
304 #[error("Transfer from owned account must be authenticated by the right signer")]
305 UnauthenticatedTransferOwner,
306 #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
307 InsufficientBalance {
308 balance: Amount,
309 account: AccountOwner,
310 },
311 #[error("Required execution fees exceeded the total funding available. Fees {fees}, available balance: {balance}")]
312 FeesExceedFunding { fees: Amount, balance: Amount },
313 #[error("Claim must have positive amount")]
314 IncorrectClaimAmount,
315 #[error("Claim must be authenticated by the right signer")]
316 UnauthenticatedClaimOwner,
317 #[error("Admin operations are only allowed on the admin chain.")]
318 AdminOperationOnNonAdminChain,
319 #[error("Failed to create new committee: expected {expected}, but got {provided}")]
320 InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
321 #[error("Failed to remove committee")]
322 InvalidCommitteeRemoval,
323 #[error("Amount overflow")]
324 AmountOverflow,
325 #[error("Amount underflow")]
326 AmountUnderflow,
327 #[error("Chain balance overflow")]
328 BalanceOverflow,
329 #[error("Chain balance underflow")]
330 BalanceUnderflow,
331 #[error("Application {0:?} is not registered by the chain")]
332 UnknownApplicationId(Box<ApplicationId>),
333 #[error("No recorded response for oracle query")]
334 MissingOracleResponse,
335 #[error("process_streams was not called for all stream updates")]
336 UnprocessedStreams,
337 #[error("Internal error: {0}")]
338 InternalError(&'static str),
339 #[error("UpdateStreams is outdated")]
340 OutdatedUpdateStreams,
341}
342
343pub trait UserContract {
345 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
347
348 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
350
351 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
353
354 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
356
357 fn finalize(&mut self) -> Result<(), ExecutionError>;
359}
360
361pub trait UserService {
363 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
365}
366
367#[derive(Clone, Copy, Default)]
369pub struct ExecutionRuntimeConfig {}
370
371#[cfg_attr(not(web), async_trait)]
374#[cfg_attr(web, async_trait(?Send))]
375pub trait ExecutionRuntimeContext {
376 fn chain_id(&self) -> ChainId;
377
378 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
379
380 fn user_contracts(&self) -> &Arc<DashMap<ApplicationId, UserContractCode>>;
381
382 fn user_services(&self) -> &Arc<DashMap<ApplicationId, UserServiceCode>>;
383
384 async fn get_user_contract(
385 &self,
386 description: &ApplicationDescription,
387 ) -> Result<UserContractCode, ExecutionError>;
388
389 async fn get_user_service(
390 &self,
391 description: &ApplicationDescription,
392 ) -> Result<UserServiceCode, ExecutionError>;
393
394 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError>;
395
396 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError>;
397
398 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
399
400 async fn committees_for(
401 &self,
402 epoch_range: RangeInclusive<Epoch>,
403 ) -> Result<BTreeMap<Epoch, Committee>, ViewError>;
404
405 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
406
407 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
408
409 #[cfg(with_testing)]
410 async fn add_blobs(
411 &self,
412 blobs: impl IntoIterator<Item = Blob> + Send,
413 ) -> Result<(), ViewError>;
414
415 #[cfg(with_testing)]
416 async fn add_events(
417 &self,
418 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
419 ) -> Result<(), ViewError>;
420}
421
422#[derive(Clone, Copy, Debug)]
423pub struct OperationContext {
424 pub chain_id: ChainId,
426 #[debug(skip_if = Option::is_none)]
428 pub authenticated_signer: Option<AccountOwner>,
429 #[debug(skip_if = Option::is_none)]
432 pub authenticated_caller_id: Option<ApplicationId>,
433 pub height: BlockHeight,
435 pub round: Option<u32>,
437 pub timestamp: Timestamp,
439}
440
441#[derive(Clone, Copy, Debug)]
442pub struct MessageContext {
443 pub chain_id: ChainId,
445 pub is_bouncing: bool,
447 #[debug(skip_if = Option::is_none)]
449 pub authenticated_signer: Option<AccountOwner>,
450 #[debug(skip_if = Option::is_none)]
452 pub refund_grant_to: Option<Account>,
453 pub height: BlockHeight,
455 pub round: Option<u32>,
457 pub timestamp: Timestamp,
459 pub message_id: MessageId,
462}
463
464#[derive(Clone, Copy, Debug)]
465pub struct ProcessStreamsContext {
466 pub chain_id: ChainId,
468 pub height: BlockHeight,
470 pub round: Option<u32>,
472 pub timestamp: Timestamp,
474}
475
476impl From<MessageContext> for ProcessStreamsContext {
477 fn from(context: MessageContext) -> Self {
478 Self {
479 chain_id: context.chain_id,
480 height: context.height,
481 round: context.round,
482 timestamp: context.timestamp,
483 }
484 }
485}
486
487impl From<OperationContext> for ProcessStreamsContext {
488 fn from(context: OperationContext) -> Self {
489 Self {
490 chain_id: context.chain_id,
491 height: context.height,
492 round: context.round,
493 timestamp: context.timestamp,
494 }
495 }
496}
497
498#[derive(Clone, Copy, Debug)]
499pub struct FinalizeContext {
500 pub chain_id: ChainId,
502 #[debug(skip_if = Option::is_none)]
504 pub authenticated_signer: Option<AccountOwner>,
505 pub height: BlockHeight,
507 pub round: Option<u32>,
509}
510
511#[derive(Clone, Copy, Debug, Eq, PartialEq)]
512pub struct QueryContext {
513 pub chain_id: ChainId,
515 pub next_block_height: BlockHeight,
517 pub local_time: Timestamp,
519}
520
521pub trait BaseRuntime {
522 type Read: fmt::Debug + Send + Sync;
523 type ContainsKey: fmt::Debug + Send + Sync;
524 type ContainsKeys: fmt::Debug + Send + Sync;
525 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
526 type ReadValueBytes: fmt::Debug + Send + Sync;
527 type FindKeysByPrefix: fmt::Debug + Send + Sync;
528 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
529
530 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
532
533 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
535
536 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
538
539 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
541
542 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
544
545 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
547
548 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
550
551 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
553
554 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
556
557 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
559
560 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
562
563 #[cfg(feature = "test")]
565 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
566 let promise = self.contains_key_new(key)?;
567 self.contains_key_wait(&promise)
568 }
569
570 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
572
573 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
575
576 #[cfg(feature = "test")]
578 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
579 let promise = self.contains_keys_new(keys)?;
580 self.contains_keys_wait(&promise)
581 }
582
583 fn contains_keys_new(
585 &mut self,
586 keys: Vec<Vec<u8>>,
587 ) -> Result<Self::ContainsKeys, ExecutionError>;
588
589 fn contains_keys_wait(
591 &mut self,
592 promise: &Self::ContainsKeys,
593 ) -> Result<Vec<bool>, ExecutionError>;
594
595 #[cfg(feature = "test")]
597 fn read_multi_values_bytes(
598 &mut self,
599 keys: Vec<Vec<u8>>,
600 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
601 let promise = self.read_multi_values_bytes_new(keys)?;
602 self.read_multi_values_bytes_wait(&promise)
603 }
604
605 fn read_multi_values_bytes_new(
607 &mut self,
608 keys: Vec<Vec<u8>>,
609 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
610
611 fn read_multi_values_bytes_wait(
613 &mut self,
614 promise: &Self::ReadMultiValuesBytes,
615 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
616
617 #[cfg(feature = "test")]
619 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
620 let promise = self.read_value_bytes_new(key)?;
621 self.read_value_bytes_wait(&promise)
622 }
623
624 fn read_value_bytes_new(
626 &mut self,
627 key: Vec<u8>,
628 ) -> Result<Self::ReadValueBytes, ExecutionError>;
629
630 fn read_value_bytes_wait(
632 &mut self,
633 promise: &Self::ReadValueBytes,
634 ) -> Result<Option<Vec<u8>>, ExecutionError>;
635
636 fn find_keys_by_prefix_new(
638 &mut self,
639 key_prefix: Vec<u8>,
640 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
641
642 fn find_keys_by_prefix_wait(
644 &mut self,
645 promise: &Self::FindKeysByPrefix,
646 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
647
648 #[cfg(feature = "test")]
650 #[expect(clippy::type_complexity)]
651 fn find_key_values_by_prefix(
652 &mut self,
653 key_prefix: Vec<u8>,
654 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
655 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
656 self.find_key_values_by_prefix_wait(&promise)
657 }
658
659 fn find_key_values_by_prefix_new(
661 &mut self,
662 key_prefix: Vec<u8>,
663 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
664
665 #[expect(clippy::type_complexity)]
667 fn find_key_values_by_prefix_wait(
668 &mut self,
669 promise: &Self::FindKeyValuesByPrefix,
670 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
671
672 fn perform_http_request(
674 &mut self,
675 request: http::Request,
676 ) -> Result<http::Response, ExecutionError>;
677
678 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
684
685 fn read_data_blob(&mut self, hash: &CryptoHash) -> Result<Vec<u8>, ExecutionError>;
687
688 fn assert_data_blob_exists(&mut self, hash: &CryptoHash) -> Result<(), ExecutionError>;
690}
691
692pub trait ServiceRuntime: BaseRuntime {
693 fn try_query_application(
695 &mut self,
696 queried_id: ApplicationId,
697 argument: Vec<u8>,
698 ) -> Result<Vec<u8>, ExecutionError>;
699
700 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
702
703 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
705}
706
707pub trait ContractRuntime: BaseRuntime {
708 fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
710
711 fn message_id(&mut self) -> Result<Option<MessageId>, ExecutionError>;
713
714 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
717
718 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
721
722 fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
724
725 fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
727
728 fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
730
731 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
733
734 fn transfer(
736 &mut self,
737 source: AccountOwner,
738 destination: Account,
739 amount: Amount,
740 ) -> Result<(), ExecutionError>;
741
742 fn claim(
744 &mut self,
745 source: Account,
746 destination: Account,
747 amount: Amount,
748 ) -> Result<(), ExecutionError>;
749
750 fn try_call_application(
753 &mut self,
754 authenticated: bool,
755 callee_id: ApplicationId,
756 argument: Vec<u8>,
757 ) -> Result<Vec<u8>, ExecutionError>;
758
759 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
761
762 fn read_event(
766 &mut self,
767 chain_id: ChainId,
768 stream_name: StreamName,
769 index: u32,
770 ) -> Result<Vec<u8>, ExecutionError>;
771
772 fn subscribe_to_events(
774 &mut self,
775 chain_id: ChainId,
776 application_id: ApplicationId,
777 stream_name: StreamName,
778 ) -> Result<(), ExecutionError>;
779
780 fn unsubscribe_from_events(
782 &mut self,
783 chain_id: ChainId,
784 application_id: ApplicationId,
785 stream_name: StreamName,
786 ) -> Result<(), ExecutionError>;
787
788 fn query_service(
790 &mut self,
791 application_id: ApplicationId,
792 query: Vec<u8>,
793 ) -> Result<Vec<u8>, ExecutionError>;
794
795 fn open_chain(
797 &mut self,
798 ownership: ChainOwnership,
799 application_permissions: ApplicationPermissions,
800 balance: Amount,
801 ) -> Result<ChainId, ExecutionError>;
802
803 fn close_chain(&mut self) -> Result<(), ExecutionError>;
805
806 fn change_application_permissions(
808 &mut self,
809 application_permissions: ApplicationPermissions,
810 ) -> Result<(), ExecutionError>;
811
812 fn create_application(
814 &mut self,
815 module_id: ModuleId,
816 parameters: Vec<u8>,
817 argument: Vec<u8>,
818 required_application_ids: Vec<ApplicationId>,
819 ) -> Result<ApplicationId, ExecutionError>;
820
821 fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<BlobId, ExecutionError>;
823
824 fn publish_module(
826 &mut self,
827 contract: Bytecode,
828 service: Bytecode,
829 vm_runtime: VmRuntime,
830 ) -> Result<ModuleId, ExecutionError>;
831
832 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
834
835 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
837}
838
839#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
841pub enum Operation {
842 System(Box<SystemOperation>),
844 User {
846 application_id: ApplicationId,
847 #[serde(with = "serde_bytes")]
848 #[debug(with = "hex_debug")]
849 bytes: Vec<u8>,
850 },
851}
852
853impl BcsHashable<'_> for Operation {}
854
855#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
857pub enum Message {
858 System(SystemMessage),
860 User {
862 application_id: ApplicationId,
863 #[serde(with = "serde_bytes")]
864 #[debug(with = "hex_debug")]
865 bytes: Vec<u8>,
866 },
867}
868
869#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
871pub enum Query {
872 System(SystemQuery),
874 User {
876 application_id: ApplicationId,
877 #[serde(with = "serde_bytes")]
878 #[debug(with = "hex_debug")]
879 bytes: Vec<u8>,
880 },
881}
882
883#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
885pub struct QueryOutcome<Response = QueryResponse> {
886 pub response: Response,
887 pub operations: Vec<Operation>,
888}
889
890impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
891 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
892 let QueryOutcome {
893 response,
894 operations,
895 } = system_outcome;
896
897 QueryOutcome {
898 response: QueryResponse::System(response),
899 operations,
900 }
901 }
902}
903
904impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
905 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
906 let QueryOutcome {
907 response,
908 operations,
909 } = user_service_outcome;
910
911 QueryOutcome {
912 response: QueryResponse::User(response),
913 operations,
914 }
915 }
916}
917
918#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
920pub enum QueryResponse {
921 System(SystemResponse),
923 User(
925 #[serde(with = "serde_bytes")]
926 #[debug(with = "hex_debug")]
927 Vec<u8>,
928 ),
929}
930
931#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy)]
933pub enum MessageKind {
934 Simple,
936 Protected,
939 Tracked,
942 Bouncing,
944}
945
946#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
948pub struct OutgoingMessage {
949 pub destination: ChainId,
951 #[debug(skip_if = Option::is_none)]
953 pub authenticated_signer: Option<AccountOwner>,
954 #[debug(skip_if = Amount::is_zero)]
956 pub grant: Amount,
957 #[debug(skip_if = Option::is_none)]
959 pub refund_grant_to: Option<Account>,
960 pub kind: MessageKind,
962 pub message: Message,
964}
965
966impl BcsHashable<'_> for OutgoingMessage {}
967
968impl OutgoingMessage {
969 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
971 OutgoingMessage {
972 destination: recipient,
973 authenticated_signer: None,
974 grant: Amount::ZERO,
975 refund_grant_to: None,
976 kind: MessageKind::Simple,
977 message: message.into(),
978 }
979 }
980
981 pub fn with_kind(mut self, kind: MessageKind) -> Self {
983 self.kind = kind;
984 self
985 }
986
987 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
989 self.authenticated_signer = authenticated_signer;
990 self
991 }
992}
993
994impl OperationContext {
995 fn refund_grant_to(&self) -> Option<Account> {
998 self.authenticated_signer.map(|owner| Account {
999 chain_id: self.chain_id,
1000 owner,
1001 })
1002 }
1003}
1004
1005#[cfg(with_testing)]
1006#[derive(Clone)]
1007pub struct TestExecutionRuntimeContext {
1008 chain_id: ChainId,
1009 execution_runtime_config: ExecutionRuntimeConfig,
1010 user_contracts: Arc<DashMap<ApplicationId, UserContractCode>>,
1011 user_services: Arc<DashMap<ApplicationId, UserServiceCode>>,
1012 blobs: Arc<DashMap<BlobId, Blob>>,
1013 events: Arc<DashMap<EventId, Vec<u8>>>,
1014}
1015
1016#[cfg(with_testing)]
1017impl TestExecutionRuntimeContext {
1018 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1019 Self {
1020 chain_id,
1021 execution_runtime_config,
1022 user_contracts: Arc::default(),
1023 user_services: Arc::default(),
1024 blobs: Arc::default(),
1025 events: Arc::default(),
1026 }
1027 }
1028}
1029
1030#[cfg(with_testing)]
1031#[cfg_attr(not(web), async_trait)]
1032#[cfg_attr(web, async_trait(?Send))]
1033impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1034 fn chain_id(&self) -> ChainId {
1035 self.chain_id
1036 }
1037
1038 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1039 self.execution_runtime_config
1040 }
1041
1042 fn user_contracts(&self) -> &Arc<DashMap<ApplicationId, UserContractCode>> {
1043 &self.user_contracts
1044 }
1045
1046 fn user_services(&self) -> &Arc<DashMap<ApplicationId, UserServiceCode>> {
1047 &self.user_services
1048 }
1049
1050 async fn get_user_contract(
1051 &self,
1052 description: &ApplicationDescription,
1053 ) -> Result<UserContractCode, ExecutionError> {
1054 let application_id = description.into();
1055 Ok(self
1056 .user_contracts()
1057 .get(&application_id)
1058 .ok_or_else(|| {
1059 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1060 })?
1061 .clone())
1062 }
1063
1064 async fn get_user_service(
1065 &self,
1066 description: &ApplicationDescription,
1067 ) -> Result<UserServiceCode, ExecutionError> {
1068 let application_id = description.into();
1069 Ok(self
1070 .user_services()
1071 .get(&application_id)
1072 .ok_or_else(|| {
1073 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1074 })?
1075 .clone())
1076 }
1077
1078 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError> {
1079 match self.blobs.get(&blob_id) {
1080 None => Ok(None),
1081 Some(blob) => Ok(Some(blob.clone())),
1082 }
1083 }
1084
1085 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError> {
1086 match self.events.get(&event_id) {
1087 None => Ok(None),
1088 Some(event) => Ok(Some(event.clone())),
1089 }
1090 }
1091
1092 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1093 Ok(Some(NetworkDescription {
1094 admin_chain_id: dummy_chain_description(0).id(),
1095 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1096 genesis_timestamp: Timestamp::from(0),
1097 genesis_committee_blob_hash: CryptoHash::test_hash("genesis committee"),
1098 name: "dummy network description".to_string(),
1099 }))
1100 }
1101
1102 async fn committees_for(
1103 &self,
1104 epoch_range: RangeInclusive<Epoch>,
1105 ) -> Result<BTreeMap<Epoch, Committee>, ViewError> {
1106 let committee_blob_bytes = self
1107 .blobs
1108 .iter()
1109 .find(|item| item.key().blob_type == BlobType::Committee)
1110 .ok_or_else(|| ViewError::NotFound("committee not found".to_owned()))?
1111 .value()
1112 .bytes()
1113 .to_vec();
1114 let committee: Committee = bcs::from_bytes(&committee_blob_bytes)?;
1115 Ok((epoch_range.start().0..=epoch_range.end().0)
1119 .map(|epoch| (Epoch::from(epoch), committee.clone()))
1120 .collect())
1121 }
1122
1123 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1124 Ok(self.blobs.contains_key(&blob_id))
1125 }
1126
1127 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1128 Ok(self.events.contains_key(&event_id))
1129 }
1130
1131 #[cfg(with_testing)]
1132 async fn add_blobs(
1133 &self,
1134 blobs: impl IntoIterator<Item = Blob> + Send,
1135 ) -> Result<(), ViewError> {
1136 for blob in blobs {
1137 self.blobs.insert(blob.id(), blob);
1138 }
1139
1140 Ok(())
1141 }
1142
1143 #[cfg(with_testing)]
1144 async fn add_events(
1145 &self,
1146 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1147 ) -> Result<(), ViewError> {
1148 for (event_id, bytes) in events {
1149 self.events.insert(event_id, bytes);
1150 }
1151
1152 Ok(())
1153 }
1154}
1155
1156impl From<SystemOperation> for Operation {
1157 fn from(operation: SystemOperation) -> Self {
1158 Operation::System(Box::new(operation))
1159 }
1160}
1161
1162impl Operation {
1163 pub fn system(operation: SystemOperation) -> Self {
1164 Operation::System(Box::new(operation))
1165 }
1166
1167 #[cfg(with_testing)]
1169 pub fn user<A: Abi>(
1170 application_id: ApplicationId<A>,
1171 operation: &A::Operation,
1172 ) -> Result<Self, bcs::Error> {
1173 Self::user_without_abi(application_id.forget_abi(), operation)
1174 }
1175
1176 #[cfg(with_testing)]
1179 pub fn user_without_abi(
1180 application_id: ApplicationId,
1181 operation: &impl Serialize,
1182 ) -> Result<Self, bcs::Error> {
1183 Ok(Operation::User {
1184 application_id,
1185 bytes: bcs::to_bytes(&operation)?,
1186 })
1187 }
1188
1189 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1192 match self {
1193 Operation::System(system_operation) => Some(system_operation),
1194 Operation::User { .. } => None,
1195 }
1196 }
1197
1198 pub fn application_id(&self) -> GenericApplicationId {
1199 match self {
1200 Self::System(_) => GenericApplicationId::System,
1201 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1202 }
1203 }
1204
1205 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1207 match self.as_system_operation() {
1208 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1209 vec![BlobId::new(*blob_hash, BlobType::Data)]
1210 }
1211 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1212 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1213 }
1214 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1215 _ => vec![],
1216 }
1217 }
1218
1219 pub fn is_exempt_from_permissions(&self) -> bool {
1221 let Operation::System(system_op) = self else {
1222 return false;
1223 };
1224 matches!(
1225 **system_op,
1226 SystemOperation::ProcessNewEpoch(_)
1227 | SystemOperation::ProcessRemovedEpoch(_)
1228 | SystemOperation::UpdateStreams(_)
1229 )
1230 }
1231}
1232
1233impl From<SystemMessage> for Message {
1234 fn from(message: SystemMessage) -> Self {
1235 Message::System(message)
1236 }
1237}
1238
1239impl Message {
1240 pub fn system(message: SystemMessage) -> Self {
1241 Message::System(message)
1242 }
1243
1244 pub fn user<A, M: Serialize>(
1247 application_id: ApplicationId<A>,
1248 message: &M,
1249 ) -> Result<Self, bcs::Error> {
1250 let application_id = application_id.forget_abi();
1251 let bytes = bcs::to_bytes(&message)?;
1252 Ok(Message::User {
1253 application_id,
1254 bytes,
1255 })
1256 }
1257
1258 pub fn application_id(&self) -> GenericApplicationId {
1259 match self {
1260 Self::System(_) => GenericApplicationId::System,
1261 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1262 }
1263 }
1264}
1265
1266impl From<SystemQuery> for Query {
1267 fn from(query: SystemQuery) -> Self {
1268 Query::System(query)
1269 }
1270}
1271
1272impl Query {
1273 pub fn system(query: SystemQuery) -> Self {
1274 Query::System(query)
1275 }
1276
1277 pub fn user<A: Abi>(
1279 application_id: ApplicationId<A>,
1280 query: &A::Query,
1281 ) -> Result<Self, serde_json::Error> {
1282 Self::user_without_abi(application_id.forget_abi(), query)
1283 }
1284
1285 pub fn user_without_abi(
1288 application_id: ApplicationId,
1289 query: &impl Serialize,
1290 ) -> Result<Self, serde_json::Error> {
1291 Ok(Query::User {
1292 application_id,
1293 bytes: serde_json::to_vec(&query)?,
1294 })
1295 }
1296
1297 pub fn application_id(&self) -> GenericApplicationId {
1298 match self {
1299 Self::System(_) => GenericApplicationId::System,
1300 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1301 }
1302 }
1303}
1304
1305impl From<SystemResponse> for QueryResponse {
1306 fn from(response: SystemResponse) -> Self {
1307 QueryResponse::System(response)
1308 }
1309}
1310
1311impl From<Vec<u8>> for QueryResponse {
1312 fn from(response: Vec<u8>) -> Self {
1313 QueryResponse::User(response)
1314 }
1315}
1316
1317#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1319pub struct BlobState {
1320 pub last_used_by: Option<CryptoHash>,
1324 pub chain_id: ChainId,
1326 pub block_height: BlockHeight,
1328 pub epoch: Option<Epoch>,
1330}
1331
1332#[derive(Clone, Copy, Display)]
1334#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1335pub enum WasmRuntime {
1336 #[cfg(with_wasmer)]
1337 #[default]
1338 #[display("wasmer")]
1339 Wasmer,
1340 #[cfg(with_wasmtime)]
1341 #[cfg_attr(not(with_wasmer), default)]
1342 #[display("wasmtime")]
1343 Wasmtime,
1344}
1345
1346#[derive(Clone, Copy, Display)]
1347#[cfg_attr(with_revm, derive(Debug, Default))]
1348pub enum EvmRuntime {
1349 #[cfg(with_revm)]
1350 #[default]
1351 #[display("revm")]
1352 Revm,
1353}
1354
1355pub trait WithWasmDefault {
1357 fn with_wasm_default(self) -> Self;
1358}
1359
1360impl WithWasmDefault for Option<WasmRuntime> {
1361 fn with_wasm_default(self) -> Self {
1362 #[cfg(with_wasm_runtime)]
1363 {
1364 Some(self.unwrap_or_default())
1365 }
1366 #[cfg(not(with_wasm_runtime))]
1367 {
1368 None
1369 }
1370 }
1371}
1372
1373impl FromStr for WasmRuntime {
1374 type Err = InvalidWasmRuntime;
1375
1376 fn from_str(string: &str) -> Result<Self, Self::Err> {
1377 match string {
1378 #[cfg(with_wasmer)]
1379 "wasmer" => Ok(WasmRuntime::Wasmer),
1380 #[cfg(with_wasmtime)]
1381 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1382 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1383 }
1384 }
1385}
1386
1387#[derive(Clone, Debug, Error)]
1389#[error("{0:?} is not a valid WebAssembly runtime")]
1390pub struct InvalidWasmRuntime(String);
1391
1392doc_scalar!(Operation, "An operation to be executed in a block");
1393doc_scalar!(
1394 Message,
1395 "A message to be sent and possibly executed in the receiver's block."
1396);
1397doc_scalar!(MessageKind, "The kind of outgoing message being sent");