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

feat(protocol): Batch TX Data #195

Merged
merged 7 commits into from
Oct 29, 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
6 changes: 6 additions & 0 deletions crates/protocol/src/batch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
86 changes: 86 additions & 0 deletions crates/protocol/src/batch/tx_data/eip1559.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//! This module contains the eip1559 transaction data type for a span batch.

use crate::{SpanBatchError, SpanDecodingError};
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)]
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 an [OpTxEnvelope].
pub fn to_enveloped_tx(
&self,
nonce: u64,
gas: u64,
to: Option<Address>,
chain_id: u64,
signature: Signature,
) -> Result<OpTxEnvelope, SpanBatchError> {
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(OpTxEnvelope::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);
}
}
79 changes: 79 additions & 0 deletions crates/protocol/src/batch/tx_data/eip2930.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//! This module contains the eip2930 transaction data type for a span batch.

use crate::{SpanBatchError, SpanDecodingError};
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)]
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 [OpTxEnvelope].
pub fn to_enveloped_tx(
&self,
nonce: u64,
gas: u64,
to: Option<Address>,
chain_id: u64,
signature: Signature,
) -> Result<OpTxEnvelope, SpanBatchError> {
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(OpTxEnvelope::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);
}
}
74 changes: 74 additions & 0 deletions crates/protocol/src/batch/tx_data/legacy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//! This module contains the legacy transaction data type for a span batch.

use crate::{SpanBatchError, SpanDecodingError};
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)]
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 [OpTxEnvelope].
pub fn to_enveloped_tx(
&self,
nonce: u64,
gas: u64,
to: Option<Address>,
chain_id: u64,
signature: Signature,
) -> Result<OpTxEnvelope, SpanBatchError> {
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(OpTxEnvelope::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);
}
}
13 changes: 13 additions & 0 deletions crates/protocol/src/batch/tx_data/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Loading
Loading