reth_primitives_traits/
serde_bincode_compat.rs

1use core::fmt::Debug;
2use serde::{de::DeserializeOwned, Serialize};
3
4pub use super::{
5    block::{serde_bincode_compat as block, serde_bincode_compat::*},
6    header::{serde_bincode_compat as header, serde_bincode_compat::*},
7};
8pub use block_bincode::{Block, BlockBody};
9
10/// Trait for types that can be serialized and deserialized using bincode.
11///
12/// The recommended way to add bincode compatible serialization is via the
13/// [`serde_with`] crate and the `serde_as` macro that. See for reference [`header`].
14pub trait SerdeBincodeCompat: Sized + 'static {
15    /// Serde representation of the type for bincode serialization.
16    ///
17    /// This type defines the bincode compatible serde format for the type.
18    type BincodeRepr<'a>: Debug + Serialize + DeserializeOwned;
19
20    /// Convert this type into its bincode representation
21    fn as_repr(&self) -> Self::BincodeRepr<'_>;
22
23    /// Convert from the bincode representation
24    fn from_repr(repr: Self::BincodeRepr<'_>) -> Self;
25}
26
27impl SerdeBincodeCompat for alloy_consensus::Header {
28    type BincodeRepr<'a> = alloy_consensus::serde_bincode_compat::Header<'a>;
29
30    fn as_repr(&self) -> Self::BincodeRepr<'_> {
31        self.into()
32    }
33
34    fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
35        repr.into()
36    }
37}
38
39/// Type alias for the [`SerdeBincodeCompat::BincodeRepr`] associated type.
40pub type BincodeReprFor<'a, T> = <T as SerdeBincodeCompat>::BincodeRepr<'a>;
41
42mod block_bincode {
43    use crate::serde_bincode_compat::SerdeBincodeCompat;
44    use alloc::{borrow::Cow, vec::Vec};
45    use alloy_consensus::TxEip4844;
46    use alloy_eips::eip4895::Withdrawals;
47    use serde::{Deserialize, Deserializer, Serialize, Serializer};
48    use serde_with::{DeserializeAs, SerializeAs};
49
50    /// Bincode-compatible [`alloy_consensus::Block`] serde implementation.
51    ///
52    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
53    /// ```rust
54    /// use alloy_consensus::Block;
55    /// use reth_primitives_traits::serde_bincode_compat::{self, SerdeBincodeCompat};
56    /// use serde::{Deserialize, Serialize};
57    /// use serde_with::serde_as;
58    ///
59    /// #[serde_as]
60    /// #[derive(Serialize, Deserialize)]
61    /// struct Data<T: SerdeBincodeCompat, H: SerdeBincodeCompat> {
62    ///     #[serde_as(as = "serde_bincode_compat::Block<'_, T, H>")]
63    ///     body: Block<T, H>,
64    /// }
65    /// ```
66    #[derive(derive_more::Debug, Serialize, Deserialize)]
67    #[debug(bound())]
68    pub struct Block<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> {
69        header: H::BincodeRepr<'a>,
70        #[serde(bound = "BlockBody<'a, T, H>: Serialize + serde::de::DeserializeOwned")]
71        body: BlockBody<'a, T, H>,
72    }
73
74    impl<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> From<&'a alloy_consensus::Block<T, H>>
75        for Block<'a, T, H>
76    {
77        fn from(value: &'a alloy_consensus::Block<T, H>) -> Self {
78            Self { header: value.header.as_repr(), body: (&value.body).into() }
79        }
80    }
81
82    impl<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> From<Block<'a, T, H>>
83        for alloy_consensus::Block<T, H>
84    {
85        fn from(value: Block<'a, T, H>) -> Self {
86            Self { header: SerdeBincodeCompat::from_repr(value.header), body: value.body.into() }
87        }
88    }
89
90    impl<T: SerdeBincodeCompat, H: SerdeBincodeCompat> SerializeAs<alloy_consensus::Block<T, H>>
91        for Block<'_, T, H>
92    {
93        fn serialize_as<S>(
94            source: &alloy_consensus::Block<T, H>,
95            serializer: S,
96        ) -> Result<S::Ok, S::Error>
97        where
98            S: Serializer,
99        {
100            Block::from(source).serialize(serializer)
101        }
102    }
103
104    impl<'de, T: SerdeBincodeCompat, H: SerdeBincodeCompat>
105        DeserializeAs<'de, alloy_consensus::Block<T, H>> for Block<'de, T, H>
106    {
107        fn deserialize_as<D>(deserializer: D) -> Result<alloy_consensus::Block<T, H>, D::Error>
108        where
109            D: Deserializer<'de>,
110        {
111            Block::deserialize(deserializer).map(Into::into)
112        }
113    }
114
115    impl<T: SerdeBincodeCompat, H: SerdeBincodeCompat> SerdeBincodeCompat
116        for alloy_consensus::Block<T, H>
117    {
118        type BincodeRepr<'a> = Block<'a, T, H>;
119
120        fn as_repr(&self) -> Self::BincodeRepr<'_> {
121            self.into()
122        }
123
124        fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
125            repr.into()
126        }
127    }
128
129    /// Bincode-compatible [`alloy_consensus::BlockBody`] serde implementation.
130    ///
131    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
132    /// ```rust
133    /// use reth_primitives_traits::serde_bincode_compat::{self, SerdeBincodeCompat};
134    /// use serde::{Deserialize, Serialize};
135    /// use serde_with::serde_as;
136    ///
137    /// #[serde_as]
138    /// #[derive(Serialize, Deserialize)]
139    /// struct Data<T: SerdeBincodeCompat, H: SerdeBincodeCompat> {
140    ///     #[serde_as(as = "serde_bincode_compat::BlockBody<'_, T, H>")]
141    ///     body: alloy_consensus::BlockBody<T, H>,
142    /// }
143    /// ```
144    #[derive(derive_more::Debug, Serialize, Deserialize)]
145    #[debug(bound())]
146    pub struct BlockBody<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> {
147        transactions: Vec<T::BincodeRepr<'a>>,
148        ommers: Vec<H::BincodeRepr<'a>>,
149        withdrawals: Cow<'a, Option<Withdrawals>>,
150    }
151
152    impl<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat>
153        From<&'a alloy_consensus::BlockBody<T, H>> for BlockBody<'a, T, H>
154    {
155        fn from(value: &'a alloy_consensus::BlockBody<T, H>) -> Self {
156            Self {
157                transactions: value.transactions.iter().map(|tx| tx.as_repr()).collect(),
158                ommers: value.ommers.iter().map(|h| h.as_repr()).collect(),
159                withdrawals: Cow::Borrowed(&value.withdrawals),
160            }
161        }
162    }
163
164    impl<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> From<BlockBody<'a, T, H>>
165        for alloy_consensus::BlockBody<T, H>
166    {
167        fn from(value: BlockBody<'a, T, H>) -> Self {
168            Self {
169                transactions: value
170                    .transactions
171                    .into_iter()
172                    .map(SerdeBincodeCompat::from_repr)
173                    .collect(),
174                ommers: value.ommers.into_iter().map(SerdeBincodeCompat::from_repr).collect(),
175                withdrawals: value.withdrawals.into_owned(),
176            }
177        }
178    }
179
180    impl<T: SerdeBincodeCompat, H: SerdeBincodeCompat> SerializeAs<alloy_consensus::BlockBody<T, H>>
181        for BlockBody<'_, T, H>
182    {
183        fn serialize_as<S>(
184            source: &alloy_consensus::BlockBody<T, H>,
185            serializer: S,
186        ) -> Result<S::Ok, S::Error>
187        where
188            S: Serializer,
189        {
190            BlockBody::from(source).serialize(serializer)
191        }
192    }
193
194    impl<'de, T: SerdeBincodeCompat, H: SerdeBincodeCompat>
195        DeserializeAs<'de, alloy_consensus::BlockBody<T, H>> for BlockBody<'de, T, H>
196    {
197        fn deserialize_as<D>(deserializer: D) -> Result<alloy_consensus::BlockBody<T, H>, D::Error>
198        where
199            D: Deserializer<'de>,
200        {
201            BlockBody::deserialize(deserializer).map(Into::into)
202        }
203    }
204
205    impl<T: SerdeBincodeCompat, H: SerdeBincodeCompat> SerdeBincodeCompat
206        for alloy_consensus::BlockBody<T, H>
207    {
208        type BincodeRepr<'a> = BlockBody<'a, T, H>;
209
210        fn as_repr(&self) -> Self::BincodeRepr<'_> {
211            self.into()
212        }
213
214        fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
215            repr.into()
216        }
217    }
218
219    impl super::SerdeBincodeCompat for alloy_consensus::EthereumTxEnvelope<TxEip4844> {
220        type BincodeRepr<'a> =
221            alloy_consensus::serde_bincode_compat::transaction::EthereumTxEnvelope<'a>;
222
223        fn as_repr(&self) -> Self::BincodeRepr<'_> {
224            self.into()
225        }
226
227        fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
228            repr.into()
229        }
230    }
231
232    #[cfg(feature = "op")]
233    impl super::SerdeBincodeCompat for op_alloy_consensus::OpTxEnvelope {
234        type BincodeRepr<'a> =
235            op_alloy_consensus::serde_bincode_compat::transaction::OpTxEnvelope<'a>;
236
237        fn as_repr(&self) -> Self::BincodeRepr<'_> {
238            self.into()
239        }
240
241        fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
242            repr.into()
243        }
244    }
245}