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