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