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, CryptoHash, 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_certificate_hash: CryptoHash::default(),
133 origin_timestamp: Default::default(),
134 is_bouncing: false,
135 authenticated_owner,
136 refund_grant_to: None,
137 height: BlockHeight(0),
138 round: Some(0),
139 timestamp: Default::default(),
140 }
141}
142
143pub fn create_dummy_query_context() -> QueryContext {
145 QueryContext {
146 chain_id: dummy_chain_description(0).id(),
147 next_block_height: BlockHeight(0),
148 local_time: Timestamp::from(0),
149 }
150}
151
152#[allow(async_fn_in_trait)]
154pub trait RegisterMockApplication {
155 async fn creator_chain_id(&self) -> ChainId;
159
160 async fn register_mock_application(
163 &mut self,
164 index: u32,
165 ) -> anyhow::Result<(ApplicationId, MockApplication, [BlobId; 3])> {
166 let (description, contract, service) = create_dummy_user_application_description(index);
167 let description_blob_id = Blob::new_application_description(&description).id();
168 let contract_blob_id = contract.id();
169 let service_blob_id = service.id();
170
171 let (app_id, application) = self
172 .register_mock_application_with(description, contract, service)
173 .await?;
174 Ok((
175 app_id,
176 application,
177 [description_blob_id, contract_blob_id, service_blob_id],
178 ))
179 }
180
181 async fn register_mock_application_with(
184 &mut self,
185 description: ApplicationDescription,
186 contract: Blob,
187 service: Blob,
188 ) -> anyhow::Result<(ApplicationId, MockApplication)>;
189}
190
191impl<C> RegisterMockApplication for ExecutionStateView<C>
192where
193 C: Context + Clone + Send + Sync + 'static,
194 C::Extra: ExecutionRuntimeContext,
195{
196 async fn creator_chain_id(&self) -> ChainId {
197 self.system.creator_chain_id().await
198 }
199
200 async fn register_mock_application_with(
201 &mut self,
202 description: ApplicationDescription,
203 contract: Blob,
204 service: Blob,
205 ) -> anyhow::Result<(ApplicationId, MockApplication)> {
206 self.system
207 .register_mock_application_with(description, contract, service)
208 .await
209 }
210}
211
212impl<C> RegisterMockApplication for SystemExecutionStateView<C>
213where
214 C: Context + Clone + Send + Sync + 'static,
215 C::Extra: ExecutionRuntimeContext,
216{
217 async fn creator_chain_id(&self) -> ChainId {
218 self.description.get().await.expect("failed to load description").as_ref().expect(
219 "Can't register applications on a system state with no associated `ChainDescription`",
220 ).into()
221 }
222
223 async fn register_mock_application_with(
224 &mut self,
225 description: ApplicationDescription,
226 contract: Blob,
227 service: Blob,
228 ) -> anyhow::Result<(ApplicationId, MockApplication)> {
229 let id = From::from(&description);
230 let context = self.context();
231 let extra = context.extra();
232 let mock_application = MockApplication::default();
233
234 extra
235 .user_contracts()
236 .pin()
237 .insert(id, mock_application.clone().into());
238 extra
239 .user_services()
240 .pin()
241 .insert(id, mock_application.clone().into());
242 extra
243 .add_blobs([
244 contract,
245 service,
246 Blob::new_application_description(&description),
247 ])
248 .await?;
249
250 Ok((id, mock_application))
251 }
252}
253
254pub fn create_dummy_user_application_registrations(
256 count: u32,
257) -> anyhow::Result<Vec<(ApplicationId, ApplicationDescription, Blob, Blob)>> {
258 let mut ids = Vec::with_capacity(count as usize);
259
260 for index in 0..count {
261 let (description, contract_blob, service_blob) =
262 create_dummy_user_application_description(index);
263 let id = From::from(&description);
264
265 ids.push((id, description, contract_blob, service_blob));
266 }
267
268 Ok(ids)
269}
270
271impl QueryContext {
272 pub fn spawn_service_runtime_actor(self) -> ServiceRuntimeEndpoint {
276 let (execution_state_sender, incoming_execution_requests) =
277 futures::channel::mpsc::unbounded();
278 let (runtime_request_sender, runtime_request_receiver) = std::sync::mpsc::channel();
279
280 thread::spawn(move || {
281 ServiceSyncRuntime::new(execution_state_sender, self).run(&runtime_request_receiver)
282 });
283
284 ServiceRuntimeEndpoint {
285 incoming_execution_requests,
286 runtime_request_sender,
287 }
288 }
289}
290
291pub fn test_accounts_strategy() -> impl Strategy<Value = BTreeMap<AccountOwner, Amount>> {
294 proptest::collection::btree_map(
295 any::<AccountOwner>(),
296 (1_u128..).prop_map(Amount::from_tokens),
297 0..5,
298 )
299}
300
301pub fn blob_oracle_responses<'a>(blobs: impl Iterator<Item = &'a BlobId>) -> Vec<OracleResponse> {
303 blobs
304 .into_iter()
305 .copied()
306 .map(OracleResponse::Blob)
307 .collect()
308}