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::{ProtocolFlag, 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::{PreparedCheckpoint, 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("Blobs not found: {0:?}")]
318 BlobsNotFound(Vec<BlobId>),
319 #[error("Events not found: {0:?}")]
320 EventsNotFound(Vec<EventId>),
321
322 #[error("Invalid HTTP header name used for HTTP request")]
323 InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
324 #[error("Invalid HTTP header value used for HTTP request")]
325 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
326
327 #[error("No NetworkDescription found in storage")]
328 NoNetworkDescriptionFound,
329 #[error("{epoch:?} is not recognized by chain {chain_id:}")]
330 InvalidEpoch { chain_id: ChainId, epoch: Epoch },
331 #[error("Transfer must have positive amount")]
332 IncorrectTransferAmount,
333 #[error("Transfer from owned account must be authenticated by the right owner")]
334 UnauthenticatedTransferOwner,
335 #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
336 InsufficientBalance {
337 balance: Amount,
338 account: AccountOwner,
339 },
340 #[error("Required execution fees exceeded the total funding available. Fees {fees}, available balance: {balance}")]
341 FeesExceedFunding { fees: Amount, balance: Amount },
342 #[error("Claim must have positive amount")]
343 IncorrectClaimAmount,
344 #[error("Claim must be authenticated by the right owner")]
345 UnauthenticatedClaimOwner,
346 #[error("The transferred amount must not exceed the allowance for spender {spender} from owner {owner}: {allowance}")]
347 InsufficientAllowance {
348 allowance: Amount,
349 owner: AccountOwner,
350 spender: AccountOwner,
351 },
352 #[error("Admin operations are only allowed on the admin chain.")]
353 AdminOperationOnNonAdminChain,
354 #[error("Failed to create new committee: expected {expected}, but got {provided}")]
355 InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
356 #[error("Failed to remove committee")]
357 InvalidCommitteeRemoval,
358 #[error("No recorded response for oracle query")]
359 MissingOracleResponse,
360 #[error("process_streams was not called for all stream updates")]
361 UnprocessedStreams,
362 #[error("Internal error: {0}")]
363 InternalError(&'static str),
364 #[error("UpdateStream is outdated")]
365 OutdatedUpdateStream,
366 #[error("UpdateStream references an application that is not subscribed")]
367 UnsubscribedUpdateStream,
368 #[error("Checkpoint precondition failed: {0}")]
369 CheckpointPreconditionFailed(&'static str),
370}
371
372impl ExecutionError {
373 pub fn is_local(&self) -> bool {
377 match self {
378 ExecutionError::ArithmeticError(_)
379 | ExecutionError::UserError(_)
380 | ExecutionError::DecompressionError(_)
381 | ExecutionError::InvalidPromise
382 | ExecutionError::CrossApplicationCallInFinalize { .. }
383 | ExecutionError::ReentrantCall(_)
384 | ExecutionError::ApplicationBytecodeNotFound(_)
385 | ExecutionError::UnsupportedDynamicApplicationLoad(_)
386 | ExecutionError::ExcessiveRead
387 | ExecutionError::ExcessiveWrite
388 | ExecutionError::MaximumFuelExceeded(_)
389 | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
390 | ExecutionError::ServiceOracleResponseTooLarge
391 | ExecutionError::BlockTooLarge
392 | ExecutionError::HttpResponseSizeLimitExceeded { .. }
393 | ExecutionError::UnauthorizedApplication(_)
394 | ExecutionError::UnexpectedOracleResponse
395 | ExecutionError::JsonError(_)
396 | ExecutionError::BcsError(_)
397 | ExecutionError::OracleResponseMismatch
398 | ExecutionError::ServiceOracleQueryOperations(_)
399 | ExecutionError::AssertBefore { .. }
400 | ExecutionError::StreamNameTooLong
401 | ExecutionError::BlobTooLarge
402 | ExecutionError::BytecodeTooLarge
403 | ExecutionError::UnauthorizedHttpRequest(_)
404 | ExecutionError::InvalidUrlForHttpRequest(_)
405 | ExecutionError::BlobsNotFound(_)
406 | ExecutionError::EventsNotFound(_)
407 | ExecutionError::InvalidHeaderName(_)
408 | ExecutionError::InvalidHeaderValue(_)
409 | ExecutionError::InvalidEpoch { .. }
410 | ExecutionError::IncorrectTransferAmount
411 | ExecutionError::UnauthenticatedTransferOwner
412 | ExecutionError::InsufficientBalance { .. }
413 | ExecutionError::FeesExceedFunding { .. }
414 | ExecutionError::IncorrectClaimAmount
415 | ExecutionError::UnauthenticatedClaimOwner
416 | ExecutionError::InsufficientAllowance { .. }
417 | ExecutionError::AdminOperationOnNonAdminChain
418 | ExecutionError::InvalidCommitteeEpoch { .. }
419 | ExecutionError::InvalidCommitteeRemoval
420 | ExecutionError::MissingOracleResponse
421 | ExecutionError::UnprocessedStreams
422 | ExecutionError::OutdatedUpdateStream
423 | ExecutionError::UnsubscribedUpdateStream
424 | ExecutionError::CheckpointPreconditionFailed(_)
425 | ExecutionError::ViewError(ViewError::NotFound(_)) => false,
426 #[cfg(with_wasm_runtime)]
427 ExecutionError::WasmError(_) => false,
428 #[cfg(with_revm)]
429 ExecutionError::EvmError(..) => false,
430 ExecutionError::MissingRuntimeResponse
431 | ExecutionError::ViewError(_)
432 | ExecutionError::ReqwestError(_)
433 | ExecutionError::Thread(_)
434 | ExecutionError::NoNetworkDescriptionFound
435 | ExecutionError::InternalError(_)
436 | ExecutionError::IoError(_) => true,
437 }
438 }
439
440 pub fn error_type(&self) -> String {
443 let variant: &'static str = self.into();
444 format!("ExecutionError::{variant}")
445 }
446
447 pub fn is_limit_error(&self) -> bool {
452 matches!(
453 self,
454 ExecutionError::ExcessiveRead
455 | ExecutionError::ExcessiveWrite
456 | ExecutionError::MaximumFuelExceeded(_)
457 | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
458 | ExecutionError::BlockTooLarge
459 )
460 }
461
462 pub fn is_transient_error(&self) -> bool {
468 matches!(
469 self,
470 ExecutionError::BlobsNotFound(_) | ExecutionError::EventsNotFound(_)
471 )
472 }
473}
474
475pub trait UserContract {
477 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
479
480 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
482
483 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
485
486 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
488
489 fn finalize(&mut self) -> Result<(), ExecutionError>;
491}
492
493pub trait UserService {
495 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
497}
498
499#[derive(Clone, Copy)]
501pub struct ExecutionRuntimeConfig {
502 pub allow_application_logs: bool,
505}
506
507impl Default for ExecutionRuntimeConfig {
508 fn default() -> Self {
509 Self {
510 allow_application_logs: true,
511 }
512 }
513}
514
515#[cfg_attr(not(web), async_trait)]
518#[cfg_attr(web, async_trait(?Send))]
519pub trait ExecutionRuntimeContext {
520 fn chain_id(&self) -> ChainId;
521
522 fn thread_pool(&self) -> &Arc<ThreadPool>;
523
524 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
525
526 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>>;
527
528 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>>;
529
530 async fn get_user_contract(
531 &self,
532 description: &ApplicationDescription,
533 txn_tracker: &TransactionTracker,
534 ) -> Result<UserContractCode, ExecutionError>;
535
536 async fn get_user_service(
537 &self,
538 description: &ApplicationDescription,
539 txn_tracker: &TransactionTracker,
540 ) -> Result<UserServiceCode, ExecutionError>;
541
542 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError>;
543
544 async fn get_event(&self, event_id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError>;
545
546 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
547
548 async fn get_or_load_committee_by_hash(
556 &self,
557 hash: CryptoHash,
558 ) -> Result<Arc<Committee>, ExecutionError>;
559
560 async fn get_committee_hashes(
562 &self,
563 epoch_range: RangeInclusive<Epoch>,
564 ) -> Result<BTreeMap<Epoch, CryptoHash>, ExecutionError> {
565 let net_description = self
566 .get_network_description()
567 .await?
568 .ok_or(ExecutionError::NoNetworkDescriptionFound)?;
569 let committee_hashes = futures::future::join_all(
570 (epoch_range.start().0..=epoch_range.end().0).map(|epoch| async move {
571 if epoch == 0 {
572 Ok((Epoch(epoch), net_description.genesis_committee_blob_hash))
574 } else {
575 let event_id = EventId {
576 chain_id: net_description.admin_chain_id,
577 stream_id: StreamId::system(EPOCH_STREAM_NAME),
578 index: epoch,
579 };
580 let event = self
581 .get_event(event_id.clone())
582 .await?
583 .ok_or_else(|| ExecutionError::EventsNotFound(vec![event_id]))?;
584 let event_data: EpochEventData = bcs::from_bytes(&event)?;
585 Ok((Epoch(epoch), event_data.blob_hash))
586 }
587 }),
588 )
589 .await;
590 let missing_events = committee_hashes
591 .iter()
592 .filter_map(|result| {
593 if let Err(ExecutionError::EventsNotFound(event_ids)) = result {
594 return Some(event_ids);
595 }
596 None
597 })
598 .flatten()
599 .cloned()
600 .collect::<Vec<_>>();
601 ensure!(
602 missing_events.is_empty(),
603 ExecutionError::EventsNotFound(missing_events)
604 );
605 committee_hashes.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 origin_certificate_hash: CryptoHash,
648 pub origin_timestamp: Timestamp,
650 pub is_bouncing: bool,
652 #[debug(skip_if = Option::is_none)]
654 pub authenticated_owner: Option<AccountOwner>,
655 #[debug(skip_if = Option::is_none)]
657 pub refund_grant_to: Option<Account>,
658 pub height: BlockHeight,
660 pub round: Option<u32>,
662 pub timestamp: Timestamp,
664}
665
666#[derive(Clone, Copy, Debug)]
667pub struct ProcessStreamsContext {
668 pub chain_id: ChainId,
670 pub height: BlockHeight,
672 pub round: Option<u32>,
674 pub timestamp: Timestamp,
676}
677
678impl From<MessageContext> for ProcessStreamsContext {
679 fn from(context: MessageContext) -> Self {
680 Self {
681 chain_id: context.chain_id,
682 height: context.height,
683 round: context.round,
684 timestamp: context.timestamp,
685 }
686 }
687}
688
689impl From<OperationContext> for ProcessStreamsContext {
690 fn from(context: OperationContext) -> Self {
691 Self {
692 chain_id: context.chain_id,
693 height: context.height,
694 round: context.round,
695 timestamp: context.timestamp,
696 }
697 }
698}
699
700#[derive(Clone, Copy, Debug)]
701pub struct FinalizeContext {
702 pub chain_id: ChainId,
704 #[debug(skip_if = Option::is_none)]
706 pub authenticated_owner: Option<AccountOwner>,
707 pub height: BlockHeight,
709 pub round: Option<u32>,
711}
712
713#[derive(Clone, Copy, Debug, Eq, PartialEq)]
714pub struct QueryContext {
715 pub chain_id: ChainId,
717 pub next_block_height: BlockHeight,
719 pub local_time: Timestamp,
721}
722
723pub trait BaseRuntime {
724 type Read: fmt::Debug + Send + Sync;
725 type ContainsKey: fmt::Debug + Send + Sync;
726 type ContainsKeys: fmt::Debug + Send + Sync;
727 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
728 type ReadValueBytes: fmt::Debug + Send + Sync;
729 type FindKeysByPrefix: fmt::Debug + Send + Sync;
730 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
731
732 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
734
735 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
737
738 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
740
741 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
743
744 fn read_application_description(
746 &mut self,
747 application_id: ApplicationId,
748 ) -> Result<ApplicationDescription, ExecutionError>;
749
750 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
752
753 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
755
756 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
758
759 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
761
762 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
764
765 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
767
768 fn read_allowance(
770 &mut self,
771 owner: AccountOwner,
772 spender: AccountOwner,
773 ) -> Result<Amount, ExecutionError>;
774
775 fn read_allowances(
777 &mut self,
778 ) -> Result<Vec<(AccountOwner, AccountOwner, Amount)>, ExecutionError>;
779
780 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
782
783 fn application_permissions(&mut self) -> Result<ApplicationPermissions, ExecutionError>;
785
786 #[cfg(feature = "test")]
788 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
789 let promise = self.contains_key_new(key)?;
790 self.contains_key_wait(&promise)
791 }
792
793 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
795
796 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
798
799 #[cfg(feature = "test")]
801 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
802 let promise = self.contains_keys_new(keys)?;
803 self.contains_keys_wait(&promise)
804 }
805
806 fn contains_keys_new(
808 &mut self,
809 keys: Vec<Vec<u8>>,
810 ) -> Result<Self::ContainsKeys, ExecutionError>;
811
812 fn contains_keys_wait(
814 &mut self,
815 promise: &Self::ContainsKeys,
816 ) -> Result<Vec<bool>, ExecutionError>;
817
818 #[cfg(feature = "test")]
820 fn read_multi_values_bytes(
821 &mut self,
822 keys: Vec<Vec<u8>>,
823 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
824 let promise = self.read_multi_values_bytes_new(keys)?;
825 self.read_multi_values_bytes_wait(&promise)
826 }
827
828 fn read_multi_values_bytes_new(
830 &mut self,
831 keys: Vec<Vec<u8>>,
832 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
833
834 fn read_multi_values_bytes_wait(
836 &mut self,
837 promise: &Self::ReadMultiValuesBytes,
838 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
839
840 #[cfg(feature = "test")]
842 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
843 let promise = self.read_value_bytes_new(key)?;
844 self.read_value_bytes_wait(&promise)
845 }
846
847 fn read_value_bytes_new(
849 &mut self,
850 key: Vec<u8>,
851 ) -> Result<Self::ReadValueBytes, ExecutionError>;
852
853 fn read_value_bytes_wait(
855 &mut self,
856 promise: &Self::ReadValueBytes,
857 ) -> Result<Option<Vec<u8>>, ExecutionError>;
858
859 fn find_keys_by_prefix_new(
861 &mut self,
862 key_prefix: Vec<u8>,
863 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
864
865 fn find_keys_by_prefix_wait(
867 &mut self,
868 promise: &Self::FindKeysByPrefix,
869 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
870
871 #[cfg(feature = "test")]
873 #[expect(clippy::type_complexity)]
874 fn find_key_values_by_prefix(
875 &mut self,
876 key_prefix: Vec<u8>,
877 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
878 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
879 self.find_key_values_by_prefix_wait(&promise)
880 }
881
882 fn find_key_values_by_prefix_new(
884 &mut self,
885 key_prefix: Vec<u8>,
886 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
887
888 #[expect(clippy::type_complexity)]
890 fn find_key_values_by_prefix_wait(
891 &mut self,
892 promise: &Self::FindKeyValuesByPrefix,
893 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
894
895 fn perform_http_request(
897 &mut self,
898 request: http::Request,
899 ) -> Result<http::Response, ExecutionError>;
900
901 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
907
908 fn read_data_blob(&mut self, hash: DataBlobHash) -> Result<Vec<u8>, ExecutionError>;
910
911 fn assert_data_blob_exists(&mut self, hash: DataBlobHash) -> Result<(), ExecutionError>;
913
914 fn has_empty_storage(&mut self, application: ApplicationId) -> Result<bool, ExecutionError>;
916
917 fn maximum_blob_size(&mut self) -> Result<u64, ExecutionError>;
919
920 fn allow_application_logs(&mut self) -> Result<bool, ExecutionError>;
923
924 #[cfg(web)]
927 fn send_log(&mut self, message: String, level: tracing::log::Level);
928}
929
930pub trait ServiceRuntime: BaseRuntime {
931 fn try_query_application(
933 &mut self,
934 queried_id: ApplicationId,
935 argument: Vec<u8>,
936 ) -> Result<Vec<u8>, ExecutionError>;
937
938 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
940
941 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
943}
944
945pub trait ContractRuntime: BaseRuntime {
946 fn authenticated_owner(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
948
949 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
952
953 fn message_origin_chain_id(&mut self) -> Result<Option<ChainId>, ExecutionError>;
955
956 fn message_origin_timestamp(&mut self) -> Result<Option<Timestamp>, 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 approve(
994 &mut self,
995 owner: AccountOwner,
996 spender: AccountOwner,
997 amount: Amount,
998 ) -> Result<(), ExecutionError>;
999
1000 fn transfer_from(
1002 &mut self,
1003 owner: AccountOwner,
1004 spender: AccountOwner,
1005 destination: Account,
1006 amount: Amount,
1007 ) -> Result<(), ExecutionError>;
1008
1009 fn try_call_application(
1012 &mut self,
1013 authenticated: bool,
1014 callee_id: ApplicationId,
1015 argument: Vec<u8>,
1016 ) -> Result<Vec<u8>, ExecutionError>;
1017
1018 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
1020
1021 fn read_event(
1025 &mut self,
1026 chain_id: ChainId,
1027 stream_name: StreamName,
1028 index: u32,
1029 ) -> Result<Vec<u8>, ExecutionError>;
1030
1031 fn subscribe_to_events(
1033 &mut self,
1034 chain_id: ChainId,
1035 application_id: ApplicationId,
1036 stream_name: StreamName,
1037 ) -> Result<(), ExecutionError>;
1038
1039 fn unsubscribe_from_events(
1041 &mut self,
1042 chain_id: ChainId,
1043 application_id: ApplicationId,
1044 stream_name: StreamName,
1045 ) -> Result<(), ExecutionError>;
1046
1047 fn query_service(
1049 &mut self,
1050 application_id: ApplicationId,
1051 query: Vec<u8>,
1052 ) -> Result<Vec<u8>, ExecutionError>;
1053
1054 fn open_chain(
1056 &mut self,
1057 ownership: ChainOwnership,
1058 application_permissions: ApplicationPermissions,
1059 balance: Amount,
1060 ) -> Result<ChainId, ExecutionError>;
1061
1062 fn close_chain(&mut self) -> Result<(), ExecutionError>;
1064
1065 fn change_ownership(&mut self, ownership: ChainOwnership) -> Result<(), ExecutionError>;
1067
1068 fn change_application_permissions(
1070 &mut self,
1071 application_permissions: ApplicationPermissions,
1072 ) -> Result<(), ExecutionError>;
1073
1074 fn create_application(
1076 &mut self,
1077 module_id: ModuleId,
1078 parameters: Vec<u8>,
1079 argument: Vec<u8>,
1080 required_application_ids: Vec<ApplicationId>,
1081 ) -> Result<ApplicationId, ExecutionError>;
1082
1083 fn peek_application_index(&mut self) -> Result<u32, ExecutionError>;
1086
1087 fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<DataBlobHash, ExecutionError>;
1089
1090 fn publish_module(
1093 &mut self,
1094 contract: Bytecode,
1095 service: Bytecode,
1096 vm_runtime: VmRuntime,
1097 formats: Option<Vec<u8>>,
1098 ) -> Result<ModuleId, ExecutionError>;
1099
1100 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
1102
1103 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
1105}
1106
1107#[derive(
1109 Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative, strum::AsRefStr,
1110)]
1111pub enum Operation {
1112 System(Box<SystemOperation>),
1114 User {
1116 application_id: ApplicationId,
1117 #[serde(with = "serde_bytes")]
1118 #[debug(with = "hex_debug")]
1119 bytes: Vec<u8>,
1120 },
1121}
1122
1123impl BcsHashable<'_> for Operation {}
1124
1125#[derive(
1127 Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative, strum::AsRefStr,
1128)]
1129pub enum Message {
1130 System(SystemMessage),
1132 User {
1134 application_id: ApplicationId,
1135 #[serde(with = "serde_bytes")]
1136 #[debug(with = "hex_debug")]
1137 bytes: Vec<u8>,
1138 },
1139}
1140
1141#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1143pub enum Query {
1144 System(SystemQuery),
1146 User {
1148 application_id: ApplicationId,
1149 #[serde(with = "serde_bytes")]
1150 #[debug(with = "hex_debug")]
1151 bytes: Vec<u8>,
1152 },
1153}
1154
1155#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1157pub struct QueryOutcome<Response = QueryResponse> {
1158 pub response: Response,
1159 pub operations: Vec<Operation>,
1160}
1161
1162impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
1163 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
1164 let QueryOutcome {
1165 response,
1166 operations,
1167 } = system_outcome;
1168
1169 QueryOutcome {
1170 response: QueryResponse::System(response),
1171 operations,
1172 }
1173 }
1174}
1175
1176impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
1177 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
1178 let QueryOutcome {
1179 response,
1180 operations,
1181 } = user_service_outcome;
1182
1183 QueryOutcome {
1184 response: QueryResponse::User(response),
1185 operations,
1186 }
1187 }
1188}
1189
1190#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1192pub enum QueryResponse {
1193 System(SystemResponse),
1195 User(
1197 #[serde(with = "serde_bytes")]
1198 #[debug(with = "hex_debug")]
1199 Vec<u8>,
1200 ),
1201}
1202
1203#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy, Allocative)]
1205pub enum MessageKind {
1206 Simple,
1208 Protected,
1211 Tracked,
1214 Bouncing,
1216}
1217
1218impl Display for MessageKind {
1219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1220 match self {
1221 MessageKind::Simple => write!(f, "Simple"),
1222 MessageKind::Protected => write!(f, "Protected"),
1223 MessageKind::Tracked => write!(f, "Tracked"),
1224 MessageKind::Bouncing => write!(f, "Bouncing"),
1225 }
1226 }
1227}
1228
1229#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1231pub struct OutgoingMessage {
1232 pub destination: ChainId,
1234 #[debug(skip_if = Option::is_none)]
1236 pub authenticated_owner: Option<AccountOwner>,
1237 #[debug(skip_if = Amount::is_zero)]
1239 pub grant: Amount,
1240 #[debug(skip_if = Option::is_none)]
1242 pub refund_grant_to: Option<Account>,
1243 pub kind: MessageKind,
1245 pub message: Message,
1247}
1248
1249impl BcsHashable<'_> for OutgoingMessage {}
1250
1251impl OutgoingMessage {
1252 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
1254 OutgoingMessage {
1255 destination: recipient,
1256 authenticated_owner: None,
1257 grant: Amount::ZERO,
1258 refund_grant_to: None,
1259 kind: MessageKind::Simple,
1260 message: message.into(),
1261 }
1262 }
1263
1264 pub fn with_kind(mut self, kind: MessageKind) -> Self {
1266 self.kind = kind;
1267 self
1268 }
1269
1270 pub fn with_authenticated_owner(mut self, authenticated_owner: Option<AccountOwner>) -> Self {
1272 self.authenticated_owner = authenticated_owner;
1273 self
1274 }
1275}
1276
1277impl OperationContext {
1278 fn refund_grant_to(&self) -> Option<Account> {
1281 self.authenticated_owner.map(|owner| Account {
1282 chain_id: self.chain_id,
1283 owner,
1284 })
1285 }
1286}
1287
1288#[cfg(with_testing)]
1289#[derive(Clone)]
1290pub struct TestExecutionRuntimeContext {
1291 chain_id: ChainId,
1292 thread_pool: Arc<ThreadPool>,
1293 execution_runtime_config: ExecutionRuntimeConfig,
1294 user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
1295 user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
1296 blobs: Arc<papaya::HashMap<BlobId, Blob>>,
1297 events: Arc<papaya::HashMap<EventId, Vec<u8>>>,
1298}
1299
1300#[cfg(with_testing)]
1301impl TestExecutionRuntimeContext {
1302 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1303 Self {
1304 chain_id,
1305 thread_pool: Arc::new(ThreadPool::new(20)),
1306 execution_runtime_config,
1307 user_contracts: Arc::default(),
1308 user_services: Arc::default(),
1309 blobs: Arc::default(),
1310 events: Arc::default(),
1311 }
1312 }
1313}
1314
1315#[cfg(with_testing)]
1316#[cfg_attr(not(web), async_trait)]
1317#[cfg_attr(web, async_trait(?Send))]
1318impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1319 fn chain_id(&self) -> ChainId {
1320 self.chain_id
1321 }
1322
1323 fn thread_pool(&self) -> &Arc<ThreadPool> {
1324 &self.thread_pool
1325 }
1326
1327 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1328 self.execution_runtime_config
1329 }
1330
1331 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>> {
1332 &self.user_contracts
1333 }
1334
1335 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>> {
1336 &self.user_services
1337 }
1338
1339 async fn get_user_contract(
1340 &self,
1341 description: &ApplicationDescription,
1342 _txn_tracker: &TransactionTracker,
1343 ) -> Result<UserContractCode, ExecutionError> {
1344 let application_id: ApplicationId = description.into();
1345 let pinned = self.user_contracts().pin();
1346 Ok(pinned
1347 .get(&application_id)
1348 .ok_or_else(|| {
1349 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1350 })?
1351 .clone())
1352 }
1353
1354 async fn get_user_service(
1355 &self,
1356 description: &ApplicationDescription,
1357 _txn_tracker: &TransactionTracker,
1358 ) -> Result<UserServiceCode, ExecutionError> {
1359 let application_id: ApplicationId = description.into();
1360 let pinned = self.user_services().pin();
1361 Ok(pinned
1362 .get(&application_id)
1363 .ok_or_else(|| {
1364 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1365 })?
1366 .clone())
1367 }
1368
1369 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError> {
1370 Ok(self.blobs.pin().get(&blob_id).cloned().map(Arc::new))
1371 }
1372
1373 async fn get_event(&self, event_id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError> {
1374 Ok(self.events.pin().get(&event_id).cloned().map(Arc::new))
1375 }
1376
1377 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1378 let pinned = self.blobs.pin();
1379 let genesis_committee_blob_hash = pinned
1380 .iter()
1381 .find(|(_, blob)| blob.content().blob_type() == BlobType::Committee)
1382 .map_or_else(
1383 || CryptoHash::test_hash("genesis committee"),
1384 |(_, blob)| blob.id().hash,
1385 );
1386 Ok(Some(NetworkDescription {
1387 admin_chain_id: dummy_chain_description(0).id(),
1388 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1389 genesis_timestamp: Timestamp::from(0),
1390 genesis_committee_blob_hash,
1391 name: "dummy network description".to_string(),
1392 }))
1393 }
1394
1395 async fn get_or_load_committee_by_hash(
1396 &self,
1397 hash: CryptoHash,
1398 ) -> Result<Arc<Committee>, ExecutionError> {
1399 let blob_id = BlobId::new(hash, BlobType::Committee);
1400 let blob = self
1401 .blobs
1402 .pin()
1403 .get(&blob_id)
1404 .cloned()
1405 .ok_or(ExecutionError::BlobsNotFound(vec![blob_id]))?;
1406 let committee: Committee = bcs::from_bytes(blob.bytes())?;
1407 Ok(Arc::new(committee))
1408 }
1409
1410 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1411 Ok(self.blobs.pin().contains_key(&blob_id))
1412 }
1413
1414 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1415 Ok(self.events.pin().contains_key(&event_id))
1416 }
1417
1418 #[cfg(with_testing)]
1419 async fn add_blobs(
1420 &self,
1421 blobs: impl IntoIterator<Item = Blob> + Send,
1422 ) -> Result<(), ViewError> {
1423 let pinned = self.blobs.pin();
1424 for blob in blobs {
1425 pinned.insert(blob.id(), blob);
1426 }
1427
1428 Ok(())
1429 }
1430
1431 #[cfg(with_testing)]
1432 async fn add_events(
1433 &self,
1434 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1435 ) -> Result<(), ViewError> {
1436 let pinned = self.events.pin();
1437 for (event_id, bytes) in events {
1438 pinned.insert(event_id, bytes);
1439 }
1440
1441 Ok(())
1442 }
1443}
1444
1445impl From<SystemOperation> for Operation {
1446 fn from(operation: SystemOperation) -> Self {
1447 Operation::System(Box::new(operation))
1448 }
1449}
1450
1451impl Operation {
1452 pub fn system(operation: SystemOperation) -> Self {
1453 Operation::System(Box::new(operation))
1454 }
1455
1456 #[cfg(with_testing)]
1458 pub fn user<A: Abi>(
1459 application_id: ApplicationId<A>,
1460 operation: &A::Operation,
1461 ) -> Result<Self, bcs::Error> {
1462 Self::user_without_abi(application_id.forget_abi(), operation)
1463 }
1464
1465 #[cfg(with_testing)]
1468 pub fn user_without_abi(
1469 application_id: ApplicationId,
1470 operation: &impl Serialize,
1471 ) -> Result<Self, bcs::Error> {
1472 Ok(Operation::User {
1473 application_id,
1474 bytes: bcs::to_bytes(&operation)?,
1475 })
1476 }
1477
1478 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1481 match self {
1482 Operation::System(system_operation) => Some(system_operation),
1483 Operation::User { .. } => None,
1484 }
1485 }
1486
1487 pub fn application_id(&self) -> GenericApplicationId {
1488 match self {
1489 Self::System(_) => GenericApplicationId::System,
1490 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1491 }
1492 }
1493
1494 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1496 match self.as_system_operation() {
1497 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1498 vec![BlobId::new(*blob_hash, BlobType::Data)]
1499 }
1500 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1501 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1502 }
1503 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1504 _ => vec![],
1505 }
1506 }
1507
1508 pub fn is_exempt_from_permissions(&self) -> bool {
1510 let Operation::System(system_op) = self else {
1511 return false;
1512 };
1513 matches!(
1514 **system_op,
1515 SystemOperation::ProcessNewEpoch(_) | SystemOperation::UpdateStream { .. }
1516 )
1517 }
1518
1519 pub fn is_update_stream(&self) -> bool {
1521 let Operation::System(system_op) = self else {
1522 return false;
1523 };
1524 matches!(**system_op, SystemOperation::UpdateStream { .. })
1525 }
1526
1527 pub fn is_checkpoint(&self) -> bool {
1529 let Operation::System(system_op) = self else {
1530 return false;
1531 };
1532 matches!(**system_op, SystemOperation::Checkpoint)
1533 }
1534}
1535
1536impl From<SystemMessage> for Message {
1537 fn from(message: SystemMessage) -> Self {
1538 Message::System(message)
1539 }
1540}
1541
1542impl Message {
1543 pub fn system(message: SystemMessage) -> Self {
1544 Message::System(message)
1545 }
1546
1547 pub fn is_checkpoint_ack(&self) -> bool {
1549 matches!(self, Message::System(SystemMessage::CheckpointAck { .. }))
1550 }
1551
1552 pub fn user<A, M: Serialize>(
1555 application_id: ApplicationId<A>,
1556 message: &M,
1557 ) -> Result<Self, bcs::Error> {
1558 let application_id = application_id.forget_abi();
1559 let bytes = bcs::to_bytes(&message)?;
1560 Ok(Message::User {
1561 application_id,
1562 bytes,
1563 })
1564 }
1565
1566 pub fn application_id(&self) -> GenericApplicationId {
1567 match self {
1568 Self::System(_) => GenericApplicationId::System,
1569 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1570 }
1571 }
1572}
1573
1574impl From<SystemQuery> for Query {
1575 fn from(query: SystemQuery) -> Self {
1576 Query::System(query)
1577 }
1578}
1579
1580impl Query {
1581 pub fn system(query: SystemQuery) -> Self {
1582 Query::System(query)
1583 }
1584
1585 pub fn user<A: Abi>(
1587 application_id: ApplicationId<A>,
1588 query: &A::Query,
1589 ) -> Result<Self, serde_json::Error> {
1590 Self::user_without_abi(application_id.forget_abi(), query)
1591 }
1592
1593 pub fn user_without_abi(
1596 application_id: ApplicationId,
1597 query: &impl Serialize,
1598 ) -> Result<Self, serde_json::Error> {
1599 Ok(Query::User {
1600 application_id,
1601 bytes: serde_json::to_vec(&query)?,
1602 })
1603 }
1604
1605 pub fn application_id(&self) -> GenericApplicationId {
1606 match self {
1607 Self::System(_) => GenericApplicationId::System,
1608 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1609 }
1610 }
1611}
1612
1613impl From<SystemResponse> for QueryResponse {
1614 fn from(response: SystemResponse) -> Self {
1615 QueryResponse::System(response)
1616 }
1617}
1618
1619impl From<Vec<u8>> for QueryResponse {
1620 fn from(response: Vec<u8>) -> Self {
1621 QueryResponse::User(response)
1622 }
1623}
1624
1625#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1629pub enum BlobOrigin {
1630 Genesis,
1635 Published {
1638 chain_id: ChainId,
1639 block_height: BlockHeight,
1640 },
1641}
1642
1643#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1645pub struct BlobState {
1646 pub origin: BlobOrigin,
1648 pub last_used_by: Option<CryptoHash>,
1652 pub epoch: Option<Epoch>,
1654}
1655
1656impl BlobState {
1657 pub const GENESIS: BlobState = BlobState {
1660 origin: BlobOrigin::Genesis,
1661 last_used_by: None,
1662 epoch: None,
1663 };
1664}
1665
1666#[derive(Clone, Copy, Display)]
1668#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1669pub enum WasmRuntime {
1670 #[cfg(with_wasmer)]
1671 #[default]
1672 #[display("wasmer")]
1673 Wasmer,
1674 #[cfg(with_wasmtime)]
1675 #[cfg_attr(not(with_wasmer), default)]
1676 #[display("wasmtime")]
1677 Wasmtime,
1678}
1679
1680#[derive(Clone, Copy, Display)]
1681#[cfg_attr(with_revm, derive(Debug, Default))]
1682pub enum EvmRuntime {
1683 #[cfg(with_revm)]
1684 #[default]
1685 #[display("revm")]
1686 Revm,
1687}
1688
1689pub trait WithWasmDefault {
1691 fn with_wasm_default(self) -> Self;
1692}
1693
1694impl WithWasmDefault for Option<WasmRuntime> {
1695 fn with_wasm_default(self) -> Self {
1696 #[cfg(with_wasm_runtime)]
1697 {
1698 Some(self.unwrap_or_default())
1699 }
1700 #[cfg(not(with_wasm_runtime))]
1701 {
1702 None
1703 }
1704 }
1705}
1706
1707impl FromStr for WasmRuntime {
1708 type Err = InvalidWasmRuntime;
1709
1710 fn from_str(string: &str) -> Result<Self, Self::Err> {
1711 match string {
1712 #[cfg(with_wasmer)]
1713 "wasmer" => Ok(WasmRuntime::Wasmer),
1714 #[cfg(with_wasmtime)]
1715 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1716 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1717 }
1718 }
1719}
1720
1721#[derive(Clone, Debug, Error)]
1723#[error("{0:?} is not a valid WebAssembly runtime")]
1724pub struct InvalidWasmRuntime(String);
1725
1726doc_scalar!(Operation, "An operation to be executed in a block");
1727doc_scalar!(
1728 Message,
1729 "A message to be sent and possibly executed in the receiver's block."
1730);
1731doc_scalar!(MessageKind, "The kind of outgoing message being sent");