linera_execution/test_utils/
system_execution_state.rs1use 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#[derive(Default, Debug, PartialEq, Eq, Clone)]
26pub struct SystemExecutionState {
27 pub description: Option<ChainDescription>,
29 pub epoch: Epoch,
31 pub admin_chain_id: Option<ChainId>,
33 pub committees: BTreeMap<Epoch, Committee>,
35 pub ownership: ChainOwnership,
37 pub balance: Amount,
39 #[debug(skip_if = BTreeMap::is_empty)]
41 pub balances: BTreeMap<AccountOwner, Amount>,
42 pub timestamp: Timestamp,
44 pub used_blobs: BTreeSet<BlobId>,
46 #[debug(skip_if = Not::not)]
48 pub closed: bool,
49 pub application_permissions: ApplicationPermissions,
51 #[debug(skip_if = Vec::is_empty)]
53 pub extra_blobs: Vec<Blob>,
54 #[debug(skip_if = BTreeMap::is_empty)]
56 pub mock_applications: BTreeMap<ApplicationId, MockApplication>,
57}
58
59impl SystemExecutionState {
60 pub fn new(description: ChainDescription) -> Self {
62 let ownership = description.config().ownership.clone();
63 let balance = description.config().balance;
64 let epoch = description.config().epoch;
65 let admin_chain_id = Some(dummy_chain_description(0).id());
66 SystemExecutionState {
67 epoch,
68 description: Some(description),
69 admin_chain_id,
70 ownership,
71 balance,
72 committees: dummy_committees(),
73 ..SystemExecutionState::default()
74 }
75 }
76
77 pub fn dummy_chain_state(index: u32) -> (Self, ChainId) {
80 let description = dummy_chain_description(index);
81 let chain_id = description.id();
82 (Self::new(description), chain_id)
83 }
84
85 pub async fn into_hash(self) -> CryptoHash {
87 let mut view = self.into_view().await;
88 view.crypto_hash_mut()
89 .await
90 .expect("hashing from memory should not fail")
91 }
92
93 pub async fn into_view(self) -> ExecutionStateView<MemoryContext<TestExecutionRuntimeContext>> {
95 let chain_id = self
96 .description
97 .as_ref()
98 .expect("Chain description should be set")
99 .into();
100 self.into_view_with(chain_id, ExecutionRuntimeConfig::default())
101 .await
102 }
103
104 pub async fn into_view_with(
107 self,
108 chain_id: ChainId,
109 execution_runtime_config: ExecutionRuntimeConfig,
110 ) -> ExecutionStateView<MemoryContext<TestExecutionRuntimeContext>> {
111 let SystemExecutionState {
113 description,
114 epoch,
115 admin_chain_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 .pin()
137 .insert(id, mock_application.clone().into());
138 extra
139 .user_services()
140 .pin()
141 .insert(id, mock_application.into());
142 }
143
144 let mut committee_hashes = BTreeMap::new();
145 for (committee_epoch, committee) in committees {
146 let blob = Blob::new_committee(bcs::to_bytes(&committee).expect("BCS should succeed"));
147 let hash = blob.id().hash;
148 extra
149 .add_blobs([blob])
150 .await
151 .expect("Adding committee blobs should not fail");
152 committee_hashes.insert(committee_epoch, hash);
153 }
154
155 let context = MemoryContext::new_for_testing(extra);
156 let mut view = ExecutionStateView::load(context)
157 .await
158 .expect("Loading from memory should work");
159 view.system.description.set(description);
160 view.system.epoch.set(epoch);
161 view.system.admin_chain_id.set(admin_chain_id);
162 view.system
163 .committee_hash
164 .set(committee_hashes.get(&epoch).copied());
165 view.system.ownership.set(ownership);
166 view.system.balance.set(balance);
167 for (account_owner, balance) in balances {
168 view.system
169 .balances
170 .insert(&account_owner, balance)
171 .expect("insertion of balances should not fail");
172 }
173 view.system.timestamp.set(timestamp);
174 for blob_id in used_blobs {
175 view.system
176 .used_blobs
177 .insert(&blob_id)
178 .expect("inserting blob IDs should not fail");
179 }
180 view.system.closed.set(closed);
181 view.system
182 .application_permissions
183 .set(application_permissions);
184 view
185 }
186}
187
188impl RegisterMockApplication for SystemExecutionState {
189 async fn creator_chain_id(&self) -> ChainId {
190 self.description.as_ref().expect(
191 "Can't register applications on a system state with no associated `ChainDescription`",
192 ).into()
193 }
194
195 async fn register_mock_application_with(
196 &mut self,
197 description: ApplicationDescription,
198 contract: Blob,
199 service: Blob,
200 ) -> anyhow::Result<(ApplicationId, MockApplication)> {
201 let id = ApplicationId::from(&description);
202 let application = MockApplication::default();
203
204 self.extra_blobs.extend([
205 contract,
206 service,
207 Blob::new_application_description(&description),
208 ]);
209 self.mock_applications.insert(id, application.clone());
210
211 Ok((id, application))
212 }
213}