feat: use reth-ethereum-primitives (#13830)

This commit is contained in:
Arsenii Kulikov
2025-01-17 04:22:21 +04:00
committed by GitHub
parent 7e972ea23d
commit 8efe441cc0
67 changed files with 941 additions and 3025 deletions

View File

@ -13,48 +13,24 @@ workspace = true
[dependencies]
# reth
reth-ethereum-primitives.workspace = true
reth-primitives-traits = { workspace = true, features = ["serde"] }
reth-ethereum-forks.workspace = true
reth-static-file-types.workspace = true
revm-primitives = { workspace = true, features = ["serde"] }
reth-codecs = { workspace = true, optional = true }
reth-zstd-compressors = { workspace = true, optional = true }
# ethereum
alloy-consensus.workspace = true
alloy-network = { workspace = true, optional = true }
alloy-primitives = { workspace = true, features = ["rand", "rlp"] }
alloy-rlp = { workspace = true, features = ["arrayvec"] }
alloy-rpc-types = { workspace = true, optional = true }
alloy-serde = { workspace = true, optional = true }
alloy-eips = { workspace = true, features = ["serde"] }
alloy-trie = { workspace = true, features = ["serde"] }
# optimism
op-alloy-rpc-types = { workspace = true, optional = true }
op-alloy-consensus = { workspace = true, features = [
"arbitrary",
"serde",
], optional = true }
# for eip-4844
c-kzg = { workspace = true, features = ["serde"], optional = true }
# crypto
secp256k1 = { workspace = true, features = [
"global-context",
"recovery",
"rand",
], optional = true }
# misc
bytes.workspace = true
derive_more.workspace = true
modular-bitfield = { workspace = true, optional = true }
once_cell.workspace = true
rand = { workspace = true, optional = true }
serde.workspace = true
serde_with = { workspace = true, optional = true }
# arbitrary utils
arbitrary = { workspace = true, features = ["derive"], optional = true }
@ -62,30 +38,20 @@ arbitrary = { workspace = true, features = ["derive"], optional = true }
[dev-dependencies]
# eth
reth-chainspec = { workspace = true, features = ["arbitrary"] }
reth-codecs = { workspace = true, features = ["test-utils"] }
reth-primitives-traits = { workspace = true, features = ["arbitrary", "test-utils"] }
reth-testing-utils.workspace = true
reth-trie-common = { workspace = true, features = ["arbitrary"] }
revm-primitives = { workspace = true, features = ["arbitrary"] }
alloy-rlp.workspace = true
alloy-eips = { workspace = true, features = ["arbitrary"] }
alloy-genesis.workspace = true
arbitrary = { workspace = true, features = ["derive"] }
secp256k1 = { workspace = true, features = [
"global-context",
"recovery",
"rand",
] }
assert_matches.workspace = true
bincode.workspace = true
proptest-arbitrary-interop.workspace = true
proptest.workspace = true
rand.workspace = true
serde_json.workspace = true
test-fuzz.workspace = true
rstest.workspace = true
reth-codecs.workspace = true
criterion.workspace = true
pprof = { workspace = true, features = [
@ -102,50 +68,36 @@ std = [
"alloy-eips/std",
"alloy-genesis/std",
"alloy-primitives/std",
"alloy-serde?/std",
"once_cell/std",
"revm-primitives/std",
"serde/std",
"alloy-trie/std",
"serde_with?/std",
"alloy-rlp/std",
"reth-ethereum-forks/std",
"bytes/std",
"derive_more/std",
"reth-zstd-compressors?/std",
"secp256k1?/std",
"reth-trie-common/std",
"op-alloy-consensus?/std",
"op-alloy-rpc-types?/std",
"serde_json/std",
"reth-chainspec/std"
"reth-chainspec/std",
"reth-ethereum-primitives/std",
"alloy-rlp/std"
]
reth-codec = [
"dep:reth-codecs",
"dep:reth-zstd-compressors",
"dep:modular-bitfield", "std",
"std",
"reth-primitives-traits/reth-codec",
"reth-ethereum-primitives/reth-codec"
]
asm-keccak = ["alloy-primitives/asm-keccak", "revm-primitives/asm-keccak"]
asm-keccak = ["alloy-primitives/asm-keccak"]
arbitrary = [
"dep:arbitrary",
"alloy-eips/arbitrary",
"rand",
"reth-codec",
"reth-ethereum-forks/arbitrary",
"reth-primitives-traits/arbitrary",
"revm-primitives/arbitrary",
"reth-chainspec/arbitrary",
"alloy-consensus/arbitrary",
"alloy-primitives/arbitrary",
"alloy-rpc-types?/arbitrary",
"alloy-serde?/arbitrary",
"op-alloy-consensus?/arbitrary",
"op-alloy-rpc-types?/arbitrary",
"reth-codecs?/arbitrary",
"alloy-trie/arbitrary",
"reth-trie-common/arbitrary",
"dep:secp256k1"
"reth-ethereum-primitives/arbitrary",
"reth-codecs/arbitrary"
]
secp256k1 = [
"reth-primitives-traits/secp256k1",
@ -154,33 +106,23 @@ c-kzg = [
"dep:c-kzg",
"alloy-consensus/kzg",
"alloy-eips/kzg",
"revm-primitives/c-kzg",
]
optimism = [
"dep:op-alloy-consensus",
"reth-codecs?/op",
"revm-primitives/optimism",
]
alloy-compat = [
"dep:alloy-rpc-types",
"dep:alloy-serde",
"dep:op-alloy-rpc-types",
"dep:alloy-network",
"reth-ethereum-primitives/alloy-compat"
]
test-utils = [
"reth-primitives-traits/test-utils",
"reth-chainspec/test-utils",
"reth-codecs?/test-utils",
"reth-trie-common/test-utils",
"arbitrary",
"reth-codecs/test-utils"
]
serde-bincode-compat = [
"serde_with",
"alloy-eips/serde-bincode-compat",
"alloy-consensus/serde-bincode-compat",
"op-alloy-consensus?/serde-bincode-compat",
"reth-primitives-traits/serde-bincode-compat",
"reth-trie-common/serde-bincode-compat",
"reth-ethereum-primitives/serde-bincode-compat"
]
[[bench]]

View File

@ -1,189 +0,0 @@
//! Common conversions from alloy types.
use crate::{Transaction, TransactionSigned};
use alloc::string::ToString;
use alloy_consensus::TxEnvelope;
use alloy_network::{AnyRpcTransaction, AnyTxEnvelope};
use alloy_serde::WithOtherFields;
use op_alloy_rpc_types as _;
impl TryFrom<AnyRpcTransaction> for TransactionSigned {
type Error = alloy_rpc_types::ConversionError;
fn try_from(tx: AnyRpcTransaction) -> Result<Self, Self::Error> {
use alloy_rpc_types::ConversionError;
let WithOtherFields { inner: tx, other: _ } = tx;
#[allow(unreachable_patterns)]
let (transaction, signature, hash) = match tx.inner {
AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(tx)) => {
let (tx, signature, hash) = tx.into_parts();
(Transaction::Legacy(tx), signature, hash)
}
AnyTxEnvelope::Ethereum(TxEnvelope::Eip2930(tx)) => {
let (tx, signature, hash) = tx.into_parts();
(Transaction::Eip2930(tx), signature, hash)
}
AnyTxEnvelope::Ethereum(TxEnvelope::Eip1559(tx)) => {
let (tx, signature, hash) = tx.into_parts();
(Transaction::Eip1559(tx), signature, hash)
}
AnyTxEnvelope::Ethereum(TxEnvelope::Eip4844(tx)) => {
let (tx, signature, hash) = tx.into_parts();
(Transaction::Eip4844(tx.into()), signature, hash)
}
AnyTxEnvelope::Ethereum(TxEnvelope::Eip7702(tx)) => {
let (tx, signature, hash) = tx.into_parts();
(Transaction::Eip7702(tx), signature, hash)
}
#[cfg(feature = "optimism")]
AnyTxEnvelope::Unknown(alloy_network::UnknownTxEnvelope { hash, inner }) => {
use alloy_consensus::{Transaction as _, Typed2718};
if inner.ty() == crate::TxType::Deposit {
let fields: op_alloy_rpc_types::OpTransactionFields = inner
.fields
.clone()
.deserialize_into::<op_alloy_rpc_types::OpTransactionFields>()
.map_err(|e| ConversionError::Custom(e.to_string()))?;
(
Transaction::Deposit(op_alloy_consensus::TxDeposit {
source_hash: fields.source_hash.ok_or_else(|| {
ConversionError::Custom("MissingSourceHash".to_string())
})?,
from: tx.from,
to: revm_primitives::TxKind::from(inner.to()),
mint: fields.mint.filter(|n| *n != 0),
value: inner.value(),
gas_limit: inner.gas_limit(),
is_system_transaction: fields.is_system_tx.unwrap_or(false),
input: inner.input().clone(),
}),
op_alloy_consensus::TxDeposit::signature(),
hash,
)
} else {
return Err(ConversionError::Custom("unknown transaction type".to_string()))
}
}
_ => return Err(ConversionError::Custom("unknown transaction type".to_string())),
};
Ok(Self { transaction, signature, hash: hash.into() })
}
}
#[cfg(test)]
#[cfg(feature = "optimism")]
mod tests {
use super::*;
use alloy_primitives::{address, Address, B256, U256};
use revm_primitives::TxKind;
#[test]
fn optimism_deposit_tx_conversion_no_mint() {
let input = r#"{
"blockHash": "0xef664d656f841b5ad6a2b527b963f1eb48b97d7889d742f6cbff6950388e24cd",
"blockNumber": "0x73a78fd",
"depositReceiptVersion": "0x1",
"from": "0x36bde71c97b33cc4729cf772ae268934f7ab70b2",
"gas": "0xc27a8",
"gasPrice": "0x0",
"hash": "0x0bf1845c5d7a82ec92365d5027f7310793d53004f3c86aa80965c67bf7e7dc80",
"input":
"0xd764ad0b000100000000000000000000000000000000000000000000000000000001cf5400000000000000000000000099c9fc46f92e8a1c0dec1b1747d010903e884be100000000000000000000000042000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a12000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e40166a07a0000000000000000000000000994206dfe8de6ec6920ff4d779b0d950605fb53000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd52000000000000000000000000ca74f404e0c7bfa35b13b511097df966d5a65597000000000000000000000000ca74f404e0c7bfa35b13b511097df966d5a65597000000000000000000000000000000000000000000000216614199391dbba2ba00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
, "mint": "0x0",
"nonce": "0x74060",
"r": "0x0",
"s": "0x0",
"sourceHash": "0x074adb22f2e6ed9bdd31c52eefc1f050e5db56eb85056450bccd79a6649520b3",
"to": "0x4200000000000000000000000000000000000007",
"transactionIndex": "0x1",
"type": "0x7e",
"v": "0x0",
"value": "0x0"
}"#;
let alloy_tx: WithOtherFields<alloy_rpc_types::Transaction<AnyTxEnvelope>> =
serde_json::from_str(input).expect("failed to deserialize");
let TransactionSigned { transaction: reth_tx, .. } =
alloy_tx.try_into().expect("failed to convert");
if let Transaction::Deposit(deposit_tx) = reth_tx {
assert_eq!(
deposit_tx.source_hash,
"0x074adb22f2e6ed9bdd31c52eefc1f050e5db56eb85056450bccd79a6649520b3"
.parse::<B256>()
.unwrap()
);
assert_eq!(
deposit_tx.from,
"0x36bde71c97b33cc4729cf772ae268934f7ab70b2".parse::<Address>().unwrap()
);
assert_eq!(
deposit_tx.to,
TxKind::from(address!("4200000000000000000000000000000000000007"))
);
assert_eq!(deposit_tx.mint, None);
assert_eq!(deposit_tx.value, U256::ZERO);
assert_eq!(deposit_tx.gas_limit, 796584);
assert!(!deposit_tx.is_system_transaction);
} else {
panic!("Expected Deposit transaction");
}
}
#[test]
fn optimism_deposit_tx_conversion_mint() {
let input = r#"{
"blockHash": "0x7194f63b105e93fb1a27c50d23d62e422d4185a68536c55c96284911415699b2",
"blockNumber": "0x73a82cc",
"depositReceiptVersion": "0x1",
"from": "0x36bde71c97b33cc4729cf772ae268934f7ab70b2",
"gas": "0x7812e",
"gasPrice": "0x0",
"hash": "0xf7e83886d3c6864f78e01c453ebcd57020c5795d96089e8f0e0b90a467246ddb",
"input":
"0xd764ad0b000100000000000000000000000000000000000000000000000000000001cf5f00000000000000000000000099c9fc46f92e8a1c0dec1b1747d010903e884be100000000000000000000000042000000000000000000000000000000000000100000000000000000000000000000000000000000000000239c2e16a5ca5900000000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e41635f5fd0000000000000000000000002ce910fbba65b454bbaf6a18c952a70f3bcd82990000000000000000000000002ce910fbba65b454bbaf6a18c952a70f3bcd82990000000000000000000000000000000000000000000000239c2e16a5ca590000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
, "mint": "0x239c2e16a5ca590000",
"nonce": "0x7406b",
"r": "0x0",
"s": "0x0",
"sourceHash": "0xe0358cd2b2686d297c5c859646a613124a874fb9d9c4a2c88636a46a65c06e48",
"to": "0x4200000000000000000000000000000000000007",
"transactionIndex": "0x1",
"type": "0x7e",
"v": "0x0",
"value": "0x239c2e16a5ca590000"
}"#;
let alloy_tx: WithOtherFields<alloy_rpc_types::Transaction<AnyTxEnvelope>> =
serde_json::from_str(input).expect("failed to deserialize");
let TransactionSigned { transaction: reth_tx, .. } =
alloy_tx.try_into().expect("failed to convert");
if let Transaction::Deposit(deposit_tx) = reth_tx {
assert_eq!(
deposit_tx.source_hash,
"0xe0358cd2b2686d297c5c859646a613124a874fb9d9c4a2c88636a46a65c06e48"
.parse::<B256>()
.unwrap()
);
assert_eq!(
deposit_tx.from,
"0x36bde71c97b33cc4729cf772ae268934f7ab70b2".parse::<Address>().unwrap()
);
assert_eq!(
deposit_tx.to,
TxKind::from(address!("4200000000000000000000000000000000000007"))
);
assert_eq!(deposit_tx.mint, Some(656890000000000000000));
assert_eq!(deposit_tx.value, U256::from(0x239c2e16a5ca590000_u128));
assert_eq!(deposit_tx.gas_limit, 491822);
assert!(!deposit_tx.is_system_transaction);
} else {
panic!("Expected Deposit transaction");
}
}
}

View File

@ -1,4 +1,4 @@
use crate::TransactionSigned;
use reth_ethereum_primitives::TransactionSigned;
#[cfg(any(test, feature = "arbitrary"))]
pub use reth_primitives_traits::test_utils::{generate_valid_header, valid_header_strategy};

View File

@ -21,8 +21,6 @@
extern crate alloc;
#[cfg(feature = "alloy-compat")]
mod alloy_compat;
mod block;
pub mod proofs;
mod receipt;
@ -69,7 +67,6 @@ pub use c_kzg as kzg;
/// Read more: <https://github.com/bincode-org/bincode/issues/326>
#[cfg(feature = "serde-bincode-compat")]
pub mod serde_bincode_compat {
pub use super::transaction::{serde_bincode_compat as transaction, serde_bincode_compat::*};
pub use reth_primitives_traits::serde_bincode_compat::*;
}

View File

@ -1,6 +1,7 @@
//! Helper function for calculating Merkle proofs and hashes.
use crate::Receipt;
use alloy_consensus::TxReceipt;
use alloy_eips::eip2718::Encodable2718;
use alloy_primitives::B256;
pub use alloy_trie::root::ordered_trie_root_with_encoder;
@ -31,20 +32,15 @@ pub fn calculate_receipt_root_no_memo(receipts: &[&Receipt]) -> B256 {
#[cfg(test)]
mod tests {
use super::*;
use crate::Block;
use crate::{Block, TxType};
use alloy_consensus::EMPTY_ROOT_HASH;
use alloy_genesis::GenesisAccount;
use alloy_primitives::{b256, hex_literal::hex, Address, U256};
use alloy_primitives::{b256, bloom, hex_literal::hex, Address, Log, LogData, U256};
use alloy_rlp::Decodable;
use reth_chainspec::{HOLESKY, MAINNET, SEPOLIA};
use reth_trie_common::root::{state_root_ref_unhashed, state_root_unhashed};
use std::collections::HashMap;
#[cfg(not(feature = "optimism"))]
use crate::TxType;
#[cfg(not(feature = "optimism"))]
use alloy_primitives::{bloom, Log, LogData};
#[test]
fn check_transaction_root() {
let data = &hex!("f90262f901f9a092230ce5476ae868e98c7979cfc165a93f8b6ad1922acf2df62e340916efd49da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa02307107a867056ca33b5087e77c4174f47625e48fb49f1c70ced34890ddd88f3a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba0c598f69a5674cae9337261b669970e24abc0b46e6d284372a239ec8ccbf20b0ab901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8618203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0");
@ -55,7 +51,6 @@ mod tests {
assert_eq!(block.transactions_root, tx_root, "Must be the same");
}
#[cfg(not(feature = "optimism"))]
#[test]
fn check_receipt_root_optimism() {
use alloy_consensus::ReceiptWithBloom;

View File

@ -1,287 +1,13 @@
use alloc::{vec, vec::Vec};
use reth_primitives_traits::InMemorySize;
use alloy_consensus::{
Eip2718EncodableReceipt, Eip658Value, ReceiptWithBloom, RlpDecodableReceipt,
RlpEncodableReceipt, TxReceipt, Typed2718,
};
use alloy_primitives::{Bloom, Log, B256};
use alloy_rlp::{Decodable, Encodable, Header, RlpDecodable, RlpEncodable};
use bytes::BufMut;
use alloy_primitives::B256;
use derive_more::{DerefMut, From, IntoIterator};
use serde::{Deserialize, Serialize};
use crate::TxType;
/// Retrieves gas spent by transactions as a vector of tuples (transaction index, gas used).
pub use reth_primitives_traits::receipt::gas_spent_by_transactions;
/// Receipt containing result of transaction execution.
#[derive(
Clone, Debug, PartialEq, Eq, Default, RlpEncodable, RlpDecodable, Serialize, Deserialize,
)]
#[cfg_attr(any(test, feature = "reth-codec"), derive(reth_codecs::CompactZstd))]
#[cfg_attr(any(test, feature = "reth-codec"), reth_zstd(
compressor = reth_zstd_compressors::RECEIPT_COMPRESSOR,
decompressor = reth_zstd_compressors::RECEIPT_DECOMPRESSOR
))]
#[rlp(trailing)]
pub struct Receipt {
/// Receipt type.
pub tx_type: TxType,
/// If transaction is executed successfully.
///
/// This is the `statusCode`
pub success: bool,
/// Gas used
pub cumulative_gas_used: u64,
/// Log send from contracts.
pub logs: Vec<Log>,
/// Deposit nonce for Optimism deposit transactions
#[cfg(feature = "optimism")]
pub deposit_nonce: Option<u64>,
/// Deposit receipt version for Optimism deposit transactions
///
///
/// The deposit receipt version was introduced in Canyon to indicate an update to how
/// receipt hashes should be computed when set. The state transition process
/// ensures this is only set for post-Canyon deposit transactions.
#[cfg(feature = "optimism")]
pub deposit_receipt_version: Option<u64>,
}
impl Receipt {
/// Calculates [`Log`]'s bloom filter. this is slow operation and [`ReceiptWithBloom`] can
/// be used to cache this value.
pub fn bloom_slow(&self) -> Bloom {
alloy_primitives::logs_bloom(self.logs.iter())
}
/// Calculates the bloom filter for the receipt and returns the [`ReceiptWithBloom`] container
/// type.
pub fn with_bloom(self) -> ReceiptWithBloom<Self> {
self.into()
}
/// Calculates the bloom filter for the receipt and returns the [`ReceiptWithBloom`]
/// container type.
pub fn with_bloom_ref(&self) -> ReceiptWithBloom<&Self> {
self.into()
}
/// Returns length of RLP-encoded receipt fields with the given [`Bloom`] without an RLP header.
pub fn rlp_encoded_fields_length(&self, bloom: &Bloom) -> usize {
let len = self.success.length() +
self.cumulative_gas_used.length() +
bloom.length() +
self.logs.length();
#[cfg(feature = "optimism")]
if self.tx_type == TxType::Deposit {
let mut len = len;
if let Some(deposit_nonce) = self.deposit_nonce {
len += deposit_nonce.length();
}
if let Some(deposit_receipt_version) = self.deposit_receipt_version {
len += deposit_receipt_version.length();
}
return len
}
len
}
/// RLP-encodes receipt fields with the given [`Bloom`] without an RLP header.
pub fn rlp_encode_fields(&self, bloom: &Bloom, out: &mut dyn BufMut) {
self.success.encode(out);
self.cumulative_gas_used.encode(out);
bloom.encode(out);
self.logs.encode(out);
#[cfg(feature = "optimism")]
if self.tx_type == TxType::Deposit {
if let Some(nonce) = self.deposit_nonce {
nonce.encode(out);
}
if let Some(version) = self.deposit_receipt_version {
version.encode(out);
}
}
}
/// Returns RLP header for inner encoding.
pub fn rlp_header_inner(&self, bloom: &Bloom) -> Header {
Header { list: true, payload_length: self.rlp_encoded_fields_length(bloom) }
}
fn decode_receipt_with_bloom(
buf: &mut &[u8],
tx_type: TxType,
) -> alloy_rlp::Result<ReceiptWithBloom<Self>> {
let b = &mut &**buf;
let rlp_head = alloy_rlp::Header::decode(b)?;
if !rlp_head.list {
return Err(alloy_rlp::Error::UnexpectedString)
}
let started_len = b.len();
let success = Decodable::decode(b)?;
let cumulative_gas_used = Decodable::decode(b)?;
let bloom = Decodable::decode(b)?;
let logs = Decodable::decode(b)?;
let receipt = match tx_type {
#[cfg(feature = "optimism")]
TxType::Deposit => {
let remaining = |b: &[u8]| rlp_head.payload_length - (started_len - b.len()) > 0;
let deposit_nonce = remaining(b).then(|| Decodable::decode(b)).transpose()?;
let deposit_receipt_version =
remaining(b).then(|| Decodable::decode(b)).transpose()?;
Self {
tx_type,
success,
cumulative_gas_used,
logs,
deposit_nonce,
deposit_receipt_version,
}
}
_ => Self {
tx_type,
success,
cumulative_gas_used,
logs,
#[cfg(feature = "optimism")]
deposit_nonce: None,
#[cfg(feature = "optimism")]
deposit_receipt_version: None,
},
};
let this = ReceiptWithBloom { receipt, logs_bloom: bloom };
let consumed = started_len - b.len();
if consumed != rlp_head.payload_length {
return Err(alloy_rlp::Error::ListLengthMismatch {
expected: rlp_head.payload_length,
got: consumed,
})
}
*buf = *b;
Ok(this)
}
}
impl Eip2718EncodableReceipt for Receipt {
fn eip2718_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize {
self.rlp_header_inner(bloom).length_with_payload() +
!matches!(self.tx_type, TxType::Legacy) as usize // account for type prefix
}
fn eip2718_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) {
if !matches!(self.tx_type, TxType::Legacy) {
out.put_u8(self.tx_type as u8);
}
self.rlp_header_inner(bloom).encode(out);
self.rlp_encode_fields(bloom, out);
}
}
impl RlpEncodableReceipt for Receipt {
fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize {
let mut len = self.eip2718_encoded_length_with_bloom(bloom);
if !matches!(self.tx_type, TxType::Legacy) {
len += Header {
list: false,
payload_length: self.eip2718_encoded_length_with_bloom(bloom),
}
.length();
}
len
}
fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) {
if !matches!(self.tx_type, TxType::Legacy) {
Header { list: false, payload_length: self.eip2718_encoded_length_with_bloom(bloom) }
.encode(out);
}
self.eip2718_encode_with_bloom(bloom, out);
}
}
impl RlpDecodableReceipt for Receipt {
fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>> {
let header_buf = &mut &**buf;
let header = Header::decode(header_buf)?;
if header.list {
return Self::decode_receipt_with_bloom(buf, TxType::Legacy);
}
*buf = *header_buf;
let remaining = buf.len();
let tx_type = TxType::decode(buf)?;
let this = Self::decode_receipt_with_bloom(buf, tx_type)?;
if buf.len() + header.payload_length != remaining {
return Err(alloy_rlp::Error::UnexpectedLength);
}
Ok(this)
}
}
impl TxReceipt for Receipt {
type Log = Log;
fn status_or_post_state(&self) -> Eip658Value {
self.success.into()
}
fn status(&self) -> bool {
self.success
}
fn bloom(&self) -> Bloom {
alloy_primitives::logs_bloom(self.logs.iter())
}
fn cumulative_gas_used(&self) -> u64 {
self.cumulative_gas_used
}
fn logs(&self) -> &[Log] {
&self.logs
}
}
impl Typed2718 for Receipt {
fn ty(&self) -> u8 {
self.tx_type as u8
}
}
impl reth_primitives_traits::Receipt for Receipt {}
impl InMemorySize for Receipt {
/// Calculates a heuristic for the in-memory size of the [Receipt].
#[inline]
fn size(&self) -> usize {
let total_size = self.tx_type.size() +
core::mem::size_of::<bool>() +
core::mem::size_of::<u64>() +
self.logs.capacity() * core::mem::size_of::<Log>();
#[cfg(feature = "optimism")]
return total_size + 2 * core::mem::size_of::<Option<u64>>();
#[cfg(not(feature = "optimism"))]
total_size
}
}
pub use reth_ethereum_primitives::Receipt;
/// A collection of receipts organized as a two-dimensional vector.
#[derive(
@ -342,248 +68,3 @@ impl<T> Default for Receipts<T> {
Self { receipt_vec: Vec::new() }
}
}
#[cfg(any(test, feature = "arbitrary"))]
impl<'a> arbitrary::Arbitrary<'a> for Receipt {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let tx_type = TxType::arbitrary(u)?;
let success = bool::arbitrary(u)?;
let cumulative_gas_used = u64::arbitrary(u)?;
let logs = Vec::<Log>::arbitrary(u)?;
// Only receipts for deposit transactions may contain a deposit nonce
#[cfg(feature = "optimism")]
let (deposit_nonce, deposit_receipt_version) = if tx_type == TxType::Deposit {
let deposit_nonce = Option::<u64>::arbitrary(u)?;
let deposit_nonce_version =
deposit_nonce.map(|_| Option::<u64>::arbitrary(u)).transpose()?.flatten();
(deposit_nonce, deposit_nonce_version)
} else {
(None, None)
};
Ok(Self {
tx_type,
success,
cumulative_gas_used,
logs,
#[cfg(feature = "optimism")]
deposit_nonce,
#[cfg(feature = "optimism")]
deposit_receipt_version,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_eips::eip2718::Encodable2718;
use alloy_primitives::{address, b256, bytes, hex_literal::hex, Bytes};
use reth_codecs::Compact;
#[test]
fn test_decode_receipt() {
#[cfg(not(feature = "optimism"))]
reth_codecs::test_utils::test_decode::<Receipt>(&hex!(
"c428b52ffd23fc42696156b10200f034792b6a94c3850215c2fef7aea361a0c31b79d9a32652eefc0d4e2e730036061cff7344b6fc6132b50cda0ed810a991ae58ef013150c12b2522533cb3b3a8b19b7786a8b5ff1d3cdc84225e22b02def168c8858df"
));
#[cfg(feature = "optimism")]
reth_codecs::test_utils::test_decode::<Receipt>(&hex!(
"c30328b52ffd23fc426961a00105007eb0042307705a97e503562eacf2b95060cce9de6de68386b6c155b73a9650021a49e2f8baad17f30faff5899d785c4c0873e45bc268bcf07560106424570d11f9a59e8f3db1efa4ceec680123712275f10d92c3411e1caaa11c7c5d591bc11487168e09934a9986848136da1b583babf3a7188e3aed007a1520f1cf4c1ca7d3482c6c28d37c298613c70a76940008816c4c95644579fd08471dc34732fd0f24"
));
}
// Test vector from: https://eips.ethereum.org/EIPS/eip-2481
#[test]
fn encode_legacy_receipt() {
let expected = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff");
let mut data = Vec::with_capacity(expected.length());
let receipt = ReceiptWithBloom {
receipt: Receipt {
tx_type: TxType::Legacy,
cumulative_gas_used: 0x1u64,
logs: vec![Log::new_unchecked(
address!("0000000000000000000000000000000000000011"),
vec![
b256!("000000000000000000000000000000000000000000000000000000000000dead"),
b256!("000000000000000000000000000000000000000000000000000000000000beef"),
],
bytes!("0100ff"),
)],
success: false,
#[cfg(feature = "optimism")]
deposit_nonce: None,
#[cfg(feature = "optimism")]
deposit_receipt_version: None,
},
logs_bloom: [0; 256].into(),
};
receipt.encode(&mut data);
// check that the rlp length equals the length of the expected rlp
assert_eq!(receipt.length(), expected.len());
assert_eq!(data, expected);
}
// Test vector from: https://eips.ethereum.org/EIPS/eip-2481
#[test]
fn decode_legacy_receipt() {
let data = hex!("f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff");
// EIP658Receipt
let expected = ReceiptWithBloom {
receipt: Receipt {
tx_type: TxType::Legacy,
cumulative_gas_used: 0x1u64,
logs: vec![Log::new_unchecked(
address!("0000000000000000000000000000000000000011"),
vec![
b256!("000000000000000000000000000000000000000000000000000000000000dead"),
b256!("000000000000000000000000000000000000000000000000000000000000beef"),
],
bytes!("0100ff"),
)],
success: false,
#[cfg(feature = "optimism")]
deposit_nonce: None,
#[cfg(feature = "optimism")]
deposit_receipt_version: None,
},
logs_bloom: [0; 256].into(),
};
let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
assert_eq!(receipt, expected);
}
#[cfg(feature = "optimism")]
#[test]
fn decode_deposit_receipt_regolith_roundtrip() {
let data = hex!("b901107ef9010c0182b741b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0833d3bbf");
// Deposit Receipt (post-regolith)
let expected = ReceiptWithBloom {
receipt: Receipt {
tx_type: TxType::Deposit,
cumulative_gas_used: 46913,
logs: vec![],
success: true,
deposit_nonce: Some(4012991),
deposit_receipt_version: None,
},
logs_bloom: [0; 256].into(),
};
let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
assert_eq!(receipt, expected);
let mut buf = Vec::with_capacity(data.len());
receipt.encode(&mut buf);
assert_eq!(buf, &data[..]);
}
#[cfg(feature = "optimism")]
#[test]
fn decode_deposit_receipt_canyon_roundtrip() {
let data = hex!("b901117ef9010d0182b741b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0833d3bbf01");
// Deposit Receipt (post-regolith)
let expected = ReceiptWithBloom {
receipt: Receipt {
tx_type: TxType::Deposit,
cumulative_gas_used: 46913,
logs: vec![],
success: true,
deposit_nonce: Some(4012991),
deposit_receipt_version: Some(1),
},
logs_bloom: [0; 256].into(),
};
let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
assert_eq!(receipt, expected);
let mut buf = Vec::with_capacity(data.len());
expected.encode(&mut buf);
assert_eq!(buf, &data[..]);
}
#[test]
fn gigantic_receipt() {
let receipt = Receipt {
cumulative_gas_used: 16747627,
success: true,
tx_type: TxType::Legacy,
logs: vec![
Log::new_unchecked(
address!("4bf56695415f725e43c3e04354b604bcfb6dfb6e"),
vec![b256!("c69dc3d7ebff79e41f525be431d5cd3cc08f80eaf0f7819054a726eeb7086eb9")],
Bytes::from(vec![1; 0xffffff]),
),
Log::new_unchecked(
address!("faca325c86bf9c2d5b413cd7b90b209be92229c2"),
vec![b256!("8cca58667b1e9ffa004720ac99a3d61a138181963b294d270d91c53d36402ae2")],
Bytes::from(vec![1; 0xffffff]),
),
],
#[cfg(feature = "optimism")]
deposit_nonce: None,
#[cfg(feature = "optimism")]
deposit_receipt_version: None,
};
let mut data = vec![];
receipt.to_compact(&mut data);
let (decoded, _) = Receipt::from_compact(&data[..], data.len());
assert_eq!(decoded, receipt);
}
#[test]
fn test_encode_2718_length() {
let receipt = ReceiptWithBloom {
receipt: Receipt {
tx_type: TxType::Eip1559,
success: true,
cumulative_gas_used: 21000,
logs: vec![],
#[cfg(feature = "optimism")]
deposit_nonce: None,
#[cfg(feature = "optimism")]
deposit_receipt_version: None,
},
logs_bloom: Bloom::default(),
};
let encoded = receipt.encoded_2718();
assert_eq!(
encoded.len(),
receipt.encode_2718_len(),
"Encoded length should match the actual encoded data length"
);
// Test for legacy receipt as well
let legacy_receipt = ReceiptWithBloom {
receipt: Receipt {
tx_type: TxType::Legacy,
success: true,
cumulative_gas_used: 21000,
logs: vec![],
#[cfg(feature = "optimism")]
deposit_nonce: None,
#[cfg(feature = "optimism")]
deposit_receipt_version: None,
},
logs_bloom: Bloom::default(),
};
let legacy_encoded = legacy_receipt.encoded_2718();
assert_eq!(
legacy_encoded.len(),
legacy_receipt.encode_2718_len(),
"Encoded length for legacy receipt should match the actual encoded data length"
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -12,10 +12,9 @@ mod tests {
use super::*;
use alloy_consensus::{transaction::RlpEcdsaTx, Transaction as _, TxLegacy};
use alloy_eips::eip2718::Decodable2718;
use alloy_primitives::{address, hex};
use alloy_primitives::{address, hex, Bytes};
use alloy_rlp::Decodable;
use assert_matches::assert_matches;
use bytes::Bytes;
#[test]
fn invalid_legacy_pooled_decoding_input_too_short() {

View File

@ -1,16 +1,3 @@
use alloy_consensus::{
constants::{
EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID,
LEGACY_TX_TYPE_ID,
},
Typed2718,
};
use alloy_primitives::{U64, U8};
use alloy_rlp::{Decodable, Encodable};
use derive_more::Display;
use reth_primitives_traits::InMemorySize;
use serde::{Deserialize, Serialize};
/// Transaction Type
///
/// Currently being used as 2-bit type when encoding it to `reth_codecs::Compact` on
@ -18,292 +5,4 @@ use serde::{Deserialize, Serialize};
/// database format.
///
/// Other required changes when adding a new type can be seen on [PR#3953](https://github.com/paradigmxyz/reth/pull/3953/files).
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Default,
Serialize,
Deserialize,
Hash,
Display,
)]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
#[display("tx type: {_variant}")]
pub enum TxType {
/// Legacy transaction pre EIP-2929
#[default]
#[display("legacy (0)")]
Legacy = 0_isize,
/// AccessList transaction
#[display("eip2930 (1)")]
Eip2930 = 1_isize,
/// Transaction with Priority fee
#[display("eip1559 (2)")]
Eip1559 = 2_isize,
/// Shard Blob Transactions - EIP-4844
#[display("eip4844 (3)")]
Eip4844 = 3_isize,
/// EOA Contract Code Transactions - EIP-7702
#[display("eip7702 (4)")]
Eip7702 = 4_isize,
/// Optimism Deposit transaction.
#[cfg(feature = "optimism")]
#[display("deposit (126)")]
Deposit = 126_isize,
}
impl TxType {
/// The max type reserved by an EIP.
pub const MAX_RESERVED_EIP: Self = Self::Eip7702;
/// Check if the transaction type has an access list.
pub const fn has_access_list(&self) -> bool {
match self {
Self::Legacy => false,
Self::Eip2930 | Self::Eip1559 | Self::Eip4844 | Self::Eip7702 => true,
#[cfg(feature = "optimism")]
Self::Deposit => false,
}
}
}
impl Typed2718 for TxType {
fn ty(&self) -> u8 {
(*self).into()
}
}
impl InMemorySize for TxType {
/// Calculates a heuristic for the in-memory size of the [`TxType`].
#[inline]
fn size(&self) -> usize {
core::mem::size_of::<Self>()
}
}
impl From<TxType> for u8 {
fn from(value: TxType) -> Self {
match value {
TxType::Legacy => LEGACY_TX_TYPE_ID,
TxType::Eip2930 => EIP2930_TX_TYPE_ID,
TxType::Eip1559 => EIP1559_TX_TYPE_ID,
TxType::Eip4844 => EIP4844_TX_TYPE_ID,
TxType::Eip7702 => EIP7702_TX_TYPE_ID,
#[cfg(feature = "optimism")]
TxType::Deposit => op_alloy_consensus::DEPOSIT_TX_TYPE_ID,
}
}
}
impl From<TxType> for U8 {
fn from(value: TxType) -> Self {
Self::from(u8::from(value))
}
}
impl TryFrom<u8> for TxType {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
#[cfg(feature = "optimism")]
if value == Self::Deposit {
return Ok(Self::Deposit)
}
if value == Self::Legacy {
return Ok(Self::Legacy)
} else if value == Self::Eip2930 {
return Ok(Self::Eip2930)
} else if value == Self::Eip1559 {
return Ok(Self::Eip1559)
} else if value == Self::Eip4844 {
return Ok(Self::Eip4844)
} else if value == Self::Eip7702 {
return Ok(Self::Eip7702)
}
Err("invalid tx type")
}
}
impl TryFrom<u64> for TxType {
type Error = &'static str;
fn try_from(value: u64) -> Result<Self, Self::Error> {
let value: u8 = value.try_into().map_err(|_| "invalid tx type")?;
Self::try_from(value)
}
}
impl TryFrom<U64> for TxType {
type Error = &'static str;
fn try_from(value: U64) -> Result<Self, Self::Error> {
value.to::<u64>().try_into()
}
}
#[cfg(any(test, feature = "reth-codec"))]
impl reth_codecs::Compact for TxType {
fn to_compact<B>(&self, buf: &mut B) -> usize
where
B: bytes::BufMut + AsMut<[u8]>,
{
use reth_codecs::txtype::*;
match self {
Self::Legacy => COMPACT_IDENTIFIER_LEGACY,
Self::Eip2930 => COMPACT_IDENTIFIER_EIP2930,
Self::Eip1559 => COMPACT_IDENTIFIER_EIP1559,
Self::Eip4844 => {
buf.put_u8(EIP4844_TX_TYPE_ID);
COMPACT_EXTENDED_IDENTIFIER_FLAG
}
Self::Eip7702 => {
buf.put_u8(EIP7702_TX_TYPE_ID);
COMPACT_EXTENDED_IDENTIFIER_FLAG
}
#[cfg(feature = "optimism")]
Self::Deposit => {
buf.put_u8(op_alloy_consensus::DEPOSIT_TX_TYPE_ID);
COMPACT_EXTENDED_IDENTIFIER_FLAG
}
}
}
// For backwards compatibility purposes only 2 bits of the type are encoded in the identifier
// parameter. In the case of a [`COMPACT_EXTENDED_IDENTIFIER_FLAG`], the full transaction type
// is read from the buffer as a single byte.
fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) {
use bytes::Buf;
(
match identifier {
reth_codecs::txtype::COMPACT_IDENTIFIER_LEGACY => Self::Legacy,
reth_codecs::txtype::COMPACT_IDENTIFIER_EIP2930 => Self::Eip2930,
reth_codecs::txtype::COMPACT_IDENTIFIER_EIP1559 => Self::Eip1559,
reth_codecs::txtype::COMPACT_EXTENDED_IDENTIFIER_FLAG => {
let extended_identifier = buf.get_u8();
match extended_identifier {
EIP4844_TX_TYPE_ID => Self::Eip4844,
EIP7702_TX_TYPE_ID => Self::Eip7702,
#[cfg(feature = "optimism")]
op_alloy_consensus::DEPOSIT_TX_TYPE_ID => Self::Deposit,
_ => panic!("Unsupported TxType identifier: {extended_identifier}"),
}
}
_ => panic!("Unknown identifier for TxType: {identifier}"),
},
buf,
)
}
}
impl PartialEq<u8> for TxType {
fn eq(&self, other: &u8) -> bool {
*self as u8 == *other
}
}
impl PartialEq<TxType> for u8 {
fn eq(&self, other: &TxType) -> bool {
*self == *other as Self
}
}
impl Encodable for TxType {
fn encode(&self, out: &mut dyn bytes::BufMut) {
(*self as u8).encode(out);
}
fn length(&self) -> usize {
1
}
}
impl Decodable for TxType {
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
let ty = u8::decode(buf)?;
Self::try_from(ty).map_err(alloy_rlp::Error::Custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::hex;
use reth_codecs::{txtype::*, Compact};
use rstest::rstest;
#[rstest]
#[case(U64::from(LEGACY_TX_TYPE_ID), Ok(TxType::Legacy))]
#[case(U64::from(EIP2930_TX_TYPE_ID), Ok(TxType::Eip2930))]
#[case(U64::from(EIP1559_TX_TYPE_ID), Ok(TxType::Eip1559))]
#[case(U64::from(EIP4844_TX_TYPE_ID), Ok(TxType::Eip4844))]
#[case(U64::from(EIP7702_TX_TYPE_ID), Ok(TxType::Eip7702))]
#[cfg_attr(
feature = "optimism",
case(U64::from(op_alloy_consensus::DEPOSIT_TX_TYPE_ID), Ok(TxType::Deposit))
)]
#[case(U64::MAX, Err("invalid tx type"))]
fn test_u64_to_tx_type(#[case] input: U64, #[case] expected: Result<TxType, &'static str>) {
let tx_type_result = TxType::try_from(input);
assert_eq!(tx_type_result, expected);
}
#[rstest]
#[case(TxType::Legacy, COMPACT_IDENTIFIER_LEGACY, vec![])]
#[case(TxType::Eip2930, COMPACT_IDENTIFIER_EIP2930, vec![])]
#[case(TxType::Eip1559, COMPACT_IDENTIFIER_EIP1559, vec![])]
#[case(TxType::Eip4844, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![EIP4844_TX_TYPE_ID])]
#[case(TxType::Eip7702, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![EIP7702_TX_TYPE_ID])]
#[cfg_attr(feature = "optimism", case(TxType::Deposit, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![op_alloy_consensus::DEPOSIT_TX_TYPE_ID]))]
fn test_txtype_to_compact(
#[case] tx_type: TxType,
#[case] expected_identifier: usize,
#[case] expected_buf: Vec<u8>,
) {
let mut buf = vec![];
let identifier = tx_type.to_compact(&mut buf);
assert_eq!(identifier, expected_identifier, "Unexpected identifier for TxType {tx_type:?}",);
assert_eq!(buf, expected_buf, "Unexpected buffer for TxType {tx_type:?}",);
}
#[rstest]
#[case(TxType::Legacy, COMPACT_IDENTIFIER_LEGACY, vec![])]
#[case(TxType::Eip2930, COMPACT_IDENTIFIER_EIP2930, vec![])]
#[case(TxType::Eip1559, COMPACT_IDENTIFIER_EIP1559, vec![])]
#[case(TxType::Eip4844, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![EIP4844_TX_TYPE_ID])]
#[case(TxType::Eip7702, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![EIP7702_TX_TYPE_ID])]
#[cfg_attr(feature = "optimism", case(TxType::Deposit, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![op_alloy_consensus::DEPOSIT_TX_TYPE_ID]))]
fn test_txtype_from_compact(
#[case] expected_type: TxType,
#[case] identifier: usize,
#[case] buf: Vec<u8>,
) {
let (actual_type, remaining_buf) = TxType::from_compact(&buf, identifier);
assert_eq!(actual_type, expected_type, "Unexpected TxType for identifier {identifier}");
assert!(remaining_buf.is_empty(), "Buffer not fully consumed for identifier {identifier}");
}
#[rstest]
#[case(&hex!("80"), Ok(TxType::Legacy))]
#[case(&[EIP2930_TX_TYPE_ID], Ok(TxType::Eip2930))]
#[case(&[EIP1559_TX_TYPE_ID], Ok(TxType::Eip1559))]
#[case(&[EIP4844_TX_TYPE_ID], Ok(TxType::Eip4844))]
#[case(&[EIP7702_TX_TYPE_ID], Ok(TxType::Eip7702))]
#[case(&[u8::MAX], Err(alloy_rlp::Error::InputTooShort))]
#[cfg_attr(feature = "optimism", case(&[op_alloy_consensus::DEPOSIT_TX_TYPE_ID], Ok(TxType::Deposit)))]
fn decode_tx_type(#[case] input: &[u8], #[case] expected: Result<TxType, alloy_rlp::Error>) {
let tx_type_result = TxType::decode(&mut &input[..]);
assert_eq!(tx_type_result, expected)
}
}
pub use alloy_consensus::TxType;