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