From 85fcd9d9193569ad199c583d5a3e29c8aa4ef6b0 Mon Sep 17 00:00:00 2001 From: refcell Date: Sun, 27 Oct 2024 19:45:56 -0400 Subject: [PATCH] feat: add batch utilities --- crates/protocol/src/lib.rs | 5 ++- crates/protocol/src/utils.rs | 81 +++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs index 29a90e6ba..6dbe8932a 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 e415e1ad0..d84569430 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,6 +231,78 @@ 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::*;