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