reth_exex_types/
notification.rs

1use std::sync::Arc;
2
3use reth_chain_state::CanonStateNotification;
4use reth_execution_types::Chain;
5use reth_primitives_traits::NodePrimitives;
6
7/// Notifications sent to an `ExEx`.
8#[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    /// Chain got committed without a reorg, and only the new chain is returned.
12    ChainCommitted {
13        /// The new chain after commit.
14        new: Arc<Chain<N>>,
15    },
16    /// Chain got reorged, and both the old and the new chains are returned.
17    ChainReorged {
18        /// The old chain before reorg.
19        old: Arc<Chain<N>>,
20        /// The new chain after reorg.
21        new: Arc<Chain<N>>,
22    },
23    /// Chain got reverted, and only the old chain is returned.
24    ChainReverted {
25        /// The old chain before reversion.
26        old: Arc<Chain<N>>,
27    },
28}
29
30impl<N: NodePrimitives> ExExNotification<N> {
31    /// Returns the committed chain from the [`Self::ChainCommitted`] and [`Self::ChainReorged`]
32    /// variants, if any.
33    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    /// Returns the reverted chain from the [`Self::ChainReorged`] and [`Self::ChainReverted`]
41    /// variants, if any.
42    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    /// Converts the notification into a notification that is the inverse of the original one.
50    ///
51    /// - For [`Self::ChainCommitted`], it's [`Self::ChainReverted`].
52    /// - For [`Self::ChainReverted`], it's [`Self::ChainCommitted`].
53    /// - For [`Self::ChainReorged`], it's [`Self::ChainReorged`] with the new chain as the old
54    ///   chain and the old chain as the new chain.
55    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/// Bincode-compatible [`ExExNotification`] serde implementation.
74#[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    /// Bincode-compatible [`super::ExExNotification`] serde implementation.
83    ///
84    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
85    /// ```rust
86    /// use reth_exex_types::{serde_bincode_compat, ExExNotification};
87    /// use reth_primitives_traits::NodePrimitives;
88    /// use serde::{Deserialize, Serialize};
89    /// use serde_with::serde_as;
90    ///
91    /// #[serde_as]
92    /// #[derive(Serialize, Deserialize)]
93    /// struct Data<N: NodePrimitives> {
94    ///     #[serde_as(as = "serde_bincode_compat::ExExNotification<'_, N>")]
95    ///     notification: ExExNotification<N>,
96    /// }
97    /// ```
98    #[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}