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
88#[derive(Clone)]
90pub struct UserContractCode(Box<dyn UserContractModule>);
91
92#[derive(Clone)]
94pub struct UserServiceCode(Box<dyn UserServiceModule>);
95
96pub type UserContractInstance = Box<dyn UserContract>;
98
99pub type UserServiceInstance = Box<dyn UserService>;
101
102pub trait UserContractModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
104 fn instantiate(
105 &self,
106 runtime: ContractSyncRuntimeHandle,
107 ) -> Result<UserContractInstance, ExecutionError>;
108}
109
110impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
111 fn from(module: T) -> Self {
112 Self(Box::new(module))
113 }
114}
115
116dyn_clone::clone_trait_object!(UserContractModule);
117
118pub trait UserServiceModule: dyn_clone::DynClone + Any + task::Post + Send + Sync {
120 fn instantiate(
121 &self,
122 runtime: ServiceSyncRuntimeHandle,
123 ) -> Result<UserServiceInstance, ExecutionError>;
124}
125
126impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
127 fn from(module: T) -> Self {
128 Self(Box::new(module))
129 }
130}
131
132dyn_clone::clone_trait_object!(UserServiceModule);
133
134impl UserServiceCode {
135 fn instantiate(
136 &self,
137 runtime: ServiceSyncRuntimeHandle,
138 ) -> Result<UserServiceInstance, ExecutionError> {
139 self.0.instantiate(runtime)
140 }
141}
142
143impl UserContractCode {
144 fn instantiate(
145 &self,
146 runtime: ContractSyncRuntimeHandle,
147 ) -> Result<UserContractInstance, ExecutionError> {
148 self.0.instantiate(runtime)
149 }
150}
151
152#[cfg(web)]
153const _: () = {
154 impl From<UserContractCode> for JsValue {
158 fn from(code: UserContractCode) -> JsValue {
159 let module: WasmContractModule = *(code.0 as Box<dyn Any>)
160 .downcast()
161 .expect("we only support Wasm modules on the Web for now");
162 module.into()
163 }
164 }
165
166 impl From<UserServiceCode> for JsValue {
167 fn from(code: UserServiceCode) -> JsValue {
168 let module: WasmServiceModule = *(code.0 as Box<dyn Any>)
169 .downcast()
170 .expect("we only support Wasm modules on the Web for now");
171 module.into()
172 }
173 }
174
175 impl TryFrom<JsValue> for UserContractCode {
176 type Error = JsValue;
177 fn try_from(value: JsValue) -> Result<Self, JsValue> {
178 WasmContractModule::try_from(value).map(Into::into)
179 }
180 }
181
182 impl TryFrom<JsValue> for UserServiceCode {
183 type Error = JsValue;
184 fn try_from(value: JsValue) -> Result<Self, JsValue> {
185 WasmServiceModule::try_from(value).map(Into::into)
186 }
187 }
188};
189
190#[derive(Error, Debug)]
192pub enum ExecutionError {
193 #[error(transparent)]
194 ViewError(#[from] ViewError),
195 #[error(transparent)]
196 ArithmeticError(#[from] ArithmeticError),
197 #[error("User application reported an error: {0}")]
198 UserError(String),
199 #[cfg(with_wasm_runtime)]
200 #[error(transparent)]
201 WasmError(#[from] WasmExecutionError),
202 #[cfg(with_revm)]
203 #[error(transparent)]
204 EvmError(#[from] EvmExecutionError),
205 #[error(transparent)]
206 DecompressionError(#[from] DecompressionError),
207 #[error("The given promise is invalid or was polled once already")]
208 InvalidPromise,
209
210 #[error("Attempted to perform a reentrant call to application {0}")]
211 ReentrantCall(ApplicationId),
212 #[error(
213 "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
214 from `finalize`"
215 )]
216 CrossApplicationCallInFinalize {
217 caller_id: Box<ApplicationId>,
218 callee_id: Box<ApplicationId>,
219 },
220 #[error("Failed to load bytecode from storage {0:?}")]
221 ApplicationBytecodeNotFound(Box<ApplicationDescription>),
222 #[error("Unsupported dynamic application load: {0:?}")]
224 UnsupportedDynamicApplicationLoad(Box<ApplicationId>),
225
226 #[error("Excessive number of bytes read from storage")]
227 ExcessiveRead,
228 #[error("Excessive number of bytes written to storage")]
229 ExcessiveWrite,
230 #[error("Block execution required too much fuel for VM {0}")]
231 MaximumFuelExceeded(VmRuntime),
232 #[error("Services running as oracles in block took longer than allowed")]
233 MaximumServiceOracleExecutionTimeExceeded,
234 #[error("Service running as an oracle produced a response that's too large")]
235 ServiceOracleResponseTooLarge,
236 #[error("Serialized size of the block exceeds limit")]
237 BlockTooLarge,
238 #[error("HTTP response exceeds the size limit of {limit} bytes, having at least {size} bytes")]
239 HttpResponseSizeLimitExceeded { limit: u64, size: u64 },
240 #[error("Runtime failed to respond to application")]
241 MissingRuntimeResponse,
242 #[error("Application is not authorized to perform system operations on this chain: {0:}")]
243 UnauthorizedApplication(ApplicationId),
244 #[error("Failed to make network reqwest: {0}")]
245 ReqwestError(#[from] reqwest::Error),
246 #[error("Encountered I/O error: {0}")]
247 IoError(#[from] std::io::Error),
248 #[error("More recorded oracle responses than expected")]
249 UnexpectedOracleResponse,
250 #[error("Invalid JSON: {0}")]
251 JsonError(#[from] serde_json::Error),
252 #[error(transparent)]
253 BcsError(#[from] bcs::Error),
254 #[error("Recorded response for oracle query has the wrong type")]
255 OracleResponseMismatch,
256 #[error("Service oracle query tried to create operations: {0:?}")]
257 ServiceOracleQueryOperations(Vec<Operation>),
258 #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
259 AssertBefore {
260 timestamp: Timestamp,
261 local_time: Timestamp,
262 },
263
264 #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
265 StreamNameTooLong,
266 #[error("Blob exceeds size limit")]
267 BlobTooLarge,
268 #[error("Bytecode exceeds size limit")]
269 BytecodeTooLarge,
270 #[error("Attempt to perform an HTTP request to an unauthorized host: {0:?}")]
271 UnauthorizedHttpRequest(reqwest::Url),
272 #[error("Attempt to perform an HTTP request to an invalid URL")]
273 InvalidUrlForHttpRequest(#[from] url::ParseError),
274 #[error("Failed to send contract code to worker thread: {0:?}")]
275 ContractModuleSend(#[from] linera_base::task::SendError<UserContractCode>),
276 #[error("Failed to send service code to worker thread: {0:?}")]
277 ServiceModuleSend(#[from] linera_base::task::SendError<UserServiceCode>),
278 #[error("The chain being queried is not active {0}")]
279 InactiveChain(ChainId),
280 #[error("Blobs not found: {0:?}")]
281 BlobsNotFound(Vec<BlobId>),
282 #[error("Events not found: {0:?}")]
283 EventsNotFound(Vec<EventId>),
284
285 #[error("Invalid HTTP header name used for HTTP request")]
286 InvalidHeaderName(#[from] reqwest::header::InvalidHeaderName),
287 #[error("Invalid HTTP header value used for HTTP request")]
288 InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
289
290 #[error("No NetworkDescription found in storage")]
291 NoNetworkDescriptionFound,
292 #[error("{epoch:?} is not recognized by chain {chain_id:}")]
293 InvalidEpoch { chain_id: ChainId, epoch: Epoch },
294 #[error("Transfer must have positive amount")]
295 IncorrectTransferAmount,
296 #[error("Transfer from owned account must be authenticated by the right signer")]
297 UnauthenticatedTransferOwner,
298 #[error("The transferred amount must not exceed the balance of the current account {account}: {balance}")]
299 InsufficientBalance {
300 balance: Amount,
301 account: AccountOwner,
302 },
303 #[error("Required execution fees exceeded the total funding available. Fees {fees}, available balance: {balance}")]
304 FeesExceedFunding { fees: Amount, balance: Amount },
305 #[error("Claim must have positive amount")]
306 IncorrectClaimAmount,
307 #[error("Claim must be authenticated by the right signer")]
308 UnauthenticatedClaimOwner,
309 #[error("Admin operations are only allowed on the admin chain.")]
310 AdminOperationOnNonAdminChain,
311 #[error("Failed to create new committee: expected {expected}, but got {provided}")]
312 InvalidCommitteeEpoch { expected: Epoch, provided: Epoch },
313 #[error("Failed to remove committee")]
314 InvalidCommitteeRemoval,
315 #[error("No recorded response for oracle query")]
316 MissingOracleResponse,
317 #[error("process_streams was not called for all stream updates")]
318 UnprocessedStreams,
319 #[error("Internal error: {0}")]
320 InternalError(&'static str),
321 #[error("UpdateStreams is outdated")]
322 OutdatedUpdateStreams,
323}
324
325pub trait UserContract {
327 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError>;
329
330 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
332
333 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError>;
335
336 fn process_streams(&mut self, updates: Vec<StreamUpdate>) -> Result<(), ExecutionError>;
338
339 fn finalize(&mut self) -> Result<(), ExecutionError>;
341}
342
343pub trait UserService {
345 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError>;
347}
348
349#[derive(Clone, Copy, Default)]
351pub struct ExecutionRuntimeConfig {}
352
353#[cfg_attr(not(web), async_trait)]
356#[cfg_attr(web, async_trait(?Send))]
357pub trait ExecutionRuntimeContext {
358 fn chain_id(&self) -> ChainId;
359
360 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
361
362 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>>;
363
364 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>>;
365
366 async fn get_user_contract(
367 &self,
368 description: &ApplicationDescription,
369 txn_tracker: &TransactionTracker,
370 ) -> Result<UserContractCode, ExecutionError>;
371
372 async fn get_user_service(
373 &self,
374 description: &ApplicationDescription,
375 txn_tracker: &TransactionTracker,
376 ) -> Result<UserServiceCode, ExecutionError>;
377
378 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError>;
379
380 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError>;
381
382 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
383
384 async fn committees_for(
385 &self,
386 epoch_range: RangeInclusive<Epoch>,
387 ) -> Result<BTreeMap<Epoch, Committee>, ViewError>;
388
389 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
390
391 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError>;
392
393 #[cfg(with_testing)]
394 async fn add_blobs(
395 &self,
396 blobs: impl IntoIterator<Item = Blob> + Send,
397 ) -> Result<(), ViewError>;
398
399 #[cfg(with_testing)]
400 async fn add_events(
401 &self,
402 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
403 ) -> Result<(), ViewError>;
404}
405
406#[derive(Clone, Copy, Debug)]
407pub struct OperationContext {
408 pub chain_id: ChainId,
410 #[debug(skip_if = Option::is_none)]
412 pub authenticated_signer: Option<AccountOwner>,
413 pub height: BlockHeight,
415 pub round: Option<u32>,
417 pub timestamp: Timestamp,
419}
420
421#[derive(Clone, Copy, Debug)]
422pub struct MessageContext {
423 pub chain_id: ChainId,
425 pub origin: ChainId,
427 pub is_bouncing: bool,
429 #[debug(skip_if = Option::is_none)]
431 pub authenticated_signer: Option<AccountOwner>,
432 #[debug(skip_if = Option::is_none)]
434 pub refund_grant_to: Option<Account>,
435 pub height: BlockHeight,
437 pub round: Option<u32>,
439 pub timestamp: Timestamp,
441}
442
443#[derive(Clone, Copy, Debug)]
444pub struct ProcessStreamsContext {
445 pub chain_id: ChainId,
447 pub height: BlockHeight,
449 pub round: Option<u32>,
451 pub timestamp: Timestamp,
453}
454
455impl From<MessageContext> for ProcessStreamsContext {
456 fn from(context: MessageContext) -> Self {
457 Self {
458 chain_id: context.chain_id,
459 height: context.height,
460 round: context.round,
461 timestamp: context.timestamp,
462 }
463 }
464}
465
466impl From<OperationContext> for ProcessStreamsContext {
467 fn from(context: OperationContext) -> Self {
468 Self {
469 chain_id: context.chain_id,
470 height: context.height,
471 round: context.round,
472 timestamp: context.timestamp,
473 }
474 }
475}
476
477#[derive(Clone, Copy, Debug)]
478pub struct FinalizeContext {
479 pub chain_id: ChainId,
481 #[debug(skip_if = Option::is_none)]
483 pub authenticated_signer: Option<AccountOwner>,
484 pub height: BlockHeight,
486 pub round: Option<u32>,
488}
489
490#[derive(Clone, Copy, Debug, Eq, PartialEq)]
491pub struct QueryContext {
492 pub chain_id: ChainId,
494 pub next_block_height: BlockHeight,
496 pub local_time: Timestamp,
498}
499
500pub trait BaseRuntime {
501 type Read: fmt::Debug + Send + Sync;
502 type ContainsKey: fmt::Debug + Send + Sync;
503 type ContainsKeys: fmt::Debug + Send + Sync;
504 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
505 type ReadValueBytes: fmt::Debug + Send + Sync;
506 type FindKeysByPrefix: fmt::Debug + Send + Sync;
507 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
508
509 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
511
512 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
514
515 fn application_id(&mut self) -> Result<ApplicationId, ExecutionError>;
517
518 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
520
521 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
523
524 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
526
527 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
529
530 fn read_owner_balance(&mut self, owner: AccountOwner) -> Result<Amount, ExecutionError>;
532
533 fn read_owner_balances(&mut self) -> Result<Vec<(AccountOwner, Amount)>, ExecutionError>;
535
536 fn read_balance_owners(&mut self) -> Result<Vec<AccountOwner>, ExecutionError>;
538
539 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
541
542 #[cfg(feature = "test")]
544 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
545 let promise = self.contains_key_new(key)?;
546 self.contains_key_wait(&promise)
547 }
548
549 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
551
552 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
554
555 #[cfg(feature = "test")]
557 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
558 let promise = self.contains_keys_new(keys)?;
559 self.contains_keys_wait(&promise)
560 }
561
562 fn contains_keys_new(
564 &mut self,
565 keys: Vec<Vec<u8>>,
566 ) -> Result<Self::ContainsKeys, ExecutionError>;
567
568 fn contains_keys_wait(
570 &mut self,
571 promise: &Self::ContainsKeys,
572 ) -> Result<Vec<bool>, ExecutionError>;
573
574 #[cfg(feature = "test")]
576 fn read_multi_values_bytes(
577 &mut self,
578 keys: Vec<Vec<u8>>,
579 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
580 let promise = self.read_multi_values_bytes_new(keys)?;
581 self.read_multi_values_bytes_wait(&promise)
582 }
583
584 fn read_multi_values_bytes_new(
586 &mut self,
587 keys: Vec<Vec<u8>>,
588 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
589
590 fn read_multi_values_bytes_wait(
592 &mut self,
593 promise: &Self::ReadMultiValuesBytes,
594 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
595
596 #[cfg(feature = "test")]
598 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
599 let promise = self.read_value_bytes_new(key)?;
600 self.read_value_bytes_wait(&promise)
601 }
602
603 fn read_value_bytes_new(
605 &mut self,
606 key: Vec<u8>,
607 ) -> Result<Self::ReadValueBytes, ExecutionError>;
608
609 fn read_value_bytes_wait(
611 &mut self,
612 promise: &Self::ReadValueBytes,
613 ) -> Result<Option<Vec<u8>>, ExecutionError>;
614
615 fn find_keys_by_prefix_new(
617 &mut self,
618 key_prefix: Vec<u8>,
619 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
620
621 fn find_keys_by_prefix_wait(
623 &mut self,
624 promise: &Self::FindKeysByPrefix,
625 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
626
627 #[cfg(feature = "test")]
629 #[expect(clippy::type_complexity)]
630 fn find_key_values_by_prefix(
631 &mut self,
632 key_prefix: Vec<u8>,
633 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
634 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
635 self.find_key_values_by_prefix_wait(&promise)
636 }
637
638 fn find_key_values_by_prefix_new(
640 &mut self,
641 key_prefix: Vec<u8>,
642 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
643
644 #[expect(clippy::type_complexity)]
646 fn find_key_values_by_prefix_wait(
647 &mut self,
648 promise: &Self::FindKeyValuesByPrefix,
649 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
650
651 fn perform_http_request(
653 &mut self,
654 request: http::Request,
655 ) -> Result<http::Response, ExecutionError>;
656
657 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
663
664 fn read_data_blob(&mut self, hash: DataBlobHash) -> Result<Vec<u8>, ExecutionError>;
666
667 fn assert_data_blob_exists(&mut self, hash: DataBlobHash) -> Result<(), ExecutionError>;
669}
670
671pub trait ServiceRuntime: BaseRuntime {
672 fn try_query_application(
674 &mut self,
675 queried_id: ApplicationId,
676 argument: Vec<u8>,
677 ) -> Result<Vec<u8>, ExecutionError>;
678
679 fn schedule_operation(&mut self, operation: Vec<u8>) -> Result<(), ExecutionError>;
681
682 fn check_execution_time(&mut self) -> Result<(), ExecutionError>;
684}
685
686pub trait ContractRuntime: BaseRuntime {
687 fn authenticated_signer(&mut self) -> Result<Option<AccountOwner>, ExecutionError>;
689
690 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
693
694 fn message_origin_chain_id(&mut self) -> Result<Option<ChainId>, ExecutionError>;
696
697 fn authenticated_caller_id(&mut self) -> Result<Option<ApplicationId>, ExecutionError>;
700
701 fn maximum_fuel_per_block(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
703
704 fn remaining_fuel(&mut self, vm_runtime: VmRuntime) -> Result<u64, ExecutionError>;
706
707 fn consume_fuel(&mut self, fuel: u64, vm_runtime: VmRuntime) -> Result<(), ExecutionError>;
709
710 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
712
713 fn transfer(
715 &mut self,
716 source: AccountOwner,
717 destination: Account,
718 amount: Amount,
719 ) -> Result<(), ExecutionError>;
720
721 fn claim(
723 &mut self,
724 source: Account,
725 destination: Account,
726 amount: Amount,
727 ) -> Result<(), ExecutionError>;
728
729 fn try_call_application(
732 &mut self,
733 authenticated: bool,
734 callee_id: ApplicationId,
735 argument: Vec<u8>,
736 ) -> Result<Vec<u8>, ExecutionError>;
737
738 fn emit(&mut self, name: StreamName, value: Vec<u8>) -> Result<u32, ExecutionError>;
740
741 fn read_event(
745 &mut self,
746 chain_id: ChainId,
747 stream_name: StreamName,
748 index: u32,
749 ) -> Result<Vec<u8>, ExecutionError>;
750
751 fn subscribe_to_events(
753 &mut self,
754 chain_id: ChainId,
755 application_id: ApplicationId,
756 stream_name: StreamName,
757 ) -> Result<(), ExecutionError>;
758
759 fn unsubscribe_from_events(
761 &mut self,
762 chain_id: ChainId,
763 application_id: ApplicationId,
764 stream_name: StreamName,
765 ) -> Result<(), ExecutionError>;
766
767 fn query_service(
769 &mut self,
770 application_id: ApplicationId,
771 query: Vec<u8>,
772 ) -> Result<Vec<u8>, ExecutionError>;
773
774 fn open_chain(
776 &mut self,
777 ownership: ChainOwnership,
778 application_permissions: ApplicationPermissions,
779 balance: Amount,
780 ) -> Result<ChainId, ExecutionError>;
781
782 fn close_chain(&mut self) -> Result<(), ExecutionError>;
784
785 fn change_application_permissions(
787 &mut self,
788 application_permissions: ApplicationPermissions,
789 ) -> Result<(), ExecutionError>;
790
791 fn create_application(
793 &mut self,
794 module_id: ModuleId,
795 parameters: Vec<u8>,
796 argument: Vec<u8>,
797 required_application_ids: Vec<ApplicationId>,
798 ) -> Result<ApplicationId, ExecutionError>;
799
800 fn create_data_blob(&mut self, bytes: Vec<u8>) -> Result<DataBlobHash, ExecutionError>;
802
803 fn publish_module(
805 &mut self,
806 contract: Bytecode,
807 service: Bytecode,
808 vm_runtime: VmRuntime,
809 ) -> Result<ModuleId, ExecutionError>;
810
811 fn validation_round(&mut self) -> Result<Option<u32>, ExecutionError>;
813
814 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
816}
817
818#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
820pub enum Operation {
821 System(Box<SystemOperation>),
823 User {
825 application_id: ApplicationId,
826 #[serde(with = "serde_bytes")]
827 #[debug(with = "hex_debug")]
828 bytes: Vec<u8>,
829 },
830}
831
832impl BcsHashable<'_> for Operation {}
833
834#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
836pub enum Message {
837 System(SystemMessage),
839 User {
841 application_id: ApplicationId,
842 #[serde(with = "serde_bytes")]
843 #[debug(with = "hex_debug")]
844 bytes: Vec<u8>,
845 },
846}
847
848#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
850pub enum Query {
851 System(SystemQuery),
853 User {
855 application_id: ApplicationId,
856 #[serde(with = "serde_bytes")]
857 #[debug(with = "hex_debug")]
858 bytes: Vec<u8>,
859 },
860}
861
862#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
864pub struct QueryOutcome<Response = QueryResponse> {
865 pub response: Response,
866 pub operations: Vec<Operation>,
867}
868
869impl From<QueryOutcome<SystemResponse>> for QueryOutcome {
870 fn from(system_outcome: QueryOutcome<SystemResponse>) -> Self {
871 let QueryOutcome {
872 response,
873 operations,
874 } = system_outcome;
875
876 QueryOutcome {
877 response: QueryResponse::System(response),
878 operations,
879 }
880 }
881}
882
883impl From<QueryOutcome<Vec<u8>>> for QueryOutcome {
884 fn from(user_service_outcome: QueryOutcome<Vec<u8>>) -> Self {
885 let QueryOutcome {
886 response,
887 operations,
888 } = user_service_outcome;
889
890 QueryOutcome {
891 response: QueryResponse::User(response),
892 operations,
893 }
894 }
895}
896
897#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
899pub enum QueryResponse {
900 System(SystemResponse),
902 User(
904 #[serde(with = "serde_bytes")]
905 #[debug(with = "hex_debug")]
906 Vec<u8>,
907 ),
908}
909
910#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy)]
912pub enum MessageKind {
913 Simple,
915 Protected,
918 Tracked,
921 Bouncing,
923}
924
925impl Display for MessageKind {
926 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
927 match self {
928 MessageKind::Simple => write!(f, "Simple"),
929 MessageKind::Protected => write!(f, "Protected"),
930 MessageKind::Tracked => write!(f, "Tracked"),
931 MessageKind::Bouncing => write!(f, "Bouncing"),
932 }
933 }
934}
935
936#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, SimpleObject)]
938pub struct OutgoingMessage {
939 pub destination: ChainId,
941 #[debug(skip_if = Option::is_none)]
943 pub authenticated_signer: Option<AccountOwner>,
944 #[debug(skip_if = Amount::is_zero)]
946 pub grant: Amount,
947 #[debug(skip_if = Option::is_none)]
949 pub refund_grant_to: Option<Account>,
950 pub kind: MessageKind,
952 pub message: Message,
954}
955
956impl BcsHashable<'_> for OutgoingMessage {}
957
958impl OutgoingMessage {
959 pub fn new(recipient: ChainId, message: impl Into<Message>) -> Self {
961 OutgoingMessage {
962 destination: recipient,
963 authenticated_signer: None,
964 grant: Amount::ZERO,
965 refund_grant_to: None,
966 kind: MessageKind::Simple,
967 message: message.into(),
968 }
969 }
970
971 pub fn with_kind(mut self, kind: MessageKind) -> Self {
973 self.kind = kind;
974 self
975 }
976
977 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<AccountOwner>) -> Self {
979 self.authenticated_signer = authenticated_signer;
980 self
981 }
982}
983
984impl OperationContext {
985 fn refund_grant_to(&self) -> Option<Account> {
988 self.authenticated_signer.map(|owner| Account {
989 chain_id: self.chain_id,
990 owner,
991 })
992 }
993}
994
995#[cfg(with_testing)]
996#[derive(Clone)]
997pub struct TestExecutionRuntimeContext {
998 chain_id: ChainId,
999 execution_runtime_config: ExecutionRuntimeConfig,
1000 user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
1001 user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
1002 blobs: Arc<papaya::HashMap<BlobId, Blob>>,
1003 events: Arc<papaya::HashMap<EventId, Vec<u8>>>,
1004}
1005
1006#[cfg(with_testing)]
1007impl TestExecutionRuntimeContext {
1008 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
1009 Self {
1010 chain_id,
1011 execution_runtime_config,
1012 user_contracts: Arc::default(),
1013 user_services: Arc::default(),
1014 blobs: Arc::default(),
1015 events: Arc::default(),
1016 }
1017 }
1018}
1019
1020#[cfg(with_testing)]
1021#[cfg_attr(not(web), async_trait)]
1022#[cfg_attr(web, async_trait(?Send))]
1023impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
1024 fn chain_id(&self) -> ChainId {
1025 self.chain_id
1026 }
1027
1028 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
1029 self.execution_runtime_config
1030 }
1031
1032 fn user_contracts(&self) -> &Arc<papaya::HashMap<ApplicationId, UserContractCode>> {
1033 &self.user_contracts
1034 }
1035
1036 fn user_services(&self) -> &Arc<papaya::HashMap<ApplicationId, UserServiceCode>> {
1037 &self.user_services
1038 }
1039
1040 async fn get_user_contract(
1041 &self,
1042 description: &ApplicationDescription,
1043 _txn_tracker: &TransactionTracker,
1044 ) -> Result<UserContractCode, ExecutionError> {
1045 let application_id: ApplicationId = description.into();
1046 let pinned = self.user_contracts().pin();
1047 Ok(pinned
1048 .get(&application_id)
1049 .ok_or_else(|| {
1050 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1051 })?
1052 .clone())
1053 }
1054
1055 async fn get_user_service(
1056 &self,
1057 description: &ApplicationDescription,
1058 _txn_tracker: &TransactionTracker,
1059 ) -> Result<UserServiceCode, ExecutionError> {
1060 let application_id: ApplicationId = description.into();
1061 let pinned = self.user_services().pin();
1062 Ok(pinned
1063 .get(&application_id)
1064 .ok_or_else(|| {
1065 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
1066 })?
1067 .clone())
1068 }
1069
1070 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<Blob>, ViewError> {
1071 Ok(self.blobs.pin().get(&blob_id).cloned())
1072 }
1073
1074 async fn get_event(&self, event_id: EventId) -> Result<Option<Vec<u8>>, ViewError> {
1075 Ok(self.events.pin().get(&event_id).cloned())
1076 }
1077
1078 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1079 Ok(Some(NetworkDescription {
1080 admin_chain_id: dummy_chain_description(0).id(),
1081 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1082 genesis_timestamp: Timestamp::from(0),
1083 genesis_committee_blob_hash: CryptoHash::test_hash("genesis committee"),
1084 name: "dummy network description".to_string(),
1085 }))
1086 }
1087
1088 async fn committees_for(
1089 &self,
1090 epoch_range: RangeInclusive<Epoch>,
1091 ) -> Result<BTreeMap<Epoch, Committee>, ViewError> {
1092 let pinned = self.blobs.pin();
1093 let committee_blob_bytes = pinned
1094 .values()
1095 .find(|blob| blob.content().blob_type() == BlobType::Committee)
1096 .ok_or_else(|| ViewError::NotFound("committee not found".to_owned()))?
1097 .bytes()
1098 .to_vec();
1099 let committee: Committee = bcs::from_bytes(&committee_blob_bytes)?;
1100 Ok((epoch_range.start().0..=epoch_range.end().0)
1104 .map(|epoch| (Epoch::from(epoch), committee.clone()))
1105 .collect())
1106 }
1107
1108 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1109 Ok(self.blobs.pin().contains_key(&blob_id))
1110 }
1111
1112 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1113 Ok(self.events.pin().contains_key(&event_id))
1114 }
1115
1116 #[cfg(with_testing)]
1117 async fn add_blobs(
1118 &self,
1119 blobs: impl IntoIterator<Item = Blob> + Send,
1120 ) -> Result<(), ViewError> {
1121 let pinned = self.blobs.pin();
1122 for blob in blobs {
1123 pinned.insert(blob.id(), blob);
1124 }
1125
1126 Ok(())
1127 }
1128
1129 #[cfg(with_testing)]
1130 async fn add_events(
1131 &self,
1132 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1133 ) -> Result<(), ViewError> {
1134 let pinned = self.events.pin();
1135 for (event_id, bytes) in events {
1136 pinned.insert(event_id, bytes);
1137 }
1138
1139 Ok(())
1140 }
1141}
1142
1143impl From<SystemOperation> for Operation {
1144 fn from(operation: SystemOperation) -> Self {
1145 Operation::System(Box::new(operation))
1146 }
1147}
1148
1149impl Operation {
1150 pub fn system(operation: SystemOperation) -> Self {
1151 Operation::System(Box::new(operation))
1152 }
1153
1154 #[cfg(with_testing)]
1156 pub fn user<A: Abi>(
1157 application_id: ApplicationId<A>,
1158 operation: &A::Operation,
1159 ) -> Result<Self, bcs::Error> {
1160 Self::user_without_abi(application_id.forget_abi(), operation)
1161 }
1162
1163 #[cfg(with_testing)]
1166 pub fn user_without_abi(
1167 application_id: ApplicationId,
1168 operation: &impl Serialize,
1169 ) -> Result<Self, bcs::Error> {
1170 Ok(Operation::User {
1171 application_id,
1172 bytes: bcs::to_bytes(&operation)?,
1173 })
1174 }
1175
1176 pub fn as_system_operation(&self) -> Option<&SystemOperation> {
1179 match self {
1180 Operation::System(system_operation) => Some(system_operation),
1181 Operation::User { .. } => None,
1182 }
1183 }
1184
1185 pub fn application_id(&self) -> GenericApplicationId {
1186 match self {
1187 Self::System(_) => GenericApplicationId::System,
1188 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1189 }
1190 }
1191
1192 pub fn published_blob_ids(&self) -> Vec<BlobId> {
1194 match self.as_system_operation() {
1195 Some(SystemOperation::PublishDataBlob { blob_hash }) => {
1196 vec![BlobId::new(*blob_hash, BlobType::Data)]
1197 }
1198 Some(SystemOperation::Admin(AdminOperation::PublishCommitteeBlob { blob_hash })) => {
1199 vec![BlobId::new(*blob_hash, BlobType::Committee)]
1200 }
1201 Some(SystemOperation::PublishModule { module_id }) => module_id.bytecode_blob_ids(),
1202 _ => vec![],
1203 }
1204 }
1205
1206 pub fn is_exempt_from_permissions(&self) -> bool {
1208 let Operation::System(system_op) = self else {
1209 return false;
1210 };
1211 matches!(
1212 **system_op,
1213 SystemOperation::ProcessNewEpoch(_)
1214 | SystemOperation::ProcessRemovedEpoch(_)
1215 | SystemOperation::UpdateStreams(_)
1216 )
1217 }
1218}
1219
1220impl From<SystemMessage> for Message {
1221 fn from(message: SystemMessage) -> Self {
1222 Message::System(message)
1223 }
1224}
1225
1226impl Message {
1227 pub fn system(message: SystemMessage) -> Self {
1228 Message::System(message)
1229 }
1230
1231 pub fn user<A, M: Serialize>(
1234 application_id: ApplicationId<A>,
1235 message: &M,
1236 ) -> Result<Self, bcs::Error> {
1237 let application_id = application_id.forget_abi();
1238 let bytes = bcs::to_bytes(&message)?;
1239 Ok(Message::User {
1240 application_id,
1241 bytes,
1242 })
1243 }
1244
1245 pub fn application_id(&self) -> GenericApplicationId {
1246 match self {
1247 Self::System(_) => GenericApplicationId::System,
1248 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1249 }
1250 }
1251}
1252
1253impl From<SystemQuery> for Query {
1254 fn from(query: SystemQuery) -> Self {
1255 Query::System(query)
1256 }
1257}
1258
1259impl Query {
1260 pub fn system(query: SystemQuery) -> Self {
1261 Query::System(query)
1262 }
1263
1264 pub fn user<A: Abi>(
1266 application_id: ApplicationId<A>,
1267 query: &A::Query,
1268 ) -> Result<Self, serde_json::Error> {
1269 Self::user_without_abi(application_id.forget_abi(), query)
1270 }
1271
1272 pub fn user_without_abi(
1275 application_id: ApplicationId,
1276 query: &impl Serialize,
1277 ) -> Result<Self, serde_json::Error> {
1278 Ok(Query::User {
1279 application_id,
1280 bytes: serde_json::to_vec(&query)?,
1281 })
1282 }
1283
1284 pub fn application_id(&self) -> GenericApplicationId {
1285 match self {
1286 Self::System(_) => GenericApplicationId::System,
1287 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1288 }
1289 }
1290}
1291
1292impl From<SystemResponse> for QueryResponse {
1293 fn from(response: SystemResponse) -> Self {
1294 QueryResponse::System(response)
1295 }
1296}
1297
1298impl From<Vec<u8>> for QueryResponse {
1299 fn from(response: Vec<u8>) -> Self {
1300 QueryResponse::User(response)
1301 }
1302}
1303
1304#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1306pub struct BlobState {
1307 pub last_used_by: Option<CryptoHash>,
1311 pub chain_id: ChainId,
1313 pub block_height: BlockHeight,
1315 pub epoch: Option<Epoch>,
1317}
1318
1319#[derive(Clone, Copy, Display)]
1321#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1322pub enum WasmRuntime {
1323 #[cfg(with_wasmer)]
1324 #[default]
1325 #[display("wasmer")]
1326 Wasmer,
1327 #[cfg(with_wasmtime)]
1328 #[cfg_attr(not(with_wasmer), default)]
1329 #[display("wasmtime")]
1330 Wasmtime,
1331}
1332
1333#[derive(Clone, Copy, Display)]
1334#[cfg_attr(with_revm, derive(Debug, Default))]
1335pub enum EvmRuntime {
1336 #[cfg(with_revm)]
1337 #[default]
1338 #[display("revm")]
1339 Revm,
1340}
1341
1342pub trait WithWasmDefault {
1344 fn with_wasm_default(self) -> Self;
1345}
1346
1347impl WithWasmDefault for Option<WasmRuntime> {
1348 fn with_wasm_default(self) -> Self {
1349 #[cfg(with_wasm_runtime)]
1350 {
1351 Some(self.unwrap_or_default())
1352 }
1353 #[cfg(not(with_wasm_runtime))]
1354 {
1355 None
1356 }
1357 }
1358}
1359
1360impl FromStr for WasmRuntime {
1361 type Err = InvalidWasmRuntime;
1362
1363 fn from_str(string: &str) -> Result<Self, Self::Err> {
1364 match string {
1365 #[cfg(with_wasmer)]
1366 "wasmer" => Ok(WasmRuntime::Wasmer),
1367 #[cfg(with_wasmtime)]
1368 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1369 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1370 }
1371 }
1372}
1373
1374#[derive(Clone, Debug, Error)]
1376#[error("{0:?} is not a valid WebAssembly runtime")]
1377pub struct InvalidWasmRuntime(String);
1378
1379doc_scalar!(Operation, "An operation to be executed in a block");
1380doc_scalar!(
1381 Message,
1382 "A message to be sent and possibly executed in the receiver's block."
1383);
1384doc_scalar!(MessageKind, "The kind of outgoing message being sent");