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";
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("The chain being queried is not active {0}")]
338 InactiveChain(ChainId),
339 #[error("Blobs not found: {0:?}")]
340 BlobsNotFound(Vec<BlobId>),
341 #[error("Events not found: {0:?}")]
342 EventsNotFound(Vec<EventId>),
343
344 #[error("Invalid HTTP header name used for HTTP request")]
345 InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
346 #[error("Invalid HTTP header value used for HTTP request")]
347 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
348
349 #[error("No NetworkDescription found in storage")]
350 NoNetworkDescriptionFound,
351 #[error("{epoch:?} is not recognized by chain {chain_id:}")]
352 InvalidEpoch { chain_id: ChainId, epoch: Epoch },
353 #[error("Transfer must have positive amount")]
354 IncorrectTransferAmount,
355 #[error("Transfer from owned account must be authenticated by the right signer")]
356 UnauthenticatedTransferOwner,
357 #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
358 InsufficientBalance {
359 balance: Amount,
360 account: AccountOwner,
361 },
362 #[error("Required execution fees exceeded the total funding available. Fees {fees}, available balance: {balance}")]
363 FeesExceedFunding { fees: Amount, balance: Amount },
364 #[error("Claim must have positive amount")]
365 IncorrectClaimAmount,
366 #[error("Claim must be authenticated by the right signer")]
367 UnauthenticatedClaimOwner,
368 #[error("Admin operations are only allowed on the admin chain.")]
369 AdminOperationOnNonAdminChain,
370 #[error("Failed to create new committee: expected {expected}, but got {provided}")]
371 InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
372 #[error("Failed to remove committee")]
373 InvalidCommitteeRemoval,
374 #[error("No recorded response for oracle query")]
375 MissingOracleResponse,
376 #[error("process_streams was not called for all stream updates")]
377 UnprocessedStreams,
378 #[error("Internal error: {0}")]
379 InternalError(&'static str),
380 #[error("UpdateStreams is outdated")]
381 OutdatedUpdateStreams,
382}
383
384impl ExecutionError {
385 pub fn is_local(&self) -> bool {
389 match self {
390 ExecutionError::ArithmeticError(_)
391 | ExecutionError::UserError(_)
392 | ExecutionError::DecompressionError(_)
393 | ExecutionError::InvalidPromise
394 | ExecutionError::CrossApplicationCallInFinalize { .. }
395 | ExecutionError::ReentrantCall(_)
396 | ExecutionError::ApplicationBytecodeNotFound(_)
397 | ExecutionError::UnsupportedDynamicApplicationLoad(_)
398 | ExecutionError::ExcessiveRead
399 | ExecutionError::ExcessiveWrite
400 | ExecutionError::MaximumFuelExceeded(_)
401 | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
402 | ExecutionError::ServiceOracleResponseTooLarge
403 | ExecutionError::BlockTooLarge
404 | ExecutionError::HttpResponseSizeLimitExceeded { .. }
405 | ExecutionError::UnauthorizedApplication(_)
406 | ExecutionError::UnexpectedOracleResponse
407 | ExecutionError::JsonError(_)
408 | ExecutionError::BcsError(_)
409 | ExecutionError::OracleResponseMismatch
410 | ExecutionError::ServiceOracleQueryOperations(_)
411 | ExecutionError::AssertBefore { .. }
412 | ExecutionError::StreamNameTooLong
413 | ExecutionError::BlobTooLarge
414 | ExecutionError::BytecodeTooLarge
415 | ExecutionError::UnauthorizedHttpRequest(_)
416 | ExecutionError::InvalidUrlForHttpRequest(_)
417 | ExecutionError::InactiveChain(_)
418 | ExecutionError::BlobsNotFound(_)
419 | ExecutionError::EventsNotFound(_)
420 | ExecutionError::InvalidHeaderName(_)
421 | ExecutionError::InvalidHeaderValue(_)
422 | ExecutionError::InvalidEpoch { .. }
423 | ExecutionError::IncorrectTransferAmount
424 | ExecutionError::UnauthenticatedTransferOwner
425 | ExecutionError::InsufficientBalance { .. }
426 | ExecutionError::FeesExceedFunding { .. }
427 | ExecutionError::IncorrectClaimAmount
428 | ExecutionError::UnauthenticatedClaimOwner
429 | ExecutionError::AdminOperationOnNonAdminChain
430 | ExecutionError::InvalidCommitteeEpoch { .. }
431 | ExecutionError::InvalidCommitteeRemoval
432 | ExecutionError::MissingOracleResponse
433 | ExecutionError::UnprocessedStreams
434 | ExecutionError::OutdatedUpdateStreams
435 | ExecutionError::ViewError(ViewError::NotFound(_)) => false,
436 #[cfg(with_wasm_runtime)]
437 ExecutionError::WasmError(_) => false,
438 #[cfg(with_revm)]
439 ExecutionError::EvmError(..) => false,
440 ExecutionError::MissingRuntimeResponse
441 | ExecutionError::ViewError(_)
442 | ExecutionError::ReqwestError(_)
443 | ExecutionError::Thread(_)
444 | ExecutionError::NoNetworkDescriptionFound
445 | ExecutionError::InternalError(_)
446 | ExecutionError::IoError(_) => true,
447 }
448 }
449
450 pub fn error_type(&self) -> String {
453 let variant: &'static str = self.into();
454 format!("ExecutionError::{variant}")
455 }
456
457 pub fn is_limit_error(&self) -> bool {
462 matches!(
463 self,
464 ExecutionError::ExcessiveRead
465 | ExecutionError::ExcessiveWrite
466 | ExecutionError::MaximumFuelExceeded(_)
467 | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
468 | ExecutionError::BlockTooLarge
469 )
470 }
471
472 pub fn is_transient_error(&self) -> bool {
478 matches!(
479 self,
480 ExecutionError::BlobsNotFound(_) | ExecutionError::EventsNotFound(_)
481 )
482 }
483}
484
485pub trait UserContract {
487 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
489
490 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
492
493 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
495
496 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
498
499 fn finalize(&mut self) -> Result<(), ExecutionError>;
501}
502
503pub trait UserService {
505 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
507}
508
509#[derive(Clone, Copy)]
511pub struct ExecutionRuntimeConfig {
512 pub allow_application_logs: bool,
515}
516
517impl Default for ExecutionRuntimeConfig {
518 fn default() -> Self {
519 Self {
520 allow_application_logs: true,
521 }
522 }
523}
524
525#[cfg_attr(not(web), async_trait)]
528#[cfg_attr(web, async_trait(?Send))]
529pub trait ExecutionRuntimeContext {
530 fn chain_id(&self) -> ChainId;
531
532 fn thread_pool(&self) -> &Arc<ThreadPool>;
533
534 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
535
536 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>>;
537
538 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>>;
539
540 async fn get_user_contract(
541 &self,
542 description: &ApplicationDescription,
543 txn_tracker: &TransactionTracker,
544 ) -> Result<UserContractCode, ExecutionError>;
545
546 async fn get_user_service(
547 &self,
548 description: &ApplicationDescription,
549 txn_tracker: &TransactionTracker,
550 ) -> Result<UserServiceCode, ExecutionError>;
551
552 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError>;
553
554 async fn get_event(&self, event_id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError>;
555
556 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
557
558 async fn get_committees(
560 &self,
561 epoch_range: RangeInclusive<Epoch>,
562 ) -> Result<BTreeMap<Epoch, Committee>, ExecutionError> {
563 let net_description = self
564 .get_network_description()
565 .await?
566 .ok_or(ExecutionError::NoNetworkDescriptionFound)?;
567 let committee_hashes = futures::future::join_all(
568 (epoch_range.start().0..=epoch_range.end().0).map(|epoch| async move {
569 if epoch == 0 {
570 Ok((epoch, net_description.genesis_committee_blob_hash))
572 } else {
573 let event_id = EventId {
574 chain_id: net_description.admin_chain_id,
575 stream_id: StreamId::system(EPOCH_STREAM_NAME),
576 index: epoch,
577 };
578 let event = self
579 .get_event(event_id.clone())
580 .await?
581 .ok_or_else(|| ExecutionError::EventsNotFound(vec![event_id]))?;
582 Ok((epoch, bcs::from_bytes(&event)?))
583 }
584 }),
585 )
586 .await;
587 let missing_events = committee_hashes
588 .iter()
589 .filter_map(|result| {
590 if let Err(ExecutionError::EventsNotFound(event_ids)) = result {
591 return Some(event_ids);
592 }
593 None
594 })
595 .flatten()
596 .cloned()
597 .collect::<Vec<_>>();
598 ensure!(
599 missing_events.is_empty(),
600 ExecutionError::EventsNotFound(missing_events)
601 );
602 let committee_hashes = committee_hashes
603 .into_iter()
604 .collect::<Result<Vec<_>, _>>()?;
605 let committees = futures::future::join_all(committee_hashes.into_iter().map(
606 |(epoch, committee_hash)| async move {
607 let blob_id = BlobId::new(committee_hash, BlobType::Committee);
608 let committee_blob = self
609 .get_blob(blob_id)
610 .await?
611 .ok_or_else(|| ExecutionError::BlobsNotFound(vec![blob_id]))?;
612 Ok((Epoch(epoch), bcs::from_bytes(committee_blob.bytes())?))
613 },
614 ))
615 .await;
616 let missing_blobs = committees
617 .iter()
618 .filter_map(|result| {
619 if let Err(ExecutionError::BlobsNotFound(blob_ids)) = result {
620 return Some(blob_ids);
621 }
622 None
623 })
624 .flatten()
625 .cloned()
626 .collect::<Vec<_>>();
627 ensure!(
628 missing_blobs.is_empty(),
629 ExecutionError::BlobsNotFound(missing_blobs)
630 );
631 committees.into_iter().collect()
632 }
633
634 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
635
636 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
637
638 #[cfg(with_testing)]
639 async fn add_blobs(
640 &self,
641 blobs: impl IntoIterator<Item = Blob> + Send,
642 ) -> Result<(), ViewError>;
643
644 #[cfg(with_testing)]
645 async fn add_events(
646 &self,
647 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
648 ) -> Result<(), ViewError>;
649}
650
651#[derive(Clone, Copy, Debug)]
652pub struct OperationContext {
653 pub chain_id: ChainId,
655 #[debug(skip_if = Option::is_none)]
657 pub authenticated_signer: Option<AccountOwner>,
658 pub height: BlockHeight,
660 pub round: Option<u32>,
662 pub timestamp: Timestamp,
664}
665
666#[derive(Clone, Copy, Debug)]
667pub struct MessageContext {
668 pub chain_id: ChainId,
670 pub origin: ChainId,
672 pub is_bouncing: bool,
674 #[debug(skip_if = Option::is_none)]
676 pub authenticated_signer: Option<AccountOwner>,
677 #[debug(skip_if = Option::is_none)]
679 pub refund_grant_to: Option<Account>,
680 pub height: BlockHeight,
682 pub round: Option<u32>,
684 pub timestamp: Timestamp,
686}
687
688#[derive(Clone, Copy, Debug)]
689pub struct ProcessStreamsContext {
690 pub chain_id: ChainId,
692 pub height: BlockHeight,
694 pub round: Option<u32>,
696 pub timestamp: Timestamp,
698}
699
700impl From<MessageContext> for ProcessStreamsContext {
701 fn from(context: MessageContext) -> Self {
702 Self {
703 chain_id: context.chain_id,
704 height: context.height,
705 round: context.round,
706 timestamp: context.timestamp,
707 }
708 }
709}
710
711impl From<OperationContext> for ProcessStreamsContext {
712 fn from(context: OperationContext) -> Self {
713 Self {
714 chain_id: context.chain_id,
715 height: context.height,
716 round: context.round,
717 timestamp: context.timestamp,
718 }
719 }
720}
721
722#[derive(Clone, Copy, Debug)]
723pub struct FinalizeContext {
724 pub chain_id: ChainId,
726 #[debug(skip_if = Option::is_none)]
728 pub authenticated_signer: Option<AccountOwner>,
729 pub height: BlockHeight,
731 pub round: Option<u32>,
733}
734
735#[derive(Clone, Copy, Debug, Eq, PartialEq)]
736pub struct QueryContext {
737 pub chain_id: ChainId,
739 pub next_block_height: BlockHeight,
741 pub local_time: Timestamp,
743}
744
745pub trait BaseRuntime {
746 type Read: fmt::Debug + Send + Sync;
747 type ContainsKey: fmt::Debug + Send + Sync;
748 type ContainsKeys: fmt::Debug + Send + Sync;
749 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
750 type ReadValueBytes: fmt::Debug + Send + Sync;
751 type FindKeysByPrefix: fmt::Debug + Send + Sync;
752 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
753
754 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
756
757 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
759
760 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
762
763 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
765
766 fn read_application_description(
768 &mut self,
769 application_id: ApplicationId,
770 ) -> Result<ApplicationDescription, ExecutionError>;
771
772 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
774
775 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
777
778 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
780
781 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
783
784 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
786
787 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
789
790 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
792
793 fn application_permissions(&mut self) -> Result<ApplicationPermissions, ExecutionError>;
795
796 #[cfg(feature = "test")]
798 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
799 let promise = self.contains_key_new(key)?;
800 self.contains_key_wait(&promise)
801 }
802
803 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
805
806 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
808
809 #[cfg(feature = "test")]
811 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
812 let promise = self.contains_keys_new(keys)?;
813 self.contains_keys_wait(&promise)
814 }
815
816 fn contains_keys_new(
818 &mut self,
819 keys: Vec<Vec<u8>>,
820 ) -> Result<Self::ContainsKeys, ExecutionError>;
821
822 fn contains_keys_wait(
824 &mut self,
825 promise: &Self::ContainsKeys,
826 ) -> Result<Vec<bool>, ExecutionError>;
827
828 #[cfg(feature = "test")]
830 fn read_multi_values_bytes(
831 &mut self,
832 keys: Vec<Vec<u8>>,
833 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
834 let promise = self.read_multi_values_bytes_new(keys)?;
835 self.read_multi_values_bytes_wait(&promise)
836 }
837
838 fn read_multi_values_bytes_new(
840 &mut self,
841 keys: Vec<Vec<u8>>,
842 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
843
844 fn read_multi_values_bytes_wait(
846 &mut self,
847 promise: &Self::ReadMultiValuesBytes,
848 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
849
850 #[cfg(feature = "test")]
852 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
853 let promise = self.read_value_bytes_new(key)?;
854 self.read_value_bytes_wait(&promise)
855 }
856
857 fn read_value_bytes_new(
859 &mut self,
860 key: Vec<u8>,
861 ) -> Result<Self::ReadValueBytes, ExecutionError>;
862
863 fn read_value_bytes_wait(
865 &mut self,
866 promise: &Self::ReadValueBytes,
867 ) -> Result<Option<Vec<u8>>, ExecutionError>;
868
869 fn find_keys_by_prefix_new(
871 &mut self,
872 key_prefix: Vec<u8>,
873 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
874
875 fn find_keys_by_prefix_wait(
877 &mut self,
878 promise: &Self::FindKeysByPrefix,
879 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
880
881 #[cfg(feature = "test")]
883 #[expect(clippy::type_complexity)]
884 fn find_key_values_by_prefix(
885 &mut self,
886 key_prefix: Vec<u8>,
887 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
888 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
889 self.find_key_values_by_prefix_wait(&promise)
890 }
891
892 fn find_key_values_by_prefix_new(
894 &mut self,
895 key_prefix: Vec<u8>,
896 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
897
898 #[expect(clippy::type_complexity)]
900 fn find_key_values_by_prefix_wait(
901 &mut self,
902 promise: &Self::FindKeyValuesByPrefix,
903 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
904
905 fn perform_http_request(
907 &mut self,
908 request: http::Request,
909 ) -> Result<http::Response, ExecutionError>;
910
911 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
917
918 fn read_data_blob(&mut self, hash: DataBlobHash) -> Result<Vec<u8>, ExecutionError>;
920
921 fn assert_data_blob_exists(&mut self, hash: DataBlobHash) -> Result<(), ExecutionError>;
923
924 fn allow_application_logs(&mut self) -> Result<bool, ExecutionError>;
927
928 #[cfg(web)]
931 fn send_log(&mut self, message: String, level: tracing::log::Level);
932}
933
934pub trait ServiceRuntime: BaseRuntime {
935 fn try_query_application(
937 &mut self,
938 queried_id: ApplicationId,
939 argument: Vec<u8>,
940 ) -> Result<Vec<u8>, ExecutionError>;
941
942 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
944
945 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
947}
948
949pub trait ContractRuntime: BaseRuntime {
950 fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
952
953 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
956
957 fn message_origin_chain_id(&mut self) -> Result<Option<ChainId>, ExecutionError>;
959
960 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
963
964 fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
966
967 fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
969
970 fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
972
973 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
975
976 fn transfer(
978 &mut self,
979 source: AccountOwner,
980 destination: Account,
981 amount: Amount,
982 ) -> Result<(), ExecutionError>;
983
984 fn claim(
986 &mut self,
987 source: Account,
988 destination: Account,
989 amount: Amount,
990 ) -> Result<(), ExecutionError>;
991
992 fn try_call_application(
995 &mut self,
996 authenticated: bool,
997 callee_id: ApplicationId,
998 argument: Vec<u8>,
999 ) -> Result<Vec<u8>, ExecutionError>;
1000
1001 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
1003
1004 fn read_event(
1008 &mut self,
1009 chain_id: ChainId,
1010 stream_name: StreamName,
1011 index: u32,
1012 ) -> Result<Vec<u8>, ExecutionError>;
1013
1014 fn subscribe_to_events(
1016 &mut self,
1017 chain_id: ChainId,
1018 application_id: ApplicationId,
1019 stream_name: StreamName,
1020 ) -> Result<(), ExecutionError>;
1021
1022 fn unsubscribe_from_events(
1024 &mut self,
1025 chain_id: ChainId,
1026 application_id: ApplicationId,
1027 stream_name: StreamName,
1028 ) -> Result<(), ExecutionError>;
1029
1030 fn query_service(
1032 &mut self,
1033 application_id: ApplicationId,
1034 query: Vec<u8>,
1035 ) -> Result<Vec<u8>, ExecutionError>;
1036
1037 fn open_chain(
1039 &mut self,
1040 ownership: ChainOwnership,
1041 application_permissions: ApplicationPermissions,
1042 balance: Amount,
1043 ) -> Result<ChainId, ExecutionError>;
1044
1045 fn close_chain(&mut self) -> Result<(), ExecutionError>;
1047
1048 fn change_ownership(&mut self, ownership: ChainOwnership) -> Result<(), ExecutionError>;
1050
1051 fn change_application_permissions(
1053 &mut self,
1054 application_permissions: ApplicationPermissions,
1055 ) -> Result<(), ExecutionError>;
1056
1057 fn create_application(
1059 &mut self,
1060 module_id: ModuleId,
1061 parameters: Vec<u8>,
1062 argument: Vec<u8>,
1063 required_application_ids: Vec<ApplicationId>,
1064 ) -> Result<ApplicationId, ExecutionError>;
1065
1066 fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<DataBlobHash, ExecutionError>;
1068
1069 fn publish_module(
1071 &mut self,
1072 contract: Bytecode,
1073 service: Bytecode,
1074 vm_runtime: VmRuntime,
1075 ) -> Result<ModuleId, ExecutionError>;
1076
1077 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
1079
1080 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
1082}
1083
1084#[derive(
1086 Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative, strum::AsRefStr,
1087)]
1088pub enum Operation {
1089 System(Box<SystemOperation>),
1091 User {
1093 application_id: ApplicationId,
1094 #[serde(with = "serde_bytes")]
1095 #[debug(with = "hex_debug")]
1096 bytes: Vec<u8>,
1097 },
1098}
1099
1100impl BcsHashable<'_> for Operation {}
1101
1102#[derive(
1104 Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative, strum::AsRefStr,
1105)]
1106pub enum Message {
1107 System(SystemMessage),
1109 User {
1111 application_id: ApplicationId,
1112 #[serde(with = "serde_bytes")]
1113 #[debug(with = "hex_debug")]
1114 bytes: Vec<u8>,
1115 },
1116}
1117
1118#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1120pub enum Query {
1121 System(SystemQuery),
1123 User {
1125 application_id: ApplicationId,
1126 #[serde(with = "serde_bytes")]
1127 #[debug(with = "hex_debug")]
1128 bytes: Vec<u8>,
1129 },
1130}
1131
1132#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1134pub struct QueryOutcome<Response = QueryResponse> {
1135 pub response: Response,
1136 pub operations: Vec<Operation>,
1137}
1138
1139impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
1140 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
1141 let QueryOutcome {
1142 response,
1143 operations,
1144 } = system_outcome;
1145
1146 QueryOutcome {
1147 response: QueryResponse::System(response),
1148 operations,
1149 }
1150 }
1151}
1152
1153impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
1154 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
1155 let QueryOutcome {
1156 response,
1157 operations,
1158 } = user_service_outcome;
1159
1160 QueryOutcome {
1161 response: QueryResponse::User(response),
1162 operations,
1163 }
1164 }
1165}
1166
1167#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1169pub enum QueryResponse {
1170 System(SystemResponse),
1172 User(
1174 #[serde(with = "serde_bytes")]
1175 #[debug(with = "hex_debug")]
1176 Vec<u8>,
1177 ),
1178}
1179
1180#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy, Allocative)]
1182pub enum MessageKind {
1183 Simple,
1185 Protected,
1188 Tracked,
1191 Bouncing,
1193}
1194
1195impl Display for MessageKind {
1196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1197 match self {
1198 MessageKind::Simple => write!(f, "Simple"),
1199 MessageKind::Protected => write!(f, "Protected"),
1200 MessageKind::Tracked => write!(f, "Tracked"),
1201 MessageKind::Bouncing => write!(f, "Bouncing"),
1202 }
1203 }
1204}
1205
1206#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1208pub struct OutgoingMessage {
1209 pub destination: ChainId,
1211 #[debug(skip_if = Option::is_none)]
1213 pub authenticated_signer: Option<AccountOwner>,
1214 #[debug(skip_if = Amount::is_zero)]
1216 pub grant: Amount,
1217 #[debug(skip_if = Option::is_none)]
1219 pub refund_grant_to: Option<Account>,
1220 pub kind: MessageKind,
1222 pub message: Message,
1224}
1225
1226impl BcsHashable<'_> for OutgoingMessage {}
1227
1228impl OutgoingMessage {
1229 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
1231 OutgoingMessage {
1232 destination: recipient,
1233 authenticated_signer: None,
1234 grant: Amount::ZERO,
1235 refund_grant_to: None,
1236 kind: MessageKind::Simple,
1237 message: message.into(),
1238 }
1239 }
1240
1241 pub fn with_kind(mut self, kind: MessageKind) -> Self {
1243 self.kind = kind;
1244 self
1245 }
1246
1247 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
1249 self.authenticated_signer = authenticated_signer;
1250 self
1251 }
1252}
1253
1254impl OperationContext {
1255 fn refund_grant_to(&self) -> Option<Account> {
1258 self.authenticated_signer.map(|owner| Account {
1259 chain_id: self.chain_id,
1260 owner,
1261 })
1262 }
1263}
1264
1265#[cfg(with_testing)]
1266#[derive(Clone)]
1267pub struct TestExecutionRuntimeContext {
1268 chain_id: ChainId,
1269 thread_pool: Arc<ThreadPool>,
1270 execution_runtime_config: ExecutionRuntimeConfig,
1271 user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
1272 user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
1273 blobs: Arc<papaya::HashMap<BlobId, Blob>>,
1274 events: Arc<papaya::HashMap<EventId, Vec<u8>>>,
1275}
1276
1277#[cfg(with_testing)]
1278impl TestExecutionRuntimeContext {
1279 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1280 Self {
1281 chain_id,
1282 thread_pool: Arc::new(ThreadPool::new(20)),
1283 execution_runtime_config,
1284 user_contracts: Arc::default(),
1285 user_services: Arc::default(),
1286 blobs: Arc::default(),
1287 events: Arc::default(),
1288 }
1289 }
1290}
1291
1292#[cfg(with_testing)]
1293#[cfg_attr(not(web), async_trait)]
1294#[cfg_attr(web, async_trait(?Send))]
1295impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1296 fn chain_id(&self) -> ChainId {
1297 self.chain_id
1298 }
1299
1300 fn thread_pool(&self) -> &Arc<ThreadPool> {
1301 &self.thread_pool
1302 }
1303
1304 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1305 self.execution_runtime_config
1306 }
1307
1308 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>> {
1309 &self.user_contracts
1310 }
1311
1312 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>> {
1313 &self.user_services
1314 }
1315
1316 async fn get_user_contract(
1317 &self,
1318 description: &ApplicationDescription,
1319 _txn_tracker: &TransactionTracker,
1320 ) -> Result<UserContractCode, ExecutionError> {
1321 let application_id: ApplicationId = description.into();
1322 let pinned = self.user_contracts().pin();
1323 Ok(pinned
1324 .get(&application_id)
1325 .ok_or_else(|| {
1326 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1327 })?
1328 .clone())
1329 }
1330
1331 async fn get_user_service(
1332 &self,
1333 description: &ApplicationDescription,
1334 _txn_tracker: &TransactionTracker,
1335 ) -> Result<UserServiceCode, ExecutionError> {
1336 let application_id: ApplicationId = description.into();
1337 let pinned = self.user_services().pin();
1338 Ok(pinned
1339 .get(&application_id)
1340 .ok_or_else(|| {
1341 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1342 })?
1343 .clone())
1344 }
1345
1346 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError> {
1347 Ok(self.blobs.pin().get(&blob_id).cloned().map(Arc::new))
1348 }
1349
1350 async fn get_event(&self, event_id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError> {
1351 Ok(self.events.pin().get(&event_id).cloned().map(Arc::new))
1352 }
1353
1354 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1355 let pinned = self.blobs.pin();
1356 let genesis_committee_blob_hash = pinned
1357 .iter()
1358 .find(|(_, blob)| blob.content().blob_type() == BlobType::Committee)
1359 .map_or_else(
1360 || CryptoHash::test_hash("genesis committee"),
1361 |(_, blob)| blob.id().hash,
1362 );
1363 Ok(Some(NetworkDescription {
1364 admin_chain_id: dummy_chain_description(0).id(),
1365 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1366 genesis_timestamp: Timestamp::from(0),
1367 genesis_committee_blob_hash,
1368 name: "dummy network description".to_string(),
1369 }))
1370 }
1371
1372 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1373 Ok(self.blobs.pin().contains_key(&blob_id))
1374 }
1375
1376 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1377 Ok(self.events.pin().contains_key(&event_id))
1378 }
1379
1380 #[cfg(with_testing)]
1381 async fn add_blobs(
1382 &self,
1383 blobs: impl IntoIterator<Item = Blob> + Send,
1384 ) -> Result<(), ViewError> {
1385 let pinned = self.blobs.pin();
1386 for blob in blobs {
1387 pinned.insert(blob.id(), blob);
1388 }
1389
1390 Ok(())
1391 }
1392
1393 #[cfg(with_testing)]
1394 async fn add_events(
1395 &self,
1396 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1397 ) -> Result<(), ViewError> {
1398 let pinned = self.events.pin();
1399 for (event_id, bytes) in events {
1400 pinned.insert(event_id, bytes);
1401 }
1402
1403 Ok(())
1404 }
1405}
1406
1407impl From<SystemOperation> for Operation {
1408 fn from(operation: SystemOperation) -> Self {
1409 Operation::System(Box::new(operation))
1410 }
1411}
1412
1413impl Operation {
1414 pub fn system(operation: SystemOperation) -> Self {
1415 Operation::System(Box::new(operation))
1416 }
1417
1418 #[cfg(with_testing)]
1420 pub fn user<A: Abi>(
1421 application_id: ApplicationId<A>,
1422 operation: &A::Operation,
1423 ) -> Result<Self, bcs::Error> {
1424 Self::user_without_abi(application_id.forget_abi(), operation)
1425 }
1426
1427 #[cfg(with_testing)]
1430 pub fn user_without_abi(
1431 application_id: ApplicationId,
1432 operation: &impl Serialize,
1433 ) -> Result<Self, bcs::Error> {
1434 Ok(Operation::User {
1435 application_id,
1436 bytes: bcs::to_bytes(&operation)?,
1437 })
1438 }
1439
1440 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1443 match self {
1444 Operation::System(system_operation) => Some(system_operation),
1445 Operation::User { .. } => None,
1446 }
1447 }
1448
1449 pub fn application_id(&self) -> GenericApplicationId {
1450 match self {
1451 Self::System(_) => GenericApplicationId::System,
1452 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1453 }
1454 }
1455
1456 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1458 match self.as_system_operation() {
1459 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1460 vec![BlobId::new(*blob_hash, BlobType::Data)]
1461 }
1462 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1463 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1464 }
1465 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1466 _ => vec![],
1467 }
1468 }
1469
1470 pub fn is_exempt_from_permissions(&self) -> bool {
1472 let Operation::System(system_op) = self else {
1473 return false;
1474 };
1475 matches!(
1476 **system_op,
1477 SystemOperation::ProcessNewEpoch(_)
1478 | SystemOperation::ProcessRemovedEpoch(_)
1479 | SystemOperation::UpdateStreams(_)
1480 )
1481 }
1482}
1483
1484impl From<SystemMessage> for Message {
1485 fn from(message: SystemMessage) -> Self {
1486 Message::System(message)
1487 }
1488}
1489
1490impl Message {
1491 pub fn system(message: SystemMessage) -> Self {
1492 Message::System(message)
1493 }
1494
1495 pub fn user<A, M: Serialize>(
1498 application_id: ApplicationId<A>,
1499 message: &M,
1500 ) -> Result<Self, bcs::Error> {
1501 let application_id = application_id.forget_abi();
1502 let bytes = bcs::to_bytes(&message)?;
1503 Ok(Message::User {
1504 application_id,
1505 bytes,
1506 })
1507 }
1508
1509 pub fn application_id(&self) -> GenericApplicationId {
1510 match self {
1511 Self::System(_) => GenericApplicationId::System,
1512 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1513 }
1514 }
1515}
1516
1517impl From<SystemQuery> for Query {
1518 fn from(query: SystemQuery) -> Self {
1519 Query::System(query)
1520 }
1521}
1522
1523impl Query {
1524 pub fn system(query: SystemQuery) -> Self {
1525 Query::System(query)
1526 }
1527
1528 pub fn user<A: Abi>(
1530 application_id: ApplicationId<A>,
1531 query: &A::Query,
1532 ) -> Result<Self, serde_json::Error> {
1533 Self::user_without_abi(application_id.forget_abi(), query)
1534 }
1535
1536 pub fn user_without_abi(
1539 application_id: ApplicationId,
1540 query: &impl Serialize,
1541 ) -> Result<Self, serde_json::Error> {
1542 Ok(Query::User {
1543 application_id,
1544 bytes: serde_json::to_vec(&query)?,
1545 })
1546 }
1547
1548 pub fn application_id(&self) -> GenericApplicationId {
1549 match self {
1550 Self::System(_) => GenericApplicationId::System,
1551 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1552 }
1553 }
1554}
1555
1556impl From<SystemResponse> for QueryResponse {
1557 fn from(response: SystemResponse) -> Self {
1558 QueryResponse::System(response)
1559 }
1560}
1561
1562impl From<Vec<u8>> for QueryResponse {
1563 fn from(response: Vec<u8>) -> Self {
1564 QueryResponse::User(response)
1565 }
1566}
1567
1568#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1570pub struct BlobState {
1571 pub last_used_by: Option<CryptoHash>,
1575 pub chain_id: ChainId,
1577 pub block_height: BlockHeight,
1579 pub epoch: Option<Epoch>,
1581}
1582
1583#[derive(Clone, Copy, Display)]
1585#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1586pub enum WasmRuntime {
1587 #[cfg(with_wasmer)]
1588 #[default]
1589 #[display("wasmer")]
1590 Wasmer,
1591 #[cfg(with_wasmtime)]
1592 #[cfg_attr(not(with_wasmer), default)]
1593 #[display("wasmtime")]
1594 Wasmtime,
1595}
1596
1597#[derive(Clone, Copy, Display)]
1598#[cfg_attr(with_revm, derive(Debug, Default))]
1599pub enum EvmRuntime {
1600 #[cfg(with_revm)]
1601 #[default]
1602 #[display("revm")]
1603 Revm,
1604}
1605
1606pub trait WithWasmDefault {
1608 fn with_wasm_default(self) -> Self;
1609}
1610
1611impl WithWasmDefault for Option<WasmRuntime> {
1612 fn with_wasm_default(self) -> Self {
1613 #[cfg(with_wasm_runtime)]
1614 {
1615 Some(self.unwrap_or_default())
1616 }
1617 #[cfg(not(with_wasm_runtime))]
1618 {
1619 None
1620 }
1621 }
1622}
1623
1624impl FromStr for WasmRuntime {
1625 type Err = InvalidWasmRuntime;
1626
1627 fn from_str(string: &str) -> Result<Self, Self::Err> {
1628 match string {
1629 #[cfg(with_wasmer)]
1630 "wasmer" => Ok(WasmRuntime::Wasmer),
1631 #[cfg(with_wasmtime)]
1632 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1633 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1634 }
1635 }
1636}
1637
1638#[derive(Clone, Debug, Error)]
1640#[error("{0:?} is not a valid WebAssembly runtime")]
1641pub struct InvalidWasmRuntime(String);
1642
1643doc_scalar!(Operation, "An operation to be executed in a block");
1644doc_scalar!(
1645 Message,
1646 "A message to be sent and possibly executed in the receiver's block."
1647);
1648doc_scalar!(MessageKind, "The kind of outgoing message being sent");