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, fmt, 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 DecompressionError, Epoch, NetworkDescription, SendMessageRequest, StreamUpdate, Timestamp,
40 },
41 doc_scalar, hex_debug, http,
42 identifiers::{
43 Account, AccountOwner, ApplicationId, BlobId, BlobType, ChainId, EventId,
44 GenericApplicationId, MessageId, ModuleId, StreamName,
45 },
46 ownership::ChainOwnership,
47 task,
48 vm::VmRuntime,
49};
50use linera_views::{batch::Batch, ViewError};
51use serde::{Deserialize, Serialize};
52use system::AdminOperation;
53use thiserror::Error;
54
55#[cfg(with_revm)]
56use crate::evm::EvmExecutionError;
57use crate::runtime::ContractSyncRuntime;
58#[cfg(with_testing)]
59use crate::test_utils::dummy_chain_description;
60#[cfg(all(with_testing, with_wasm_runtime))]
61pub use crate::wasm::test as wasm_test;
62#[cfg(with_wasm_runtime)]
63pub use crate::wasm::{
64 BaseRuntimeApi, ContractEntrypoints, ContractRuntimeApi, RuntimeApiData, ServiceEntrypoints,
65 ServiceRuntimeApi, WasmContractModule, WasmExecutionError, WasmServiceModule,
66};
67pub use crate::{
68 execution::{ExecutionStateView, ServiceRuntimeEndpoint},
69 execution_state_actor::ExecutionRequest,
70 policy::ResourceControlPolicy,
71 resources::{BalanceHolder, ResourceController, ResourceTracker},
72 runtime::{
73 ContractSyncRuntimeHandle, ServiceRuntimeRequest, ServiceSyncRuntime,
74 ServiceSyncRuntimeHandle,
75 },
76 system::{
77 SystemExecutionStateView, SystemMessage, SystemOperation, SystemQuery, SystemResponse,
78 },
79 transaction_tracker::{TransactionOutcome, TransactionTracker},
80};
81
82pub const LINERA_SOL: &str = include_str!("../solidity/Linera.sol");
85pub const LINERA_TYPES_SOL: &str = include_str!("../solidity/LineraTypes.sol");
86
87const MAX_STREAM_NAME_LEN: usize = 64;
89
90#[derive(Clone)]
92pub struct UserContractCode(Box<dyn UserContractModule>);
93
94#[derive(Clone)]
96pub struct UserServiceCode(Box<dyn UserServiceModule>);
97
98pub type UserContractInstance = Box<dyn UserContract>;
100
101pub type UserServiceInstance = Box<dyn UserService>;
103
104pub trait UserContractModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
106 fn instantiate(
107 &self,
108 runtime: ContractSyncRuntimeHandle,
109 ) -> Result<UserContractInstance, ExecutionError>;
110}
111
112impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
113 fn from(module: T) -> Self {
114 Self(Box::new(module))
115 }
116}
117
118dyn_clone::clone_trait_object!(UserContractModule);
119
120pub trait UserServiceModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
122 fn instantiate(
123 &self,
124 runtime: ServiceSyncRuntimeHandle,
125 ) -> Result<UserServiceInstance, ExecutionError>;
126}
127
128impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
129 fn from(module: T) -> Self {
130 Self(Box::new(module))
131 }
132}
133
134dyn_clone::clone_trait_object!(UserServiceModule);
135
136impl UserServiceCode {
137 fn instantiate(
138 &self,
139 runtime: ServiceSyncRuntimeHandle,
140 ) -> Result<UserServiceInstance, ExecutionError> {
141 self.0.instantiate(runtime)
142 }
143}
144
145impl UserContractCode {
146 fn instantiate(
147 &self,
148 runtime: ContractSyncRuntimeHandle,
149 ) -> Result<UserContractInstance, ExecutionError> {
150 self.0.instantiate(runtime)
151 }
152}
153
154#[cfg(web)]
155const _: () = {
156 impl From<UserContractCode> for JsValue {
160 fn from(code: UserContractCode) -> JsValue {
161 let module: WasmContractModule = *(code.0 as Box<dyn Any>)
162 .downcast()
163 .expect("we only support Wasm modules on the Web for now");
164 module.into()
165 }
166 }
167
168 impl From<UserServiceCode> for JsValue {
169 fn from(code: UserServiceCode) -> JsValue {
170 let module: WasmServiceModule = *(code.0 as Box<dyn Any>)
171 .downcast()
172 .expect("we only support Wasm modules on the Web for now");
173 module.into()
174 }
175 }
176
177 impl TryFrom<JsValue> for UserContractCode {
178 type Error = JsValue;
179 fn try_from(value: JsValue) -> Result<Self, JsValue> {
180 WasmContractModule::try_from(value).map(Into::into)
181 }
182 }
183
184 impl TryFrom<JsValue> for UserServiceCode {
185 type Error = JsValue;
186 fn try_from(value: JsValue) -> Result<Self, JsValue> {
187 WasmServiceModule::try_from(value).map(Into::into)
188 }
189 }
190};
191
192#[derive(Error, Debug)]
194pub enum ExecutionError {
195 #[error(transparent)]
196 ViewError(#[from] ViewError),
197 #[error(transparent)]
198 ArithmeticError(#[from] ArithmeticError),
199 #[error("User application reported an error: {0}")]
200 UserError(String),
201 #[cfg(with_wasm_runtime)]
202 #[error(transparent)]
203 WasmError(#[from] WasmExecutionError),
204 #[cfg(with_revm)]
205 #[error(transparent)]
206 EvmError(#[from] EvmExecutionError),
207 #[error(transparent)]
208 DecompressionError(#[from] DecompressionError),
209 #[error("The given promise is invalid or was polled once already")]
210 InvalidPromise,
211
212 #[error("Attempted to perform a reentrant call to application {0}")]
213 ReentrantCall(ApplicationId),
214 #[error(
215 "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
216 from `finalize`"
217 )]
218 CrossApplicationCallInFinalize {
219 caller_id: Box<ApplicationId>,
220 callee_id: Box<ApplicationId>,
221 },
222 #[error("Attempt to write to storage from a contract")]
223 ServiceWriteAttempt,
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("Module ID {0:?} is invalid")]
247 InvalidModuleId(ModuleId),
248 #[error("AccountOwner is None")]
249 OwnerIsNone,
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("Cannot decrease the chain's timestamp")]
332 TicksOutOfOrder,
333 #[error("Application {0:?} is not registered by the chain")]
334 UnknownApplicationId(Box<ApplicationId>),
335 #[error("No recorded response for oracle query")]
336 MissingOracleResponse,
337 #[error("process_streams was not called for all stream updates")]
338 UnprocessedStreams,
339 #[error("Internal error: {0}")]
340 InternalError(&'static str),
341 #[error("UpdateStreams contains an unknown event")]
342 EventNotFound(EventId),
343 #[error("UpdateStreams is outdated")]
344 OutdatedUpdateStreams,
345}
346
347pub trait UserContract {
349 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
351
352 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
354
355 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
357
358 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
360
361 fn finalize(&mut self) -> Result<(), ExecutionError>;
363}
364
365pub trait UserService {
367 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
369}
370
371#[derive(Clone, Copy, Default)]
373pub struct ExecutionRuntimeConfig {}
374
375#[cfg_attr(not(web), async_trait)]
378#[cfg_attr(web, async_trait(?Send))]
379pub trait ExecutionRuntimeContext {
380 fn chain_id(&self) -> ChainId;
381
382 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
383
384 fn user_contracts(&self) -> &Arc<DashMap<ApplicationId, UserContractCode>>;
385
386 fn user_services(&self) -> &Arc<DashMap<ApplicationId, UserServiceCode>>;
387
388 async fn get_user_contract(
389 &self,
390 description: &ApplicationDescription,
391 ) -> Result<UserContractCode, ExecutionError>;
392
393 async fn get_user_service(
394 &self,
395 description: &ApplicationDescription,
396 ) -> Result<UserServiceCode, ExecutionError>;
397
398 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError>;
399
400 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError>;
401
402 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
403
404 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
405
406 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
407
408 #[cfg(with_testing)]
409 async fn add_blobs(
410 &self,
411 blobs: impl IntoIterator<Item = Blob> + Send,
412 ) -> Result<(), ViewError>;
413
414 #[cfg(with_testing)]
415 async fn add_events(
416 &self,
417 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
418 ) -> Result<(), ViewError>;
419}
420
421#[derive(Clone, Copy, Debug)]
422pub struct OperationContext {
423 pub chain_id: ChainId,
425 #[debug(skip_if = Option::is_none)]
427 pub authenticated_signer: Option<AccountOwner>,
428 #[debug(skip_if = Option::is_none)]
431 pub authenticated_caller_id: Option<ApplicationId>,
432 pub height: BlockHeight,
434 pub round: Option<u32>,
436 pub timestamp: Timestamp,
438}
439
440#[derive(Clone, Copy, Debug)]
441pub struct MessageContext {
442 pub chain_id: ChainId,
444 pub is_bouncing: bool,
446 #[debug(skip_if = Option::is_none)]
448 pub authenticated_signer: Option<AccountOwner>,
449 #[debug(skip_if = Option::is_none)]
451 pub refund_grant_to: Option<Account>,
452 pub height: BlockHeight,
454 pub round: Option<u32>,
456 pub timestamp: Timestamp,
458 pub message_id: MessageId,
461}
462
463#[derive(Clone, Copy, Debug)]
464pub struct ProcessStreamsContext {
465 pub chain_id: ChainId,
467 pub height: BlockHeight,
469 pub round: Option<u32>,
471 pub timestamp: Timestamp,
473}
474
475impl From<MessageContext> for ProcessStreamsContext {
476 fn from(context: MessageContext) -> Self {
477 Self {
478 chain_id: context.chain_id,
479 height: context.height,
480 round: context.round,
481 timestamp: context.timestamp,
482 }
483 }
484}
485
486impl From<OperationContext> for ProcessStreamsContext {
487 fn from(context: OperationContext) -> Self {
488 Self {
489 chain_id: context.chain_id,
490 height: context.height,
491 round: context.round,
492 timestamp: context.timestamp,
493 }
494 }
495}
496
497#[derive(Clone, Copy, Debug)]
498pub struct FinalizeContext {
499 pub chain_id: ChainId,
501 #[debug(skip_if = Option::is_none)]
503 pub authenticated_signer: Option<AccountOwner>,
504 pub height: BlockHeight,
506 pub round: Option<u32>,
508}
509
510#[derive(Clone, Copy, Debug, Eq, PartialEq)]
511pub struct QueryContext {
512 pub chain_id: ChainId,
514 pub next_block_height: BlockHeight,
516 pub local_time: Timestamp,
518}
519
520pub trait BaseRuntime {
521 type Read: fmt::Debug + Send + Sync;
522 type ContainsKey: fmt::Debug + Send + Sync;
523 type ContainsKeys: fmt::Debug + Send + Sync;
524 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
525 type ReadValueBytes: fmt::Debug + Send + Sync;
526 type FindKeysByPrefix: fmt::Debug + Send + Sync;
527 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
528
529 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
531
532 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
534
535 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
537
538 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
540
541 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
543
544 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
546
547 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
549
550 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
552
553 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
555
556 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
558
559 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
561
562 #[cfg(feature = "test")]
564 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
565 let promise = self.contains_key_new(key)?;
566 self.contains_key_wait(&promise)
567 }
568
569 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
571
572 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
574
575 #[cfg(feature = "test")]
577 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
578 let promise = self.contains_keys_new(keys)?;
579 self.contains_keys_wait(&promise)
580 }
581
582 fn contains_keys_new(
584 &mut self,
585 keys: Vec<Vec<u8>>,
586 ) -> Result<Self::ContainsKeys, ExecutionError>;
587
588 fn contains_keys_wait(
590 &mut self,
591 promise: &Self::ContainsKeys,
592 ) -> Result<Vec<bool>, ExecutionError>;
593
594 #[cfg(feature = "test")]
596 fn read_multi_values_bytes(
597 &mut self,
598 keys: Vec<Vec<u8>>,
599 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
600 let promise = self.read_multi_values_bytes_new(keys)?;
601 self.read_multi_values_bytes_wait(&promise)
602 }
603
604 fn read_multi_values_bytes_new(
606 &mut self,
607 keys: Vec<Vec<u8>>,
608 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
609
610 fn read_multi_values_bytes_wait(
612 &mut self,
613 promise: &Self::ReadMultiValuesBytes,
614 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
615
616 #[cfg(feature = "test")]
618 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
619 let promise = self.read_value_bytes_new(key)?;
620 self.read_value_bytes_wait(&promise)
621 }
622
623 fn read_value_bytes_new(
625 &mut self,
626 key: Vec<u8>,
627 ) -> Result<Self::ReadValueBytes, ExecutionError>;
628
629 fn read_value_bytes_wait(
631 &mut self,
632 promise: &Self::ReadValueBytes,
633 ) -> Result<Option<Vec<u8>>, ExecutionError>;
634
635 fn find_keys_by_prefix_new(
637 &mut self,
638 key_prefix: Vec<u8>,
639 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
640
641 fn find_keys_by_prefix_wait(
643 &mut self,
644 promise: &Self::FindKeysByPrefix,
645 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
646
647 #[cfg(feature = "test")]
649 #[expect(clippy::type_complexity)]
650 fn find_key_values_by_prefix(
651 &mut self,
652 key_prefix: Vec<u8>,
653 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
654 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
655 self.find_key_values_by_prefix_wait(&promise)
656 }
657
658 fn find_key_values_by_prefix_new(
660 &mut self,
661 key_prefix: Vec<u8>,
662 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
663
664 #[expect(clippy::type_complexity)]
666 fn find_key_values_by_prefix_wait(
667 &mut self,
668 promise: &Self::FindKeyValuesByPrefix,
669 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
670
671 fn perform_http_request(
673 &mut self,
674 request: http::Request,
675 ) -> Result<http::Response, ExecutionError>;
676
677 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
683
684 fn read_data_blob(&mut self, hash: &CryptoHash) -> Result<Vec<u8>, ExecutionError>;
686
687 fn assert_data_blob_exists(&mut self, hash: &CryptoHash) -> Result<(), ExecutionError>;
689}
690
691pub trait ServiceRuntime: BaseRuntime {
692 fn try_query_application(
694 &mut self,
695 queried_id: ApplicationId,
696 argument: Vec<u8>,
697 ) -> Result<Vec<u8>, ExecutionError>;
698
699 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
701
702 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
704}
705
706pub trait ContractRuntime: BaseRuntime {
707 fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
709
710 fn message_id(&mut self) -> Result<Option<MessageId>, ExecutionError>;
712
713 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
716
717 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
720
721 fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
723
724 fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
726
727 fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
729
730 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
732
733 fn transfer(
735 &mut self,
736 source: AccountOwner,
737 destination: Account,
738 amount: Amount,
739 ) -> Result<(), ExecutionError>;
740
741 fn claim(
743 &mut self,
744 source: Account,
745 destination: Account,
746 amount: Amount,
747 ) -> Result<(), ExecutionError>;
748
749 fn try_call_application(
752 &mut self,
753 authenticated: bool,
754 callee_id: ApplicationId,
755 argument: Vec<u8>,
756 ) -> Result<Vec<u8>, ExecutionError>;
757
758 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
760
761 fn read_event(
765 &mut self,
766 chain_id: ChainId,
767 stream_name: StreamName,
768 index: u32,
769 ) -> Result<Vec<u8>, ExecutionError>;
770
771 fn subscribe_to_events(
773 &mut self,
774 chain_id: ChainId,
775 application_id: ApplicationId,
776 stream_name: StreamName,
777 ) -> Result<(), ExecutionError>;
778
779 fn unsubscribe_from_events(
781 &mut self,
782 chain_id: ChainId,
783 application_id: ApplicationId,
784 stream_name: StreamName,
785 ) -> Result<(), ExecutionError>;
786
787 fn query_service(
789 &mut self,
790 application_id: ApplicationId,
791 query: Vec<u8>,
792 ) -> Result<Vec<u8>, ExecutionError>;
793
794 fn open_chain(
796 &mut self,
797 ownership: ChainOwnership,
798 application_permissions: ApplicationPermissions,
799 balance: Amount,
800 ) -> Result<ChainId, ExecutionError>;
801
802 fn close_chain(&mut self) -> Result<(), ExecutionError>;
804
805 fn change_application_permissions(
807 &mut self,
808 application_permissions: ApplicationPermissions,
809 ) -> Result<(), ExecutionError>;
810
811 fn create_application(
813 &mut self,
814 module_id: ModuleId,
815 parameters: Vec<u8>,
816 argument: Vec<u8>,
817 required_application_ids: Vec<ApplicationId>,
818 ) -> Result<ApplicationId, ExecutionError>;
819
820 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
822
823 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
825}
826
827#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
829pub enum Operation {
830 System(Box<SystemOperation>),
832 User {
834 application_id: ApplicationId,
835 #[serde(with = "serde_bytes")]
836 #[debug(with = "hex_debug")]
837 bytes: Vec<u8>,
838 },
839}
840
841impl BcsHashable<'_> for Operation {}
842
843#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
845pub enum Message {
846 System(SystemMessage),
848 User {
850 application_id: ApplicationId,
851 #[serde(with = "serde_bytes")]
852 #[debug(with = "hex_debug")]
853 bytes: Vec<u8>,
854 },
855}
856
857#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
859pub enum Query {
860 System(SystemQuery),
862 User {
864 application_id: ApplicationId,
865 #[serde(with = "serde_bytes")]
866 #[debug(with = "hex_debug")]
867 bytes: Vec<u8>,
868 },
869}
870
871#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
873pub struct QueryOutcome<Response = QueryResponse> {
874 pub response: Response,
875 pub operations: Vec<Operation>,
876}
877
878impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
879 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
880 let QueryOutcome {
881 response,
882 operations,
883 } = system_outcome;
884
885 QueryOutcome {
886 response: QueryResponse::System(response),
887 operations,
888 }
889 }
890}
891
892impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
893 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
894 let QueryOutcome {
895 response,
896 operations,
897 } = user_service_outcome;
898
899 QueryOutcome {
900 response: QueryResponse::User(response),
901 operations,
902 }
903 }
904}
905
906#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
908pub enum QueryResponse {
909 System(SystemResponse),
911 User(
913 #[serde(with = "serde_bytes")]
914 #[debug(with = "hex_debug")]
915 Vec<u8>,
916 ),
917}
918
919#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy)]
921pub enum MessageKind {
922 Simple,
924 Protected,
927 Tracked,
930 Bouncing,
932}
933
934#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
936pub struct OutgoingMessage {
937 pub destination: ChainId,
939 #[debug(skip_if = Option::is_none)]
941 pub authenticated_signer: Option<AccountOwner>,
942 #[debug(skip_if = Amount::is_zero)]
944 pub grant: Amount,
945 #[debug(skip_if = Option::is_none)]
947 pub refund_grant_to: Option<Account>,
948 pub kind: MessageKind,
950 pub message: Message,
952}
953
954impl BcsHashable<'_> for OutgoingMessage {}
955
956impl OutgoingMessage {
957 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
959 OutgoingMessage {
960 destination: recipient,
961 authenticated_signer: None,
962 grant: Amount::ZERO,
963 refund_grant_to: None,
964 kind: MessageKind::Simple,
965 message: message.into(),
966 }
967 }
968
969 pub fn with_kind(mut self, kind: MessageKind) -> Self {
971 self.kind = kind;
972 self
973 }
974
975 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
977 self.authenticated_signer = authenticated_signer;
978 self
979 }
980}
981
982impl OperationContext {
983 fn refund_grant_to(&self) -> Option<Account> {
986 self.authenticated_signer.map(|owner| Account {
987 chain_id: self.chain_id,
988 owner,
989 })
990 }
991}
992
993#[cfg(with_testing)]
994#[derive(Clone)]
995pub struct TestExecutionRuntimeContext {
996 chain_id: ChainId,
997 execution_runtime_config: ExecutionRuntimeConfig,
998 user_contracts: Arc<DashMap<ApplicationId, UserContractCode>>,
999 user_services: Arc<DashMap<ApplicationId, UserServiceCode>>,
1000 blobs: Arc<DashMap<BlobId, Blob>>,
1001 events: Arc<DashMap<EventId, Vec<u8>>>,
1002}
1003
1004#[cfg(with_testing)]
1005impl TestExecutionRuntimeContext {
1006 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1007 Self {
1008 chain_id,
1009 execution_runtime_config,
1010 user_contracts: Arc::default(),
1011 user_services: Arc::default(),
1012 blobs: Arc::default(),
1013 events: Arc::default(),
1014 }
1015 }
1016}
1017
1018#[cfg(with_testing)]
1019#[cfg_attr(not(web), async_trait)]
1020#[cfg_attr(web, async_trait(?Send))]
1021impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1022 fn chain_id(&self) -> ChainId {
1023 self.chain_id
1024 }
1025
1026 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1027 self.execution_runtime_config
1028 }
1029
1030 fn user_contracts(&self) -> &Arc<DashMap<ApplicationId, UserContractCode>> {
1031 &self.user_contracts
1032 }
1033
1034 fn user_services(&self) -> &Arc<DashMap<ApplicationId, UserServiceCode>> {
1035 &self.user_services
1036 }
1037
1038 async fn get_user_contract(
1039 &self,
1040 description: &ApplicationDescription,
1041 ) -> Result<UserContractCode, ExecutionError> {
1042 let application_id = description.into();
1043 Ok(self
1044 .user_contracts()
1045 .get(&application_id)
1046 .ok_or_else(|| {
1047 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1048 })?
1049 .clone())
1050 }
1051
1052 async fn get_user_service(
1053 &self,
1054 description: &ApplicationDescription,
1055 ) -> Result<UserServiceCode, ExecutionError> {
1056 let application_id = description.into();
1057 Ok(self
1058 .user_services()
1059 .get(&application_id)
1060 .ok_or_else(|| {
1061 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1062 })?
1063 .clone())
1064 }
1065
1066 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError> {
1067 match self.blobs.get(&blob_id) {
1068 None => Ok(None),
1069 Some(blob) => Ok(Some(blob.clone())),
1070 }
1071 }
1072
1073 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError> {
1074 match self.events.get(&event_id) {
1075 None => Ok(None),
1076 Some(event) => Ok(Some(event.clone())),
1077 }
1078 }
1079
1080 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1081 Ok(Some(NetworkDescription {
1082 admin_chain_id: dummy_chain_description(0).id(),
1083 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1084 genesis_timestamp: Timestamp::from(0),
1085 name: "dummy network description".to_string(),
1086 }))
1087 }
1088
1089 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1090 Ok(self.blobs.contains_key(&blob_id))
1091 }
1092
1093 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1094 Ok(self.events.contains_key(&event_id))
1095 }
1096
1097 #[cfg(with_testing)]
1098 async fn add_blobs(
1099 &self,
1100 blobs: impl IntoIterator<Item = Blob> + Send,
1101 ) -> Result<(), ViewError> {
1102 for blob in blobs {
1103 self.blobs.insert(blob.id(), blob);
1104 }
1105
1106 Ok(())
1107 }
1108
1109 #[cfg(with_testing)]
1110 async fn add_events(
1111 &self,
1112 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1113 ) -> Result<(), ViewError> {
1114 for (event_id, bytes) in events {
1115 self.events.insert(event_id, bytes);
1116 }
1117
1118 Ok(())
1119 }
1120}
1121
1122impl From<SystemOperation> for Operation {
1123 fn from(operation: SystemOperation) -> Self {
1124 Operation::System(Box::new(operation))
1125 }
1126}
1127
1128impl Operation {
1129 pub fn system(operation: SystemOperation) -> Self {
1130 Operation::System(Box::new(operation))
1131 }
1132
1133 #[cfg(with_testing)]
1135 pub fn user<A: Abi>(
1136 application_id: ApplicationId<A>,
1137 operation: &A::Operation,
1138 ) -> Result<Self, bcs::Error> {
1139 Self::user_without_abi(application_id.forget_abi(), operation)
1140 }
1141
1142 #[cfg(with_testing)]
1145 pub fn user_without_abi(
1146 application_id: ApplicationId,
1147 operation: &impl Serialize,
1148 ) -> Result<Self, bcs::Error> {
1149 Ok(Operation::User {
1150 application_id,
1151 bytes: bcs::to_bytes(&operation)?,
1152 })
1153 }
1154
1155 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1158 match self {
1159 Operation::System(system_operation) => Some(system_operation),
1160 Operation::User { .. } => None,
1161 }
1162 }
1163
1164 pub fn application_id(&self) -> GenericApplicationId {
1165 match self {
1166 Self::System(_) => GenericApplicationId::System,
1167 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1168 }
1169 }
1170
1171 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1173 match self.as_system_operation() {
1174 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1175 vec![BlobId::new(*blob_hash, BlobType::Data)]
1176 }
1177 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1178 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1179 }
1180 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1181 _ => vec![],
1182 }
1183 }
1184
1185 pub fn is_exempt_from_permissions(&self) -> bool {
1187 let Operation::System(system_op) = self else {
1188 return false;
1189 };
1190 matches!(
1191 **system_op,
1192 SystemOperation::ProcessNewEpoch(_)
1193 | SystemOperation::ProcessRemovedEpoch(_)
1194 | SystemOperation::UpdateStreams(_)
1195 )
1196 }
1197}
1198
1199impl From<SystemMessage> for Message {
1200 fn from(message: SystemMessage) -> Self {
1201 Message::System(message)
1202 }
1203}
1204
1205impl Message {
1206 pub fn system(message: SystemMessage) -> Self {
1207 Message::System(message)
1208 }
1209
1210 pub fn user<A, M: Serialize>(
1213 application_id: ApplicationId<A>,
1214 message: &M,
1215 ) -> Result<Self, bcs::Error> {
1216 let application_id = application_id.forget_abi();
1217 let bytes = bcs::to_bytes(&message)?;
1218 Ok(Message::User {
1219 application_id,
1220 bytes,
1221 })
1222 }
1223
1224 pub fn application_id(&self) -> GenericApplicationId {
1225 match self {
1226 Self::System(_) => GenericApplicationId::System,
1227 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1228 }
1229 }
1230}
1231
1232impl From<SystemQuery> for Query {
1233 fn from(query: SystemQuery) -> Self {
1234 Query::System(query)
1235 }
1236}
1237
1238impl Query {
1239 pub fn system(query: SystemQuery) -> Self {
1240 Query::System(query)
1241 }
1242
1243 pub fn user<A: Abi>(
1245 application_id: ApplicationId<A>,
1246 query: &A::Query,
1247 ) -> Result<Self, serde_json::Error> {
1248 Self::user_without_abi(application_id.forget_abi(), query)
1249 }
1250
1251 pub fn user_without_abi(
1254 application_id: ApplicationId,
1255 query: &impl Serialize,
1256 ) -> Result<Self, serde_json::Error> {
1257 Ok(Query::User {
1258 application_id,
1259 bytes: serde_json::to_vec(&query)?,
1260 })
1261 }
1262
1263 pub fn application_id(&self) -> GenericApplicationId {
1264 match self {
1265 Self::System(_) => GenericApplicationId::System,
1266 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1267 }
1268 }
1269}
1270
1271impl From<SystemResponse> for QueryResponse {
1272 fn from(response: SystemResponse) -> Self {
1273 QueryResponse::System(response)
1274 }
1275}
1276
1277impl From<Vec<u8>> for QueryResponse {
1278 fn from(response: Vec<u8>) -> Self {
1279 QueryResponse::User(response)
1280 }
1281}
1282
1283#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1285pub struct BlobState {
1286 pub last_used_by: Option<CryptoHash>,
1290 pub chain_id: ChainId,
1292 pub block_height: BlockHeight,
1294 pub epoch: Option<Epoch>,
1296}
1297
1298#[derive(Clone, Copy, Display)]
1300#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1301pub enum WasmRuntime {
1302 #[cfg(with_wasmer)]
1303 #[default]
1304 #[display("wasmer")]
1305 Wasmer,
1306 #[cfg(with_wasmtime)]
1307 #[cfg_attr(not(with_wasmer), default)]
1308 #[display("wasmtime")]
1309 Wasmtime,
1310}
1311
1312#[derive(Clone, Copy, Display)]
1313#[cfg_attr(with_revm, derive(Debug, Default))]
1314pub enum EvmRuntime {
1315 #[cfg(with_revm)]
1316 #[default]
1317 #[display("revm")]
1318 Revm,
1319}
1320
1321pub trait WithWasmDefault {
1323 fn with_wasm_default(self) -> Self;
1324}
1325
1326impl WithWasmDefault for Option<WasmRuntime> {
1327 fn with_wasm_default(self) -> Self {
1328 #[cfg(with_wasm_runtime)]
1329 {
1330 Some(self.unwrap_or_default())
1331 }
1332 #[cfg(not(with_wasm_runtime))]
1333 {
1334 None
1335 }
1336 }
1337}
1338
1339impl FromStr for WasmRuntime {
1340 type Err = InvalidWasmRuntime;
1341
1342 fn from_str(string: &str) -> Result<Self, Self::Err> {
1343 match string {
1344 #[cfg(with_wasmer)]
1345 "wasmer" => Ok(WasmRuntime::Wasmer),
1346 #[cfg(with_wasmtime)]
1347 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1348 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1349 }
1350 }
1351}
1352
1353#[derive(Clone, Debug, Error)]
1355#[error("{0:?} is not a valid WebAssembly runtime")]
1356pub struct InvalidWasmRuntime(String);
1357
1358doc_scalar!(Operation, "An operation to be executed in a block");
1359doc_scalar!(
1360 Message,
1361 "A message to be sent and possibly executed in the receiver's block."
1362);
1363doc_scalar!(MessageKind, "The kind of outgoing message being sent");