linera_execution/test_utils/
mod.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4// Some of these items are only used by some tests, but Rust will complain about unused
5// items for the tests where they aren't used
6#![allow(unused_imports)]
7
8mod mock_application;
9#[cfg(with_revm)]
10pub mod solidity;
11mod system_execution_state;
12
13use std::{collections::BTreeMap, sync::Arc, thread, vec};
14
15use linera_base::{
16    crypto::{AccountPublicKey, BcsSignable, CryptoHash, ValidatorPublicKey},
17    data_types::{
18        Amount, Blob, BlockHeight, ChainDescription, ChainOrigin, CompressedBytecode, Epoch,
19        InitialChainConfig, OracleResponse, Timestamp,
20    },
21    identifiers::{AccountOwner, ApplicationId, BlobId, BlobType, ChainId, MessageId, ModuleId},
22    ownership::ChainOwnership,
23    vm::VmRuntime,
24};
25use linera_views::{context::Context, views::View, ViewError};
26use proptest::{prelude::any, strategy::Strategy};
27use serde::{Deserialize, Serialize};
28
29pub use self::{
30    mock_application::{ExpectedCall, MockApplication, MockApplicationInstance},
31    system_execution_state::SystemExecutionState,
32};
33use crate::{
34    committee::Committee, ApplicationDescription, ExecutionRequest, ExecutionRuntimeContext,
35    ExecutionStateView, MessageContext, OperationContext, QueryContext, ServiceRuntimeEndpoint,
36    ServiceRuntimeRequest, ServiceSyncRuntime, SystemExecutionStateView,
37    TestExecutionRuntimeContext,
38};
39
40pub fn dummy_committee() -> Committee {
41    Committee::make_simple(vec![(
42        ValidatorPublicKey::test_key(0),
43        AccountPublicKey::test_key(0),
44    )])
45}
46
47pub fn dummy_committees() -> BTreeMap<Epoch, Committee> {
48    let committee = dummy_committee();
49    BTreeMap::from([(Epoch::ZERO, committee)])
50}
51
52pub fn dummy_chain_description_with_ownership_and_balance(
53    index: u32,
54    ownership: ChainOwnership,
55    balance: Amount,
56) -> ChainDescription {
57    let origin = ChainOrigin::Root(index);
58    let config = InitialChainConfig {
59        application_permissions: Default::default(),
60        balance,
61        epoch: Epoch::ZERO,
62        min_active_epoch: Epoch::ZERO,
63        max_active_epoch: Epoch::ZERO,
64        ownership,
65    };
66    ChainDescription::new(origin, config, Timestamp::default())
67}
68
69pub fn dummy_chain_description_with_owner(index: u32, owner: AccountOwner) -> ChainDescription {
70    dummy_chain_description_with_ownership_and_balance(
71        index,
72        ChainOwnership::single(owner),
73        Amount::MAX,
74    )
75}
76
77pub fn dummy_chain_description(index: u32) -> ChainDescription {
78    let chain_key = AccountPublicKey::test_key(2 * (index % 128) as u8 + 1);
79    let ownership = ChainOwnership::single(chain_key.into());
80    dummy_chain_description_with_ownership_and_balance(index, ownership, Amount::MAX)
81}
82
83/// Creates a dummy [`ApplicationDescription`] for use in tests.
84pub fn create_dummy_user_application_description(
85    index: u32,
86) -> (ApplicationDescription, Blob, Blob) {
87    let chain_id = dummy_chain_description(1).id();
88    let mut contract_bytes = b"contract".to_vec();
89    let mut service_bytes = b"service".to_vec();
90    contract_bytes.push(index as u8);
91    service_bytes.push(index as u8);
92    let contract_blob = Blob::new_contract_bytecode(CompressedBytecode {
93        compressed_bytes: contract_bytes,
94    });
95    let service_blob = Blob::new_service_bytecode(CompressedBytecode {
96        compressed_bytes: service_bytes,
97    });
98
99    let vm_runtime = VmRuntime::Wasm;
100    (
101        ApplicationDescription {
102            module_id: ModuleId::new(contract_blob.id().hash, service_blob.id().hash, vm_runtime),
103            creator_chain_id: chain_id,
104            block_height: 0.into(),
105            application_index: index,
106            required_application_ids: vec![],
107            parameters: vec![],
108        },
109        contract_blob,
110        service_blob,
111    )
112}
113
114/// Creates a dummy [`OperationContext`] to use in tests.
115pub fn create_dummy_operation_context(chain_id: ChainId) -> OperationContext {
116    OperationContext {
117        chain_id,
118        height: BlockHeight(0),
119        round: Some(0),
120        authenticated_signer: None,
121        authenticated_caller_id: None,
122        timestamp: Default::default(),
123    }
124}
125
126/// Creates a dummy [`MessageContext`] to use in tests.
127pub fn create_dummy_message_context(
128    chain_id: ChainId,
129    authenticated_signer: Option<AccountOwner>,
130) -> MessageContext {
131    MessageContext {
132        chain_id,
133        is_bouncing: false,
134        authenticated_signer,
135        refund_grant_to: None,
136        height: BlockHeight(0),
137        round: Some(0),
138        message_id: MessageId {
139            chain_id,
140            height: BlockHeight(0),
141            index: 0,
142        },
143        timestamp: Default::default(),
144    }
145}
146
147/// Creates a dummy [`QueryContext`] to use in tests.
148pub fn create_dummy_query_context() -> QueryContext {
149    QueryContext {
150        chain_id: dummy_chain_description(0).id(),
151        next_block_height: BlockHeight(0),
152        local_time: Timestamp::from(0),
153    }
154}
155
156/// Registration of [`MockApplication`]s to use in tests.
157#[allow(async_fn_in_trait)]
158pub trait RegisterMockApplication {
159    /// Returns the chain to use for the creation of the application.
160    ///
161    /// This is included in the mocked [`ApplicationId`].
162    fn creator_chain_id(&self) -> ChainId;
163
164    /// Registers a new [`MockApplication`] and returns it with the [`ApplicationId`] that was
165    /// used for it.
166    async fn register_mock_application(
167        &mut self,
168        index: u32,
169    ) -> anyhow::Result<(ApplicationId, MockApplication, [BlobId; 3])> {
170        let (description, contract, service) = create_dummy_user_application_description(index);
171        let description_blob_id = Blob::new_application_description(&description).id();
172        let contract_blob_id = contract.id();
173        let service_blob_id = service.id();
174
175        let (app_id, application) = self
176            .register_mock_application_with(description, contract, service)
177            .await?;
178        Ok((
179            app_id,
180            application,
181            [description_blob_id, contract_blob_id, service_blob_id],
182        ))
183    }
184
185    /// Registers a new [`MockApplication`] associated with a [`ApplicationDescription`] and
186    /// its bytecode [`Blob`]s.
187    async fn register_mock_application_with(
188        &mut self,
189        description: ApplicationDescription,
190        contract: Blob,
191        service: Blob,
192    ) -> anyhow::Result<(ApplicationId, MockApplication)>;
193}
194
195impl<C> RegisterMockApplication for ExecutionStateView<C>
196where
197    C: Context + Clone + Send + Sync + 'static,
198    C::Extra: ExecutionRuntimeContext,
199{
200    fn creator_chain_id(&self) -> ChainId {
201        self.system.creator_chain_id()
202    }
203
204    async fn register_mock_application_with(
205        &mut self,
206        description: ApplicationDescription,
207        contract: Blob,
208        service: Blob,
209    ) -> anyhow::Result<(ApplicationId, MockApplication)> {
210        self.system
211            .register_mock_application_with(description, contract, service)
212            .await
213    }
214}
215
216impl<C> RegisterMockApplication for SystemExecutionStateView<C>
217where
218    C: Context + Clone + Send + Sync + 'static,
219    C::Extra: ExecutionRuntimeContext,
220{
221    fn creator_chain_id(&self) -> ChainId {
222        self.description.get().as_ref().expect(
223            "Can't register applications on a system state with no associated `ChainDescription`",
224        ).into()
225    }
226
227    async fn register_mock_application_with(
228        &mut self,
229        description: ApplicationDescription,
230        contract: Blob,
231        service: Blob,
232    ) -> anyhow::Result<(ApplicationId, MockApplication)> {
233        let id = From::from(&description);
234        let extra = self.context().extra();
235        let mock_application = MockApplication::default();
236
237        extra
238            .user_contracts()
239            .insert(id, mock_application.clone().into());
240        extra
241            .user_services()
242            .insert(id, mock_application.clone().into());
243        extra
244            .add_blobs([
245                contract,
246                service,
247                Blob::new_application_description(&description),
248            ])
249            .await?;
250
251        Ok((id, mock_application))
252    }
253}
254
255pub async 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    /// Spawns a thread running the [`ServiceSyncRuntime`] actor.
273    ///
274    /// Returns the endpoints to communicate with the actor.
275    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
291/// Creates a [`Strategy`] for creating a [`BTreeMap`] of [`AccountOwner`]s with an initial
292/// non-zero [`Amount`] of tokens.
293pub 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
301/// Creates a vector of ['OracleResponse`]s for the supplied [`BlobId`]s.
302pub 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}