alloy_hardforks/
forkcondition.rs1use alloy_primitives::{BlockNumber, U256};
2
3#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub enum ForkCondition {
7 Block(BlockNumber),
9 TTD {
11 activation_block_number: BlockNumber,
17 fork_block: Option<BlockNumber>,
24 total_difficulty: U256,
26 },
27 Timestamp(u64),
29 #[default]
31 Never,
32}
33
34impl ForkCondition {
35 pub const ZERO_BLOCK: Self = Self::Block(0);
37
38 pub const ZERO_TIMESTAMP: Self = Self::Timestamp(0);
40
41 pub const fn is_timestamp(&self) -> bool {
43 matches!(self, Self::Timestamp(_))
44 }
45
46 pub const fn is_ttd(&self) -> bool {
48 matches!(self, Self::TTD { .. })
49 }
50
51 pub const fn is_block(&self) -> bool {
53 matches!(self, Self::Block(_))
54 }
55
56 pub const fn active_at_block(&self, current_block: BlockNumber) -> bool {
64 matches!(self, Self::Block(block)
65 | Self::TTD { activation_block_number: block, .. } if current_block >= *block)
66 }
67
68 pub const fn transitions_at_block(&self, current_block: BlockNumber) -> bool {
72 matches!(self, Self::Block(block) if current_block == *block)
73 }
74
75 pub fn active_at_ttd(&self, ttd: U256, difficulty: U256) -> bool {
85 matches!(self, Self::TTD { total_difficulty, .. }
86 if ttd.saturating_sub(difficulty) >= *total_difficulty)
87 }
88
89 pub const fn active_at_timestamp(&self, timestamp: u64) -> bool {
93 matches!(self, Self::Timestamp(time) if timestamp >= *time)
94 }
95
96 pub const fn transitions_at_timestamp(&self, timestamp: u64, parent_timestamp: u64) -> bool {
100 matches!(self, Self::Timestamp(time) if timestamp >= *time && parent_timestamp < *time)
101 }
102
103 pub const fn active_at_timestamp_or_number(&self, timestamp: u64, block_number: u64) -> bool {
105 self.active_at_timestamp(timestamp) || self.active_at_block(block_number)
106 }
107
108 pub const fn ttd(&self) -> Option<U256> {
112 match self {
113 Self::TTD { total_difficulty, .. } => Some(*total_difficulty),
114 _ => None,
115 }
116 }
117
118 pub const fn block_number(&self) -> Option<u64> {
121 match self {
122 Self::Block(number) => Some(*number),
123 Self::TTD { activation_block_number, .. } => Some(*activation_block_number),
124 _ => None,
125 }
126 }
127
128 pub const fn as_timestamp(&self) -> Option<u64> {
130 match self {
131 Self::Timestamp(timestamp) => Some(*timestamp),
132 _ => None,
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use alloy_primitives::U256;
141
142 #[test]
143 fn test_active_at_block() {
144 let fork_condition = ForkCondition::Block(10);
146 assert!(fork_condition.active_at_block(10), "The condition should be active at block 10");
147
148 assert!(
150 !fork_condition.active_at_block(9),
151 "The condition should not be active at block 9"
152 );
153
154 let fork_condition = ForkCondition::TTD {
156 activation_block_number: 10,
157 fork_block: Some(10),
158 total_difficulty: U256::from(1000),
159 };
160 assert!(
161 fork_condition.active_at_block(10),
162 "The TTD condition should be active at block 10"
163 );
164 }
165
166 #[test]
167 fn test_transitions_at_block() {
168 let fork_condition = ForkCondition::Block(10);
170 assert!(
171 fork_condition.transitions_at_block(10),
172 "The condition should transition at block 10"
173 );
174
175 assert!(
177 !fork_condition.transitions_at_block(9),
178 "The condition should not transition at a different block number"
179 );
180 assert!(
181 !fork_condition.transitions_at_block(11),
182 "The condition should not transition at a different block number"
183 );
184 }
185
186 #[test]
187 fn test_active_at_ttd() {
188 let fork_condition = ForkCondition::TTD {
190 activation_block_number: 10,
191 fork_block: Some(10),
192 total_difficulty: U256::from(1000),
193 };
194 assert!(
195 fork_condition.active_at_ttd(U256::from(1000000), U256::from(100)),
196 "The TTD condition should be active when the total difficulty matches"
197 );
198
199 assert!(
201 !fork_condition.active_at_ttd(U256::from(900), U256::from(100)),
202 "The TTD condition should not be active when the total difficulty is lower"
203 );
204
205 assert!(
207 !fork_condition.active_at_ttd(U256::from(900), U256::from(1000)),
208 "The TTD condition should not be active when the subtraction saturates"
209 );
210 }
211
212 #[test]
213 fn test_active_at_timestamp() {
214 let fork_condition = ForkCondition::Timestamp(12345);
216 assert!(
217 fork_condition.active_at_timestamp(12345),
218 "The condition should be active at timestamp 12345"
219 );
220
221 assert!(
223 !fork_condition.active_at_timestamp(12344),
224 "The condition should not be active at an earlier timestamp"
225 );
226 }
227
228 #[test]
229 fn test_transitions_at_timestamp() {
230 let fork_condition = ForkCondition::Timestamp(12345);
232 assert!(
233 fork_condition.transitions_at_timestamp(12345, 12344),
234 "The condition should transition at timestamp 12345"
235 );
236
237 assert!(
239 !fork_condition.transitions_at_timestamp(12345, 12345),
240 "The condition should not transition if the parent timestamp is already 12345"
241 );
242 assert!(
244 !fork_condition.transitions_at_timestamp(123, 122),
245 "The condition should not transition if the parent timestamp is earlier"
246 );
247 }
248}