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