1#![deny(clippy::large_futures)]
8
9mod applications;
10pub mod committee;
11mod execution;
12mod execution_state_actor;
13mod graphql;
14mod policy;
15mod resources;
16mod runtime;
17pub mod system;
18#[cfg(with_testing)]
19pub mod test_utils;
20mod transaction_tracker;
21mod util;
22mod wasm;
23
24use std::{fmt, str::FromStr, sync::Arc};
25
26use async_graphql::SimpleObject;
27use async_trait::async_trait;
28use committee::Epoch;
29use custom_debug_derive::Debug;
30use dashmap::DashMap;
31use derive_more::Display;
32use linera_base::{
33 abi::Abi,
34 crypto::CryptoHash,
35 data_types::{
36 Amount, ApplicationPermissions, ArithmeticError, Blob, BlockHeight, DecompressionError,
37 Resources, SendMessageRequest, Timestamp, UserApplicationDescription,
38 },
39 doc_scalar, hex_debug,
40 identifiers::{
41 Account, ApplicationId, BlobId, BytecodeId, ChainId, ChannelName, Destination,
42 GenericApplicationId, MessageId, Owner, StreamName, UserApplicationId,
43 },
44 ownership::ChainOwnership,
45};
46use linera_views::{batch::Batch, views::ViewError};
47use serde::{Deserialize, Serialize};
48use system::OpenChainConfig;
49use thiserror::Error;
50
51#[cfg(with_testing)]
52pub use crate::applications::ApplicationRegistry;
53use crate::runtime::ContractSyncRuntime;
54#[cfg(all(with_testing, with_wasm_runtime))]
55pub use crate::wasm::test as wasm_test;
56#[cfg(with_wasm_runtime)]
57pub use crate::wasm::{
58 ContractEntrypoints, ContractSystemApi, ServiceEntrypoints, ServiceSystemApi, SystemApiData,
59 ViewSystemApi, WasmContractModule, WasmExecutionError, WasmServiceModule,
60};
61pub use crate::{
62 applications::ApplicationRegistryView,
63 execution::{ExecutionStateView, ServiceRuntimeEndpoint},
64 execution_state_actor::ExecutionRequest,
65 policy::ResourceControlPolicy,
66 resources::{ResourceController, ResourceTracker},
67 runtime::{
68 ContractSyncRuntimeHandle, ServiceRuntimeRequest, ServiceSyncRuntime,
69 ServiceSyncRuntimeHandle,
70 },
71 system::{
72 SystemExecutionError, SystemExecutionStateView, SystemMessage, SystemOperation,
73 SystemQuery, SystemResponse,
74 },
75 transaction_tracker::TransactionTracker,
76};
77
78const MAX_EVENT_KEY_LEN: usize = 64;
80const MAX_STREAM_NAME_LEN: usize = 64;
82
83pub type UserContractCode = Box<dyn UserContractModule + Send + Sync>;
85
86pub type UserServiceCode = Box<dyn UserServiceModule + Send + Sync>;
88
89pub type UserContractInstance = Box<dyn UserContract>;
91
92pub type UserServiceInstance = Box<dyn UserService>;
94
95pub trait UserContractModule: dyn_clone::DynClone {
97 fn instantiate(
98 &self,
99 runtime: ContractSyncRuntimeHandle,
100 ) -> Result<UserContractInstance, ExecutionError>;
101}
102
103impl<T: UserContractModule + Send + Sync + 'static> From<T> for UserContractCode {
104 fn from(module: T) -> Self {
105 Box::new(module)
106 }
107}
108
109dyn_clone::clone_trait_object!(UserContractModule);
110
111pub trait UserServiceModule: dyn_clone::DynClone {
113 fn instantiate(
114 &self,
115 runtime: ServiceSyncRuntimeHandle,
116 ) -> Result<UserServiceInstance, ExecutionError>;
117}
118
119impl<T: UserServiceModule + Send + Sync + 'static> From<T> for UserServiceCode {
120 fn from(module: T) -> Self {
121 Box::new(module)
122 }
123}
124
125dyn_clone::clone_trait_object!(UserServiceModule);
126
127#[derive(Error, Debug)]
129pub enum ExecutionError {
130 #[error(transparent)]
131 ViewError(#[from] ViewError),
132 #[error(transparent)]
133 ArithmeticError(#[from] ArithmeticError),
134 #[error(transparent)]
135 SystemError(#[from] SystemExecutionError),
136 #[error("User application reported an error: {0}")]
137 UserError(String),
138 #[cfg(any(with_wasmer, with_wasmtime))]
139 #[error(transparent)]
140 WasmError(#[from] WasmExecutionError),
141 #[error(transparent)]
142 JoinError(#[from] linera_base::task::Error),
143 #[error(transparent)]
144 DecompressionError(#[from] DecompressionError),
145 #[error("The given promise is invalid or was polled once already")]
146 InvalidPromise,
147
148 #[error("Attempted to perform a reentrant call to application {0}")]
149 ReentrantCall(UserApplicationId),
150 #[error(
151 "Application {caller_id} attempted to perform a cross-application to {callee_id} call \
152 from `finalize`"
153 )]
154 CrossApplicationCallInFinalize {
155 caller_id: Box<UserApplicationId>,
156 callee_id: Box<UserApplicationId>,
157 },
158 #[error("Attempt to write to storage from a contract")]
159 ServiceWriteAttempt,
160 #[error("Failed to load bytecode from storage {0:?}")]
161 ApplicationBytecodeNotFound(Box<UserApplicationDescription>),
162
163 #[error("Excessive number of bytes read from storage")]
164 ExcessiveRead,
165 #[error("Excessive number of bytes written to storage")]
166 ExcessiveWrite,
167 #[error("Block execution required too much fuel")]
168 MaximumFuelExceeded,
169 #[error("Serialized size of the executed block exceeds limit")]
170 ExecutedBlockTooLarge,
171 #[error("Runtime failed to respond to application")]
172 MissingRuntimeResponse,
173 #[error("Bytecode ID {0:?} is invalid")]
174 InvalidBytecodeId(BytecodeId),
175 #[error("Owner is None")]
176 OwnerIsNone,
177 #[error("Application is not authorized to perform system operations on this chain: {0:}")]
178 UnauthorizedApplication(UserApplicationId),
179 #[error("Failed to make network reqwest")]
180 ReqwestError(#[from] reqwest::Error),
181 #[error("Encountered IO error")]
182 IoError(#[from] std::io::Error),
183 #[error("More recorded oracle responses than expected")]
184 UnexpectedOracleResponse,
185 #[error("Invalid JSON: {}", .0)]
186 Json(#[from] serde_json::Error),
187 #[error(transparent)]
188 Bcs(#[from] bcs::Error),
189 #[error("Recorded response for oracle query has the wrong type")]
190 OracleResponseMismatch,
191 #[error("Assertion failed: local time {local_time} is not earlier than {timestamp}")]
192 AssertBefore {
193 timestamp: Timestamp,
194 local_time: Timestamp,
195 },
196
197 #[error("Event keys can be at most {MAX_EVENT_KEY_LEN} bytes.")]
198 EventKeyTooLong,
199 #[error("Stream names can be at most {MAX_STREAM_NAME_LEN} bytes.")]
200 StreamNameTooLong,
201 #[error("Unstable oracles are disabled on this network.")]
204 UnstableOracle,
205}
206
207pub trait UserContract {
209 fn instantiate(
211 &mut self,
212 context: OperationContext,
213 argument: Vec<u8>,
214 ) -> Result<(), ExecutionError>;
215
216 fn execute_operation(
218 &mut self,
219 context: OperationContext,
220 operation: Vec<u8>,
221 ) -> Result<Vec<u8>, ExecutionError>;
222
223 fn execute_message(
225 &mut self,
226 context: MessageContext,
227 message: Vec<u8>,
228 ) -> Result<(), ExecutionError>;
229
230 fn finalize(&mut self, context: FinalizeContext) -> Result<(), ExecutionError>;
232}
233
234pub trait UserService {
236 fn handle_query(
238 &mut self,
239 context: QueryContext,
240 argument: Vec<u8>,
241 ) -> Result<Vec<u8>, ExecutionError>;
242}
243
244#[derive(Default)]
246pub struct ApplicationCallOutcome {
247 pub value: Vec<u8>,
249 pub execution_outcome: RawExecutionOutcome<Vec<u8>>,
251}
252
253impl ApplicationCallOutcome {
254 pub fn with_message(mut self, message: RawOutgoingMessage<Vec<u8>>) -> Self {
256 self.execution_outcome.messages.push(message);
257 self
258 }
259}
260
261#[derive(Clone, Copy, Default)]
263pub struct ExecutionRuntimeConfig {}
264
265#[async_trait]
268pub trait ExecutionRuntimeContext {
269 fn chain_id(&self) -> ChainId;
270
271 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig;
272
273 fn user_contracts(&self) -> &Arc<DashMap<UserApplicationId, UserContractCode>>;
274
275 fn user_services(&self) -> &Arc<DashMap<UserApplicationId, UserServiceCode>>;
276
277 async fn get_user_contract(
278 &self,
279 description: &UserApplicationDescription,
280 ) -> Result<UserContractCode, ExecutionError>;
281
282 async fn get_user_service(
283 &self,
284 description: &UserApplicationDescription,
285 ) -> Result<UserServiceCode, ExecutionError>;
286
287 async fn get_blob(&self, blob_id: BlobId) -> Result<Blob, ExecutionError>;
288
289 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
290}
291
292#[derive(Clone, Copy, Debug)]
293pub struct OperationContext {
294 pub chain_id: ChainId,
296 pub authenticated_signer: Option<Owner>,
298 pub authenticated_caller_id: Option<UserApplicationId>,
301 pub height: BlockHeight,
303 pub index: Option<u32>,
305}
306
307#[derive(Clone, Copy, Debug)]
308pub struct MessageContext {
309 pub chain_id: ChainId,
311 pub is_bouncing: bool,
313 pub authenticated_signer: Option<Owner>,
315 pub refund_grant_to: Option<Account>,
317 pub height: BlockHeight,
319 pub certificate_hash: CryptoHash,
321 pub message_id: MessageId,
324}
325
326#[derive(Clone, Copy, Debug)]
327pub struct FinalizeContext {
328 pub chain_id: ChainId,
330 pub authenticated_signer: Option<Owner>,
332 pub height: BlockHeight,
334}
335
336#[derive(Clone, Copy, Debug, Eq, PartialEq)]
337pub struct QueryContext {
338 pub chain_id: ChainId,
340 pub next_block_height: BlockHeight,
342 pub local_time: Timestamp,
344}
345
346pub trait BaseRuntime {
347 type Read: fmt::Debug + Send + Sync;
348 type ContainsKey: fmt::Debug + Send + Sync;
349 type ContainsKeys: fmt::Debug + Send + Sync;
350 type ReadMultiValuesBytes: fmt::Debug + Send + Sync;
351 type ReadValueBytes: fmt::Debug + Send + Sync;
352 type FindKeysByPrefix: fmt::Debug + Send + Sync;
353 type FindKeyValuesByPrefix: fmt::Debug + Send + Sync;
354
355 fn chain_id(&mut self) -> Result<ChainId, ExecutionError>;
357
358 fn block_height(&mut self) -> Result<BlockHeight, ExecutionError>;
360
361 fn application_id(&mut self) -> Result<UserApplicationId, ExecutionError>;
363
364 fn application_creator_chain_id(&mut self) -> Result<ChainId, ExecutionError>;
366
367 fn application_parameters(&mut self) -> Result<Vec<u8>, ExecutionError>;
369
370 fn read_system_timestamp(&mut self) -> Result<Timestamp, ExecutionError>;
372
373 fn read_chain_balance(&mut self) -> Result<Amount, ExecutionError>;
375
376 fn read_owner_balance(&mut self, owner: Owner) -> Result<Amount, ExecutionError>;
378
379 fn read_owner_balances(&mut self) -> Result<Vec<(Owner, Amount)>, ExecutionError>;
381
382 fn read_balance_owners(&mut self) -> Result<Vec<Owner>, ExecutionError>;
384
385 fn chain_ownership(&mut self) -> Result<ChainOwnership, ExecutionError>;
387
388 #[cfg(feature = "test")]
390 fn contains_key(&mut self, key: Vec<u8>) -> Result<bool, ExecutionError> {
391 let promise = self.contains_key_new(key)?;
392 self.contains_key_wait(&promise)
393 }
394
395 fn contains_key_new(&mut self, key: Vec<u8>) -> Result<Self::ContainsKey, ExecutionError>;
397
398 fn contains_key_wait(&mut self, promise: &Self::ContainsKey) -> Result<bool, ExecutionError>;
400
401 #[cfg(feature = "test")]
403 fn contains_keys(&mut self, keys: Vec<Vec<u8>>) -> Result<Vec<bool>, ExecutionError> {
404 let promise = self.contains_keys_new(keys)?;
405 self.contains_keys_wait(&promise)
406 }
407
408 fn contains_keys_new(
410 &mut self,
411 keys: Vec<Vec<u8>>,
412 ) -> Result<Self::ContainsKeys, ExecutionError>;
413
414 fn contains_keys_wait(
416 &mut self,
417 promise: &Self::ContainsKeys,
418 ) -> Result<Vec<bool>, ExecutionError>;
419
420 #[cfg(feature = "test")]
422 fn read_multi_values_bytes(
423 &mut self,
424 keys: Vec<Vec<u8>>,
425 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError> {
426 let promise = self.read_multi_values_bytes_new(keys)?;
427 self.read_multi_values_bytes_wait(&promise)
428 }
429
430 fn read_multi_values_bytes_new(
432 &mut self,
433 keys: Vec<Vec<u8>>,
434 ) -> Result<Self::ReadMultiValuesBytes, ExecutionError>;
435
436 fn read_multi_values_bytes_wait(
438 &mut self,
439 promise: &Self::ReadMultiValuesBytes,
440 ) -> Result<Vec<Option<Vec<u8>>>, ExecutionError>;
441
442 #[cfg(feature = "test")]
444 fn read_value_bytes(&mut self, key: Vec<u8>) -> Result<Option<Vec<u8>>, ExecutionError> {
445 let promise = self.read_value_bytes_new(key)?;
446 self.read_value_bytes_wait(&promise)
447 }
448
449 fn read_value_bytes_new(
451 &mut self,
452 key: Vec<u8>,
453 ) -> Result<Self::ReadValueBytes, ExecutionError>;
454
455 fn read_value_bytes_wait(
457 &mut self,
458 promise: &Self::ReadValueBytes,
459 ) -> Result<Option<Vec<u8>>, ExecutionError>;
460
461 fn find_keys_by_prefix_new(
463 &mut self,
464 key_prefix: Vec<u8>,
465 ) -> Result<Self::FindKeysByPrefix, ExecutionError>;
466
467 fn find_keys_by_prefix_wait(
469 &mut self,
470 promise: &Self::FindKeysByPrefix,
471 ) -> Result<Vec<Vec<u8>>, ExecutionError>;
472
473 #[cfg(feature = "test")]
475 #[expect(clippy::type_complexity)]
476 fn find_key_values_by_prefix(
477 &mut self,
478 key_prefix: Vec<u8>,
479 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError> {
480 let promise = self.find_key_values_by_prefix_new(key_prefix)?;
481 self.find_key_values_by_prefix_wait(&promise)
482 }
483
484 fn find_key_values_by_prefix_new(
486 &mut self,
487 key_prefix: Vec<u8>,
488 ) -> Result<Self::FindKeyValuesByPrefix, ExecutionError>;
489
490 #[expect(clippy::type_complexity)]
492 fn find_key_values_by_prefix_wait(
493 &mut self,
494 promise: &Self::FindKeyValuesByPrefix,
495 ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ExecutionError>;
496
497 fn query_service(
499 &mut self,
500 application_id: ApplicationId,
501 query: Vec<u8>,
502 ) -> Result<Vec<u8>, ExecutionError>;
503
504 fn http_post(
506 &mut self,
507 url: &str,
508 content_type: String,
509 payload: Vec<u8>,
510 ) -> Result<Vec<u8>, ExecutionError>;
511
512 fn assert_before(&mut self, timestamp: Timestamp) -> Result<(), ExecutionError>;
518
519 fn read_data_blob(&mut self, hash: &CryptoHash) -> Result<Vec<u8>, ExecutionError>;
521
522 fn assert_data_blob_exists(&mut self, hash: &CryptoHash) -> Result<(), ExecutionError>;
524}
525
526pub trait ServiceRuntime: BaseRuntime {
527 fn try_query_application(
529 &mut self,
530 queried_id: UserApplicationId,
531 argument: Vec<u8>,
532 ) -> Result<Vec<u8>, ExecutionError>;
533
534 fn fetch_url(&mut self, url: &str) -> Result<Vec<u8>, ExecutionError>;
536}
537
538pub trait ContractRuntime: BaseRuntime {
539 fn authenticated_signer(&mut self) -> Result<Option<Owner>, ExecutionError>;
541
542 fn message_id(&mut self) -> Result<Option<MessageId>, ExecutionError>;
544
545 fn message_is_bouncing(&mut self) -> Result<Option<bool>, ExecutionError>;
548
549 fn authenticated_caller_id(&mut self) -> Result<Option<UserApplicationId>, ExecutionError>;
552
553 fn remaining_fuel(&mut self) -> Result<u64, ExecutionError>;
555
556 fn consume_fuel(&mut self, fuel: u64) -> Result<(), ExecutionError>;
558
559 fn send_message(&mut self, message: SendMessageRequest<Vec<u8>>) -> Result<(), ExecutionError>;
561
562 fn subscribe(&mut self, chain: ChainId, channel: ChannelName) -> Result<(), ExecutionError>;
564
565 fn unsubscribe(&mut self, chain: ChainId, channel: ChannelName) -> Result<(), ExecutionError>;
567
568 fn transfer(
570 &mut self,
571 source: Option<Owner>,
572 destination: Account,
573 amount: Amount,
574 ) -> Result<(), ExecutionError>;
575
576 fn claim(
578 &mut self,
579 source: Account,
580 destination: Account,
581 amount: Amount,
582 ) -> Result<(), ExecutionError>;
583
584 fn try_call_application(
587 &mut self,
588 authenticated: bool,
589 callee_id: UserApplicationId,
590 argument: Vec<u8>,
591 ) -> Result<Vec<u8>, ExecutionError>;
592
593 fn emit(
595 &mut self,
596 name: StreamName,
597 key: Vec<u8>,
598 value: Vec<u8>,
599 ) -> Result<(), ExecutionError>;
600
601 fn open_chain(
603 &mut self,
604 ownership: ChainOwnership,
605 application_permissions: ApplicationPermissions,
606 balance: Amount,
607 ) -> Result<(MessageId, ChainId), ExecutionError>;
608
609 fn close_chain(&mut self) -> Result<(), ExecutionError>;
611
612 fn write_batch(&mut self, batch: Batch) -> Result<(), ExecutionError>;
614}
615
616#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
618pub enum Operation {
619 System(SystemOperation),
621 User {
623 application_id: UserApplicationId,
624 #[serde(with = "serde_bytes")]
625 #[debug(with = "hex_debug")]
626 bytes: Vec<u8>,
627 },
628}
629
630#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
632pub enum Message {
633 System(SystemMessage),
635 User {
637 application_id: UserApplicationId,
638 #[serde(with = "serde_bytes")]
639 #[debug(with = "hex_debug")]
640 bytes: Vec<u8>,
641 },
642}
643
644#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
646pub enum Query {
647 System(SystemQuery),
649 User {
651 application_id: UserApplicationId,
652 #[serde(with = "serde_bytes")]
653 #[debug(with = "hex_debug")]
654 bytes: Vec<u8>,
655 },
656}
657
658#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
660pub enum Response {
661 System(SystemResponse),
663 User(
665 #[serde(with = "serde_bytes")]
666 #[debug(with = "hex_debug")]
667 Vec<u8>,
668 ),
669}
670
671#[derive(Clone, Debug)]
673#[cfg_attr(with_testing, derive(Eq, PartialEq))]
674pub struct RawOutgoingMessage<Message, Grant = Resources> {
675 pub destination: Destination,
677 pub authenticated: bool,
679 pub grant: Grant,
681 pub kind: MessageKind,
683 pub message: Message,
685}
686
687impl<Message> From<SendMessageRequest<Message>> for RawOutgoingMessage<Message, Resources> {
688 fn from(request: SendMessageRequest<Message>) -> Self {
689 let SendMessageRequest {
690 destination,
691 authenticated,
692 grant,
693 is_tracked,
694 message,
695 } = request;
696
697 let kind = if is_tracked {
698 MessageKind::Tracked
699 } else {
700 MessageKind::Simple
701 };
702
703 RawOutgoingMessage {
704 destination,
705 authenticated,
706 grant,
707 kind,
708 message,
709 }
710 }
711}
712
713#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy)]
715pub enum MessageKind {
716 Simple,
718 Protected,
721 Tracked,
724 Bouncing,
726}
727
728#[derive(Debug)]
731#[cfg_attr(with_testing, derive(Eq, PartialEq))]
732pub struct RawExecutionOutcome<Message, Grant = Resources> {
733 pub authenticated_signer: Option<Owner>,
735 pub refund_grant_to: Option<Account>,
737 pub messages: Vec<RawOutgoingMessage<Message, Grant>>,
740 pub events: Vec<(StreamName, Vec<u8>, Vec<u8>)>,
742 pub subscribe: Vec<(ChannelName, ChainId)>,
744 pub unsubscribe: Vec<(ChannelName, ChainId)>,
746}
747
748#[derive(
750 Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash, Serialize, Deserialize, SimpleObject,
751)]
752pub struct ChannelSubscription {
753 pub chain_id: ChainId,
755 pub name: ChannelName,
757}
758
759#[derive(Debug)]
761#[cfg_attr(with_testing, derive(Eq, PartialEq))]
762#[expect(clippy::large_enum_variant)]
763pub enum ExecutionOutcome {
764 System(RawExecutionOutcome<SystemMessage, Amount>),
765 User(UserApplicationId, RawExecutionOutcome<Vec<u8>, Amount>),
766}
767
768impl ExecutionOutcome {
769 pub fn application_id(&self) -> GenericApplicationId {
770 match self {
771 ExecutionOutcome::System(_) => GenericApplicationId::System,
772 ExecutionOutcome::User(app_id, _) => GenericApplicationId::User(*app_id),
773 }
774 }
775
776 pub fn message_count(&self) -> usize {
777 match self {
778 ExecutionOutcome::System(outcome) => outcome.messages.len(),
779 ExecutionOutcome::User(_, outcome) => outcome.messages.len(),
780 }
781 }
782}
783
784impl<Message, Grant> RawExecutionOutcome<Message, Grant> {
785 pub fn with_authenticated_signer(mut self, authenticated_signer: Option<Owner>) -> Self {
786 self.authenticated_signer = authenticated_signer;
787 self
788 }
789
790 pub fn with_refund_grant_to(mut self, refund_grant_to: Option<Account>) -> Self {
791 self.refund_grant_to = refund_grant_to;
792 self
793 }
794
795 pub fn with_message(mut self, message: RawOutgoingMessage<Message, Grant>) -> Self {
797 self.messages.push(message);
798 self
799 }
800}
801
802impl<Message, Grant> Default for RawExecutionOutcome<Message, Grant> {
803 fn default() -> Self {
804 Self {
805 authenticated_signer: None,
806 refund_grant_to: None,
807 messages: Vec::new(),
808 events: Vec::new(),
809 subscribe: Vec::new(),
810 unsubscribe: Vec::new(),
811 }
812 }
813}
814
815impl<Message> RawOutgoingMessage<Message, Resources> {
816 pub fn into_priced(
817 self,
818 policy: &ResourceControlPolicy,
819 ) -> Result<RawOutgoingMessage<Message, Amount>, ArithmeticError> {
820 let RawOutgoingMessage {
821 destination,
822 authenticated,
823 grant,
824 kind,
825 message,
826 } = self;
827 Ok(RawOutgoingMessage {
828 destination,
829 authenticated,
830 grant: policy.total_price(&grant)?,
831 kind,
832 message,
833 })
834 }
835}
836
837impl<Message> RawExecutionOutcome<Message, Resources> {
838 pub fn into_priced(
839 self,
840 policy: &ResourceControlPolicy,
841 ) -> Result<RawExecutionOutcome<Message, Amount>, ArithmeticError> {
842 let RawExecutionOutcome {
843 authenticated_signer,
844 refund_grant_to,
845 messages,
846 events,
847 subscribe,
848 unsubscribe,
849 } = self;
850 let messages = messages
851 .into_iter()
852 .map(|message| message.into_priced(policy))
853 .collect::<Result<_, _>>()?;
854 Ok(RawExecutionOutcome {
855 authenticated_signer,
856 refund_grant_to,
857 messages,
858 events,
859 subscribe,
860 unsubscribe,
861 })
862 }
863}
864
865impl OperationContext {
866 fn refund_grant_to(&self) -> Option<Account> {
867 Some(Account {
868 chain_id: self.chain_id,
869 owner: self.authenticated_signer,
870 })
871 }
872
873 fn next_message_id(&self, next_message_index: u32) -> MessageId {
874 MessageId {
875 chain_id: self.chain_id,
876 height: self.height,
877 index: next_message_index,
878 }
879 }
880}
881
882#[cfg(with_testing)]
883#[derive(Clone)]
884pub struct TestExecutionRuntimeContext {
885 chain_id: ChainId,
886 execution_runtime_config: ExecutionRuntimeConfig,
887 user_contracts: Arc<DashMap<UserApplicationId, UserContractCode>>,
888 user_services: Arc<DashMap<UserApplicationId, UserServiceCode>>,
889 blobs: Arc<DashMap<BlobId, Blob>>,
890}
891
892#[cfg(with_testing)]
893impl TestExecutionRuntimeContext {
894 pub fn new(chain_id: ChainId, execution_runtime_config: ExecutionRuntimeConfig) -> Self {
895 Self {
896 chain_id,
897 execution_runtime_config,
898 user_contracts: Arc::default(),
899 user_services: Arc::default(),
900 blobs: Arc::default(),
901 }
902 }
903
904 pub fn add_blobs(&self, blobs: Vec<Blob>) {
905 for blob in blobs {
906 self.blobs.insert(blob.id(), blob);
907 }
908 }
909}
910
911#[cfg(with_testing)]
912#[async_trait]
913impl ExecutionRuntimeContext for TestExecutionRuntimeContext {
914 fn chain_id(&self) -> ChainId {
915 self.chain_id
916 }
917
918 fn execution_runtime_config(&self) -> ExecutionRuntimeConfig {
919 self.execution_runtime_config
920 }
921
922 fn user_contracts(&self) -> &Arc<DashMap<UserApplicationId, UserContractCode>> {
923 &self.user_contracts
924 }
925
926 fn user_services(&self) -> &Arc<DashMap<UserApplicationId, UserServiceCode>> {
927 &self.user_services
928 }
929
930 async fn get_user_contract(
931 &self,
932 description: &UserApplicationDescription,
933 ) -> Result<UserContractCode, ExecutionError> {
934 let application_id = description.into();
935 Ok(self
936 .user_contracts()
937 .get(&application_id)
938 .ok_or_else(|| {
939 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
940 })?
941 .clone())
942 }
943
944 async fn get_user_service(
945 &self,
946 description: &UserApplicationDescription,
947 ) -> Result<UserServiceCode, ExecutionError> {
948 let application_id = description.into();
949 Ok(self
950 .user_services()
951 .get(&application_id)
952 .ok_or_else(|| {
953 ExecutionError::ApplicationBytecodeNotFound(Box::new(description.clone()))
954 })?
955 .clone())
956 }
957
958 async fn get_blob(&self, blob_id: BlobId) -> Result<Blob, ExecutionError> {
959 Ok(self
960 .blobs
961 .get(&blob_id)
962 .ok_or_else(|| SystemExecutionError::BlobNotFoundOnRead(blob_id))?
963 .clone())
964 }
965
966 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
967 Ok(self.blobs.contains_key(&blob_id))
968 }
969}
970
971impl From<SystemOperation> for Operation {
972 fn from(operation: SystemOperation) -> Self {
973 Operation::System(operation)
974 }
975}
976
977impl Operation {
978 pub fn system(operation: SystemOperation) -> Self {
979 Operation::System(operation)
980 }
981
982 pub fn user<A: Abi>(
983 application_id: UserApplicationId<A>,
984 operation: &A::Operation,
985 ) -> Result<Self, bcs::Error> {
986 let application_id = application_id.forget_abi();
987 let bytes = bcs::to_bytes(&operation)?;
988 Ok(Operation::User {
989 application_id,
990 bytes,
991 })
992 }
993
994 pub fn application_id(&self) -> GenericApplicationId {
995 match self {
996 Self::System(_) => GenericApplicationId::System,
997 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
998 }
999 }
1000}
1001
1002impl From<SystemMessage> for Message {
1003 fn from(message: SystemMessage) -> Self {
1004 Message::System(message)
1005 }
1006}
1007
1008impl Message {
1009 pub fn system(message: SystemMessage) -> Self {
1010 Message::System(message)
1011 }
1012
1013 pub fn user<A: Abi, M: Serialize>(
1014 application_id: UserApplicationId<A>,
1015 message: &M,
1016 ) -> Result<Self, bcs::Error> {
1017 let application_id = application_id.forget_abi();
1018 let bytes = bcs::to_bytes(&message)?;
1019 Ok(Message::User {
1020 application_id,
1021 bytes,
1022 })
1023 }
1024
1025 pub fn application_id(&self) -> GenericApplicationId {
1026 match self {
1027 Self::System(_) => GenericApplicationId::System,
1028 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1029 }
1030 }
1031
1032 pub fn goes_to_inbox(&self) -> bool {
1034 !matches!(
1035 self,
1036 Message::System(SystemMessage::Subscribe { .. } | SystemMessage::Unsubscribe { .. })
1037 )
1038 }
1039
1040 pub fn matches_subscribe(&self) -> Option<(&ChainId, &ChannelSubscription)> {
1041 match self {
1042 Message::System(SystemMessage::Subscribe { id, subscription }) => {
1043 Some((id, subscription))
1044 }
1045 _ => None,
1046 }
1047 }
1048
1049 pub fn matches_unsubscribe(&self) -> Option<(&ChainId, &ChannelSubscription)> {
1050 match self {
1051 Message::System(SystemMessage::Unsubscribe { id, subscription }) => {
1052 Some((id, subscription))
1053 }
1054 _ => None,
1055 }
1056 }
1057
1058 pub fn matches_open_chain(&self) -> Option<&OpenChainConfig> {
1059 match self {
1060 Message::System(SystemMessage::OpenChain(config)) => Some(config),
1061 _ => None,
1062 }
1063 }
1064}
1065
1066impl From<SystemQuery> for Query {
1067 fn from(query: SystemQuery) -> Self {
1068 Query::System(query)
1069 }
1070}
1071
1072impl Query {
1073 pub fn system(query: SystemQuery) -> Self {
1074 Query::System(query)
1075 }
1076
1077 pub fn user<A: Abi>(
1078 application_id: UserApplicationId<A>,
1079 query: &A::Query,
1080 ) -> Result<Self, serde_json::Error> {
1081 let application_id = application_id.forget_abi();
1082 let bytes = serde_json::to_vec(&query)?;
1083 Ok(Query::User {
1084 application_id,
1085 bytes,
1086 })
1087 }
1088
1089 pub fn application_id(&self) -> GenericApplicationId {
1090 match self {
1091 Self::System(_) => GenericApplicationId::System,
1092 Self::User { application_id, .. } => GenericApplicationId::User(*application_id),
1093 }
1094 }
1095}
1096
1097impl From<SystemResponse> for Response {
1098 fn from(response: SystemResponse) -> Self {
1099 Response::System(response)
1100 }
1101}
1102
1103impl From<Vec<u8>> for Response {
1104 fn from(response: Vec<u8>) -> Self {
1105 Response::User(response)
1106 }
1107}
1108
1109#[derive(Eq, PartialEq, Debug, Hash, Clone, Serialize, Deserialize)]
1111pub struct BlobState {
1112 pub last_used_by: CryptoHash,
1114 pub epoch: Epoch,
1116}
1117
1118#[derive(Clone, Copy, Display)]
1120#[cfg_attr(with_wasm_runtime, derive(Debug, Default))]
1121pub enum WasmRuntime {
1122 #[cfg(with_wasmer)]
1123 #[default]
1124 #[display("wasmer")]
1125 Wasmer,
1126 #[cfg(with_wasmtime)]
1127 #[cfg_attr(not(with_wasmer), default)]
1128 #[display("wasmtime")]
1129 Wasmtime,
1130 #[cfg(with_wasmer)]
1131 WasmerWithSanitizer,
1132 #[cfg(with_wasmtime)]
1133 WasmtimeWithSanitizer,
1134}
1135
1136pub trait WithWasmDefault {
1138 fn with_wasm_default(self) -> Self;
1139}
1140
1141impl WasmRuntime {
1142 #[cfg(with_wasm_runtime)]
1143 pub fn default_with_sanitizer() -> Self {
1144 cfg_if::cfg_if! {
1145 if #[cfg(with_wasmer)] {
1146 WasmRuntime::WasmerWithSanitizer
1147 } else if #[cfg(with_wasmtime)] {
1148 WasmRuntime::WasmtimeWithSanitizer
1149 } else {
1150 compile_error!("BUG: Wasm runtime unhandled in `WasmRuntime::default_with_sanitizer`")
1151 }
1152 }
1153 }
1154
1155 pub fn needs_sanitizer(self) -> bool {
1156 match self {
1157 #[cfg(with_wasmer)]
1158 WasmRuntime::WasmerWithSanitizer => true,
1159 #[cfg(with_wasmtime)]
1160 WasmRuntime::WasmtimeWithSanitizer => true,
1161 #[cfg(with_wasm_runtime)]
1162 _ => false,
1163 }
1164 }
1165}
1166
1167impl WithWasmDefault for Option<WasmRuntime> {
1168 fn with_wasm_default(self) -> Self {
1169 #[cfg(with_wasm_runtime)]
1170 {
1171 Some(self.unwrap_or_default())
1172 }
1173 #[cfg(not(with_wasm_runtime))]
1174 {
1175 None
1176 }
1177 }
1178}
1179
1180impl FromStr for WasmRuntime {
1181 type Err = InvalidWasmRuntime;
1182
1183 fn from_str(string: &str) -> Result<Self, Self::Err> {
1184 match string {
1185 #[cfg(with_wasmer)]
1186 "wasmer" => Ok(WasmRuntime::Wasmer),
1187 #[cfg(with_wasmtime)]
1188 "wasmtime" => Ok(WasmRuntime::Wasmtime),
1189 unknown => Err(InvalidWasmRuntime(unknown.to_owned())),
1190 }
1191 }
1192}
1193
1194#[derive(Clone, Debug, Error)]
1196#[error("{0:?} is not a valid WebAssembly runtime")]
1197pub struct InvalidWasmRuntime(String);
1198
1199doc_scalar!(Operation, "An operation to be executed in a block");
1200doc_scalar!(
1201 Message,
1202 "An message to be sent and possibly executed in the receiver's block."
1203);
1204doc_scalar!(MessageKind, "The kind of outgoing message being sent");