From ab291ad370df9880a7a004dafcbdcfbf952db3cf Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 13 Sep 2023 09:26:04 -0600 Subject: [PATCH 1/3] Merge MutableAccount and EVMAccount (#5863) Merge MutableAccount and EVMAccount functionalities by removing EVMAccount, all calls to getMutable, and the WrappedEVMAccount that was wrapping non-EVMAccounts in a mutable fashion. Instead, use a MutableAccount in all cases an EVMAccount would have been used. This also tends to reduce a level of layering in many places. Signed-off-by: Danno Ferrin --- .../blockcreation/AbstractBlockCreator.java | 18 +-- .../vm/EntriesFromIntegrationTest.java | 12 +- .../TransientStorageOperationBenchmark.java | 2 +- .../besu/ethereum/bonsai/BonsaiAccount.java | 29 ++-- .../BonsaiWorldStateUpdateAccumulator.java | 118 ++++++-------- .../besu/ethereum/chain/GenesisState.java | 6 +- .../mainnet/ClassicBlockProcessor.java | 5 +- .../mainnet/MainnetBlockProcessor.java | 6 +- .../mainnet/MainnetProtocolSpecs.java | 4 +- .../mainnet/MainnetTransactionProcessor.java | 14 +- .../mainnet/ParentBeaconBlockRootHelper.java | 6 +- .../mainnet/WithdrawalsProcessor.java | 6 +- ...PrivateGroupRehydrationBlockProcessor.java | 5 +- .../privacy/PrivateStateGenesisAllocator.java | 11 +- .../privacy/PrivateTransactionProcessor.java | 7 +- .../PrivateMigrationBlockProcessor.java | 5 +- .../ethereum/vm/DebugOperationTracer.java | 7 +- ...efaultMutablePrivateWorldStateUpdater.java | 15 +- .../ethereum/core/BlockDataGenerator.java | 48 ++---- .../besu/ethereum/bonsai/LogRollingTests.java | 25 ++- .../MainnetTransactionProcessorTest.java | 11 +- .../mainnet/PrivacyBlockProcessorTest.java | 11 +- .../ethereum/vm/DebugOperationTracerTest.java | 31 ++-- .../DefaultMutableWorldStateTest.java | 152 +++++++++--------- .../hyperledger/besu/evmtool/T8nExecutor.java | 1 - .../ReferenceTestWorldState.java | 2 +- .../besu/evm/account/EvmAccount.java | 28 ---- .../besu/evm/account/MutableAccount.java | 5 + .../besu/evm/fluent/SimpleAccount.java | 30 +++- .../besu/evm/fluent/SimpleWorld.java | 9 +- .../operation/AbstractCreateOperation.java | 2 +- .../besu/evm/operation/SStoreOperation.java | 3 +- .../evm/operation/SelfDestructOperation.java | 5 +- .../processor/ContractCreationProcessor.java | 7 +- .../evm/processor/MessageCallProcessor.java | 15 +- .../evm/worldstate/AbstractWorldUpdater.java | 15 +- .../evm/worldstate/UpdateTrackingAccount.java | 30 +++- .../besu/evm/worldstate/WorldUpdater.java | 15 +- .../evm/worldstate/WrappedEvmAccount.java | 119 -------------- .../AbstractCreateOperationTest.java | 13 +- .../evm/operations/Create2OperationTest.java | 28 ++-- .../evm/operations/CreateOperationTest.java | 46 ++---- .../operations/ExtCodeHashOperationTest.java | 8 +- .../evm/operations/SStoreOperationTest.java | 2 +- .../operations/SelfDestructOperationTest.java | 17 +- .../evm/operations/TStoreOperationTest.java | 2 +- .../besu/evm/testutils/TestCodeExecutor.java | 5 +- .../besu/evm/toy/EvmToyCommand.java | 4 +- .../hyperledger/besu/evm/toy/ToyAccount.java | 30 +++- .../hyperledger/besu/evm/toy/ToyWorld.java | 8 +- .../tracing/ExtendedOperationTracerTest.java | 6 +- 51 files changed, 401 insertions(+), 608 deletions(-) delete mode 100644 evm/src/main/java/org/hyperledger/besu/evm/account/EvmAccount.java delete mode 100644 evm/src/main/java/org/hyperledger/besu/evm/worldstate/WrappedEvmAccount.java diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java index cde0b95bbb7..80037c8c6c1 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java @@ -50,7 +50,7 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; -import org.hyperledger.besu.evm.account.EvmAccount; +import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.plugin.services.exception.StorageException; import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException; @@ -190,10 +190,10 @@ protected BlockCreationResult createBlock( final List ommers = maybeOmmers.orElse(selectOmmers()); - if (maybeParentBeaconBlockRoot.isPresent()) { - ParentBeaconBlockRootHelper.storeParentBeaconBlockRoot( - disposableWorldState.updater(), timestamp, maybeParentBeaconBlockRoot.get()); - } + maybeParentBeaconBlockRoot.ifPresent( + bytes32 -> + ParentBeaconBlockRootHelper.storeParentBeaconBlockRoot( + disposableWorldState.updater(), timestamp, bytes32)); throwIfStopped(); final TransactionSelectionResults transactionResults = @@ -466,9 +466,9 @@ boolean rewardBeneficiary( .getBlockProcessor() .getCoinbaseReward(blockReward, header.getNumber(), ommers.size()); final WorldUpdater updater = worldState.updater(); - final EvmAccount beneficiary = updater.getOrCreate(miningBeneficiary); + final MutableAccount beneficiary = updater.getOrCreate(miningBeneficiary); - beneficiary.getMutable().incrementBalance(coinbaseReward); + beneficiary.incrementBalance(coinbaseReward); for (final BlockHeader ommerHeader : ommers) { if (ommerHeader.getNumber() - header.getNumber() > MAX_GENERATION) { LOG.trace( @@ -479,12 +479,12 @@ boolean rewardBeneficiary( return false; } - final EvmAccount ommerCoinbase = updater.getOrCreate(ommerHeader.getCoinbase()); + final MutableAccount ommerCoinbase = updater.getOrCreate(ommerHeader.getCoinbase()); final Wei ommerReward = protocolSpec .getBlockProcessor() .getOmmerReward(blockReward, header.getNumber(), ommerHeader.getNumber()); - ommerCoinbase.getMutable().incrementBalance(ommerReward); + ommerCoinbase.incrementBalance(ommerReward); } updater.commit(); diff --git a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/EntriesFromIntegrationTest.java b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/EntriesFromIntegrationTest.java index e139d69352d..fcfd71f206e 100644 --- a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/EntriesFromIntegrationTest.java +++ b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/EntriesFromIntegrationTest.java @@ -39,7 +39,7 @@ public class EntriesFromIntegrationTest { public void shouldCollectStateEntries() { final MutableWorldState worldState = createInMemoryWorldStateArchive().getMutable(); final WorldUpdater updater = worldState.updater(); - MutableAccount account = updater.getOrCreate(Address.fromHexString("0x56")).getMutable(); + MutableAccount account = updater.getOrCreate(Address.fromHexString("0x56")); final Map expectedValues = new TreeMap<>(); final int nodeCount = 100_000; final Random random = new Random(42989428249L); @@ -49,19 +49,19 @@ public void shouldCollectStateEntries() { addExpectedValue( account, expectedValues, - UInt256.valueOf(Math.abs(random.nextLong())), - UInt256.valueOf(i * 10 + 1)); + UInt256.valueOf(random.nextLong(Long.MAX_VALUE)), + UInt256.valueOf(i * 10 + 1L)); } updater.commit(); // Add some changes on top that AbstractWorldUpdater.UpdateTrackingAccount will have to merge. - account = worldState.updater().getOrCreate(Address.fromHexString("0x56")).getMutable(); + account = worldState.updater().getOrCreate(Address.fromHexString("0x56")); for (int i = 0; i <= nodeCount; i++) { addExpectedValue( account, expectedValues, - UInt256.valueOf(Math.abs(random.nextLong())), - UInt256.valueOf(i * 10 + 1)); + UInt256.valueOf(random.nextLong(Long.MAX_VALUE)), + UInt256.valueOf(i * 10 + 1L)); } final Map values = diff --git a/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/TransientStorageOperationBenchmark.java b/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/TransientStorageOperationBenchmark.java index 612bedc2c74..19d3c8b45ca 100644 --- a/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/TransientStorageOperationBenchmark.java +++ b/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/TransientStorageOperationBenchmark.java @@ -59,7 +59,7 @@ private MessageFrame createMessageFrame(final Address address) { .blockHeader(blockHeader) .blockchain(blockchain) .build(); - worldStateUpdater.getOrCreate(address).getMutable().setBalance(Wei.of(1)); + worldStateUpdater.getOrCreate(address).setBalance(Wei.of(1)); worldStateUpdater.commit(); return benchmarkFrame; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java index 14be477a07d..a5488cb9a2e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java @@ -28,7 +28,6 @@ import org.hyperledger.besu.ethereum.rlp.RLPOutput; import org.hyperledger.besu.evm.ModificationNotAllowedException; import org.hyperledger.besu.evm.account.AccountStorageEntry; -import org.hyperledger.besu.evm.account.EvmAccount; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.UpdateTrackingAccount; @@ -41,9 +40,9 @@ import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; -public class BonsaiAccount implements MutableAccount, EvmAccount, AccountValue { +public class BonsaiAccount implements MutableAccount, AccountValue { private final BonsaiWorldView context; - private final boolean mutable; + private boolean mutable; private final Address address; private final Hash addressHash; @@ -163,7 +162,7 @@ public long getNonce() { @Override public void setNonce(final long value) { if (!mutable) { - throw new UnsupportedOperationException("Account is immutable"); + throw new ModificationNotAllowedException(); } nonce = value; } @@ -176,7 +175,7 @@ public Wei getBalance() { @Override public void setBalance(final Wei value) { if (!mutable) { - throw new UnsupportedOperationException("Account is immutable"); + throw new ModificationNotAllowedException(); } balance = value; } @@ -192,7 +191,7 @@ public Bytes getCode() { @Override public void setCode(final Bytes code) { if (!mutable) { - throw new UnsupportedOperationException("Account is immutable"); + throw new ModificationNotAllowedException(); } this.code = code; if (code == null || code.isEmpty()) { @@ -244,7 +243,7 @@ public void writeTo(final RLPOutput out) { @Override public void setStorageValue(final UInt256 key, final UInt256 value) { if (!mutable) { - throw new UnsupportedOperationException("Account is immutable"); + throw new ModificationNotAllowedException(); } updatedStorage.put(key, value); } @@ -259,15 +258,6 @@ public Map getUpdatedStorage() { return updatedStorage; } - @Override - public MutableAccount getMutable() throws ModificationNotAllowedException { - if (mutable) { - return this; - } else { - throw new ModificationNotAllowedException(); - } - } - @Override public Hash getStorageRoot() { return storageRoot; @@ -275,11 +265,16 @@ public Hash getStorageRoot() { public void setStorageRoot(final Hash storageRoot) { if (!mutable) { - throw new UnsupportedOperationException("Account is immutable"); + throw new ModificationNotAllowedException(); } this.storageRoot = storageRoot; } + @Override + public void becomeImmutable() { + mutable = false; + } + @Override public String toString() { return "AccountState{" diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java index 2a04f92fb19..b1cd64378b9 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java @@ -27,10 +27,9 @@ import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.trie.MerkleTrieException; import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.account.EvmAccount; +import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.AbstractWorldUpdater; import org.hyperledger.besu.evm.worldstate.UpdateTrackingAccount; -import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount; import org.hyperledger.besu.plugin.services.trielogs.TrieLog; import org.hyperledger.besu.plugin.services.trielogs.TrieLogAccumulator; @@ -44,6 +43,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.function.Function; import com.google.common.collect.ForwardingMap; @@ -115,12 +115,12 @@ protected UpdateTrackingAccount track( } @Override - public EvmAccount getAccount(final Address address) { + public MutableAccount getAccount(final Address address) { return super.getAccount(address); } @Override - public EvmAccount createAccount(final Address address, final long nonce, final Wei balance) { + public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) { BonsaiValue bonsaiValue = accountsToUpdate.get(address); if (bonsaiValue == null) { @@ -141,7 +141,7 @@ public EvmAccount createAccount(final Address address, final long nonce, final W Hash.EMPTY, true); bonsaiValue.setUpdated(newAccount); - return new WrappedEvmAccount(track(new UpdateTrackingAccount<>(newAccount))); + return track(new UpdateTrackingAccount<>(newAccount)); } @Override @@ -176,17 +176,15 @@ protected BonsaiAccount loadAccount( final BonsaiValue bonsaiValue = accountsToUpdate.get(address); if (bonsaiValue == null) { final Account account; - if (wrappedWorldView() instanceof BonsaiWorldStateUpdateAccumulator) { - account = - ((BonsaiWorldStateUpdateAccumulator) wrappedWorldView()) - .loadAccount(address, bonsaiAccountFunction); + if (wrappedWorldView() + instanceof BonsaiWorldStateUpdateAccumulator bonsaiWorldStateUpdateAccumulator) { + account = bonsaiWorldStateUpdateAccumulator.loadAccount(address, bonsaiAccountFunction); } else { account = wrappedWorldView().get(address); } - BonsaiAccount mutableAccount = null; - if (account instanceof BonsaiAccount) { - mutableAccount = new BonsaiAccount((BonsaiAccount) account, this, true); - accountsToUpdate.put(address, new BonsaiValue<>((BonsaiAccount) account, mutableAccount)); + if (account instanceof BonsaiAccount bonsaiAccount) { + BonsaiAccount mutableAccount = new BonsaiAccount(bonsaiAccount, this, true); + accountsToUpdate.put(address, new BonsaiValue<>(bonsaiAccount, mutableAccount)); return mutableAccount; } else { // add the empty read in accountsToUpdate @@ -418,14 +416,13 @@ public Optional getStorageValueByStorageSlotKey( } try { final Optional valueUInt = - (wrappedWorldView() instanceof BonsaiWorldState) - ? ((BonsaiWorldState) wrappedWorldView()) - .getStorageValueByStorageSlotKey( - () -> - Optional.ofNullable(loadAccount(address, BonsaiValue::getPrior)) - .map(BonsaiAccount::getStorageRoot), - address, - storageSlotKey) + (wrappedWorldView() instanceof BonsaiWorldState bonsaiWorldState) + ? bonsaiWorldState.getStorageValueByStorageSlotKey( + () -> + Optional.ofNullable(loadAccount(address, BonsaiValue::getPrior)) + .map(BonsaiAccount::getStorageRoot), + address, + storageSlotKey) : wrappedWorldView().getStorageValueByStorageSlotKey(address, storageSlotKey); storageToUpdate .computeIfAbsent( @@ -495,53 +492,43 @@ public BonsaiWorldStateKeyValueStorage getWorldStateStorage() { } public void rollForward(final TrieLog layer) { - layer.getAccountChanges().entrySet().stream() + layer + .getAccountChanges() .forEach( - entry -> - rollAccountChange( - entry.getKey(), entry.getValue().getPrior(), entry.getValue().getUpdated())); - layer.getCodeChanges().entrySet().stream() + (address, change) -> + rollAccountChange(address, change.getPrior(), change.getUpdated())); + layer + .getCodeChanges() .forEach( - entry -> - rollCodeChange( - entry.getKey(), entry.getValue().getPrior(), entry.getValue().getUpdated())); - layer.getStorageChanges().entrySet().stream() + (address, change) -> rollCodeChange(address, change.getPrior(), change.getUpdated())); + layer + .getStorageChanges() .forEach( - entry -> - entry - .getValue() - .forEach( - (storageSlotKey, value) -> - rollStorageChange( - entry.getKey(), - storageSlotKey, - value.getPrior(), - value.getUpdated()))); + (address, storage) -> + storage.forEach( + (storageSlotKey, value) -> + rollStorageChange( + address, storageSlotKey, value.getPrior(), value.getUpdated()))); } public void rollBack(final TrieLog layer) { - layer.getAccountChanges().entrySet().stream() + layer + .getAccountChanges() .forEach( - entry -> - rollAccountChange( - entry.getKey(), entry.getValue().getUpdated(), entry.getValue().getPrior())); - layer.getCodeChanges().entrySet().stream() + (address, change) -> + rollAccountChange(address, change.getUpdated(), change.getPrior())); + layer + .getCodeChanges() .forEach( - entry -> - rollCodeChange( - entry.getKey(), entry.getValue().getUpdated(), entry.getValue().getPrior())); - layer.getStorageChanges().entrySet().stream() + (address, change) -> rollCodeChange(address, change.getUpdated(), change.getPrior())); + layer + .getStorageChanges() .forEach( - entry -> - entry - .getValue() - .forEach( - (storageSlotKey, value) -> - rollStorageChange( - entry.getKey(), - storageSlotKey, - value.getUpdated(), - value.getPrior()))); + (address, storage) -> + storage.forEach( + (storageSlotKey, value) -> + rollStorageChange( + address, storageSlotKey, value.getUpdated(), value.getPrior()))); } private void rollAccountChange( @@ -598,8 +585,7 @@ private BonsaiValue loadAccountFromParent( final Address address, final BonsaiValue defaultValue) { try { final Account parentAccount = wrappedWorldView().get(address); - if (parentAccount instanceof BonsaiAccount) { - final BonsaiAccount account = (BonsaiAccount) parentAccount; + if (parentAccount instanceof BonsaiAccount account) { final BonsaiValue loadedAccountValue = new BonsaiValue<>(new BonsaiAccount(account), account); accountsToUpdate.put(address, loadedAccountValue); @@ -634,7 +620,7 @@ private void rollCodeChange( } if (codeValue == null) { - if ((expectedCode == null || expectedCode.size() == 0) && replacementCode != null) { + if ((expectedCode == null || expectedCode.isEmpty()) && replacementCode != null) { codeToUpdate.put(address, new BonsaiValue<>(null, replacementCode)); } else { throw new IllegalStateException( @@ -772,11 +758,11 @@ public void reset() { public static class AccountConsumingMap extends ForwardingMap { - private final ConcurrentHashMap accounts; + private final ConcurrentMap accounts; private final Consumer consumer; public AccountConsumingMap( - final ConcurrentHashMap accounts, final Consumer consumer) { + final ConcurrentMap accounts, final Consumer consumer) { this.accounts = accounts; this.consumer = consumer; } @@ -801,11 +787,11 @@ public static class StorageConsumingMap extends ForwardingMap { private final Address address; - private final ConcurrentHashMap storages; + private final ConcurrentMap storages; private final Consumer consumer; public StorageConsumingMap( - final Address address, final ConcurrentHashMap storages, final Consumer consumer) { + final Address address, final ConcurrentMap storages, final Consumer consumer) { this.address = address; this.storages = storages; this.consumer = consumer; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java index b873161976a..f6c969d58b2 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/GenesisState.java @@ -48,7 +48,6 @@ import java.util.Optional; import java.util.OptionalLong; import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.Stream; import com.google.common.base.MoreObjects; @@ -86,8 +85,7 @@ public static GenesisState fromJson(final String json, final ProtocolSchedule pr */ public static GenesisState fromConfig( final GenesisConfigFile config, final ProtocolSchedule protocolSchedule) { - final List genesisAccounts = - parseAllocations(config).collect(Collectors.toList()); + final List genesisAccounts = parseAllocations(config).toList(); final Block block = new Block( buildHeader(config, calculateGenesisStateHash(genesisAccounts), protocolSchedule), @@ -124,7 +122,7 @@ private static void writeAccountsTo( final WorldUpdater updater = target.updater(); genesisAccounts.forEach( genesisAccount -> { - final MutableAccount account = updater.getOrCreate(genesisAccount.address).getMutable(); + final MutableAccount account = updater.getOrCreate(genesisAccount.address); account.setNonce(genesisAccount.nonce); account.setBalance(genesisAccount.balance); account.setCode(genesisAccount.code); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicBlockProcessor.java index 5079c0763be..044017b8728 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ClassicBlockProcessor.java @@ -64,7 +64,7 @@ boolean rewardCoinbase( } final Wei coinbaseReward = getCoinbaseReward(blockReward, header.getNumber(), ommers.size()); final WorldUpdater updater = worldState.updater(); - final MutableAccount coinbase = updater.getOrCreate(header.getCoinbase()).getMutable(); + final MutableAccount coinbase = updater.getOrCreate(header.getCoinbase()); coinbase.incrementBalance(coinbaseReward); for (final BlockHeader ommerHeader : ommers) { @@ -77,8 +77,7 @@ boolean rewardCoinbase( return false; } - final MutableAccount ommerCoinbase = - updater.getOrCreate(ommerHeader.getCoinbase()).getMutable(); + final MutableAccount ommerCoinbase = updater.getOrCreate(ommerHeader.getCoinbase()); final Wei ommerReward = getOmmerReward(blockReward, header.getNumber(), ommerHeader.getNumber()); ommerCoinbase.incrementBalance(ommerReward); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessor.java index ae1187eb6bb..9f2096f63d2 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessor.java @@ -59,8 +59,7 @@ protected boolean rewardCoinbase( final Wei coinbaseReward = getCoinbaseReward(blockReward, header.getNumber(), ommers.size()); final WorldUpdater updater = worldState.updater(); final Address miningBeneficiary = getMiningBeneficiaryCalculator().calculateBeneficiary(header); - final MutableAccount miningBeneficiaryAccount = - updater.getOrCreate(miningBeneficiary).getMutable(); + final MutableAccount miningBeneficiaryAccount = updater.getOrCreate(miningBeneficiary); miningBeneficiaryAccount.incrementBalance(coinbaseReward); for (final BlockHeader ommerHeader : ommers) { @@ -73,8 +72,7 @@ protected boolean rewardCoinbase( return false; } - final MutableAccount ommerCoinbase = - updater.getOrCreate(ommerHeader.getCoinbase()).getMutable(); + final MutableAccount ommerCoinbase = updater.getOrCreate(ommerHeader.getCoinbase()); final Wei ommerReward = getOmmerReward(blockReward, header.getNumber(), ommerHeader.getNumber()); ommerCoinbase.incrementBalance(ommerReward); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index dd94e60b5bd..02f757483df 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -870,9 +870,9 @@ private void updateWorldStateForDao(final MutableWorldState worldState) { .toList(); final WorldUpdater worldUpdater = worldState.updater(); final MutableAccount daoRefundContract = - worldUpdater.getOrCreate(DAO_REFUND_CONTRACT_ADDRESS).getMutable(); + worldUpdater.getOrCreate(DAO_REFUND_CONTRACT_ADDRESS); for (final Address address : addresses) { - final MutableAccount account = worldUpdater.getOrCreate(address).getMutable(); + final MutableAccount account = worldUpdater.getOrCreate(address); final Wei balance = account.getBalance(); account.decrementBalance(balance); daoRefundContract.incrementBalance(balance); 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 6dbd3b4c3db..7d235fb3a39 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 @@ -33,7 +33,6 @@ import org.hyperledger.besu.ethereum.trie.MerkleTrieException; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.account.EvmAccount; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; @@ -285,7 +284,7 @@ public TransactionProcessingResult processTransaction( final Address senderAddress = transaction.getSender(); - final EvmAccount sender = worldState.getOrCreateSenderAccount(senderAddress); + final MutableAccount sender = worldState.getOrCreateSenderAccount(senderAddress); validationResult = transactionValidator.validateForSender(transaction, sender, transactionValidationParams); @@ -294,8 +293,7 @@ public TransactionProcessingResult processTransaction( return TransactionProcessingResult.invalid(validationResult); } - final MutableAccount senderMutableAccount = sender.getMutable(); - final long previousNonce = senderMutableAccount.incrementNonce(); + final long previousNonce = sender.incrementNonce(); LOG.trace( "Incremented sender {} nonce ({} -> {})", senderAddress, @@ -309,7 +307,7 @@ public TransactionProcessingResult processTransaction( final Wei upfrontGasCost = transaction.getUpfrontGasCost(transactionGasPrice, blobGasPrice, blobGas); - final Wei previousBalance = senderMutableAccount.decrementBalance(upfrontGasCost); + final Wei previousBalance = sender.decrementBalance(upfrontGasCost); LOG.trace( "Deducted sender {} upfront gas cost {} ({} -> {})", senderAddress, @@ -386,7 +384,7 @@ public TransactionProcessingResult processTransaction( final MessageFrame initialFrame; if (transaction.isContractCreation()) { final Address contractAddress = - Address.contractAddress(senderAddress, senderMutableAccount.getNonce() - 1L); + Address.contractAddress(senderAddress, sender.getNonce() - 1L); final Bytes initCodeBytes = transaction.getPayload(); initialFrame = @@ -443,7 +441,7 @@ public TransactionProcessingResult processTransaction( final long refundedGas = refunded(transaction, initialFrame.getRemainingGas(), baseRefundGas); final Wei refundedWei = transactionGasPrice.multiply(refundedGas); final Wei balancePriorToRefund = sender.getBalance(); - senderMutableAccount.incrementBalance(refundedWei); + sender.incrementBalance(refundedWei); LOG.atTrace() .setMessage("refunded sender {} {} wei ({} -> {})") .addArgument(senderAddress) @@ -454,7 +452,7 @@ public TransactionProcessingResult processTransaction( final long gasUsedByTransaction = transaction.getGasLimit() - initialFrame.getRemainingGas(); // update the coinbase - final var coinbase = worldState.getOrCreate(miningBeneficiary).getMutable(); + final var coinbase = worldState.getOrCreate(miningBeneficiary); final long usedGas = transaction.getGasLimit() - refundedGas; final CoinbaseFeePriceCalculator coinbaseCalculator; if (blockHeader.getBaseFee().isPresent()) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ParentBeaconBlockRootHelper.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ParentBeaconBlockRootHelper.java index 7d110fb425b..669815f7d52 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ParentBeaconBlockRootHelper.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ParentBeaconBlockRootHelper.java @@ -22,14 +22,14 @@ import org.apache.tuweni.units.bigints.UInt256; /** A helper class to store the parent beacon block root. */ -public class ParentBeaconBlockRootHelper { +public interface ParentBeaconBlockRootHelper { // Modulus use to for the timestamp to store the root public static final long HISTORICAL_ROOTS_MODULUS = 8191; public static final Address BEACON_ROOTS_ADDRESS = Address.fromHexString("0xBEaC020001c6C8B69E5257f4754e46e25f5dc9cB"); - public static void storeParentBeaconBlockRoot( + static void storeParentBeaconBlockRoot( final WorldUpdater worldUpdater, final long timestamp, final Bytes32 root) { /* see EIP-4788: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4788.md @@ -40,7 +40,7 @@ public static void storeParentBeaconBlockRoot( final UInt256 timestampIndex = UInt256.valueOf(timestampReduced); final UInt256 rootIndex = UInt256.valueOf(timestampExtended); - final MutableAccount account = worldUpdater.getOrCreate(BEACON_ROOTS_ADDRESS).getMutable(); + final MutableAccount account = worldUpdater.getOrCreate(BEACON_ROOTS_ADDRESS); account.setStorageValue(timestampIndex, UInt256.valueOf(timestamp)); account.setStorageValue(rootIndex, UInt256.fromBytes(root)); worldUpdater.commit(); 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 b91f81208a6..d2b0f8edf78 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 @@ -15,7 +15,7 @@ package org.hyperledger.besu.ethereum.mainnet; import org.hyperledger.besu.ethereum.core.Withdrawal; -import org.hyperledger.besu.evm.account.EvmAccount; +import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.List; @@ -25,8 +25,8 @@ public class WithdrawalsProcessor { public void processWithdrawals( final List withdrawals, final WorldUpdater worldUpdater) { for (final Withdrawal withdrawal : withdrawals) { - final EvmAccount account = worldUpdater.getOrCreate(withdrawal.getAddress()); - account.getMutable().incrementBalance(withdrawal.getAmount().getAsWei()); + final MutableAccount account = worldUpdater.getOrCreate(withdrawal.getAddress()); + account.incrementBalance(withdrawal.getAmount().getAsWei()); } worldUpdater.clearAccountsThatAreEmpty(); worldUpdater.commit(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateGroupRehydrationBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateGroupRehydrationBlockProcessor.java index 713d73c48e5..462bd9c6619 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateGroupRehydrationBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateGroupRehydrationBlockProcessor.java @@ -231,7 +231,7 @@ private boolean rewardCoinbase( final Wei coinbaseReward = blockReward.add(blockReward.multiply(ommers.size()).divide(32)); final WorldUpdater updater = worldState.updater(); - final MutableAccount coinbase = updater.getOrCreate(header.getCoinbase()).getMutable(); + final MutableAccount coinbase = updater.getOrCreate(header.getCoinbase()); coinbase.incrementBalance(coinbaseReward); for (final BlockHeader ommerHeader : ommers) { @@ -245,8 +245,7 @@ private boolean rewardCoinbase( return false; } - final MutableAccount ommerCoinbase = - updater.getOrCreate(ommerHeader.getCoinbase()).getMutable(); + final MutableAccount ommerCoinbase = updater.getOrCreate(ommerHeader.getCoinbase()); final long distance = header.getNumber() - ommerHeader.getNumber(); final Wei ommerReward = blockReward.subtract(blockReward.multiply(distance).divide(8)); ommerCoinbase.incrementBalance(ommerReward); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateGenesisAllocator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateGenesisAllocator.java index 1f84f210b92..f1a022fa7c2 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateGenesisAllocator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateGenesisAllocator.java @@ -56,7 +56,7 @@ public void applyGenesisToPrivateWorldState( final PrivacyGenesis genesis = privacyGroupGenesisProvider.getPrivacyGenesis(privacyGroupId, blockNumber); - if (genesis.getAccounts().size() > 0) { + if (!genesis.getAccounts().isEmpty()) { LOG.debug( "Applying {} privacy accounts onto {} private state genesis at {}", @@ -67,15 +67,14 @@ public void applyGenesisToPrivateWorldState( genesis .getAccounts() .forEach( - (genesisAccount) -> { + genesisAccount -> { final Address address = genesisAccount.getAddress(); if (address.toBigInteger().compareTo(BigInteger.valueOf(Byte.MAX_VALUE)) < 0) { LOG.warn( "Genesis address {} is in reserved range and may be overwritten", address); } - final MutableAccount account = - privateWorldStateUpdater.createAccount(address).getMutable(); + final MutableAccount account = privateWorldStateUpdater.createAccount(address); LOG.debug("{} applied to genesis", address.toHexString()); @@ -90,14 +89,14 @@ public void applyGenesisToPrivateWorldState( if (isFlexiblePrivacyEnabled) { // inject management final MutableAccount managementContract = - privateWorldStateUpdater.createAccount(DEFAULT_FLEXIBLE_PRIVACY_MANAGEMENT).getMutable(); + privateWorldStateUpdater.createAccount(DEFAULT_FLEXIBLE_PRIVACY_MANAGEMENT); // this is the code for the simple management contract managementContract.setCode(FlexibleGroupManagement.DEFAULT_GROUP_MANAGEMENT_RUNTIME_BYTECODE); // inject proxy final MutableAccount procyContract = - privateWorldStateUpdater.createAccount(FLEXIBLE_PRIVACY_PROXY).getMutable(); + privateWorldStateUpdater.createAccount(FLEXIBLE_PRIVACY_PROXY); // this is the code for the proxy contract procyContract.setCode(FlexibleGroupManagement.PROXY_RUNTIME_BYTECODE); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java index 2eeb29cf32b..c953ed93d1d 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java @@ -26,7 +26,6 @@ import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.worldstate.DefaultMutablePrivateWorldStateUpdater; import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.account.EvmAccount; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -90,11 +89,11 @@ public TransactionProcessingResult processTransaction( LOG.trace("Starting private execution of {}", transaction); final Address senderAddress = transaction.getSender(); - final EvmAccount maybePrivateSender = privateWorldState.getAccount(senderAddress); + final MutableAccount maybePrivateSender = privateWorldState.getAccount(senderAddress); final MutableAccount sender = maybePrivateSender != null - ? maybePrivateSender.getMutable() - : privateWorldState.createAccount(senderAddress, 0, Wei.ZERO).getMutable(); + ? maybePrivateSender + : privateWorldState.createAccount(senderAddress, 0, Wei.ZERO); final ValidationResult validationResult = privateTransactionValidator.validate(transaction, sender.getNonce(), false); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java index 73ebea34384..0310700e1f5 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java @@ -141,7 +141,7 @@ private boolean rewardCoinbase( final Wei coinbaseReward = blockReward.add(blockReward.multiply(ommers.size()).divide(32)); final WorldUpdater updater = worldState.updater(); - final MutableAccount coinbase = updater.getOrCreate(header.getCoinbase()).getMutable(); + final MutableAccount coinbase = updater.getOrCreate(header.getCoinbase()); coinbase.incrementBalance(coinbaseReward); for (final BlockHeader ommerHeader : ommers) { @@ -155,8 +155,7 @@ private boolean rewardCoinbase( return false; } - final MutableAccount ommerCoinbase = - updater.getOrCreate(ommerHeader.getCoinbase()).getMutable(); + final MutableAccount ommerCoinbase = updater.getOrCreate(ommerHeader.getCoinbase()); final long distance = header.getNumber() - ommerHeader.getNumber(); final Wei ommerReward = blockReward.subtract(blockReward.multiply(distance).divide(8)); ommerCoinbase.incrementBalance(ommerReward); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java index 07a10e218ab..b1ef1bc0b1c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java @@ -199,11 +199,8 @@ private Optional> captureStorage(final MessageFrame frame) try { final Map storageContents = new TreeMap<>( - frame - .getWorldUpdater() - .getAccount(frame.getRecipientAddress()) - .getMutable() - .getUpdatedStorage()); + frame.getWorldUpdater().getAccount(frame.getRecipientAddress()).getUpdatedStorage()); + return Optional.of(storageContents); } catch (final ModificationNotAllowedException e) { return Optional.of(new TreeMap<>()); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java index fe1bf39ecbd..e52319463cc 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java @@ -17,9 +17,8 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.account.EvmAccount; +import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount; import java.util.Collection; import java.util.Optional; @@ -39,24 +38,24 @@ public DefaultMutablePrivateWorldStateUpdater( } @Override - public EvmAccount createAccount(final Address address, final long nonce, final Wei balance) { + public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) { return privateWorldUpdater.createAccount(address, nonce, balance); } @Override - public EvmAccount createAccount(final Address address) { + public MutableAccount createAccount(final Address address) { return privateWorldUpdater.createAccount(address); } @Override - public EvmAccount getAccount(final Address address) { - final EvmAccount privateAccount = privateWorldUpdater.getAccount(address); + public MutableAccount getAccount(final Address address) { + final MutableAccount privateAccount = privateWorldUpdater.getAccount(address); if (privateAccount != null && !privateAccount.isEmpty()) { return privateAccount; } - final EvmAccount publicAccount = publicWorldUpdater.getAccount(address); + final MutableAccount publicAccount = publicWorldUpdater.getAccount(address); if (publicAccount != null && !publicAccount.isEmpty()) { - ((WrappedEvmAccount) publicAccount).setImmutable(true); // FIXME + publicAccount.becomeImmutable(); return publicAccount; } return privateAccount; diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java index e02406927fc..3b2b8e30958 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java @@ -15,7 +15,6 @@ package org.hyperledger.besu.ethereum.core; import static com.google.common.base.Preconditions.checkArgument; -import static java.util.stream.Collectors.toUnmodifiableList; import static java.util.stream.Collectors.toUnmodifiableSet; import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryWorldStateArchive; @@ -58,7 +57,6 @@ import java.util.Random; import java.util.Set; import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.tuweni.bytes.Bytes; @@ -141,7 +139,7 @@ private List blockSequence( // Mutate accounts accountsToSetup.forEach( hash -> { - final MutableAccount a = stateUpdater.getAccount(hash).getMutable(); + final MutableAccount a = stateUpdater.getAccount(hash); a.incrementNonce(); a.setBalance(Wei.of(positiveLong())); storageKeys.forEach(key -> a.setStorageValue(key, UInt256.ONE)); @@ -181,7 +179,7 @@ private List createRandomAccounts( final WorldUpdater updater = worldState.updater(); final List accounts = new ArrayList<>(count); for (int i = 0; i < count; i++) { - final MutableAccount account = updater.getOrCreate(address()).getMutable(); + final MutableAccount account = updater.getOrCreate(address()); if (random.nextFloat() < percentContractAccounts) { // Some percentage of accounts are contract accounts account.setCode(bytesValue(5, 50)); @@ -373,21 +371,13 @@ public Transaction transaction(final TransactionType transactionType, final Byte public Transaction transaction( final TransactionType transactionType, final Bytes payload, final Address to) { - switch (transactionType) { - case FRONTIER: - return frontierTransaction(payload, to); - case EIP1559: - return eip1559Transaction(payload, to); - case ACCESS_LIST: - return accessListTransaction(payload, to); - case BLOB: - return blobTransaction(payload, to); - default: - throw new RuntimeException( - String.format( - "Developer Error. No random transaction generator defined for %s", - transactionType)); - } + return switch (transactionType) { + case FRONTIER -> frontierTransaction(payload, to); + case EIP1559 -> eip1559Transaction(payload, to); + case ACCESS_LIST -> accessListTransaction(payload, to); + case BLOB -> blobTransaction(payload, to); + // no default, all types accounted for. + }; } private Transaction accessListTransaction(final Bytes payload, final Address to) { @@ -406,13 +396,12 @@ private Transaction accessListTransaction(final Bytes payload, final Address to) private List accessList() { final List
accessedAddresses = - Stream.generate(this::address).limit(1L + random.nextInt(3)).collect(toUnmodifiableList()); + Stream.generate(this::address).limit(1L + random.nextInt(3)).toList(); final List accessedStorage = new ArrayList<>(); for (int i = 0; i < accessedAddresses.size(); ++i) { accessedStorage.add( new AccessListEntry( - accessedAddresses.get(i), - Stream.generate(this::bytes32).limit(2L * i).collect(toUnmodifiableList()))); + accessedAddresses.get(i), Stream.generate(this::bytes32).limit(2L * i).toList())); } return accessedStorage; } @@ -469,10 +458,7 @@ public Set transactions(final int n, final TransactionType... trans public Set transactions(final int n) { return transactions( - n, - new TransactionType[] { - TransactionType.FRONTIER, TransactionType.ACCESS_LIST, TransactionType.EIP1559 - }); + n, TransactionType.FRONTIER, TransactionType.ACCESS_LIST, TransactionType.EIP1559); } public Set transactionsWithAllTypes() { @@ -483,10 +469,7 @@ public Set transactionsWithAllTypes(final int atLeast) { checkArgument(atLeast >= 0); final HashSet remainingTransactionTypes = new HashSet<>( - Set.of( - new TransactionType[] { - TransactionType.FRONTIER, TransactionType.ACCESS_LIST, TransactionType.EIP1559 - })); + Set.of(TransactionType.FRONTIER, TransactionType.ACCESS_LIST, TransactionType.EIP1559)); final HashSet transactions = new HashSet<>(); while (transactions.size() < atLeast || !remainingTransactionTypes.isEmpty()) { final Transaction newTransaction = transaction(); @@ -540,7 +523,7 @@ public List receipts(final Block block) { } public List logs(final int logsCount, final int topicsPerLog) { - return Stream.generate(() -> log(topicsPerLog)).limit(logsCount).collect(Collectors.toList()); + return Stream.generate(() -> log(topicsPerLog)).limit(logsCount).toList(); } public Log log() { @@ -548,8 +531,7 @@ public Log log() { } public Log log(final int topicCount) { - final List topics = - Stream.generate(this::logTopic).limit(topicCount).collect(Collectors.toList()); + final List topics = Stream.generate(this::logTopic).limit(topicCount).toList(); return new Log(address(), bytesValue(5, 15), topics); } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/LogRollingTests.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/LogRollingTests.java index b26120e69fb..0e2fb0c0d1a 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/LogRollingTests.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/LogRollingTests.java @@ -53,7 +53,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class LogRollingTests { +class LogRollingTests { private BonsaiWorldStateProvider archive; @@ -126,7 +126,7 @@ public class LogRollingTests { new MainnetBlockHeaderFunctions()); @BeforeEach - public void createStorage() { + void createStorage() { provider = new InMemoryKeyValueStorageProvider(); final CachedMerkleTrieLoader cachedMerkleTrieLoader = new CachedMerkleTrieLoader(new NoOpMetricsSystem()); @@ -167,15 +167,14 @@ public void createStorage() { } @Test - public void simpleRollForwardTest() { + void simpleRollForwardTest() { final BonsaiWorldState worldState = new BonsaiWorldState( archive, new BonsaiWorldStateKeyValueStorage(provider, new NoOpMetricsSystem())); final WorldUpdater updater = worldState.updater(); - final MutableAccount mutableAccount = - updater.createAccount(addressOne, 1, Wei.of(1L)).getMutable(); + final MutableAccount mutableAccount = updater.createAccount(addressOne, 1, Wei.of(1L)); mutableAccount.setCode(Bytes.of(0, 1, 2)); mutableAccount.setStorageValue(UInt256.ONE, UInt256.ONE); updater.commit(); @@ -210,14 +209,13 @@ public void simpleRollForwardTest() { } @Test - public void rollForwardTwice() { + void rollForwardTwice() { final BonsaiWorldState worldState = new BonsaiWorldState( archive, new BonsaiWorldStateKeyValueStorage(provider, new NoOpMetricsSystem())); final WorldUpdater updater = worldState.updater(); - final MutableAccount mutableAccount = - updater.createAccount(addressOne, 1, Wei.of(1L)).getMutable(); + final MutableAccount mutableAccount = updater.createAccount(addressOne, 1, Wei.of(1L)); mutableAccount.setCode(Bytes.of(0, 1, 2)); mutableAccount.setStorageValue(UInt256.ONE, UInt256.ONE); updater.commit(); @@ -225,7 +223,7 @@ public void rollForwardTwice() { worldState.persist(headerOne); final WorldUpdater updater2 = worldState.updater(); - final MutableAccount mutableAccount2 = updater2.getAccount(addressOne).getMutable(); + final MutableAccount mutableAccount2 = updater2.getAccount(addressOne); mutableAccount2.setStorageValue(UInt256.ONE, UInt256.valueOf(2)); updater2.commit(); @@ -261,14 +259,13 @@ public void rollForwardTwice() { } @Test - public void rollBackOnce() { + void rollBackOnce() { final BonsaiWorldState worldState = new BonsaiWorldState( archive, new BonsaiWorldStateKeyValueStorage(provider, new NoOpMetricsSystem())); final WorldUpdater updater = worldState.updater(); - final MutableAccount mutableAccount = - updater.createAccount(addressOne, 1, Wei.of(1L)).getMutable(); + final MutableAccount mutableAccount = updater.createAccount(addressOne, 1, Wei.of(1L)); mutableAccount.setCode(Bytes.of(0, 1, 2)); mutableAccount.setStorageValue(UInt256.ONE, UInt256.ONE); updater.commit(); @@ -276,7 +273,7 @@ public void rollBackOnce() { worldState.persist(headerOne); final WorldUpdater updater2 = worldState.updater(); - final MutableAccount mutableAccount2 = updater2.getAccount(addressOne).getMutable(); + final MutableAccount mutableAccount2 = updater2.getAccount(addressOne); mutableAccount2.setStorageValue(UInt256.ONE, UInt256.valueOf(2)); updater2.commit(); @@ -296,7 +293,7 @@ public void rollBackOnce() { final WorldUpdater secondUpdater = secondWorldState.updater(); final MutableAccount secondMutableAccount = - secondUpdater.createAccount(addressOne, 1, Wei.of(1L)).getMutable(); + secondUpdater.createAccount(addressOne, 1, Wei.of(1L)); secondMutableAccount.setCode(Bytes.of(0, 1, 2)); secondMutableAccount.setStorageValue(UInt256.ONE, UInt256.ONE); secondUpdater.commit(); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java index 2fe864a5c90..4bc2c6944e2 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java @@ -29,7 +29,6 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; -import org.hyperledger.besu.evm.account.EvmAccount; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -49,7 +48,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class MainnetTransactionProcessorTest { +class MainnetTransactionProcessorTest { private static final int MAX_STACK_SIZE = 1024; @@ -67,8 +66,7 @@ public class MainnetTransactionProcessorTest { @Mock private Transaction transaction; @Mock private BlockHashLookup blockHashLookup; - @Mock private EvmAccount senderAccount; - @Mock private MutableAccount mutableSenderAccount; + @Mock private MutableAccount senderAccount; MainnetTransactionProcessor createTransactionProcessor(final boolean warmCoinbase) { return new MainnetTransactionProcessor( @@ -84,14 +82,13 @@ MainnetTransactionProcessor createTransactionProcessor(final boolean warmCoinbas } @Test - public void shouldWarmCoinbaseIfRequested() { + void shouldWarmCoinbaseIfRequested() { Optional
toAddresss = Optional.of(Address.fromHexString("0x2222222222222222222222222222222222222222")); when(transaction.getTo()).thenReturn(toAddresss); Address senderAddress = Address.fromHexString("0x5555555555555555555555555555555555555555"); Address coinbaseAddress = Address.fromHexString("0x4242424242424242424242424242424242424242"); - when(senderAccount.getMutable()).thenReturn(mutableSenderAccount); when(transaction.getHash()).thenReturn(Hash.EMPTY); when(transaction.getPayload()).thenReturn(Bytes.EMPTY); when(transaction.getSender()).thenReturn(senderAddress); @@ -145,7 +142,7 @@ public void shouldWarmCoinbaseIfRequested() { } @Test - public void shouldCallTransactionValidatorWithExpectedTransactionValidationParams() { + void shouldCallTransactionValidatorWithExpectedTransactionValidationParams() { final ArgumentCaptor txValidationParamCaptor = transactionValidationParamCaptor(); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java index 8835854ee05..4711e456cf0 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java @@ -45,7 +45,6 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import java.util.Collections; @@ -59,7 +58,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class PrivacyBlockProcessorTest { +class PrivacyBlockProcessorTest { private PrivacyBlockProcessor privacyBlockProcessor; private PrivateStateStorage privateStateStorage; @@ -91,7 +90,7 @@ public void setUp() { } @Test - public void mustCopyPreviousPrivacyGroupBlockHeadMap() { + void mustCopyPreviousPrivacyGroupBlockHeadMap() { final BlockDataGenerator blockDataGenerator = new BlockDataGenerator(); final Blockchain blockchain = mock(Blockchain.class); final MutableWorldState mutableWorldState = mock(MutableWorldState.class); @@ -132,7 +131,7 @@ public void mustCopyPreviousPrivacyGroupBlockHeadMap() { } @Test - public void mustPerformRehydration() { + void mustPerformRehydration() { final BlockDataGenerator blockDataGenerator = new BlockDataGenerator(); final Blockchain blockchain = mock(Blockchain.class); final MutableWorldState mutableWorldState = mock(MutableWorldState.class); @@ -191,9 +190,7 @@ public void mustPerformRehydration() { private MutableWorldState mockPrivateStateArchive() { final MutableWorldState mockPrivateState = mock(MutableWorldState.class); final WorldUpdater mockWorldUpdater = mock(WorldUpdater.class); - final WrappedEvmAccount mockWrappedEvmAccount = mock(WrappedEvmAccount.class); - final MutableAccount mockMutableAccount = mock(MutableAccount.class); - when(mockWrappedEvmAccount.getMutable()).thenReturn(mockMutableAccount); + final MutableAccount mockWrappedEvmAccount = mock(MutableAccount.class); when(mockWorldUpdater.createAccount(any())).thenReturn(mockWrappedEvmAccount); when(mockPrivateState.updater()).thenReturn(mockWorldUpdater); when(mockPrivateState.rootHash()).thenReturn(Hash.ZERO); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracerTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracerTest.java index 48fe26af187..146213722b5 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracerTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracerTest.java @@ -33,7 +33,6 @@ import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.operation.Operation.OperationResult; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount; import java.util.Map; import java.util.TreeMap; @@ -47,7 +46,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class DebugOperationTracerTest { +class DebugOperationTracerTest { private static final int DEPTH = 4; private static final long INITIAL_GAS = 1000L; @@ -65,7 +64,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { }; @Test - public void shouldRecordProgramCounter() { + void shouldRecordProgramCounter() { final MessageFrame frame = validMessageFrame(); frame.setPC(10); final TraceFrame traceFrame = traceFrame(frame); @@ -74,14 +73,14 @@ public void shouldRecordProgramCounter() { } @Test - public void shouldRecordOpcode() { + void shouldRecordOpcode() { final MessageFrame frame = validMessageFrame(); final TraceFrame traceFrame = traceFrame(frame); assertThat(traceFrame.getOpcode()).isEqualTo("MUL"); } @Test - public void shouldRecordDepth() { + void shouldRecordDepth() { final MessageFrame frame = validMessageFrame(); // simulate 4 calls frame.getMessageFrameStack().add(frame); @@ -93,7 +92,7 @@ public void shouldRecordDepth() { } @Test - public void shouldRecordRemainingGas() { + void shouldRecordRemainingGas() { final MessageFrame frame = validMessageFrame(); // final Gas currentGasCost = Gas.of(50); final TraceFrame traceFrame = traceFrame(frame); @@ -101,7 +100,7 @@ public void shouldRecordRemainingGas() { } @Test - public void shouldRecordStackWhenEnabled() { + void shouldRecordStackWhenEnabled() { final MessageFrame frame = validMessageFrame(); final UInt256 stackItem1 = UInt256.fromHexString("0x01"); final UInt256 stackItem2 = UInt256.fromHexString("0x02"); @@ -115,14 +114,14 @@ public void shouldRecordStackWhenEnabled() { } @Test - public void shouldNotRecordStackWhenDisabled() { + void shouldNotRecordStackWhenDisabled() { final TraceFrame traceFrame = traceFrame(validMessageFrame(), new TraceOptions(false, false, false)); assertThat(traceFrame.getStack()).isEmpty(); } @Test - public void shouldRecordMemoryWhenEnabled() { + void shouldRecordMemoryWhenEnabled() { final MessageFrame frame = validMessageFrame(); final Bytes32 word1 = Bytes32.fromHexString("0x01"); final Bytes32 word2 = Bytes32.fromHexString("0x02"); @@ -136,14 +135,14 @@ public void shouldRecordMemoryWhenEnabled() { } @Test - public void shouldNotRecordMemoryWhenDisabled() { + void shouldNotRecordMemoryWhenDisabled() { final TraceFrame traceFrame = traceFrame(validMessageFrame(), new TraceOptions(false, false, false)); assertThat(traceFrame.getMemory()).isEmpty(); } @Test - public void shouldRecordStorageWhenEnabled() { + void shouldRecordStorageWhenEnabled() { final MessageFrame frame = validMessageFrame(); final Map updatedStorage = setupStorageForCapture(frame); final TraceFrame traceFrame = traceFrame(frame, new TraceOptions(true, false, false)); @@ -152,14 +151,14 @@ public void shouldRecordStorageWhenEnabled() { } @Test - public void shouldNotRecordStorageWhenDisabled() { + void shouldNotRecordStorageWhenDisabled() { final TraceFrame traceFrame = traceFrame(validMessageFrame(), new TraceOptions(false, false, false)); assertThat(traceFrame.getStorage()).isEmpty(); } @Test - public void shouldCaptureFrameWhenExceptionalHaltOccurs() { + void shouldCaptureFrameWhenExceptionalHaltOccurs() { final MessageFrame frame = validMessageFrame(); final Map updatedStorage = setupStorageForCapture(frame); @@ -210,15 +209,13 @@ private MessageFrameTestFixture validMessageFrameBuilder() { } private Map setupStorageForCapture(final MessageFrame frame) { - final WrappedEvmAccount account = mock(WrappedEvmAccount.class); - final MutableAccount mutableAccount = mock(MutableAccount.class); - when(account.getMutable()).thenReturn(mutableAccount); + final MutableAccount account = mock(MutableAccount.class); when(worldUpdater.getAccount(frame.getRecipientAddress())).thenReturn(account); final Map updatedStorage = new TreeMap<>(); updatedStorage.put(UInt256.ZERO, UInt256.valueOf(233)); updatedStorage.put(UInt256.ONE, UInt256.valueOf(2424)); - when(mutableAccount.getUpdatedStorage()).thenReturn(updatedStorage); + when(account.getUpdatedStorage()).thenReturn(updatedStorage); final Bytes32 word1 = Bytes32.fromHexString("0x01"); final Bytes32 word2 = Bytes32.fromHexString("0x02"); final Bytes32 word3 = Bytes32.fromHexString("0x03"); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java index 7f910364511..bc632e0f62a 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutableWorldStateTest.java @@ -46,7 +46,7 @@ // TODO: make that an abstract mutable world state test, and create sub-class for all world state // implementations. -public class DefaultMutableWorldStateTest { +class DefaultMutableWorldStateTest { // The following test cases are loosely derived from the testTransactionToItself // GeneralStateReferenceTest. @@ -64,7 +64,7 @@ private static MutableWorldState createEmpty() { } @Test - public void rootHash_Empty() { + void rootHash_Empty() { final MutableWorldState worldState = createEmpty(); assertThat(worldState.rootHash()).isEqualTo(MerkleTrie.EMPTY_TRIE_NODE_HASH); @@ -73,16 +73,16 @@ public void rootHash_Empty() { } @Test - public void containsAccount_AccountDoesNotExist() { + void containsAccount_AccountDoesNotExist() { final WorldState worldState = createEmpty(); assertThat(worldState.get(ADDRESS)).isNull(); } @Test - public void containsAccount_AccountExists() { + void containsAccount_AccountExists() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).getMutable().setBalance(Wei.of(100000)); + updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); updater.commit(); assertThat(worldState.get(ADDRESS)).isNotNull(); assertThat(worldState.rootHash()) @@ -92,7 +92,7 @@ public void containsAccount_AccountExists() { } @Test - public void removeAccount_AccountDoesNotExist() { + void removeAccount_AccountDoesNotExist() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); updater.deleteAccount(ADDRESS); @@ -104,10 +104,10 @@ public void removeAccount_AccountDoesNotExist() { } @Test - public void removeAccount_UpdatedAccount() { + void removeAccount_UpdatedAccount() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).getMutable().setBalance(Wei.of(100000)); + updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); updater.deleteAccount(ADDRESS); updater.commit(); assertThat(worldState.rootHash()).isEqualTo(MerkleTrie.EMPTY_TRIE_NODE_HASH); @@ -117,11 +117,11 @@ public void removeAccount_UpdatedAccount() { } @Test - public void removeAccount_AccountExists() { + void removeAccount_AccountExists() { // Create a world state with one account final MutableWorldState worldState = createEmpty(); WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).getMutable().setBalance(Wei.of(100000)); + updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); updater.commit(); assertThat(worldState.get(ADDRESS)).isNotNull(); assertThat(worldState.rootHash()).isNotEqualTo(MerkleTrie.EMPTY_TRIE_NODE_HASH); @@ -138,11 +138,11 @@ public void removeAccount_AccountExists() { } @Test - public void removeAccount_AccountExistsAndIsPersisted() { + void removeAccount_AccountExistsAndIsPersisted() { // Create a world state with one account final MutableWorldState worldState = createEmpty(); WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).getMutable().setBalance(Wei.of(100000)); + updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); updater.commit(); worldState.persist(null); assertThat(worldState.get(ADDRESS)).isNotNull(); @@ -164,35 +164,35 @@ public void removeAccount_AccountExistsAndIsPersisted() { } @Test - public void streamAccounts_empty() { + void streamAccounts_empty() { final MutableWorldState worldState = createEmpty(); final Stream accounts = worldState.streamAccounts(Bytes32.ZERO, 10); - assertThat(accounts.count()).isEqualTo(0L); + assertThat(accounts.count()).isZero(); } @Test - public void streamAccounts_singleAccount() { + void streamAccounts_singleAccount() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).getMutable().setBalance(Wei.of(100000)); + updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); updater.commit(); List accounts = worldState.streamAccounts(Bytes32.ZERO, 10).collect(Collectors.toList()); - assertThat(accounts.size()).isEqualTo(1L); + assertThat(accounts).hasSize(1); assertThat(accounts.get(0).getAddress()).hasValue(ADDRESS); assertThat(accounts.get(0).getBalance()).isEqualTo(Wei.of(100000)); // Check again after persisting worldState.persist(null); accounts = worldState.streamAccounts(Bytes32.ZERO, 10).collect(Collectors.toList()); - assertThat(accounts.size()).isEqualTo(1L); + assertThat(accounts).hasSize(1); assertThat(accounts.get(0).getAddress()).hasValue(ADDRESS); assertThat(accounts.get(0).getBalance()).isEqualTo(Wei.of(100000)); } @Test - public void streamAccounts_multipleAccounts() { + void streamAccounts_multipleAccounts() { final Address addr1 = Address.fromHexString("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"); final Address addr2 = Address.fromHexString("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0c"); @@ -200,10 +200,10 @@ public void streamAccounts_multipleAccounts() { final WorldUpdater updater = worldState.updater(); // Create an account - final MutableAccount accountA = updater.createAccount(addr1).getMutable(); + final MutableAccount accountA = updater.createAccount(addr1); accountA.setBalance(Wei.of(100000)); // Create another - final MutableAccount accountB = updater.createAccount(addr2).getMutable(); + final MutableAccount accountB = updater.createAccount(addr2); accountB.setNonce(1); // Commit changes updater.commit(); @@ -217,16 +217,14 @@ public void streamAccounts_multipleAccounts() { final Hash startHash = accountAIsFirst ? accountA.getAddressHash() : accountB.getAddressHash(); // Get first account - final List firstAccount = - worldState.streamAccounts(startHash, 1).collect(Collectors.toList()); - assertThat(firstAccount.size()).isEqualTo(1L); + final List firstAccount = worldState.streamAccounts(startHash, 1).toList(); + assertThat(firstAccount).hasSize(1); assertThat(firstAccount.get(0).getAddress()) .hasValue(accountAIsFirst ? accountA.getAddress() : accountB.getAddress()); // Get both accounts - final List allAccounts = - worldState.streamAccounts(Bytes32.ZERO, 2).collect(Collectors.toList()); - assertThat(allAccounts.size()).isEqualTo(2L); + final List allAccounts = worldState.streamAccounts(Bytes32.ZERO, 2).toList(); + assertThat(allAccounts).hasSize(2); assertThat(allAccounts.get(0).getAddress()) .hasValue(accountAIsFirst ? accountA.getAddress() : accountB.getAddress()); assertThat(allAccounts.get(1).getAddress()) @@ -235,14 +233,14 @@ public void streamAccounts_multipleAccounts() { // Get second account final Bytes32 startHashForSecondAccount = UInt256.fromBytes(startHash).add(1L); final List secondAccount = - worldState.streamAccounts(startHashForSecondAccount, 100).collect(Collectors.toList()); - assertThat(secondAccount.size()).isEqualTo(1L); + worldState.streamAccounts(startHashForSecondAccount, 100).toList(); + assertThat(secondAccount).hasSize(1); assertThat(secondAccount.get(0).getAddress()) .hasValue(accountAIsFirst ? accountB.getAddress() : accountA.getAddress()); } @Test - public void commitAndPersist() { + void commitAndPersist() { final KeyValueStorage storage = new InMemoryKeyValueStorage(); final WorldStateKeyValueStorage kvWorldStateStorage = new WorldStateKeyValueStorage(storage); final MutableWorldState worldState = createEmpty(kvWorldStateStorage); @@ -252,7 +250,7 @@ public void commitAndPersist() { Hash.fromHexString("0xa3e1c133a5a51b03399ed9ad0380f3182e9e18322f232b816dd4b9094f871e1b"); // Update account and assert we get the expected response from updater - updater.createAccount(ADDRESS).getMutable().setBalance(newBalance); + updater.createAccount(ADDRESS).setBalance(newBalance); assertThat(updater.get(ADDRESS)).isNotNull(); assertThat(updater.get(ADDRESS).getBalance()).isEqualTo(newBalance); @@ -285,10 +283,10 @@ public void commitAndPersist() { } @Test - public void getAccountNonce_AccountExists() { + void getAccountNonce_AccountExists() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).getMutable().setNonce(1L); + updater.createAccount(ADDRESS).setNonce(1L); updater.commit(); assertThat(worldState.get(ADDRESS).getNonce()).isEqualTo(1L); assertThat(worldState.rootHash()) @@ -298,10 +296,10 @@ public void getAccountNonce_AccountExists() { } @Test - public void replaceAccountNonce() { + void replaceAccountNonce() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); + final MutableAccount account = updater.createAccount(ADDRESS); account.setNonce(1L); account.setNonce(2L); updater.commit(); @@ -313,19 +311,19 @@ public void replaceAccountNonce() { } @Test - public void getAccountBalance_AccountExists() { + void getAccountBalance_AccountExists() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - updater.createAccount(ADDRESS).getMutable().setBalance(Wei.of(100000)); + updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); updater.commit(); assertThat(worldState.get(ADDRESS).getBalance()).isEqualTo(Wei.of(100000)); } @Test - public void replaceAccountBalance() { + void replaceAccountBalance() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); + final MutableAccount account = updater.createAccount(ADDRESS); account.setBalance(Wei.of(100000)); account.setBalance(Wei.of(200000)); updater.commit(); @@ -337,10 +335,10 @@ public void replaceAccountBalance() { } @Test - public void setStorageValue_ZeroValue() { + void setStorageValue_ZeroValue() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); + final MutableAccount account = updater.createAccount(ADDRESS); account.setBalance(Wei.of(100000)); account.setStorageValue(UInt256.ZERO, UInt256.ZERO); updater.commit(); @@ -352,10 +350,10 @@ public void setStorageValue_ZeroValue() { } @Test - public void setStorageValue_NonzeroValue() { + void setStorageValue_NonzeroValue() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); + final MutableAccount account = updater.createAccount(ADDRESS); account.setBalance(Wei.of(100000)); account.setStorageValue(UInt256.ONE, UInt256.valueOf(2)); updater.commit(); @@ -367,10 +365,10 @@ public void setStorageValue_NonzeroValue() { } @Test - public void replaceStorageValue_NonzeroValue() { + void replaceStorageValue_NonzeroValue() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); + final MutableAccount account = updater.createAccount(ADDRESS); account.setBalance(Wei.of(100000)); account.setStorageValue(UInt256.ONE, UInt256.valueOf(2)); account.setStorageValue(UInt256.ONE, UInt256.valueOf(3)); @@ -383,10 +381,10 @@ public void replaceStorageValue_NonzeroValue() { } @Test - public void replaceStorageValue_ZeroValue() { + void replaceStorageValue_ZeroValue() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); + final MutableAccount account = updater.createAccount(ADDRESS); account.setBalance(Wei.of(100000)); account.setStorageValue(UInt256.ONE, UInt256.valueOf(2)); account.setStorageValue(UInt256.ONE, UInt256.ZERO); @@ -398,15 +396,15 @@ public void replaceStorageValue_ZeroValue() { } @Test - public void getOriginalStorageValue() { + void getOriginalStorageValue() { final MutableWorldState worldState = createEmpty(); final WorldUpdater setupUpdater = worldState.updater(); - final MutableAccount setupAccount = setupUpdater.createAccount(ADDRESS).getMutable(); + final MutableAccount setupAccount = setupUpdater.createAccount(ADDRESS); setupAccount.setStorageValue(UInt256.ONE, UInt256.valueOf(2)); setupUpdater.commit(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.getOrCreate(ADDRESS).getMutable(); + final MutableAccount account = updater.getOrCreate(ADDRESS); assertThat(account.getOriginalStorageValue(UInt256.ONE)).isEqualTo(UInt256.valueOf(2)); account.setStorageValue(UInt256.ONE, UInt256.valueOf(3)); @@ -414,35 +412,35 @@ public void getOriginalStorageValue() { } @Test - public void originalStorageValueIsAlwaysZeroIfStorageWasCleared() { + void originalStorageValueIsAlwaysZeroIfStorageWasCleared() { final MutableWorldState worldState = createEmpty(); final WorldUpdater setupUpdater = worldState.updater(); - final MutableAccount setupAccount = setupUpdater.createAccount(ADDRESS).getMutable(); + final MutableAccount setupAccount = setupUpdater.createAccount(ADDRESS); setupAccount.setStorageValue(UInt256.ONE, UInt256.valueOf(2)); setupUpdater.commit(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.getOrCreate(ADDRESS).getMutable(); + final MutableAccount account = updater.getOrCreate(ADDRESS); account.clearStorage(); assertThat(account.getOriginalStorageValue(UInt256.ONE)).isEqualTo(UInt256.ZERO); } @Test - public void clearStorage() { + void clearStorage() { final UInt256 storageKey = UInt256.ONE; final UInt256 storageValue = UInt256.valueOf(2L); // Create a world state with one account final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - MutableAccount account = updater.createAccount(ADDRESS).getMutable(); + MutableAccount account = updater.createAccount(ADDRESS); account.setBalance(Wei.of(100000)); account.setStorageValue(storageKey, storageValue); assertThat(account.getStorageValue(storageKey)).isEqualTo(storageValue); // Clear storage - account = updater.getAccount(ADDRESS).getMutable(); + account = updater.getAccount(ADDRESS); assertThat(account).isNotNull(); assertThat(account.getStorageValue(storageKey)).isEqualTo(storageValue); account.clearStorage(); @@ -462,14 +460,14 @@ public void clearStorage() { } @Test - public void clearStorage_AfterPersisting() { + void clearStorage_AfterPersisting() { final UInt256 storageKey = UInt256.ONE; final UInt256 storageValue = UInt256.valueOf(2L); // Create a world state with one account final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - MutableAccount account = updater.createAccount(ADDRESS).getMutable(); + MutableAccount account = updater.createAccount(ADDRESS); account.setBalance(Wei.of(100000)); account.setStorageValue(storageKey, storageValue); updater.commit(); @@ -478,7 +476,7 @@ public void clearStorage_AfterPersisting() { assertThat(worldState.rootHash()).isNotEqualTo(MerkleTrie.EMPTY_TRIE_NODE_HASH); // Clear storage - account = updater.getAccount(ADDRESS).getMutable(); + account = updater.getAccount(ADDRESS); assertThat(account).isNotNull(); assertThat(account.getStorageValue(storageKey)).isEqualTo(storageValue); account.clearStorage(); @@ -499,7 +497,7 @@ public void clearStorage_AfterPersisting() { } @Test - public void clearStorageThenEdit() { + void clearStorageThenEdit() { final UInt256 storageKey = UInt256.ONE; final UInt256 originalStorageValue = UInt256.valueOf(2L); final UInt256 newStorageValue = UInt256.valueOf(3L); @@ -507,13 +505,13 @@ public void clearStorageThenEdit() { // Create a world state with one account final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - MutableAccount account = updater.createAccount(ADDRESS).getMutable(); + MutableAccount account = updater.createAccount(ADDRESS); account.setBalance(Wei.of(100000)); account.setStorageValue(storageKey, originalStorageValue); assertThat(account.getStorageValue(storageKey)).isEqualTo(originalStorageValue); // Clear storage then edit - account = updater.getAccount(ADDRESS).getMutable(); + account = updater.getAccount(ADDRESS); assertThat(account).isNotNull(); assertThat(account.getStorageValue(storageKey)).isEqualTo(originalStorageValue); assertThat(updater.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(originalStorageValue); @@ -534,7 +532,7 @@ public void clearStorageThenEdit() { } @Test - public void clearStorageThenEditAfterPersisting() { + void clearStorageThenEditAfterPersisting() { final UInt256 storageKey = UInt256.ONE; final UInt256 originalStorageValue = UInt256.valueOf(2L); final UInt256 newStorageValue = UInt256.valueOf(3L); @@ -542,7 +540,7 @@ public void clearStorageThenEditAfterPersisting() { // Create a world state with one account final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - MutableAccount account = updater.createAccount(ADDRESS).getMutable(); + MutableAccount account = updater.createAccount(ADDRESS); account.setBalance(Wei.of(100000)); account.setStorageValue(storageKey, originalStorageValue); assertThat(account.getStorageValue(storageKey)).isEqualTo(originalStorageValue); @@ -550,7 +548,7 @@ public void clearStorageThenEditAfterPersisting() { worldState.persist(null); // Clear storage then edit - account = updater.getAccount(ADDRESS).getMutable(); + account = updater.getAccount(ADDRESS); assertThat(account).isNotNull(); assertThat(account.getStorageValue(storageKey)).isEqualTo(originalStorageValue); assertThat(updater.get(ADDRESS).getStorageValue(storageKey)).isEqualTo(originalStorageValue); @@ -572,10 +570,10 @@ public void clearStorageThenEditAfterPersisting() { } @Test - public void replaceAccountCode() { + void replaceAccountCode() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater = worldState.updater(); - final MutableAccount account = updater.createAccount(ADDRESS).getMutable(); + final MutableAccount account = updater.createAccount(ADDRESS); account.setBalance(Wei.of(100000)); account.setCode(Bytes.of(1, 2, 3)); account.setCode(Bytes.of(3, 2, 1)); @@ -588,15 +586,15 @@ public void replaceAccountCode() { } @Test - public void revert() { + void revert() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater1 = worldState.updater(); - final MutableAccount account1 = updater1.createAccount(ADDRESS).getMutable(); + final MutableAccount account1 = updater1.createAccount(ADDRESS); account1.setBalance(Wei.of(200000)); updater1.commit(); final WorldUpdater updater2 = worldState.updater(); - final MutableAccount account2 = updater2.getAccount(ADDRESS).getMutable(); + final MutableAccount account2 = updater2.getAccount(ADDRESS); account2.setBalance(Wei.of(300000)); assertThat(updater2.get(ADDRESS).getBalance()).isEqualTo(Wei.of(300000)); @@ -613,27 +611,27 @@ public void revert() { } @Test - public void shouldReturnNullForGetMutableWhenAccountDeletedInAncestor() { + void shouldReturnNullForGetMutableWhenAccountDeletedInAncestor() { final MutableWorldState worldState = createEmpty(); final WorldUpdater updater1 = worldState.updater(); - final MutableAccount account1 = updater1.createAccount(ADDRESS).getMutable(); + final MutableAccount account1 = updater1.createAccount(ADDRESS); updater1.commit(); assertThat(updater1.get(ADDRESS)) .isEqualToComparingOnlyGivenFields(account1, "address", "nonce", "balance", "codeHash"); updater1.deleteAccount(ADDRESS); final WorldUpdater updater2 = updater1.updater(); - assertThat(updater2.get(ADDRESS)).isEqualTo(null); + assertThat(updater2.get(ADDRESS)).isNull(); final WorldUpdater updater3 = updater2.updater(); - assertThat(updater3.getAccount(ADDRESS)).isEqualTo(null); + assertThat(updater3.getAccount(ADDRESS)).isNull(); } @Test - public void shouldCombineUnchangedAndChangedValuesWhenRetrievingStorageEntries() { + void shouldCombineUnchangedAndChangedValuesWhenRetrievingStorageEntries() { final MutableWorldState worldState = createEmpty(); WorldUpdater updater = worldState.updater(); - MutableAccount account = updater.createAccount(ADDRESS).getMutable(); + MutableAccount account = updater.createAccount(ADDRESS); account.setBalance(Wei.of(100000)); account.setStorageValue(UInt256.ONE, UInt256.valueOf(2)); account.setStorageValue(UInt256.valueOf(2), UInt256.valueOf(5)); @@ -647,7 +645,7 @@ public void shouldCombineUnchangedAndChangedValuesWhenRetrievingStorageEntries() initialSetOfEntries.forEach(entry -> initialEntries.put(entry.getKeyHash(), entry)); updater = worldState.updater(); - account = updater.getAccount(ADDRESS).getMutable(); + account = updater.getAccount(ADDRESS); account.setStorageValue(UInt256.ONE, UInt256.valueOf(3)); account.setStorageValue(UInt256.valueOf(3), UInt256.valueOf(6)); diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java index 63005b81140..9d2faeb7dc8 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java @@ -354,7 +354,6 @@ static T8nResult runTest( : Wei.of(Long.decode(rewardString)); worldStateUpdater .getOrCreateSenderAccount(blockHeader.getCoinbase()) - .getMutable() .incrementBalance(reward); } diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java index 9518d2c99d4..659847c1211 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java @@ -81,7 +81,7 @@ public Map getStorage() { static void insertAccount( final WorldUpdater updater, final Address address, final AccountMock toCopy) { - final MutableAccount account = updater.getOrCreate(address).getMutable(); + final MutableAccount account = updater.getOrCreate(address); account.setNonce(toCopy.getNonce()); account.setBalance(toCopy.getBalance()); account.setCode(toCopy.getCode()); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/account/EvmAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/account/EvmAccount.java deleted file mode 100644 index ecd3cf3ceaf..00000000000 --- a/evm/src/main/java/org/hyperledger/besu/evm/account/EvmAccount.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.evm.account; - -import org.hyperledger.besu.evm.ModificationNotAllowedException; - -/** The interface Evm account. */ -public interface EvmAccount extends Account { - /** - * Gets mutable account. - * - * @return the mutable account - * @throws ModificationNotAllowedException the modification not allowed exception - */ - public MutableAccount getMutable() throws ModificationNotAllowedException; -} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/account/MutableAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/account/MutableAccount.java index a4caf35c288..455df4a8b3f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/account/MutableAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/account/MutableAccount.java @@ -105,4 +105,9 @@ default Wei decrementBalance(final Wei value) { * @return a map of storage that has been modified. */ Map getUpdatedStorage(); + + /** + * Make this instance immutable. Used for private world state interactions with public contracts. + */ + void becomeImmutable(); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java index 75c86070950..b8a4db21eab 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleAccount.java @@ -21,7 +21,6 @@ import org.hyperledger.besu.evm.ModificationNotAllowedException; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.AccountStorageEntry; -import org.hyperledger.besu.evm.account.EvmAccount; import org.hyperledger.besu.evm.account.MutableAccount; import java.util.HashMap; @@ -35,10 +34,12 @@ import org.apache.tuweni.units.bigints.UInt256; /** The Simple account. */ -public class SimpleAccount implements EvmAccount, MutableAccount { +public class SimpleAccount implements MutableAccount { private final Account parent; + private boolean mutable = true; + private Address address; private final Supplier addressHash = Suppliers.memoize(() -> address == null ? Hash.ZERO : address.addressHash()); @@ -137,34 +138,44 @@ public NavigableMap storageEntriesFrom( "Storage iteration not supported in simple account facade"); } - @Override - public MutableAccount getMutable() throws ModificationNotAllowedException { - return this; - } - @Override public void setNonce(final long value) { + if (!mutable) { + throw new ModificationNotAllowedException(); + } nonce = value; } @Override public void setBalance(final Wei value) { + if (!mutable) { + throw new ModificationNotAllowedException(); + } balance = value; } @Override public void setCode(final Bytes code) { + if (!mutable) { + throw new ModificationNotAllowedException(); + } this.code = code; codeHash = Suppliers.memoize(() -> this.code == null ? Hash.EMPTY : Hash.hash(this.code)); } @Override public void setStorageValue(final UInt256 key, final UInt256 value) { + if (!mutable) { + throw new ModificationNotAllowedException(); + } storage.put(key, value); } @Override public void clearStorage() { + if (!mutable) { + throw new ModificationNotAllowedException(); + } storage.clear(); } @@ -172,4 +183,9 @@ public void clearStorage() { public Map getUpdatedStorage() { return storage; } + + @Override + public void becomeImmutable() { + mutable = false; + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java index a7288602083..6bc19ca0dae 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/SimpleWorld.java @@ -18,14 +18,13 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.account.EvmAccount; +import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; /** The Simple world. */ public class SimpleWorld implements WorldUpdater { @@ -66,14 +65,14 @@ public Account get(final Address address) { } @Override - public EvmAccount createAccount(final Address address, final long nonce, final Wei balance) { + public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) { SimpleAccount account = new SimpleAccount(address, nonce, balance); accounts.put(address, account); return account; } @Override - public EvmAccount getAccount(final Address address) { + public MutableAccount getAccount(final Address address) { if (accounts.containsKey(address)) { return accounts.get(address); } else if (parent != null) { @@ -98,7 +97,7 @@ public Collection
getDeletedAccountAddresses() { return accounts.entrySet().stream() .filter(e -> e.getValue() == null) .map(Map.Entry::getKey) - .collect(Collectors.toList()); + .toList(); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java index 50ced6fd8b7..d695edf1c8c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java @@ -79,7 +79,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { final Wei value = Wei.wrap(frame.getStackItem(0)); final Address address = frame.getRecipientAddress(); - final MutableAccount account = frame.getWorldUpdater().getAccount(address).getMutable(); + final MutableAccount account = frame.getWorldUpdater().getAccount(address); frame.clearReturnData(); final long inputOffset = clampedToLong(frame.getStackItem(1)); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SStoreOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SStoreOperation.java index b4fa49751e4..d5055e0bd81 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SStoreOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SStoreOperation.java @@ -66,8 +66,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { final UInt256 key = UInt256.fromBytes(frame.popStackItem()); final UInt256 newValue = UInt256.fromBytes(frame.popStackItem()); - final MutableAccount account = - frame.getWorldUpdater().getAccount(frame.getRecipientAddress()).getMutable(); + final MutableAccount account = frame.getWorldUpdater().getAccount(frame.getRecipientAddress()); if (account == null) { return ILLEGAL_STATE_CHANGE; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SelfDestructOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SelfDestructOperation.java index 98807652034..3f29fcea57e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SelfDestructOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SelfDestructOperation.java @@ -75,10 +75,9 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { } // We passed preliminary checks, get mutable accounts. - final MutableAccount originatorAccount = - frame.getWorldUpdater().getAccount(originatorAddress).getMutable(); + final MutableAccount originatorAccount = frame.getWorldUpdater().getAccount(originatorAddress); final MutableAccount beneficiaryAccount = - frame.getWorldUpdater().getOrCreate(beneficiaryAddress).getMutable(); + frame.getWorldUpdater().getOrCreate(beneficiaryAddress); // Do the "sweep," all modes send all originator balance to the beneficiary account. originatorAccount.decrementBalance(originatorBalance); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java b/evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java index 17fa4da60b8..7acea893724 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java @@ -108,12 +108,11 @@ public void start(final MessageFrame frame, final OperationTracer operationTrace } try { - final MutableAccount sender = frame.getWorldUpdater().getSenderAccount(frame).getMutable(); + final MutableAccount sender = frame.getWorldUpdater().getSenderAccount(frame); sender.decrementBalance(frame.getValue()); Address contractAddress = frame.getContractAddress(); - final MutableAccount contract = - frame.getWorldUpdater().getOrCreate(contractAddress).getMutable(); + final MutableAccount contract = frame.getWorldUpdater().getOrCreate(contractAddress); if (accountExists(contract)) { LOG.trace( "Contract creation error: account has already been created for address {}", @@ -169,7 +168,7 @@ public void codeSuccess(final MessageFrame frame, final OperationTracer operatio // Finalize contract creation, setting the contract code. final MutableAccount contract = - frame.getWorldUpdater().getOrCreate(frame.getContractAddress()).getMutable(); + frame.getWorldUpdater().getOrCreate(frame.getContractAddress()); contract.setCode(contractCode); LOG.trace( "Successful creation of contract {} with code of size {} (Gas remaining: {})", diff --git a/evm/src/main/java/org/hyperledger/besu/evm/processor/MessageCallProcessor.java b/evm/src/main/java/org/hyperledger/besu/evm/processor/MessageCallProcessor.java index b1d41da97a1..0c100e33407 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/processor/MessageCallProcessor.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/processor/MessageCallProcessor.java @@ -18,7 +18,7 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.ModificationNotAllowedException; -import org.hyperledger.besu.evm.account.EvmAccount; +import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.precompile.PrecompileContractRegistry; @@ -28,8 +28,8 @@ import java.util.Collection; import java.util.Objects; import java.util.Optional; +import java.util.Set; -import com.google.common.collect.ImmutableSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,7 +61,7 @@ public MessageCallProcessor( * @param precompiles the precompiles */ public MessageCallProcessor(final EVM evm, final PrecompileContractRegistry precompiles) { - super(evm, ImmutableSet.of()); + super(evm, Set.of()); this.precompiles = precompiles; } @@ -102,11 +102,11 @@ protected void codeSuccess(final MessageFrame frame, final OperationTracer opera * of the world state of this executor. */ private void transferValue(final MessageFrame frame) { - final EvmAccount senderAccount = frame.getWorldUpdater().getSenderAccount(frame); + final MutableAccount senderAccount = frame.getWorldUpdater().getSenderAccount(frame); // The yellow paper explicitly states that if the recipient account doesn't exist at this // point, it is created. Even if the value is zero we are still creating an account with 0x! - final EvmAccount recipientAccount = + final MutableAccount recipientAccount = frame.getWorldUpdater().getOrCreate(frame.getRecipientAddress()); if (Objects.equals(frame.getValue(), Wei.ZERO)) { @@ -125,9 +125,8 @@ private void transferValue(final MessageFrame frame) { if (frame.getRecipientAddress().equals(frame.getSenderAddress())) { LOG.trace("Message call of {} to itself: no fund transferred", frame.getSenderAddress()); } else { - final Wei prevSenderBalance = senderAccount.getMutable().decrementBalance(frame.getValue()); - final Wei prevRecipientBalance = - recipientAccount.getMutable().incrementBalance(frame.getValue()); + final Wei prevSenderBalance = senderAccount.decrementBalance(frame.getValue()); + final Wei prevRecipientBalance = recipientAccount.incrementBalance(frame.getValue()); LOG.trace( "Transferred value {} for message call from {} ({} -> {}) to {} ({} -> {})", diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java index fde3aa8c244..41cacdbea06 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java @@ -17,7 +17,6 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.account.EvmAccount; import org.hyperledger.besu.evm.account.MutableAccount; import java.util.Collection; @@ -78,11 +77,11 @@ protected UpdateTrackingAccount track(final UpdateTrackingAccount account) } @Override - public EvmAccount createAccount(final Address address, final long nonce, final Wei balance) { + public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) { final UpdateTrackingAccount account = new UpdateTrackingAccount<>(address); account.setNonce(nonce); account.setBalance(balance); - return new WrappedEvmAccount(track(account)); + return track(account); } @Override @@ -99,11 +98,11 @@ public Account get(final Address address) { } @Override - public EvmAccount getAccount(final Address address) { + public MutableAccount getAccount(final Address address) { // We may have updated it already, so check that first. final MutableAccount existing = updatedAccounts.get(address); if (existing != null) { - return new WrappedEvmAccount(existing); + return existing; } if (deletedAccounts.contains(address)) { return null; @@ -114,7 +113,7 @@ public EvmAccount getAccount(final Address address) { if (origin == null) { return null; } else { - return new WrappedEvmAccount(track(new UpdateTrackingAccount<>(origin))); + return track(new UpdateTrackingAccount<>(origin)); } } @@ -150,8 +149,8 @@ protected W wrappedWorldView() { @Override public Optional parentUpdater() { - if (world instanceof WorldUpdater) { - return Optional.of((WorldUpdater) world); + if (world instanceof WorldUpdater worldUpdater) { + return Optional.of(worldUpdater); } else { return Optional.empty(); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java index 9b9f5e2a127..bc8a0fdfcd5 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/UpdateTrackingAccount.java @@ -24,7 +24,6 @@ import org.hyperledger.besu.evm.ModificationNotAllowedException; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.AccountStorageEntry; -import org.hyperledger.besu.evm.account.EvmAccount; import org.hyperledger.besu.evm.account.MutableAccount; import java.util.Map; @@ -47,12 +46,14 @@ * * @param the type parameter */ -public class UpdateTrackingAccount implements MutableAccount, EvmAccount { +public class UpdateTrackingAccount implements MutableAccount { private final Address address; private final Hash addressHash; @Nullable private A account; // null if this is a new account. + private boolean mutable = true; + private long nonce; private Wei balance; @@ -172,6 +173,9 @@ public long getNonce() { @Override public void setNonce(final long value) { + if (!mutable) { + throw new ModificationNotAllowedException(); + } this.nonce = value; } @@ -182,6 +186,9 @@ public Wei getBalance() { @Override public void setBalance(final Wei value) { + if (!mutable) { + throw new ModificationNotAllowedException(); + } this.balance = value; } @@ -214,6 +221,9 @@ public boolean hasCode() { @Override public void setCode(final Bytes code) { + if (!mutable) { + throw new ModificationNotAllowedException(); + } this.updatedCode = code; this.updatedCodeHash = null; } @@ -271,15 +281,26 @@ public NavigableMap storageEntriesFrom( @Override public void setStorageValue(final UInt256 key, final UInt256 value) { + if (!mutable) { + throw new ModificationNotAllowedException(); + } updatedStorage.put(key, value); } @Override public void clearStorage() { + if (!mutable) { + throw new ModificationNotAllowedException(); + } storageWasCleared = true; updatedStorage.clear(); } + @Override + public void becomeImmutable() { + mutable = false; + } + /** * Gets storage was cleared. * @@ -308,9 +329,4 @@ public String toString() { "%s -> {nonce: %s, balance:%s, code:%s, storage:%s }", address, nonce, balance, updatedCode == null ? "[not updated]" : updatedCode, storage); } - - @Override - public MutableAccount getMutable() throws ModificationNotAllowedException { - return this; - } } 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 a9b012facbf..fe88c86a7df 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 @@ -17,7 +17,6 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.account.EvmAccount; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -48,7 +47,7 @@ public interface WorldUpdater extends MutableWorldView { * @return the account {@code address}, which will have nonce {@code nonce}, balance {@code * balance} and empty code and storage. */ - EvmAccount createAccount(Address address, long nonce, Wei balance); + MutableAccount createAccount(Address address, long nonce, Wei balance); /** * Creates a new account, or reset it (that is, act as if it was deleted and created anew) if it @@ -61,7 +60,7 @@ public interface WorldUpdater extends MutableWorldView { * @return the account {@code address}, which will have 0 for the nonce and balance and empty code * and storage. */ - default EvmAccount createAccount(final Address address) { + default MutableAccount createAccount(final Address address) { return createAccount(address, Account.DEFAULT_NONCE, Account.DEFAULT_BALANCE); } @@ -73,8 +72,8 @@ default EvmAccount createAccount(final Address address) { * #getAccount(Address)}, otherwise, it is created and returned as if by {@link * #createAccount(Address)} (and thus all his fields will be zero/empty). */ - default EvmAccount getOrCreate(final Address address) { - final EvmAccount account = getAccount(address); + default MutableAccount getOrCreate(final Address address) { + final MutableAccount account = getAccount(address); return account == null ? createAccount(address) : account; } @@ -85,7 +84,7 @@ default EvmAccount getOrCreate(final Address address) { * @param address the address of the account. * @return the account of the sender for {@code address} */ - default EvmAccount getOrCreateSenderAccount(final Address address) { + default MutableAccount getOrCreateSenderAccount(final Address address) { return getOrCreate(address); } @@ -96,7 +95,7 @@ default EvmAccount getOrCreateSenderAccount(final Address address) { * @param address the address of the account. * @return the account {@code address}, or {@code null} if the account does not exist. */ - EvmAccount getAccount(Address address); + MutableAccount getAccount(Address address); /** * Retrieves the senders account, returning a modifiable object (whose updates are accumulated by @@ -105,7 +104,7 @@ default EvmAccount getOrCreateSenderAccount(final Address address) { * @param frame the current message frame. * @return the account {@code address}, or {@code null} if the account does not exist. */ - default EvmAccount getSenderAccount(final MessageFrame frame) { + default MutableAccount getSenderAccount(final MessageFrame frame) { return getAccount(frame.getSenderAddress()); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WrappedEvmAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WrappedEvmAccount.java deleted file mode 100644 index 8d644d9650d..00000000000 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WrappedEvmAccount.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.evm.worldstate; - -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.evm.ModificationNotAllowedException; -import org.hyperledger.besu.evm.account.AccountStorageEntry; -import org.hyperledger.besu.evm.account.EvmAccount; -import org.hyperledger.besu.evm.account.MutableAccount; - -import java.util.NavigableMap; - -import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.bytes.Bytes32; -import org.apache.tuweni.units.bigints.UInt256; - -/** The Wrapped evm account. */ -public class WrappedEvmAccount implements EvmAccount { - private final MutableAccount mutableAccount; - - /** - * Is immutable. - * - * @return the boolean - */ - public boolean isImmutable() { - return isImmutable; - } - - /** - * Sets immutable. - * - * @param immutable the immutable - */ - public void setImmutable(final boolean immutable) { - isImmutable = immutable; - } - - private boolean isImmutable; - - /** - * Instantiates a new Wrapped evm account. - * - * @param mutableAccount the mutable account - */ - public WrappedEvmAccount(final MutableAccount mutableAccount) { - - this.mutableAccount = mutableAccount; - this.isImmutable = false; - } - - @Override - public MutableAccount getMutable() throws ModificationNotAllowedException { - if (isImmutable) { - throw new ModificationNotAllowedException(); - } - return mutableAccount; - } - - @Override - public Address getAddress() { - return mutableAccount.getAddress(); - } - - @Override - public Hash getAddressHash() { - return mutableAccount.getAddressHash(); - } - - @Override - public long getNonce() { - return mutableAccount.getNonce(); - } - - @Override - public Wei getBalance() { - return mutableAccount.getBalance(); - } - - @Override - public Bytes getCode() { - return mutableAccount.getCode(); - } - - @Override - public Hash getCodeHash() { - return mutableAccount.getCodeHash(); - } - - @Override - public UInt256 getStorageValue(final UInt256 key) { - return mutableAccount.getStorageValue(key); - } - - @Override - public UInt256 getOriginalStorageValue(final UInt256 key) { - return mutableAccount.getOriginalStorageValue(key); - } - - @Override - public NavigableMap storageEntriesFrom( - final Bytes32 startKeyHash, final int limit) { - return mutableAccount.storageEntriesFrom(startKeyHash, limit); - } -} diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java index aa3dfd40217..1a703f489d7 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java @@ -39,7 +39,6 @@ import org.hyperledger.besu.evm.processor.ContractCreationProcessor; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount; import java.util.Deque; import java.util.List; @@ -52,10 +51,8 @@ class AbstractCreateOperationTest { private final WorldUpdater worldUpdater = mock(WorldUpdater.class); - private final WrappedEvmAccount account = mock(WrappedEvmAccount.class); - private final WrappedEvmAccount newAccount = mock(WrappedEvmAccount.class); - private final MutableAccount mutableAccount = mock(MutableAccount.class); - private final MutableAccount newMutableAccount = mock(MutableAccount.class); + private final MutableAccount account = mock(MutableAccount.class); + private final MutableAccount newAccount = mock(MutableAccount.class); private final FakeCreateOperation operation = new FakeCreateOperation(new ConstantinopleGasCalculator(), Integer.MAX_VALUE); @@ -169,15 +166,13 @@ private void executeOperation(final Bytes contract, final EVM evm) { messageFrame.expandMemory(0, 500); messageFrame.writeMemory(memoryOffset.trimLeadingZeros().toInt(), contract.size(), contract); - when(account.getMutable()).thenReturn(mutableAccount); when(account.getNonce()).thenReturn(55L); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); + when(account.getBalance()).thenReturn(Wei.ZERO); when(worldUpdater.getAccount(any())).thenReturn(account); when(worldUpdater.get(any())).thenReturn(account); when(worldUpdater.getSenderAccount(any())).thenReturn(account); when(worldUpdater.getOrCreate(any())).thenReturn(newAccount); - when(newAccount.getMutable()).thenReturn(newMutableAccount); - when(newMutableAccount.getCode()).thenReturn(Bytes.EMPTY); + when(newAccount.getCode()).thenReturn(Bytes.EMPTY); when(worldUpdater.updater()).thenReturn(worldUpdater); operation.execute(messageFrame, evm); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java index 5590cd09b4f..d9b41d4681e 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/Create2OperationTest.java @@ -40,7 +40,6 @@ import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount; import java.util.Deque; import java.util.List; @@ -56,11 +55,9 @@ public class Create2OperationTest { private MessageFrame messageFrame; private final WorldUpdater worldUpdater = mock(WorldUpdater.class); - private final WrappedEvmAccount account = mock(WrappedEvmAccount.class); - private final MutableAccount mutableAccount = mock(MutableAccount.class); + private final MutableAccount account = mock(MutableAccount.class); private final EVM evm = mock(EVM.class); - private final WrappedEvmAccount newAccount = mock(WrappedEvmAccount.class); - private final MutableAccount newMutableAccount = mock(MutableAccount.class); + private final MutableAccount newAccount = mock(MutableAccount.class); private final Create2Operation operation = new Create2Operation(new ConstantinopleGasCalculator(), Integer.MAX_VALUE); @@ -147,7 +144,6 @@ public void setUp(final String sender, final String salt, final String code) { final UInt256 memoryOffset = UInt256.fromHexString("0xFF"); final Bytes codeBytes = Bytes.fromHexString(code); final UInt256 memoryLength = UInt256.valueOf(codeBytes.size()); - when(account.getMutable()).thenReturn(mutableAccount); messageFrame = MessageFrame.builder() .type(MessageFrame.Type.CONTRACT_CREATION) @@ -174,7 +170,7 @@ public void setUp(final String sender, final String salt, final String code) { messageFrame.expandMemory(0, 500); messageFrame.writeMemory(memoryOffset.trimLeadingZeros().toInt(), code.length(), codeBytes); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); + when(account.getBalance()).thenReturn(Wei.ZERO); when(worldUpdater.getAccount(any())).thenReturn(account); when(worldUpdater.updater()).thenReturn(worldUpdater); when(evm.getCode(any(), any())) @@ -214,15 +210,13 @@ void shanghaiMaxInitCodeSizeCreate() { final UInt256 memoryLength = UInt256.fromHexString("0xc000"); final MessageFrame messageFrame = testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1); - when(account.getMutable()).thenReturn(mutableAccount); when(account.getNonce()).thenReturn(55L); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); + when(account.getBalance()).thenReturn(Wei.ZERO); when(worldUpdater.getAccount(any())).thenReturn(account); when(worldUpdater.get(any())).thenReturn(account); when(worldUpdater.getSenderAccount(any())).thenReturn(account); when(worldUpdater.getOrCreate(any())).thenReturn(newAccount); - when(newAccount.getMutable()).thenReturn(newMutableAccount); - when(newMutableAccount.getCode()).thenReturn(Bytes.EMPTY); + when(newAccount.getCode()).thenReturn(Bytes.EMPTY); when(worldUpdater.updater()).thenReturn(worldUpdater); final EVM evm = MainnetEVMs.shanghai(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); @@ -244,15 +238,13 @@ void shanghaiMaxInitCodeSizePlus1Create() { final UInt256 memoryLength = UInt256.fromHexString("0xc001"); final MessageFrame messageFrame = testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1); - when(account.getMutable()).thenReturn(mutableAccount); when(account.getNonce()).thenReturn(55L); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); + when(account.getBalance()).thenReturn(Wei.ZERO); when(worldUpdater.getAccount(any())).thenReturn(account); when(worldUpdater.get(any())).thenReturn(account); when(worldUpdater.getSenderAccount(any())).thenReturn(account); when(worldUpdater.getOrCreate(any())).thenReturn(newAccount); - when(newAccount.getMutable()).thenReturn(newMutableAccount); - when(newMutableAccount.getCode()).thenReturn(Bytes.EMPTY); + when(newAccount.getCode()).thenReturn(Bytes.EMPTY); when(worldUpdater.updater()).thenReturn(worldUpdater); final EVM evm = MainnetEVMs.shanghai(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); @@ -314,8 +306,7 @@ void eofV1CannotCreateLegacy() { .build(); messageFrame.writeMemory(memoryOffset.toLong(), memoryLength.toLong(), SIMPLE_CREATE); - when(account.getMutable()).thenReturn(mutableAccount); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); + when(account.getBalance()).thenReturn(Wei.ZERO); when(worldUpdater.getAccount(any())).thenReturn(account); final EVM evm = MainnetEVMs.cancun(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); @@ -339,9 +330,8 @@ void legacyCanCreateEOFv1() { .build(); messageFrame.writeMemory(memoryOffset.toLong(), memoryLength.toLong(), SIMPLE_EOF); - when(account.getMutable()).thenReturn(mutableAccount); when(account.getNonce()).thenReturn(55L); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); + when(account.getBalance()).thenReturn(Wei.ZERO); when(worldUpdater.getAccount(any())).thenReturn(account); when(worldUpdater.get(any())).thenReturn(account); when(worldUpdater.getSenderAccount(any())).thenReturn(account); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java index b685b8702ff..822f515741e 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/CreateOperationTest.java @@ -40,7 +40,6 @@ import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount; import java.util.Deque; import java.util.List; @@ -53,10 +52,8 @@ class CreateOperationTest { private final WorldUpdater worldUpdater = mock(WorldUpdater.class); - private final WrappedEvmAccount account = mock(WrappedEvmAccount.class); - private final WrappedEvmAccount newAccount = mock(WrappedEvmAccount.class); - private final MutableAccount mutableAccount = mock(MutableAccount.class); - private final MutableAccount newMutableAccount = mock(MutableAccount.class); + private final MutableAccount account = mock(MutableAccount.class); + private final MutableAccount newAccount = mock(MutableAccount.class); private final CreateOperation operation = new CreateOperation(new ConstantinopleGasCalculator(), Integer.MAX_VALUE); private final CreateOperation maxInitCodeOperation = @@ -91,15 +88,13 @@ void createFromMemoryMutationSafe() { final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size()); final MessageFrame messageFrame = testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1); - when(account.getMutable()).thenReturn(mutableAccount); when(account.getNonce()).thenReturn(55L); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); + when(account.getBalance()).thenReturn(Wei.ZERO); when(worldUpdater.getAccount(any())).thenReturn(account); when(worldUpdater.get(any())).thenReturn(account); when(worldUpdater.getSenderAccount(any())).thenReturn(account); when(worldUpdater.getOrCreate(any())).thenReturn(newAccount); - when(newAccount.getMutable()).thenReturn(newMutableAccount); - when(newMutableAccount.getCode()).thenReturn(Bytes.EMPTY); + when(newAccount.getCode()).thenReturn(Bytes.EMPTY); when(worldUpdater.updater()).thenReturn(worldUpdater); final EVM evm = MainnetEVMs.london(EvmConfiguration.DEFAULT); @@ -131,9 +126,8 @@ void nonceTooLarge() { final MessageFrame messageFrame = testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1); when(worldUpdater.getAccount(any())).thenReturn(account); - when(account.getMutable()).thenReturn(mutableAccount); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); - when(mutableAccount.getNonce()).thenReturn(-1L); + when(account.getBalance()).thenReturn(Wei.ZERO); + when(account.getNonce()).thenReturn(-1L); final EVM evm = MainnetEVMs.london(EvmConfiguration.DEFAULT); operation.execute(messageFrame, evm); @@ -149,9 +143,8 @@ void messageFrameStackTooDeep() { testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1025); when(worldUpdater.getAccount(any())).thenReturn(account); - when(account.getMutable()).thenReturn(mutableAccount); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); - when(mutableAccount.getNonce()).thenReturn(55L); + when(account.getBalance()).thenReturn(Wei.ZERO); + when(account.getNonce()).thenReturn(55L); final EVM evm = MainnetEVMs.london(EvmConfiguration.DEFAULT); operation.execute(messageFrame, evm); @@ -171,9 +164,8 @@ void notEnoughValue() { } when(worldUpdater.getAccount(any())).thenReturn(account); - when(account.getMutable()).thenReturn(mutableAccount); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); - when(mutableAccount.getNonce()).thenReturn(55L); + when(account.getBalance()).thenReturn(Wei.ZERO); + when(account.getNonce()).thenReturn(55L); final EVM evm = MainnetEVMs.london(EvmConfiguration.DEFAULT); operation.execute(messageFrame, evm); @@ -187,15 +179,13 @@ void shanghaiMaxInitCodeSizeCreate() { final UInt256 memoryLength = UInt256.fromHexString("0xc000"); final MessageFrame messageFrame = testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1); - when(account.getMutable()).thenReturn(mutableAccount); when(account.getNonce()).thenReturn(55L); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); + when(account.getBalance()).thenReturn(Wei.ZERO); when(worldUpdater.getAccount(any())).thenReturn(account); when(worldUpdater.get(any())).thenReturn(account); when(worldUpdater.getSenderAccount(any())).thenReturn(account); when(worldUpdater.getOrCreate(any())).thenReturn(newAccount); - when(newAccount.getMutable()).thenReturn(newMutableAccount); - when(newMutableAccount.getCode()).thenReturn(Bytes.EMPTY); + when(newAccount.getCode()).thenReturn(Bytes.EMPTY); when(worldUpdater.updater()).thenReturn(worldUpdater); final EVM evm = MainnetEVMs.shanghai(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); @@ -217,15 +207,13 @@ void shanghaiMaxInitCodeSizePlus1Create() { final UInt256 memoryLength = UInt256.fromHexString("0xc001"); final MessageFrame messageFrame = testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1); - when(account.getMutable()).thenReturn(mutableAccount); when(account.getNonce()).thenReturn(55L); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); + when(account.getBalance()).thenReturn(Wei.ZERO); when(worldUpdater.getAccount(any())).thenReturn(account); when(worldUpdater.get(any())).thenReturn(account); when(worldUpdater.getSenderAccount(any())).thenReturn(account); when(worldUpdater.getOrCreate(any())).thenReturn(newAccount); - when(newAccount.getMutable()).thenReturn(newMutableAccount); - when(newMutableAccount.getCode()).thenReturn(Bytes.EMPTY); + when(newAccount.getCode()).thenReturn(Bytes.EMPTY); when(worldUpdater.updater()).thenReturn(worldUpdater); final EVM evm = MainnetEVMs.shanghai(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); @@ -247,8 +235,7 @@ void eofV1CannotCreateLegacy() { .build(); messageFrame.writeMemory(memoryOffset.toLong(), memoryLength.toLong(), SIMPLE_CREATE); - when(account.getMutable()).thenReturn(mutableAccount); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); + when(account.getBalance()).thenReturn(Wei.ZERO); when(worldUpdater.getAccount(any())).thenReturn(account); final EVM evm = MainnetEVMs.cancun(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT); @@ -271,9 +258,8 @@ void legacyCanCreateEOFv1() { .build(); messageFrame.writeMemory(memoryOffset.toLong(), memoryLength.toLong(), SIMPLE_EOF); - when(account.getMutable()).thenReturn(mutableAccount); when(account.getNonce()).thenReturn(55L); - when(mutableAccount.getBalance()).thenReturn(Wei.ZERO); + when(account.getBalance()).thenReturn(Wei.ZERO); when(worldUpdater.getAccount(any())).thenReturn(account); when(worldUpdater.get(any())).thenReturn(account); when(worldUpdater.getSenderAccount(any())).thenReturn(account); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCodeHashOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCodeHashOperationTest.java index 3127970c3b9..a7ca5eefe67 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCodeHashOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCodeHashOperationTest.java @@ -69,7 +69,7 @@ void shouldReturnZeroWhenAccountDoesNotExist() { @Test void shouldReturnHashOfEmptyDataWhenAccountExistsButDoesNotHaveCode() { - worldStateUpdater.getOrCreate(REQUESTED_ADDRESS).getMutable().setBalance(Wei.of(1)); + worldStateUpdater.getOrCreate(REQUESTED_ADDRESS).setBalance(Wei.of(1)); assertThat(executeOperation(REQUESTED_ADDRESS)).isEqualTo(Hash.EMPTY); } @@ -87,14 +87,14 @@ void shouldReturnZeroWhenPrecompiledContractHasNoBalance() { @Test void shouldReturnEmptyCodeHashWhenPrecompileHasBalance() { // Sending money to a precompile causes it to exist in the world state archive. - worldStateUpdater.getOrCreate(Address.ECREC).getMutable().setBalance(Wei.of(10)); + worldStateUpdater.getOrCreate(Address.ECREC).setBalance(Wei.of(10)); assertThat(executeOperation(Address.ECREC)).isEqualTo(Hash.EMPTY); } @Test void shouldGetHashOfAccountCodeWhenCodeIsPresent() { final Bytes code = Bytes.fromHexString("0xabcdef"); - final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS).getMutable(); + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); account.setCode(code); assertThat(executeOperation(REQUESTED_ADDRESS)).isEqualTo(Hash.hash(code)); } @@ -103,7 +103,7 @@ void shouldGetHashOfAccountCodeWhenCodeIsPresent() { void shouldZeroOutLeftMostBitsToGetAddress() { // If EXTCODEHASH of A is X, then EXTCODEHASH of A + 2**160 is X. final Bytes code = Bytes.fromHexString("0xabcdef"); - final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS).getMutable(); + final MutableAccount account = worldStateUpdater.getOrCreate(REQUESTED_ADDRESS); account.setCode(code); final UInt256 value = UInt256.fromBytes(Words.fromAddress(REQUESTED_ADDRESS)) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/SStoreOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/SStoreOperationTest.java index fbf21ab5102..e2f14dbdb2a 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/SStoreOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/SStoreOperationTest.java @@ -64,7 +64,7 @@ private MessageFrame createMessageFrame( .blockValues(blockHeader) .initialGas(initialGas) .build(); - worldStateUpdater.getOrCreate(address).getMutable().setBalance(Wei.of(1)); + worldStateUpdater.getOrCreate(address).setBalance(Wei.of(1)); worldStateUpdater.commit(); frame.setGasRemaining(remainingGas); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java index f596bed8aad..6c178dda2a9 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/SelfDestructOperationTest.java @@ -33,7 +33,6 @@ import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.operation.SelfDestructOperation; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount; import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.extension.ExtendWith; @@ -54,10 +53,8 @@ public class SelfDestructOperationTest { private MessageFrame messageFrame; @Mock private WorldUpdater worldUpdater; - @Mock private WrappedEvmAccount accountOriginator; - @Mock private WrappedEvmAccount accountBeneficiary; - @Mock private MutableAccount mutableAccountOriginator; - @Mock private MutableAccount mutableAccountBeneficiary; + @Mock private MutableAccount accountOriginator; + @Mock private MutableAccount accountBeneficiary; @Mock private EVM evm; private final SelfDestructOperation frontierOperation = @@ -104,9 +101,7 @@ void checkContractDeletionCommon( when(worldUpdater.get(beneficiaryAddress)).thenReturn(accountBeneficiary); } when(worldUpdater.getOrCreate(beneficiaryAddress)).thenReturn(accountBeneficiary); - when(accountOriginator.getMutable()).thenReturn(mutableAccountOriginator); when(accountOriginator.getBalance()).thenReturn(Wei.fromHexString(balanceHex)); - when(accountBeneficiary.getMutable()).thenReturn(mutableAccountBeneficiary); final Operation.OperationResult operationResult = operation.execute(messageFrame, evm); assertThat(operationResult).isNotNull(); @@ -115,10 +110,10 @@ void checkContractDeletionCommon( // some subset of these calls. verify(accountOriginator, atLeast(0)).getBalance(); verify(accountBeneficiary, atLeast(0)).getBalance(); - verify(mutableAccountOriginator, atLeast(0)).getBalance(); - verify(mutableAccountOriginator).decrementBalance(Wei.fromHexString(balanceHex)); - verify(mutableAccountOriginator, atLeast(0)).setBalance(Wei.ZERO); - verify(mutableAccountBeneficiary).incrementBalance(Wei.fromHexString(balanceHex)); + verify(accountOriginator, atLeast(0)).getBalance(); + verify(accountOriginator).decrementBalance(Wei.fromHexString(balanceHex)); + verify(accountOriginator, atLeast(0)).setBalance(Wei.ZERO); + verify(accountBeneficiary).incrementBalance(Wei.fromHexString(balanceHex)); } public static Object[][] params() { diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/TStoreOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/TStoreOperationTest.java index 403bd68a7a6..fed95ec0820 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/TStoreOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/TStoreOperationTest.java @@ -60,7 +60,7 @@ private MessageFrame createMessageFrame( .blockValues(blockHeader) .initialGas(initialGas) .build(); - worldStateUpdater.getOrCreate(address).getMutable().setBalance(Wei.of(1)); + worldStateUpdater.getOrCreate(address).setBalance(Wei.of(1)); worldStateUpdater.commit(); frame.setGasRemaining(remainingGas); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestCodeExecutor.java b/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestCodeExecutor.java index 02f6ffba191..7cc12b5867f 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestCodeExecutor.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestCodeExecutor.java @@ -83,7 +83,7 @@ public MessageFrame executeCode( public static void deployContract( final WorldUpdater worldUpdater, final Address contractAddress, final String codeHexString) { var updater = worldUpdater.updater(); - final MutableAccount contract = updater.getOrCreate(contractAddress).getMutable(); + final MutableAccount contract = updater.getOrCreate(contractAddress); contract.setNonce(0); contract.clearStorage(); @@ -95,8 +95,7 @@ public static WorldUpdater createInitialWorldState(final Consumer addressHash = Suppliers.memoize(() -> address == null ? Hash.ZERO : address.addressHash()); @@ -117,34 +118,44 @@ public NavigableMap storageEntriesFrom( throw new UnsupportedOperationException("Storage iteration not supported in toy evm"); } - @Override - public MutableAccount getMutable() throws ModificationNotAllowedException { - return this; - } - @Override public void setNonce(final long value) { + if (!mutable) { + throw new ModificationNotAllowedException(); + } nonce = value; } @Override public void setBalance(final Wei value) { + if (!mutable) { + throw new ModificationNotAllowedException(); + } balance = value; } @Override public void setCode(final Bytes code) { + if (!mutable) { + throw new ModificationNotAllowedException(); + } this.code = code; codeHash = Suppliers.memoize(() -> this.code == null ? Hash.EMPTY : Hash.hash(this.code)); } @Override public void setStorageValue(final UInt256 key, final UInt256 value) { + if (!mutable) { + throw new ModificationNotAllowedException(); + } storage.put(key, value); } @Override public void clearStorage() { + if (!mutable) { + throw new ModificationNotAllowedException(); + } storage.clear(); } @@ -152,4 +163,9 @@ public void clearStorage() { public Map getUpdatedStorage() { return storage; } + + @Override + public void becomeImmutable() { + mutable = false; + } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/toy/ToyWorld.java b/evm/src/test/java/org/hyperledger/besu/evm/toy/ToyWorld.java index b49d4930e5f..06aa833bcd2 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/toy/ToyWorld.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/toy/ToyWorld.java @@ -18,7 +18,7 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.account.EvmAccount; +import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.Collection; @@ -59,11 +59,11 @@ public Account get(final Address address) { } @Override - public EvmAccount createAccount(final Address address, final long nonce, final Wei balance) { + public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) { return createAccount(null, address, nonce, balance, Bytes.EMPTY); } - public EvmAccount createAccount( + public MutableAccount createAccount( final Account parentAccount, final Address address, final long nonce, @@ -75,7 +75,7 @@ public EvmAccount createAccount( } @Override - public EvmAccount getAccount(final Address address) { + public MutableAccount getAccount(final Address address) { if (accounts.containsKey(address)) { return accounts.get(address); } else if (parent != null) { diff --git a/evm/src/test/java/org/hyperledger/besu/evm/tracing/ExtendedOperationTracerTest.java b/evm/src/test/java/org/hyperledger/besu/evm/tracing/ExtendedOperationTracerTest.java index e74d43d8ec4..9b57bd938f4 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/tracing/ExtendedOperationTracerTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/tracing/ExtendedOperationTracerTest.java @@ -20,7 +20,6 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.evm.EVM; -import org.hyperledger.besu.evm.account.EvmAccount; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -45,7 +44,6 @@ class ExtendedOperationTracerTest { @Mock EVM evm; @Mock MessageFrame frame; @Mock WorldUpdater worldUpdater; - @Mock EvmAccount evmAccount; @Mock MutableAccount mutableAccount; @BeforeEach @@ -55,8 +53,7 @@ void setUp() { when(frame.getRemainingGas()).thenReturn(1L); when(frame.getWorldUpdater()).thenReturn(worldUpdater); - when(worldUpdater.getOrCreate(any())).thenReturn(evmAccount); - when(evmAccount.getMutable()).thenReturn(mutableAccount); + when(worldUpdater.getOrCreate(any())).thenReturn(mutableAccount); } @Test @@ -69,7 +66,6 @@ void shouldCallTraceAccountCreationResultIfIsExtendedTracing() { // traceAccountCreationResult has been called and values have been set assertThat(tracer.frame).isNotNull(); - assertThat(tracer.haltReason).isNotNull(); assertThat(tracer.haltReason).isEmpty(); } From 35385611ae5735c37ae7c7ed3a737f1b2d69ba6e Mon Sep 17 00:00:00 2001 From: delehef Date: Thu, 14 Sep 2023 04:57:25 +0200 Subject: [PATCH 2/3] Add world context to transaction tracing API (#5836) * Add world context to transaction tracing API Signed-off-by: Franklin Delehelle * Update changelog with PR ID Signed-off-by: Franklin Delehelle * Add the Transaction to traceEndTransaction Signed-off-by: Franklin Delehelle * Rebase on main Signed-off-by: Franklin Delehelle * Add receipt-linked information to the transaction tracer Signed-off-by: Franklin Delehelle * added test Signed-off-by: Daniel Lehrner --------- Signed-off-by: Franklin Delehelle Signed-off-by: Daniel Lehrner Co-authored-by: Daniel Lehrner Co-authored-by: Sally MacFarlane --- CHANGELOG.md | 1 + .../besu/services/TraceServiceImpl.java | 15 ++- .../besu/services/TraceServiceImplTest.java | 106 ++++++++++++++++++ .../hyperledger/besu/evmtool/T8nExecutor.java | 11 +- .../besu/evm/tracing/OperationTracer.java | 19 +++- .../besu/evm/tracing/StandardJsonTracer.java | 12 +- 6 files changed, 155 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 197a9b3478d..793f5f86378 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 23.7.3 ### Additions and Improvements +- Add access to an immutable world view to start/end transaction hooks in the tracing API[#5836](https://github.com/hyperledger/besu/pull/5836) ### Breaking Changes - Removed support for Kotti network (ETC) [#5816](https://github.com/hyperledger/besu/pull/5816) diff --git a/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java b/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java index 8b27108191f..485880ac50d 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java @@ -173,12 +173,12 @@ private List trace( .map(parent -> calculateExcessBlobGasForParent(protocolSpec, parent)) .orElse(BlobGas.ZERO)); - tracer.traceStartTransaction(transaction); - + final WorldUpdater worldUpdater = chainUpdater.getNextUpdater(); + tracer.traceStartTransaction(worldUpdater, transaction); final TransactionProcessingResult result = transactionProcessor.processTransaction( blockchain, - chainUpdater.getNextUpdater(), + worldUpdater, header, transaction, header.getCoinbase(), @@ -188,7 +188,14 @@ private List trace( blobGasPrice); long transactionGasUsed = transaction.getGasLimit() - result.getGasRemaining(); - tracer.traceEndTransaction(result.getOutput(), transactionGasUsed, 0); + tracer.traceEndTransaction( + worldUpdater, + transaction, + result.isSuccessful(), + result.getOutput(), + result.getLogs(), + transactionGasUsed, + 0); results.add(result); }); diff --git a/besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java b/besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java index 0c8acd6a190..cb88a9b9730 100644 --- a/besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java +++ b/besu/src/test/java/org/hyperledger/besu/services/TraceServiceImplTest.java @@ -17,14 +17,21 @@ import static org.assertj.core.api.Assertions.assertThat; import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; +import org.hyperledger.besu.evm.log.Log; +import org.hyperledger.besu.evm.worldstate.WorldView; import org.hyperledger.besu.plugin.services.TraceService; import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer; +import java.util.List; + +import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; @@ -102,4 +109,103 @@ void shouldRetrieveStateUpdatePostTracingForAllBlocks() { assertThat(worldStateArchive.getMutable().get(addressToVerify).getNonce()) .isEqualTo(persistedNonceForAccount); } + + @Test + void shouldReturnTheCorrectWorldViewForTxStartEnd() { + final TxStartEndTracer txStartEndTracer = new TxStartEndTracer(); + + // block contains 1 transaction + traceService.traceBlock(31, txStartEndTracer); + + assertThat(txStartEndTracer.txStartWorldView).isNotNull(); + assertThat(txStartEndTracer.txEndWorldView).isNotNull(); + + assertThat(txStartEndTracer.txStartTransaction.getNonce()) + .isEqualTo(txStartEndTracer.txEndTransaction.getNonce()) + .isEqualTo(30); + assertThat(txStartEndTracer.txStartTransaction.getGasLimit()) + .isEqualTo(txStartEndTracer.txEndTransaction.getGasLimit()) + .isEqualTo(314159); + assertThat(txStartEndTracer.txStartTransaction.getTo().get()) + .isEqualTo(txStartEndTracer.txEndTransaction.getTo().get()) + .isEqualTo(Address.fromHexString("0x6295ee1b4f6dd65047762f924ecd367c17eabf8f")); + assertThat(txStartEndTracer.txStartTransaction.getValue()) + .isEqualTo(txStartEndTracer.txEndTransaction.getValue()) + .isEqualTo( + Wei.fromHexString( + "0x000000000000000000000000000000000000000000000000000000000000000a")); + assertThat(txStartEndTracer.txStartTransaction.getPayload()) + .isEqualTo(txStartEndTracer.txEndTransaction.getPayload()) + .isEqualTo(Bytes.fromHexString("0xfd408767")); + + assertThat(txStartEndTracer.txEndStatus).isTrue(); + assertThat(txStartEndTracer.txEndOutput).isEqualTo(Bytes.fromHexString("0x")); + assertThat(txStartEndTracer.txEndGasUsed).isEqualTo(24303); + assertThat(txStartEndTracer.txEndTimeNs).isNotNull(); + + assertThat(txStartEndTracer.txEndLogs).isNotEmpty(); + + final Log actualLog = txStartEndTracer.txEndLogs.get(0); + assertThat(actualLog.getLogger()) + .isEqualTo(Address.fromHexString("0x6295ee1b4f6dd65047762f924ecd367c17eabf8f")); + assertThat(actualLog.getData()) + .isEqualTo( + Bytes.fromHexString( + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe9000000000000000000000000000000000000000000000000000000000000002a")); + assertThat(actualLog.getTopics()).hasSize(4); + assertThat(actualLog.getTopics().get(0)) + .isEqualTo( + Bytes.fromHexString( + "0xd5f0a30e4be0c6be577a71eceb7464245a796a7e6a55c0d971837b250de05f4e")); + assertThat(actualLog.getTopics().get(1)) + .isEqualTo( + Bytes.fromHexString( + "0x0000000000000000000000000000000000000000000000000000000000000001")); + assertThat(actualLog.getTopics().get(2)) + .isEqualTo( + Bytes.fromHexString( + "0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")); + assertThat(actualLog.getTopics().get(3)) + .isEqualTo( + Bytes.fromHexString( + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + } + + private static class TxStartEndTracer implements BlockAwareOperationTracer { + public WorldView txStartWorldView; + public WorldView txEndWorldView; + + public Transaction txStartTransaction; + public Transaction txEndTransaction; + + public boolean txEndStatus; + public Bytes txEndOutput; + public List txEndLogs; + public long txEndGasUsed; + public Long txEndTimeNs; + + @Override + public void traceStartTransaction(final WorldView worldView, final Transaction transaction) { + txStartWorldView = worldView; + txStartTransaction = transaction; + } + + @Override + public void traceEndTransaction( + final WorldView worldView, + final Transaction transaction, + final boolean status, + final Bytes output, + final List logs, + final long gasUsed, + final long timeNs) { + txEndWorldView = worldView; + txEndTransaction = transaction; + txEndStatus = status; + txEndOutput = output; + txEndLogs = logs; + txEndGasUsed = gasUsed; + txEndTimeNs = timeNs; + } + } } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java index 9d2faeb7dc8..816954353dd 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java @@ -270,6 +270,7 @@ static T8nResult runTest( final TransactionProcessingResult result; try { tracer = tracerManager.getManagedTracer(i, transaction.getHash()); + tracer.traceStartTransaction(worldStateUpdater, transaction); result = processor.processTransaction( blockchain, @@ -312,12 +313,18 @@ static T8nResult runTest( .getGasCalculator() .transactionIntrinsicGasCost( transaction.getPayload(), transaction.getTo().isEmpty()); - tracer.traceEndTransaction( - result.getOutput(), gasUsed - intrinsicGas, timer.elapsed(TimeUnit.NANOSECONDS)); TransactionReceipt receipt = protocolSpec .getTransactionReceiptFactory() .create(transaction.getType(), result, worldState, gasUsed); + tracer.traceEndTransaction( + worldStateUpdater, + transaction, + result.isSuccessful(), + result.getOutput(), + result.getLogs(), + gasUsed - intrinsicGas, + timer.elapsed(TimeUnit.NANOSECONDS)); Bytes gasUsedInTransaction = Bytes.ofUnsignedLong(transactionGasUsed); receipts.add(receipt); ObjectNode receiptObject = receiptsArray.addObject(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java b/evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java index 4552fec4543..455911c6ca1 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/tracing/OperationTracer.java @@ -17,8 +17,11 @@ import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.log.Log; import org.hyperledger.besu.evm.operation.Operation.OperationResult; +import org.hyperledger.besu.evm.worldstate.WorldView; +import java.util.List; import java.util.Optional; import org.apache.tuweni.bytes.Bytes; @@ -67,18 +70,30 @@ default void traceAccountCreationResult( /** * Trace the start of a transaction. * + * @param worldView an immutable view of the execution context * @param transaction the transaction which will be processed */ - default void traceStartTransaction(final Transaction transaction) {} + default void traceStartTransaction(final WorldView worldView, final Transaction transaction) {} /** * Trace the end of a transaction. * + * @param worldView an immutable view of the execution context + * @param tx the transaction that just concluded + * @param status true if the transaction is successful, false otherwise * @param output the bytes output from the transaction + * @param logs the logs emitted by this transaction * @param gasUsed the gas used by the entire transaction * @param timeNs the time in nanoseconds it took to execute the transaction */ - default void traceEndTransaction(final Bytes output, final long gasUsed, final long timeNs) {} + default void traceEndTransaction( + final WorldView worldView, + final Transaction tx, + final boolean status, + final Bytes output, + final List logs, + final long gasUsed, + final long timeNs) {} /** * Trace the entering of a new context diff --git a/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java b/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java index 5dbc99dd0cf..1734e7f6730 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/tracing/StandardJsonTracer.java @@ -18,10 +18,13 @@ import static com.google.common.base.Strings.padStart; +import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.internal.Words; +import org.hyperledger.besu.evm.log.Log; import org.hyperledger.besu.evm.operation.Operation; +import org.hyperledger.besu.evm.worldstate.WorldView; import java.io.PrintStream; import java.io.PrintWriter; @@ -216,7 +219,14 @@ public void traceAccountCreationResult( } @Override - public void traceEndTransaction(final Bytes output, final long gasUsed, final long timeNs) { + public void traceEndTransaction( + final WorldView _worldView, + final Transaction _tx, + final boolean _status, + final Bytes output, + final List _logs, + final long gasUsed, + final long timeNs) { final StringBuilder sb = new StringBuilder(1024); sb.append("{"); if (output.size() > 0) { From 4b2ef689c1f64e3fdb293dec826af1ad57e54fca Mon Sep 17 00:00:00 2001 From: garyschulte Date: Wed, 13 Sep 2023 21:30:18 -0700 Subject: [PATCH 3/3] Bonsai based reference test worldstate (#5686) * create a bonsai based reference test worldstate -> getOrCreate in BonsaiWorldStateUpdateAccumulator - do not throw if we discover an empty account in a non-null BonsaiValue -> add curentStateRoot to t8n -> storageEntriesFrom and streamAccounts implemented in BonsaiWorldStateKeyValueStorage -> add endKey version of streamFromKey * bonsai fix for self-destruct and create2 at the same address and same block Signed-off-by: garyschulte Signed-off-by: Karim TAAM Co-authored-by: Karim TAAM --- .../besu/ethereum/bonsai/BonsaiAccount.java | 2 +- .../bonsai/BonsaiWorldStateProvider.java | 2 +- .../bonsai/storage/BonsaiPreImageProxy.java | 68 +++++++ .../BonsaiWorldStateKeyValueStorage.java | 8 +- .../storage/flat/FlatDbReaderStrategy.java | 13 +- .../bonsai/worldview/BonsaiWorldState.java | 80 ++++---- .../BonsaiWorldStateUpdateAccumulator.java | 60 ++++-- .../mainnet/MainnetBlockProcessorTest.java | 3 + .../backwardsync/BackwardSyncContextTest.java | 7 +- .../backwardsync/ForwardSyncStepTest.java | 5 +- .../besu/evmtool/StateTestSubCommand.java | 7 +- .../hyperledger/besu/evmtool/T8nExecutor.java | 6 +- .../besu/evmtool/T8nSubCommand.java | 3 +- .../besu/evmtool/state-test/blockhash.json | 4 +- .../t8n/berlin-calculate-difficulty.json | 3 +- .../besu/evmtool/t8n/berlin-example-yul.json | 3 +- .../t8n/berlin-negative-cli-reward.json | 3 +- .../evmtool/t8n/berlin-no-cli-reward.json | 3 +- .../besu/evmtool/t8n/berlin-no-tx.json | 3 +- .../besu/evmtool/t8n/berlin-simple.json | 3 +- .../t8n/cancun-6780-selfdestruct-sweep.json | 3 +- .../t8n/cancun-6780-selfdestruct-to-self.json | 3 +- .../cancun-6780-selfdestruct-transient.json | 3 +- .../besu/evmtool/t8n/cancun-blobs-per-tx.json | 3 +- .../evmtool/t8n/istanbul-cumulative-gas.json | 1 + .../evmtool/t8n/london-env-no-basefee.json | 3 +- .../besu/evmtool/t8n/london-hex.json | 3 +- .../besu/evmtool/t8n/shanghai-blockhash.json | 3 +- .../besu/evmtool/t8n/shanghai-init-code.json | 3 +- .../besu/evmtool/t8n/shanghai-invalidTx.json | 3 +- .../t8n/shanghai-withdrawals-no-nonce.json | 3 +- .../t8n/shanghai-withdrawals-overflow.json | 3 +- .../evmtool/t8n/shanghai-withdrawals.json | 3 +- ethereum/referencetests/build.gradle | 3 + .../BonsaiReferenceTestUpdateAccumulator.java | 44 +++++ .../BonsaiReferenceTestWorldState.java | 182 ++++++++++++++++++ .../BonsaiReferenceTestWorldStateStorage.java | 85 ++++++++ .../DefaultReferenceTestWorldState.java | 60 ++++++ .../GeneralStateTestCaseEipSpec.java | 7 +- .../GeneralStateTestCaseSpec.java | 11 +- .../referencetests/ReferenceTestEnv.java | 3 +- .../ReferenceTestWorldState.java | 30 +-- .../vm/GeneralStateReferenceTestTools.java | 8 +- plugin-api/build.gradle | 2 +- .../services/storage/KeyValueStorage.java | 11 ++ .../storage/SegmentedKeyValueStorage.java | 13 ++ .../RocksDBColumnarKeyValueSnapshot.java | 9 +- .../RocksDBColumnarKeyValueStorage.java | 11 ++ .../segmented/RocksDBSnapshotTransaction.java | 43 +++++ .../kvstore/LayeredKeyValueStorage.java | 12 +- .../LimitedInMemoryKeyValueStorage.java | 7 + .../SegmentedInMemoryKeyValueStorage.java | 13 +- .../SegmentedKeyValueStorageAdapter.java | 10 +- 53 files changed, 746 insertions(+), 141 deletions(-) create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/storage/BonsaiPreImageProxy.java create mode 100644 ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestUpdateAccumulator.java create mode 100644 ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldState.java create mode 100644 ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldStateStorage.java create mode 100644 ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/DefaultReferenceTestWorldState.java diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java index a5488cb9a2e..7362d345ce4 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiAccount.java @@ -219,7 +219,7 @@ public UInt256 getOriginalStorageValue(final UInt256 key) { @Override public NavigableMap storageEntriesFrom( final Bytes32 startKeyHash, final int limit) { - throw new RuntimeException("Bonsai Tries does not currently support enumerating storage"); + return context.getWorldStateStorage().storageEntriesFrom(this.addressHash, startKeyHash, limit); } public Bytes serializeAccount() { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateProvider.java index 986e736745a..6388d62a584 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateProvider.java @@ -102,8 +102,8 @@ public BonsaiWorldStateProvider( pluginContext); this.blockchain = blockchain; this.worldStateStorage = worldStateStorage; - this.persistedState = new BonsaiWorldState(this, worldStateStorage); this.cachedMerkleTrieLoader = cachedMerkleTrieLoader; + this.persistedState = new BonsaiWorldState(this, worldStateStorage); blockchain .getBlockHeader(persistedState.getWorldStateBlockHash()) .ifPresent( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/storage/BonsaiPreImageProxy.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/storage/BonsaiPreImageProxy.java new file mode 100644 index 00000000000..248e5d895fd --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/storage/BonsaiPreImageProxy.java @@ -0,0 +1,68 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +package org.hyperledger.besu.ethereum.bonsai.storage; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage; + +import java.util.Optional; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.units.bigints.UInt256; + +/** Acts as both a Hasher and PreImageStorage for Bonsai storage format. */ +public interface BonsaiPreImageProxy extends WorldStatePreimageStorage { + /** + * If this value is not already present, save in preImage store and return the hash value. + * + * @param value value to hash + * @return Hash of value + */ + Hash hashAndSavePreImage(Bytes value); + + /** + * A caching PreImageProxy suitable for ReferenceTestWorldState which saves hashes in an unbounded + * BiMap. + */ + class BonsaiReferenceTestPreImageProxy implements BonsaiPreImageProxy { + BiMap preImageCache = HashBiMap.create(); + + @Override + public synchronized Hash hashAndSavePreImage(final Bytes value) { + return preImageCache.inverse().computeIfAbsent(value, Hash::hash); + } + + @Override + public Optional getStorageTrieKeyPreimage(final Bytes32 trieKey) { + return Optional.ofNullable(preImageCache.get(trieKey)).map(UInt256::fromBytes); + } + + @Override + public Optional
getAccountTrieKeyPreimage(final Bytes32 trieKey) { + return Optional.ofNullable(preImageCache.get(trieKey)).map(Address::wrap); + } + + @Override + public Updater updater() { + throw new UnsupportedOperationException( + "BonsaiReferenceTestPreImageProxy does not implement an updater"); + } + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/storage/BonsaiWorldStateKeyValueStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/storage/BonsaiWorldStateKeyValueStorage.java index 4ceb63ef219..1201fffb3b8 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/storage/BonsaiWorldStateKeyValueStorage.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/storage/BonsaiWorldStateKeyValueStorage.java @@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.worldstate.FlatDbMode; import org.hyperledger.besu.ethereum.worldstate.StateTrieAccountValue; import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage; +import org.hyperledger.besu.evm.account.AccountStorageEntry; import org.hyperledger.besu.metrics.ObservableMetricsSystem; import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction; @@ -41,6 +42,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; +import java.util.NavigableMap; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; @@ -53,7 +55,6 @@ @SuppressWarnings("unused") public class BonsaiWorldStateKeyValueStorage implements WorldStateStorage, AutoCloseable { - private static final Logger LOG = LoggerFactory.getLogger(BonsaiWorldStateKeyValueStorage.class); // 0x776f726c64526f6f74 @@ -250,6 +251,11 @@ public Map streamFlatStorages( composedWorldStateStorage, accountHash, startKeyHash, endKeyHash, max); } + public NavigableMap storageEntriesFrom( + final Hash addressHash, final Bytes32 startKeyHash, final int limit) { + throw new RuntimeException("Bonsai Tries does not currently support enumerating storage"); + } + @Override public Optional getNodeData(final Bytes location, final Bytes32 hash) { return Optional.empty(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/storage/flat/FlatDbReaderStrategy.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/storage/flat/FlatDbReaderStrategy.java index 1a35dbb93fe..74c7b101c25 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/storage/flat/FlatDbReaderStrategy.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/storage/flat/FlatDbReaderStrategy.java @@ -135,10 +135,10 @@ public Map streamAccountFlatDatabase( final long max) { final Stream> pairStream = storage - .streamFromKey(ACCOUNT_INFO_STATE, startKeyHash.toArrayUnsafe()) + .streamFromKey( + ACCOUNT_INFO_STATE, startKeyHash.toArrayUnsafe(), endKeyHash.toArrayUnsafe()) .limit(max) - .map(pair -> new Pair<>(Bytes32.wrap(pair.getKey()), Bytes.wrap(pair.getValue()))) - .takeWhile(pair -> pair.getFirst().compareTo(endKeyHash) <= 0); + .map(pair -> new Pair<>(Bytes32.wrap(pair.getKey()), Bytes.wrap(pair.getValue()))); final TreeMap collected = pairStream.collect( @@ -157,15 +157,14 @@ public Map streamStorageFlatDatabase( storage .streamFromKey( ACCOUNT_STORAGE_STORAGE, - Bytes.concatenate(accountHash, startKeyHash).toArrayUnsafe()) - .takeWhile(pair -> Bytes.wrap(pair.getKey()).slice(0, Hash.SIZE).equals(accountHash)) + Bytes.concatenate(accountHash, startKeyHash).toArrayUnsafe(), + Bytes.concatenate(accountHash, endKeyHash).toArrayUnsafe()) .limit(max) .map( pair -> new Pair<>( Bytes32.wrap(Bytes.wrap(pair.getKey()).slice(Hash.SIZE)), - RLP.encodeValue(Bytes.wrap(pair.getValue()).trimLeadingZeros()))) - .takeWhile(pair -> pair.getFirst().compareTo(endKeyHash) <= 0); + RLP.encodeValue(Bytes.wrap(pair.getValue()).trimLeadingZeros()))); final TreeMap collected = pairStream.collect( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldState.java index 865471eca99..f28e24b0970 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldState.java @@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.bonsai.BonsaiAccount; import org.hyperledger.besu.ethereum.bonsai.BonsaiValue; import org.hyperledger.besu.ethereum.bonsai.BonsaiWorldStateProvider; +import org.hyperledger.besu.ethereum.bonsai.cache.CachedMerkleTrieLoader; import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiSnapshotWorldStateKeyValueStorage; import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateKeyValueStorage; import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateKeyValueStorage.BonsaiStorageSubscriber; @@ -65,51 +66,52 @@ public class BonsaiWorldState private static final Logger LOG = LoggerFactory.getLogger(BonsaiWorldState.class); - private BonsaiWorldStateKeyValueStorage worldStateStorage; + protected BonsaiWorldStateKeyValueStorage worldStateStorage; - private final BonsaiWorldStateProvider archive; - private final BonsaiWorldStateUpdateAccumulator accumulator; + protected final CachedMerkleTrieLoader cachedMerkleTrieLoader; + protected final TrieLogManager trieLogManager; + private BonsaiWorldStateUpdateAccumulator accumulator; - private Hash worldStateRootHash; + protected Hash worldStateRootHash; Hash worldStateBlockHash; - private boolean isFrozen; public BonsaiWorldState( final BonsaiWorldStateProvider archive, final BonsaiWorldStateKeyValueStorage worldStateStorage) { - this.archive = archive; + this(worldStateStorage, archive.getCachedMerkleTrieLoader(), archive.getTrieLogManager()); + } + + protected BonsaiWorldState( + final BonsaiWorldStateKeyValueStorage worldStateStorage, + final CachedMerkleTrieLoader cachedMerkleTrieLoader, + final TrieLogManager trieLogManager) { this.worldStateStorage = worldStateStorage; - worldStateRootHash = + this.worldStateRootHash = Hash.wrap( Bytes32.wrap(worldStateStorage.getWorldStateRootHash().orElse(Hash.EMPTY_TRIE_HASH))); - worldStateBlockHash = + this.worldStateBlockHash = Hash.wrap(Bytes32.wrap(worldStateStorage.getWorldStateBlockHash().orElse(Hash.ZERO))); - accumulator = + this.accumulator = new BonsaiWorldStateUpdateAccumulator( this, (addr, value) -> - archive - .getCachedMerkleTrieLoader() - .preLoadAccount(getWorldStateStorage(), worldStateRootHash, addr), + cachedMerkleTrieLoader.preLoadAccount( + getWorldStateStorage(), worldStateRootHash, addr), (addr, value) -> - archive - .getCachedMerkleTrieLoader() - .preLoadStorageSlot(getWorldStateStorage(), addr, value)); + cachedMerkleTrieLoader.preLoadStorageSlot(getWorldStateStorage(), addr, value)); + this.cachedMerkleTrieLoader = cachedMerkleTrieLoader; + this.trieLogManager = trieLogManager; } - public BonsaiWorldState( - final BonsaiWorldStateProvider archive, - final BonsaiWorldStateKeyValueStorage worldStateStorage, - final BonsaiWorldStateUpdateAccumulator updater) { - this.archive = archive; - this.worldStateStorage = worldStateStorage; - this.worldStateRootHash = - Hash.wrap( - Bytes32.wrap(worldStateStorage.getWorldStateRootHash().orElse(Hash.EMPTY_TRIE_HASH))); - this.worldStateBlockHash = - Hash.wrap(Bytes32.wrap(worldStateStorage.getWorldStateBlockHash().orElse(Hash.ZERO))); - this.accumulator = updater; + /** + * Having a protected method to override the accumulator solves the chicken-egg problem of needing + * a worldstate reference (this) when construction the Accumulator. + * + * @param accumulator accumulator to use. + */ + protected void setAccumulator(final BonsaiWorldStateUpdateAccumulator accumulator) { + this.accumulator = accumulator; } /** @@ -130,10 +132,6 @@ public Hash getWorldStateRootHash() { return worldStateRootHash; } - public BonsaiWorldStateProvider getArchive() { - return archive; - } - @Override public boolean isPersisted() { return isPersisted(worldStateStorage); @@ -189,9 +187,7 @@ private Hash calculateRootHash( final StoredMerklePatriciaTrie accountTrie = createTrie( (location, hash) -> - archive - .getCachedMerkleTrieLoader() - .getAccountStateTrieNode(worldStateStorage, location, hash), + cachedMerkleTrieLoader.getAccountStateTrieNode(worldStateStorage, location, hash), worldStateRootHash); // for manicured tries and composting, collect branches here (not implemented) @@ -223,7 +219,7 @@ private void updateTheAccounts( final BonsaiAccount updatedAccount = bonsaiValue.getUpdated(); try { if (updatedAccount == null) { - final Hash addressHash = Hash.hash(accountKey); + final Hash addressHash = hashAndSavePreImage(accountKey); accountTrie.remove(addressHash); maybeStateUpdater.ifPresent( bonsaiUpdater -> bonsaiUpdater.removeAccountInfoState(addressHash)); @@ -232,7 +228,7 @@ private void updateTheAccounts( final Bytes accountValue = updatedAccount.serializeAccount(); maybeStateUpdater.ifPresent( bonsaiUpdater -> - bonsaiUpdater.putAccountInfoState(Hash.hash(accountKey), accountValue)); + bonsaiUpdater.putAccountInfoState(hashAndSavePreImage(accountKey), accountValue)); accountTrie.put(addressHash, accountValue); } } catch (MerkleTrieException e) { @@ -277,10 +273,8 @@ private void updateAccountStorageState( final StoredMerklePatriciaTrie storageTrie = createTrie( (location, key) -> - archive - .getCachedMerkleTrieLoader() - .getAccountStorageTrieNode( - worldStateStorage, updatedAddressHash, location, key), + cachedMerkleTrieLoader.getAccountStorageTrieNode( + worldStateStorage, updatedAddressHash, location, key), storageRoot); // for manicured tries and composting, collect branches here (not implemented) @@ -405,7 +399,6 @@ public void persist(final BlockHeader blockHeader) { } saveTrieLog = () -> { - final TrieLogManager trieLogManager = archive.getTrieLogManager(); trieLogManager.saveTrieLog(localCopy, newWorldStateRootHash, blockHeader, this); // not save a frozen state in the cache if (!isFrozen) { @@ -626,4 +619,9 @@ private void closeFrozenStorage() { // no op } } + + protected Hash hashAndSavePreImage(final Bytes value) { + // by default do not save has preImages + return Hash.hash(value); + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java index b1cd64378b9..23fd7625fa3 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java @@ -127,14 +127,18 @@ public MutableAccount createAccount(final Address address, final long nonce, fin bonsaiValue = new BonsaiValue<>(null, null); accountsToUpdate.put(address, bonsaiValue); } else if (bonsaiValue.getUpdated() != null) { - throw new IllegalStateException("Cannot create an account when one already exists"); + if (bonsaiValue.getUpdated().isEmpty()) { + return track(new UpdateTrackingAccount<>(bonsaiValue.getUpdated())); + } else { + throw new IllegalStateException("Cannot create an account when one already exists"); + } } final BonsaiAccount newAccount = new BonsaiAccount( this, address, - address.addressHash(), + hashAndSavePreImage(address), nonce, balance, Hash.EMPTY_TRIE_HASH, @@ -288,6 +292,19 @@ public void commit() { final BonsaiAccount updatedAccount; final BonsaiValue updatedAccountValue = accountsToUpdate.get(updatedAddress); + + final Map> pendingStorageUpdates = + storageToUpdate.computeIfAbsent( + updatedAddress, + k -> + new StorageConsumingMap<>( + updatedAddress, new ConcurrentHashMap<>(), storagePreloader)); + + if (tracked.getStorageWasCleared()) { + storageToClear.add(updatedAddress); + pendingStorageUpdates.clear(); + } + if (tracked.getWrappedAccount() == null) { updatedAccount = new BonsaiAccount(this, tracked); tracked.setWrappedAccount(updatedAccount); @@ -307,6 +324,17 @@ public void commit() { } if (tracked.getStorageWasCleared()) { updatedAccount.clearStorage(); + wrappedWorldView() + .getAllAccountStorage(updatedAddress, updatedAccount.getStorageRoot()) + .forEach( + (keyHash, entryValue) -> { + final StorageSlotKey storageSlotKey = + new StorageSlotKey(Hash.wrap(keyHash), Optional.empty()); + final UInt256 value = UInt256.fromBytes(RLP.decodeOne(entryValue)); + pendingStorageUpdates.put( + storageSlotKey, new BonsaiValue<>(value, null, true)); + }); + updatedAccount.setStorageRoot(Hash.EMPTY_TRIE_HASH); } tracked.getUpdatedStorage().forEach(updatedAccount::setStorageValue); } @@ -329,24 +357,12 @@ public void commit() { pendingCode.setUpdated(updatedAccount.getCode()); } - // This is especially to avoid unnecessary computation for withdrawals + // This is especially to avoid unnecessary computation for withdrawals and + // self-destruct beneficiaries if (updatedAccount.getUpdatedStorage().isEmpty()) { return; } - final StorageConsumingMap> - pendingStorageUpdates = - storageToUpdate.computeIfAbsent( - updatedAddress, - __ -> - new StorageConsumingMap<>( - updatedAddress, new ConcurrentHashMap<>(), storagePreloader)); - - if (tracked.getStorageWasCleared()) { - storageToClear.add(updatedAddress); - pendingStorageUpdates.clear(); - } - final TreeSet> entries = new TreeSet<>(Map.Entry.comparingByKey()); entries.addAll(updatedAccount.getUpdatedStorage().entrySet()); @@ -355,7 +371,7 @@ public void commit() { entries.forEach( storageUpdate -> { final UInt256 keyUInt = storageUpdate.getKey(); - final Hash slotHash = Hash.hash(keyUInt); + final Hash slotHash = hashAndSavePreImage(keyUInt); final StorageSlotKey slotKey = new StorageSlotKey(slotHash, Optional.of(keyUInt)); final UInt256 value = storageUpdate.getValue(); @@ -399,7 +415,8 @@ public Optional getCode(final Address address, final Hash codeHash) { @Override public UInt256 getStorageValue(final Address address, final UInt256 slotKey) { - StorageSlotKey storageSlotKey = new StorageSlotKey(Hash.hash(slotKey), Optional.of(slotKey)); + StorageSlotKey storageSlotKey = + new StorageSlotKey(hashAndSavePreImage(slotKey), Optional.of(slotKey)); return getStorageValueByStorageSlotKey(address, storageSlotKey).orElse(UInt256.ZERO); } @@ -443,7 +460,7 @@ public Optional getStorageValueByStorageSlotKey( public UInt256 getPriorStorageValue(final Address address, final UInt256 storageKey) { // TODO maybe log the read into the trie layer? StorageSlotKey storageSlotKey = - new StorageSlotKey(Hash.hash(storageKey), Optional.of(storageKey)); + new StorageSlotKey(hashAndSavePreImage(storageKey), Optional.of(storageKey)); final Map> localAccountStorage = storageToUpdate.get(address); if (localAccountStorage != null) { @@ -816,4 +833,9 @@ protected Map delegate() { public interface Consumer { void process(final Address address, T value); } + + protected Hash hashAndSavePreImage(final Bytes bytes) { + // by default do not save hash preImages + return Hash.hash(bytes); + } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java index 6bd6978acb1..1b106686bfc 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockProcessorTest.java @@ -94,6 +94,9 @@ public void accountCreatedWhenBlockRewardIsZeroAndNotSkipped() { final BlockHeader emptyBlockHeader = new BlockHeaderTestFixture() .transactionsRoot(Hash.EMPTY_LIST_HASH) + .stateRoot( + Hash.fromHexString( + "0xa6b5d50f7b3c39b969c2fe8fed091939c674fef49b4826309cb6994361e39b71")) .ommersHash(Hash.EMPTY_LIST_HASH) .buildHeader(); blockProcessor.processBlock(blockchain, worldState, emptyBlockHeader, emptyList(), emptyList()); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncContextTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncContextTest.java index 3ed28e66f4d..b5909c4d429 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncContextTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncContextTest.java @@ -47,7 +47,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; -import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState; +import org.hyperledger.besu.ethereum.referencetests.DefaultReferenceTestWorldState; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; @@ -149,7 +149,10 @@ public void setup() { return new BlockProcessingResult( Optional.of( new BlockProcessingOutputs( - new ReferenceTestWorldState(), blockDataGenerator.receipts(block)))); + // use forest-based worldstate since it does not require + // blockheader stateroot to match actual worldstate root + DefaultReferenceTestWorldState.create(Collections.emptyMap()), + blockDataGenerator.receipts(block)))); }); backwardChain = inMemoryBackwardChain(); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/ForwardSyncStepTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/ForwardSyncStepTest.java index 77308ebcc3b..e38a9647b79 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/ForwardSyncStepTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/ForwardSyncStepTest.java @@ -38,7 +38,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState; +import org.hyperledger.besu.ethereum.referencetests.DefaultReferenceTestWorldState; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import java.nio.charset.StandardCharsets; @@ -138,7 +138,8 @@ public void setup() { return new BlockProcessingResult( Optional.of( new BlockProcessingOutputs( - new ReferenceTestWorldState(), blockDataGenerator.receipts(block)))); + DefaultReferenceTestWorldState.create(Collections.emptyMap()), + blockDataGenerator.receipts(block)))); }); } diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java index 95af770c589..2bc5f815309 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java @@ -37,12 +37,10 @@ import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain; import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules; import org.hyperledger.besu.ethereum.rlp.RLP; -import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.log.Log; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.tracing.StandardJsonTracer; -import org.hyperledger.besu.evm.worldstate.WorldState; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.evmtool.exception.UnsupportedForkException; import org.hyperledger.besu.util.LogConfigurator; @@ -200,9 +198,7 @@ private void traceTestSpecs(final String test, final List transactions, final List rejections, final TracerManager tracerManager) { @@ -238,7 +238,7 @@ static T8nResult runTest( ReferenceTestProtocolSchedules.create( new StubGenesisConfigOptions().chainId(BigInteger.valueOf(chainId))); - final MutableWorldState worldState = new DefaultMutableWorldState(initialWorldState); + final MutableWorldState worldState = initialWorldState.copy(); final ProtocolSchedule protocolSchedule = referenceTestProtocolSchedules.getByName(fork); if (protocolSchedule == null) { diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java index 1571dc3551e..ea2984c5c90 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nSubCommand.java @@ -21,7 +21,6 @@ import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.referencetests.ReferenceTestEnv; import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState; @@ -175,7 +174,7 @@ public void run() { final ObjectMapper objectMapper = JsonUtils.createObjectMapper(); final ObjectReader t8nReader = objectMapper.reader(); - MutableWorldState initialWorldState; + ReferenceTestWorldState initialWorldState; ReferenceTestEnv referenceTestEnv; List transactions = new ArrayList<>(); List rejections = new ArrayList<>(); diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/blockhash.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/blockhash.json index b5bac5462ef..de44541641d 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/blockhash.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/state-test/blockhash.json @@ -61,7 +61,7 @@ "post": { "Shanghai": [ { - "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "hash": "0xd14c10ed22a1cfb642e374be985ac581c39f3969bd59249e0405aca3beb47a47", "logs": "0x0000000000000000000000000000000000000000000000000000000000000000", "indexes": { "data": 0, @@ -101,4 +101,4 @@ {"pc":83,"op":62,"gas":"0x79bc18","gasCost":"0x0","memSize":0,"stack":["0x0","0x1","0x1","0x2","0x2","0xffff","0x1f4","0x78859e5b97166c486532b1595a673e9f9073643f1b519c6f18511b9913","0x2","0x389","0x0","0x0","0x1","0x0","0x3e3d6d5ff042148d326c1898713a76759ca273","0xb94f5374fce5edbc8e2a8697c15331677e6ebf1b"],"depth":1,"refund":0,"opName":"RETURNDATACOPY","error":"Out of bounds"}, {"output":"","gasUsed":"0x7a1200","test":"00000936-mixed-1","fork":"Shanghai","d":0,"g":0,"v":0,"postHash":"0xd14c10ed22a1cfb642e374be985ac581c39f3969bd59249e0405aca3beb47a47","postLogsHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","pass":false} ] -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-calculate-difficulty.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-calculate-difficulty.json index 1a3c7a178c6..005cb1498ad 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-calculate-difficulty.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-calculate-difficulty.json @@ -48,6 +48,7 @@ "currentGasLimit": "100000000000000000", "currentNumber": "1", "currentTimestamp": "1000", + "currentStateRoot": "0xd921e74bfe864514fa508003336b8f66eb98d748c5163749827029a1ed7db265", "parentDifficulty": "131072", "parentGasUsed": "50000000000000000", "parentGasLimit": "100000000000000000", @@ -101,4 +102,4 @@ "gasUsed": "0xa8ad" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-example-yul.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-example-yul.json index 1a3c7a178c6..005cb1498ad 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-example-yul.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-example-yul.json @@ -48,6 +48,7 @@ "currentGasLimit": "100000000000000000", "currentNumber": "1", "currentTimestamp": "1000", + "currentStateRoot": "0xd921e74bfe864514fa508003336b8f66eb98d748c5163749827029a1ed7db265", "parentDifficulty": "131072", "parentGasUsed": "50000000000000000", "parentGasLimit": "100000000000000000", @@ -101,4 +102,4 @@ "gasUsed": "0xa8ad" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-negative-cli-reward.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-negative-cli-reward.json index 4ef273955c4..f41eb4989c4 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-negative-cli-reward.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-negative-cli-reward.json @@ -46,6 +46,7 @@ "env": { "currentCoinbase": "0x0000000000000000000000000000000000000000", "currentDifficulty": "0x0", + "currentStateRoot": "0xde762e485411037c02119ff1115b422945b37efe91f4135dd442c3346629d0ef", "currentGasLimit": "0x0", "currentNumber": "0", "currentTimestamp": "0" @@ -93,4 +94,4 @@ "gasUsed": "0xa8ad" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-no-cli-reward.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-no-cli-reward.json index d06c75ccfd2..4aba3a31a1d 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-no-cli-reward.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-no-cli-reward.json @@ -45,6 +45,7 @@ "env": { "currentCoinbase": "0x0000000000000000000000000000000000000000", "currentDifficulty": "0x0", + "currentStateRoot": "0x9580f6eec8ad54773eff8e370d12dfea7b3216e929657c48a509af64c4190be6", "currentGasLimit": "0x0", "currentNumber": "0", "currentTimestamp": "0" @@ -92,4 +93,4 @@ "gasUsed": "0xa8ad" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-no-tx.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-no-tx.json index 5abcf7bb347..8c655b71ec9 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-no-tx.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-no-tx.json @@ -30,6 +30,7 @@ "env": { "currentCoinbase": "0x0000000000000000000000000000000000000000", "currentDifficulty": "0x0", + "currentStateRoot": "0x369ba8b2e5b32d675d07933d6fb851d97d3ca66c60a829f7356163d92ae0439a", "currentGasLimit": "0x0", "currentNumber": "0", "currentTimestamp": "0" @@ -57,4 +58,4 @@ "gasUsed": "0x0" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-simple.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-simple.json index e4555f220b3..3fef5b5a296 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-simple.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/berlin-simple.json @@ -46,6 +46,7 @@ "env": { "currentCoinbase": "0x0000000000000000000000000000000000000000", "currentDifficulty": "0x0", + "currentStateRoot": "0xde762e485411037c02119ff1115b422945b37efe91f4135dd442c3346629d0ef", "currentGasLimit": "0x0", "currentNumber": "0", "currentTimestamp": "0" @@ -93,4 +94,4 @@ "gasUsed": "0xa8ad" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-6780-selfdestruct-sweep.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-6780-selfdestruct-sweep.json index bc7d113ec79..7e1ffaf68d1 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-6780-selfdestruct-sweep.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-6780-selfdestruct-sweep.json @@ -49,6 +49,7 @@ "currentTimestamp": "1000", "currentRandom": "0", "currentDifficulty": "0", + "currentStateRoot": "0x3a0e532de836d767cae901aba671040fedc07557d277f7203066f640ed95f78d", "parentDifficulty": "0", "parentBaseFee": "7", "parentGasUsed": "0", @@ -113,4 +114,4 @@ "currentExcessBlobGas": "0x0" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-6780-selfdestruct-to-self.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-6780-selfdestruct-to-self.json index 7d8907d9bfb..d922c54baa7 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-6780-selfdestruct-to-self.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-6780-selfdestruct-to-self.json @@ -51,6 +51,7 @@ "currentTimestamp": "1000", "currentRandom": "0", "currentDifficulty": "0", + "currentStateRoot" : "0xddd3a541e86e2dd0293959736de63e1fad74ae95149f34740b1173378e82527a", "parentDifficulty": "0", "parentBaseFee": "7", "parentGasUsed": "0", @@ -112,4 +113,4 @@ "currentExcessBlobGas": "0x0" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-6780-selfdestruct-transient.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-6780-selfdestruct-transient.json index 1860990b0f7..997b29f76bd 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-6780-selfdestruct-transient.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-6780-selfdestruct-transient.json @@ -43,6 +43,7 @@ "currentTimestamp": "1000", "currentRandom": "0", "currentDifficulty": "0", + "currentStateRoot": "0xe8a9461dcfdbaa48bbddca4f4baa439e45f1489827e5deeb2797363323e34769", "parentDifficulty": "0", "parentBaseFee": "7", "parentGasUsed": "0", @@ -97,4 +98,4 @@ "currentExcessBlobGas": "0x0" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-blobs-per-tx.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-blobs-per-tx.json index b2ed324b3d6..0912b52b81a 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-blobs-per-tx.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/cancun-blobs-per-tx.json @@ -53,6 +53,7 @@ "currentTimestamp": "24", "currentRandom": "0", "currentDifficulty": "0", + "currentStateRoot": "0xd29f5a8dd1a63e0a299009f546bdf447fb61f1604d95e737bd8eb3c089d78060", "parentDifficulty": "0", "parentBaseFee": "7", "parentGasUsed": "21000", @@ -114,4 +115,4 @@ "currentExcessBlobGas": "0x0" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/istanbul-cumulative-gas.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/istanbul-cumulative-gas.json index d3dd7021df7..087eae7745b 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/istanbul-cumulative-gas.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/istanbul-cumulative-gas.json @@ -363,6 +363,7 @@ "currentGasLimit": "100000000000000000", "currentNumber": "1", "currentTimestamp": "1000", + "currentStateRoot": "0xf4f4aed0d1813d2880d8bb1cb5697303c20002ac6bfc206635645f43805ccbcb", "parentDifficulty": "131072", "parentGasUsed": "50000000000000000", "parentGasLimit": "100000000000000000", diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/london-env-no-basefee.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/london-env-no-basefee.json index 052ebb5260b..bb2d2da5dbc 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/london-env-no-basefee.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/london-env-no-basefee.json @@ -48,6 +48,7 @@ "currentGasLimit": "100000000000000000", "currentNumber": "1", "currentTimestamp": "1000", + "currentStateRoot": "0x5c0f211cd1bea44ac01bd3fd7e0ab742d316adabd139bcf60a7aac6c458d596d", "parentDifficulty": "131072", "parentBaseFee": "7", "parentGasUsed": "50000000000000000", @@ -103,4 +104,4 @@ "currentBaseFee": "0x7" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/london-hex.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/london-hex.json index 502ac628de6..1ccce1739d6 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/london-hex.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/london-hex.json @@ -48,6 +48,7 @@ "currentNumber": "0x1", "currentTimestamp": "0x0", "currentGasLimit": "0xff112233445566", + "currentStateRoot": "0x5775e461ac316f8c02466d23e4492d24bbe546336f6fc632dd1311d6a5b67b22", "previousHash": "0xb271e9e5796d0ff5a2fd519ba666393e42d4f38680854761121d84a7a96ff017", "parentTimestamp": "0x00", "parentDifficulty": "0x00", @@ -102,4 +103,4 @@ "currentBaseFee": "0xa" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-blockhash.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-blockhash.json index e1c331206d2..9b2908976fc 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-blockhash.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-blockhash.json @@ -48,6 +48,7 @@ "currentTimestamp": "24", "currentRandom": "0", "currentDifficulty": "0", + "currentStateRoot": "0x89412c0f1bb31b983d0317e6e2801a4e604e1ef9987a132ab63c52f2d5a3994b", "parentDifficulty": "0", "parentGasUsed": "0", "parentGasLimit": "100000000000000000", @@ -106,4 +107,4 @@ "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-init-code.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-init-code.json index d536a6a7260..6c502e80cc8 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-init-code.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-init-code.json @@ -42,6 +42,7 @@ "currentTimestamp": "12", "currentRandom": "0", "currentDifficulty": "0", + "currentStateRoot": "0x70c42824108fafccadbfce71e6e22660c4fad89be18be324cd15ef351969a8c8", "parentDifficulty": "0", "parentBaseFee": "7", "parentGasUsed": "0", @@ -81,4 +82,4 @@ "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-invalidTx.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-invalidTx.json index 7ec8a1d7763..79087bf545a 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-invalidTx.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-invalidTx.json @@ -42,6 +42,7 @@ "currentNumber": "1", "currentTimestamp": "1000", "currentDifficulty": "0x20000", + "currentStateRoot": "0xec92f4c572101075d17eb5aaf15c33df92b6d5519cbed635fc53353b99e8e6da", "currentBaseFee": "0xa", "blockHashes": { "0": "0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -84,4 +85,4 @@ "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-withdrawals-no-nonce.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-withdrawals-no-nonce.json index 86cefab468f..4e9f1febebe 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-withdrawals-no-nonce.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-withdrawals-no-nonce.json @@ -40,6 +40,7 @@ "currentTimestamp": "24", "currentRandom": "0", "currentDifficulty": "0", + "currentStateRoot": "0x41855ebd4b425591ae60f57de6ec700c8a1128c7761b63371b09671b5f1abb3f", "parentDifficulty": "0", "parentGasUsed": "0", "parentGasLimit": "100000000000000000", @@ -98,4 +99,4 @@ "withdrawalsRoot": "0xa485a3bd07e29cb8234b5f093d5216eb8b965fb2693c66fea254f6cacef97a6f" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-withdrawals-overflow.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-withdrawals-overflow.json index 8f8b29b7525..e334d94cbff 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-withdrawals-overflow.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-withdrawals-overflow.json @@ -34,6 +34,7 @@ "currentTimestamp": "12", "currentRandom": "0", "currentDifficulty": "0", + "currentStateRoot": "0x2a23ea102cd739aa3ad83a9be1555d621e50b3a3f780a7369ec2a6c03f22e18d", "parentDifficulty": "0", "parentBaseFee": "7", "parentGasUsed": "0", @@ -78,4 +79,4 @@ "withdrawalsRoot": "0x7bae13217931ff0d7a6c5823c996492f28cd92da5a7788a9a68856bb2206741a" } } -} \ No newline at end of file +} diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-withdrawals.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-withdrawals.json index 5a4efc2eea0..946a97ceac6 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-withdrawals.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/t8n/shanghai-withdrawals.json @@ -35,6 +35,7 @@ "currentTimestamp": "12", "currentRandom": "0", "currentDifficulty": "0", + "currentStateRoot": "0x7075d070d6ec70a91216fc2dba6ceae6b8d30815166f6b7b7249776767fb9855", "parentDifficulty": "0", "parentBaseFee": "7", "parentGasUsed": "0", @@ -81,4 +82,4 @@ "withdrawalsRoot": "0xa485a3bd07e29cb8234b5f093d5216eb8b965fb2693c66fea254f6cacef97a6f" } } -} \ No newline at end of file +} diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index a0b7db03d48..cf1a6e1f8a8 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -147,11 +147,14 @@ dependencies { implementation project(':crypto:algorithms') implementation project(':datatypes') implementation project(':ethereum:core') + implementation project(':metrics:core') + implementation project(':util') implementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts') implementation project(':ethereum:rlp') implementation project(':evm') implementation project(':services:kvstore') + implementation 'io.tmio:tuweni-rlp' implementation 'com.fasterxml.jackson.core:jackson-databind' implementation 'com.google.guava:guava' diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestUpdateAccumulator.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestUpdateAccumulator.java new file mode 100644 index 00000000000..c8349b7f790 --- /dev/null +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestUpdateAccumulator.java @@ -0,0 +1,44 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.referencetests; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.StorageSlotKey; +import org.hyperledger.besu.ethereum.bonsai.BonsaiAccount; +import org.hyperledger.besu.ethereum.bonsai.BonsaiValue; +import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiPreImageProxy; +import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldStateUpdateAccumulator; +import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldView; + +import org.apache.tuweni.bytes.Bytes; + +public class BonsaiReferenceTestUpdateAccumulator extends BonsaiWorldStateUpdateAccumulator { + private final BonsaiPreImageProxy preImageProxy; + + public BonsaiReferenceTestUpdateAccumulator( + final BonsaiWorldView world, + final Consumer> accountPreloader, + final Consumer storagePreloader, + final BonsaiPreImageProxy preImageProxy) { + super(world, accountPreloader, storagePreloader); + this.preImageProxy = preImageProxy; + } + + @Override + protected Hash hashAndSavePreImage(final Bytes bytes) { + // by default do not save hash preImages + return preImageProxy.hashAndSavePreImage(bytes); + } +} diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldState.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldState.java new file mode 100644 index 00000000000..e40b3774a97 --- /dev/null +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldState.java @@ -0,0 +1,182 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.referencetests; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.bonsai.cache.CachedMerkleTrieLoader; +import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiPreImageProxy; +import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateKeyValueStorage; +import org.hyperledger.besu.ethereum.bonsai.trielog.TrieLogAddedEvent; +import org.hyperledger.besu.ethereum.bonsai.trielog.TrieLogFactoryImpl; +import org.hyperledger.besu.ethereum.bonsai.trielog.TrieLogManager; +import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldState; +import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldStateUpdateAccumulator; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; +import org.hyperledger.besu.metrics.ObservableMetricsSystem; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; +import org.hyperledger.besu.plugin.services.trielogs.TrieLog; +import org.hyperledger.besu.plugin.services.trielogs.TrieLogEvent; +import org.hyperledger.besu.plugin.services.trielogs.TrieLogFactory; +import org.hyperledger.besu.util.Subscribers; + +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +import com.fasterxml.jackson.annotation.JsonCreator; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; + +public class BonsaiReferenceTestWorldState extends BonsaiWorldState + implements ReferenceTestWorldState { + + private final BonsaiReferenceTestWorldStateStorage refTestStorage; + private final BonsaiPreImageProxy preImageProxy; + + protected BonsaiReferenceTestWorldState( + final BonsaiReferenceTestWorldStateStorage worldStateStorage, + final CachedMerkleTrieLoader cachedMerkleTrieLoader, + final TrieLogManager trieLogManager, + final BonsaiPreImageProxy preImageProxy) { + super(worldStateStorage, cachedMerkleTrieLoader, trieLogManager); + this.refTestStorage = worldStateStorage; + this.preImageProxy = preImageProxy; + setAccumulator( + new BonsaiReferenceTestUpdateAccumulator( + this, + (addr, value) -> + cachedMerkleTrieLoader.preLoadAccount( + getWorldStateStorage(), worldStateRootHash, addr), + (addr, value) -> + cachedMerkleTrieLoader.preLoadStorageSlot(getWorldStateStorage(), addr, value), + preImageProxy)); + } + + @Override + public ReferenceTestWorldState copy() { + var layerCopy = new BonsaiReferenceTestWorldStateStorage(worldStateStorage, preImageProxy); + return new BonsaiReferenceTestWorldState( + layerCopy, cachedMerkleTrieLoader, trieLogManager, preImageProxy); + } + + @JsonCreator + public static BonsaiReferenceTestWorldState create( + final Map accounts) { + final ObservableMetricsSystem metricsSystem = new NoOpMetricsSystem(); + final CachedMerkleTrieLoader cachedMerkleTrieLoader = new CachedMerkleTrieLoader(metricsSystem); + final TrieLogManager trieLogManager = new NoOpTrieLogManager(); + final BonsaiPreImageProxy preImageProxy = + new BonsaiPreImageProxy.BonsaiReferenceTestPreImageProxy(); + + final BonsaiReferenceTestWorldStateStorage worldStateStorage = + new BonsaiReferenceTestWorldStateStorage( + new BonsaiWorldStateKeyValueStorage( + new InMemoryKeyValueStorageProvider(), metricsSystem), + preImageProxy); + + final BonsaiReferenceTestWorldState worldState = + new BonsaiReferenceTestWorldState( + worldStateStorage, cachedMerkleTrieLoader, trieLogManager, preImageProxy); + + final WorldUpdater updater = worldState.updater(); + for (final Map.Entry entry : accounts.entrySet()) { + ReferenceTestWorldState.insertAccount( + updater, Address.fromHexString(entry.getKey()), entry.getValue()); + } + updater.commit(); + return worldState; + } + + @Override + public Stream streamAccounts(final Bytes32 startKeyHash, final int limit) { + return this.refTestStorage.streamAccounts(this, startKeyHash, limit); + } + + static class NoOpTrieLogManager implements TrieLogManager { + private final Subscribers trieLogObservers = Subscribers.create(); + private final TrieLogFactory trieLogFactory = new TrieLogFactoryImpl(); + + @Override + public void saveTrieLog( + final BonsaiWorldStateUpdateAccumulator localUpdater, + final Hash forWorldStateRootHash, + final BlockHeader forBlockHeader, + final BonsaiWorldState forWorldState) { + // notify trie log added observers, synchronously + TrieLog trieLog = trieLogFactory.create(localUpdater, forBlockHeader); + trieLogObservers.forEach(o -> o.onTrieLogAdded(new TrieLogAddedEvent(trieLog))); + } + + @Override + public void addCachedLayer( + final BlockHeader blockHeader, + final Hash worldStateRootHash, + final BonsaiWorldState forWorldState) {} + + @Override + public boolean containWorldStateStorage(final Hash blockHash) { + return false; + } + + @Override + public Optional getWorldState(final Hash blockHash) { + return Optional.empty(); + } + + @Override + public Optional getNearestWorldState(final BlockHeader blockHeader) { + return Optional.empty(); + } + + @Override + public Optional getHeadWorldState( + final Function> hashBlockHeaderFunction) { + return Optional.empty(); + } + + @Override + public long getMaxLayersToLoad() { + return 0; + } + + @Override + public void reset() {} + + @Override + public Optional getTrieLogLayer(final Hash blockHash) { + return Optional.empty(); + } + + @Override + public synchronized long subscribe(final TrieLogEvent.TrieLogObserver sub) { + return trieLogObservers.subscribe(sub); + } + + @Override + public synchronized void unsubscribe(final long id) { + trieLogObservers.unsubscribe(id); + } + } + + @Override + protected Hash hashAndSavePreImage(final Bytes value) { + // by default do not save has preImages + return preImageProxy.hashAndSavePreImage(value); + } +} diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldStateStorage.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldStateStorage.java new file mode 100644 index 00000000000..b998bf7d25b --- /dev/null +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BonsaiReferenceTestWorldStateStorage.java @@ -0,0 +1,85 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.referencetests; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.bonsai.BonsaiAccount; +import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiPreImageProxy; +import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateKeyValueStorage; +import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateLayerStorage; +import org.hyperledger.besu.ethereum.bonsai.worldview.BonsaiWorldView; +import org.hyperledger.besu.evm.account.AccountStorageEntry; +import org.hyperledger.besu.evm.worldstate.WorldState; + +import java.util.Comparator; +import java.util.NavigableMap; +import java.util.Optional; +import java.util.TreeMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.rlp.RLP; +import org.apache.tuweni.units.bigints.UInt256; + +public class BonsaiReferenceTestWorldStateStorage extends BonsaiWorldStateLayerStorage { + private final BonsaiPreImageProxy preImageProxy; + + public BonsaiReferenceTestWorldStateStorage( + final BonsaiWorldStateKeyValueStorage parent, final BonsaiPreImageProxy preImageProxy) { + super(parent); + this.preImageProxy = preImageProxy; + } + + @Override + public NavigableMap storageEntriesFrom( + final Hash addressHash, final Bytes32 startKeyHash, final int limit) { + return streamFlatStorages(addressHash, startKeyHash, UInt256.MAX_VALUE, limit) + .entrySet() + // map back to slot keys using preImage provider: + .stream() + .collect( + Collectors.toMap( + e -> e.getKey(), + e -> + AccountStorageEntry.create( + UInt256.fromBytes(RLP.decodeValue(e.getValue())), + Hash.wrap(e.getKey()), + preImageProxy.getStorageTrieKeyPreimage(e.getKey())), + (a, b) -> a, + TreeMap::new)); + } + + public Stream streamAccounts( + final BonsaiWorldView context, final Bytes32 startKeyHash, final int limit) { + return streamFlatAccounts(startKeyHash, UInt256.MAX_VALUE, limit) + .entrySet() + // map back to addresses using preImage provider: + .stream() + .map( + entry -> + preImageProxy + .getAccountTrieKeyPreimage(entry.getKey()) + .map( + address -> + new WorldState.StreamableAccount( + Optional.of(address), + BonsaiAccount.fromRLP(context, address, entry.getValue(), false)))) + .filter(Optional::isPresent) + .map(Optional::get) + .sorted(Comparator.comparing(account -> account.getAddress().orElse(Address.ZERO))); + } +} diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/DefaultReferenceTestWorldState.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/DefaultReferenceTestWorldState.java new file mode 100644 index 00000000000..ec0b058ba99 --- /dev/null +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/DefaultReferenceTestWorldState.java @@ -0,0 +1,60 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.referencetests; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage; +import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValueStorage; +import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState; +import org.hyperledger.besu.evm.worldstate.WorldState; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; +import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonCreator; + +public class DefaultReferenceTestWorldState extends DefaultMutableWorldState + implements ReferenceTestWorldState { + + DefaultReferenceTestWorldState() { + super( + new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()), + new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage())); + } + + public DefaultReferenceTestWorldState(final WorldState worldState) { + super(worldState); + } + + @Override + public ReferenceTestWorldState copy() { + return new DefaultReferenceTestWorldState(this); + } + + @JsonCreator + public static ReferenceTestWorldState create(final Map accounts) { + final ReferenceTestWorldState worldState = new DefaultReferenceTestWorldState(); + final WorldUpdater updater = worldState.updater(); + + for (final Map.Entry entry : accounts.entrySet()) { + ReferenceTestWorldState.insertAccount( + updater, Address.fromHexString(entry.getKey()), entry.getValue()); + } + + updater.commit(); + return worldState; + } +} diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseEipSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseEipSpec.java index b396eff98f8..def8c14f17e 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseEipSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseEipSpec.java @@ -18,7 +18,6 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.Transaction; -import org.hyperledger.besu.evm.worldstate.WorldState; import java.util.function.Supplier; @@ -37,7 +36,7 @@ public class GeneralStateTestCaseEipSpec { // is why this is a supplier: calling get() actually does the signing. private final Supplier transactionSupplier; - private final WorldState initialWorldState; + private final ReferenceTestWorldState initialWorldState; private final Hash expectedRootHash; @@ -54,7 +53,7 @@ public class GeneralStateTestCaseEipSpec { GeneralStateTestCaseEipSpec( final String fork, final Supplier transactionSupplier, - final WorldState initialWorldState, + final ReferenceTestWorldState initialWorldState, final Hash expectedRootHash, final Hash expectedLogsHash, final BlockHeader blockHeader, @@ -78,7 +77,7 @@ public String getFork() { return fork; } - public WorldState getInitialWorldState() { + public ReferenceTestWorldState getInitialWorldState() { return initialWorldState; } diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java index 328922e1876..59f0a86fe09 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java @@ -17,7 +17,10 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; +import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -37,6 +40,7 @@ public class GeneralStateTestCaseSpec { private final Map> finalStateSpecs; + private static final BlockHeaderFunctions MAINNET_FUNCTIONS = new MainnetBlockHeaderFunctions(); @JsonCreator public GeneralStateTestCaseSpec( @@ -49,7 +53,7 @@ public GeneralStateTestCaseSpec( } private Map> generate( - final BlockHeader blockHeader, + final BlockHeader rawBlockHeader, final ReferenceTestWorldState initialWorldState, final Map> postSections, final StateTestVersionedTransaction versionedTransaction) { @@ -62,6 +66,11 @@ private Map> generate( final List post = entry.getValue(); final List specs = new ArrayList<>(post.size()); for (final PostSection p : post) { + final BlockHeader blockHeader = + BlockHeaderBuilder.fromHeader(rawBlockHeader) + .stateRoot(p.rootHash) + .blockHeaderFunctions(MAINNET_FUNCTIONS) + .buildBlockHeader(); final Supplier txSupplier = () -> versionedTransaction.get(p.indexes); specs.add( new GeneralStateTestCaseEipSpec( diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestEnv.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestEnv.java index 79356331fb6..5f7af60719a 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestEnv.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestEnv.java @@ -104,6 +104,7 @@ public ReferenceTestEnv( @JsonProperty("currentBaseFee") final String baseFee, @JsonProperty("currentTimestamp") final String timestamp, @JsonProperty("currentRandom") final String random, + @JsonProperty("currentStateRoot") final String stateRoot, @JsonProperty("previousHash") final String previousHash, @JsonProperty("parentDifficulty") final String parentDifficulty, @JsonProperty("parentBaseFee") final String parentBaseFee, @@ -127,7 +128,7 @@ public ReferenceTestEnv( generateTestBlockHash(previousHash, number), Hash.EMPTY_LIST_HASH, // ommersHash Address.fromHexString(coinbase), - Hash.EMPTY, // stateRoot + Optional.ofNullable(stateRoot).map(Hash::fromHexString).orElse(Hash.EMPTY), // stateRoot Hash.EMPTY, // transactionsRoot Hash.EMPTY, // receiptsRoot new LogsBloomFilter(), diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java index 659847c1211..825d7a2cd4b 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java @@ -17,12 +17,9 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage; -import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStatePreimageKeyValueStorage; -import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState; +import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import java.util.HashMap; import java.util.Map; @@ -35,9 +32,9 @@ /** Represent a worldState for testing. */ @JsonIgnoreProperties(ignoreUnknown = true) -public class ReferenceTestWorldState extends DefaultMutableWorldState { +public interface ReferenceTestWorldState extends MutableWorldState { - public static class AccountMock { + class AccountMock { private final long nonce; private final Wei balance; private final Bytes code; @@ -90,22 +87,11 @@ static void insertAccount( } } - @JsonCreator - public static ReferenceTestWorldState create(final Map accounts) { - final ReferenceTestWorldState worldState = new ReferenceTestWorldState(); - final WorldUpdater updater = worldState.updater(); - - for (final Map.Entry entry : accounts.entrySet()) { - insertAccount(updater, Address.fromHexString(entry.getKey()), entry.getValue()); - } + ReferenceTestWorldState copy(); - updater.commit(); - return worldState; - } - - public ReferenceTestWorldState() { - super( - new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()), - new WorldStatePreimageKeyValueStorage(new InMemoryKeyValueStorage())); + @JsonCreator + static ReferenceTestWorldState create(final Map accounts) { + // delegate to a Bonsai reference test world state: + return BonsaiReferenceTestWorldState.create(accounts); } } diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java index e01e08c0414..83388fe2526 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java @@ -21,8 +21,10 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; +import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; @@ -31,6 +33,7 @@ import org.hyperledger.besu.ethereum.referencetests.GeneralStateTestCaseSpec; import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain; import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules; +import org.hyperledger.besu.ethereum.referencetests.ReferenceTestWorldState; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState; import org.hyperledger.besu.evm.account.Account; @@ -123,7 +126,7 @@ public static Collection generateTestParametersForConfig(final String[ public static void executeTest(final GeneralStateTestCaseEipSpec spec) { final BlockHeader blockHeader = spec.getBlockHeader(); - final WorldState initialWorldState = spec.getInitialWorldState(); + final ReferenceTestWorldState initialWorldState = spec.getInitialWorldState(); final Transaction transaction = spec.getTransaction(); // Sometimes the tests ask us assemble an invalid transaction. If we have @@ -136,7 +139,7 @@ public static void executeTest(final GeneralStateTestCaseEipSpec spec) { return; } - final MutableWorldState worldState = new DefaultMutableWorldState(initialWorldState); + final MutableWorldState worldState = initialWorldState.copy(); // Several of the GeneralStateTests check if the transaction could potentially // consume more gas than is left for the block it's attempted to be included in. // This check is performed within the `BlockImporter` rather than inside the @@ -178,6 +181,7 @@ public static void executeTest(final GeneralStateTestCaseEipSpec spec) { worldStateUpdater.deleteAccount(coinbase.getAddress()); } worldStateUpdater.commit(); + worldState.persist(blockHeader); // Check the world state root hash. final Hash expectedRootHash = spec.getExpectedRootHash(); diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index aedc706bab1..e544419b8ab 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -69,7 +69,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = 'fPOd/MnNB1PwfTV7HDTXc3oYRcoeUzMJrlzDUdg/HNk=' + knownHash = 'tpSnjt4HgqSiOTJhBbYdB0r1nFX4QZbicjfloI71Wf0=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java index 6ccdb3b0a24..3d0e9e5efea 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/KeyValueStorage.java @@ -82,6 +82,17 @@ public interface KeyValueStorage extends Closeable { */ Stream> streamFromKey(final byte[] startKey); + /** + * Returns a stream of key-value pairs starting from the specified key, ending at the specified + * key. This method is used to retrieve a stream of data from the storage, starting from the given + * key. If no data is available from the specified key onwards, an empty stream is returned. + * + * @param startKey The key from which the stream should start. + * @param endKey The key at which the stream should stop. + * @return A stream of key-value pairs starting from the specified key. + */ + Stream> streamFromKey(final byte[] startKey, final byte[] endKey); + /** * Returns a stream of all keys. * diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/SegmentedKeyValueStorage.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/SegmentedKeyValueStorage.java index 21e6ecca6f6..55d04d470c4 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/SegmentedKeyValueStorage.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/SegmentedKeyValueStorage.java @@ -78,6 +78,19 @@ default boolean containsKey(final SegmentIdentifier segment, final byte[] key) Stream> streamFromKey( final SegmentIdentifier segmentIdentifier, final byte[] startKey); + /** + * Returns a stream of key-value pairs starting from the specified key, ending at the specified + * key. This method is used to retrieve a stream of data from the storage, starting from the given + * key. If no data is available from the specified key onwards, an empty stream is returned. + * + * @param segmentIdentifier The segment identifier whose keys we want to stream. + * @param startKey The key from which the stream should start. + * @param endKey The key at which the stream should stop. + * @return A stream of key-value pairs starting from the specified key. + */ + Stream> streamFromKey( + final SegmentIdentifier segmentIdentifier, final byte[] startKey, final byte[] endKey); + /** * Stream keys. * diff --git a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueSnapshot.java b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueSnapshot.java index d51bdb310b8..6a123069001 100644 --- a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueSnapshot.java +++ b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueSnapshot.java @@ -33,7 +33,6 @@ import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; -import org.apache.tuweni.bytes.Bytes; import org.rocksdb.ColumnFamilyHandle; import org.rocksdb.OptimisticTransactionDB; import org.slf4j.Logger; @@ -83,7 +82,13 @@ public Stream> stream(final SegmentIdentifier segment) { @Override public Stream> streamFromKey( final SegmentIdentifier segment, final byte[] startKey) { - return stream(segment).filter(e -> Bytes.wrap(startKey).compareTo(Bytes.wrap(e.getKey())) <= 0); + return snapTx.streamFromKey(segment, startKey); + } + + @Override + public Stream> streamFromKey( + final SegmentIdentifier segment, final byte[] startKey, final byte[] endKey) { + return snapTx.streamFromKey(segment, startKey, endKey); } @Override diff --git a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java index 3d98be37523..9462f9bcb14 100644 --- a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java +++ b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java @@ -333,6 +333,17 @@ public Stream> streamFromKey( return RocksDbIterator.create(rocksIterator).toStream(); } + @Override + public Stream> streamFromKey( + final SegmentIdentifier segmentIdentifier, final byte[] startKey, final byte[] endKey) { + final Bytes endKeyBytes = Bytes.wrap(endKey); + final RocksIterator rocksIterator = getDB().newIterator(safeColumnHandle(segmentIdentifier)); + rocksIterator.seek(startKey); + return RocksDbIterator.create(rocksIterator) + .toStream() + .takeWhile(e -> endKeyBytes.compareTo(Bytes.wrap(e.getKey())) >= 0); + } + @Override public Stream streamKeys(final SegmentIdentifier segmentIdentifier) { final RocksIterator rocksIterator = getDB().newIterator(safeColumnHandle(segmentIdentifier)); diff --git a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBSnapshotTransaction.java b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBSnapshotTransaction.java index c1c404628d9..0897493eb19 100644 --- a/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBSnapshotTransaction.java +++ b/plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBSnapshotTransaction.java @@ -28,6 +28,7 @@ import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; +import org.apache.tuweni.bytes.Bytes; import org.rocksdb.ColumnFamilyHandle; import org.rocksdb.OptimisticTransactionDB; import org.rocksdb.ReadOptions; @@ -165,6 +166,48 @@ public Stream streamKeys(final SegmentIdentifier segmentId) { rocksIterator.seekToFirst(); return RocksDbIterator.create(rocksIterator).toStreamKeys(); } + /** + * Returns a stream of key-value pairs starting from the specified key. This method is used to + * retrieve a stream of data reading through the transaction, starting from the given key. If no + * data is available from the specified key onwards, an empty stream is returned. + * + * @param segment The segment identifier whose keys we want to stream. + * @param startKey The key from which the stream should start. + * @return A stream of key-value pairs starting from the specified key. + */ + public Stream> streamFromKey( + final SegmentIdentifier segment, final byte[] startKey) { + throwIfClosed(); + + final RocksIterator rocksIterator = + db.newIterator(columnFamilyMapper.apply(segment), readOptions); + rocksIterator.seek(startKey); + return RocksDbIterator.create(rocksIterator).toStream(); + } + + /** + * Returns a stream of key-value pairs starting from the specified key, ending at the specified + * key. This method is used to retrieve a stream of data reading through the transaction, starting + * from the given key. If no data is available from the specified key onwards, an empty stream is + * returned. + * + * @param segment The segment identifier whose keys we want to stream. + * @param startKey The key from which the stream should start. + * @param endKey The key at which the stream should stop. + * @return A stream of key-value pairs starting from the specified key. + */ + public Stream> streamFromKey( + final SegmentIdentifier segment, final byte[] startKey, final byte[] endKey) { + throwIfClosed(); + final Bytes endKeyBytes = Bytes.wrap(endKey); + + final RocksIterator rocksIterator = + db.newIterator(columnFamilyMapper.apply(segment), readOptions); + rocksIterator.seek(startKey); + return RocksDbIterator.create(rocksIterator) + .toStream() + .takeWhile(e -> endKeyBytes.compareTo(Bytes.wrap(e.getKey())) >= 0); + } @Override public void commit() throws StorageException { diff --git a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LayeredKeyValueStorage.java b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LayeredKeyValueStorage.java index 9a028becdf8..ffc57530682 100644 --- a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LayeredKeyValueStorage.java +++ b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LayeredKeyValueStorage.java @@ -122,8 +122,18 @@ public Stream> stream(final SegmentIdentifier segmentId) { @Override public Stream> streamFromKey( final SegmentIdentifier segmentId, final byte[] startKey) { + final Bytes startKeyBytes = Bytes.wrap(startKey); + return stream(segmentId).filter(e -> startKeyBytes.compareTo(Bytes.wrap(e.getKey())) <= 0); + } + + @Override + public Stream> streamFromKey( + final SegmentIdentifier segmentId, final byte[] startKey, final byte[] endKey) { + final Bytes startKeyBytes = Bytes.wrap(startKey); + final Bytes endKeyBytes = Bytes.wrap(endKey); return stream(segmentId) - .filter(e -> Bytes.wrap(startKey).compareTo(Bytes.wrap(e.getKey())) <= 0); + .filter(e -> startKeyBytes.compareTo(Bytes.wrap(e.getKey())) <= 0) + .filter(e -> endKeyBytes.compareTo(Bytes.wrap(e.getKey())) >= 0); } @Override diff --git a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java index 7ab04ec26e8..aa52e49284f 100644 --- a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java +++ b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/LimitedInMemoryKeyValueStorage.java @@ -118,6 +118,13 @@ public Stream> streamFromKey(final byte[] startKey) { return stream().filter(e -> Bytes.wrap(startKey).compareTo(Bytes.wrap(e.getKey())) <= 0); } + @Override + public Stream> streamFromKey(final byte[] startKey, final byte[] endKey) { + return stream() + .filter(e -> Bytes.wrap(startKey).compareTo(Bytes.wrap(e.getKey())) <= 0) + .takeWhile(e -> Bytes.wrap(endKey).compareTo(Bytes.wrap(e.getKey())) >= 0); + } + @Override public Stream streamKeys() { final Lock lock = rwLock.readLock(); diff --git a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedInMemoryKeyValueStorage.java b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedInMemoryKeyValueStorage.java index adc9a4c71dc..66d70bac4dc 100644 --- a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedInMemoryKeyValueStorage.java +++ b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedInMemoryKeyValueStorage.java @@ -149,8 +149,19 @@ public Stream> stream(final SegmentIdentifier segmentIdenti @Override public Stream> streamFromKey( final SegmentIdentifier segmentIdentifier, final byte[] startKey) { + final Bytes startKeyBytes = Bytes.wrap(startKey); return stream(segmentIdentifier) - .filter(e -> Bytes.wrap(startKey).compareTo(Bytes.wrap(e.getKey())) <= 0); + .filter(e -> startKeyBytes.compareTo(Bytes.wrap(e.getKey())) <= 0); + } + + @Override + public Stream> streamFromKey( + final SegmentIdentifier segmentIdentifier, final byte[] startKey, final byte[] endKey) { + final Bytes startKeyHash = Bytes.wrap(startKey); + final Bytes endKeyHash = Bytes.wrap(endKey); + return stream(segmentIdentifier) + .filter(e -> startKeyHash.compareTo(Bytes.wrap(e.getKey())) <= 0) + .filter(e -> endKeyHash.compareTo(Bytes.wrap(e.getKey())) >= 0); } @Override diff --git a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.java b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.java index 81a492624f2..a02e9284fa6 100644 --- a/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.java +++ b/services/kvstore/src/main/java/org/hyperledger/besu/services/kvstore/SegmentedKeyValueStorageAdapter.java @@ -88,8 +88,14 @@ public Stream> stream() { } @Override - public Stream> streamFromKey(final byte[] startKey) throws StorageException { - return storage.streamFromKey(segmentIdentifier, startKey); + public Stream> streamFromKey(final byte[] startKeyHash) + throws StorageException { + return storage.streamFromKey(segmentIdentifier, startKeyHash); + } + + @Override + public Stream> streamFromKey(final byte[] startKey, final byte[] endKey) { + return storage.streamFromKey(segmentIdentifier, startKey, endKey); } @Override