Skip to content

Commit 5b0cc50

Browse files
mattsserkrasiuk
authored andcommitted
feat: add bincode compat for receipt envelope (#2246)
* fix: use unwrap_or_else for subscribe * feat: add bincode to receipt * feat: receipt envelope bincode
1 parent e913e86 commit 5b0cc50

File tree

2 files changed

+132
-1
lines changed

2 files changed

+132
-1
lines changed

crates/consensus/src/receipt/envelope.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,137 @@ where
280280
}
281281
}
282282

283+
/// Bincode-compatible [`ReceiptEnvelope`] serde implementation.
284+
#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
285+
pub(crate) mod serde_bincode_compat {
286+
use crate::{Receipt, ReceiptWithBloom, TxType};
287+
use alloc::borrow::Cow;
288+
use alloy_primitives::{Bloom, Log, U8};
289+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
290+
use serde_with::{DeserializeAs, SerializeAs};
291+
292+
/// Bincode-compatible [`super::ReceiptEnvelope`] serde implementation.
293+
///
294+
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
295+
/// ```rust
296+
/// use alloy_consensus::{serde_bincode_compat, ReceiptEnvelope};
297+
/// use serde::{de::DeserializeOwned, Deserialize, Serialize};
298+
/// use serde_with::serde_as;
299+
///
300+
/// #[serde_as]
301+
/// #[derive(Serialize, Deserialize)]
302+
/// struct Data<T: Serialize + DeserializeOwned + Clone + 'static> {
303+
/// #[serde_as(as = "serde_bincode_compat::ReceiptEnvelope<'_, T>")]
304+
/// receipt: ReceiptEnvelope<T>,
305+
/// }
306+
/// ```
307+
#[derive(Debug, Serialize, Deserialize)]
308+
pub struct ReceiptEnvelope<'a, T: Clone = Log> {
309+
#[serde(deserialize_with = "deserde_txtype")]
310+
tx_type: TxType,
311+
success: bool,
312+
cumulative_gas_used: u64,
313+
logs_bloom: Cow<'a, Bloom>,
314+
logs: Cow<'a, [T]>,
315+
}
316+
317+
/// Ensures that txtype is deserialized symmetrically as U8
318+
fn deserde_txtype<'de, D>(deserializer: D) -> Result<TxType, D::Error>
319+
where
320+
D: Deserializer<'de>,
321+
{
322+
let value = U8::deserialize(deserializer)?;
323+
value.to::<u8>().try_into().map_err(serde::de::Error::custom)
324+
}
325+
326+
impl<'a, T: Clone> From<&'a super::ReceiptEnvelope<T>> for ReceiptEnvelope<'a, T> {
327+
fn from(value: &'a super::ReceiptEnvelope<T>) -> Self {
328+
Self {
329+
tx_type: value.tx_type(),
330+
success: value.status(),
331+
cumulative_gas_used: value.cumulative_gas_used(),
332+
logs_bloom: Cow::Borrowed(value.logs_bloom()),
333+
logs: Cow::Borrowed(value.logs()),
334+
}
335+
}
336+
}
337+
338+
impl<'a, T: Clone> From<ReceiptEnvelope<'a, T>> for super::ReceiptEnvelope<T> {
339+
fn from(value: ReceiptEnvelope<'a, T>) -> Self {
340+
let ReceiptEnvelope { tx_type, success, cumulative_gas_used, logs_bloom, logs } = value;
341+
let receipt = ReceiptWithBloom {
342+
receipt: Receipt {
343+
status: success.into(),
344+
cumulative_gas_used,
345+
logs: logs.into_owned(),
346+
},
347+
logs_bloom: logs_bloom.into_owned(),
348+
};
349+
match tx_type {
350+
TxType::Legacy => Self::Legacy(receipt),
351+
TxType::Eip2930 => Self::Eip2930(receipt),
352+
TxType::Eip1559 => Self::Eip1559(receipt),
353+
TxType::Eip4844 => Self::Eip4844(receipt),
354+
TxType::Eip7702 => Self::Eip7702(receipt),
355+
}
356+
}
357+
}
358+
359+
impl<T: Serialize + Clone> SerializeAs<super::ReceiptEnvelope<T>> for ReceiptEnvelope<'_, T> {
360+
fn serialize_as<S>(
361+
source: &super::ReceiptEnvelope<T>,
362+
serializer: S,
363+
) -> Result<S::Ok, S::Error>
364+
where
365+
S: Serializer,
366+
{
367+
ReceiptEnvelope::<'_, T>::from(source).serialize(serializer)
368+
}
369+
}
370+
371+
impl<'de, T: Deserialize<'de> + Clone> DeserializeAs<'de, super::ReceiptEnvelope<T>>
372+
for ReceiptEnvelope<'de, T>
373+
{
374+
fn deserialize_as<D>(deserializer: D) -> Result<super::ReceiptEnvelope<T>, D::Error>
375+
where
376+
D: Deserializer<'de>,
377+
{
378+
ReceiptEnvelope::<'_, T>::deserialize(deserializer).map(Into::into)
379+
}
380+
}
381+
382+
#[cfg(test)]
383+
mod tests {
384+
use super::super::{serde_bincode_compat, ReceiptEnvelope};
385+
use alloy_primitives::Log;
386+
use arbitrary::Arbitrary;
387+
use rand::Rng;
388+
use serde::{Deserialize, Serialize};
389+
use serde_with::serde_as;
390+
391+
#[test]
392+
fn test_receipt_evelope_bincode_roundtrip() {
393+
#[serde_as]
394+
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
395+
struct Data {
396+
#[serde_as(as = "serde_bincode_compat::ReceiptEnvelope<'_>")]
397+
transaction: ReceiptEnvelope<Log>,
398+
}
399+
400+
let mut bytes = [0u8; 1024];
401+
rand::thread_rng().fill(bytes.as_mut_slice());
402+
let data = Data {
403+
transaction: ReceiptEnvelope::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
404+
.unwrap(),
405+
};
406+
407+
let encoded = bincode::serialize(&data).unwrap();
408+
let decoded: Data = bincode::deserialize(&encoded).unwrap();
409+
assert_eq!(decoded, data);
410+
}
411+
}
412+
}
413+
283414
#[cfg(test)]
284415
mod test {
285416
#[cfg(feature = "serde")]

crates/consensus/src/receipt/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use alloy_eips::Typed2718;
1616
/// Bincode-compatible serde implementations for receipt types.
1717
#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
1818
pub(crate) mod serde_bincode_compat {
19-
pub use super::receipts::serde_bincode_compat::*;
19+
pub use super::{envelope::serde_bincode_compat::*, receipts::serde_bincode_compat::*};
2020
}
2121

2222
/// Receipt is the result of a transaction execution.

0 commit comments

Comments
 (0)