linera_execution/test_utils/
mod.rs1mod mock_application;
5#[cfg(with_revm)]
6pub mod solidity;
7mod system_execution_state;
8
9use std::{collections::BTreeMap, sync::Arc, thread, vec};
10
11use linera_base::{
12 crypto::{AccountPublicKey, ValidatorPublicKey},
13 data_types::{
14 Amount, Blob, BlockHeight, ChainDescription, ChainOrigin, CompressedBytecode, Epoch,
15 InitialChainConfig, OracleResponse, Timestamp,
16 },
17 identifiers::{AccountOwner, ApplicationId, BlobId, ChainId, ModuleId},
18 ownership::ChainOwnership,
19 vm::VmRuntime,
20};
21use linera_views::{context::Context, views::View};
22use proptest::{prelude::any, strategy::Strategy};
23
24pub use self::{
25 mock_application::{ExpectedCall, MockApplication, MockApplicationInstance},
26 system_execution_state::SystemExecutionState,
27};
28use crate::{
29 committee::Committee, ApplicationDescription, ExecutionRuntimeContext, ExecutionStateView,
30 MessageContext, OperationContext, QueryContext, ServiceRuntimeEndpoint, ServiceSyncRuntime,
31 SystemExecutionStateView,
32};
33
34pub fn dummy_committee() -> Committee {
35 Committee::make_simple(vec![(
36 ValidatorPublicKey::test_key(0),
37 AccountPublicKey::test_key(0),
38 )])
39}
40
41pub fn dummy_committees() -> BTreeMap<Epoch, Committee> {
42 let committee = dummy_committee();
43 BTreeMap::from([(Epoch::ZERO, committee)])
44}
45
46pub fn dummy_chain_description_with_ownership_and_balance(
47 index: u32,
48 ownership: ChainOwnership,
49 balance: Amount,
50) -> ChainDescription {
51 let origin = ChainOrigin::Root(index);
52 let config = InitialChainConfig {
53 application_permissions: Default::default(),
54 balance,
55 epoch: Epoch::ZERO,
56 min_active_epoch: Epoch::ZERO,
57 max_active_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_signer: None,
115 timestamp: Default::default(),
116 }
117}
118
119pub fn create_dummy_message_context(
121 chain_id: ChainId,
122 authenticated_signer: Option<AccountOwner>,
123) -> MessageContext {
124 MessageContext {
125 chain_id,
126 origin: chain_id,
127 is_bouncing: false,
128 authenticated_signer,
129 refund_grant_to: None,
130 height: BlockHeight(0),
131 round: Some(0),
132 timestamp: Default::default(),
133 }
134}
135
136pub fn create_dummy_query_context() -> QueryContext {
138 QueryContext {
139 chain_id: dummy_chain_description(0).id(),
140 next_block_height: BlockHeight(0),
141 local_time: Timestamp::from(0),
142 }
143}
144
145#[allow(async_fn_in_trait)]
147pub trait RegisterMockApplication {
148 fn creator_chain_id(&self) -> ChainId;
152
153 async fn register_mock_application(
156 &mut self,
157 index: u32,
158 ) -> anyhow::Result<(ApplicationId, MockApplication, [BlobId; 3])> {
159 let (description, contract, service) = create_dummy_user_application_description(index);
160 let description_blob_id = Blob::new_application_description(&description).id();
161 let contract_blob_id = contract.id();
162 let service_blob_id = service.id();
163
164 let (app_id, application) = self
165 .register_mock_application_with(description, contract, service)
166 .await?;
167 Ok((
168 app_id,
169 application,
170 [description_blob_id, contract_blob_id, service_blob_id],
171 ))
172 }
173
174 async fn register_mock_application_with(
177 &mut self,
178 description: ApplicationDescription,
179 contract: Blob,
180 service: Blob,
181 ) -> anyhow::Result<(ApplicationId, MockApplication)>;
182}
183
184impl<C> RegisterMockApplication for ExecutionStateView<C>
185where
186 C: Context + Clone + Send + Sync + 'static,
187 C::Extra: ExecutionRuntimeContext,
188{
189 fn creator_chain_id(&self) -> ChainId {
190 self.system.creator_chain_id()
191 }
192
193 async fn register_mock_application_with(
194 &mut self,
195 description: ApplicationDescription,
196 contract: Blob,
197 service: Blob,
198 ) -> anyhow::Result<(ApplicationId, MockApplication)> {
199 self.system
200 .register_mock_application_with(description, contract, service)
201 .await
202 }
203}
204
205impl<C> RegisterMockApplication for SystemExecutionStateView<C>
206where
207 C: Context + Clone + Send + Sync + 'static,
208 C::Extra: ExecutionRuntimeContext,
209{
210 fn creator_chain_id(&self) -> ChainId {
211 self.description.get().as_ref().expect(
212 "Can't register applications on a system state with no associated `ChainDescription`",
213 ).into()
214 }
215
216 async fn register_mock_application_with(
217 &mut self,
218 description: ApplicationDescription,
219 contract: Blob,
220 service: Blob,
221 ) -> anyhow::Result<(ApplicationId, MockApplication)> {
222 let id = From::from(&description);
223 let extra = self.context().extra();
224 let mock_application = MockApplication::default();
225
226 extra
227 .user_contracts()
228 .pin()
229 .insert(id, mock_application.clone().into());
230 extra
231 .user_services()
232 .pin()
233 .insert(id, mock_application.clone().into());
234 extra
235 .add_blobs([
236 contract,
237 service,
238 Blob::new_application_description(&description),
239 ])
240 .await?;
241
242 Ok((id, mock_application))
243 }
244}
245
246pub fn create_dummy_user_application_registrations(
247 count: u32,
248) -> anyhow::Result<Vec<(ApplicationId, ApplicationDescription, Blob, Blob)>> {
249 let mut ids = Vec::with_capacity(count as usize);
250
251 for index in 0..count {
252 let (description, contract_blob, service_blob) =
253 create_dummy_user_application_description(index);
254 let id = From::from(&description);
255
256 ids.push((id, description, contract_blob, service_blob));
257 }
258
259 Ok(ids)
260}
261
262impl QueryContext {
263 pub fn spawn_service_runtime_actor(self) -> ServiceRuntimeEndpoint {
267 let (execution_state_sender, incoming_execution_requests) =
268 futures::channel::mpsc::unbounded();
269 let (runtime_request_sender, runtime_request_receiver) = std::sync::mpsc::channel();
270
271 thread::spawn(move || {
272 ServiceSyncRuntime::new(execution_state_sender, self).run(runtime_request_receiver)
273 });
274
275 ServiceRuntimeEndpoint {
276 incoming_execution_requests,
277 runtime_request_sender,
278 }
279 }
280}
281
282pub fn test_accounts_strategy() -> impl Strategy<Value = BTreeMap<AccountOwner, Amount>> {
285 proptest::collection::btree_map(
286 any::<AccountOwner>(),
287 (1_u128..).prop_map(Amount::from_tokens),
288 0..5,
289 )
290}
291
292pub fn blob_oracle_responses<'a>(blobs: impl Iterator<Item = &'a BlobId>) -> Vec<OracleResponse> {
294 blobs
295 .into_iter()
296 .copied()
297 .map(OracleResponse::Blob)
298 .collect()
299}