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_eips::eip4895::Withdrawals;
46    use serde::{Deserialize, Deserializer, Serialize, Serializer};
47    use serde_with::{DeserializeAs, SerializeAs};
48
49    /// Bincode-compatible [`alloy_consensus::Block`] serde implementation.
50    ///
51    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
52    /// ```rust
53    /// use alloy_consensus::Block;
54    /// use reth_primitives_traits::serde_bincode_compat::{self, SerdeBincodeCompat};
55    /// use serde::{Deserialize, Serialize};
56    /// use serde_with::serde_as;
57    ///
58    /// #[serde_as]
59    /// #[derive(Serialize, Deserialize)]
60    /// struct Data<T: SerdeBincodeCompat, H: SerdeBincodeCompat> {
61    ///     #[serde_as(as = "serde_bincode_compat::Block<'_, T, H>")]
62    ///     body: Block<T, H>,
63    /// }
64    /// ```
65    #[derive(derive_more::Debug, Serialize, Deserialize)]
66    #[debug(bound())]
67    pub struct Block<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> {
68        header: H::BincodeRepr<'a>,
69        #[serde(bound = "BlockBody<'a, T, H>: Serialize + serde::de::DeserializeOwned")]
70        body: BlockBody<'a, T, H>,
71    }
72
73    impl<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> From<&'a alloy_consensus::Block<T, H>>
74        for Block<'a, T, H>
75    {
76        fn from(value: &'a alloy_consensus::Block<T, H>) -> Self {
77            Self { header: value.header.as_repr(), body: (&value.body).into() }
78        }
79    }
80
81    impl<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> From<Block<'a, T, H>>
82        for alloy_consensus::Block<T, H>
83    {
84        fn from(value: Block<'a, T, H>) -> Self {
85            Self { header: SerdeBincodeCompat::from_repr(value.header), body: value.body.into() }
86        }
87    }
88
89    impl<T: SerdeBincodeCompat, H: SerdeBincodeCompat> SerializeAs<alloy_consensus::Block<T, H>>
90        for Block<'_, T, H>
91    {
92        fn serialize_as<S>(
93            source: &alloy_consensus::Block<T, H>,
94            serializer: S,
95        ) -> Result<S::Ok, S::Error>
96        where
97            S: Serializer,
98        {
99            Block::from(source).serialize(serializer)
100        }
101    }
102
103    impl<'de, T: SerdeBincodeCompat, H: SerdeBincodeCompat>
104        DeserializeAs<'de, alloy_consensus::Block<T, H>> for Block<'de, T, H>
105    {
106        fn deserialize_as<D>(deserializer: D) -> Result<alloy_consensus::Block<T, H>, D::Error>
107        where
108            D: Deserializer<'de>,
109        {
110            Block::deserialize(deserializer).map(Into::into)
111        }
112    }
113
114    impl<T: SerdeBincodeCompat, H: SerdeBincodeCompat> SerdeBincodeCompat
115        for alloy_consensus::Block<T, H>
116    {
117        type BincodeRepr<'a> = Block<'a, T, H>;
118
119        fn as_repr(&self) -> Self::BincodeRepr<'_> {
120            self.into()
121        }
122
123        fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
124            repr.into()
125        }
126    }
127
128    /// Bincode-compatible [`alloy_consensus::BlockBody`] serde implementation.
129    ///
130    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
131    /// ```rust
132    /// use reth_primitives_traits::serde_bincode_compat::{self, SerdeBincodeCompat};
133    /// use serde::{Deserialize, Serialize};
134    /// use serde_with::serde_as;
135    ///
136    /// #[serde_as]
137    /// #[derive(Serialize, Deserialize)]
138    /// struct Data<T: SerdeBincodeCompat, H: SerdeBincodeCompat> {
139    ///     #[serde_as(as = "serde_bincode_compat::BlockBody<'_, T, H>")]
140    ///     body: alloy_consensus::BlockBody<T, H>,
141    /// }
142    /// ```
143    #[derive(derive_more::Debug, Serialize, Deserialize)]
144    #[debug(bound())]
145    pub struct BlockBody<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> {
146        transactions: Vec<T::BincodeRepr<'a>>,
147        ommers: Vec<H::BincodeRepr<'a>>,
148        withdrawals: Cow<'a, Option<Withdrawals>>,
149    }
150
151    impl<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat>
152        From<&'a alloy_consensus::BlockBody<T, H>> for BlockBody<'a, T, H>
153    {
154        fn from(value: &'a alloy_consensus::BlockBody<T, H>) -> Self {
155            Self {
156                transactions: value.transactions.iter().map(|tx| tx.as_repr()).collect(),
157                ommers: value.ommers.iter().map(|h| h.as_repr()).collect(),
158                withdrawals: Cow::Borrowed(&value.withdrawals),
159            }
160        }
161    }
162
163    impl<'a, T: SerdeBincodeCompat, H: SerdeBincodeCompat> From<BlockBody<'a, T, H>>
164        for alloy_consensus::BlockBody<T, H>
165    {
166        fn from(value: BlockBody<'a, T, H>) -> Self {
167            Self {
168                transactions: value
169                    .transactions
170                    .into_iter()
171                    .map(SerdeBincodeCompat::from_repr)
172                    .collect(),
173                ommers: value.ommers.into_iter().map(SerdeBincodeCompat::from_repr).collect(),
174                withdrawals: value.withdrawals.into_owned(),
175            }
176        }
177    }
178
179    impl<T: SerdeBincodeCompat, H: SerdeBincodeCompat> SerializeAs<alloy_consensus::BlockBody<T, H>>
180        for BlockBody<'_, T, H>
181    {
182        fn serialize_as<S>(
183            source: &alloy_consensus::BlockBody<T, H>,
184            serializer: S,
185        ) -> Result<S::Ok, S::Error>
186        where
187            S: Serializer,
188        {
189            BlockBody::from(source).serialize(serializer)
190        }
191    }
192
193    impl<'de, T: SerdeBincodeCompat, H: SerdeBincodeCompat>
194        DeserializeAs<'de, alloy_consensus::BlockBody<T, H>> for BlockBody<'de, T, H>
195    {
196        fn deserialize_as<D>(deserializer: D) -> Result<alloy_consensus::BlockBody<T, H>, D::Error>
197        where
198            D: Deserializer<'de>,
199        {
200            BlockBody::deserialize(deserializer).map(Into::into)
201        }
202    }
203
204    impl<T: SerdeBincodeCompat, H: SerdeBincodeCompat> SerdeBincodeCompat
205        for alloy_consensus::BlockBody<T, H>
206    {
207        type BincodeRepr<'a> = BlockBody<'a, T, H>;
208
209        fn as_repr(&self) -> Self::BincodeRepr<'_> {
210            self.into()
211        }
212
213        fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
214            repr.into()
215        }
216    }
217}