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