Skip to content

Commit

Permalink
chore(consensus): Move OpTxType and add tests (#282)
Browse files Browse the repository at this point in the history
### Description

Moves `OpTxType` from the `envelope` mod into it's own `tx_type` mod to
make it more digestible.

Also adds tests, including an `OpTxType::ALL` test that requires a
newly-added variant is added to the list.
  • Loading branch information
refcell authored Nov 18, 2024
1 parent ed40d3b commit f1af8da
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 119 deletions.
121 changes: 3 additions & 118 deletions crates/consensus/src/transaction/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,125 +7,10 @@ use alloy_eips::{
eip2930::AccessList,
eip7702::SignedAuthorization,
};
use alloy_primitives::{Address, Bytes, TxKind, B256, U256, U64};
use alloy_rlp::{BufMut, Decodable, Encodable};
use derive_more::Display;
use alloy_primitives::{Address, Bytes, TxKind, B256, U256};
use alloy_rlp::{Decodable, Encodable};

use crate::TxDeposit;

/// Identifier for an Optimism deposit transaction
pub const DEPOSIT_TX_TYPE_ID: u8 = 126; // 0x7E

/// Optimism `TransactionType` flags as specified in EIPs [2718], [1559], and
/// [2930], as well as the [deposit transaction spec][deposit-spec]
///
/// [2718]: https://eips.ethereum.org/EIPS/eip-2718
/// [1559]: https://eips.ethereum.org/EIPS/eip-1559
/// [2930]: https://eips.ethereum.org/EIPS/eip-2930
/// [4844]: https://eips.ethereum.org/EIPS/eip-4844
/// [deposit-spec]: https://specs.optimism.io/protocol/deposits.html
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Display)]
pub enum OpTxType {
/// Legacy transaction type.
#[display("legacy")]
Legacy = 0,
/// EIP-2930 transaction type.
#[display("eip2930")]
Eip2930 = 1,
/// EIP-1559 transaction type.
#[display("eip1559")]
Eip1559 = 2,
/// EIP-7702 transaction type.
#[display("eip7702")]
Eip7702 = 4,
/// Optimism Deposit transaction type.
#[display("deposit")]
Deposit = 126,
}

impl OpTxType {
/// List of all variants.
pub const ALL: [Self; 5] =
[Self::Legacy, Self::Eip2930, Self::Eip1559, Self::Eip7702, Self::Deposit];
}

#[cfg(any(test, feature = "arbitrary"))]
impl arbitrary::Arbitrary<'_> for OpTxType {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let i = u.choose_index(Self::ALL.len())?;
Ok(Self::ALL[i])
}
}

impl From<OpTxType> for u8 {
fn from(v: OpTxType) -> Self {
v as Self
}
}

impl TryFrom<u8> for OpTxType {
type Error = Eip2718Error;

fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(match value {
0 => Self::Legacy,
1 => Self::Eip2930,
2 => Self::Eip1559,
4 => Self::Eip7702,
126 => Self::Deposit,
_ => return Err(Eip2718Error::UnexpectedType(value)),
})
}
}

impl TryFrom<u64> for OpTxType {
type Error = &'static str;

fn try_from(value: u64) -> Result<Self, Self::Error> {
let err = || "invalid tx type";
let value: u8 = value.try_into().map_err(|_| err())?;
Self::try_from(value).map_err(|_| err())
}
}

impl TryFrom<U64> for OpTxType {
type Error = &'static str;

fn try_from(value: U64) -> Result<Self, Self::Error> {
value.to::<u64>().try_into()
}
}

impl PartialEq<u8> for OpTxType {
fn eq(&self, other: &u8) -> bool {
(*self as u8) == *other
}
}

impl PartialEq<OpTxType> for u8 {
fn eq(&self, other: &OpTxType) -> bool {
*self == *other as Self
}
}

impl Encodable for OpTxType {
fn encode(&self, out: &mut dyn BufMut) {
(*self as u8).encode(out);
}

fn length(&self) -> usize {
1
}
}

impl Decodable for OpTxType {
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
let ty = u8::decode(buf)?;

Self::try_from(ty).map_err(|_| alloy_rlp::Error::Custom("invalid transaction type"))
}
}
use crate::{OpTxType, TxDeposit};

/// The Ethereum [EIP-2718] Transaction Envelope, modified for OP Stack chains.
///
Expand Down
5 changes: 4 additions & 1 deletion crates/consensus/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
mod deposit;
pub use deposit::TxDeposit;

mod tx_type;
pub use tx_type::{OpTxType, DEPOSIT_TX_TYPE_ID};

mod envelope;
pub use envelope::{OpTxEnvelope, OpTxType, DEPOSIT_TX_TYPE_ID};
pub use envelope::OpTxEnvelope;

mod typed;
pub use typed::OpTypedTransaction;
Expand Down
149 changes: 149 additions & 0 deletions crates/consensus/src/transaction/tx_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
//! Contains the transaction type identifier for Optimism.

use alloy_eips::eip2718::Eip2718Error;
use alloy_primitives::U64;
use alloy_rlp::{BufMut, Decodable, Encodable};
use derive_more::Display;

/// Identifier for an Optimism deposit transaction
pub const DEPOSIT_TX_TYPE_ID: u8 = 126; // 0x7E

/// Optimism `TransactionType` flags as specified in EIPs [2718], [1559], and
/// [2930], as well as the [deposit transaction spec][deposit-spec]
///
/// [2718]: https://eips.ethereum.org/EIPS/eip-2718
/// [1559]: https://eips.ethereum.org/EIPS/eip-1559
/// [2930]: https://eips.ethereum.org/EIPS/eip-2930
/// [4844]: https://eips.ethereum.org/EIPS/eip-4844
/// [deposit-spec]: https://specs.optimism.io/protocol/deposits.html
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Display)]
pub enum OpTxType {
/// Legacy transaction type.
#[display("legacy")]
Legacy = 0,
/// EIP-2930 transaction type.
#[display("eip2930")]
Eip2930 = 1,
/// EIP-1559 transaction type.
#[display("eip1559")]
Eip1559 = 2,
/// EIP-7702 transaction type.
#[display("eip7702")]
Eip7702 = 4,
/// Optimism Deposit transaction type.
#[display("deposit")]
Deposit = 126,
}

impl OpTxType {
/// List of all variants.
pub const ALL: [Self; 5] =
[Self::Legacy, Self::Eip2930, Self::Eip1559, Self::Eip7702, Self::Deposit];
}

#[cfg(any(test, feature = "arbitrary"))]
impl arbitrary::Arbitrary<'_> for OpTxType {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let i = u.choose_index(Self::ALL.len())?;
Ok(Self::ALL[i])
}
}

impl From<OpTxType> for u8 {
fn from(v: OpTxType) -> Self {
v as Self
}
}

impl TryFrom<u8> for OpTxType {
type Error = Eip2718Error;

fn try_from(value: u8) -> Result<Self, Self::Error> {
Ok(match value {
0 => Self::Legacy,
1 => Self::Eip2930,
2 => Self::Eip1559,
4 => Self::Eip7702,
126 => Self::Deposit,
_ => return Err(Eip2718Error::UnexpectedType(value)),
})
}
}

impl TryFrom<u64> for OpTxType {
type Error = &'static str;

fn try_from(value: u64) -> Result<Self, Self::Error> {
let err = || "invalid tx type";
let value: u8 = value.try_into().map_err(|_| err())?;
Self::try_from(value).map_err(|_| err())
}
}

impl TryFrom<U64> for OpTxType {
type Error = &'static str;

fn try_from(value: U64) -> Result<Self, Self::Error> {
value.to::<u64>().try_into()
}
}

impl PartialEq<u8> for OpTxType {
fn eq(&self, other: &u8) -> bool {
(*self as u8) == *other
}
}

impl PartialEq<OpTxType> for u8 {
fn eq(&self, other: &OpTxType) -> bool {
*self == *other as Self
}
}

impl Encodable for OpTxType {
fn encode(&self, out: &mut dyn BufMut) {
(*self as u8).encode(out);
}

fn length(&self) -> usize {
1
}
}

impl Decodable for OpTxType {
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
let ty = u8::decode(buf)?;

Self::try_from(ty).map_err(|_| alloy_rlp::Error::Custom("invalid transaction type"))
}
}

#[cfg(test)]
mod tests {
use super::*;
use alloc::{vec, vec::Vec};

#[test]
fn test_all_tx_types() {
assert_eq!(OpTxType::ALL.len(), 5);
let all = vec![
OpTxType::Legacy,
OpTxType::Eip2930,
OpTxType::Eip1559,
OpTxType::Eip7702,
OpTxType::Deposit,
];
assert_eq!(OpTxType::ALL.to_vec(), all);
}

#[test]
fn tx_type_roundtrip() {
for &tx_type in &OpTxType::ALL {
let mut buf = Vec::new();
tx_type.encode(&mut buf);
let decoded = OpTxType::decode(&mut &buf[..]).unwrap();
assert_eq!(tx_type, decoded);
}
}
}

0 comments on commit f1af8da

Please sign in to comment.