1use std::sync::Arc;
2
3use reth_chain_state::CanonStateNotification;
4use reth_execution_types::Chain;
5use reth_primitives_traits::NodePrimitives;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub enum ExExNotification<N: NodePrimitives = reth_chain_state::EthPrimitives> {
11 ChainCommitted {
13 new: Arc<Chain<N>>,
15 },
16 ChainReorged {
18 old: Arc<Chain<N>>,
20 new: Arc<Chain<N>>,
22 },
23 ChainReverted {
25 old: Arc<Chain<N>>,
27 },
28}
29
30impl<N: NodePrimitives> ExExNotification<N> {
31 pub fn committed_chain(&self) -> Option<Arc<Chain<N>>> {
34 match self {
35 Self::ChainCommitted { new } | Self::ChainReorged { old: _, new } => Some(new.clone()),
36 Self::ChainReverted { .. } => None,
37 }
38 }
39
40 pub fn reverted_chain(&self) -> Option<Arc<Chain<N>>> {
43 match self {
44 Self::ChainReorged { old, new: _ } | Self::ChainReverted { old } => Some(old.clone()),
45 Self::ChainCommitted { .. } => None,
46 }
47 }
48
49 pub fn into_inverted(self) -> Self {
56 match self {
57 Self::ChainCommitted { new } => Self::ChainReverted { old: new },
58 Self::ChainReverted { old } => Self::ChainCommitted { new: old },
59 Self::ChainReorged { old, new } => Self::ChainReorged { old: new, new: old },
60 }
61 }
62}
63
64impl<P: NodePrimitives> From<CanonStateNotification<P>> for ExExNotification<P> {
65 fn from(notification: CanonStateNotification<P>) -> Self {
66 match notification {
67 CanonStateNotification::Commit { new } => Self::ChainCommitted { new },
68 CanonStateNotification::Reorg { old, new } => Self::ChainReorged { old, new },
69 }
70 }
71}
72
73#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
75pub(super) mod serde_bincode_compat {
76 use reth_execution_types::serde_bincode_compat::Chain;
77 use reth_primitives_traits::NodePrimitives;
78 use serde::{Deserialize, Deserializer, Serialize, Serializer};
79 use serde_with::{DeserializeAs, SerializeAs};
80 use std::sync::Arc;
81
82 #[derive(Debug, Serialize, Deserialize)]
102 #[serde(bound = "")]
103 #[expect(clippy::large_enum_variant)]
104 pub enum ExExNotification<'a, N>
105 where
106 N: NodePrimitives,
107 {
108 ChainCommitted {
110 new: Chain<'a, N>,
112 },
113 ChainReorged {
115 old: Chain<'a, N>,
117 new: Chain<'a, N>,
119 },
120 ChainReverted {
122 old: Chain<'a, N>,
124 },
125 }
126
127 impl<'a, N> From<&'a super::ExExNotification<N>> for ExExNotification<'a, N>
128 where
129 N: NodePrimitives,
130 {
131 fn from(value: &'a super::ExExNotification<N>) -> Self {
132 match value {
133 super::ExExNotification::ChainCommitted { new } => {
134 ExExNotification::ChainCommitted { new: Chain::from(new.as_ref()) }
135 }
136 super::ExExNotification::ChainReorged { old, new } => {
137 ExExNotification::ChainReorged {
138 old: Chain::from(old.as_ref()),
139 new: Chain::from(new.as_ref()),
140 }
141 }
142 super::ExExNotification::ChainReverted { old } => {
143 ExExNotification::ChainReverted { old: Chain::from(old.as_ref()) }
144 }
145 }
146 }
147 }
148
149 impl<'a, N> From<ExExNotification<'a, N>> for super::ExExNotification<N>
150 where
151 N: NodePrimitives,
152 {
153 fn from(value: ExExNotification<'a, N>) -> Self {
154 match value {
155 ExExNotification::ChainCommitted { new } => {
156 Self::ChainCommitted { new: Arc::new(new.into()) }
157 }
158 ExExNotification::ChainReorged { old, new } => {
159 Self::ChainReorged { old: Arc::new(old.into()), new: Arc::new(new.into()) }
160 }
161 ExExNotification::ChainReverted { old } => {
162 Self::ChainReverted { old: Arc::new(old.into()) }
163 }
164 }
165 }
166 }
167
168 impl<N> SerializeAs<super::ExExNotification<N>> for ExExNotification<'_, N>
169 where
170 N: NodePrimitives,
171 {
172 fn serialize_as<S>(
173 source: &super::ExExNotification<N>,
174 serializer: S,
175 ) -> Result<S::Ok, S::Error>
176 where
177 S: Serializer,
178 {
179 ExExNotification::from(source).serialize(serializer)
180 }
181 }
182
183 impl<'de, N> DeserializeAs<'de, super::ExExNotification<N>> for ExExNotification<'de, N>
184 where
185 N: NodePrimitives,
186 {
187 fn deserialize_as<D>(deserializer: D) -> Result<super::ExExNotification<N>, D::Error>
188 where
189 D: Deserializer<'de>,
190 {
191 ExExNotification::deserialize(deserializer).map(Into::into)
192 }
193 }
194
195 #[cfg(test)]
196 mod tests {
197 use super::super::{serde_bincode_compat, ExExNotification};
198 use arbitrary::Arbitrary;
199 use rand::Rng;
200 use reth_execution_types::Chain;
201 use reth_primitives_traits::RecoveredBlock;
202 use serde::{Deserialize, Serialize};
203 use serde_with::serde_as;
204 use std::sync::Arc;
205
206 #[test]
207 fn test_exex_notification_bincode_roundtrip() {
208 #[serde_as]
209 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
210 struct Data {
211 #[serde_as(
212 as = "serde_bincode_compat::ExExNotification<'_, reth_ethereum_primitives::EthPrimitives>"
213 )]
214 notification: ExExNotification,
215 }
216
217 let mut bytes = [0u8; 1024];
218 rand::rng().fill(bytes.as_mut_slice());
219 let data = Data {
220 notification: ExExNotification::ChainReorged {
221 old: Arc::new(Chain::new(
222 vec![RecoveredBlock::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
223 .unwrap()],
224 Default::default(),
225 None,
226 )),
227 new: Arc::new(Chain::new(
228 vec![RecoveredBlock::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
229 .unwrap()],
230 Default::default(),
231 None,
232 )),
233 },
234 };
235
236 let encoded = bincode::serialize(&data).unwrap();
237 let decoded: Data = bincode::deserialize(&encoded).unwrap();
238 assert_eq!(decoded, data);
239 }
240 }
241}