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::{dummy_chain_description, MockApplication, RegisterMockApplication};
25use crate::{
26 committee::Committee, execution::UserAction, ApplicationDescription, ExecutionError,
27 ExecutionRuntimeConfig, ExecutionRuntimeContext, ExecutionStateView, OperationContext,
28 ResourceControlPolicy, ResourceController, ResourceTracker, TestExecutionRuntimeContext,
29 UserContractCode,
30};
31
32#[derive(Default, Debug, PartialEq, Eq, Clone)]
34pub struct SystemExecutionState {
35 pub description: Option<ChainDescription>,
36 pub epoch: Epoch,
37 pub admin_id: Option<ChainId>,
38 pub committees: BTreeMap<Epoch, Committee>,
39 pub ownership: ChainOwnership,
40 pub balance: Amount,
41 #[debug(skip_if = BTreeMap::is_empty)]
42 pub balances: BTreeMap<AccountOwner, Amount>,
43 pub timestamp: Timestamp,
44 pub used_blobs: BTreeSet<BlobId>,
45 #[debug(skip_if = Not::not)]
46 pub closed: bool,
47 pub application_permissions: ApplicationPermissions,
48 #[debug(skip_if = Vec::is_empty)]
49 pub extra_blobs: Vec<Blob>,
50 #[debug(skip_if = BTreeMap::is_empty)]
51 pub mock_applications: BTreeMap<ApplicationId, MockApplication>,
52}
53
54impl SystemExecutionState {
55 pub fn new(description: ChainDescription) -> Self {
56 let ownership = description.config().ownership.clone();
57 let balance = description.config().balance;
58 let epoch = description.config().epoch;
59 let admin_id = Some(dummy_chain_description(0).id());
60 let committees = description
61 .config()
62 .committees
63 .iter()
64 .map(|(epoch, serialized_committee)| {
65 (
66 *epoch,
67 bcs::from_bytes::<Committee>(serialized_committee)
68 .expect("should correctly deserialize a committee"),
69 )
70 })
71 .collect();
72 SystemExecutionState {
73 epoch,
74 description: Some(description),
75 admin_id,
76 ownership,
77 balance,
78 committees,
79 ..SystemExecutionState::default()
80 }
81 }
82
83 pub fn dummy_chain_state(index: u32) -> (Self, ChainId) {
84 let description = dummy_chain_description(index);
85 let chain_id = description.id();
86 (Self::new(description), chain_id)
87 }
88
89 pub async fn into_hash(self) -> CryptoHash {
90 let view = self.into_view().await;
91 view.crypto_hash()
92 .await
93 .expect("hashing from memory should not fail")
94 }
95
96 pub async fn into_view(self) -> ExecutionStateView<MemoryContext<TestExecutionRuntimeContext>> {
97 let chain_id = self
98 .description
99 .as_ref()
100 .expect("Chain description should be set")
101 .into();
102 self.into_view_with(chain_id, ExecutionRuntimeConfig::default())
103 .await
104 }
105
106 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_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 .insert(id, mock_application.clone().into());
137 extra.user_services().insert(id, mock_application.into());
138 }
139
140 let context = MemoryContext::new_for_testing(extra);
141 let mut view = ExecutionStateView::load(context)
142 .await
143 .expect("Loading from memory should work");
144 view.system.description.set(description);
145 view.system.epoch.set(epoch);
146 view.system.admin_id.set(admin_id);
147 view.system.committees.set(committees);
148 view.system.ownership.set(ownership);
149 view.system.balance.set(balance);
150 for (account_owner, balance) in balances {
151 view.system
152 .balances
153 .insert(&account_owner, balance)
154 .expect("insertion of balances should not fail");
155 }
156 view.system.timestamp.set(timestamp);
157 for blob_id in used_blobs {
158 view.system
159 .used_blobs
160 .insert(&blob_id)
161 .expect("inserting blob IDs should not fail");
162 }
163 view.system.closed.set(closed);
164 view.system
165 .application_permissions
166 .set(application_permissions);
167 view
168 }
169}
170
171impl RegisterMockApplication for SystemExecutionState {
172 fn creator_chain_id(&self) -> ChainId {
173 self.description.as_ref().expect(
174 "Can't register applications on a system state with no associated `ChainDescription`",
175 ).into()
176 }
177
178 async fn register_mock_application_with(
179 &mut self,
180 description: ApplicationDescription,
181 contract: Blob,
182 service: Blob,
183 ) -> anyhow::Result<(ApplicationId, MockApplication)> {
184 let id = ApplicationId::from(&description);
185 let application = MockApplication::default();
186
187 self.extra_blobs.extend([
188 contract,
189 service,
190 Blob::new_application_description(&description),
191 ]);
192 self.mock_applications.insert(id, application.clone());
193
194 Ok((id, application))
195 }
196}