radix_engine/updates/
protocol_update_executor.rs

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            // The status update itself will get added when the batch is processed
83            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                                // Cheap fallback
139                                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    // If false, hooks aren't called, so opt out of constructing things like receipts.
267    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
280/// Using a struct allows lots of parameters to be passed, without
281/// having a large number of method arguments
282pub 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}