radix_engine/updates/
protocol_updates.rs

1use crate::internal_prelude::*;
2
3define_single_versioned! {
4    #[derive(Debug, Clone, PartialEq, Eq, Sbor)]
5    pub ProtocolUpdateStatusSummarySubstate(ProtocolUpdateStatusSummaryVersions) => ProtocolUpdateStatusSummary = ProtocolUpdateStatusSummaryV1,
6    outer_attributes: [
7        #[derive(ScryptoSborAssertion)]
8        #[sbor_assert(backwards_compatible(
9            cuttlefish = "FILE:protocol_update_status_substate_cuttlefish_schema.bin",
10            dugong = "FILE:protocol_update_status_substate_dugong_schema.bin",
11        ))]
12    ]
13}
14
15impl ProtocolUpdateStatusSummarySubstate {
16    pub fn load(database: &impl SubstateDatabase) -> Self {
17        let substate = database.get_substate(
18            TRANSACTION_TRACKER,
19            PROTOCOL_UPDATE_STATUS_PARTITION,
20            ProtocolUpdateStatusField::Summary,
21        );
22        if let Some(value) = substate {
23            return value;
24        }
25        // We are pre-cuttlefish. Need to distinguish between different versions.
26        let protocol_version = if database
27            .get_raw_substate(
28                TRANSACTION_TRACKER,
29                BOOT_LOADER_PARTITION,
30                BootLoaderField::SystemBoot,
31            )
32            .is_some()
33        {
34            ProtocolVersion::Bottlenose
35        } else if database
36            .get_raw_substate(
37                TRANSACTION_TRACKER,
38                BOOT_LOADER_PARTITION,
39                BootLoaderField::VmBoot,
40            )
41            .is_some()
42        {
43            ProtocolVersion::Anemone
44        } else if database
45            .get_raw_substate(
46                TRANSACTION_TRACKER,
47                TYPE_INFO_FIELD_PARTITION,
48                TypeInfoField::TypeInfo,
49            )
50            .is_some()
51        {
52            ProtocolVersion::Babylon
53        } else {
54            ProtocolVersion::Unbootstrapped
55        };
56
57        ProtocolUpdateStatusSummaryV1 {
58            protocol_version,
59            update_status: ProtocolUpdateStatus::Complete,
60        }
61        .into()
62    }
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
66pub struct ProtocolUpdateStatusSummaryV1 {
67    pub protocol_version: ProtocolVersion,
68    pub update_status: ProtocolUpdateStatus,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
72pub enum ProtocolUpdateStatus {
73    Complete,
74    InProgress {
75        latest_commit: LatestProtocolUpdateCommitBatch,
76    },
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
80pub struct LatestProtocolUpdateCommitBatch {
81    pub batch_group_index: usize,
82    pub batch_group_name: String,
83    pub batch_index: usize,
84    pub batch_name: String,
85}
86
87macro_rules! count {
88    (
89        $ident: ident, $($other_idents: ident),* $(,)?
90    ) => {
91        1 + count!( $($other_idents),* )
92    };
93    (
94        $ident: ident $(,)?
95    ) => {
96        1
97    }
98}
99
100macro_rules! latest {
101    (
102        $enum_ident: ident, $ident: ident, $($other_idents: ident),* $(,)?
103    ) => {
104        latest!( $enum_ident, $($other_idents),* )
105    };
106    (
107        $enum_ident: ident, $ident: ident $(,)?
108    ) => {
109        $enum_ident :: $ident
110    }
111}
112
113macro_rules! define_enum {
114    (
115        $ident:ident,
116        $(
117            (
118                $variant_name: ident,
119                $logical_name: expr,
120                $display_name: expr
121            )
122        ),* $(,)?
123    ) => {
124        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Sbor)]
125        pub enum $ident {
126            $($variant_name),*
127        }
128
129        impl $ident {
130            const VARIANTS: [Self; count!( $($variant_name),* )] = [
131                $(
132                    Self::$variant_name
133                ),*
134            ];
135
136            pub const LATEST: $ident = latest!( $ident, $($variant_name),* );
137
138            pub const fn logical_name(&self) -> &'static str {
139                match self {
140                    $(
141                        Self::$variant_name => $logical_name
142                    ),*
143                }
144            }
145
146            pub const fn display_name(&self) -> &'static str {
147                match self {
148                    $(
149                        Self::$variant_name => $display_name
150                    ),*
151                }
152            }
153
154            pub fn try_from_logical_name(logical_name: &str) -> Option<Self> {
155                match logical_name {
156                    $(
157                        $logical_name => Some(Self::$variant_name)
158                    ),*,
159                    _ => None
160                }
161            }
162
163            pub fn try_from_display_name(display_name: &str) -> Option<Self> {
164                match display_name {
165                    $(
166                        $display_name => Some(Self::$variant_name)
167                    ),*,
168                    _ => None
169                }
170            }
171        }
172    };
173}
174
175macro_rules! define_protocol_version_and_updates {
176    (
177        pregenesis: {
178            variant_name: $pregenesis_variant_name: ident,
179            logical_name: $pregenesis_logical_name: expr,
180            display_name: $pregenesis_display_name: expr $(,)?
181        },
182        genesis: {
183            variant_name: $genesis_variant_name: ident,
184            logical_name: $genesis_logical_name: expr,
185            display_name: $genesis_display_name: expr $(,)?
186        },
187        protocol_updates: [
188            $(
189                {
190                    variant_name: $protocol_update_variant_name: ident,
191                    logical_name: $protocol_update_logical_name: expr,
192                    display_name: $protocol_update_display_name: expr $(,)?
193                }
194            ),* $(,)?
195        ]
196    ) => {
197        define_enum!(
198            ProtocolVersion,
199            ($pregenesis_variant_name, $pregenesis_logical_name, $pregenesis_display_name),
200            ($genesis_variant_name, $genesis_logical_name, $genesis_display_name),
201            $(($protocol_update_variant_name, $protocol_update_logical_name, $protocol_update_display_name)),*
202        );
203
204        impl ProtocolVersion {
205            pub const PRE_GENESIS: Self = Self::$pregenesis_variant_name;
206            pub const GENESIS: Self = Self::$genesis_variant_name;
207        }
208    };
209}
210
211impl ProtocolVersion {
212    /// This points to `CuttlefishPart2`, for symmetry with updates which didn't need to be
213    /// in two parts.
214    #[allow(non_upper_case_globals)]
215    pub const Cuttlefish: Self = Self::CuttlefishPart2;
216}
217
218// This macro defines the protocol version and the protocol updates enums and all of the methods
219// needed on them.
220//
221// The order in which the protocol updates is defined is very important since many places in our
222// codebase relies on it such as applying the protocol updates in order. If the order is changed
223// then the protocol updates will be applied in a different order. So, only thing we can do to
224// is append to this list, never change.
225define_protocol_version_and_updates! {
226    pregenesis: {
227        variant_name: Unbootstrapped,
228        logical_name: "unbootstrapped",
229        display_name: "Unbootstrapped",
230    },
231    genesis: {
232        variant_name: Babylon,
233        logical_name: "babylon",
234        display_name: "Babylon",
235    },
236    protocol_updates: [
237        {
238            variant_name: Anemone,
239            logical_name: "anemone",
240            display_name: "Anemone",
241        },
242        {
243            variant_name: Bottlenose,
244            logical_name: "bottlenose",
245            display_name: "Bottlenose",
246        },
247        {
248            variant_name: CuttlefishPart1,
249            logical_name: "cuttlefish",
250            display_name: "Cuttlefish (Part 1)",
251        },
252        {
253            variant_name: CuttlefishPart2,
254            logical_name: "cuttlefish-part2",
255            display_name: "Cuttlefish (Part 2)",
256        },
257        {
258            variant_name: Dugong,
259            logical_name: "dugong",
260            display_name: "Dugong",
261        }
262    ]
263}
264
265impl ProtocolVersion {
266    pub fn all_from(
267        from_version_inclusive: ProtocolVersion,
268    ) -> impl Iterator<Item = ProtocolVersion> {
269        Self::VARIANTS
270            .into_iter()
271            .skip_while(move |v| *v < from_version_inclusive)
272    }
273
274    pub fn all_between_inclusive(
275        from_version_inclusive: ProtocolVersion,
276        to_version_inclusive: ProtocolVersion,
277    ) -> impl Iterator<Item = ProtocolVersion> {
278        Self::VARIANTS
279            .into_iter()
280            .skip_while(move |v| *v < from_version_inclusive)
281            .take_while(move |v| *v <= to_version_inclusive)
282    }
283
284    pub fn all_between(
285        from_version_inclusive: ProtocolVersion,
286        to_version_exclusive: ProtocolVersion,
287    ) -> impl Iterator<Item = ProtocolVersion> {
288        Self::VARIANTS
289            .into_iter()
290            .skip_while(move |v| *v < from_version_inclusive)
291            .take_while(move |v| *v < to_version_exclusive)
292    }
293
294    pub fn next(&self) -> Option<Self> {
295        Self::VARIANTS.iter().find(|&v| v > self).cloned()
296    }
297}
298
299#[cfg(test)]
300mod tests {
301    use super::*;
302
303    #[test]
304    fn assert_latest_protocol_version_is_as_expected() {
305        assert_eq!(ProtocolVersion::LATEST, ProtocolVersion::Dugong);
306    }
307
308    #[test]
309    fn test_next() {
310        assert_eq!(
311            ProtocolVersion::PRE_GENESIS.next(),
312            Some(ProtocolVersion::GENESIS)
313        );
314        assert_eq!(
315            ProtocolVersion::GENESIS.next(),
316            Some(ProtocolVersion::Anemone)
317        );
318        assert_eq!(
319            ProtocolVersion::Anemone.next(),
320            Some(ProtocolVersion::Bottlenose)
321        );
322        assert_eq!(ProtocolVersion::LATEST.next(), None);
323    }
324
325    #[test]
326    fn assert_protocol_versions_have_the_expected_order() {
327        let variants =
328            ProtocolVersion::all_from(ProtocolVersion::Unbootstrapped).collect::<Vec<_>>();
329
330        assert_eq!(
331            variants,
332            vec![
333                ProtocolVersion::Unbootstrapped,
334                ProtocolVersion::Babylon,
335                ProtocolVersion::Anemone,
336                ProtocolVersion::Bottlenose,
337                ProtocolVersion::CuttlefishPart1,
338                ProtocolVersion::CuttlefishPart2,
339                ProtocolVersion::Dugong,
340            ],
341        );
342        assert!(variants.windows(2).all(|item| item[0] < item[1]))
343    }
344
345    #[test]
346    fn assert_protocol_version_range_queries_work() {
347        assert_eq!(
348            ProtocolVersion::all_between(ProtocolVersion::Babylon, ProtocolVersion::Bottlenose,)
349                .collect::<Vec<_>>(),
350            vec![ProtocolVersion::Babylon, ProtocolVersion::Anemone,],
351        );
352        assert_eq!(
353            ProtocolVersion::all_between_inclusive(
354                ProtocolVersion::Babylon,
355                ProtocolVersion::Bottlenose,
356            )
357            .collect::<Vec<_>>(),
358            vec![
359                ProtocolVersion::Babylon,
360                ProtocolVersion::Anemone,
361                ProtocolVersion::Bottlenose,
362            ],
363        );
364    }
365}