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

fix: correct implementations of Encodable and Decodable for sidecars #1528

Merged
merged 2 commits into from
Oct 21, 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
8 changes: 4 additions & 4 deletions crates/consensus/src/transaction/eip4844.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl TxEip4844Variant {
Self::TxEip4844WithSidecar(tx) => {
let payload_length = tx.tx().fields_len() + signature.rlp_vrs_len();
let inner_header = Header { list: true, payload_length };
inner_header.length() + payload_length + tx.sidecar().fields_len()
inner_header.length() + payload_length + tx.sidecar().rlp_encoded_fields_length()
}
};

Expand Down Expand Up @@ -875,7 +875,7 @@ impl TxEip4844WithSidecar {
let inner_header = Header { list: true, payload_length: inner_payload_length };

let outer_payload_length =
inner_header.length() + inner_payload_length + self.sidecar.fields_len();
inner_header.length() + inner_payload_length + self.sidecar.rlp_encoded_fields_length();
let outer_header = Header { list: true, payload_length: outer_payload_length };

// write the two headers
Expand All @@ -885,7 +885,7 @@ impl TxEip4844WithSidecar {
// now write the fields
self.tx.encode_fields(out);
signature.write_rlp_vrs(out);
self.sidecar.encode(out);
self.sidecar.rlp_encode_fields(out);
}

/// Decodes the transaction from RLP bytes, including the signature.
Expand All @@ -910,7 +910,7 @@ impl TxEip4844WithSidecar {
let inner_tx = TxEip4844::decode_signed_fields(buf)?;

// decode the sidecar
let sidecar = BlobTransactionSidecar::decode(buf)?;
let sidecar = BlobTransactionSidecar::rlp_decode_fields(buf)?;

if buf.len() + header.payload_length != original_len {
return Err(alloy_rlp::Error::ListLengthMismatch {
Expand Down
4 changes: 3 additions & 1 deletion crates/consensus/src/transaction/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,9 @@ impl TxEnvelope {
let inner_payload_length = tx.tx().fields_len() + t.signature().rlp_vrs_len();
let inner_header = Header { list: true, payload_length: inner_payload_length };

inner_header.length() + inner_payload_length + tx.sidecar.fields_len()
inner_header.length()
+ inner_payload_length
+ tx.sidecar.rlp_encoded_fields_length()
}
},
Self::Eip7702(t) => t.tx().fields_len() + t.signature().rlp_vrs_len(),
Expand Down
4 changes: 2 additions & 2 deletions crates/eips/src/eip2718.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ pub trait Encodable2718: Sized + Send + Sync + 'static {
Sealed::new_unchecked(self, hash)
}

/// The length of the 2718 encoded envelope in network format. This is the length of the header
/// + the length of the type flag and inner encoding.
/// The length of the 2718 encoded envelope in network format. This is the
/// length of the header + the length of the type flag and inner encoding.
fn network_len(&self) -> usize {
let mut payload_length = self.encode_2718_len();
if !self.is_legacy() {
Expand Down
106 changes: 77 additions & 29 deletions crates/eips/src/eip4844/sidecar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::eip4844::{
};
use alloc::boxed::Box;
use alloy_primitives::{bytes::BufMut, B256};
use alloy_rlp::{Decodable, Encodable};
use alloy_rlp::{Decodable, Encodable, Header};

#[cfg(any(test, feature = "arbitrary"))]
use crate::eip4844::MAX_BLOBS_PER_BLOCK;
Expand All @@ -19,7 +19,7 @@ pub(crate) const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
/// This represents a set of blobs, and its corresponding commitments and proofs.
///
/// This type encodes and decodes the fields without an rlp header.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[derive(Clone, Default, PartialEq, Eq, Hash)]
#[repr(C)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[doc(alias = "BlobTxSidecar")]
Expand All @@ -36,6 +36,16 @@ pub struct BlobTransactionSidecar {
pub proofs: Vec<Bytes48>,
}

impl core::fmt::Debug for BlobTransactionSidecar {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BlobTransactionSidecar")
.field("blobs", &self.blobs.len())
.field("commitments", &self.commitments)
.field("proofs", &self.proofs)
.finish()
}
}

impl IntoIterator for BlobTransactionSidecar {
type Item = BlobTransactionSidecarItem;
type IntoIter = alloc::vec::IntoIter<BlobTransactionSidecarItem>;
Expand Down Expand Up @@ -248,25 +258,6 @@ impl BlobTransactionSidecar {
self.commitments.get(blob_index).map(|c| kzg_to_versioned_hash(c.as_slice()))
}

/// Encodes the inner [BlobTransactionSidecar] fields as RLP bytes, __without__ a RLP header.
///
/// This encodes the fields in the following order:
/// - `blobs`
/// - `commitments`
/// - `proofs`
#[inline]
pub(crate) fn encode_inner(&self, out: &mut dyn BufMut) {
// Encode the blobs, commitments, and proofs
self.blobs.encode(out);
self.commitments.encode(out);
self.proofs.encode(out);
}

/// Outputs the RLP length of the [BlobTransactionSidecar] fields, without a RLP header.
pub fn fields_len(&self) -> usize {
self.blobs.length() + self.commitments.length() + self.proofs.length()
}

/// Calculates a size heuristic for the in-memory size of the [BlobTransactionSidecar].
#[inline]
pub fn size(&self) -> usize {
Expand Down Expand Up @@ -302,27 +293,84 @@ impl BlobTransactionSidecar {

Ok(Self::from_kzg(blobs, commitments, proofs))
}

/// Outputs the RLP length of the [BlobTransactionSidecar] fields, without
/// a RLP header.
#[doc(hidden)]
pub fn rlp_encoded_fields_length(&self) -> usize {
self.blobs.length() + self.commitments.length() + self.proofs.length()
}

/// Encodes the inner [BlobTransactionSidecar] fields as RLP bytes, __without__ a RLP header.
///
/// This encodes the fields in the following order:
/// - `blobs`
/// - `commitments`
/// - `proofs`
#[inline]
#[doc(hidden)]
pub fn rlp_encode_fields(&self, out: &mut dyn BufMut) {
// Encode the blobs, commitments, and proofs
self.blobs.encode(out);
self.commitments.encode(out);
self.proofs.encode(out);
}

/// Creates an RLP header for the [BlobTransactionSidecar].
fn rlp_header(&self) -> Header {
Header { list: true, payload_length: self.rlp_encoded_fields_length() }
}

/// Calculates the length of the [BlobTransactionSidecar] when encoded as
/// RLP.
pub fn rlp_encoded_length(&self) -> usize {
self.rlp_header().length() + self.rlp_encoded_fields_length()
}

/// Encodes the [BlobTransactionSidecar] as RLP bytes.
pub fn rlp_encode(&self, out: &mut dyn BufMut) {
self.rlp_header().encode(out);
self.rlp_encode_fields(out);
}

/// RLP decode the fields of a [BlobTransactionSidecar].
#[doc(hidden)]
pub fn rlp_decode_fields(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
Ok(Self {
blobs: Decodable::decode(buf)?,
commitments: Decodable::decode(buf)?,
proofs: Decodable::decode(buf)?,
})
}

/// Decodes the [BlobTransactionSidecar] from RLP bytes.
pub fn rlp_decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
let header = Header::decode(buf)?;
if header.list {
return Err(alloy_rlp::Error::UnexpectedList);
}
if buf.len() < header.payload_length {
return Err(alloy_rlp::Error::InputTooShort);
}
Self::rlp_decode_fields(buf)
}
}

impl Encodable for BlobTransactionSidecar {
/// Encodes the inner [BlobTransactionSidecar] fields as RLP bytes, without a RLP header.
fn encode(&self, s: &mut dyn BufMut) {
self.encode_inner(s);
fn encode(&self, out: &mut dyn BufMut) {
self.rlp_encode(out);
}

fn length(&self) -> usize {
self.fields_len()
self.rlp_encoded_length()
}
}

impl Decodable for BlobTransactionSidecar {
/// Decodes the inner [BlobTransactionSidecar] fields from RLP bytes, without a RLP header.
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
Ok(Self {
blobs: Decodable::decode(buf)?,
commitments: Decodable::decode(buf)?,
proofs: Decodable::decode(buf)?,
})
Self::rlp_decode(buf)
}
}

Expand Down