diff --git a/crates/executor/src/executor.rs b/crates/executor/src/executor.rs index 48fd8902ec..8312d7d378 100644 --- a/crates/executor/src/executor.rs +++ b/crates/executor/src/executor.rs @@ -52,6 +52,7 @@ impl AccountInfoChangeSet { tx: &TX, address: Address, tx_index: u64, + has_state_clear_eip: bool, ) -> Result<(), DbError> { match self { AccountInfoChangeSet::Changed { old, new } => { @@ -64,6 +65,11 @@ impl AccountInfoChangeSet { tx.put::(address, new)?; } AccountInfoChangeSet::Created { new } => { + // Ignore account that are created empty and state clear (SpuriousDragon) hardfork + // is activated. + if has_state_clear_eip && new.is_empty() { + return Ok(()) + } tx.put::( tx_index, AccountBeforeTx { address, info: None }, @@ -829,7 +835,7 @@ mod tests { // check Changed changeset AccountInfoChangeSet::Changed { new: acc1, old: acc2 } - .apply_to_db(&tx, address, tx_num) + .apply_to_db(&tx, address, tx_num, true) .unwrap(); assert_eq!( tx.get::(tx_num), @@ -837,7 +843,9 @@ mod tests { ); assert_eq!(tx.get::(address), Ok(Some(acc1))); - AccountInfoChangeSet::Created { new: acc1 }.apply_to_db(&tx, address, tx_num).unwrap(); + AccountInfoChangeSet::Created { new: acc1 } + .apply_to_db(&tx, address, tx_num, true) + .unwrap(); assert_eq!( tx.get::(tx_num), Ok(Some(AccountBeforeTx { address, info: None })) @@ -847,7 +855,9 @@ mod tests { // delete old value, as it is dupsorted tx.delete::(tx_num, None).unwrap(); - AccountInfoChangeSet::Destroyed { old: acc2 }.apply_to_db(&tx, address, tx_num).unwrap(); + AccountInfoChangeSet::Destroyed { old: acc2 } + .apply_to_db(&tx, address, tx_num, true) + .unwrap(); assert_eq!(tx.get::(address), Ok(None)); assert_eq!( tx.get::(tx_num), diff --git a/crates/primitives/src/account.rs b/crates/primitives/src/account.rs index 6d96a2c510..eb77388a3f 100644 --- a/crates/primitives/src/account.rs +++ b/crates/primitives/src/account.rs @@ -1,4 +1,4 @@ -use crate::{H256, U256}; +use crate::{H256, KECCAK_EMPTY, U256}; use reth_codecs::{main_codec, Compact}; /// An Ethereum account. @@ -18,6 +18,17 @@ impl Account { pub fn has_bytecode(&self) -> bool { self.bytecode_hash.is_some() } + + /// After SpuriousDragon empty account is defined as account with nonce == 0 && balance == 0 && + /// bytecode = None. + pub fn is_empty(&self) -> bool { + let is_bytecode_empty = match self.bytecode_hash { + None => true, + Some(hash) => hash == KECCAK_EMPTY, + }; + + self.nonce == 0 && self.balance == U256::ZERO && is_bytecode_empty + } } #[cfg(test)] diff --git a/crates/stages/src/stages/execution.rs b/crates/stages/src/stages/execution.rs index 48ad2f0b11..94d5c31fb9 100644 --- a/crates/stages/src/stages/execution.rs +++ b/crates/stages/src/stages/execution.rs @@ -3,7 +3,7 @@ use crate::{ Stage, StageError, StageId, UnwindInput, UnwindOutput, }; use reth_db::{ - cursor::{DbCursorRO, DbCursorRW}, + cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO}, database::Database, models::{BlockNumHash, StoredBlockBody, TransitionIdAddress}, tables, @@ -14,7 +14,8 @@ use reth_executor::{ revm_wrap::{State, SubState}, }; use reth_primitives::{ - Address, ChainSpec, Header, StorageEntry, TransactionSignedEcRecovered, H256, MAINNET, U256, + Address, ChainSpec, Hardfork, Header, StorageEntry, TransactionSignedEcRecovered, H256, + MAINNET, U256, }; use reth_provider::LatestStateProviderRef; use std::fmt::Debug; @@ -199,15 +200,19 @@ impl Stage for ExecutionStage { handle.join().expect("Expects for thread to not panic") }) .map_err(|error| StageError::ExecutionError { block: header.number, error })?; - block_change_patches.push(changeset); + block_change_patches.push((changeset, num)); } // Get last tx count so that we can know amount of transaction in the block. let mut current_transition_id = tx.get_block_transition(last_block)?; info!(target: "sync::stages::execution", current_transition_id, blocks = block_change_patches.len(), "Inserting execution results"); + let spurious_dragon_activation = + self.chain_spec.fork_block(Hardfork::SpuriousDragon).unwrap_or_default(); + // apply changes to plain database. - for results in block_change_patches.into_iter() { + for (results, block_number) in block_change_patches.into_iter() { + let spurious_dragon_active = block_number >= spurious_dragon_activation; // insert state change set for result in results.changesets.into_iter() { for (address, account_change_set) in result.changeset.into_iter() { @@ -215,7 +220,12 @@ impl Stage for ExecutionStage { // apply account change to db. Updates AccountChangeSet and PlainAccountState // tables. trace!(target: "sync::stages::execution", ?address, current_transition_id, ?account, wipe_storage, "Applying account changeset"); - account.apply_to_db(&**tx, address, current_transition_id)?; + account.apply_to_db( + &**tx, + address, + current_transition_id, + spurious_dragon_active, + )?; let storage_id = TransitionIdAddress((current_transition_id, address)); @@ -293,7 +303,12 @@ impl Stage for ExecutionStage { // we are sure that block reward index is present. for (address, changeset) in block_reward_changeset.into_iter() { trace!(target: "sync::stages::execution", ?address, current_transition_id, "Applying block reward"); - changeset.apply_to_db(&**tx, address, current_transition_id)?; + changeset.apply_to_db( + &**tx, + address, + current_transition_id, + spurious_dragon_active, + )?; } current_transition_id += 1; } @@ -341,7 +356,6 @@ impl Stage for ExecutionStage { // revert all changes to PlainState for (_, changeset) in account_changeset_batch.into_iter().rev() { - // TODO refactor in db fn called tx.aplly_account_changeset if let Some(account_info) = changeset.info { tx.put::(changeset.address, account_info)?; } else { @@ -360,12 +374,15 @@ impl Stage for ExecutionStage { .collect::, _>>()?; // revert all changes to PlainStorage + let mut plain_storage_cursor = tx.cursor_dup_write::()?; + for (key, storage) in storage_changeset_batch.into_iter().rev() { let address = key.address(); - tx.put::(address, storage)?; - if storage.value == U256::ZERO { - // delete value that is zero - tx.delete::(address, Some(storage))?; + if plain_storage_cursor.seek_by_key_subkey(address, storage.key)?.is_some() { + plain_storage_cursor.delete_current()?; + } + if storage.value != U256::ZERO { + plain_storage_cursor.upsert(address, storage)?; } } @@ -375,6 +392,7 @@ impl Stage for ExecutionStage { if transition_id < from_transition_rev { break } + // delete all changesets tx.delete::(transition_id, None)?; } @@ -383,6 +401,7 @@ impl Stage for ExecutionStage { if key.transition_id() < from_transition_rev { break } + // delete all changesets tx.delete::(key, None)?; } @@ -651,7 +670,6 @@ mod tests { assert_eq!( plain_accounts, vec![ - (H160::zero(), Account::default()), ( beneficiary_address, Account { @@ -678,13 +696,6 @@ mod tests { assert_eq!( account_changesets, vec![ - ( - 1, - AccountBeforeTx { - address: H160(hex!("0000000000000000000000000000000000000000")), - info: None - } - ), (1, AccountBeforeTx { address: destroyed_address, info: Some(destroyed_info) }), (1, AccountBeforeTx { address: beneficiary_address, info: None }), (1, AccountBeforeTx { address: caller_address, info: Some(caller_info) }),