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, ChainProgress, ExecutionRuntimeConfig,
21 ExecutionRuntimeContext, 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 pub num_incoming_bundles: u32,
59 pub num_operations: u32,
61 pub num_outgoing_messages: u32,
63}
64
65impl SystemExecutionState {
66 pub fn new(description: ChainDescription) -> Self {
68 let ownership = description.config().ownership.clone();
69 let account = description.config().account;
70 let initial_balance = description.config().balance;
71 let epoch = description.config().epoch;
72 let admin_chain_id = Some(dummy_chain_description(0).id());
73 let (balance, balances) = if account.is_chain() {
74 (initial_balance, BTreeMap::new())
75 } else {
76 (
77 Amount::ZERO,
78 BTreeMap::from_iter([(account, initial_balance)]),
79 )
80 };
81 SystemExecutionState {
82 epoch,
83 description: Some(description),
84 admin_chain_id,
85 ownership,
86 balance,
87 balances,
88 committees: dummy_committees(),
89 ..SystemExecutionState::default()
90 }
91 }
92
93 pub fn dummy_chain_state(index: u32) -> (Self, ChainId) {
96 let description = dummy_chain_description(index);
97 let chain_id = description.id();
98 (Self::new(description), chain_id)
99 }
100
101 pub async fn into_hash(self) -> CryptoHash {
103 let mut view = self.into_view().await;
104 view.crypto_hash_mut()
105 .await
106 .expect("hashing from memory should not fail")
107 }
108
109 pub async fn into_view(self) -> ExecutionStateView<MemoryContext<TestExecutionRuntimeContext>> {
111 let chain_id = self
112 .description
113 .as_ref()
114 .expect("Chain description should be set")
115 .into();
116 self.into_view_with(chain_id, ExecutionRuntimeConfig::default())
117 .await
118 }
119
120 pub async fn into_view_with(
123 self,
124 chain_id: ChainId,
125 execution_runtime_config: ExecutionRuntimeConfig,
126 ) -> ExecutionStateView<MemoryContext<TestExecutionRuntimeContext>> {
127 let SystemExecutionState {
129 description,
130 epoch,
131 admin_chain_id,
132 committees,
133 ownership,
134 balance,
135 balances,
136 timestamp,
137 used_blobs,
138 closed,
139 application_permissions,
140 extra_blobs,
141 mock_applications,
142 num_incoming_bundles,
143 num_operations,
144 num_outgoing_messages,
145 } = self;
146
147 let extra = TestExecutionRuntimeContext::new(chain_id, execution_runtime_config);
148 extra
149 .add_blobs(extra_blobs)
150 .await
151 .expect("Adding blobs to the `TestExecutionRuntimeContext` should not fail");
152 for (id, mock_application) in mock_applications {
153 extra
154 .user_contracts()
155 .pin()
156 .insert(id, mock_application.clone().into());
157 extra
158 .user_services()
159 .pin()
160 .insert(id, mock_application.into());
161 }
162
163 let mut committee_hashes = BTreeMap::new();
164 for (committee_epoch, committee) in committees {
165 let blob = Blob::new_committee(bcs::to_bytes(&committee).expect("BCS should succeed"));
166 let hash = blob.id().hash;
167 extra
168 .add_blobs([blob])
169 .await
170 .expect("Adding committee blobs should not fail");
171 committee_hashes.insert(committee_epoch, hash);
172 }
173
174 let context = MemoryContext::new_for_testing(extra);
175 let mut view = ExecutionStateView::load(context)
176 .await
177 .expect("Loading from memory should work");
178 view.system.description.set(description);
179 view.system.epoch.set(epoch);
180 view.system.admin_chain_id.set(admin_chain_id);
181 view.system
182 .committee_hash
183 .set(committee_hashes.get(&epoch).copied());
184 view.system.ownership.set(ownership);
185 view.system.balance.set(balance);
186 for (account_owner, balance) in balances {
187 view.system
188 .balances
189 .insert(&account_owner, balance)
190 .expect("insertion of balances should not fail");
191 }
192 for blob_id in used_blobs {
193 view.system
194 .used_blobs
195 .insert(&blob_id)
196 .expect("inserting blob IDs should not fail");
197 }
198 view.system.closed.set(closed);
199 view.system
200 .application_permissions
201 .set(application_permissions);
202 view.system.progress.set(ChainProgress {
203 timestamp,
204 num_incoming_bundles,
205 num_operations,
206 num_outgoing_messages,
207 });
208 view
209 }
210}
211
212impl RegisterMockApplication for SystemExecutionState {
213 async fn creator_chain_id(&self) -> ChainId {
214 self.description.as_ref().expect(
215 "Can't register applications on a system state with no associated `ChainDescription`",
216 ).into()
217 }
218
219 async fn register_mock_application_with(
220 &mut self,
221 description: ApplicationDescription,
222 contract: Blob,
223 service: Blob,
224 ) -> anyhow::Result<(ApplicationId, MockApplication)> {
225 let id = ApplicationId::from(&description);
226 let application = MockApplication::default();
227
228 self.extra_blobs.extend([
229 contract,
230 service,
231 Blob::new_application_description(&description),
232 ]);
233 self.mock_applications.insert(id, application.clone());
234
235 Ok((id, application))
236 }
237}