diff --git a/bin/programs/client/src/l2/executor/canyon.rs b/bin/programs/client/src/l2/executor/canyon.rs index 1894159bf..5687b26c9 100644 --- a/bin/programs/client/src/l2/executor/canyon.rs +++ b/bin/programs/client/src/l2/executor/canyon.rs @@ -1,5 +1,6 @@ //! Contains logic specific to Canyon hardfork activation. +use alloy_consensus::Header; use alloy_primitives::{address, b256, hex, Address, Bytes, B256}; use kona_derive::types::RollupConfig; use revm::{ @@ -24,14 +25,14 @@ pub(crate) fn ensure_create2_deployer_canyon( db: &mut State, config: &RollupConfig, timestamp: u64, + parent_header: &Header, ) -> Result<(), DB::Error> where DB: revm::Database, { // If the canyon hardfork is active at the current timestamp, and it was not active at the - // previous block timestamp (heuristically, block time is not perfectly constant at 2s), and the - // chain is an optimism chain, then we need to force-deploy the create2 deployer contract. - if config.is_canyon_active(timestamp) && !config.is_canyon_active(timestamp.saturating_sub(2)) { + // previous block timestamp, then we need to force-deploy the create2 deployer contract. + if config.is_canyon_active(timestamp) && !config.is_canyon_active(parent_header.timestamp) { // Load the create2 deployer account from the cache. let acc = db.load_cache_account(CREATE_2_DEPLOYER_ADDR)?; diff --git a/bin/programs/client/src/l2/executor/mod.rs b/bin/programs/client/src/l2/executor/mod.rs index c4d34421d..67d18f199 100644 --- a/bin/programs/client/src/l2/executor/mod.rs +++ b/bin/programs/client/src/l2/executor/mod.rs @@ -30,6 +30,8 @@ pub(crate) use canyon::ensure_create2_deployer_canyon; mod util; pub(crate) use util::{logs_bloom, wrap_receipt_with_bloom}; +use self::util::{extract_tx_gas_limit, is_system_transaction}; + /// The block executor for the L2 client program. Operates off of a [TrieDB] backed [State], /// allowing for stateless block execution of OP Stack blocks. #[derive(Debug)] @@ -103,8 +105,12 @@ where )?; // Ensure that the create2 contract is deployed upon transition to the Canyon hardfork. - // TODO(clabby): Pass parent header - ensure_create2_deployer_canyon(&mut self.state, self.config.as_ref(), payload.timestamp)?; + ensure_create2_deployer_canyon( + &mut self.state, + self.config.as_ref(), + payload.timestamp, + &self.parent_header, + )?; // Construct the EVM with the given configuration. // TODO(clabby): Accelerate precompiles w/ custom precompile handler. @@ -125,11 +131,12 @@ where // The sum of the transaction’s gas limit, Tg, and the gas utilized in this block prior, // must be no greater than the block’s gasLimit. // let block_available_gas = gas_limit - cumulative_gas_used; - // if transaction.gas_limit() > block_available_gas - // && (is_regolith || !transaction.is_system_transaction()) - // { - // anyhow::bail!("Transaction gas limit exceeds block gas limit") - // } + let block_available_gas = (gas_limit - cumulative_gas_used) as u128; + if extract_tx_gas_limit(&transaction) > block_available_gas && + (is_regolith || !is_system_transaction(&transaction)) + { + anyhow::bail!("Transaction gas limit exceeds block gas limit") + } // Reject any EIP-4844 transactions. if matches!(transaction, OpTxEnvelope::Eip4844(_)) { @@ -277,8 +284,7 @@ where /// The active [SpecId] for the executor. fn revm_spec_id(&self, timestamp: u64) -> SpecId { if self.config.is_fjord_active(timestamp) { - // TODO(clabby): Replace w/ Fjord Spec ID, once in a revm release. - SpecId::ECOTONE + SpecId::FJORD } else if self.config.is_ecotone_active(timestamp) { SpecId::ECOTONE } else if self.config.is_canyon_active(timestamp) { diff --git a/bin/programs/client/src/l2/executor/util.rs b/bin/programs/client/src/l2/executor/util.rs index b6711166f..f11f1c9cb 100644 --- a/bin/programs/client/src/l2/executor/util.rs +++ b/bin/programs/client/src/l2/executor/util.rs @@ -1,7 +1,8 @@ //! Contains utilities for the L2 executor. +use alloy_consensus::Transaction; use alloy_primitives::{Bloom, Log}; -use op_alloy_consensus::{OpReceiptEnvelope, OpReceiptWithBloom, OpTxType}; +use op_alloy_consensus::{OpReceiptEnvelope, OpReceiptWithBloom, OpTxEnvelope, OpTxType}; /// Compute the logs bloom filter for the given logs. pub(crate) fn logs_bloom<'a>(logs: impl IntoIterator) -> Bloom { @@ -28,3 +29,23 @@ pub(crate) fn wrap_receipt_with_bloom( OpTxType::Deposit => OpReceiptEnvelope::Deposit(receipt), } } + +/// Extract the gas limit from an [OpTxEnvelope]. +pub(crate) fn extract_tx_gas_limit(tx: &OpTxEnvelope) -> u128 { + match tx { + OpTxEnvelope::Legacy(tx) => tx.tx().gas_limit, + OpTxEnvelope::Eip2930(tx) => tx.tx().gas_limit, + OpTxEnvelope::Eip1559(tx) => tx.tx().gas_limit, + OpTxEnvelope::Eip4844(tx) => tx.tx().gas_limit(), + OpTxEnvelope::Deposit(tx) => tx.gas_limit, + _ => unreachable!(), + } +} + +/// Returns whether or not an [OpTxEnvelope] is a system transaction. +pub(crate) fn is_system_transaction(tx: &OpTxEnvelope) -> bool { + match tx { + OpTxEnvelope::Deposit(tx) => tx.is_system_transaction, + _ => false, + } +} diff --git a/crates/mpt/src/db/mod.rs b/crates/mpt/src/db/mod.rs index 12bafea60..70dddbc5f 100644 --- a/crates/mpt/src/db/mod.rs +++ b/crates/mpt/src/db/mod.rs @@ -213,13 +213,7 @@ where trie_account.encode(&mut account_buf); // Insert or update the account in the trie. - if let Some(account_rlp_ref) = self.root_node.open(&account_path, &self.fetcher)? { - // Update the existing account in the trie. - *account_rlp_ref = account_buf.into(); - } else { - // Insert the new account into the trie. - self.root_node.insert(&account_path, account_buf.into(), &self.fetcher)?; - } + self.root_node.insert(&account_path, account_buf.into(), &self.fetcher)?; } Ok(()) @@ -247,16 +241,11 @@ where // Insert or update the storage slot in the trie. let hashed_slot_key = Nibbles::unpack(keccak256(index.to_be_bytes::<32>().as_slice())); - if let Some(storage_slot_rlp) = storage_root.open(&hashed_slot_key, fetcher)? { - if value.is_zero() { - // If the storage slot is being set to zero, prune it from the trie. - storage_root.delete(&hashed_slot_key, fetcher)?; - } else { - // Otherwise, update the storage slot. - *storage_slot_rlp = rlp_buf.into(); - } + if value.is_zero() { + // If the storage slot is being set to zero, prune it from the trie. + storage_root.delete(&hashed_slot_key, fetcher)?; } else { - // If the storage slot does not exist, insert it. + // Otherwise, update the storage slot. storage_root.insert(&hashed_slot_key, rlp_buf.into(), fetcher)?; } diff --git a/crates/mpt/src/node.rs b/crates/mpt/src/node.rs index 112ab8fae..11b4ff894 100644 --- a/crates/mpt/src/node.rs +++ b/crates/mpt/src/node.rs @@ -114,7 +114,7 @@ impl TrieNode { /// length. Alternatively, if the [TrieNode] is a [TrieNode::Blinded] node already, it /// is left as-is. pub fn blind(&mut self) { - if self.length() >= 32 && !matches!(self, TrieNode::Blinded { .. }) { + if self.length() >= B256::ZERO.len() && !matches!(self, TrieNode::Blinded { .. }) { let mut rlp_buf = Vec::with_capacity(self.length()); self.encode(&mut rlp_buf); *self = TrieNode::Blinded { commitment: keccak256(rlp_buf) } @@ -362,14 +362,10 @@ impl TrieNode { return Ok(()); } - if remaining_nibbles.starts_with(prefix) { - node.delete_inner(path, nibble_offset + prefix.len(), fetcher)?; + node.delete_inner(path, nibble_offset + prefix.len(), fetcher)?; - // Simplify extension if possible after the deletion - self.collapse_if_possible(fetcher) - } else { - anyhow::bail!("Key does not exist in trie (extension node mismatch)") - } + // Simplify extension if possible after the deletion + self.collapse_if_possible(fetcher) } TrieNode::Branch { stack } => { let branch_nibble = remaining_nibbles[0] as usize; @@ -454,9 +450,6 @@ impl TrieNode { } _ => {} }; - } else if non_empty_children.is_empty() { - // If all children are empty, convert the branch into an empty node. - *self = TrieNode::Empty; } } _ => {} @@ -541,7 +534,7 @@ impl TrieNode { /// than a [B256]. fn blinded_length(&self) -> usize { let encoded_len = self.length(); - if encoded_len >= 32 && !matches!(self, TrieNode::Blinded { .. }) { + if encoded_len >= B256::ZERO.len() && !matches!(self, TrieNode::Blinded { .. }) { B256::ZERO.length() } else { encoded_len @@ -572,15 +565,12 @@ impl Encodable for TrieNode { // In branch nodes, if an element is longer than 32 bytes in length, it is blinded. // Assuming we have an open trie node, we must re-hash the elements // that are longer than 32 bytes in length. - let blinded_nodes = stack - .iter() - .cloned() - .map(|mut node| { - node.blind(); - node - }) - .collect::>(); - blinded_nodes.encode(out); + Header { list: true, payload_length: self.payload_length() }.encode(out); + stack.iter().for_each(|node| { + let mut blinded = node.clone(); + blinded.blind(); + blinded.encode(out); + }); } } }