mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: op-reth (#4377)
Co-authored-by: Roberto Bayardo <bayardo@alum.mit.edu> Co-authored-by: refcell.eth <abigger87@gmail.com> Co-authored-by: Roman Krasiuk <rokrassyuk@gmail.com> Co-authored-by: refcell <refcell@oplabs.co> Co-authored-by: nicolas <48695862+merklefruit@users.noreply.github.com>
This commit is contained in:
@ -77,9 +77,6 @@ triehash = "0.8"
|
||||
|
||||
plain_hasher = "0.2"
|
||||
hash-db = "~0.15"
|
||||
# value-256 is needed for the main_codec proptests to pass
|
||||
reth-primitives = { path = ".", features = ["value-256"] }
|
||||
|
||||
|
||||
# necessary so we don't hit a "undeclared 'std'":
|
||||
# https://github.com/paradigmxyz/reth/pull/177#discussion_r1021172198
|
||||
@ -92,10 +89,8 @@ default = ["c-kzg"]
|
||||
arbitrary = ["revm-primitives/arbitrary", "reth-rpc-types/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"]
|
||||
c-kzg = ["revm-primitives/c-kzg", "dep:c-kzg"]
|
||||
test-utils = ["dep:plain_hasher", "dep:hash-db", "dep:ethers-core"]
|
||||
# value-256 controls whether transaction Value fields are DB-encoded as 256 bits instead of the
|
||||
# default of 128 bits.
|
||||
value-256 = ["reth-codecs/value-256"]
|
||||
clap = ["dep:clap"]
|
||||
optimism = ["reth-codecs/optimism"]
|
||||
|
||||
[[bench]]
|
||||
name = "recover_ecdsa_crit"
|
||||
|
||||
14472
crates/primitives/res/genesis/base.json
Normal file
14472
crates/primitives/res/genesis/base.json
Normal file
File diff suppressed because one or more lines are too long
15253
crates/primitives/res/genesis/goerli_base.json
Normal file
15253
crates/primitives/res/genesis/goerli_base.json
Normal file
File diff suppressed because one or more lines are too long
92
crates/primitives/res/genesis/goerli_op.json
Normal file
92
crates/primitives/res/genesis/goerli_op.json
Normal file
File diff suppressed because one or more lines are too long
@ -96,4 +96,70 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
#[test]
|
||||
fn calculate_optimism_base_fee_success() {
|
||||
let base_fee = [
|
||||
1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
|
||||
1, 2,
|
||||
];
|
||||
let gas_used = [
|
||||
10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
|
||||
10000000,
|
||||
];
|
||||
let gas_limit = [
|
||||
10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
|
||||
18000000, 18000000,
|
||||
];
|
||||
let next_base_fee = [
|
||||
1100000048, 1080000000, 1065714297, 1167067046, 1128881311, 1028254188, 1098203452, 1,
|
||||
2, 3,
|
||||
];
|
||||
|
||||
for i in 0..base_fee.len() {
|
||||
assert_eq!(
|
||||
next_base_fee[i],
|
||||
calculate_next_block_base_fee(
|
||||
gas_used[i],
|
||||
gas_limit[i],
|
||||
base_fee[i],
|
||||
crate::BaseFeeParams::optimism(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
#[test]
|
||||
fn calculate_optimism_goerli_base_fee_success() {
|
||||
let base_fee = [
|
||||
1000000000, 1000000000, 1000000000, 1072671875, 1059263476, 1049238967, 1049238967, 0,
|
||||
1, 2,
|
||||
];
|
||||
let gas_used = [
|
||||
10000000, 10000000, 10000000, 9000000, 10001000, 0, 10000000, 10000000, 10000000,
|
||||
10000000,
|
||||
];
|
||||
let gas_limit = [
|
||||
10000000, 12000000, 14000000, 10000000, 14000000, 2000000, 18000000, 18000000,
|
||||
18000000, 18000000,
|
||||
];
|
||||
let next_base_fee = [
|
||||
1180000000, 1146666666, 1122857142, 1244299375, 1189416692, 1028254188, 1144836295, 1,
|
||||
2, 3,
|
||||
];
|
||||
|
||||
for i in 0..base_fee.len() {
|
||||
assert_eq!(
|
||||
next_base_fee[i],
|
||||
calculate_next_block_base_fee(
|
||||
gas_used[i],
|
||||
gas_limit[i],
|
||||
base_fee[i],
|
||||
crate::BaseFeeParams::optimism_goerli(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,9 @@ pub use spec::{
|
||||
ForkTimestamps, DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA,
|
||||
};
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
pub use spec::{BASE_GOERLI, BASE_MAINNET, OP_GOERLI};
|
||||
|
||||
// The chain info module.
|
||||
mod info;
|
||||
pub use info::ChainInfo;
|
||||
@ -58,6 +61,9 @@ pub enum NamedChain {
|
||||
OptimismKovan = 69,
|
||||
OptimismGoerli = 420,
|
||||
|
||||
Base = 8453,
|
||||
BaseGoerli = 84531,
|
||||
|
||||
Arbitrum = 42161,
|
||||
ArbitrumTestnet = 421611,
|
||||
ArbitrumGoerli = 421613,
|
||||
@ -116,11 +122,53 @@ impl Chain {
|
||||
Chain::Named(NamedChain::Holesky)
|
||||
}
|
||||
|
||||
/// Returns the optimism goerli chain.
|
||||
pub const fn optimism_goerli() -> Self {
|
||||
Chain::Named(NamedChain::OptimismGoerli)
|
||||
}
|
||||
|
||||
/// Returns the optimism mainnet chain.
|
||||
pub const fn optimism_mainnet() -> Self {
|
||||
Chain::Named(NamedChain::Optimism)
|
||||
}
|
||||
|
||||
/// Returns the base goerli chain.
|
||||
pub const fn base_goerli() -> Self {
|
||||
Chain::Named(NamedChain::BaseGoerli)
|
||||
}
|
||||
|
||||
/// Returns the base mainnet chain.
|
||||
pub const fn base_mainnet() -> Self {
|
||||
Chain::Named(NamedChain::Base)
|
||||
}
|
||||
|
||||
/// Returns the dev chain.
|
||||
pub const fn dev() -> Self {
|
||||
Chain::Named(NamedChain::Dev)
|
||||
}
|
||||
|
||||
/// Returns true if the chain contains Optimism configuration.
|
||||
pub fn is_optimism(self) -> bool {
|
||||
self.named().map_or(false, |c| {
|
||||
matches!(
|
||||
c,
|
||||
NamedChain::Optimism |
|
||||
NamedChain::OptimismGoerli |
|
||||
NamedChain::OptimismKovan |
|
||||
NamedChain::Base |
|
||||
NamedChain::BaseGoerli
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Attempts to convert the chain into a named chain.
|
||||
pub fn named(&self) -> Option<NamedChain> {
|
||||
match self {
|
||||
Chain::Named(chain) => Some(*chain),
|
||||
Chain::Id(id) => NamedChain::try_from(*id).ok(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The id of the chain
|
||||
pub fn id(&self) -> u64 {
|
||||
match self {
|
||||
|
||||
@ -153,6 +153,7 @@ pub static SEPOLIA: Lazy<Arc<ChainSpec>> = Lazy::new(|| {
|
||||
1273020,
|
||||
b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"),
|
||||
)),
|
||||
|
||||
base_fee_params: BaseFeeParams::ethereum(),
|
||||
prune_delete_limit: 1700,
|
||||
snapshot_block_interval: 1_000_000,
|
||||
@ -241,6 +242,129 @@ pub static DEV: Lazy<Arc<ChainSpec>> = Lazy::new(|| {
|
||||
.into()
|
||||
});
|
||||
|
||||
/// The Optimism Goerli spec
|
||||
#[cfg(feature = "optimism")]
|
||||
pub static OP_GOERLI: Lazy<Arc<ChainSpec>> = Lazy::new(|| {
|
||||
ChainSpec {
|
||||
chain: Chain::optimism_goerli(),
|
||||
genesis: serde_json::from_str(include_str!("../../res/genesis/goerli_op.json"))
|
||||
.expect("Can't deserialize Optimism Goerli genesis json"),
|
||||
genesis_hash: Some(b256!(
|
||||
"c1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1"
|
||||
)),
|
||||
fork_timestamps: ForkTimestamps::default(),
|
||||
paris_block_and_final_difficulty: Some((0, U256::from(0))),
|
||||
hardforks: BTreeMap::from([
|
||||
(Hardfork::Frontier, ForkCondition::Block(0)),
|
||||
(Hardfork::Homestead, ForkCondition::Block(0)),
|
||||
(Hardfork::Tangerine, ForkCondition::Block(0)),
|
||||
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
|
||||
(Hardfork::Byzantium, ForkCondition::Block(0)),
|
||||
(Hardfork::Constantinople, ForkCondition::Block(0)),
|
||||
(Hardfork::Petersburg, ForkCondition::Block(0)),
|
||||
(Hardfork::Istanbul, ForkCondition::Block(0)),
|
||||
(Hardfork::MuirGlacier, ForkCondition::Block(0)),
|
||||
(Hardfork::Berlin, ForkCondition::Block(0)),
|
||||
(Hardfork::London, ForkCondition::Block(0)),
|
||||
(Hardfork::ArrowGlacier, ForkCondition::Block(0)),
|
||||
(Hardfork::GrayGlacier, ForkCondition::Block(0)),
|
||||
(
|
||||
Hardfork::Paris,
|
||||
ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) },
|
||||
),
|
||||
(Hardfork::Bedrock, ForkCondition::Block(4061224)),
|
||||
(Hardfork::Regolith, ForkCondition::Timestamp(1679079600)),
|
||||
]),
|
||||
base_fee_params: BaseFeeParams::optimism(),
|
||||
prune_delete_limit: 1700,
|
||||
snapshot_block_interval: 1_000_000,
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
});
|
||||
|
||||
/// The Base Goerli spec
|
||||
#[cfg(feature = "optimism")]
|
||||
pub static BASE_GOERLI: Lazy<Arc<ChainSpec>> = Lazy::new(|| {
|
||||
ChainSpec {
|
||||
chain: Chain::base_goerli(),
|
||||
genesis: serde_json::from_str(include_str!("../../res/genesis/goerli_base.json"))
|
||||
.expect("Can't deserialize Base Goerli genesis json"),
|
||||
genesis_hash: Some(b256!(
|
||||
"a3ab140f15ea7f7443a4702da64c10314eb04d488e72974e02e2d728096b4f76"
|
||||
)),
|
||||
fork_timestamps: ForkTimestamps::default(),
|
||||
paris_block_and_final_difficulty: Some((0, U256::from(0))),
|
||||
hardforks: BTreeMap::from([
|
||||
(Hardfork::Frontier, ForkCondition::Block(0)),
|
||||
(Hardfork::Homestead, ForkCondition::Block(0)),
|
||||
(Hardfork::Tangerine, ForkCondition::Block(0)),
|
||||
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
|
||||
(Hardfork::Byzantium, ForkCondition::Block(0)),
|
||||
(Hardfork::Constantinople, ForkCondition::Block(0)),
|
||||
(Hardfork::Petersburg, ForkCondition::Block(0)),
|
||||
(Hardfork::Istanbul, ForkCondition::Block(0)),
|
||||
(Hardfork::MuirGlacier, ForkCondition::Block(0)),
|
||||
(Hardfork::Berlin, ForkCondition::Block(0)),
|
||||
(Hardfork::London, ForkCondition::Block(0)),
|
||||
(Hardfork::ArrowGlacier, ForkCondition::Block(0)),
|
||||
(Hardfork::GrayGlacier, ForkCondition::Block(0)),
|
||||
(
|
||||
Hardfork::Paris,
|
||||
ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) },
|
||||
),
|
||||
(Hardfork::Bedrock, ForkCondition::Block(0)),
|
||||
(Hardfork::Regolith, ForkCondition::Timestamp(1683219600)),
|
||||
]),
|
||||
base_fee_params: BaseFeeParams::optimism_goerli(),
|
||||
prune_delete_limit: 1700,
|
||||
snapshot_block_interval: 1_000_000,
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
});
|
||||
|
||||
/// The Base mainnet spec
|
||||
#[cfg(feature = "optimism")]
|
||||
pub static BASE_MAINNET: Lazy<Arc<ChainSpec>> = Lazy::new(|| {
|
||||
ChainSpec {
|
||||
chain: Chain::base_mainnet(),
|
||||
genesis: serde_json::from_str(include_str!("../../res/genesis/base.json"))
|
||||
.expect("Can't deserialize Base genesis json"),
|
||||
genesis_hash: Some(b256!(
|
||||
"f712aa9241cc24369b143cf6dce85f0902a9731e70d66818a3a5845b296c73dd"
|
||||
)),
|
||||
fork_timestamps: ForkTimestamps::default(),
|
||||
paris_block_and_final_difficulty: Some((0, U256::from(0))),
|
||||
hardforks: BTreeMap::from([
|
||||
(Hardfork::Frontier, ForkCondition::Block(0)),
|
||||
(Hardfork::Homestead, ForkCondition::Block(0)),
|
||||
(Hardfork::Tangerine, ForkCondition::Block(0)),
|
||||
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
|
||||
(Hardfork::Byzantium, ForkCondition::Block(0)),
|
||||
(Hardfork::Constantinople, ForkCondition::Block(0)),
|
||||
(Hardfork::Petersburg, ForkCondition::Block(0)),
|
||||
(Hardfork::Istanbul, ForkCondition::Block(0)),
|
||||
(Hardfork::MuirGlacier, ForkCondition::Block(0)),
|
||||
(Hardfork::Berlin, ForkCondition::Block(0)),
|
||||
(Hardfork::London, ForkCondition::Block(0)),
|
||||
(Hardfork::ArrowGlacier, ForkCondition::Block(0)),
|
||||
(Hardfork::GrayGlacier, ForkCondition::Block(0)),
|
||||
(
|
||||
Hardfork::Paris,
|
||||
ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) },
|
||||
),
|
||||
(Hardfork::Bedrock, ForkCondition::Block(0)),
|
||||
(Hardfork::Regolith, ForkCondition::Timestamp(0)),
|
||||
]),
|
||||
base_fee_params: BaseFeeParams::optimism(),
|
||||
prune_delete_limit: 1700,
|
||||
snapshot_block_interval: 1_000_000,
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
});
|
||||
|
||||
/// BaseFeeParams contains the config parameters that control block base fee computation
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct BaseFeeParams {
|
||||
@ -258,6 +382,28 @@ impl BaseFeeParams {
|
||||
elasticity_multiplier: EIP1559_DEFAULT_ELASTICITY_MULTIPLIER,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the base fee parameters for optimism goerli
|
||||
#[cfg(feature = "optimism")]
|
||||
pub const fn optimism_goerli() -> BaseFeeParams {
|
||||
BaseFeeParams {
|
||||
max_change_denominator:
|
||||
crate::constants::OP_GOERLI_EIP1559_DEFAULT_BASE_FEE_MAX_CHANGE_DENOMINATOR,
|
||||
elasticity_multiplier:
|
||||
crate::constants::OP_GOERLI_EIP1559_DEFAULT_ELASTICITY_MULTIPLIER,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the base fee parameters for optimism mainnet
|
||||
#[cfg(feature = "optimism")]
|
||||
pub const fn optimism() -> BaseFeeParams {
|
||||
BaseFeeParams {
|
||||
max_change_denominator:
|
||||
crate::constants::OP_MAINNET_EIP1559_DEFAULT_BASE_FEE_MAX_CHANGE_DENOMINATOR,
|
||||
elasticity_multiplier:
|
||||
crate::constants::OP_MAINNET_EIP1559_DEFAULT_ELASTICITY_MULTIPLIER,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An Ethereum chain specification.
|
||||
@ -335,6 +481,11 @@ impl ChainSpec {
|
||||
self.chain
|
||||
}
|
||||
|
||||
/// Returns `true` if this chain contains Optimism configuration.
|
||||
pub fn is_optimism(&self) -> bool {
|
||||
self.chain.is_optimism()
|
||||
}
|
||||
|
||||
/// Get the genesis block specification.
|
||||
///
|
||||
/// To get the header for the genesis block, use [`Self::genesis_header`] instead.
|
||||
@ -873,6 +1024,24 @@ impl ChainSpecBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Bedrock at genesis
|
||||
#[cfg(feature = "optimism")]
|
||||
pub fn bedrock_activated(mut self) -> Self {
|
||||
self = self.paris_activated();
|
||||
self.hardforks.insert(Hardfork::Bedrock, ForkCondition::Block(0));
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Regolith at the timestamp of activation.
|
||||
/// For post-bedrock op-stack chains, this will be at genesis.
|
||||
/// For pre-bedrock op-stack chains, this will be at the timestamp of regolith activation.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub fn regolith_activated(mut self) -> Self {
|
||||
self = self.bedrock_activated();
|
||||
self.hardforks.insert(Hardfork::Regolith, ForkCondition::Timestamp(0));
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the resulting [`ChainSpec`].
|
||||
///
|
||||
/// # Panics
|
||||
@ -1197,6 +1366,9 @@ mod tests {
|
||||
use bytes::BytesMut;
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
use crate::OP_GOERLI;
|
||||
|
||||
fn test_fork_ids(spec: &ChainSpec, cases: &[(Head, ForkId)]) {
|
||||
for (block, expected_id) in cases {
|
||||
let computed_id = spec.fork_id(block);
|
||||
@ -1798,6 +1970,28 @@ Post-merge hard forks (timestamp based):
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
#[test]
|
||||
fn optimism_goerli_forkids() {
|
||||
test_fork_ids(
|
||||
&OP_GOERLI,
|
||||
&[
|
||||
(
|
||||
Head { number: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash([0x6d, 0x63, 0x76, 0xbe]), next: 4061224 },
|
||||
),
|
||||
(
|
||||
Head { number: 4061224, timestamp: 1679079599, ..Default::default() },
|
||||
ForkId { hash: ForkHash([0x03, 0x47, 0x85, 0x69]), next: 1679079600 },
|
||||
),
|
||||
(
|
||||
Head { number: 4061224, timestamp: 1679079600, ..Default::default() },
|
||||
ForkId { hash: ForkHash([0x6d, 0x43, 0x1d, 0x6c]), next: 0 },
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Checks that time-based forks work
|
||||
///
|
||||
/// This is based off of the test vectors here: https://github.com/ethereum/go-ethereum/blob/5c8cc10d1e05c23ff1108022f4150749e73c0ca1/core/forkid/forkid_test.go#L155-L188
|
||||
|
||||
@ -60,6 +60,26 @@ pub const EIP1559_DEFAULT_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8;
|
||||
/// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)
|
||||
pub const EIP1559_DEFAULT_ELASTICITY_MULTIPLIER: u64 = 2;
|
||||
|
||||
/// Base fee max change denominator for Optimism Mainnet as defined in the Optimism
|
||||
/// [transaction costs](https://community.optimism.io/docs/developers/build/differences/#transaction-costs) doc.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub const OP_MAINNET_EIP1559_DEFAULT_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 50;
|
||||
|
||||
/// Base fee max change denominator for Optimism Mainnet as defined in the Optimism
|
||||
/// [transaction costs](https://community.optimism.io/docs/developers/build/differences/#transaction-costs) doc.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub const OP_MAINNET_EIP1559_DEFAULT_ELASTICITY_MULTIPLIER: u64 = 6;
|
||||
|
||||
/// Base fee max change denominator for Optimism Goerli as defined in the Optimism
|
||||
/// [transaction costs](https://community.optimism.io/docs/developers/build/differences/#transaction-costs) doc.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub const OP_GOERLI_EIP1559_DEFAULT_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 50;
|
||||
|
||||
/// Base fee max change denominator for Optimism Goerli as defined in the Optimism
|
||||
/// [transaction costs](https://community.optimism.io/docs/developers/build/differences/#transaction-costs) doc.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub const OP_GOERLI_EIP1559_DEFAULT_ELASTICITY_MULTIPLIER: u64 = 10;
|
||||
|
||||
/// Multiplier for converting gwei to wei.
|
||||
pub const GWEI_TO_WEI: u64 = 1_000_000_000;
|
||||
|
||||
@ -92,6 +112,14 @@ pub const HOLESKY_GENESIS_HASH: B256 =
|
||||
pub const DEV_GENESIS_HASH: B256 =
|
||||
b256!("2f980576711e3617a5e4d83dd539548ec0f7792007d505a3d2e9674833af2d7c");
|
||||
|
||||
/// Optimism goerli genesis hash.
|
||||
pub const GOERLI_OP_GENESIS: B256 =
|
||||
b256!("c1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1");
|
||||
|
||||
/// Base goerli genesis hash.
|
||||
pub const GOERLI_BASE_GENESIS: B256 =
|
||||
b256!("a3ab140f15ea7f7443a4702da64c10314eb04d488e72974e02e2d728096b4f76");
|
||||
|
||||
/// Keccak256 over empty array.
|
||||
pub const KECCAK_EMPTY: B256 =
|
||||
b256!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
|
||||
|
||||
@ -41,6 +41,12 @@ pub enum Hardfork {
|
||||
Shanghai,
|
||||
/// Cancun.
|
||||
Cancun,
|
||||
/// Bedrock.
|
||||
#[cfg(feature = "optimism")]
|
||||
Bedrock,
|
||||
/// Regolith
|
||||
#[cfg(feature = "optimism")]
|
||||
Regolith,
|
||||
}
|
||||
|
||||
impl Hardfork {
|
||||
@ -85,6 +91,10 @@ impl FromStr for Hardfork {
|
||||
"paris" => Hardfork::Paris,
|
||||
"shanghai" => Hardfork::Shanghai,
|
||||
"cancun" => Hardfork::Cancun,
|
||||
#[cfg(feature = "optimism")]
|
||||
"bedrock" => Hardfork::Bedrock,
|
||||
#[cfg(feature = "optimism")]
|
||||
"regolith" => Hardfork::Regolith,
|
||||
_ => return Err(format!("Unknown hardfork: {s}")),
|
||||
};
|
||||
Ok(hardfork)
|
||||
@ -150,6 +160,18 @@ mod tests {
|
||||
assert_eq!(hardforks, expected_hardforks);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "optimism")]
|
||||
fn check_op_hardfork_from_str() {
|
||||
let hardfork_str = ["beDrOck", "rEgOlITH"];
|
||||
let expected_hardforks = [Hardfork::Bedrock, Hardfork::Regolith];
|
||||
|
||||
let hardforks: Vec<Hardfork> =
|
||||
hardfork_str.iter().map(|h| Hardfork::from_str(h).unwrap()).collect();
|
||||
|
||||
assert_eq!(hardforks, expected_hardforks);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_nonexistent_hardfork_from_str() {
|
||||
assert!(Hardfork::from_str("not a hardfork").is_err());
|
||||
|
||||
@ -33,7 +33,6 @@ mod integer_list;
|
||||
mod log;
|
||||
mod net;
|
||||
mod peer;
|
||||
mod precaution;
|
||||
pub mod proofs;
|
||||
mod prune;
|
||||
mod receipt;
|
||||
@ -59,6 +58,8 @@ pub use chain::{
|
||||
DisplayHardforks, ForkCondition, ForkTimestamps, NamedChain, DEV, GOERLI, HOLESKY, MAINNET,
|
||||
SEPOLIA,
|
||||
};
|
||||
#[cfg(feature = "optimism")]
|
||||
pub use chain::{BASE_GOERLI, BASE_MAINNET, OP_GOERLI};
|
||||
pub use compression::*;
|
||||
pub use constants::{
|
||||
DEV_GENESIS_HASH, EMPTY_OMMER_ROOT_HASH, GOERLI_GENESIS_HASH, HOLESKY_GENESIS_HASH,
|
||||
@ -101,6 +102,8 @@ pub use transaction::{
|
||||
TxEip4844, TxHashOrNumber, TxLegacy, TxType, TxValue, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID,
|
||||
EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID,
|
||||
};
|
||||
#[cfg(feature = "optimism")]
|
||||
pub use transaction::{TxDeposit, DEPOSIT_TX_TYPE_ID};
|
||||
pub use withdrawal::Withdrawal;
|
||||
|
||||
// Re-exports
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
//! Helpers to ensure certain features are enabled or disabled.
|
||||
//!
|
||||
//! The motivation for this is to prevent that a binary is accidentally built with a feature that is
|
||||
//! not intended to be used.
|
||||
//!
|
||||
//! Currently conflicting features are: `value-u256` which is required by optimism.
|
||||
|
||||
/// A macro to ensure that the crate's features are compatible with ethereum
|
||||
#[macro_export]
|
||||
macro_rules! ensure_ethereum {
|
||||
() => {
|
||||
#[cfg(feature = "value-256")]
|
||||
compile_error!("The `value-256` feature is enabled but for `ethereum` it must be disabled: https://github.com/paradigmxyz/reth/issues/4891");
|
||||
};
|
||||
}
|
||||
|
||||
/// A macro to ensure that the crate's features are compatible with optimism
|
||||
#[macro_export]
|
||||
macro_rules! ensure_optimism {
|
||||
() => {
|
||||
#[cfg(not(feature = "value-256"))]
|
||||
compile_error!("The `value-256` feature is disabled but for `optimism` it must be enabled: https://github.com/paradigmxyz/reth/issues/4891");
|
||||
};
|
||||
}
|
||||
@ -4,8 +4,8 @@ use crate::{
|
||||
constants::EMPTY_OMMER_ROOT_HASH,
|
||||
keccak256,
|
||||
trie::{HashBuilder, Nibbles},
|
||||
Address, GenesisAccount, Header, ReceiptWithBloom, ReceiptWithBloomRef, TransactionSigned,
|
||||
Withdrawal, B256,
|
||||
Address, GenesisAccount, Header, Receipt, ReceiptWithBloom, ReceiptWithBloomRef,
|
||||
TransactionSigned, Withdrawal, B256,
|
||||
};
|
||||
use alloy_rlp::Encodable;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
@ -70,18 +70,55 @@ pub fn calculate_withdrawals_root(withdrawals: &[Withdrawal]) -> B256 {
|
||||
|
||||
/// Calculates the receipt root for a header.
|
||||
pub fn calculate_receipt_root(receipts: &[ReceiptWithBloom]) -> B256 {
|
||||
#[cfg(feature = "optimism")]
|
||||
{
|
||||
// There is a minor bug in op-geth and op-erigon where in the Regolith hardfork,
|
||||
// the receipt root calculation does not include the deposit nonce in the receipt
|
||||
// encoding. This will be fixd in the next hardfork, however for now, we must strip
|
||||
// the deposit nonce from the receipts before calculating the receipt root.
|
||||
let receipts = receipts
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|mut r| {
|
||||
r.receipt.deposit_nonce = None;
|
||||
r
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
ordered_trie_root_with_encoder(receipts.as_slice(), |r, buf| r.encode_inner(buf, false))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
ordered_trie_root_with_encoder(receipts, |r, buf| r.encode_inner(buf, false))
|
||||
}
|
||||
|
||||
/// Calculates the receipt root for a header for the reference type of [ReceiptWithBloom].
|
||||
/// Calculates the receipt root for a header for the reference type of [Receipt].
|
||||
///
|
||||
/// NOTE: Prefer [calculate_receipt_root] if you have log blooms memoized.
|
||||
pub fn calculate_receipt_root_ref<T>(receipts: &[&T]) -> B256
|
||||
where
|
||||
for<'a> ReceiptWithBloomRef<'a>: From<&'a T>,
|
||||
{
|
||||
pub fn calculate_receipt_root_ref(receipts: &[&Receipt]) -> B256 {
|
||||
#[cfg(feature = "optimism")]
|
||||
{
|
||||
// There is a minor bug in op-geth and op-erigon where in the Regolith hardfork,
|
||||
// the receipt root calculation does not include the deposit nonce in the receipt
|
||||
// encoding. This will be fixd in the next hardfork, however for now, we must strip
|
||||
// the deposit nonce from the receipts before calculating the receipt root.
|
||||
let receipts = receipts
|
||||
.iter()
|
||||
.map(|r| {
|
||||
let mut r = (*r).clone();
|
||||
r.deposit_nonce = None;
|
||||
r
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
ordered_trie_root_with_encoder(&receipts, |r, buf| {
|
||||
ReceiptWithBloomRef::from(r).encode_inner(buf, false)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
ordered_trie_root_with_encoder(receipts, |r, buf| {
|
||||
ReceiptWithBloomRef::from(r).encode_inner(buf, false)
|
||||
ReceiptWithBloomRef::from(*r).encode_inner(buf, false)
|
||||
})
|
||||
}
|
||||
|
||||
@ -146,9 +183,14 @@ pub mod triehash {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
b256, bloom, constants::EMPTY_ROOT_HASH, hex, Block, Log, Receipt, TxType, B256, GOERLI,
|
||||
bloom,
|
||||
constants::EMPTY_ROOT_HASH,
|
||||
hex_literal::hex,
|
||||
proofs::{calculate_receipt_root, calculate_transaction_root, genesis_state_root},
|
||||
Address, Block, GenesisAccount, Log, Receipt, ReceiptWithBloom, TxType, B256, GOERLI,
|
||||
HOLESKY, MAINNET, SEPOLIA, U256,
|
||||
};
|
||||
use alloy_primitives::b256;
|
||||
use alloy_rlp::Decodable;
|
||||
|
||||
#[test]
|
||||
@ -161,6 +203,233 @@ mod tests {
|
||||
assert_eq!(block.transactions_root, tx_root, "Must be the same");
|
||||
}
|
||||
|
||||
/// Tests that the receipt root is computed correctly for the regolith block.
|
||||
/// This was implemented due to a minor bug in op-geth and op-erigon where in
|
||||
/// the Regolith hardfork, the receipt root calculation does not include the
|
||||
/// deposit nonce in the receipt encoding.
|
||||
/// To fix this an op-reth patch was applied to the receipt root calculation
|
||||
/// to strip the deposit nonce from each receipt before calculating the root.
|
||||
#[cfg(feature = "optimism")]
|
||||
#[test]
|
||||
fn check_optimism_receipt_root() {
|
||||
use crate::{Bloom, Bytes};
|
||||
|
||||
let receipts = vec![
|
||||
// 0xb0d6ee650637911394396d81172bd1c637d568ed1fbddab0daddfca399c58b53
|
||||
ReceiptWithBloom {
|
||||
receipt: Receipt {
|
||||
tx_type: TxType::DEPOSIT,
|
||||
success: true,
|
||||
cumulative_gas_used: 46913,
|
||||
logs: vec![],
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: Some(4012991u64),
|
||||
},
|
||||
bloom: Bloom(hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into()),
|
||||
},
|
||||
// 0x2f433586bae30573c393adfa02bc81d2a1888a3d6c9869f473fb57245166bd9a
|
||||
ReceiptWithBloom {
|
||||
receipt: Receipt {
|
||||
tx_type: TxType::EIP1559,
|
||||
success: true,
|
||||
cumulative_gas_used: 118083,
|
||||
logs: vec![
|
||||
Log {
|
||||
address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
|
||||
topics: vec![
|
||||
b256!("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"),
|
||||
b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
|
||||
b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
|
||||
b256!("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001")),
|
||||
},
|
||||
Log {
|
||||
address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
|
||||
topics: vec![
|
||||
b256!("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"),
|
||||
b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
|
||||
b256!("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001")),
|
||||
},
|
||||
Log {
|
||||
address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
|
||||
topics: vec![
|
||||
b256!("0eb774bb9698a73583fe07b6972cf2dcc08d1d97581a22861f45feb86b395820"),
|
||||
b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
|
||||
b256!("000000000000000000000000c498902843af527e674846bb7edefa8ad62b8fb9"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000003")),
|
||||
},
|
||||
],
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: None,
|
||||
},
|
||||
bloom: Bloom(hex!("00001000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000040000000000004000000000080000000000000000000000000000000000000000000000000000008000000000000080020000000000000000000000000002000000000000000000000000000080000010000").into()),
|
||||
},
|
||||
// 0x6c33676e8f6077f46a62eabab70bc6d1b1b18a624b0739086d77093a1ecf8266
|
||||
ReceiptWithBloom {
|
||||
receipt: Receipt {
|
||||
tx_type: TxType::EIP1559,
|
||||
success: true,
|
||||
cumulative_gas_used: 189253,
|
||||
logs: vec![
|
||||
Log {
|
||||
address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
|
||||
topics: vec![
|
||||
b256!("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"),
|
||||
b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
|
||||
b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
|
||||
b256!("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001")),
|
||||
},
|
||||
Log {
|
||||
address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
|
||||
topics: vec![
|
||||
b256!("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62"),
|
||||
b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
|
||||
b256!("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001")),
|
||||
},
|
||||
Log {
|
||||
address: hex!("ddb6dcce6b794415145eb5caa6cd335aeda9c272").into(),
|
||||
topics: vec![
|
||||
b256!("0eb774bb9698a73583fe07b6972cf2dcc08d1d97581a22861f45feb86b395820"),
|
||||
b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
|
||||
b256!("0000000000000000000000009d521a04bee134ff8136d2ec957e5bc8c50394ec"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000003")),
|
||||
},
|
||||
],
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: None,
|
||||
},
|
||||
bloom: Bloom(hex!("00000000000000000000200000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000002000000000020000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000040000000000004000000000080000000000000000000000000000000000000000000000000000008000000000000080020000000000000000000000000002000000000000000000000000000080000000000").into()),
|
||||
},
|
||||
// 0x4d3ecbef04ba7ce7f5ab55be0c61978ca97c117d7da448ed9771d4ff0c720a3f
|
||||
ReceiptWithBloom {
|
||||
receipt: Receipt {
|
||||
tx_type: TxType::EIP1559,
|
||||
success: true,
|
||||
cumulative_gas_used: 346969,
|
||||
logs: vec![
|
||||
Log {
|
||||
address: hex!("4200000000000000000000000000000000000006").into(),
|
||||
topics: vec![
|
||||
b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
|
||||
b256!("000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"),
|
||||
b256!("0000000000000000000000002992607c1614484fe6d865088e5c048f0650afd4"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000018de76816d8000")),
|
||||
},
|
||||
Log {
|
||||
address: hex!("cf8e7e6b26f407dee615fc4db18bf829e7aa8c09").into(),
|
||||
topics: vec![
|
||||
b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
|
||||
b256!("0000000000000000000000002992607c1614484fe6d865088e5c048f0650afd4"),
|
||||
b256!("0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("000000000000000000000000000000000000000000000002d24d8e9ac1aa79e2")),
|
||||
},
|
||||
Log {
|
||||
address: hex!("2992607c1614484fe6d865088e5c048f0650afd4").into(),
|
||||
topics: vec![
|
||||
b256!("1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("000000000000000000000000000000000000000000000009bd50642785c15736000000000000000000000000000000000000000000011bb7ac324f724a29bbbf")),
|
||||
},
|
||||
Log {
|
||||
address: hex!("2992607c1614484fe6d865088e5c048f0650afd4").into(),
|
||||
topics: vec![
|
||||
b256!("d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822"),
|
||||
b256!("00000000000000000000000029843613c7211d014f5dd5718cf32bcd314914cb"),
|
||||
b256!("0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000018de76816d800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d24d8e9ac1aa79e2")),
|
||||
},
|
||||
Log {
|
||||
address: hex!("6d0f8d488b669aa9ba2d0f0b7b75a88bf5051cd3").into(),
|
||||
topics: vec![
|
||||
b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
|
||||
b256!("0000000000000000000000008dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09"),
|
||||
b256!("000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000014bc73062aea8093")),
|
||||
},
|
||||
Log {
|
||||
address: hex!("8dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09").into(),
|
||||
topics: vec![
|
||||
b256!("1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000002f122cfadc1ca82a35000000000000000000000000000000000000000000000665879dc0609945d6d1")),
|
||||
},
|
||||
Log {
|
||||
address: hex!("8dbffe4c8bf3caf5deae3a99b50cfcf3648cbc09").into(),
|
||||
topics: vec![
|
||||
b256!("d78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822"),
|
||||
b256!("00000000000000000000000029843613c7211d014f5dd5718cf32bcd314914cb"),
|
||||
b256!("000000000000000000000000c3feb4ef4c2a5af77add15c95bd98f6b43640cc8"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d24d8e9ac1aa79e200000000000000000000000000000000000000000000000014bc73062aea80930000000000000000000000000000000000000000000000000000000000000000")),
|
||||
},
|
||||
],
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: None,
|
||||
},
|
||||
bloom: Bloom(hex!("00200000000000000000000080000000000000000000000000040000100004000000000000000000000000100000000000000000000000000000100000000000000000000000000002000008000000200000000200000000020000000000000040000000000000000400000200000000000000000000000000000010000000000400000000010400000000000000000000000000002000c80000004080002000000000000000400200000000800000000000000000000000000000000000000000000002000000000000000000000000000000000100001000000000000000000000002000000000000000000000010000000000000000000000800000800000").into()),
|
||||
},
|
||||
// 0xf738af5eb00ba23dbc1be2dbce41dbc0180f0085b7fb46646e90bf737af90351
|
||||
ReceiptWithBloom {
|
||||
receipt: Receipt {
|
||||
tx_type: TxType::EIP1559,
|
||||
success: true,
|
||||
cumulative_gas_used: 623249,
|
||||
logs: vec![
|
||||
Log {
|
||||
address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(),
|
||||
topics: vec![
|
||||
b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
|
||||
b256!("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
b256!("000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"),
|
||||
b256!("000000000000000000000000000000000000000000000000000000000011a1d3"),
|
||||
],
|
||||
data: Default::default(),
|
||||
},
|
||||
Log {
|
||||
address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(),
|
||||
topics: vec![
|
||||
b256!("9d89e36eadf856db0ad9ffb5a569e07f95634dddd9501141ecf04820484ad0dc"),
|
||||
b256!("000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"),
|
||||
b256!("000000000000000000000000000000000000000000000000000000000011a1d3"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d515141646b33736538396b47716577395256567a316b68643548375562476d4d4a485a62566f386a6d346f4a2f30000000000000000000")),
|
||||
},
|
||||
Log {
|
||||
address: hex!("ac6564f3718837caadd42eed742d75c12b90a052").into(),
|
||||
topics: vec![
|
||||
b256!("110d160a1bedeea919a88fbc4b2a9fb61b7e664084391b6ca2740db66fef80fe"),
|
||||
b256!("00000000000000000000000084d47f6eea8f8d87910448325519d1bb45c2972a"),
|
||||
b256!("000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e"),
|
||||
b256!("000000000000000000000000000000000000000000000000000000000011a1d3"),
|
||||
],
|
||||
data: Bytes::from_static(&hex!("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a4fa7f3fbf0677f254ebdb1646146864c305b76e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007717500762343034303661353035646234633961386163316433306335633332303265370000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d515141646b33736538396b47716577395256567a316b68643548375562476d4d4a485a62566f386a6d346f4a2f30000000000000000000")),
|
||||
},
|
||||
],
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: None,
|
||||
},
|
||||
bloom: Bloom(hex!("00000000000000000000000000000000400000000000000000000000000000000000004000000000000001000000000000000002000000000100000000000000000000000000000000000008000000000000000000000000000000000000000004000000020000000000000000000800000000000000000000000010200100200008000002000000000000000000800000000000000000000002000000000000000000000000000000080000000000000000000000004000000000000000000000000002000000000000000000000000000000000000200000000000000020002000000000000000002000000000000000000000000000000000000000000000").into()),
|
||||
},
|
||||
];
|
||||
let root = calculate_receipt_root(&receipts);
|
||||
assert_eq!(root, b256!("e255fed45eae7ede0556fe4fabc77b0d294d18781a5a581cab09127bc4cd9ffb"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_receipt_root() {
|
||||
let logs = vec![Log { address: Address::ZERO, topics: vec![], data: Default::default() }];
|
||||
@ -171,6 +440,8 @@ mod tests {
|
||||
success: true,
|
||||
cumulative_gas_used: 102068,
|
||||
logs,
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: None,
|
||||
},
|
||||
bloom,
|
||||
};
|
||||
|
||||
@ -6,14 +6,18 @@ use crate::{
|
||||
};
|
||||
use alloy_rlp::{length_of_length, Decodable, Encodable};
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use reth_codecs::{main_codec, Compact, CompactZstd};
|
||||
use reth_codecs::{add_arbitrary_tests, main_codec, Compact, CompactZstd};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
use proptest::strategy::Strategy;
|
||||
|
||||
/// Receipt containing result of transaction execution.
|
||||
#[main_codec(zstd)]
|
||||
#[main_codec(no_arbitrary, zstd)]
|
||||
#[add_arbitrary_tests]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Default)]
|
||||
pub struct Receipt {
|
||||
/// Receipt type.
|
||||
@ -25,13 +29,10 @@ pub struct Receipt {
|
||||
/// Gas used
|
||||
pub cumulative_gas_used: u64,
|
||||
/// Log send from contracts.
|
||||
#[cfg_attr(
|
||||
any(test, feature = "arbitrary"),
|
||||
proptest(
|
||||
strategy = "proptest::collection::vec(proptest::arbitrary::any::<Log>(), 0..=20)"
|
||||
)
|
||||
)]
|
||||
pub logs: Vec<Log>,
|
||||
/// Deposit nonce for Optimism deposited transactions
|
||||
#[cfg(feature = "optimism")]
|
||||
pub deposit_nonce: Option<u64>,
|
||||
}
|
||||
|
||||
impl Receipt {
|
||||
@ -181,6 +182,60 @@ impl ReceiptWithBloom {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl proptest::arbitrary::Arbitrary for Receipt {
|
||||
type Parameters = ();
|
||||
|
||||
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
|
||||
use proptest::prelude::{any, prop_compose};
|
||||
|
||||
prop_compose! {
|
||||
fn arbitrary_receipt()(tx_type in any::<TxType>(),
|
||||
success in any::<bool>(),
|
||||
cumulative_gas_used in any::<u64>(),
|
||||
logs in proptest::collection::vec(proptest::arbitrary::any::<Log>(), 0..=20),
|
||||
_deposit_nonce in any::<Option<u64>>()) -> Receipt
|
||||
{
|
||||
Receipt { tx_type,
|
||||
success,
|
||||
cumulative_gas_used,
|
||||
logs,
|
||||
// Only receipts for deposit transactions may contain a deposit nonce
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: (tx_type == TxType::DEPOSIT).then_some(_deposit_nonce).flatten()
|
||||
}
|
||||
}
|
||||
};
|
||||
arbitrary_receipt().boxed()
|
||||
}
|
||||
|
||||
type Strategy = proptest::strategy::BoxedStrategy<Receipt>;
|
||||
}
|
||||
|
||||
#[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 =
|
||||
if tx_type == TxType::DEPOSIT { Option::<u64>::arbitrary(u)? } else { None };
|
||||
|
||||
Ok(Self {
|
||||
tx_type,
|
||||
success,
|
||||
cumulative_gas_used,
|
||||
logs,
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ReceiptWithBloom {
|
||||
/// Encode receipt with or without the header data.
|
||||
pub fn encode_inner(&self, out: &mut dyn BufMut, with_header: bool) {
|
||||
@ -201,7 +256,27 @@ impl ReceiptWithBloom {
|
||||
let bloom = Decodable::decode(b)?;
|
||||
let logs = alloy_rlp::Decodable::decode(b)?;
|
||||
|
||||
let this = Self { receipt: Receipt { tx_type, success, cumulative_gas_used, logs }, bloom };
|
||||
let receipt = match tx_type {
|
||||
#[cfg(feature = "optimism")]
|
||||
TxType::DEPOSIT => {
|
||||
let consumed = started_len - b.len();
|
||||
let has_nonce = rlp_head.payload_length - consumed > 0;
|
||||
let deposit_nonce =
|
||||
if has_nonce { Some(alloy_rlp::Decodable::decode(b)?) } else { None };
|
||||
|
||||
Receipt { tx_type, success, cumulative_gas_used, logs, deposit_nonce }
|
||||
}
|
||||
_ => Receipt {
|
||||
tx_type,
|
||||
success,
|
||||
cumulative_gas_used,
|
||||
logs,
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: None,
|
||||
},
|
||||
};
|
||||
|
||||
let this = Self { receipt, bloom };
|
||||
let consumed = started_len - b.len();
|
||||
if consumed != rlp_head.payload_length {
|
||||
return Err(alloy_rlp::Error::ListLengthMismatch {
|
||||
@ -239,17 +314,25 @@ impl Decodable for ReceiptWithBloom {
|
||||
let receipt_type = *buf.first().ok_or(alloy_rlp::Error::Custom(
|
||||
"typed receipt cannot be decoded from an empty slice",
|
||||
))?;
|
||||
if receipt_type == 0x01 {
|
||||
buf.advance(1);
|
||||
Self::decode_receipt(buf, TxType::EIP2930)
|
||||
} else if receipt_type == 0x02 {
|
||||
buf.advance(1);
|
||||
Self::decode_receipt(buf, TxType::EIP1559)
|
||||
} else if receipt_type == 0x03 {
|
||||
buf.advance(1);
|
||||
Self::decode_receipt(buf, TxType::EIP4844)
|
||||
} else {
|
||||
Err(alloy_rlp::Error::Custom("invalid receipt type"))
|
||||
match receipt_type {
|
||||
0x01 => {
|
||||
buf.advance(1);
|
||||
Self::decode_receipt(buf, TxType::EIP2930)
|
||||
}
|
||||
0x02 => {
|
||||
buf.advance(1);
|
||||
Self::decode_receipt(buf, TxType::EIP1559)
|
||||
}
|
||||
0x03 => {
|
||||
buf.advance(1);
|
||||
Self::decode_receipt(buf, TxType::EIP4844)
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
0x7E => {
|
||||
buf.advance(1);
|
||||
Self::decode_receipt(buf, TxType::DEPOSIT)
|
||||
}
|
||||
_ => Err(alloy_rlp::Error::Custom("invalid receipt type")),
|
||||
}
|
||||
}
|
||||
Ordering::Equal => {
|
||||
@ -317,6 +400,13 @@ impl<'a> ReceiptWithBloomEncoder<'a> {
|
||||
rlp_head.payload_length += self.bloom.length();
|
||||
rlp_head.payload_length += self.receipt.logs.length();
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
if self.receipt.tx_type == TxType::DEPOSIT {
|
||||
if let Some(deposit_nonce) = self.receipt.deposit_nonce {
|
||||
rlp_head.payload_length += deposit_nonce.length();
|
||||
}
|
||||
}
|
||||
|
||||
rlp_head
|
||||
}
|
||||
|
||||
@ -327,6 +417,12 @@ impl<'a> ReceiptWithBloomEncoder<'a> {
|
||||
self.receipt.cumulative_gas_used.encode(out);
|
||||
self.bloom.encode(out);
|
||||
self.receipt.logs.encode(out);
|
||||
#[cfg(feature = "optimism")]
|
||||
if self.receipt.tx_type == TxType::DEPOSIT {
|
||||
if let Some(deposit_nonce) = self.receipt.deposit_nonce {
|
||||
deposit_nonce.encode(out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode receipt with or without the header data.
|
||||
@ -355,6 +451,10 @@ impl<'a> ReceiptWithBloomEncoder<'a> {
|
||||
TxType::EIP4844 => {
|
||||
out.put_u8(0x03);
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
TxType::DEPOSIT => {
|
||||
out.put_u8(0x7E);
|
||||
}
|
||||
_ => unreachable!("legacy handled; qed."),
|
||||
}
|
||||
out.put_slice(payload.as_ref());
|
||||
@ -374,7 +474,7 @@ impl<'a> Encodable for ReceiptWithBloomEncoder<'a> {
|
||||
fn length(&self) -> usize {
|
||||
let mut payload_len = self.receipt_length();
|
||||
// account for eip-2718 type prefix and set the list
|
||||
if matches!(self.receipt.tx_type, TxType::EIP1559 | TxType::EIP2930 | TxType::EIP4844) {
|
||||
if !matches!(self.receipt.tx_type, TxType::Legacy) {
|
||||
payload_len += 1;
|
||||
// we include a string header for typed receipts, so include the length here
|
||||
payload_len += length_of_length(payload_len);
|
||||
@ -410,6 +510,8 @@ mod tests {
|
||||
data: bytes!("0100ff"),
|
||||
}],
|
||||
success: false,
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: None,
|
||||
},
|
||||
bloom: [0; 256].into(),
|
||||
};
|
||||
@ -440,6 +542,8 @@ mod tests {
|
||||
data: bytes!("0100ff"),
|
||||
}],
|
||||
success: false,
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: None,
|
||||
},
|
||||
bloom: [0; 256].into(),
|
||||
};
|
||||
@ -448,6 +552,31 @@ mod tests {
|
||||
assert_eq!(receipt, expected);
|
||||
}
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
#[test]
|
||||
fn decode_deposit_receipt_regolith_roundtrip() {
|
||||
let data = hex!("7ef9010c0182b741b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0833d3bbf");
|
||||
|
||||
// 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),
|
||||
},
|
||||
bloom: [0; 256].into(),
|
||||
};
|
||||
|
||||
let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap();
|
||||
assert_eq!(receipt, expected);
|
||||
|
||||
let mut buf = BytesMut::default();
|
||||
receipt.encode_inner(&mut buf, false);
|
||||
assert_eq!(buf.freeze(), &data[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gigantic_receipt() {
|
||||
let receipt = Receipt {
|
||||
@ -470,6 +599,8 @@ mod tests {
|
||||
data: Bytes::from(vec![1; 0xffffff]),
|
||||
},
|
||||
],
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: None,
|
||||
};
|
||||
|
||||
let mut data = vec![];
|
||||
|
||||
@ -8,6 +8,15 @@ pub fn revm_spec_by_timestamp_after_merge(
|
||||
chain_spec: &ChainSpec,
|
||||
timestamp: u64,
|
||||
) -> revm_primitives::SpecId {
|
||||
#[cfg(feature = "optimism")]
|
||||
if chain_spec.is_optimism() {
|
||||
if chain_spec.fork(Hardfork::Regolith).active_at_timestamp(timestamp) {
|
||||
return revm_primitives::REGOLITH
|
||||
} else {
|
||||
return revm_primitives::BEDROCK
|
||||
}
|
||||
}
|
||||
|
||||
if chain_spec.is_cancun_active_at_timestamp(timestamp) {
|
||||
revm_primitives::CANCUN
|
||||
} else if chain_spec.is_shanghai_active_at_timestamp(timestamp) {
|
||||
@ -19,6 +28,15 @@ pub fn revm_spec_by_timestamp_after_merge(
|
||||
|
||||
/// return revm_spec from spec configuration.
|
||||
pub fn revm_spec(chain_spec: &ChainSpec, block: Head) -> revm_primitives::SpecId {
|
||||
#[cfg(feature = "optimism")]
|
||||
if chain_spec.is_optimism() {
|
||||
if chain_spec.fork(Hardfork::Regolith).active_at_head(&block) {
|
||||
return revm_primitives::REGOLITH
|
||||
} else if chain_spec.fork(Hardfork::Bedrock).active_at_head(&block) {
|
||||
return revm_primitives::BEDROCK
|
||||
}
|
||||
}
|
||||
|
||||
if chain_spec.fork(Hardfork::Cancun).active_at_head(&block) {
|
||||
revm_primitives::CANCUN
|
||||
} else if chain_spec.fork(Hardfork::Shanghai).active_at_head(&block) {
|
||||
@ -112,6 +130,23 @@ mod tests {
|
||||
revm_spec(&ChainSpecBuilder::mainnet().frontier_activated().build(), Head::default()),
|
||||
revm_primitives::FRONTIER
|
||||
);
|
||||
#[cfg(feature = "optimism")]
|
||||
{
|
||||
#[inline(always)]
|
||||
fn op_cs(f: impl FnOnce(ChainSpecBuilder) -> ChainSpecBuilder) -> ChainSpec {
|
||||
let cs = ChainSpecBuilder::mainnet().chain(crate::Chain::Id(10));
|
||||
f(cs).build()
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
revm_spec(&op_cs(|cs| cs.bedrock_activated()), Head::default()),
|
||||
revm_primitives::BEDROCK
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec(&op_cs(|cs| cs.regolith_activated()), Head::default()),
|
||||
revm_primitives::REGOLITH
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -4,9 +4,12 @@ use crate::{
|
||||
revm::config::revm_spec,
|
||||
revm_primitives::{AnalysisKind, BlockEnv, CfgEnv, Env, SpecId, TransactTo, TxEnv},
|
||||
Address, Bytes, Chain, ChainSpec, Head, Header, Transaction, TransactionKind,
|
||||
TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxEip4844, TxLegacy, B256, U256,
|
||||
TransactionSignedEcRecovered, B256, U256,
|
||||
};
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
use revm_primitives::OptimismFields;
|
||||
|
||||
/// Convenience function to call both [fill_cfg_env] and [fill_block_env]
|
||||
pub fn fill_cfg_and_block_env(
|
||||
cfg: &mut CfgEnv,
|
||||
@ -41,6 +44,11 @@ pub fn fill_cfg_env(
|
||||
cfg_env.chain_id = chain_spec.chain().id();
|
||||
cfg_env.spec_id = spec_id;
|
||||
cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse;
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
{
|
||||
cfg_env.optimism = chain_spec.is_optimism();
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill block environment from Block.
|
||||
@ -109,7 +117,17 @@ pub fn recover_header_signer(header: &Header) -> Option<Address> {
|
||||
/// Returns a new [TxEnv] filled with the transaction's data.
|
||||
pub fn tx_env_with_recovered(transaction: &TransactionSignedEcRecovered) -> TxEnv {
|
||||
let mut tx_env = TxEnv::default();
|
||||
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
fill_tx_env(&mut tx_env, transaction.as_ref(), transaction.signer());
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
{
|
||||
let mut envelope_buf = Vec::with_capacity(transaction.length_without_header());
|
||||
transaction.encode_enveloped(&mut envelope_buf);
|
||||
fill_tx_env(&mut tx_env, transaction.as_ref(), transaction.signer(), envelope_buf.into());
|
||||
}
|
||||
|
||||
tx_env
|
||||
}
|
||||
|
||||
@ -149,6 +167,13 @@ pub fn fill_tx_env_with_beacon_root_contract_call(env: &mut Env, parent_beacon_b
|
||||
// blob fields can be None for this tx
|
||||
blob_hashes: Vec::new(),
|
||||
max_fee_per_blob_gas: None,
|
||||
#[cfg(feature = "optimism")]
|
||||
optimism: OptimismFields {
|
||||
source_hash: None,
|
||||
mint: None,
|
||||
is_system_transaction: Some(false),
|
||||
enveloped_tx: None,
|
||||
},
|
||||
};
|
||||
|
||||
// ensure the block gas limit is >= the tx
|
||||
@ -159,63 +184,65 @@ pub fn fill_tx_env_with_beacon_root_contract_call(env: &mut Env, parent_beacon_b
|
||||
}
|
||||
|
||||
/// Fill transaction environment from [TransactionSignedEcRecovered].
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
pub fn fill_tx_env_with_recovered(tx_env: &mut TxEnv, transaction: &TransactionSignedEcRecovered) {
|
||||
fill_tx_env(tx_env, transaction.as_ref(), transaction.signer())
|
||||
fill_tx_env(tx_env, transaction.as_ref(), transaction.signer());
|
||||
}
|
||||
|
||||
/// Fill transaction environment from [TransactionSignedEcRecovered] and the given envelope.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub fn fill_tx_env_with_recovered(
|
||||
tx_env: &mut TxEnv,
|
||||
transaction: &TransactionSignedEcRecovered,
|
||||
envelope: Bytes,
|
||||
) {
|
||||
fill_tx_env(tx_env, transaction.as_ref(), transaction.signer(), envelope);
|
||||
}
|
||||
|
||||
/// Fill transaction environment from a [Transaction] and the given sender address.
|
||||
pub fn fill_tx_env<T>(tx_env: &mut TxEnv, transaction: T, sender: Address)
|
||||
where
|
||||
pub fn fill_tx_env<T>(
|
||||
tx_env: &mut TxEnv,
|
||||
transaction: T,
|
||||
sender: Address,
|
||||
#[cfg(feature = "optimism")] envelope: Bytes,
|
||||
) where
|
||||
T: AsRef<Transaction>,
|
||||
{
|
||||
tx_env.caller = sender;
|
||||
match transaction.as_ref() {
|
||||
Transaction::Legacy(TxLegacy {
|
||||
nonce,
|
||||
chain_id,
|
||||
gas_price,
|
||||
gas_limit,
|
||||
to,
|
||||
value,
|
||||
input,
|
||||
}) => {
|
||||
tx_env.gas_limit = *gas_limit;
|
||||
tx_env.gas_price = U256::from(*gas_price);
|
||||
Transaction::Legacy(tx) => {
|
||||
tx_env.gas_limit = tx.gas_limit;
|
||||
tx_env.gas_price = U256::from(tx.gas_price);
|
||||
tx_env.gas_priority_fee = None;
|
||||
tx_env.transact_to = match to {
|
||||
TransactionKind::Call(to) => TransactTo::Call(*to),
|
||||
tx_env.transact_to = match tx.to {
|
||||
TransactionKind::Call(to) => TransactTo::Call(to),
|
||||
TransactionKind::Create => TransactTo::create(),
|
||||
};
|
||||
tx_env.value = (*value).into();
|
||||
tx_env.data = input.clone();
|
||||
tx_env.chain_id = *chain_id;
|
||||
tx_env.nonce = Some(*nonce);
|
||||
tx_env.value = tx.value.into();
|
||||
tx_env.data = tx.input.clone();
|
||||
tx_env.chain_id = tx.chain_id;
|
||||
tx_env.nonce = Some(tx.nonce);
|
||||
tx_env.access_list.clear();
|
||||
tx_env.blob_hashes.clear();
|
||||
tx_env.max_fee_per_blob_gas.take();
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
fill_op_tx_env(tx_env, transaction, envelope);
|
||||
}
|
||||
Transaction::Eip2930(TxEip2930 {
|
||||
nonce,
|
||||
chain_id,
|
||||
gas_price,
|
||||
gas_limit,
|
||||
to,
|
||||
value,
|
||||
input,
|
||||
access_list,
|
||||
}) => {
|
||||
tx_env.gas_limit = *gas_limit;
|
||||
tx_env.gas_price = U256::from(*gas_price);
|
||||
Transaction::Eip2930(tx) => {
|
||||
tx_env.gas_limit = tx.gas_limit;
|
||||
tx_env.gas_price = U256::from(tx.gas_price);
|
||||
tx_env.gas_priority_fee = None;
|
||||
tx_env.transact_to = match to {
|
||||
TransactionKind::Call(to) => TransactTo::Call(*to),
|
||||
tx_env.transact_to = match tx.to {
|
||||
TransactionKind::Call(to) => TransactTo::Call(to),
|
||||
TransactionKind::Create => TransactTo::create(),
|
||||
};
|
||||
tx_env.value = (*value).into();
|
||||
tx_env.data = input.clone();
|
||||
tx_env.chain_id = Some(*chain_id);
|
||||
tx_env.nonce = Some(*nonce);
|
||||
tx_env.access_list = access_list
|
||||
tx_env.value = tx.value.into();
|
||||
tx_env.data = tx.input.clone();
|
||||
tx_env.chain_id = Some(tx.chain_id);
|
||||
tx_env.nonce = Some(tx.nonce);
|
||||
tx_env.access_list = tx
|
||||
.access_list
|
||||
.0
|
||||
.iter()
|
||||
.map(|l| {
|
||||
@ -224,30 +251,24 @@ where
|
||||
.collect();
|
||||
tx_env.blob_hashes.clear();
|
||||
tx_env.max_fee_per_blob_gas.take();
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
fill_op_tx_env(tx_env, transaction, envelope);
|
||||
}
|
||||
Transaction::Eip1559(TxEip1559 {
|
||||
nonce,
|
||||
chain_id,
|
||||
gas_limit,
|
||||
max_fee_per_gas,
|
||||
max_priority_fee_per_gas,
|
||||
to,
|
||||
value,
|
||||
input,
|
||||
access_list,
|
||||
}) => {
|
||||
tx_env.gas_limit = *gas_limit;
|
||||
tx_env.gas_price = U256::from(*max_fee_per_gas);
|
||||
tx_env.gas_priority_fee = Some(U256::from(*max_priority_fee_per_gas));
|
||||
tx_env.transact_to = match to {
|
||||
TransactionKind::Call(to) => TransactTo::Call(*to),
|
||||
Transaction::Eip1559(tx) => {
|
||||
tx_env.gas_limit = tx.gas_limit;
|
||||
tx_env.gas_price = U256::from(tx.max_fee_per_gas);
|
||||
tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas));
|
||||
tx_env.transact_to = match tx.to {
|
||||
TransactionKind::Call(to) => TransactTo::Call(to),
|
||||
TransactionKind::Create => TransactTo::create(),
|
||||
};
|
||||
tx_env.value = (*value).into();
|
||||
tx_env.data = input.clone();
|
||||
tx_env.chain_id = Some(*chain_id);
|
||||
tx_env.nonce = Some(*nonce);
|
||||
tx_env.access_list = access_list
|
||||
tx_env.value = tx.value.into();
|
||||
tx_env.data = tx.input.clone();
|
||||
tx_env.chain_id = Some(tx.chain_id);
|
||||
tx_env.nonce = Some(tx.nonce);
|
||||
tx_env.access_list = tx
|
||||
.access_list
|
||||
.0
|
||||
.iter()
|
||||
.map(|l| {
|
||||
@ -256,40 +277,74 @@ where
|
||||
.collect();
|
||||
tx_env.blob_hashes.clear();
|
||||
tx_env.max_fee_per_blob_gas.take();
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
fill_op_tx_env(tx_env, transaction, envelope);
|
||||
}
|
||||
Transaction::Eip4844(TxEip4844 {
|
||||
nonce,
|
||||
chain_id,
|
||||
gas_limit,
|
||||
max_fee_per_gas,
|
||||
max_priority_fee_per_gas,
|
||||
to,
|
||||
value,
|
||||
access_list,
|
||||
blob_versioned_hashes,
|
||||
max_fee_per_blob_gas,
|
||||
input,
|
||||
}) => {
|
||||
tx_env.gas_limit = *gas_limit;
|
||||
tx_env.gas_price = U256::from(*max_fee_per_gas);
|
||||
tx_env.gas_priority_fee = Some(U256::from(*max_priority_fee_per_gas));
|
||||
tx_env.transact_to = match to {
|
||||
TransactionKind::Call(to) => TransactTo::Call(*to),
|
||||
Transaction::Eip4844(tx) => {
|
||||
tx_env.gas_limit = tx.gas_limit;
|
||||
tx_env.gas_price = U256::from(tx.max_fee_per_gas);
|
||||
tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas));
|
||||
tx_env.transact_to = match tx.to {
|
||||
TransactionKind::Call(to) => TransactTo::Call(to),
|
||||
TransactionKind::Create => TransactTo::create(),
|
||||
};
|
||||
tx_env.value = (*value).into();
|
||||
tx_env.data = input.clone();
|
||||
tx_env.chain_id = Some(*chain_id);
|
||||
tx_env.nonce = Some(*nonce);
|
||||
tx_env.access_list = access_list
|
||||
tx_env.value = tx.value.into();
|
||||
tx_env.data = tx.input.clone();
|
||||
tx_env.chain_id = Some(tx.chain_id);
|
||||
tx_env.nonce = Some(tx.nonce);
|
||||
tx_env.access_list = tx
|
||||
.access_list
|
||||
.0
|
||||
.iter()
|
||||
.map(|l| {
|
||||
(l.address, l.storage_keys.iter().map(|k| U256::from_be_bytes(k.0)).collect())
|
||||
})
|
||||
.collect();
|
||||
tx_env.blob_hashes = blob_versioned_hashes.clone();
|
||||
tx_env.max_fee_per_blob_gas = Some(U256::from(*max_fee_per_blob_gas));
|
||||
tx_env.blob_hashes = tx.blob_versioned_hashes.clone();
|
||||
tx_env.max_fee_per_blob_gas = Some(U256::from(tx.max_fee_per_blob_gas));
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
fill_op_tx_env(tx_env, transaction, envelope);
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(tx) => {
|
||||
tx_env.gas_limit = tx.gas_limit;
|
||||
tx_env.gas_price = U256::ZERO;
|
||||
tx_env.gas_priority_fee = None;
|
||||
match tx.to {
|
||||
TransactionKind::Call(to) => tx_env.transact_to = TransactTo::Call(to),
|
||||
TransactionKind::Create => tx_env.transact_to = TransactTo::create(),
|
||||
}
|
||||
tx_env.value = tx.value.into();
|
||||
tx_env.data = tx.input.clone();
|
||||
tx_env.chain_id = None;
|
||||
tx_env.nonce = None;
|
||||
|
||||
fill_op_tx_env(tx_env, transaction, envelope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
#[inline(always)]
|
||||
fn fill_op_tx_env<T: AsRef<Transaction>>(tx_env: &mut TxEnv, transaction: T, envelope: Bytes) {
|
||||
match transaction.as_ref() {
|
||||
Transaction::Deposit(tx) => {
|
||||
tx_env.optimism = OptimismFields {
|
||||
source_hash: Some(tx.source_hash),
|
||||
mint: tx.mint,
|
||||
is_system_transaction: Some(tx.is_system_transaction),
|
||||
enveloped_tx: Some(envelope),
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
tx_env.optimism = OptimismFields {
|
||||
source_hash: None,
|
||||
mint: None,
|
||||
is_system_transaction: Some(false),
|
||||
enveloped_tx: Some(envelope),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,6 +49,15 @@ mod tx_value;
|
||||
pub(crate) mod util;
|
||||
mod variant;
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
mod optimism;
|
||||
#[cfg(feature = "optimism")]
|
||||
pub use optimism::TxDeposit;
|
||||
#[cfg(feature = "optimism")]
|
||||
use revm_primitives::U256;
|
||||
#[cfg(feature = "optimism")]
|
||||
pub use tx_type::DEPOSIT_TX_TYPE_ID;
|
||||
|
||||
// Expected number of transactions where we can expect a speed-up by recovering the senders in
|
||||
// parallel.
|
||||
pub(crate) static PARALLEL_SENDER_RECOVERY_THRESHOLD: Lazy<usize> =
|
||||
@ -103,6 +112,9 @@ pub enum Transaction {
|
||||
/// EIP-4844, also known as proto-danksharding, implements the framework and logic of
|
||||
/// danksharding, introducing new transaction formats and verification rules.
|
||||
Eip4844(TxEip4844),
|
||||
/// Optimism deposit transaction.
|
||||
#[cfg(feature = "optimism")]
|
||||
Deposit(TxDeposit),
|
||||
}
|
||||
|
||||
// === impl Transaction ===
|
||||
@ -116,6 +128,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(tx) => tx.signature_hash(),
|
||||
Transaction::Eip1559(tx) => tx.signature_hash(),
|
||||
Transaction::Eip4844(tx) => tx.signature_hash(),
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => B256::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,6 +140,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(TxEip2930 { chain_id, .. }) |
|
||||
Transaction::Eip1559(TxEip1559 { chain_id, .. }) |
|
||||
Transaction::Eip4844(TxEip4844 { chain_id, .. }) => Some(*chain_id),
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,6 +152,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(TxEip2930 { chain_id: ref mut c, .. }) |
|
||||
Transaction::Eip1559(TxEip1559 { chain_id: ref mut c, .. }) |
|
||||
Transaction::Eip4844(TxEip4844 { chain_id: ref mut c, .. }) => *c = chain_id,
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => { /* noop */ }
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,6 +165,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(TxEip2930 { to, .. }) |
|
||||
Transaction::Eip1559(TxEip1559 { to, .. }) |
|
||||
Transaction::Eip4844(TxEip4844 { to, .. }) => to,
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(TxDeposit { to, .. }) => to,
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,6 +182,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(access_list_tx) => access_list_tx.tx_type(),
|
||||
Transaction::Eip1559(dynamic_fee_tx) => dynamic_fee_tx.tx_type(),
|
||||
Transaction::Eip4844(blob_tx) => blob_tx.tx_type(),
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(deposit_tx) => deposit_tx.tx_type(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,6 +194,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(TxEip2930 { value, .. }) |
|
||||
Transaction::Eip1559(TxEip1559 { value, .. }) |
|
||||
Transaction::Eip4844(TxEip4844 { value, .. }) => value,
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(TxDeposit { value, .. }) => value,
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,6 +206,9 @@ impl Transaction {
|
||||
Transaction::Eip2930(TxEip2930 { nonce, .. }) |
|
||||
Transaction::Eip1559(TxEip1559 { nonce, .. }) |
|
||||
Transaction::Eip4844(TxEip4844 { nonce, .. }) => *nonce,
|
||||
// Deposit transactions do not have nonces.
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,6 +221,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(tx) => Some(&tx.access_list),
|
||||
Transaction::Eip1559(tx) => Some(&tx.access_list),
|
||||
Transaction::Eip4844(tx) => Some(&tx.access_list),
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,6 +233,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(TxEip2930 { gas_limit, .. }) |
|
||||
Transaction::Eip1559(TxEip1559 { gas_limit, .. }) |
|
||||
Transaction::Eip4844(TxEip4844 { gas_limit, .. }) => *gas_limit,
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(TxDeposit { gas_limit, .. }) => *gas_limit,
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,6 +243,8 @@ impl Transaction {
|
||||
match self {
|
||||
Transaction::Legacy(_) | Transaction::Eip2930(_) => false,
|
||||
Transaction::Eip1559(_) | Transaction::Eip4844(_) => true,
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,6 +257,10 @@ impl Transaction {
|
||||
Transaction::Eip2930(TxEip2930 { gas_price, .. }) => *gas_price,
|
||||
Transaction::Eip1559(TxEip1559 { max_fee_per_gas, .. }) |
|
||||
Transaction::Eip4844(TxEip4844 { max_fee_per_gas, .. }) => *max_fee_per_gas,
|
||||
// Deposit transactions buy their L2 gas on L1 and, as such, the L2 gas is not
|
||||
// refundable.
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,6 +275,8 @@ impl Transaction {
|
||||
Transaction::Eip4844(TxEip4844 { max_priority_fee_per_gas, .. }) => {
|
||||
Some(*max_priority_fee_per_gas)
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,6 +290,8 @@ impl Transaction {
|
||||
Transaction::Eip4844(TxEip4844 { blob_versioned_hashes, .. }) => {
|
||||
Some(blob_versioned_hashes.to_vec())
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,6 +333,8 @@ impl Transaction {
|
||||
Transaction::Eip4844(TxEip4844 { max_priority_fee_per_gas, .. }) => {
|
||||
*max_priority_fee_per_gas
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,6 +347,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(tx) => tx.gas_price,
|
||||
Transaction::Eip1559(dynamic_tx) => dynamic_tx.effective_gas_price(base_fee),
|
||||
Transaction::Eip4844(dynamic_tx) => dynamic_tx.effective_gas_price(base_fee),
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,9 +390,47 @@ impl Transaction {
|
||||
Transaction::Eip2930(TxEip2930 { input, .. }) |
|
||||
Transaction::Eip1559(TxEip1559 { input, .. }) |
|
||||
Transaction::Eip4844(TxEip4844 { input, .. }) => input,
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(TxDeposit { input, .. }) => input,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the source hash of the transaction, which uniquely identifies its source.
|
||||
/// If not a deposit transaction, this will always return `None`.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub fn source_hash(&self) -> Option<B256> {
|
||||
match self {
|
||||
Transaction::Deposit(TxDeposit { source_hash, .. }) => Some(*source_hash),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the amount of ETH locked up on L1 that will be minted on L2. If the transaction
|
||||
/// is not a deposit transaction, this will always return `None`.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub fn mint(&self) -> Option<u128> {
|
||||
match self {
|
||||
Transaction::Deposit(TxDeposit { mint, .. }) => *mint,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not the transaction is a system transaction. If the transaction
|
||||
/// is not a deposit transaction, this will always return `false`.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub fn is_system_transaction(&self) -> bool {
|
||||
match self {
|
||||
Transaction::Deposit(TxDeposit { is_system_transaction, .. }) => *is_system_transaction,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not the transaction is an Optimism Deposited transaction.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub fn is_deposit(&self) -> bool {
|
||||
matches!(self, Transaction::Deposit(_))
|
||||
}
|
||||
|
||||
/// This encodes the transaction _without_ the signature, and is only suitable for creating a
|
||||
/// hash intended for signing.
|
||||
pub fn encode_without_signature(&self, out: &mut dyn bytes::BufMut) {
|
||||
@ -376,6 +459,8 @@ impl Transaction {
|
||||
Transaction::Eip4844(blob_tx) => {
|
||||
blob_tx.encode_with_signature(signature, out, with_header)
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(deposit_tx) => deposit_tx.encode(out, with_header),
|
||||
}
|
||||
}
|
||||
|
||||
@ -386,6 +471,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(tx) => tx.nonce = nonce,
|
||||
Transaction::Eip1559(tx) => tx.nonce = nonce,
|
||||
Transaction::Eip4844(tx) => tx.nonce = nonce,
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => { /* noop */ }
|
||||
}
|
||||
}
|
||||
|
||||
@ -396,6 +483,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(tx) => tx.value = value,
|
||||
Transaction::Eip1559(tx) => tx.value = value,
|
||||
Transaction::Eip4844(tx) => tx.value = value,
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(tx) => tx.value = value,
|
||||
}
|
||||
}
|
||||
|
||||
@ -406,6 +495,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(tx) => tx.input = input,
|
||||
Transaction::Eip1559(tx) => tx.input = input,
|
||||
Transaction::Eip4844(tx) => tx.input = input,
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(tx) => tx.input = input,
|
||||
}
|
||||
}
|
||||
|
||||
@ -417,6 +508,8 @@ impl Transaction {
|
||||
Transaction::Eip2930(tx) => tx.size(),
|
||||
Transaction::Eip1559(tx) => tx.size(),
|
||||
Transaction::Eip4844(tx) => tx.size(),
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(tx) => tx.size(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -502,31 +595,38 @@ impl From<TxEip4844> for Transaction {
|
||||
}
|
||||
|
||||
impl Compact for Transaction {
|
||||
// Serializes the TxType to the buffer if necessary, returning 2 bits of the type as an
|
||||
// identifier instead of the length.
|
||||
fn to_compact<B>(self, buf: &mut B) -> usize
|
||||
where
|
||||
B: bytes::BufMut + AsMut<[u8]>,
|
||||
{
|
||||
let identifier = self.tx_type().to_compact(buf);
|
||||
match self {
|
||||
Transaction::Legacy(tx) => {
|
||||
tx.to_compact(buf);
|
||||
0
|
||||
}
|
||||
Transaction::Eip2930(tx) => {
|
||||
tx.to_compact(buf);
|
||||
1
|
||||
}
|
||||
Transaction::Eip1559(tx) => {
|
||||
tx.to_compact(buf);
|
||||
2
|
||||
}
|
||||
Transaction::Eip4844(tx) => {
|
||||
tx.to_compact(buf);
|
||||
3
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(tx) => {
|
||||
tx.to_compact(buf);
|
||||
}
|
||||
}
|
||||
identifier
|
||||
}
|
||||
|
||||
fn from_compact(buf: &[u8], identifier: usize) -> (Self, &[u8]) {
|
||||
// For backwards compatibility purposes, only 2 bits of the type are encoded in the identifier
|
||||
// parameter. In the case of a 3, the full transaction type is read from the buffer as a
|
||||
// single byte.
|
||||
fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) {
|
||||
match identifier {
|
||||
0 => {
|
||||
let (tx, buf) = TxLegacy::from_compact(buf, buf.len());
|
||||
@ -541,8 +641,24 @@ impl Compact for Transaction {
|
||||
(Transaction::Eip1559(tx), buf)
|
||||
}
|
||||
3 => {
|
||||
let (tx, buf) = TxEip4844::from_compact(buf, buf.len());
|
||||
(Transaction::Eip4844(tx), buf)
|
||||
// An identifier of 3 indicates that the transaction type did not fit into
|
||||
// the backwards compatible 2 bit identifier, their transaction types are
|
||||
// larger than 2 bits (eg. 4844 and Deposit Transactions). In this case,
|
||||
// we need to read the concrete transaction type from the buffer by
|
||||
// reading the full 8 bits (single byte) and match on this transaction type.
|
||||
let identifier = buf.get_u8() as usize;
|
||||
match identifier {
|
||||
3 => {
|
||||
let (tx, buf) = TxEip4844::from_compact(buf, buf.len());
|
||||
(Transaction::Eip4844(tx), buf)
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
126 => {
|
||||
let (tx, buf) = TxDeposit::from_compact(buf, buf.len());
|
||||
(Transaction::Deposit(tx), buf)
|
||||
}
|
||||
_ => unreachable!("Junk data in database: unknown Transaction variant"),
|
||||
}
|
||||
}
|
||||
_ => unreachable!("Junk data in database: unknown Transaction variant"),
|
||||
}
|
||||
@ -572,6 +688,10 @@ impl Encodable for Transaction {
|
||||
Transaction::Eip4844(blob_tx) => {
|
||||
blob_tx.encode_for_signing(out);
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(deposit_tx) => {
|
||||
deposit_tx.encode(out, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -581,6 +701,8 @@ impl Encodable for Transaction {
|
||||
Transaction::Eip2930(access_list_tx) => access_list_tx.payload_len_for_signature(),
|
||||
Transaction::Eip1559(dynamic_fee_tx) => dynamic_fee_tx.payload_len_for_signature(),
|
||||
Transaction::Eip4844(blob_tx) => blob_tx.payload_len_for_signature(),
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(deposit_tx) => deposit_tx.payload_len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -861,6 +983,12 @@ impl TransactionSigned {
|
||||
///
|
||||
/// Returns `None` if the transaction's signature is invalid, see also [Self::recover_signer].
|
||||
pub fn recover_signer(&self) -> Option<Address> {
|
||||
// Optimism's Deposit transaction does not have a signature. Directly return the
|
||||
// `from` address.
|
||||
#[cfg(feature = "optimism")]
|
||||
if let Transaction::Deposit(TxDeposit { from, .. }) = self.transaction {
|
||||
return Some(from)
|
||||
}
|
||||
let signature_hash = self.signature_hash();
|
||||
self.signature.recover_signer(signature_hash)
|
||||
}
|
||||
@ -942,6 +1070,8 @@ impl TransactionSigned {
|
||||
dynamic_fee_tx.payload_len_with_signature(&self.signature)
|
||||
}
|
||||
Transaction::Eip4844(blob_tx) => blob_tx.payload_len_with_signature(&self.signature),
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(deposit_tx) => deposit_tx.payload_len(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1065,11 +1195,21 @@ impl TransactionSigned {
|
||||
1 => Transaction::Eip2930(TxEip2930::decode_inner(data)?),
|
||||
2 => Transaction::Eip1559(TxEip1559::decode_inner(data)?),
|
||||
3 => Transaction::Eip4844(TxEip4844::decode_inner(data)?),
|
||||
#[cfg(feature = "optimism")]
|
||||
0x7E => Transaction::Deposit(TxDeposit::decode_inner(data)?),
|
||||
_ => return Err(RlpError::Custom("unsupported typed transaction type")),
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
let signature = Signature::decode(data)?;
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
let signature = if tx_type == DEPOSIT_TX_TYPE_ID {
|
||||
Signature::default()
|
||||
} else {
|
||||
Signature::decode(data)?
|
||||
};
|
||||
|
||||
let bytes_consumed = remaining_len - data.len();
|
||||
if bytes_consumed != header.payload_length {
|
||||
return Err(RlpError::UnexpectedLength)
|
||||
@ -1127,6 +1267,8 @@ impl TransactionSigned {
|
||||
Transaction::Eip4844(blob_tx) => {
|
||||
blob_tx.payload_len_with_signature_without_header(&self.signature)
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(deposit_tx) => deposit_tx.payload_len_without_header(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1212,6 +1354,14 @@ impl proptest::arbitrary::Arbitrary for TransactionSigned {
|
||||
// Otherwise we might overflow when calculating `v` on `recalculate_hash`
|
||||
transaction.set_chain_id(chain_id % (u64::MAX / 2 - 36));
|
||||
}
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
let sig = if transaction.is_deposit() {
|
||||
Signature { r: U256::ZERO, s: U256::ZERO, odd_y_parity: false }
|
||||
} else {
|
||||
sig
|
||||
};
|
||||
|
||||
let mut tx =
|
||||
TransactionSigned { hash: Default::default(), signature: sig, transaction };
|
||||
tx.hash = tx.recalculate_hash();
|
||||
@ -1232,14 +1382,16 @@ impl<'a> arbitrary::Arbitrary<'a> for TransactionSigned {
|
||||
transaction.set_chain_id(chain_id % (u64::MAX / 2 - 36));
|
||||
}
|
||||
|
||||
let mut tx = TransactionSigned {
|
||||
hash: Default::default(),
|
||||
signature: Signature::arbitrary(u)?,
|
||||
transaction,
|
||||
};
|
||||
tx.hash = tx.recalculate_hash();
|
||||
let signature = Signature::arbitrary(u)?;
|
||||
|
||||
Ok(tx)
|
||||
#[cfg(feature = "optimism")]
|
||||
let signature = if transaction.is_deposit() {
|
||||
Signature { r: U256::ZERO, s: U256::ZERO, odd_y_parity: false }
|
||||
} else {
|
||||
signature
|
||||
};
|
||||
|
||||
Ok(TransactionSigned::from_transaction_and_signature(transaction, signature))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
169
crates/primitives/src/transaction/optimism.rs
Normal file
169
crates/primitives/src/transaction/optimism.rs
Normal file
@ -0,0 +1,169 @@
|
||||
use crate::{Address, Bytes, TransactionKind, TxType, TxValue, B256};
|
||||
use alloy_rlp::{
|
||||
length_of_length, Decodable, Encodable, Error as DecodeError, Header, EMPTY_STRING_CODE,
|
||||
};
|
||||
use bytes::Buf;
|
||||
use reth_codecs::{main_codec, Compact};
|
||||
use std::mem;
|
||||
|
||||
/// Deposit transactions, also known as deposits are initiated on L1, and executed on L2.
|
||||
#[main_codec]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct TxDeposit {
|
||||
/// Hash that uniquely identifies the source of the deposit.
|
||||
pub source_hash: B256,
|
||||
/// The address of the sender account.
|
||||
pub from: Address,
|
||||
/// The address of the recipient account, or the null (zero-length) address if the deposited
|
||||
/// transaction is a contract creation.
|
||||
pub to: TransactionKind,
|
||||
/// The ETH value to mint on L2.
|
||||
pub mint: Option<u128>,
|
||||
/// The ETH value to send to the recipient account.
|
||||
pub value: TxValue,
|
||||
/// The gas limit for the L2 transaction.
|
||||
pub gas_limit: u64,
|
||||
/// Field indicating if this transaction is exempt from the L2 gas limit.
|
||||
pub is_system_transaction: bool,
|
||||
/// Input has two uses depending if transaction is Create or Call (if `to` field is None or
|
||||
/// Some).
|
||||
pub input: Bytes,
|
||||
}
|
||||
|
||||
impl TxDeposit {
|
||||
/// Calculates a heuristic for the in-memory size of the [TxDeposit] transaction.
|
||||
#[inline]
|
||||
pub fn size(&self) -> usize {
|
||||
mem::size_of::<B256>() + // source_hash
|
||||
mem::size_of::<Address>() + // from
|
||||
self.to.size() + // to
|
||||
mem::size_of::<Option<u128>>() + // mint
|
||||
mem::size_of::<TxValue>() + // value
|
||||
mem::size_of::<u64>() + // gas_limit
|
||||
mem::size_of::<bool>() + // is_system_transaction
|
||||
self.input.len() // input
|
||||
}
|
||||
|
||||
/// Decodes the inner [TxDeposit] fields from RLP bytes.
|
||||
///
|
||||
/// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following
|
||||
/// RLP fields in the following order:
|
||||
///
|
||||
/// - `source_hash`
|
||||
/// - `from`
|
||||
/// - `to`
|
||||
/// - `mint`
|
||||
/// - `value`
|
||||
/// - `gas_limit`
|
||||
/// - `is_system_transaction`
|
||||
/// - `input`
|
||||
pub fn decode_inner(buf: &mut &[u8]) -> Result<Self, DecodeError> {
|
||||
Ok(Self {
|
||||
source_hash: Decodable::decode(buf)?,
|
||||
from: Decodable::decode(buf)?,
|
||||
to: Decodable::decode(buf)?,
|
||||
mint: if *buf.first().ok_or(DecodeError::InputTooShort)? == EMPTY_STRING_CODE {
|
||||
buf.advance(1);
|
||||
None
|
||||
} else {
|
||||
Some(Decodable::decode(buf)?)
|
||||
},
|
||||
value: Decodable::decode(buf)?,
|
||||
gas_limit: Decodable::decode(buf)?,
|
||||
is_system_transaction: Decodable::decode(buf)?,
|
||||
input: Decodable::decode(buf)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Outputs the length of the transaction's fields, without a RLP header or length of the
|
||||
/// eip155 fields.
|
||||
pub(crate) fn fields_len(&self) -> usize {
|
||||
let mut len = 0;
|
||||
len += self.source_hash.length();
|
||||
len += self.from.length();
|
||||
len += self.to.length();
|
||||
len += self.mint.map_or(1, |mint| mint.length());
|
||||
len += self.value.length();
|
||||
len += self.gas_limit.length();
|
||||
len += self.is_system_transaction.length();
|
||||
len += self.input.0.length();
|
||||
len
|
||||
}
|
||||
|
||||
/// Encodes only the transaction's fields into the desired buffer, without a RLP header.
|
||||
/// <https://github.com/ethereum-optimism/optimism/blob/develop/specs/deposits.md#the-deposited-transaction-type>
|
||||
pub(crate) fn encode_fields(&self, out: &mut dyn bytes::BufMut) {
|
||||
self.source_hash.encode(out);
|
||||
self.from.encode(out);
|
||||
self.to.encode(out);
|
||||
if let Some(mint) = self.mint {
|
||||
mint.encode(out);
|
||||
} else {
|
||||
out.put_u8(EMPTY_STRING_CODE);
|
||||
}
|
||||
self.value.encode(out);
|
||||
self.gas_limit.encode(out);
|
||||
self.is_system_transaction.encode(out);
|
||||
self.input.encode(out);
|
||||
}
|
||||
|
||||
/// Inner encoding function that is used for both rlp [`Encodable`] trait and for calculating
|
||||
/// hash that for eip2718 does not require rlp header
|
||||
pub(crate) fn encode(&self, out: &mut dyn bytes::BufMut, with_header: bool) {
|
||||
let payload_length = self.fields_len();
|
||||
if with_header {
|
||||
Header {
|
||||
list: false,
|
||||
payload_length: 1 + length_of_length(payload_length) + payload_length,
|
||||
}
|
||||
.encode(out);
|
||||
}
|
||||
out.put_u8(self.tx_type() as u8);
|
||||
let header = Header { list: true, payload_length };
|
||||
header.encode(out);
|
||||
self.encode_fields(out);
|
||||
}
|
||||
|
||||
/// Output the length of the RLP signed transaction encoding. This encodes with a RLP header.
|
||||
pub(crate) fn payload_len(&self) -> usize {
|
||||
let payload_length = self.fields_len();
|
||||
// 'tx type' + 'header length' + 'payload length'
|
||||
let len = 1 + length_of_length(payload_length) + payload_length;
|
||||
length_of_length(len) + len
|
||||
}
|
||||
|
||||
pub(crate) fn payload_len_without_header(&self) -> usize {
|
||||
let payload_length = self.fields_len();
|
||||
// 'transaction type byte length' + 'header length' + 'payload length'
|
||||
1 + length_of_length(payload_length) + payload_length
|
||||
}
|
||||
|
||||
/// Get the transaction type
|
||||
pub(crate) fn tx_type(&self) -> TxType {
|
||||
TxType::DEPOSIT
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Bytes, TransactionSigned};
|
||||
use alloy_rlp::Decodable;
|
||||
use bytes::BytesMut;
|
||||
use revm_primitives::hex_literal::hex;
|
||||
|
||||
#[test]
|
||||
fn test_rlp_roundtrip() {
|
||||
let bytes = Bytes::from_static(&hex!("7ef9015aa044bae9d41b8380d781187b426c6fe43df5fb2fb57bd4466ef6a701e1f01e015694deaddeaddeaddeaddeaddeaddeaddeaddead000194420000000000000000000000000000000000001580808408f0d18001b90104015d8eb900000000000000000000000000000000000000000000000000000000008057650000000000000000000000000000000000000000000000000000000063d96d10000000000000000000000000000000000000000000000000000000000009f35273d89754a1e0387b89520d989d3be9c37c1f32495a88faf1ea05c61121ab0d1900000000000000000000000000000000000000000000000000000000000000010000000000000000000000002d679b567db6187c0c8323fa982cfb88b74dbcc7000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240"));
|
||||
|
||||
let tx_a = TransactionSigned::decode_enveloped(bytes.clone()).unwrap();
|
||||
let tx_b = TransactionSigned::decode(&mut &bytes[..]).unwrap();
|
||||
|
||||
let mut buf_a = BytesMut::default();
|
||||
tx_a.encode_enveloped(&mut buf_a);
|
||||
assert_eq!(&buf_a[..], &bytes[..]);
|
||||
|
||||
let mut buf_b = BytesMut::default();
|
||||
tx_b.encode_enveloped(&mut buf_b);
|
||||
assert_eq!(&buf_b[..], &bytes[..]);
|
||||
}
|
||||
}
|
||||
@ -47,6 +47,16 @@ pub enum PooledTransactionsElement {
|
||||
},
|
||||
/// A blob transaction, which includes the transaction, blob data, commitments, and proofs.
|
||||
BlobTransaction(BlobTransaction),
|
||||
/// An Optimism deposit transaction
|
||||
#[cfg(feature = "optimism")]
|
||||
Deposit {
|
||||
/// The inner transaction
|
||||
transaction: crate::TxDeposit,
|
||||
/// The signature
|
||||
signature: Signature,
|
||||
/// The hash of the transaction
|
||||
hash: TxHash,
|
||||
},
|
||||
}
|
||||
|
||||
impl PooledTransactionsElement {
|
||||
@ -69,6 +79,8 @@ impl PooledTransactionsElement {
|
||||
Self::Eip2930 { transaction, .. } => transaction.signature_hash(),
|
||||
Self::Eip1559 { transaction, .. } => transaction.signature_hash(),
|
||||
Self::BlobTransaction(blob_tx) => blob_tx.transaction.signature_hash(),
|
||||
#[cfg(feature = "optimism")]
|
||||
Self::Deposit { .. } => B256::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,6 +91,8 @@ impl PooledTransactionsElement {
|
||||
PooledTransactionsElement::Eip2930 { hash, .. } => hash,
|
||||
PooledTransactionsElement::Eip1559 { hash, .. } => hash,
|
||||
PooledTransactionsElement::BlobTransaction(tx) => &tx.hash,
|
||||
#[cfg(feature = "optimism")]
|
||||
PooledTransactionsElement::Deposit { hash, .. } => hash,
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,6 +103,10 @@ impl PooledTransactionsElement {
|
||||
Self::Eip2930 { signature, .. } => signature,
|
||||
Self::Eip1559 { signature, .. } => signature,
|
||||
Self::BlobTransaction(blob_tx) => &blob_tx.signature,
|
||||
#[cfg(feature = "optimism")]
|
||||
Self::Deposit { .. } => {
|
||||
panic!("Deposit transactions do not have a signature! This is a bug.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,6 +117,8 @@ impl PooledTransactionsElement {
|
||||
Self::Eip2930 { transaction, .. } => transaction.nonce,
|
||||
Self::Eip1559 { transaction, .. } => transaction.nonce,
|
||||
Self::BlobTransaction(blob_tx) => blob_tx.transaction.nonce,
|
||||
#[cfg(feature = "optimism")]
|
||||
Self::Deposit { .. } => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,6 +224,12 @@ impl PooledTransactionsElement {
|
||||
signature: typed_tx.signature,
|
||||
hash: typed_tx.hash,
|
||||
}),
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(tx) => Ok(PooledTransactionsElement::Deposit {
|
||||
transaction: tx,
|
||||
signature: typed_tx.signature,
|
||||
hash: typed_tx.hash,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -232,6 +258,12 @@ impl PooledTransactionsElement {
|
||||
hash,
|
||||
},
|
||||
Self::BlobTransaction(blob_tx) => blob_tx.into_parts().0,
|
||||
#[cfg(feature = "optimism")]
|
||||
Self::Deposit { transaction, signature, hash } => TransactionSigned {
|
||||
transaction: Transaction::Deposit(transaction),
|
||||
signature,
|
||||
hash,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,6 +286,8 @@ impl PooledTransactionsElement {
|
||||
// the encoding does not use a header, so we set `with_header` to false
|
||||
blob_tx.payload_len_with_type(false)
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
Self::Deposit { transaction, .. } => transaction.payload_len_without_header(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -279,6 +313,10 @@ impl Encodable for PooledTransactionsElement {
|
||||
// `rlp(tx_type || rlp([transaction_payload_body, blobs, commitments, proofs]))`
|
||||
blob_tx.encode_with_type_inner(out, true);
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
Self::Deposit { transaction, .. } => {
|
||||
transaction.encode(out, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -300,6 +338,11 @@ impl Encodable for PooledTransactionsElement {
|
||||
// the encoding uses a header, so we set `with_header` to true
|
||||
blob_tx.payload_len_with_type(true)
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
Self::Deposit { transaction, .. } => {
|
||||
// method computes the payload len with a RLP header
|
||||
transaction.payload_len()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -401,6 +444,12 @@ impl Decodable for PooledTransactionsElement {
|
||||
signature: typed_tx.signature,
|
||||
hash: typed_tx.hash,
|
||||
}),
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(tx) => Ok(PooledTransactionsElement::Deposit {
|
||||
transaction: tx,
|
||||
signature: typed_tx.signature,
|
||||
hash: typed_tx.hash,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -432,6 +481,10 @@ impl From<TransactionSigned> for PooledTransactionsElement {
|
||||
sidecar: Default::default(),
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(tx) => {
|
||||
PooledTransactionsElement::Deposit { transaction: tx, signature, hash }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,6 +62,11 @@ impl Signature {
|
||||
/// Output the `v` of the signature depends on chain_id
|
||||
#[inline]
|
||||
pub fn v(&self, chain_id: Option<u64>) -> u64 {
|
||||
#[cfg(feature = "optimism")]
|
||||
if self.r == U256::ZERO && self.s == U256::ZERO {
|
||||
return 0
|
||||
}
|
||||
|
||||
if let Some(chain_id) = chain_id {
|
||||
// EIP-155: v = {0, 1} + CHAIN_ID * 2 + 35
|
||||
self.odd_y_parity as u64 + chain_id * 2 + 35
|
||||
@ -158,27 +163,50 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_payload_len_with_eip155_chain_id() {
|
||||
let signature = Signature { r: U256::default(), s: U256::default(), odd_y_parity: false };
|
||||
// Select 1 as an arbitrary nonzero value for R and S, as v() always returns 0 for (0, 0).
|
||||
let signature = Signature { r: U256::from(1), s: U256::from(1), odd_y_parity: false };
|
||||
|
||||
assert_eq!(3, signature.payload_len_with_eip155_chain_id(None));
|
||||
assert_eq!(3, signature.payload_len_with_eip155_chain_id(Some(1)));
|
||||
assert_eq!(4, signature.payload_len_with_eip155_chain_id(Some(47)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
#[test]
|
||||
fn test_zero_signature_payload_len_with_eip155_chain_id() {
|
||||
let zero_signature = Signature { r: U256::ZERO, s: U256::ZERO, odd_y_parity: false };
|
||||
|
||||
assert_eq!(3, zero_signature.payload_len_with_eip155_chain_id(None));
|
||||
assert_eq!(3, zero_signature.payload_len_with_eip155_chain_id(Some(1)));
|
||||
assert_eq!(3, zero_signature.payload_len_with_eip155_chain_id(Some(47)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_v() {
|
||||
let signature = Signature { r: U256::default(), s: U256::default(), odd_y_parity: false };
|
||||
// Select 1 as an arbitrary nonzero value for R and S, as v() always returns 0 for (0, 0).
|
||||
let signature = Signature { r: U256::from(1), s: U256::from(1), odd_y_parity: false };
|
||||
assert_eq!(27, signature.v(None));
|
||||
assert_eq!(37, signature.v(Some(1)));
|
||||
|
||||
let signature = Signature { r: U256::default(), s: U256::default(), odd_y_parity: true };
|
||||
let signature = Signature { r: U256::from(1), s: U256::from(1), odd_y_parity: true };
|
||||
assert_eq!(28, signature.v(None));
|
||||
assert_eq!(38, signature.v(Some(1)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
#[test]
|
||||
fn test_zero_signature_v() {
|
||||
let signature = Signature { r: U256::ZERO, s: U256::ZERO, odd_y_parity: false };
|
||||
|
||||
assert_eq!(0, signature.v(None));
|
||||
assert_eq!(0, signature.v(Some(1)));
|
||||
assert_eq!(0, signature.v(Some(47)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_and_decode_with_eip155_chain_id() {
|
||||
let signature = Signature { r: U256::default(), s: U256::default(), odd_y_parity: false };
|
||||
// Select 1 as an arbitrary nonzero value for R and S, as v() always returns 0 for (0, 0).
|
||||
let signature = Signature { r: U256::from(1), s: U256::from(1), odd_y_parity: false };
|
||||
|
||||
let mut encoded = BytesMut::new();
|
||||
signature.encode_with_eip155_chain_id(&mut encoded, None);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::U8;
|
||||
use bytes::Buf;
|
||||
use reth_codecs::{derive_arbitrary, Compact};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -15,6 +16,10 @@ pub const EIP1559_TX_TYPE_ID: u8 = 2;
|
||||
/// Identifier for [TxEip4844](crate::TxEip4844) transaction.
|
||||
pub const EIP4844_TX_TYPE_ID: u8 = 3;
|
||||
|
||||
/// Identifier for [TxDeposit](crate::TxDeposit) transaction.
|
||||
#[cfg(feature = "optimism")]
|
||||
pub const DEPOSIT_TX_TYPE_ID: u8 = 126;
|
||||
|
||||
/// Transaction Type
|
||||
///
|
||||
/// Currently being used as 2-bit type when encoding it to [`Compact`] on
|
||||
@ -34,6 +39,9 @@ pub enum TxType {
|
||||
EIP1559 = 2_isize,
|
||||
/// Shard Blob Transactions - EIP-4844
|
||||
EIP4844 = 3_isize,
|
||||
/// Optimism Deposit transaction.
|
||||
#[cfg(feature = "optimism")]
|
||||
DEPOSIT = 126_isize,
|
||||
}
|
||||
|
||||
impl From<TxType> for u8 {
|
||||
@ -43,6 +51,8 @@ impl From<TxType> for u8 {
|
||||
TxType::EIP2930 => EIP2930_TX_TYPE_ID,
|
||||
TxType::EIP1559 => EIP1559_TX_TYPE_ID,
|
||||
TxType::EIP4844 => EIP4844_TX_TYPE_ID,
|
||||
#[cfg(feature = "optimism")]
|
||||
TxType::DEPOSIT => DEPOSIT_TX_TYPE_ID,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,7 +64,7 @@ impl From<TxType> for U8 {
|
||||
}
|
||||
|
||||
impl Compact for TxType {
|
||||
fn to_compact<B>(self, _: &mut B) -> usize
|
||||
fn to_compact<B>(self, buf: &mut B) -> usize
|
||||
where
|
||||
B: bytes::BufMut + AsMut<[u8]>,
|
||||
{
|
||||
@ -62,17 +72,30 @@ impl Compact for TxType {
|
||||
TxType::Legacy => 0,
|
||||
TxType::EIP2930 => 1,
|
||||
TxType::EIP1559 => 2,
|
||||
TxType::EIP4844 => 3,
|
||||
_ => {
|
||||
buf.put_u8(self as u8);
|
||||
3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_compact(buf: &[u8], identifier: usize) -> (Self, &[u8]) {
|
||||
// For backwards compatibility purposes only 2 bits of the type are encoded in the identifier
|
||||
// parameter. In the case of a 3, the full transaction type is read from the buffer as a
|
||||
// single byte.
|
||||
fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) {
|
||||
(
|
||||
match identifier {
|
||||
0 => TxType::Legacy,
|
||||
1 => TxType::EIP2930,
|
||||
2 => TxType::EIP1559,
|
||||
_ => TxType::EIP4844,
|
||||
_ => {
|
||||
let identifier = buf.get_u8() as usize;
|
||||
match identifier {
|
||||
#[cfg(feature = "optimism")]
|
||||
126 => TxType::DEPOSIT,
|
||||
_ => TxType::EIP4844,
|
||||
}
|
||||
}
|
||||
},
|
||||
buf,
|
||||
)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#[allow(unused_imports)]
|
||||
// suppress warning for UIntTryTo, which is required only when value-256 feature is disabled
|
||||
// suppress warning for UIntTryTo, which is required only when optimism feature is disabled
|
||||
use crate::{
|
||||
ruint::{ToUintError, UintTryFrom, UintTryTo},
|
||||
U256,
|
||||
@ -88,11 +88,11 @@ impl Compact for TxValue {
|
||||
where
|
||||
B: bytes::BufMut + AsMut<[u8]>,
|
||||
{
|
||||
#[cfg(feature = "value-256")]
|
||||
#[cfg(feature = "optimism")]
|
||||
{
|
||||
self.0.to_compact(buf)
|
||||
}
|
||||
#[cfg(not(feature = "value-256"))]
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
{
|
||||
// SAFETY: For ethereum mainnet this is safe as the max value is
|
||||
// 120000000000000000000000000 wei
|
||||
@ -103,12 +103,12 @@ impl Compact for TxValue {
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
fn from_compact(buf: &[u8], identifier: usize) -> (Self, &[u8]) {
|
||||
#[cfg(feature = "value-256")]
|
||||
#[cfg(feature = "optimism")]
|
||||
{
|
||||
let (i, buf) = U256::from_compact(buf, identifier);
|
||||
(TxValue(i), buf)
|
||||
}
|
||||
#[cfg(not(feature = "value-256"))]
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
{
|
||||
let (i, buf) = u128::from_compact(buf, identifier);
|
||||
(TxValue::from(i), buf)
|
||||
@ -119,12 +119,12 @@ impl Compact for TxValue {
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl<'a> arbitrary::Arbitrary<'a> for TxValue {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
#[cfg(feature = "value-256")]
|
||||
#[cfg(feature = "optimism")]
|
||||
{
|
||||
Ok(Self(U256::arbitrary(u)?))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "value-256"))]
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
{
|
||||
Ok(Self::try_from(u128::arbitrary(u)?).expect("to fit"))
|
||||
}
|
||||
@ -135,12 +135,12 @@ impl<'a> arbitrary::Arbitrary<'a> for TxValue {
|
||||
impl proptest::arbitrary::Arbitrary for TxValue {
|
||||
type Parameters = ParamsFor<()>;
|
||||
fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
|
||||
#[cfg(feature = "value-256")]
|
||||
#[cfg(feature = "optimism")]
|
||||
{
|
||||
proptest::prelude::any::<U256>().prop_map(Self).boxed()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "value-256"))]
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
{
|
||||
proptest::prelude::any::<u128>()
|
||||
.prop_map(|num| Self::try_from(num).expect("to fit"))
|
||||
|
||||
Reference in New Issue
Block a user