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)]
99 #[expect(missing_docs)]
100 #[serde(bound = "")]
101 #[expect(clippy::large_enum_variant)]
102 pub enum ExExNotification<'a, N>
103 where
104 N: NodePrimitives,
105 {
106 ChainCommitted { new: Chain<'a, N> },
107 ChainReorged { old: Chain<'a, N>, new: Chain<'a, N> },
108 ChainReverted { old: Chain<'a, N> },
109 }
110
111 impl<'a, N> From<&'a super::ExExNotification<N>> for ExExNotification<'a, N>
112 where
113 N: NodePrimitives,
114 {
115 fn from(value: &'a super::ExExNotification<N>) -> Self {
116 match value {
117 super::ExExNotification::ChainCommitted { new } => {
118 ExExNotification::ChainCommitted { new: Chain::from(new.as_ref()) }
119 }
120 super::ExExNotification::ChainReorged { old, new } => {
121 ExExNotification::ChainReorged {
122 old: Chain::from(old.as_ref()),
123 new: Chain::from(new.as_ref()),
124 }
125 }
126 super::ExExNotification::ChainReverted { old } => {
127 ExExNotification::ChainReverted { old: Chain::from(old.as_ref()) }
128 }
129 }
130 }
131 }
132
133 impl<'a, N> From<ExExNotification<'a, N>> for super::ExExNotification<N>
134 where
135 N: NodePrimitives,
136 {
137 fn from(value: ExExNotification<'a, N>) -> Self {
138 match value {
139 ExExNotification::ChainCommitted { new } => {
140 Self::ChainCommitted { new: Arc::new(new.into()) }
141 }
142 ExExNotification::ChainReorged { old, new } => {
143 Self::ChainReorged { old: Arc::new(old.into()), new: Arc::new(new.into()) }
144 }
145 ExExNotification::ChainReverted { old } => {
146 Self::ChainReverted { old: Arc::new(old.into()) }
147 }
148 }
149 }
150 }
151
152 impl<N> SerializeAs<super::ExExNotification<N>> for ExExNotification<'_, N>
153 where
154 N: NodePrimitives,
155 {
156 fn serialize_as<S>(
157 source: &super::ExExNotification<N>,
158 serializer: S,
159 ) -> Result<S::Ok, S::Error>
160 where
161 S: Serializer,
162 {
163 ExExNotification::from(source).serialize(serializer)
164 }
165 }
166
167 impl<'de, N> DeserializeAs<'de, super::ExExNotification<N>> for ExExNotification<'de, N>
168 where
169 N: NodePrimitives,
170 {
171 fn deserialize_as<D>(deserializer: D) -> Result<super::ExExNotification<N>, D::Error>
172 where
173 D: Deserializer<'de>,
174 {
175 ExExNotification::deserialize(deserializer).map(Into::into)
176 }
177 }
178
179 #[cfg(test)]
180 mod tests {
181 use super::super::{serde_bincode_compat, ExExNotification};
182 use arbitrary::Arbitrary;
183 use rand::Rng;
184 use reth_execution_types::Chain;
185 use reth_primitives_traits::RecoveredBlock;
186 use serde::{Deserialize, Serialize};
187 use serde_with::serde_as;
188 use std::sync::Arc;
189
190 #[test]
191 fn test_exex_notification_bincode_roundtrip() {
192 #[serde_as]
193 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
194 struct Data {
195 #[serde_as(
196 as = "serde_bincode_compat::ExExNotification<'_, reth_ethereum_primitives::EthPrimitives>"
197 )]
198 notification: ExExNotification,
199 }
200
201 let mut bytes = [0u8; 1024];
202 rand::rng().fill(bytes.as_mut_slice());
203 let data = Data {
204 notification: ExExNotification::ChainReorged {
205 old: Arc::new(Chain::new(
206 vec![RecoveredBlock::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
207 .unwrap()],
208 Default::default(),
209 None,
210 )),
211 new: Arc::new(Chain::new(
212 vec![RecoveredBlock::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
213 .unwrap()],
214 Default::default(),
215 None,
216 )),
217 },
218 };
219
220 let encoded = bincode::serialize(&data).unwrap();
221 let decoded: Data = bincode::deserialize(&encoded).unwrap();
222 assert_eq!(decoded, data);
223 }
224 }
225}