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