Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: impl Encodable2718 for ReceiptWithBloom #1719

Merged
merged 4 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/consensus-any/src/receipt/envelope.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use alloy_consensus::{Eip658Value, Receipt, ReceiptWithBloom, RlpReceipt, TxReceipt};
use alloy_consensus::{Eip658Value, Receipt, ReceiptWithBloom, RlpEncodableReceipt, TxReceipt};
use alloy_eips::eip2718::{Decodable2718, Eip2718Result, Encodable2718};
use alloy_primitives::{bytes::BufMut, Bloom, Log};
use alloy_rlp::{Decodable, Encodable};
Expand Down Expand Up @@ -32,7 +32,7 @@ impl<R> AnyReceiptEnvelope<R> {
}
}

impl<R: RlpReceipt> AnyReceiptEnvelope<R> {
impl<R: RlpEncodableReceipt> AnyReceiptEnvelope<R> {
/// Calculate the length of the rlp payload of the network encoded receipt.
pub fn rlp_payload_length(&self) -> usize {
let length = self.inner.length();
Expand Down
3 changes: 2 additions & 1 deletion crates/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ pub use constants::{EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH};

mod receipt;
pub use receipt::{
Eip658Value, Receipt, ReceiptEnvelope, ReceiptWithBloom, Receipts, RlpReceipt, TxReceipt,
Eip2718EncodableReceipt, Eip658Value, Receipt, ReceiptEnvelope, ReceiptWithBloom, Receipts,
RlpDecodableReceipt, RlpEncodableReceipt, TxReceipt,
};

pub mod proofs;
Expand Down
91 changes: 39 additions & 52 deletions crates/consensus/src/receipt/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloy_primitives::Bloom;
use alloy_rlp::{Buf, BufMut, Header};
use alloy_rlp::BufMut;
use core::fmt;

mod envelope;
Expand All @@ -11,6 +11,8 @@ pub use receipts::{Receipt, ReceiptWithBloom, Receipts};
mod status;
pub use status::Eip658Value;

use crate::Typed2718;

/// Receipt is the result of a transaction execution.
#[doc(alias = "TransactionReceipt")]
#[auto_impl::auto_impl(&, Arc)]
Expand Down Expand Up @@ -61,63 +63,33 @@ pub trait TxReceipt: Clone + fmt::Debug + PartialEq + Eq + Send + Sync {
fn logs(&self) -> &[Self::Log];
}

/// Receipt type that knows how to encode and decode itself with a [`Bloom`] value.
pub trait RlpReceipt: Sized {
/// Returns the length of the RLP encoded receipt fields with the provided bloom filter, without
/// RLP header.
fn rlp_encoded_fields_length_with_bloom(&self, bloom: Bloom) -> usize;

/// RLP encodes the receipt fields with the provided bloom filter, without RLP header.
fn rlp_encode_fields_with_bloom(&self, bloom: Bloom, out: &mut dyn BufMut);

/// Returns the RLP header for the receipt payload with the provided bloom filter.
fn rlp_header_with_bloom(&self, bloom: Bloom) -> alloy_rlp::Header {
alloy_rlp::Header {
list: true,
payload_length: self.rlp_encoded_fields_length_with_bloom(bloom),
}
}

/// Receipt type that knows how to encode itself with a [`Bloom`] value.
#[auto_impl::auto_impl(&)]
pub trait RlpEncodableReceipt {
/// Returns the length of the receipt payload with the provided bloom filter.
fn rlp_encoded_length_with_bloom(&self, bloom: Bloom) -> usize {
self.rlp_header_with_bloom(bloom).length_with_payload()
}
fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize;

/// RLP encodes the receipt with the provided bloom filter.
fn rlp_encode_with_bloom(&self, bloom: Bloom, out: &mut dyn BufMut) {
self.rlp_header_with_bloom(bloom).encode(out);
self.rlp_encode_fields_with_bloom(bloom, out);
}

/// RLP decodes receipt's fields and [`Bloom`] into [`ReceiptWithBloom`] instance.
///
/// Note: this should not decode an RLP header.
fn rlp_decode_fields_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>>;
fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut);
}

/// Receipt type that knows how to decode itself with a [`Bloom`] value.
pub trait RlpDecodableReceipt: Sized {
/// RLP decodes receipt and [`Bloom`] into [`ReceiptWithBloom`] instance.
fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>> {
let header = Header::decode(buf)?;
if !header.list {
return Err(alloy_rlp::Error::UnexpectedString);
}

if header.payload_length > buf.len() {
return Err(alloy_rlp::Error::InputTooShort);
}

// Note: we pass a new slice to `Self::rlp_decode_fields_with_bloom` so that it knows the
// length of the payload specified in header.
let mut fields_buf = &buf[..header.payload_length];
let this = Self::rlp_decode_fields_with_bloom(&mut fields_buf)?;

if !fields_buf.is_empty() {
return Err(alloy_rlp::Error::UnexpectedLength);
}

buf.advance(header.payload_length);
fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>>;
}

Ok(this)
}
/// Receipt type that knows its EIP-2718 encoding.
///
/// Main consumer of this trait is [`ReceiptWithBloom`]. It is expected that [`RlpEncodableReceipt`]
/// implementation for this type produces network encoding whcih is used by [`alloy_rlp::Encodable`]
/// implementation for [`ReceiptWithBloom`].
pub trait Eip2718EncodableReceipt: RlpEncodableReceipt + Typed2718 {
/// EIP-2718 encoded length with the provided bloom filter.
fn eip2718_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize;

/// EIP-2718 encodes the receipt with the provided bloom filter.
fn eip2718_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut);
}

#[cfg(test)]
Expand Down Expand Up @@ -227,4 +199,19 @@ mod tests {
// let (decoded, _) = Receipt::from_compact(&data[..], data.len());
assert_eq!(decoded, receipt);
}

#[test]
fn can_encode_by_reference() {
let receipt: Receipt =
Receipt { cumulative_gas_used: 16747627, status: true.into(), logs: vec![] };

let encoded_ref = alloy_rlp::encode(&ReceiptWithBloom {
receipt: &receipt,
logs_bloom: receipt.bloom_slow(),
});
let encoded =
alloy_rlp::encode(&ReceiptWithBloom { logs_bloom: receipt.bloom_slow(), receipt });

assert_eq!(encoded, encoded_ref);
}
}
74 changes: 65 additions & 9 deletions crates/consensus/src/receipt/receipts.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use crate::receipt::{Eip658Value, RlpReceipt, TxReceipt};
use crate::receipt::{Eip658Value, RlpDecodableReceipt, RlpEncodableReceipt, TxReceipt};
use alloc::{vec, vec::Vec};
use alloy_eips::eip2718::Encodable2718;
use alloy_primitives::{Bloom, Log};
use alloy_rlp::{BufMut, Decodable, Encodable};
use alloy_rlp::{BufMut, Decodable, Encodable, Header};
use core::{borrow::Borrow, fmt};
use derive_more::{DerefMut, From, IntoIterator};

use super::Eip2718EncodableReceipt;

/// Receipt containing result of transaction execution.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
Expand Down Expand Up @@ -68,21 +71,38 @@ where
}
}

impl<T: Encodable + Decodable> RlpReceipt for Receipt<T> {
fn rlp_encoded_fields_length_with_bloom(&self, bloom: Bloom) -> usize {
impl<T: Encodable> Receipt<T> {
fn rlp_encoded_fields_length_with_bloom(&self, bloom: &Bloom) -> usize {
self.status.length()
+ self.cumulative_gas_used.length()
+ bloom.length()
+ self.logs.length()
}

fn rlp_encode_fields_with_bloom(&self, bloom: Bloom, out: &mut dyn BufMut) {
fn rlp_encode_fields_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) {
self.status.encode(out);
self.cumulative_gas_used.encode(out);
bloom.encode(out);
self.logs.encode(out);
}

fn rlp_header_with_bloom(&self, bloom: &Bloom) -> Header {
Header { list: true, payload_length: self.rlp_encoded_fields_length_with_bloom(bloom) }
}
}

impl<T: Encodable> RlpEncodableReceipt for Receipt<T> {
fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize {
self.rlp_header_with_bloom(bloom).length_with_payload()
}

fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) {
self.rlp_header_with_bloom(bloom).encode(out);
self.rlp_encode_fields_with_bloom(bloom, out);
}
}

impl<T: Decodable> Receipt<T> {
fn rlp_decode_fields_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>> {
let status = Decodable::decode(buf)?;
let cumulative_gas_used = Decodable::decode(buf)?;
Expand All @@ -93,6 +113,25 @@ impl<T: Encodable + Decodable> RlpReceipt for Receipt<T> {
}
}

impl<T: Decodable> RlpDecodableReceipt for Receipt<T> {
fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>> {
let header = Header::decode(buf)?;
if !header.list {
return Err(alloy_rlp::Error::UnexpectedString);
}

let remaining = buf.len();

let this = Self::rlp_decode_fields_with_bloom(buf)?;

if buf.len() + header.payload_length != remaining {
return Err(alloy_rlp::Error::UnexpectedLength);
}

Ok(this)
}
}

impl<T> From<ReceiptWithBloom<Self>> for Receipt<T> {
/// Consume the structure, returning only the receipt
fn from(receipt_with_bloom: ReceiptWithBloom<Self>) -> Self {
Expand Down Expand Up @@ -210,22 +249,39 @@ impl<R> ReceiptWithBloom<R> {
}
}

impl<R: RlpReceipt> Encodable for ReceiptWithBloom<R> {
impl<R: RlpEncodableReceipt> Encodable for ReceiptWithBloom<R> {
fn encode(&self, out: &mut dyn BufMut) {
self.receipt.rlp_encode_with_bloom(self.logs_bloom, out);
self.receipt.rlp_encode_with_bloom(&self.logs_bloom, out);
}

fn length(&self) -> usize {
self.receipt.rlp_encoded_length_with_bloom(self.logs_bloom)
self.receipt.rlp_encoded_length_with_bloom(&self.logs_bloom)
}
}

impl<R: RlpReceipt> Decodable for ReceiptWithBloom<R> {
impl<R: RlpDecodableReceipt> Decodable for ReceiptWithBloom<R> {
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
R::rlp_decode_with_bloom(buf)
}
}

impl<R> Encodable2718 for ReceiptWithBloom<R>
where
R: Eip2718EncodableReceipt + Send + Sync,
{
fn type_flag(&self) -> Option<u8> {
(!self.receipt.is_legacy()).then_some(self.receipt.ty())
}

fn encode_2718(&self, out: &mut dyn BufMut) {
self.receipt.eip2718_encode_with_bloom(&self.logs_bloom, out);
}

fn encode_2718_len(&self) -> usize {
self.receipt.eip2718_encoded_length_with_bloom(&self.logs_bloom)
}
}

#[cfg(any(test, feature = "arbitrary"))]
impl<'a, R> arbitrary::Arbitrary<'a> for ReceiptWithBloom<R>
where
Expand Down
24 changes: 2 additions & 22 deletions crates/consensus/src/transaction/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,28 +126,8 @@ impl Decodable for TxType {
}

impl Typed2718 for TxType {
fn is_type(&self, ty: u8) -> bool {
*self == ty
}

fn is_legacy(&self) -> bool {
matches!(self, Self::Legacy)
}

fn is_eip2930(&self) -> bool {
matches!(self, Self::Eip2930)
}

fn is_eip1559(&self) -> bool {
matches!(self, Self::Eip1559)
}

fn is_eip4844(&self) -> bool {
matches!(self, Self::Eip4844)
}

fn is_eip7702(&self) -> bool {
matches!(self, Self::Eip7702)
fn ty(&self) -> u8 {
(*self).into()
}
}

Expand Down
28 changes: 22 additions & 6 deletions crates/consensus/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,22 +336,38 @@ impl<T: Transaction> Transaction for alloy_serde::WithOtherFields<T> {
}

/// A trait that helps to determine the type of the transaction.
#[auto_impl::auto_impl(&)]
pub trait Typed2718 {
/// Returns the EIP-2718 type flag.
fn ty(&self) -> u8;

/// Returns true if the type matches the given type.
fn is_type(&self, ty: u8) -> bool;
fn is_type(&self, ty: u8) -> bool {
self.ty() == ty
}

/// Returns true if the type is a legacy transaction.
fn is_legacy(&self) -> bool;
fn is_legacy(&self) -> bool {
self.ty() == 0
}

/// Returns true if the type is an EIP-2930 transaction.
fn is_eip2930(&self) -> bool;
fn is_eip2930(&self) -> bool {
self.ty() == 1
}

/// Returns true if the type is an EIP-1559 transaction.
fn is_eip1559(&self) -> bool;
fn is_eip1559(&self) -> bool {
self.ty() == 2
}

/// Returns true if the type is an EIP-4844 transaction.
fn is_eip4844(&self) -> bool;
fn is_eip4844(&self) -> bool {
self.ty() == 3
}

/// Returns true if the type is an EIP-7702 transaction.
fn is_eip7702(&self) -> bool;
fn is_eip7702(&self) -> bool {
self.ty() == 4
}
}
2 changes: 1 addition & 1 deletion crates/eips/src/eip2718.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ pub trait Decodable2718: Sized {
/// over the accepted transaction types.
///
/// [EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718
pub trait Encodable2718: Sized + Send + Sync + 'static {
pub trait Encodable2718: Sized + Send + Sync {
/// Return the type flag (if any).
///
/// This should return `None` for the default (legacy) variant of the
Expand Down