Skip to main content

dusk_node_data/
hard_fork.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7use std::sync::OnceLock;
8
9use dusk_core::signatures::bls::BlsVersion;
10use dusk_core::transfer::TransactionFormat;
11
12/// Activation height value that means "never activate".
13const NEVER: u64 = u64::MAX;
14
15/// Active protocol hardfork.
16#[derive(Debug, Clone, Copy, Eq, PartialEq)]
17pub enum HardFork {
18    /// Behavior before any explicit hardfork activation.
19    PreFork,
20    /// Behavior after Aegis activation.
21    Aegis,
22    /// Behavior after Boreas activation.
23    Boreas,
24}
25
26impl HardFork {
27    /// Returns the BLS signature version for this hardfork.
28    pub const fn bls_version(self) -> BlsVersion {
29        match self {
30            HardFork::Aegis | HardFork::Boreas => BlsVersion::V2,
31            HardFork::PreFork => BlsVersion::V1,
32        }
33    }
34
35    /// Returns the ledger transaction format for this hardfork era.
36    pub const fn ledger_tx_format(self) -> TransactionFormat {
37        match self {
38            HardFork::PreFork => TransactionFormat::PreAegis,
39            HardFork::Aegis => TransactionFormat::Aegis,
40            HardFork::Boreas => TransactionFormat::Boreas,
41        }
42    }
43
44    /// Returns the ingress transaction format for this hardfork era.
45    pub const fn ingress_tx_format(self) -> TransactionFormat {
46        match self {
47            HardFork::PreFork | HardFork::Aegis => TransactionFormat::Aegis,
48            HardFork::Boreas => TransactionFormat::Boreas,
49        }
50    }
51}
52
53/// Returns the BLS version for the given block height.
54pub fn bls_version_at(block_height: u64) -> BlsVersion {
55    hard_fork_at(block_height).bls_version()
56}
57
58static AEGIS_ACTIVATION_HEIGHT: OnceLock<u64> = OnceLock::new();
59static BOREAS_ACTIVATION_HEIGHT: OnceLock<u64> = OnceLock::new();
60
61/// Initializes the Aegis activation height once for this process.
62pub fn set_aegis_activation_height(block_height: u64) {
63    if let Some(existing) = AEGIS_ACTIVATION_HEIGHT.get() {
64        debug_assert_eq!(
65            *existing, block_height,
66            "Aegis activation height changed after initialization"
67        );
68        return;
69    }
70
71    let _ = AEGIS_ACTIVATION_HEIGHT.set(block_height);
72}
73
74/// Initializes the Boreas activation height once for this process.
75pub fn set_boreas_activation_height(block_height: u64) {
76    if let Some(existing) = BOREAS_ACTIVATION_HEIGHT.get() {
77        debug_assert_eq!(
78            *existing, block_height,
79            "Boreas activation height changed after initialization"
80        );
81        return;
82    }
83
84    let _ = BOREAS_ACTIVATION_HEIGHT.set(block_height);
85}
86
87/// Returns the configured Aegis activation height, or `NEVER` if unset.
88fn aegis_activation_height() -> u64 {
89    *AEGIS_ACTIVATION_HEIGHT.get().unwrap_or(&NEVER)
90}
91
92/// Returns the configured Boreas activation height, or `NEVER` if unset.
93fn boreas_activation_height() -> u64 {
94    *BOREAS_ACTIVATION_HEIGHT.get().unwrap_or(&NEVER)
95}
96
97/// Returns the active hardfork for `block_height`.
98pub fn hard_fork_at(block_height: u64) -> HardFork {
99    if block_height >= boreas_activation_height() {
100        HardFork::Boreas
101    } else if block_height >= aegis_activation_height() {
102        HardFork::Aegis
103    } else {
104        HardFork::PreFork
105    }
106}
107
108/// Returns the ledger transaction format for `block_height`.
109pub fn ledger_tx_format_at(block_height: u64) -> TransactionFormat {
110    hard_fork_at(block_height).ledger_tx_format()
111}
112
113/// Returns the ingress transaction format for the target block height.
114pub fn ingress_tx_format_at(block_height: u64) -> TransactionFormat {
115    hard_fork_at(block_height).ingress_tx_format()
116}
117
118/// Returns the active hardfork for `block_height`, given an activation height.
119#[cfg(test)]
120pub(crate) const fn hard_fork_at_with_activation(
121    block_height: u64,
122    aegis_activation_height: u64,
123    boreas_activation_height: u64,
124) -> HardFork {
125    if block_height >= boreas_activation_height {
126        HardFork::Boreas
127    } else if block_height >= aegis_activation_height {
128        HardFork::Aegis
129    } else {
130        HardFork::PreFork
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn hard_fork_and_format_policy_matrix() {
140        let cases = [
141            (
142                99,
143                100,
144                NEVER,
145                HardFork::PreFork,
146                TransactionFormat::PreAegis,
147                TransactionFormat::Aegis,
148            ),
149            (
150                100,
151                100,
152                NEVER,
153                HardFork::Aegis,
154                TransactionFormat::Aegis,
155                TransactionFormat::Aegis,
156            ),
157            (
158                101,
159                NEVER,
160                NEVER,
161                HardFork::PreFork,
162                TransactionFormat::PreAegis,
163                TransactionFormat::Aegis,
164            ),
165            (
166                199,
167                100,
168                200,
169                HardFork::Aegis,
170                TransactionFormat::Aegis,
171                TransactionFormat::Aegis,
172            ),
173            (
174                200,
175                100,
176                200,
177                HardFork::Boreas,
178                TransactionFormat::Boreas,
179                TransactionFormat::Boreas,
180            ),
181        ];
182
183        for (
184            height,
185            aegis_activation_height,
186            boreas_activation_height,
187            expected_hard_fork,
188            expected_ledger_format,
189            expected_ingress_format,
190        ) in cases
191        {
192            let hard_fork = hard_fork_at_with_activation(
193                height,
194                aegis_activation_height,
195                boreas_activation_height,
196            );
197
198            assert_eq!(hard_fork, expected_hard_fork);
199            assert_eq!(hard_fork.ledger_tx_format(), expected_ledger_format);
200            assert_eq!(hard_fork.ingress_tx_format(), expected_ingress_format);
201        }
202    }
203}