From 48b86a99643823d891fb8f894abbf48030736f5b Mon Sep 17 00:00:00 2001 From: refcell Date: Sun, 27 Oct 2024 19:45:56 -0400 Subject: [PATCH 1/6] feat: add batch utilities --- crates/protocol/src/lib.rs | 5 +- crates/protocol/src/utils.rs | 142 ++++++++++++++++++++++++++++++++++- 2 files changed, 145 insertions(+), 2 deletions(-) diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs index 29a90e6b..6dbe8932 100644 --- a/crates/protocol/src/lib.rs +++ b/crates/protocol/src/lib.rs @@ -30,7 +30,10 @@ mod iter; pub use iter::FrameIter; mod utils; -pub use utils::{starts_with_2718_deposit, to_system_config, OpBlockConversionError}; +pub use utils::{ + convert_v_to_y_parity, is_protected_v, read_tx_data, starts_with_2718_deposit, + to_system_config, OpBlockConversionError, +}; mod channel; pub use channel::{ diff --git a/crates/protocol/src/utils.rs b/crates/protocol/src/utils.rs index e415e1ad..62b66580 100644 --- a/crates/protocol/src/utils.rs +++ b/crates/protocol/src/utils.rs @@ -1,10 +1,17 @@ //! Utility methods used by protocol types. -use crate::{block_info::DecodeError, L1BlockInfoBedrock, L1BlockInfoEcotone, L1BlockInfoTx}; +use alloc::vec::Vec; +use alloy_consensus::{TxEnvelope, TxType}; use alloy_primitives::B256; +use alloy_rlp::{Buf, Header}; use op_alloy_consensus::{OpBlock, OpTxEnvelope}; use op_alloy_genesis::{RollupConfig, SystemConfig}; +use crate::{ + block_info::DecodeError, L1BlockInfoBedrock, L1BlockInfoEcotone, L1BlockInfoTx, SpanBatchError, + SpanDecodingError, +}; + /// Returns if the given `value` is a deposit transaction. pub fn starts_with_2718_deposit(value: &B) -> bool where @@ -224,9 +231,85 @@ fn u24(input: &[u8], idx: u32) -> u32 { + (u32::from(input[(idx + 2) as usize]) << 16) } +/// Reads transaction data from a reader. +#[allow(unused)] +pub fn read_tx_data(r: &mut &[u8]) -> Result<(Vec, TxType), SpanBatchError> { + let mut tx_data = Vec::new(); + let first_byte = + *r.first().ok_or(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData))?; + let mut tx_type = 0; + if first_byte <= 0x7F { + // EIP-2718: Non-legacy tx, so write tx type + tx_type = first_byte; + tx_data.push(tx_type); + r.advance(1); + } + + // Read the RLP header with a different reader pointer. This prevents the initial pointer from + // being advanced in the case that what we read is invalid. + let rlp_header = Header::decode(&mut (**r).as_ref()) + .map_err(|_| SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData))?; + + let tx_payload = if rlp_header.list { + // Grab the raw RLP for the transaction data from `r`. It was unaffected since we copied it. + let payload_length_with_header = rlp_header.payload_length + rlp_header.length(); + let payload = r[0..payload_length_with_header].to_vec(); + r.advance(payload_length_with_header); + Ok(payload) + } else { + Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData)) + }?; + tx_data.extend_from_slice(&tx_payload); + + Ok(( + tx_data, + tx_type + .try_into() + .map_err(|_| SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionType))?, + )) +} + +/// Converts a `v` value to a y parity bit, from the transaaction type. +#[allow(unused)] +pub const fn convert_v_to_y_parity(v: u64, tx_type: TxType) -> Result { + match tx_type { + TxType::Legacy => { + if v != 27 && v != 28 { + // EIP-155: v = 2 * chain_id + 35 + yParity + Ok((v - 35) & 1 == 1) + } else { + // Unprotected legacy txs must have v = 27 or 28 + Ok(v - 27 == 1) + } + } + TxType::Eip2930 | TxType::Eip1559 => Ok(v == 1), + _ => Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionType)), + } +} + +/// Checks if the signature of the passed [TxEnvelope] is protected. +#[allow(unused)] +pub const fn is_protected_v(tx: &TxEnvelope) -> bool { + match tx { + TxEnvelope::Legacy(tx) => { + let v = tx.signature().v().to_u64(); + if 64 - v.leading_zeros() <= 8 { + return v != 27 && v != 28 && v != 1 && v != 0; + } + // anything not 27 or 28 is considered protected + true + } + _ => true, + } +} + #[cfg(test)] mod tests { use super::*; + use alloy_consensus::{ + Signed, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip7702, TxLegacy, + }; + use alloy_primitives::{b256, Signature}; use alloy_sol_types::{sol, SolCall}; use revm::{ db::BenchmarkDB, @@ -237,6 +320,63 @@ mod tests { use rstest::rstest; + #[test] + fn test_convert_v_to_y_parity() { + assert_eq!(convert_v_to_y_parity(27, TxType::Legacy), Ok(false)); + assert_eq!(convert_v_to_y_parity(28, TxType::Legacy), Ok(true)); + assert_eq!(convert_v_to_y_parity(36, TxType::Legacy), Ok(true)); + assert_eq!(convert_v_to_y_parity(37, TxType::Legacy), Ok(false)); + assert_eq!(convert_v_to_y_parity(1, TxType::Eip2930), Ok(true)); + assert_eq!(convert_v_to_y_parity(1, TxType::Eip1559), Ok(true)); + assert_eq!( + convert_v_to_y_parity(1, TxType::Eip4844), + Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionType)) + ); + assert_eq!( + convert_v_to_y_parity(0, TxType::Eip7702), + Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionType)) + ); + } + + #[test] + fn test_is_protected_v() { + let sig = Signature::test_signature(); + assert!(!is_protected_v(&TxEnvelope::Legacy(Signed::new_unchecked( + TxLegacy::default(), + sig, + Default::default(), + )))); + let r = b256!("840cfc572845f5786e702984c2a582528cad4b49b2a10b9db1be7fca90058565"); + let s = b256!("25e7109ceb98168d95b09b18bbf6b685130e0562f233877d492b94eee0c5b6d1"); + let v = 27; + let valid_sig = Signature::from_scalars_and_parity(r, s, v).unwrap(); + assert!(!is_protected_v(&TxEnvelope::Legacy(Signed::new_unchecked( + TxLegacy::default(), + valid_sig, + Default::default(), + )))); + assert!(is_protected_v(&TxEnvelope::Eip2930(Signed::new_unchecked( + TxEip2930::default(), + sig, + Default::default(), + )))); + assert!(is_protected_v(&TxEnvelope::Eip1559(Signed::new_unchecked( + TxEip1559::default(), + sig, + Default::default(), + )))); + assert!(is_protected_v(&TxEnvelope::Eip4844(Signed::new_unchecked( + TxEip4844Variant::TxEip4844(TxEip4844::default()), + sig, + Default::default(), + )))); + assert!(is_protected_v(&TxEnvelope::Eip7702(Signed::new_unchecked( + TxEip7702::default(), + sig, + Default::default(), + )))); + } + #[rstest] #[case::empty(&[], 0)] #[case::thousand_zeros(&[0; 1000], 21)] From 3e9bba124469e4e030b2db0c29676d1fb618f121 Mon Sep 17 00:00:00 2001 From: refcell Date: Mon, 28 Oct 2024 08:55:23 -0400 Subject: [PATCH 2/6] fix: nits --- crates/protocol/src/lib.rs | 4 ++-- crates/protocol/src/utils.rs | 41 +----------------------------------- 2 files changed, 3 insertions(+), 42 deletions(-) diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs index 6dbe8932..015dee85 100644 --- a/crates/protocol/src/lib.rs +++ b/crates/protocol/src/lib.rs @@ -31,8 +31,8 @@ pub use iter::FrameIter; mod utils; pub use utils::{ - convert_v_to_y_parity, is_protected_v, read_tx_data, starts_with_2718_deposit, - to_system_config, OpBlockConversionError, + is_protected_v, read_tx_data, starts_with_2718_deposit, to_system_config, + OpBlockConversionError, }; mod channel; diff --git a/crates/protocol/src/utils.rs b/crates/protocol/src/utils.rs index 62b66580..003e7dd3 100644 --- a/crates/protocol/src/utils.rs +++ b/crates/protocol/src/utils.rs @@ -232,7 +232,6 @@ fn u24(input: &[u8], idx: u32) -> u32 { } /// Reads transaction data from a reader. -#[allow(unused)] pub fn read_tx_data(r: &mut &[u8]) -> Result<(Vec, TxType), SpanBatchError> { let mut tx_data = Vec::new(); let first_byte = @@ -269,26 +268,7 @@ pub fn read_tx_data(r: &mut &[u8]) -> Result<(Vec, TxType), SpanBatchError> )) } -/// Converts a `v` value to a y parity bit, from the transaaction type. -#[allow(unused)] -pub const fn convert_v_to_y_parity(v: u64, tx_type: TxType) -> Result { - match tx_type { - TxType::Legacy => { - if v != 27 && v != 28 { - // EIP-155: v = 2 * chain_id + 35 + yParity - Ok((v - 35) & 1 == 1) - } else { - // Unprotected legacy txs must have v = 27 or 28 - Ok(v - 27 == 1) - } - } - TxType::Eip2930 | TxType::Eip1559 => Ok(v == 1), - _ => Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionType)), - } -} - /// Checks if the signature of the passed [TxEnvelope] is protected. -#[allow(unused)] pub const fn is_protected_v(tx: &TxEnvelope) -> bool { match tx { TxEnvelope::Legacy(tx) => { @@ -316,27 +296,8 @@ mod tests { primitives::{address, bytes, Bytecode, Bytes, TxKind, U256}, Evm, }; - use std::vec::Vec; - use rstest::rstest; - - #[test] - fn test_convert_v_to_y_parity() { - assert_eq!(convert_v_to_y_parity(27, TxType::Legacy), Ok(false)); - assert_eq!(convert_v_to_y_parity(28, TxType::Legacy), Ok(true)); - assert_eq!(convert_v_to_y_parity(36, TxType::Legacy), Ok(true)); - assert_eq!(convert_v_to_y_parity(37, TxType::Legacy), Ok(false)); - assert_eq!(convert_v_to_y_parity(1, TxType::Eip2930), Ok(true)); - assert_eq!(convert_v_to_y_parity(1, TxType::Eip1559), Ok(true)); - assert_eq!( - convert_v_to_y_parity(1, TxType::Eip4844), - Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionType)) - ); - assert_eq!( - convert_v_to_y_parity(0, TxType::Eip7702), - Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionType)) - ); - } + use std::vec::Vec; #[test] fn test_is_protected_v() { From c62c64a518618baed5a07459073cf1c574f84d12 Mon Sep 17 00:00:00 2001 From: refcell Date: Sun, 27 Oct 2024 20:05:28 -0400 Subject: [PATCH 3/6] feat(protocol): span batch element type --- crates/protocol/Cargo.toml | 1 + crates/protocol/src/batch/element.rs | 56 ++++++++++++++++++++++++++++ crates/protocol/src/batch/mod.rs | 3 ++ crates/protocol/src/lib.rs | 4 +- 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 crates/protocol/src/batch/element.rs diff --git a/crates/protocol/Cargo.toml b/crates/protocol/Cargo.toml index 1538e192..738fc612 100644 --- a/crates/protocol/Cargo.toml +++ b/crates/protocol/Cargo.toml @@ -37,6 +37,7 @@ serde = { workspace = true, optional = true } alloy-serde = { workspace = true, optional = true } [dev-dependencies] +proptest.workspace = true arbitrary = { workspace = true, features = ["derive"] } rand.workspace = true serde_json.workspace = true diff --git a/crates/protocol/src/batch/element.rs b/crates/protocol/src/batch/element.rs new file mode 100644 index 00000000..c17d4e1e --- /dev/null +++ b/crates/protocol/src/batch/element.rs @@ -0,0 +1,56 @@ +//! Span Batch Element + +use crate::SingleBatch; +use alloc::vec::Vec; +use alloy_primitives::Bytes; + +/// MAX_SPAN_BATCH_ELEMENTS is the maximum number of blocks, transactions in total, +/// or transaction per block allowed in a span batch. +pub const MAX_SPAN_BATCH_ELEMENTS: u64 = 10_000_000; + +/// A single batch element is similar to the [SingleBatch] type +/// but does not contain the parent hash and epoch hash since spans +/// do not contain this data for every block in the span. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct SpanBatchElement { + /// The epoch number of the L1 block + pub epoch_num: u64, + /// The timestamp of the L2 block + pub timestamp: u64, + /// The transactions in the L2 block + pub transactions: Vec, +} + +impl From for SpanBatchElement { + fn from(batch: SingleBatch) -> Self { + Self { + epoch_num: batch.epoch_num, + timestamp: batch.timestamp, + transactions: batch.transactions, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use proptest::{collection::vec, prelude::any, proptest}; + + proptest! { + #[test] + fn test_span_batch_element_from_single_batch(epoch_num in 0u64..u64::MAX, timestamp in 0u64..u64::MAX, transactions in vec(any::(), 0..100)) { + let single_batch = SingleBatch { + epoch_num, + timestamp, + transactions: transactions.clone(), + ..Default::default() + }; + + let span_batch_element: SpanBatchElement = single_batch.into(); + + assert_eq!(span_batch_element.epoch_num, epoch_num); + assert_eq!(span_batch_element.timestamp, timestamp); + assert_eq!(span_batch_element.transactions, transactions); + } + } +} diff --git a/crates/protocol/src/batch/mod.rs b/crates/protocol/src/batch/mod.rs index f3487f8c..1d49116e 100644 --- a/crates/protocol/src/batch/mod.rs +++ b/crates/protocol/src/batch/mod.rs @@ -6,6 +6,9 @@ pub use r#type::*; mod errors; pub use errors::{SpanBatchError, SpanDecodingError}; +mod element; +pub use element::{SpanBatchElement, MAX_SPAN_BATCH_ELEMENTS}; + mod validity; pub use validity::BatchValidity; diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs index 015dee85..31986611 100644 --- a/crates/protocol/src/lib.rs +++ b/crates/protocol/src/lib.rs @@ -11,8 +11,8 @@ extern crate alloc; mod batch; pub use batch::{ - BatchType, BatchValidationProvider, BatchValidity, SingleBatch, SpanBatchError, - SpanDecodingError, SINGLE_BATCH_TYPE, SPAN_BATCH_TYPE, + BatchType, BatchValidationProvider, BatchValidity, SingleBatch, SpanBatchElement, + SpanBatchError, SpanDecodingError, MAX_SPAN_BATCH_ELEMENTS, SINGLE_BATCH_TYPE, SPAN_BATCH_TYPE, }; mod block; From c711ee73abc8717d2d54e0ce609c12815ac353e9 Mon Sep 17 00:00:00 2001 From: refcell Date: Sun, 27 Oct 2024 20:05:28 -0400 Subject: [PATCH 4/6] feat(protocol): span batch bits --- crates/protocol/src/batch/bits.rs | 224 ++++++++++++++++++++++++++++++ crates/protocol/src/batch/mod.rs | 3 + crates/protocol/src/lib.rs | 5 +- 3 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 crates/protocol/src/batch/bits.rs diff --git a/crates/protocol/src/batch/bits.rs b/crates/protocol/src/batch/bits.rs new file mode 100644 index 00000000..a82b937f --- /dev/null +++ b/crates/protocol/src/batch/bits.rs @@ -0,0 +1,224 @@ +//! Module for working with span batch bits. + +use crate::SpanBatchError; +use alloc::{vec, vec::Vec}; +use alloy_rlp::Buf; +use core::cmp::Ordering; + +/// Type for span batch bits. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct SpanBatchBits(Vec); + +impl AsRef<[u8]> for SpanBatchBits { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl SpanBatchBits { + /// Decodes a standard span-batch bitlist from a reader. + /// The bitlist is encoded as big-endian integer, left-padded with zeroes to a multiple of 8 + /// bits. The encoded bitlist cannot be longer than `bit_length`. + pub fn decode(b: &mut &[u8], bit_length: usize) -> Result { + let buffer_len = bit_length / 8 + if bit_length % 8 != 0 { 1 } else { 0 }; + let bits = if b.len() < buffer_len { + let mut bits = vec![0; buffer_len]; + bits[..b.len()].copy_from_slice(b); + b.advance(b.len()); + bits + } else { + let v = b[..buffer_len].to_vec(); + b.advance(buffer_len); + v + }; + let sb_bits = Self(bits); + + if sb_bits.bit_len() > bit_length { + return Err(SpanBatchError::BitfieldTooLong); + } + + Ok(sb_bits) + } + + /// Encodes a standard span-batch bitlist. + /// The bitlist is encoded as big-endian integer, left-padded with zeroes to a multiple of 8 + /// bits. The encoded bitlist cannot be longer than `bit_length` + pub fn encode(w: &mut Vec, bit_length: usize, bits: &Self) -> Result<(), SpanBatchError> { + if bits.bit_len() > bit_length { + return Err(SpanBatchError::BitfieldTooLong); + } + + // Round up, ensure enough bytes when number of bits is not a multiple of 8. + // Alternative of (L+7)/8 is not overflow-safe. + let buf_len = bit_length / 8 + if bit_length % 8 != 0 { 1 } else { 0 }; + let mut buf = vec![0; buf_len]; + buf[buf_len - bits.0.len()..].copy_from_slice(bits.as_ref()); + w.extend_from_slice(&buf); + Ok(()) + } + + /// Get a bit from the [SpanBatchBits] bitlist. + pub fn get_bit(&self, index: usize) -> Option { + let byte_index = index / 8; + let bit_index = index % 8; + + // Check if the byte index is within the bounds of the bitlist + if byte_index < self.0.len() { + // Retrieve the specific byte that contains the bit we're interested in + let byte = self.0[self.0.len() - byte_index - 1]; + + // Shift the bits of the byte to the right, based on the bit index, and + // mask it with 1 to isolate the bit we're interested in. + // If the result is not zero, the bit is set to 1, otherwise it's 0. + Some(if byte & (1 << bit_index) != 0 { 1 } else { 0 }) + } else { + // Return None if the index is out of bounds + None + } + } + + /// Sets a bit in the [SpanBatchBits] bitlist. + pub fn set_bit(&mut self, index: usize, value: bool) { + let byte_index = index / 8; + let bit_index = index % 8; + + // Ensure the vector is large enough to contain the bit at 'index'. + // If not, resize the vector, filling with 0s. + if byte_index >= self.0.len() { + Self::resize_from_right(&mut self.0, byte_index + 1); + } + + // Retrieve the specific byte to modify + let len = self.0.len(); + let byte = &mut self.0[len - byte_index - 1]; + + if value { + // Set the bit to 1 + *byte |= 1 << bit_index; + } else { + // Set the bit to 0 + *byte &= !(1 << bit_index); + } + } + + /// Calculates the bit length of the [SpanBatchBits] bitfield. + pub fn bit_len(&self) -> usize { + // Iterate over the bytes from left to right to find the first non-zero byte + for (i, &byte) in self.0.iter().enumerate() { + if byte != 0 { + // Calculate the index of the most significant bit in the byte + let msb_index = 7 - byte.leading_zeros() as usize; // 0-based index + + // Calculate the total bit length + let total_bit_length = msb_index + 1 + ((self.0.len() - i - 1) * 8); + return total_bit_length; + } + } + + // If all bytes are zero, the bitlist is considered to have a length of 0 + 0 + } + + /// Resizes an array from the right. Useful for big-endian zero extension. + fn resize_from_right(vec: &mut Vec, new_size: usize) { + let current_size = vec.len(); + match new_size.cmp(¤t_size) { + Ordering::Less => { + // Remove elements from the beginning. + let remove_count = current_size - new_size; + vec.drain(0..remove_count); + } + Ordering::Greater => { + // Calculate how many new elements to add. + let additional = new_size - current_size; + // Prepend new elements with default values. + let mut prepend_elements = vec![T::default(); additional]; + prepend_elements.append(vec); + *vec = prepend_elements; + } + Ordering::Equal => { /* If new_size == current_size, do nothing. */ } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use proptest::{collection::vec, prelude::any, proptest}; + + proptest! { + #[test] + fn test_encode_decode_roundtrip_span_bitlist(vec in vec(any::(), 0..5096)) { + let bits = SpanBatchBits(vec); + assert_eq!(SpanBatchBits::decode(&mut bits.as_ref(), bits.0.len() * 8).unwrap(), bits); + let mut encoded = Vec::new(); + SpanBatchBits::encode(&mut encoded, bits.0.len() * 8, &bits).unwrap(); + assert_eq!(encoded, bits.0); + } + + #[test] + fn test_span_bitlist_bitlen(index in 0usize..65536) { + let mut bits = SpanBatchBits::default(); + bits.set_bit(index, true); + assert_eq!(bits.0.len(), (index / 8) + 1); + assert_eq!(bits.bit_len(), index + 1); + } + + #[test] + fn test_span_bitlist_bitlen_shrink(first_index in 8usize..65536) { + let second_index = first_index.clamp(0, first_index - 8); + let mut bits = SpanBatchBits::default(); + + // Set and clear first index. + bits.set_bit(first_index, true); + assert_eq!(bits.0.len(), (first_index / 8) + 1); + assert_eq!(bits.bit_len(), first_index + 1); + bits.set_bit(first_index, false); + assert_eq!(bits.0.len(), (first_index / 8) + 1); + assert_eq!(bits.bit_len(), 0); + + // Set second bit. Even though the array is larger, as it was originally allocated with more words, + // the bitlength should still be lowered as the higher-order words are 0'd out. + bits.set_bit(second_index, true); + assert_eq!(bits.0.len(), (first_index / 8) + 1); + assert_eq!(bits.bit_len(), second_index + 1); + } + } + + #[test] + fn bitlist_big_endian_zero_extended() { + let mut bits = SpanBatchBits::default(); + + bits.set_bit(1, true); + bits.set_bit(6, true); + bits.set_bit(8, true); + bits.set_bit(15, true); + assert_eq!(bits.0[0], 0b1000_0001); + assert_eq!(bits.0[1], 0b0100_0010); + assert_eq!(bits.0.len(), 2); + assert_eq!(bits.bit_len(), 16); + } + + #[test] + fn test_static_set_get_bits_span_bitlist() { + let mut bits = SpanBatchBits::default(); + assert!(bits.0.is_empty()); + + bits.set_bit(0, true); + bits.set_bit(1, true); + bits.set_bit(2, true); + bits.set_bit(4, true); + bits.set_bit(7, true); + assert_eq!(bits.0.len(), 1); + assert_eq!(bits.get_bit(0), Some(1)); + assert_eq!(bits.get_bit(1), Some(1)); + assert_eq!(bits.get_bit(2), Some(1)); + assert_eq!(bits.get_bit(3), Some(0)); + assert_eq!(bits.get_bit(4), Some(1)); + + bits.set_bit(17, true); + assert_eq!(bits.get_bit(17), Some(1)); + assert_eq!(bits.get_bit(32), None); + assert_eq!(bits.0.len(), 3); + } +} diff --git a/crates/protocol/src/batch/mod.rs b/crates/protocol/src/batch/mod.rs index 1d49116e..69573381 100644 --- a/crates/protocol/src/batch/mod.rs +++ b/crates/protocol/src/batch/mod.rs @@ -6,6 +6,9 @@ pub use r#type::*; mod errors; pub use errors::{SpanBatchError, SpanDecodingError}; +mod bits; +pub use bits::SpanBatchBits; + mod element; pub use element::{SpanBatchElement, MAX_SPAN_BATCH_ELEMENTS}; diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs index 31986611..ff8494ef 100644 --- a/crates/protocol/src/lib.rs +++ b/crates/protocol/src/lib.rs @@ -11,8 +11,9 @@ extern crate alloc; mod batch; pub use batch::{ - BatchType, BatchValidationProvider, BatchValidity, SingleBatch, SpanBatchElement, - SpanBatchError, SpanDecodingError, MAX_SPAN_BATCH_ELEMENTS, SINGLE_BATCH_TYPE, SPAN_BATCH_TYPE, + BatchType, BatchValidationProvider, BatchValidity, SingleBatch, SpanBatchBits, + SpanBatchElement, SpanBatchError, SpanDecodingError, MAX_SPAN_BATCH_ELEMENTS, + SINGLE_BATCH_TYPE, SPAN_BATCH_TYPE, }; mod block; From b38e647a115875f2a984732dfe79b34e3a9f12af Mon Sep 17 00:00:00 2001 From: refcell Date: Sun, 27 Oct 2024 20:05:28 -0400 Subject: [PATCH 5/6] feat(protocol): batch tx data --- crates/protocol/src/batch/mod.rs | 6 + crates/protocol/src/batch/tx_data/eip1559.rs | 85 ++++++++++++ crates/protocol/src/batch/tx_data/eip2930.rs | 78 +++++++++++ crates/protocol/src/batch/tx_data/legacy.rs | 73 +++++++++++ crates/protocol/src/batch/tx_data/mod.rs | 13 ++ crates/protocol/src/batch/tx_data/wrapper.rs | 131 +++++++++++++++++++ crates/protocol/src/lib.rs | 5 +- 7 files changed, 389 insertions(+), 2 deletions(-) create mode 100644 crates/protocol/src/batch/tx_data/eip1559.rs create mode 100644 crates/protocol/src/batch/tx_data/eip2930.rs create mode 100644 crates/protocol/src/batch/tx_data/legacy.rs create mode 100644 crates/protocol/src/batch/tx_data/mod.rs create mode 100644 crates/protocol/src/batch/tx_data/wrapper.rs diff --git a/crates/protocol/src/batch/mod.rs b/crates/protocol/src/batch/mod.rs index 69573381..62705bf5 100644 --- a/crates/protocol/src/batch/mod.rs +++ b/crates/protocol/src/batch/mod.rs @@ -18,5 +18,11 @@ pub use validity::BatchValidity; mod single; pub use single::SingleBatch; +mod tx_data; +pub use tx_data::{ + SpanBatchEip1559TransactionData, SpanBatchEip2930TransactionData, + SpanBatchLegacyTransactionData, SpanBatchTransactionData, +}; + mod traits; pub use traits::BatchValidationProvider; diff --git a/crates/protocol/src/batch/tx_data/eip1559.rs b/crates/protocol/src/batch/tx_data/eip1559.rs new file mode 100644 index 00000000..9bb6302d --- /dev/null +++ b/crates/protocol/src/batch/tx_data/eip1559.rs @@ -0,0 +1,85 @@ +//! This module contains the eip1559 transaction data type for a span batch. + +use crate::{SpanBatchError, SpanDecodingError}; +use alloy_consensus::{SignableTransaction, Signed, TxEip1559, TxEnvelope}; +use alloy_eips::eip2930::AccessList; +use alloy_primitives::{Address, Signature, TxKind, U256}; +use alloy_rlp::{Bytes, RlpDecodable, RlpEncodable}; + +/// The transaction data for an EIP-1559 transaction within a span batch. +#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] +pub struct SpanBatchEip1559TransactionData { + /// The ETH value of the transaction. + pub value: U256, + /// Maximum priority fee per gas. + pub max_priority_fee_per_gas: U256, + /// Maximum fee per gas. + pub max_fee_per_gas: U256, + /// Transaction calldata. + pub data: Bytes, + /// Access list, used to pre-warm storage slots through static declaration. + pub access_list: AccessList, +} + +impl SpanBatchEip1559TransactionData { + /// Converts [SpanBatchEip1559TransactionData] into a [TxEnvelope]. + pub fn to_enveloped_tx( + &self, + nonce: u64, + gas: u64, + to: Option
, + chain_id: u64, + signature: Signature, + ) -> Result { + let eip1559_tx = TxEip1559 { + chain_id, + nonce, + max_fee_per_gas: u128::from_be_bytes( + self.max_fee_per_gas.to_be_bytes::<32>()[16..].try_into().map_err(|_| { + SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData) + })?, + ), + max_priority_fee_per_gas: u128::from_be_bytes( + self.max_priority_fee_per_gas.to_be_bytes::<32>()[16..].try_into().map_err( + |_| SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData), + )?, + ), + gas_limit: gas, + to: to.map_or(TxKind::Create, TxKind::Call), + value: self.value, + input: self.data.clone().into(), + access_list: self.access_list.clone(), + }; + let signature_hash = eip1559_tx.signature_hash(); + let signed_eip1559_tx = Signed::new_unchecked(eip1559_tx, signature, signature_hash); + Ok(TxEnvelope::Eip1559(signed_eip1559_tx)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::SpanBatchTransactionData; + use alloc::vec::Vec; + use alloy_rlp::{Decodable, Encodable}; + + #[test] + fn encode_eip1559_tx_data_roundtrip() { + let variable_fee_tx = SpanBatchEip1559TransactionData { + value: U256::from(0xFF), + max_fee_per_gas: U256::from(0xEE), + max_priority_fee_per_gas: U256::from(0xDD), + data: Bytes::from(alloc::vec![0x01, 0x02, 0x03]), + access_list: AccessList::default(), + }; + let mut encoded_buf = Vec::new(); + SpanBatchTransactionData::Eip1559(variable_fee_tx.clone()).encode(&mut encoded_buf); + + let decoded = SpanBatchTransactionData::decode(&mut encoded_buf.as_slice()).unwrap(); + let SpanBatchTransactionData::Eip1559(variable_fee_decoded) = decoded else { + panic!("Expected SpanBatchEip1559TransactionData, got {:?}", decoded); + }; + + assert_eq!(variable_fee_tx, variable_fee_decoded); + } +} diff --git a/crates/protocol/src/batch/tx_data/eip2930.rs b/crates/protocol/src/batch/tx_data/eip2930.rs new file mode 100644 index 00000000..3cbad474 --- /dev/null +++ b/crates/protocol/src/batch/tx_data/eip2930.rs @@ -0,0 +1,78 @@ +//! This module contains the eip2930 transaction data type for a span batch. + +use crate::{SpanBatchError, SpanDecodingError}; +use alloy_consensus::{SignableTransaction, Signed, TxEip2930, TxEnvelope}; +use alloy_eips::eip2930::AccessList; +use alloy_primitives::{Address, Signature, TxKind, U256}; +use alloy_rlp::{Bytes, RlpDecodable, RlpEncodable}; + +/// The transaction data for an EIP-2930 transaction within a span batch. +#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] +pub struct SpanBatchEip2930TransactionData { + /// The ETH value of the transaction. + pub value: U256, + /// The gas price of the transaction. + pub gas_price: U256, + /// Transaction calldata. + pub data: Bytes, + /// Access list, used to pre-warm storage slots through static declaration. + pub access_list: AccessList, +} + +impl SpanBatchEip2930TransactionData { + /// Converts [SpanBatchEip2930TransactionData] into a [TxEnvelope]. + pub fn to_enveloped_tx( + &self, + nonce: u64, + gas: u64, + to: Option
, + chain_id: u64, + signature: Signature, + ) -> Result { + let access_list_tx = TxEip2930 { + chain_id, + nonce, + gas_price: u128::from_be_bytes( + self.gas_price.to_be_bytes::<32>()[16..].try_into().map_err(|_| { + SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData) + })?, + ), + gas_limit: gas, + to: to.map_or(TxKind::Create, TxKind::Call), + value: self.value, + input: self.data.clone().into(), + access_list: self.access_list.clone(), + }; + let signature_hash = access_list_tx.signature_hash(); + let signed_access_list_tx = + Signed::new_unchecked(access_list_tx, signature, signature_hash); + Ok(TxEnvelope::Eip2930(signed_access_list_tx)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::SpanBatchTransactionData; + use alloc::vec::Vec; + use alloy_rlp::{Decodable, Encodable}; + + #[test] + fn encode_eip2930_tx_data_roundtrip() { + let access_list_tx = SpanBatchEip2930TransactionData { + value: U256::from(0xFF), + gas_price: U256::from(0xEE), + data: Bytes::from(alloc::vec![0x01, 0x02, 0x03]), + access_list: AccessList::default(), + }; + let mut encoded_buf = Vec::new(); + SpanBatchTransactionData::Eip2930(access_list_tx.clone()).encode(&mut encoded_buf); + + let decoded = SpanBatchTransactionData::decode(&mut encoded_buf.as_slice()).unwrap(); + let SpanBatchTransactionData::Eip2930(access_list_decoded) = decoded else { + panic!("Expected SpanBatchEip2930TransactionData, got {:?}", decoded); + }; + + assert_eq!(access_list_tx, access_list_decoded); + } +} diff --git a/crates/protocol/src/batch/tx_data/legacy.rs b/crates/protocol/src/batch/tx_data/legacy.rs new file mode 100644 index 00000000..4bcb2c61 --- /dev/null +++ b/crates/protocol/src/batch/tx_data/legacy.rs @@ -0,0 +1,73 @@ +//! This module contains the legacy transaction data type for a span batch. + +use crate::{SpanBatchError, SpanDecodingError}; +use alloy_consensus::{SignableTransaction, Signed, TxEnvelope, TxLegacy}; +use alloy_primitives::{Address, Signature, TxKind, U256}; +use alloy_rlp::{Bytes, RlpDecodable, RlpEncodable}; + +/// The transaction data for a legacy transaction within a span batch. +#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] +pub struct SpanBatchLegacyTransactionData { + /// The ETH value of the transaction. + pub value: U256, + /// The gas price of the transaction. + pub gas_price: U256, + /// Transaction calldata. + pub data: Bytes, +} + +impl SpanBatchLegacyTransactionData { + /// Converts [SpanBatchLegacyTransactionData] into a [TxEnvelope]. + pub fn to_enveloped_tx( + &self, + nonce: u64, + gas: u64, + to: Option
, + chain_id: u64, + signature: Signature, + ) -> Result { + let legacy_tx = TxLegacy { + chain_id: Some(chain_id), + nonce, + gas_price: u128::from_be_bytes( + self.gas_price.to_be_bytes::<32>()[16..].try_into().map_err(|_| { + SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData) + })?, + ), + gas_limit: gas, + to: to.map_or(TxKind::Create, TxKind::Call), + value: self.value, + input: self.data.clone().into(), + }; + let signature_hash = legacy_tx.signature_hash(); + let signed_legacy_tx = Signed::new_unchecked(legacy_tx, signature, signature_hash); + Ok(TxEnvelope::Legacy(signed_legacy_tx)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::SpanBatchTransactionData; + use alloc::vec::Vec; + use alloy_rlp::{Decodable, Encodable as _}; + + #[test] + fn encode_legacy_tx_data_roundtrip() { + let legacy_tx = SpanBatchLegacyTransactionData { + value: U256::from(0xFF), + gas_price: U256::from(0xEE), + data: Bytes::from(alloc::vec![0x01, 0x02, 0x03]), + }; + + let mut encoded_buf = Vec::new(); + SpanBatchTransactionData::Legacy(legacy_tx.clone()).encode(&mut encoded_buf); + + let decoded = SpanBatchTransactionData::decode(&mut encoded_buf.as_slice()).unwrap(); + let SpanBatchTransactionData::Legacy(legacy_decoded) = decoded else { + panic!("Expected SpanBatchLegacyTransactionData, got {:?}", decoded); + }; + + assert_eq!(legacy_tx, legacy_decoded); + } +} diff --git a/crates/protocol/src/batch/tx_data/mod.rs b/crates/protocol/src/batch/tx_data/mod.rs new file mode 100644 index 00000000..06f13ad1 --- /dev/null +++ b/crates/protocol/src/batch/tx_data/mod.rs @@ -0,0 +1,13 @@ +//! Contains all the Span Batch Transaction Data types. + +mod wrapper; +pub use wrapper::SpanBatchTransactionData; + +mod legacy; +pub use legacy::SpanBatchLegacyTransactionData; + +mod eip1559; +pub use eip1559::SpanBatchEip1559TransactionData; + +mod eip2930; +pub use eip2930::SpanBatchEip2930TransactionData; diff --git a/crates/protocol/src/batch/tx_data/wrapper.rs b/crates/protocol/src/batch/tx_data/wrapper.rs new file mode 100644 index 00000000..27520c19 --- /dev/null +++ b/crates/protocol/src/batch/tx_data/wrapper.rs @@ -0,0 +1,131 @@ +//! This module contains the top level span batch transaction data type. + +use alloy_consensus::{Transaction, TxEnvelope, TxType}; +use alloy_primitives::{Address, Signature, U256}; +use alloy_rlp::{Bytes, Decodable, Encodable}; + +use crate::{ + SpanBatchEip1559TransactionData, SpanBatchEip2930TransactionData, SpanBatchError, + SpanBatchLegacyTransactionData, SpanDecodingError, +}; + +/// The typed transaction data for a transaction within a span batch. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SpanBatchTransactionData { + /// Legacy transaction data. + Legacy(SpanBatchLegacyTransactionData), + /// EIP-2930 transaction data. + Eip2930(SpanBatchEip2930TransactionData), + /// EIP-1559 transaction data. + Eip1559(SpanBatchEip1559TransactionData), +} + +impl Encodable for SpanBatchTransactionData { + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + match self { + Self::Legacy(data) => { + data.encode(out); + } + Self::Eip2930(data) => { + out.put_u8(TxType::Eip2930 as u8); + data.encode(out); + } + Self::Eip1559(data) => { + out.put_u8(TxType::Eip1559 as u8); + data.encode(out); + } + } + } +} + +impl Decodable for SpanBatchTransactionData { + fn decode(r: &mut &[u8]) -> Result { + if !r.is_empty() && r[0] > 0x7F { + // Legacy transaction + return Ok(Self::Legacy(SpanBatchLegacyTransactionData::decode(r)?)); + } + // Non-legacy transaction (EIP-2718 envelope encoding) + Self::decode_typed(r) + } +} + +impl TryFrom<&TxEnvelope> for SpanBatchTransactionData { + type Error = SpanBatchError; + + fn try_from(tx_envelope: &TxEnvelope) -> Result { + match tx_envelope { + TxEnvelope::Legacy(s) => { + let s = s.tx(); + Ok(Self::Legacy(SpanBatchLegacyTransactionData { + value: s.value, + gas_price: U256::from(s.gas_price), + data: Bytes::from(s.input().to_vec()), + })) + } + TxEnvelope::Eip2930(s) => { + let s = s.tx(); + Ok(Self::Eip2930(SpanBatchEip2930TransactionData { + value: s.value, + gas_price: U256::from(s.gas_price), + data: Bytes::from(s.input().to_vec()), + access_list: s.access_list.clone(), + })) + } + TxEnvelope::Eip1559(s) => { + let s = s.tx(); + Ok(Self::Eip1559(SpanBatchEip1559TransactionData { + value: s.value, + max_fee_per_gas: U256::from(s.max_fee_per_gas), + max_priority_fee_per_gas: U256::from(s.max_priority_fee_per_gas), + data: Bytes::from(s.input().to_vec()), + access_list: s.access_list.clone(), + })) + } + _ => Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionType)), + } + } +} + +impl SpanBatchTransactionData { + /// Returns the transaction type of the [SpanBatchTransactionData]. + pub const fn tx_type(&self) -> TxType { + match self { + Self::Legacy(_) => TxType::Legacy, + Self::Eip2930(_) => TxType::Eip2930, + Self::Eip1559(_) => TxType::Eip1559, + } + } + + /// Decodes a typed transaction into a [SpanBatchTransactionData] from a byte slice. + pub fn decode_typed(b: &[u8]) -> Result { + if b.len() <= 1 { + return Err(alloy_rlp::Error::Custom("Invalid transaction data")); + } + + match b[0].try_into().map_err(|_| alloy_rlp::Error::Custom("Invalid tx type"))? { + TxType::Eip2930 => { + Ok(Self::Eip2930(SpanBatchEip2930TransactionData::decode(&mut &b[1..])?)) + } + TxType::Eip1559 => { + Ok(Self::Eip1559(SpanBatchEip1559TransactionData::decode(&mut &b[1..])?)) + } + _ => Err(alloy_rlp::Error::Custom("Invalid transaction type")), + } + } + + /// Converts the [SpanBatchTransactionData] into a [TxEnvelope]. + pub fn to_enveloped_tx( + &self, + nonce: u64, + gas: u64, + to: Option
, + chain_id: u64, + signature: Signature, + ) -> Result { + match self { + Self::Legacy(data) => data.to_enveloped_tx(nonce, gas, to, chain_id, signature), + Self::Eip2930(data) => data.to_enveloped_tx(nonce, gas, to, chain_id, signature), + Self::Eip1559(data) => data.to_enveloped_tx(nonce, gas, to, chain_id, signature), + } + } +} diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs index ff8494ef..b9b55e75 100644 --- a/crates/protocol/src/lib.rs +++ b/crates/protocol/src/lib.rs @@ -12,8 +12,9 @@ extern crate alloc; mod batch; pub use batch::{ BatchType, BatchValidationProvider, BatchValidity, SingleBatch, SpanBatchBits, - SpanBatchElement, SpanBatchError, SpanDecodingError, MAX_SPAN_BATCH_ELEMENTS, - SINGLE_BATCH_TYPE, SPAN_BATCH_TYPE, + SpanBatchEip1559TransactionData, SpanBatchEip2930TransactionData, SpanBatchElement, + SpanBatchError, SpanBatchLegacyTransactionData, SpanBatchTransactionData, SpanDecodingError, + MAX_SPAN_BATCH_ELEMENTS, SINGLE_BATCH_TYPE, SPAN_BATCH_TYPE, }; mod block; From 1ccdba6c51a850009b5cf2f33c4c14018a53c4d4 Mon Sep 17 00:00:00 2001 From: refcell Date: Tue, 29 Oct 2024 11:41:37 -0400 Subject: [PATCH 6/6] optxenvelope --- crates/protocol/src/batch/tx_data/eip1559.rs | 9 +++++---- crates/protocol/src/batch/tx_data/eip2930.rs | 9 +++++---- crates/protocol/src/batch/tx_data/legacy.rs | 9 +++++---- crates/protocol/src/batch/tx_data/wrapper.rs | 5 +++-- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/crates/protocol/src/batch/tx_data/eip1559.rs b/crates/protocol/src/batch/tx_data/eip1559.rs index 9bb6302d..c8406a56 100644 --- a/crates/protocol/src/batch/tx_data/eip1559.rs +++ b/crates/protocol/src/batch/tx_data/eip1559.rs @@ -1,10 +1,11 @@ //! This module contains the eip1559 transaction data type for a span batch. use crate::{SpanBatchError, SpanDecodingError}; -use alloy_consensus::{SignableTransaction, Signed, TxEip1559, TxEnvelope}; +use alloy_consensus::{SignableTransaction, Signed, TxEip1559}; use alloy_eips::eip2930::AccessList; use alloy_primitives::{Address, Signature, TxKind, U256}; use alloy_rlp::{Bytes, RlpDecodable, RlpEncodable}; +use op_alloy_consensus::OpTxEnvelope; /// The transaction data for an EIP-1559 transaction within a span batch. #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] @@ -22,7 +23,7 @@ pub struct SpanBatchEip1559TransactionData { } impl SpanBatchEip1559TransactionData { - /// Converts [SpanBatchEip1559TransactionData] into a [TxEnvelope]. + /// Converts [SpanBatchEip1559TransactionData] into an [OpTxEnvelope]. pub fn to_enveloped_tx( &self, nonce: u64, @@ -30,7 +31,7 @@ impl SpanBatchEip1559TransactionData { to: Option
, chain_id: u64, signature: Signature, - ) -> Result { + ) -> Result { let eip1559_tx = TxEip1559 { chain_id, nonce, @@ -52,7 +53,7 @@ impl SpanBatchEip1559TransactionData { }; let signature_hash = eip1559_tx.signature_hash(); let signed_eip1559_tx = Signed::new_unchecked(eip1559_tx, signature, signature_hash); - Ok(TxEnvelope::Eip1559(signed_eip1559_tx)) + Ok(OpTxEnvelope::Eip1559(signed_eip1559_tx)) } } diff --git a/crates/protocol/src/batch/tx_data/eip2930.rs b/crates/protocol/src/batch/tx_data/eip2930.rs index 3cbad474..d829f333 100644 --- a/crates/protocol/src/batch/tx_data/eip2930.rs +++ b/crates/protocol/src/batch/tx_data/eip2930.rs @@ -1,10 +1,11 @@ //! This module contains the eip2930 transaction data type for a span batch. use crate::{SpanBatchError, SpanDecodingError}; -use alloy_consensus::{SignableTransaction, Signed, TxEip2930, TxEnvelope}; +use alloy_consensus::{SignableTransaction, Signed, TxEip2930}; use alloy_eips::eip2930::AccessList; use alloy_primitives::{Address, Signature, TxKind, U256}; use alloy_rlp::{Bytes, RlpDecodable, RlpEncodable}; +use op_alloy_consensus::OpTxEnvelope; /// The transaction data for an EIP-2930 transaction within a span batch. #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] @@ -20,7 +21,7 @@ pub struct SpanBatchEip2930TransactionData { } impl SpanBatchEip2930TransactionData { - /// Converts [SpanBatchEip2930TransactionData] into a [TxEnvelope]. + /// Converts [SpanBatchEip2930TransactionData] into a [OpTxEnvelope]. pub fn to_enveloped_tx( &self, nonce: u64, @@ -28,7 +29,7 @@ impl SpanBatchEip2930TransactionData { to: Option
, chain_id: u64, signature: Signature, - ) -> Result { + ) -> Result { let access_list_tx = TxEip2930 { chain_id, nonce, @@ -46,7 +47,7 @@ impl SpanBatchEip2930TransactionData { let signature_hash = access_list_tx.signature_hash(); let signed_access_list_tx = Signed::new_unchecked(access_list_tx, signature, signature_hash); - Ok(TxEnvelope::Eip2930(signed_access_list_tx)) + Ok(OpTxEnvelope::Eip2930(signed_access_list_tx)) } } diff --git a/crates/protocol/src/batch/tx_data/legacy.rs b/crates/protocol/src/batch/tx_data/legacy.rs index 4bcb2c61..67a09aca 100644 --- a/crates/protocol/src/batch/tx_data/legacy.rs +++ b/crates/protocol/src/batch/tx_data/legacy.rs @@ -1,9 +1,10 @@ //! This module contains the legacy transaction data type for a span batch. use crate::{SpanBatchError, SpanDecodingError}; -use alloy_consensus::{SignableTransaction, Signed, TxEnvelope, TxLegacy}; +use alloy_consensus::{SignableTransaction, Signed, TxLegacy}; use alloy_primitives::{Address, Signature, TxKind, U256}; use alloy_rlp::{Bytes, RlpDecodable, RlpEncodable}; +use op_alloy_consensus::OpTxEnvelope; /// The transaction data for a legacy transaction within a span batch. #[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] @@ -17,7 +18,7 @@ pub struct SpanBatchLegacyTransactionData { } impl SpanBatchLegacyTransactionData { - /// Converts [SpanBatchLegacyTransactionData] into a [TxEnvelope]. + /// Converts [SpanBatchLegacyTransactionData] into a [OpTxEnvelope]. pub fn to_enveloped_tx( &self, nonce: u64, @@ -25,7 +26,7 @@ impl SpanBatchLegacyTransactionData { to: Option
, chain_id: u64, signature: Signature, - ) -> Result { + ) -> Result { let legacy_tx = TxLegacy { chain_id: Some(chain_id), nonce, @@ -41,7 +42,7 @@ impl SpanBatchLegacyTransactionData { }; let signature_hash = legacy_tx.signature_hash(); let signed_legacy_tx = Signed::new_unchecked(legacy_tx, signature, signature_hash); - Ok(TxEnvelope::Legacy(signed_legacy_tx)) + Ok(OpTxEnvelope::Legacy(signed_legacy_tx)) } } diff --git a/crates/protocol/src/batch/tx_data/wrapper.rs b/crates/protocol/src/batch/tx_data/wrapper.rs index 27520c19..b4f41d8c 100644 --- a/crates/protocol/src/batch/tx_data/wrapper.rs +++ b/crates/protocol/src/batch/tx_data/wrapper.rs @@ -3,6 +3,7 @@ use alloy_consensus::{Transaction, TxEnvelope, TxType}; use alloy_primitives::{Address, Signature, U256}; use alloy_rlp::{Bytes, Decodable, Encodable}; +use op_alloy_consensus::OpTxEnvelope; use crate::{ SpanBatchEip1559TransactionData, SpanBatchEip2930TransactionData, SpanBatchError, @@ -113,7 +114,7 @@ impl SpanBatchTransactionData { } } - /// Converts the [SpanBatchTransactionData] into a [TxEnvelope]. + /// Converts the [SpanBatchTransactionData] into a [OpTxEnvelope]. pub fn to_enveloped_tx( &self, nonce: u64, @@ -121,7 +122,7 @@ impl SpanBatchTransactionData { to: Option
, chain_id: u64, signature: Signature, - ) -> Result { + ) -> Result { match self { Self::Legacy(data) => data.to_enveloped_tx(nonce, gas, to, chain_id, signature), Self::Eip2930(data) => data.to_enveloped_tx(nonce, gas, to, chain_id, signature),