From 4c678a1050043ab5bb012774f3b63fac2ff1fe35 Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Wed, 25 Jan 2023 07:20:02 +0100 Subject: [PATCH] Withdrawals processor to clear empty accounts (#4995) Signed-off-by: Jason Frame --- .../mainnet/MainnetTransactionProcessor.java | 8 +---- .../mainnet/WithdrawalsProcessor.java | 1 + .../mainnet/WithdrawalsProcessorTest.java | 29 ++++++++++++++++--- .../besu/evm/worldstate/WorldUpdater.java | 7 +++++ 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index f9c731744a..8232f326bb 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -45,7 +45,6 @@ import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.ArrayDeque; -import java.util.ArrayList; import java.util.Deque; import java.util.HashSet; import java.util.List; @@ -463,7 +462,7 @@ public class MainnetTransactionProcessor { initialFrame.getSelfDestructs().forEach(worldState::deleteAccount); if (clearEmptyAccounts) { - clearAccountsThatAreEmpty(worldState); + worldState.clearAccountsThatAreEmpty(); } if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { @@ -489,11 +488,6 @@ public class MainnetTransactionProcessor { return transactionValidator; } - private static void clearAccountsThatAreEmpty(final WorldUpdater worldState) { - new ArrayList<>(worldState.getTouchedAccounts()) - .stream().filter(Account::isEmpty).forEach(a -> worldState.deleteAccount(a.getAddress())); - } - protected void process(final MessageFrame frame, final OperationTracer operationTracer) { final AbstractMessageProcessor executor = getMessageProcessor(frame.getType()); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsProcessor.java index 30db999ef7..b91f81208a 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsProcessor.java @@ -28,6 +28,7 @@ public class WithdrawalsProcessor { final EvmAccount account = worldUpdater.getOrCreate(withdrawal.getAddress()); account.getMutable().incrementBalance(withdrawal.getAmount().getAsWei()); } + worldUpdater.clearAccountsThatAreEmpty(); worldUpdater.commit(); } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsProcessorTest.java index 570668c0d7..d244782461 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalsProcessorTest.java @@ -35,7 +35,7 @@ import org.junit.jupiter.api.Test; class WithdrawalsProcessorTest { @Test - void defaultProcessor_shouldProcessEmptyWithdrawalsWithoutChangingWorldState() { + void shouldProcessEmptyWithdrawalsWithoutChangingWorldState() { final MutableWorldState worldState = createWorldStateWithAccounts(List.of(entry("0x1", 1), entry("0x2", 2), entry("0x3", 3))); final MutableWorldState originalState = worldState.copy(); @@ -51,7 +51,7 @@ class WithdrawalsProcessorTest { } @Test - void defaultProcessor_shouldProcessWithdrawalsUpdatingExistingAccountsBalance() { + void shouldProcessWithdrawalsUpdatingExistingAccountsBalance() { final MutableWorldState worldState = createWorldStateWithAccounts(List.of(entry("0x1", 1), entry("0x2", 2), entry("0x3", 3))); final WorldUpdater updater = worldState.updater(); @@ -79,7 +79,7 @@ class WithdrawalsProcessorTest { } @Test - void defaultProcessor_shouldProcessWithdrawalsUpdatingEmptyAccountsBalance() { + void shouldProcessWithdrawalsUpdatingEmptyAccountsBalance() { final MutableWorldState worldState = createWorldStateWithAccounts(Collections.emptyList()); final WorldUpdater updater = worldState.updater(); @@ -104,6 +104,27 @@ class WithdrawalsProcessorTest { .isEqualTo(GWei.of(200).getAsWei()); } + @Test + void shouldDeleteEmptyAccounts() { + final MutableWorldState worldState = createWorldStateWithAccounts(List.of(entry("0x1", 0))); + final WorldUpdater updater = worldState.updater(); + + final List withdrawals = + List.of( + new Withdrawal( + UInt64.valueOf(100), UInt64.valueOf(1000), Address.fromHexString("0x1"), GWei.ZERO), + new Withdrawal( + UInt64.valueOf(200), + UInt64.valueOf(2000), + Address.fromHexString("0x2"), + GWei.ZERO)); + final WithdrawalsProcessor withdrawalsProcessor = new WithdrawalsProcessor(); + withdrawalsProcessor.processWithdrawals(withdrawals, updater); + + assertThat(worldState.get(Address.fromHexString("0x1"))).isNull(); + assertThat(worldState.get(Address.fromHexString("0x2"))).isNull(); + } + private MutableWorldState createWorldStateWithAccounts( final List> accountBalances) { final MutableWorldState worldState = InMemoryKeyValueStorageProvider.createInMemoryWorldState(); @@ -111,7 +132,7 @@ class WithdrawalsProcessorTest { accountBalances.forEach( entry -> updater.createAccount( - Address.fromHexString(entry.getKey()), 1, Wei.of(entry.getValue()))); + Address.fromHexString(entry.getKey()), 0, Wei.of(entry.getValue()))); updater.commit(); return worldState; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java index b32594b1e2..a9b012facb 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.evm.account.EvmAccount; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.frame.MessageFrame; +import java.util.ArrayList; import java.util.Collection; import java.util.Optional; @@ -145,4 +146,10 @@ public interface WorldUpdater extends MutableWorldView { * @return The parent WorldUpdater if this wraps another one, empty otherwise */ Optional parentUpdater(); + + /** Clears any accounts that are empty */ + default void clearAccountsThatAreEmpty() { + new ArrayList<>(getTouchedAccounts()) + .stream().filter(Account::isEmpty).forEach(a -> deleteAccount(a.getAddress())); + } }