Skip to content

Commit

Permalink
feat: add AnyReceiptEnvelope (#446)
Browse files Browse the repository at this point in the history
* feat: add AnyReceiptEnvelope

* fix docs
  • Loading branch information
mattsse authored Apr 3, 2024
1 parent 6945c16 commit 0b889d9
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 4 deletions.
2 changes: 1 addition & 1 deletion crates/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod header;
pub use header::{Header, EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH};

mod receipt;
pub use receipt::{Receipt, ReceiptEnvelope, ReceiptWithBloom, TxReceipt};
pub use receipt::{AnyReceiptEnvelope, Receipt, ReceiptEnvelope, ReceiptWithBloom, TxReceipt};

mod transaction;
pub use transaction::{
Expand Down
74 changes: 74 additions & 0 deletions crates/consensus/src/receipt/any.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use crate::ReceiptWithBloom;
use alloy_eips::eip2718::{Decodable2718, Encodable2718};
use alloy_primitives::{bytes::BufMut, Log};
use alloy_rlp::{Decodable, Encodable};

/// Receipt envelope, as defined in [EIP-2718].
///
/// This enum distinguishes between tagged and untagged legacy receipts, as the
/// in-protocol merkle tree may commit to EITHER 0-prefixed or raw. Therefore
/// we must ensure that encoding returns the precise byte-array that was
/// decoded, preserving the presence or absence of the `TransactionType` flag.
///
/// Transaction receipt payloads are specified in their respective EIPs.
///
/// [EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AnyReceiptEnvelope<T = Log> {
/// The receipt envelope.
#[cfg_attr(feature = "serde", serde(flatten))]
pub inner: ReceiptWithBloom<T>,
/// The transaction type.
#[cfg_attr(feature = "serde", serde(with = "alloy_serde::num::u8_hex"))]
pub r#type: u8,
}

impl AnyReceiptEnvelope {
/// Returns whether this is a legacy receipt (type 0)
pub const fn is_legacy(&self) -> bool {
self.r#type == 0
}

/// Calculate the length of the rlp payload of the network encoded receipt.
pub fn rlp_payload_length(&self) -> usize {
let length = self.inner.length();
if self.is_legacy() {
length
} else {
length + 1
}
}
}

impl Encodable2718 for AnyReceiptEnvelope {
fn type_flag(&self) -> Option<u8> {
match self.r#type {
0 => None,
ty => Some(ty),
}
}

fn encode_2718_len(&self) -> usize {
self.inner.length() + !self.is_legacy() as usize
}

fn encode_2718(&self, out: &mut dyn BufMut) {
match self.type_flag() {
None => {}
Some(ty) => out.put_u8(ty),
}
self.inner.encode(out);
}
}

impl Decodable2718 for AnyReceiptEnvelope {
fn typed_decode(ty: u8, buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
let receipt = Decodable::decode(buf)?;
Ok(Self { inner: receipt, r#type: ty })
}

fn fallback_decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
Self::typed_decode(0, buf)
}
}
3 changes: 3 additions & 0 deletions crates/consensus/src/receipt/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use alloy_primitives::{Bloom, Log};

mod any;
pub use any::AnyReceiptEnvelope;

mod envelope;
pub use envelope::ReceiptEnvelope;

Expand Down
47 changes: 44 additions & 3 deletions crates/serde/src/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,47 @@ impl<'de> Deserialize<'de> for U64HexOrNumber {
}
}

/// serde functions for handling `u8` as [U8](alloy_primitives::U8)
pub mod u8_hex {
use alloy_primitives::U8;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

/// Deserializes an `u8` from [U8] accepting a hex quantity string with optional 0x prefix
pub fn deserialize<'de, D>(deserializer: D) -> Result<u8, D::Error>
where
D: Deserializer<'de>,
{
U8::deserialize(deserializer).map(|val| val.to())
}

/// Serializes u64 as hex string
pub fn serialize<S: Serializer>(value: &u8, s: S) -> Result<S::Ok, S::Error> {
U8::from(*value).serialize(s)
}
}

/// serde functions for handling `Option<u8>` as [U8](alloy_primitives::U8)
pub mod u8_hex_opt {
use alloy_primitives::U8;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

/// Serializes u64 as hex string
pub fn serialize<S: Serializer>(value: &Option<u8>, s: S) -> Result<S::Ok, S::Error> {
match value {
Some(val) => U8::from(*val).serialize(s),
None => s.serialize_none(),
}
}

/// Deserializes an `Option` from [U8] accepting a hex quantity string with optional 0x prefix
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<u8>, D::Error>
where
D: Deserializer<'de>,
{
Ok(U8::deserialize(deserializer).map_or(None, |v| Some(u8::from_be_bytes(v.to_be_bytes()))))
}
}

/// serde functions for handling `u64` as [U64]
pub mod u64_hex {
use alloy_primitives::U64;
Expand Down Expand Up @@ -203,12 +244,12 @@ where
NumberOrHexU256::deserialize(deserializer)?.try_into_u256()
}

/// serde functions for handling primitive `u64` as [U64]
/// serde functions for handling primitive `u128` as [U128](alloy_primitives::U128)
pub mod u128_hex_or_decimal {
use alloy_primitives::U128;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

/// Deserializes an `u64` accepting a hex quantity string with optional 0x prefix or
/// Deserializes an `u128` accepting a hex quantity string with optional 0x prefix or
/// a number
pub fn deserialize<'de, D>(deserializer: D) -> Result<u128, D::Error>
where
Expand All @@ -217,7 +258,7 @@ pub mod u128_hex_or_decimal {
U128::deserialize(deserializer).map(|val| val.to())
}

/// Serializes u64 as hex string
/// Serializes u128 as hex string
pub fn serialize<S: Serializer>(value: &u128, s: S) -> Result<S::Ok, S::Error> {
U128::from(*value).serialize(s)
}
Expand Down

0 comments on commit 0b889d9

Please sign in to comment.