diff --git a/crates/consensus/src/transaction/eip4844.rs b/crates/consensus/src/transaction/eip4844.rs index 0036666eb97..a3ad4dbceaf 100644 --- a/crates/consensus/src/transaction/eip4844.rs +++ b/crates/consensus/src/transaction/eip4844.rs @@ -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() } }; @@ -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 @@ -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. @@ -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 { diff --git a/crates/consensus/src/transaction/envelope.rs b/crates/consensus/src/transaction/envelope.rs index 73549a4db4b..14768f55a83 100644 --- a/crates/consensus/src/transaction/envelope.rs +++ b/crates/consensus/src/transaction/envelope.rs @@ -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(), diff --git a/crates/eips/src/eip2718.rs b/crates/eips/src/eip2718.rs index 17550c10c7b..82c11a1f759 100644 --- a/crates/eips/src/eip2718.rs +++ b/crates/eips/src/eip2718.rs @@ -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() { diff --git a/crates/eips/src/eip4844/sidecar.rs b/crates/eips/src/eip4844/sidecar.rs index 2db41652f89..9ce3d137bbb 100644 --- a/crates/eips/src/eip4844/sidecar.rs +++ b/crates/eips/src/eip4844/sidecar.rs @@ -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; @@ -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")] @@ -36,6 +36,16 @@ pub struct BlobTransactionSidecar { pub proofs: Vec, } +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; @@ -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 { @@ -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 { + 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 { + 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 { - Ok(Self { - blobs: Decodable::decode(buf)?, - commitments: Decodable::decode(buf)?, - proofs: Decodable::decode(buf)?, - }) + Self::rlp_decode(buf) } }