reth_ethereum_forks/display.rs
1use crate::{hardforks::Hardforks, ForkCondition};
2use alloc::{
3 format,
4 string::{String, ToString},
5 vec::Vec,
6};
7
8/// A container to pretty-print a hardfork.
9///
10/// The fork is formatted depending on its fork condition:
11///
12/// - Block and timestamp based forks are formatted in the same manner (`{name} <({eip})>
13/// @{condition}`)
14/// - TTD based forks are formatted separately as `{name} <({eip})> @{ttd} (network is <not> known
15/// to be merged)`
16///
17/// An optional EIP can be attached to the fork to display as well. This should generally be in the
18/// form of just `EIP-x`, e.g. `EIP-1559`.
19#[derive(Debug)]
20struct DisplayFork {
21 /// The name of the hardfork (e.g. Frontier)
22 name: String,
23 /// The fork condition
24 activated_at: ForkCondition,
25 /// An optional EIP (e.g. `EIP-1559`).
26 eip: Option<String>,
27}
28
29impl core::fmt::Display for DisplayFork {
30 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
31 let name_with_eip = if let Some(eip) = &self.eip {
32 format!("{} ({})", self.name, eip)
33 } else {
34 self.name.clone()
35 };
36
37 match self.activated_at {
38 ForkCondition::Block(at) | ForkCondition::Timestamp(at) => {
39 write!(f, "{name_with_eip:32} @{at}")?;
40 }
41 ForkCondition::TTD { fork_block, total_difficulty } => {
42 write!(
43 f,
44 "{:32} @{} ({})",
45 name_with_eip,
46 total_difficulty,
47 if fork_block.is_some() {
48 "network is known to be merged"
49 } else {
50 "network is not known to be merged"
51 }
52 )?;
53 }
54 ForkCondition::Never => unreachable!(),
55 }
56
57 Ok(())
58 }
59}
60
61// # Examples
62//
63// ```
64// # use reth_chainspec::MAINNET;
65// println!("{}", MAINNET.display_hardforks());
66// ```
67//
68/// A container for pretty-printing a list of hardforks.
69///
70/// An example of the output:
71///
72/// ```text
73/// Pre-merge hard forks (block based):
74// - Frontier @0
75// - Homestead @1150000
76// - Dao @1920000
77// - Tangerine @2463000
78// - SpuriousDragon @2675000
79// - Byzantium @4370000
80// - Constantinople @7280000
81// - Petersburg @7280000
82// - Istanbul @9069000
83// - MuirGlacier @9200000
84// - Berlin @12244000
85// - London @12965000
86// - ArrowGlacier @13773000
87// - GrayGlacier @15050000
88// Merge hard forks:
89// - Paris @58750000000000000000000 (network is known to be merged)
90// Post-merge hard forks (timestamp based):
91// - Shanghai @1681338455
92// - Cancun @1710338135"
93/// ```
94#[derive(Debug)]
95pub struct DisplayHardforks {
96 /// A list of pre-merge (block based) hardforks
97 pre_merge: Vec<DisplayFork>,
98 /// A list of merge (TTD based) hardforks
99 with_merge: Vec<DisplayFork>,
100 /// A list of post-merge (timestamp based) hardforks
101 post_merge: Vec<DisplayFork>,
102}
103
104impl core::fmt::Display for DisplayHardforks {
105 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
106 fn format(
107 header: &str,
108 forks: &[DisplayFork],
109 next_is_empty: bool,
110 f: &mut core::fmt::Formatter<'_>,
111 ) -> core::fmt::Result {
112 writeln!(f, "{header}:")?;
113 let mut iter = forks.iter().peekable();
114 while let Some(fork) = iter.next() {
115 write!(f, "- {fork}")?;
116 if !next_is_empty || iter.peek().is_some() {
117 writeln!(f)?;
118 }
119 }
120 Ok(())
121 }
122
123 format(
124 "Pre-merge hard forks (block based)",
125 &self.pre_merge,
126 self.with_merge.is_empty(),
127 f,
128 )?;
129
130 if !self.with_merge.is_empty() {
131 format("Merge hard forks", &self.with_merge, self.post_merge.is_empty(), f)?;
132 }
133
134 if !self.post_merge.is_empty() {
135 format("Post-merge hard forks (timestamp based)", &self.post_merge, true, f)?;
136 }
137
138 Ok(())
139 }
140}
141
142impl DisplayHardforks {
143 /// Creates a new [`DisplayHardforks`] from an iterator of hardforks.
144 pub fn new<H: Hardforks>(hardforks: &H, known_paris_block: Option<u64>) -> Self {
145 let mut pre_merge = Vec::new();
146 let mut with_merge = Vec::new();
147 let mut post_merge = Vec::new();
148
149 for (fork, condition) in hardforks.forks_iter() {
150 let mut display_fork =
151 DisplayFork { name: fork.name().to_string(), activated_at: condition, eip: None };
152
153 match condition {
154 ForkCondition::Block(_) => {
155 pre_merge.push(display_fork);
156 }
157 ForkCondition::TTD { total_difficulty, .. } => {
158 display_fork.activated_at =
159 ForkCondition::TTD { fork_block: known_paris_block, total_difficulty };
160 with_merge.push(display_fork);
161 }
162 ForkCondition::Timestamp(_) => {
163 post_merge.push(display_fork);
164 }
165 ForkCondition::Never => continue,
166 }
167 }
168
169 Self { pre_merge, with_merge, post_merge }
170 }
171}