mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: use reth-ethereum-primitives (#13830)
This commit is contained in:
@ -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]]
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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};
|
||||
|
||||
|
||||
@ -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::*;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
@ -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() {
|
||||
|
||||
@ -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;
|
||||
|
||||
Reference in New Issue
Block a user