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)]
467pub struct ExecutionRuntimeConfig {
468 pub allow_application_logs: bool,
471}
472
473impl Default for ExecutionRuntimeConfig {
474 fn default() -> Self {
475 Self {
476 allow_application_logs: true,
477 }
478 }
479}
480
481#[cfg_attr(not(web), async_trait)]
484#[cfg_attr(web, async_trait(?Send))]
485pub trait ExecutionRuntimeContext {
486 fn chain_id(&self) -> ChainId;
487
488 fn thread_pool(&self) -> &Arc<ThreadPool>;
489
490 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
491
492 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>>;
493
494 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>>;
495
496 async fn get_user_contract(
497 &self,
498 description: &ApplicationDescription,
499 txn_tracker: &TransactionTracker,
500 ) -> Result<UserContractCode, ExecutionError>;
501
502 async fn get_user_service(
503 &self,
504 description: &ApplicationDescription,
505 txn_tracker: &TransactionTracker,
506 ) -> Result<UserServiceCode, ExecutionError>;
507
508 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError>;
509
510 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError>;
511
512 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
513
514 async fn get_committees(
516 &self,
517 epoch_range: RangeInclusive<Epoch>,
518 ) -> Result<BTreeMap<Epoch, Committee>, ExecutionError> {
519 let net_description = self
520 .get_network_description()
521 .await?
522 .ok_or(ExecutionError::NoNetworkDescriptionFound)?;
523 let committee_hashes = futures::future::join_all(
524 (epoch_range.start().0..=epoch_range.end().0).map(|epoch| async move {
525 if epoch == 0 {
526 Ok((epoch, net_description.genesis_committee_blob_hash))
528 } else {
529 let event_id = EventId {
530 chain_id: net_description.admin_chain_id,
531 stream_id: StreamId::system(EPOCH_STREAM_NAME),
532 index: epoch,
533 };
534 let event = self
535 .get_event(event_id.clone())
536 .await?
537 .ok_or_else(|| ExecutionError::EventsNotFound(vec![event_id]))?;
538 Ok((epoch, bcs::from_bytes(&event)?))
539 }
540 }),
541 )
542 .await;
543 let missing_events = committee_hashes
544 .iter()
545 .filter_map(|result| {
546 if let Err(ExecutionError::EventsNotFound(event_ids)) = result {
547 return Some(event_ids);
548 }
549 None
550 })
551 .flatten()
552 .cloned()
553 .collect::<Vec<_>>();
554 ensure!(
555 missing_events.is_empty(),
556 ExecutionError::EventsNotFound(missing_events)
557 );
558 let committee_hashes = committee_hashes
559 .into_iter()
560 .collect::<Result<Vec<_>, _>>()?;
561 let committees = futures::future::join_all(committee_hashes.into_iter().map(
562 |(epoch, committee_hash)| async move {
563 let blob_id = BlobId::new(committee_hash, BlobType::Committee);
564 let committee_blob = self
565 .get_blob(blob_id)
566 .await?
567 .ok_or_else(|| ExecutionError::BlobsNotFound(vec![blob_id]))?;
568 Ok((Epoch(epoch), bcs::from_bytes(committee_blob.bytes())?))
569 },
570 ))
571 .await;
572 let missing_blobs = committees
573 .iter()
574 .filter_map(|result| {
575 if let Err(ExecutionError::BlobsNotFound(blob_ids)) = result {
576 return Some(blob_ids);
577 }
578 None
579 })
580 .flatten()
581 .cloned()
582 .collect::<Vec<_>>();
583 ensure!(
584 missing_blobs.is_empty(),
585 ExecutionError::BlobsNotFound(missing_blobs)
586 );
587 committees.into_iter().collect()
588 }
589
590 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
591
592 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
593
594 #[cfg(with_testing)]
595 async fn add_blobs(
596 &self,
597 blobs: impl IntoIterator<Item = Blob> + Send,
598 ) -> Result<(), ViewError>;
599
600 #[cfg(with_testing)]
601 async fn add_events(
602 &self,
603 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
604 ) -> Result<(), ViewError>;
605}
606
607#[derive(Clone, Copy, Debug)]
608pub struct OperationContext {
609 pub chain_id: ChainId,
611 #[debug(skip_if = Option::is_none)]
613 pub authenticated_signer: Option<AccountOwner>,
614 pub height: BlockHeight,
616 pub round: Option<u32>,
618 pub timestamp: Timestamp,
620}
621
622#[derive(Clone, Copy, Debug)]
623pub struct MessageContext {
624 pub chain_id: ChainId,
626 pub origin: ChainId,
628 pub is_bouncing: bool,
630 #[debug(skip_if = Option::is_none)]
632 pub authenticated_signer: Option<AccountOwner>,
633 #[debug(skip_if = Option::is_none)]
635 pub refund_grant_to: Option<Account>,
636 pub height: BlockHeight,
638 pub round: Option<u32>,
640 pub timestamp: Timestamp,
642}
643
644#[derive(Clone, Copy, Debug)]
645pub struct ProcessStreamsContext {
646 pub chain_id: ChainId,
648 pub height: BlockHeight,
650 pub round: Option<u32>,
652 pub timestamp: Timestamp,
654}
655
656impl From<MessageContext> for ProcessStreamsContext {
657 fn from(context: MessageContext) -> Self {
658 Self {
659 chain_id: context.chain_id,
660 height: context.height,
661 round: context.round,
662 timestamp: context.timestamp,
663 }
664 }
665}
666
667impl From<OperationContext> for ProcessStreamsContext {
668 fn from(context: OperationContext) -> Self {
669 Self {
670 chain_id: context.chain_id,
671 height: context.height,
672 round: context.round,
673 timestamp: context.timestamp,
674 }
675 }
676}
677
678#[derive(Clone, Copy, Debug)]
679pub struct FinalizeContext {
680 pub chain_id: ChainId,
682 #[debug(skip_if = Option::is_none)]
684 pub authenticated_signer: Option<AccountOwner>,
685 pub height: BlockHeight,
687 pub round: Option<u32>,
689}
690
691#[derive(Clone, Copy, Debug, Eq, PartialEq)]
692pub struct QueryContext {
693 pub chain_id: ChainId,
695 pub next_block_height: BlockHeight,
697 pub local_time: Timestamp,
699}
700
701pub trait BaseRuntime {
702 type Read: fmt::Debug + Send + Sync;
703 type ContainsKey: fmt::Debug + Send + Sync;
704 type ContainsKeys: fmt::Debug + Send + Sync;
705 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
706 type ReadValueBytes: fmt::Debug + Send + Sync;
707 type FindKeysByPrefix: fmt::Debug + Send + Sync;
708 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
709
710 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
712
713 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
715
716 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
718
719 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
721
722 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
724
725 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
727
728 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
730
731 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
733
734 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
736
737 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
739
740 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
742
743 fn application_permissions(&mut self) -> Result<ApplicationPermissions, ExecutionError>;
745
746 #[cfg(feature = "test")]
748 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
749 let promise = self.contains_key_new(key)?;
750 self.contains_key_wait(&promise)
751 }
752
753 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
755
756 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
758
759 #[cfg(feature = "test")]
761 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
762 let promise = self.contains_keys_new(keys)?;
763 self.contains_keys_wait(&promise)
764 }
765
766 fn contains_keys_new(
768 &mut self,
769 keys: Vec<Vec<u8>>,
770 ) -> Result<Self::ContainsKeys, ExecutionError>;
771
772 fn contains_keys_wait(
774 &mut self,
775 promise: &Self::ContainsKeys,
776 ) -> Result<Vec<bool>, ExecutionError>;
777
778 #[cfg(feature = "test")]
780 fn read_multi_values_bytes(
781 &mut self,
782 keys: Vec<Vec<u8>>,
783 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
784 let promise = self.read_multi_values_bytes_new(keys)?;
785 self.read_multi_values_bytes_wait(&promise)
786 }
787
788 fn read_multi_values_bytes_new(
790 &mut self,
791 keys: Vec<Vec<u8>>,
792 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
793
794 fn read_multi_values_bytes_wait(
796 &mut self,
797 promise: &Self::ReadMultiValuesBytes,
798 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
799
800 #[cfg(feature = "test")]
802 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
803 let promise = self.read_value_bytes_new(key)?;
804 self.read_value_bytes_wait(&promise)
805 }
806
807 fn read_value_bytes_new(
809 &mut self,
810 key: Vec<u8>,
811 ) -> Result<Self::ReadValueBytes, ExecutionError>;
812
813 fn read_value_bytes_wait(
815 &mut self,
816 promise: &Self::ReadValueBytes,
817 ) -> Result<Option<Vec<u8>>, ExecutionError>;
818
819 fn find_keys_by_prefix_new(
821 &mut self,
822 key_prefix: Vec<u8>,
823 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
824
825 fn find_keys_by_prefix_wait(
827 &mut self,
828 promise: &Self::FindKeysByPrefix,
829 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
830
831 #[cfg(feature = "test")]
833 #[expect(clippy::type_complexity)]
834 fn find_key_values_by_prefix(
835 &mut self,
836 key_prefix: Vec<u8>,
837 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
838 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
839 self.find_key_values_by_prefix_wait(&promise)
840 }
841
842 fn find_key_values_by_prefix_new(
844 &mut self,
845 key_prefix: Vec<u8>,
846 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
847
848 #[expect(clippy::type_complexity)]
850 fn find_key_values_by_prefix_wait(
851 &mut self,
852 promise: &Self::FindKeyValuesByPrefix,
853 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
854
855 fn perform_http_request(
857 &mut self,
858 request: http::Request,
859 ) -> Result<http::Response, ExecutionError>;
860
861 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
867
868 fn read_data_blob(&mut self, hash: DataBlobHash) -> Result<Vec<u8>, ExecutionError>;
870
871 fn assert_data_blob_exists(&mut self, hash: DataBlobHash) -> Result<(), ExecutionError>;
873
874 fn allow_application_logs(&mut self) -> Result<bool, ExecutionError>;
877
878 #[cfg(web)]
881 fn send_log(&mut self, message: String, level: tracing::log::Level);
882}
883
884pub trait ServiceRuntime: BaseRuntime {
885 fn try_query_application(
887 &mut self,
888 queried_id: ApplicationId,
889 argument: Vec<u8>,
890 ) -> Result<Vec<u8>, ExecutionError>;
891
892 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
894
895 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
897}
898
899pub trait ContractRuntime: BaseRuntime {
900 fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
902
903 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
906
907 fn message_origin_chain_id(&mut self) -> Result<Option<ChainId>, ExecutionError>;
909
910 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
913
914 fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
916
917 fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
919
920 fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
922
923 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
925
926 fn transfer(
928 &mut self,
929 source: AccountOwner,
930 destination: Account,
931 amount: Amount,
932 ) -> Result<(), ExecutionError>;
933
934 fn claim(
936 &mut self,
937 source: Account,
938 destination: Account,
939 amount: Amount,
940 ) -> Result<(), ExecutionError>;
941
942 fn try_call_application(
945 &mut self,
946 authenticated: bool,
947 callee_id: ApplicationId,
948 argument: Vec<u8>,
949 ) -> Result<Vec<u8>, ExecutionError>;
950
951 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
953
954 fn read_event(
958 &mut self,
959 chain_id: ChainId,
960 stream_name: StreamName,
961 index: u32,
962 ) -> Result<Vec<u8>, ExecutionError>;
963
964 fn subscribe_to_events(
966 &mut self,
967 chain_id: ChainId,
968 application_id: ApplicationId,
969 stream_name: StreamName,
970 ) -> Result<(), ExecutionError>;
971
972 fn unsubscribe_from_events(
974 &mut self,
975 chain_id: ChainId,
976 application_id: ApplicationId,
977 stream_name: StreamName,
978 ) -> Result<(), ExecutionError>;
979
980 fn query_service(
982 &mut self,
983 application_id: ApplicationId,
984 query: Vec<u8>,
985 ) -> Result<Vec<u8>, ExecutionError>;
986
987 fn open_chain(
989 &mut self,
990 ownership: ChainOwnership,
991 application_permissions: ApplicationPermissions,
992 balance: Amount,
993 ) -> Result<ChainId, ExecutionError>;
994
995 fn close_chain(&mut self) -> Result<(), ExecutionError>;
997
998 fn change_ownership(&mut self, ownership: ChainOwnership) -> Result<(), ExecutionError>;
1000
1001 fn change_application_permissions(
1003 &mut self,
1004 application_permissions: ApplicationPermissions,
1005 ) -> Result<(), ExecutionError>;
1006
1007 fn create_application(
1009 &mut self,
1010 module_id: ModuleId,
1011 parameters: Vec<u8>,
1012 argument: Vec<u8>,
1013 required_application_ids: Vec<ApplicationId>,
1014 ) -> Result<ApplicationId, ExecutionError>;
1015
1016 fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<DataBlobHash, ExecutionError>;
1018
1019 fn publish_module(
1021 &mut self,
1022 contract: Bytecode,
1023 service: Bytecode,
1024 vm_runtime: VmRuntime,
1025 ) -> Result<ModuleId, ExecutionError>;
1026
1027 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
1029
1030 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
1032}
1033
1034#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1036pub enum Operation {
1037 System(Box<SystemOperation>),
1039 User {
1041 application_id: ApplicationId,
1042 #[serde(with = "serde_bytes")]
1043 #[debug(with = "hex_debug")]
1044 bytes: Vec<u8>,
1045 },
1046}
1047
1048impl BcsHashable<'_> for Operation {}
1049
1050#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative)]
1052pub enum Message {
1053 System(SystemMessage),
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 enum Query {
1067 System(SystemQuery),
1069 User {
1071 application_id: ApplicationId,
1072 #[serde(with = "serde_bytes")]
1073 #[debug(with = "hex_debug")]
1074 bytes: Vec<u8>,
1075 },
1076}
1077
1078#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1080pub struct QueryOutcome<Response = QueryResponse> {
1081 pub response: Response,
1082 pub operations: Vec<Operation>,
1083}
1084
1085impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
1086 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
1087 let QueryOutcome {
1088 response,
1089 operations,
1090 } = system_outcome;
1091
1092 QueryOutcome {
1093 response: QueryResponse::System(response),
1094 operations,
1095 }
1096 }
1097}
1098
1099impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
1100 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
1101 let QueryOutcome {
1102 response,
1103 operations,
1104 } = user_service_outcome;
1105
1106 QueryOutcome {
1107 response: QueryResponse::User(response),
1108 operations,
1109 }
1110 }
1111}
1112
1113#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1115pub enum QueryResponse {
1116 System(SystemResponse),
1118 User(
1120 #[serde(with = "serde_bytes")]
1121 #[debug(with = "hex_debug")]
1122 Vec<u8>,
1123 ),
1124}
1125
1126#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy, Allocative)]
1128pub enum MessageKind {
1129 Simple,
1131 Protected,
1134 Tracked,
1137 Bouncing,
1139}
1140
1141impl Display for MessageKind {
1142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1143 match self {
1144 MessageKind::Simple => write!(f, "Simple"),
1145 MessageKind::Protected => write!(f, "Protected"),
1146 MessageKind::Tracked => write!(f, "Tracked"),
1147 MessageKind::Bouncing => write!(f, "Bouncing"),
1148 }
1149 }
1150}
1151
1152#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1154pub struct OutgoingMessage {
1155 pub destination: ChainId,
1157 #[debug(skip_if = Option::is_none)]
1159 pub authenticated_signer: Option<AccountOwner>,
1160 #[debug(skip_if = Amount::is_zero)]
1162 pub grant: Amount,
1163 #[debug(skip_if = Option::is_none)]
1165 pub refund_grant_to: Option<Account>,
1166 pub kind: MessageKind,
1168 pub message: Message,
1170}
1171
1172impl BcsHashable<'_> for OutgoingMessage {}
1173
1174impl OutgoingMessage {
1175 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
1177 OutgoingMessage {
1178 destination: recipient,
1179 authenticated_signer: None,
1180 grant: Amount::ZERO,
1181 refund_grant_to: None,
1182 kind: MessageKind::Simple,
1183 message: message.into(),
1184 }
1185 }
1186
1187 pub fn with_kind(mut self, kind: MessageKind) -> Self {
1189 self.kind = kind;
1190 self
1191 }
1192
1193 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
1195 self.authenticated_signer = authenticated_signer;
1196 self
1197 }
1198}
1199
1200impl OperationContext {
1201 fn refund_grant_to(&self) -> Option<Account> {
1204 self.authenticated_signer.map(|owner| Account {
1205 chain_id: self.chain_id,
1206 owner,
1207 })
1208 }
1209}
1210
1211#[cfg(with_testing)]
1212#[derive(Clone)]
1213pub struct TestExecutionRuntimeContext {
1214 chain_id: ChainId,
1215 thread_pool: Arc<ThreadPool>,
1216 execution_runtime_config: ExecutionRuntimeConfig,
1217 user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
1218 user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
1219 blobs: Arc<papaya::HashMap<BlobId, Blob>>,
1220 events: Arc<papaya::HashMap<EventId, Vec<u8>>>,
1221}
1222
1223#[cfg(with_testing)]
1224impl TestExecutionRuntimeContext {
1225 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1226 Self {
1227 chain_id,
1228 thread_pool: Arc::new(ThreadPool::new(20)),
1229 execution_runtime_config,
1230 user_contracts: Arc::default(),
1231 user_services: Arc::default(),
1232 blobs: Arc::default(),
1233 events: Arc::default(),
1234 }
1235 }
1236}
1237
1238#[cfg(with_testing)]
1239#[cfg_attr(not(web), async_trait)]
1240#[cfg_attr(web, async_trait(?Send))]
1241impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1242 fn chain_id(&self) -> ChainId {
1243 self.chain_id
1244 }
1245
1246 fn thread_pool(&self) -> &Arc<ThreadPool> {
1247 &self.thread_pool
1248 }
1249
1250 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1251 self.execution_runtime_config
1252 }
1253
1254 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>> {
1255 &self.user_contracts
1256 }
1257
1258 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>> {
1259 &self.user_services
1260 }
1261
1262 async fn get_user_contract(
1263 &self,
1264 description: &ApplicationDescription,
1265 _txn_tracker: &TransactionTracker,
1266 ) -> Result<UserContractCode, ExecutionError> {
1267 let application_id: ApplicationId = description.into();
1268 let pinned = self.user_contracts().pin();
1269 Ok(pinned
1270 .get(&application_id)
1271 .ok_or_else(|| {
1272 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1273 })?
1274 .clone())
1275 }
1276
1277 async fn get_user_service(
1278 &self,
1279 description: &ApplicationDescription,
1280 _txn_tracker: &TransactionTracker,
1281 ) -> Result<UserServiceCode, ExecutionError> {
1282 let application_id: ApplicationId = description.into();
1283 let pinned = self.user_services().pin();
1284 Ok(pinned
1285 .get(&application_id)
1286 .ok_or_else(|| {
1287 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1288 })?
1289 .clone())
1290 }
1291
1292 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError> {
1293 Ok(self.blobs.pin().get(&blob_id).cloned())
1294 }
1295
1296 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError> {
1297 Ok(self.events.pin().get(&event_id).cloned())
1298 }
1299
1300 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1301 let pinned = self.blobs.pin();
1302 let genesis_committee_blob_hash = pinned
1303 .iter()
1304 .find(|(_, blob)| blob.content().blob_type() == BlobType::Committee)
1305 .map_or_else(
1306 || CryptoHash::test_hash("genesis committee"),
1307 |(_, blob)| blob.id().hash,
1308 );
1309 Ok(Some(NetworkDescription {
1310 admin_chain_id: dummy_chain_description(0).id(),
1311 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1312 genesis_timestamp: Timestamp::from(0),
1313 genesis_committee_blob_hash,
1314 name: "dummy network description".to_string(),
1315 }))
1316 }
1317
1318 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1319 Ok(self.blobs.pin().contains_key(&blob_id))
1320 }
1321
1322 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1323 Ok(self.events.pin().contains_key(&event_id))
1324 }
1325
1326 #[cfg(with_testing)]
1327 async fn add_blobs(
1328 &self,
1329 blobs: impl IntoIterator<Item = Blob> + Send,
1330 ) -> Result<(), ViewError> {
1331 let pinned = self.blobs.pin();
1332 for blob in blobs {
1333 pinned.insert(blob.id(), blob);
1334 }
1335
1336 Ok(())
1337 }
1338
1339 #[cfg(with_testing)]
1340 async fn add_events(
1341 &self,
1342 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1343 ) -> Result<(), ViewError> {
1344 let pinned = self.events.pin();
1345 for (event_id, bytes) in events {
1346 pinned.insert(event_id, bytes);
1347 }
1348
1349 Ok(())
1350 }
1351}
1352
1353impl From<SystemOperation> for Operation {
1354 fn from(operation: SystemOperation) -> Self {
1355 Operation::System(Box::new(operation))
1356 }
1357}
1358
1359impl Operation {
1360 pub fn system(operation: SystemOperation) -> Self {
1361 Operation::System(Box::new(operation))
1362 }
1363
1364 #[cfg(with_testing)]
1366 pub fn user<A: Abi>(
1367 application_id: ApplicationId<A>,
1368 operation: &A::Operation,
1369 ) -> Result<Self, bcs::Error> {
1370 Self::user_without_abi(application_id.forget_abi(), operation)
1371 }
1372
1373 #[cfg(with_testing)]
1376 pub fn user_without_abi(
1377 application_id: ApplicationId,
1378 operation: &impl Serialize,
1379 ) -> Result<Self, bcs::Error> {
1380 Ok(Operation::User {
1381 application_id,
1382 bytes: bcs::to_bytes(&operation)?,
1383 })
1384 }
1385
1386 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1389 match self {
1390 Operation::System(system_operation) => Some(system_operation),
1391 Operation::User { .. } => None,
1392 }
1393 }
1394
1395 pub fn application_id(&self) -> GenericApplicationId {
1396 match self {
1397 Self::System(_) => GenericApplicationId::System,
1398 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1399 }
1400 }
1401
1402 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1404 match self.as_system_operation() {
1405 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1406 vec![BlobId::new(*blob_hash, BlobType::Data)]
1407 }
1408 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1409 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1410 }
1411 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1412 _ => vec![],
1413 }
1414 }
1415
1416 pub fn is_exempt_from_permissions(&self) -> bool {
1418 let Operation::System(system_op) = self else {
1419 return false;
1420 };
1421 matches!(
1422 **system_op,
1423 SystemOperation::ProcessNewEpoch(_)
1424 | SystemOperation::ProcessRemovedEpoch(_)
1425 | SystemOperation::UpdateStreams(_)
1426 )
1427 }
1428}
1429
1430impl From<SystemMessage> for Message {
1431 fn from(message: SystemMessage) -> Self {
1432 Message::System(message)
1433 }
1434}
1435
1436impl Message {
1437 pub fn system(message: SystemMessage) -> Self {
1438 Message::System(message)
1439 }
1440
1441 pub fn user<A, M: Serialize>(
1444 application_id: ApplicationId<A>,
1445 message: &M,
1446 ) -> Result<Self, bcs::Error> {
1447 let application_id = application_id.forget_abi();
1448 let bytes = bcs::to_bytes(&message)?;
1449 Ok(Message::User {
1450 application_id,
1451 bytes,
1452 })
1453 }
1454
1455 pub fn application_id(&self) -> GenericApplicationId {
1456 match self {
1457 Self::System(_) => GenericApplicationId::System,
1458 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1459 }
1460 }
1461}
1462
1463impl From<SystemQuery> for Query {
1464 fn from(query: SystemQuery) -> Self {
1465 Query::System(query)
1466 }
1467}
1468
1469impl Query {
1470 pub fn system(query: SystemQuery) -> Self {
1471 Query::System(query)
1472 }
1473
1474 pub fn user<A: Abi>(
1476 application_id: ApplicationId<A>,
1477 query: &A::Query,
1478 ) -> Result<Self, serde_json::Error> {
1479 Self::user_without_abi(application_id.forget_abi(), query)
1480 }
1481
1482 pub fn user_without_abi(
1485 application_id: ApplicationId,
1486 query: &impl Serialize,
1487 ) -> Result<Self, serde_json::Error> {
1488 Ok(Query::User {
1489 application_id,
1490 bytes: serde_json::to_vec(&query)?,
1491 })
1492 }
1493
1494 pub fn application_id(&self) -> GenericApplicationId {
1495 match self {
1496 Self::System(_) => GenericApplicationId::System,
1497 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1498 }
1499 }
1500}
1501
1502impl From<SystemResponse> for QueryResponse {
1503 fn from(response: SystemResponse) -> Self {
1504 QueryResponse::System(response)
1505 }
1506}
1507
1508impl From<Vec<u8>> for QueryResponse {
1509 fn from(response: Vec<u8>) -> Self {
1510 QueryResponse::User(response)
1511 }
1512}
1513
1514#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1516pub struct BlobState {
1517 pub last_used_by: Option<CryptoHash>,
1521 pub chain_id: ChainId,
1523 pub block_height: BlockHeight,
1525 pub epoch: Option<Epoch>,
1527}
1528
1529#[derive(Clone, Copy, Display)]
1531#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1532pub enum WasmRuntime {
1533 #[cfg(with_wasmer)]
1534 #[default]
1535 #[display("wasmer")]
1536 Wasmer,
1537 #[cfg(with_wasmtime)]
1538 #[cfg_attr(not(with_wasmer), default)]
1539 #[display("wasmtime")]
1540 Wasmtime,
1541}
1542
1543#[derive(Clone, Copy, Display)]
1544#[cfg_attr(with_revm, derive(Debug, Default))]
1545pub enum EvmRuntime {
1546 #[cfg(with_revm)]
1547 #[default]
1548 #[display("revm")]
1549 Revm,
1550}
1551
1552pub trait WithWasmDefault {
1554 fn with_wasm_default(self) -> Self;
1555}
1556
1557impl WithWasmDefault for Option<WasmRuntime> {
1558 fn with_wasm_default(self) -> Self {
1559 #[cfg(with_wasm_runtime)]
1560 {
1561 Some(self.unwrap_or_default())
1562 }
1563 #[cfg(not(with_wasm_runtime))]
1564 {
1565 None
1566 }
1567 }
1568}
1569
1570impl FromStr for WasmRuntime {
1571 type Err = InvalidWasmRuntime;
1572
1573 fn from_str(string: &str) -> Result<Self, Self::Err> {
1574 match string {
1575 #[cfg(with_wasmer)]
1576 "wasmer" => Ok(WasmRuntime::Wasmer),
1577 #[cfg(with_wasmtime)]
1578 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1579 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1580 }
1581 }
1582}
1583
1584#[derive(Clone, Debug, Error)]
1586#[error("{0:?} is not a valid WebAssembly runtime")]
1587pub struct InvalidWasmRuntime(String);
1588
1589doc_scalar!(Operation, "An operation to be executed in a block");
1590doc_scalar!(
1591 Message,
1592 "A message to be sent and possibly executed in the receiver's block."
1593);
1594doc_scalar!(MessageKind, "The kind of outgoing message being sent");