linera_execution/test_utils/
system_execution_state.rs1use 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#[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 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}