1pub mod committee;
8pub mod evm;
9mod execution;
10pub mod execution_state_actor;
11#[cfg(with_graphql)]
12mod graphql;
13mod policy;
14mod resources;
15mod runtime;
16pub mod system;
17#[cfg(with_testing)]
18pub mod test_utils;
19mod transaction_tracker;
20mod util;
21mod wasm;
22
23use std::{any::Any, collections::BTreeMap, fmt, ops::RangeInclusive, str::FromStr, sync::Arc};
24
25use allocative::Allocative;
26use async_graphql::SimpleObject;
27use async_trait::async_trait;
28use custom_debug_derive::Debug;
29use derive_more::Display;
30#[cfg(web)]
31use js_sys::wasm_bindgen::JsValue;
32use linera_base::{
33 abi::Abi,
34 crypto::{BcsHashable, CryptoHash},
35 data_types::{
36 Amount, ApplicationDescription, ApplicationPermissions, ArithmeticError, Blob, BlockHeight,
37 Bytecode, DecompressionError, Epoch, NetworkDescription, SendMessageRequest, StreamUpdate,
38 Timestamp,
39 },
40 doc_scalar, ensure, hex_debug, http,
41 identifiers::{
42 Account, AccountOwner, ApplicationId, BlobId, BlobType, ChainId, DataBlobHash, EventId,
43 GenericApplicationId, ModuleId, StreamId, StreamName,
44 },
45 ownership::ChainOwnership,
46 vm::VmRuntime,
47};
48use linera_views::{batch::Batch, ViewError};
49use serde::{Deserialize, Serialize};
50use system::AdminOperation;
51use thiserror::Error;
52
53#[cfg(with_revm)]
54use crate::evm::EvmExecutionError;
55use crate::system::EPOCH_STREAM_NAME;
56#[cfg(with_testing)]
57use crate::test_utils::dummy_chain_description;
58#[cfg(all(with_testing, with_wasm_runtime))]
59pub use crate::wasm::test as wasm_test;
60#[cfg(with_wasm_runtime)]
61pub use crate::wasm::{
62 BaseRuntimeApi, ContractEntrypoints, ContractRuntimeApi, RuntimeApiData, ServiceEntrypoints,
63 ServiceRuntimeApi, WasmContractModule, WasmExecutionError, WasmServiceModule,
64};
65pub use crate::{
66 committee::Committee,
67 execution::{ExecutionStateView, ServiceRuntimeEndpoint},
68 execution_state_actor::{ExecutionRequest, ExecutionStateActor},
69 policy::ResourceControlPolicy,
70 resources::{BalanceHolder, ResourceController, ResourceTracker},
71 runtime::{
72 ContractSyncRuntimeHandle, ServiceRuntimeRequest, ServiceSyncRuntime,
73 ServiceSyncRuntimeHandle,
74 },
75 system::{
76 SystemExecutionStateView, SystemMessage, SystemOperation, SystemQuery, SystemResponse,
77 },
78 transaction_tracker::{TransactionOutcome, TransactionTracker},
79};
80
81pub const LINERA_SOL: &str = include_str!("../solidity/Linera.sol");
84pub const LINERA_TYPES_SOL: &str = include_str!("../solidity/LineraTypes.sol");
85
86const MAX_STREAM_NAME_LEN: usize = 64;
88
89pub const FLAG_ZERO_HASH: &str = "FLAG_ZERO_HASH.linera.network";
94pub const FLAG_FREE_REJECT: &str = "FLAG_FREE_REJECT.linera.network";
98
99#[derive(Clone)]
101pub struct UserContractCode(Box<dyn UserContractModule>);
102
103#[derive(Clone)]
105pub struct UserServiceCode(Box<dyn UserServiceModule>);
106
107pub type UserContractInstance = Box<dyn UserContract>;
109
110pub type UserServiceInstance = Box<dyn UserService>;
112
113pub trait UserContractModule: dyn_clone::DynClone + Any + web_thread::Post + Send + Sync {
115 fn instantiate(
116 &self,
117 runtime: ContractSyncRuntimeHandle,
118 ) -> Result<UserContractInstance, ExecutionError>;
119}
120
121impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
122 fn from(module: T) -> Self {
123 Self(Box::new(module))
124 }
125}
126
127dyn_clone::clone_trait_object!(UserContractModule);
128
129pub trait UserServiceModule: dyn_clone::DynClone + Any + web_thread::Post + Send + Sync {
131 fn instantiate(
132 &self,
133 runtime: ServiceSyncRuntimeHandle,
134 ) -> Result<UserServiceInstance, ExecutionError>;
135}
136
137impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
138 fn from(module: T) -> Self {
139 Self(Box::new(module))
140 }
141}
142
143dyn_clone::clone_trait_object!(UserServiceModule);
144
145impl UserServiceCode {
146 fn instantiate(
147 &self,
148 runtime: ServiceSyncRuntimeHandle,
149 ) -> Result<UserServiceInstance, ExecutionError> {
150 self.0.instantiate(runtime)
151 }
152}
153
154impl UserContractCode {
155 fn instantiate(
156 &self,
157 runtime: ContractSyncRuntimeHandle,
158 ) -> Result<UserContractInstance, ExecutionError> {
159 self.0.instantiate(runtime)
160 }
161}
162
163pub struct JsVec<T>(pub Vec<T>);
164
165#[cfg(web)]
166const _: () = {
167 impl web_thread::AsJs for UserContractCode {
171 fn to_js(&self) -> Result<JsValue, JsValue> {
172 ((&*self.0) as &dyn Any)
173 .downcast_ref::<WasmContractModule>()
174 .expect("we only support Wasm modules on the Web for now")
175 .to_js()
176 }
177
178 fn from_js(value: JsValue) -> Result<Self, JsValue> {
179 WasmContractModule::from_js(value).map(Into::into)
180 }
181 }
182
183 impl web_thread::Post for UserContractCode {
184 fn transferables(&self) -> js_sys::Array {
185 self.0.transferables()
186 }
187 }
188
189 impl web_thread::AsJs for UserServiceCode {
190 fn to_js(&self) -> Result<JsValue, JsValue> {
191 ((&*self.0) as &dyn Any)
192 .downcast_ref::<WasmServiceModule>()
193 .expect("we only support Wasm modules on the Web for now")
194 .to_js()
195 }
196
197 fn from_js(value: JsValue) -> Result<Self, JsValue> {
198 WasmServiceModule::from_js(value).map(Into::into)
199 }
200 }
201
202 impl web_thread::Post for UserServiceCode {
203 fn transferables(&self) -> js_sys::Array {
204 self.0.transferables()
205 }
206 }
207
208 impl<T: web_thread::AsJs> web_thread::AsJs for JsVec<T> {
209 fn to_js(&self) -> Result<JsValue, JsValue> {
210 let array = self
211 .0
212 .iter()
213 .map(T::to_js)
214 .collect::<Result<js_sys::Array, _>>()?;
215 Ok(array.into())
216 }
217
218 fn from_js(value: JsValue) -> Result<Self, JsValue> {
219 let array = js_sys::Array::from(&value);
220 let v = array
221 .into_iter()
222 .map(T::from_js)
223 .collect::<Result<Vec<_>, _>>()?;
224 Ok(JsVec(v))
225 }
226 }
227
228 impl<T: web_thread::Post> web_thread::Post for JsVec<T> {
229 fn transferables(&self) -> js_sys::Array {
230 let mut array = js_sys::Array::new();
231 for x in &self.0 {
232 array = array.concat(&x.transferables());
233 }
234 array
235 }
236 }
237};
238
239#[derive(Error, Debug)]
241pub enum ExecutionError {
242 #[error(transparent)]
243 ViewError(#[from] ViewError),
244 #[error(transparent)]
245 ArithmeticError(#[from] ArithmeticError),
246 #[error("User application reported an error: {0}")]
247 UserError(String),
248 #[cfg(with_wasm_runtime)]
249 #[error(transparent)]
250 WasmError(#[from] WasmExecutionError),
251 #[cfg(with_revm)]
252 #[error(transparent)]
253 EvmError(#[from] EvmExecutionError),
254 #[error(transparent)]
255 DecompressionError(#[from] DecompressionError),
256 #[error("The given promise is invalid or was polled once already")]
257 InvalidPromise,
258
259 #[error("Attempted to perform a reentrant call to application {0}")]
260 ReentrantCall(ApplicationId),
261 #[error(
262 "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
263 from `finalize`"
264 )]
265 CrossApplicationCallInFinalize {
266 caller_id: Box<ApplicationId>,
267 callee_id: Box<ApplicationId>,
268 },
269 #[error("Failed to load bytecode from storage {0:?}")]
270 ApplicationBytecodeNotFound(Box<ApplicationDescription>),
271 #[error("Unsupported dynamic application load: {0:?}")]
273 UnsupportedDynamicApplicationLoad(Box<ApplicationId>),
274
275 #[error("Excessive number of bytes read from storage")]
276 ExcessiveRead,
277 #[error("Excessive number of bytes written to storage")]
278 ExcessiveWrite,
279 #[error("Block execution required too much fuel for VM {0}")]
280 MaximumFuelExceeded(VmRuntime),
281 #[error("Services running as oracles in block took longer than allowed")]
282 MaximumServiceOracleExecutionTimeExceeded,
283 #[error("Service running as an oracle produced a response that's too large")]
284 ServiceOracleResponseTooLarge,
285 #[error("Serialized size of the block exceeds limit")]
286 BlockTooLarge,
287 #[error("HTTP response exceeds the size limit of {limit} bytes, having at least {size} bytes")]
288 HttpResponseSizeLimitExceeded { limit: u64, size: u64 },
289 #[error("Runtime failed to respond to application")]
290 MissingRuntimeResponse,
291 #[error("Application is not authorized to perform system operations on this chain: {0:}")]
292 UnauthorizedApplication(ApplicationId),
293 #[error("Failed to make network reqwest: {0}")]
294 ReqwestError(#[from] reqwest::Error),
295 #[error("Encountered I/O error: {0}")]
296 IoError(#[from] std::io::Error),
297 #[error("More recorded oracle responses than expected")]
298 UnexpectedOracleResponse,
299 #[error("Invalid JSON: {0}")]
300 JsonError(#[from] serde_json::Error),
301 #[error(transparent)]
302 BcsError(#[from] bcs::Error),
303 #[error("Recorded response for oracle query has the wrong type")]
304 OracleResponseMismatch,
305 #[error("Service oracle query tried to create operations: {0:?}")]
306 ServiceOracleQueryOperations(Vec<Operation>),
307 #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
308 AssertBefore {
309 timestamp: Timestamp,
310 local_time: Timestamp,
311 },
312
313 #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
314 StreamNameTooLong,
315 #[error("Blob exceeds size limit")]
316 BlobTooLarge,
317 #[error("Bytecode exceeds size limit")]
318 BytecodeTooLarge,
319 #[error("Attempt to perform an HTTP request to an unauthorized host: {0:?}")]
320 UnauthorizedHttpRequest(reqwest::Url),
321 #[error("Attempt to perform an HTTP request to an invalid URL")]
322 InvalidUrlForHttpRequest(#[from] url::ParseError),
323 #[error("Worker thread failure: {0:?}")]
324 Thread(#[from] web_thread::Error),
325 #[error("The chain being queried is not active {0}")]
326 InactiveChain(ChainId),
327 #[error("Blobs not found: {0:?}")]
328 BlobsNotFound(Vec<BlobId>),
329 #[error("Events not found: {0:?}")]
330 EventsNotFound(Vec<EventId>),
331
332 #[error("Invalid HTTP header name used for HTTP request")]
333 InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
334 #[error("Invalid HTTP header value used for HTTP request")]
335 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
336
337 #[error("No NetworkDescription found in storage")]
338 NoNetworkDescriptionFound,
339 #[error("{epoch:?} is not recognized by chain {chain_id:}")]
340 InvalidEpoch { chain_id: ChainId, epoch: Epoch },
341 #[error("Transfer must have positive amount")]
342 IncorrectTransferAmount,
343 #[error("Transfer from owned account must be authenticated by the right signer")]
344 UnauthenticatedTransferOwner,
345 #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
346 InsufficientBalance {
347 balance: Amount,
348 account: AccountOwner,
349 },
350 #[error("Required execution fees exceeded the total funding available. Fees {fees}, available balance: {balance}")]
351 FeesExceedFunding { fees: Amount, balance: Amount },
352 #[error("Claim must have positive amount")]
353 IncorrectClaimAmount,
354 #[error("Claim must be authenticated by the right signer")]
355 UnauthenticatedClaimOwner,
356 #[error("Admin operations are only allowed on the admin chain.")]
357 AdminOperationOnNonAdminChain,
358 #[error("Failed to create new committee: expected {expected}, but got {provided}")]
359 InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
360 #[error("Failed to remove committee")]
361 InvalidCommitteeRemoval,
362 #[error("No recorded response for oracle query")]
363 MissingOracleResponse,
364 #[error("process_streams was not called for all stream updates")]
365 UnprocessedStreams,
366 #[error("Internal error: {0}")]
367 InternalError(&'static str),
368 #[error("UpdateStreams is outdated")]
369 OutdatedUpdateStreams,
370}
371
372impl ExecutionError {
373 pub fn is_local(&self) -> bool {
377 match self {
378 ExecutionError::ArithmeticError(_)
379 | ExecutionError::UserError(_)
380 | ExecutionError::DecompressionError(_)
381 | ExecutionError::InvalidPromise
382 | ExecutionError::CrossApplicationCallInFinalize { .. }
383 | ExecutionError::ReentrantCall(_)
384 | ExecutionError::ApplicationBytecodeNotFound(_)
385 | ExecutionError::UnsupportedDynamicApplicationLoad(_)
386 | ExecutionError::ExcessiveRead
387 | ExecutionError::ExcessiveWrite
388 | ExecutionError::MaximumFuelExceeded(_)
389 | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
390 | ExecutionError::ServiceOracleResponseTooLarge
391 | ExecutionError::BlockTooLarge
392 | ExecutionError::HttpResponseSizeLimitExceeded { .. }
393 | ExecutionError::UnauthorizedApplication(_)
394 | ExecutionError::UnexpectedOracleResponse
395 | ExecutionError::JsonError(_)
396 | ExecutionError::BcsError(_)
397 | ExecutionError::OracleResponseMismatch
398 | ExecutionError::ServiceOracleQueryOperations(_)
399 | ExecutionError::AssertBefore { .. }
400 | ExecutionError::StreamNameTooLong
401 | ExecutionError::BlobTooLarge
402 | ExecutionError::BytecodeTooLarge
403 | ExecutionError::UnauthorizedHttpRequest(_)
404 | ExecutionError::InvalidUrlForHttpRequest(_)
405 | ExecutionError::InactiveChain(_)
406 | ExecutionError::BlobsNotFound(_)
407 | ExecutionError::EventsNotFound(_)
408 | ExecutionError::InvalidHeaderName(_)
409 | ExecutionError::InvalidHeaderValue(_)
410 | ExecutionError::InvalidEpoch { .. }
411 | ExecutionError::IncorrectTransferAmount
412 | ExecutionError::UnauthenticatedTransferOwner
413 | ExecutionError::InsufficientBalance { .. }
414 | ExecutionError::FeesExceedFunding { .. }
415 | ExecutionError::IncorrectClaimAmount
416 | ExecutionError::UnauthenticatedClaimOwner
417 | ExecutionError::AdminOperationOnNonAdminChain
418 | ExecutionError::InvalidCommitteeEpoch { .. }
419 | ExecutionError::InvalidCommitteeRemoval
420 | ExecutionError::MissingOracleResponse
421 | ExecutionError::UnprocessedStreams
422 | ExecutionError::OutdatedUpdateStreams
423 | ExecutionError::ViewError(ViewError::NotFound(_)) => false,
424 #[cfg(with_wasm_runtime)]
425 ExecutionError::WasmError(_) => false,
426 #[cfg(with_revm)]
427 ExecutionError::EvmError(..) => false,
428 ExecutionError::MissingRuntimeResponse
429 | ExecutionError::ViewError(_)
430 | ExecutionError::ReqwestError(_)
431 | ExecutionError::Thread(_)
432 | ExecutionError::NoNetworkDescriptionFound
433 | ExecutionError::InternalError(_)
434 | ExecutionError::IoError(_) => true,
435 }
436 }
437}
438
439pub trait UserContract {
441 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
443
444 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
446
447 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
449
450 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
452
453 fn finalize(&mut self) -> Result<(), ExecutionError>;
455}
456
457pub trait UserService {
459 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
461}
462
463#[derive(Clone, Copy, Default)]
465pub struct ExecutionRuntimeConfig {}
466
467#[cfg_attr(not(web), async_trait)]
470#[cfg_attr(web, async_trait(?Send))]
471pub trait ExecutionRuntimeContext {
472 fn chain_id(&self) -> ChainId;
473
474 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
475
476 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>>;
477
478 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>>;
479
480 async fn get_user_contract(
481 &self,
482 description: &ApplicationDescription,
483 txn_tracker: &TransactionTracker,
484 ) -> Result<UserContractCode, ExecutionError>;
485
486 async fn get_user_service(
487 &self,
488 description: &ApplicationDescription,
489 txn_tracker: &TransactionTracker,
490 ) -> Result<UserServiceCode, ExecutionError>;
491
492 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError>;
493
494 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError>;
495
496 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
497
498 async fn get_committees(
500 &self,
501 epoch_range: RangeInclusive<Epoch>,
502 ) -> Result<BTreeMap<Epoch, Committee>, ExecutionError> {
503 let net_description = self
504 .get_network_description()
505 .await?
506 .ok_or(ExecutionError::NoNetworkDescriptionFound)?;
507 let committee_hashes = futures::future::join_all(
508 (epoch_range.start().0..=epoch_range.end().0).map(|epoch| async move {
509 if epoch == 0 {
510 Ok((epoch, net_description.genesis_committee_blob_hash))
512 } else {
513 let event_id = EventId {
514 chain_id: net_description.admin_chain_id,
515 stream_id: StreamId::system(EPOCH_STREAM_NAME),
516 index: epoch,
517 };
518 let event = self
519 .get_event(event_id.clone())
520 .await?
521 .ok_or_else(|| ExecutionError::EventsNotFound(vec![event_id]))?;
522 Ok((epoch, bcs::from_bytes(&event)?))
523 }
524 }),
525 )
526 .await;
527 let missing_events = committee_hashes
528 .iter()
529 .filter_map(|result| {
530 if let Err(ExecutionError::EventsNotFound(event_ids)) = result {
531 return Some(event_ids);
532 }
533 None
534 })
535 .flatten()
536 .cloned()
537 .collect::<Vec<_>>();
538 ensure!(
539 missing_events.is_empty(),
540 ExecutionError::EventsNotFound(missing_events)
541 );
542 let committee_hashes = committee_hashes
543 .into_iter()
544 .collect::<Result<Vec<_>, _>>()?;
545 let committees = futures::future::join_all(committee_hashes.into_iter().map(
546 |(epoch, committee_hash)| async move {
547 let blob_id = BlobId::new(committee_hash, BlobType::Committee);
548 let committee_blob = self
549 .get_blob(blob_id)
550 .await?
551 .ok_or_else(|| ExecutionError::BlobsNotFound(vec![blob_id]))?;
552 Ok((Epoch(epoch), bcs::from_bytes(committee_blob.bytes())?))
553 },
554 ))
555 .await;
556 let missing_blobs = committees
557 .iter()
558 .filter_map(|result| {
559 if let Err(ExecutionError::BlobsNotFound(blob_ids)) = result {
560 return Some(blob_ids);
561 }
562 None
563 })
564 .flatten()
565 .cloned()
566 .collect::<Vec<_>>();
567 ensure!(
568 missing_blobs.is_empty(),
569 ExecutionError::BlobsNotFound(missing_blobs)
570 );
571 committees.into_iter().collect()
572 }
573
574 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
575
576 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
577
578 #[cfg(with_testing)]
579 async fn add_blobs(
580 &self,
581 blobs: impl IntoIterator<Item = Blob> + Send,
582 ) -> Result<(), ViewError>;
583
584 #[cfg(with_testing)]
585 async fn add_events(
586 &self,
587 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
588 ) -> Result<(), ViewError>;
589}
590
591#[derive(Clone, Copy, Debug)]
592pub struct OperationContext {
593 pub chain_id: ChainId,
595 #[debug(skip_if = Option::is_none)]
597 pub authenticated_signer: Option<AccountOwner>,
598 pub height: BlockHeight,
600 pub round: Option<u32>,
602 pub timestamp: Timestamp,
604}
605
606#[derive(Clone, Copy, Debug)]
607pub struct MessageContext {
608 pub chain_id: ChainId,
610 pub origin: ChainId,
612 pub is_bouncing: bool,
614 #[debug(skip_if = Option::is_none)]
616 pub authenticated_signer: Option<AccountOwner>,
617 #[debug(skip_if = Option::is_none)]
619 pub refund_grant_to: Option<Account>,
620 pub height: BlockHeight,
622 pub round: Option<u32>,
624 pub timestamp: Timestamp,
626}
627
628#[derive(Clone, Copy, Debug)]
629pub struct ProcessStreamsContext {
630 pub chain_id: ChainId,
632 pub height: BlockHeight,
634 pub round: Option<u32>,
636 pub timestamp: Timestamp,
638}
639
640impl From<MessageContext> for ProcessStreamsContext {
641 fn from(context: MessageContext) -> Self {
642 Self {
643 chain_id: context.chain_id,
644 height: context.height,
645 round: context.round,
646 timestamp: context.timestamp,
647 }
648 }
649}
650
651impl From<OperationContext> for ProcessStreamsContext {
652 fn from(context: OperationContext) -> Self {
653 Self {
654 chain_id: context.chain_id,
655 height: context.height,
656 round: context.round,
657 timestamp: context.timestamp,
658 }
659 }
660}
661
662#[derive(Clone, Copy, Debug)]
663pub struct FinalizeContext {
664 pub chain_id: ChainId,
666 #[debug(skip_if = Option::is_none)]
668 pub authenticated_signer: Option<AccountOwner>,
669 pub height: BlockHeight,
671 pub round: Option<u32>,
673}
674
675#[derive(Clone, Copy, Debug, Eq, PartialEq)]
676pub struct QueryContext {
677 pub chain_id: ChainId,
679 pub next_block_height: BlockHeight,
681 pub local_time: Timestamp,
683}
684
685pub trait BaseRuntime {
686 type Read: fmt::Debug + Send + Sync;
687 type ContainsKey: fmt::Debug + Send + Sync;
688 type ContainsKeys: fmt::Debug + Send + Sync;
689 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
690 type ReadValueBytes: fmt::Debug + Send + Sync;
691 type FindKeysByPrefix: fmt::Debug + Send + Sync;
692 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
693
694 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
696
697 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
699
700 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
702
703 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
705
706 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
708
709 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
711
712 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
714
715 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
717
718 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
720
721 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
723
724 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
726
727 #[cfg(feature = "test")]
729 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
730 let promise = self.contains_key_new(key)?;
731 self.contains_key_wait(&promise)
732 }
733
734 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
736
737 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
739
740 #[cfg(feature = "test")]
742 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
743 let promise = self.contains_keys_new(keys)?;
744 self.contains_keys_wait(&promise)
745 }
746
747 fn contains_keys_new(
749 &mut self,
750 keys: Vec<Vec<u8>>,
751 ) -> Result<Self::ContainsKeys, ExecutionError>;
752
753 fn contains_keys_wait(
755 &mut self,
756 promise: &Self::ContainsKeys,
757 ) -> Result<Vec<bool>, ExecutionError>;
758
759 #[cfg(feature = "test")]
761 fn read_multi_values_bytes(
762 &mut self,
763 keys: Vec<Vec<u8>>,
764 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
765 let promise = self.read_multi_values_bytes_new(keys)?;
766 self.read_multi_values_bytes_wait(&promise)
767 }
768
769 fn read_multi_values_bytes_new(
771 &mut self,
772 keys: Vec<Vec<u8>>,
773 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
774
775 fn read_multi_values_bytes_wait(
777 &mut self,
778 promise: &Self::ReadMultiValuesBytes,
779 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
780
781 #[cfg(feature = "test")]
783 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
784 let promise = self.read_value_bytes_new(key)?;
785 self.read_value_bytes_wait(&promise)
786 }
787
788 fn read_value_bytes_new(
790 &mut self,
791 key: Vec<u8>,
792 ) -> Result<Self::ReadValueBytes, ExecutionError>;
793
794 fn read_value_bytes_wait(
796 &mut self,
797 promise: &Self::ReadValueBytes,
798 ) -> Result<Option<Vec<u8>>, ExecutionError>;
799
800 fn find_keys_by_prefix_new(
802 &mut self,
803 key_prefix: Vec<u8>,
804 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
805
806 fn find_keys_by_prefix_wait(
808 &mut self,
809 promise: &Self::FindKeysByPrefix,
810 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
811
812 #[cfg(feature = "test")]
814 #[expect(clippy::type_complexity)]
815 fn find_key_values_by_prefix(
816 &mut self,
817 key_prefix: Vec<u8>,
818 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
819 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
820 self.find_key_values_by_prefix_wait(&promise)
821 }
822
823 fn find_key_values_by_prefix_new(
825 &mut self,
826 key_prefix: Vec<u8>,
827 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
828
829 #[expect(clippy::type_complexity)]
831 fn find_key_values_by_prefix_wait(
832 &mut self,
833 promise: &Self::FindKeyValuesByPrefix,
834 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
835
836 fn perform_http_request(
838 &mut self,
839 request: http::Request,
840 ) -> Result<http::Response, ExecutionError>;
841
842 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
848
849 fn read_data_blob(&mut self, hash: DataBlobHash) -> Result<Vec<u8>, ExecutionError>;
851
852 fn assert_data_blob_exists(&mut self, hash: DataBlobHash) -> Result<(), ExecutionError>;
854}
855
856pub trait ServiceRuntime: BaseRuntime {
857 fn try_query_application(
859 &mut self,
860 queried_id: ApplicationId,
861 argument: Vec<u8>,
862 ) -> Result<Vec<u8>, ExecutionError>;
863
864 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
866
867 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
869}
870
871pub trait ContractRuntime: BaseRuntime {
872 fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
874
875 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
878
879 fn message_origin_chain_id(&mut self) -> Result<Option<ChainId>, ExecutionError>;
881
882 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
885
886 fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
888
889 fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
891
892 fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
894
895 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
897
898 fn transfer(
900 &mut self,
901 source: AccountOwner,
902 destination: Account,
903 amount: Amount,
904 ) -> Result<(), ExecutionError>;
905
906 fn claim(
908 &mut self,
909 source: Account,
910 destination: Account,
911 amount: Amount,
912 ) -> Result<(), ExecutionError>;
913
914 fn try_call_application(
917 &mut self,
918 authenticated: bool,
919 callee_id: ApplicationId,
920 argument: Vec<u8>,
921 ) -> Result<Vec<u8>, ExecutionError>;
922
923 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
925
926 fn read_event(
930 &mut self,
931 chain_id: ChainId,
932 stream_name: StreamName,
933 index: u32,
934 ) -> Result<Vec<u8>, ExecutionError>;
935
936 fn subscribe_to_events(
938 &mut self,
939 chain_id: ChainId,
940 application_id: ApplicationId,
941 stream_name: StreamName,
942 ) -> Result<(), ExecutionError>;
943
944 fn unsubscribe_from_events(
946 &mut self,
947 chain_id: ChainId,
948 application_id: ApplicationId,
949 stream_name: StreamName,
950 ) -> Result<(), ExecutionError>;
951
952 fn query_service(
954 &mut self,
955 application_id: ApplicationId,
956 query: Vec<u8>,
957 ) -> Result<Vec<u8>, ExecutionError>;
958
959 fn open_chain(
961 &mut self,
962 ownership: ChainOwnership,
963 application_permissions: ApplicationPermissions,
964 balance: Amount,
965 ) -> Result<ChainId, ExecutionError>;
966
967 fn close_chain(&mut self) -> Result<(), ExecutionError>;
969
970 fn change_application_permissions(
972 &mut self,
973 application_permissions: ApplicationPermissions,
974 ) -> Result<(), ExecutionError>;
975
976 fn create_application(
978 &mut self,
979 module_id: ModuleId,
980 parameters: Vec<u8>,
981 argument: Vec<u8>,
982 required_application_ids: Vec<ApplicationId>,
983 ) -> Result<ApplicationId, ExecutionError>;
984
985 fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<DataBlobHash, ExecutionError>;
987
988 fn publish_module(
990 &mut self,
991 contract: Bytecode,
992 service: Bytecode,
993 vm_runtime: VmRuntime,
994 ) -> Result<ModuleId, ExecutionError>;
995
996 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
998
999 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
1001}
1002
1003#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1005pub enum Operation {
1006 System(Box<SystemOperation>),
1008 User {
1010 application_id: ApplicationId,
1011 #[serde(with = "serde_bytes")]
1012 #[debug(with = "hex_debug")]
1013 bytes: Vec<u8>,
1014 },
1015}
1016
1017impl BcsHashable<'_> for Operation {}
1018
1019#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1021pub enum Message {
1022 System(SystemMessage),
1024 User {
1026 application_id: ApplicationId,
1027 #[serde(with = "serde_bytes")]
1028 #[debug(with = "hex_debug")]
1029 bytes: Vec<u8>,
1030 },
1031}
1032
1033#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1035pub enum Query {
1036 System(SystemQuery),
1038 User {
1040 application_id: ApplicationId,
1041 #[serde(with = "serde_bytes")]
1042 #[debug(with = "hex_debug")]
1043 bytes: Vec<u8>,
1044 },
1045}
1046
1047#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1049pub struct QueryOutcome<Response = QueryResponse> {
1050 pub response: Response,
1051 pub operations: Vec<Operation>,
1052}
1053
1054impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
1055 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
1056 let QueryOutcome {
1057 response,
1058 operations,
1059 } = system_outcome;
1060
1061 QueryOutcome {
1062 response: QueryResponse::System(response),
1063 operations,
1064 }
1065 }
1066}
1067
1068impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
1069 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
1070 let QueryOutcome {
1071 response,
1072 operations,
1073 } = user_service_outcome;
1074
1075 QueryOutcome {
1076 response: QueryResponse::User(response),
1077 operations,
1078 }
1079 }
1080}
1081
1082#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1084pub enum QueryResponse {
1085 System(SystemResponse),
1087 User(
1089 #[serde(with = "serde_bytes")]
1090 #[debug(with = "hex_debug")]
1091 Vec<u8>,
1092 ),
1093}
1094
1095#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy, Allocative)]
1097pub enum MessageKind {
1098 Simple,
1100 Protected,
1103 Tracked,
1106 Bouncing,
1108}
1109
1110impl Display for MessageKind {
1111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1112 match self {
1113 MessageKind::Simple => write!(f, "Simple"),
1114 MessageKind::Protected => write!(f, "Protected"),
1115 MessageKind::Tracked => write!(f, "Tracked"),
1116 MessageKind::Bouncing => write!(f, "Bouncing"),
1117 }
1118 }
1119}
1120
1121#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1123pub struct OutgoingMessage {
1124 pub destination: ChainId,
1126 #[debug(skip_if = Option::is_none)]
1128 pub authenticated_signer: Option<AccountOwner>,
1129 #[debug(skip_if = Amount::is_zero)]
1131 pub grant: Amount,
1132 #[debug(skip_if = Option::is_none)]
1134 pub refund_grant_to: Option<Account>,
1135 pub kind: MessageKind,
1137 pub message: Message,
1139}
1140
1141impl BcsHashable<'_> for OutgoingMessage {}
1142
1143impl OutgoingMessage {
1144 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
1146 OutgoingMessage {
1147 destination: recipient,
1148 authenticated_signer: None,
1149 grant: Amount::ZERO,
1150 refund_grant_to: None,
1151 kind: MessageKind::Simple,
1152 message: message.into(),
1153 }
1154 }
1155
1156 pub fn with_kind(mut self, kind: MessageKind) -> Self {
1158 self.kind = kind;
1159 self
1160 }
1161
1162 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
1164 self.authenticated_signer = authenticated_signer;
1165 self
1166 }
1167}
1168
1169impl OperationContext {
1170 fn refund_grant_to(&self) -> Option<Account> {
1173 self.authenticated_signer.map(|owner| Account {
1174 chain_id: self.chain_id,
1175 owner,
1176 })
1177 }
1178}
1179
1180#[cfg(with_testing)]
1181#[derive(Clone)]
1182pub struct TestExecutionRuntimeContext {
1183 chain_id: ChainId,
1184 execution_runtime_config: ExecutionRuntimeConfig,
1185 user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
1186 user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
1187 blobs: Arc<papaya::HashMap<BlobId, Blob>>,
1188 events: Arc<papaya::HashMap<EventId, Vec<u8>>>,
1189}
1190
1191#[cfg(with_testing)]
1192impl TestExecutionRuntimeContext {
1193 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1194 Self {
1195 chain_id,
1196 execution_runtime_config,
1197 user_contracts: Arc::default(),
1198 user_services: Arc::default(),
1199 blobs: Arc::default(),
1200 events: Arc::default(),
1201 }
1202 }
1203}
1204
1205#[cfg(with_testing)]
1206#[cfg_attr(not(web), async_trait)]
1207#[cfg_attr(web, async_trait(?Send))]
1208impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1209 fn chain_id(&self) -> ChainId {
1210 self.chain_id
1211 }
1212
1213 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1214 self.execution_runtime_config
1215 }
1216
1217 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>> {
1218 &self.user_contracts
1219 }
1220
1221 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>> {
1222 &self.user_services
1223 }
1224
1225 async fn get_user_contract(
1226 &self,
1227 description: &ApplicationDescription,
1228 _txn_tracker: &TransactionTracker,
1229 ) -> Result<UserContractCode, ExecutionError> {
1230 let application_id: ApplicationId = description.into();
1231 let pinned = self.user_contracts().pin();
1232 Ok(pinned
1233 .get(&application_id)
1234 .ok_or_else(|| {
1235 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1236 })?
1237 .clone())
1238 }
1239
1240 async fn get_user_service(
1241 &self,
1242 description: &ApplicationDescription,
1243 _txn_tracker: &TransactionTracker,
1244 ) -> Result<UserServiceCode, ExecutionError> {
1245 let application_id: ApplicationId = description.into();
1246 let pinned = self.user_services().pin();
1247 Ok(pinned
1248 .get(&application_id)
1249 .ok_or_else(|| {
1250 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1251 })?
1252 .clone())
1253 }
1254
1255 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError> {
1256 Ok(self.blobs.pin().get(&blob_id).cloned())
1257 }
1258
1259 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError> {
1260 Ok(self.events.pin().get(&event_id).cloned())
1261 }
1262
1263 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1264 let pinned = self.blobs.pin();
1265 let genesis_committee_blob_hash = pinned
1266 .iter()
1267 .find(|(_, blob)| blob.content().blob_type() == BlobType::Committee)
1268 .map_or_else(
1269 || CryptoHash::test_hash("genesis committee"),
1270 |(_, blob)| blob.id().hash,
1271 );
1272 Ok(Some(NetworkDescription {
1273 admin_chain_id: dummy_chain_description(0).id(),
1274 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1275 genesis_timestamp: Timestamp::from(0),
1276 genesis_committee_blob_hash,
1277 name: "dummy network description".to_string(),
1278 }))
1279 }
1280
1281 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1282 Ok(self.blobs.pin().contains_key(&blob_id))
1283 }
1284
1285 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1286 Ok(self.events.pin().contains_key(&event_id))
1287 }
1288
1289 #[cfg(with_testing)]
1290 async fn add_blobs(
1291 &self,
1292 blobs: impl IntoIterator<Item = Blob> + Send,
1293 ) -> Result<(), ViewError> {
1294 let pinned = self.blobs.pin();
1295 for blob in blobs {
1296 pinned.insert(blob.id(), blob);
1297 }
1298
1299 Ok(())
1300 }
1301
1302 #[cfg(with_testing)]
1303 async fn add_events(
1304 &self,
1305 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1306 ) -> Result<(), ViewError> {
1307 let pinned = self.events.pin();
1308 for (event_id, bytes) in events {
1309 pinned.insert(event_id, bytes);
1310 }
1311
1312 Ok(())
1313 }
1314}
1315
1316impl From<SystemOperation> for Operation {
1317 fn from(operation: SystemOperation) -> Self {
1318 Operation::System(Box::new(operation))
1319 }
1320}
1321
1322impl Operation {
1323 pub fn system(operation: SystemOperation) -> Self {
1324 Operation::System(Box::new(operation))
1325 }
1326
1327 #[cfg(with_testing)]
1329 pub fn user<A: Abi>(
1330 application_id: ApplicationId<A>,
1331 operation: &A::Operation,
1332 ) -> Result<Self, bcs::Error> {
1333 Self::user_without_abi(application_id.forget_abi(), operation)
1334 }
1335
1336 #[cfg(with_testing)]
1339 pub fn user_without_abi(
1340 application_id: ApplicationId,
1341 operation: &impl Serialize,
1342 ) -> Result<Self, bcs::Error> {
1343 Ok(Operation::User {
1344 application_id,
1345 bytes: bcs::to_bytes(&operation)?,
1346 })
1347 }
1348
1349 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1352 match self {
1353 Operation::System(system_operation) => Some(system_operation),
1354 Operation::User { .. } => None,
1355 }
1356 }
1357
1358 pub fn application_id(&self) -> GenericApplicationId {
1359 match self {
1360 Self::System(_) => GenericApplicationId::System,
1361 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1362 }
1363 }
1364
1365 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1367 match self.as_system_operation() {
1368 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1369 vec![BlobId::new(*blob_hash, BlobType::Data)]
1370 }
1371 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1372 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1373 }
1374 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1375 _ => vec![],
1376 }
1377 }
1378
1379 pub fn is_exempt_from_permissions(&self) -> bool {
1381 let Operation::System(system_op) = self else {
1382 return false;
1383 };
1384 matches!(
1385 **system_op,
1386 SystemOperation::ProcessNewEpoch(_)
1387 | SystemOperation::ProcessRemovedEpoch(_)
1388 | SystemOperation::UpdateStreams(_)
1389 )
1390 }
1391}
1392
1393impl From<SystemMessage> for Message {
1394 fn from(message: SystemMessage) -> Self {
1395 Message::System(message)
1396 }
1397}
1398
1399impl Message {
1400 pub fn system(message: SystemMessage) -> Self {
1401 Message::System(message)
1402 }
1403
1404 pub fn user<A, M: Serialize>(
1407 application_id: ApplicationId<A>,
1408 message: &M,
1409 ) -> Result<Self, bcs::Error> {
1410 let application_id = application_id.forget_abi();
1411 let bytes = bcs::to_bytes(&message)?;
1412 Ok(Message::User {
1413 application_id,
1414 bytes,
1415 })
1416 }
1417
1418 pub fn application_id(&self) -> GenericApplicationId {
1419 match self {
1420 Self::System(_) => GenericApplicationId::System,
1421 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1422 }
1423 }
1424}
1425
1426impl From<SystemQuery> for Query {
1427 fn from(query: SystemQuery) -> Self {
1428 Query::System(query)
1429 }
1430}
1431
1432impl Query {
1433 pub fn system(query: SystemQuery) -> Self {
1434 Query::System(query)
1435 }
1436
1437 pub fn user<A: Abi>(
1439 application_id: ApplicationId<A>,
1440 query: &A::Query,
1441 ) -> Result<Self, serde_json::Error> {
1442 Self::user_without_abi(application_id.forget_abi(), query)
1443 }
1444
1445 pub fn user_without_abi(
1448 application_id: ApplicationId,
1449 query: &impl Serialize,
1450 ) -> Result<Self, serde_json::Error> {
1451 Ok(Query::User {
1452 application_id,
1453 bytes: serde_json::to_vec(&query)?,
1454 })
1455 }
1456
1457 pub fn application_id(&self) -> GenericApplicationId {
1458 match self {
1459 Self::System(_) => GenericApplicationId::System,
1460 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1461 }
1462 }
1463}
1464
1465impl From<SystemResponse> for QueryResponse {
1466 fn from(response: SystemResponse) -> Self {
1467 QueryResponse::System(response)
1468 }
1469}
1470
1471impl From<Vec<u8>> for QueryResponse {
1472 fn from(response: Vec<u8>) -> Self {
1473 QueryResponse::User(response)
1474 }
1475}
1476
1477#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1479pub struct BlobState {
1480 pub last_used_by: Option<CryptoHash>,
1484 pub chain_id: ChainId,
1486 pub block_height: BlockHeight,
1488 pub epoch: Option<Epoch>,
1490}
1491
1492#[derive(Clone, Copy, Display)]
1494#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1495pub enum WasmRuntime {
1496 #[cfg(with_wasmer)]
1497 #[default]
1498 #[display("wasmer")]
1499 Wasmer,
1500 #[cfg(with_wasmtime)]
1501 #[cfg_attr(not(with_wasmer), default)]
1502 #[display("wasmtime")]
1503 Wasmtime,
1504}
1505
1506#[derive(Clone, Copy, Display)]
1507#[cfg_attr(with_revm, derive(Debug, Default))]
1508pub enum EvmRuntime {
1509 #[cfg(with_revm)]
1510 #[default]
1511 #[display("revm")]
1512 Revm,
1513}
1514
1515pub trait WithWasmDefault {
1517 fn with_wasm_default(self) -> Self;
1518}
1519
1520impl WithWasmDefault for Option<WasmRuntime> {
1521 fn with_wasm_default(self) -> Self {
1522 #[cfg(with_wasm_runtime)]
1523 {
1524 Some(self.unwrap_or_default())
1525 }
1526 #[cfg(not(with_wasm_runtime))]
1527 {
1528 None
1529 }
1530 }
1531}
1532
1533impl FromStr for WasmRuntime {
1534 type Err = InvalidWasmRuntime;
1535
1536 fn from_str(string: &str) -> Result<Self, Self::Err> {
1537 match string {
1538 #[cfg(with_wasmer)]
1539 "wasmer" => Ok(WasmRuntime::Wasmer),
1540 #[cfg(with_wasmtime)]
1541 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1542 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1543 }
1544 }
1545}
1546
1547#[derive(Clone, Debug, Error)]
1549#[error("{0:?} is not a valid WebAssembly runtime")]
1550pub struct InvalidWasmRuntime(String);
1551
1552doc_scalar!(Operation, "An operation to be executed in a block");
1553doc_scalar!(
1554 Message,
1555 "A message to be sent and possibly executed in the receiver's block."
1556);
1557doc_scalar!(MessageKind, "The kind of outgoing message being sent");