1use super::*;
2use crate::internal_prelude::*;
3use radix_transactions::{model::*, validation::TransactionValidator};
4
5use crate::{
6 system::bootstrap::FlashReceipt,
7 transaction::*,
8 vm::{VmInitialize, VmModules},
9};
10
11pub struct ProtocolUpdateExecutor {
12 pub network_definition: NetworkDefinition,
13 pub protocol_version: ProtocolVersion,
14 pub generator: Box<dyn ProtocolUpdateGenerator>,
15 pub start_at_batch_group_index: usize,
16 pub start_at_batch_index_in_first_group: usize,
17}
18
19impl ProtocolUpdateExecutor {
20 pub fn new_for_version(protocol_version: ProtocolVersion, settings: &ProtocolSettings) -> Self {
21 let network_definition = settings.network_definition.clone();
22 let generator = settings.resolve_generator_for_update(&protocol_version);
23 Self {
24 network_definition,
25 protocol_version,
26 generator,
27 start_at_batch_group_index: 0,
28 start_at_batch_index_in_first_group: 0,
29 }
30 }
31
32 pub fn continue_for_version(
33 protocol_version: ProtocolVersion,
34 settings: &ProtocolSettings,
35 from_inclusive: (usize, usize),
36 ) -> Self {
37 let network_definition = settings.network_definition.clone();
38 let batch_generator = settings.resolve_generator_for_update(&protocol_version);
39 Self {
40 network_definition,
41 protocol_version,
42 generator: batch_generator,
43 start_at_batch_group_index: from_inclusive.0,
44 start_at_batch_index_in_first_group: from_inclusive.1,
45 }
46 }
47
48 pub fn new<US: UpdateSettings + 'static>(
49 network_definition: NetworkDefinition,
50 update_settings: US,
51 ) -> Self {
52 let protocol_version = US::protocol_version();
53 let batch_generator = Box::new(update_settings.create_generator());
54 Self {
55 network_definition,
56 protocol_version,
57 generator: batch_generator,
58 start_at_batch_group_index: 0,
59 start_at_batch_index_in_first_group: 0,
60 }
61 }
62
63 pub fn run_and_commit(self, store: &mut (impl SubstateDatabase + CommittableSubstateDatabase)) {
64 self.run_and_commit_advanced(store, &mut (), &VmModules::default());
65 }
66
67 pub fn run_and_commit_advanced<
68 S: SubstateDatabase + CommittableSubstateDatabase,
69 H: ProtocolUpdateExecutionHooks,
70 M: VmInitialize,
71 >(
72 self,
73 store: &mut S,
74 hooks: &mut H,
75 vm_modules: &M,
76 ) {
77 let add_status_update = self.generator.insert_status_tracking_flash_transactions();
78
79 let mut batch_groups = self.generator.batch_groups();
80
81 if add_status_update {
82 batch_groups.push(
84 FixedBatchGroupGenerator::named("completion")
85 .add_batch("record-completion", |_| ProtocolUpdateBatch::empty())
86 .build(),
87 );
88 }
89
90 let batch_group_count = batch_groups.len();
91
92 for (batch_group_index, batch_group_generator) in batch_groups
93 .into_iter()
94 .skip(self.start_at_batch_group_index)
95 .enumerate()
96 {
97 let batch_group_name = batch_group_generator.batch_group_name();
98 let start_at_batch = if batch_group_index == self.start_at_batch_group_index {
99 self.start_at_batch_index_in_first_group
100 } else {
101 0
102 };
103 let batches = batch_group_generator.generate_batches(&*store);
104 let batch_count = batches.len();
105
106 for (batch_index, batch_generator) in
107 batches.into_iter().skip(start_at_batch).enumerate()
108 {
109 let batch_name = batch_generator.batch_name().to_string();
110 let batch_name = batch_name.as_str();
111
112 let mut batch = batch_generator.generate_batch(store);
113
114 if add_status_update {
115 batch.mut_add(generate_update_status_flash_transaction(
116 self.protocol_version,
117 batch_group_index,
118 batch_group_name,
119 batch_group_count,
120 batch_index,
121 batch_name,
122 batch_count,
123 ));
124 }
125
126 for (transaction_index, transaction) in batch.transactions.into_iter().enumerate() {
127 let receipt = match &transaction {
128 ProtocolUpdateTransaction::FlashTransactionV1(flash) => {
129 let db_updates = flash.state_updates.create_database_updates();
130 let receipt = if H::IS_ENABLED {
131 let before_store = &*store;
132 FlashReceipt::from_state_updates(
133 flash.state_updates.clone(),
134 before_store,
135 )
136 .into()
137 } else {
138 TransactionReceipt::empty_commit_success()
140 };
141
142 store.commit(&db_updates);
143 receipt
144 }
145 ProtocolUpdateTransaction::SystemTransactionV1(
146 ProtocolSystemTransactionV1 {
147 transaction,
148 disable_auth,
149 ..
150 },
151 ) => {
152 let execution_config = if *disable_auth {
153 ExecutionConfig::for_auth_disabled_system_transaction(
154 self.network_definition.clone(),
155 )
156 } else {
157 ExecutionConfig::for_system_transaction(
158 self.network_definition.clone(),
159 )
160 };
161 let execution_config = hooks.adapt_execution_config(execution_config);
162 let validator =
163 TransactionValidator::new(store, &self.network_definition);
164
165 let receipt = execute_and_commit_transaction(
166 store,
167 vm_modules,
168 &execution_config,
169 transaction
170 .with_proofs_ref(btreeset![system_execution(
171 SystemExecution::Protocol
172 )])
173 .into_executable(&validator)
174 .expect(
175 "Expected protocol update transaction to be preparable",
176 ),
177 );
178 receipt.expect_commit_success();
179 receipt
180 }
181 };
182
183 if H::IS_ENABLED {
184 hooks.on_transaction_executed(OnProtocolTransactionExecuted {
185 protocol_version: self.protocol_version,
186 batch_group_index,
187 batch_group_name,
188 batch_index,
189 batch_name,
190 transaction_index,
191 transaction: &transaction,
192 receipt: &receipt,
193 resultant_store: store,
194 });
195 }
196 }
197
198 if H::IS_ENABLED {
199 hooks.on_transaction_batch_committed(OnProtocolTransactionBatchCommitted {
200 protocol_version: self.protocol_version,
201 batch_group_index,
202 batch_group_name,
203 batch_index,
204 batch_name: &batch_name,
205 status_update_committed: add_status_update,
206 resultant_store: store,
207 });
208 }
209 }
210 }
211
212 if H::IS_ENABLED {
213 hooks.on_protocol_update_completed(OnProtocolUpdateCompleted {
214 protocol_version: self.protocol_version,
215 status_update_committed: add_status_update,
216 resultant_store: store,
217 });
218 }
219 }
220}
221
222pub fn generate_update_status_flash_transaction(
223 protocol_version: ProtocolVersion,
224 batch_group_index: usize,
225 batch_group_name: &str,
226 batch_group_count: usize,
227 batch_index: usize,
228 batch_name: &str,
229 batch_count: usize,
230) -> ProtocolUpdateTransaction {
231 let is_last_batch =
232 batch_group_index == batch_group_count - 1 && batch_index == batch_count - 1;
233
234 let status = if is_last_batch {
235 ProtocolUpdateStatusSummary {
236 protocol_version: protocol_version,
237 update_status: ProtocolUpdateStatus::Complete,
238 }
239 } else {
240 ProtocolUpdateStatusSummary {
241 protocol_version: protocol_version,
242 update_status: ProtocolUpdateStatus::InProgress {
243 latest_commit: LatestProtocolUpdateCommitBatch {
244 batch_group_index,
245 batch_group_name: batch_group_name.to_string(),
246 batch_index,
247 batch_name: batch_name.to_string(),
248 },
249 },
250 }
251 };
252
253 ProtocolUpdateTransaction::flash(
254 "status-summary",
255 StateUpdates::empty().set_substate(
256 TRANSACTION_TRACKER,
257 PROTOCOL_UPDATE_STATUS_PARTITION,
258 ProtocolUpdateStatusField::Summary,
259 ProtocolUpdateStatusSummarySubstate::from_latest_version(status),
260 ),
261 )
262}
263
264#[allow(unused_variables)]
265pub trait ProtocolUpdateExecutionHooks {
266 const IS_ENABLED: bool = true;
268
269 fn adapt_execution_config(&mut self, config: ExecutionConfig) -> ExecutionConfig {
270 config
271 }
272
273 fn on_transaction_executed(&mut self, event: OnProtocolTransactionExecuted) {}
274
275 fn on_transaction_batch_committed(&mut self, event: OnProtocolTransactionBatchCommitted) {}
276
277 fn on_protocol_update_completed(&mut self, event: OnProtocolUpdateCompleted) {}
278}
279
280pub struct OnProtocolTransactionExecuted<'a> {
283 pub protocol_version: ProtocolVersion,
284 pub batch_group_index: usize,
285 pub batch_group_name: &'a str,
286 pub batch_index: usize,
287 pub batch_name: &'a str,
288 pub transaction_index: usize,
289 pub transaction: &'a ProtocolUpdateTransaction,
290 pub receipt: &'a TransactionReceipt,
291 pub resultant_store: &'a mut dyn SubstateDatabase,
292}
293
294pub struct OnProtocolTransactionBatchCommitted<'a> {
295 pub protocol_version: ProtocolVersion,
296 pub batch_group_index: usize,
297 pub batch_group_name: &'a str,
298 pub batch_index: usize,
299 pub batch_name: &'a str,
300 pub status_update_committed: bool,
301 pub resultant_store: &'a mut dyn SubstateDatabase,
302}
303
304pub struct OnProtocolUpdateCompleted<'a> {
305 pub protocol_version: ProtocolVersion,
306 pub status_update_committed: bool,
307 pub resultant_store: &'a mut dyn SubstateDatabase,
308}
309
310impl ProtocolUpdateExecutionHooks for () {
311 const IS_ENABLED: bool = false;
312}