1#![deny(missing_docs)]
8
9pub mod committee;
11pub mod evm;
12mod execution;
13pub mod execution_state_actor;
14#[cfg(with_graphql)]
15mod graphql;
16mod policy;
17mod resources;
18mod runtime;
19pub mod system;
21#[cfg(with_testing)]
23pub mod test_utils;
24mod transaction_tracker;
25mod util;
26mod wasm;
27
28use std::{any::Any, collections::BTreeMap, fmt, ops::RangeInclusive, str::FromStr, sync::Arc};
29
30use allocative::Allocative;
31use async_graphql::SimpleObject;
32use async_trait::async_trait;
33use custom_debug_derive::Debug;
34use derive_more::Display;
35#[cfg(web)]
36use js_sys::wasm_bindgen::JsValue;
37use linera_base::{
38 abi::Abi,
39 crypto::{BcsHashable, CryptoHash},
40 data_types::{
41 Amount, ApplicationDescription, ApplicationPermissions, ArithmeticError, Blob, BlockHeight,
42 Bytecode, DecompressionError, Epoch, NetworkDescription, SendMessageRequest, StreamUpdate,
43 Timestamp,
44 },
45 doc_scalar, hex_debug, http,
46 identifiers::{
47 Account, AccountOwner, ApplicationId, BlobId, BlobType, ChainId, DataBlobHash, EventId,
48 GenericApplicationId, ModuleId, StreamId, StreamName,
49 },
50 ownership::ChainOwnership,
51 vm::VmRuntime,
52};
53use linera_views::{batch::Batch, ViewError};
54use serde::{Deserialize, Serialize};
55use system::AdminOperation;
56use thiserror::Error;
57pub use web_thread_pool::Pool as ThreadPool;
58use web_thread_select as web_thread;
59
60#[cfg(with_revm)]
61use crate::evm::EvmExecutionError;
62use crate::system::EPOCH_STREAM_NAME;
63#[cfg(with_testing)]
64use crate::test_utils::dummy_chain_description;
65#[cfg(all(with_testing, with_wasm_runtime))]
66pub use crate::wasm::test as wasm_test;
67#[cfg(with_wasm_runtime)]
68pub use crate::wasm::{
69 BaseRuntimeApi, ContractEntrypoints, ContractRuntimeApi, RuntimeApiData, ServiceEntrypoints,
70 ServiceRuntimeApi, WasmContractModule, WasmExecutionError, WasmServiceModule,
71};
72pub use crate::{
73 committee::{Committee, SharedCommittees},
74 execution::{ExecutionStateView, ServiceRuntimeEndpoint},
75 execution_state_actor::{ExecutionRequest, ExecutionStateActor},
76 policy::ResourceControlPolicy,
77 resources::{BalanceHolder, ResourceController, ResourceTracker},
78 runtime::{
79 ContractSyncRuntimeHandle, ServiceRuntimeRequest, ServiceSyncRuntime,
80 ServiceSyncRuntimeHandle,
81 },
82 system::{
83 SystemExecutionStateView, SystemMessage, SystemOperation, SystemQuery, SystemResponse,
84 },
85 transaction_tracker::{TransactionOutcome, TransactionTracker},
86};
87
88pub const LINERA_SOL: &str = include_str!("../solidity/Linera.sol");
91pub const LINERA_TYPES_SOL: &str = include_str!("../solidity/LineraTypes.sol");
94
95const MAX_STREAM_NAME_LEN: usize = 64;
97
98pub const FLAG_ZERO_HASH: &str = "FLAG_ZERO_HASH.linera.network";
103pub const FLAG_HISTORICAL_HASH: &str = "FLAG_HISTORICAL_HASH.linera.network";
110pub const FLAG_HISTORICAL_HASH_SHADOW: &str = "FLAG_HISTORICAL_HASH_SHADOW.linera.network";
116pub const FLAG_FREE_REJECT: &str = "FLAG_FREE_REJECT.linera.network";
120pub const FLAG_MANDATORY_APPS_NEED_ACCEPTED_MESSAGE: &str =
124 "FLAG_MANDATORY_APPS_NEED_ACCEPTED_MESSAGE.linera.network";
125pub const FLAG_FREE_APPLICATION_ID_PREFIX: &str = "FLAG_FREE_APPLICATION_ID_";
128pub const FLAG_FREE_APPLICATION_ID_SUFFIX: &str = ".linera.network";
130
131#[derive(Clone)]
133pub struct UserContractCode(Box<dyn UserContractModule>);
134
135#[derive(Clone)]
137pub struct UserServiceCode(Box<dyn UserServiceModule>);
138
139pub type UserContractInstance = Box<dyn UserContract>;
141
142pub type UserServiceInstance = Box<dyn UserService>;
144
145pub trait UserContractModule: dyn_clone::DynClone + Any + web_thread::Post + Send + Sync {
147 fn instantiate(
149 &self,
150 runtime: ContractSyncRuntimeHandle,
151 ) -> Result<UserContractInstance, ExecutionError>;
152}
153
154impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
155 fn from(module: T) -> Self {
156 Self(Box::new(module))
157 }
158}
159
160dyn_clone::clone_trait_object!(UserContractModule);
161
162pub trait UserServiceModule: dyn_clone::DynClone + Any + web_thread::Post + Send + Sync {
164 fn instantiate(
166 &self,
167 runtime: ServiceSyncRuntimeHandle,
168 ) -> Result<UserServiceInstance, ExecutionError>;
169}
170
171impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
172 fn from(module: T) -> Self {
173 Self(Box::new(module))
174 }
175}
176
177dyn_clone::clone_trait_object!(UserServiceModule);
178
179impl UserServiceCode {
180 fn instantiate(
181 &self,
182 runtime: ServiceSyncRuntimeHandle,
183 ) -> Result<UserServiceInstance, ExecutionError> {
184 self.0.instantiate(runtime)
185 }
186}
187
188impl UserContractCode {
189 fn instantiate(
190 &self,
191 runtime: ContractSyncRuntimeHandle,
192 ) -> Result<UserContractInstance, ExecutionError> {
193 self.0.instantiate(runtime)
194 }
195}
196
197pub struct JsVec<T>(pub Vec<T>);
199
200#[cfg(web)]
201const _: () = {
202 impl web_thread::AsJs for UserContractCode {
206 fn to_js(&self) -> Result<JsValue, JsValue> {
207 ((&*self.0) as &dyn Any)
208 .downcast_ref::<WasmContractModule>()
209 .expect("we only support Wasm modules on the Web for now")
210 .to_js()
211 }
212
213 fn from_js(value: JsValue) -> Result<Self, JsValue> {
214 WasmContractModule::from_js(value).map(Into::into)
215 }
216 }
217
218 impl web_thread::Post for UserContractCode {
219 fn transferables(&self) -> js_sys::Array {
220 self.0.transferables()
221 }
222 }
223
224 impl web_thread::AsJs for UserServiceCode {
225 fn to_js(&self) -> Result<JsValue, JsValue> {
226 ((&*self.0) as &dyn Any)
227 .downcast_ref::<WasmServiceModule>()
228 .expect("we only support Wasm modules on the Web for now")
229 .to_js()
230 }
231
232 fn from_js(value: JsValue) -> Result<Self, JsValue> {
233 WasmServiceModule::from_js(value).map(Into::into)
234 }
235 }
236
237 impl web_thread::Post for UserServiceCode {
238 fn transferables(&self) -> js_sys::Array {
239 self.0.transferables()
240 }
241 }
242
243 impl<T: web_thread::AsJs> web_thread::AsJs for JsVec<T> {
244 fn to_js(&self) -> Result<JsValue, JsValue> {
245 let array = self
246 .0
247 .iter()
248 .map(T::to_js)
249 .collect::<Result<js_sys::Array, _>>()?;
250 Ok(array.into())
251 }
252
253 fn from_js(value: JsValue) -> Result<Self, JsValue> {
254 let array = js_sys::Array::from(&value);
255 let v = array
256 .into_iter()
257 .map(T::from_js)
258 .collect::<Result<Vec<_>, _>>()?;
259 Ok(JsVec(v))
260 }
261 }
262
263 impl<T: web_thread::Post> web_thread::Post for JsVec<T> {
264 fn transferables(&self) -> js_sys::Array {
265 let mut array = js_sys::Array::new();
266 for x in &self.0 {
267 array = array.concat(&x.transferables());
268 }
269 array
270 }
271 }
272};
273
274#[derive(Error, Debug, strum::IntoStaticStr)]
276#[allow(missing_docs)]
277pub enum ExecutionError {
278 #[error(transparent)]
279 ViewError(#[from] ViewError),
280 #[error(transparent)]
281 ArithmeticError(#[from] ArithmeticError),
282 #[error("User application reported an error: {0}")]
283 UserError(String),
284 #[cfg(with_wasm_runtime)]
285 #[error(transparent)]
286 WasmError(#[from] WasmExecutionError),
287 #[cfg(with_revm)]
288 #[error(transparent)]
289 EvmError(#[from] EvmExecutionError),
290 #[error(transparent)]
291 DecompressionError(#[from] DecompressionError),
292 #[error("The given promise is invalid or was polled once already")]
293 InvalidPromise,
294
295 #[error("Attempted to perform a reentrant call to application {0}")]
296 ReentrantCall(ApplicationId),
297 #[error(
298 "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
299 from `finalize`"
300 )]
301 CrossApplicationCallInFinalize {
302 caller_id: Box<ApplicationId>,
303 callee_id: Box<ApplicationId>,
304 },
305 #[error("Failed to load bytecode from storage {0:?}")]
306 ApplicationBytecodeNotFound(Box<ApplicationDescription>),
307 #[error("Unsupported dynamic application load: {0:?}")]
309 UnsupportedDynamicApplicationLoad(Box<ApplicationId>),
310
311 #[error("Excessive number of bytes read from storage")]
312 ExcessiveRead,
313 #[error("Excessive number of bytes written to storage")]
314 ExcessiveWrite,
315 #[error("Block execution required too much fuel for VM {0}")]
316 MaximumFuelExceeded(VmRuntime),
317 #[error("Services running as oracles in block took longer than allowed")]
318 MaximumServiceOracleExecutionTimeExceeded,
319 #[error("Service running as an oracle produced a response that's too large")]
320 ServiceOracleResponseTooLarge,
321 #[error("Serialized size of the block exceeds limit")]
322 BlockTooLarge,
323 #[error("HTTP response exceeds the size limit of {limit} bytes, having at least {size} bytes")]
324 HttpResponseSizeLimitExceeded { limit: u64, size: u64 },
325 #[error("Runtime failed to respond to application")]
326 MissingRuntimeResponse,
327 #[error("Application is not authorized to perform system operations on this chain: {0:}")]
328 UnauthorizedApplication(ApplicationId),
329 #[error("Failed to make network reqwest: {0}")]
330 ReqwestError(#[from] reqwest::Error),
331 #[error("Encountered I/O error: {0}")]
332 IoError(#[from] std::io::Error),
333 #[error("More recorded oracle responses than expected")]
334 UnexpectedOracleResponse,
335 #[error("Invalid JSON: {0}")]
336 JsonError(#[from] serde_json::Error),
337 #[error(transparent)]
338 BcsError(#[from] bcs::Error),
339 #[error("Recorded response for oracle query has the wrong type")]
340 OracleResponseMismatch,
341 #[error("Service oracle query tried to create operations: {0:?}")]
342 ServiceOracleQueryOperations(Vec<Operation>),
343 #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
344 AssertBefore {
345 timestamp: Timestamp,
346 local_time: Timestamp,
347 },
348
349 #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
350 StreamNameTooLong,
351 #[error("Blob exceeds size limit")]
352 BlobTooLarge,
353 #[error("Bytecode exceeds size limit")]
354 BytecodeTooLarge,
355 #[error("Attempt to perform an HTTP request to an unauthorized host: {0:?}")]
356 UnauthorizedHttpRequest(reqwest::Url),
357 #[error("Attempt to perform an HTTP request to an invalid URL")]
358 InvalidUrlForHttpRequest(#[from] url::ParseError),
359 #[error("Worker thread failure: {0:?}")]
360 Thread(#[from] web_thread::Error),
361 #[error("Blobs not found: {0:?}")]
362 BlobsNotFound(Vec<BlobId>),
363 #[error("Events not found: {0:?}")]
364 EventsNotFound(Vec<EventId>),
365
366 #[error("Invalid HTTP header name used for HTTP request")]
367 InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
368 #[error("Invalid HTTP header value used for HTTP request")]
369 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
370
371 #[error("No NetworkDescription found in storage")]
372 NoNetworkDescriptionFound,
373 #[error("{epoch:?} is not recognized by chain {chain_id:}")]
374 InvalidEpoch { chain_id: ChainId, epoch: Epoch },
375 #[error("Transfer must have positive amount")]
376 IncorrectTransferAmount,
377 #[error("Transfer from owned account must be authenticated by the right signer")]
378 UnauthenticatedTransferOwner,
379 #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
380 InsufficientBalance {
381 balance: Amount,
382 account: AccountOwner,
383 },
384 #[error("Required execution fees exceeded the total funding available. Fees {fees}, available balance: {balance}")]
385 FeesExceedFunding { fees: Amount, balance: Amount },
386 #[error("Claim must have positive amount")]
387 IncorrectClaimAmount,
388 #[error("Claim must be authenticated by the right signer")]
389 UnauthenticatedClaimOwner,
390 #[error("Admin operations are only allowed on the admin chain.")]
391 AdminOperationOnNonAdminChain,
392 #[error("Failed to create new committee: expected {expected}, but got {provided}")]
393 InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
394 #[error("Failed to remove committee")]
395 InvalidCommitteeRemoval,
396 #[error("No recorded response for oracle query")]
397 MissingOracleResponse,
398 #[error("process_streams was not called for all stream updates")]
399 UnprocessedStreams,
400 #[error("Internal error: {0}")]
401 InternalError(&'static str),
402 #[error("UpdateStreams is outdated")]
403 OutdatedUpdateStreams,
404}
405
406impl ExecutionError {
407 pub fn is_local(&self) -> bool {
411 match self {
412 ExecutionError::ArithmeticError(_)
413 | ExecutionError::UserError(_)
414 | ExecutionError::DecompressionError(_)
415 | ExecutionError::InvalidPromise
416 | ExecutionError::CrossApplicationCallInFinalize { .. }
417 | ExecutionError::ReentrantCall(_)
418 | ExecutionError::ApplicationBytecodeNotFound(_)
419 | ExecutionError::UnsupportedDynamicApplicationLoad(_)
420 | ExecutionError::ExcessiveRead
421 | ExecutionError::ExcessiveWrite
422 | ExecutionError::MaximumFuelExceeded(_)
423 | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
424 | ExecutionError::ServiceOracleResponseTooLarge
425 | ExecutionError::BlockTooLarge
426 | ExecutionError::HttpResponseSizeLimitExceeded { .. }
427 | ExecutionError::UnauthorizedApplication(_)
428 | ExecutionError::UnexpectedOracleResponse
429 | ExecutionError::JsonError(_)
430 | ExecutionError::BcsError(_)
431 | ExecutionError::OracleResponseMismatch
432 | ExecutionError::ServiceOracleQueryOperations(_)
433 | ExecutionError::AssertBefore { .. }
434 | ExecutionError::StreamNameTooLong
435 | ExecutionError::BlobTooLarge
436 | ExecutionError::BytecodeTooLarge
437 | ExecutionError::UnauthorizedHttpRequest(_)
438 | ExecutionError::InvalidUrlForHttpRequest(_)
439 | ExecutionError::BlobsNotFound(_)
440 | ExecutionError::EventsNotFound(_)
441 | ExecutionError::InvalidHeaderName(_)
442 | ExecutionError::InvalidHeaderValue(_)
443 | ExecutionError::InvalidEpoch { .. }
444 | ExecutionError::IncorrectTransferAmount
445 | ExecutionError::UnauthenticatedTransferOwner
446 | ExecutionError::InsufficientBalance { .. }
447 | ExecutionError::FeesExceedFunding { .. }
448 | ExecutionError::IncorrectClaimAmount
449 | ExecutionError::UnauthenticatedClaimOwner
450 | ExecutionError::AdminOperationOnNonAdminChain
451 | ExecutionError::InvalidCommitteeEpoch { .. }
452 | ExecutionError::InvalidCommitteeRemoval
453 | ExecutionError::MissingOracleResponse
454 | ExecutionError::UnprocessedStreams
455 | ExecutionError::OutdatedUpdateStreams
456 | ExecutionError::ViewError(ViewError::NotFound(_)) => false,
457 #[cfg(with_wasm_runtime)]
458 ExecutionError::WasmError(_) => false,
459 #[cfg(with_revm)]
460 ExecutionError::EvmError(..) => false,
461 ExecutionError::MissingRuntimeResponse
462 | ExecutionError::ViewError(_)
463 | ExecutionError::ReqwestError(_)
464 | ExecutionError::Thread(_)
465 | ExecutionError::NoNetworkDescriptionFound
466 | ExecutionError::InternalError(_)
467 | ExecutionError::IoError(_) => true,
468 }
469 }
470
471 pub fn error_type(&self) -> String {
474 let variant: &'static str = self.into();
475 format!("ExecutionError::{variant}")
476 }
477
478 pub fn is_limit_error(&self) -> bool {
483 matches!(
484 self,
485 ExecutionError::ExcessiveRead
486 | ExecutionError::ExcessiveWrite
487 | ExecutionError::MaximumFuelExceeded(_)
488 | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
489 | ExecutionError::BlockTooLarge
490 )
491 }
492
493 pub fn is_transient_error(&self) -> bool {
499 matches!(
500 self,
501 ExecutionError::BlobsNotFound(_) | ExecutionError::EventsNotFound(_)
502 )
503 }
504}
505
506pub trait UserContract {
508 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
510
511 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
513
514 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
516
517 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
519
520 fn finalize(&mut self) -> Result<(), ExecutionError>;
522}
523
524pub trait UserService {
526 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
528}
529
530#[derive(Clone, Copy)]
532pub struct ExecutionRuntimeConfig {
533 pub allow_application_logs: bool,
536}
537
538impl Default for ExecutionRuntimeConfig {
539 fn default() -> Self {
540 Self {
541 allow_application_logs: true,
542 }
543 }
544}
545
546#[cfg_attr(not(web), async_trait)]
549#[cfg_attr(web, async_trait(?Send))]
550pub trait ExecutionRuntimeContext {
551 fn chain_id(&self) -> ChainId;
553
554 fn thread_pool(&self) -> &Arc<ThreadPool>;
556
557 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
559
560 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>>;
562
563 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>>;
565
566 async fn get_user_contract(
568 &self,
569 description: &ApplicationDescription,
570 txn_tracker: &TransactionTracker,
571 ) -> Result<UserContractCode, ExecutionError>;
572
573 async fn get_user_service(
575 &self,
576 description: &ApplicationDescription,
577 txn_tracker: &TransactionTracker,
578 ) -> Result<UserServiceCode, ExecutionError>;
579
580 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError>;
582
583 async fn get_event(&self, event_id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError>;
585
586 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
588
589 async fn get_committees(
593 &self,
594 epoch_range: RangeInclusive<Epoch>,
595 ) -> Result<BTreeMap<Epoch, Committee>, ExecutionError> {
596 let mut committees = BTreeMap::new();
597 let mut missing = Vec::new();
598 for index in epoch_range.start().0..=epoch_range.end().0 {
599 let epoch = Epoch(index);
600 match self.get_or_load_committee(epoch).await? {
601 Some(committee) => {
602 committees.insert(epoch, (*committee).clone());
603 }
604 None => missing.push(epoch),
605 }
606 }
607 if !missing.is_empty() {
608 let net_description = self
609 .get_network_description()
610 .await?
611 .ok_or(ExecutionError::NoNetworkDescriptionFound)?;
612 let event_ids = missing
613 .into_iter()
614 .map(|epoch| EventId {
615 chain_id: net_description.admin_chain_id,
616 stream_id: StreamId::system(EPOCH_STREAM_NAME),
617 index: epoch.0,
618 })
619 .collect();
620 return Err(ExecutionError::EventsNotFound(event_ids));
621 }
622 Ok(committees)
623 }
624
625 async fn get_or_load_committee(
638 &self,
639 epoch: Epoch,
640 ) -> Result<Option<Arc<Committee>>, ViewError>;
641
642 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
644
645 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
647
648 #[cfg(with_testing)]
650 async fn add_blobs(
651 &self,
652 blobs: impl IntoIterator<Item = Blob> + Send,
653 ) -> Result<(), ViewError>;
654
655 #[cfg(with_testing)]
657 async fn add_events(
658 &self,
659 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
660 ) -> Result<(), ViewError>;
661}
662
663#[derive(Clone, Copy, Debug)]
665pub struct OperationContext {
666 pub chain_id: ChainId,
668 #[debug(skip_if = Option::is_none)]
670 pub authenticated_signer: Option<AccountOwner>,
671 pub height: BlockHeight,
673 pub round: Option<u32>,
675 pub timestamp: Timestamp,
677}
678
679#[derive(Clone, Copy, Debug)]
681pub struct MessageContext {
682 pub chain_id: ChainId,
684 pub origin: ChainId,
686 pub is_bouncing: bool,
688 #[debug(skip_if = Option::is_none)]
690 pub authenticated_signer: Option<AccountOwner>,
691 #[debug(skip_if = Option::is_none)]
693 pub refund_grant_to: Option<Account>,
694 pub height: BlockHeight,
696 pub round: Option<u32>,
698 pub timestamp: Timestamp,
700}
701
702#[derive(Clone, Copy, Debug)]
704pub struct ProcessStreamsContext {
705 pub chain_id: ChainId,
707 pub height: BlockHeight,
709 pub round: Option<u32>,
711 pub timestamp: Timestamp,
713}
714
715impl From<MessageContext> for ProcessStreamsContext {
716 fn from(context: MessageContext) -> Self {
717 Self {
718 chain_id: context.chain_id,
719 height: context.height,
720 round: context.round,
721 timestamp: context.timestamp,
722 }
723 }
724}
725
726impl From<OperationContext> for ProcessStreamsContext {
727 fn from(context: OperationContext) -> Self {
728 Self {
729 chain_id: context.chain_id,
730 height: context.height,
731 round: context.round,
732 timestamp: context.timestamp,
733 }
734 }
735}
736
737#[derive(Clone, Copy, Debug)]
739pub struct FinalizeContext {
740 pub chain_id: ChainId,
742 #[debug(skip_if = Option::is_none)]
744 pub authenticated_signer: Option<AccountOwner>,
745 pub height: BlockHeight,
747 pub round: Option<u32>,
749}
750
751#[derive(Clone, Copy, Debug, Eq, PartialEq)]
753pub struct QueryContext {
754 pub chain_id: ChainId,
756 pub next_block_height: BlockHeight,
758 pub local_time: Timestamp,
760}
761
762pub trait BaseRuntime {
764 type Read: fmt::Debug + Send + Sync;
766 type ContainsKey: fmt::Debug + Send + Sync;
768 type ContainsKeys: fmt::Debug + Send + Sync;
770 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
772 type ReadValueBytes: fmt::Debug + Send + Sync;
774 type FindKeysByPrefix: fmt::Debug + Send + Sync;
776 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
778
779 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
781
782 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
784
785 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
787
788 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
790
791 fn read_application_description(
793 &mut self,
794 application_id: ApplicationId,
795 ) -> Result<ApplicationDescription, ExecutionError>;
796
797 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
799
800 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
802
803 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
805
806 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
808
809 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
811
812 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
814
815 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
817
818 fn application_permissions(&mut self) -> Result<ApplicationPermissions, ExecutionError>;
820
821 #[cfg(feature = "test")]
823 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
824 let promise = self.contains_key_new(key)?;
825 self.contains_key_wait(&promise)
826 }
827
828 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
830
831 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
833
834 #[cfg(feature = "test")]
836 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
837 let promise = self.contains_keys_new(keys)?;
838 self.contains_keys_wait(&promise)
839 }
840
841 fn contains_keys_new(
843 &mut self,
844 keys: Vec<Vec<u8>>,
845 ) -> Result<Self::ContainsKeys, ExecutionError>;
846
847 fn contains_keys_wait(
849 &mut self,
850 promise: &Self::ContainsKeys,
851 ) -> Result<Vec<bool>, ExecutionError>;
852
853 #[cfg(feature = "test")]
855 fn read_multi_values_bytes(
856 &mut self,
857 keys: Vec<Vec<u8>>,
858 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
859 let promise = self.read_multi_values_bytes_new(keys)?;
860 self.read_multi_values_bytes_wait(&promise)
861 }
862
863 fn read_multi_values_bytes_new(
865 &mut self,
866 keys: Vec<Vec<u8>>,
867 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
868
869 fn read_multi_values_bytes_wait(
871 &mut self,
872 promise: &Self::ReadMultiValuesBytes,
873 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
874
875 #[cfg(feature = "test")]
877 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
878 let promise = self.read_value_bytes_new(key)?;
879 self.read_value_bytes_wait(&promise)
880 }
881
882 fn read_value_bytes_new(
884 &mut self,
885 key: Vec<u8>,
886 ) -> Result<Self::ReadValueBytes, ExecutionError>;
887
888 fn read_value_bytes_wait(
890 &mut self,
891 promise: &Self::ReadValueBytes,
892 ) -> Result<Option<Vec<u8>>, ExecutionError>;
893
894 fn find_keys_by_prefix_new(
896 &mut self,
897 key_prefix: Vec<u8>,
898 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
899
900 fn find_keys_by_prefix_wait(
902 &mut self,
903 promise: &Self::FindKeysByPrefix,
904 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
905
906 #[cfg(feature = "test")]
908 #[expect(clippy::type_complexity)]
909 fn find_key_values_by_prefix(
910 &mut self,
911 key_prefix: Vec<u8>,
912 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
913 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
914 self.find_key_values_by_prefix_wait(&promise)
915 }
916
917 fn find_key_values_by_prefix_new(
919 &mut self,
920 key_prefix: Vec<u8>,
921 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
922
923 #[expect(clippy::type_complexity)]
925 fn find_key_values_by_prefix_wait(
926 &mut self,
927 promise: &Self::FindKeyValuesByPrefix,
928 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
929
930 fn perform_http_request(
932 &mut self,
933 request: http::Request,
934 ) -> Result<http::Response, ExecutionError>;
935
936 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
942
943 fn read_data_blob(&mut self, hash: DataBlobHash) -> Result<Vec<u8>, ExecutionError>;
945
946 fn assert_data_blob_exists(&mut self, hash: DataBlobHash) -> Result<(), ExecutionError>;
948
949 fn allow_application_logs(&mut self) -> Result<bool, ExecutionError>;
952
953 #[cfg(web)]
956 fn send_log(&mut self, message: String, level: tracing::log::Level);
957}
958
959pub trait ServiceRuntime: BaseRuntime {
961 fn try_query_application(
963 &mut self,
964 queried_id: ApplicationId,
965 argument: Vec<u8>,
966 ) -> Result<Vec<u8>, ExecutionError>;
967
968 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
970
971 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
973}
974
975pub trait ContractRuntime: BaseRuntime {
977 fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
979
980 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
983
984 fn message_origin_chain_id(&mut self) -> Result<Option<ChainId>, ExecutionError>;
986
987 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
990
991 fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
993
994 fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
996
997 fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
999
1000 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
1002
1003 fn transfer(
1005 &mut self,
1006 source: AccountOwner,
1007 destination: Account,
1008 amount: Amount,
1009 ) -> Result<(), ExecutionError>;
1010
1011 fn claim(
1013 &mut self,
1014 source: Account,
1015 destination: Account,
1016 amount: Amount,
1017 ) -> Result<(), ExecutionError>;
1018
1019 fn try_call_application(
1022 &mut self,
1023 authenticated: bool,
1024 callee_id: ApplicationId,
1025 argument: Vec<u8>,
1026 ) -> Result<Vec<u8>, ExecutionError>;
1027
1028 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
1030
1031 fn read_event(
1035 &mut self,
1036 chain_id: ChainId,
1037 stream_name: StreamName,
1038 index: u32,
1039 ) -> Result<Vec<u8>, ExecutionError>;
1040
1041 fn subscribe_to_events(
1043 &mut self,
1044 chain_id: ChainId,
1045 application_id: ApplicationId,
1046 stream_name: StreamName,
1047 ) -> Result<(), ExecutionError>;
1048
1049 fn unsubscribe_from_events(
1051 &mut self,
1052 chain_id: ChainId,
1053 application_id: ApplicationId,
1054 stream_name: StreamName,
1055 ) -> Result<(), ExecutionError>;
1056
1057 fn query_service(
1059 &mut self,
1060 application_id: ApplicationId,
1061 query: Vec<u8>,
1062 ) -> Result<Vec<u8>, ExecutionError>;
1063
1064 fn open_chain(
1066 &mut self,
1067 ownership: ChainOwnership,
1068 application_permissions: ApplicationPermissions,
1069 balance: Amount,
1070 ) -> Result<ChainId, ExecutionError>;
1071
1072 fn close_chain(&mut self) -> Result<(), ExecutionError>;
1074
1075 fn change_ownership(&mut self, ownership: ChainOwnership) -> Result<(), ExecutionError>;
1077
1078 fn change_application_permissions(
1080 &mut self,
1081 application_permissions: ApplicationPermissions,
1082 ) -> Result<(), ExecutionError>;
1083
1084 fn create_application(
1086 &mut self,
1087 module_id: ModuleId,
1088 parameters: Vec<u8>,
1089 argument: Vec<u8>,
1090 required_application_ids: Vec<ApplicationId>,
1091 ) -> Result<ApplicationId, ExecutionError>;
1092
1093 fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<DataBlobHash, ExecutionError>;
1095
1096 fn publish_module(
1098 &mut self,
1099 contract: Bytecode,
1100 service: Bytecode,
1101 vm_runtime: VmRuntime,
1102 ) -> Result<ModuleId, ExecutionError>;
1103
1104 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
1106
1107 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
1109}
1110
1111#[derive(
1113 Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative, strum::AsRefStr,
1114)]
1115pub enum Operation {
1116 System(Box<SystemOperation>),
1118 User {
1120 application_id: ApplicationId,
1122 #[serde(with = "serde_bytes")]
1124 #[debug(with = "hex_debug")]
1125 bytes: Vec<u8>,
1126 },
1127}
1128
1129impl BcsHashable<'_> for Operation {}
1130
1131#[derive(
1133 Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative, strum::AsRefStr,
1134)]
1135pub enum Message {
1136 System(SystemMessage),
1138 User {
1140 application_id: ApplicationId,
1142 #[serde(with = "serde_bytes")]
1144 #[debug(with = "hex_debug")]
1145 bytes: Vec<u8>,
1146 },
1147}
1148
1149#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1151pub enum Query {
1152 System(SystemQuery),
1154 User {
1156 application_id: ApplicationId,
1158 #[serde(with = "serde_bytes")]
1160 #[debug(with = "hex_debug")]
1161 bytes: Vec<u8>,
1162 },
1163}
1164
1165#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1167pub struct QueryOutcome<Response = QueryResponse> {
1168 pub response: Response,
1170 pub operations: Vec<Operation>,
1172}
1173
1174impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
1175 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
1176 let QueryOutcome {
1177 response,
1178 operations,
1179 } = system_outcome;
1180
1181 QueryOutcome {
1182 response: QueryResponse::System(response),
1183 operations,
1184 }
1185 }
1186}
1187
1188impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
1189 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
1190 let QueryOutcome {
1191 response,
1192 operations,
1193 } = user_service_outcome;
1194
1195 QueryOutcome {
1196 response: QueryResponse::User(response),
1197 operations,
1198 }
1199 }
1200}
1201
1202#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1204pub enum QueryResponse {
1205 System(SystemResponse),
1207 User(
1209 #[serde(with = "serde_bytes")]
1210 #[debug(with = "hex_debug")]
1211 Vec<u8>,
1212 ),
1213}
1214
1215#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy, Allocative)]
1217pub enum MessageKind {
1218 Simple,
1220 Protected,
1223 Tracked,
1226 Bouncing,
1228}
1229
1230impl Display for MessageKind {
1231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1232 match self {
1233 MessageKind::Simple => write!(f, "Simple"),
1234 MessageKind::Protected => write!(f, "Protected"),
1235 MessageKind::Tracked => write!(f, "Tracked"),
1236 MessageKind::Bouncing => write!(f, "Bouncing"),
1237 }
1238 }
1239}
1240
1241#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1243pub struct OutgoingMessage {
1244 pub destination: ChainId,
1246 #[debug(skip_if = Option::is_none)]
1248 pub authenticated_signer: Option<AccountOwner>,
1249 #[debug(skip_if = Amount::is_zero)]
1251 pub grant: Amount,
1252 #[debug(skip_if = Option::is_none)]
1254 pub refund_grant_to: Option<Account>,
1255 pub kind: MessageKind,
1257 pub message: Message,
1259}
1260
1261impl BcsHashable<'_> for OutgoingMessage {}
1262
1263impl OutgoingMessage {
1264 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
1266 OutgoingMessage {
1267 destination: recipient,
1268 authenticated_signer: None,
1269 grant: Amount::ZERO,
1270 refund_grant_to: None,
1271 kind: MessageKind::Simple,
1272 message: message.into(),
1273 }
1274 }
1275
1276 pub fn with_kind(mut self, kind: MessageKind) -> Self {
1278 self.kind = kind;
1279 self
1280 }
1281
1282 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
1284 self.authenticated_signer = authenticated_signer;
1285 self
1286 }
1287}
1288
1289impl OperationContext {
1290 fn refund_grant_to(&self) -> Option<Account> {
1293 self.authenticated_signer.map(|owner| Account {
1294 chain_id: self.chain_id,
1295 owner,
1296 })
1297 }
1298}
1299
1300#[cfg(with_testing)]
1302#[derive(Clone)]
1303pub struct TestExecutionRuntimeContext {
1304 chain_id: ChainId,
1305 thread_pool: Arc<ThreadPool>,
1306 execution_runtime_config: ExecutionRuntimeConfig,
1307 user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
1308 user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
1309 blobs: Arc<papaya::HashMap<BlobId, Blob>>,
1310 events: Arc<papaya::HashMap<EventId, Vec<u8>>>,
1311}
1312
1313#[cfg(with_testing)]
1314impl TestExecutionRuntimeContext {
1315 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1317 Self {
1318 chain_id,
1319 thread_pool: Arc::new(ThreadPool::new(20)),
1320 execution_runtime_config,
1321 user_contracts: Arc::default(),
1322 user_services: Arc::default(),
1323 blobs: Arc::default(),
1324 events: Arc::default(),
1325 }
1326 }
1327}
1328
1329#[cfg(with_testing)]
1330#[cfg_attr(not(web), async_trait)]
1331#[cfg_attr(web, async_trait(?Send))]
1332impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1333 fn chain_id(&self) -> ChainId {
1334 self.chain_id
1335 }
1336
1337 fn thread_pool(&self) -> &Arc<ThreadPool> {
1338 &self.thread_pool
1339 }
1340
1341 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1342 self.execution_runtime_config
1343 }
1344
1345 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>> {
1346 &self.user_contracts
1347 }
1348
1349 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>> {
1350 &self.user_services
1351 }
1352
1353 async fn get_user_contract(
1354 &self,
1355 description: &ApplicationDescription,
1356 _txn_tracker: &TransactionTracker,
1357 ) -> Result<UserContractCode, ExecutionError> {
1358 let application_id: ApplicationId = description.into();
1359 let pinned = self.user_contracts().pin();
1360 Ok(pinned
1361 .get(&application_id)
1362 .ok_or_else(|| {
1363 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1364 })?
1365 .clone())
1366 }
1367
1368 async fn get_user_service(
1369 &self,
1370 description: &ApplicationDescription,
1371 _txn_tracker: &TransactionTracker,
1372 ) -> Result<UserServiceCode, ExecutionError> {
1373 let application_id: ApplicationId = description.into();
1374 let pinned = self.user_services().pin();
1375 Ok(pinned
1376 .get(&application_id)
1377 .ok_or_else(|| {
1378 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1379 })?
1380 .clone())
1381 }
1382
1383 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError> {
1384 Ok(self.blobs.pin().get(&blob_id).cloned().map(Arc::new))
1385 }
1386
1387 async fn get_event(&self, event_id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError> {
1388 Ok(self.events.pin().get(&event_id).cloned().map(Arc::new))
1389 }
1390
1391 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1392 let pinned = self.blobs.pin();
1393 let genesis_committee_blob_hash = pinned
1394 .iter()
1395 .find(|(_, blob)| blob.content().blob_type() == BlobType::Committee)
1396 .map_or_else(
1397 || CryptoHash::test_hash("genesis committee"),
1398 |(_, blob)| blob.id().hash,
1399 );
1400 Ok(Some(NetworkDescription {
1401 admin_chain_id: dummy_chain_description(0).id(),
1402 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1403 genesis_timestamp: Timestamp::from(0),
1404 genesis_committee_blob_hash,
1405 name: "dummy network description".to_string(),
1406 }))
1407 }
1408
1409 async fn get_or_load_committee(
1410 &self,
1411 epoch: Epoch,
1412 ) -> Result<Option<Arc<Committee>>, ViewError> {
1413 let Some(net_description) = self.get_network_description().await? else {
1417 return Ok(None);
1418 };
1419 let blob_hash = if epoch.0 == 0 {
1420 net_description.genesis_committee_blob_hash
1421 } else {
1422 let event_id = EventId {
1423 chain_id: net_description.admin_chain_id,
1424 stream_id: StreamId::system(EPOCH_STREAM_NAME),
1425 index: epoch.0,
1426 };
1427 match self.get_event(event_id).await? {
1428 Some(bytes) => bcs::from_bytes(&bytes)?,
1429 None => return Ok(None),
1430 }
1431 };
1432 let blob_id = BlobId::new(blob_hash, BlobType::Committee);
1433 let Some(blob) = self.get_blob(blob_id).await? else {
1434 return Ok(None);
1435 };
1436 let committee: Committee = bcs::from_bytes(blob.bytes())?;
1437 Ok(Some(Arc::new(committee)))
1438 }
1439
1440 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1441 Ok(self.blobs.pin().contains_key(&blob_id))
1442 }
1443
1444 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1445 Ok(self.events.pin().contains_key(&event_id))
1446 }
1447
1448 #[cfg(with_testing)]
1449 async fn add_blobs(
1450 &self,
1451 blobs: impl IntoIterator<Item = Blob> + Send,
1452 ) -> Result<(), ViewError> {
1453 let pinned = self.blobs.pin();
1454 for blob in blobs {
1455 pinned.insert(blob.id(), blob);
1456 }
1457
1458 Ok(())
1459 }
1460
1461 #[cfg(with_testing)]
1462 async fn add_events(
1463 &self,
1464 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1465 ) -> Result<(), ViewError> {
1466 let pinned = self.events.pin();
1467 for (event_id, bytes) in events {
1468 pinned.insert(event_id, bytes);
1469 }
1470
1471 Ok(())
1472 }
1473}
1474
1475impl From<SystemOperation> for Operation {
1476 fn from(operation: SystemOperation) -> Self {
1477 Operation::System(Box::new(operation))
1478 }
1479}
1480
1481impl Operation {
1482 pub fn system(operation: SystemOperation) -> Self {
1484 Operation::System(Box::new(operation))
1485 }
1486
1487 #[cfg(with_testing)]
1489 pub fn user<A: Abi>(
1490 application_id: ApplicationId<A>,
1491 operation: &A::Operation,
1492 ) -> Result<Self, bcs::Error> {
1493 Self::user_without_abi(application_id.forget_abi(), operation)
1494 }
1495
1496 #[cfg(with_testing)]
1499 pub fn user_without_abi(
1500 application_id: ApplicationId,
1501 operation: &impl Serialize,
1502 ) -> Result<Self, bcs::Error> {
1503 Ok(Operation::User {
1504 application_id,
1505 bytes: bcs::to_bytes(&operation)?,
1506 })
1507 }
1508
1509 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1512 match self {
1513 Operation::System(system_operation) => Some(system_operation),
1514 Operation::User { .. } => None,
1515 }
1516 }
1517
1518 pub fn application_id(&self) -> GenericApplicationId {
1520 match self {
1521 Self::System(_) => GenericApplicationId::System,
1522 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1523 }
1524 }
1525
1526 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1528 match self.as_system_operation() {
1529 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1530 vec![BlobId::new(*blob_hash, BlobType::Data)]
1531 }
1532 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1533 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1534 }
1535 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1536 _ => vec![],
1537 }
1538 }
1539
1540 pub fn is_exempt_from_permissions(&self) -> bool {
1542 let Operation::System(system_op) = self else {
1543 return false;
1544 };
1545 matches!(
1546 **system_op,
1547 SystemOperation::ProcessNewEpoch(_)
1548 | SystemOperation::ProcessRemovedEpoch(_)
1549 | SystemOperation::UpdateStreams(_)
1550 )
1551 }
1552}
1553
1554impl From<SystemMessage> for Message {
1555 fn from(message: SystemMessage) -> Self {
1556 Message::System(message)
1557 }
1558}
1559
1560impl Message {
1561 pub fn system(message: SystemMessage) -> Self {
1563 Message::System(message)
1564 }
1565
1566 pub fn user<A, M: Serialize>(
1569 application_id: ApplicationId<A>,
1570 message: &M,
1571 ) -> Result<Self, bcs::Error> {
1572 let application_id = application_id.forget_abi();
1573 let bytes = bcs::to_bytes(&message)?;
1574 Ok(Message::User {
1575 application_id,
1576 bytes,
1577 })
1578 }
1579
1580 pub fn application_id(&self) -> GenericApplicationId {
1582 match self {
1583 Self::System(_) => GenericApplicationId::System,
1584 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1585 }
1586 }
1587}
1588
1589impl From<SystemQuery> for Query {
1590 fn from(query: SystemQuery) -> Self {
1591 Query::System(query)
1592 }
1593}
1594
1595impl Query {
1596 pub fn system(query: SystemQuery) -> Self {
1598 Query::System(query)
1599 }
1600
1601 pub fn user<A: Abi>(
1603 application_id: ApplicationId<A>,
1604 query: &A::Query,
1605 ) -> Result<Self, serde_json::Error> {
1606 Self::user_without_abi(application_id.forget_abi(), query)
1607 }
1608
1609 pub fn user_without_abi(
1612 application_id: ApplicationId,
1613 query: &impl Serialize,
1614 ) -> Result<Self, serde_json::Error> {
1615 Ok(Query::User {
1616 application_id,
1617 bytes: serde_json::to_vec(&query)?,
1618 })
1619 }
1620
1621 pub fn application_id(&self) -> GenericApplicationId {
1623 match self {
1624 Self::System(_) => GenericApplicationId::System,
1625 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1626 }
1627 }
1628}
1629
1630impl From<SystemResponse> for QueryResponse {
1631 fn from(response: SystemResponse) -> Self {
1632 QueryResponse::System(response)
1633 }
1634}
1635
1636impl From<Vec<u8>> for QueryResponse {
1637 fn from(response: Vec<u8>) -> Self {
1638 QueryResponse::User(response)
1639 }
1640}
1641
1642#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1644pub struct BlobState {
1645 pub last_used_by: Option<CryptoHash>,
1649 pub chain_id: ChainId,
1651 pub block_height: BlockHeight,
1653 pub epoch: Option<Epoch>,
1655}
1656
1657#[derive(Clone, Copy, Display)]
1659#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1660#[allow(missing_docs)]
1661pub enum WasmRuntime {
1662 #[cfg(with_wasmer)]
1663 #[default]
1664 #[display("wasmer")]
1665 Wasmer,
1666 #[cfg(with_wasmtime)]
1667 #[cfg_attr(not(with_wasmer), default)]
1668 #[display("wasmtime")]
1669 Wasmtime,
1670}
1671
1672#[derive(Clone, Copy, Display)]
1674#[cfg_attr(with_revm, derive(Debug, Default))]
1675#[allow(missing_docs)]
1676pub enum EvmRuntime {
1677 #[cfg(with_revm)]
1678 #[default]
1679 #[display("revm")]
1680 Revm,
1681}
1682
1683pub trait WithWasmDefault {
1685 fn with_wasm_default(self) -> Self;
1687}
1688
1689impl WithWasmDefault for Option<WasmRuntime> {
1690 fn with_wasm_default(self) -> Self {
1691 #[cfg(with_wasm_runtime)]
1692 {
1693 Some(self.unwrap_or_default())
1694 }
1695 #[cfg(not(with_wasm_runtime))]
1696 {
1697 None
1698 }
1699 }
1700}
1701
1702impl FromStr for WasmRuntime {
1703 type Err = InvalidWasmRuntime;
1704
1705 fn from_str(string: &str) -> Result<Self, Self::Err> {
1706 match string {
1707 #[cfg(with_wasmer)]
1708 "wasmer" => Ok(WasmRuntime::Wasmer),
1709 #[cfg(with_wasmtime)]
1710 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1711 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1712 }
1713 }
1714}
1715
1716#[derive(Clone, Debug, Error)]
1718#[error("{0:?} is not a valid WebAssembly runtime")]
1719pub struct InvalidWasmRuntime(String);
1720
1721doc_scalar!(Operation, "An operation to be executed in a block");
1722doc_scalar!(
1723 Message,
1724 "A message to be sent and possibly executed in the receiver's block."
1725);
1726doc_scalar!(MessageKind, "The kind of outgoing message being sent");