Skip to content

Commit

Permalink
fix: correct implementations of Encodable and Decodable for sidecars
Browse files Browse the repository at this point in the history
  • Loading branch information
prestwich committed Oct 19, 2024
1 parent 1ad42f9 commit a7b7e2c
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 36 deletions.
9 changes: 5 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 Expand Up @@ -1085,6 +1085,7 @@ mod tests {
let len = expected_envelope.length();
let mut buf = Vec::with_capacity(len);
expected_envelope.encode(&mut buf);
dbg!(alloy_primitives::hex::encode(&buf[0..16]));
assert_eq!(buf.len(), len);

// ensure it's also the same size that `actual` claims to be, since we just changed the
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

0 comments on commit a7b7e2c

Please sign in to comment.