From 836137a76a82f5286f46f70ba5f0273ddc277af4 Mon Sep 17 00:00:00 2001 From: Luis Pinto Date: Tue, 19 Nov 2024 14:08:29 +0000 Subject: [PATCH] [REMOVE AFTER #7971] eip-7709 implement BLOCKHASH opcode from system contract state Signed-off-by: Luis Pinto --- .../besu/services/TraceServiceImpl.java | 5 +- .../methods/ExecuteTransactionStep.java | 6 +- .../internal/processor/BlockReplay.java | 12 +- .../internal/processor/BlockTracer.java | 6 +- .../internal/processor/TransactionTracer.java | 6 +- .../privateProcessor/PrivateBlockReplay.java | 4 + .../privateProcessor/PrivateBlockTracer.java | 6 +- .../processor/TransactionTracerTest.java | 3 + .../blockcreation/AbstractBlockCreator.java | 8 +- .../txselection/BlockTransactionSelector.java | 7 +- .../vm/TraceTransactionIntegrationTest.java | 16 +- .../BlockHashOperationBenchmark.java | 4 +- .../mainnet/AbstractBlockProcessor.java | 11 +- .../mainnet/MainnetTransactionProcessor.java | 2 +- .../ethereum/mainnet/SystemCallProcessor.java | 6 +- .../mainnet/blockhash/BlockHashProcessor.java | 10 +- .../blockhash/CancunBlockHashProcessor.java | 9 +- .../blockhash/Eip7709BlockHashProcessor.java | 29 +++ .../blockhash/FrontierBlockHashProcessor.java | 25 ++- .../blockhash/PragueBlockHashProcessor.java | 23 +-- .../MainnetParallelBlockProcessor.java | 6 +- ...lelizedConcurrentTransactionProcessor.java | 6 +- .../requests/ProcessRequestContext.java | 2 +- ...PrivateGroupRehydrationBlockProcessor.java | 2 +- .../privacy/PrivateStateRehydration.java | 3 +- .../privacy/PrivateTransactionProcessor.java | 2 +- .../privacy/PrivateTransactionSimulator.java | 3 +- .../PrivateMigrationBlockProcessor.java | 3 +- .../migration/PrivateStorageMigration.java | 3 +- .../transaction/TransactionSimulator.java | 5 +- ...va => BlockchainBasedBlockHashLookup.java} | 20 +- .../vm/ContractBasedBlockHashLookup.java | 117 +++++++++++ .../core/MessageFrameTestFixture.java | 11 +- .../MainnetTransactionProcessorTest.java | 2 +- .../mainnet/PrivacyBlockProcessorTest.java | 3 + .../mainnet/SystemCallProcessorTest.java | 6 +- .../blockhash/BlockHashProcessorTest.java | 14 +- ...zedConcurrentTransactionProcessorTest.java | 12 +- ...lexiblePrivacyPrecompiledContractTest.java | 2 +- .../PrivacyPluginPrecompiledContractTest.java | 2 +- .../PrivacyPrecompiledContractTest.java | 2 +- ...> BlockchainBasedBlockHashLookupTest.java} | 83 +++++--- .../vm/ContractBasedBlockHashLookupTest.java | 192 ++++++++++++++++++ .../ethereum/vm/DebugOperationTracerTest.java | 2 + .../besu/evmtool/EvmToolCommand.java | 6 +- .../besu/evmtool/StateTestSubCommand.java | 5 +- .../hyperledger/besu/evmtool/T8nExecutor.java | 26 ++- .../evmtool/benchmarks/BenchmarkExecutor.java | 2 +- .../vm/GeneralStateReferenceTestTools.java | 2 +- .../besu/evm/blockhash/BlockHashLookup.java | 28 +++ .../besu/evm/fluent/EVMExecutor.java | 4 +- .../besu/evm/frame/MessageFrame.java | 2 +- .../hyperledger/besu/evm/frame/TxValues.java | 3 +- .../evm/operation/BlockHashOperation.java | 65 +++--- .../hyperledger/besu/evm/code/CodeV0Test.java | 2 +- .../besu/evm/fluent/EVMExecutorTest.java | 2 +- .../besu/evm/frame/MessageFrameTest.java | 2 +- .../AbstractCreateOperationTest.java | 3 +- .../evm/operation/BlockHashOperationTest.java | 84 ++++---- .../evm/operation/Create2OperationTest.java | 5 +- .../evm/operation/CreateOperationTest.java | 3 +- .../evm/operation/EofCreateOperationTest.java | 3 +- .../besu/evm/operation/PushOperationTest.java | 2 +- .../operation/SelfDestructOperationTest.java | 3 +- .../besu/evm/precompile/Benchmarks.java | 2 +- .../testutils/TestMessageFrameBuilder.java | 5 +- .../besu/evm/toy/EvmToyCommand.java | 2 +- 67 files changed, 706 insertions(+), 256 deletions(-) create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/Eip7709BlockHashProcessor.java rename ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/{CachingBlockHashLookup.java => BlockchainBasedBlockHashLookup.java} (75%) create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/ContractBasedBlockHashLookup.java rename ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/{CachingBlockHashLookupTest.java => BlockchainBasedBlockHashLookupTest.java} (61%) create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/ContractBasedBlockHashLookupTest.java create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/blockhash/BlockHashLookup.java 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 6a0804b6f46..225825c72e0 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java @@ -31,7 +31,6 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.plugin.Unstable; import org.hyperledger.besu.plugin.data.BlockTraceResult; @@ -217,7 +216,9 @@ private List trace( transaction, protocolSpec.getMiningBeneficiaryCalculator().calculateBeneficiary(header), tracer, - new CachingBlockHashLookup(header, blockchain), + protocolSpec + .getBlockHashProcessor() + .createBlockHashLookup(blockchain, header), false, blobGasPrice); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/ExecuteTransactionStep.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/ExecuteTransactionStep.java index db062a64d95..9f596b8a436 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/ExecuteTransactionStep.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/ExecuteTransactionStep.java @@ -26,9 +26,8 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; -import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import java.util.List; import java.util.Optional; @@ -95,7 +94,8 @@ public TransactionTrace apply(final TransactionTrace transactionTrace) { maybeParentHeader .map(parent -> calculateExcessBlobGasForParent(protocolSpec, parent)) .orElse(BlobGas.ZERO)); - final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(header, blockchain); + final BlockHashLookup blockHashLookup = + protocolSpec.getBlockHashProcessor().createBlockHashLookup(blockchain, header); result = transactionProcessor.processTransaction( chainUpdater.getNextUpdater(), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java index b1a9be021e9..ddd09c28a65 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockReplay.java @@ -30,8 +30,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; -import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import java.util.List; import java.util.Optional; @@ -90,7 +89,8 @@ public Optional beforeTransactionInBlock( return performActionWithBlock( blockHash, (body, header, blockchain, transactionProcessor, protocolSpec) -> { - final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(header, blockchain); + final BlockHashLookup blockHashLookup = + protocolSpec.getBlockHashProcessor().createBlockHashLookup(blockchain, header); final Wei blobGasPrice = protocolSpec .getFeeMarket() @@ -137,7 +137,7 @@ public Optional afterTransactionInBlock( blockHeader, transaction, spec.getMiningBeneficiaryCalculator().calculateBeneficiary(blockHeader), - new CachingBlockHashLookup(blockHeader, blockchain), + spec.getBlockHashProcessor().createBlockHashLookup(blockchain, blockHeader), false, TransactionValidationParams.blockReplay(), blobGasPrice); @@ -180,6 +180,10 @@ private Optional getBlock(final Hash blockHash) { return Optional.empty(); } + public ProtocolSpec getProtocolSpec(final BlockHeader header) { + return protocolSchedule.getByBlockHeader(header); + } + @FunctionalInterface public interface BlockAction { Optional perform( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java index 5ede2d4a7b2..67395c6999e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/BlockTracer.java @@ -19,7 +19,6 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; @@ -69,7 +68,10 @@ private BlockReplay.TransactionAction prepareReplayAction( transaction, header.getCoinbase(), tracer, - new CachingBlockHashLookup(header, blockchain), + blockReplay + .getProtocolSpec(header) + .getBlockHashProcessor() + .createBlockHashLookup(blockchain, header), false, blobGasPrice); final List traceFrames = tracer.copyTraceFrames(); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java index 6524109ac6f..8416b499bc2 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracer.java @@ -29,7 +29,6 @@ import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams; import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.tracing.StandardJsonTracer; @@ -192,7 +191,10 @@ private TransactionProcessingResult processTransaction( transaction, header.getCoinbase(), tracer, - new CachingBlockHashLookup(header, blockchain), + blockReplay + .getProtocolSpec(header) + .getBlockHashProcessor() + .createBlockHashLookup(blockchain, header), false, ImmutableTransactionValidationParams.builder().isAllowFutureNonce(true).build(), blobGasPrice); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/privateProcessor/PrivateBlockReplay.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/privateProcessor/PrivateBlockReplay.java index de5c11940b5..80721c90cbd 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/privateProcessor/PrivateBlockReplay.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/privateProcessor/PrivateBlockReplay.java @@ -89,6 +89,10 @@ private Optional performActionWithBlock( return action.perform(body, header, blockchain, transactionProcessor, protocolSpec); } + public ProtocolSpec getProtocolSpec(final BlockHeader header) { + return protocolSchedule.getByBlockHeader(header); + } + @FunctionalInterface public interface BlockAction { Optional perform( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/privateProcessor/PrivateBlockTracer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/privateProcessor/PrivateBlockTracer.java index c58af8fc3fc..cbf4870f903 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/privateProcessor/PrivateBlockTracer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/privateProcessor/PrivateBlockTracer.java @@ -18,7 +18,6 @@ import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.privacy.storage.PrivateBlockMetadata; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; import org.hyperledger.besu.evm.worldstate.StackedUpdater; import org.hyperledger.besu.evm.worldstate.WorldUpdater; @@ -76,7 +75,10 @@ private PrivateBlockReplay.TransactionAction prepareRep transaction, header.getCoinbase(), tracer, - new CachingBlockHashLookup(header, blockchain), + blockReplay + .getProtocolSpec(header) + .getBlockHashProcessor() + .createBlockHashLookup(blockchain, header), Bytes32.wrap(Bytes.fromBase64String(privacyGroupId))); final List traceFrames = tracer.copyTraceFrames(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java index 6626a66bb09..e099f4b4927 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java @@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.ethereum.mainnet.blockhash.BlockHashProcessor; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; @@ -87,6 +88,7 @@ public class TransactionTracerTest { @Mock private ProtocolSpec protocolSpec; @Mock private GasCalculator gasCalculator; + @Mock private BlockHashProcessor blockHashProcessor; @Mock private Tracer.TraceableState mutableWorldState; @@ -123,6 +125,7 @@ public void setUp() throws Exception { when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0L)); when(blockchain.getChainHeadHeader()).thenReturn(blockHeader); when(protocolSpec.getGasCalculator()).thenReturn(gasCalculator); + when(protocolSpec.getBlockHashProcessor()).thenReturn(blockHashProcessor); when(protocolContext.getBadBlockManager()).thenReturn(badBlockManager); lenient().when(gasCalculator.computeExcessBlobGas(anyLong(), anyInt())).thenReturn(0L); } 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 c4a3f89aaa3..a532486ef6d 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 @@ -52,7 +52,6 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.mainnet.requests.ProcessRequestContext; import org.hyperledger.besu.ethereum.mainnet.requests.RequestProcessorCoordinator; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.plugin.services.exception.StorageException; @@ -213,8 +212,7 @@ protected BlockCreationResult createBlock( newProtocolSpec .getBlockHashProcessor() - .processBlockHashes( - protocolContext.getBlockchain(), disposableWorldState, processableBlockHeader); + .processBlockHashes(disposableWorldState, processableBlockHeader); throwIfStopped(); @@ -261,7 +259,9 @@ protected BlockCreationResult createBlock( disposableWorldState, newProtocolSpec, transactionResults.getReceipts(), - new CachingBlockHashLookup(processableBlockHeader, protocolContext.getBlockchain()), + newProtocolSpec + .getBlockHashProcessor() + .createBlockHashLookup(protocolContext.getBlockchain(), processableBlockHeader), operationTracer); Optional> maybeRequests = diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java index a9b57a697ae..b353a1a01d5 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java @@ -45,9 +45,8 @@ import org.hyperledger.besu.ethereum.mainnet.blockhash.BlockHashProcessor; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer; @@ -376,7 +375,9 @@ private TransactionSelectionResult evaluatePostProcessing( private TransactionProcessingResult processTransaction( final PendingTransaction pendingTransaction, final WorldUpdater worldStateUpdater) { final BlockHashLookup blockHashLookup = - new CachingBlockHashLookup(blockSelectionContext.pendingBlockHeader(), blockchain); + blockSelectionContext + .blockHashProcessor() + .createBlockHashLookup(blockchain, blockSelectionContext.pendingBlockHeader()); return transactionProcessor.processTransaction( worldStateUpdater, blockSelectionContext.pendingBlockHeader(), diff --git a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java index a029c4b4a49..31d04feeda1 100644 --- a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java +++ b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java @@ -16,7 +16,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.config.GenesisConfigFile; import org.hyperledger.besu.crypto.KeyPair; @@ -34,11 +33,13 @@ import org.hyperledger.besu.ethereum.debug.TraceOptions; import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.List; @@ -76,11 +77,14 @@ public void setUp() { blockchain = contextTestFixture.getBlockchain(); worldStateArchive = contextTestFixture.getStateArchive(); final ProtocolSchedule protocolSchedule = contextTestFixture.getProtocolSchedule(); - transactionProcessor = - protocolSchedule - .getByBlockHeader(new BlockHeaderTestFixture().number(0L).buildHeader()) - .getTransactionProcessor(); - blockHashLookup = new CachingBlockHashLookup(genesisBlock.getHeader(), blockchain); + final ProtocolSpec protocolSpec = + protocolSchedule.getByBlockHeader(new BlockHeaderTestFixture().number(0L).buildHeader()); + + transactionProcessor = protocolSpec.getTransactionProcessor(); + blockHashLookup = + protocolSpec + .getBlockHashProcessor() + .createBlockHashLookup(blockchain, genesisBlock.getHeader()); } @Test diff --git a/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/BlockHashOperationBenchmark.java b/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/BlockHashOperationBenchmark.java index e431e1420cd..c44bc660c91 100644 --- a/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/BlockHashOperationBenchmark.java +++ b/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/BlockHashOperationBenchmark.java @@ -15,7 +15,7 @@ package org.hyperledger.besu.ethereum.vm.operations; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; +import org.hyperledger.besu.ethereum.vm.BlockchainBasedBlockHashLookup; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator; import org.hyperledger.besu.evm.operation.BlockHashOperation; @@ -68,7 +68,7 @@ public Bytes executeOperationWithEmptyHashCache() { operationBenchmarkHelper .createMessageFrameBuilder() .blockHashLookup( - new CachingBlockHashLookup( + new BlockchainBasedBlockHashLookup( (ProcessableBlockHeader) frame.getBlockValues(), operationBenchmarkHelper.getBlockchain())) .build(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java index a4772bc94d9..74d319f3428 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java @@ -15,7 +15,6 @@ package org.hyperledger.besu.ethereum.mainnet; import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator.calculateExcessBlobGasForParent; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.TransactionType; @@ -36,9 +35,8 @@ import org.hyperledger.besu.ethereum.trie.MerkleTrieException; import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldState; import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; -import org.hyperledger.besu.evm.operation.BlockHashOperation; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldState; import org.hyperledger.besu.evm.worldstate.WorldUpdater; @@ -108,8 +106,9 @@ public BlockProcessingResult processBlock( final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(blockHeader); - protocolSpec.getBlockHashProcessor().processBlockHashes(blockchain, worldState, blockHeader); - final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(blockHeader, blockchain); + protocolSpec.getBlockHashProcessor().processBlockHashes(worldState, blockHeader); + final BlockHashLookup blockHashLookup = + protocolSpec.getBlockHashProcessor().createBlockHashLookup(blockchain, blockHeader); final Address miningBeneficiary = miningBeneficiaryCalculator.calculateBeneficiary(blockHeader); @@ -252,7 +251,7 @@ protected Optional runBlockPreProcessing( final BlockHeader blockHeader, final List transactions, final Address miningBeneficiary, - final BlockHashOperation.BlockHashLookup blockHashLookup, + final BlockHashLookup blockHashLookup, final Wei blobGasPrice) { return Optional.empty(); } 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 2948f6d7225..51932fecb07 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 @@ -18,7 +18,6 @@ import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_PRIVATE_METADATA_UPDATER; import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION; import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION_HASH; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.collections.trie.BytesTrieSet; import org.hyperledger.besu.datatypes.AccessListEntry; @@ -37,6 +36,7 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.code.CodeInvalid; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/SystemCallProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/SystemCallProcessor.java index 06aa3efd8ef..ccf6703bef6 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/SystemCallProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/SystemCallProcessor.java @@ -20,9 +20,9 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.MessageFrame; -import org.hyperledger.besu.evm.operation.BlockHashOperation; import org.hyperledger.besu.evm.processor.AbstractMessageProcessor; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; @@ -64,7 +64,7 @@ public Bytes process( final WorldUpdater worldState, final ProcessableBlockHeader blockHeader, final OperationTracer operationTracer, - final BlockHashOperation.BlockHashLookup blockHashLookup) { + final BlockHashLookup blockHashLookup) { // if no code exists at CALL_ADDRESS, the call must fail silently final Account maybeContract = worldState.get(callAddress); @@ -109,7 +109,7 @@ private MessageFrame createCallFrame( final Address callAddress, final WorldUpdater worldUpdater, final ProcessableBlockHeader blockHeader, - final BlockHashOperation.BlockHashLookup blockHashLookup) { + final BlockHashLookup blockHashLookup) { final Optional maybeContract = Optional.ofNullable(worldUpdater.get(callAddress)); final AbstractMessageProcessor processor = diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/BlockHashProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/BlockHashProcessor.java index e64b51afa41..ae4bf5b6f34 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/BlockHashProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/BlockHashProcessor.java @@ -16,12 +16,12 @@ import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.MutableWorldState; -import org.hyperledger.besu.plugin.data.ProcessableBlockHeader; +import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; public interface BlockHashProcessor { - void processBlockHashes( - Blockchain blockchain, - MutableWorldState worldState, - ProcessableBlockHeader currentBlockHeader); + void processBlockHashes(MutableWorldState worldState, ProcessableBlockHeader currentBlockHeader); + + BlockHashLookup createBlockHashLookup(Blockchain blockchain, ProcessableBlockHeader blockHeader); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/CancunBlockHashProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/CancunBlockHashProcessor.java index 0e8b1ae8afa..3be30719e18 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/CancunBlockHashProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/CancunBlockHashProcessor.java @@ -14,20 +14,17 @@ */ package org.hyperledger.besu.ethereum.mainnet.blockhash; -import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.mainnet.ParentBeaconBlockRootHelper; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import org.hyperledger.besu.plugin.data.ProcessableBlockHeader; /** Processes the beacon block storage if it is present in the block header. */ -public class CancunBlockHashProcessor implements BlockHashProcessor { +public class CancunBlockHashProcessor extends FrontierBlockHashProcessor { @Override public void processBlockHashes( - final Blockchain blockchain, - final MutableWorldState mutableWorldState, - final ProcessableBlockHeader currentBlockHeader) { + final MutableWorldState mutableWorldState, final ProcessableBlockHeader currentBlockHeader) { currentBlockHeader .getParentBeaconBlockRoot() .ifPresent( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/Eip7709BlockHashProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/Eip7709BlockHashProcessor.java new file mode 100644 index 00000000000..0f3ca406e29 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/Eip7709BlockHashProcessor.java @@ -0,0 +1,29 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * 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.mainnet.blockhash; + +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; +import org.hyperledger.besu.ethereum.vm.ContractBasedBlockHashLookup; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; + +public class Eip7709BlockHashProcessor extends PragueBlockHashProcessor { + + @Override + public BlockHashLookup createBlockHashLookup( + final Blockchain blockchain, final ProcessableBlockHeader blockHeader) { + return new ContractBasedBlockHashLookup(blockHeader, historyStorageAddress, historyServeWindow); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/FrontierBlockHashProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/FrontierBlockHashProcessor.java index f1722936b58..eba80a0eb82 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/FrontierBlockHashProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/FrontierBlockHashProcessor.java @@ -16,14 +16,31 @@ import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.MutableWorldState; -import org.hyperledger.besu.plugin.data.ProcessableBlockHeader; +import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; +import org.hyperledger.besu.ethereum.vm.BlockchainBasedBlockHashLookup; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; +import org.hyperledger.besu.evm.operation.BlockHashOperation; public class FrontierBlockHashProcessor implements BlockHashProcessor { + @Override public void processBlockHashes( - final Blockchain blockchain, - final MutableWorldState mutableWorldState, - final ProcessableBlockHeader currentBlockHeader) { + final MutableWorldState mutableWorldState, final ProcessableBlockHeader currentBlockHeader) { // do nothing } + + /** + * Creates a new BlockHashLookup function that calculates and caches block hashes by number + * following the chain for a specific branch. This is used by {@link BlockHashOperation} and + * ensures that the correct block hash is returned even when the block being imported is on a + * fork. + * + *

A new BlockHashCache must be created for each block being processed but should be reused for + * all transactions within that block. + */ + @Override + public BlockHashLookup createBlockHashLookup( + final Blockchain blockchain, final ProcessableBlockHeader blockHeader) { + return new BlockchainBasedBlockHashLookup(blockHeader, blockchain); + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/PragueBlockHashProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/PragueBlockHashProcessor.java index 46a83c8b3b4..b059688c605 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/PragueBlockHashProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/blockhash/PragueBlockHashProcessor.java @@ -16,11 +16,10 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import org.hyperledger.besu.plugin.data.ProcessableBlockHeader; import com.google.common.annotations.VisibleForTesting; import org.apache.tuweni.units.bigints.UInt256; @@ -40,10 +39,10 @@ public class PragueBlockHashProcessor extends CancunBlockHashProcessor { // 0x0aae40965e6800cd9b1f4b05ff21581047e3f91e /** The HISTORY_SERVE_WINDOW */ - public static final long HISTORY_SERVE_WINDOW = 8192; + private static final long HISTORY_SERVE_WINDOW = 8192; - private final long historySaveWindow; - private final Address historyStorageAddress; + protected final long historyServeWindow; + protected final Address historyStorageAddress; /** Constructs a BlockHashProcessor. */ public PragueBlockHashProcessor() { @@ -55,21 +54,19 @@ public PragueBlockHashProcessor() { * primarily used for testing. * * @param historyStorageAddress the address of the contract storing the history - * @param historySaveWindow The number of blocks for which history should be saved. + * @param historyServeWindow The number of blocks for which history should be saved. */ @VisibleForTesting public PragueBlockHashProcessor( - final Address historyStorageAddress, final long historySaveWindow) { + final Address historyStorageAddress, final long historyServeWindow) { this.historyStorageAddress = historyStorageAddress; - this.historySaveWindow = historySaveWindow; + this.historyServeWindow = historyServeWindow; } @Override public void processBlockHashes( - final Blockchain blockchain, - final MutableWorldState mutableWorldState, - final ProcessableBlockHeader currentBlockHeader) { - super.processBlockHashes(blockchain, mutableWorldState, currentBlockHeader); + final MutableWorldState mutableWorldState, final ProcessableBlockHeader currentBlockHeader) { + super.processBlockHashes(mutableWorldState, currentBlockHeader); WorldUpdater worldUpdater = mutableWorldState.updater(); final MutableAccount historyStorageAccount = worldUpdater.getOrCreate(historyStorageAddress); @@ -98,7 +95,7 @@ private void storeParentHash(final MutableAccount account, final ProcessableBloc * @param hash The hash to be stored. */ private void storeHash(final MutableAccount account, final long number, final Hash hash) { - UInt256 slot = UInt256.valueOf(number % historySaveWindow); + UInt256 slot = UInt256.valueOf(number % historyServeWindow); UInt256 value = UInt256.fromBytes(hash); LOG.trace( "Writing to {} {}=%{}", account.getAddress(), slot.toDecimalString(), value.toHexString()); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/MainnetParallelBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/MainnetParallelBlockProcessor.java index d1f0d9c5127..688ff918cb4 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/MainnetParallelBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/MainnetParallelBlockProcessor.java @@ -28,7 +28,7 @@ import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldState; -import org.hyperledger.besu.evm.operation.BlockHashOperation; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.plugin.services.MetricsSystem; @@ -85,7 +85,7 @@ protected Optional runBlockPreProcessing( final BlockHeader blockHeader, final List transactions, final Address miningBeneficiary, - final BlockHashOperation.BlockHashLookup blockHashLookup, + final BlockHashLookup blockHashLookup, final Wei blobGasPrice) { if ((worldState instanceof DiffBasedWorldState)) { ParallelizedConcurrentTransactionProcessor parallelizedConcurrentTransactionProcessor = @@ -117,7 +117,7 @@ protected TransactionProcessingResult getTransactionProcessingResult( final Address miningBeneficiary, final Transaction transaction, final int location, - final BlockHashOperation.BlockHashLookup blockHashLookup) { + final BlockHashLookup blockHashLookup) { TransactionProcessingResult transactionProcessingResult = null; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessor.java index c6beaa2f40e..d707982640c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessor.java @@ -27,7 +27,7 @@ import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState; import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldState; import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator; -import org.hyperledger.besu.evm.operation.BlockHashOperation; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldView; import org.hyperledger.besu.plugin.services.metrics.Counter; @@ -102,7 +102,7 @@ public void runAsyncBlock( final BlockHeader blockHeader, final List transactions, final Address miningBeneficiary, - final BlockHashOperation.BlockHashLookup blockHashLookup, + final BlockHashLookup blockHashLookup, final Wei blobGasPrice, final PrivateMetadataUpdater privateMetadataUpdater) { for (int i = 0; i < transactions.size(); i++) { @@ -133,7 +133,7 @@ public void runTransaction( final int transactionLocation, final Transaction transaction, final Address miningBeneficiary, - final BlockHashOperation.BlockHashLookup blockHashLookup, + final BlockHashLookup blockHashLookup, final Wei blobGasPrice, final PrivateMetadataUpdater privateMetadataUpdater) { try (final DiffBasedWorldState roundWorldState = diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/ProcessRequestContext.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/ProcessRequestContext.java index 63f4a8d5144..5615d41d716 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/ProcessRequestContext.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/ProcessRequestContext.java @@ -18,7 +18,7 @@ import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; -import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.tracing.OperationTracer; import java.util.List; 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 307a1d1e6fa..cab4d148fa7 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 @@ -15,7 +15,6 @@ package org.hyperledger.besu.ethereum.privacy; import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; @@ -39,6 +38,7 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRehydration.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRehydration.java index 6d4811a4bca..dc2d991258a 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRehydration.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRehydration.java @@ -25,7 +25,6 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import java.util.LinkedHashMap; @@ -167,7 +166,7 @@ public void rehydrate( privateStateStorage, privateStateRootResolver, block, - new CachingBlockHashLookup(blockHeader, blockchain), + protocolSpec.getBlockHashProcessor().createBlockHashLookup(blockchain, blockHeader), pmtHashToPrivateTransactionMap, block.getBody().getOmmers()); 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 ad7de59aee7..73061a86c30 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 @@ -15,7 +15,6 @@ package org.hyperledger.besu.ethereum.privacy; import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION_HASH; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; @@ -28,6 +27,7 @@ import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.processor.AbstractMessageProcessor; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionSimulator.java index 061c56353fb..218c88f4cb2 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionSimulator.java @@ -28,7 +28,6 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.transaction.CallParameter; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.tracing.OperationTracer; @@ -140,7 +139,7 @@ private Optional process( transaction, protocolSpec.getMiningBeneficiaryCalculator().calculateBeneficiary(header), OperationTracer.NO_TRACING, - new CachingBlockHashLookup(header, blockchain), + protocolSpec.getBlockHashProcessor().createBlockHashLookup(blockchain, header), privacyGroupId); return Optional.of(result); 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 7c51ac01cb2..d76b1a4bf7b 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 @@ -14,8 +14,6 @@ */ package org.hyperledger.besu.ethereum.privacy.storage.migration; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; - import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.BlockProcessingOutputs; @@ -33,6 +31,7 @@ import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.ArrayList; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigration.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigration.java index 834633a997d..3a005db5bc3 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigration.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigration.java @@ -29,7 +29,6 @@ import org.hyperledger.besu.ethereum.privacy.storage.LegacyPrivateStateStorage; import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import java.util.List; @@ -113,7 +112,7 @@ public void migratePrivateStorage() { privateMigrationBlockProcessor.processBlock( blockchain, publicWorldState, - new CachingBlockHashLookup(blockHeader, blockchain), + protocolSpec.getBlockHashProcessor().createBlockHashLookup(blockchain, blockHeader), blockHeader, transactionsToProcess, ommers); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java index 1c6140f1d1f..ce82e03c111 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java @@ -36,7 +36,6 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.account.Account; @@ -338,7 +337,9 @@ public Optional processWithWorldUpdater( protocolSpec .getMiningBeneficiaryCalculator() .calculateBeneficiary(blockHeaderToProcess), - new CachingBlockHashLookup(blockHeaderToProcess, blockchain), + protocolSpec + .getBlockHashProcessor() + .createBlockHashLookup(blockchain, blockHeaderToProcess), false, transactionValidationParams, operationTracer, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/CachingBlockHashLookup.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/BlockchainBasedBlockHashLookup.java similarity index 75% rename from ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/CachingBlockHashLookup.java rename to ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/BlockchainBasedBlockHashLookup.java index c70af6b836b..9c09956aba9 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/CachingBlockHashLookup.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/BlockchainBasedBlockHashLookup.java @@ -19,8 +19,9 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; +import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.operation.BlockHashOperation; -import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import java.util.HashMap; import java.util.Map; @@ -33,21 +34,32 @@ *

A new BlockHashCache must be created for each block being processed but should be reused for * all transactions within that block. */ -public class CachingBlockHashLookup implements BlockHashLookup { +public class BlockchainBasedBlockHashLookup implements BlockHashLookup { + private static final int MAX_RELATIVE_BLOCK = 256; + private final long currentBlockNumber; private ProcessableBlockHeader searchStartHeader; private final Blockchain blockchain; private final Map hashByNumber = new HashMap<>(); - public CachingBlockHashLookup( + public BlockchainBasedBlockHashLookup( final ProcessableBlockHeader currentBlock, final Blockchain blockchain) { + this.currentBlockNumber = currentBlock.getNumber(); this.searchStartHeader = currentBlock; this.blockchain = blockchain; hashByNumber.put(currentBlock.getNumber() - 1, currentBlock.getParentHash()); } @Override - public Hash apply(final Long blockNumber) { + public Hash apply(final MessageFrame frame, final Long blockNumber) { + // If the current block is the genesis block or the sought block is + // not within the last 256 completed blocks, zero is returned. + if (currentBlockNumber == 0 + || blockNumber >= currentBlockNumber + || blockNumber < (currentBlockNumber - MAX_RELATIVE_BLOCK)) { + return ZERO; + } + final Hash cachedHash = hashByNumber.get(blockNumber); if (cachedHash != null) { return cachedHash; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/ContractBasedBlockHashLookup.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/ContractBasedBlockHashLookup.java new file mode 100644 index 00000000000..8aab22d9d64 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/ContractBasedBlockHashLookup.java @@ -0,0 +1,117 @@ +/* + * Copyright contributors to Besu. + * + * 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.vm; + +import static org.hyperledger.besu.datatypes.Hash.ZERO; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import java.util.HashMap; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.tuweni.units.bigints.UInt256; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Retrieves block hashes from system contract storage and caches hashes by number, used by + * BLOCKHASH operation. + */ +public class ContractBasedBlockHashLookup implements BlockHashLookup { + private static final Logger LOG = LoggerFactory.getLogger(ContractBasedBlockHashLookup.class); + private static final long BLOCKHASH_SERVE_WINDOW = 256L; + + private final ProcessableBlockHeader blockHeader; + private final Address contractAddress; + private final long historyServeWindow; + private final long blockHashServeWindow; + private final HashMap hashByNumber = new HashMap<>(); + + /** + * Constructs a ContractBasedBlockHashLookup. + * + * @param currentBlock current block header being processed. + * @param contractAddress the address of the contract storing the history. + * @param historyServeWindow the number of blocks for which history should be saved. + */ + public ContractBasedBlockHashLookup( + final ProcessableBlockHeader currentBlock, + final Address contractAddress, + final long historyServeWindow) { + this(currentBlock, contractAddress, historyServeWindow, BLOCKHASH_SERVE_WINDOW); + } + + /** + * Constructs a ContractBasedBlockHashLookup with a specified blockHashServeWindow. This + * constructor is only used for testing. + * + * @param currentBlock current block header being processed. + * @param contractAddress the address of the contract storing the history. + * @param historyServeWindow the number of blocks for which history should be saved. + * @param blockHashServeWindow the number of block for which contract can serve the BLOCKHASH + * opcode. + */ + @VisibleForTesting + ContractBasedBlockHashLookup( + final ProcessableBlockHeader currentBlock, + final Address contractAddress, + final long historyServeWindow, + final long blockHashServeWindow) { + this.blockHeader = currentBlock; + this.contractAddress = contractAddress; + this.historyServeWindow = historyServeWindow; + this.blockHashServeWindow = blockHashServeWindow; + } + + @Override + public Hash apply(final MessageFrame frame, final Long blockNumber) { + final long currentBlockNumber = blockHeader.getNumber(); + final long minBlockServe = Math.max(0, currentBlockNumber - blockHashServeWindow); + if (blockNumber >= currentBlockNumber || blockNumber < minBlockServe) { + LOG.debug("failed to read hash from system account for block {}", blockNumber); + return ZERO; + } + + final Hash cachedHash = hashByNumber.get(blockNumber); + if (cachedHash != null) { + return cachedHash; + } + + final WorldUpdater worldUpdater = frame.getWorldUpdater(); + Account account = worldUpdater.get(contractAddress); + if (account == null) { + LOG.error("cannot query system contract {}", contractAddress); + return ZERO; + } + + UInt256 slot = UInt256.valueOf(blockNumber % historyServeWindow); + final UInt256 value = account.getStorageValue(slot); + LOG.atTrace() + .log( + () -> + String.format( + "Read block %s for account %s returned value %s", + account.getAddress(), slot.toDecimalString(), value.toString())); + Hash blockHash = Hash.wrap(value); + hashByNumber.put(blockNumber, blockHash); + return blockHash; + } +} diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/MessageFrameTestFixture.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/MessageFrameTestFixture.java index 800af844fb0..c4f492e3c80 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/MessageFrameTestFixture.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/MessageFrameTestFixture.java @@ -15,13 +15,13 @@ package org.hyperledger.besu.ethereum.core; import static org.hyperledger.besu.evm.frame.MessageFrame.DEFAULT_MAX_STACK_SIZE; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.chain.Blockchain; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.worldstate.WorldUpdater; @@ -157,6 +157,8 @@ public MessageFrame build() { final Blockchain localBlockchain = this.blockchain.orElseGet(this::createDefaultBlockchain); final BlockHeader localBlockHeader = this.blockHeader.orElseGet(() -> localBlockchain.getBlockHeader(0).get()); + final ProtocolSpec protocolSpec = + executionContextTestFixture.getProtocolSchedule().getByBlockHeader(localBlockHeader); final MessageFrame frame = MessageFrame.builder() .parentMessageFrame(parentFrame) @@ -178,7 +180,10 @@ public MessageFrame build() { .miningBeneficiary(localBlockHeader.getCoinbase()) .blockHashLookup( blockHashLookup.orElseGet( - () -> new CachingBlockHashLookup(localBlockHeader, localBlockchain))) + () -> + protocolSpec + .getBlockHashProcessor() + .createBlockHashLookup(localBlockchain, localBlockHeader))) .maxStackSize(maxStackSize) .build(); stackItems.forEach(frame::pushStackItem); 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 ea09dacc127..abf53b2157d 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 @@ -15,7 +15,6 @@ package org.hyperledger.besu.ethereum.mainnet; import static org.assertj.core.api.Assertions.assertThat; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.when; @@ -31,6 +30,7 @@ import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.trie.MerkleTrieException; import org.hyperledger.besu.evm.account.MutableAccount; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator; 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 2749b37fcdc..d80e183da28 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 @@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture; import org.hyperledger.besu.ethereum.core.TransactionReceipt; +import org.hyperledger.besu.ethereum.mainnet.blockhash.BlockHashProcessor; import org.hyperledger.besu.ethereum.privacy.PrivateStateGenesisAllocator; import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor; @@ -221,6 +222,8 @@ private ProtocolSpec mockProtocolSpec() { when(protocolSpec.getMiningBeneficiaryCalculator()) .thenReturn(mock(MiningBeneficiaryCalculator.class)); when(protocolSpec.isSkipZeroBlockRewards()).thenReturn(true); + final BlockHashProcessor blockHashProcessor = mock(BlockHashProcessor.class); + when(protocolSpec.getBlockHashProcessor()).thenReturn(blockHashProcessor); return protocolSpec; } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/SystemCallProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/SystemCallProcessorTest.java index 14275f7d12e..fe44c3ce4c6 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/SystemCallProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/SystemCallProcessorTest.java @@ -25,8 +25,8 @@ import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.frame.MessageFrame; -import org.hyperledger.besu.evm.operation.BlockHashOperation; import org.hyperledger.besu.evm.processor.AbstractMessageProcessor; import org.hyperledger.besu.evm.processor.MessageCallProcessor; import org.hyperledger.besu.evm.tracing.OperationTracer; @@ -41,7 +41,7 @@ public class SystemCallProcessorTest { private static final Bytes EXPECTED_OUTPUT = Bytes.fromHexString("0x01"); private ProcessableBlockHeader mockBlockHeader; private MainnetTransactionProcessor mockTransactionProcessor; - private BlockHashOperation.BlockHashLookup mockBlockHashLookup; + private BlockHashLookup mockBlockHashLookup; private AbstractMessageProcessor mockMessageCallProcessor; @BeforeEach @@ -49,7 +49,7 @@ public void setUp() { mockBlockHeader = mock(ProcessableBlockHeader.class); mockTransactionProcessor = mock(MainnetTransactionProcessor.class); mockMessageCallProcessor = mock(MessageCallProcessor.class); - mockBlockHashLookup = mock(BlockHashOperation.BlockHashLookup.class); + mockBlockHashLookup = mock(BlockHashLookup.class); when(mockTransactionProcessor.getMessageProcessor(any())).thenReturn(mockMessageCallProcessor); } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/blockhash/BlockHashProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/blockhash/BlockHashProcessorTest.java index 8ef178559a9..b558b0f2759 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/blockhash/BlockHashProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/blockhash/BlockHashProcessorTest.java @@ -23,20 +23,16 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import java.util.Optional; - import org.apache.tuweni.units.bigints.UInt256; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class BlockHashProcessorTest { - private Blockchain blockchain; private WorldUpdater worldUpdater; private MutableWorldState mutableWorldState; private MutableAccount account; @@ -46,7 +42,6 @@ class BlockHashProcessorTest { @BeforeEach void setUp() { - blockchain = mock(Blockchain.class); mutableWorldState = mock(MutableWorldState.class); worldUpdater = mock(WorldUpdater.class); account = mock(MutableAccount.class); @@ -61,7 +56,7 @@ void shouldStoreParentBlockHash() { processor = new PragueBlockHashProcessor(); BlockHeader currentBlockHeader = mockBlockHeader(currentBlock); mockAncestorHeaders(currentBlockHeader, 3); - processor.processBlockHashes(blockchain, mutableWorldState, currentBlockHeader); + processor.processBlockHashes(mutableWorldState, currentBlockHeader); // only parent slot number must be set verify(account, times(1)).setStorageValue(any(), any()); verifyAccount(currentBlock - 1, historicalWindow); @@ -76,7 +71,7 @@ void shouldNotStoreBlockHashForGenesisBlock() { BlockHeader currentBlockHeader = mockBlockHeader(currentBlock); mockAncestorHeaders(currentBlockHeader, 0); - processor.processBlockHashes(blockchain, mutableWorldState, currentBlockHeader); + processor.processBlockHashes(mutableWorldState, currentBlockHeader); verifyNoInteractions(account); } @@ -89,7 +84,7 @@ void shouldStoreAncestorBlockHashesAtForkCorrectlyParentIsGenesis() { BlockHeader currentBlockHeader = mockBlockHeader(currentBlock); mockAncestorHeaders(currentBlockHeader, 10); - processor.processBlockHashes(blockchain, mutableWorldState, currentBlockHeader); + processor.processBlockHashes(mutableWorldState, currentBlockHeader); verify(account, times(1)).setStorageValue(any(), any()); verifyAccount(0, historicalWindow); } @@ -99,7 +94,7 @@ void shouldWriteGenesisHashAtSlot0() { processor = new PragueBlockHashProcessor(); BlockHeader header = mockBlockHeader(1); mockAncestorHeaders(header, 1); - processor.processBlockHashes(blockchain, mutableWorldState, header); + processor.processBlockHashes(mutableWorldState, header); verify(account) .setStorageValue(UInt256.valueOf(0), UInt256.fromHexString(Hash.ZERO.toHexString())); } @@ -126,7 +121,6 @@ private BlockHeader mockBlockHeader(final long currentNumber) { when(blockHeader.getHash()).thenReturn(hash); when(blockHeader.getTimestamp()).thenReturn(currentNumber); when(blockHeader.getParentHash()).thenReturn(parentHash); - when(blockchain.getBlockHeader(hash)).thenReturn(Optional.of(blockHeader)); return blockHeader; } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessorTest.java index bba91843568..c446b18f28b 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/parallelization/ParallelizedConcurrentTransactionProcessorTest.java @@ -42,8 +42,8 @@ import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.DiffBasedWorldStateConfig; import org.hyperledger.besu.ethereum.trie.diffbased.common.worldview.accumulator.DiffBasedWorldStateUpdateAccumulator; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.operation.BlockHashOperation; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; @@ -110,7 +110,7 @@ void testRunTransaction() { 0, transaction, miningBeneficiary, - (blockNumber) -> Hash.EMPTY, + (__, ___) -> Hash.EMPTY, blobGasPrice, privateMetadataUpdater); @@ -121,7 +121,7 @@ void testRunTransaction() { eq(transaction), eq(miningBeneficiary), any(OperationTracer.class), - any(BlockHashOperation.BlockHashLookup.class), + any(BlockHashLookup.class), eq(true), eq(TransactionValidationParams.processingBlock()), eq(privateMetadataUpdater), @@ -156,7 +156,7 @@ void testRunTransactionWithFailure() { 0, transaction, miningBeneficiary, - (blockNumber) -> Hash.EMPTY, + (__, ___) -> Hash.EMPTY, blobGasPrice, privateMetadataUpdater); @@ -185,7 +185,7 @@ void testRunTransactionWithConflict() { 0, transaction, miningBeneficiary, - (blockNumber) -> Hash.EMPTY, + (__, ___) -> Hash.EMPTY, blobGasPrice, privateMetadataUpdater); @@ -196,7 +196,7 @@ void testRunTransactionWithConflict() { eq(transaction), eq(miningBeneficiary), any(OperationTracer.class), - any(BlockHashOperation.BlockHashLookup.class), + any(BlockHashLookup.class), eq(true), eq(TransactionValidationParams.processingBlock()), eq(privateMetadataUpdater), diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/FlexiblePrivacyPrecompiledContractTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/FlexiblePrivacyPrecompiledContractTest.java index fc08f519d3d..c4653856ef1 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/FlexiblePrivacyPrecompiledContractTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/FlexiblePrivacyPrecompiledContractTest.java @@ -20,7 +20,6 @@ import static org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture.versionedPrivateTransactionBesu; import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_IS_PERSISTING_PRIVATE_STATE; import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_PRIVATE_METADATA_UPDATER; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; @@ -51,6 +50,7 @@ import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator; import org.hyperledger.besu.evm.log.Log; diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPluginPrecompiledContractTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPluginPrecompiledContractTest.java index 34a5c3af355..abe356c944e 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPluginPrecompiledContractTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPluginPrecompiledContractTest.java @@ -21,7 +21,6 @@ import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION; import static org.hyperledger.besu.ethereum.privacy.PrivateTransaction.readFrom; import static org.hyperledger.besu.ethereum.privacy.PrivateTransaction.serialize; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -43,6 +42,7 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator; import org.hyperledger.besu.evm.log.Log; diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java index 7d253057ac7..6b333ef3497 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java @@ -21,7 +21,6 @@ import static org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture.privateTransactionBesu; import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_IS_PERSISTING_PRIVATE_STATE; import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_PRIVATE_METADATA_UPDATER; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; @@ -54,6 +53,7 @@ import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator; import org.hyperledger.besu.evm.log.Log; diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/CachingBlockHashLookupTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/BlockchainBasedBlockHashLookupTest.java similarity index 61% rename from ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/CachingBlockHashLookupTest.java rename to ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/BlockchainBasedBlockHashLookupTest.java index 9ea4219a1ac..8504d613967 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/CachingBlockHashLookupTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/BlockchainBasedBlockHashLookupTest.java @@ -14,11 +14,11 @@ */ package org.hyperledger.besu.ethereum.vm; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -26,7 +26,8 @@ import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; -import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; +import org.hyperledger.besu.evm.frame.MessageFrame; import java.util.Optional; @@ -38,15 +39,25 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -class CachingBlockHashLookupTest { +class BlockchainBasedBlockHashLookupTest { - private static final int CURRENT_BLOCK_NUMBER = 256; + private static final int MAXIMUM_COMPLETE_BLOCKS_BEHIND = 256; + private static final int CURRENT_BLOCK_NUMBER = MAXIMUM_COMPLETE_BLOCKS_BEHIND * 2; private final Blockchain blockchain = mock(Blockchain.class); - private final BlockHeader[] headers = new BlockHeader[CURRENT_BLOCK_NUMBER]; + private BlockHeader[] headers; private BlockHashLookup lookup; + private final MessageFrame messageFrameMock = mock(MessageFrame.class); @BeforeEach void setUp() { + setUpBlockchain(CURRENT_BLOCK_NUMBER); + lookup = + new BlockchainBasedBlockHashLookup( + createHeader(CURRENT_BLOCK_NUMBER, headers[headers.length - 1]), blockchain); + } + + private void setUpBlockchain(final int blockNumberAtHead) { + headers = new BlockHeader[blockNumberAtHead]; BlockHeader parentHeader = null; for (int i = 0; i < headers.length; i++) { final BlockHeader header = createHeader(i, parentHeader); @@ -54,9 +65,6 @@ void setUp() { headers[i] = header; parentHeader = headers[i]; } - lookup = - new CachingBlockHashLookup( - createHeader(CURRENT_BLOCK_NUMBER, headers[headers.length - 1]), blockchain); } @AfterEach @@ -72,50 +80,65 @@ void shouldGetHashOfImmediateParent() { } @Test - void shouldGetHashOfGenesisBlock() { - assertHashForBlockNumber(0); - } - - @Test - void shouldGetHashForRecentBlockAfterOlderBlock() { - assertHashForBlockNumber(10); - assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 1); + void shouldGetHashOfMaxBlocksBehind() { + assertHashForBlockNumber(MAXIMUM_COMPLETE_BLOCKS_BEHIND); } @Test void shouldReturnEmptyHashWhenRequestedBlockNotOnchain() { - Assertions.assertThat(lookup.apply(CURRENT_BLOCK_NUMBER + 20L)).isEqualTo(Hash.ZERO); + assertThat(lookup.apply(messageFrameMock, CURRENT_BLOCK_NUMBER + 20L)).isEqualTo(Hash.ZERO); } @Test void shouldReturnEmptyHashWhenParentBlockNotOnchain() { final BlockHashLookup lookupWithUnavailableParent = - new CachingBlockHashLookup( + new BlockchainBasedBlockHashLookup( new BlockHeaderTestFixture().number(CURRENT_BLOCK_NUMBER + 20).buildHeader(), blockchain); - Assertions.assertThat(lookupWithUnavailableParent.apply((long) CURRENT_BLOCK_NUMBER)) + Assertions.assertThat( + lookupWithUnavailableParent.apply(messageFrameMock, (long) CURRENT_BLOCK_NUMBER)) .isEqualTo(Hash.ZERO); } - @Test - void shouldGetParentHashFromCurrentBlock() { - assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 1); - verifyNoInteractions(blockchain); - } - @Test void shouldCacheBlockHashesWhileIteratingBackToPreviousHeader() { assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 4); assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 1); - verify(blockchain).getBlockHeader(headers[CURRENT_BLOCK_NUMBER - 1].getHash()); - verify(blockchain).getBlockHeader(headers[CURRENT_BLOCK_NUMBER - 2].getHash()); - verify(blockchain).getBlockHeader(headers[CURRENT_BLOCK_NUMBER - 3].getHash()); + assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 7); + for (int i = 1; i < 7; i++) { + verify(blockchain).getBlockHeader(headers[CURRENT_BLOCK_NUMBER - i].getHash()); + } verifyNoMoreInteractions(blockchain); } + @Test + void shouldReturnZeroWhenCurrentBlockIsGenesis() { + lookup = new BlockchainBasedBlockHashLookup(createHeader(0, null), mock(Blockchain.class)); + assertHashForBlockNumber(0, Hash.ZERO); + } + + @Test + void shouldReturnZeroWhenRequestedBlockEqualToImportingBlock() { + assertHashForBlockNumber(CURRENT_BLOCK_NUMBER, Hash.ZERO); + } + + @Test + void shouldReturnZeroWhenRequestedBlockAheadOfCurrent() { + assertHashForBlockNumber(CURRENT_BLOCK_NUMBER + 1, Hash.ZERO); + } + + @Test + void shouldReturnZeroWhenRequestedBlockTooFarBehindCurrent() { + assertHashForBlockNumber(MAXIMUM_COMPLETE_BLOCKS_BEHIND - 1, Hash.ZERO); + assertHashForBlockNumber(10, Hash.ZERO); + } + private void assertHashForBlockNumber(final int blockNumber) { - Assertions.assertThat(lookup.apply((long) blockNumber)) - .isEqualTo(headers[blockNumber].getHash()); + assertHashForBlockNumber(blockNumber, headers[blockNumber].getHash()); + } + + private void assertHashForBlockNumber(final int blockNumber, final Hash hash) { + Assertions.assertThat(lookup.apply(messageFrameMock, (long) blockNumber)).isEqualTo(hash); } private BlockHeader createHeader(final int blockNumber, final BlockHeader parentHeader) { diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/ContractBasedBlockHashLookupTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/ContractBasedBlockHashLookupTest.java new file mode 100644 index 00000000000..0b8201bbe70 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/ContractBasedBlockHashLookupTest.java @@ -0,0 +1,192 @@ +/* + * Copyright contributors to Besu. + * + * 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.vm; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; +import org.hyperledger.besu.evm.fluent.SimpleAccount; +import org.hyperledger.besu.evm.fluent.SimpleWorld; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.tuweni.units.bigints.UInt256; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ContractBasedBlockHashLookupTest { + private static final long BLOCKHASH_SERVE_WINDOW = 160; + private static final Address STORAGE_ADDRESS = Address.fromHexString("0x0"); + private static final long HISTORY_SERVE_WINDOW = 200L; + private static final int CURRENT_BLOCK_NUMBER = + Math.toIntExact(HISTORY_SERVE_WINDOW + BLOCKHASH_SERVE_WINDOW / 2); + private List headers; + private BlockHashLookup lookup; + private MessageFrame frame; + + @BeforeEach + void setUp() { + headers = new ArrayList<>(); + frame = mock(MessageFrame.class); + final WorldUpdater worldUpdater = createWorldUpdater(0, CURRENT_BLOCK_NUMBER); + when(frame.getWorldUpdater()).thenReturn(worldUpdater); + lookup = + new ContractBasedBlockHashLookup( + createHeader(CURRENT_BLOCK_NUMBER, headers.getLast()), + STORAGE_ADDRESS, + HISTORY_SERVE_WINDOW, + BLOCKHASH_SERVE_WINDOW); + } + + private WorldUpdater createWorldUpdater(final int fromBlockNumber, final int toBlockNumber) { + WorldUpdater worldUpdaterMock = mock(WorldUpdater.class); + SimpleAccount contractAccount = spy(new SimpleAccount(STORAGE_ADDRESS, 0, Wei.ZERO)); + when(worldUpdaterMock.get(STORAGE_ADDRESS)).thenReturn(contractAccount); + BlockHeader parentHeader = null; + for (int i = fromBlockNumber; i < toBlockNumber; i++) { + final BlockHeader header = createHeader(i, parentHeader); + headers.add(header); + contractAccount.setStorageValue( + UInt256.valueOf(i % HISTORY_SERVE_WINDOW), UInt256.fromBytes(header.getHash())); + parentHeader = header; + } + return worldUpdaterMock; + } + + @Test + void shouldGetHashOfImmediateParent() { + assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 1); + } + + @Test + void shouldGetHashOfMaxBlocksBehind() { + assertHashForBlockNumber(Math.toIntExact(CURRENT_BLOCK_NUMBER - BLOCKHASH_SERVE_WINDOW)); + } + + @Test + void shouldReturnEmptyHashWhenRequestedBlockHigherThanHead() { + assertThat(lookup.apply(frame, CURRENT_BLOCK_NUMBER + 20L)).isEqualTo(Hash.ZERO); + } + + @Test + void shouldReturnEmptyHashWhenSystemContractNotExists() { + final WorldUpdater worldUpdater = new SimpleWorld(); + when(frame.getWorldUpdater()).thenReturn(worldUpdater); + assertThat(lookup.apply(frame, CURRENT_BLOCK_NUMBER - 1L)).isEqualTo(Hash.ZERO); + } + + @Test + void shouldReturnEmptyHashWhenParentBlockNotInContract() { + final WorldUpdater worldUpdater = + createWorldUpdater(CURRENT_BLOCK_NUMBER - 10, CURRENT_BLOCK_NUMBER); + when(frame.getWorldUpdater()).thenReturn(worldUpdater); + lookup = + new ContractBasedBlockHashLookup( + new BlockHeaderTestFixture().number(CURRENT_BLOCK_NUMBER).buildHeader(), + STORAGE_ADDRESS, + HISTORY_SERVE_WINDOW, + BLOCKHASH_SERVE_WINDOW); + assertHashForBlockNumber(CURRENT_BLOCK_NUMBER - 20, Hash.ZERO); + } + + @Test + void shouldCacheBlockHashes() { + final WorldUpdater worldUpdater = createWorldUpdater(0, CURRENT_BLOCK_NUMBER); + when(frame.getWorldUpdater()).thenReturn(worldUpdater); + final Account account = worldUpdater.get(STORAGE_ADDRESS); + clearInvocations(account); + + int blockNumber1 = CURRENT_BLOCK_NUMBER - 1; + int blockNumber2 = CURRENT_BLOCK_NUMBER - 4; + int blockNumber3 = CURRENT_BLOCK_NUMBER - 5; + assertHashForBlockNumber(blockNumber1); + assertHashForBlockNumber(blockNumber1); + assertHashForBlockNumber(blockNumber2); + assertHashForBlockNumber(blockNumber3); + assertHashForBlockNumber(blockNumber3); + assertHashForBlockNumber(blockNumber3); + + verify(account, times(1)) + .getStorageValue(eq(UInt256.valueOf(blockNumber1 % HISTORY_SERVE_WINDOW))); + verify(account, times(1)) + .getStorageValue(eq(UInt256.valueOf(blockNumber2 % HISTORY_SERVE_WINDOW))); + verify(account, times(1)) + .getStorageValue(eq(UInt256.valueOf(blockNumber3 % HISTORY_SERVE_WINDOW))); + verifyNoMoreInteractions(account); + } + + @Test + void shouldGetHashWhenParentIsGenesis() { + final WorldUpdater worldUpdater = createWorldUpdater(0, 1); + when(frame.getWorldUpdater()).thenReturn(worldUpdater); + lookup = + new ContractBasedBlockHashLookup( + createHeader(1, headers.getFirst()), + STORAGE_ADDRESS, + HISTORY_SERVE_WINDOW, + BLOCKHASH_SERVE_WINDOW); + assertHashForBlockNumber(0); + } + + @Test + void shouldReturnZeroWhenRequestedBlockEqualToImportingBlock() { + assertHashForBlockNumber(CURRENT_BLOCK_NUMBER, Hash.ZERO); + } + + @Test + void shouldReturnZeroWhenRequestedBlockAheadOfCurrent() { + assertHashForBlockNumber(CURRENT_BLOCK_NUMBER + 1, Hash.ZERO); + } + + @Test + void shouldReturnZeroWhenRequestedBlockTooFarBehindCurrent() { + assertHashForBlockNumber( + Math.toIntExact(CURRENT_BLOCK_NUMBER - BLOCKHASH_SERVE_WINDOW - 1), Hash.ZERO); + assertHashForBlockNumber(10, Hash.ZERO); + } + + private void assertHashForBlockNumber(final int blockNumber) { + assertHashForBlockNumber(blockNumber, headers.get(blockNumber).getHash()); + } + + private void assertHashForBlockNumber(final int blockNumber, final Hash hash) { + Assertions.assertThat(lookup.apply(frame, (long) blockNumber)).isEqualTo(hash); + } + + private BlockHeader createHeader(final long blockNumber, final BlockHeader parentHeader) { + return new BlockHeaderTestFixture() + .number(blockNumber) + .parentHash(parentHeader != null ? parentHeader.getHash() : Hash.EMPTY) + .buildHeader(); + } +} 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 2a1bb23a097..2d97f4308bc 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 @@ -21,6 +21,7 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture; import org.hyperledger.besu.ethereum.core.MessageFrameTestFixture; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.debug.TraceOptions; @@ -240,6 +241,7 @@ private MessageFrameTestFixture validMessageFrameBuilder() { return new MessageFrameTestFixture() .initialGas(INITIAL_GAS) .worldUpdater(worldUpdater) + .executionContextTestFixture(ExecutionContextTestFixture.create()) .gasPrice(Wei.of(25)) .blockHeader(blockHeader) .blockchain(blockchain); diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java index 2574d4b8e39..924b89d9a00 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java @@ -30,7 +30,6 @@ import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.EvmSpecVersion; @@ -511,7 +510,10 @@ public void run() { .blockValues(blockHeader) .completer(c -> {}) .miningBeneficiary(blockHeader.getCoinbase()) - .blockHashLookup(new CachingBlockHashLookup(blockHeader, component.getBlockchain())) + .blockHashLookup( + protocolSpec + .getBlockHashProcessor() + .createBlockHashLookup(component.getBlockchain(), blockHeader)) .accessListWarmAddresses(addressList) .build(); Deque messageFrameStack = initialMessageFrame.getMessageFrameStack(); 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 f6e05b5a498..b15aec6b9d0 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 @@ -270,7 +270,10 @@ private void traceTestSpecs(final String test, final List Hash.hash(Bytes.wrap(Long.toString(blockNumber).getBytes(UTF_8))), + (__, blockNumber) -> + blockNumber >= blockHeader.getNumber() + ? Hash.ZERO + : Hash.hash(Bytes.wrap(Long.toString(blockNumber).getBytes(UTF_8))), false, TransactionValidationParams.processingBlock(), tracer, 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 ba55fdf29ea..f6408473fc4 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 @@ -52,8 +52,9 @@ import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.trie.diffbased.common.DiffBasedAccount; -import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; +import org.hyperledger.besu.ethereum.vm.BlockchainBasedBlockHashLookup; import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.log.Log; import org.hyperledger.besu.evm.tracing.OperationTracer; @@ -349,9 +350,7 @@ static T8nResult runTest( long blobGasLimit = protocolSpec.getGasLimitCalculator().currentBlobGasLimit(); if (!referenceTestEnv.isStateTest()) { - protocolSpec - .getBlockHashProcessor() - .processBlockHashes(blockchain, worldState, referenceTestEnv); + protocolSpec.getBlockHashProcessor().processBlockHashes(worldState, referenceTestEnv); } final WorldUpdater rootWorldStateUpdater = worldState.updater(); @@ -385,13 +384,28 @@ static T8nResult runTest( tracer = tracerManager.getManagedTracer(transactionIndex, transaction.getHash()); tracer.tracePrepareTransaction(worldStateUpdater, transaction); tracer.traceStartTransaction(worldStateUpdater, transaction); + BlockHashLookup blockHashLookup = + protocolSpec.getBlockHashProcessor().createBlockHashLookup(blockchain, blockHeader); + if (blockHashLookup instanceof BlockchainBasedBlockHashLookup) { + // basically t8n test cases for blockhash are broken and one cannot create a blockchain + // from them so need to + // add in a manual BlockHashLookup + blockHashLookup = + (__, blockNumber) -> { + if (referenceTestEnv.getNumber() - blockNumber > 256L + || blockNumber >= referenceTestEnv.getNumber()) { + return Hash.ZERO; + } + return referenceTestEnv.getBlockhashByNumber(blockNumber).orElse(Hash.ZERO); + }; + } result = processor.processTransaction( worldStateUpdater, blockHeader, transaction, blockHeader.getCoinbase(), - number -> referenceTestEnv.getBlockhashByNumber(number).orElse(Hash.ZERO), + blockHashLookup, false, TransactionValidationParams.processingBlock(), tracer, @@ -524,7 +538,7 @@ static T8nResult runTest( worldState, protocolSpec, receipts, - new CachingBlockHashLookup(blockHeader, blockchain), + protocolSpec.getBlockHashProcessor().createBlockHashLookup(blockchain, blockHeader), OperationTracer.NO_TRACING); Optional> maybeRequests = Optional.of(rpc.process(context)); Hash requestsHash = BodyValidation.requestsHash(maybeRequests.orElse(List.of())); diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/benchmarks/BenchmarkExecutor.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/benchmarks/BenchmarkExecutor.java index 35d5de5d666..c3eb1ea307a 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/benchmarks/BenchmarkExecutor.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/benchmarks/BenchmarkExecutor.java @@ -63,7 +63,7 @@ public abstract class BenchmarkExecutor { .code(CodeV0.EMPTY_CODE) .completer(__ -> {}) .address(Address.ZERO) - .blockHashLookup(n -> null) + .blockHashLookup((__, ___) -> null) .blockValues(new SimpleBlockValues()) .gasPrice(Wei.ZERO) .miningBeneficiary(Address.ZERO) 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 354642f1ad7..8e1ac386a79 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 @@ -155,7 +155,7 @@ public static void executeTest(final GeneralStateTestCaseEipSpec spec) { blockHeader, transaction, blockHeader.getCoinbase(), - new CachingBlockHashLookup(blockHeader, blockchain), + protocolSpec.getBlockHashProcessor().createBlockHashLookup(blockchain, blockHeader), false, TransactionValidationParams.processingBlock(), blobGasPrice); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/blockhash/BlockHashLookup.java b/evm/src/main/java/org/hyperledger/besu/evm/blockhash/BlockHashLookup.java new file mode 100644 index 00000000000..47f9414a4a9 --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/blockhash/BlockHashLookup.java @@ -0,0 +1,28 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * 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.blockhash; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.evm.frame.MessageFrame; + +import java.util.function.BiFunction; + +/** + * Function that gets the block hash, passed in as part of TxValues. + * + *

Arg is the current executing message frame. The Result is the Hash, which may be zero based on + * lookup rules. + */ +public interface BlockHashLookup extends BiFunction {} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java index 36218c0e3c4..06b033d2735 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java @@ -15,7 +15,6 @@ package org.hyperledger.besu.evm.fluent; import static com.google.common.base.Preconditions.checkNotNull; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.collections.trie.BytesTrieSet; import org.hyperledger.besu.datatypes.AccessWitness; @@ -27,6 +26,7 @@ import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.EvmSpecVersion; import org.hyperledger.besu.evm.MainnetEVMs; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.contractvalidation.ContractValidationRule; import org.hyperledger.besu.evm.contractvalidation.MaxCodeSizeRule; @@ -75,7 +75,7 @@ public class EVMExecutor { private Wei ethValue = Wei.ZERO; private Code code = CodeV0.EMPTY_CODE; private BlockValues blockValues = new SimpleBlockValues(); - private BlockHashLookup blockHashLookup = n -> null; + private BlockHashLookup blockHashLookup = (__, ___) -> null; private Optional> versionedHashes = Optional.empty(); private OperationTracer tracer = OperationTracer.NO_TRACING; private boolean requireDeposit = true; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java index c16e9aa932a..4ea478afde8 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java @@ -26,6 +26,7 @@ import org.hyperledger.besu.datatypes.VersionedHash; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.gascalculator.stateless.NoopAccessWitness; import org.hyperledger.besu.evm.internal.MemoryEntry; import org.hyperledger.besu.evm.internal.OperandStack; @@ -33,7 +34,6 @@ import org.hyperledger.besu.evm.internal.StorageEntry; import org.hyperledger.besu.evm.internal.UnderflowException; import org.hyperledger.besu.evm.log.Log; -import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.evm.operation.Operation; import org.hyperledger.besu.evm.worldstate.WorldUpdater; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java index 81ebbdb767a..58110080459 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/TxValues.java @@ -14,14 +14,13 @@ */ package org.hyperledger.besu.evm.frame; -import static org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; - import org.hyperledger.besu.collections.undo.UndoScalar; import org.hyperledger.besu.collections.undo.UndoSet; import org.hyperledger.besu.collections.undo.UndoTable; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.VersionedHash; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import java.util.Deque; import java.util.List; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/BlockHashOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/BlockHashOperation.java index 019e1a7237e..c88dc137093 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/BlockHashOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/BlockHashOperation.java @@ -16,30 +16,15 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.evm.EVM; -import org.hyperledger.besu.evm.frame.BlockValues; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import java.util.function.Function; - import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.bytes.Bytes32; -import org.apache.tuweni.units.bigints.UInt256; /** The Block hash operation. */ -public class BlockHashOperation extends AbstractFixedCostOperation { - - /** - * Function that gets the block hash, passed in as part of TxValues. - * - *

Arg is the current block number. The Result is the Hash, which may be zero based on lookup - * rules. - */ - public interface BlockHashLookup extends Function {} - - /** Frontier maximum relative block delta */ - public static final int MAX_RELATIVE_BLOCK = 256; - +public class BlockHashOperation extends AbstractOperation { private static final int MAX_BLOCK_ARG_SIZE = 8; /** @@ -48,36 +33,36 @@ public interface BlockHashLookup extends Function {} * @param gasCalculator the gas calculator */ public BlockHashOperation(final GasCalculator gasCalculator) { - super(0x40, "BLOCKHASH", 1, 1, gasCalculator, gasCalculator.getBlockHashOperationGasCost()); + super(0x40, "BLOCKHASH", 1, 1, gasCalculator); } @Override - public Operation.OperationResult executeFixedCostOperation( - final MessageFrame frame, final EVM evm) { - final Bytes blockArg = frame.popStackItem().trimLeadingZeros(); + public OperationResult execute(final MessageFrame frame, final EVM evm) { + final long cost = gasCalculator().getBlockHashOperationGasCost(); + if (frame.getRemainingGas() < cost) { + return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); + } - // Short-circuit if value is unreasonably large + // Make sure we can convert to long + final Bytes blockArg = frame.popStackItem().trimLeadingZeros(); if (blockArg.size() > MAX_BLOCK_ARG_SIZE) { - frame.pushStackItem(UInt256.ZERO); - return successResponse; + frame.pushStackItem(Hash.ZERO); + return new OperationResult(cost, null); } - final long soughtBlock = blockArg.toLong(); - final BlockValues blockValues = frame.getBlockValues(); - final long currentBlockNumber = blockValues.getNumber(); + final BlockHashLookup blockHashLookup = frame.getBlockHashLookup(); + final Hash blockHash = blockHashLookup.apply(frame, blockArg.toLong()); + frame.pushStackItem(blockHash); - // If the current block is the genesis block or the sought block is - // not within the last 256 completed blocks, zero is returned. - if (currentBlockNumber == 0 - || soughtBlock >= currentBlockNumber - || soughtBlock < (currentBlockNumber - MAX_RELATIVE_BLOCK)) { - frame.pushStackItem(Bytes32.ZERO); - } else { - final BlockHashLookup blockHashLookup = frame.getBlockHashLookup(); - final Hash blockHash = blockHashLookup.apply(soughtBlock); - frame.pushStackItem(blockHash); - } + return new OperationResult(cost, null); + } - return successResponse; + /** + * Cost of the opcode execution. + * + * @return the cost + */ + protected long cost() { + return gasCalculator().getBlockHashOperationGasCost(); } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV0Test.java b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV0Test.java index 54d457e6284..585afecfff3 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV0Test.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/code/CodeV0Test.java @@ -89,7 +89,7 @@ private MessageFrame createJumpFrame(final CodeV0 getsCached) { .blockValues(mock(BlockValues.class)) .completer(f -> {}) .miningBeneficiary(Address.ZERO) - .blockHashLookup(l -> Hash.EMPTY) + .blockHashLookup((__, ___) -> Hash.EMPTY) .build(); frame.setPC(CURRENT_PC); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java index 6bd96b3a371..aab368660ef 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/fluent/EVMExecutorTest.java @@ -182,7 +182,7 @@ void giantExecuteStack() { .number(1) .timestamp(100L) .gasLimit(15_000_000L) - .blockHashLookup(number -> Hash.ZERO) + .blockHashLookup((__, ___) -> Hash.ZERO) .versionedHashes(Optional.empty()) .precompileContractRegistry(new PrecompileContractRegistry()) .requireDeposit(false) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/frame/MessageFrameTest.java b/evm/src/test/java/org/hyperledger/besu/evm/frame/MessageFrameTest.java index b41883f2a9c..949c367e903 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/frame/MessageFrameTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/frame/MessageFrameTest.java @@ -48,7 +48,7 @@ void setUp() { .blobGasPrice(Wei.ONE) .blockValues(new ToyBlockValues()) .miningBeneficiary(Address.ZERO) - .blockHashLookup((l) -> Hash.ZERO) + .blockHashLookup((__, ___) -> Hash.ZERO) .type(MessageFrame.Type.MESSAGE_CALL) .initialGas(1) .address(Address.ZERO) 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 4c5da84f318..bd6687b0f16 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 @@ -37,7 +37,6 @@ import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.processor.ContractCreationProcessor; import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; @@ -167,7 +166,7 @@ private void executeOperation(final Bytes contract, final EVM evm) { .code(evm.getCodeUncached(SIMPLE_CREATE)) .completer(__ -> {}) .address(Address.fromHexString(SENDER)) - .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) + .blockHashLookup((__, ___) -> Hash.ZERO) .blockValues(mock(BlockValues.class)) .gasPrice(Wei.ZERO) .miningBeneficiary(Address.ZERO) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/BlockHashOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/BlockHashOperationTest.java index 3ce7e3c207a..9fd5c3243f8 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/BlockHashOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/BlockHashOperationTest.java @@ -17,9 +17,10 @@ import static org.assertj.core.api.Assertions.assertThat; import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator; -import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.evm.testutils.FakeBlockValues; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; @@ -29,75 +30,90 @@ import org.junit.jupiter.api.Test; class BlockHashOperationTest { + private static final long ENOUGH_GAS = 30_000_000L; - private static final int MAXIMUM_COMPLETE_BLOCKS_BEHIND = 256; private final BlockHashOperation blockHashOperation = new BlockHashOperation(new FrontierGasCalculator()); @Test void shouldReturnZeroWhenArgIsBiggerThanALong() { assertBlockHash( - Bytes32.fromHexString("F".repeat(64)), Bytes32.ZERO, 100, n -> Hash.EMPTY_LIST_HASH); + Bytes32.fromHexString("F".repeat(64)), + Bytes32.ZERO, + 100, + (__, ___) -> Hash.EMPTY_LIST_HASH, + ENOUGH_GAS); } @Test - void shouldReturnZeroWhenCurrentBlockIsGenesis() { - assertBlockHash(Bytes32.ZERO, Bytes32.ZERO, 0, block -> Hash.EMPTY_LIST_HASH); - } - - @Test - void shouldReturnZeroWhenRequestedBlockAheadOfCurrent() { - assertBlockHash(250, Bytes32.ZERO, 100, block -> Hash.EMPTY_LIST_HASH); - } - - @Test - void shouldReturnZeroWhenRequestedBlockTooFarBehindCurrent() { - final int requestedBlock = 10; - // Our block is the one after the chain head (it's a new block), hence the + 1. - final int importingBlockNumber = MAXIMUM_COMPLETE_BLOCKS_BEHIND + requestedBlock + 1; + void shouldReturnBlockHashUsingLookupFromFrameWhenItIsWithinTheAllowedRange() { + final Hash blockHash = Hash.hash(Bytes.fromHexString("0x1293487297")); assertBlockHash( - requestedBlock, Bytes32.ZERO, importingBlockNumber, block -> Hash.EMPTY_LIST_HASH); - } - - @Test - void shouldReturnZeroWhenRequestedBlockGreaterThanImportingBlock() { - assertBlockHash(101, Bytes32.ZERO, 100, block -> Hash.EMPTY_LIST_HASH); - } - - @Test - void shouldReturnZeroWhenRequestedBlockEqualToImportingBlock() { - assertBlockHash(100, Bytes32.ZERO, 100, block -> Hash.EMPTY_LIST_HASH); + 100, + blockHash, + 200, + (__, block) -> block == 100 ? blockHash : Hash.EMPTY_LIST_HASH, + ENOUGH_GAS); } @Test - void shouldReturnBlockHashUsingLookupFromFrameWhenItIsWithinTheAllowedRange() { - final Hash blockHash = Hash.hash(Bytes.fromHexString("0x1293487297")); - assertBlockHash(100, blockHash, 200, block -> block == 100 ? blockHash : Hash.EMPTY_LIST_HASH); + void shouldFailWithInsufficientGas() { + assertFailure( + Bytes32.fromHexString("0x64"), + ExceptionalHaltReason.INSUFFICIENT_GAS, + 200, + (__, ___) -> Hash.hash(Bytes.fromHexString("0x1293487297")), + 1); } private void assertBlockHash( final long requestedBlock, final Bytes32 expectedOutput, final long currentBlockNumber, - final BlockHashLookup blockHashLookup) { + final BlockHashLookup blockHashLookup, + final long initialGas) { assertBlockHash( - UInt256.valueOf(requestedBlock), expectedOutput, currentBlockNumber, blockHashLookup); + UInt256.valueOf(requestedBlock), + expectedOutput, + currentBlockNumber, + blockHashLookup, + initialGas); } private void assertBlockHash( final Bytes32 input, final Bytes32 expectedOutput, final long currentBlockNumber, - final BlockHashLookup blockHashLookup) { + final BlockHashLookup blockHashLookup, + final long initialGas) { final MessageFrame frame = new TestMessageFrameBuilder() .blockHashLookup(blockHashLookup) .blockValues(new FakeBlockValues(currentBlockNumber)) .pushStackItem(UInt256.fromBytes(input)) + .initialGas(initialGas) .build(); blockHashOperation.execute(frame, null); final Bytes result = frame.popStackItem(); assertThat(result).isEqualTo(expectedOutput); assertThat(frame.stackSize()).isZero(); } + + private void assertFailure( + final Bytes32 input, + final ExceptionalHaltReason haltReason, + final long currentBlockNumber, + final BlockHashLookup blockHashLookup, + final long initialGas) { + final MessageFrame frame = + new TestMessageFrameBuilder() + .blockHashLookup(blockHashLookup) + .blockValues(new FakeBlockValues(currentBlockNumber)) + .pushStackItem(UInt256.fromBytes(input)) + .initialGas(initialGas) + .build(); + Operation.OperationResult operationResult = blockHashOperation.execute(frame, null); + assertThat(operationResult.getHaltReason()).isEqualTo(haltReason); + assertThat(frame.stackSize()).isOne(); + } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/Create2OperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/Create2OperationTest.java index a70b09a60b9..a54b438d706 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/Create2OperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/Create2OperationTest.java @@ -33,7 +33,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.log.Log; import org.hyperledger.besu.evm.operation.Operation.OperationResult; import org.hyperledger.besu.evm.processor.ContractCreationProcessor; @@ -151,7 +150,7 @@ public void setUp(final String sender, final String salt, final String code) { .code(evm.getCodeUncached(codeBytes)) .completer(__ -> {}) .address(Address.fromHexString(sender)) - .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) + .blockHashLookup((__, ___) -> Hash.ZERO) .blockValues(mock(BlockValues.class)) .gasPrice(Wei.ZERO) .miningBeneficiary(Address.ZERO) @@ -263,7 +262,7 @@ private MessageFrame testMemoryFrame(final UInt256 memoryOffset, final UInt256 m .code(evm.getCodeUncached(SIMPLE_CREATE)) .completer(__ -> {}) .address(Address.fromHexString(SENDER)) - .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) + .blockHashLookup((__, ___) -> Hash.ZERO) .blockValues(mock(BlockValues.class)) .gasPrice(Wei.ZERO) .miningBeneficiary(Address.ZERO) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/CreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/CreateOperationTest.java index 697d87ef1ee..3e8c5ae6ee9 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/CreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/CreateOperationTest.java @@ -32,7 +32,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.log.Log; import org.hyperledger.besu.evm.processor.ContractCreationProcessor; import org.hyperledger.besu.evm.testutils.TestMessageFrameBuilder; @@ -260,7 +259,7 @@ private MessageFrame testMemoryFrame( .code(evm.getCodeUncached(SIMPLE_CREATE)) .completer(__ -> {}) .address(Address.fromHexString(SENDER)) - .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) + .blockHashLookup((__, ___) -> Hash.ZERO) .blockValues(mock(BlockValues.class)) .gasPrice(Wei.ZERO) .miningBeneficiary(Address.ZERO) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/EofCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/EofCreateOperationTest.java index 5e9be626654..0cb1f3a0eb8 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/EofCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/EofCreateOperationTest.java @@ -33,7 +33,6 @@ import org.hyperledger.besu.evm.frame.BlockValues; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.log.Log; import org.hyperledger.besu.evm.precompile.MainnetPrecompiledContracts; import org.hyperledger.besu.evm.processor.ContractCreationProcessor; @@ -147,7 +146,7 @@ private MessageFrame testMemoryFrame(final Code code, final Bytes initData) { .code(code) .completer(__ -> {}) .address(Address.fromHexString(SENDER)) - .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) + .blockHashLookup((__, ___) -> Hash.ZERO) .blockValues(mock(BlockValues.class)) .gasPrice(Wei.ZERO) .miningBeneficiary(Address.ZERO) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/PushOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/PushOperationTest.java index aca7178189e..bd99e247a75 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/PushOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/PushOperationTest.java @@ -56,7 +56,7 @@ private MessageFrame createMessageFrame(final int pc) { .blobGasPrice(Wei.ONE) .blockValues(new ToyBlockValues()) .miningBeneficiary(Address.ZERO) - .blockHashLookup((l) -> Hash.ZERO) + .blockHashLookup((__, ___) -> Hash.ZERO) .type(MessageFrame.Type.MESSAGE_CALL) .initialGas(1) .address(Address.ZERO) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/SelfDestructOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/SelfDestructOperationTest.java index 8fbd4d142d3..ca6c76876d2 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/SelfDestructOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/SelfDestructOperationTest.java @@ -30,7 +30,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.internal.EvmConfiguration; -import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.apache.tuweni.bytes.Bytes; @@ -82,7 +81,7 @@ void checkContractDeletionCommon( .code(evm.getCodeUncached(SELFDESTRUCT_CODE)) .completer(__ -> {}) .address(originatorAddress) - .blockHashLookup(n -> Hash.hash(Words.longBytes(n))) + .blockHashLookup((__, ___) -> Hash.ZERO) .blockValues(mock(BlockValues.class)) .gasPrice(Wei.ZERO) .miningBeneficiary(Address.ZERO) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/precompile/Benchmarks.java b/evm/src/test/java/org/hyperledger/besu/evm/precompile/Benchmarks.java index 458f0c95587..8a49ece49f9 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/precompile/Benchmarks.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/precompile/Benchmarks.java @@ -65,7 +65,7 @@ public class Benchmarks { .code(CodeV0.EMPTY_CODE) .completer(__ -> {}) .address(Address.ZERO) - .blockHashLookup(n -> null) + .blockHashLookup((__, ___) -> null) .blockValues(new SimpleBlockValues()) .gasPrice(Wei.ZERO) .miningBeneficiary(Address.ZERO) diff --git a/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java b/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java index 801fcae8e2d..60eb9153752 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/testutils/TestMessageFrameBuilder.java @@ -20,11 +20,11 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.blockhash.BlockHashLookup; import org.hyperledger.besu.evm.code.CodeV0; import org.hyperledger.besu.evm.frame.BlockValues; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.internal.Words; -import org.hyperledger.besu.evm.operation.BlockHashOperation.BlockHashLookup; import org.hyperledger.besu.evm.toy.ToyWorld; import org.hyperledger.besu.evm.worldstate.WorldUpdater; @@ -167,7 +167,8 @@ public MessageFrame build() { .blockValues(blockValues.orElseGet(() -> new FakeBlockValues(1337))) .completer(c -> {}) .miningBeneficiary(Address.ZERO) - .blockHashLookup(blockHashLookup.orElse(number -> Hash.hash(Words.longBytes(number)))) + .blockHashLookup( + blockHashLookup.orElse((__, number) -> Hash.hash(Words.longBytes(number)))) .maxStackSize(maxStackSize) .isStatic(isStatic) .build(); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/toy/EvmToyCommand.java b/evm/src/test/java/org/hyperledger/besu/evm/toy/EvmToyCommand.java index 9a5c3cd1eaf..151effefe1b 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/toy/EvmToyCommand.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/toy/EvmToyCommand.java @@ -189,7 +189,7 @@ public void run() { .blockValues(new ToyBlockValues()) .completer(c -> {}) .miningBeneficiary(Address.ZERO) - .blockHashLookup(n -> null) + .blockHashLookup((__, ___) -> null) .build(); final MessageCallProcessor mcp = new MessageCallProcessor(evm, precompileContractRegistry);