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