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 async_graphql::SimpleObject;
26use async_trait::async_trait;
27use custom_debug_derive::Debug;
28use derive_more::Display;
29#[cfg(web)]
30use js_sys::wasm_bindgen::JsValue;
31use linera_base::{
32 abi::Abi,
33 crypto::{BcsHashable, CryptoHash},
34 data_types::{
35 Amount, ApplicationDescription, ApplicationPermissions, ArithmeticError, Blob, BlockHeight,
36 Bytecode, DecompressionError, Epoch, NetworkDescription, SendMessageRequest, StreamUpdate,
37 Timestamp,
38 },
39 doc_scalar, hex_debug, http,
40 identifiers::{
41 Account, AccountOwner, ApplicationId, BlobId, BlobType, ChainId, DataBlobHash, EventId,
42 GenericApplicationId, ModuleId, StreamName,
43 },
44 ownership::ChainOwnership,
45 task,
46 vm::VmRuntime,
47};
48use linera_views::{batch::Batch, ViewError};
49use serde::{Deserialize, Serialize};
50use system::AdminOperation;
51use thiserror::Error;
52
53#[cfg(with_revm)]
54use crate::evm::EvmExecutionError;
55#[cfg(with_testing)]
56use crate::test_utils::dummy_chain_description;
57#[cfg(all(with_testing, with_wasm_runtime))]
58pub use crate::wasm::test as wasm_test;
59#[cfg(with_wasm_runtime)]
60pub use crate::wasm::{
61 BaseRuntimeApi, ContractEntrypoints, ContractRuntimeApi, RuntimeApiData, ServiceEntrypoints,
62 ServiceRuntimeApi, WasmContractModule, WasmExecutionError, WasmServiceModule,
63};
64pub use crate::{
65 committee::Committee,
66 execution::{ExecutionStateView, ServiceRuntimeEndpoint},
67 execution_state_actor::{ExecutionRequest, ExecutionStateActor},
68 policy::ResourceControlPolicy,
69 resources::{BalanceHolder, ResourceController, ResourceTracker},
70 runtime::{
71 ContractSyncRuntimeHandle, ServiceRuntimeRequest, ServiceSyncRuntime,
72 ServiceSyncRuntimeHandle,
73 },
74 system::{
75 SystemExecutionStateView, SystemMessage, SystemOperation, SystemQuery, SystemResponse,
76 },
77 transaction_tracker::{TransactionOutcome, TransactionTracker},
78};
79
80pub const LINERA_SOL: &str = include_str!("../solidity/Linera.sol");
83pub const LINERA_TYPES_SOL: &str = include_str!("../solidity/LineraTypes.sol");
84
85const MAX_STREAM_NAME_LEN: usize = 64;
87
88pub const FLAG_ZERO_HASH: &str = "FLAG_ZERO_HASH.linera.network";
93pub const FLAG_FREE_REJECT: &str = "FLAG_FREE_REJECT.linera.network";
97
98#[derive(Clone)]
100pub struct UserContractCode(Box<dyn UserContractModule>);
101
102#[derive(Clone)]
104pub struct UserServiceCode(Box<dyn UserServiceModule>);
105
106pub type UserContractInstance = Box<dyn UserContract>;
108
109pub type UserServiceInstance = Box<dyn UserService>;
111
112pub trait UserContractModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
114 fn instantiate(
115 &self,
116 runtime: ContractSyncRuntimeHandle,
117 ) -> Result<UserContractInstance, ExecutionError>;
118}
119
120impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
121 fn from(module: T) -> Self {
122 Self(Box::new(module))
123 }
124}
125
126dyn_clone::clone_trait_object!(UserContractModule);
127
128pub trait UserServiceModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
130 fn instantiate(
131 &self,
132 runtime: ServiceSyncRuntimeHandle,
133 ) -> Result<UserServiceInstance, ExecutionError>;
134}
135
136impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
137 fn from(module: T) -> Self {
138 Self(Box::new(module))
139 }
140}
141
142dyn_clone::clone_trait_object!(UserServiceModule);
143
144impl UserServiceCode {
145 fn instantiate(
146 &self,
147 runtime: ServiceSyncRuntimeHandle,
148 ) -> Result<UserServiceInstance, ExecutionError> {
149 self.0.instantiate(runtime)
150 }
151}
152
153impl UserContractCode {
154 fn instantiate(
155 &self,
156 runtime: ContractSyncRuntimeHandle,
157 ) -> Result<UserContractInstance, ExecutionError> {
158 self.0.instantiate(runtime)
159 }
160}
161
162#[cfg(web)]
163const _: () = {
164 impl From<UserContractCode> for JsValue {
168 fn from(code: UserContractCode) -> JsValue {
169 let module: WasmContractModule = *(code.0 as Box<dyn Any>)
170 .downcast()
171 .expect("we only support Wasm modules on the Web for now");
172 module.into()
173 }
174 }
175
176 impl From<UserServiceCode> for JsValue {
177 fn from(code: UserServiceCode) -> JsValue {
178 let module: WasmServiceModule = *(code.0 as Box<dyn Any>)
179 .downcast()
180 .expect("we only support Wasm modules on the Web for now");
181 module.into()
182 }
183 }
184
185 impl TryFrom<JsValue> for UserContractCode {
186 type Error = JsValue;
187 fn try_from(value: JsValue) -> Result<Self, JsValue> {
188 WasmContractModule::try_from(value).map(Into::into)
189 }
190 }
191
192 impl TryFrom<JsValue> for UserServiceCode {
193 type Error = JsValue;
194 fn try_from(value: JsValue) -> Result<Self, JsValue> {
195 WasmServiceModule::try_from(value).map(Into::into)
196 }
197 }
198};
199
200#[derive(Error, Debug)]
202pub enum ExecutionError {
203 #[error(transparent)]
204 ViewError(#[from] ViewError),
205 #[error(transparent)]
206 ArithmeticError(#[from] ArithmeticError),
207 #[error("User application reported an error: {0}")]
208 UserError(String),
209 #[cfg(with_wasm_runtime)]
210 #[error(transparent)]
211 WasmError(#[from] WasmExecutionError),
212 #[cfg(with_revm)]
213 #[error(transparent)]
214 EvmError(#[from] EvmExecutionError),
215 #[error(transparent)]
216 DecompressionError(#[from] DecompressionError),
217 #[error("The given promise is invalid or was polled once already")]
218 InvalidPromise,
219
220 #[error("Attempted to perform a reentrant call to application {0}")]
221 ReentrantCall(ApplicationId),
222 #[error(
223 "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
224 from `finalize`"
225 )]
226 CrossApplicationCallInFinalize {
227 caller_id: Box<ApplicationId>,
228 callee_id: Box<ApplicationId>,
229 },
230 #[error("Failed to load bytecode from storage {0:?}")]
231 ApplicationBytecodeNotFound(Box<ApplicationDescription>),
232 #[error("Unsupported dynamic application load: {0:?}")]
234 UnsupportedDynamicApplicationLoad(Box<ApplicationId>),
235
236 #[error("Excessive number of bytes read from storage")]
237 ExcessiveRead,
238 #[error("Excessive number of bytes written to storage")]
239 ExcessiveWrite,
240 #[error("Block execution required too much fuel for VM {0}")]
241 MaximumFuelExceeded(VmRuntime),
242 #[error("Services running as oracles in block took longer than allowed")]
243 MaximumServiceOracleExecutionTimeExceeded,
244 #[error("Service running as an oracle produced a response that's too large")]
245 ServiceOracleResponseTooLarge,
246 #[error("Serialized size of the block exceeds limit")]
247 BlockTooLarge,
248 #[error("HTTP response exceeds the size limit of {limit} bytes, having at least {size} bytes")]
249 HttpResponseSizeLimitExceeded { limit: u64, size: u64 },
250 #[error("Runtime failed to respond to application")]
251 MissingRuntimeResponse,
252 #[error("Application is not authorized to perform system operations on this chain: {0:}")]
253 UnauthorizedApplication(ApplicationId),
254 #[error("Failed to make network reqwest: {0}")]
255 ReqwestError(#[from] reqwest::Error),
256 #[error("Encountered I/O error: {0}")]
257 IoError(#[from] std::io::Error),
258 #[error("More recorded oracle responses than expected")]
259 UnexpectedOracleResponse,
260 #[error("Invalid JSON: {0}")]
261 JsonError(#[from] serde_json::Error),
262 #[error(transparent)]
263 BcsError(#[from] bcs::Error),
264 #[error("Recorded response for oracle query has the wrong type")]
265 OracleResponseMismatch,
266 #[error("Service oracle query tried to create operations: {0:?}")]
267 ServiceOracleQueryOperations(Vec<Operation>),
268 #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
269 AssertBefore {
270 timestamp: Timestamp,
271 local_time: Timestamp,
272 },
273
274 #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
275 StreamNameTooLong,
276 #[error("Blob exceeds size limit")]
277 BlobTooLarge,
278 #[error("Bytecode exceeds size limit")]
279 BytecodeTooLarge,
280 #[error("Attempt to perform an HTTP request to an unauthorized host: {0:?}")]
281 UnauthorizedHttpRequest(reqwest::Url),
282 #[error("Attempt to perform an HTTP request to an invalid URL")]
283 InvalidUrlForHttpRequest(#[from] url::ParseError),
284 #[error("Failed to send contract code to worker thread: {0:?}")]
285 ContractModuleSend(#[from] linera_base::task::SendError<UserContractCode>),
286 #[error("Failed to send service code to worker thread: {0:?}")]
287 ServiceModuleSend(#[from] linera_base::task::SendError<UserServiceCode>),
288 #[error("The chain being queried is not active {0}")]
289 InactiveChain(ChainId),
290 #[error("Blobs not found: {0:?}")]
291 BlobsNotFound(Vec<BlobId>),
292 #[error("Events not found: {0:?}")]
293 EventsNotFound(Vec<EventId>),
294
295 #[error("Invalid HTTP header name used for HTTP request")]
296 InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
297 #[error("Invalid HTTP header value used for HTTP request")]
298 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
299
300 #[error("No NetworkDescription found in storage")]
301 NoNetworkDescriptionFound,
302 #[error("{epoch:?} is not recognized by chain {chain_id:}")]
303 InvalidEpoch { chain_id: ChainId, epoch: Epoch },
304 #[error("Transfer must have positive amount")]
305 IncorrectTransferAmount,
306 #[error("Transfer from owned account must be authenticated by the right signer")]
307 UnauthenticatedTransferOwner,
308 #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
309 InsufficientBalance {
310 balance: Amount,
311 account: AccountOwner,
312 },
313 #[error("Required execution fees exceeded the total funding available. Fees {fees}, available balance: {balance}")]
314 FeesExceedFunding { fees: Amount, balance: Amount },
315 #[error("Claim must have positive amount")]
316 IncorrectClaimAmount,
317 #[error("Claim must be authenticated by the right signer")]
318 UnauthenticatedClaimOwner,
319 #[error("Admin operations are only allowed on the admin chain.")]
320 AdminOperationOnNonAdminChain,
321 #[error("Failed to create new committee: expected {expected}, but got {provided}")]
322 InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
323 #[error("Failed to remove committee")]
324 InvalidCommitteeRemoval,
325 #[error("No recorded response for oracle query")]
326 MissingOracleResponse,
327 #[error("process_streams was not called for all stream updates")]
328 UnprocessedStreams,
329 #[error("Internal error: {0}")]
330 InternalError(&'static str),
331 #[error("UpdateStreams is outdated")]
332 OutdatedUpdateStreams,
333}
334
335impl ExecutionError {
336 pub fn is_local(&self) -> bool {
340 match self {
341 ExecutionError::ArithmeticError(_)
342 | ExecutionError::UserError(_)
343 | ExecutionError::DecompressionError(_)
344 | ExecutionError::InvalidPromise
345 | ExecutionError::CrossApplicationCallInFinalize { .. }
346 | ExecutionError::ReentrantCall(_)
347 | ExecutionError::ApplicationBytecodeNotFound(_)
348 | ExecutionError::UnsupportedDynamicApplicationLoad(_)
349 | ExecutionError::ExcessiveRead
350 | ExecutionError::ExcessiveWrite
351 | ExecutionError::MaximumFuelExceeded(_)
352 | ExecutionError::MaximumServiceOracleExecutionTimeExceeded
353 | ExecutionError::ServiceOracleResponseTooLarge
354 | ExecutionError::BlockTooLarge
355 | ExecutionError::HttpResponseSizeLimitExceeded { .. }
356 | ExecutionError::UnauthorizedApplication(_)
357 | ExecutionError::UnexpectedOracleResponse
358 | ExecutionError::JsonError(_)
359 | ExecutionError::BcsError(_)
360 | ExecutionError::OracleResponseMismatch
361 | ExecutionError::ServiceOracleQueryOperations(_)
362 | ExecutionError::AssertBefore { .. }
363 | ExecutionError::StreamNameTooLong
364 | ExecutionError::BlobTooLarge
365 | ExecutionError::BytecodeTooLarge
366 | ExecutionError::UnauthorizedHttpRequest(_)
367 | ExecutionError::InvalidUrlForHttpRequest(_)
368 | ExecutionError::InactiveChain(_)
369 | ExecutionError::BlobsNotFound(_)
370 | ExecutionError::EventsNotFound(_)
371 | ExecutionError::InvalidHeaderName(_)
372 | ExecutionError::InvalidHeaderValue(_)
373 | ExecutionError::InvalidEpoch { .. }
374 | ExecutionError::IncorrectTransferAmount
375 | ExecutionError::UnauthenticatedTransferOwner
376 | ExecutionError::InsufficientBalance { .. }
377 | ExecutionError::FeesExceedFunding { .. }
378 | ExecutionError::IncorrectClaimAmount
379 | ExecutionError::UnauthenticatedClaimOwner
380 | ExecutionError::AdminOperationOnNonAdminChain
381 | ExecutionError::InvalidCommitteeEpoch { .. }
382 | ExecutionError::InvalidCommitteeRemoval
383 | ExecutionError::MissingOracleResponse
384 | ExecutionError::UnprocessedStreams
385 | ExecutionError::OutdatedUpdateStreams
386 | ExecutionError::ViewError(ViewError::NotFound(_)) => false,
387 #[cfg(with_wasm_runtime)]
388 ExecutionError::WasmError(_) => false,
389 #[cfg(with_revm)]
390 ExecutionError::EvmError(..) => false,
391 ExecutionError::MissingRuntimeResponse
392 | ExecutionError::ViewError(_)
393 | ExecutionError::ReqwestError(_)
394 | ExecutionError::ContractModuleSend(_)
395 | ExecutionError::ServiceModuleSend(_)
396 | ExecutionError::NoNetworkDescriptionFound
397 | ExecutionError::InternalError(_)
398 | ExecutionError::IoError(_) => true,
399 }
400 }
401}
402
403pub trait UserContract {
405 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
407
408 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
410
411 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
413
414 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
416
417 fn finalize(&mut self) -> Result<(), ExecutionError>;
419}
420
421pub trait UserService {
423 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
425}
426
427#[derive(Clone, Copy, Default)]
429pub struct ExecutionRuntimeConfig {}
430
431#[cfg_attr(not(web), async_trait)]
434#[cfg_attr(web, async_trait(?Send))]
435pub trait ExecutionRuntimeContext {
436 fn chain_id(&self) -> ChainId;
437
438 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
439
440 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>>;
441
442 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>>;
443
444 async fn get_user_contract(
445 &self,
446 description: &ApplicationDescription,
447 txn_tracker: &TransactionTracker,
448 ) -> Result<UserContractCode, ExecutionError>;
449
450 async fn get_user_service(
451 &self,
452 description: &ApplicationDescription,
453 txn_tracker: &TransactionTracker,
454 ) -> Result<UserServiceCode, ExecutionError>;
455
456 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError>;
457
458 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError>;
459
460 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
461
462 async fn committees_for(
463 &self,
464 epoch_range: RangeInclusive<Epoch>,
465 ) -> Result<BTreeMap<Epoch, Committee>, ViewError>;
466
467 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
468
469 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
470
471 #[cfg(with_testing)]
472 async fn add_blobs(
473 &self,
474 blobs: impl IntoIterator<Item = Blob> + Send,
475 ) -> Result<(), ViewError>;
476
477 #[cfg(with_testing)]
478 async fn add_events(
479 &self,
480 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
481 ) -> Result<(), ViewError>;
482}
483
484#[derive(Clone, Copy, Debug)]
485pub struct OperationContext {
486 pub chain_id: ChainId,
488 #[debug(skip_if = Option::is_none)]
490 pub authenticated_signer: Option<AccountOwner>,
491 pub height: BlockHeight,
493 pub round: Option<u32>,
495 pub timestamp: Timestamp,
497}
498
499#[derive(Clone, Copy, Debug)]
500pub struct MessageContext {
501 pub chain_id: ChainId,
503 pub origin: ChainId,
505 pub is_bouncing: bool,
507 #[debug(skip_if = Option::is_none)]
509 pub authenticated_signer: Option<AccountOwner>,
510 #[debug(skip_if = Option::is_none)]
512 pub refund_grant_to: Option<Account>,
513 pub height: BlockHeight,
515 pub round: Option<u32>,
517 pub timestamp: Timestamp,
519}
520
521#[derive(Clone, Copy, Debug)]
522pub struct ProcessStreamsContext {
523 pub chain_id: ChainId,
525 pub height: BlockHeight,
527 pub round: Option<u32>,
529 pub timestamp: Timestamp,
531}
532
533impl From<MessageContext> for ProcessStreamsContext {
534 fn from(context: MessageContext) -> Self {
535 Self {
536 chain_id: context.chain_id,
537 height: context.height,
538 round: context.round,
539 timestamp: context.timestamp,
540 }
541 }
542}
543
544impl From<OperationContext> for ProcessStreamsContext {
545 fn from(context: OperationContext) -> Self {
546 Self {
547 chain_id: context.chain_id,
548 height: context.height,
549 round: context.round,
550 timestamp: context.timestamp,
551 }
552 }
553}
554
555#[derive(Clone, Copy, Debug)]
556pub struct FinalizeContext {
557 pub chain_id: ChainId,
559 #[debug(skip_if = Option::is_none)]
561 pub authenticated_signer: Option<AccountOwner>,
562 pub height: BlockHeight,
564 pub round: Option<u32>,
566}
567
568#[derive(Clone, Copy, Debug, Eq, PartialEq)]
569pub struct QueryContext {
570 pub chain_id: ChainId,
572 pub next_block_height: BlockHeight,
574 pub local_time: Timestamp,
576}
577
578pub trait BaseRuntime {
579 type Read: fmt::Debug + Send + Sync;
580 type ContainsKey: fmt::Debug + Send + Sync;
581 type ContainsKeys: fmt::Debug + Send + Sync;
582 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
583 type ReadValueBytes: fmt::Debug + Send + Sync;
584 type FindKeysByPrefix: fmt::Debug + Send + Sync;
585 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
586
587 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
589
590 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
592
593 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
595
596 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
598
599 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
601
602 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
604
605 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
607
608 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
610
611 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
613
614 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
616
617 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
619
620 #[cfg(feature = "test")]
622 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
623 let promise = self.contains_key_new(key)?;
624 self.contains_key_wait(&promise)
625 }
626
627 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
629
630 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
632
633 #[cfg(feature = "test")]
635 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
636 let promise = self.contains_keys_new(keys)?;
637 self.contains_keys_wait(&promise)
638 }
639
640 fn contains_keys_new(
642 &mut self,
643 keys: Vec<Vec<u8>>,
644 ) -> Result<Self::ContainsKeys, ExecutionError>;
645
646 fn contains_keys_wait(
648 &mut self,
649 promise: &Self::ContainsKeys,
650 ) -> Result<Vec<bool>, ExecutionError>;
651
652 #[cfg(feature = "test")]
654 fn read_multi_values_bytes(
655 &mut self,
656 keys: Vec<Vec<u8>>,
657 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
658 let promise = self.read_multi_values_bytes_new(keys)?;
659 self.read_multi_values_bytes_wait(&promise)
660 }
661
662 fn read_multi_values_bytes_new(
664 &mut self,
665 keys: Vec<Vec<u8>>,
666 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
667
668 fn read_multi_values_bytes_wait(
670 &mut self,
671 promise: &Self::ReadMultiValuesBytes,
672 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
673
674 #[cfg(feature = "test")]
676 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
677 let promise = self.read_value_bytes_new(key)?;
678 self.read_value_bytes_wait(&promise)
679 }
680
681 fn read_value_bytes_new(
683 &mut self,
684 key: Vec<u8>,
685 ) -> Result<Self::ReadValueBytes, ExecutionError>;
686
687 fn read_value_bytes_wait(
689 &mut self,
690 promise: &Self::ReadValueBytes,
691 ) -> Result<Option<Vec<u8>>, ExecutionError>;
692
693 fn find_keys_by_prefix_new(
695 &mut self,
696 key_prefix: Vec<u8>,
697 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
698
699 fn find_keys_by_prefix_wait(
701 &mut self,
702 promise: &Self::FindKeysByPrefix,
703 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
704
705 #[cfg(feature = "test")]
707 #[expect(clippy::type_complexity)]
708 fn find_key_values_by_prefix(
709 &mut self,
710 key_prefix: Vec<u8>,
711 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
712 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
713 self.find_key_values_by_prefix_wait(&promise)
714 }
715
716 fn find_key_values_by_prefix_new(
718 &mut self,
719 key_prefix: Vec<u8>,
720 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
721
722 #[expect(clippy::type_complexity)]
724 fn find_key_values_by_prefix_wait(
725 &mut self,
726 promise: &Self::FindKeyValuesByPrefix,
727 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
728
729 fn perform_http_request(
731 &mut self,
732 request: http::Request,
733 ) -> Result<http::Response, ExecutionError>;
734
735 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
741
742 fn read_data_blob(&mut self, hash: DataBlobHash) -> Result<Vec<u8>, ExecutionError>;
744
745 fn assert_data_blob_exists(&mut self, hash: DataBlobHash) -> Result<(), ExecutionError>;
747}
748
749pub trait ServiceRuntime: BaseRuntime {
750 fn try_query_application(
752 &mut self,
753 queried_id: ApplicationId,
754 argument: Vec<u8>,
755 ) -> Result<Vec<u8>, ExecutionError>;
756
757 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
759
760 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
762}
763
764pub trait ContractRuntime: BaseRuntime {
765 fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
767
768 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
771
772 fn message_origin_chain_id(&mut self) -> Result<Option<ChainId>, ExecutionError>;
774
775 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
778
779 fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
781
782 fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
784
785 fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
787
788 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
790
791 fn transfer(
793 &mut self,
794 source: AccountOwner,
795 destination: Account,
796 amount: Amount,
797 ) -> Result<(), ExecutionError>;
798
799 fn claim(
801 &mut self,
802 source: Account,
803 destination: Account,
804 amount: Amount,
805 ) -> Result<(), ExecutionError>;
806
807 fn try_call_application(
810 &mut self,
811 authenticated: bool,
812 callee_id: ApplicationId,
813 argument: Vec<u8>,
814 ) -> Result<Vec<u8>, ExecutionError>;
815
816 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
818
819 fn read_event(
823 &mut self,
824 chain_id: ChainId,
825 stream_name: StreamName,
826 index: u32,
827 ) -> Result<Vec<u8>, ExecutionError>;
828
829 fn subscribe_to_events(
831 &mut self,
832 chain_id: ChainId,
833 application_id: ApplicationId,
834 stream_name: StreamName,
835 ) -> Result<(), ExecutionError>;
836
837 fn unsubscribe_from_events(
839 &mut self,
840 chain_id: ChainId,
841 application_id: ApplicationId,
842 stream_name: StreamName,
843 ) -> Result<(), ExecutionError>;
844
845 fn query_service(
847 &mut self,
848 application_id: ApplicationId,
849 query: Vec<u8>,
850 ) -> Result<Vec<u8>, ExecutionError>;
851
852 fn open_chain(
854 &mut self,
855 ownership: ChainOwnership,
856 application_permissions: ApplicationPermissions,
857 balance: Amount,
858 ) -> Result<ChainId, ExecutionError>;
859
860 fn close_chain(&mut self) -> Result<(), ExecutionError>;
862
863 fn change_application_permissions(
865 &mut self,
866 application_permissions: ApplicationPermissions,
867 ) -> Result<(), ExecutionError>;
868
869 fn create_application(
871 &mut self,
872 module_id: ModuleId,
873 parameters: Vec<u8>,
874 argument: Vec<u8>,
875 required_application_ids: Vec<ApplicationId>,
876 ) -> Result<ApplicationId, ExecutionError>;
877
878 fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<DataBlobHash, ExecutionError>;
880
881 fn publish_module(
883 &mut self,
884 contract: Bytecode,
885 service: Bytecode,
886 vm_runtime: VmRuntime,
887 ) -> Result<ModuleId, ExecutionError>;
888
889 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
891
892 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
894}
895
896#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
898pub enum Operation {
899 System(Box<SystemOperation>),
901 User {
903 application_id: ApplicationId,
904 #[serde(with = "serde_bytes")]
905 #[debug(with = "hex_debug")]
906 bytes: Vec<u8>,
907 },
908}
909
910impl BcsHashable<'_> for Operation {}
911
912#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
914pub enum Message {
915 System(SystemMessage),
917 User {
919 application_id: ApplicationId,
920 #[serde(with = "serde_bytes")]
921 #[debug(with = "hex_debug")]
922 bytes: Vec<u8>,
923 },
924}
925
926#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
928pub enum Query {
929 System(SystemQuery),
931 User {
933 application_id: ApplicationId,
934 #[serde(with = "serde_bytes")]
935 #[debug(with = "hex_debug")]
936 bytes: Vec<u8>,
937 },
938}
939
940#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
942pub struct QueryOutcome<Response = QueryResponse> {
943 pub response: Response,
944 pub operations: Vec<Operation>,
945}
946
947impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
948 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
949 let QueryOutcome {
950 response,
951 operations,
952 } = system_outcome;
953
954 QueryOutcome {
955 response: QueryResponse::System(response),
956 operations,
957 }
958 }
959}
960
961impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
962 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
963 let QueryOutcome {
964 response,
965 operations,
966 } = user_service_outcome;
967
968 QueryOutcome {
969 response: QueryResponse::User(response),
970 operations,
971 }
972 }
973}
974
975#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
977pub enum QueryResponse {
978 System(SystemResponse),
980 User(
982 #[serde(with = "serde_bytes")]
983 #[debug(with = "hex_debug")]
984 Vec<u8>,
985 ),
986}
987
988#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy)]
990pub enum MessageKind {
991 Simple,
993 Protected,
996 Tracked,
999 Bouncing,
1001}
1002
1003impl Display for MessageKind {
1004 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1005 match self {
1006 MessageKind::Simple => write!(f, "Simple"),
1007 MessageKind::Protected => write!(f, "Protected"),
1008 MessageKind::Tracked => write!(f, "Tracked"),
1009 MessageKind::Bouncing => write!(f, "Bouncing"),
1010 }
1011 }
1012}
1013
1014#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
1016pub struct OutgoingMessage {
1017 pub destination: ChainId,
1019 #[debug(skip_if = Option::is_none)]
1021 pub authenticated_signer: Option<AccountOwner>,
1022 #[debug(skip_if = Amount::is_zero)]
1024 pub grant: Amount,
1025 #[debug(skip_if = Option::is_none)]
1027 pub refund_grant_to: Option<Account>,
1028 pub kind: MessageKind,
1030 pub message: Message,
1032}
1033
1034impl BcsHashable<'_> for OutgoingMessage {}
1035
1036impl OutgoingMessage {
1037 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
1039 OutgoingMessage {
1040 destination: recipient,
1041 authenticated_signer: None,
1042 grant: Amount::ZERO,
1043 refund_grant_to: None,
1044 kind: MessageKind::Simple,
1045 message: message.into(),
1046 }
1047 }
1048
1049 pub fn with_kind(mut self, kind: MessageKind) -> Self {
1051 self.kind = kind;
1052 self
1053 }
1054
1055 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
1057 self.authenticated_signer = authenticated_signer;
1058 self
1059 }
1060}
1061
1062impl OperationContext {
1063 fn refund_grant_to(&self) -> Option<Account> {
1066 self.authenticated_signer.map(|owner| Account {
1067 chain_id: self.chain_id,
1068 owner,
1069 })
1070 }
1071}
1072
1073#[cfg(with_testing)]
1074#[derive(Clone)]
1075pub struct TestExecutionRuntimeContext {
1076 chain_id: ChainId,
1077 execution_runtime_config: ExecutionRuntimeConfig,
1078 user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
1079 user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
1080 blobs: Arc<papaya::HashMap<BlobId, Blob>>,
1081 events: Arc<papaya::HashMap<EventId, Vec<u8>>>,
1082}
1083
1084#[cfg(with_testing)]
1085impl TestExecutionRuntimeContext {
1086 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1087 Self {
1088 chain_id,
1089 execution_runtime_config,
1090 user_contracts: Arc::default(),
1091 user_services: Arc::default(),
1092 blobs: Arc::default(),
1093 events: Arc::default(),
1094 }
1095 }
1096}
1097
1098#[cfg(with_testing)]
1099#[cfg_attr(not(web), async_trait)]
1100#[cfg_attr(web, async_trait(?Send))]
1101impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1102 fn chain_id(&self) -> ChainId {
1103 self.chain_id
1104 }
1105
1106 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1107 self.execution_runtime_config
1108 }
1109
1110 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>> {
1111 &self.user_contracts
1112 }
1113
1114 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>> {
1115 &self.user_services
1116 }
1117
1118 async fn get_user_contract(
1119 &self,
1120 description: &ApplicationDescription,
1121 _txn_tracker: &TransactionTracker,
1122 ) -> Result<UserContractCode, ExecutionError> {
1123 let application_id: ApplicationId = description.into();
1124 let pinned = self.user_contracts().pin();
1125 Ok(pinned
1126 .get(&application_id)
1127 .ok_or_else(|| {
1128 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1129 })?
1130 .clone())
1131 }
1132
1133 async fn get_user_service(
1134 &self,
1135 description: &ApplicationDescription,
1136 _txn_tracker: &TransactionTracker,
1137 ) -> Result<UserServiceCode, ExecutionError> {
1138 let application_id: ApplicationId = description.into();
1139 let pinned = self.user_services().pin();
1140 Ok(pinned
1141 .get(&application_id)
1142 .ok_or_else(|| {
1143 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1144 })?
1145 .clone())
1146 }
1147
1148 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError> {
1149 Ok(self.blobs.pin().get(&blob_id).cloned())
1150 }
1151
1152 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError> {
1153 Ok(self.events.pin().get(&event_id).cloned())
1154 }
1155
1156 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1157 Ok(Some(NetworkDescription {
1158 admin_chain_id: dummy_chain_description(0).id(),
1159 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1160 genesis_timestamp: Timestamp::from(0),
1161 genesis_committee_blob_hash: CryptoHash::test_hash("genesis committee"),
1162 name: "dummy network description".to_string(),
1163 }))
1164 }
1165
1166 async fn committees_for(
1167 &self,
1168 epoch_range: RangeInclusive<Epoch>,
1169 ) -> Result<BTreeMap<Epoch, Committee>, ViewError> {
1170 let pinned = self.blobs.pin();
1171 let committee_blob_bytes = pinned
1172 .values()
1173 .find(|blob| blob.content().blob_type() == BlobType::Committee)
1174 .ok_or_else(|| ViewError::NotFound("committee not found".to_owned()))?
1175 .bytes()
1176 .to_vec();
1177 let committee: Committee = bcs::from_bytes(&committee_blob_bytes)?;
1178 Ok((epoch_range.start().0..=epoch_range.end().0)
1182 .map(|epoch| (Epoch::from(epoch), committee.clone()))
1183 .collect())
1184 }
1185
1186 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1187 Ok(self.blobs.pin().contains_key(&blob_id))
1188 }
1189
1190 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1191 Ok(self.events.pin().contains_key(&event_id))
1192 }
1193
1194 #[cfg(with_testing)]
1195 async fn add_blobs(
1196 &self,
1197 blobs: impl IntoIterator<Item = Blob> + Send,
1198 ) -> Result<(), ViewError> {
1199 let pinned = self.blobs.pin();
1200 for blob in blobs {
1201 pinned.insert(blob.id(), blob);
1202 }
1203
1204 Ok(())
1205 }
1206
1207 #[cfg(with_testing)]
1208 async fn add_events(
1209 &self,
1210 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1211 ) -> Result<(), ViewError> {
1212 let pinned = self.events.pin();
1213 for (event_id, bytes) in events {
1214 pinned.insert(event_id, bytes);
1215 }
1216
1217 Ok(())
1218 }
1219}
1220
1221impl From<SystemOperation> for Operation {
1222 fn from(operation: SystemOperation) -> Self {
1223 Operation::System(Box::new(operation))
1224 }
1225}
1226
1227impl Operation {
1228 pub fn system(operation: SystemOperation) -> Self {
1229 Operation::System(Box::new(operation))
1230 }
1231
1232 #[cfg(with_testing)]
1234 pub fn user<A: Abi>(
1235 application_id: ApplicationId<A>,
1236 operation: &A::Operation,
1237 ) -> Result<Self, bcs::Error> {
1238 Self::user_without_abi(application_id.forget_abi(), operation)
1239 }
1240
1241 #[cfg(with_testing)]
1244 pub fn user_without_abi(
1245 application_id: ApplicationId,
1246 operation: &impl Serialize,
1247 ) -> Result<Self, bcs::Error> {
1248 Ok(Operation::User {
1249 application_id,
1250 bytes: bcs::to_bytes(&operation)?,
1251 })
1252 }
1253
1254 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1257 match self {
1258 Operation::System(system_operation) => Some(system_operation),
1259 Operation::User { .. } => None,
1260 }
1261 }
1262
1263 pub fn application_id(&self) -> GenericApplicationId {
1264 match self {
1265 Self::System(_) => GenericApplicationId::System,
1266 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1267 }
1268 }
1269
1270 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1272 match self.as_system_operation() {
1273 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1274 vec![BlobId::new(*blob_hash, BlobType::Data)]
1275 }
1276 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1277 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1278 }
1279 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1280 _ => vec![],
1281 }
1282 }
1283
1284 pub fn is_exempt_from_permissions(&self) -> bool {
1286 let Operation::System(system_op) = self else {
1287 return false;
1288 };
1289 matches!(
1290 **system_op,
1291 SystemOperation::ProcessNewEpoch(_)
1292 | SystemOperation::ProcessRemovedEpoch(_)
1293 | SystemOperation::UpdateStreams(_)
1294 )
1295 }
1296}
1297
1298impl From<SystemMessage> for Message {
1299 fn from(message: SystemMessage) -> Self {
1300 Message::System(message)
1301 }
1302}
1303
1304impl Message {
1305 pub fn system(message: SystemMessage) -> Self {
1306 Message::System(message)
1307 }
1308
1309 pub fn user<A, M: Serialize>(
1312 application_id: ApplicationId<A>,
1313 message: &M,
1314 ) -> Result<Self, bcs::Error> {
1315 let application_id = application_id.forget_abi();
1316 let bytes = bcs::to_bytes(&message)?;
1317 Ok(Message::User {
1318 application_id,
1319 bytes,
1320 })
1321 }
1322
1323 pub fn application_id(&self) -> GenericApplicationId {
1324 match self {
1325 Self::System(_) => GenericApplicationId::System,
1326 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1327 }
1328 }
1329}
1330
1331impl From<SystemQuery> for Query {
1332 fn from(query: SystemQuery) -> Self {
1333 Query::System(query)
1334 }
1335}
1336
1337impl Query {
1338 pub fn system(query: SystemQuery) -> Self {
1339 Query::System(query)
1340 }
1341
1342 pub fn user<A: Abi>(
1344 application_id: ApplicationId<A>,
1345 query: &A::Query,
1346 ) -> Result<Self, serde_json::Error> {
1347 Self::user_without_abi(application_id.forget_abi(), query)
1348 }
1349
1350 pub fn user_without_abi(
1353 application_id: ApplicationId,
1354 query: &impl Serialize,
1355 ) -> Result<Self, serde_json::Error> {
1356 Ok(Query::User {
1357 application_id,
1358 bytes: serde_json::to_vec(&query)?,
1359 })
1360 }
1361
1362 pub fn application_id(&self) -> GenericApplicationId {
1363 match self {
1364 Self::System(_) => GenericApplicationId::System,
1365 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1366 }
1367 }
1368}
1369
1370impl From<SystemResponse> for QueryResponse {
1371 fn from(response: SystemResponse) -> Self {
1372 QueryResponse::System(response)
1373 }
1374}
1375
1376impl From<Vec<u8>> for QueryResponse {
1377 fn from(response: Vec<u8>) -> Self {
1378 QueryResponse::User(response)
1379 }
1380}
1381
1382#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1384pub struct BlobState {
1385 pub last_used_by: Option<CryptoHash>,
1389 pub chain_id: ChainId,
1391 pub block_height: BlockHeight,
1393 pub epoch: Option<Epoch>,
1395}
1396
1397#[derive(Clone, Copy, Display)]
1399#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1400pub enum WasmRuntime {
1401 #[cfg(with_wasmer)]
1402 #[default]
1403 #[display("wasmer")]
1404 Wasmer,
1405 #[cfg(with_wasmtime)]
1406 #[cfg_attr(not(with_wasmer), default)]
1407 #[display("wasmtime")]
1408 Wasmtime,
1409}
1410
1411#[derive(Clone, Copy, Display)]
1412#[cfg_attr(with_revm, derive(Debug, Default))]
1413pub enum EvmRuntime {
1414 #[cfg(with_revm)]
1415 #[default]
1416 #[display("revm")]
1417 Revm,
1418}
1419
1420pub trait WithWasmDefault {
1422 fn with_wasm_default(self) -> Self;
1423}
1424
1425impl WithWasmDefault for Option<WasmRuntime> {
1426 fn with_wasm_default(self) -> Self {
1427 #[cfg(with_wasm_runtime)]
1428 {
1429 Some(self.unwrap_or_default())
1430 }
1431 #[cfg(not(with_wasm_runtime))]
1432 {
1433 None
1434 }
1435 }
1436}
1437
1438impl FromStr for WasmRuntime {
1439 type Err = InvalidWasmRuntime;
1440
1441 fn from_str(string: &str) -> Result<Self, Self::Err> {
1442 match string {
1443 #[cfg(with_wasmer)]
1444 "wasmer" => Ok(WasmRuntime::Wasmer),
1445 #[cfg(with_wasmtime)]
1446 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1447 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1448 }
1449 }
1450}
1451
1452#[derive(Clone, Debug, Error)]
1454#[error("{0:?} is not a valid WebAssembly runtime")]
1455pub struct InvalidWasmRuntime(String);
1456
1457doc_scalar!(Operation, "An operation to be executed in a block");
1458doc_scalar!(
1459 Message,
1460 "A message to be sent and possibly executed in the receiver's block."
1461);
1462doc_scalar!(MessageKind, "The kind of outgoing message being sent");