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, 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, SharedCommittees},
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";
100pub const FLAG_MANDATORY_APPS_NEED_ACCEPTED_MESSAGE: &str =
104 "FLAG_MANDATORY_APPS_NEED_ACCEPTED_MESSAGE.linera.network";
105pub const FLAG_FREE_APPLICATION_ID_PREFIX: &str = "FLAG_FREE_APPLICATION_ID_";
108pub const FLAG_FREE_APPLICATION_ID_SUFFIX: &str = ".linera.network";
110
111#[derive(Clone)]
113pub struct UserContractCode(Box<dyn UserContractModule>);
114
115#[derive(Clone)]
117pub struct UserServiceCode(Box<dyn UserServiceModule>);
118
119pub type UserContractInstance = Box<dyn UserContract>;
121
122pub type UserServiceInstance = Box<dyn UserService>;
124
125pub trait UserContractModule: dyn_clone::DynClone + Any + web_thread::Post + Send + Sync {
127 fn instantiate(
128 &self,
129 runtime: ContractSyncRuntimeHandle,
130 ) -> Result<UserContractInstance, ExecutionError>;
131}
132
133impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
134 fn from(module: T) -> Self {
135 Self(Box::new(module))
136 }
137}
138
139dyn_clone::clone_trait_object!(UserContractModule);
140
141pub trait UserServiceModule: dyn_clone::DynClone + Any + web_thread::Post + Send + Sync {
143 fn instantiate(
144 &self,
145 runtime: ServiceSyncRuntimeHandle,
146 ) -> Result<UserServiceInstance, ExecutionError>;
147}
148
149impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
150 fn from(module: T) -> Self {
151 Self(Box::new(module))
152 }
153}
154
155dyn_clone::clone_trait_object!(UserServiceModule);
156
157impl UserServiceCode {
158 fn instantiate(
159 &self,
160 runtime: ServiceSyncRuntimeHandle,
161 ) -> Result<UserServiceInstance, ExecutionError> {
162 self.0.instantiate(runtime)
163 }
164}
165
166impl UserContractCode {
167 fn instantiate(
168 &self,
169 runtime: ContractSyncRuntimeHandle,
170 ) -> Result<UserContractInstance, ExecutionError> {
171 self.0.instantiate(runtime)
172 }
173}
174
175pub struct JsVec<T>(pub Vec<T>);
176
177#[cfg(web)]
178const _: () = {
179 impl web_thread::AsJs for UserContractCode {
183 fn to_js(&self) -> Result<JsValue, JsValue> {
184 ((&*self.0) as &dyn Any)
185 .downcast_ref::<WasmContractModule>()
186 .expect("we only support Wasm modules on the Web for now")
187 .to_js()
188 }
189
190 fn from_js(value: JsValue) -> Result<Self, JsValue> {
191 WasmContractModule::from_js(value).map(Into::into)
192 }
193 }
194
195 impl web_thread::Post for UserContractCode {
196 fn transferables(&self) -> js_sys::Array {
197 self.0.transferables()
198 }
199 }
200
201 impl web_thread::AsJs for UserServiceCode {
202 fn to_js(&self) -> Result<JsValue, JsValue> {
203 ((&*self.0) as &dyn Any)
204 .downcast_ref::<WasmServiceModule>()
205 .expect("we only support Wasm modules on the Web for now")
206 .to_js()
207 }
208
209 fn from_js(value: JsValue) -> Result<Self, JsValue> {
210 WasmServiceModule::from_js(value).map(Into::into)
211 }
212 }
213
214 impl web_thread::Post for UserServiceCode {
215 fn transferables(&self) -> js_sys::Array {
216 self.0.transferables()
217 }
218 }
219
220 impl<T: web_thread::AsJs> web_thread::AsJs for JsVec<T> {
221 fn to_js(&self) -> Result<JsValue, JsValue> {
222 let array = self
223 .0
224 .iter()
225 .map(T::to_js)
226 .collect::<Result<js_sys::Array, _>>()?;
227 Ok(array.into())
228 }
229
230 fn from_js(value: JsValue) -> Result<Self, JsValue> {
231 let array = js_sys::Array::from(&value);
232 let v = array
233 .into_iter()
234 .map(T::from_js)
235 .collect::<Result<Vec<_>, _>>()?;
236 Ok(JsVec(v))
237 }
238 }
239
240 impl<T: web_thread::Post> web_thread::Post for JsVec<T> {
241 fn transferables(&self) -> js_sys::Array {
242 let mut array = js_sys::Array::new();
243 for x in &self.0 {
244 array = array.concat(&x.transferables());
245 }
246 array
247 }
248 }
249};
250
251#[derive(Error, Debug, strum::IntoStaticStr)]
253pub enum ExecutionError {
254 #[error(transparent)]
255 ViewError(#[from] ViewError),
256 #[error(transparent)]
257 ArithmeticError(#[from] ArithmeticError),
258 #[error("User application reported an error: {0}")]
259 UserError(String),
260 #[cfg(with_wasm_runtime)]
261 #[error(transparent)]
262 WasmError(#[from] WasmExecutionError),
263 #[cfg(with_revm)]
264 #[error(transparent)]
265 EvmError(#[from] EvmExecutionError),
266 #[error(transparent)]
267 DecompressionError(#[from] DecompressionError),
268 #[error("The given promise is invalid or was polled once already")]
269 InvalidPromise,
270
271 #[error("Attempted to perform a reentrant call to application {0}")]
272 ReentrantCall(ApplicationId),
273 #[error(
274 "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
275 from `finalize`"
276 )]
277 CrossApplicationCallInFinalize {
278 caller_id: Box<ApplicationId>,
279 callee_id: Box<ApplicationId>,
280 },
281 #[error("Failed to load bytecode from storage {0:?}")]
282 ApplicationBytecodeNotFound(Box<ApplicationDescription>),
283 #[error("Unsupported dynamic application load: {0:?}")]
285 UnsupportedDynamicApplicationLoad(Box<ApplicationId>),
286
287 #[error("Excessive number of bytes read from storage")]
288 ExcessiveRead,
289 #[error("Excessive number of bytes written to storage")]
290 ExcessiveWrite,
291 #[error("Block execution required too much fuel for VM {0}")]
292 MaximumFuelExceeded(VmRuntime),
293 #[error("Services running as oracles in block took longer than allowed")]
294 MaximumServiceOracleExecutionTimeExceeded,
295 #[error("Service running as an oracle produced a response that's too large")]
296 ServiceOracleResponseTooLarge,
297 #[error("Serialized size of the block exceeds limit")]
298 BlockTooLarge,
299 #[error("HTTP response exceeds the size limit of {limit} bytes, having at least {size} bytes")]
300 HttpResponseSizeLimitExceeded { limit: u64, size: u64 },
301 #[error("Runtime failed to respond to application")]
302 MissingRuntimeResponse,
303 #[error("Application is not authorized to perform system operations on this chain: {0:}")]
304 UnauthorizedApplication(ApplicationId),
305 #[error("Failed to make network reqwest: {0}")]
306 ReqwestError(#[from] reqwest::Error),
307 #[error("Encountered I/O error: {0}")]
308 IoError(#[from] std::io::Error),
309 #[error("More recorded oracle responses than expected")]
310 UnexpectedOracleResponse,
311 #[error("Invalid JSON: {0}")]
312 JsonError(#[from] serde_json::Error),
313 #[error(transparent)]
314 BcsError(#[from] bcs::Error),
315 #[error("Recorded response for oracle query has the wrong type")]
316 OracleResponseMismatch,
317 #[error("Service oracle query tried to create operations: {0:?}")]
318 ServiceOracleQueryOperations(Vec<Operation>),
319 #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
320 AssertBefore {
321 timestamp: Timestamp,
322 local_time: Timestamp,
323 },
324
325 #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
326 StreamNameTooLong,
327 #[error("Blob exceeds size limit")]
328 BlobTooLarge,
329 #[error("Bytecode exceeds size limit")]
330 BytecodeTooLarge,
331 #[error("Attempt to perform an HTTP request to an unauthorized host: {0:?}")]
332 UnauthorizedHttpRequest(reqwest::Url),
333 #[error("Attempt to perform an HTTP request to an invalid URL")]
334 InvalidUrlForHttpRequest(#[from] url::ParseError),
335 #[error("Worker thread failure: {0:?}")]
336 Thread(#[from] web_thread::Error),
337 #[error("Blobs not found: {0:?}")]
338 BlobsNotFound(Vec<BlobId>),
339 #[error("Events not found: {0:?}")]
340 EventsNotFound(Vec<EventId>),
341
342 #[error("Invalid HTTP header name used for HTTP request")]
343 InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
344 #[error("Invalid HTTP header value used for HTTP request")]
345 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
346
347 #[error("No NetworkDescription found in storage")]
348 NoNetworkDescriptionFound,
349 #[error("{epoch:?} is not recognized by chain {chain_id:}")]
350 InvalidEpoch { chain_id: ChainId, epoch: Epoch },
351 #[error("Transfer must have positive amount")]
352 IncorrectTransferAmount,
353 #[error("Transfer from owned account must be authenticated by the right signer")]
354 UnauthenticatedTransferOwner,
355 #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
356 InsufficientBalance {
357 balance: Amount,
358 account: AccountOwner,
359 },
360 #[error("Required execution fees exceeded the total funding available. Fees {fees}, available balance: {balance}")]
361 FeesExceedFunding { fees: Amount, balance: Amount },
362 #[error("Claim must have positive amount")]
363 IncorrectClaimAmount,
364 #[error("Claim must be authenticated by the right signer")]
365 UnauthenticatedClaimOwner,
366 #[error("Admin operations are only allowed on the admin chain.")]
367 AdminOperationOnNonAdminChain,
368 #[error("Failed to create new committee: expected {expected}, but got {provided}")]
369 InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
370 #[error("Failed to remove committee")]
371 InvalidCommitteeRemoval,
372 #[error("No recorded response for oracle query")]
373 MissingOracleResponse,
374 #[error("process_streams was not called for all stream updates")]
375 UnprocessedStreams,
376 #[error("Internal error: {0}")]
377 InternalError(&'static str),
378 #[error("UpdateStreams is outdated")]
379 OutdatedUpdateStreams,
380}
381
382impl ExecutionError {
383 pub fn is_local(&self) -> bool {
387 match self {
388 ExecutionError::ArithmeticError(_)
389 | ExecutionError::UserError(_)
390 | ExecutionError::DecompressionError(_)
391 | ExecutionError::InvalidPromise
392 | ExecutionError::CrossApplicationCallInFinalize { .. }
393 | ExecutionError::ReentrantCall(_)
394 | ExecutionError::ApplicationBytecodeNotFound(_)
395 | ExecutionError::UnsupportedDynamicApplicationLoad(_)
396 | ExecutionError::ExcessiveRead
397 | ExecutionError::ExcessiveWrite
398 | ExecutionError::MaximumFuelExceeded(_)
399 | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
400 | ExecutionError::ServiceOracleResponseTooLarge
401 | ExecutionError::BlockTooLarge
402 | ExecutionError::HttpResponseSizeLimitExceeded { .. }
403 | ExecutionError::UnauthorizedApplication(_)
404 | ExecutionError::UnexpectedOracleResponse
405 | ExecutionError::JsonError(_)
406 | ExecutionError::BcsError(_)
407 | ExecutionError::OracleResponseMismatch
408 | ExecutionError::ServiceOracleQueryOperations(_)
409 | ExecutionError::AssertBefore { .. }
410 | ExecutionError::StreamNameTooLong
411 | ExecutionError::BlobTooLarge
412 | ExecutionError::BytecodeTooLarge
413 | ExecutionError::UnauthorizedHttpRequest(_)
414 | ExecutionError::InvalidUrlForHttpRequest(_)
415 | ExecutionError::BlobsNotFound(_)
416 | ExecutionError::EventsNotFound(_)
417 | ExecutionError::InvalidHeaderName(_)
418 | ExecutionError::InvalidHeaderValue(_)
419 | ExecutionError::InvalidEpoch { .. }
420 | ExecutionError::IncorrectTransferAmount
421 | ExecutionError::UnauthenticatedTransferOwner
422 | ExecutionError::InsufficientBalance { .. }
423 | ExecutionError::FeesExceedFunding { .. }
424 | ExecutionError::IncorrectClaimAmount
425 | ExecutionError::UnauthenticatedClaimOwner
426 | ExecutionError::AdminOperationOnNonAdminChain
427 | ExecutionError::InvalidCommitteeEpoch { .. }
428 | ExecutionError::InvalidCommitteeRemoval
429 | ExecutionError::MissingOracleResponse
430 | ExecutionError::UnprocessedStreams
431 | ExecutionError::OutdatedUpdateStreams
432 | ExecutionError::ViewError(ViewError::NotFound(_)) => false,
433 #[cfg(with_wasm_runtime)]
434 ExecutionError::WasmError(_) => false,
435 #[cfg(with_revm)]
436 ExecutionError::EvmError(..) => false,
437 ExecutionError::MissingRuntimeResponse
438 | ExecutionError::ViewError(_)
439 | ExecutionError::ReqwestError(_)
440 | ExecutionError::Thread(_)
441 | ExecutionError::NoNetworkDescriptionFound
442 | ExecutionError::InternalError(_)
443 | ExecutionError::IoError(_) => true,
444 }
445 }
446
447 pub fn error_type(&self) -> String {
450 let variant: &'static str = self.into();
451 format!("ExecutionError::{variant}")
452 }
453
454 pub fn is_limit_error(&self) -> bool {
459 matches!(
460 self,
461 ExecutionError::ExcessiveRead
462 | ExecutionError::ExcessiveWrite
463 | ExecutionError::MaximumFuelExceeded(_)
464 | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
465 | ExecutionError::BlockTooLarge
466 )
467 }
468
469 pub fn is_transient_error(&self) -> bool {
475 matches!(
476 self,
477 ExecutionError::BlobsNotFound(_) | ExecutionError::EventsNotFound(_)
478 )
479 }
480}
481
482pub trait UserContract {
484 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
486
487 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
489
490 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
492
493 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
495
496 fn finalize(&mut self) -> Result<(), ExecutionError>;
498}
499
500pub trait UserService {
502 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
504}
505
506#[derive(Clone, Copy)]
508pub struct ExecutionRuntimeConfig {
509 pub allow_application_logs: bool,
512}
513
514impl Default for ExecutionRuntimeConfig {
515 fn default() -> Self {
516 Self {
517 allow_application_logs: true,
518 }
519 }
520}
521
522#[cfg_attr(not(web), async_trait)]
525#[cfg_attr(web, async_trait(?Send))]
526pub trait ExecutionRuntimeContext {
527 fn chain_id(&self) -> ChainId;
528
529 fn thread_pool(&self) -> &Arc<ThreadPool>;
530
531 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
532
533 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>>;
534
535 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>>;
536
537 async fn get_user_contract(
538 &self,
539 description: &ApplicationDescription,
540 txn_tracker: &TransactionTracker,
541 ) -> Result<UserContractCode, ExecutionError>;
542
543 async fn get_user_service(
544 &self,
545 description: &ApplicationDescription,
546 txn_tracker: &TransactionTracker,
547 ) -> Result<UserServiceCode, ExecutionError>;
548
549 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError>;
550
551 async fn get_event(&self, event_id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError>;
552
553 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
554
555 async fn get_committees(
559 &self,
560 epoch_range: RangeInclusive<Epoch>,
561 ) -> Result<BTreeMap<Epoch, Committee>, ExecutionError> {
562 let mut committees = BTreeMap::new();
563 let mut missing = Vec::new();
564 for index in epoch_range.start().0..=epoch_range.end().0 {
565 let epoch = Epoch(index);
566 match self.get_or_load_committee(epoch).await? {
567 Some(committee) => {
568 committees.insert(epoch, (*committee).clone());
569 }
570 None => missing.push(epoch),
571 }
572 }
573 if !missing.is_empty() {
574 let net_description = self
575 .get_network_description()
576 .await?
577 .ok_or(ExecutionError::NoNetworkDescriptionFound)?;
578 let event_ids = missing
579 .into_iter()
580 .map(|epoch| EventId {
581 chain_id: net_description.admin_chain_id,
582 stream_id: StreamId::system(EPOCH_STREAM_NAME),
583 index: epoch.0,
584 })
585 .collect();
586 return Err(ExecutionError::EventsNotFound(event_ids));
587 }
588 Ok(committees)
589 }
590
591 async fn get_or_load_committee(
604 &self,
605 epoch: Epoch,
606 ) -> Result<Option<Arc<Committee>>, ViewError>;
607
608 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
609
610 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
611
612 #[cfg(with_testing)]
613 async fn add_blobs(
614 &self,
615 blobs: impl IntoIterator<Item = Blob> + Send,
616 ) -> Result<(), ViewError>;
617
618 #[cfg(with_testing)]
619 async fn add_events(
620 &self,
621 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
622 ) -> Result<(), ViewError>;
623}
624
625#[derive(Clone, Copy, Debug)]
626pub struct OperationContext {
627 pub chain_id: ChainId,
629 #[debug(skip_if = Option::is_none)]
631 pub authenticated_signer: Option<AccountOwner>,
632 pub height: BlockHeight,
634 pub round: Option<u32>,
636 pub timestamp: Timestamp,
638}
639
640#[derive(Clone, Copy, Debug)]
641pub struct MessageContext {
642 pub chain_id: ChainId,
644 pub origin: ChainId,
646 pub is_bouncing: bool,
648 #[debug(skip_if = Option::is_none)]
650 pub authenticated_signer: Option<AccountOwner>,
651 #[debug(skip_if = Option::is_none)]
653 pub refund_grant_to: Option<Account>,
654 pub height: BlockHeight,
656 pub round: Option<u32>,
658 pub timestamp: Timestamp,
660}
661
662#[derive(Clone, Copy, Debug)]
663pub struct ProcessStreamsContext {
664 pub chain_id: ChainId,
666 pub height: BlockHeight,
668 pub round: Option<u32>,
670 pub timestamp: Timestamp,
672}
673
674impl From<MessageContext> for ProcessStreamsContext {
675 fn from(context: MessageContext) -> Self {
676 Self {
677 chain_id: context.chain_id,
678 height: context.height,
679 round: context.round,
680 timestamp: context.timestamp,
681 }
682 }
683}
684
685impl From<OperationContext> for ProcessStreamsContext {
686 fn from(context: OperationContext) -> Self {
687 Self {
688 chain_id: context.chain_id,
689 height: context.height,
690 round: context.round,
691 timestamp: context.timestamp,
692 }
693 }
694}
695
696#[derive(Clone, Copy, Debug)]
697pub struct FinalizeContext {
698 pub chain_id: ChainId,
700 #[debug(skip_if = Option::is_none)]
702 pub authenticated_signer: Option<AccountOwner>,
703 pub height: BlockHeight,
705 pub round: Option<u32>,
707}
708
709#[derive(Clone, Copy, Debug, Eq, PartialEq)]
710pub struct QueryContext {
711 pub chain_id: ChainId,
713 pub next_block_height: BlockHeight,
715 pub local_time: Timestamp,
717}
718
719pub trait BaseRuntime {
720 type Read: fmt::Debug + Send + Sync;
721 type ContainsKey: fmt::Debug + Send + Sync;
722 type ContainsKeys: fmt::Debug + Send + Sync;
723 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
724 type ReadValueBytes: fmt::Debug + Send + Sync;
725 type FindKeysByPrefix: fmt::Debug + Send + Sync;
726 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
727
728 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
730
731 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
733
734 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
736
737 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
739
740 fn read_application_description(
742 &mut self,
743 application_id: ApplicationId,
744 ) -> Result<ApplicationDescription, ExecutionError>;
745
746 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
748
749 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
751
752 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
754
755 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
757
758 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
760
761 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
763
764 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
766
767 fn application_permissions(&mut self) -> Result<ApplicationPermissions, ExecutionError>;
769
770 #[cfg(feature = "test")]
772 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
773 let promise = self.contains_key_new(key)?;
774 self.contains_key_wait(&promise)
775 }
776
777 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
779
780 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
782
783 #[cfg(feature = "test")]
785 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
786 let promise = self.contains_keys_new(keys)?;
787 self.contains_keys_wait(&promise)
788 }
789
790 fn contains_keys_new(
792 &mut self,
793 keys: Vec<Vec<u8>>,
794 ) -> Result<Self::ContainsKeys, ExecutionError>;
795
796 fn contains_keys_wait(
798 &mut self,
799 promise: &Self::ContainsKeys,
800 ) -> Result<Vec<bool>, ExecutionError>;
801
802 #[cfg(feature = "test")]
804 fn read_multi_values_bytes(
805 &mut self,
806 keys: Vec<Vec<u8>>,
807 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
808 let promise = self.read_multi_values_bytes_new(keys)?;
809 self.read_multi_values_bytes_wait(&promise)
810 }
811
812 fn read_multi_values_bytes_new(
814 &mut self,
815 keys: Vec<Vec<u8>>,
816 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
817
818 fn read_multi_values_bytes_wait(
820 &mut self,
821 promise: &Self::ReadMultiValuesBytes,
822 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
823
824 #[cfg(feature = "test")]
826 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
827 let promise = self.read_value_bytes_new(key)?;
828 self.read_value_bytes_wait(&promise)
829 }
830
831 fn read_value_bytes_new(
833 &mut self,
834 key: Vec<u8>,
835 ) -> Result<Self::ReadValueBytes, ExecutionError>;
836
837 fn read_value_bytes_wait(
839 &mut self,
840 promise: &Self::ReadValueBytes,
841 ) -> Result<Option<Vec<u8>>, ExecutionError>;
842
843 fn find_keys_by_prefix_new(
845 &mut self,
846 key_prefix: Vec<u8>,
847 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
848
849 fn find_keys_by_prefix_wait(
851 &mut self,
852 promise: &Self::FindKeysByPrefix,
853 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
854
855 #[cfg(feature = "test")]
857 #[expect(clippy::type_complexity)]
858 fn find_key_values_by_prefix(
859 &mut self,
860 key_prefix: Vec<u8>,
861 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
862 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
863 self.find_key_values_by_prefix_wait(&promise)
864 }
865
866 fn find_key_values_by_prefix_new(
868 &mut self,
869 key_prefix: Vec<u8>,
870 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
871
872 #[expect(clippy::type_complexity)]
874 fn find_key_values_by_prefix_wait(
875 &mut self,
876 promise: &Self::FindKeyValuesByPrefix,
877 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
878
879 fn perform_http_request(
881 &mut self,
882 request: http::Request,
883 ) -> Result<http::Response, ExecutionError>;
884
885 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
891
892 fn read_data_blob(&mut self, hash: DataBlobHash) -> Result<Vec<u8>, ExecutionError>;
894
895 fn assert_data_blob_exists(&mut self, hash: DataBlobHash) -> Result<(), ExecutionError>;
897
898 fn allow_application_logs(&mut self) -> Result<bool, ExecutionError>;
901
902 #[cfg(web)]
905 fn send_log(&mut self, message: String, level: tracing::log::Level);
906}
907
908pub trait ServiceRuntime: BaseRuntime {
909 fn try_query_application(
911 &mut self,
912 queried_id: ApplicationId,
913 argument: Vec<u8>,
914 ) -> Result<Vec<u8>, ExecutionError>;
915
916 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
918
919 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
921}
922
923pub trait ContractRuntime: BaseRuntime {
924 fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
926
927 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
930
931 fn message_origin_chain_id(&mut self) -> Result<Option<ChainId>, ExecutionError>;
933
934 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
937
938 fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
940
941 fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
943
944 fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
946
947 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
949
950 fn transfer(
952 &mut self,
953 source: AccountOwner,
954 destination: Account,
955 amount: Amount,
956 ) -> Result<(), ExecutionError>;
957
958 fn claim(
960 &mut self,
961 source: Account,
962 destination: Account,
963 amount: Amount,
964 ) -> Result<(), ExecutionError>;
965
966 fn try_call_application(
969 &mut self,
970 authenticated: bool,
971 callee_id: ApplicationId,
972 argument: Vec<u8>,
973 ) -> Result<Vec<u8>, ExecutionError>;
974
975 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
977
978 fn read_event(
982 &mut self,
983 chain_id: ChainId,
984 stream_name: StreamName,
985 index: u32,
986 ) -> Result<Vec<u8>, ExecutionError>;
987
988 fn subscribe_to_events(
990 &mut self,
991 chain_id: ChainId,
992 application_id: ApplicationId,
993 stream_name: StreamName,
994 ) -> Result<(), ExecutionError>;
995
996 fn unsubscribe_from_events(
998 &mut self,
999 chain_id: ChainId,
1000 application_id: ApplicationId,
1001 stream_name: StreamName,
1002 ) -> Result<(), ExecutionError>;
1003
1004 fn query_service(
1006 &mut self,
1007 application_id: ApplicationId,
1008 query: Vec<u8>,
1009 ) -> Result<Vec<u8>, ExecutionError>;
1010
1011 fn open_chain(
1013 &mut self,
1014 ownership: ChainOwnership,
1015 application_permissions: ApplicationPermissions,
1016 balance: Amount,
1017 ) -> Result<ChainId, ExecutionError>;
1018
1019 fn close_chain(&mut self) -> Result<(), ExecutionError>;
1021
1022 fn change_ownership(&mut self, ownership: ChainOwnership) -> Result<(), ExecutionError>;
1024
1025 fn change_application_permissions(
1027 &mut self,
1028 application_permissions: ApplicationPermissions,
1029 ) -> Result<(), ExecutionError>;
1030
1031 fn create_application(
1033 &mut self,
1034 module_id: ModuleId,
1035 parameters: Vec<u8>,
1036 argument: Vec<u8>,
1037 required_application_ids: Vec<ApplicationId>,
1038 ) -> Result<ApplicationId, ExecutionError>;
1039
1040 fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<DataBlobHash, ExecutionError>;
1042
1043 fn publish_module(
1045 &mut self,
1046 contract: Bytecode,
1047 service: Bytecode,
1048 vm_runtime: VmRuntime,
1049 ) -> Result<ModuleId, ExecutionError>;
1050
1051 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
1053
1054 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
1056}
1057
1058#[derive(
1060 Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative, strum::AsRefStr,
1061)]
1062pub enum Operation {
1063 System(Box<SystemOperation>),
1065 User {
1067 application_id: ApplicationId,
1068 #[serde(with = "serde_bytes")]
1069 #[debug(with = "hex_debug")]
1070 bytes: Vec<u8>,
1071 },
1072}
1073
1074impl BcsHashable<'_> for Operation {}
1075
1076#[derive(
1078 Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative, strum::AsRefStr,
1079)]
1080pub enum Message {
1081 System(SystemMessage),
1083 User {
1085 application_id: ApplicationId,
1086 #[serde(with = "serde_bytes")]
1087 #[debug(with = "hex_debug")]
1088 bytes: Vec<u8>,
1089 },
1090}
1091
1092#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1094pub enum Query {
1095 System(SystemQuery),
1097 User {
1099 application_id: ApplicationId,
1100 #[serde(with = "serde_bytes")]
1101 #[debug(with = "hex_debug")]
1102 bytes: Vec<u8>,
1103 },
1104}
1105
1106#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1108pub struct QueryOutcome<Response = QueryResponse> {
1109 pub response: Response,
1110 pub operations: Vec<Operation>,
1111}
1112
1113impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
1114 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
1115 let QueryOutcome {
1116 response,
1117 operations,
1118 } = system_outcome;
1119
1120 QueryOutcome {
1121 response: QueryResponse::System(response),
1122 operations,
1123 }
1124 }
1125}
1126
1127impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
1128 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
1129 let QueryOutcome {
1130 response,
1131 operations,
1132 } = user_service_outcome;
1133
1134 QueryOutcome {
1135 response: QueryResponse::User(response),
1136 operations,
1137 }
1138 }
1139}
1140
1141#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1143pub enum QueryResponse {
1144 System(SystemResponse),
1146 User(
1148 #[serde(with = "serde_bytes")]
1149 #[debug(with = "hex_debug")]
1150 Vec<u8>,
1151 ),
1152}
1153
1154#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy, Allocative)]
1156pub enum MessageKind {
1157 Simple,
1159 Protected,
1162 Tracked,
1165 Bouncing,
1167}
1168
1169impl Display for MessageKind {
1170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1171 match self {
1172 MessageKind::Simple => write!(f, "Simple"),
1173 MessageKind::Protected => write!(f, "Protected"),
1174 MessageKind::Tracked => write!(f, "Tracked"),
1175 MessageKind::Bouncing => write!(f, "Bouncing"),
1176 }
1177 }
1178}
1179
1180#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1182pub struct OutgoingMessage {
1183 pub destination: ChainId,
1185 #[debug(skip_if = Option::is_none)]
1187 pub authenticated_signer: Option<AccountOwner>,
1188 #[debug(skip_if = Amount::is_zero)]
1190 pub grant: Amount,
1191 #[debug(skip_if = Option::is_none)]
1193 pub refund_grant_to: Option<Account>,
1194 pub kind: MessageKind,
1196 pub message: Message,
1198}
1199
1200impl BcsHashable<'_> for OutgoingMessage {}
1201
1202impl OutgoingMessage {
1203 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
1205 OutgoingMessage {
1206 destination: recipient,
1207 authenticated_signer: None,
1208 grant: Amount::ZERO,
1209 refund_grant_to: None,
1210 kind: MessageKind::Simple,
1211 message: message.into(),
1212 }
1213 }
1214
1215 pub fn with_kind(mut self, kind: MessageKind) -> Self {
1217 self.kind = kind;
1218 self
1219 }
1220
1221 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
1223 self.authenticated_signer = authenticated_signer;
1224 self
1225 }
1226}
1227
1228impl OperationContext {
1229 fn refund_grant_to(&self) -> Option<Account> {
1232 self.authenticated_signer.map(|owner| Account {
1233 chain_id: self.chain_id,
1234 owner,
1235 })
1236 }
1237}
1238
1239#[cfg(with_testing)]
1240#[derive(Clone)]
1241pub struct TestExecutionRuntimeContext {
1242 chain_id: ChainId,
1243 thread_pool: Arc<ThreadPool>,
1244 execution_runtime_config: ExecutionRuntimeConfig,
1245 user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
1246 user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
1247 blobs: Arc<papaya::HashMap<BlobId, Blob>>,
1248 events: Arc<papaya::HashMap<EventId, Vec<u8>>>,
1249}
1250
1251#[cfg(with_testing)]
1252impl TestExecutionRuntimeContext {
1253 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1254 Self {
1255 chain_id,
1256 thread_pool: Arc::new(ThreadPool::new(20)),
1257 execution_runtime_config,
1258 user_contracts: Arc::default(),
1259 user_services: Arc::default(),
1260 blobs: Arc::default(),
1261 events: Arc::default(),
1262 }
1263 }
1264}
1265
1266#[cfg(with_testing)]
1267#[cfg_attr(not(web), async_trait)]
1268#[cfg_attr(web, async_trait(?Send))]
1269impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1270 fn chain_id(&self) -> ChainId {
1271 self.chain_id
1272 }
1273
1274 fn thread_pool(&self) -> &Arc<ThreadPool> {
1275 &self.thread_pool
1276 }
1277
1278 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1279 self.execution_runtime_config
1280 }
1281
1282 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>> {
1283 &self.user_contracts
1284 }
1285
1286 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>> {
1287 &self.user_services
1288 }
1289
1290 async fn get_user_contract(
1291 &self,
1292 description: &ApplicationDescription,
1293 _txn_tracker: &TransactionTracker,
1294 ) -> Result<UserContractCode, ExecutionError> {
1295 let application_id: ApplicationId = description.into();
1296 let pinned = self.user_contracts().pin();
1297 Ok(pinned
1298 .get(&application_id)
1299 .ok_or_else(|| {
1300 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1301 })?
1302 .clone())
1303 }
1304
1305 async fn get_user_service(
1306 &self,
1307 description: &ApplicationDescription,
1308 _txn_tracker: &TransactionTracker,
1309 ) -> Result<UserServiceCode, ExecutionError> {
1310 let application_id: ApplicationId = description.into();
1311 let pinned = self.user_services().pin();
1312 Ok(pinned
1313 .get(&application_id)
1314 .ok_or_else(|| {
1315 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1316 })?
1317 .clone())
1318 }
1319
1320 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError> {
1321 Ok(self.blobs.pin().get(&blob_id).cloned().map(Arc::new))
1322 }
1323
1324 async fn get_event(&self, event_id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError> {
1325 Ok(self.events.pin().get(&event_id).cloned().map(Arc::new))
1326 }
1327
1328 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1329 let pinned = self.blobs.pin();
1330 let genesis_committee_blob_hash = pinned
1331 .iter()
1332 .find(|(_, blob)| blob.content().blob_type() == BlobType::Committee)
1333 .map_or_else(
1334 || CryptoHash::test_hash("genesis committee"),
1335 |(_, blob)| blob.id().hash,
1336 );
1337 Ok(Some(NetworkDescription {
1338 admin_chain_id: dummy_chain_description(0).id(),
1339 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1340 genesis_timestamp: Timestamp::from(0),
1341 genesis_committee_blob_hash,
1342 name: "dummy network description".to_string(),
1343 }))
1344 }
1345
1346 async fn get_or_load_committee(
1347 &self,
1348 epoch: Epoch,
1349 ) -> Result<Option<Arc<Committee>>, ViewError> {
1350 let Some(net_description) = self.get_network_description().await? else {
1354 return Ok(None);
1355 };
1356 let blob_hash = if epoch.0 == 0 {
1357 net_description.genesis_committee_blob_hash
1358 } else {
1359 let event_id = EventId {
1360 chain_id: net_description.admin_chain_id,
1361 stream_id: StreamId::system(EPOCH_STREAM_NAME),
1362 index: epoch.0,
1363 };
1364 match self.get_event(event_id).await? {
1365 Some(bytes) => bcs::from_bytes(&bytes)?,
1366 None => return Ok(None),
1367 }
1368 };
1369 let blob_id = BlobId::new(blob_hash, BlobType::Committee);
1370 let Some(blob) = self.get_blob(blob_id).await? else {
1371 return Ok(None);
1372 };
1373 let committee: Committee = bcs::from_bytes(blob.bytes())?;
1374 Ok(Some(Arc::new(committee)))
1375 }
1376
1377 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1378 Ok(self.blobs.pin().contains_key(&blob_id))
1379 }
1380
1381 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1382 Ok(self.events.pin().contains_key(&event_id))
1383 }
1384
1385 #[cfg(with_testing)]
1386 async fn add_blobs(
1387 &self,
1388 blobs: impl IntoIterator<Item = Blob> + Send,
1389 ) -> Result<(), ViewError> {
1390 let pinned = self.blobs.pin();
1391 for blob in blobs {
1392 pinned.insert(blob.id(), blob);
1393 }
1394
1395 Ok(())
1396 }
1397
1398 #[cfg(with_testing)]
1399 async fn add_events(
1400 &self,
1401 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1402 ) -> Result<(), ViewError> {
1403 let pinned = self.events.pin();
1404 for (event_id, bytes) in events {
1405 pinned.insert(event_id, bytes);
1406 }
1407
1408 Ok(())
1409 }
1410}
1411
1412impl From<SystemOperation> for Operation {
1413 fn from(operation: SystemOperation) -> Self {
1414 Operation::System(Box::new(operation))
1415 }
1416}
1417
1418impl Operation {
1419 pub fn system(operation: SystemOperation) -> Self {
1420 Operation::System(Box::new(operation))
1421 }
1422
1423 #[cfg(with_testing)]
1425 pub fn user<A: Abi>(
1426 application_id: ApplicationId<A>,
1427 operation: &A::Operation,
1428 ) -> Result<Self, bcs::Error> {
1429 Self::user_without_abi(application_id.forget_abi(), operation)
1430 }
1431
1432 #[cfg(with_testing)]
1435 pub fn user_without_abi(
1436 application_id: ApplicationId,
1437 operation: &impl Serialize,
1438 ) -> Result<Self, bcs::Error> {
1439 Ok(Operation::User {
1440 application_id,
1441 bytes: bcs::to_bytes(&operation)?,
1442 })
1443 }
1444
1445 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1448 match self {
1449 Operation::System(system_operation) => Some(system_operation),
1450 Operation::User { .. } => None,
1451 }
1452 }
1453
1454 pub fn application_id(&self) -> GenericApplicationId {
1455 match self {
1456 Self::System(_) => GenericApplicationId::System,
1457 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1458 }
1459 }
1460
1461 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1463 match self.as_system_operation() {
1464 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1465 vec![BlobId::new(*blob_hash, BlobType::Data)]
1466 }
1467 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1468 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1469 }
1470 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1471 _ => vec![],
1472 }
1473 }
1474
1475 pub fn is_exempt_from_permissions(&self) -> bool {
1477 let Operation::System(system_op) = self else {
1478 return false;
1479 };
1480 matches!(
1481 **system_op,
1482 SystemOperation::ProcessNewEpoch(_)
1483 | SystemOperation::ProcessRemovedEpoch(_)
1484 | SystemOperation::UpdateStreams(_)
1485 )
1486 }
1487}
1488
1489impl From<SystemMessage> for Message {
1490 fn from(message: SystemMessage) -> Self {
1491 Message::System(message)
1492 }
1493}
1494
1495impl Message {
1496 pub fn system(message: SystemMessage) -> Self {
1497 Message::System(message)
1498 }
1499
1500 pub fn user<A, M: Serialize>(
1503 application_id: ApplicationId<A>,
1504 message: &M,
1505 ) -> Result<Self, bcs::Error> {
1506 let application_id = application_id.forget_abi();
1507 let bytes = bcs::to_bytes(&message)?;
1508 Ok(Message::User {
1509 application_id,
1510 bytes,
1511 })
1512 }
1513
1514 pub fn application_id(&self) -> GenericApplicationId {
1515 match self {
1516 Self::System(_) => GenericApplicationId::System,
1517 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1518 }
1519 }
1520}
1521
1522impl From<SystemQuery> for Query {
1523 fn from(query: SystemQuery) -> Self {
1524 Query::System(query)
1525 }
1526}
1527
1528impl Query {
1529 pub fn system(query: SystemQuery) -> Self {
1530 Query::System(query)
1531 }
1532
1533 pub fn user<A: Abi>(
1535 application_id: ApplicationId<A>,
1536 query: &A::Query,
1537 ) -> Result<Self, serde_json::Error> {
1538 Self::user_without_abi(application_id.forget_abi(), query)
1539 }
1540
1541 pub fn user_without_abi(
1544 application_id: ApplicationId,
1545 query: &impl Serialize,
1546 ) -> Result<Self, serde_json::Error> {
1547 Ok(Query::User {
1548 application_id,
1549 bytes: serde_json::to_vec(&query)?,
1550 })
1551 }
1552
1553 pub fn application_id(&self) -> GenericApplicationId {
1554 match self {
1555 Self::System(_) => GenericApplicationId::System,
1556 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1557 }
1558 }
1559}
1560
1561impl From<SystemResponse> for QueryResponse {
1562 fn from(response: SystemResponse) -> Self {
1563 QueryResponse::System(response)
1564 }
1565}
1566
1567impl From<Vec<u8>> for QueryResponse {
1568 fn from(response: Vec<u8>) -> Self {
1569 QueryResponse::User(response)
1570 }
1571}
1572
1573#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1575pub struct BlobState {
1576 pub last_used_by: Option<CryptoHash>,
1580 pub chain_id: ChainId,
1582 pub block_height: BlockHeight,
1584 pub epoch: Option<Epoch>,
1586}
1587
1588#[derive(Clone, Copy, Display)]
1590#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1591pub enum WasmRuntime {
1592 #[cfg(with_wasmer)]
1593 #[default]
1594 #[display("wasmer")]
1595 Wasmer,
1596 #[cfg(with_wasmtime)]
1597 #[cfg_attr(not(with_wasmer), default)]
1598 #[display("wasmtime")]
1599 Wasmtime,
1600}
1601
1602#[derive(Clone, Copy, Display)]
1603#[cfg_attr(with_revm, derive(Debug, Default))]
1604pub enum EvmRuntime {
1605 #[cfg(with_revm)]
1606 #[default]
1607 #[display("revm")]
1608 Revm,
1609}
1610
1611pub trait WithWasmDefault {
1613 fn with_wasm_default(self) -> Self;
1614}
1615
1616impl WithWasmDefault for Option<WasmRuntime> {
1617 fn with_wasm_default(self) -> Self {
1618 #[cfg(with_wasm_runtime)]
1619 {
1620 Some(self.unwrap_or_default())
1621 }
1622 #[cfg(not(with_wasm_runtime))]
1623 {
1624 None
1625 }
1626 }
1627}
1628
1629impl FromStr for WasmRuntime {
1630 type Err = InvalidWasmRuntime;
1631
1632 fn from_str(string: &str) -> Result<Self, Self::Err> {
1633 match string {
1634 #[cfg(with_wasmer)]
1635 "wasmer" => Ok(WasmRuntime::Wasmer),
1636 #[cfg(with_wasmtime)]
1637 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1638 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1639 }
1640 }
1641}
1642
1643#[derive(Clone, Debug, Error)]
1645#[error("{0:?} is not a valid WebAssembly runtime")]
1646pub struct InvalidWasmRuntime(String);
1647
1648doc_scalar!(Operation, "An operation to be executed in a block");
1649doc_scalar!(
1650 Message,
1651 "A message to be sent and possibly executed in the receiver's block."
1652);
1653doc_scalar!(MessageKind, "The kind of outgoing message being sent");