linera_execution/test_utils/
mod.rs1#![allow(clippy::cast_possible_truncation)]
5
6mod mock_application;
7#[cfg(with_revm)]
8pub mod solidity;
9mod system_execution_state;
10
11use std::{collections::BTreeMap, sync::Arc, thread, vec};
12
13use linera_base::{
14 crypto::{AccountPublicKey, ValidatorPublicKey},
15 data_types::{
16 Amount, Blob, BlockHeight, ChainDescription, ChainOrigin, CompressedBytecode, Epoch,
17 InitialChainConfig, OracleResponse, Timestamp,
18 },
19 identifiers::{AccountOwner, ApplicationId, BlobId, ChainId, ModuleId},
20 ownership::ChainOwnership,
21 vm::VmRuntime,
22};
23use linera_views::{context::Context, views::View};
24use proptest::{prelude::any, strategy::Strategy};
25
26pub use self::{
27 mock_application::{ExpectedCall, MockApplication, MockApplicationInstance},
28 system_execution_state::SystemExecutionState,
29};
30use crate::{
31 committee::Committee, ApplicationDescription, ExecutionRuntimeContext, ExecutionStateView,
32 MessageContext, OperationContext, QueryContext, ServiceRuntimeEndpoint, ServiceSyncRuntime,
33 SystemExecutionStateView,
34};
35
36pub fn dummy_committee() -> Committee {
38 Committee::make_simple(vec![(
39 ValidatorPublicKey::test_key(0),
40 AccountPublicKey::test_key(0),
41 )])
42}
43
44pub fn dummy_committees() -> BTreeMap<Epoch, Committee> {
46 let committee = dummy_committee();
47 BTreeMap::from([(Epoch::ZERO, committee)])
48}
49
50pub fn dummy_chain_description_with_ownership_and_balance(
52 index: u32,
53 ownership: ChainOwnership,
54 balance: Amount,
55) -> ChainDescription {
56 let origin = ChainOrigin::Root(index);
57 let config = InitialChainConfig {
58 application_permissions: Default::default(),
59 balance,
60 epoch: Epoch::ZERO,
61 ownership,
62 };
63 ChainDescription::new(origin, config, Timestamp::default())
64}
65
66pub fn dummy_chain_description_with_owner(index: u32, owner: AccountOwner) -> ChainDescription {
68 dummy_chain_description_with_ownership_and_balance(
69 index,
70 ChainOwnership::single(owner),
71 Amount::MAX,
72 )
73}
74
75pub fn dummy_chain_description(index: u32) -> ChainDescription {
77 let chain_key = AccountPublicKey::test_key(2 * (index % 128) as u8 + 1);
78 let ownership = ChainOwnership::single(chain_key.into());
79 dummy_chain_description_with_ownership_and_balance(index, ownership, Amount::MAX)
80}
81
82pub fn create_dummy_user_application_description(
84 index: u32,
85) -> (ApplicationDescription, Blob, Blob) {
86 let chain_id = dummy_chain_description(1).id();
87 let mut contract_bytes = b"contract".to_vec();
88 let mut service_bytes = b"service".to_vec();
89 contract_bytes.push(index as u8);
90 service_bytes.push(index as u8);
91 let contract_blob = Blob::new_contract_bytecode(CompressedBytecode {
92 compressed_bytes: Arc::new(contract_bytes.into_boxed_slice()),
93 });
94 let service_blob = Blob::new_service_bytecode(CompressedBytecode {
95 compressed_bytes: Arc::new(service_bytes.into_boxed_slice()),
96 });
97
98 let vm_runtime = VmRuntime::Wasm;
99 (
100 ApplicationDescription {
101 module_id: ModuleId::new(contract_blob.id().hash, service_blob.id().hash, vm_runtime),
102 creator_chain_id: chain_id,
103 block_height: 0.into(),
104 application_index: index,
105 required_application_ids: vec![],
106 parameters: vec![],
107 },
108 contract_blob,
109 service_blob,
110 )
111}
112
113pub fn create_dummy_operation_context(chain_id: ChainId) -> OperationContext {
115 OperationContext {
116 chain_id,
117 height: BlockHeight(0),
118 round: Some(0),
119 authenticated_owner: None,
120 timestamp: Default::default(),
121 }
122}
123
124pub fn create_dummy_message_context(
126 chain_id: ChainId,
127 authenticated_owner: Option<AccountOwner>,
128) -> MessageContext {
129 MessageContext {
130 chain_id,
131 origin: chain_id,
132 origin_timestamp: Default::default(),
133 is_bouncing: false,
134 authenticated_owner,
135 refund_grant_to: None,
136 height: BlockHeight(0),
137 round: Some(0),
138 timestamp: Default::default(),
139 }
140}
141
142pub fn create_dummy_query_context() -> QueryContext {
144 QueryContext {
145 chain_id: dummy_chain_description(0).id(),
146 next_block_height: BlockHeight(0),
147 local_time: Timestamp::from(0),
148 }
149}
150
151#[allow(async_fn_in_trait)]
153pub trait RegisterMockApplication {
154 async fn creator_chain_id(&self) -> ChainId;
158
159 async fn register_mock_application(
162 &mut self,
163 index: u32,
164 ) -> anyhow::Result<(ApplicationId, MockApplication, [BlobId; 3])> {
165 let (description, contract, service) = create_dummy_user_application_description(index);
166 let description_blob_id = Blob::new_application_description(&description).id();
167 let contract_blob_id = contract.id();
168 let service_blob_id = service.id();
169
170 let (app_id, application) = self
171 .register_mock_application_with(description, contract, service)
172 .await?;
173 Ok((
174 app_id,
175 application,
176 [description_blob_id, contract_blob_id, service_blob_id],
177 ))
178 }
179
180 async fn register_mock_application_with(
183 &mut self,
184 description: ApplicationDescription,
185 contract: Blob,
186 service: Blob,
187 ) -> anyhow::Result<(ApplicationId, MockApplication)>;
188}
189
190impl<C> RegisterMockApplication for ExecutionStateView<C>
191where
192 C: Context + Clone + Send + Sync + 'static,
193 C::Extra: ExecutionRuntimeContext,
194{
195 async fn creator_chain_id(&self) -> ChainId {
196 self.system.creator_chain_id().await
197 }
198
199 async fn register_mock_application_with(
200 &mut self,
201 description: ApplicationDescription,
202 contract: Blob,
203 service: Blob,
204 ) -> anyhow::Result<(ApplicationId, MockApplication)> {
205 self.system
206 .register_mock_application_with(description, contract, service)
207 .await
208 }
209}
210
211impl<C> RegisterMockApplication for SystemExecutionStateView<C>
212where
213 C: Context + Clone + Send + Sync + 'static,
214 C::Extra: ExecutionRuntimeContext,
215{
216 async fn creator_chain_id(&self) -> ChainId {
217 self.description.get().await.expect("failed to load description").as_ref().expect(
218 "Can't register applications on a system state with no associated `ChainDescription`",
219 ).into()
220 }
221
222 async fn register_mock_application_with(
223 &mut self,
224 description: ApplicationDescription,
225 contract: Blob,
226 service: Blob,
227 ) -> anyhow::Result<(ApplicationId, MockApplication)> {
228 let id = From::from(&description);
229 let context = self.context();
230 let extra = context.extra();
231 let mock_application = MockApplication::default();
232
233 extra
234 .user_contracts()
235 .pin()
236 .insert(id, mock_application.clone().into());
237 extra
238 .user_services()
239 .pin()
240 .insert(id, mock_application.clone().into());
241 extra
242 .add_blobs([
243 contract,
244 service,
245 Blob::new_application_description(&description),
246 ])
247 .await?;
248
249 Ok((id, mock_application))
250 }
251}
252
253pub fn create_dummy_user_application_registrations(
255 count: u32,
256) -> anyhow::Result<Vec<(ApplicationId, ApplicationDescription, Blob, Blob)>> {
257 let mut ids = Vec::with_capacity(count as usize);
258
259 for index in 0..count {
260 let (description, contract_blob, service_blob) =
261 create_dummy_user_application_description(index);
262 let id = From::from(&description);
263
264 ids.push((id, description, contract_blob, service_blob));
265 }
266
267 Ok(ids)
268}
269
270impl QueryContext {
271 pub fn spawn_service_runtime_actor(self) -> ServiceRuntimeEndpoint {
275 let (execution_state_sender, incoming_execution_requests) =
276 futures::channel::mpsc::unbounded();
277 let (runtime_request_sender, runtime_request_receiver) = std::sync::mpsc::channel();
278
279 thread::spawn(move || {
280 ServiceSyncRuntime::new(execution_state_sender, self).run(&runtime_request_receiver)
281 });
282
283 ServiceRuntimeEndpoint {
284 incoming_execution_requests,
285 runtime_request_sender,
286 }
287 }
288}
289
290pub fn test_accounts_strategy() -> impl Strategy<Value = BTreeMap<AccountOwner, Amount>> {
293 proptest::collection::btree_map(
294 any::<AccountOwner>(),
295 (1_u128..).prop_map(Amount::from_tokens),
296 0..5,
297 )
298}
299
300pub fn blob_oracle_responses<'a>(blobs: impl Iterator<Item = &'a BlobId>) -> Vec<OracleResponse> {
302 blobs
303 .into_iter()
304 .copied()
305 .map(OracleResponse::Blob)
306 .collect()
307}