linera_execution/test_utils/
system_execution_state.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{
5    collections::{BTreeMap, BTreeSet},
6    ops::Not,
7};
8
9use custom_debug_derive::Debug;
10use linera_base::{
11    crypto::CryptoHash,
12    data_types::{Amount, ApplicationPermissions, Blob, ChainDescription, Epoch, Timestamp},
13    identifiers::{AccountOwner, ApplicationId, BlobId, ChainId},
14    ownership::ChainOwnership,
15};
16use linera_views::{
17    context::MemoryContext,
18    views::{CryptoHashView, View},
19};
20
21use super::{dummy_chain_description, dummy_committees, MockApplication, RegisterMockApplication};
22use crate::{
23    committee::Committee, ApplicationDescription, ExecutionRuntimeConfig, ExecutionRuntimeContext,
24    ExecutionStateView, TestExecutionRuntimeContext,
25};
26
27/// A system execution state, not represented as a view but as a simple struct.
28#[derive(Default, Debug, PartialEq, Eq, Clone)]
29pub struct SystemExecutionState {
30    pub description: Option<ChainDescription>,
31    pub epoch: Epoch,
32    pub admin_id: Option<ChainId>,
33    pub committees: BTreeMap<Epoch, Committee>,
34    pub ownership: ChainOwnership,
35    pub balance: Amount,
36    #[debug(skip_if = BTreeMap::is_empty)]
37    pub balances: BTreeMap<AccountOwner, Amount>,
38    pub timestamp: Timestamp,
39    pub used_blobs: BTreeSet<BlobId>,
40    #[debug(skip_if = Not::not)]
41    pub closed: bool,
42    pub application_permissions: ApplicationPermissions,
43    #[debug(skip_if = Vec::is_empty)]
44    pub extra_blobs: Vec<Blob>,
45    #[debug(skip_if = BTreeMap::is_empty)]
46    pub mock_applications: BTreeMap<ApplicationId, MockApplication>,
47}
48
49impl SystemExecutionState {
50    pub fn new(description: ChainDescription) -> Self {
51        let ownership = description.config().ownership.clone();
52        let balance = description.config().balance;
53        let epoch = description.config().epoch;
54        let admin_id = Some(dummy_chain_description(0).id());
55        SystemExecutionState {
56            epoch,
57            description: Some(description),
58            admin_id,
59            ownership,
60            balance,
61            committees: dummy_committees(),
62            ..SystemExecutionState::default()
63        }
64    }
65
66    pub fn dummy_chain_state(index: u32) -> (Self, ChainId) {
67        let description = dummy_chain_description(index);
68        let chain_id = description.id();
69        (Self::new(description), chain_id)
70    }
71
72    pub async fn into_hash(self) -> CryptoHash {
73        let mut view = self.into_view().await;
74        view.crypto_hash_mut()
75            .await
76            .expect("hashing from memory should not fail")
77    }
78
79    pub async fn into_view(self) -> ExecutionStateView<MemoryContext<TestExecutionRuntimeContext>> {
80        let chain_id = self
81            .description
82            .as_ref()
83            .expect("Chain description should be set")
84            .into();
85        self.into_view_with(chain_id, ExecutionRuntimeConfig::default())
86            .await
87    }
88
89    pub async fn into_view_with(
90        self,
91        chain_id: ChainId,
92        execution_runtime_config: ExecutionRuntimeConfig,
93    ) -> ExecutionStateView<MemoryContext<TestExecutionRuntimeContext>> {
94        // Destructure, to make sure we don't miss any fields.
95        let SystemExecutionState {
96            description,
97            epoch,
98            admin_id,
99            committees,
100            ownership,
101            balance,
102            balances,
103            timestamp,
104            used_blobs,
105            closed,
106            application_permissions,
107            extra_blobs,
108            mock_applications,
109        } = self;
110
111        let extra = TestExecutionRuntimeContext::new(chain_id, execution_runtime_config);
112        extra
113            .add_blobs(extra_blobs)
114            .await
115            .expect("Adding blobs to the `TestExecutionRuntimeContext` should not fail");
116        for (id, mock_application) in mock_applications {
117            extra
118                .user_contracts()
119                .pin()
120                .insert(id, mock_application.clone().into());
121            extra
122                .user_services()
123                .pin()
124                .insert(id, mock_application.into());
125        }
126
127        let context = MemoryContext::new_for_testing(extra);
128        let mut view = ExecutionStateView::load(context)
129            .await
130            .expect("Loading from memory should work");
131        view.system.description.set(description);
132        view.system.epoch.set(epoch);
133        view.system.admin_id.set(admin_id);
134        view.system.committees.set(committees);
135        view.system.ownership.set(ownership);
136        view.system.balance.set(balance);
137        for (account_owner, balance) in balances {
138            view.system
139                .balances
140                .insert(&account_owner, balance)
141                .expect("insertion of balances should not fail");
142        }
143        view.system.timestamp.set(timestamp);
144        for blob_id in used_blobs {
145            view.system
146                .used_blobs
147                .insert(&blob_id)
148                .expect("inserting blob IDs should not fail");
149        }
150        view.system.closed.set(closed);
151        view.system
152            .application_permissions
153            .set(application_permissions);
154        view
155    }
156}
157
158impl RegisterMockApplication for SystemExecutionState {
159    fn creator_chain_id(&self) -> ChainId {
160        self.description.as_ref().expect(
161            "Can't register applications on a system state with no associated `ChainDescription`",
162        ).into()
163    }
164
165    async fn register_mock_application_with(
166        &mut self,
167        description: ApplicationDescription,
168        contract: Blob,
169        service: Blob,
170    ) -> anyhow::Result<(ApplicationId, MockApplication)> {
171        let id = ApplicationId::from(&description);
172        let application = MockApplication::default();
173
174        self.extra_blobs.extend([
175            contract,
176            service,
177            Blob::new_application_description(&description),
178        ]);
179        self.mock_applications.insert(id, application.clone());
180
181        Ok((id, application))
182    }
183}