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