1#![deny(missing_docs)]
8
9pub mod committee;
11pub mod evm;
12mod execution;
13pub mod execution_state_actor;
14#[cfg(with_graphql)]
15mod graphql;
16mod policy;
17mod resources;
18mod runtime;
19pub mod system;
21#[cfg(with_testing)]
23pub mod test_utils;
24mod transaction_tracker;
25mod util;
26mod wasm;
27
28use std::{any::Any, collections::BTreeMap, fmt, ops::RangeInclusive, str::FromStr, sync::Arc};
29
30use allocative::Allocative;
31use async_graphql::SimpleObject;
32use async_trait::async_trait;
33use custom_debug_derive::Debug;
34use derive_more::Display;
35#[cfg(web)]
36use js_sys::wasm_bindgen::JsValue;
37use linera_base::{
38 abi::Abi,
39 crypto::{BcsHashable, CryptoHash},
40 data_types::{
41 Amount, ApplicationDescription, ApplicationPermissions, ArithmeticError, Blob, BlockHeight,
42 Bytecode, DecompressionError, Epoch, NetworkDescription, SendMessageRequest, StreamUpdate,
43 Timestamp,
44 },
45 doc_scalar, ensure, hex_debug, http,
46 identifiers::{
47 Account, AccountOwner, ApplicationId, BlobId, BlobType, ChainId, DataBlobHash, EventId,
48 GenericApplicationId, ModuleId, StreamId, StreamName,
49 },
50 ownership::ChainOwnership,
51 vm::VmRuntime,
52};
53use linera_views::{batch::Batch, ViewError};
54use serde::{Deserialize, Serialize};
55use system::AdminOperation;
56use thiserror::Error;
57pub use web_thread_pool::Pool as ThreadPool;
58use web_thread_select as web_thread;
59
60#[cfg(with_revm)]
61use crate::evm::EvmExecutionError;
62use crate::system::{EpochEventData, EPOCH_STREAM_NAME};
63#[cfg(with_testing)]
64use crate::test_utils::dummy_chain_description;
65#[cfg(all(with_testing, with_wasm_runtime))]
66pub use crate::wasm::test as wasm_test;
67#[cfg(with_wasm_runtime)]
68pub use crate::wasm::{
69 BaseRuntimeApi, ContractEntrypoints, ContractRuntimeApi, RuntimeApiData, ServiceEntrypoints,
70 ServiceRuntimeApi, WasmContractModule, WasmExecutionError, WasmServiceModule,
71};
72pub use crate::{
73 committee::{Committee, SharedCommittees},
74 execution::{ExecutionStateView, ServiceRuntimeEndpoint},
75 execution_state_actor::{ExecutionRequest, ExecutionStateActor},
76 policy::{ProtocolFlag, ResourceControlPolicy},
77 resources::{BalanceHolder, ResourceController, ResourceTracker},
78 runtime::{
79 ContractSyncRuntimeHandle, ServiceRuntimeRequest, ServiceSyncRuntime,
80 ServiceSyncRuntimeHandle,
81 },
82 system::{
83 ChainProgress, SystemExecutionStateView, SystemMessage, SystemOperation, SystemQuery,
84 SystemResponse,
85 },
86 transaction_tracker::{PreparedCheckpoint, TransactionOutcome, TransactionTracker},
87};
88
89pub const LINERA_SOL: &str = include_str!("../solidity/Linera.sol");
92pub const LINERA_TYPES_SOL: &str = include_str!("../solidity/LineraTypes.sol");
95
96const MAX_STREAM_NAME_LEN: usize = 64;
98
99#[derive(Clone)]
101pub struct UserContractCode(Box<dyn UserContractModule>);
102
103#[derive(Clone)]
105pub struct UserServiceCode(Box<dyn UserServiceModule>);
106
107pub type UserContractInstance = Box<dyn UserContract>;
109
110pub type UserServiceInstance = Box<dyn UserService>;
112
113pub trait UserContractModule: dyn_clone::DynClone + Any + web_thread::Post + Send + Sync {
115 fn instantiate(
117 &self,
118 runtime: ContractSyncRuntimeHandle,
119 ) -> Result<UserContractInstance, ExecutionError>;
120}
121
122impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
123 fn from(module: T) -> Self {
124 Self(Box::new(module))
125 }
126}
127
128dyn_clone::clone_trait_object!(UserContractModule);
129
130pub trait UserServiceModule: dyn_clone::DynClone + Any + web_thread::Post + Send + Sync {
132 fn instantiate(
134 &self,
135 runtime: ServiceSyncRuntimeHandle,
136 ) -> Result<UserServiceInstance, ExecutionError>;
137}
138
139impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
140 fn from(module: T) -> Self {
141 Self(Box::new(module))
142 }
143}
144
145dyn_clone::clone_trait_object!(UserServiceModule);
146
147impl UserServiceCode {
148 fn instantiate(
149 &self,
150 runtime: ServiceSyncRuntimeHandle,
151 ) -> Result<UserServiceInstance, ExecutionError> {
152 self.0.instantiate(runtime)
153 }
154}
155
156impl UserContractCode {
157 fn instantiate(
158 &self,
159 runtime: ContractSyncRuntimeHandle,
160 ) -> Result<UserContractInstance, ExecutionError> {
161 self.0.instantiate(runtime)
162 }
163}
164
165pub struct JsVec<T>(pub Vec<T>);
167
168#[cfg(web)]
169const _: () = {
170 impl web_thread::AsJs for UserContractCode {
174 fn to_js(&self) -> Result<JsValue, JsValue> {
175 ((&*self.0) as &dyn Any)
176 .downcast_ref::<WasmContractModule>()
177 .expect("we only support Wasm modules on the Web for now")
178 .to_js()
179 }
180
181 fn from_js(value: JsValue) -> Result<Self, JsValue> {
182 WasmContractModule::from_js(value).map(Into::into)
183 }
184 }
185
186 impl web_thread::Post for UserContractCode {
187 fn transferables(&self) -> js_sys::Array {
188 self.0.transferables()
189 }
190 }
191
192 impl web_thread::AsJs for UserServiceCode {
193 fn to_js(&self) -> Result<JsValue, JsValue> {
194 ((&*self.0) as &dyn Any)
195 .downcast_ref::<WasmServiceModule>()
196 .expect("we only support Wasm modules on the Web for now")
197 .to_js()
198 }
199
200 fn from_js(value: JsValue) -> Result<Self, JsValue> {
201 WasmServiceModule::from_js(value).map(Into::into)
202 }
203 }
204
205 impl web_thread::Post for UserServiceCode {
206 fn transferables(&self) -> js_sys::Array {
207 self.0.transferables()
208 }
209 }
210
211 impl<T: web_thread::AsJs> web_thread::AsJs for JsVec<T> {
212 fn to_js(&self) -> Result<JsValue, JsValue> {
213 let array = self
214 .0
215 .iter()
216 .map(T::to_js)
217 .collect::<Result<js_sys::Array, _>>()?;
218 Ok(array.into())
219 }
220
221 fn from_js(value: JsValue) -> Result<Self, JsValue> {
222 let array = js_sys::Array::from(&value);
223 let v = array
224 .into_iter()
225 .map(T::from_js)
226 .collect::<Result<Vec<_>, _>>()?;
227 Ok(JsVec(v))
228 }
229 }
230
231 impl<T: web_thread::Post> web_thread::Post for JsVec<T> {
232 fn transferables(&self) -> js_sys::Array {
233 let mut array = js_sys::Array::new();
234 for x in &self.0 {
235 array = array.concat(&x.transferables());
236 }
237 array
238 }
239 }
240};
241
242#[derive(Error, Debug, strum::IntoStaticStr)]
244#[allow(missing_docs)]
245pub enum ExecutionError {
246 #[error(transparent)]
247 ViewError(#[from] ViewError),
248 #[error(transparent)]
249 ArithmeticError(#[from] ArithmeticError),
250 #[error("User application reported an error: {0}")]
251 UserError(String),
252 #[cfg(with_wasm_runtime)]
253 #[error(transparent)]
254 WasmError(#[from] WasmExecutionError),
255 #[cfg(with_revm)]
256 #[error(transparent)]
257 EvmError(#[from] EvmExecutionError),
258 #[error(transparent)]
259 DecompressionError(#[from] DecompressionError),
260 #[error("The given promise is invalid or was polled once already")]
261 InvalidPromise,
262
263 #[error("Attempted to perform a reentrant call to application {0}")]
264 ReentrantCall(ApplicationId),
265 #[error(
266 "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
267 from `finalize`"
268 )]
269 CrossApplicationCallInFinalize {
270 caller_id: Box<ApplicationId>,
271 callee_id: Box<ApplicationId>,
272 },
273 #[error("Failed to load bytecode from storage {0:?}")]
274 ApplicationBytecodeNotFound(Box<ApplicationDescription>),
275 #[error("Unsupported dynamic application load: {0:?}")]
277 UnsupportedDynamicApplicationLoad(Box<ApplicationId>),
278
279 #[error("Excessive number of bytes read from storage")]
280 ExcessiveRead,
281 #[error("Excessive number of bytes written to storage")]
282 ExcessiveWrite,
283 #[error("Block execution required too much fuel for VM {0}")]
284 MaximumFuelExceeded(VmRuntime),
285 #[error("Services running as oracles in block took longer than allowed")]
286 MaximumServiceOracleExecutionTimeExceeded,
287 #[error("Service running as an oracle produced a response that's too large")]
288 ServiceOracleResponseTooLarge,
289 #[error("Serialized size of the block exceeds limit")]
290 BlockTooLarge,
291 #[error("HTTP response exceeds the size limit of {limit} bytes, having at least {size} bytes")]
292 HttpResponseSizeLimitExceeded { limit: u64, size: u64 },
293 #[error("Runtime failed to respond to application")]
294 MissingRuntimeResponse,
295 #[error("Application is not authorized to perform system operations on this chain: {0:}")]
296 UnauthorizedApplication(ApplicationId),
297 #[error("Failed to make network reqwest: {0}")]
298 ReqwestError(#[from] reqwest::Error),
299 #[error("Encountered I/O error: {0}")]
300 IoError(#[from] std::io::Error),
301 #[error("More recorded oracle responses than expected")]
302 UnexpectedOracleResponse,
303 #[error("Invalid JSON: {0}")]
304 JsonError(#[from] serde_json::Error),
305 #[error(transparent)]
306 BcsError(#[from] bcs::Error),
307 #[error("Recorded response for oracle query has the wrong type")]
308 OracleResponseMismatch,
309 #[error("Service oracle query tried to create operations: {0:?}")]
310 ServiceOracleQueryOperations(Vec<Operation>),
311 #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
312 AssertBefore {
313 timestamp: Timestamp,
314 local_time: Timestamp,
315 },
316
317 #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
318 StreamNameTooLong,
319 #[error("Blob exceeds size limit")]
320 BlobTooLarge,
321 #[error("Bytecode exceeds size limit")]
322 BytecodeTooLarge,
323 #[error("Attempt to perform an HTTP request to an unauthorized host: {0:?}")]
324 UnauthorizedHttpRequest(reqwest::Url),
325 #[error("Attempt to perform an HTTP request to an invalid URL")]
326 InvalidUrlForHttpRequest(#[from] url::ParseError),
327 #[error("Worker thread failure: {0:?}")]
328 Thread(#[from] web_thread::Error),
329 #[error("Blobs not found: {0:?}")]
330 BlobsNotFound(Vec<BlobId>),
331 #[error("Events not found: {0:?}")]
332 EventsNotFound(Vec<EventId>),
333
334 #[error("Invalid HTTP header name used for HTTP request")]
335 InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
336 #[error("Invalid HTTP header value used for HTTP request")]
337 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
338
339 #[error("No NetworkDescription found in storage")]
340 NoNetworkDescriptionFound,
341 #[error("{epoch:?} is not recognized by chain {chain_id:}")]
342 InvalidEpoch { chain_id: ChainId, epoch: Epoch },
343 #[error("Transfer must have positive amount")]
344 IncorrectTransferAmount,
345 #[error("Transfer from owned account must be authenticated by the right owner")]
346 UnauthenticatedTransferOwner,
347 #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
348 InsufficientBalance {
349 balance: Amount,
350 account: AccountOwner,
351 },
352 #[error("Required execution fees exceeded the total funding available. Fees {fees}, available balance: {balance}")]
353 FeesExceedFunding { fees: Amount, balance: Amount },
354 #[error("Claim must have positive amount")]
355 IncorrectClaimAmount,
356 #[error("Claim must be authenticated by the right owner")]
357 UnauthenticatedClaimOwner,
358 #[error("The transferred amount must not exceed the allowance for spender {spender} from owner {owner}: {allowance}")]
359 InsufficientAllowance {
360 allowance: Amount,
361 owner: AccountOwner,
362 spender: AccountOwner,
363 },
364 #[error("Admin operations are only allowed on the admin chain.")]
365 AdminOperationOnNonAdminChain,
366 #[error("Failed to create new committee: expected {expected}, but got {provided}")]
367 InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
368 #[error("Failed to remove committee")]
369 InvalidCommitteeRemoval,
370 #[error("No recorded response for oracle query")]
371 MissingOracleResponse,
372 #[error("process_streams was not called for all stream updates")]
373 UnprocessedStreams,
374 #[error("Internal error: {0}")]
375 InternalError(&'static str),
376 #[error("UpdateStream is outdated")]
377 OutdatedUpdateStream,
378 #[error("UpdateStream references an application that is not subscribed")]
379 UnsubscribedUpdateStream,
380 #[error("Checkpoint precondition failed: {0}")]
381 CheckpointPreconditionFailed(&'static str),
382}
383
384impl ExecutionError {
385 pub fn is_local(&self) -> bool {
389 match self {
390 ExecutionError::ArithmeticError(_)
391 | ExecutionError::UserError(_)
392 | ExecutionError::DecompressionError(_)
393 | ExecutionError::InvalidPromise
394 | ExecutionError::CrossApplicationCallInFinalize { .. }
395 | ExecutionError::ReentrantCall(_)
396 | ExecutionError::ApplicationBytecodeNotFound(_)
397 | ExecutionError::UnsupportedDynamicApplicationLoad(_)
398 | ExecutionError::ExcessiveRead
399 | ExecutionError::ExcessiveWrite
400 | ExecutionError::MaximumFuelExceeded(_)
401 | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
402 | ExecutionError::ServiceOracleResponseTooLarge
403 | ExecutionError::BlockTooLarge
404 | ExecutionError::HttpResponseSizeLimitExceeded { .. }
405 | ExecutionError::UnauthorizedApplication(_)
406 | ExecutionError::UnexpectedOracleResponse
407 | ExecutionError::JsonError(_)
408 | ExecutionError::BcsError(_)
409 | ExecutionError::OracleResponseMismatch
410 | ExecutionError::ServiceOracleQueryOperations(_)
411 | ExecutionError::AssertBefore { .. }
412 | ExecutionError::StreamNameTooLong
413 | ExecutionError::BlobTooLarge
414 | ExecutionError::BytecodeTooLarge
415 | ExecutionError::UnauthorizedHttpRequest(_)
416 | ExecutionError::InvalidUrlForHttpRequest(_)
417 | ExecutionError::BlobsNotFound(_)
418 | ExecutionError::EventsNotFound(_)
419 | ExecutionError::InvalidHeaderName(_)
420 | ExecutionError::InvalidHeaderValue(_)
421 | ExecutionError::InvalidEpoch { .. }
422 | ExecutionError::IncorrectTransferAmount
423 | ExecutionError::UnauthenticatedTransferOwner
424 | ExecutionError::InsufficientBalance { .. }
425 | ExecutionError::FeesExceedFunding { .. }
426 | ExecutionError::IncorrectClaimAmount
427 | ExecutionError::UnauthenticatedClaimOwner
428 | ExecutionError::InsufficientAllowance { .. }
429 | ExecutionError::AdminOperationOnNonAdminChain
430 | ExecutionError::InvalidCommitteeEpoch { .. }
431 | ExecutionError::InvalidCommitteeRemoval
432 | ExecutionError::MissingOracleResponse
433 | ExecutionError::UnprocessedStreams
434 | ExecutionError::OutdatedUpdateStream
435 | ExecutionError::UnsubscribedUpdateStream
436 | ExecutionError::CheckpointPreconditionFailed(_)
437 | ExecutionError::ViewError(ViewError::NotFound(_)) => false,
438 #[cfg(with_wasm_runtime)]
439 ExecutionError::WasmError(_) => false,
440 #[cfg(with_revm)]
441 ExecutionError::EvmError(..) => false,
442 ExecutionError::MissingRuntimeResponse
443 | ExecutionError::ViewError(_)
444 | ExecutionError::ReqwestError(_)
445 | ExecutionError::Thread(_)
446 | ExecutionError::NoNetworkDescriptionFound
447 | ExecutionError::InternalError(_)
448 | ExecutionError::IoError(_) => true,
449 }
450 }
451
452 pub fn error_type(&self) -> String {
455 let variant: &'static str = self.into();
456 format!("ExecutionError::{variant}")
457 }
458
459 pub fn is_limit_error(&self) -> bool {
464 matches!(
465 self,
466 ExecutionError::ExcessiveRead
467 | ExecutionError::ExcessiveWrite
468 | ExecutionError::MaximumFuelExceeded(_)
469 | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
470 | ExecutionError::BlockTooLarge
471 )
472 }
473
474 pub fn is_transient_error(&self) -> bool {
480 matches!(
481 self,
482 ExecutionError::BlobsNotFound(_) | ExecutionError::EventsNotFound(_)
483 )
484 }
485}
486
487pub trait UserContract {
489 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
491
492 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
494
495 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
497
498 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
500
501 fn summarize_events(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
504
505 fn finalize(&mut self) -> Result<(), ExecutionError>;
507}
508
509pub trait UserService {
511 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
513}
514
515#[derive(Clone, Copy)]
517pub struct ExecutionRuntimeConfig {
518 pub allow_application_logs: bool,
521}
522
523impl Default for ExecutionRuntimeConfig {
524 fn default() -> Self {
525 Self {
526 allow_application_logs: true,
527 }
528 }
529}
530
531#[cfg_attr(not(web), async_trait)]
534#[cfg_attr(web, async_trait(?Send))]
535pub trait ExecutionRuntimeContext {
536 fn chain_id(&self) -> ChainId;
538
539 fn thread_pool(&self) -> &Arc<ThreadPool>;
541
542 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
544
545 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>>;
547
548 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>>;
550
551 async fn get_user_contract(
553 &self,
554 description: &ApplicationDescription,
555 txn_tracker: &TransactionTracker,
556 ) -> Result<UserContractCode, ExecutionError>;
557
558 async fn get_user_service(
560 &self,
561 description: &ApplicationDescription,
562 txn_tracker: &TransactionTracker,
563 ) -> Result<UserServiceCode, ExecutionError>;
564
565 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError>;
567
568 async fn get_event(&self, event_id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError>;
570
571 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
573
574 async fn get_or_load_committee_by_hash(
582 &self,
583 hash: CryptoHash,
584 ) -> Result<Arc<Committee>, ExecutionError>;
585
586 async fn get_committee_hashes(
588 &self,
589 epoch_range: RangeInclusive<Epoch>,
590 ) -> Result<BTreeMap<Epoch, CryptoHash>, ExecutionError> {
591 let net_description = self
592 .get_network_description()
593 .await?
594 .ok_or(ExecutionError::NoNetworkDescriptionFound)?;
595 let committee_hashes = futures::future::join_all(
596 (epoch_range.start().0..=epoch_range.end().0).map(|epoch| async move {
597 if epoch == 0 {
598 Ok((Epoch(epoch), net_description.genesis_committee_blob_hash))
600 } else {
601 let event_id = EventId {
602 chain_id: net_description.admin_chain_id,
603 stream_id: StreamId::system(EPOCH_STREAM_NAME),
604 index: epoch,
605 };
606 let event = self
607 .get_event(event_id.clone())
608 .await?
609 .ok_or_else(|| ExecutionError::EventsNotFound(vec![event_id]))?;
610 let event_data: EpochEventData = bcs::from_bytes(&event)?;
611 Ok((Epoch(epoch), event_data.blob_hash))
612 }
613 }),
614 )
615 .await;
616 let missing_events = committee_hashes
617 .iter()
618 .filter_map(|result| {
619 if let Err(ExecutionError::EventsNotFound(event_ids)) = result {
620 return Some(event_ids);
621 }
622 None
623 })
624 .flatten()
625 .cloned()
626 .collect::<Vec<_>>();
627 ensure!(
628 missing_events.is_empty(),
629 ExecutionError::EventsNotFound(missing_events)
630 );
631 committee_hashes.into_iter().collect()
632 }
633
634 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
636
637 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
639
640 #[cfg(with_testing)]
642 async fn add_blobs(
643 &self,
644 blobs: impl IntoIterator<Item = Blob> + Send,
645 ) -> Result<(), ViewError>;
646
647 #[cfg(with_testing)]
649 async fn add_events(
650 &self,
651 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
652 ) -> Result<(), ViewError>;
653}
654
655#[derive(Clone, Copy, Debug)]
657pub struct OperationContext {
658 pub chain_id: ChainId,
660 #[debug(skip_if = Option::is_none)]
662 pub authenticated_owner: Option<AccountOwner>,
663 pub height: BlockHeight,
665 pub round: Option<u32>,
667 pub timestamp: Timestamp,
669}
670
671#[derive(Clone, Copy, Debug)]
673pub struct MessageContext {
674 pub chain_id: ChainId,
676 pub origin: ChainId,
678 pub origin_timestamp: Timestamp,
680 pub is_bouncing: bool,
682 #[debug(skip_if = Option::is_none)]
684 pub authenticated_owner: Option<AccountOwner>,
685 #[debug(skip_if = Option::is_none)]
687 pub refund_grant_to: Option<Account>,
688 pub height: BlockHeight,
690 pub round: Option<u32>,
692 pub timestamp: Timestamp,
694}
695
696#[derive(Clone, Copy, Debug)]
698pub struct ProcessStreamsContext {
699 pub chain_id: ChainId,
701 pub height: BlockHeight,
703 pub round: Option<u32>,
705 pub timestamp: Timestamp,
707}
708
709impl From<MessageContext> for ProcessStreamsContext {
710 fn from(context: MessageContext) -> Self {
711 Self {
712 chain_id: context.chain_id,
713 height: context.height,
714 round: context.round,
715 timestamp: context.timestamp,
716 }
717 }
718}
719
720impl From<OperationContext> for ProcessStreamsContext {
721 fn from(context: OperationContext) -> Self {
722 Self {
723 chain_id: context.chain_id,
724 height: context.height,
725 round: context.round,
726 timestamp: context.timestamp,
727 }
728 }
729}
730
731#[derive(Clone, Copy, Debug)]
733pub struct FinalizeContext {
734 pub chain_id: ChainId,
736 #[debug(skip_if = Option::is_none)]
738 pub authenticated_owner: Option<AccountOwner>,
739 pub height: BlockHeight,
741 pub round: Option<u32>,
743}
744
745#[derive(Clone, Copy, Debug, Eq, PartialEq)]
747pub struct QueryContext {
748 pub chain_id: ChainId,
750 pub next_block_height: BlockHeight,
752 pub local_time: Timestamp,
754}
755
756pub trait BaseRuntime {
758 type Read: fmt::Debug + Send + Sync;
760 type ContainsKey: fmt::Debug + Send + Sync;
762 type ContainsKeys: fmt::Debug + Send + Sync;
764 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
766 type ReadValueBytes: fmt::Debug + Send + Sync;
768 type FindKeysByPrefix: fmt::Debug + Send + Sync;
770 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
772
773 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
775
776 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
778
779 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
781
782 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
784
785 fn read_application_description(
787 &mut self,
788 application_id: ApplicationId,
789 ) -> Result<ApplicationDescription, ExecutionError>;
790
791 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
793
794 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
796
797 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
799
800 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
802
803 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
805
806 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
808
809 fn read_allowance(
811 &mut self,
812 owner: AccountOwner,
813 spender: AccountOwner,
814 ) -> Result<Amount, ExecutionError>;
815
816 fn read_allowances(
818 &mut self,
819 ) -> Result<Vec<(AccountOwner, AccountOwner, Amount)>, ExecutionError>;
820
821 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
823
824 fn application_permissions(&mut self) -> Result<ApplicationPermissions, ExecutionError>;
826
827 #[cfg(feature = "test")]
829 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
830 let promise = self.contains_key_new(key)?;
831 self.contains_key_wait(&promise)
832 }
833
834 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
836
837 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
839
840 #[cfg(feature = "test")]
842 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
843 let promise = self.contains_keys_new(keys)?;
844 self.contains_keys_wait(&promise)
845 }
846
847 fn contains_keys_new(
849 &mut self,
850 keys: Vec<Vec<u8>>,
851 ) -> Result<Self::ContainsKeys, ExecutionError>;
852
853 fn contains_keys_wait(
855 &mut self,
856 promise: &Self::ContainsKeys,
857 ) -> Result<Vec<bool>, ExecutionError>;
858
859 #[cfg(feature = "test")]
861 fn read_multi_values_bytes(
862 &mut self,
863 keys: Vec<Vec<u8>>,
864 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
865 let promise = self.read_multi_values_bytes_new(keys)?;
866 self.read_multi_values_bytes_wait(&promise)
867 }
868
869 fn read_multi_values_bytes_new(
871 &mut self,
872 keys: Vec<Vec<u8>>,
873 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
874
875 fn read_multi_values_bytes_wait(
877 &mut self,
878 promise: &Self::ReadMultiValuesBytes,
879 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
880
881 #[cfg(feature = "test")]
883 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
884 let promise = self.read_value_bytes_new(key)?;
885 self.read_value_bytes_wait(&promise)
886 }
887
888 fn read_value_bytes_new(
890 &mut self,
891 key: Vec<u8>,
892 ) -> Result<Self::ReadValueBytes, ExecutionError>;
893
894 fn read_value_bytes_wait(
896 &mut self,
897 promise: &Self::ReadValueBytes,
898 ) -> Result<Option<Vec<u8>>, ExecutionError>;
899
900 fn find_keys_by_prefix_new(
902 &mut self,
903 key_prefix: Vec<u8>,
904 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
905
906 fn find_keys_by_prefix_wait(
908 &mut self,
909 promise: &Self::FindKeysByPrefix,
910 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
911
912 #[cfg(feature = "test")]
914 #[expect(clippy::type_complexity)]
915 fn find_key_values_by_prefix(
916 &mut self,
917 key_prefix: Vec<u8>,
918 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
919 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
920 self.find_key_values_by_prefix_wait(&promise)
921 }
922
923 fn find_key_values_by_prefix_new(
925 &mut self,
926 key_prefix: Vec<u8>,
927 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
928
929 #[expect(clippy::type_complexity)]
931 fn find_key_values_by_prefix_wait(
932 &mut self,
933 promise: &Self::FindKeyValuesByPrefix,
934 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
935
936 fn perform_http_request(
938 &mut self,
939 request: http::Request,
940 ) -> Result<http::Response, ExecutionError>;
941
942 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
948
949 fn read_data_blob(&mut self, hash: DataBlobHash) -> Result<Vec<u8>, ExecutionError>;
951
952 fn assert_data_blob_exists(&mut self, hash: DataBlobHash) -> Result<(), ExecutionError>;
954
955 fn has_empty_storage(&mut self, application: ApplicationId) -> Result<bool, ExecutionError>;
957
958 fn maximum_blob_size(&mut self) -> Result<u64, ExecutionError>;
960
961 fn allow_application_logs(&mut self) -> Result<bool, ExecutionError>;
964
965 #[cfg(web)]
968 fn send_log(&mut self, message: String, level: tracing::log::Level);
969}
970
971pub trait ServiceRuntime: BaseRuntime {
973 fn try_query_application(
975 &mut self,
976 queried_id: ApplicationId,
977 argument: Vec<u8>,
978 ) -> Result<Vec<u8>, ExecutionError>;
979
980 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
982
983 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
985}
986
987pub trait ContractRuntime: BaseRuntime {
989 fn authenticated_owner(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
991
992 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
995
996 fn message_origin_chain_id(&mut self) -> Result<Option<ChainId>, ExecutionError>;
998
999 fn message_origin_timestamp(&mut self) -> Result<Option<Timestamp>, ExecutionError>;
1002
1003 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
1006
1007 fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
1009
1010 fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
1012
1013 fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
1015
1016 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
1018
1019 fn transfer(
1021 &mut self,
1022 source: AccountOwner,
1023 destination: Account,
1024 amount: Amount,
1025 ) -> Result<(), ExecutionError>;
1026
1027 fn claim(
1029 &mut self,
1030 source: Account,
1031 destination: Account,
1032 amount: Amount,
1033 ) -> Result<(), ExecutionError>;
1034
1035 fn approve(
1037 &mut self,
1038 owner: AccountOwner,
1039 spender: AccountOwner,
1040 amount: Amount,
1041 ) -> Result<(), ExecutionError>;
1042
1043 fn transfer_from(
1045 &mut self,
1046 owner: AccountOwner,
1047 spender: AccountOwner,
1048 destination: Account,
1049 amount: Amount,
1050 ) -> Result<(), ExecutionError>;
1051
1052 fn try_call_application(
1055 &mut self,
1056 authenticated: bool,
1057 callee_id: ApplicationId,
1058 argument: Vec<u8>,
1059 ) -> Result<Vec<u8>, ExecutionError>;
1060
1061 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
1063
1064 fn read_event(
1068 &mut self,
1069 chain_id: ChainId,
1070 stream_name: StreamName,
1071 index: u32,
1072 ) -> Result<Vec<u8>, ExecutionError>;
1073
1074 fn subscribe_to_events(
1076 &mut self,
1077 chain_id: ChainId,
1078 application_id: ApplicationId,
1079 stream_name: StreamName,
1080 ) -> Result<(), ExecutionError>;
1081
1082 fn unsubscribe_from_events(
1084 &mut self,
1085 chain_id: ChainId,
1086 application_id: ApplicationId,
1087 stream_name: StreamName,
1088 ) -> Result<(), ExecutionError>;
1089
1090 fn query_service(
1092 &mut self,
1093 application_id: ApplicationId,
1094 query: Vec<u8>,
1095 ) -> Result<Vec<u8>, ExecutionError>;
1096
1097 fn open_chain(
1099 &mut self,
1100 ownership: ChainOwnership,
1101 application_permissions: ApplicationPermissions,
1102 account: AccountOwner,
1103 balance: Amount,
1104 ) -> Result<ChainId, ExecutionError>;
1105
1106 fn close_chain(&mut self) -> Result<(), ExecutionError>;
1108
1109 fn change_ownership(&mut self, ownership: ChainOwnership) -> Result<(), ExecutionError>;
1111
1112 fn change_application_permissions(
1114 &mut self,
1115 application_permissions: ApplicationPermissions,
1116 ) -> Result<(), ExecutionError>;
1117
1118 fn create_application(
1120 &mut self,
1121 module_id: ModuleId,
1122 parameters: Vec<u8>,
1123 argument: Vec<u8>,
1124 required_application_ids: Vec<ApplicationId>,
1125 ) -> Result<ApplicationId, ExecutionError>;
1126
1127 fn peek_application_index(&mut self) -> Result<u32, ExecutionError>;
1130
1131 fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<DataBlobHash, ExecutionError>;
1133
1134 fn publish_module(
1137 &mut self,
1138 contract: Bytecode,
1139 service: Bytecode,
1140 vm_runtime: VmRuntime,
1141 formats: Option<Vec<u8>>,
1142 ) -> Result<ModuleId, ExecutionError>;
1143
1144 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
1146
1147 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
1149}
1150
1151#[derive(
1153 Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative, strum::AsRefStr,
1154)]
1155pub enum Operation {
1156 System(Box<SystemOperation>),
1158 User {
1160 application_id: ApplicationId,
1162 #[serde(with = "serde_bytes")]
1164 #[debug(with = "hex_debug")]
1165 bytes: Vec<u8>,
1166 },
1167}
1168
1169impl BcsHashable<'_> for Operation {}
1170
1171#[derive(
1173 Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Allocative, strum::AsRefStr,
1174)]
1175pub enum Message {
1176 System(SystemMessage),
1178 User {
1180 application_id: ApplicationId,
1182 #[serde(with = "serde_bytes")]
1184 #[debug(with = "hex_debug")]
1185 bytes: Vec<u8>,
1186 },
1187}
1188
1189#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1191pub enum Query {
1192 System(SystemQuery),
1194 User {
1196 application_id: ApplicationId,
1198 #[serde(with = "serde_bytes")]
1200 #[debug(with = "hex_debug")]
1201 bytes: Vec<u8>,
1202 },
1203}
1204
1205#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1207pub struct QueryOutcome<Response = QueryResponse> {
1208 pub response: Response,
1210 pub operations: Vec<Operation>,
1212}
1213
1214impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
1215 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
1216 let QueryOutcome {
1217 response,
1218 operations,
1219 } = system_outcome;
1220
1221 QueryOutcome {
1222 response: QueryResponse::System(response),
1223 operations,
1224 }
1225 }
1226}
1227
1228impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
1229 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
1230 let QueryOutcome {
1231 response,
1232 operations,
1233 } = user_service_outcome;
1234
1235 QueryOutcome {
1236 response: QueryResponse::User(response),
1237 operations,
1238 }
1239 }
1240}
1241
1242#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
1244pub enum QueryResponse {
1245 System(SystemResponse),
1247 User(
1249 #[serde(with = "serde_bytes")]
1250 #[debug(with = "hex_debug")]
1251 Vec<u8>,
1252 ),
1253}
1254
1255#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy, Allocative)]
1257pub enum MessageKind {
1258 Simple,
1260 Protected,
1263 Tracked,
1266 Bouncing,
1268}
1269
1270impl Display for MessageKind {
1271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1272 match self {
1273 MessageKind::Simple => write!(f, "Simple"),
1274 MessageKind::Protected => write!(f, "Protected"),
1275 MessageKind::Tracked => write!(f, "Tracked"),
1276 MessageKind::Bouncing => write!(f, "Bouncing"),
1277 }
1278 }
1279}
1280
1281#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject, Allocative)]
1283pub struct OutgoingMessage {
1284 pub destination: ChainId,
1286 #[debug(skip_if = Option::is_none)]
1288 pub authenticated_owner: Option<AccountOwner>,
1289 #[debug(skip_if = Amount::is_zero)]
1291 pub grant: Amount,
1292 #[debug(skip_if = Option::is_none)]
1294 pub refund_grant_to: Option<Account>,
1295 pub kind: MessageKind,
1297 pub message: Message,
1299}
1300
1301impl BcsHashable<'_> for OutgoingMessage {}
1302
1303impl OutgoingMessage {
1304 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
1306 OutgoingMessage {
1307 destination: recipient,
1308 authenticated_owner: None,
1309 grant: Amount::ZERO,
1310 refund_grant_to: None,
1311 kind: MessageKind::Simple,
1312 message: message.into(),
1313 }
1314 }
1315
1316 pub fn with_kind(mut self, kind: MessageKind) -> Self {
1318 self.kind = kind;
1319 self
1320 }
1321
1322 pub fn with_authenticated_owner(mut self, authenticated_owner: Option<AccountOwner>) -> Self {
1324 self.authenticated_owner = authenticated_owner;
1325 self
1326 }
1327}
1328
1329impl OperationContext {
1330 fn refund_grant_to(&self) -> Option<Account> {
1333 self.authenticated_owner.map(|owner| Account {
1334 chain_id: self.chain_id,
1335 owner,
1336 })
1337 }
1338}
1339
1340#[cfg(with_testing)]
1342#[derive(Clone)]
1343pub struct TestExecutionRuntimeContext {
1344 chain_id: ChainId,
1345 thread_pool: Arc<ThreadPool>,
1346 execution_runtime_config: ExecutionRuntimeConfig,
1347 user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
1348 user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
1349 blobs: Arc<papaya::HashMap<BlobId, Blob>>,
1350 events: Arc<papaya::HashMap<EventId, Vec<u8>>>,
1351}
1352
1353#[cfg(with_testing)]
1354impl TestExecutionRuntimeContext {
1355 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1357 Self {
1358 chain_id,
1359 thread_pool: Arc::new(ThreadPool::new(20)),
1360 execution_runtime_config,
1361 user_contracts: Arc::default(),
1362 user_services: Arc::default(),
1363 blobs: Arc::default(),
1364 events: Arc::default(),
1365 }
1366 }
1367}
1368
1369#[cfg(with_testing)]
1370#[cfg_attr(not(web), async_trait)]
1371#[cfg_attr(web, async_trait(?Send))]
1372impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1373 fn chain_id(&self) -> ChainId {
1374 self.chain_id
1375 }
1376
1377 fn thread_pool(&self) -> &Arc<ThreadPool> {
1378 &self.thread_pool
1379 }
1380
1381 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1382 self.execution_runtime_config
1383 }
1384
1385 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>> {
1386 &self.user_contracts
1387 }
1388
1389 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>> {
1390 &self.user_services
1391 }
1392
1393 async fn get_user_contract(
1394 &self,
1395 description: &ApplicationDescription,
1396 _txn_tracker: &TransactionTracker,
1397 ) -> Result<UserContractCode, ExecutionError> {
1398 let application_id: ApplicationId = description.into();
1399 let pinned = self.user_contracts().pin();
1400 Ok(pinned
1401 .get(&application_id)
1402 .ok_or_else(|| {
1403 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1404 })?
1405 .clone())
1406 }
1407
1408 async fn get_user_service(
1409 &self,
1410 description: &ApplicationDescription,
1411 _txn_tracker: &TransactionTracker,
1412 ) -> Result<UserServiceCode, ExecutionError> {
1413 let application_id: ApplicationId = description.into();
1414 let pinned = self.user_services().pin();
1415 Ok(pinned
1416 .get(&application_id)
1417 .ok_or_else(|| {
1418 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1419 })?
1420 .clone())
1421 }
1422
1423 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError> {
1424 Ok(self.blobs.pin().get(&blob_id).cloned().map(Arc::new))
1425 }
1426
1427 async fn get_event(&self, event_id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError> {
1428 Ok(self.events.pin().get(&event_id).cloned().map(Arc::new))
1429 }
1430
1431 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1432 let pinned = self.blobs.pin();
1433 let genesis_committee_blob_hash = pinned
1434 .iter()
1435 .find(|(_, blob)| blob.content().blob_type() == BlobType::Committee)
1436 .map_or_else(
1437 || CryptoHash::test_hash("genesis committee"),
1438 |(_, blob)| blob.id().hash,
1439 );
1440 Ok(Some(NetworkDescription {
1441 admin_chain_id: dummy_chain_description(0).id(),
1442 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1443 genesis_timestamp: Timestamp::from(0),
1444 genesis_committee_blob_hash,
1445 name: "dummy network description".to_string(),
1446 }))
1447 }
1448
1449 async fn get_or_load_committee_by_hash(
1450 &self,
1451 hash: CryptoHash,
1452 ) -> Result<Arc<Committee>, ExecutionError> {
1453 let blob_id = BlobId::new(hash, BlobType::Committee);
1454 let blob = self
1455 .blobs
1456 .pin()
1457 .get(&blob_id)
1458 .cloned()
1459 .ok_or(ExecutionError::BlobsNotFound(vec![blob_id]))?;
1460 let committee: Committee = bcs::from_bytes(blob.bytes())?;
1461 Ok(Arc::new(committee))
1462 }
1463
1464 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1465 Ok(self.blobs.pin().contains_key(&blob_id))
1466 }
1467
1468 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1469 Ok(self.events.pin().contains_key(&event_id))
1470 }
1471
1472 #[cfg(with_testing)]
1473 async fn add_blobs(
1474 &self,
1475 blobs: impl IntoIterator<Item = Blob> + Send,
1476 ) -> Result<(), ViewError> {
1477 let pinned = self.blobs.pin();
1478 for blob in blobs {
1479 pinned.insert(blob.id(), blob);
1480 }
1481
1482 Ok(())
1483 }
1484
1485 #[cfg(with_testing)]
1486 async fn add_events(
1487 &self,
1488 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1489 ) -> Result<(), ViewError> {
1490 let pinned = self.events.pin();
1491 for (event_id, bytes) in events {
1492 pinned.insert(event_id, bytes);
1493 }
1494
1495 Ok(())
1496 }
1497}
1498
1499impl From<SystemOperation> for Operation {
1500 fn from(operation: SystemOperation) -> Self {
1501 Operation::System(Box::new(operation))
1502 }
1503}
1504
1505impl Operation {
1506 pub fn system(operation: SystemOperation) -> Self {
1508 Operation::System(Box::new(operation))
1509 }
1510
1511 #[cfg(with_testing)]
1513 pub fn user<A: Abi>(
1514 application_id: ApplicationId<A>,
1515 operation: &A::Operation,
1516 ) -> Result<Self, bcs::Error> {
1517 Self::user_without_abi(application_id.forget_abi(), operation)
1518 }
1519
1520 #[cfg(with_testing)]
1523 pub fn user_without_abi(
1524 application_id: ApplicationId,
1525 operation: &impl Serialize,
1526 ) -> Result<Self, bcs::Error> {
1527 Ok(Operation::User {
1528 application_id,
1529 bytes: bcs::to_bytes(&operation)?,
1530 })
1531 }
1532
1533 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1536 match self {
1537 Operation::System(system_operation) => Some(system_operation),
1538 Operation::User { .. } => None,
1539 }
1540 }
1541
1542 pub fn application_id(&self) -> GenericApplicationId {
1544 match self {
1545 Self::System(_) => GenericApplicationId::System,
1546 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1547 }
1548 }
1549
1550 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1552 match self.as_system_operation() {
1553 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1554 vec![BlobId::new(*blob_hash, BlobType::Data)]
1555 }
1556 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1557 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1558 }
1559 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1560 _ => vec![],
1561 }
1562 }
1563
1564 pub fn is_exempt_from_permissions(&self) -> bool {
1566 let Operation::System(system_op) = self else {
1567 return false;
1568 };
1569 matches!(
1570 **system_op,
1571 SystemOperation::ProcessNewEpoch(_) | SystemOperation::UpdateStream { .. }
1572 )
1573 }
1574
1575 pub fn is_update_stream(&self) -> bool {
1577 let Operation::System(system_op) = self else {
1578 return false;
1579 };
1580 matches!(**system_op, SystemOperation::UpdateStream { .. })
1581 }
1582
1583 pub fn is_checkpoint(&self) -> bool {
1585 let Operation::System(system_op) = self else {
1586 return false;
1587 };
1588 matches!(**system_op, SystemOperation::Checkpoint)
1589 }
1590}
1591
1592impl From<SystemMessage> for Message {
1593 fn from(message: SystemMessage) -> Self {
1594 Message::System(message)
1595 }
1596}
1597
1598impl Message {
1599 pub fn system(message: SystemMessage) -> Self {
1601 Message::System(message)
1602 }
1603
1604 pub fn is_checkpoint_ack(&self) -> bool {
1606 matches!(self, Message::System(SystemMessage::CheckpointAck { .. }))
1607 }
1608
1609 pub fn user<A, M: Serialize>(
1612 application_id: ApplicationId<A>,
1613 message: &M,
1614 ) -> Result<Self, bcs::Error> {
1615 let application_id = application_id.forget_abi();
1616 let bytes = bcs::to_bytes(&message)?;
1617 Ok(Message::User {
1618 application_id,
1619 bytes,
1620 })
1621 }
1622
1623 pub fn application_id(&self) -> GenericApplicationId {
1625 match self {
1626 Self::System(_) => GenericApplicationId::System,
1627 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1628 }
1629 }
1630}
1631
1632impl From<SystemQuery> for Query {
1633 fn from(query: SystemQuery) -> Self {
1634 Query::System(query)
1635 }
1636}
1637
1638impl Query {
1639 pub fn system(query: SystemQuery) -> Self {
1641 Query::System(query)
1642 }
1643
1644 pub fn user<A: Abi>(
1646 application_id: ApplicationId<A>,
1647 query: &A::Query,
1648 ) -> Result<Self, serde_json::Error> {
1649 Self::user_without_abi(application_id.forget_abi(), query)
1650 }
1651
1652 pub fn user_without_abi(
1655 application_id: ApplicationId,
1656 query: &impl Serialize,
1657 ) -> Result<Self, serde_json::Error> {
1658 Ok(Query::User {
1659 application_id,
1660 bytes: serde_json::to_vec(&query)?,
1661 })
1662 }
1663
1664 pub fn application_id(&self) -> GenericApplicationId {
1666 match self {
1667 Self::System(_) => GenericApplicationId::System,
1668 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1669 }
1670 }
1671}
1672
1673impl From<SystemResponse> for QueryResponse {
1674 fn from(response: SystemResponse) -> Self {
1675 QueryResponse::System(response)
1676 }
1677}
1678
1679impl From<Vec<u8>> for QueryResponse {
1680 fn from(response: Vec<u8>) -> Self {
1681 QueryResponse::User(response)
1682 }
1683}
1684
1685#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1689pub enum BlobOrigin {
1690 Genesis,
1695 Published {
1698 chain_id: ChainId,
1700 block_height: BlockHeight,
1702 },
1703}
1704
1705#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1707pub struct BlobState {
1708 pub origin: BlobOrigin,
1710 pub last_used_by: Option<CryptoHash>,
1714 pub epoch: Option<Epoch>,
1716}
1717
1718impl BlobState {
1719 pub const GENESIS: BlobState = BlobState {
1722 origin: BlobOrigin::Genesis,
1723 last_used_by: None,
1724 epoch: None,
1725 };
1726}
1727
1728#[derive(Clone, Copy, Display)]
1730#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1731#[allow(missing_docs)]
1732pub enum WasmRuntime {
1733 #[cfg(with_wasmer)]
1734 #[default]
1735 #[display("wasmer")]
1736 Wasmer,
1737 #[cfg(with_wasmtime)]
1738 #[cfg_attr(not(with_wasmer), default)]
1739 #[display("wasmtime")]
1740 Wasmtime,
1741}
1742
1743#[derive(Clone, Copy, Display)]
1745#[cfg_attr(with_revm, derive(Debug, Default))]
1746#[allow(missing_docs)]
1747pub enum EvmRuntime {
1748 #[cfg(with_revm)]
1749 #[default]
1750 #[display("revm")]
1751 Revm,
1752}
1753
1754pub trait WithWasmDefault {
1756 fn with_wasm_default(self) -> Self;
1758}
1759
1760impl WithWasmDefault for Option<WasmRuntime> {
1761 fn with_wasm_default(self) -> Self {
1762 #[cfg(with_wasm_runtime)]
1763 {
1764 Some(self.unwrap_or_default())
1765 }
1766 #[cfg(not(with_wasm_runtime))]
1767 {
1768 None
1769 }
1770 }
1771}
1772
1773impl FromStr for WasmRuntime {
1774 type Err = InvalidWasmRuntime;
1775
1776 fn from_str(string: &str) -> Result<Self, Self::Err> {
1777 match string {
1778 #[cfg(with_wasmer)]
1779 "wasmer" => Ok(WasmRuntime::Wasmer),
1780 #[cfg(with_wasmtime)]
1781 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1782 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1783 }
1784 }
1785}
1786
1787#[derive(Clone, Debug, Error)]
1789#[error("{0:?} is not a valid WebAssembly runtime")]
1790pub struct InvalidWasmRuntime(String);
1791
1792doc_scalar!(Operation, "An operation to be executed in a block");
1793doc_scalar!(
1794 Message,
1795 "A message to be sent and possibly executed in the receiver's block."
1796);
1797doc_scalar!(MessageKind, "The kind of outgoing message being sent");
1798
1799#[cfg(with_metrics)]
1805pub fn init_metrics() {
1806 linera_base::init_metrics();
1807 linera_views::init_metrics();
1808 #[cfg(with_revm)]
1809 evm::revm::metrics::init_metrics();
1810 execution_state_actor::metrics::init_metrics();
1811 system::metrics::init_metrics();
1812 #[cfg(with_wasm_runtime)]
1813 wasm::metrics::init_metrics();
1814}