From 15ff0078f8f4c81bb263631e0205277f0397624b Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Sun, 26 Feb 2023 11:55:26 +0200 Subject: [PATCH 01/43] feat: deposit tx --- crates/primitives/Cargo.toml | 9 +- crates/primitives/src/lib.rs | 2 + crates/primitives/src/transaction/mod.rs | 130 +++++++++++++++++- crates/primitives/src/transaction/optimism.rs | 70 ++++++++++ crates/primitives/src/transaction/tx_type.rs | 18 ++- 5 files changed, 222 insertions(+), 7 deletions(-) create mode 100644 crates/primitives/src/transaction/optimism.rs diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index aca12eb5468..dcdb2ad9eca 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -90,8 +90,13 @@ pprof = { version = "0.12", features = ["flamegraph", "frame-pointer", "criterio [features] default = [] -arbitrary = ["revm-primitives/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"] -test-utils = [] +optimism = [] +arbitrary = [ + "revm-primitives/arbitrary", + "dep:arbitrary", + "dep:proptest", + "dep:proptest-derive", +] [[bench]] name = "recover_ecdsa_crit" diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 0248f008a24..75861811243 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -91,6 +91,8 @@ pub use transaction::{ TxEip1559, TxEip2930, TxLegacy, TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, LEGACY_TX_TYPE_ID, }; +#[cfg(feature = "optimism")] +pub use transaction::{TxDeposit, DEPOSIT_TX_TYPE, DEPOSIT_VERSION}; pub use withdrawal::Withdrawal; /// A block hash. diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index c0371aa2a84..4a1dcb035e9 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -24,6 +24,11 @@ mod signature; mod tx_type; pub(crate) mod util; +#[cfg(feature = "optimism")] +mod optimism; +#[cfg(feature = "optimism")] +pub use optimism::{TxDeposit, DEPOSIT_TX_TYPE, DEPOSIT_VERSION}; + /// Legacy transaction. #[main_codec] #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] @@ -227,6 +232,9 @@ pub enum Transaction { Eip2930(TxEip2930), /// A transaction with a priority fee ([EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)). Eip1559(TxEip1559), + #[cfg(feature = "optimism")] + /// Deposit transaction. + Deposit(TxDeposit), } impl Transaction { @@ -367,6 +375,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { chain_id, .. }) => *chain_id, Transaction::Eip2930(TxEip2930 { chain_id, .. }) => Some(*chain_id), Transaction::Eip1559(TxEip1559 { chain_id, .. }) => Some(*chain_id), + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => None, } } @@ -376,6 +386,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { chain_id: ref mut c, .. }) => *c = Some(chain_id), Transaction::Eip2930(TxEip2930 { chain_id: ref mut c, .. }) => *c = chain_id, Transaction::Eip1559(TxEip1559 { chain_id: ref mut c, .. }) => *c = chain_id, + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => (), } } @@ -386,6 +398,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { to, .. }) | Transaction::Eip2930(TxEip2930 { to, .. }) | Transaction::Eip1559(TxEip1559 { to, .. }) => to, + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { to, .. }) => to, } } @@ -400,6 +414,8 @@ impl Transaction { Transaction::Legacy { .. } => TxType::Legacy, Transaction::Eip2930 { .. } => TxType::EIP2930, Transaction::Eip1559 { .. } => TxType::EIP1559, + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => TxType::DEPOSIT, } } @@ -409,6 +425,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { value, .. }) => value, Transaction::Eip2930(TxEip2930 { value, .. }) => value, Transaction::Eip1559(TxEip1559 { value, .. }) => value, + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { value, .. }) => value, } } @@ -418,6 +436,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { nonce, .. }) => *nonce, Transaction::Eip2930(TxEip2930 { nonce, .. }) => *nonce, Transaction::Eip1559(TxEip1559 { nonce, .. }) => *nonce, + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => todo!(), // TODO: } } @@ -427,6 +447,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { gas_limit, .. }) | Transaction::Eip2930(TxEip2930 { gas_limit, .. }) | Transaction::Eip1559(TxEip1559 { gas_limit, .. }) => *gas_limit, + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { gas_limit, .. }) => *gas_limit, } } @@ -438,6 +460,10 @@ impl Transaction { Transaction::Legacy(TxLegacy { gas_price, .. }) | Transaction::Eip2930(TxEip2930 { gas_price, .. }) => *gas_price, Transaction::Eip1559(TxEip1559 { max_fee_per_gas, .. }) => *max_fee_per_gas, + #[cfg(feature = "optimism")] + // Deposit transactions buy their L2 gas on L1 and, as such, the L2 gas is not + // refundable. + Transaction::Deposit(_) => 0, } } @@ -452,6 +478,8 @@ impl Transaction { Transaction::Eip1559(TxEip1559 { max_priority_fee_per_gas, .. }) => { Some(*max_priority_fee_per_gas) } + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => None, } } @@ -535,6 +563,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { input, .. }) => input, Transaction::Eip2930(TxEip2930 { input, .. }) => input, Transaction::Eip1559(TxEip1559 { input, .. }) => input, + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { input, .. }) => input, } } @@ -631,6 +661,8 @@ impl Transaction { len += access_list.length(); len } + #[cfg(feature = "optimism")] + Transaction::Deposit(deposit) => deposit.fields_len(), } } @@ -693,6 +725,8 @@ impl Transaction { input.0.encode(out); access_list.encode(out); } + #[cfg(feature = "optimism")] + Transaction::Deposit(deposit) => deposit.encode_fields(out), } } } @@ -714,6 +748,13 @@ impl Encodable for Transaction { self.encode_fields(out); self.encode_eip155_fields(out); } + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => { + out.put_u8(self.tx_type() as u8); + out.put_u8(DEPOSIT_VERSION); + Header { list: true, payload_length: self.fields_len() }.encode(out); + self.encode_fields(out); + } _ => { out.put_u8(self.tx_type() as u8); Header { list: true, payload_length: self.fields_len() }.encode(out); @@ -729,6 +770,12 @@ impl Encodable for Transaction { // 'header length' + 'payload length' length_of_length(payload_length) + payload_length } + #[cfg(feature = "optimism")] + Transaction::Deposit { .. } => { + let payload_length = self.fields_len(); + // 'tx type byte length' + 'version byte' + 'header length' + 'payload length' + 1 + 1 + length_of_length(payload_length) + payload_length + } _ => { let payload_length = self.fields_len(); // 'transaction type byte length' + 'header length' + 'payload length' @@ -1007,6 +1054,10 @@ impl TransactionSigned { /// /// Returns `None` if the transaction's signature is invalid, see also [Self::recover_signer]. pub fn recover_signer(&self) -> Option
{ + #[cfg(feature = "optimism")] + if let Transaction::Deposit(TxDeposit { from, .. }) = self.transaction { + return Some(from.clone()) + } let signature_hash = self.signature_hash(); self.signature.recover_signer(signature_hash) } @@ -1058,7 +1109,49 @@ impl TransactionSigned { /// 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_inner(&self, out: &mut dyn bytes::BufMut, with_header: bool) { - self.transaction.encode_with_signature(&self.signature, out, with_header); + match self.transaction { + Transaction::Legacy(TxLegacy { chain_id, .. }) => { + // do nothing w/ with_header + let payload_length = self.transaction.fields_len() + + self.signature.payload_len_with_eip155_chain_id(chain_id); + let header = Header { list: true, payload_length }; + header.encode(out); + self.transaction.encode_fields(out); + self.signature.encode_with_eip155_chain_id(out, chain_id); + } + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => { + let payload_length = self.transaction.fields_len() + self.signature.payload_len(); + if with_header { + Header { + list: false, + payload_length: 1 + 1 + length_of_length(payload_length) + payload_length, + } + .encode(out); + } + out.put_u8(self.transaction.tx_type() as u8); + out.put_u8(DEPOSIT_VERSION); + let header = Header { list: true, payload_length }; + header.encode(out); + self.transaction.encode_fields(out); + self.signature.encode(out); + } + _ => { + let payload_length = self.transaction.fields_len() + self.signature.payload_len(); + if with_header { + Header { + list: false, + payload_length: 1 + length_of_length(payload_length) + payload_length, + } + .encode(out); + } + out.put_u8(self.transaction.tx_type() as u8); + let header = Header { list: true, payload_length }; + header.encode(out); + self.transaction.encode_fields(out); + self.signature.encode(out); + } + } } /// Output the length of the encode_inner(out, true). Note to assume that `with_header` is only @@ -1071,6 +1164,13 @@ impl TransactionSigned { // 'header length' + 'payload length' length_of_length(payload_length) + payload_length } + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => { + let payload_length = self.transaction.fields_len() + self.signature.payload_len(); + // 'tx type byte length' + 'version byte' + 'header length' + 'payload length' + let len = 1 + 1 + length_of_length(payload_length) + payload_length; + length_of_length(len) + len + } _ => { let payload_length = self.transaction.fields_len() + self.signature.payload_len(); // 'transaction type byte length' + 'header length' + 'payload length' @@ -1142,6 +1242,16 @@ impl TransactionSigned { let tx_type = *data.first().ok_or(DecodeError::InputTooShort)?; data.advance(1); + + #[cfg(feature = "optimism")] + if tx_type == DEPOSIT_TX_TYPE { + let version = *data.first().ok_or(DecodeError::InputTooShort)?; + if version != DEPOSIT_VERSION { + return Err(DecodeError::Custom("Deposit version mismatch")) + } + data.advance(1); + } + // decode the list header for the rest of the transaction let header = Header::decode(data)?; if !header.list { @@ -1150,6 +1260,8 @@ impl TransactionSigned { // length of tx encoding = tx type byte (size = 1) + length of header + payload length let tx_length = 1 + header.length() + header.payload_length; + #[cfg(feature = "optimism")] + let tx_length = if tx_type == DEPOSIT_TX_TYPE { tx_length + 1 } else { tx_length }; // decode common fields let transaction = match tx_type { @@ -1174,6 +1286,22 @@ impl TransactionSigned { input: Bytes(Decodable::decode(data)?), access_list: Decodable::decode(data)?, }), + #[cfg(feature = "optimism")] + DEPOSIT_TX_TYPE => Transaction::Deposit(TxDeposit { + source_hash: Decodable::decode(data)?, + from: Decodable::decode(data)?, + to: Decodable::decode(data)?, + mint: if *data.first().ok_or(DecodeError::InputTooShort)? == EMPTY_STRING_CODE { + data.advance(1); + None + } else { + Some(Decodable::decode(data)?) + }, + value: Decodable::decode(data)?, + input: Decodable::decode(data)?, + gas_limit: Decodable::decode(data)?, + is_system_transaction: Decodable::decode(data)?, + }), _ => return Err(DecodeError::Custom("unsupported typed transaction type")), }; diff --git a/crates/primitives/src/transaction/optimism.rs b/crates/primitives/src/transaction/optimism.rs new file mode 100644 index 00000000000..1b48f43fd23 --- /dev/null +++ b/crates/primitives/src/transaction/optimism.rs @@ -0,0 +1,70 @@ +use crate::{Address, Bytes, TransactionKind, H256}; +use reth_codecs::{main_codec, Compact}; +use reth_rlp::{Encodable, EMPTY_STRING_CODE}; + +/// EIP-2718 transaction type selector. +pub const DEPOSIT_TX_TYPE: u8 = 126; + +/// A versioned byte sequence to enable the protocol to upgrade the deposit transaction type without +/// changing the transaction type selector. +pub const DEPOSIT_VERSION: u8 = 0; + +/// Deposited transactions, also known as deposits are transactions which are initiated on L1, and +/// executed on L2. This document outlines a new transaction type for deposits. It also describes +/// how deposits are initiated on L1, along with the authorization and validation conditions 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: H256, + /// 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, + /// The ETH value to send to the recipient account. + pub value: u128, + /// 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 { + /// 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(|mint| mint.length()).unwrap_or(1); + len += self.value.length(); + len += self.input.0.length(); + len += self.gas_limit.length(); + len += self.is_system_transaction.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.input.encode(out); + self.gas_limit.encode(out); + self.is_system_transaction.encode(out); + } +} diff --git a/crates/primitives/src/transaction/tx_type.rs b/crates/primitives/src/transaction/tx_type.rs index 428e06f4534..1e7c9edfae5 100644 --- a/crates/primitives/src/transaction/tx_type.rs +++ b/crates/primitives/src/transaction/tx_type.rs @@ -23,14 +23,19 @@ pub enum TxType { EIP2930 = 1_isize, /// Transaction with Priority fee EIP1559 = 2_isize, + #[cfg(feature = "optimism")] + /// OP Deposit transaction. + DEPOSIT = 126_isize, } impl From for u8 { fn from(value: TxType) -> Self { match value { - TxType::Legacy => LEGACY_TX_TYPE_ID, - TxType::EIP2930 => EIP2930_TX_TYPE_ID, - TxType::EIP1559 => EIP1559_TX_TYPE_ID, + TxType::Legacy => 0, + TxType::EIP2930 => 1, + TxType::EIP1559 => 2, + #[cfg(feature = "optimism")] + TxType::DEPOSIT => 126, } } } @@ -50,6 +55,8 @@ impl Compact for TxType { TxType::Legacy => 0, TxType::EIP2930 => 1, TxType::EIP1559 => 2, + #[cfg(feature = "optimism")] + TxType::DEPOSIT => 126, } } @@ -58,7 +65,10 @@ impl Compact for TxType { match identifier { 0 => TxType::Legacy, 1 => TxType::EIP2930, - _ => TxType::EIP1559, + 2 => TxType::EIP1559, + #[cfg(feature = "optimism")] + 126 => TxType::DEPOSIT, + _ => panic!("unknown transaction type {identifier}"), }, buf, ) From 28bfaaaf7ff107a7a98b675ef5cdad7a48964e85 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Sun, 26 Feb 2023 14:13:36 +0200 Subject: [PATCH 02/43] chainspec and forks --- crates/primitives/src/chain/spec.rs | 228 +++++++++++++++------------- crates/primitives/src/hardfork.rs | 22 +++ 2 files changed, 142 insertions(+), 108 deletions(-) diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 0c84dc27539..9d493d2a14c 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -16,118 +16,94 @@ use std::{ }; /// The Ethereum mainnet spec -pub static MAINNET: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::mainnet(), - genesis: serde_json::from_str(include_str!("../../res/genesis/mainnet.json")) - .expect("Can't deserialize Mainnet genesis json"), - genesis_hash: Some(H256(hex!( - "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" - ))), - // - paris_block_and_final_difficulty: Some(( - 15537394, - U256::from(58_750_003_716_598_352_816_469u128), - )), - fork_timestamps: ForkTimestamps::default().shanghai(1681338455), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(1150000)), - (Hardfork::Dao, ForkCondition::Block(1920000)), - (Hardfork::Tangerine, ForkCondition::Block(2463000)), - (Hardfork::SpuriousDragon, ForkCondition::Block(2675000)), - (Hardfork::Byzantium, ForkCondition::Block(4370000)), - (Hardfork::Constantinople, ForkCondition::Block(7280000)), - (Hardfork::Petersburg, ForkCondition::Block(7280000)), - (Hardfork::Istanbul, ForkCondition::Block(9069000)), - (Hardfork::MuirGlacier, ForkCondition::Block(9200000)), - (Hardfork::Berlin, ForkCondition::Block(12244000)), - (Hardfork::London, ForkCondition::Block(12965000)), - (Hardfork::ArrowGlacier, ForkCondition::Block(13773000)), - (Hardfork::GrayGlacier, ForkCondition::Block(15050000)), - ( - Hardfork::Paris, - ForkCondition::TTD { - fork_block: None, - total_difficulty: U256::from(58_750_000_000_000_000_000_000_u128), - }, - ), - (Hardfork::Shanghai, ForkCondition::Timestamp(1681338455)), - ]), - } - .into() +pub static MAINNET: Lazy = Lazy::new(|| ChainSpec { + chain: Chain::mainnet(), + genesis: serde_json::from_str(include_str!("../../res/genesis/mainnet.json")) + .expect("Can't deserialize Mainnet genesis json"), + genesis_hash: Some(H256(hex!( + "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" + ))), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(1150000)), + (Hardfork::Dao, ForkCondition::Block(1920000)), + (Hardfork::Tangerine, ForkCondition::Block(2463000)), + (Hardfork::SpuriousDragon, ForkCondition::Block(2675000)), + (Hardfork::Byzantium, ForkCondition::Block(4370000)), + (Hardfork::Constantinople, ForkCondition::Block(7280000)), + (Hardfork::Petersburg, ForkCondition::Block(7280000)), + (Hardfork::Istanbul, ForkCondition::Block(9069000)), + (Hardfork::MuirGlacier, ForkCondition::Block(9200000)), + (Hardfork::Berlin, ForkCondition::Block(12244000)), + (Hardfork::London, ForkCondition::Block(12965000)), + (Hardfork::ArrowGlacier, ForkCondition::Block(13773000)), + (Hardfork::GrayGlacier, ForkCondition::Block(15050000)), + ( + Hardfork::Paris, + ForkCondition::TTD { + fork_block: None, + total_difficulty: U256::from(58_750_000_000_000_000_000_000_u128), + }, + ), + ]), + #[cfg(feature = "optimism")] + optimism: None, }); /// The Goerli spec -pub static GOERLI: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::goerli(), - genesis: serde_json::from_str(include_str!("../../res/genesis/goerli.json")) - .expect("Can't deserialize Goerli genesis json"), - genesis_hash: Some(H256(hex!( - "bf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" - ))), - // - paris_block_and_final_difficulty: Some((7382818, U256::from(10_790_000))), - fork_timestamps: ForkTimestamps::default().shanghai(1678832736), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(0)), - (Hardfork::Dao, 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(1561651)), - (Hardfork::Berlin, ForkCondition::Block(4460644)), - (Hardfork::London, ForkCondition::Block(5062605)), - ( - Hardfork::Paris, - ForkCondition::TTD { fork_block: None, total_difficulty: U256::from(10_790_000) }, - ), - (Hardfork::Shanghai, ForkCondition::Timestamp(1678832736)), - ]), - } - .into() +pub static GOERLI: Lazy = Lazy::new(|| ChainSpec { + chain: Chain::goerli(), + genesis: serde_json::from_str(include_str!("../../res/genesis/goerli.json")) + .expect("Can't deserialize Goerli genesis json"), + genesis_hash: Some(H256(hex!( + "bf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" + ))), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Istanbul, ForkCondition::Block(1561651)), + (Hardfork::Berlin, ForkCondition::Block(4460644)), + (Hardfork::London, ForkCondition::Block(5062605)), + ( + Hardfork::Paris, + ForkCondition::TTD { fork_block: None, total_difficulty: U256::from(10_790_000) }, + ), + ]), + #[cfg(feature = "optimism")] + optimism: None, }); /// The Sepolia spec -pub static SEPOLIA: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::sepolia(), - genesis: serde_json::from_str(include_str!("../../res/genesis/sepolia.json")) - .expect("Can't deserialize Sepolia genesis json"), - genesis_hash: Some(H256(hex!( - "25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9" - ))), - // - paris_block_and_final_difficulty: Some((1450409, U256::from(17_000_018_015_853_232u128))), - fork_timestamps: ForkTimestamps::default().shanghai(1677557088), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(0)), - (Hardfork::Dao, 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::Paris, - ForkCondition::TTD { - fork_block: Some(1735371), - total_difficulty: U256::from(17_000_000_000_000_000u64), - }, - ), - (Hardfork::Shanghai, ForkCondition::Timestamp(1677557088)), - ]), - } - .into() +pub static SEPOLIA: Lazy = Lazy::new(|| ChainSpec { + chain: Chain::sepolia(), + genesis: serde_json::from_str(include_str!("../../res/genesis/sepolia.json")) + .expect("Can't deserialize Sepolia genesis json"), + genesis_hash: Some(H256(hex!( + "25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9" + ))), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(0)), + (Hardfork::Dao, 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::Paris, + ForkCondition::TTD { + fork_block: Some(1735371), + total_difficulty: U256::from(17_000_000_000_000_000u64), + }, + ), + (Hardfork::Shanghai, ForkCondition::Timestamp(1677557088)), + ]), + #[cfg(feature = "optimism")] + optimism: None, }); /// An Ethereum chain specification. @@ -164,6 +140,20 @@ pub struct ChainSpec { /// The active hard forks and their activation conditions pub hardforks: BTreeMap, + + #[cfg(feature = "optimism")] + /// Optimism configuration + pub optimism: Option, +} + +#[cfg(feature = "optimism")] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Optimism configuration. +pub struct OptimismConfig { + /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) + pub eip_1559_elasticity: u64, + /// Base fee max change denominator as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) + pub eip_1559_denominator: u64, } impl ChainSpec { @@ -386,7 +376,8 @@ impl From for ChainSpec { genesis_hash: None, fork_timestamps: ForkTimestamps::from_hardforks(&hardforks), hardforks, - paris_block_and_final_difficulty: None, + #[cfg(feature = "optimism")] + optimism: None, } } } @@ -458,6 +449,8 @@ pub struct ChainSpecBuilder { chain: Option, genesis: Option, hardforks: BTreeMap, + #[cfg(feature = "optimism")] + optimism: Option, } impl ChainSpecBuilder { @@ -467,6 +460,8 @@ impl ChainSpecBuilder { chain: Some(MAINNET.chain), genesis: Some(MAINNET.genesis.clone()), hardforks: MAINNET.hardforks.clone(), + #[cfg(feature = "optimism")] + optimism: None, } } @@ -577,6 +572,20 @@ impl ChainSpecBuilder { self } + #[cfg(feature = "optimism")] + /// Enable Bedrock at genesis + pub fn bedrock_activated(mut self) -> Self { + self.hardforks.insert(Hardfork::Bedrock, ForkCondition::Block(0)); + self + } + + #[cfg(feature = "optimism")] + /// Enable Bedrock at genesis + pub fn regolith_activated(mut self) -> Self { + self.hardforks.insert(Hardfork::Regolith, ForkCondition::Timestamp(0)); + self + } + /// Build the resulting [`ChainSpec`]. /// /// # Panics @@ -590,7 +599,8 @@ impl ChainSpecBuilder { genesis_hash: None, fork_timestamps: ForkTimestamps::from_hardforks(&self.hardforks), hardforks: self.hardforks, - paris_block_and_final_difficulty: None, + #[cfg(feature = "optimism")] + optimism: self.optimism, } } } @@ -601,6 +611,8 @@ impl From<&Arc> for ChainSpecBuilder { chain: Some(value.chain), genesis: Some(value.genesis.clone()), hardforks: value.hardforks.clone(), + #[cfg(feature = "optimism")] + optimism: value.optimism.clone(), } } } diff --git a/crates/primitives/src/hardfork.rs b/crates/primitives/src/hardfork.rs index f26ffbcc403..a2f0f7be399 100644 --- a/crates/primitives/src/hardfork.rs +++ b/crates/primitives/src/hardfork.rs @@ -39,6 +39,12 @@ pub enum Hardfork { Paris, /// Shanghai. Shanghai, + #[cfg(feature = "optimism")] + /// Bedrock. + Bedrock, + #[cfg(feature = "optimism")] + /// Regolith + Regolith, } impl Hardfork { @@ -82,6 +88,10 @@ impl FromStr for Hardfork { "grayglacier" => Hardfork::GrayGlacier, "paris" => Hardfork::Paris, "shanghai" => Hardfork::Shanghai, + #[cfg(feature = "optimism")] + "bedrock" => Hardfork::Bedrock, + #[cfg(feature = "optimism")] + "regolith" => Hardfork::Regolith, _ => return Err(format!("Unknown hardfork: {s}")), }; Ok(hardfork) @@ -146,6 +156,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_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()); From c760918188261655d28d4ce7e0c444b3010a2481 Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Sun, 26 Feb 2023 14:41:49 +0200 Subject: [PATCH 03/43] feat: payload attributes op fields --- crates/rpc/rpc-types/Cargo.toml | 9 +- crates/rpc/rpc-types/src/eth/engine.rs | 246 +++++++++++++++++++++++++ 2 files changed, 249 insertions(+), 6 deletions(-) create mode 100644 crates/rpc/rpc-types/src/eth/engine.rs diff --git a/crates/rpc/rpc-types/Cargo.toml b/crates/rpc/rpc-types/Cargo.toml index 5db05498bac..7e56ccbe319 100644 --- a/crates/rpc/rpc-types/Cargo.toml +++ b/crates/rpc/rpc-types/Cargo.toml @@ -24,10 +24,7 @@ serde_json = { workspace = true } jsonrpsee-types = { version = "0.18" } [dev-dependencies] -# reth -reth-interfaces = { workspace = true, features = ["test-utils"] } +reth-interfaces = { path = "../../interfaces", features = ["test-utils"] } -# misc -rand = { workspace = true } -assert_matches = "1.5" -similar-asserts = "1.4" +[features] +optimism = ["reth-primitives/optimism"] diff --git a/crates/rpc/rpc-types/src/eth/engine.rs b/crates/rpc/rpc-types/src/eth/engine.rs new file mode 100644 index 00000000000..22c736dc1ab --- /dev/null +++ b/crates/rpc/rpc-types/src/eth/engine.rs @@ -0,0 +1,246 @@ +//! Engine API types: and following the execution specs + +#![allow(missing_docs)] + +use reth_primitives::{ + Address, Block, Bloom, Bytes, SealedBlock, Withdrawal, H256, H64, U256, U64, +}; +use reth_rlp::Encodable; +use serde::{Deserialize, Serialize}; + +/// The list of supported Engine capabilities +pub const CAPABILITIES: [&str; 9] = [ + "engine_forkchoiceUpdatedV1", + "engine_forkchoiceUpdatedV2", + "engine_exchangeTransitionConfigurationV1", + "engine_getPayloadV1", + "engine_getPayloadV2", + "engine_newPayloadV1", + "engine_newPayloadV2", + "engine_getPayloadBodiesByHashV1", + "engine_getPayloadBodiesByRangeV1", +]; + +/// This structure maps on the ExecutionPayload structure of the beacon chain spec. +/// +/// See also: +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ExecutionPayload { + pub parent_hash: H256, + pub fee_recipient: Address, + pub state_root: H256, + pub receipts_root: H256, + pub logs_bloom: Bloom, + pub prev_randao: H256, + pub block_number: U64, + pub gas_limit: U64, + pub gas_used: U64, + pub timestamp: U64, + pub extra_data: Bytes, + pub base_fee_per_gas: U256, + pub block_hash: H256, + pub transactions: Vec, + /// Array of [`Withdrawal`] enabled with V2 + /// See + #[serde(default, skip_serializing_if = "Option::is_none")] + pub withdrawals: Option>, +} + +impl From for ExecutionPayload { + fn from(value: SealedBlock) -> Self { + let transactions = value + .body + .iter() + .map(|tx| { + let mut encoded = Vec::new(); + tx.encode(&mut encoded); + encoded.into() + }) + .collect(); + ExecutionPayload { + parent_hash: value.parent_hash, + fee_recipient: value.beneficiary, + state_root: value.state_root, + receipts_root: value.receipts_root, + logs_bloom: value.logs_bloom, + prev_randao: value.mix_hash, + block_number: value.number.into(), + gas_limit: value.gas_limit.into(), + gas_used: value.gas_used.into(), + timestamp: value.timestamp.into(), + extra_data: value.extra_data.clone(), + base_fee_per_gas: U256::from(value.base_fee_per_gas.unwrap_or_default()), + block_hash: value.hash(), + transactions, + withdrawals: value.withdrawals, + } + } +} + +/// This structure contains a body of an execution payload. +/// +/// See also: +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct ExecutionPayloadBody { + pub transactions: Vec, + pub withdrawals: Vec, +} + +impl From for ExecutionPayloadBody { + fn from(value: Block) -> Self { + let transactions = value.body.into_iter().map(|tx| { + let mut out = Vec::new(); + tx.encode(&mut out); + out.into() + }); + ExecutionPayloadBody { + transactions: transactions.collect(), + withdrawals: value.withdrawals.unwrap_or_default(), + } + } +} + +/// The execution payload body response that allows for `null` values. +pub type ExecutionPayloadBodies = Vec>; + +/// This structure encapsulates the fork choice state +#[derive(Default, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ForkchoiceState { + pub head_block_hash: H256, + pub safe_block_hash: H256, + pub finalized_block_hash: H256, +} + +/// This structure contains the attributes required to initiate a payload build process in the +/// context of an `engine_forkchoiceUpdated` call. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PayloadAttributes { + pub timestamp: U64, + pub prev_randao: H256, + pub suggested_fee_recipient: Address, + /// Array of [`Withdrawal`] enabled with V2 + /// See + #[serde(default, skip_serializing_if = "Option::is_none")] + pub withdrawals: Option>, + + #[cfg(feature = "optimism")] + /// Transactions is a field for rollups: the transactions list is forced into the block + #[serde(default, skip_serializing_if = "Option::is_none")] + pub transactions: Option>, + + #[cfg(feature = "optimism")] + /// If true, the no transactions are taken out of the tx-pool, only transactions from the above + /// Transactions list will be included. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub no_tx_pool: Option, + + #[cfg(feature = "optimism")] + /// If set, this sets the exact gas limit the block produced with. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub gas_limit: Option, +} + +/// This structure contains the result of processing a payload +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PayloadStatus { + #[serde(flatten)] + pub status: PayloadStatusEnum, + /// Hash of the most recent valid block in the branch defined by payload and its ancestors + pub latest_valid_hash: Option, +} + +impl PayloadStatus { + pub fn new(status: PayloadStatusEnum, latest_valid_hash: H256) -> Self { + Self { status, latest_valid_hash: Some(latest_valid_hash) } + } + + pub fn from_status(status: PayloadStatusEnum) -> Self { + Self { status, latest_valid_hash: None } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "status", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum PayloadStatusEnum { + Valid, + Invalid { + #[serde(rename = "validationError")] + validation_error: String, + }, + Syncing, + Accepted, + InvalidBlockHash { + #[serde(rename = "validationError")] + validation_error: String, + }, +} + +/// This structure contains configurable settings of the transition process. +#[derive(Default, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TransitionConfiguration { + /// Maps on the TERMINAL_TOTAL_DIFFICULTY parameter of EIP-3675 + pub terminal_total_difficulty: U256, + /// Maps on TERMINAL_BLOCK_HASH parameter of EIP-3675 + pub terminal_block_hash: H256, + /// Maps on TERMINAL_BLOCK_NUMBER parameter of EIP-3675 + pub terminal_block_number: U64, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ForkchoiceUpdated { + pub payload_status: PayloadStatus, + pub payload_id: Option, +} + +impl ForkchoiceUpdated { + pub fn new(payload_status: PayloadStatus) -> Self { + Self { payload_status, payload_id: None } + } + + pub fn from_status(status: PayloadStatusEnum) -> Self { + Self { payload_status: PayloadStatus::from_status(status), payload_id: None } + } + + pub fn with_latest_valid_hash(mut self, hash: H256) -> Self { + self.payload_status.latest_valid_hash = Some(hash); + self + } + + pub fn with_payload_id(mut self, id: H64) -> Self { + self.payload_id = Some(id); + self + } +} + +#[cfg(test)] +mod tests { + use super::*; + use reth_interfaces::test_utils::generators::random_block_range; + use reth_primitives::{TransactionSigned, H256}; + use reth_rlp::Decodable; + + #[test] + fn payload_body_roundtrip() { + for block in random_block_range(0..100, H256::default(), 0..2) { + let unsealed = block.clone().unseal(); + let payload_body: ExecutionPayloadBody = unsealed.into(); + + assert_eq!( + Ok(block.body), + payload_body + .transactions + .iter() + .map(|x| TransactionSigned::decode(&mut &x[..])) + .collect::, _>>(), + ); + + assert_eq!(block.withdrawals.unwrap_or_default(), payload_body.withdrawals); + } + } +} From 15be115a4aedefd4cce69bda672e36b2b34b77de Mon Sep 17 00:00:00 2001 From: Andreas Bigger Date: Sun, 26 Feb 2023 18:39:58 -0700 Subject: [PATCH 04/43] small fixes --- crates/primitives/src/transaction/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 4a1dcb035e9..b2c7c6bb9b4 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -669,6 +669,17 @@ impl Transaction { /// Encodes only the transaction's fields into the desired buffer, without a RLP header. pub(crate) fn encode_fields(&self, out: &mut dyn bytes::BufMut) { match self { + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { to, mint, value, gas_limit, input, .. }) => { + tx_env.gas_limit = *gas_limit; + tx_env.gas_price = U256::from(*mint); + tx_env.gas_priority_fee = None; + tx_env.transact_to = TransactTo::Call(*to); + tx_env.value = U256::from(*value); + tx_env.data = input.0.clone(); + tx_env.chain_id = None; + tx_env.nonce = None; + } Transaction::Legacy(TxLegacy { chain_id: _, nonce, From b761db0ca7f19ba48653df8746650b3411ad4f1f Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 26 Feb 2023 20:36:42 -0500 Subject: [PATCH 05/43] Small changes to the deposit tx primitive --- crates/primitives/src/transaction/mod.rs | 50 +++++++++++++++++-- crates/primitives/src/transaction/optimism.rs | 2 +- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index b2c7c6bb9b4..7391688bdc2 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -13,7 +13,7 @@ use reth_codecs::{add_arbitrary_tests, derive_arbitrary, main_codec, Compact}; use reth_rlp::{ length_of_length, Decodable, DecodeError, Encodable, Header, EMPTY_LIST_CODE, EMPTY_STRING_CODE, }; -use serde::{Deserialize, Serialize}; +use revm_primitives::U256; pub use signature::Signature; pub use tx_type::{TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, LEGACY_TX_TYPE_ID}; @@ -437,7 +437,8 @@ impl Transaction { Transaction::Eip2930(TxEip2930 { nonce, .. }) => *nonce, Transaction::Eip1559(TxEip1559 { nonce, .. }) => *nonce, #[cfg(feature = "optimism")] - Transaction::Deposit(_) => todo!(), // TODO: + // Deposit transactions don't have a nonce, so they default to zero. + Transaction::Deposit(_) => 0, } } @@ -568,6 +569,36 @@ impl Transaction { } } + #[cfg(feature = "optimism")] + /// Returns the source hash of the transaction, which uniquely identifies its source. + /// If the transaction is not a deposit transaction, this will always return `H256::zero()`. + pub fn source_hash(&self) -> H256 { + match self { + Transaction::Deposit(TxDeposit { source_hash, .. }) => *source_hash, + _ => H256::zero(), + } + } + + #[cfg(feature = "optimism")] + /// 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`. + pub fn mint(&self) -> Option { + match self { + Transaction::Deposit(TxDeposit { mint, .. }) => *mint, + _ => None, + } + } + + #[cfg(feature = "optimism")] + /// Returns whether or not the transaction is a system transaction. If the transaction + /// is not a deposit transaction, this will always return `false`. + pub fn is_system_transaction(&self) -> bool { + match self { + Transaction::Deposit(TxDeposit { is_system_transaction, .. }) => *is_system_transaction, + _ => false, + } + } + /// Encodes EIP-155 arguments into the desired buffer. Only encodes values for legacy /// transactions. pub(crate) fn encode_eip155_fields(&self, out: &mut dyn bytes::BufMut) { @@ -1145,7 +1176,16 @@ impl TransactionSigned { let header = Header { list: true, payload_length }; header.encode(out); self.transaction.encode_fields(out); - self.signature.encode(out); + // Deposit transactions do not have a signature. If the signature's values are not + // zero, then the transaction is invalid. + if self.signature().v(self.chain_id()) != 0 || + self.signature().r != U256::ZERO || + self.signature().s != U256::ZERO + { + // TODO: Ensure that this transaction may never have a non-zero signature + // higher up - we shouldn't be panicking here. + panic!("Deposit transactions must have a zero signature"); + } } _ => { let payload_length = self.transaction.fields_len() + self.signature.payload_len(); @@ -1255,6 +1295,8 @@ impl TransactionSigned { data.advance(1); #[cfg(feature = "optimism")] + // If the transaction is a deposit, we need to first ensure that the version + // byte is correct. if tx_type == DEPOSIT_TX_TYPE { let version = *data.first().ok_or(DecodeError::InputTooShort)?; if version != DEPOSIT_VERSION { @@ -1272,6 +1314,8 @@ impl TransactionSigned { // length of tx encoding = tx type byte (size = 1) + length of header + payload length let tx_length = 1 + header.length() + header.payload_length; #[cfg(feature = "optimism")] + // If the transaction is a deposit, we need to add one to the length to account for the + // version byte. let tx_length = if tx_type == DEPOSIT_TX_TYPE { tx_length + 1 } else { tx_length }; // decode common fields diff --git a/crates/primitives/src/transaction/optimism.rs b/crates/primitives/src/transaction/optimism.rs index 1b48f43fd23..31c272555fc 100644 --- a/crates/primitives/src/transaction/optimism.rs +++ b/crates/primitives/src/transaction/optimism.rs @@ -43,7 +43,7 @@ impl TxDeposit { len += self.source_hash.length(); len += self.from.length(); len += self.to.length(); - len += self.mint.map(|mint| mint.length()).unwrap_or(1); + len += self.mint.map_or(1, |mint| mint.length()); len += self.value.length(); len += self.input.0.length(); len += self.gas_limit.length(); From 968c2bf28f62cd3d0ce6a28646d9f35adffea946 Mon Sep 17 00:00:00 2001 From: Andreas Bigger Date: Sun, 26 Feb 2023 19:39:42 -0700 Subject: [PATCH 06/43] more small fixes --- crates/primitives/src/transaction/mod.rs | 14 ++------ crates/revm/revm-primitives/Cargo.toml | 7 ++-- crates/revm/revm-primitives/src/env.rs | 14 ++++++++ crates/transaction-pool/Cargo.toml | 1 + .../transaction-pool/src/test_utils/mock.rs | 34 +++++++++++++++++++ crates/transaction-pool/src/traits.rs | 8 +++-- 6 files changed, 60 insertions(+), 18 deletions(-) diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 7391688bdc2..7e844d04ea7 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -13,6 +13,7 @@ use reth_codecs::{add_arbitrary_tests, derive_arbitrary, main_codec, Compact}; use reth_rlp::{ length_of_length, Decodable, DecodeError, Encodable, Header, EMPTY_LIST_CODE, EMPTY_STRING_CODE, }; +#[cfg(feature = "optimism")] use revm_primitives::U256; pub use signature::Signature; pub use tx_type::{TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, LEGACY_TX_TYPE_ID}; @@ -700,17 +701,6 @@ impl Transaction { /// Encodes only the transaction's fields into the desired buffer, without a RLP header. pub(crate) fn encode_fields(&self, out: &mut dyn bytes::BufMut) { match self { - #[cfg(feature = "optimism")] - Transaction::Deposit(TxDeposit { to, mint, value, gas_limit, input, .. }) => { - tx_env.gas_limit = *gas_limit; - tx_env.gas_price = U256::from(*mint); - tx_env.gas_priority_fee = None; - tx_env.transact_to = TransactTo::Call(*to); - tx_env.value = U256::from(*value); - tx_env.data = input.0.clone(); - tx_env.chain_id = None; - tx_env.nonce = None; - } Transaction::Legacy(TxLegacy { chain_id: _, nonce, @@ -1098,7 +1088,7 @@ impl TransactionSigned { pub fn recover_signer(&self) -> Option
{ #[cfg(feature = "optimism")] if let Transaction::Deposit(TxDeposit { from, .. }) = self.transaction { - return Some(from.clone()) + return Some(from) } let signature_hash = self.signature_hash(); self.signature.recover_signer(signature_hash) diff --git a/crates/revm/revm-primitives/Cargo.toml b/crates/revm/revm-primitives/Cargo.toml index 96efb46ff86..8c00dc3670a 100644 --- a/crates/revm/revm-primitives/Cargo.toml +++ b/crates/revm/revm-primitives/Cargo.toml @@ -9,7 +9,8 @@ repository.workspace = true description = "core reth specific revm utilities" [dependencies] -# reth -reth-primitives = { workspace = true } +reth-primitives = { path = "../../primitives" } +revm = { version = "3.0.0" } -revm = { workspace = true } +[features] +optimism = [] diff --git a/crates/revm/revm-primitives/src/env.rs b/crates/revm/revm-primitives/src/env.rs index 4a3ee323928..27ffd532478 100644 --- a/crates/revm/revm-primitives/src/env.rs +++ b/crates/revm/revm-primitives/src/env.rs @@ -5,6 +5,9 @@ use reth_primitives::{ }; use revm::primitives::{AnalysisKind, BlockEnv, CfgEnv, SpecId, TransactTo, TxEnv}; +#[cfg(feature = "optimism")] +use reth_primitives::TxDeposit; + /// Convenience function to call both [fill_cfg_env] and [fill_block_env] pub fn fill_cfg_and_block_env( cfg: &mut CfgEnv, @@ -119,6 +122,17 @@ where { tx_env.caller = sender; match transaction.as_ref() { + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { to, mint, value, gas_limit, input, .. }) => { + tx_env.gas_limit = *gas_limit; + tx_env.gas_price = U256::from(*mint); + tx_env.gas_priority_fee = None; + tx_env.transact_to = TransactTo::Call(*to); + tx_env.value = U256::from(*value); + tx_env.data = input.0.clone(); + tx_env.chain_id = None; + tx_env.nonce = None; + } Transaction::Legacy(TxLegacy { nonce, chain_id, diff --git a/crates/transaction-pool/Cargo.toml b/crates/transaction-pool/Cargo.toml index c562df71cf5..47f44068627 100644 --- a/crates/transaction-pool/Cargo.toml +++ b/crates/transaction-pool/Cargo.toml @@ -53,3 +53,4 @@ rand = "0.8" default = ["serde"] serde = ["dep:serde"] test-utils = ["rand", "paste", "serde"] +optimism = [] \ No newline at end of file diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index 13ec9b5af01..9189391a815 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -22,6 +22,9 @@ pub(crate) type MockTxPool = TxPool; pub type MockValidTx = ValidPoolTransaction; +#[cfg(feature = "optimism")] +use reth_primitives::TxDeposit; + /// Create an empty `TxPool` pub(crate) fn mock_tx_pool() -> MockTxPool { MockTxPool::new(Default::default(), Default::default()) @@ -99,6 +102,17 @@ pub enum MockTransaction { to: TransactionKind, value: U256, }, + #[cfg(feature = "optimism")] + DepositTx { + source_hash: H256, + from: Address, + to: TransactionKind, + mint: Option, + value: u128, + gas_limit: u64, + is_system_transaction: bool, + input: Bytes, + }, } // === impl MockTransaction === @@ -393,6 +407,26 @@ impl FromRecoveredTransaction for MockTransaction { let transaction = tx.into_signed(); let hash = transaction.hash(); match transaction.transaction { + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { + source_hash, + from, + to, + mint, + value, + gas_limit, + is_system_transaction, + input, + }) => MockTransaction::DepositTx { + source_hash, + from, + to, + mint, + value, + gas_limit, + is_system_transaction, + input, + }, Transaction::Legacy(TxLegacy { chain_id, nonce, diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index d44c25f1b52..0f027dc6e23 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -582,9 +582,11 @@ impl PoolTransaction for PooledTransaction { /// This is also commonly referred to as the "Gas Fee Cap" (`GasFeeCap`). fn max_fee_per_gas(&self) -> u128 { match &self.transaction.transaction { - Transaction::Legacy(tx) => tx.gas_price, - Transaction::Eip2930(tx) => tx.gas_price, - Transaction::Eip1559(tx) => tx.max_fee_per_gas, + #[cfg(feature = "optimism")] + Transaction::Optimism(_) => None, + Transaction::Legacy(_) => None, + Transaction::Eip2930(_) => None, + Transaction::Eip1559(tx) => Some(tx.max_fee_per_gas), } } From 1c17ad88a5f060c3468ae4cd894feff5cd839c7b Mon Sep 17 00:00:00 2001 From: Andreas Bigger Date: Sun, 26 Feb 2023 22:26:58 -0700 Subject: [PATCH 07/43] compiles --- crates/consensus/Cargo.toml | 24 +++++++++ crates/consensus/common/src/validation.rs | 10 ++++ crates/primitives/src/hardfork.rs | 8 +-- crates/revm/revm-primitives/src/env.rs | 11 +++- crates/rpc/rpc-types/src/eth/engine.rs | 2 +- .../rpc/rpc-types/src/eth/transaction/mod.rs | 18 +++---- .../transaction-pool/src/test_utils/mock.rs | 52 ++++++++++++++++--- crates/transaction-pool/src/traits.rs | 33 ++++++++++-- 8 files changed, 126 insertions(+), 32 deletions(-) create mode 100644 crates/consensus/Cargo.toml diff --git a/crates/consensus/Cargo.toml b/crates/consensus/Cargo.toml new file mode 100644 index 00000000000..bd742d0bed0 --- /dev/null +++ b/crates/consensus/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "reth-consensus" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/paradigmxyz/reth" +readme = "README.md" + +[dependencies] +# reth +reth-primitives = { path = "../primitives" } +reth-interfaces = { path = "../interfaces" } +reth-provider = { path = "../storage/provider" } + +# async +tokio = { version = "1", features = ["sync"] } + +[dev-dependencies] +reth-interfaces = { path = "../interfaces", features = ["test-utils"] } +reth-provider = { path = "../storage/provider", features = ["test-utils"] } +assert_matches = "1.5.0" + +[features] +optimism = [] \ No newline at end of file diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index f61e160f039..d54e33e80d4 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -10,6 +10,11 @@ use std::{ time::SystemTime, }; +use reth_primitives::constants; + +#[cfg(feature = "optimism")] +use reth_primitives::TxDeposit; + /// Validate header standalone pub fn validate_header_standalone( header: &SealedHeader, @@ -64,6 +69,11 @@ pub fn validate_transaction_regarding_header( base_fee: Option, ) -> Result<(), ConsensusError> { let chain_id = match transaction { + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { .. }) => { + // TODO: get the chain id + None + } Transaction::Legacy(TxLegacy { chain_id, .. }) => { // EIP-155: Simple replay attack protection: https://eips.ethereum.org/EIPS/eip-155 if chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(at_block_number) && diff --git a/crates/primitives/src/hardfork.rs b/crates/primitives/src/hardfork.rs index a2f0f7be399..cc5d384faeb 100644 --- a/crates/primitives/src/hardfork.rs +++ b/crates/primitives/src/hardfork.rs @@ -180,8 +180,8 @@ mod tests { genesis: Genesis::default(), genesis_hash: None, hardforks: BTreeMap::from([(Hardfork::Frontier, ForkCondition::Never)]), - fork_timestamps: Default::default(), - paris_block_and_final_difficulty: None, + #[cfg(feature = "optimism")] + optimism: None, }; assert_eq!(Hardfork::Frontier.fork_id(&spec), None); @@ -194,8 +194,8 @@ mod tests { genesis: Genesis::default(), genesis_hash: None, hardforks: BTreeMap::from([(Hardfork::Shanghai, ForkCondition::Never)]), - fork_timestamps: Default::default(), - paris_block_and_final_difficulty: None, + #[cfg(feature = "optimism")] + optimism: None, }; assert_eq!(Hardfork::Shanghai.fork_filter(&spec), None); diff --git a/crates/revm/revm-primitives/src/env.rs b/crates/revm/revm-primitives/src/env.rs index 27ffd532478..5351914c1e8 100644 --- a/crates/revm/revm-primitives/src/env.rs +++ b/crates/revm/revm-primitives/src/env.rs @@ -125,9 +125,16 @@ where #[cfg(feature = "optimism")] Transaction::Deposit(TxDeposit { to, mint, value, gas_limit, input, .. }) => { tx_env.gas_limit = *gas_limit; - tx_env.gas_price = U256::from(*mint); + if let Some(m) = mint { + tx_env.gas_price = U256::from(*m); + } else { + tx_env.gas_price = U256::ZERO; + } tx_env.gas_priority_fee = None; - tx_env.transact_to = TransactTo::Call(*to); + match to { + TransactionKind::Call(to) => tx_env.transact_to = TransactTo::Call(*to), + TransactionKind::Create => tx_env.transact_to = TransactTo::create(), + } tx_env.value = U256::from(*value); tx_env.data = input.0.clone(); tx_env.chain_id = None; diff --git a/crates/rpc/rpc-types/src/eth/engine.rs b/crates/rpc/rpc-types/src/eth/engine.rs index 22c736dc1ab..e8be77f3adc 100644 --- a/crates/rpc/rpc-types/src/eth/engine.rs +++ b/crates/rpc/rpc-types/src/eth/engine.rs @@ -140,7 +140,7 @@ pub struct PayloadAttributes { #[cfg(feature = "optimism")] /// If set, this sets the exact gas limit the block produced with. #[serde(default, skip_serializing_if = "Option::is_none")] - pub gas_limit: Option, + pub gas_limit: Option, } /// This structure contains the result of processing a payload diff --git a/crates/rpc/rpc-types/src/eth/transaction/mod.rs b/crates/rpc/rpc-types/src/eth/transaction/mod.rs index 24005376c82..8c90fb83b2f 100644 --- a/crates/rpc/rpc-types/src/eth/transaction/mod.rs +++ b/crates/rpc/rpc-types/src/eth/transaction/mod.rs @@ -111,22 +111,16 @@ impl Transaction { let (gas_price, max_fee_per_gas) = match signed_tx.tx_type() { TxType::Legacy => (Some(U128::from(signed_tx.max_fee_per_gas())), None), - TxType::EIP2930 => (Some(U128::from(signed_tx.max_fee_per_gas())), None), - TxType::EIP1559 => { - // the gas price field for EIP1559 is set to `min(tip, gasFeeCap - baseFee) + - // baseFee` - let gas_price = base_fee - .and_then(|base_fee| { - signed_tx.effective_tip_per_gas(base_fee).map(|tip| tip + base_fee as u128) - }) - .unwrap_or_else(|| signed_tx.max_fee_per_gas()); - - (Some(U128::from(gas_price)), Some(U128::from(signed_tx.max_fee_per_gas()))) - } + TxType::EIP2930 => (None, Some(U128::from(signed_tx.max_fee_per_gas()))), + TxType::EIP1559 => (None, Some(U128::from(signed_tx.max_fee_per_gas()))), + #[cfg(feature = "optimism")] + TxType::DEPOSIT => (None, None), }; let chain_id = signed_tx.chain_id().map(U64::from); let access_list = match &signed_tx.transaction { + #[cfg(feature = "optimism")] + PrimitiveTransaction::Deposit(_) => None, PrimitiveTransaction::Legacy(_) => None, PrimitiveTransaction::Eip2930(tx) => Some( tx.access_list diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index 9189391a815..8531d21ab99 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -22,9 +22,15 @@ pub(crate) type MockTxPool = TxPool; pub type MockValidTx = ValidPoolTransaction; +#[cfg(feature = "optimism")] +use reth_primitives::DEPOSIT_TX_TYPE; + #[cfg(feature = "optimism")] use reth_primitives::TxDeposit; +#[cfg(feature = "optimism")] +use reth_primitives::Bytes; + /// Create an empty `TxPool` pub(crate) fn mock_tx_pool() -> MockTxPool { MockTxPool::new(Default::default(), Default::default()) @@ -35,6 +41,10 @@ macro_rules! set_value { ($this:ident => $field:ident) => { let new_value = $field; match $this { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { ref mut $field, .. } => { + *$field = new_value; + } MockTransaction::Legacy { ref mut $field, .. } => { *$field = new_value; } @@ -49,6 +59,8 @@ macro_rules! set_value { macro_rules! get_value { ($this:ident => $field:ident) => { match $this { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { $field, .. } => $field, MockTransaction::Legacy { $field, .. } => $field, MockTransaction::Eip1559 { $field, .. } => $field, } @@ -104,14 +116,15 @@ pub enum MockTransaction { }, #[cfg(feature = "optimism")] DepositTx { - source_hash: H256, - from: Address, + hash: H256, + sender: Address, + nonce: u64, to: TransactionKind, mint: Option, - value: u128, gas_limit: u64, is_system_transaction: bool, input: Bytes, + value: U256, }, } @@ -199,6 +212,8 @@ impl MockTransaction { pub fn set_gas_price(&mut self, val: u128) -> &mut Self { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => {} MockTransaction::Legacy { gas_price, .. } => { *gas_price = val; } @@ -212,6 +227,8 @@ impl MockTransaction { pub fn with_gas_price(mut self, val: u128) -> Self { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => {} MockTransaction::Legacy { ref mut gas_price, .. } => { *gas_price = val; } @@ -229,6 +246,8 @@ impl MockTransaction { pub fn get_gas_price(&self) -> u128 { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => 0, MockTransaction::Legacy { gas_price, .. } => *gas_price, MockTransaction::Eip1559 { max_fee_per_gas, .. } => *max_fee_per_gas, } @@ -313,6 +332,8 @@ impl MockTransaction { impl PoolTransaction for MockTransaction { fn hash(&self) -> &TxHash { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { hash, .. } => hash, MockTransaction::Legacy { hash, .. } => hash, MockTransaction::Eip1559 { hash, .. } => hash, } @@ -320,6 +341,8 @@ impl PoolTransaction for MockTransaction { fn sender(&self) -> Address { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { sender, .. } => *sender, MockTransaction::Legacy { sender, .. } => *sender, MockTransaction::Eip1559 { sender, .. } => *sender, } @@ -327,6 +350,8 @@ impl PoolTransaction for MockTransaction { fn nonce(&self) -> u64 { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { nonce, .. } => *nonce, MockTransaction::Legacy { nonce, .. } => *nonce, MockTransaction::Eip1559 { nonce, .. } => *nonce, } @@ -334,6 +359,8 @@ impl PoolTransaction for MockTransaction { fn cost(&self) -> U256 { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => U256::from(0), MockTransaction::Legacy { gas_price, value, gas_limit, .. } => { U256::from(*gas_limit) * U256::from(*gas_price) + *value } @@ -360,13 +387,17 @@ impl PoolTransaction for MockTransaction { fn max_fee_per_gas(&self) -> u128 { match self { - MockTransaction::Legacy { gas_price, .. } => *gas_price, - MockTransaction::Eip1559 { max_fee_per_gas, .. } => *max_fee_per_gas, + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => None, + MockTransaction::Legacy { .. } => None, + MockTransaction::Eip1559 { max_fee_per_gas, .. } => Some(*max_fee_per_gas), } } fn max_priority_fee_per_gas(&self) -> Option { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => None, MockTransaction::Legacy { .. } => None, MockTransaction::Eip1559 { max_priority_fee_per_gas, .. } => { Some(*max_priority_fee_per_gas) @@ -376,6 +407,8 @@ impl PoolTransaction for MockTransaction { fn kind(&self) -> &TransactionKind { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { to, .. } => to, MockTransaction::Legacy { to, .. } => to, MockTransaction::Eip1559 { to, .. } => to, } @@ -387,6 +420,8 @@ impl PoolTransaction for MockTransaction { fn tx_type(&self) -> u8 { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => DEPOSIT_TX_TYPE, MockTransaction::Legacy { .. } => TxType::Legacy.into(), MockTransaction::Eip1559 { .. } => TxType::EIP1559.into(), } @@ -418,11 +453,12 @@ impl FromRecoveredTransaction for MockTransaction { is_system_transaction, input, }) => MockTransaction::DepositTx { - source_hash, - from, + nonce: 0u64, + hash: source_hash, + sender: from, to, mint, - value, + value: U256::from(value), gas_limit, is_system_transaction, input, diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index 0f027dc6e23..ed81554c60b 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -583,7 +583,7 @@ impl PoolTransaction for PooledTransaction { fn max_fee_per_gas(&self) -> u128 { match &self.transaction.transaction { #[cfg(feature = "optimism")] - Transaction::Optimism(_) => None, + Transaction::Deposit(_) => None, Transaction::Legacy(_) => None, Transaction::Eip2930(_) => None, Transaction::Eip1559(tx) => Some(tx.max_fee_per_gas), @@ -595,6 +595,8 @@ impl PoolTransaction for PooledTransaction { /// This will return `None` for non-EIP1559 transactions fn max_priority_fee_per_gas(&self) -> Option { match &self.transaction.transaction { + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => None, Transaction::Legacy(_) => None, Transaction::Eip2930(_) => None, Transaction::Eip1559(tx) => Some(tx.max_priority_fee_per_gas), @@ -630,10 +632,31 @@ impl PoolTransaction for PooledTransaction { impl FromRecoveredTransaction for PooledTransaction { fn from_recovered_transaction(tx: TransactionSignedEcRecovered) -> Self { - let gas_cost = match &tx.transaction { - Transaction::Legacy(t) => U256::from(t.gas_price) * U256::from(t.gas_limit), - Transaction::Eip2930(t) => U256::from(t.gas_price) * U256::from(t.gas_limit), - Transaction::Eip1559(t) => U256::from(t.max_fee_per_gas) * U256::from(t.gas_limit), + let (cost, effective_gas_price) = match &tx.transaction { + #[cfg(feature = "optimism")] + Transaction::Deposit(t) => { + // TODO: fix this gas price estimate + let gas_price = U256::from(0); + let cost = U256::from(gas_price) * U256::from(t.gas_limit) + U256::from(t.value); + let effective_gas_price = 0u128; + (cost, effective_gas_price) + } + Transaction::Legacy(t) => { + let cost = U256::from(t.gas_price) * U256::from(t.gas_limit) + U256::from(t.value); + let effective_gas_price = t.gas_price; + (cost, effective_gas_price) + } + Transaction::Eip2930(t) => { + let cost = U256::from(t.gas_price) * U256::from(t.gas_limit) + U256::from(t.value); + let effective_gas_price = t.gas_price; + (cost, effective_gas_price) + } + Transaction::Eip1559(t) => { + let cost = + U256::from(t.max_fee_per_gas) * U256::from(t.gas_limit) + U256::from(t.value); + let effective_gas_price = t.max_priority_fee_per_gas; + (cost, effective_gas_price) + } }; let cost = gas_cost + U256::from(tx.value()); From 74cee5d06aaa07319644b869e36540d5b906f2c1 Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 27 Feb 2023 09:05:01 -0500 Subject: [PATCH 08/43] Move feature flags below comments --- crates/primitives/src/chain/spec.rs | 8 ++++---- crates/primitives/src/hardfork.rs | 4 ++-- crates/primitives/src/transaction/mod.rs | 16 ++++++++-------- crates/primitives/src/transaction/tx_type.rs | 2 +- crates/rpc/rpc-types/src/eth/engine.rs | 6 +++--- crates/transaction-pool/src/test_utils/mock.rs | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 9d493d2a14c..86d1fbbbbee 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -141,14 +141,14 @@ pub struct ChainSpec { /// The active hard forks and their activation conditions pub hardforks: BTreeMap, - #[cfg(feature = "optimism")] /// Optimism configuration + #[cfg(feature = "optimism")] pub optimism: Option, } +/// Optimism configuration. #[cfg(feature = "optimism")] #[derive(Serialize, Deserialize, Debug, Clone)] -/// Optimism configuration. pub struct OptimismConfig { /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) pub eip_1559_elasticity: u64, @@ -572,15 +572,15 @@ impl ChainSpecBuilder { self } - #[cfg(feature = "optimism")] /// Enable Bedrock at genesis + #[cfg(feature = "optimism")] pub fn bedrock_activated(mut self) -> Self { self.hardforks.insert(Hardfork::Bedrock, ForkCondition::Block(0)); self } - #[cfg(feature = "optimism")] /// Enable Bedrock at genesis + #[cfg(feature = "optimism")] pub fn regolith_activated(mut self) -> Self { self.hardforks.insert(Hardfork::Regolith, ForkCondition::Timestamp(0)); self diff --git a/crates/primitives/src/hardfork.rs b/crates/primitives/src/hardfork.rs index cc5d384faeb..0c3942ebd9d 100644 --- a/crates/primitives/src/hardfork.rs +++ b/crates/primitives/src/hardfork.rs @@ -39,11 +39,11 @@ pub enum Hardfork { Paris, /// Shanghai. Shanghai, - #[cfg(feature = "optimism")] /// Bedrock. - Bedrock, #[cfg(feature = "optimism")] + Bedrock, /// Regolith + #[cfg(feature = "optimism")] Regolith, } diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 7e844d04ea7..e59f2cc8e8a 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -233,8 +233,8 @@ pub enum Transaction { Eip2930(TxEip2930), /// A transaction with a priority fee ([EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)). Eip1559(TxEip1559), - #[cfg(feature = "optimism")] /// Deposit transaction. + #[cfg(feature = "optimism")] Deposit(TxDeposit), } @@ -437,8 +437,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { nonce, .. }) => *nonce, Transaction::Eip2930(TxEip2930 { nonce, .. }) => *nonce, Transaction::Eip1559(TxEip1559 { nonce, .. }) => *nonce, - #[cfg(feature = "optimism")] // Deposit transactions don't have a nonce, so they default to zero. + #[cfg(feature = "optimism")] Transaction::Deposit(_) => 0, } } @@ -462,9 +462,9 @@ impl Transaction { Transaction::Legacy(TxLegacy { gas_price, .. }) | Transaction::Eip2930(TxEip2930 { gas_price, .. }) => *gas_price, Transaction::Eip1559(TxEip1559 { max_fee_per_gas, .. }) => *max_fee_per_gas, - #[cfg(feature = "optimism")] // Deposit transactions buy their L2 gas on L1 and, as such, the L2 gas is not // refundable. + #[cfg(feature = "optimism")] Transaction::Deposit(_) => 0, } } @@ -570,9 +570,9 @@ impl Transaction { } } - #[cfg(feature = "optimism")] /// Returns the source hash of the transaction, which uniquely identifies its source. /// If the transaction is not a deposit transaction, this will always return `H256::zero()`. + #[cfg(feature = "optimism")] pub fn source_hash(&self) -> H256 { match self { Transaction::Deposit(TxDeposit { source_hash, .. }) => *source_hash, @@ -580,9 +580,9 @@ impl Transaction { } } - #[cfg(feature = "optimism")] /// 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 { match self { Transaction::Deposit(TxDeposit { mint, .. }) => *mint, @@ -590,9 +590,9 @@ impl Transaction { } } - #[cfg(feature = "optimism")] /// 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, @@ -1284,9 +1284,9 @@ impl TransactionSigned { let tx_type = *data.first().ok_or(DecodeError::InputTooShort)?; data.advance(1); - #[cfg(feature = "optimism")] // If the transaction is a deposit, we need to first ensure that the version // byte is correct. + #[cfg(feature = "optimism")] if tx_type == DEPOSIT_TX_TYPE { let version = *data.first().ok_or(DecodeError::InputTooShort)?; if version != DEPOSIT_VERSION { @@ -1303,9 +1303,9 @@ impl TransactionSigned { // length of tx encoding = tx type byte (size = 1) + length of header + payload length let tx_length = 1 + header.length() + header.payload_length; - #[cfg(feature = "optimism")] // If the transaction is a deposit, we need to add one to the length to account for the // version byte. + #[cfg(feature = "optimism")] let tx_length = if tx_type == DEPOSIT_TX_TYPE { tx_length + 1 } else { tx_length }; // decode common fields diff --git a/crates/primitives/src/transaction/tx_type.rs b/crates/primitives/src/transaction/tx_type.rs index 1e7c9edfae5..39fa1710737 100644 --- a/crates/primitives/src/transaction/tx_type.rs +++ b/crates/primitives/src/transaction/tx_type.rs @@ -23,8 +23,8 @@ pub enum TxType { EIP2930 = 1_isize, /// Transaction with Priority fee EIP1559 = 2_isize, - #[cfg(feature = "optimism")] /// OP Deposit transaction. + #[cfg(feature = "optimism")] DEPOSIT = 126_isize, } diff --git a/crates/rpc/rpc-types/src/eth/engine.rs b/crates/rpc/rpc-types/src/eth/engine.rs index e8be77f3adc..548ed16645b 100644 --- a/crates/rpc/rpc-types/src/eth/engine.rs +++ b/crates/rpc/rpc-types/src/eth/engine.rs @@ -126,19 +126,19 @@ pub struct PayloadAttributes { #[serde(default, skip_serializing_if = "Option::is_none")] pub withdrawals: Option>, - #[cfg(feature = "optimism")] /// Transactions is a field for rollups: the transactions list is forced into the block + #[cfg(feature = "optimism")] #[serde(default, skip_serializing_if = "Option::is_none")] pub transactions: Option>, - #[cfg(feature = "optimism")] /// If true, the no transactions are taken out of the tx-pool, only transactions from the above /// Transactions list will be included. + #[cfg(feature = "optimism")] #[serde(default, skip_serializing_if = "Option::is_none")] pub no_tx_pool: Option, - #[cfg(feature = "optimism")] /// If set, this sets the exact gas limit the block produced with. + #[cfg(feature = "optimism")] #[serde(default, skip_serializing_if = "Option::is_none")] pub gas_limit: Option, } diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index 8531d21ab99..62cc70afc46 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -360,7 +360,7 @@ impl PoolTransaction for MockTransaction { fn cost(&self) -> U256 { match self { #[cfg(feature = "optimism")] - MockTransaction::DepositTx { .. } => U256::from(0), + MockTransaction::DepositTx { .. } => U256::ZERO, MockTransaction::Legacy { gas_price, value, gas_limit, .. } => { U256::from(*gas_limit) * U256::from(*gas_price) + *value } From bd71db39c65f4a250c07260a0b875eca41107480 Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 27 Feb 2023 09:14:05 -0500 Subject: [PATCH 09/43] Doc auto link --- crates/primitives/src/transaction/optimism.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/primitives/src/transaction/optimism.rs b/crates/primitives/src/transaction/optimism.rs index 31c272555fc..faad6908d48 100644 --- a/crates/primitives/src/transaction/optimism.rs +++ b/crates/primitives/src/transaction/optimism.rs @@ -52,7 +52,7 @@ impl TxDeposit { } /// 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); From ce41d797b5741d5b0d8bd74def0fb54aaa23c0ba Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 5 Mar 2023 13:08:35 -0700 Subject: [PATCH 10/43] WIP: OP Goerli genesis --- crates/primitives/res/genesis/goerli_op.json | 92 +++++++++++++++ crates/primitives/src/chain/mod.rs | 15 +++ crates/primitives/src/chain/spec.rs | 50 +++++++- crates/primitives/src/constants.rs | 5 + crates/primitives/src/lib.rs | 2 + crates/rpc/rpc-engine-api/Cargo.toml | 3 + crates/rpc/rpc-engine-api/src/engine_api.rs | 113 +++++++++++++++++++ crates/rpc/rpc-engine-api/src/error.rs | 49 +------- 8 files changed, 283 insertions(+), 46 deletions(-) create mode 100644 crates/primitives/res/genesis/goerli_op.json diff --git a/crates/primitives/res/genesis/goerli_op.json b/crates/primitives/res/genesis/goerli_op.json new file mode 100644 index 00000000000..0ea4638d942 --- /dev/null +++ b/crates/primitives/res/genesis/goerli_op.json @@ -0,0 +1,92 @@ +{ + "nonce": "0x00", + "timestamp": "0x00", + "extraData": "0x000000000000000000000000000000000000000000000000000000000000000027770a9694e4B4b1E130Ab91Bc327C36855f612E0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x01", + "gasLimit": "15000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x00", + "number": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "stateRoot": "0x9e6b478a1cd331a979c39e4bddf42c676bcf5a63382f898dc441fe3fe5eb0837", + "alloc": { + "0x4200000000000000000000000000000000000000": { + "balance": "00", + "storage": {}, + "code": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806382e3702d1461003b578063cafa81dc14610072575b600080fd5b61005e610049366004610112565b60006020819052908152604090205460ff1681565b604051901515815260200160405180910390f35b61008561008036600461015a565b610087565b005b6001600080833360405160200161009f929190610229565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291815281516020928301208352908201929092520160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905550565b60006020828403121561012457600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561016c57600080fd5b813567ffffffffffffffff8082111561018457600080fd5b818401915084601f83011261019857600080fd5b8135818111156101aa576101aa61012b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156101f0576101f061012b565b8160405282815287602084870101111561020957600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000835160005b8181101561024a5760208187018101518583015201610230565b81811115610259576000828501525b5060609390931b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016919092019081526014019291505056fea26469706673582212209ffc0b44ce8a27c46cae74a3b3b620a72f10aaea97ed37c15b5d36792abd2aa464736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000002": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "code": "0x608060405234801561001057600080fd5b50600436106100725760003560e01c80639b19251a116100505780639b19251a146100e9578063b1540a011461011c578063bdc7b54f1461012f57600080fd5b806308fd63221461007757806313af40351461008c5780638da5cb5b1461009f575b600080fd5b61008a610085366004610614565b610137565b005b61008a61009a366004610650565b610271565b6000546100bf9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b61010c6100f7366004610650565b60016020526000908152604090205460ff1681565b60405190151581526020016100e0565b61010c61012a366004610650565b61047c565b61008a6104cd565b60005473ffffffffffffffffffffffffffffffffffffffff1633146101e3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f46756e6374696f6e2063616e206f6e6c792062652063616c6c6564206279207460448201527f6865206f776e6572206f66207468697320636f6e74726163742e00000000000060648201526084015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660008181526001602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558251938452908301527f8daaf060c3306c38e068a75c054bf96ecd85a3db1252712c4d93632744c42e0d910160405180910390a15050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610318576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f46756e6374696f6e2063616e206f6e6c792062652063616c6c6564206279207460448201527f6865206f776e6572206f66207468697320636f6e74726163742e00000000000060648201526084016101da565b73ffffffffffffffffffffffffffffffffffffffff81166103e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f4f564d5f4465706c6f79657257686974656c6973743a2063616e206f6e6c792060448201527f62652064697361626c65642076696120656e61626c654172626974726172794360648201527f6f6e74726163744465706c6f796d656e74000000000000000000000000000000608482015260a4016101da565b6000546040805173ffffffffffffffffffffffffffffffffffffffff928316815291831660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a1600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000805473ffffffffffffffffffffffffffffffffffffffff1615806104c7575073ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604090205460ff165b92915050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610574576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f46756e6374696f6e2063616e206f6e6c792062652063616c6c6564206279207460448201527f6865206f776e6572206f66207468697320636f6e74726163742e00000000000060648201526084016101da565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681527fc0e106cf568e50698fdbde1eff56f5a5c966cc7958e37e276918e9e4ccdf8cd49060200160405180910390a1600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b803573ffffffffffffffffffffffffffffffffffffffff8116811461060f57600080fd5b919050565b6000806040838503121561062757600080fd5b610630836105eb565b91506020830135801515811461064557600080fd5b809150509250929050565b60006020828403121561066257600080fd5b61066b826105eb565b939250505056fea264697066735822122045a02b3906eca00a51b37c2965ab13be381f71f60af681951849865fb2daa75f64736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000007": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x000000000000000000000000000000000000000000000000000000000000dead", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000005086d1eef304eb5284a0f6720f79403b4e9be294", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x00000000000000000000000000000000000000000000000000000000000186a0" + }, + "code": "0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063a71198691161005b578063a71198691461012a578063b1b1b2091461014a578063cbd4ece91461016d578063ecc704281461018057600080fd5b806321d800ec1461008d5780633dbb202b146100c55780636e296e45146100da57806382e3702d14610107575b600080fd5b6100b061009b366004610826565b60006020819052908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6100d86100d3366004610942565b610197565b005b6100e26102e2565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bc565b6100b0610115366004610826565b60026020526000908152604090205460ff1681565b6005546100e29073ffffffffffffffffffffffffffffffffffffffff1681565b6100b0610158366004610826565b60016020526000908152604090205460ff1681565b6100d861017b3660046109ad565b61038b565b61018960035481565b6040519081526020016100bc565b60006101a784338560035461078d565b80516020808301919091206000908152600290915260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517fcafa81dc0000000000000000000000000000000000000000000000000000000081529091507342000000000000000000000000000000000000009063cafa81dc9061023c908490600401610a89565b600060405180830381600087803b15801561025657600080fd5b505af115801561026a573d6000803e3d6000fd5b505050508373ffffffffffffffffffffffffffffffffffffffff167fcb0f7ffd78f9aee47a248fae8db181db6eee833039123e026dcbff529522e52a3385600354866040516102bc9493929190610aa3565b60405180910390a26001600360008282546102d79190610aef565b909155505050505050565b60045460009073ffffffffffffffffffffffffffffffffffffffff1661dead141561036e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f78446f6d61696e4d65737361676553656e646572206973206e6f74207365740060448201526064015b60405180910390fd5b5060045473ffffffffffffffffffffffffffffffffffffffff1690565b60055473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffeeeeffffffffffffffffffffffffffffffffeeef330173ffffffffffffffffffffffffffffffffffffffff161461046a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f50726f7669646564206d65737361676520636f756c64206e6f7420626520766560448201527f7269666965642e000000000000000000000000000000000000000000000000006064820152608401610365565b60006104788585858561078d565b8051602080830191909120600081815260019092526040909120549192509060ff1615610527576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f50726f7669646564206d6573736167652068617320616c72656164792062656560448201527f6e2072656365697665642e0000000000000000000000000000000000000000006064820152608401610365565b73ffffffffffffffffffffffffffffffffffffffff8616734200000000000000000000000000000000000000141561059957600090815260016020819052604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909117905550610787565b600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff878116919091179091556040516000918816906105f2908790610b2e565b6000604051808303816000865af19150503d806000811461062f576040519150601f19603f3d011682016040523d82523d6000602084013e610634565b606091505b5050600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead1790559050801515600114156106d557600082815260016020819052604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169092179091555183917f4641df4a962071e12719d8c8c8e5ac7fc4d97b927346a3d7a335b1f7517e133c91a2610701565b60405182907f99d0e048484baa1b1540b1367cb128acd7ab2946d1ed91ec10e3c85e4bf51b8f90600090a25b600083334360405160200161071893929190610b4a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181528151602092830120600090815291829052902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055505050505b50505050565b6060848484846040516024016107a69493929190610b9c565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fcbd4ece9000000000000000000000000000000000000000000000000000000001790529050949350505050565b60006020828403121561083857600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461086357600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126108a857600080fd5b813567ffffffffffffffff808211156108c3576108c3610868565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561090957610909610868565b8160405283815286602085880101111561092257600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561095757600080fd5b6109608461083f565b9250602084013567ffffffffffffffff81111561097c57600080fd5b61098886828701610897565b925050604084013563ffffffff811681146109a257600080fd5b809150509250925092565b600080600080608085870312156109c357600080fd5b6109cc8561083f565b93506109da6020860161083f565b9250604085013567ffffffffffffffff8111156109f657600080fd5b610a0287828801610897565b949793965093946060013593505050565b60005b83811015610a2e578181015183820152602001610a16565b838111156107875750506000910152565b60008151808452610a57816020860160208601610a13565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610a9c6020830184610a3f565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff85168152608060208201526000610ad26080830186610a3f565b905083604083015263ffffffff8316606083015295945050505050565b60008219821115610b29577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500190565b60008251610b40818460208701610a13565b9190910192915050565b60008451610b5c818460208901610a13565b60609490941b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001691909301908152601481019190915260340192915050565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525060806040830152610bd56080830185610a3f565b90508260608301529594505050505056fea26469706673582212200afb9537c52292543adecf37773b8e4d795c984adf4e80970731e434679b4ec264736f6c63430008090033" + }, + "0x420000000000000000000000000000000000000F": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000a693b8f8207ff043f6bbc2e2120bbe4c2251efe9", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000abe", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x000000000000000000000000000000000000000000000000000000000016e360", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000006" + }, + "code": "0x608060405234801561001057600080fd5b50600436106100f55760003560e01c80638c8885c811610097578063de26c4a111610066578063de26c4a1146101cc578063f2fde38b146101df578063f45e65d8146101f2578063fe173b97146101fb57600080fd5b80638c8885c81461016b5780638da5cb5b1461017e578063bede39b5146101a6578063bf1fe420146101b957600080fd5b806349948e0e116100d357806349948e0e14610134578063519b4bd3146101475780637046559714610150578063715018a61461016357600080fd5b80630c18c162146100fa578063313ce567146101165780633577afc51461011f575b600080fd5b61010360035481565b6040519081526020015b60405180910390f35b61010360055481565b61013261012d3660046108d0565b610204565b005b610103610142366004610918565b6102c6565b61010360025481565b61013261015e3660046108d0565b610322565b6101326103d8565b6101326101793660046108d0565b610465565b60005460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161010d565b6101326101b43660046108d0565b61051b565b6101326101c73660046108d0565b6105d1565b6101036101da366004610918565b610687565b6101326101ed3660046109e7565b61072b565b61010360045481565b61010360015481565b60005473ffffffffffffffffffffffffffffffffffffffff16331461028a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b60038190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6000806102d283610687565b90506000600254826102e49190610a53565b90506000600554600a6102f79190610bb2565b90506000600454836103099190610a53565b905060006103178383610bbe565b979650505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146103a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610281565b60048190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a906020016102bb565b60005473ffffffffffffffffffffffffffffffffffffffff163314610459576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610281565b610463600061085b565b565b60005473ffffffffffffffffffffffffffffffffffffffff1633146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610281565b60058190556040518181527fd68112a8707e326d08be3656b528c1bcc5bbbfc47f4177e2179b14d8640838c1906020016102bb565b60005473ffffffffffffffffffffffffffffffffffffffff16331461059c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610281565b60028190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c44906020016102bb565b60005473ffffffffffffffffffffffffffffffffffffffff163314610652576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610281565b60018190556040518181527ffcdccc6074c6c42e4bd578aa9870c697dc976a270968452d2b8c8dc369fae396906020016102bb565b600080805b8351811015610704578381815181106106a7576106a7610bf9565b01602001517fff00000000000000000000000000000000000000000000000000000000000000166106e4576106dd600483610c28565b91506106f2565b6106ef601083610c28565b91505b806106fc81610c40565b91505061068c565b506000600354826107159190610c28565b905061072381610440610c28565b949350505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146107ac576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610281565b73ffffffffffffffffffffffffffffffffffffffff811661084f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610281565b6108588161085b565b50565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156108e257600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561092a57600080fd5b813567ffffffffffffffff8082111561094257600080fd5b818401915084601f83011261095657600080fd5b813581811115610968576109686108e9565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156109ae576109ae6108e9565b816040528281528760208487010111156109c757600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000602082840312156109f957600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610a1d57600080fd5b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615610a8b57610a8b610a24565b500290565b600181815b80851115610ae957817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115610acf57610acf610a24565b80851615610adc57918102915b93841c9390800290610a95565b509250929050565b600082610b0057506001610bac565b81610b0d57506000610bac565b8160018114610b235760028114610b2d57610b49565b6001915050610bac565b60ff841115610b3e57610b3e610a24565b50506001821b610bac565b5060208310610133831016604e8410600b8410161715610b6c575081810a610bac565b610b768383610a90565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115610ba857610ba8610a24565b0290505b92915050565b6000610a1d8383610af1565b600082610bf4577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008219821115610c3b57610c3b610a24565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415610c7257610c72610a24565b506001019056fea2646970667358221220b949ef5f9defd6c0aab6259672d00d239cb8854c9972ba1866af1c6ec6433d4c64736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000010": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000636af16bf2f682dd3109e60102b8e1a089fedaa8", + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000004200000000000000000000000000000000000007" + }, + "code": "0x608060405234801561001057600080fd5b50600436106100675760003560e01c80633cb747bf116100505780633cb747bf146100ca578063662a633a146100ea578063a3a79548146100fd57600080fd5b806332b7006d1461006c57806336c717c114610081575b600080fd5b61007f61007a366004610d0f565b610110565b005b6001546100a19073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b6000546100a19073ffffffffffffffffffffffffffffffffffffffff1681565b61007f6100f8366004610d80565b610126565b61007f61010b366004610e18565b6106c1565b61011f853333878787876106d8565b5050505050565b60015473ffffffffffffffffffffffffffffffffffffffff1661015e60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461021d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4f564d5f58434841494e3a206d657373656e67657220636f6e7472616374207560448201527f6e61757468656e7469636174656400000000000000000000000000000000000060648201526084015b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1661025360005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff16636e296e456040518163ffffffff1660e01b815260040160206040518083038186803b15801561029857600080fd5b505afa1580156102ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102d09190610e9b565b73ffffffffffffffffffffffffffffffffffffffff1614610373576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f4f564d5f58434841494e3a2077726f6e672073656e646572206f662063726f7360448201527f732d646f6d61696e206d657373616765000000000000000000000000000000006064820152608401610214565b61039d877f1d1d8b6300000000000000000000000000000000000000000000000000000000610a32565b801561045357508673ffffffffffffffffffffffffffffffffffffffff1663c01e1bd66040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156103ec57600080fd5b505af1158015610400573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104249190610e9b565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16145b15610567576040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152602482018690528816906340c10f1990604401600060405180830381600087803b1580156104c857600080fd5b505af11580156104dc573d6000803e3d6000fd5b505050508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff167fb0444523268717a02698be47d0803aa7468c00acbed2f8bd93a0459cde61dd898888888860405161055a9493929190610f08565b60405180910390a46106b7565b600063a9f9e67560e01b8989888a89898960405160240161058e9796959493929190610f3e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526001549091506106339073ffffffffffffffffffffffffffffffffffffffff16600083610a57565b8673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff167f7ea89a4591614515571c2b51f5ea06494056f261c10ab1ed8c03c7590d87bce0898989896040516106ad9493929190610f08565b60405180910390a4505b5050505050505050565b6106d0863387878787876106d8565b505050505050565b6040517f9dc29fac0000000000000000000000000000000000000000000000000000000081523360048201526024810185905273ffffffffffffffffffffffffffffffffffffffff881690639dc29fac90604401600060405180830381600087803b15801561074657600080fd5b505af115801561075a573d6000803e3d6000fd5b5050505060008773ffffffffffffffffffffffffffffffffffffffff1663c01e1bd66040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156107a857600080fd5b505af11580156107bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e09190610e9b565b9050606073ffffffffffffffffffffffffffffffffffffffff891673deaddeaddeaddeaddeaddeaddeaddeaddead000014156108d5576040517f1532ec340000000000000000000000000000000000000000000000000000000090610851908a908a908a9089908990602401610f9b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529050610994565b6040517fa9f9e67500000000000000000000000000000000000000000000000000000000906109149084908c908c908c908c908b908b90602401610f3e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290505b6001546109b89073ffffffffffffffffffffffffffffffffffffffff168683610a57565b3373ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f73d170910aba9e6d50b102db522b1dbcd796216f5128b445aa2135272886497e8a8a89896040516106ad9493929190610f08565b6000610a3d83610ae8565b8015610a4e5750610a4e8383610b4c565b90505b92915050565b6000546040517f3dbb202b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690633dbb202b90610ab190869085908790600401611016565b600060405180830381600087803b158015610acb57600080fd5b505af1158015610adf573d6000803e3d6000fd5b50505050505050565b6000610b14827f01ffc9a700000000000000000000000000000000000000000000000000000000610b4c565b8015610a515750610b45827fffffffff00000000000000000000000000000000000000000000000000000000610b4c565b1592915050565b604080517fffffffff00000000000000000000000000000000000000000000000000000000831660248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a7000000000000000000000000000000000000000000000000000000001790529051600091908290819073ffffffffffffffffffffffffffffffffffffffff87169061753090610c06908690611092565b6000604051808303818686fa925050503d8060008114610c42576040519150601f19603f3d011682016040523d82523d6000602084013e610c47565b606091505b5091509150602081511015610c625760009350505050610a51565b818015610c7e575080806020019051810190610c7e91906110ae565b9695505050505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610caa57600080fd5b50565b803563ffffffff81168114610cc157600080fd5b919050565b60008083601f840112610cd857600080fd5b50813567ffffffffffffffff811115610cf057600080fd5b602083019150836020828501011115610d0857600080fd5b9250929050565b600080600080600060808688031215610d2757600080fd5b8535610d3281610c88565b945060208601359350610d4760408701610cad565b9250606086013567ffffffffffffffff811115610d6357600080fd5b610d6f88828901610cc6565b969995985093965092949392505050565b600080600080600080600060c0888a031215610d9b57600080fd5b8735610da681610c88565b96506020880135610db681610c88565b95506040880135610dc681610c88565b94506060880135610dd681610c88565b93506080880135925060a088013567ffffffffffffffff811115610df957600080fd5b610e058a828b01610cc6565b989b979a50959850939692959293505050565b60008060008060008060a08789031215610e3157600080fd5b8635610e3c81610c88565b95506020870135610e4c81610c88565b945060408701359350610e6160608801610cad565b9250608087013567ffffffffffffffff811115610e7d57600080fd5b610e8989828a01610cc6565b979a9699509497509295939492505050565b600060208284031215610ead57600080fd5b8151610eb881610c88565b9392505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201526000610c7e606083018486610ebf565b600073ffffffffffffffffffffffffffffffffffffffff808a1683528089166020840152808816604084015280871660608401525084608083015260c060a0830152610f8e60c083018486610ebf565b9998505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152610fdb608083018486610ebf565b979650505050505050565b60005b83811015611001578181015183820152602001610fe9565b83811115611010576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff841681526060602082015260008351806060840152611051816080850160208801610fe6565b63ffffffff93909316604083015250601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160160800192915050565b600082516110a4818460208701610fe6565b9190910192915050565b6000602082840312156110c057600080fd5b81518015158114610eb857600080fdfea2646970667358221220df892c82e9ba0fc965240aa38614ff1bd6af4d227ce2c0e9933d7f50711a886264736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000011": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000fd1d2e729ae8eee2e146c033bf4400fe75284301" + }, + "code": "0x6080604052600436106100385760003560e01c80633ccfd60b14610044578063d3e5792b1461005b578063d4ff92181461008a57600080fd5b3661003f57005b600080fd5b34801561005057600080fd5b506100596100dc565b005b34801561006757600080fd5b5061007767d02ab486cedc000081565b6040519081526020015b60405180910390f35b34801561009657600080fd5b506000546100b79073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610081565b67d02ab486cedc000047101561019e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605760248201527f4f564d5f53657175656e6365724665655661756c743a2077697468647261776160448201527f6c20616d6f756e74206d7573742062652067726561746572207468616e206d6960648201527f6e696d756d207769746864726177616c20616d6f756e74000000000000000000608482015260a40160405180910390fd5b600080546040805160208101825283815290517fa3a795480000000000000000000000000000000000000000000000000000000081527342000000000000000000000000000000000000109363a3a79548936102309373deaddeaddeaddeaddeaddeaddeaddeaddead00009373ffffffffffffffffffffffffffffffffffffffff909216924792909190600401610264565b600060405180830381600087803b15801561024a57600080fd5b505af115801561025e573d6000803e3d6000fd5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835260208188168185015286604085015263ffffffff8616606085015260a06080850152845191508160a085015260005b828110156102cb5785810182015185820160c0015281016102af565b828111156102dd57600060c084870101525b5050601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160c001969550505050505056fea2646970667358221220387a6116dde263ea48767352a397053c8cffa776aecb43cded2f25a4a9cfbdc264736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000012": { + "balance": "00", + "storage": {}, + "code": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063896f93d114610030575b600080fd5b61004361003e36600461025f565b610045565b005b73ffffffffffffffffffffffffffffffffffffffff83166100c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d7573742070726f76696465204c3120746f6b656e2061646472657373000000604482015260640160405180910390fd5b60007342000000000000000000000000000000000000108484846040516100ec90610178565b6100f99493929190610359565b604051809103906000f080158015610115573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fceeb8e7d520d7f3b65fc11a262b91066940193b05d4f93df07cfdced0eb551cf60405160405180910390a350505050565b6113d7806103b083390190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126101c557600080fd5b813567ffffffffffffffff808211156101e0576101e0610185565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561022657610226610185565b8160405283815286602085880101111561023f57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561027457600080fd5b833573ffffffffffffffffffffffffffffffffffffffff8116811461029857600080fd5b9250602084013567ffffffffffffffff808211156102b557600080fd5b6102c1878388016101b4565b935060408601359150808211156102d757600080fd5b506102e4868287016101b4565b9150509250925092565b6000815180845260005b81811015610314576020818501810151868301820152016102f8565b81811115610326576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600073ffffffffffffffffffffffffffffffffffffffff80871683528086166020840152506080604083015261039260808301856102ee565b82810360608401526103a481856102ee565b97965050505050505056fe60806040523480156200001157600080fd5b50604051620013d7380380620013d783398101604081905262000034916200022f565b8151829082906200004d9060039060208501906200009f565b508051620000639060049060208401906200009f565b5050600580546001600160a01b039586166001600160a01b031991821617909155600680549690951695169490941790925550620002fc915050565b828054620000ad90620002bf565b90600052602060002090601f016020900481019282620000d157600085556200011c565b82601f10620000ec57805160ff19168380011785556200011c565b828001600101855582156200011c579182015b828111156200011c578251825591602001919060010190620000ff565b506200012a9291506200012e565b5090565b5b808211156200012a57600081556001016200012f565b80516001600160a01b03811681146200015d57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200018a57600080fd5b81516001600160401b0380821115620001a757620001a762000162565b604051601f8301601f19908116603f01168101908282118183101715620001d257620001d262000162565b81604052838152602092508683858801011115620001ef57600080fd5b600091505b83821015620002135785820183015181830184015290820190620001f4565b83821115620002255760008385830101525b9695505050505050565b600080600080608085870312156200024657600080fd5b620002518562000145565b9350620002616020860162000145565b60408601519093506001600160401b03808211156200027f57600080fd5b6200028d8883890162000178565b93506060870151915080821115620002a457600080fd5b50620002b38782880162000178565b91505092959194509250565b600181811c90821680620002d457607f821691505b60208210811415620002f657634e487b7160e01b600052602260045260246000fd5b50919050565b6110cb806200030c6000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c806370a0823111610097578063a9059cbb11610066578063a9059cbb14610215578063ae1f6aaf14610228578063c01e1bd61461026d578063dd62ed3e1461028d57600080fd5b806370a08231146101b157806395d89b41146101e75780639dc29fac146101ef578063a457c2d71461020257600080fd5b806323b872dd116100d357806323b872dd14610167578063313ce5671461017a578063395093511461018957806340c10f191461019c57600080fd5b806301ffc9a71461010557806306fdde031461012d578063095ea7b31461014257806318160ddd14610155575b600080fd5b610118610113366004610e4a565b6102d3565b60405190151581526020015b60405180910390f35b610135610393565b6040516101249190610e93565b610118610150366004610f2f565b610425565b6002545b604051908152602001610124565b610118610175366004610f59565b61043b565b60405160128152602001610124565b610118610197366004610f2f565b61050c565b6101af6101aa366004610f2f565b610555565b005b6101596101bf366004610f95565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b61013561061a565b6101af6101fd366004610f2f565b610629565b610118610210366004610f2f565b6106e2565b610118610223366004610f2f565b6107a0565b6006546102489073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610124565b6005546102489073ffffffffffffffffffffffffffffffffffffffff1681565b61015961029b366004610fb0565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b60007f01ffc9a7a5cef8baa21ed3c5c0d7e23accb804b619e9333b597f47a0d84076e27f1d1d8b63000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000084167f01ffc9a700000000000000000000000000000000000000000000000000000000148061038b57507fffffffff00000000000000000000000000000000000000000000000000000000848116908216145b949350505050565b6060600380546103a290610fe3565b80601f01602080910402602001604051908101604052809291908181526020018280546103ce90610fe3565b801561041b5780601f106103f05761010080835404028352916020019161041b565b820191906000526020600020905b8154815290600101906020018083116103fe57829003601f168201915b5050505050905090565b60006104323384846107ad565b50600192915050565b600061044884848461092d565b73ffffffffffffffffffffffffffffffffffffffff84166000908152600160209081526040808320338452909152902054828110156104f45760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206160448201527f6c6c6f77616e636500000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b61050185338584036107ad565b506001949350505050565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091610432918590610550908690611066565b6107ad565b60065473ffffffffffffffffffffffffffffffffffffffff1633146105bc5760405162461bcd60e51b815260206004820181905260248201527f4f6e6c79204c32204272696467652063616e206d696e7420616e64206275726e60448201526064016104eb565b6105c68282610b93565b8173ffffffffffffffffffffffffffffffffffffffff167f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968858260405161060e91815260200190565b60405180910390a25050565b6060600480546103a290610fe3565b60065473ffffffffffffffffffffffffffffffffffffffff1633146106905760405162461bcd60e51b815260206004820181905260248201527f4f6e6c79204c32204272696467652063616e206d696e7420616e64206275726e60448201526064016104eb565b61069a8282610c99565b8173ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca58260405161060e91815260200190565b33600090815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff86168452909152812054828110156107895760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084016104eb565b61079633858584036107ad565b5060019392505050565b600061043233848461092d565b73ffffffffffffffffffffffffffffffffffffffff83166108355760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff82166108be5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f737300000000000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff83166109b65760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f647265737300000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff8216610a3f5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f657373000000000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015610adb5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e6365000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff808516600090815260208190526040808220858503905591851681529081208054849290610b1f908490611066565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610b8591815260200190565b60405180910390a350505050565b73ffffffffffffffffffffffffffffffffffffffff8216610bf65760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016104eb565b8060026000828254610c089190611066565b909155505073ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604081208054839290610c42908490611066565b909155505060405181815273ffffffffffffffffffffffffffffffffffffffff8316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff8216610d225760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f730000000000000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090205481811015610dbe5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f636500000000000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff83166000908152602081905260408120838303905560028054849290610dfa90849061107e565b909155505060405182815260009073ffffffffffffffffffffffffffffffffffffffff8516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610920565b600060208284031215610e5c57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610e8c57600080fd5b9392505050565b600060208083528351808285015260005b81811015610ec057858101830151858201604001528201610ea4565b81811115610ed2576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610f2a57600080fd5b919050565b60008060408385031215610f4257600080fd5b610f4b83610f06565b946020939093013593505050565b600080600060608486031215610f6e57600080fd5b610f7784610f06565b9250610f8560208501610f06565b9150604084013590509250925092565b600060208284031215610fa757600080fd5b610e8c82610f06565b60008060408385031215610fc357600080fd5b610fcc83610f06565b9150610fda60208401610f06565b90509250929050565b600181811c90821680610ff757607f821691505b60208210811415611031577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000821982111561107957611079611037565b500190565b60008282101561109057611090611037565b50039056fea264697066735822122069a2d33039157f3f2f180571262ca2a5d0a3a24d33bf9448f3b7c2ce9ff757f964736f6c63430008090033a2646970667358221220d2e13f28319115807ec7308d1cd88642a8542d0b838e00b8769f8a85d696f26764736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000013": { + "balance": "00", + "storage": {}, + "code": "0x4B60005260206000F3" + }, + "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000004200000000000000000000000000000000000010", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x457468657200000000000000000000000000000000000000000000000000000a", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x4554480000000000000000000000000000000000000000000000000000000006" + }, + "code": "0x608060405234801561001057600080fd5b50600436106101005760003560e01c806370a0823111610097578063a9059cbb11610066578063a9059cbb14610215578063ae1f6aaf14610228578063c01e1bd61461026d578063dd62ed3e1461028d57600080fd5b806370a08231146101b157806395d89b41146101e75780639dc29fac146101ef578063a457c2d71461020257600080fd5b806323b872dd116100d357806323b872dd14610167578063313ce5671461017a578063395093511461018957806340c10f191461019c57600080fd5b806301ffc9a71461010557806306fdde031461012d578063095ea7b31461014257806318160ddd14610155575b600080fd5b610118610113366004610c6d565b6102d3565b60405190151581526020015b60405180910390f35b610135610393565b6040516101249190610cb6565b610118610150366004610d52565b610425565b6002545b604051908152602001610124565b610118610175366004610d7c565b6104db565b60405160128152602001610124565b610118610197366004610d52565b61058c565b6101af6101aa366004610d52565b61063d565b005b6101596101bf366004610db8565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b61013561071c565b6101af6101fd366004610d52565b61072b565b610118610210366004610d52565b6107fe565b610118610223366004610d52565b6108af565b6006546102489073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610124565b6005546102489073ffffffffffffffffffffffffffffffffffffffff1681565b61015961029b366004610dd3565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b60007f01ffc9a7a5cef8baa21ed3c5c0d7e23accb804b619e9333b597f47a0d84076e27f1d1d8b63000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000084167f01ffc9a700000000000000000000000000000000000000000000000000000000148061038b57507fffffffff00000000000000000000000000000000000000000000000000000000848116908216145b949350505050565b6060600380546103a290610e06565b80601f01602080910402602001604051908101604052809291908181526020018280546103ce90610e06565b801561041b5780601f106103f05761010080835404028352916020019161041b565b820191906000526020600020905b8154815290600101906020018083116103fe57829003601f168201915b5050505050905090565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604260248201527f4f564d5f4554483a20617070726f76652069732064697361626c65642070656e60448201527f64696e67206675727468657220636f6d6d756e6974792064697363757373696f60648201527f6e2e000000000000000000000000000000000000000000000000000000000000608482015260009060a4015b60405180910390fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604760248201527f4f564d5f4554483a207472616e7366657246726f6d2069732064697361626c6560448201527f642070656e64696e67206675727468657220636f6d6d756e697479206469736360648201527f757373696f6e2e00000000000000000000000000000000000000000000000000608482015260009060a4016104d2565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f4f564d5f4554483a20696e637265617365416c6c6f77616e636520697320646960448201527f7361626c65642070656e64696e67206675727468657220636f6d6d756e69747960648201527f2064697363757373696f6e2e0000000000000000000000000000000000000000608482015260009060a4016104d2565b60065473ffffffffffffffffffffffffffffffffffffffff1633146106be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f6e6c79204c32204272696467652063616e206d696e7420616e64206275726e60448201526064016104d2565b6106c88282610960565b8173ffffffffffffffffffffffffffffffffffffffff167f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968858260405161071091815260200190565b60405180910390a25050565b6060600480546103a290610e06565b60065473ffffffffffffffffffffffffffffffffffffffff1633146107ac576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f6e6c79204c32204272696467652063616e206d696e7420616e64206275726e60448201526064016104d2565b6107b68282610a80565b8173ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca58260405161071091815260200190565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f4f564d5f4554483a206465637265617365416c6c6f77616e636520697320646960448201527f7361626c65642070656e64696e67206675727468657220636f6d6d756e69747960648201527f2064697363757373696f6e2e0000000000000000000000000000000000000000608482015260009060a4016104d2565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4f564d5f4554483a207472616e736665722069732064697361626c656420706560448201527f6e64696e67206675727468657220636f6d6d756e69747920646973637573736960648201527f6f6e2e0000000000000000000000000000000000000000000000000000000000608482015260009060a4016104d2565b73ffffffffffffffffffffffffffffffffffffffff82166109dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016104d2565b80600260008282546109ef9190610e89565b909155505073ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604081208054839290610a29908490610e89565b909155505060405181815273ffffffffffffffffffffffffffffffffffffffff8316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff8216610b23576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f730000000000000000000000000000000000000000000000000000000000000060648201526084016104d2565b73ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090205481811015610bd9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f636500000000000000000000000000000000000000000000000000000000000060648201526084016104d2565b73ffffffffffffffffffffffffffffffffffffffff83166000908152602081905260408120838303905560028054849290610c15908490610ea1565b909155505060405182815260009073ffffffffffffffffffffffffffffffffffffffff8516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b600060208284031215610c7f57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610caf57600080fd5b9392505050565b600060208083528351808285015260005b81811015610ce357858101830151858201604001528201610cc7565b81811115610cf5576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610d4d57600080fd5b919050565b60008060408385031215610d6557600080fd5b610d6e83610d29565b946020939093013593505050565b600080600060608486031215610d9157600080fd5b610d9a84610d29565b9250610da860208501610d29565b9150604084013590509250925092565b600060208284031215610dca57600080fd5b610caf82610d29565b60008060408385031215610de657600080fd5b610def83610d29565b9150610dfd60208401610d29565b90509250929050565b600181811c90821680610e1a57607f821691505b60208210811415610e54577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610e9c57610e9c610e5a565b500190565b600082821015610eb357610eb3610e5a565b50039056fea2646970667358221220b71535a5111461b42945e5d842957b3a5926f7ed07d271872f6da21952b5f8b464736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000006": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x577261707065642045746865720000000000000000000000000000000000001a", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x5745544800000000000000000000000000000000000000000000000000000008", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000012" + }, + "code": "0x6080604052600436106100bc5760003560e01c8063313ce56711610074578063a9059cbb1161004e578063a9059cbb146102cb578063d0e30db0146100bc578063dd62ed3e14610311576100bc565b8063313ce5671461024b57806370a082311461027657806395d89b41146102b6576100bc565b806318160ddd116100a557806318160ddd146101aa57806323b872dd146101d15780632e1a7d4d14610221576100bc565b806306fdde03146100c6578063095ea7b314610150575b6100c4610359565b005b3480156100d257600080fd5b506100db6103a8565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101155781810151838201526020016100fd565b50505050905090810190601f1680156101425780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015c57600080fd5b506101966004803603604081101561017357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610454565b604080519115158252519081900360200190f35b3480156101b657600080fd5b506101bf6104c7565b60408051918252519081900360200190f35b3480156101dd57600080fd5b50610196600480360360608110156101f457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602081013590911690604001356104cb565b34801561022d57600080fd5b506100c46004803603602081101561024457600080fd5b503561066b565b34801561025757600080fd5b50610260610700565b6040805160ff9092168252519081900360200190f35b34801561028257600080fd5b506101bf6004803603602081101561029957600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16610709565b3480156102c257600080fd5b506100db61071b565b3480156102d757600080fd5b50610196600480360360408110156102ee57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610793565b34801561031d57600080fd5b506101bf6004803603604081101561033457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160200135166107a7565b33600081815260036020908152604091829020805434908101909155825190815291517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9281900390910190a2565b6000805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561044c5780601f106104215761010080835404028352916020019161044c565b820191906000526020600020905b81548152906001019060200180831161042f57829003601f168201915b505050505081565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a350600192915050565b4790565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600360205260408120548211156104fd57600080fd5b73ffffffffffffffffffffffffffffffffffffffff84163314801590610573575073ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14155b156105ed5773ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020548211156105b557600080fd5b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020805483900390555b73ffffffffffffffffffffffffffffffffffffffff808516600081815260036020908152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060019392505050565b3360009081526003602052604090205481111561068757600080fd5b33600081815260036020526040808220805485900390555183156108fc0291849190818181858888f193505050501580156106c6573d6000803e3d6000fd5b5060408051828152905133917f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65919081900360200190a250565b60025460ff1681565b60036020526000908152604090205481565b60018054604080516020600284861615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561044c5780601f106104215761010080835404028352916020019161044c565b60006107a03384846104cb565b9392505050565b60046020908152600092835260408084209091529082529020548156fea265627a7a7231582091c18790e0cca5011d2518024840ee00fecc67e11f56fd746f2cf84d5b583e0064736f6c63430005110032" + } + } +} diff --git a/crates/primitives/src/chain/mod.rs b/crates/primitives/src/chain/mod.rs index 425939ce44b..9b1a9da20b0 100644 --- a/crates/primitives/src/chain/mod.rs +++ b/crates/primitives/src/chain/mod.rs @@ -14,6 +14,9 @@ pub use spec::{ ForkTimestamps, GOERLI, MAINNET, SEPOLIA, }; +#[cfg(feature = "optimism")] +pub use spec::OP_GOERLI; + // The chain info module. mod info; pub use info::ChainInfo; @@ -44,6 +47,18 @@ impl Chain { Chain::Named(ethers_core::types::Chain::Sepolia) } + /// Returns the optimism goerli chain. + #[cfg(feature = "optimism")] + pub const fn optimism_goerli() -> Self { + Chain::Named(ethers_core::types::Chain::OptimismGoerli) + } + + /// Returns the optimism mainnet chain. + #[cfg(feature = "optimism")] + pub const fn optimism_mainnet() -> Self { + Chain::Named(ethers_core::types::Chain::Optimism) + } + /// The id of the chain pub fn id(&self) -> u64 { match self { diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 86d1fbbbbee..3d44b28e474 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -106,6 +106,30 @@ pub static SEPOLIA: Lazy = Lazy::new(|| ChainSpec { optimism: None, }); +/// The Optimism Goerli spec +#[cfg(feature = "optimism")] +pub static OP_GOERLI: Lazy = 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(H256(hex!( + "c1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" + ))), + hardforks: BTreeMap::from([ + (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(4061224)), + (Hardfork::ArrowGlacier, ForkCondition::Block(4061224)), + (Hardfork::GrayGlacier, ForkCondition::Block(4061224)), + (Hardfork::Bedrock, ForkCondition::Block(4061224)), + ]), + optimism: Some(OptimismConfig { eip_1559_elasticity: 10, eip_1559_denominator: 50 }), +}); + /// An Ethereum chain specification. /// /// A chain specification describes: @@ -902,9 +926,10 @@ mod tests { ForkCondition, ForkHash, ForkId, Genesis, Hardfork, Head, GOERLI, H256, MAINNET, SEPOLIA, U256, }; - use bytes::BytesMut; - use ethers_core::types as EtherType; - use reth_rlp::Encodable; + + #[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); @@ -1200,6 +1225,25 @@ Post-merge hard forks (timestamp based): ); } + #[cfg(feature = "optimism")] + #[test] + fn optimism_goerli_forkids() { + // TODO + test_fork_ids( + &OP_GOERLI, + &[ + ( + Head { number: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x00, 0x00, 0x00, 0x00]), next: 4061224 }, + ), + ( + Head { number: 4061224, ..Default::default() }, + ForkId { hash: ForkHash([0x00, 0x00, 0x00, 0x00]), 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 diff --git a/crates/primitives/src/constants.rs b/crates/primitives/src/constants.rs index 005d69380ab..d6f3a88a7fd 100644 --- a/crates/primitives/src/constants.rs +++ b/crates/primitives/src/constants.rs @@ -77,6 +77,11 @@ pub const GOERLI_GENESIS: H256 = pub const SEPOLIA_GENESIS: H256 = H256(hex!("25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9")); +/// Optimism goerli genesis hash. +#[cfg(feature = "optimism")] +pub const GOERLI_OP_GENESIS: H256 = + H256(hex!("c1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1")); + /// Keccak256 over empty array. pub const KECCAK_EMPTY: H256 = H256(hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 75861811243..44f5ecc30d1 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -58,6 +58,8 @@ pub use block::{ BlockWithSenders, ForkBlock, SealedBlock, SealedBlockWithSenders, }; pub use bloom::Bloom; +#[cfg(feature = "optimism")] +pub use chain::OP_GOERLI; pub use chain::{ AllGenesisFormats, Chain, ChainInfo, ChainSpec, ChainSpecBuilder, DisplayHardforks, ForkCondition, ForkTimestamps, GOERLI, MAINNET, SEPOLIA, diff --git a/crates/rpc/rpc-engine-api/Cargo.toml b/crates/rpc/rpc-engine-api/Cargo.toml index 219edaca6f4..162c801e19a 100644 --- a/crates/rpc/rpc-engine-api/Cargo.toml +++ b/crates/rpc/rpc-engine-api/Cargo.toml @@ -34,3 +34,6 @@ reth-interfaces = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } reth-payload-builder = { workspace = true, features = ["test-utils"] } assert_matches = "1.5.0" + +[features] +optimism = ["reth-primitives/optimism", "reth-rpc-types/optimism"] diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index 2a0581595f4..a1435b28519 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -242,6 +242,119 @@ where Ok(result) } + /// When the Consensus layer receives a new block via the consensus gossip protocol, + /// the transactions in the block are sent to the execution layer in the form of a + /// `ExecutionPayload`. The Execution layer executes the transactions and validates the + /// state in the block header, then passes validation data back to Consensus layer, that + /// adds the block to the head of its own blockchain and attests to it. The block is then + /// broadcasted over the consensus p2p network in the form of a "Beacon block". + pub fn new_payload(&mut self, payload: ExecutionPayload) -> EngineApiResult { + let block = match self.try_construct_block(payload) { + Ok(b) => b, + Err(err) => { + return Ok(PayloadStatus::from_status(PayloadStatusEnum::InvalidBlockHash { + validation_error: err.to_string(), + })) + } + }; + let block_hash = block.header.hash(); + let parent_hash = block.parent_hash; + + // The block already exists in our database + if self.client.is_known(&block_hash)? { + return Ok(PayloadStatus::new(PayloadStatusEnum::Valid, block_hash)) + } + + let Some(parent) = self.client.block_by_hash(parent_hash)? else { + // TODO: cache block for storing later + return Ok(PayloadStatus::from_status(PayloadStatusEnum::Syncing)) + }; + + let parent_td = if let Some(parent_td) = self.client.header_td(&block.parent_hash)? { + parent_td + } else { + return Ok(PayloadStatus::from_status(PayloadStatusEnum::Invalid { + validation_error: EngineApiError::PayloadPreMerge.to_string(), + })) + }; + + // Short circuit the check by passing parent total difficulty. + if !self.chain_spec.fork(Hardfork::Paris).active_at_ttd(parent_td, U256::ZERO) { + return Ok(PayloadStatus::from_status(PayloadStatusEnum::Invalid { + validation_error: EngineApiError::PayloadPreMerge.to_string(), + })) + } + + if block.timestamp <= parent.timestamp { + return Ok(PayloadStatus::from_status(PayloadStatusEnum::Invalid { + validation_error: EngineApiError::PayloadTimestamp { + invalid: block.timestamp, + latest: parent.timestamp, + } + .to_string(), + })) + } + + let state_provider = self.client.latest()?; + let total_difficulty = parent_td + block.header.difficulty; + match executor::execute_and_verify_receipt( + &block.unseal(), + total_difficulty, + None, + &self.chain_spec, + &mut SubState::new(State::new(&state_provider)), + ) { + Ok(_) => Ok(PayloadStatus::new(PayloadStatusEnum::Valid, block_hash)), + Err(err) => Ok(PayloadStatus::new( + PayloadStatusEnum::Invalid { validation_error: err.to_string() }, + parent_hash, // The parent hash is already in our database hence it is valid + )), + } + } + + /// Called to resolve chain forks and ensure that the Execution layer is working with the latest + /// valid chain. + pub fn fork_choice_updated( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> EngineApiResult { + let ForkchoiceState { head_block_hash, finalized_block_hash, .. } = fork_choice_state; + + if head_block_hash.is_zero() { + return Ok(ForkchoiceUpdated::from_status(PayloadStatusEnum::Invalid { + validation_error: EngineApiError::ForkchoiceEmptyHead.to_string(), + })) + } + + // Block is not known, nothing to do. + if !self.client.is_known(&head_block_hash)? { + return Ok(ForkchoiceUpdated::from_status(PayloadStatusEnum::Syncing)) + } + + // The finalized block hash is not known, we are still syncing + if !finalized_block_hash.is_zero() && !self.client.is_known(&finalized_block_hash)? { + return Ok(ForkchoiceUpdated::from_status(PayloadStatusEnum::Syncing)) + } + + if let Err(error) = self.forkchoice_state_tx.send(fork_choice_state) { + tracing::error!(target: "rpc::engine_api", ?error, "Failed to update forkchoice state"); + } + + if let Some(attr) = payload_attributes { + #[cfg(feature = "optimism")] + if self.chain_spec.optimism.is_some() && attr.gas_limit.is_none() { + return Err(EngineApiError::MissingGasLimitInPayloadAttributes) + } + + // TODO: optionally build the block + } + + let chain_info = self.client.chain_info()?; + Ok(ForkchoiceUpdated::from_status(PayloadStatusEnum::Valid) + .with_latest_valid_hash(chain_info.best_hash)) + } + /// Called to verify network configuration parameters and ensure that Consensus and Execution /// layers are using the latest configuration. pub async fn exchange_transition_configuration( diff --git a/crates/rpc/rpc-engine-api/src/error.rs b/crates/rpc/rpc-engine-api/src/error.rs index 26c29916c30..8e146b07f09 100644 --- a/crates/rpc/rpc-engine-api/src/error.rs +++ b/crates/rpc/rpc-engine-api/src/error.rs @@ -68,47 +68,10 @@ pub enum EngineApiError { ForkChoiceUpdate(#[from] BeaconForkChoiceUpdateError), /// An error occurred while processing a new payload in the beacon consensus engine. #[error(transparent)] - NewPayload(#[from] BeaconOnNewPayloadError), - /// Encountered an internal error. - #[error(transparent)] - Internal(Box), - /// Fetching the payload failed - #[error(transparent)] - GetPayloadError(#[from] PayloadBuilderError), -} - -impl From for jsonrpsee_types::error::ErrorObject<'static> { - fn from(error: EngineApiError) -> Self { - let code = match error { - EngineApiError::InvalidBodiesRange { .. } | - EngineApiError::WithdrawalsNotSupportedInV1 | - EngineApiError::NoWithdrawalsPostShanghai | - EngineApiError::HasWithdrawalsPreShanghai => INVALID_PARAMS_CODE, - EngineApiError::UnknownPayload => UNKNOWN_PAYLOAD_CODE, - EngineApiError::PayloadRequestTooLarge { .. } => REQUEST_TOO_LARGE_CODE, - - // Error responses from the consensus engine - EngineApiError::ForkChoiceUpdate(ref err) => match err { - BeaconForkChoiceUpdateError::ForkchoiceUpdateError(err) => return (*err).into(), - BeaconForkChoiceUpdateError::EngineUnavailable | - BeaconForkChoiceUpdateError::Internal(_) => INTERNAL_ERROR_CODE, - }, - EngineApiError::NewPayload(ref err) => match err { - BeaconOnNewPayloadError::Internal(_) | - BeaconOnNewPayloadError::EngineUnavailable => INTERNAL_ERROR_CODE, - }, - // Any other server error - EngineApiError::TerminalTD { .. } | - EngineApiError::TerminalBlockHash { .. } | - EngineApiError::Internal(_) | - EngineApiError::GetPayloadError(_) => INTERNAL_ERROR_CODE, - }; - jsonrpsee_types::error::ErrorObject::owned(code, error.to_string(), None::<()>) - } -} - -impl From for jsonrpsee_core::error::Error { - fn from(error: EngineApiError) -> Self { - jsonrpsee_core::error::Error::Call(error.into()) - } + Internal(#[from] reth_interfaces::Error), + /// If the optimism feature flag is enabled, the payload attributes must have a present + /// gas limit for the forkchoice updated method. + #[cfg(feature = "optimism")] + #[error("Missing gas limit in payload attributes")] + MissingGasLimitInPayloadAttributes, } From e803e079eb2d27df8c5969027d5e1c457c4ccf44 Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 5 Mar 2023 15:02:33 -0700 Subject: [PATCH 11/43] Signature fix --- crates/primitives/src/transaction/signature.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/primitives/src/transaction/signature.rs b/crates/primitives/src/transaction/signature.rs index 6977d8c2618..f45dfa6806a 100644 --- a/crates/primitives/src/transaction/signature.rs +++ b/crates/primitives/src/transaction/signature.rs @@ -61,6 +61,11 @@ impl Signature { /// Output the `v` of the signature depends on chain_id #[inline] pub fn v(&self, chain_id: Option) -> 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 From e53eb2025247d75a399267c839658dd421d6efc3 Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 19 Mar 2023 16:34:05 -0400 Subject: [PATCH 12/43] TEMP: Expose some CLI items --- bin/reth/src/cli.rs | 6 +----- bin/reth/src/dirs.rs | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/bin/reth/src/cli.rs b/bin/reth/src/cli.rs index c32b1ae17ed..06f4d3a6a2f 100644 --- a/bin/reth/src/cli.rs +++ b/bin/reth/src/cli.rs @@ -91,10 +91,6 @@ struct Cli { #[derive(Debug, Args)] #[command(next_help_heading = "Logging")] pub struct Logs { - /// The flag to enable persistent logs. - #[arg(long = "log.persistent", global = true, conflicts_with = "journald")] - persistent: bool, - /// The path to put log files in. #[arg( long = "log.directory", @@ -116,7 +112,7 @@ pub struct Logs { impl Logs { /// Builds a tracing layer from the current log options. - pub fn layer(&self) -> eyre::Result, Option)>> + pub fn layer(&self) -> (BoxedLayer, Option) where S: Subscriber, for<'a> S: LookupSpan<'a>, diff --git a/bin/reth/src/dirs.rs b/bin/reth/src/dirs.rs index 796377b6a16..ccdd4125750 100644 --- a/bin/reth/src/dirs.rs +++ b/bin/reth/src/dirs.rs @@ -86,7 +86,6 @@ impl XdgPath for LogsDir { /// A small helper trait for unit structs that represent a standard path following the XDG /// path specification. pub trait XdgPath { - /// Resolve the standard path. fn resolve() -> Option; } From 98c173cd36b393a46e33a5fc902cd07983a5893c Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 19 Mar 2023 16:53:10 -0400 Subject: [PATCH 13/43] Resolve conflicts Remove `consensus` crate `Cargo.toml` Reduce diff --- bin/reth/src/dirs.rs | 1 + crates/consensus/Cargo.toml | 24 ------------- crates/consensus/common/Cargo.toml | 3 ++ crates/consensus/common/src/validation.rs | 12 +++---- crates/primitives/src/chain/spec.rs | 10 ++++-- crates/primitives/src/transaction/mod.rs | 2 +- .../primitives/src/transaction/signature.rs | 2 +- crates/primitives/src/transaction/tx_type.rs | 20 ++++++----- crates/revm/Cargo.toml | 11 ++---- crates/revm/revm-primitives/Cargo.toml | 2 +- crates/revm/revm-primitives/src/env.rs | 36 +++++++++---------- crates/rpc/rpc-engine-api/src/engine_api.rs | 4 +-- crates/transaction-pool/Cargo.toml | 2 +- crates/transaction-pool/src/traits.rs | 24 ++++++------- 14 files changed, 68 insertions(+), 85 deletions(-) delete mode 100644 crates/consensus/Cargo.toml diff --git a/bin/reth/src/dirs.rs b/bin/reth/src/dirs.rs index ccdd4125750..796377b6a16 100644 --- a/bin/reth/src/dirs.rs +++ b/bin/reth/src/dirs.rs @@ -86,6 +86,7 @@ impl XdgPath for LogsDir { /// A small helper trait for unit structs that represent a standard path following the XDG /// path specification. pub trait XdgPath { + /// Resolve the standard path. fn resolve() -> Option; } diff --git a/crates/consensus/Cargo.toml b/crates/consensus/Cargo.toml deleted file mode 100644 index bd742d0bed0..00000000000 --- a/crates/consensus/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "reth-consensus" -version = "0.1.0" -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/paradigmxyz/reth" -readme = "README.md" - -[dependencies] -# reth -reth-primitives = { path = "../primitives" } -reth-interfaces = { path = "../interfaces" } -reth-provider = { path = "../storage/provider" } - -# async -tokio = { version = "1", features = ["sync"] } - -[dev-dependencies] -reth-interfaces = { path = "../interfaces", features = ["test-utils"] } -reth-provider = { path = "../storage/provider", features = ["test-utils"] } -assert_matches = "1.5.0" - -[features] -optimism = [] \ No newline at end of file diff --git a/crates/consensus/common/Cargo.toml b/crates/consensus/common/Cargo.toml index 55253ae5ece..e09f4ba3dfc 100644 --- a/crates/consensus/common/Cargo.toml +++ b/crates/consensus/common/Cargo.toml @@ -18,3 +18,6 @@ reth-interfaces = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } assert_matches = "1.5.0" mockall = "0.11.3" + +[features] +optimism = ["reth-primitives/optimism"] diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index d54e33e80d4..adce6c26848 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -10,8 +10,6 @@ use std::{ time::SystemTime, }; -use reth_primitives::constants; - #[cfg(feature = "optimism")] use reth_primitives::TxDeposit; @@ -69,11 +67,6 @@ pub fn validate_transaction_regarding_header( base_fee: Option, ) -> Result<(), ConsensusError> { let chain_id = match transaction { - #[cfg(feature = "optimism")] - Transaction::Deposit(TxDeposit { .. }) => { - // TODO: get the chain id - None - } Transaction::Legacy(TxLegacy { chain_id, .. }) => { // EIP-155: Simple replay attack protection: https://eips.ethereum.org/EIPS/eip-155 if chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(at_block_number) && @@ -109,6 +102,11 @@ pub fn validate_transaction_regarding_header( Some(*chain_id) } + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { .. }) => { + // TODO: I believe the chain id should be None here, but have to confirm. + None + } }; if let Some(chain_id) = chain_id { if chain_id != chain_spec.chain().id() { diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 3d44b28e474..731d5207048 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -126,6 +126,7 @@ pub static OP_GOERLI: Lazy = Lazy::new(|| ChainSpec { (Hardfork::ArrowGlacier, ForkCondition::Block(4061224)), (Hardfork::GrayGlacier, ForkCondition::Block(4061224)), (Hardfork::Bedrock, ForkCondition::Block(4061224)), + (Hardfork::Regolith, ForkCondition::Timestamp(1679079600)), ]), optimism: Some(OptimismConfig { eip_1559_elasticity: 10, eip_1559_denominator: 50 }), }); @@ -599,14 +600,16 @@ impl ChainSpecBuilder { /// Enable Bedrock at genesis #[cfg(feature = "optimism")] pub fn bedrock_activated(mut self) -> Self { + self = self.london_activated(); self.hardforks.insert(Hardfork::Bedrock, ForkCondition::Block(0)); self } - /// Enable Bedrock at genesis + /// Enable Regolith at genesis #[cfg(feature = "optimism")] pub fn regolith_activated(mut self) -> Self { - self.hardforks.insert(Hardfork::Regolith, ForkCondition::Timestamp(0)); + self = self.bedrock_activated(); + self.hardforks.insert(Hardfork::Regolith, ForkCondition::Block(0)); self } @@ -926,6 +929,9 @@ mod tests { ForkCondition, ForkHash, ForkId, Genesis, Hardfork, Head, GOERLI, H256, MAINNET, SEPOLIA, U256, }; + use bytes::BytesMut; + use ethers_core::types as EtherType; + use reth_rlp::Encodable; #[cfg(feature = "optimism")] use crate::OP_GOERLI; diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index e59f2cc8e8a..3ca17085a95 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -233,7 +233,7 @@ pub enum Transaction { Eip2930(TxEip2930), /// A transaction with a priority fee ([EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)). Eip1559(TxEip1559), - /// Deposit transaction. + /// Optimism deposit transaction. #[cfg(feature = "optimism")] Deposit(TxDeposit), } diff --git a/crates/primitives/src/transaction/signature.rs b/crates/primitives/src/transaction/signature.rs index f45dfa6806a..eff0c56b392 100644 --- a/crates/primitives/src/transaction/signature.rs +++ b/crates/primitives/src/transaction/signature.rs @@ -63,7 +63,7 @@ impl Signature { pub fn v(&self, chain_id: Option) -> u64 { #[cfg(feature = "optimism")] if self.r == U256::ZERO && self.s == U256::ZERO { - return 0; + return 0 } if let Some(chain_id) = chain_id { diff --git a/crates/primitives/src/transaction/tx_type.rs b/crates/primitives/src/transaction/tx_type.rs index 39fa1710737..ade0eb66d61 100644 --- a/crates/primitives/src/transaction/tx_type.rs +++ b/crates/primitives/src/transaction/tx_type.rs @@ -12,6 +12,10 @@ pub const EIP2930_TX_TYPE_ID: u8 = 1; /// Identifier for [TxEip1559](crate::TxEip1559) transaction. pub const EIP1559_TX_TYPE_ID: u8 = 2; +/// Identifier for [TxDeposit](crate::TxDeposit) transaction. +#[cfg(feature = "optimism")] +pub(crate) const DEPOSIT_TX_TYPE_ID: u8 = 126; + /// Transaction Type #[derive_arbitrary(compact)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)] @@ -31,11 +35,11 @@ pub enum TxType { impl From for u8 { fn from(value: TxType) -> Self { match value { - TxType::Legacy => 0, - TxType::EIP2930 => 1, - TxType::EIP1559 => 2, + TxType::Legacy => LEGACY_TX_TYPE_ID, + TxType::EIP2930 => EIP2930_TX_TYPE_ID, + TxType::EIP1559 => EIP1559_TX_TYPE_ID, #[cfg(feature = "optimism")] - TxType::DEPOSIT => 126, + TxType::DEPOSIT => DEPOSIT_TX_TYPE_ID, } } } @@ -52,11 +56,11 @@ impl Compact for TxType { B: bytes::BufMut + AsMut<[u8]>, { match self { - TxType::Legacy => 0, - TxType::EIP2930 => 1, - TxType::EIP1559 => 2, + TxType::Legacy => LEGACY_TX_TYPE_ID as usize, + TxType::EIP2930 => EIP2930_TX_TYPE_ID as usize, + TxType::EIP1559 => EIP1559_TX_TYPE_ID as usize, #[cfg(feature = "optimism")] - TxType::DEPOSIT => 126, + TxType::DEPOSIT => DEPOSIT_TX_TYPE_ID as usize, } } diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index e80ab708c0f..0f375476570 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -17,12 +17,7 @@ reth-revm-primitives = { path = "./revm-primitives" } reth-revm-inspectors = { path = "./revm-inspectors" } reth-consensus-common = { path = "../consensus/common" } -# revm -revm = { workspace = true } +revm = { version = "3.0.0" } -# common -tracing = { workspace = true } - -[dev-dependencies] -reth-rlp = { workspace = true } -once_cell = "1.17.0" +[features] +optimism = ["reth-primitives/optimism", "reth-revm-primitives/optimism"] diff --git a/crates/revm/revm-primitives/Cargo.toml b/crates/revm/revm-primitives/Cargo.toml index 8c00dc3670a..c40942e6514 100644 --- a/crates/revm/revm-primitives/Cargo.toml +++ b/crates/revm/revm-primitives/Cargo.toml @@ -13,4 +13,4 @@ reth-primitives = { path = "../../primitives" } revm = { version = "3.0.0" } [features] -optimism = [] +optimism = ["reth-primitives/optimism"] diff --git a/crates/revm/revm-primitives/src/env.rs b/crates/revm/revm-primitives/src/env.rs index 5351914c1e8..e590387a7b3 100644 --- a/crates/revm/revm-primitives/src/env.rs +++ b/crates/revm/revm-primitives/src/env.rs @@ -122,24 +122,6 @@ where { tx_env.caller = sender; match transaction.as_ref() { - #[cfg(feature = "optimism")] - Transaction::Deposit(TxDeposit { to, mint, value, gas_limit, input, .. }) => { - tx_env.gas_limit = *gas_limit; - if let Some(m) = mint { - tx_env.gas_price = U256::from(*m); - } else { - tx_env.gas_price = U256::ZERO; - } - tx_env.gas_priority_fee = None; - match to { - TransactionKind::Call(to) => tx_env.transact_to = TransactTo::Call(*to), - TransactionKind::Create => tx_env.transact_to = TransactTo::create(), - } - tx_env.value = U256::from(*value); - tx_env.data = input.0.clone(); - tx_env.chain_id = None; - tx_env.nonce = None; - } Transaction::Legacy(TxLegacy { nonce, chain_id, @@ -233,5 +215,23 @@ where }) .collect(); } + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { to, mint, value, gas_limit, input, .. }) => { + tx_env.gas_limit = *gas_limit; + if let Some(m) = mint { + tx_env.gas_price = U256::from(*m); + } else { + tx_env.gas_price = U256::ZERO; + } + tx_env.gas_priority_fee = None; + match to { + TransactionKind::Call(to) => tx_env.transact_to = TransactTo::Call(*to), + TransactionKind::Create => tx_env.transact_to = TransactTo::create(), + } + tx_env.value = U256::from(*value); + tx_env.data = input.0.clone(); + tx_env.chain_id = None; + tx_env.nonce = None; + } } } diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index a1435b28519..c4ec8e0f38f 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -341,9 +341,9 @@ where tracing::error!(target: "rpc::engine_api", ?error, "Failed to update forkchoice state"); } - if let Some(attr) = payload_attributes { + if let Some(_attr) = payload_attributes { #[cfg(feature = "optimism")] - if self.chain_spec.optimism.is_some() && attr.gas_limit.is_none() { + if self.chain_spec.optimism.is_some() && _attr.gas_limit.is_none() { return Err(EngineApiError::MissingGasLimitInPayloadAttributes) } diff --git a/crates/transaction-pool/Cargo.toml b/crates/transaction-pool/Cargo.toml index 47f44068627..f506f751474 100644 --- a/crates/transaction-pool/Cargo.toml +++ b/crates/transaction-pool/Cargo.toml @@ -53,4 +53,4 @@ rand = "0.8" default = ["serde"] serde = ["dep:serde"] test-utils = ["rand", "paste", "serde"] -optimism = [] \ No newline at end of file +optimism = ["reth-primitives/optimism"] diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index ed81554c60b..51dfd699fa0 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -582,11 +582,11 @@ impl PoolTransaction for PooledTransaction { /// This is also commonly referred to as the "Gas Fee Cap" (`GasFeeCap`). fn max_fee_per_gas(&self) -> u128 { match &self.transaction.transaction { - #[cfg(feature = "optimism")] - Transaction::Deposit(_) => None, Transaction::Legacy(_) => None, Transaction::Eip2930(_) => None, Transaction::Eip1559(tx) => Some(tx.max_fee_per_gas), + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => None, } } @@ -595,11 +595,11 @@ impl PoolTransaction for PooledTransaction { /// This will return `None` for non-EIP1559 transactions fn max_priority_fee_per_gas(&self) -> Option { match &self.transaction.transaction { - #[cfg(feature = "optimism")] - Transaction::Deposit(_) => None, Transaction::Legacy(_) => None, Transaction::Eip2930(_) => None, Transaction::Eip1559(tx) => Some(tx.max_priority_fee_per_gas), + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => None, } } @@ -633,14 +633,6 @@ impl PoolTransaction for PooledTransaction { impl FromRecoveredTransaction for PooledTransaction { fn from_recovered_transaction(tx: TransactionSignedEcRecovered) -> Self { let (cost, effective_gas_price) = match &tx.transaction { - #[cfg(feature = "optimism")] - Transaction::Deposit(t) => { - // TODO: fix this gas price estimate - let gas_price = U256::from(0); - let cost = U256::from(gas_price) * U256::from(t.gas_limit) + U256::from(t.value); - let effective_gas_price = 0u128; - (cost, effective_gas_price) - } Transaction::Legacy(t) => { let cost = U256::from(t.gas_price) * U256::from(t.gas_limit) + U256::from(t.value); let effective_gas_price = t.gas_price; @@ -657,6 +649,14 @@ impl FromRecoveredTransaction for PooledTransaction { let effective_gas_price = t.max_priority_fee_per_gas; (cost, effective_gas_price) } + #[cfg(feature = "optimism")] + Transaction::Deposit(t) => { + // TODO: fix this gas price estimate + let gas_price = U256::from(0); + let cost = U256::from(gas_price) * U256::from(t.gas_limit) + U256::from(t.value); + let effective_gas_price = 0u128; + (cost, effective_gas_price) + } }; let cost = gas_cost + U256::from(tx.value()); From 1bf2b08c6801bac74d8fc303080b17f93e2ead1e Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 19 Mar 2023 19:40:53 -0400 Subject: [PATCH 14/43] Allow gaslimit to be set by the EngineAPI caller on OP --- crates/consensus/common/src/validation.rs | 35 +++++++++++++---------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index adce6c26848..2cca186e171 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -273,27 +273,32 @@ pub fn validate_header_regarding_parent( // TODO Check difficulty increment between parent and child // Ace age did increment it by some formula that we need to follow. - let mut parent_gas_limit = parent.gas_limit; + // Check gas limit, max diff between child/parent gas_limit should be max_diff=parent_gas/1024 + // On Optimism, the gas limit can adjust instantly, so we skip this check if the optimism + // feature flag is enabled. + #[cfg(not(feature = "optimism"))] + { + let mut parent_gas_limit = parent.gas_limit; - // By consensus, gas_limit is multiplied by elasticity (*2) on - // on exact block that hardfork happens. - if chain_spec.fork(Hardfork::London).transitions_at_block(child.number) { - parent_gas_limit = parent.gas_limit * constants::EIP1559_ELASTICITY_MULTIPLIER; - } + // By consensus, gas_limit is multiplied by elasticity (*2) on + // on exact block that hardfork happens. + if chain_spec.fork(Hardfork::London).transitions_at_block(child.number) { + parent_gas_limit = parent.gas_limit * constants::EIP1559_ELASTICITY_MULTIPLIER; + } - // Check gas limit, max diff between child/parent gas_limit should be max_diff=parent_gas/1024 - if child.gas_limit > parent_gas_limit { - if child.gas_limit - parent_gas_limit >= parent_gas_limit / 1024 { - return Err(ConsensusError::GasLimitInvalidIncrease { + if child.gas_limit > parent_gas_limit { + if child.gas_limit - parent_gas_limit >= parent_gas_limit / 1024 { + return Err(ConsensusError::GasLimitInvalidIncrease { + parent_gas_limit, + child_gas_limit: child.gas_limit, + }) + } + } else if parent_gas_limit - child.gas_limit >= parent_gas_limit / 1024 { + return Err(ConsensusError::GasLimitInvalidDecrease { parent_gas_limit, child_gas_limit: child.gas_limit, }) } - } else if parent_gas_limit - child.gas_limit >= parent_gas_limit / 1024 { - return Err(ConsensusError::GasLimitInvalidDecrease { - parent_gas_limit, - child_gas_limit: child.gas_limit, - }) } // EIP-1559 check base fee From 8105ddd02ad8e37df03f8cb4ca2decf96fd03c42 Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 19 Mar 2023 19:53:10 -0400 Subject: [PATCH 15/43] Add Optimism EIP1559 elasticity multiplier / max base fee change denominator --- crates/primitives/src/constants.rs | 10 ++++++++++ crates/primitives/src/transaction/tx_type.rs | 8 ++++---- crates/revm/revm-primitives/Cargo.toml | 2 ++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/primitives/src/constants.rs b/crates/primitives/src/constants.rs index d6f3a88a7fd..177082ddb6f 100644 --- a/crates/primitives/src/constants.rs +++ b/crates/primitives/src/constants.rs @@ -48,11 +48,21 @@ pub const MIN_PROTOCOL_BASE_FEE_U256: U256 = U256::from_limbs([7u64, 0, 0, 0]); pub const EIP1559_INITIAL_BASE_FEE: u64 = 1_000_000_000; /// Base fee max change denominator as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) +#[cfg(not(feature = "optimism"))] pub const EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8; /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) +#[cfg(not(feature = "optimism"))] pub const EIP1559_ELASTICITY_MULTIPLIER: u64 = 2; +/// Base fee max change denominator for Optimism. +#[cfg(feature = "optimism")] +pub const EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 50; + +/// Elasticity multiplier for Optimism. +#[cfg(feature = "optimism")] +pub const EIP1559_ELASTICITY_MULTIPLIER: u64 = 10; + /// Multiplier for converting gwei to wei. pub const GWEI_TO_WEI: u64 = 1_000_000_000; diff --git a/crates/primitives/src/transaction/tx_type.rs b/crates/primitives/src/transaction/tx_type.rs index ade0eb66d61..e44bfba1dfb 100644 --- a/crates/primitives/src/transaction/tx_type.rs +++ b/crates/primitives/src/transaction/tx_type.rs @@ -14,7 +14,7 @@ pub const EIP1559_TX_TYPE_ID: u8 = 2; /// Identifier for [TxDeposit](crate::TxDeposit) transaction. #[cfg(feature = "optimism")] -pub(crate) const DEPOSIT_TX_TYPE_ID: u8 = 126; +use crate::DEPOSIT_TX_TYPE; /// Transaction Type #[derive_arbitrary(compact)] @@ -29,7 +29,7 @@ pub enum TxType { EIP1559 = 2_isize, /// OP Deposit transaction. #[cfg(feature = "optimism")] - DEPOSIT = 126_isize, + DEPOSIT = DEPOSIT_TX_TYPE as isize, } impl From for u8 { @@ -39,7 +39,7 @@ impl From for u8 { TxType::EIP2930 => EIP2930_TX_TYPE_ID, TxType::EIP1559 => EIP1559_TX_TYPE_ID, #[cfg(feature = "optimism")] - TxType::DEPOSIT => DEPOSIT_TX_TYPE_ID, + TxType::DEPOSIT => DEPOSIT_TX_TYPE, } } } @@ -60,7 +60,7 @@ impl Compact for TxType { TxType::EIP2930 => EIP2930_TX_TYPE_ID as usize, TxType::EIP1559 => EIP1559_TX_TYPE_ID as usize, #[cfg(feature = "optimism")] - TxType::DEPOSIT => DEPOSIT_TX_TYPE_ID as usize, + TxType::DEPOSIT => DEPOSIT_TX_TYPE as usize, } } diff --git a/crates/revm/revm-primitives/Cargo.toml b/crates/revm/revm-primitives/Cargo.toml index c40942e6514..d03fb908510 100644 --- a/crates/revm/revm-primitives/Cargo.toml +++ b/crates/revm/revm-primitives/Cargo.toml @@ -9,7 +9,9 @@ repository.workspace = true description = "core reth specific revm utilities" [dependencies] +# reth reth-primitives = { path = "../../primitives" } + revm = { version = "3.0.0" } [features] From c99732fac689a41b18af618f48afe8745f07310f Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Sun, 11 Jun 2023 14:46:56 +0200 Subject: [PATCH 16/43] feat: executor deposits --- .gitignore | 3 ++ crates/executor/Cargo.toml | 58 ++++++++++++++++++++++++ crates/interfaces/src/executor.rs | 4 ++ crates/primitives/src/transaction/mod.rs | 6 +++ crates/revm/src/executor.rs | 32 ++++++++++++- 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 crates/executor/Cargo.toml diff --git a/.gitignore b/.gitignore index c2c144f4a1c..2cebc21f55e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ target/ # MSVC Windows builds of rustc generate these, which store debugging information *.pdb +# Generated by VSCode +.vscode + # Generated by Intellij-based IDEs. .idea diff --git a/crates/executor/Cargo.toml b/crates/executor/Cargo.toml new file mode 100644 index 00000000000..53acae3de9f --- /dev/null +++ b/crates/executor/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "reth-executor" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/paradigmxyz/reth" +readme = "README.md" + +[package.metadata.cargo-udeps.ignore] +normal = [ + # Used for diagrams in docs + "aquamarine", +] + +[dependencies] +# reth +reth-primitives = { path = "../primitives" } +reth-interfaces = { path = "../interfaces" } +reth-revm = { path = "../revm" } +reth-revm-inspectors = { path = "../revm/revm-inspectors" } +reth-rlp = { path = "../rlp" } +reth-db = { path = "../storage/db" } +reth-provider = { path = "../storage/provider" } + +# revm +revm = { version = "3.0.0" } + +# common +async-trait = "0.1.57" +thiserror = "1.0.37" +auto_impl = "1.0" +tracing = "0.1.37" +tokio = { version = "1.21.2", features = ["sync"] } + +# mics +aquamarine = "0.3.0" +parking_lot = { version = "0.12", optional = true } + +triehash = "0.8" +# See to replace hashers to simplify libraries +plain_hasher = "0.2" +hash-db = "0.15" +# todo replace with faster rlp impl +rlp = { version = "0.5", default-features = false } +# replace with tiny-keccak (it is faster hasher) +sha3 = { version = "0.10", default-features = false } + + +[dev-dependencies] +reth-db = { path = "../storage/db", features = ["test-utils"] } +reth-interfaces = { path = "../interfaces", features = ["test-utils"] } +reth-primitives = { path = "../primitives", features = ["test-utils"] } +reth-provider = { path = "../storage/provider", features = ["test-utils"] } +parking_lot = "0.12" + +[features] +test-utils = ["parking_lot"] +optimism = [] diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index cc747e29fdb..7dc8c0508f3 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -7,6 +7,10 @@ use thiserror::Error; pub enum BlockValidationError { #[error("EVM reported invalid transaction ({hash:?}): {message}")] EVM { hash: H256, message: String }, + #[error("Verification failed")] + VerificationFailed, + #[error("Fatal internal error")] + ExecutionFatalError, #[error("Failed to recover sender for transaction")] SenderRecoveryError, #[error("Receipt root {got:?} is different than expected {expected:?}.")] diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 3ca17085a95..8661b543fed 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -600,6 +600,12 @@ impl Transaction { } } + /// Returns whether or not the transaction is an Optimism Deposited transaction. + #[cfg(feature = "optimism")] + pub fn is_deposit(&self) -> bool { + matches!(self, Transaction::Deposit(_)) + } + /// Encodes EIP-155 arguments into the desired buffer. Only encodes values for legacy /// transactions. pub(crate) fn encode_eip155_fields(&self, out: &mut dyn bytes::BufMut) { diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 5ce714521fb..8efc3d8512b 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -17,7 +17,7 @@ use revm::{ db::{AccountState, CacheDB, DatabaseRef}, primitives::{ hash_map::{self, Entry}, - Account as RevmAccount, AccountInfo, ResultAndState, + Account as RevmAccount, AccountInfo, ExecutionResult, ResultAndState, }, EVM, }; @@ -240,9 +240,39 @@ where } .into()) } + + #[cfg(feature = "optimism")] + if let Some(m) = transaction.mint() { + // Add balance to the caller account equivalent to the minted amount + self.increment_account_balance(sender, U256::from(m), &mut post_state)?; + } + // Execute transaction. let ResultAndState { result, state } = self.transact(transaction, sender)?; + if transaction.is_deposit() && !matches!(result, ExecutionResult::Success { .. }) { + // If the deposit transaction failed, the deposit must still be included. + // In this case, we need to increment the sender nonce and disregard the + // state changes. The tx is invalid so it is also recorded as using all gas. + let mut acc = self.db().load_account(sender).map_err(|_| Error::ProviderError)?; + let old = to_reth_acc(&acc.info); + acc.info.nonce += 1; + let new = to_reth_acc(&acc.info); + + post_state.change_account(sender, old, new); + cumulative_gas_used += transaction.gas_limit(); + + post_state.add_receipt(Receipt { + tx_type: transaction.tx_type(), + success: false, + cumulative_gas_used, + bloom: Bloom::zero(), + logs: vec![], + }); + post_state.finish_transition(); + continue + } + // commit changes self.commit_changes( block.number, From 6266b9f3cd3a31e8a698bf446d291506a235e039 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Sun, 11 Jun 2023 21:43:32 +0200 Subject: [PATCH 17/43] feat: receipts, l1 cost wip --- crates/executor/src/lib.rs | 30 +++++ crates/interfaces/src/executor.rs | 26 +--- crates/net/eth-wire/Cargo.toml | 1 + crates/net/eth-wire/src/types/receipts.rs | 22 +++- crates/primitives/src/receipt.rs | 151 ++++++++++++++++------ crates/revm/src/executor.rs | 37 ++++-- crates/revm/src/optimism.rs | 58 +++++++++ crates/transaction-pool/src/traits.rs | 4 +- 8 files changed, 256 insertions(+), 73 deletions(-) create mode 100644 crates/executor/src/lib.rs create mode 100644 crates/revm/src/optimism.rs diff --git a/crates/executor/src/lib.rs b/crates/executor/src/lib.rs new file mode 100644 index 00000000000..27be2283d23 --- /dev/null +++ b/crates/executor/src/lib.rs @@ -0,0 +1,30 @@ +#![warn(missing_docs, unreachable_pub)] +#![deny(unused_must_use, rust_2018_idioms)] +#![doc(test( + no_crate_inject, + attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) +))] + +//! Reth executor executes transaction in block of data. + +pub mod eth_dao_fork; +pub mod substate; + +/// Execution result types. +pub use reth_provider::post_state; + +pub mod blockchain_tree; + +/// Executor +pub mod executor; + +/// ExecutorFactory impl +pub mod factory; +pub use factory::Factory; + +#[cfg(any(test, feature = "test-utils"))] +/// Common test helpers for mocking out executor and executor factory +pub mod test_utils; + +#[cfg(feature = "optimism")] +pub mod optimism; diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index 7dc8c0508f3..250286db602 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -46,26 +46,8 @@ pub enum BlockExecutionError { CanonicalRevert { inner: String }, #[error("Transaction error on commit: {inner:?}")] CanonicalCommit { inner: String }, - - // === tree errors === - // TODO(mattsse): move this to tree error - #[error("Block hash {block_hash} not found in blockchain tree chain")] - BlockHashNotFoundInChain { block_hash: BlockHash }, - #[error( - "Appending chain on fork (other_chain_fork:?) is not possible as the tip is {chain_tip:?}" - )] - AppendChainDoesntConnect { chain_tip: BlockNumHash, other_chain_fork: BlockNumHash }, - - /// Only used for TestExecutor - /// - /// Note: this is not feature gated for convenience. - #[error("Execution unavailable for tests")] - UnavailableForTest, -} - -impl BlockExecutionError { - /// Returns `true` if the error is fatal. - pub fn is_fatal(&self) -> bool { - matches!(self, Self::CanonicalCommit { .. } | Self::CanonicalRevert { .. }) - } + #[error("Transaction error on pipeline status update: {inner:?}")] + PipelineStatusUpdate { inner: String }, + #[error("DB Error during transaction execution: {inner:?}")] + DBError { inner: String }, } diff --git a/crates/net/eth-wire/Cargo.toml b/crates/net/eth-wire/Cargo.toml index 987f7505d9c..e2b4a2be3b7 100644 --- a/crates/net/eth-wire/Cargo.toml +++ b/crates/net/eth-wire/Cargo.toml @@ -64,6 +64,7 @@ proptest-derive = "0.3" default = ["serde"] serde = ["dep:serde", "smol_str/serde"] arbitrary = ["reth-primitives/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"] +optimism = [] [[test]] name = "fuzz_roundtrip" diff --git a/crates/net/eth-wire/src/types/receipts.rs b/crates/net/eth-wire/src/types/receipts.rs index f1bb3bc1209..39eeae1e316 100644 --- a/crates/net/eth-wire/src/types/receipts.rs +++ b/crates/net/eth-wire/src/types/receipts.rs @@ -51,6 +51,9 @@ mod test { logs: vec![], }, bloom: Default::default(), + logs: vec![], + #[cfg(feature = "optimism")] + deposit_nonce: None, }]]); let mut out = vec![]; @@ -118,8 +121,9 @@ mod test { }, ], success: false, - },bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(), - } + #[cfg(feature = "optimism")] + deposit_nonce: None, + }, ], ]), }; @@ -155,6 +159,20 @@ mod test { success: false, }, bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(), + cumulative_gas_used: 0x1u64, + logs: vec![ + Log { + address: hex!("0000000000000000000000000000000000000011").into(), + topics: vec![ + hex!("000000000000000000000000000000000000000000000000000000000000dead").into(), + hex!("000000000000000000000000000000000000000000000000000000000000beef").into(), + ], + data: hex!("0100ff")[..].into(), + }, + ], + success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, }, ], ]), diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 15a5a2308df..8360ada8faf 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -28,43 +28,42 @@ pub struct Receipt { ) )] pub logs: Vec, + /// Deposit nonce for Optimism deposit transactions + #[cfg(feature = "optimism")] + pub deposit_nonce: Option, } impl Receipt { - /// Calculates [`Log`]'s bloom filter. this is slow operation and [ReceiptWithBloom] can - /// be used to cache this value. - pub fn bloom_slow(&self) -> Bloom { - logs_bloom(self.logs.iter()) - } + /// Returns the rlp header for the receipt payload. + fn receipt_rlp_header(&self) -> reth_rlp::Header { + let mut rlp_head = reth_rlp::Header { list: true, payload_length: 0 }; - /// Calculates the bloom filter for the receipt and returns the [ReceiptWithBloom] container - /// type. - pub fn with_bloom(self) -> ReceiptWithBloom { - self.into() - } -} + rlp_head.payload_length += self.success.length(); + rlp_head.payload_length += self.cumulative_gas_used.length(); + rlp_head.payload_length += self.bloom.length(); + rlp_head.payload_length += self.logs.length(); + #[cfg(feature = "optimism")] + if self.tx_type == TxType::DEPOSIT { + // Transactions pre-Regolith don't have a deposit nonce + if let Some(nonce) = self.deposit_nonce { + rlp_head.payload_length += nonce.length(); + } + } -impl From for ReceiptWithBloom { - fn from(receipt: Receipt) -> Self { - let bloom = receipt.bloom_slow(); - ReceiptWithBloom { receipt, bloom } + rlp_head } -} - -/// [`Receipt`] with calculated bloom filter. -#[main_codec] -#[derive(Clone, Debug, PartialEq, Eq, Default)] -pub struct ReceiptWithBloom { - /// Bloom filter build from logs. - pub bloom: Bloom, - /// Main receipt body - pub receipt: Receipt, -} -impl ReceiptWithBloom { - /// Create new [ReceiptWithBloom] - pub fn new(receipt: Receipt, bloom: Bloom) -> Self { - Self { receipt, bloom } + /// Encodes the receipt data. + fn encode_fields(&self, out: &mut dyn BufMut) { + self.receipt_rlp_header().encode(out); + self.success.encode(out); + self.cumulative_gas_used.encode(out); + self.bloom.encode(out); + self.logs.encode(out); + #[cfg(feature = "optimism")] + if let Some(nonce) = self.deposit_nonce { + nonce.encode(out); + } } /// Consume the structure, returning only the receipt @@ -86,7 +85,33 @@ impl ReceiptWithBloom { impl ReceiptWithBloom { /// Encode receipt with or without the header data. pub fn encode_inner(&self, out: &mut dyn BufMut, with_header: bool) { - self.as_encoder().encode_inner(out, with_header) + if matches!(self.tx_type, TxType::Legacy) { + self.encode_fields(out); + return + } + + let mut payload = BytesMut::new(); + self.encode_fields(&mut payload); + + if with_header { + let payload_length = payload.len() + 1; + let header = reth_rlp::Header { list: false, payload_length }; + header.encode(out); + } + + match self.tx_type { + TxType::EIP2930 => out.put_u8(0x01), + TxType::EIP1559 => out.put_u8(0x02), + TxType::DEPOSIT => out.put_u8(0x7E), + TxType::Legacy => unreachable!("legacy handled; qed."), + } + out.put_slice(payload.as_ref()); + } + + /// Returns the length of the receipt data. + fn receipt_length(&self) -> usize { + let rlp_head = self.receipt_rlp_header(); + length_of_length(rlp_head.payload_length) + rlp_head.payload_length } /// Decodes the receipt payload @@ -98,12 +123,27 @@ impl ReceiptWithBloom { } let started_len = b.len(); - let success = reth_rlp::Decodable::decode(b)?; - let cumulative_gas_used = reth_rlp::Decodable::decode(b)?; - let bloom = Decodable::decode(b)?; - let logs = reth_rlp::Decodable::decode(b)?; + let this = match tx_type { + #[cfg(feature = "optimism")] + TxType::DEPOSIT => Self { + tx_type, + success: reth_rlp::Decodable::decode(b)?, + cumulative_gas_used: reth_rlp::Decodable::decode(b)?, + bloom: reth_rlp::Decodable::decode(b)?, + logs: reth_rlp::Decodable::decode(b)?, + deposit_nonce: Some(reth_rlp::Decodable::decode(b)?), + }, + _ => Self { + tx_type, + success: reth_rlp::Decodable::decode(b)?, + cumulative_gas_used: reth_rlp::Decodable::decode(b)?, + bloom: reth_rlp::Decodable::decode(b)?, + logs: reth_rlp::Decodable::decode(b)?, + #[cfg(feature = "optimism")] + deposit_nonce: None, + }, + }; - let this = Self { receipt: Receipt { tx_type, success, cumulative_gas_used, logs }, bloom }; let consumed = started_len - b.len(); if consumed != rlp_head.payload_length { return Err(reth_rlp::DecodeError::ListLengthMismatch { @@ -147,6 +187,9 @@ impl Decodable for ReceiptWithBloom { } else if receipt_type == 0x02 { buf.advance(1); Self::decode_receipt(buf, TxType::EIP1559) + } else if receipt_type == 0x7E { + buf.advance(1); + Self::decode_receipt(buf, TxType::DEPOSIT) } else { Err(reth_rlp::DecodeError::Custom("invalid receipt type")) } @@ -315,6 +358,24 @@ mod tests { success: false, }, bloom: [0; 256].into(), + cumulative_gas_used: 0x1u64, + logs: vec![Log { + address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), + topics: vec![ + H256::from_str( + "000000000000000000000000000000000000000000000000000000000000dead", + ) + .unwrap(), + H256::from_str( + "000000000000000000000000000000000000000000000000000000000000beef", + ) + .unwrap(), + ], + data: Bytes::from_str("0100ff").unwrap().0.into(), + }], + success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, }; receipt.encode(&mut data); @@ -351,6 +412,24 @@ mod tests { success: false, }, bloom: [0; 256].into(), + cumulative_gas_used: 0x1u64, + logs: vec![Log { + address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), + topics: vec![ + H256::from_str( + "000000000000000000000000000000000000000000000000000000000000dead", + ) + .unwrap(), + H256::from_str( + "000000000000000000000000000000000000000000000000000000000000beef", + ) + .unwrap(), + ], + data: Bytes::from_str("0100ff").unwrap().0.into(), + }], + success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, }; let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap(); diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 8efc3d8512b..e5c2980c658 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -227,6 +227,9 @@ where self.init_env(&block.header, total_difficulty); + #[cfg(feature = "optimism")] + let mut l1_cost_oracle = crate::optimism::OptimismGasCostOracle::default(); + let mut cumulative_gas_used = 0; let mut post_state = PostState::with_tx_capacity(block.number, block.body.len()); for (transaction, sender) in block.body.iter().zip(senders) { @@ -250,6 +253,15 @@ where // Execute transaction. let ResultAndState { result, state } = self.transact(transaction, sender)?; + #[cfg(feature = "optimism")] + let l1_cost = l1_cost_oracle + .calculate_l1_cost(self.db(), block.header.number, transaction.clone()) + .map_err(|db_err| Error::DBError { inner: db_err.to_string() })?; + + // TODO: inject l1 cost into resulting state + // TODO: better manage the optimism flag in this function + + #[cfg(feature = "optimism")] if transaction.is_deposit() && !matches!(result, ExecutionResult::Success { .. }) { // If the deposit transaction failed, the deposit must still be included. // In this case, we need to increment the sender nonce and disregard the @@ -268,6 +280,7 @@ where cumulative_gas_used, bloom: Bloom::zero(), logs: vec![], + deposit_nonce: None, // TODO: add correct deposit nonce }); post_state.finish_transition(); continue @@ -285,18 +298,18 @@ where cumulative_gas_used += result.gas_used(); // Push transaction changeset and calculate header bloom filter for receipt. - post_state.add_receipt( - block.number, - Receipt { - tx_type: transaction.tx_type(), - // Success flag was added in `EIP-658: Embedding transaction status code in - // receipts`. - success: result.is_success(), - cumulative_gas_used, - // convert to reth log - logs: result.into_logs().into_iter().map(into_reth_log).collect(), - }, - ); + post_state.add_receipt(Receipt { + tx_type: transaction.tx_type(), + // Success flag was added in `EIP-658: Embedding transaction status code in + // receipts`. + success: result.is_success(), + cumulative_gas_used, + bloom: logs_bloom(logs.iter()), + logs, + #[cfg(feature = "optimism")] + deposit_nonce: None, // TODO: add correct deposit nonce + }); + post_state.finish_transition(); } Ok((post_state, cumulative_gas_used)) diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs new file mode 100644 index 00000000000..86b75716769 --- /dev/null +++ b/crates/revm/src/optimism.rs @@ -0,0 +1,58 @@ +use std::str::FromStr; + +use reth_primitives::{Address, TransactionSigned, U256}; +use revm::db::DatabaseRef; + +const L1_BLOCK_CONTRACT: &str = "0x4200000000000000000000000000000000000015"; +const L1_BASE_FEE_SLOT: u64 = 1; +const OVERHEAD_SLOT: u64 = 5; +const SCALAR_SLOT: u64 = 6; +const ZERO_BYTE_COST: u64 = 4; +const NON_ZERO_BYTE_COST: u64 = 16; + +/// OptimismGasCostOracle +/// +/// This is used to calculate the gas cost of a transaction on the L1 chain. +#[derive(Default)] +pub struct OptimismGasCostOracle { + /// The block number of the last time the L1 gas cost was updated. + pub block_num: u64, + /// The base fee of the L1 chain. + pub l1_base_fee: U256, + /// The overhead value used to calculate the gas cost. + pub overhead: U256, + /// The scalar value used to calculate the gas cost. + pub scalar: U256, +} + +impl OptimismGasCostOracle { + /// Calculate the gas cost of a transaction on the L1 chain. + pub fn calculate_l1_cost( + &mut self, + db: &mut DB, + block_num: u64, + tx: TransactionSigned, + ) -> Result, DB::Error> { + let rollup_data_gas_cost = U256::from(tx.input().iter().fold(0, |acc, byte| { + acc + if byte == &0x00 { ZERO_BYTE_COST } else { NON_ZERO_BYTE_COST } + })); + + if tx.is_deposit() || rollup_data_gas_cost == U256::ZERO { + return Ok(None) + } + + if block_num != self.block_num { + let l1_block_address = Address::from_str(L1_BLOCK_CONTRACT).unwrap(); + + self.l1_base_fee = db.storage(l1_block_address, U256::from(L1_BASE_FEE_SLOT))?; + self.overhead = db.storage(l1_block_address, U256::from(OVERHEAD_SLOT))?; + self.scalar = db.storage(l1_block_address, U256::from(SCALAR_SLOT))?; + } + + Ok(rollup_data_gas_cost + .saturating_add(self.overhead) + .saturating_mul(self.l1_base_fee) + .saturating_mul(self.scalar) + .checked_div(U256::from(1_000_000))) + } +} diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index 51dfd699fa0..fa0e777b896 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -651,7 +651,9 @@ impl FromRecoveredTransaction for PooledTransaction { } #[cfg(feature = "optimism")] Transaction::Deposit(t) => { - // TODO: fix this gas price estimate + // Gas price is always set to 0 for deposits in order to zero out ETH refunds. + // The actual L1 cost varies depending on the gas oracle feed, and is calculated + // during tx execution. let gas_price = U256::from(0); let cost = U256::from(gas_price) * U256::from(t.gas_limit) + U256::from(t.value); let effective_gas_price = 0u128; From 0d793f8a471622509d6fb367403fa70d94409b8f Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Sun, 11 Jun 2023 22:02:13 +0200 Subject: [PATCH 18/43] chore: small fixes --- crates/executor/src/lib.rs | 1 + crates/primitives/src/proofs.rs | 3 +++ crates/revm/src/executor.rs | 2 +- crates/revm/src/optimism.rs | 18 +++++++++++------- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/crates/executor/src/lib.rs b/crates/executor/src/lib.rs index 27be2283d23..c4e4b13664e 100644 --- a/crates/executor/src/lib.rs +++ b/crates/executor/src/lib.rs @@ -27,4 +27,5 @@ pub use factory::Factory; pub mod test_utils; #[cfg(feature = "optimism")] +/// Optimism-specific utilities for the executor pub mod optimism; diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index 95fd279d12b..8e90eeb970f 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -173,6 +173,9 @@ mod tests { logs, }, bloom, + logs, + #[cfg(feature = "optimism")] + deposit_nonce: None, }; let receipt = vec![receipt]; let root = calculate_receipt_root(&receipt); diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index e5c2980c658..8b01d9f1b44 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -259,7 +259,7 @@ where .map_err(|db_err| Error::DBError { inner: db_err.to_string() })?; // TODO: inject l1 cost into resulting state - // TODO: better manage the optimism flag in this function + // TODO: fix the use of the optimism flag in this function #[cfg(feature = "optimism")] if transaction.is_deposit() && !matches!(result, ExecutionResult::Success { .. }) { diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index 86b75716769..d27a9a4cc7d 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -10,13 +10,18 @@ const SCALAR_SLOT: u64 = 6; const ZERO_BYTE_COST: u64 = 4; const NON_ZERO_BYTE_COST: u64 = 16; -/// OptimismGasCostOracle +/// Optimism Gas Cost Oracle /// -/// This is used to calculate the gas cost of a transaction on the L1 chain. +/// This struct is used to calculate the gas cost of a transaction on the L1 chain. +/// The L1 block contract on L2 is used to propagate changes to the gas cost over time, +/// so we need to fetch it from the Reth database. +/// +/// To make this more efficient, we cache the values and only fetch them when the block +/// number changes. #[derive(Default)] pub struct OptimismGasCostOracle { - /// The block number of the last time the L1 gas cost was updated. - pub block_num: u64, + /// The cached block number + pub block_number: Option, /// The base fee of the L1 chain. pub l1_base_fee: U256, /// The overhead value used to calculate the gas cost. @@ -26,7 +31,7 @@ pub struct OptimismGasCostOracle { } impl OptimismGasCostOracle { - /// Calculate the gas cost of a transaction on the L1 chain. + /// Calculate the gas cost of a transaction based on L1 block data posted on L2 pub fn calculate_l1_cost( &mut self, db: &mut DB, @@ -41,9 +46,8 @@ impl OptimismGasCostOracle { return Ok(None) } - if block_num != self.block_num { + if self.block_number.cmp(&Some(block_num)).is_ne() { let l1_block_address = Address::from_str(L1_BLOCK_CONTRACT).unwrap(); - self.l1_base_fee = db.storage(l1_block_address, U256::from(L1_BASE_FEE_SLOT))?; self.overhead = db.storage(l1_block_address, U256::from(OVERHEAD_SLOT))?; self.scalar = db.storage(l1_block_address, U256::from(SCALAR_SLOT))?; From 5b1b62091f2ea54846cc775c9176874f74f77aa5 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 12 Jun 2023 21:48:03 +0200 Subject: [PATCH 19/43] wip: transact() optimism changes --- crates/interfaces/Cargo.toml | 5 +-- crates/interfaces/src/executor.rs | 5 +++ crates/revm/src/executor.rs | 55 ++++++++++++++++++++++--------- crates/revm/src/optimism.rs | 27 ++++++++------- 4 files changed, 63 insertions(+), 29 deletions(-) diff --git a/crates/interfaces/Cargo.toml b/crates/interfaces/Cargo.toml index be012c7aaf5..016330402f7 100644 --- a/crates/interfaces/Cargo.toml +++ b/crates/interfaces/Cargo.toml @@ -53,5 +53,6 @@ secp256k1 = { workspace = true, features = [ ] } [features] -test-utils = ["tokio-stream/sync", "secp256k1", "rand/std_rng"] -cli = ["clap"] \ No newline at end of file +bench = [] +optimism = [] +test-utils = ["tokio-stream/sync", "secp256k1"] diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index 250286db602..242bc3ed9e5 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -48,6 +48,11 @@ pub enum BlockExecutionError { CanonicalCommit { inner: String }, #[error("Transaction error on pipeline status update: {inner:?}")] PipelineStatusUpdate { inner: String }, + + #[cfg(feature = "optimism")] #[error("DB Error during transaction execution: {inner:?}")] DBError { inner: String }, + #[cfg(feature = "optimism")] + #[error("Insufficient funds to cover transaction cost: want {want}, have {have}")] + InsufficientFunds { want: u64, have: u64 }, } diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 8b01d9f1b44..8558fbddaa3 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -245,31 +245,56 @@ where } #[cfg(feature = "optimism")] - if let Some(m) = transaction.mint() { - // Add balance to the caller account equivalent to the minted amount - self.increment_account_balance(sender, U256::from(m), &mut post_state)?; + { + if let Some(m) = transaction.mint() { + // Add balance to the caller account equivalent to the minted amount + self.increment_account_balance(sender, U256::from(m), &mut post_state)?; + } + + // Check if the sender balance can cover the total cost including L1 cost + // total cost: gas_limit * gas_price + l1_cost + let db = self.db(); + let l1_cost = l1_cost_oracle + .calculate_l1_cost(db, block.header.number, transaction.clone()) + .map_err(|db_err| Error::DBError { inner: db_err.to_string() })?; + + // TODO: check if max_fee_per_gas works for 1559 txs here, + // for legacy this is the same as gas_price and for deposit this is 0. + // If this doesn't work we can replicate revm's effective_gas_price logic here + let total_cost = U256::from(transaction.gas_limit()) + .saturating_mul(U256::from(transaction.max_fee_per_gas())) + .saturating_add(U256::from(l1_cost)); + + let sender_account = db.load_account(sender).map_err(|_| Error::ProviderError)?; + if sender_account.info.balance.cmp(&total_cost) == std::cmp::Ordering::Less { + return Err(Error::InsufficientFunds { + have: sender_account.info.balance.to::(), + want: total_cost.to::(), + }) + } + + // Safely take l1_cost from sender (the rest will be deducted by the + // EVM execution and included in result.gas_used()) + // TODO: handle calls with `disable_balance_check` flag set? + let old = to_reth_acc(&sender_account.info); + sender_account.info.balance -= l1_cost; + let new = to_reth_acc(&sender_account.info); + post_state.change_account(sender, old, new); } // Execute transaction. let ResultAndState { result, state } = self.transact(transaction, sender)?; - #[cfg(feature = "optimism")] - let l1_cost = l1_cost_oracle - .calculate_l1_cost(self.db(), block.header.number, transaction.clone()) - .map_err(|db_err| Error::DBError { inner: db_err.to_string() })?; - - // TODO: inject l1 cost into resulting state - // TODO: fix the use of the optimism flag in this function - #[cfg(feature = "optimism")] if transaction.is_deposit() && !matches!(result, ExecutionResult::Success { .. }) { // If the deposit transaction failed, the deposit must still be included. // In this case, we need to increment the sender nonce and disregard the // state changes. The tx is invalid so it is also recorded as using all gas. - let mut acc = self.db().load_account(sender).map_err(|_| Error::ProviderError)?; - let old = to_reth_acc(&acc.info); - acc.info.nonce += 1; - let new = to_reth_acc(&acc.info); + let sender_account = + self.db().load_account(sender).map_err(|_| Error::ProviderError)?; + let old = to_reth_acc(&sender_account.info); + sender_account.info.nonce += 1; + let new = to_reth_acc(&sender_account.info); post_state.change_account(sender, old, new); cumulative_gas_used += transaction.gas_limit(); diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index d27a9a4cc7d..ba07ff63fb7 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -16,18 +16,18 @@ const NON_ZERO_BYTE_COST: u64 = 16; /// The L1 block contract on L2 is used to propagate changes to the gas cost over time, /// so we need to fetch it from the Reth database. /// -/// To make this more efficient, we cache the values and only fetch them when the block -/// number changes. +/// To make this slightly more efficient, we cache the values and only fetch them again +/// when the block number changes. #[derive(Default)] pub struct OptimismGasCostOracle { /// The cached block number - pub block_number: Option, - /// The base fee of the L1 chain. - pub l1_base_fee: U256, - /// The overhead value used to calculate the gas cost. - pub overhead: U256, - /// The scalar value used to calculate the gas cost. - pub scalar: U256, + block_number: Option, + /// The base fee of the L1 chain + l1_base_fee: U256, + /// The overhead value used to calculate the gas cost + overhead: U256, + /// The scalar value used to calculate the gas cost + scalar: U256, } impl OptimismGasCostOracle { @@ -37,17 +37,19 @@ impl OptimismGasCostOracle { db: &mut DB, block_num: u64, tx: TransactionSigned, - ) -> Result, DB::Error> { + ) -> Result { let rollup_data_gas_cost = U256::from(tx.input().iter().fold(0, |acc, byte| { acc + if byte == &0x00 { ZERO_BYTE_COST } else { NON_ZERO_BYTE_COST } })); if tx.is_deposit() || rollup_data_gas_cost == U256::ZERO { - return Ok(None) + return Ok(U256::ZERO) } if self.block_number.cmp(&Some(block_num)).is_ne() { let l1_block_address = Address::from_str(L1_BLOCK_CONTRACT).unwrap(); + + // TODO: what block is the DB info last written in? Is this always up-to-date? self.l1_base_fee = db.storage(l1_block_address, U256::from(L1_BASE_FEE_SLOT))?; self.overhead = db.storage(l1_block_address, U256::from(OVERHEAD_SLOT))?; self.scalar = db.storage(l1_block_address, U256::from(SCALAR_SLOT))?; @@ -57,6 +59,7 @@ impl OptimismGasCostOracle { .saturating_add(self.overhead) .saturating_mul(self.l1_base_fee) .saturating_mul(self.scalar) - .checked_div(U256::from(1_000_000))) + .checked_div(U256::from(1_000_000)) + .unwrap_or_default()) } } From 2adfe437021ea4b5908e4bf4d77d7ce87573ae31 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 12 Jun 2023 21:55:00 +0200 Subject: [PATCH 20/43] chore: minor changes related to optimism execution --- crates/revm/src/executor.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 8558fbddaa3..fb934e12d57 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -246,11 +246,6 @@ where #[cfg(feature = "optimism")] { - if let Some(m) = transaction.mint() { - // Add balance to the caller account equivalent to the minted amount - self.increment_account_balance(sender, U256::from(m), &mut post_state)?; - } - // Check if the sender balance can cover the total cost including L1 cost // total cost: gas_limit * gas_price + l1_cost let db = self.db(); @@ -258,6 +253,13 @@ where .calculate_l1_cost(db, block.header.number, transaction.clone()) .map_err(|db_err| Error::DBError { inner: db_err.to_string() })?; + let sender_account = db.load_account(sender).map_err(|_| Error::ProviderError)?; + let old_sender_info = to_reth_acc(&sender_account.info); + if let Some(m) = transaction.mint() { + // Add balance to the caller account equivalent to the minted amount + sender_account.info.balance += U256::from(m); + } + // TODO: check if max_fee_per_gas works for 1559 txs here, // for legacy this is the same as gas_price and for deposit this is 0. // If this doesn't work we can replicate revm's effective_gas_price logic here @@ -265,7 +267,6 @@ where .saturating_mul(U256::from(transaction.max_fee_per_gas())) .saturating_add(U256::from(l1_cost)); - let sender_account = db.load_account(sender).map_err(|_| Error::ProviderError)?; if sender_account.info.balance.cmp(&total_cost) == std::cmp::Ordering::Less { return Err(Error::InsufficientFunds { have: sender_account.info.balance.to::(), @@ -275,11 +276,11 @@ where // Safely take l1_cost from sender (the rest will be deducted by the // EVM execution and included in result.gas_used()) - // TODO: handle calls with `disable_balance_check` flag set? - let old = to_reth_acc(&sender_account.info); + // TODO: need to handle calls with `disable_balance_check` flag set? sender_account.info.balance -= l1_cost; - let new = to_reth_acc(&sender_account.info); - post_state.change_account(sender, old, new); + + let new_sender_info = to_reth_acc(&sender_account.info); + post_state.change_account(sender, old_sender_info, new_sender_info); } // Execute transaction. From 891b841f6f1cad5b3fade6d9b8e1501c391fb88c Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Sat, 17 Jun 2023 22:25:28 +0200 Subject: [PATCH 21/43] wip: removed receipts changes + improved gas handling --- crates/interfaces/src/executor.rs | 4 +-- crates/primitives/src/receipt.rs | 21 ------------ crates/revm/revm-primitives/src/env.rs | 1 + crates/revm/src/executor.rs | 47 ++++++++++++-------------- crates/revm/src/optimism.rs | 12 +++---- 5 files changed, 30 insertions(+), 55 deletions(-) diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index 242bc3ed9e5..d0c35209fdb 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -53,6 +53,6 @@ pub enum BlockExecutionError { #[error("DB Error during transaction execution: {inner:?}")] DBError { inner: String }, #[cfg(feature = "optimism")] - #[error("Insufficient funds to cover transaction cost: want {want}, have {have}")] - InsufficientFunds { want: u64, have: u64 }, + #[error("Insufficient funds to cover transaction L1 cost: want {want}, have {have}")] + InsufficientFundsForL1Cost { want: u64, have: u64 }, } diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 8360ada8faf..7995ee17a92 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -28,9 +28,6 @@ pub struct Receipt { ) )] pub logs: Vec, - /// Deposit nonce for Optimism deposit transactions - #[cfg(feature = "optimism")] - pub deposit_nonce: Option, } impl Receipt { @@ -42,13 +39,6 @@ impl Receipt { rlp_head.payload_length += self.cumulative_gas_used.length(); rlp_head.payload_length += self.bloom.length(); rlp_head.payload_length += self.logs.length(); - #[cfg(feature = "optimism")] - if self.tx_type == TxType::DEPOSIT { - // Transactions pre-Regolith don't have a deposit nonce - if let Some(nonce) = self.deposit_nonce { - rlp_head.payload_length += nonce.length(); - } - } rlp_head } @@ -60,10 +50,6 @@ impl Receipt { self.cumulative_gas_used.encode(out); self.bloom.encode(out); self.logs.encode(out); - #[cfg(feature = "optimism")] - if let Some(nonce) = self.deposit_nonce { - nonce.encode(out); - } } /// Consume the structure, returning only the receipt @@ -131,7 +117,6 @@ impl ReceiptWithBloom { cumulative_gas_used: reth_rlp::Decodable::decode(b)?, bloom: reth_rlp::Decodable::decode(b)?, logs: reth_rlp::Decodable::decode(b)?, - deposit_nonce: Some(reth_rlp::Decodable::decode(b)?), }, _ => Self { tx_type, @@ -139,8 +124,6 @@ impl ReceiptWithBloom { cumulative_gas_used: reth_rlp::Decodable::decode(b)?, bloom: reth_rlp::Decodable::decode(b)?, logs: reth_rlp::Decodable::decode(b)?, - #[cfg(feature = "optimism")] - deposit_nonce: None, }, }; @@ -374,8 +357,6 @@ mod tests { data: Bytes::from_str("0100ff").unwrap().0.into(), }], success: false, - #[cfg(feature = "optimism")] - deposit_nonce: None, }; receipt.encode(&mut data); @@ -428,8 +409,6 @@ mod tests { data: Bytes::from_str("0100ff").unwrap().0.into(), }], success: false, - #[cfg(feature = "optimism")] - deposit_nonce: None, }; let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap(); diff --git a/crates/revm/revm-primitives/src/env.rs b/crates/revm/revm-primitives/src/env.rs index e590387a7b3..be2702cc7c0 100644 --- a/crates/revm/revm-primitives/src/env.rs +++ b/crates/revm/revm-primitives/src/env.rs @@ -219,6 +219,7 @@ where Transaction::Deposit(TxDeposit { to, mint, value, gas_limit, input, .. }) => { tx_env.gas_limit = *gas_limit; if let Some(m) = mint { + // TODO: why is this not zero? tx_env.gas_price = U256::from(*m); } else { tx_env.gas_price = U256::ZERO; diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index fb934e12d57..3e7d95830b6 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -26,6 +26,9 @@ use std::{ sync::Arc, }; +#[cfg(feature = "optimism")] +use crate::optimism::OptimismGasCostOracle; + /// Main block executor pub struct Executor where @@ -228,7 +231,7 @@ where self.init_env(&block.header, total_difficulty); #[cfg(feature = "optimism")] - let mut l1_cost_oracle = crate::optimism::OptimismGasCostOracle::default(); + let mut l1_cost_oracle = OptimismGasCostOracle::default(); let mut cumulative_gas_used = 0; let mut post_state = PostState::with_tx_capacity(block.number, block.body.len()); @@ -246,38 +249,33 @@ where #[cfg(feature = "optimism")] { - // Check if the sender balance can cover the total cost including L1 cost - // total cost: gas_limit * gas_price + l1_cost + // Check if the sender balance can cover the L1 cost let db = self.db(); let l1_cost = l1_cost_oracle - .calculate_l1_cost(db, block.header.number, transaction.clone()) + .calculate_l1_cost(db, block.header.number, transaction) .map_err(|db_err| Error::DBError { inner: db_err.to_string() })?; let sender_account = db.load_account(sender).map_err(|_| Error::ProviderError)?; let old_sender_info = to_reth_acc(&sender_account.info); if let Some(m) = transaction.mint() { - // Add balance to the caller account equivalent to the minted amount + // Add balance to the caller account equal to the minted amount. + // Note: this is unconditional, and will not be reverted if the tx fails. sender_account.info.balance += U256::from(m); } - // TODO: check if max_fee_per_gas works for 1559 txs here, - // for legacy this is the same as gas_price and for deposit this is 0. - // If this doesn't work we can replicate revm's effective_gas_price logic here - let total_cost = U256::from(transaction.gas_limit()) - .saturating_mul(U256::from(transaction.max_fee_per_gas())) - .saturating_add(U256::from(l1_cost)); - - if sender_account.info.balance.cmp(&total_cost) == std::cmp::Ordering::Less { - return Err(Error::InsufficientFunds { - have: sender_account.info.balance.to::(), - want: total_cost.to::(), - }) - } + if !transaction.is_deposit() { + if sender_account.info.balance.cmp(&l1_cost) == std::cmp::Ordering::Less { + return Err(Error::InsufficientFundsForL1Cost { + have: sender_account.info.balance.to::(), + want: l1_cost.to::(), + }) + } - // Safely take l1_cost from sender (the rest will be deducted by the - // EVM execution and included in result.gas_used()) - // TODO: need to handle calls with `disable_balance_check` flag set? - sender_account.info.balance -= l1_cost; + // Safely take l1_cost from sender (the rest will be deducted by the + // internal EVM execution and included in result.gas_used()) + // TODO: need to handle calls with `disable_balance_check` flag set? + sender_account.info.balance -= l1_cost; + } let new_sender_info = to_reth_acc(&sender_account.info); post_state.change_account(sender, old_sender_info, new_sender_info); @@ -288,7 +286,7 @@ where #[cfg(feature = "optimism")] if transaction.is_deposit() && !matches!(result, ExecutionResult::Success { .. }) { - // If the deposit transaction failed, the deposit must still be included. + // If the Deposited transaction failed, the deposit must still be included. // In this case, we need to increment the sender nonce and disregard the // state changes. The tx is invalid so it is also recorded as using all gas. let sender_account = @@ -306,7 +304,6 @@ where cumulative_gas_used, bloom: Bloom::zero(), logs: vec![], - deposit_nonce: None, // TODO: add correct deposit nonce }); post_state.finish_transition(); continue @@ -332,8 +329,6 @@ where cumulative_gas_used, bloom: logs_bloom(logs.iter()), logs, - #[cfg(feature = "optimism")] - deposit_nonce: None, // TODO: add correct deposit nonce }); post_state.finish_transition(); } diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index ba07ff63fb7..9eae8c8256e 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -12,12 +12,12 @@ const NON_ZERO_BYTE_COST: u64 = 16; /// Optimism Gas Cost Oracle /// -/// This struct is used to calculate the gas cost of a transaction on the L1 chain. +/// This is used to calculate the gas cost of a transaction on the L1 chain. /// The L1 block contract on L2 is used to propagate changes to the gas cost over time, -/// so we need to fetch it from the Reth database. +/// so we need to fetch this data from the DB. /// -/// To make this slightly more efficient, we cache the values and only fetch them again -/// when the block number changes. +/// To make this slightly more efficient when executing entire blocks, the values are cached and +/// only fetched again when the block number changes. #[derive(Default)] pub struct OptimismGasCostOracle { /// The cached block number @@ -36,7 +36,7 @@ impl OptimismGasCostOracle { &mut self, db: &mut DB, block_num: u64, - tx: TransactionSigned, + tx: &TransactionSigned, ) -> Result { let rollup_data_gas_cost = U256::from(tx.input().iter().fold(0, |acc, byte| { acc + if byte == &0x00 { ZERO_BYTE_COST } else { NON_ZERO_BYTE_COST } @@ -49,7 +49,7 @@ impl OptimismGasCostOracle { if self.block_number.cmp(&Some(block_num)).is_ne() { let l1_block_address = Address::from_str(L1_BLOCK_CONTRACT).unwrap(); - // TODO: what block is the DB info last written in? Is this always up-to-date? + // TODO: Check: Is the db data always up-to-date? self.l1_base_fee = db.storage(l1_block_address, U256::from(L1_BASE_FEE_SLOT))?; self.overhead = db.storage(l1_block_address, U256::from(OVERHEAD_SLOT))?; self.scalar = db.storage(l1_block_address, U256::from(SCALAR_SLOT))?; From 64df5ee706576d8307cda0590100a0a8352b8a47 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Sun, 18 Jun 2023 09:36:26 +0200 Subject: [PATCH 22/43] chore: cleanup --- crates/primitives/src/receipt.rs | 52 ++++++++++++++++++++++++-------- crates/revm/src/executor.rs | 23 +++++++++++--- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 7995ee17a92..ba0c43c5736 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -28,6 +28,9 @@ pub struct Receipt { ) )] pub logs: Vec, + /// Deposit nonce for Optimism deposited transactions + #[cfg(feature = "optimism")] + pub deposit_nonce: Option, } impl Receipt { @@ -39,6 +42,13 @@ impl Receipt { rlp_head.payload_length += self.cumulative_gas_used.length(); rlp_head.payload_length += self.bloom.length(); rlp_head.payload_length += self.logs.length(); + #[cfg(feature = "optimism")] + if self.tx_type == TxType::DEPOSIT { + // Transactions pre-Regolith don't have a deposit nonce + if let Some(nonce) = self.deposit_nonce { + rlp_head.payload_length += nonce.length(); + } + } rlp_head } @@ -50,6 +60,10 @@ impl Receipt { self.cumulative_gas_used.encode(out); self.bloom.encode(out); self.logs.encode(out); + #[cfg(feature = "optimism")] + if let Some(nonce) = self.deposit_nonce { + nonce.encode(out); + } } /// Consume the structure, returning only the receipt @@ -88,8 +102,10 @@ impl ReceiptWithBloom { match self.tx_type { TxType::EIP2930 => out.put_u8(0x01), TxType::EIP1559 => out.put_u8(0x02), - TxType::DEPOSIT => out.put_u8(0x7E), TxType::Legacy => unreachable!("legacy handled; qed."), + + #[cfg(feature = "optimism")] + TxType::DEPOSIT => out.put_u8(0x7E), } out.put_slice(payload.as_ref()); } @@ -117,6 +133,7 @@ impl ReceiptWithBloom { cumulative_gas_used: reth_rlp::Decodable::decode(b)?, bloom: reth_rlp::Decodable::decode(b)?, logs: reth_rlp::Decodable::decode(b)?, + deposit_nonce: Some(reth_rlp::Decodable::decode(b)?), }, _ => Self { tx_type, @@ -124,6 +141,8 @@ impl ReceiptWithBloom { cumulative_gas_used: reth_rlp::Decodable::decode(b)?, bloom: reth_rlp::Decodable::decode(b)?, logs: reth_rlp::Decodable::decode(b)?, + #[cfg(feature = "optimism")] + deposit_nonce: None, }, }; @@ -164,17 +183,22 @@ impl Decodable for ReceiptWithBloom { let receipt_type = *buf.first().ok_or(reth_rlp::DecodeError::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 == 0x7E { - buf.advance(1); - Self::decode_receipt(buf, TxType::DEPOSIT) - } else { - Err(reth_rlp::DecodeError::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) + } + #[cfg(feature = "optimism")] + 0x7E => { + buf.advance(1); + Self::decode_receipt(buf, TxType::DEPOSIT) + } + _ => Err(reth_rlp::DecodeError::Custom("invalid receipt type")), } } Ordering::Equal => { @@ -357,6 +381,8 @@ mod tests { data: Bytes::from_str("0100ff").unwrap().0.into(), }], success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, }; receipt.encode(&mut data); @@ -409,6 +435,8 @@ mod tests { data: Bytes::from_str("0100ff").unwrap().0.into(), }], success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, }; let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap(); diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 3e7d95830b6..e248de5128b 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -291,11 +291,11 @@ where // state changes. The tx is invalid so it is also recorded as using all gas. let sender_account = self.db().load_account(sender).map_err(|_| Error::ProviderError)?; - let old = to_reth_acc(&sender_account.info); + let old_sender_info = to_reth_acc(&sender_account.info); sender_account.info.nonce += 1; - let new = to_reth_acc(&sender_account.info); + let new_sender_info = to_reth_acc(&sender_account.info); - post_state.change_account(sender, old, new); + post_state.change_account(sender, old_sender_info, new_sender_info); cumulative_gas_used += transaction.gas_limit(); post_state.add_receipt(Receipt { @@ -304,6 +304,8 @@ where cumulative_gas_used, bloom: Bloom::zero(), logs: vec![], + #[cfg(feature = "optimism")] + deposit_nonce: Some(transaction.nonce()), }); post_state.finish_transition(); continue @@ -317,8 +319,17 @@ where &mut post_state, ); - // append gas used - cumulative_gas_used += result.gas_used(); + #[cfg(feature = "optimism")] + if transaction.is_deposit() { + cumulative_gas_used += transaction.gas_limit() + } else { + cumulative_gas_used += result.gas_used() + } + + #[cfg(not(feature = "optimism"))] + { + cumulative_gas_used += result.gas_used(); + } // Push transaction changeset and calculate header bloom filter for receipt. post_state.add_receipt(Receipt { @@ -329,6 +340,8 @@ where cumulative_gas_used, bloom: logs_bloom(logs.iter()), logs, + #[cfg(feature = "optimism")] + deposit_nonce: Some(transaction.nonce()), }); post_state.finish_transition(); } From f6d77a70dd273d415c4b43bbcab022b77adf1e46 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 19 Jun 2023 19:50:17 +0200 Subject: [PATCH 23/43] chore: system_tx and is_success method --- crates/revm/src/executor.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index e248de5128b..34cad72144f 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -17,7 +17,7 @@ use revm::{ db::{AccountState, CacheDB, DatabaseRef}, primitives::{ hash_map::{self, Entry}, - Account as RevmAccount, AccountInfo, ExecutionResult, ResultAndState, + Account as RevmAccount, AccountInfo, ResultAndState, }, EVM, }; @@ -285,7 +285,7 @@ where let ResultAndState { result, state } = self.transact(transaction, sender)?; #[cfg(feature = "optimism")] - if transaction.is_deposit() && !matches!(result, ExecutionResult::Success { .. }) { + if transaction.is_deposit() && !result.is_success() { // If the Deposited transaction failed, the deposit must still be included. // In this case, we need to increment the sender nonce and disregard the // state changes. The tx is invalid so it is also recorded as using all gas. @@ -320,9 +320,9 @@ where ); #[cfg(feature = "optimism")] - if transaction.is_deposit() { - cumulative_gas_used += transaction.gas_limit() - } else { + if !transaction.is_system_transaction() { + // After Regolith, deposits are reported as using the actual gas used instead of + // all the gas. System transactions are not reported as using any gas. cumulative_gas_used += result.gas_used() } From 2cf460e3c41893f504abfcad1701c3e5cab5e9bc Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Tue, 20 Jun 2023 22:41:25 +0200 Subject: [PATCH 24/43] feat: refactoring: separated optimism feature + added routing fees to vaults --- crates/revm/src/executor.rs | 150 +++++++++++++++++++++++------------- crates/revm/src/optimism.rs | 33 +++++++- 2 files changed, 128 insertions(+), 55 deletions(-) diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 34cad72144f..11dd98b1e03 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -27,7 +27,7 @@ use std::{ }; #[cfg(feature = "optimism")] -use crate::optimism::OptimismGasCostOracle; +use crate::optimism; /// Main block executor pub struct Executor @@ -231,7 +231,7 @@ where self.init_env(&block.header, total_difficulty); #[cfg(feature = "optimism")] - let mut l1_cost_oracle = OptimismGasCostOracle::default(); + let mut l1_cost_oracle = optimism::L1GasCostOracle::default(); let mut cumulative_gas_used = 0; let mut post_state = PostState::with_tx_capacity(block.number, block.body.len()); @@ -249,7 +249,6 @@ where #[cfg(feature = "optimism")] { - // Check if the sender balance can cover the L1 cost let db = self.db(); let l1_cost = l1_cost_oracle .calculate_l1_cost(db, block.header.number, transaction) @@ -259,10 +258,13 @@ where let old_sender_info = to_reth_acc(&sender_account.info); if let Some(m) = transaction.mint() { // Add balance to the caller account equal to the minted amount. - // Note: this is unconditional, and will not be reverted if the tx fails. + // Note: this is unconditional, and will not be reverted if the tx fails + // (unless the block can't be built at all due to gas limit constraints) sender_account.info.balance += U256::from(m); } + // Check if the sender balance can cover the L1 cost. + // Deposits pay for their gas directly on L1 so they are exempt from this if !transaction.is_deposit() { if sender_account.info.balance.cmp(&l1_cost) == std::cmp::Ordering::Less { return Err(Error::InsufficientFundsForL1Cost { @@ -279,71 +281,113 @@ where let new_sender_info = to_reth_acc(&sender_account.info); post_state.change_account(sender, old_sender_info, new_sender_info); - } - // Execute transaction. - let ResultAndState { result, state } = self.transact(transaction, sender)?; + // Execute transaction. + let ResultAndState { result, state } = self.transact(transaction, sender)?; + + if transaction.is_deposit() && !result.is_success() { + // If the Deposited transaction failed, the deposit must still be included. + // In this case, we need to increment the sender nonce and disregard the + // state changes. The transaction is also recorded as using all gas. + let db = self.db(); + let sender_account = + db.load_account(sender).map_err(|_| Error::ProviderError)?; + let old_sender_info = to_reth_acc(&sender_account.info); + sender_account.info.nonce += 1; + let new_sender_info = to_reth_acc(&sender_account.info); + + post_state.change_account(sender, old_sender_info, new_sender_info); + if !transaction.is_system_transaction() { + cumulative_gas_used += transaction.gas_limit(); + } - #[cfg(feature = "optimism")] - if transaction.is_deposit() && !result.is_success() { - // If the Deposited transaction failed, the deposit must still be included. - // In this case, we need to increment the sender nonce and disregard the - // state changes. The tx is invalid so it is also recorded as using all gas. - let sender_account = - self.db().load_account(sender).map_err(|_| Error::ProviderError)?; - let old_sender_info = to_reth_acc(&sender_account.info); - sender_account.info.nonce += 1; - let new_sender_info = to_reth_acc(&sender_account.info); + post_state.add_receipt(Receipt { + tx_type: transaction.tx_type(), + success: false, + cumulative_gas_used, + bloom: Bloom::zero(), + logs: vec![], + deposit_nonce: Some(transaction.nonce()), + }); + post_state.finish_transition(); + continue + } - post_state.change_account(sender, old_sender_info, new_sender_info); - cumulative_gas_used += transaction.gas_limit(); + // commit changes + self.commit_changes( + state, + self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), + &mut post_state, + ); + + if !transaction.is_system_transaction() { + // After Regolith, deposits are reported as using the actual gas used instead of + // all the gas. System transactions are not reported as using any gas. + cumulative_gas_used += result.gas_used() + } + // Route base fee and l1 cost to the appropriate optimism vaults + let db = self.db(); + optimism::route_fee_to_vault( + db, + block.base_fee_per_gas.unwrap_or_default(), + result.gas_used(), + optimism::get_base_fee_recipient(), + )?; + optimism::route_fee_to_vault( + db, + l1_cost.to::(), + result.gas_used(), + optimism::get_l1_fee_recipient(), + )?; + + // cast revm logs to reth logs + let logs: Vec = result.logs().into_iter().map(into_reth_log).collect(); + + // Push transaction changeset and calculate header bloom filter for receipt. post_state.add_receipt(Receipt { tx_type: transaction.tx_type(), - success: false, + // Success flag was added in `EIP-658: Embedding transaction status code in + // receipts`. + success: result.is_success(), cumulative_gas_used, - bloom: Bloom::zero(), - logs: vec![], - #[cfg(feature = "optimism")] + bloom: logs_bloom(logs.iter()), + logs, deposit_nonce: Some(transaction.nonce()), }); post_state.finish_transition(); - continue - } - - // commit changes - self.commit_changes( - block.number, - state, - self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), - &mut post_state, - ); - - #[cfg(feature = "optimism")] - if !transaction.is_system_transaction() { - // After Regolith, deposits are reported as using the actual gas used instead of - // all the gas. System transactions are not reported as using any gas. - cumulative_gas_used += result.gas_used() } #[cfg(not(feature = "optimism"))] { + // Execute transaction. + let ResultAndState { result, state } = self.transact(transaction, sender)?; + + // commit changes + self.commit_changes( + state, + self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), + &mut post_state, + ); + cumulative_gas_used += result.gas_used(); - } - // Push transaction changeset and calculate header bloom filter for receipt. - post_state.add_receipt(Receipt { - tx_type: transaction.tx_type(), - // Success flag was added in `EIP-658: Embedding transaction status code in - // receipts`. - success: result.is_success(), - cumulative_gas_used, - bloom: logs_bloom(logs.iter()), - logs, - #[cfg(feature = "optimism")] - deposit_nonce: Some(transaction.nonce()), - }); - post_state.finish_transition(); + // cast revm logs to reth logs + let logs: Vec = result.logs().into_iter().map(into_reth_log).collect(); + + // Push transaction changeset and calculate header bloom filter for receipt. + post_state.add_receipt(Receipt { + tx_type: transaction.tx_type(), + // Success flag was added in `EIP-658: Embedding transaction status code in + // receipts`. + success: result.is_success(), + cumulative_gas_used, + bloom: logs_bloom(logs.iter()), + logs, + deposit_nonce: Some(transaction.nonce()), + }); + post_state.finish_transition(); + } } Ok((post_state, cumulative_gas_used)) diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index 9eae8c8256e..5d62a7084ce 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -1,9 +1,15 @@ use std::str::FromStr; +use reth_interfaces::executor::Error; use reth_primitives::{Address, TransactionSigned, U256}; +use reth_provider::StateProvider; +use reth_revm::database::SubState; use revm::db::DatabaseRef; +const L1_FEE_RECIPIENT: &str = "0x420000000000000000000000000000000000001A"; +const BASE_FEE_RECIPIENT: &str = "0x4200000000000000000000000000000000000019"; const L1_BLOCK_CONTRACT: &str = "0x4200000000000000000000000000000000000015"; + const L1_BASE_FEE_SLOT: u64 = 1; const OVERHEAD_SLOT: u64 = 5; const SCALAR_SLOT: u64 = 6; @@ -19,7 +25,7 @@ const NON_ZERO_BYTE_COST: u64 = 16; /// To make this slightly more efficient when executing entire blocks, the values are cached and /// only fetched again when the block number changes. #[derive(Default)] -pub struct OptimismGasCostOracle { +pub struct L1GasCostOracle { /// The cached block number block_number: Option, /// The base fee of the L1 chain @@ -30,7 +36,7 @@ pub struct OptimismGasCostOracle { scalar: U256, } -impl OptimismGasCostOracle { +impl L1GasCostOracle { /// Calculate the gas cost of a transaction based on L1 block data posted on L2 pub fn calculate_l1_cost( &mut self, @@ -63,3 +69,26 @@ impl OptimismGasCostOracle { .unwrap_or_default()) } } + +/// Get the L1 fee recipient address +pub fn get_l1_fee_recipient() -> Address { + Address::from_str(L1_FEE_RECIPIENT).unwrap() +} + +/// Get the base fee recipient address +pub fn get_base_fee_recipient() -> Address { + Address::from_str(BASE_FEE_RECIPIENT).unwrap() +} + +/// Route any fee to a recipient account +pub fn route_fee_to_vault( + db: &mut SubState, + fee: u64, + gas_used: u64, + recipient: Address, +) -> Result<(), Error> { + let mut account = db.load_account(recipient).map_err(|_| Error::ProviderError)?; + let amount_to_send = U256::from(fee.saturating_add(gas_used)); + account.info.balance = account.info.balance.saturating_add(amount_to_send); + Ok(()) +} From 3e2082c61e1c4687133e5dcea33719cb39702803 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Tue, 20 Jun 2023 23:01:26 +0200 Subject: [PATCH 25/43] fix: fee vault calc --- crates/revm/src/executor.rs | 12 +++--------- crates/revm/src/optimism.rs | 33 +++++++++++++++++---------------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 11dd98b1e03..f1f9ec1a428 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -326,19 +326,13 @@ where cumulative_gas_used += result.gas_used() } - // Route base fee and l1 cost to the appropriate optimism vaults + // Route the l1 cost and base fee to the appropriate optimism vaults let db = self.db(); - optimism::route_fee_to_vault( + optimism::route_l1_cost_to_vault(db, l1_cost)?; + optimism::route_base_fee_to_vault( db, block.base_fee_per_gas.unwrap_or_default(), result.gas_used(), - optimism::get_base_fee_recipient(), - )?; - optimism::route_fee_to_vault( - db, - l1_cost.to::(), - result.gas_used(), - optimism::get_l1_fee_recipient(), )?; // cast revm logs to reth logs diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index 5d62a7084ce..12d617820e7 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -70,25 +70,26 @@ impl L1GasCostOracle { } } -/// Get the L1 fee recipient address -pub fn get_l1_fee_recipient() -> Address { - Address::from_str(L1_FEE_RECIPIENT).unwrap() -} - -/// Get the base fee recipient address -pub fn get_base_fee_recipient() -> Address { - Address::from_str(BASE_FEE_RECIPIENT).unwrap() +/// Route the base fee to the appropriate recipient account +pub fn route_base_fee_to_vault( + db: &mut SubState, + base_fee: u64, + gas_used: u64, +) -> Result<(), Error> { + let recipient_address = Address::from_str(BASE_FEE_RECIPIENT).unwrap(); + let mut recipient = db.load_account(recipient_address).map_err(|_| Error::ProviderError)?; + let amount_to_send = U256::from(base_fee.saturating_mul(gas_used)); + recipient.info.balance = recipient.info.balance.saturating_add(amount_to_send); + Ok(()) } -/// Route any fee to a recipient account -pub fn route_fee_to_vault( +/// Route the L1 cost to the appropriate recipient account +pub fn route_l1_cost_to_vault( db: &mut SubState, - fee: u64, - gas_used: u64, - recipient: Address, + l1_cost: U256, ) -> Result<(), Error> { - let mut account = db.load_account(recipient).map_err(|_| Error::ProviderError)?; - let amount_to_send = U256::from(fee.saturating_add(gas_used)); - account.info.balance = account.info.balance.saturating_add(amount_to_send); + let recipient_address = Address::from_str(L1_FEE_RECIPIENT).unwrap(); + let mut recipient = db.load_account(recipient_address).map_err(|_| Error::ProviderError)?; + recipient.info.balance = recipient.info.balance.saturating_add(l1_cost); Ok(()) } From 4960aaaf8c2f950cc5ebc8958c0236a4dd2375ae Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Tue, 20 Jun 2023 23:47:28 +0200 Subject: [PATCH 26/43] fix: increment balance via executor function --- crates/revm/src/executor.rs | 20 ++++++++++++++------ crates/revm/src/optimism.rs | 29 ++++++----------------------- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index f1f9ec1a428..dfb65ee8c6b 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -327,12 +327,20 @@ where } // Route the l1 cost and base fee to the appropriate optimism vaults - let db = self.db(); - optimism::route_l1_cost_to_vault(db, l1_cost)?; - optimism::route_base_fee_to_vault( - db, - block.base_fee_per_gas.unwrap_or_default(), - result.gas_used(), + self.increment_account_balance( + optimism::l1_cost_recipient(), + l1_cost, + &mut post_state, + )?; + self.increment_account_balance( + optimism::base_fee_recipient(), + U256::from( + block + .base_fee_per_gas + .unwrap_or_default() + .saturating_mul(result.gas_used()), + ), + &mut post_state, )?; // cast revm logs to reth logs diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index 12d617820e7..6db8d5db032 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -1,9 +1,6 @@ use std::str::FromStr; -use reth_interfaces::executor::Error; use reth_primitives::{Address, TransactionSigned, U256}; -use reth_provider::StateProvider; -use reth_revm::database::SubState; use revm::db::DatabaseRef; const L1_FEE_RECIPIENT: &str = "0x420000000000000000000000000000000000001A"; @@ -70,26 +67,12 @@ impl L1GasCostOracle { } } -/// Route the base fee to the appropriate recipient account -pub fn route_base_fee_to_vault( - db: &mut SubState, - base_fee: u64, - gas_used: u64, -) -> Result<(), Error> { - let recipient_address = Address::from_str(BASE_FEE_RECIPIENT).unwrap(); - let mut recipient = db.load_account(recipient_address).map_err(|_| Error::ProviderError)?; - let amount_to_send = U256::from(base_fee.saturating_mul(gas_used)); - recipient.info.balance = recipient.info.balance.saturating_add(amount_to_send); - Ok(()) +/// Get the base fee recipient address +pub fn base_fee_recipient() -> Address { + Address::from_str(BASE_FEE_RECIPIENT).unwrap() } -/// Route the L1 cost to the appropriate recipient account -pub fn route_l1_cost_to_vault( - db: &mut SubState, - l1_cost: U256, -) -> Result<(), Error> { - let recipient_address = Address::from_str(L1_FEE_RECIPIENT).unwrap(); - let mut recipient = db.load_account(recipient_address).map_err(|_| Error::ProviderError)?; - recipient.info.balance = recipient.info.balance.saturating_add(l1_cost); - Ok(()) +/// Get the L1 cost recipient address +pub fn l1_cost_recipient() -> Address { + Address::from_str(L1_FEE_RECIPIENT).unwrap() } From db6d3159a67d63ce8b6a655489064d91c5c0e042 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 21 Jun 2023 09:21:36 +0200 Subject: [PATCH 27/43] chore: minor fixes --- crates/revm/src/executor.rs | 1 - crates/transaction-pool/src/traits.rs | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index dfb65ee8c6b..77d24c2fad2 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -386,7 +386,6 @@ where cumulative_gas_used, bloom: logs_bloom(logs.iter()), logs, - deposit_nonce: Some(transaction.nonce()), }); post_state.finish_transition(); } diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index fa0e777b896..8a115b6b825 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -651,9 +651,8 @@ impl FromRecoveredTransaction for PooledTransaction { } #[cfg(feature = "optimism")] Transaction::Deposit(t) => { - // Gas price is always set to 0 for deposits in order to zero out ETH refunds. - // The actual L1 cost varies depending on the gas oracle feed, and is calculated - // during tx execution. + // Gas price is always set to 0 for deposits in order to zero out ETH refunds, + // because they already pay for their gas on L1. let gas_price = U256::from(0); let cost = U256::from(gas_price) * U256::from(t.gas_limit) + U256::from(t.value); let effective_gas_price = 0u128; From f9b1aa2b6ed0dbfc6cea7b5736b29b7b00e0bc72 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:35:17 +0200 Subject: [PATCH 28/43] feat: parse l1 block info from l2 block --- crates/interfaces/src/executor.rs | 4 +- crates/revm/src/executor.rs | 6 +- crates/revm/src/optimism.rs | 113 +++++++++++++++++++----------- 3 files changed, 77 insertions(+), 46 deletions(-) diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index d0c35209fdb..bbb0afe16b9 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -50,8 +50,8 @@ pub enum BlockExecutionError { PipelineStatusUpdate { inner: String }, #[cfg(feature = "optimism")] - #[error("DB Error during transaction execution: {inner:?}")] - DBError { inner: String }, + #[error("Could not get L1 block info from L2 block: {message:?}")] + L1BlockInfoError { message: String }, #[cfg(feature = "optimism")] #[error("Insufficient funds to cover transaction L1 cost: want {want}, have {have}")] InsufficientFundsForL1Cost { want: u64, have: u64 }, diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 77d24c2fad2..4a9fe12184a 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -231,7 +231,7 @@ where self.init_env(&block.header, total_difficulty); #[cfg(feature = "optimism")] - let mut l1_cost_oracle = optimism::L1GasCostOracle::default(); + let mut l1_block_info = optimism::L1BlockInfo::new(block)?; let mut cumulative_gas_used = 0; let mut post_state = PostState::with_tx_capacity(block.number, block.body.len()); @@ -250,9 +250,7 @@ where #[cfg(feature = "optimism")] { let db = self.db(); - let l1_cost = l1_cost_oracle - .calculate_l1_cost(db, block.header.number, transaction) - .map_err(|db_err| Error::DBError { inner: db_err.to_string() })?; + let l1_cost = l1_block_info.calculate_tx_l1_cost(transaction); let sender_account = db.load_account(sender).map_err(|_| Error::ProviderError)?; let old_sender_info = to_reth_acc(&sender_account.info); diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index 6db8d5db032..87813ecf961 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -1,69 +1,102 @@ use std::str::FromStr; -use reth_primitives::{Address, TransactionSigned, U256}; -use revm::db::DatabaseRef; +use reth_interfaces::executor; +use reth_primitives::{Address, Block, TransactionKind, TransactionSigned, U256}; const L1_FEE_RECIPIENT: &str = "0x420000000000000000000000000000000000001A"; const BASE_FEE_RECIPIENT: &str = "0x4200000000000000000000000000000000000019"; const L1_BLOCK_CONTRACT: &str = "0x4200000000000000000000000000000000000015"; -const L1_BASE_FEE_SLOT: u64 = 1; -const OVERHEAD_SLOT: u64 = 5; -const SCALAR_SLOT: u64 = 6; const ZERO_BYTE_COST: u64 = 4; const NON_ZERO_BYTE_COST: u64 = 16; -/// Optimism Gas Cost Oracle +/// L1 block info /// -/// This is used to calculate the gas cost of a transaction on the L1 chain. -/// The L1 block contract on L2 is used to propagate changes to the gas cost over time, -/// so we need to fetch this data from the DB. +/// We can extract L1 epoch data from each L2 block, by looking at the `setL1BlockValues` +/// transaction data. This data is then used to calculate the L1 cost of a transaction. /// -/// To make this slightly more efficient when executing entire blocks, the values are cached and -/// only fetched again when the block number changes. +/// Here is the format of the `setL1BlockValues` transaction data: +/// +/// setL1BlockValues(uint64 _number, uint64 _timestamp, uint256 _basefee, bytes32 _hash, +/// uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar) +/// +/// For now, we only care about the fields necessary for L1 cost calculation. #[derive(Default)] -pub struct L1GasCostOracle { - /// The cached block number - block_number: Option, - /// The base fee of the L1 chain +pub struct L1BlockInfo { l1_base_fee: U256, - /// The overhead value used to calculate the gas cost - overhead: U256, - /// The scalar value used to calculate the gas cost - scalar: U256, + l1_fee_overhead: U256, + l1_fee_scalar: U256, } -impl L1GasCostOracle { +impl L1BlockInfo { + /// Create a new L1 block info struct from a L2 block + pub fn new(block: &Block) -> Result { + let l1_block_contract = Address::from_str(L1_BLOCK_CONTRACT).unwrap(); + + let l1_info_tx_data = block + .body + .iter() + .find(|tx| matches!(tx.kind(), TransactionKind::Call(to) if to == &l1_block_contract)) + .ok_or(executor::Error::L1BlockInfoError { + message: "could not find l1 block info tx in the L2 block".to_string(), + }) + .and_then(|tx| { + tx.input().get(4..).ok_or(executor::Error::L1BlockInfoError { + message: "could not get l1 block info tx calldata bytes".to_string(), + }) + })?; + + // The setL1BlockValues tx calldata must be exactly 184 bytes long, considering that + // we already removed the first 4 bytes (the function selector). Detailed breakdown: + // 8 bytes for the block number + // + 8 bytes for the block timestamp + // + 32 bytes for the base fee + // + 32 bytes for the block hash + // + 8 bytes for the block sequence number + // + 32 bytes for the batcher hash + // + 32 bytes for the fee overhead + // + 32 bytes for the fee scalar + if l1_info_tx_data.len() != 184 { + return Err(executor::Error::L1BlockInfoError { + message: "unexpected l1 block info tx calldata length found".to_string(), + }) + } + + let l1_base_fee = U256::try_from_le_slice(&l1_info_tx_data[16..48]).ok_or( + executor::Error::L1BlockInfoError { + message: "could not convert l1 base fee".to_string(), + }, + )?; + let l1_fee_overhead = U256::try_from_le_slice(&l1_info_tx_data[120..152]).ok_or( + executor::Error::L1BlockInfoError { + message: "could not convert l1 fee overhead".to_string(), + }, + )?; + let l1_fee_scalar = U256::try_from_le_slice(&l1_info_tx_data[152..184]).ok_or( + executor::Error::L1BlockInfoError { + message: "could not convert l1 fee scalar".to_string(), + }, + )?; + + Ok(Self { l1_base_fee, l1_fee_overhead, l1_fee_scalar }) + } + /// Calculate the gas cost of a transaction based on L1 block data posted on L2 - pub fn calculate_l1_cost( - &mut self, - db: &mut DB, - block_num: u64, - tx: &TransactionSigned, - ) -> Result { + pub fn calculate_tx_l1_cost(&mut self, tx: &TransactionSigned) -> U256 { let rollup_data_gas_cost = U256::from(tx.input().iter().fold(0, |acc, byte| { acc + if byte == &0x00 { ZERO_BYTE_COST } else { NON_ZERO_BYTE_COST } })); if tx.is_deposit() || rollup_data_gas_cost == U256::ZERO { - return Ok(U256::ZERO) - } - - if self.block_number.cmp(&Some(block_num)).is_ne() { - let l1_block_address = Address::from_str(L1_BLOCK_CONTRACT).unwrap(); - - // TODO: Check: Is the db data always up-to-date? - self.l1_base_fee = db.storage(l1_block_address, U256::from(L1_BASE_FEE_SLOT))?; - self.overhead = db.storage(l1_block_address, U256::from(OVERHEAD_SLOT))?; - self.scalar = db.storage(l1_block_address, U256::from(SCALAR_SLOT))?; + return U256::ZERO } - Ok(rollup_data_gas_cost - .saturating_add(self.overhead) + rollup_data_gas_cost + .saturating_add(self.l1_fee_overhead) .saturating_mul(self.l1_base_fee) - .saturating_mul(self.scalar) + .saturating_mul(self.l1_fee_scalar) .checked_div(U256::from(1_000_000)) - .unwrap_or_default()) + .unwrap_or_default() } } From 49e54fb49c21e42930c50304d082bd7dfe3f04e9 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:44:06 +0200 Subject: [PATCH 29/43] chore: removed default trait --- crates/revm/src/optimism.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index 87813ecf961..9f579d8b688 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -21,7 +21,6 @@ const NON_ZERO_BYTE_COST: u64 = 16; /// uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar) /// /// For now, we only care about the fields necessary for L1 cost calculation. -#[derive(Default)] pub struct L1BlockInfo { l1_base_fee: U256, l1_fee_overhead: U256, From 759be838218559ea55be3a1dee6a65ea28656a2d Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Thu, 29 Jun 2023 11:10:13 +0200 Subject: [PATCH 30/43] chore: set deposit gas to 0 always --- crates/revm/revm-primitives/src/env.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/crates/revm/revm-primitives/src/env.rs b/crates/revm/revm-primitives/src/env.rs index be2702cc7c0..b1d15f2529e 100644 --- a/crates/revm/revm-primitives/src/env.rs +++ b/crates/revm/revm-primitives/src/env.rs @@ -216,14 +216,9 @@ where .collect(); } #[cfg(feature = "optimism")] - Transaction::Deposit(TxDeposit { to, mint, value, gas_limit, input, .. }) => { + Transaction::Deposit(TxDeposit { to, value, gas_limit, input, .. }) => { tx_env.gas_limit = *gas_limit; - if let Some(m) = mint { - // TODO: why is this not zero? - tx_env.gas_price = U256::from(*m); - } else { - tx_env.gas_price = U256::ZERO; - } + tx_env.gas_price = U256::ZERO; tx_env.gas_priority_fee = None; match to { TransactionKind::Call(to) => tx_env.transact_to = TransactTo::Call(*to), From 5c93e35c35eea2e425ac35fe29311508ac842904 Mon Sep 17 00:00:00 2001 From: clabby Date: Sat, 8 Jul 2023 18:47:49 -0400 Subject: [PATCH 31/43] Start resolving conflicts --- bin/reth/src/cli.rs | 6 +- crates/executor/Cargo.toml | 58 ----- crates/executor/src/lib.rs | 31 --- crates/interfaces/Cargo.toml | 4 +- crates/interfaces/src/executor.rs | 27 +- crates/net/eth-wire/src/types/receipts.rs | 42 ++- crates/primitives/Cargo.toml | 8 +- crates/primitives/src/chain/spec.rs | 244 +++++++++-------- crates/primitives/src/hardfork.rs | 4 + crates/primitives/src/proofs.rs | 5 +- crates/primitives/src/receipt.rs | 165 +++++------- crates/primitives/src/transaction/mod.rs | 54 +--- crates/primitives/src/transaction/tx_type.rs | 3 +- crates/revm/Cargo.toml | 10 +- crates/revm/revm-primitives/Cargo.toml | 4 +- crates/rpc/rpc-engine-api/src/engine_api.rs | 113 -------- crates/rpc/rpc-engine-api/src/error.rs | 47 +++- crates/rpc/rpc-types/Cargo.toml | 8 +- crates/rpc/rpc-types/src/eth/engine.rs | 246 ------------------ .../rpc/rpc-types/src/eth/engine/payload.rs | 16 ++ .../rpc/rpc-types/src/eth/transaction/mod.rs | 14 +- .../transaction-pool/src/test_utils/mock.rs | 10 +- crates/transaction-pool/src/traits.rs | 31 +-- 23 files changed, 361 insertions(+), 789 deletions(-) delete mode 100644 crates/executor/Cargo.toml delete mode 100644 crates/executor/src/lib.rs delete mode 100644 crates/rpc/rpc-types/src/eth/engine.rs diff --git a/bin/reth/src/cli.rs b/bin/reth/src/cli.rs index 06f4d3a6a2f..c32b1ae17ed 100644 --- a/bin/reth/src/cli.rs +++ b/bin/reth/src/cli.rs @@ -91,6 +91,10 @@ struct Cli { #[derive(Debug, Args)] #[command(next_help_heading = "Logging")] pub struct Logs { + /// The flag to enable persistent logs. + #[arg(long = "log.persistent", global = true, conflicts_with = "journald")] + persistent: bool, + /// The path to put log files in. #[arg( long = "log.directory", @@ -112,7 +116,7 @@ pub struct Logs { impl Logs { /// Builds a tracing layer from the current log options. - pub fn layer(&self) -> (BoxedLayer, Option) + pub fn layer(&self) -> eyre::Result, Option)>> where S: Subscriber, for<'a> S: LookupSpan<'a>, diff --git a/crates/executor/Cargo.toml b/crates/executor/Cargo.toml deleted file mode 100644 index 53acae3de9f..00000000000 --- a/crates/executor/Cargo.toml +++ /dev/null @@ -1,58 +0,0 @@ -[package] -name = "reth-executor" -version = "0.1.0" -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/paradigmxyz/reth" -readme = "README.md" - -[package.metadata.cargo-udeps.ignore] -normal = [ - # Used for diagrams in docs - "aquamarine", -] - -[dependencies] -# reth -reth-primitives = { path = "../primitives" } -reth-interfaces = { path = "../interfaces" } -reth-revm = { path = "../revm" } -reth-revm-inspectors = { path = "../revm/revm-inspectors" } -reth-rlp = { path = "../rlp" } -reth-db = { path = "../storage/db" } -reth-provider = { path = "../storage/provider" } - -# revm -revm = { version = "3.0.0" } - -# common -async-trait = "0.1.57" -thiserror = "1.0.37" -auto_impl = "1.0" -tracing = "0.1.37" -tokio = { version = "1.21.2", features = ["sync"] } - -# mics -aquamarine = "0.3.0" -parking_lot = { version = "0.12", optional = true } - -triehash = "0.8" -# See to replace hashers to simplify libraries -plain_hasher = "0.2" -hash-db = "0.15" -# todo replace with faster rlp impl -rlp = { version = "0.5", default-features = false } -# replace with tiny-keccak (it is faster hasher) -sha3 = { version = "0.10", default-features = false } - - -[dev-dependencies] -reth-db = { path = "../storage/db", features = ["test-utils"] } -reth-interfaces = { path = "../interfaces", features = ["test-utils"] } -reth-primitives = { path = "../primitives", features = ["test-utils"] } -reth-provider = { path = "../storage/provider", features = ["test-utils"] } -parking_lot = "0.12" - -[features] -test-utils = ["parking_lot"] -optimism = [] diff --git a/crates/executor/src/lib.rs b/crates/executor/src/lib.rs deleted file mode 100644 index c4e4b13664e..00000000000 --- a/crates/executor/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![warn(missing_docs, unreachable_pub)] -#![deny(unused_must_use, rust_2018_idioms)] -#![doc(test( - no_crate_inject, - attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) -))] - -//! Reth executor executes transaction in block of data. - -pub mod eth_dao_fork; -pub mod substate; - -/// Execution result types. -pub use reth_provider::post_state; - -pub mod blockchain_tree; - -/// Executor -pub mod executor; - -/// ExecutorFactory impl -pub mod factory; -pub use factory::Factory; - -#[cfg(any(test, feature = "test-utils"))] -/// Common test helpers for mocking out executor and executor factory -pub mod test_utils; - -#[cfg(feature = "optimism")] -/// Optimism-specific utilities for the executor -pub mod optimism; diff --git a/crates/interfaces/Cargo.toml b/crates/interfaces/Cargo.toml index 016330402f7..8278bc412ce 100644 --- a/crates/interfaces/Cargo.toml +++ b/crates/interfaces/Cargo.toml @@ -53,6 +53,6 @@ secp256k1 = { workspace = true, features = [ ] } [features] -bench = [] +test-utils = ["tokio-stream/sync", "secp256k1", "rand/std_rng"] +cli = ["clap"] optimism = [] -test-utils = ["tokio-stream/sync", "secp256k1"] diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index bbb0afe16b9..7f315688a35 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -7,10 +7,6 @@ use thiserror::Error; pub enum BlockValidationError { #[error("EVM reported invalid transaction ({hash:?}): {message}")] EVM { hash: H256, message: String }, - #[error("Verification failed")] - VerificationFailed, - #[error("Fatal internal error")] - ExecutionFatalError, #[error("Failed to recover sender for transaction")] SenderRecoveryError, #[error("Receipt root {got:?} is different than expected {expected:?}.")] @@ -46,8 +42,20 @@ pub enum BlockExecutionError { CanonicalRevert { inner: String }, #[error("Transaction error on commit: {inner:?}")] CanonicalCommit { inner: String }, - #[error("Transaction error on pipeline status update: {inner:?}")] - PipelineStatusUpdate { inner: String }, + // === tree errors === + // TODO(mattsse): move this to tree error + #[error("Block hash {block_hash} not found in blockchain tree chain")] + BlockHashNotFoundInChain { block_hash: BlockHash }, + #[error( + "Appending chain on fork (other_chain_fork:?) is not possible as the tip is {chain_tip:?}" + )] + AppendChainDoesntConnect { chain_tip: BlockNumHash, other_chain_fork: BlockNumHash }, + + /// Only used for TestExecutor + /// + /// Note: this is not feature gated for convenience. + #[error("Execution unavailable for tests")] + UnavailableForTest, #[cfg(feature = "optimism")] #[error("Could not get L1 block info from L2 block: {message:?}")] @@ -56,3 +64,10 @@ pub enum BlockExecutionError { #[error("Insufficient funds to cover transaction L1 cost: want {want}, have {have}")] InsufficientFundsForL1Cost { want: u64, have: u64 }, } + +impl BlockExecutionError { + /// Returns `true` if the error is fatal. + pub fn is_fatal(&self) -> bool { + matches!(self, Self::CanonicalCommit { .. } | Self::CanonicalRevert { .. }) + } +} diff --git a/crates/net/eth-wire/src/types/receipts.rs b/crates/net/eth-wire/src/types/receipts.rs index 39eeae1e316..8122ec8b437 100644 --- a/crates/net/eth-wire/src/types/receipts.rs +++ b/crates/net/eth-wire/src/types/receipts.rs @@ -49,11 +49,10 @@ mod test { success: false, cumulative_gas_used: 0, logs: vec![], + #[cfg(feature = "optimism")] + deposit_nonce: None, }, bloom: Default::default(), - logs: vec![], - #[cfg(feature = "optimism")] - deposit_nonce: None, }]]); let mut out = vec![]; @@ -105,10 +104,10 @@ mod test { let mut data = vec![]; let request = RequestPair:: { request_id: 1111, - message: Receipts(vec![ - vec![ - ReceiptWithBloom { - receipt: Receipt {tx_type: TxType::Legacy, + message: Receipts(vec![vec![ + ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::Legacy, cumulative_gas_used: 0x1u64, logs: vec![ Log { @@ -120,12 +119,13 @@ mod test { data: hex!("0100ff")[..].into(), }, ], - success: false, - #[cfg(feature = "optimism")] - deposit_nonce: None, - }, - ], - ]), + bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(), + } + success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, + }, + ]]), }; request.encode(&mut data); assert_eq!(data, expected); @@ -157,22 +157,10 @@ mod test { }, ], success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, }, bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(), - cumulative_gas_used: 0x1u64, - logs: vec![ - Log { - address: hex!("0000000000000000000000000000000000000011").into(), - topics: vec![ - hex!("000000000000000000000000000000000000000000000000000000000000dead").into(), - hex!("000000000000000000000000000000000000000000000000000000000000beef").into(), - ], - data: hex!("0100ff")[..].into(), - }, - ], - success: false, - #[cfg(feature = "optimism")] - deposit_nonce: None, }, ], ]), diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index dcdb2ad9eca..f9b592dea9c 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -90,13 +90,9 @@ pprof = { version = "0.12", features = ["flamegraph", "frame-pointer", "criterio [features] default = [] +arbitrary = ["revm-primitives/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"] +test-utils = [] optimism = [] -arbitrary = [ - "revm-primitives/arbitrary", - "dep:arbitrary", - "dep:proptest", - "dep:proptest-derive", -] [[bench]] name = "recover_ecdsa_crit" diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 731d5207048..d3dfec9ddb5 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -16,119 +16,155 @@ use std::{ }; /// The Ethereum mainnet spec -pub static MAINNET: Lazy = Lazy::new(|| ChainSpec { - chain: Chain::mainnet(), - genesis: serde_json::from_str(include_str!("../../res/genesis/mainnet.json")) - .expect("Can't deserialize Mainnet genesis json"), - genesis_hash: Some(H256(hex!( - "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" - ))), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(1150000)), - (Hardfork::Dao, ForkCondition::Block(1920000)), - (Hardfork::Tangerine, ForkCondition::Block(2463000)), - (Hardfork::SpuriousDragon, ForkCondition::Block(2675000)), - (Hardfork::Byzantium, ForkCondition::Block(4370000)), - (Hardfork::Constantinople, ForkCondition::Block(7280000)), - (Hardfork::Petersburg, ForkCondition::Block(7280000)), - (Hardfork::Istanbul, ForkCondition::Block(9069000)), - (Hardfork::MuirGlacier, ForkCondition::Block(9200000)), - (Hardfork::Berlin, ForkCondition::Block(12244000)), - (Hardfork::London, ForkCondition::Block(12965000)), - (Hardfork::ArrowGlacier, ForkCondition::Block(13773000)), - (Hardfork::GrayGlacier, ForkCondition::Block(15050000)), - ( - Hardfork::Paris, - ForkCondition::TTD { - fork_block: None, - total_difficulty: U256::from(58_750_000_000_000_000_000_000_u128), - }, - ), - ]), - #[cfg(feature = "optimism")] - optimism: None, +pub static MAINNET: Lazy> = Lazy::new(|| { + ChainSpec { + chain: Chain::mainnet(), + genesis: serde_json::from_str(include_str!("../../res/genesis/mainnet.json")) + .expect("Can't deserialize Mainnet genesis json"), + genesis_hash: Some(H256(hex!( + "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" + ))), + // + paris_block_and_final_difficulty: Some(( + 15537394, + U256::from(58_750_003_716_598_352_816_469u128), + )), + fork_timestamps: ForkTimestamps::default().shanghai(1681338455), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(1150000)), + (Hardfork::Dao, ForkCondition::Block(1920000)), + (Hardfork::Tangerine, ForkCondition::Block(2463000)), + (Hardfork::SpuriousDragon, ForkCondition::Block(2675000)), + (Hardfork::Byzantium, ForkCondition::Block(4370000)), + (Hardfork::Constantinople, ForkCondition::Block(7280000)), + (Hardfork::Petersburg, ForkCondition::Block(7280000)), + (Hardfork::Istanbul, ForkCondition::Block(9069000)), + (Hardfork::MuirGlacier, ForkCondition::Block(9200000)), + (Hardfork::Berlin, ForkCondition::Block(12244000)), + (Hardfork::London, ForkCondition::Block(12965000)), + (Hardfork::ArrowGlacier, ForkCondition::Block(13773000)), + (Hardfork::GrayGlacier, ForkCondition::Block(15050000)), + ( + Hardfork::Paris, + ForkCondition::TTD { + fork_block: None, + total_difficulty: U256::from(58_750_000_000_000_000_000_000_u128), + }, + ), + (Hardfork::Shanghai, ForkCondition::Timestamp(1681338455)), + ]), + #[cfg(feature = "optimism")] + optimism: None, + } + .into() }); /// The Goerli spec -pub static GOERLI: Lazy = Lazy::new(|| ChainSpec { - chain: Chain::goerli(), - genesis: serde_json::from_str(include_str!("../../res/genesis/goerli.json")) - .expect("Can't deserialize Goerli genesis json"), - genesis_hash: Some(H256(hex!( - "bf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" - ))), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Istanbul, ForkCondition::Block(1561651)), - (Hardfork::Berlin, ForkCondition::Block(4460644)), - (Hardfork::London, ForkCondition::Block(5062605)), - ( - Hardfork::Paris, - ForkCondition::TTD { fork_block: None, total_difficulty: U256::from(10_790_000) }, - ), - ]), - #[cfg(feature = "optimism")] - optimism: None, +pub static GOERLI: Lazy> = Lazy::new(|| { + ChainSpec { + chain: Chain::goerli(), + genesis: serde_json::from_str(include_str!("../../res/genesis/goerli.json")) + .expect("Can't deserialize Goerli genesis json"), + genesis_hash: Some(H256(hex!( + "bf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" + ))), + // + paris_block_and_final_difficulty: Some((7382818, U256::from(10_790_000))), + fork_timestamps: ForkTimestamps::default().shanghai(1678832736), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(0)), + (Hardfork::Dao, 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(1561651)), + (Hardfork::Berlin, ForkCondition::Block(4460644)), + (Hardfork::London, ForkCondition::Block(5062605)), + ( + Hardfork::Paris, + ForkCondition::TTD { fork_block: None, total_difficulty: U256::from(10_790_000) }, + ), + (Hardfork::Shanghai, ForkCondition::Timestamp(1678832736)), + ]), + #[cfg(feature = "optimism")] + optimism: None, + } + .into() }); /// The Sepolia spec -pub static SEPOLIA: Lazy = Lazy::new(|| ChainSpec { - chain: Chain::sepolia(), - genesis: serde_json::from_str(include_str!("../../res/genesis/sepolia.json")) - .expect("Can't deserialize Sepolia genesis json"), - genesis_hash: Some(H256(hex!( - "25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9" - ))), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(0)), - (Hardfork::Dao, 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::Paris, - ForkCondition::TTD { - fork_block: Some(1735371), - total_difficulty: U256::from(17_000_000_000_000_000u64), - }, - ), - (Hardfork::Shanghai, ForkCondition::Timestamp(1677557088)), - ]), - #[cfg(feature = "optimism")] - optimism: None, +pub static SEPOLIA: Lazy> = Lazy::new(|| { + ChainSpec { + chain: Chain::sepolia(), + genesis: serde_json::from_str(include_str!("../../res/genesis/sepolia.json")) + .expect("Can't deserialize Sepolia genesis json"), + genesis_hash: Some(H256(hex!( + "25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9" + ))), + // + paris_block_and_final_difficulty: Some((1450409, U256::from(17_000_018_015_853_232u128))), + fork_timestamps: ForkTimestamps::default().shanghai(1677557088), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(0)), + (Hardfork::Dao, 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::Paris, + ForkCondition::TTD { + fork_block: Some(1735371), + total_difficulty: U256::from(17_000_000_000_000_000u64), + }, + ), + (Hardfork::Shanghai, ForkCondition::Timestamp(1677557088)), + ]), + #[cfg(feature = "optimism")] + optimism: None, + } + .into() }); /// The Optimism Goerli spec #[cfg(feature = "optimism")] -pub static OP_GOERLI: Lazy = 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(H256(hex!( - "c1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" - ))), - hardforks: BTreeMap::from([ - (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(4061224)), - (Hardfork::ArrowGlacier, ForkCondition::Block(4061224)), - (Hardfork::GrayGlacier, ForkCondition::Block(4061224)), - (Hardfork::Bedrock, ForkCondition::Block(4061224)), - (Hardfork::Regolith, ForkCondition::Timestamp(1679079600)), - ]), - optimism: Some(OptimismConfig { eip_1559_elasticity: 10, eip_1559_denominator: 50 }), +pub static OP_GOERLI: Lazy> = 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(H256(hex!( + "c1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" + ))), + fork_timestamps: ForkTimestamps::default(), // TODO(clabby): update this + paris_block_and_final_difficulty: None, // TODO(clabby): update this + hardforks: BTreeMap::from([ + (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::Paris, + ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) }, + ), + (Hardfork::Shanghai, ForkCondition::Timestamp(0)), + ]), + optimism: Some(OptimismConfig { eip_1559_elasticity: 10, eip_1559_denominator: 50 }), + } + .into() }); /// An Ethereum chain specification. @@ -401,6 +437,7 @@ impl From for ChainSpec { genesis_hash: None, fork_timestamps: ForkTimestamps::from_hardforks(&hardforks), hardforks, + paris_block_and_final_difficulty: None, #[cfg(feature = "optimism")] optimism: None, } @@ -626,6 +663,7 @@ impl ChainSpecBuilder { genesis_hash: None, fork_timestamps: ForkTimestamps::from_hardforks(&self.hardforks), hardforks: self.hardforks, + paris_block_and_final_difficulty: None, #[cfg(feature = "optimism")] optimism: self.optimism, } diff --git a/crates/primitives/src/hardfork.rs b/crates/primitives/src/hardfork.rs index 0c3942ebd9d..f772e8c6cc2 100644 --- a/crates/primitives/src/hardfork.rs +++ b/crates/primitives/src/hardfork.rs @@ -180,6 +180,8 @@ mod tests { genesis: Genesis::default(), genesis_hash: None, hardforks: BTreeMap::from([(Hardfork::Frontier, ForkCondition::Never)]), + fork_timestamps: Default::default(), + paris_block_and_final_difficulty: None, #[cfg(feature = "optimism")] optimism: None, }; @@ -194,6 +196,8 @@ mod tests { genesis: Genesis::default(), genesis_hash: None, hardforks: BTreeMap::from([(Hardfork::Shanghai, ForkCondition::Never)]), + fork_timestamps: Default::default(), + paris_block_and_final_difficulty: None, #[cfg(feature = "optimism")] optimism: None, }; diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index 8e90eeb970f..40740d119af 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -171,11 +171,10 @@ mod tests { success: true, cumulative_gas_used: 102068, logs, + #[cfg(feature = "optimism")] + deposit_nonce: None, }, bloom, - logs, - #[cfg(feature = "optimism")] - deposit_nonce: None, }; let receipt = vec![receipt]; let root = calculate_receipt_root(&receipt); diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index ba0c43c5736..537d147ebff 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -34,36 +34,40 @@ pub struct Receipt { } impl Receipt { - /// Returns the rlp header for the receipt payload. - fn receipt_rlp_header(&self) -> reth_rlp::Header { - let mut rlp_head = reth_rlp::Header { list: true, payload_length: 0 }; + /// Calculates [`Log`]'s bloom filter. this is slow operation and [ReceiptWithBloom] can + /// be used to cache this value. + pub fn bloom_slow(&self) -> Bloom { + logs_bloom(self.logs.iter()) + } - rlp_head.payload_length += self.success.length(); - rlp_head.payload_length += self.cumulative_gas_used.length(); - rlp_head.payload_length += self.bloom.length(); - rlp_head.payload_length += self.logs.length(); - #[cfg(feature = "optimism")] - if self.tx_type == TxType::DEPOSIT { - // Transactions pre-Regolith don't have a deposit nonce - if let Some(nonce) = self.deposit_nonce { - rlp_head.payload_length += nonce.length(); - } - } + /// Calculates the bloom filter for the receipt and returns the [ReceiptWithBloom] container + /// type. + pub fn with_bloom(self) -> ReceiptWithBloom { + self.into() + } +} - rlp_head +impl From for ReceiptWithBloom { + fn from(receipt: Receipt) -> Self { + let bloom = receipt.bloom_slow(); + ReceiptWithBloom { receipt, bloom } } +} - /// Encodes the receipt data. - fn encode_fields(&self, out: &mut dyn BufMut) { - self.receipt_rlp_header().encode(out); - self.success.encode(out); - self.cumulative_gas_used.encode(out); - self.bloom.encode(out); - self.logs.encode(out); - #[cfg(feature = "optimism")] - if let Some(nonce) = self.deposit_nonce { - nonce.encode(out); - } +/// [`Receipt`] with calculated bloom filter. +#[main_codec] +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct ReceiptWithBloom { + /// Bloom filter build from logs. + pub bloom: Bloom, + /// Main receipt body + pub receipt: Receipt, +} + +impl ReceiptWithBloom { + /// Create new [ReceiptWithBloom] + pub fn new(receipt: Receipt, bloom: Bloom) -> Self { + Self { receipt, bloom } } /// Consume the structure, returning only the receipt @@ -85,35 +89,7 @@ impl Receipt { impl ReceiptWithBloom { /// Encode receipt with or without the header data. pub fn encode_inner(&self, out: &mut dyn BufMut, with_header: bool) { - if matches!(self.tx_type, TxType::Legacy) { - self.encode_fields(out); - return - } - - let mut payload = BytesMut::new(); - self.encode_fields(&mut payload); - - if with_header { - let payload_length = payload.len() + 1; - let header = reth_rlp::Header { list: false, payload_length }; - header.encode(out); - } - - match self.tx_type { - TxType::EIP2930 => out.put_u8(0x01), - TxType::EIP1559 => out.put_u8(0x02), - TxType::Legacy => unreachable!("legacy handled; qed."), - - #[cfg(feature = "optimism")] - TxType::DEPOSIT => out.put_u8(0x7E), - } - out.put_slice(payload.as_ref()); - } - - /// Returns the length of the receipt data. - fn receipt_length(&self) -> usize { - let rlp_head = self.receipt_rlp_header(); - length_of_length(rlp_head.payload_length) + rlp_head.payload_length + self.as_encoder().encode_inner(out, with_header) } /// Decodes the receipt payload @@ -125,27 +101,35 @@ impl ReceiptWithBloom { } let started_len = b.len(); - let this = match tx_type { + let success = reth_rlp::Decodable::decode(b)?; + let cumulative_gas_used = reth_rlp::Decodable::decode(b)?; + let bloom = Decodable::decode(b)?; + let logs = reth_rlp::Decodable::decode(b)?; + + let receipt = match tx_type { #[cfg(feature = "optimism")] - TxType::DEPOSIT => Self { - tx_type, - success: reth_rlp::Decodable::decode(b)?, - cumulative_gas_used: reth_rlp::Decodable::decode(b)?, - bloom: reth_rlp::Decodable::decode(b)?, - logs: reth_rlp::Decodable::decode(b)?, - deposit_nonce: Some(reth_rlp::Decodable::decode(b)?), - }, - _ => Self { + TxType::DEPOSIT => { + let deposit_nonce = reth_rlp::Decodable::decode(b)?; + Receipt { + tx_type, + success, + cumulative_gas_used, + logs, + #[cfg(feature = "optimism")] + deposit_nonce: Some(deposit_nonce), + } + } + _ => Receipt { tx_type, - success: reth_rlp::Decodable::decode(b)?, - cumulative_gas_used: reth_rlp::Decodable::decode(b)?, - bloom: reth_rlp::Decodable::decode(b)?, - logs: reth_rlp::Decodable::decode(b)?, + 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(reth_rlp::DecodeError::ListLengthMismatch { @@ -183,7 +167,6 @@ impl Decodable for ReceiptWithBloom { let receipt_type = *buf.first().ok_or(reth_rlp::DecodeError::Custom( "typed receipt cannot be decoded from an empty slice", ))?; - match receipt_type { 0x01 => { buf.advance(1); @@ -363,26 +346,10 @@ mod tests { data: Bytes::from_str("0100ff").unwrap().0.into(), }], success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, }, bloom: [0; 256].into(), - cumulative_gas_used: 0x1u64, - logs: vec![Log { - address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), - topics: vec![ - H256::from_str( - "000000000000000000000000000000000000000000000000000000000000dead", - ) - .unwrap(), - H256::from_str( - "000000000000000000000000000000000000000000000000000000000000beef", - ) - .unwrap(), - ], - data: Bytes::from_str("0100ff").unwrap().0.into(), - }], - success: false, - #[cfg(feature = "optimism")] - deposit_nonce: None, }; receipt.encode(&mut data); @@ -417,26 +384,10 @@ mod tests { data: Bytes::from_str("0100ff").unwrap().0.into(), }], success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, }, bloom: [0; 256].into(), - cumulative_gas_used: 0x1u64, - logs: vec![Log { - address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), - topics: vec![ - H256::from_str( - "000000000000000000000000000000000000000000000000000000000000dead", - ) - .unwrap(), - H256::from_str( - "000000000000000000000000000000000000000000000000000000000000beef", - ) - .unwrap(), - ], - data: Bytes::from_str("0100ff").unwrap().0.into(), - }], - success: false, - #[cfg(feature = "optimism")] - deposit_nonce: None, }; let receipt = ReceiptWithBloom::decode(&mut &data[..]).unwrap(); @@ -469,6 +420,8 @@ mod tests { data: crate::Bytes::from(vec![1; 0xffffff]), }, ], + #[cfg(feature = "optimism")] + deposit_nonce: None, }; let mut data = vec![]; diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 8661b543fed..674ab25575e 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -15,6 +15,7 @@ use reth_rlp::{ }; #[cfg(feature = "optimism")] use revm_primitives::U256; +use serde::{Deserialize, Serialize}; pub use signature::Signature; pub use tx_type::{TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, LEGACY_TX_TYPE_ID}; @@ -1147,58 +1148,7 @@ impl TransactionSigned { /// 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_inner(&self, out: &mut dyn bytes::BufMut, with_header: bool) { - match self.transaction { - Transaction::Legacy(TxLegacy { chain_id, .. }) => { - // do nothing w/ with_header - let payload_length = self.transaction.fields_len() + - self.signature.payload_len_with_eip155_chain_id(chain_id); - let header = Header { list: true, payload_length }; - header.encode(out); - self.transaction.encode_fields(out); - self.signature.encode_with_eip155_chain_id(out, chain_id); - } - #[cfg(feature = "optimism")] - Transaction::Deposit(_) => { - let payload_length = self.transaction.fields_len() + self.signature.payload_len(); - if with_header { - Header { - list: false, - payload_length: 1 + 1 + length_of_length(payload_length) + payload_length, - } - .encode(out); - } - out.put_u8(self.transaction.tx_type() as u8); - out.put_u8(DEPOSIT_VERSION); - let header = Header { list: true, payload_length }; - header.encode(out); - self.transaction.encode_fields(out); - // Deposit transactions do not have a signature. If the signature's values are not - // zero, then the transaction is invalid. - if self.signature().v(self.chain_id()) != 0 || - self.signature().r != U256::ZERO || - self.signature().s != U256::ZERO - { - // TODO: Ensure that this transaction may never have a non-zero signature - // higher up - we shouldn't be panicking here. - panic!("Deposit transactions must have a zero signature"); - } - } - _ => { - let payload_length = self.transaction.fields_len() + self.signature.payload_len(); - if with_header { - Header { - list: false, - payload_length: 1 + length_of_length(payload_length) + payload_length, - } - .encode(out); - } - out.put_u8(self.transaction.tx_type() as u8); - let header = Header { list: true, payload_length }; - header.encode(out); - self.transaction.encode_fields(out); - self.signature.encode(out); - } - } + self.transaction.encode_with_signature(&self.signature, out, with_header); } /// Output the length of the encode_inner(out, true). Note to assume that `with_header` is only diff --git a/crates/primitives/src/transaction/tx_type.rs b/crates/primitives/src/transaction/tx_type.rs index e44bfba1dfb..3fcca61a774 100644 --- a/crates/primitives/src/transaction/tx_type.rs +++ b/crates/primitives/src/transaction/tx_type.rs @@ -69,10 +69,9 @@ impl Compact for TxType { match identifier { 0 => TxType::Legacy, 1 => TxType::EIP2930, - 2 => TxType::EIP1559, #[cfg(feature = "optimism")] 126 => TxType::DEPOSIT, - _ => panic!("unknown transaction type {identifier}"), + _ => TxType::EIP1559, }, buf, ) diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 0f375476570..31622ab3a1f 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -17,7 +17,15 @@ reth-revm-primitives = { path = "./revm-primitives" } reth-revm-inspectors = { path = "./revm-inspectors" } reth-consensus-common = { path = "../consensus/common" } -revm = { version = "3.0.0" } +# revm +revm = { workspace = true } + +# common +tracing = { workspace = true } + +[dev-dependencies] +reth-rlp = { workspace = true } +once_cell = "1.17.0" [features] optimism = ["reth-primitives/optimism", "reth-revm-primitives/optimism"] diff --git a/crates/revm/revm-primitives/Cargo.toml b/crates/revm/revm-primitives/Cargo.toml index d03fb908510..85c66da26fd 100644 --- a/crates/revm/revm-primitives/Cargo.toml +++ b/crates/revm/revm-primitives/Cargo.toml @@ -10,9 +10,9 @@ description = "core reth specific revm utilities" [dependencies] # reth -reth-primitives = { path = "../../primitives" } +reth-primitives = { workspace = true } -revm = { version = "3.0.0" } +revm = { workspace = true } [features] optimism = ["reth-primitives/optimism"] diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index c4ec8e0f38f..2a0581595f4 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -242,119 +242,6 @@ where Ok(result) } - /// When the Consensus layer receives a new block via the consensus gossip protocol, - /// the transactions in the block are sent to the execution layer in the form of a - /// `ExecutionPayload`. The Execution layer executes the transactions and validates the - /// state in the block header, then passes validation data back to Consensus layer, that - /// adds the block to the head of its own blockchain and attests to it. The block is then - /// broadcasted over the consensus p2p network in the form of a "Beacon block". - pub fn new_payload(&mut self, payload: ExecutionPayload) -> EngineApiResult { - let block = match self.try_construct_block(payload) { - Ok(b) => b, - Err(err) => { - return Ok(PayloadStatus::from_status(PayloadStatusEnum::InvalidBlockHash { - validation_error: err.to_string(), - })) - } - }; - let block_hash = block.header.hash(); - let parent_hash = block.parent_hash; - - // The block already exists in our database - if self.client.is_known(&block_hash)? { - return Ok(PayloadStatus::new(PayloadStatusEnum::Valid, block_hash)) - } - - let Some(parent) = self.client.block_by_hash(parent_hash)? else { - // TODO: cache block for storing later - return Ok(PayloadStatus::from_status(PayloadStatusEnum::Syncing)) - }; - - let parent_td = if let Some(parent_td) = self.client.header_td(&block.parent_hash)? { - parent_td - } else { - return Ok(PayloadStatus::from_status(PayloadStatusEnum::Invalid { - validation_error: EngineApiError::PayloadPreMerge.to_string(), - })) - }; - - // Short circuit the check by passing parent total difficulty. - if !self.chain_spec.fork(Hardfork::Paris).active_at_ttd(parent_td, U256::ZERO) { - return Ok(PayloadStatus::from_status(PayloadStatusEnum::Invalid { - validation_error: EngineApiError::PayloadPreMerge.to_string(), - })) - } - - if block.timestamp <= parent.timestamp { - return Ok(PayloadStatus::from_status(PayloadStatusEnum::Invalid { - validation_error: EngineApiError::PayloadTimestamp { - invalid: block.timestamp, - latest: parent.timestamp, - } - .to_string(), - })) - } - - let state_provider = self.client.latest()?; - let total_difficulty = parent_td + block.header.difficulty; - match executor::execute_and_verify_receipt( - &block.unseal(), - total_difficulty, - None, - &self.chain_spec, - &mut SubState::new(State::new(&state_provider)), - ) { - Ok(_) => Ok(PayloadStatus::new(PayloadStatusEnum::Valid, block_hash)), - Err(err) => Ok(PayloadStatus::new( - PayloadStatusEnum::Invalid { validation_error: err.to_string() }, - parent_hash, // The parent hash is already in our database hence it is valid - )), - } - } - - /// Called to resolve chain forks and ensure that the Execution layer is working with the latest - /// valid chain. - pub fn fork_choice_updated( - &self, - fork_choice_state: ForkchoiceState, - payload_attributes: Option, - ) -> EngineApiResult { - let ForkchoiceState { head_block_hash, finalized_block_hash, .. } = fork_choice_state; - - if head_block_hash.is_zero() { - return Ok(ForkchoiceUpdated::from_status(PayloadStatusEnum::Invalid { - validation_error: EngineApiError::ForkchoiceEmptyHead.to_string(), - })) - } - - // Block is not known, nothing to do. - if !self.client.is_known(&head_block_hash)? { - return Ok(ForkchoiceUpdated::from_status(PayloadStatusEnum::Syncing)) - } - - // The finalized block hash is not known, we are still syncing - if !finalized_block_hash.is_zero() && !self.client.is_known(&finalized_block_hash)? { - return Ok(ForkchoiceUpdated::from_status(PayloadStatusEnum::Syncing)) - } - - if let Err(error) = self.forkchoice_state_tx.send(fork_choice_state) { - tracing::error!(target: "rpc::engine_api", ?error, "Failed to update forkchoice state"); - } - - if let Some(_attr) = payload_attributes { - #[cfg(feature = "optimism")] - if self.chain_spec.optimism.is_some() && _attr.gas_limit.is_none() { - return Err(EngineApiError::MissingGasLimitInPayloadAttributes) - } - - // TODO: optionally build the block - } - - let chain_info = self.client.chain_info()?; - Ok(ForkchoiceUpdated::from_status(PayloadStatusEnum::Valid) - .with_latest_valid_hash(chain_info.best_hash)) - } - /// Called to verify network configuration parameters and ensure that Consensus and Execution /// layers are using the latest configuration. pub async fn exchange_transition_configuration( diff --git a/crates/rpc/rpc-engine-api/src/error.rs b/crates/rpc/rpc-engine-api/src/error.rs index 8e146b07f09..b6dfca1b68c 100644 --- a/crates/rpc/rpc-engine-api/src/error.rs +++ b/crates/rpc/rpc-engine-api/src/error.rs @@ -68,10 +68,55 @@ pub enum EngineApiError { ForkChoiceUpdate(#[from] BeaconForkChoiceUpdateError), /// An error occurred while processing a new payload in the beacon consensus engine. #[error(transparent)] - Internal(#[from] reth_interfaces::Error), + NewPayload(#[from] BeaconOnNewPayloadError), + /// Encountered an internal error. + #[error(transparent)] + Internal(Box), + /// Fetching the payload failed + #[error(transparent)] + GetPayloadError(#[from] PayloadBuilderError), /// If the optimism feature flag is enabled, the payload attributes must have a present /// gas limit for the forkchoice updated method. #[cfg(feature = "optimism")] #[error("Missing gas limit in payload attributes")] MissingGasLimitInPayloadAttributes, } + +impl From for jsonrpsee_types::error::ErrorObject<'static> { + fn from(error: EngineApiError) -> Self { + let code = match error { + EngineApiError::InvalidBodiesRange { .. } | + EngineApiError::WithdrawalsNotSupportedInV1 | + EngineApiError::NoWithdrawalsPostShanghai | + EngineApiError::HasWithdrawalsPreShanghai => INVALID_PARAMS_CODE, + EngineApiError::UnknownPayload => UNKNOWN_PAYLOAD_CODE, + EngineApiError::PayloadRequestTooLarge { .. } => REQUEST_TOO_LARGE_CODE, + + // Error responses from the consensus engine + EngineApiError::ForkChoiceUpdate(ref err) => match err { + BeaconForkChoiceUpdateError::ForkchoiceUpdateError(err) => return (*err).into(), + BeaconForkChoiceUpdateError::EngineUnavailable | + BeaconForkChoiceUpdateError::Internal(_) => INTERNAL_ERROR_CODE, + }, + EngineApiError::NewPayload(ref err) => match err { + BeaconOnNewPayloadError::Internal(_) | + BeaconOnNewPayloadError::EngineUnavailable => INTERNAL_ERROR_CODE, + }, + // Any other server error + EngineApiError::TerminalTD { .. } | + EngineApiError::TerminalBlockHash { .. } | + EngineApiError::Internal(_) | + EngineApiError::GetPayloadError(_) => INTERNAL_ERROR_CODE, + // Optimism errors + #[cfg(feature = "optimism")] + EngineApiError::MissingGasLimitInPayloadAttributes => INVALID_PARAMS_CODE, + }; + jsonrpsee_types::error::ErrorObject::owned(code, error.to_string(), None::<()>) + } +} + +impl From for jsonrpsee_core::error::Error { + fn from(error: EngineApiError) -> Self { + jsonrpsee_core::error::Error::Call(error.into()) + } +} diff --git a/crates/rpc/rpc-types/Cargo.toml b/crates/rpc/rpc-types/Cargo.toml index 7e56ccbe319..0e06fc3e35e 100644 --- a/crates/rpc/rpc-types/Cargo.toml +++ b/crates/rpc/rpc-types/Cargo.toml @@ -24,7 +24,13 @@ serde_json = { workspace = true } jsonrpsee-types = { version = "0.18" } [dev-dependencies] -reth-interfaces = { path = "../../interfaces", features = ["test-utils"] } +# reth +reth-interfaces = { workspace = true, features = ["test-utils"] } + +# misc +rand = { workspace = true } +assert_matches = "1.5" +similar-asserts = "1.4" [features] optimism = ["reth-primitives/optimism"] diff --git a/crates/rpc/rpc-types/src/eth/engine.rs b/crates/rpc/rpc-types/src/eth/engine.rs deleted file mode 100644 index 548ed16645b..00000000000 --- a/crates/rpc/rpc-types/src/eth/engine.rs +++ /dev/null @@ -1,246 +0,0 @@ -//! Engine API types: and following the execution specs - -#![allow(missing_docs)] - -use reth_primitives::{ - Address, Block, Bloom, Bytes, SealedBlock, Withdrawal, H256, H64, U256, U64, -}; -use reth_rlp::Encodable; -use serde::{Deserialize, Serialize}; - -/// The list of supported Engine capabilities -pub const CAPABILITIES: [&str; 9] = [ - "engine_forkchoiceUpdatedV1", - "engine_forkchoiceUpdatedV2", - "engine_exchangeTransitionConfigurationV1", - "engine_getPayloadV1", - "engine_getPayloadV2", - "engine_newPayloadV1", - "engine_newPayloadV2", - "engine_getPayloadBodiesByHashV1", - "engine_getPayloadBodiesByRangeV1", -]; - -/// This structure maps on the ExecutionPayload structure of the beacon chain spec. -/// -/// See also: -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ExecutionPayload { - pub parent_hash: H256, - pub fee_recipient: Address, - pub state_root: H256, - pub receipts_root: H256, - pub logs_bloom: Bloom, - pub prev_randao: H256, - pub block_number: U64, - pub gas_limit: U64, - pub gas_used: U64, - pub timestamp: U64, - pub extra_data: Bytes, - pub base_fee_per_gas: U256, - pub block_hash: H256, - pub transactions: Vec, - /// Array of [`Withdrawal`] enabled with V2 - /// See - #[serde(default, skip_serializing_if = "Option::is_none")] - pub withdrawals: Option>, -} - -impl From for ExecutionPayload { - fn from(value: SealedBlock) -> Self { - let transactions = value - .body - .iter() - .map(|tx| { - let mut encoded = Vec::new(); - tx.encode(&mut encoded); - encoded.into() - }) - .collect(); - ExecutionPayload { - parent_hash: value.parent_hash, - fee_recipient: value.beneficiary, - state_root: value.state_root, - receipts_root: value.receipts_root, - logs_bloom: value.logs_bloom, - prev_randao: value.mix_hash, - block_number: value.number.into(), - gas_limit: value.gas_limit.into(), - gas_used: value.gas_used.into(), - timestamp: value.timestamp.into(), - extra_data: value.extra_data.clone(), - base_fee_per_gas: U256::from(value.base_fee_per_gas.unwrap_or_default()), - block_hash: value.hash(), - transactions, - withdrawals: value.withdrawals, - } - } -} - -/// This structure contains a body of an execution payload. -/// -/// See also: -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ExecutionPayloadBody { - pub transactions: Vec, - pub withdrawals: Vec, -} - -impl From for ExecutionPayloadBody { - fn from(value: Block) -> Self { - let transactions = value.body.into_iter().map(|tx| { - let mut out = Vec::new(); - tx.encode(&mut out); - out.into() - }); - ExecutionPayloadBody { - transactions: transactions.collect(), - withdrawals: value.withdrawals.unwrap_or_default(), - } - } -} - -/// The execution payload body response that allows for `null` values. -pub type ExecutionPayloadBodies = Vec>; - -/// This structure encapsulates the fork choice state -#[derive(Default, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ForkchoiceState { - pub head_block_hash: H256, - pub safe_block_hash: H256, - pub finalized_block_hash: H256, -} - -/// This structure contains the attributes required to initiate a payload build process in the -/// context of an `engine_forkchoiceUpdated` call. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct PayloadAttributes { - pub timestamp: U64, - pub prev_randao: H256, - pub suggested_fee_recipient: Address, - /// Array of [`Withdrawal`] enabled with V2 - /// See - #[serde(default, skip_serializing_if = "Option::is_none")] - pub withdrawals: Option>, - - /// Transactions is a field for rollups: the transactions list is forced into the block - #[cfg(feature = "optimism")] - #[serde(default, skip_serializing_if = "Option::is_none")] - pub transactions: Option>, - - /// If true, the no transactions are taken out of the tx-pool, only transactions from the above - /// Transactions list will be included. - #[cfg(feature = "optimism")] - #[serde(default, skip_serializing_if = "Option::is_none")] - pub no_tx_pool: Option, - - /// If set, this sets the exact gas limit the block produced with. - #[cfg(feature = "optimism")] - #[serde(default, skip_serializing_if = "Option::is_none")] - pub gas_limit: Option, -} - -/// This structure contains the result of processing a payload -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct PayloadStatus { - #[serde(flatten)] - pub status: PayloadStatusEnum, - /// Hash of the most recent valid block in the branch defined by payload and its ancestors - pub latest_valid_hash: Option, -} - -impl PayloadStatus { - pub fn new(status: PayloadStatusEnum, latest_valid_hash: H256) -> Self { - Self { status, latest_valid_hash: Some(latest_valid_hash) } - } - - pub fn from_status(status: PayloadStatusEnum) -> Self { - Self { status, latest_valid_hash: None } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(tag = "status", rename_all = "SCREAMING_SNAKE_CASE")] -pub enum PayloadStatusEnum { - Valid, - Invalid { - #[serde(rename = "validationError")] - validation_error: String, - }, - Syncing, - Accepted, - InvalidBlockHash { - #[serde(rename = "validationError")] - validation_error: String, - }, -} - -/// This structure contains configurable settings of the transition process. -#[derive(Default, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TransitionConfiguration { - /// Maps on the TERMINAL_TOTAL_DIFFICULTY parameter of EIP-3675 - pub terminal_total_difficulty: U256, - /// Maps on TERMINAL_BLOCK_HASH parameter of EIP-3675 - pub terminal_block_hash: H256, - /// Maps on TERMINAL_BLOCK_NUMBER parameter of EIP-3675 - pub terminal_block_number: U64, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ForkchoiceUpdated { - pub payload_status: PayloadStatus, - pub payload_id: Option, -} - -impl ForkchoiceUpdated { - pub fn new(payload_status: PayloadStatus) -> Self { - Self { payload_status, payload_id: None } - } - - pub fn from_status(status: PayloadStatusEnum) -> Self { - Self { payload_status: PayloadStatus::from_status(status), payload_id: None } - } - - pub fn with_latest_valid_hash(mut self, hash: H256) -> Self { - self.payload_status.latest_valid_hash = Some(hash); - self - } - - pub fn with_payload_id(mut self, id: H64) -> Self { - self.payload_id = Some(id); - self - } -} - -#[cfg(test)] -mod tests { - use super::*; - use reth_interfaces::test_utils::generators::random_block_range; - use reth_primitives::{TransactionSigned, H256}; - use reth_rlp::Decodable; - - #[test] - fn payload_body_roundtrip() { - for block in random_block_range(0..100, H256::default(), 0..2) { - let unsealed = block.clone().unseal(); - let payload_body: ExecutionPayloadBody = unsealed.into(); - - assert_eq!( - Ok(block.body), - payload_body - .transactions - .iter() - .map(|x| TransactionSigned::decode(&mut &x[..])) - .collect::, _>>(), - ); - - assert_eq!(block.withdrawals.unwrap_or_default(), payload_body.withdrawals); - } - } -} diff --git a/crates/rpc/rpc-types/src/eth/engine/payload.rs b/crates/rpc/rpc-types/src/eth/engine/payload.rs index 5b63e1b8cc5..3a0fcc4b9ec 100644 --- a/crates/rpc/rpc-types/src/eth/engine/payload.rs +++ b/crates/rpc/rpc-types/src/eth/engine/payload.rs @@ -255,6 +255,22 @@ pub struct PayloadAttributes { /// See #[serde(default, skip_serializing_if = "Option::is_none")] pub withdrawals: Option>, + + /// Transactions is a field for rollups: the transactions list is forced into the block + #[cfg(feature = "optimism")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub transactions: Option>, + + /// If true, the no transactions are taken out of the tx-pool, only transactions from the above + /// Transactions list will be included. + #[cfg(feature = "optimism")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub no_tx_pool: Option, + + /// If set, this sets the exact gas limit the block produced with. + #[cfg(feature = "optimism")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub gas_limit: Option, } /// This structure contains the result of processing a payload or fork choice update. diff --git a/crates/rpc/rpc-types/src/eth/transaction/mod.rs b/crates/rpc/rpc-types/src/eth/transaction/mod.rs index 8c90fb83b2f..109257909a1 100644 --- a/crates/rpc/rpc-types/src/eth/transaction/mod.rs +++ b/crates/rpc/rpc-types/src/eth/transaction/mod.rs @@ -111,8 +111,18 @@ impl Transaction { let (gas_price, max_fee_per_gas) = match signed_tx.tx_type() { TxType::Legacy => (Some(U128::from(signed_tx.max_fee_per_gas())), None), - TxType::EIP2930 => (None, Some(U128::from(signed_tx.max_fee_per_gas()))), - TxType::EIP1559 => (None, Some(U128::from(signed_tx.max_fee_per_gas()))), + TxType::EIP2930 => (Some(U128::from(signed_tx.max_fee_per_gas())), None), + TxType::EIP1559 => { + // the gas price field for EIP1559 is set to `min(tip, gasFeeCap - baseFee) + + // baseFee` + let gas_price = base_fee + .and_then(|base_fee| { + signed_tx.effective_tip_per_gas(base_fee).map(|tip| tip + base_fee as u128) + }) + .unwrap_or_else(|| signed_tx.max_fee_per_gas()); + + (Some(U128::from(gas_price)), Some(U128::from(signed_tx.max_fee_per_gas()))) + } #[cfg(feature = "optimism")] TxType::DEPOSIT => (None, None), }; diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index 62cc70afc46..081d0efbb50 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -378,6 +378,8 @@ impl PoolTransaction for MockTransaction { MockTransaction::Eip1559 { max_fee_per_gas, gas_limit, .. } => { U256::from(*gas_limit) * U256::from(*max_fee_per_gas) } + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => U256::ZERO, } } @@ -387,10 +389,10 @@ impl PoolTransaction for MockTransaction { fn max_fee_per_gas(&self) -> u128 { match self { + MockTransaction::Legacy { gas_price, .. } => *gas_price, + MockTransaction::Eip1559 { max_fee_per_gas, .. } => *max_fee_per_gas, #[cfg(feature = "optimism")] - MockTransaction::DepositTx { .. } => None, - MockTransaction::Legacy { .. } => None, - MockTransaction::Eip1559 { max_fee_per_gas, .. } => Some(*max_fee_per_gas), + MockTransaction::DepositTx { .. } => 0, } } @@ -421,7 +423,7 @@ impl PoolTransaction for MockTransaction { fn tx_type(&self) -> u8 { match self { #[cfg(feature = "optimism")] - MockTransaction::DepositTx { .. } => DEPOSIT_TX_TYPE, + MockTransaction::DepositTx { .. } => TxType::DEPOSIT.into(), MockTransaction::Legacy { .. } => TxType::Legacy.into(), MockTransaction::Eip1559 { .. } => TxType::EIP1559.into(), } diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index 8a115b6b825..b319eb5c21f 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -582,11 +582,11 @@ impl PoolTransaction for PooledTransaction { /// This is also commonly referred to as the "Gas Fee Cap" (`GasFeeCap`). fn max_fee_per_gas(&self) -> u128 { match &self.transaction.transaction { - Transaction::Legacy(_) => None, - Transaction::Eip2930(_) => None, - Transaction::Eip1559(tx) => Some(tx.max_fee_per_gas), + Transaction::Legacy(tx) => tx.gas_price, + Transaction::Eip2930(tx) => tx.gas_price, + Transaction::Eip1559(tx) => tx.max_fee_per_gas, #[cfg(feature = "optimism")] - Transaction::Deposit(_) => None, + Transaction::Deposit(_) => 0, } } @@ -632,29 +632,16 @@ impl PoolTransaction for PooledTransaction { impl FromRecoveredTransaction for PooledTransaction { fn from_recovered_transaction(tx: TransactionSignedEcRecovered) -> Self { - let (cost, effective_gas_price) = match &tx.transaction { - Transaction::Legacy(t) => { - let cost = U256::from(t.gas_price) * U256::from(t.gas_limit) + U256::from(t.value); - let effective_gas_price = t.gas_price; - (cost, effective_gas_price) - } - Transaction::Eip2930(t) => { - let cost = U256::from(t.gas_price) * U256::from(t.gas_limit) + U256::from(t.value); - let effective_gas_price = t.gas_price; - (cost, effective_gas_price) - } - Transaction::Eip1559(t) => { - let cost = - U256::from(t.max_fee_per_gas) * U256::from(t.gas_limit) + U256::from(t.value); - let effective_gas_price = t.max_priority_fee_per_gas; - (cost, effective_gas_price) - } + let gas_cost = match &tx.transaction { + Transaction::Legacy(t) => U256::from(t.gas_price) * U256::from(t.gas_limit), + Transaction::Eip2930(t) => U256::from(t.gas_price) * U256::from(t.gas_limit), + Transaction::Eip1559(t) => U256::from(t.max_fee_per_gas) * U256::from(t.gas_limit), #[cfg(feature = "optimism")] Transaction::Deposit(t) => { // Gas price is always set to 0 for deposits in order to zero out ETH refunds, // because they already pay for their gas on L1. let gas_price = U256::from(0); - let cost = U256::from(gas_price) * U256::from(t.gas_limit) + U256::from(t.value); + let cost = U256::from(t.value); let effective_gas_price = 0u128; (cost, effective_gas_price) } From cb1c654af8889f7d3efb5313b8eda1314f96224a Mon Sep 17 00:00:00 2001 From: clabby Date: Sat, 8 Jul 2023 20:53:30 -0400 Subject: [PATCH 32/43] More conflict resolution / updates x --- bin/reth/Cargo.toml | 10 ++ crates/consensus/beacon/Cargo.toml | 9 + .../interfaces/src/blockchain_tree/error.rs | 3 + .../interfaces/src/test_utils/generators.rs | 2 + crates/payload/basic/Cargo.toml | 8 + crates/payload/basic/src/lib.rs | 2 + crates/payload/builder/Cargo.toml | 6 + crates/primitives/src/transaction/mod.rs | 17 +- crates/revm/src/executor.rs | 156 +++--------------- crates/revm/src/lib.rs | 4 + crates/revm/src/optimism.rs | 21 ++- crates/storage/provider/Cargo.toml | 5 + .../storage/provider/src/test_utils/blocks.rs | 4 + crates/transaction-pool/src/traits.rs | 7 +- 14 files changed, 104 insertions(+), 150 deletions(-) diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index 2c0965dfaa3..5d98c73e012 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -99,6 +99,16 @@ min-warn-logs = ["tracing/release_max_level_warn"] min-info-logs = ["tracing/release_max_level_info"] min-debug-logs = ["tracing/release_max_level_debug"] min-trace-logs = ["tracing/release_max_level_trace"] +optimism = [ + "reth-primitives/optimism", + "reth-revm/optimism", + "reth-interfaces/optimism", + "reth-rpc-engine-api/optimism", + "reth-transaction-pool/optimism", + "reth-provider/optimism", + "reth-beacon-consensus/optimism", + "reth-basic-payload-builder/optimism", +] [build-dependencies] vergen = { version = "8.0.0", features = ["build", "cargo", "git", "gitcl"] } diff --git a/crates/consensus/beacon/Cargo.toml b/crates/consensus/beacon/Cargo.toml index bb676f71d10..47967ea76f4 100644 --- a/crates/consensus/beacon/Cargo.toml +++ b/crates/consensus/beacon/Cargo.toml @@ -44,3 +44,12 @@ reth-revm = { path = "../../revm" } reth-downloaders = { path = "../../net/downloaders" } assert_matches = "1.5" + +[features] +optimism = [ + "reth-consensus-common/optimism", + "reth-primitives/optimism", + "reth-interfaces/optimism", + "reth-provider/optimism", + "reth-rpc-types/optimism", +] diff --git a/crates/interfaces/src/blockchain_tree/error.rs b/crates/interfaces/src/blockchain_tree/error.rs index edcdb569bae..c2e463a7328 100644 --- a/crates/interfaces/src/blockchain_tree/error.rs +++ b/crates/interfaces/src/blockchain_tree/error.rs @@ -194,6 +194,9 @@ impl InsertBlockErrorKind { BlockExecutionError::BlockHashNotFoundInChain { .. } | BlockExecutionError::AppendChainDoesntConnect { .. } | BlockExecutionError::UnavailableForTest => false, + #[cfg(feature = "optimism")] + BlockExecutionError::L1BlockInfoError { .. } | + BlockExecutionError::InsufficientFundsForL1Cost { .. } => false, } } InsertBlockErrorKind::Tree(err) => { diff --git a/crates/interfaces/src/test_utils/generators.rs b/crates/interfaces/src/test_utils/generators.rs index fbbdf42a212..d9399f8f814 100644 --- a/crates/interfaces/src/test_utils/generators.rs +++ b/crates/interfaces/src/test_utils/generators.rs @@ -349,6 +349,8 @@ pub fn random_receipt( } else { vec![] }, + #[cfg(feature = "optimism")] + deposit_nonce: None, } } diff --git a/crates/payload/basic/Cargo.toml b/crates/payload/basic/Cargo.toml index a2a9f3288af..3e7efd4810e 100644 --- a/crates/payload/basic/Cargo.toml +++ b/crates/payload/basic/Cargo.toml @@ -29,3 +29,11 @@ futures-util = { workspace = true } ## misc tracing = { workspace = true } + +[features] +optimism = [ + "reth-primitives/optimism", + "reth-revm/optimism", + "reth-transaction-pool/optimism", + "reth-provider/optimism" +] diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 2d598a27026..f49c3afa65d 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -659,6 +659,8 @@ fn build_payload( success: result.is_success(), cumulative_gas_used, logs: result.logs().into_iter().map(into_reth_log).collect(), + #[cfg(feature = "optimism")] + deposit_nonce: None, }, ); diff --git a/crates/payload/builder/Cargo.toml b/crates/payload/builder/Cargo.toml index b83236fd9b3..7f2a80337b6 100644 --- a/crates/payload/builder/Cargo.toml +++ b/crates/payload/builder/Cargo.toml @@ -32,3 +32,9 @@ tracing = { workspace = true } [features] test-utils = [] +optimism = [ + "reth-primitives/optimism", + "reth-rpc-types/optimism", + "reth-interfaces/optimism", + "reth-revm-primitives/optimism", +] diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 674ab25575e..4e02a42499e 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -13,8 +13,6 @@ use reth_codecs::{add_arbitrary_tests, derive_arbitrary, main_codec, Compact}; use reth_rlp::{ length_of_length, Decodable, DecodeError, Encodable, Header, EMPTY_LIST_CODE, EMPTY_STRING_CODE, }; -#[cfg(feature = "optimism")] -use revm_primitives::U256; use serde::{Deserialize, Serialize}; pub use signature::Signature; pub use tx_type::{TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, LEGACY_TX_TYPE_ID}; @@ -288,6 +286,8 @@ impl Transaction { Transaction::Legacy(tx) => tx.nonce = nonce, Transaction::Eip2930(tx) => tx.nonce = nonce, Transaction::Eip1559(tx) => tx.nonce = nonce, + #[cfg(feature = "optimism")] + Transaction::Deposit(_tx) => { /* noop */ } } } @@ -297,6 +297,8 @@ impl Transaction { Transaction::Legacy(tx) => tx.value = value, Transaction::Eip2930(tx) => tx.value = value, Transaction::Eip1559(tx) => tx.value = value, + #[cfg(feature = "optimism")] + Transaction::Deposit(tx) => tx.value = value, } } @@ -306,6 +308,8 @@ impl Transaction { Transaction::Legacy(tx) => tx.input = input, Transaction::Eip2930(tx) => tx.input = input, Transaction::Eip1559(tx) => tx.input = input, + #[cfg(feature = "optimism")] + Transaction::Deposit(tx) => tx.input = input, } } @@ -338,6 +342,11 @@ impl Compact for Transaction { tx.to_compact(buf); 2 } + #[cfg(feature = "optimism")] + Transaction::Deposit(tx) => { + tx.to_compact(buf); + 126 + } } } @@ -500,6 +509,8 @@ impl Transaction { Transaction::Eip1559(TxEip1559 { max_priority_fee_per_gas, .. }) => { *max_priority_fee_per_gas } + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => 0, } } @@ -511,6 +522,8 @@ impl Transaction { Transaction::Legacy(tx) => tx.gas_price, Transaction::Eip2930(tx) => tx.gas_price, Transaction::Eip1559(dynamic_tx) => dynamic_tx.effective_gas_price(base_fee), + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => 0, } } diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 4a9fe12184a..c2ed5c7e53f 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -231,7 +231,7 @@ where self.init_env(&block.header, total_difficulty); #[cfg(feature = "optimism")] - let mut l1_block_info = optimism::L1BlockInfo::new(block)?; + let mut l1_block_info = optimism::L1BlockInfo::try_from(block)?; let mut cumulative_gas_used = 0; let mut post_state = PostState::with_tx_capacity(block.number, block.body.len()); @@ -246,147 +246,35 @@ where } .into()) } + // Execute transaction. + let ResultAndState { result, state } = self.transact(transaction, sender)?; - #[cfg(feature = "optimism")] - { - let db = self.db(); - let l1_cost = l1_block_info.calculate_tx_l1_cost(transaction); - - let sender_account = db.load_account(sender).map_err(|_| Error::ProviderError)?; - let old_sender_info = to_reth_acc(&sender_account.info); - if let Some(m) = transaction.mint() { - // Add balance to the caller account equal to the minted amount. - // Note: this is unconditional, and will not be reverted if the tx fails - // (unless the block can't be built at all due to gas limit constraints) - sender_account.info.balance += U256::from(m); - } - - // Check if the sender balance can cover the L1 cost. - // Deposits pay for their gas directly on L1 so they are exempt from this - if !transaction.is_deposit() { - if sender_account.info.balance.cmp(&l1_cost) == std::cmp::Ordering::Less { - return Err(Error::InsufficientFundsForL1Cost { - have: sender_account.info.balance.to::(), - want: l1_cost.to::(), - }) - } - - // Safely take l1_cost from sender (the rest will be deducted by the - // internal EVM execution and included in result.gas_used()) - // TODO: need to handle calls with `disable_balance_check` flag set? - sender_account.info.balance -= l1_cost; - } - - let new_sender_info = to_reth_acc(&sender_account.info); - post_state.change_account(sender, old_sender_info, new_sender_info); - - // Execute transaction. - let ResultAndState { result, state } = self.transact(transaction, sender)?; - - if transaction.is_deposit() && !result.is_success() { - // If the Deposited transaction failed, the deposit must still be included. - // In this case, we need to increment the sender nonce and disregard the - // state changes. The transaction is also recorded as using all gas. - let db = self.db(); - let sender_account = - db.load_account(sender).map_err(|_| Error::ProviderError)?; - let old_sender_info = to_reth_acc(&sender_account.info); - sender_account.info.nonce += 1; - let new_sender_info = to_reth_acc(&sender_account.info); - - post_state.change_account(sender, old_sender_info, new_sender_info); - if !transaction.is_system_transaction() { - cumulative_gas_used += transaction.gas_limit(); - } - - post_state.add_receipt(Receipt { - tx_type: transaction.tx_type(), - success: false, - cumulative_gas_used, - bloom: Bloom::zero(), - logs: vec![], - deposit_nonce: Some(transaction.nonce()), - }); - post_state.finish_transition(); - continue - } - - // commit changes - self.commit_changes( - state, - self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), - &mut post_state, - ); - - if !transaction.is_system_transaction() { - // After Regolith, deposits are reported as using the actual gas used instead of - // all the gas. System transactions are not reported as using any gas. - cumulative_gas_used += result.gas_used() - } - - // Route the l1 cost and base fee to the appropriate optimism vaults - self.increment_account_balance( - optimism::l1_cost_recipient(), - l1_cost, - &mut post_state, - )?; - self.increment_account_balance( - optimism::base_fee_recipient(), - U256::from( - block - .base_fee_per_gas - .unwrap_or_default() - .saturating_mul(result.gas_used()), - ), - &mut post_state, - )?; - - // cast revm logs to reth logs - let logs: Vec = result.logs().into_iter().map(into_reth_log).collect(); - - // Push transaction changeset and calculate header bloom filter for receipt. - post_state.add_receipt(Receipt { - tx_type: transaction.tx_type(), - // Success flag was added in `EIP-658: Embedding transaction status code in - // receipts`. - success: result.is_success(), - cumulative_gas_used, - bloom: logs_bloom(logs.iter()), - logs, - deposit_nonce: Some(transaction.nonce()), - }); - post_state.finish_transition(); - } - - #[cfg(not(feature = "optimism"))] - { - // Execute transaction. - let ResultAndState { result, state } = self.transact(transaction, sender)?; - - // commit changes - self.commit_changes( - state, - self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), - &mut post_state, - ); - - cumulative_gas_used += result.gas_used(); + // commit changes + self.commit_changes( + block.number, + state, + self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), + &mut post_state, + ); - // cast revm logs to reth logs - let logs: Vec = result.logs().into_iter().map(into_reth_log).collect(); + // append gas used + cumulative_gas_used += result.gas_used(); - // Push transaction changeset and calculate header bloom filter for receipt. - post_state.add_receipt(Receipt { + // Push transaction changeset and calculate header bloom filter for receipt. + post_state.add_receipt( + block.number, + Receipt { tx_type: transaction.tx_type(), // Success flag was added in `EIP-658: Embedding transaction status code in // receipts`. success: result.is_success(), cumulative_gas_used, - bloom: logs_bloom(logs.iter()), - logs, - }); - post_state.finish_transition(); - } + // convert to reth log + logs: result.into_logs().into_iter().map(into_reth_log).collect(), + #[cfg(feature = "optimism")] + deposit_nonce: None, + }, + ); } Ok((post_state, cumulative_gas_used)) diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 6a99b020381..d7e4a973d71 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -33,3 +33,7 @@ pub use revm; /// Etereum DAO hardfork state change data. pub mod eth_dao_fork; + +/// Optimism-specific utilities for the executor +#[cfg(feature = "optimism")] +mod optimism; diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index 9f579d8b688..6d1185e0af0 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -27,20 +27,21 @@ pub struct L1BlockInfo { l1_fee_scalar: U256, } -impl L1BlockInfo { - /// Create a new L1 block info struct from a L2 block - pub fn new(block: &Block) -> Result { +impl TryFrom<&Block> for L1BlockInfo { + type Error = executor::BlockExecutionError; + + fn try_from(block: &Block) -> Result { let l1_block_contract = Address::from_str(L1_BLOCK_CONTRACT).unwrap(); let l1_info_tx_data = block .body .iter() .find(|tx| matches!(tx.kind(), TransactionKind::Call(to) if to == &l1_block_contract)) - .ok_or(executor::Error::L1BlockInfoError { + .ok_or(executor::BlockExecutionError::L1BlockInfoError { message: "could not find l1 block info tx in the L2 block".to_string(), }) .and_then(|tx| { - tx.input().get(4..).ok_or(executor::Error::L1BlockInfoError { + tx.input().get(4..).ok_or(executor::BlockExecutionError::L1BlockInfoError { message: "could not get l1 block info tx calldata bytes".to_string(), }) })?; @@ -56,30 +57,32 @@ impl L1BlockInfo { // + 32 bytes for the fee overhead // + 32 bytes for the fee scalar if l1_info_tx_data.len() != 184 { - return Err(executor::Error::L1BlockInfoError { + return Err(executor::BlockExecutionError::L1BlockInfoError { message: "unexpected l1 block info tx calldata length found".to_string(), }) } let l1_base_fee = U256::try_from_le_slice(&l1_info_tx_data[16..48]).ok_or( - executor::Error::L1BlockInfoError { + executor::BlockExecutionError::L1BlockInfoError { message: "could not convert l1 base fee".to_string(), }, )?; let l1_fee_overhead = U256::try_from_le_slice(&l1_info_tx_data[120..152]).ok_or( - executor::Error::L1BlockInfoError { + executor::BlockExecutionError::L1BlockInfoError { message: "could not convert l1 fee overhead".to_string(), }, )?; let l1_fee_scalar = U256::try_from_le_slice(&l1_info_tx_data[152..184]).ok_or( - executor::Error::L1BlockInfoError { + executor::BlockExecutionError::L1BlockInfoError { message: "could not convert l1 fee scalar".to_string(), }, )?; Ok(Self { l1_base_fee, l1_fee_overhead, l1_fee_scalar }) } +} +impl L1BlockInfo { /// Calculate the gas cost of a transaction based on L1 block data posted on L2 pub fn calculate_tx_l1_cost(&mut self, tx: &TransactionSigned) -> U256 { let rollup_data_gas_cost = U256::from(tx.input().iter().fold(0, |acc, byte| { diff --git a/crates/storage/provider/Cargo.toml b/crates/storage/provider/Cargo.toml index 07a2a9a02f2..0cde0e6c214 100644 --- a/crates/storage/provider/Cargo.toml +++ b/crates/storage/provider/Cargo.toml @@ -43,3 +43,8 @@ tempfile = "3.3" [features] test-utils = ["reth-rlp"] +optimism = [ + "reth-primitives/optimism", + "reth-interfaces/optimism", + "reth-revm-primitives/optimism" +] diff --git a/crates/storage/provider/src/test_utils/blocks.rs b/crates/storage/provider/src/test_utils/blocks.rs index 7df40d45e00..6752a0cfb72 100644 --- a/crates/storage/provider/src/test_utils/blocks.rs +++ b/crates/storage/provider/src/test_utils/blocks.rs @@ -125,6 +125,8 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, PostState) { topics: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2)], data: Bytes::default(), }], + #[cfg(feature = "optimism")] + deposit_nonce: None, }, ); @@ -168,6 +170,8 @@ fn block2(number: BlockNumber, parent_hash: H256) -> (SealedBlockWithSenders, Po topics: vec![H256::from_low_u64_be(3), H256::from_low_u64_be(4)], data: Bytes::default(), }], + #[cfg(feature = "optimism")] + deposit_nonce: None, }, ); diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index b319eb5c21f..5af49270ab7 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -637,13 +637,10 @@ impl FromRecoveredTransaction for PooledTransaction { Transaction::Eip2930(t) => U256::from(t.gas_price) * U256::from(t.gas_limit), Transaction::Eip1559(t) => U256::from(t.max_fee_per_gas) * U256::from(t.gas_limit), #[cfg(feature = "optimism")] - Transaction::Deposit(t) => { + Transaction::Deposit(_) => { // Gas price is always set to 0 for deposits in order to zero out ETH refunds, // because they already pay for their gas on L1. - let gas_price = U256::from(0); - let cost = U256::from(t.value); - let effective_gas_price = 0u128; - (cost, effective_gas_price) + U256::ZERO } }; let cost = gas_cost + U256::from(tx.value()); From c4d38de42fc8144a6925b0d0f1ba4e5d2591a8d1 Mon Sep 17 00:00:00 2001 From: clabby Date: Sat, 8 Jul 2023 21:19:50 -0400 Subject: [PATCH 33/43] Reimplement @merklefruit's changes --- crates/revm/src/executor.rs | 198 +++++++++++++++++++++++++++++------- crates/revm/src/lib.rs | 2 +- 2 files changed, 164 insertions(+), 36 deletions(-) diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index c2ed5c7e53f..7930495e997 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -28,6 +28,8 @@ use std::{ #[cfg(feature = "optimism")] use crate::optimism; +#[cfg(feature = "optimism")] +use reth_primitives::Log; /// Main block executor pub struct Executor @@ -236,45 +238,171 @@ where let mut cumulative_gas_used = 0; let mut post_state = PostState::with_tx_capacity(block.number, block.body.len()); for (transaction, sender) in block.body.iter().zip(senders) { - // The sum of the transaction’s gas limit, Tg, and the gas utilised in this block prior, - // must be no greater than the block’s gasLimit. - let block_available_gas = block.header.gas_limit - cumulative_gas_used; - if transaction.gas_limit() > block_available_gas { - return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas { - transaction_gas_limit: transaction.gas_limit(), - block_available_gas, + #[cfg(feature = "optimism")] + { + let db = self.db(); + let l1_cost = l1_block_info.calculate_tx_l1_cost(transaction); + + let sender_account = + db.load_account(sender).map_err(|_| BlockExecutionError::ProviderError)?; + let old_sender_info = to_reth_acc(&sender_account.info); + + if let Some(m) = transaction.mint() { + // Add balance to the caler account equal to the minted amount. + // Note: This is unconditional, and will not be reverted if the tx fails + // (unless the block can't be built at all due to gas limit constraints) + sender_account.info.balance += U256::from(m); } - .into()) - } - // Execute transaction. - let ResultAndState { result, state } = self.transact(transaction, sender)?; - // commit changes - self.commit_changes( - block.number, - state, - self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), - &mut post_state, - ); + // Check if the sender balance can cover the L1 cost. + // Deposits pay for their gas directly on L1 so they are exempt from the L2 tx fee. + if !transaction.is_deposit() { + if sender_account.info.balance.cmp(&l1_cost) == std::cmp::Ordering::Less { + return Err(BlockExecutionError::InsufficientFundsForL1Cost { + have: sender_account.info.balance.to::(), + want: l1_cost.to::(), + }) + } - // append gas used - cumulative_gas_used += result.gas_used(); + // Safely take l1_cost from sender (the rest will be deducted by the + // internal EVM execution and included in result.gas_used()) + // TODO: need to handle calls with `disable_balance_check` flag set? + sender_account.info.balance -= l1_cost; + } - // Push transaction changeset and calculate header bloom filter for receipt. - post_state.add_receipt( - block.number, - Receipt { - tx_type: transaction.tx_type(), - // Success flag was added in `EIP-658: Embedding transaction status code in - // receipts`. - success: result.is_success(), - cumulative_gas_used, - // convert to reth log - logs: result.into_logs().into_iter().map(into_reth_log).collect(), - #[cfg(feature = "optimism")] - deposit_nonce: None, - }, - ); + let new_sender_info = to_reth_acc(&sender_account.info); + post_state.change_account(block.number, sender, old_sender_info, new_sender_info); + + // Execute transaction. + let ResultAndState { result, state } = self.transact(transaction, sender)?; + + if transaction.is_deposit() && !result.is_success() { + // If the Deposited transaction failed, the deposit must still be included. + // In this case, we need to increment the sender nonce and disregard the + // state changes. The transaction is also recorded as using all gas. + let db = self.db(); + let sender_account = + db.load_account(sender).map_err(|_| BlockExecutionError::ProviderError)?; + let old_sender_info = to_reth_acc(&sender_account.info); + sender_account.info.nonce += 1; + let new_sender_info = to_reth_acc(&sender_account.info); + + post_state.change_account( + block.number, + sender, + old_sender_info, + new_sender_info, + ); + if !transaction.is_system_transaction() { + cumulative_gas_used += transaction.gas_limit(); + } + + post_state.add_receipt( + block.number, + Receipt { + tx_type: transaction.tx_type(), + success: false, + cumulative_gas_used, + logs: vec![], + deposit_nonce: Some(transaction.nonce()), + }, + ); + continue + } + + // commit changes + self.commit_changes( + block.number, + state, + self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), + &mut post_state, + ); + + if !transaction.is_system_transaction() { + // After Regolith, deposits are reported as using the actual gas used instead of + // all the gas. System transactions are not reported as using any gas. + cumulative_gas_used += result.gas_used() + } + + // Route the l1 cost and base fee to the appropriate optimism vaults + self.increment_account_balance( + block.number, + optimism::l1_cost_recipient(), + l1_cost, + &mut post_state, + )?; + self.increment_account_balance( + block.number, + optimism::base_fee_recipient(), + U256::from( + block + .base_fee_per_gas + .unwrap_or_default() + .saturating_mul(result.gas_used()), + ), + &mut post_state, + )?; + + // cast revm logs to reth logs + let logs: Vec = result.logs().into_iter().map(into_reth_log).collect(); + + // Push transaction changeset and calculate header bloom filter for receipt. + post_state.add_receipt( + block.number, + Receipt { + tx_type: transaction.tx_type(), + // Success flag was added in `EIP-658: Embedding transaction status code in + // receipts`. + success: result.is_success(), + cumulative_gas_used, + logs, + deposit_nonce: Some(transaction.nonce()), + }, + ); + } + + #[cfg(not(feature = "optimism"))] + { + // The sum of the transaction’s gas limit, Tg, and the gas utilised in this block + // prior, must be no greater than the block’s gasLimit. + let block_available_gas = block.header.gas_limit - cumulative_gas_used; + if transaction.gas_limit() > block_available_gas { + return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas { + transaction_gas_limit: transaction.gas_limit(), + block_available_gas, + } + .into()) + } + // Execute transaction. + let ResultAndState { result, state } = self.transact(transaction, sender)?; + + // commit changes + self.commit_changes( + block.number, + state, + self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), + &mut post_state, + ); + + // append gas used + cumulative_gas_used += result.gas_used(); + + // Push transaction changeset and calculate header bloom filter for receipt. + post_state.add_receipt( + block.number, + Receipt { + tx_type: transaction.tx_type(), + // Success flag was added in `EIP-658: Embedding transaction status code in + // receipts`. + success: result.is_success(), + cumulative_gas_used, + // convert to reth log + logs: result.into_logs().into_iter().map(into_reth_log).collect(), + #[cfg(feature = "optimism")] + deposit_nonce: None, + }, + ); + } } Ok((post_state, cumulative_gas_used)) diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index d7e4a973d71..612a0f6482c 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -36,4 +36,4 @@ pub mod eth_dao_fork; /// Optimism-specific utilities for the executor #[cfg(feature = "optimism")] -mod optimism; +pub mod optimism; From d7013553a173fea6ee88bf93415927c4cfce0f40 Mon Sep 17 00:00:00 2001 From: clabby Date: Sat, 8 Jul 2023 22:15:33 -0400 Subject: [PATCH 34/43] :broom: --- bin/reth/src/init.rs | 2 ++ crates/consensus/common/src/validation.rs | 5 +---- crates/net/eth-wire/src/types/receipts.rs | 10 +++++----- crates/primitives/src/chain/spec.rs | 7 ++----- crates/primitives/src/receipt.rs | 1 - crates/revm/src/executor.rs | 2 -- crates/revm/src/optimism.rs | 2 +- 7 files changed, 11 insertions(+), 18 deletions(-) diff --git a/bin/reth/src/init.rs b/bin/reth/src/init.rs index a70949dbbe9..4c45edbe25b 100644 --- a/bin/reth/src/init.rs +++ b/bin/reth/src/init.rs @@ -272,6 +272,8 @@ mod tests { fork_timestamps: ForkTimestamps { shanghai: None }, genesis_hash: None, paris_block_and_final_difficulty: None, + #[cfg(feature = "optimism")] + optimism: None, }); let db = create_test_rw_db(); diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index 2cca186e171..a1907dbe9e1 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -103,10 +103,7 @@ pub fn validate_transaction_regarding_header( Some(*chain_id) } #[cfg(feature = "optimism")] - Transaction::Deposit(TxDeposit { .. }) => { - // TODO: I believe the chain id should be None here, but have to confirm. - None - } + Transaction::Deposit(TxDeposit { .. }) => None, }; if let Some(chain_id) = chain_id { if chain_id != chain_spec.chain().id() { diff --git a/crates/net/eth-wire/src/types/receipts.rs b/crates/net/eth-wire/src/types/receipts.rs index 8122ec8b437..af40108aa99 100644 --- a/crates/net/eth-wire/src/types/receipts.rs +++ b/crates/net/eth-wire/src/types/receipts.rs @@ -119,11 +119,11 @@ mod test { data: hex!("0100ff")[..].into(), }, ], - bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(), - } - success: false, - #[cfg(feature = "optimism")] - deposit_nonce: None, + success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, + }, + bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(), }, ]]), }; diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index d3dfec9ddb5..732fb13abac 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -156,11 +156,8 @@ pub static OP_GOERLI: Lazy> = Lazy::new(|| { (Hardfork::MuirGlacier, ForkCondition::Block(0)), (Hardfork::Berlin, ForkCondition::Block(0)), (Hardfork::London, ForkCondition::Block(0)), - ( - Hardfork::Paris, - ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) }, - ), - (Hardfork::Shanghai, ForkCondition::Timestamp(0)), + (Hardfork::Paris, ForkCondition::Block(0)), + (Hardfork::Regolith, ForkCondition::Timestamp(1679079600)), ]), optimism: Some(OptimismConfig { eip_1559_elasticity: 10, eip_1559_denominator: 50 }), } diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 537d147ebff..85b286c1462 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -115,7 +115,6 @@ impl ReceiptWithBloom { success, cumulative_gas_used, logs, - #[cfg(feature = "optimism")] deposit_nonce: Some(deposit_nonce), } } diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 7930495e997..5d45ebc15b3 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -398,8 +398,6 @@ where cumulative_gas_used, // convert to reth log logs: result.into_logs().into_iter().map(into_reth_log).collect(), - #[cfg(feature = "optimism")] - deposit_nonce: None, }, ); } diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index 6d1185e0af0..deeb9dcc684 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -86,7 +86,7 @@ impl L1BlockInfo { /// Calculate the gas cost of a transaction based on L1 block data posted on L2 pub fn calculate_tx_l1_cost(&mut self, tx: &TransactionSigned) -> U256 { let rollup_data_gas_cost = U256::from(tx.input().iter().fold(0, |acc, byte| { - acc + if byte == &0x00 { ZERO_BYTE_COST } else { NON_ZERO_BYTE_COST } + acc + if *byte == 0x00 { ZERO_BYTE_COST } else { NON_ZERO_BYTE_COST } })); if tx.is_deposit() || rollup_data_gas_cost == U256::ZERO { From 171b13ba4fc0bbceb8c7772496418f51086bc8ac Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Sun, 9 Jul 2023 22:58:44 +0200 Subject: [PATCH 35/43] chore: removed useless import --- crates/revm/src/executor.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 5d45ebc15b3..9d95d7e6123 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -28,8 +28,6 @@ use std::{ #[cfg(feature = "optimism")] use crate::optimism; -#[cfg(feature = "optimism")] -use reth_primitives::Log; /// Main block executor pub struct Executor @@ -344,7 +342,7 @@ where )?; // cast revm logs to reth logs - let logs: Vec = result.logs().into_iter().map(into_reth_log).collect(); + let logs = result.logs().into_iter().map(into_reth_log).collect(); // Push transaction changeset and calculate header bloom filter for receipt. post_state.add_receipt( From 59e9deb78d14057625747f3e46f1b9deba69ec16 Mon Sep 17 00:00:00 2001 From: Roberto Bayardo Date: Tue, 18 Jul 2023 20:22:29 -0400 Subject: [PATCH 36/43] fix build failures with optimism feature --- crates/interfaces/Cargo.toml | 2 +- crates/primitives/src/transaction/mod.rs | 2 ++ crates/primitives/src/transaction/optimism.rs | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/crates/interfaces/Cargo.toml b/crates/interfaces/Cargo.toml index 8278bc412ce..2acd2f02bd1 100644 --- a/crates/interfaces/Cargo.toml +++ b/crates/interfaces/Cargo.toml @@ -55,4 +55,4 @@ secp256k1 = { workspace = true, features = [ [features] test-utils = ["tokio-stream/sync", "secp256k1", "rand/std_rng"] cli = ["clap"] -optimism = [] +optimism = ["reth-eth-wire/optimism"] diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 4e02a42499e..8a722cb3002 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -320,6 +320,8 @@ impl Transaction { Transaction::Legacy(tx) => tx.size(), Transaction::Eip2930(tx) => tx.size(), Transaction::Eip1559(tx) => tx.size(), + #[cfg(feature = "optimism")] + Transaction::Deposit(tx) => tx.size(), } } } diff --git a/crates/primitives/src/transaction/optimism.rs b/crates/primitives/src/transaction/optimism.rs index faad6908d48..546a6300b6b 100644 --- a/crates/primitives/src/transaction/optimism.rs +++ b/crates/primitives/src/transaction/optimism.rs @@ -1,6 +1,7 @@ use crate::{Address, Bytes, TransactionKind, H256}; use reth_codecs::{main_codec, Compact}; use reth_rlp::{Encodable, EMPTY_STRING_CODE}; +use std::mem; /// EIP-2718 transaction type selector. pub const DEPOSIT_TX_TYPE: u8 = 126; @@ -36,6 +37,19 @@ pub struct TxDeposit { } impl TxDeposit { + /// Calculates a heuristic for the in-memory size of the [TxDeposit] transaction. + #[inline] + pub fn size(&self) -> usize { + mem::size_of::() + // source_hash + mem::size_of::
() + // from + self.to.size() + // to + mem::size_of::>() + // mint + mem::size_of::() + // value + mem::size_of::() + // gas_limit + mem::size_of::() + // is_system_transaction + self.input.len() // input + } + /// 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 { From 2e56cbc044b05ab0944c3178e3c7697a1822f5d1 Mon Sep 17 00:00:00 2001 From: clabby Date: Tue, 18 Jul 2023 20:31:41 -0400 Subject: [PATCH 37/43] Fix feature flag --- crates/rpc/rpc/Cargo.toml | 3 +++ crates/rpc/rpc/src/eth/api/pending_block.rs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 11111cafbc6..8c804ce56f7 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -66,3 +66,6 @@ jsonrpsee = { version = "0.18", features = ["client"] } assert_matches = "1.5.0" tempfile = "3.5.0" reth-interfaces = { workspace = true, features = ["test-utils"] } + +[features] +optimism = ["reth-primitives/optimism"] diff --git a/crates/rpc/rpc/src/eth/api/pending_block.rs b/crates/rpc/rpc/src/eth/api/pending_block.rs index a427df3279a..d5fc4ce6295 100644 --- a/crates/rpc/rpc/src/eth/api/pending_block.rs +++ b/crates/rpc/rpc/src/eth/api/pending_block.rs @@ -110,6 +110,8 @@ impl PendingBlockEnv { success: result.is_success(), cumulative_gas_used, logs: result.logs().into_iter().map(into_reth_log).collect(), + #[cfg(feature = "optimism")] + deposit_nonce: None, }, ); // append transaction to the list of executed transactions From 1382ab26ee1e464694fa656f888f5e192bfce636 Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 9 Jul 2023 16:11:36 -0400 Subject: [PATCH 38/43] Start Engine API / block building updates --- crates/consensus/beacon/src/engine/mod.rs | 45 ++++++++-------- crates/payload/builder/src/payload.rs | 63 +++++++++++++++++++++-- crates/payload/builder/src/service.rs | 7 +++ 3 files changed, 89 insertions(+), 26 deletions(-) diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index 8be81f2bbdc..756fd683a45 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -966,27 +966,30 @@ where // forkchoiceState.headBlockHash and identified via buildProcessId value if // payloadAttributes is not null and the forkchoice state has been updated successfully. // The build process is specified in the Payload building section. - let attributes = PayloadBuilderAttributes::new(state.head_block_hash, attrs); - - // send the payload to the builder and return the receiver for the pending payload id, - // initiating payload job is handled asynchronously - let pending_payload_id = self.payload_builder.send_new_payload(attributes); - - // Client software MUST respond to this method call in the following way: - // { - // payloadStatus: { - // status: VALID, - // latestValidHash: forkchoiceState.headBlockHash, - // validationError: null - // }, - // payloadId: buildProcessId - // } - // - // if the payload is deemed VALID and the build process has begun. - OnForkChoiceUpdated::updated_with_pending_payload_id( - PayloadStatus::new(PayloadStatusEnum::Valid, Some(state.head_block_hash)), - pending_payload_id, - ) + match PayloadBuilderAttributes::try_new(state.head_block_hash, attrs) { + Ok(attributes) => { + // send the payload to the builder and return the receiver for the pending payload + // id, initiating payload job is handled asynchronously + let pending_payload_id = self.payload_builder.send_new_payload(attributes); + + // Client software MUST respond to this method call in the following way: + // { + // payloadStatus: { + // status: VALID, + // latestValidHash: forkchoiceState.headBlockHash, + // validationError: null + // }, + // payloadId: buildProcessId + // } + // + // if the payload is deemed VALID and the build process has begun. + OnForkChoiceUpdated::updated_with_pending_payload_id( + PayloadStatus::new(PayloadStatusEnum::Valid, Some(state.head_block_hash)), + pending_payload_id, + ) + } + Err(_) => OnForkChoiceUpdated::invalid_payload_attributes(), + } } /// When the Consensus layer receives a new block via the consensus gossip protocol, diff --git a/crates/payload/builder/src/payload.rs b/crates/payload/builder/src/payload.rs index 83cfe75ec1e..43ca63331b0 100644 --- a/crates/payload/builder/src/payload.rs +++ b/crates/payload/builder/src/payload.rs @@ -2,12 +2,17 @@ use reth_primitives::{Address, ChainSpec, Header, SealedBlock, Withdrawal, H256, U256}; use reth_revm_primitives::config::revm_spec_by_timestamp_after_merge; -use reth_rlp::Encodable; +use reth_rlp::{DecodeError, Encodable}; use reth_rpc_types::engine::{ ExecutionPayload, ExecutionPayloadEnvelope, PayloadAttributes, PayloadId, }; use revm_primitives::{BlockEnv, CfgEnv}; +#[cfg(feature = "optimism")] +use reth_primitives::TransactionSigned; +#[cfg(feature = "optimism")] +use reth_rlp::Decodable; + /// Contains the built payload. /// /// According to the [engine API specification](https://github.com/ethereum/execution-apis/blob/main/src/engine/README.md) the execution layer should build the initial version of the payload with an empty transaction set and then keep update it in order to maximize the revenue. @@ -88,6 +93,15 @@ pub struct PayloadBuilderAttributes { pub prev_randao: H256, /// Withdrawals for the generated payload pub withdrawals: Vec, + /// NoTxPool option for the generated payload + #[cfg(feature = "optimism")] + pub no_tx_pool: bool, + /// Transactions for the generated payload + #[cfg(feature = "optimism")] + pub transactions: Vec, + /// The gas limit for the generated payload + #[cfg(feature = "optimism")] + pub gas_limit: u64, } // === impl PayloadBuilderAttributes === @@ -96,16 +110,36 @@ impl PayloadBuilderAttributes { /// Creates a new payload builder for the given parent block and the attributes. /// /// Derives the unique [PayloadId] for the given parent and attributes - pub fn new(parent: H256, attributes: PayloadAttributes) -> Self { + pub fn try_new(parent: H256, attributes: PayloadAttributes) -> Result { + #[cfg(feature = "optimism")] + let transactions = attributes + .transactions + .as_ref() + .unwrap_or(&Vec::default()) + .iter() + .map(|tx| TransactionSigned::decode(&mut &tx[..])) + .collect::>()?; + + #[cfg(not(feature = "optimism"))] let id = payload_id(&parent, &attributes); - Self { + + #[cfg(feature = "optimism")] + let id = payload_id(&parent, &attributes, &transactions); + + Ok(Self { id, parent, timestamp: attributes.timestamp.as_u64(), suggested_fee_recipient: attributes.suggested_fee_recipient, prev_randao: attributes.prev_randao, withdrawals: attributes.withdrawals.unwrap_or_default(), - } + #[cfg(feature = "optimism")] + no_tx_pool: attributes.no_tx_pool.unwrap_or_default(), + #[cfg(feature = "optimism")] + transactions, + #[cfg(feature = "optimism")] + gas_limit: attributes.gas_limit.unwrap_or_default(), + }) } /// Returns the configured [CfgEnv] and [BlockEnv] for the targeted payload (that has the @@ -149,7 +183,11 @@ impl PayloadBuilderAttributes { /// Generates the payload id for the configured payload /// /// Returns an 8-byte identifier by hashing the payload components with sha256 hash. -pub(crate) fn payload_id(parent: &H256, attributes: &PayloadAttributes) -> PayloadId { +pub(crate) fn payload_id( + parent: &H256, + attributes: &PayloadAttributes, + #[cfg(feature = "optimism")] txs: &Vec, +) -> PayloadId { use sha2::Digest; let mut hasher = sha2::Sha256::new(); hasher.update(parent.as_bytes()); @@ -161,6 +199,21 @@ pub(crate) fn payload_id(parent: &H256, attributes: &PayloadAttributes) -> Paylo withdrawals.encode(&mut buf); hasher.update(buf); } + + #[cfg(feature = "optimism")] + { + let no_tx_pool = attributes.no_tx_pool.unwrap_or_default(); + if no_tx_pool || !txs.is_empty() { + hasher.update([no_tx_pool as u8]); + hasher.update(txs.len().to_be_bytes()); + txs.iter().for_each(|tx| hasher.update(tx.hash())); + } + + if let Some(gas_limit) = attributes.gas_limit { + hasher.update(gas_limit.to_be_bytes()); + } + } + let out = hasher.finalize(); PayloadId::new(out.as_slice()[..8].try_into().expect("sufficient length")) } diff --git a/crates/payload/builder/src/service.rs b/crates/payload/builder/src/service.rs index 67d6bbbaf73..588cef1a9cf 100644 --- a/crates/payload/builder/src/service.rs +++ b/crates/payload/builder/src/service.rs @@ -242,6 +242,13 @@ where if this.contains_payload(id) { warn!(%id, parent = ?attr.parent, "Payload job already in progress, ignoring."); } else { + // Don't start the payload job if there is no tx pool to pull from. + #[cfg(feature = "optimism")] + if attr.no_tx_pool { + let _ = tx.send(res); + continue + } + // no job for this payload yet, create one match this.generator.new_payload_job(attr) { Ok(job) => { From 4a21b13c82b8dda4de716fa6b3ea8320dc11de63 Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 16 Jul 2023 15:01:22 -0400 Subject: [PATCH 39/43] Rebase on upstream --- crates/consensus/beacon/src/engine/mod.rs | 9 +++++++++ crates/payload/basic/src/lib.rs | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index 756fd683a45..8acb26aab4d 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -625,6 +625,8 @@ where return Ok(OnForkChoiceUpdated::syncing()) } + // TODO(clabby): Allow proposers to reorg their own chain + let status = match self.blockchain.make_canonical(&state.head_block_hash) { Ok(outcome) => { if !outcome.is_already_canonical() { @@ -645,6 +647,13 @@ where return Ok(invalid_fcu_response) } + // TODO(clabby): We should only do this if the chainspec has an optimism + // config. Does this belong here? + #[cfg(feature = "optimism")] + if attrs.gas_limit.is_none() { + return Ok(OnForkChoiceUpdated::invalid_payload_attributes()) + } + // the CL requested to build a new payload on top of this new VALID head let payload_response = self.process_payload_attributes( attrs, diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index f49c3afa65d..0d9a86a6f89 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -755,8 +755,21 @@ where let base_fee = initialized_block_env.basefee.to::(); let block_number = initialized_block_env.number.to::(); + + #[cfg(feature(not = "optimism"))] let block_gas_limit: u64 = initialized_block_env.gas_limit.try_into().unwrap_or(u64::MAX); + #[cfg(feature = "optimism")] + let mut block_gas_limit: u64 = initialized_block_env.gas_limit.try_into().unwrap_or(u64::MAX); + + #[cfg(feature = "optimism")] + { + if let Some(gas_limit) = attributes.gas_limit { + block_gas_limit = gas_limit; + } + // TODO(clabby): configure the gas limit of pending blocks with the miner gas limit config when using optimism + } + let WithdrawalsOutcome { withdrawals_root, withdrawals } = commit_withdrawals( &mut db, &mut post_state, @@ -786,7 +799,10 @@ where gas_limit: block_gas_limit, difficulty: U256::ZERO, gas_used: 0, + #[cfg(feature(not = "optimism"))] extra_data: extra_data.into(), + #[cfg(feature = "optimism")] + extra_data: if chain_spec.optimism.is_none() { extra_data.into() } else { Default::default() }, }; let block = Block { header, body: vec![], ommers: vec![], withdrawals }; From 89be4a83c0e44a51841bd6443f07dd702dc87521 Mon Sep 17 00:00:00 2001 From: clabby Date: Sun, 16 Jul 2023 15:37:13 -0400 Subject: [PATCH 40/43] Fix build --- crates/payload/basic/Cargo.toml | 3 ++- crates/payload/basic/src/lib.rs | 4 ++-- crates/payload/builder/src/payload.rs | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/payload/basic/Cargo.toml b/crates/payload/basic/Cargo.toml index 3e7efd4810e..08d2278e972 100644 --- a/crates/payload/basic/Cargo.toml +++ b/crates/payload/basic/Cargo.toml @@ -35,5 +35,6 @@ optimism = [ "reth-primitives/optimism", "reth-revm/optimism", "reth-transaction-pool/optimism", - "reth-provider/optimism" + "reth-provider/optimism", + "reth-payload-builder/optimism" ] diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 0d9a86a6f89..b2b66bbcd9d 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -756,7 +756,7 @@ where let base_fee = initialized_block_env.basefee.to::(); let block_number = initialized_block_env.number.to::(); - #[cfg(feature(not = "optimism"))] + #[cfg(not(feature = "optimism"))] let block_gas_limit: u64 = initialized_block_env.gas_limit.try_into().unwrap_or(u64::MAX); #[cfg(feature = "optimism")] @@ -799,7 +799,7 @@ where gas_limit: block_gas_limit, difficulty: U256::ZERO, gas_used: 0, - #[cfg(feature(not = "optimism"))] + #[cfg(not(feature = "optimism"))] extra_data: extra_data.into(), #[cfg(feature = "optimism")] extra_data: if chain_spec.optimism.is_none() { extra_data.into() } else { Default::default() }, diff --git a/crates/payload/builder/src/payload.rs b/crates/payload/builder/src/payload.rs index 43ca63331b0..3f3d482703b 100644 --- a/crates/payload/builder/src/payload.rs +++ b/crates/payload/builder/src/payload.rs @@ -101,7 +101,7 @@ pub struct PayloadBuilderAttributes { pub transactions: Vec, /// The gas limit for the generated payload #[cfg(feature = "optimism")] - pub gas_limit: u64, + pub gas_limit: Option, } // === impl PayloadBuilderAttributes === @@ -138,7 +138,7 @@ impl PayloadBuilderAttributes { #[cfg(feature = "optimism")] transactions, #[cfg(feature = "optimism")] - gas_limit: attributes.gas_limit.unwrap_or_default(), + gas_limit: attributes.gas_limit, }) } From f84cf3350291478caf09ee5b6aac22d95ed9824f Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 17 Jul 2023 21:52:25 -0400 Subject: [PATCH 41/43] Move gas limit check --- crates/consensus/beacon/src/engine/mod.rs | 7 ------- crates/payload/basic/src/lib.rs | 9 +++++++-- crates/rpc/rpc-engine-api/src/engine_api.rs | 5 +++++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index 8acb26aab4d..3bc2f074e76 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -647,13 +647,6 @@ where return Ok(invalid_fcu_response) } - // TODO(clabby): We should only do this if the chainspec has an optimism - // config. Does this belong here? - #[cfg(feature = "optimism")] - if attrs.gas_limit.is_none() { - return Ok(OnForkChoiceUpdated::invalid_payload_attributes()) - } - // the CL requested to build a new payload on top of this new VALID head let payload_response = self.process_payload_attributes( attrs, diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index b2b66bbcd9d..52a248bb9c6 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -767,7 +767,8 @@ where if let Some(gas_limit) = attributes.gas_limit { block_gas_limit = gas_limit; } - // TODO(clabby): configure the gas limit of pending blocks with the miner gas limit config when using optimism + // TODO(clabby): configure the gas limit of pending blocks with the miner gas limit config + // when using optimism } let WithdrawalsOutcome { withdrawals_root, withdrawals } = commit_withdrawals( @@ -802,7 +803,11 @@ where #[cfg(not(feature = "optimism"))] extra_data: extra_data.into(), #[cfg(feature = "optimism")] - extra_data: if chain_spec.optimism.is_none() { extra_data.into() } else { Default::default() }, + extra_data: if chain_spec.optimism.is_none() { + extra_data.into() + } else { + Default::default() + }, }; let block = Block { header, body: vec![], ommers: vec![], withdrawals }; diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index 2a0581595f4..242381c5a79 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -107,6 +107,11 @@ where attrs.timestamp.as_u64(), attrs.withdrawals.is_some(), )?; + + #[cfg(feature = "optimism")] + if attrs.gas_limit.is_none() && self.inner.chain_spec.optimism.is_some() { + return Err(EngineApiError::MissingGasLimitInPayloadAttributes) + } } Ok(self.inner.beacon_consensus.fork_choice_updated(state, payload_attrs).await?) } From da7f9a54272aa50f7e340563e5eafbaceda1dd43 Mon Sep 17 00:00:00 2001 From: Andreas Bigger Date: Wed, 26 Jul 2023 18:29:11 -0400 Subject: [PATCH 42/43] Fix lints: --- crates/consensus/common/src/validation.rs | 3 --- crates/primitives/src/transaction/mod.rs | 1 - crates/rpc/rpc-types/src/eth/fee.rs | 5 +---- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index 575da865c4b..5fffb7f2a2e 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -10,9 +10,6 @@ use std::collections::{hash_map::Entry, HashMap}; #[cfg(feature = "optimism")] use reth_primitives::TxDeposit; -#[cfg(feature = "optimism")] -use reth_primitives::TxDeposit; - /// Validate header standalone pub fn validate_header_standalone( header: &SealedHeader, diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index be90ee01448..2377c81bd7f 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -470,7 +470,6 @@ impl Compact for Transaction { #[cfg(feature = "optimism")] Transaction::Deposit(tx) => { tx.to_compact(buf); - 126 } } identifier diff --git a/crates/rpc/rpc-types/src/eth/fee.rs b/crates/rpc/rpc-types/src/eth/fee.rs index 6463e7b433c..c7f93f6eaf6 100644 --- a/crates/rpc/rpc-types/src/eth/fee.rs +++ b/crates/rpc/rpc-types/src/eth/fee.rs @@ -12,10 +12,7 @@ pub struct TxGasAndReward { impl PartialOrd for TxGasAndReward { fn partial_cmp(&self, other: &Self) -> Option { - // compare only the reward - // see: - // - self.reward.partial_cmp(&other.reward) + Some(self.cmp(other)) } } From 19a69f07337c5d66b0f4d167d51903b5410d6030 Mon Sep 17 00:00:00 2001 From: Andreas Bigger Date: Wed, 26 Jul 2023 18:35:57 -0400 Subject: [PATCH 43/43] Fix duplicate branch arm --- crates/primitives/src/transaction/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 2377c81bd7f..7c56a807752 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -467,10 +467,6 @@ impl Compact for Transaction { Transaction::Deposit(tx) => { tx.to_compact(buf); } - #[cfg(feature = "optimism")] - Transaction::Deposit(tx) => { - tx.to_compact(buf); - } } identifier }