kona_engine/state/
core.rs1use crate::Metrics;
4use alloy_rpc_types_engine::ForkchoiceState;
5use kona_protocol::L2BlockInfo;
6use serde::{Deserialize, Serialize};
7
8#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
26pub struct EngineSyncState {
27 unsafe_head: L2BlockInfo,
29 cross_unsafe_head: L2BlockInfo,
31 local_safe_head: L2BlockInfo,
33 safe_head: L2BlockInfo,
35 finalized_head: L2BlockInfo,
37}
38
39impl EngineSyncState {
40 pub const fn unsafe_head(&self) -> L2BlockInfo {
42 self.unsafe_head
43 }
44
45 pub const fn cross_unsafe_head(&self) -> L2BlockInfo {
47 self.cross_unsafe_head
48 }
49
50 pub const fn local_safe_head(&self) -> L2BlockInfo {
52 self.local_safe_head
53 }
54
55 pub const fn safe_head(&self) -> L2BlockInfo {
57 self.safe_head
58 }
59
60 pub const fn finalized_head(&self) -> L2BlockInfo {
62 self.finalized_head
63 }
64
65 pub const fn create_forkchoice_state(&self) -> ForkchoiceState {
73 ForkchoiceState {
74 head_block_hash: self.unsafe_head.hash(),
75 safe_block_hash: self.safe_head.hash(),
76 finalized_block_hash: self.finalized_head.hash(),
77 }
78 }
79
80 pub fn apply_update(self, sync_state_update: EngineSyncStateUpdate) -> Self {
83 if let Some(unsafe_head) = sync_state_update.unsafe_head {
84 Self::update_block_label_metric(
85 Metrics::UNSAFE_BLOCK_LABEL,
86 unsafe_head.block_info.number,
87 );
88 }
89 if let Some(cross_unsafe_head) = sync_state_update.cross_unsafe_head {
90 Self::update_block_label_metric(
91 Metrics::CROSS_UNSAFE_BLOCK_LABEL,
92 cross_unsafe_head.block_info.number,
93 );
94 }
95 if let Some(local_safe_head) = sync_state_update.local_safe_head {
96 Self::update_block_label_metric(
97 Metrics::LOCAL_SAFE_BLOCK_LABEL,
98 local_safe_head.block_info.number,
99 );
100 }
101 if let Some(safe_head) = sync_state_update.safe_head {
102 Self::update_block_label_metric(Metrics::SAFE_BLOCK_LABEL, safe_head.block_info.number);
103 }
104 if let Some(finalized_head) = sync_state_update.finalized_head {
105 Self::update_block_label_metric(
106 Metrics::FINALIZED_BLOCK_LABEL,
107 finalized_head.block_info.number,
108 );
109 }
110
111 Self {
112 unsafe_head: sync_state_update.unsafe_head.unwrap_or(self.unsafe_head),
113 cross_unsafe_head: sync_state_update
114 .cross_unsafe_head
115 .unwrap_or(self.cross_unsafe_head),
116 local_safe_head: sync_state_update.local_safe_head.unwrap_or(self.local_safe_head),
117 safe_head: sync_state_update.safe_head.unwrap_or(self.safe_head),
118 finalized_head: sync_state_update.finalized_head.unwrap_or(self.finalized_head),
119 }
120 }
121
122 #[inline]
124 fn update_block_label_metric(label: &'static str, number: u64) {
125 kona_macros::set!(gauge, Metrics::BLOCK_LABELS, "label", label, number as f64);
126 }
127}
128
129#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
131pub struct EngineSyncStateUpdate {
132 pub unsafe_head: Option<L2BlockInfo>,
134 pub cross_unsafe_head: Option<L2BlockInfo>,
136 pub local_safe_head: Option<L2BlockInfo>,
139 pub safe_head: Option<L2BlockInfo>,
141 pub finalized_head: Option<L2BlockInfo>,
144}
145
146#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
148pub struct EngineState {
149 pub sync_state: EngineSyncState,
151
152 pub el_sync_finished: bool,
154
155 pub need_fcu_call_backup_unsafe_reorg: bool,
161}
162
163impl EngineState {
164 pub fn needs_consolidation(&self) -> bool {
172 self.sync_state.safe_head() != self.sync_state.unsafe_head()
173 }
174}
175
176#[cfg(test)]
177mod test {
178 use super::*;
179 use crate::Metrics;
180 use kona_protocol::BlockInfo;
181 use metrics_exporter_prometheus::PrometheusBuilder;
182 use rstest::rstest;
183
184 impl EngineState {
185 pub fn set_unsafe_head(&mut self, unsafe_head: L2BlockInfo) {
187 self.sync_state.apply_update(EngineSyncStateUpdate {
188 unsafe_head: Some(unsafe_head),
189 ..Default::default()
190 });
191 }
192
193 pub fn set_cross_unsafe_head(&mut self, cross_unsafe_head: L2BlockInfo) {
195 self.sync_state.apply_update(EngineSyncStateUpdate {
196 cross_unsafe_head: Some(cross_unsafe_head),
197 ..Default::default()
198 });
199 }
200
201 pub fn set_local_safe_head(&mut self, local_safe_head: L2BlockInfo) {
203 self.sync_state.apply_update(EngineSyncStateUpdate {
204 local_safe_head: Some(local_safe_head),
205 ..Default::default()
206 });
207 }
208
209 pub fn set_safe_head(&mut self, safe_head: L2BlockInfo) {
211 self.sync_state.apply_update(EngineSyncStateUpdate {
212 safe_head: Some(safe_head),
213 ..Default::default()
214 });
215 }
216
217 pub fn set_finalized_head(&mut self, finalized_head: L2BlockInfo) {
219 self.sync_state.apply_update(EngineSyncStateUpdate {
220 finalized_head: Some(finalized_head),
221 ..Default::default()
222 });
223 }
224 }
225
226 #[rstest]
227 #[case::set_unsafe(EngineState::set_unsafe_head, Metrics::UNSAFE_BLOCK_LABEL, 1)]
228 #[case::set_cross_unsafe(
229 EngineState::set_cross_unsafe_head,
230 Metrics::CROSS_UNSAFE_BLOCK_LABEL,
231 2
232 )]
233 #[case::set_local_safe(EngineState::set_local_safe_head, Metrics::LOCAL_SAFE_BLOCK_LABEL, 3)]
234 #[case::set_safe_head(EngineState::set_safe_head, Metrics::SAFE_BLOCK_LABEL, 4)]
235 #[case::set_finalized_head(EngineState::set_finalized_head, Metrics::FINALIZED_BLOCK_LABEL, 5)]
236 #[cfg(feature = "metrics")]
237 fn test_chain_label_metrics(
238 #[case] set_fn: impl Fn(&mut EngineState, L2BlockInfo),
239 #[case] label_name: &str,
240 #[case] number: u64,
241 ) {
242 let handle = PrometheusBuilder::new().install_recorder().unwrap();
243 crate::Metrics::init();
244
245 let mut state = EngineState::default();
246 set_fn(
247 &mut state,
248 L2BlockInfo {
249 block_info: BlockInfo { number, ..Default::default() },
250 ..Default::default()
251 },
252 );
253
254 assert!(handle.render().contains(
255 format!("kona_node_block_labels{{label=\"{label_name}\"}} {number}").as_str()
256 ));
257 }
258}