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