From 611799521eccf54210ab96b44388dbf1119cd456 Mon Sep 17 00:00:00 2001 From: Karim Taam Date: Mon, 2 Dec 2024 23:39:09 +0700 Subject: [PATCH] Fix verkle pipeline (#7962) Signed-off-by: Karim Taam --- .../besu/ethereum/core/BlockHeader.java | 33 ++++--- .../mainnet/MainnetProtocolSpecs.java | 1 + .../VerkleDevnetBlockHeaderFunctions.java | 72 +++++++++++++++ .../evmtool/BlockchainTestSubCommand.java | 15 ++-- ethereum/referencetests/build.gradle | 4 +- .../BlockchainReferenceTestCase.java | 9 +- .../BlockchainReferenceTestCaseSpec.java | 88 ++----------------- .../referencetests/CandidateBlock.java | 72 +++++++++++++++ .../VerkleReferenceTestCaseSpec.java | 80 +++-------------- .../BlockchainReferenceTestCaseSpecTest.java | 50 +++++------ .../vm/BlockchainReferenceTestTools.java | 17 ++-- .../besu/evm/fluent/EVMExecutor.java | 18 ++-- .../besu/evm/fluent/EVMExecutorTest.java | 4 +- .../besu/evm/operation/PushOperationTest.java | 79 +++++++++++------ 14 files changed, 290 insertions(+), 252 deletions(-) create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/VerkleDevnetBlockHeaderFunctions.java create mode 100644 ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/CandidateBlock.java diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java index 41f831ed176..ef1adbb228d 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java @@ -183,19 +183,19 @@ public void writeTo(final RLPOutput out) { if (withdrawalsRoot == null) break; out.writeBytes(withdrawalsRoot); - /* - if (excessBlobGas == null || blobGasUsed == null) break; - out.writeLongScalar(blobGasUsed); - out.writeUInt64Scalar(excessBlobGas); + + if (excessBlobGas == null || blobGasUsed == null) break; + out.writeLongScalar(blobGasUsed); + out.writeUInt64Scalar(excessBlobGas); if (parentBeaconBlockRoot == null) break; - out.writeBytes(parentBeaconBlockRoot); + out.writeBytes(parentBeaconBlockRoot); - if (requestsHash == null) break; - out.writeBytes(requestsHash); + if (requestsHash == null) break; + out.writeBytes(requestsHash); - if (targetBlobCount == null) break; - out.writeUInt64Scalar(targetBlobCount);*/ + if (targetBlobCount == null) break; + out.writeUInt64Scalar(targetBlobCount); } while (false); out.endList(); } @@ -224,16 +224,13 @@ public static BlockHeader readFrom( ? Hash.wrap(input.readBytes32()) : null; - // TODO REACTIVATE - /* final Long blobGasUsed = !input.isEndOfCurrentList() ? input.readLongScalar() : null; final BlobGas excessBlobGas = !input.isEndOfCurrentList() ? BlobGas.of(input.readUInt64Scalar()) : null; final Bytes32 parentBeaconBlockRoot = !input.isEndOfCurrentList() ? input.readBytes32() : null; - final Hash requestsHash = !input.isEndOfCurrentList() ? Hash.wrap(input.readBytes32()) : null; + final Hash requestsHash = !input.isEndOfCurrentList() ? Hash.wrap(input.readBytes32()) : null; final UInt64 targetBlobCount = !input.isEndOfCurrentList() ? input.readUInt64Scalar() : null; - */ final ExecutionWitness executionWitness = !input.isEndOfCurrentList() ? ExecutionWitness.readFrom(input) : null; @@ -257,11 +254,11 @@ public static BlockHeader readFrom( mixHashOrPrevRandao, nonce, withdrawalHashRoot, - null, - null, - null, - null, - null, + blobGasUsed, + excessBlobGas, + parentBeaconBlockRoot, + requestsHash, + targetBlobCount, executionWitness, blockHeaderFunctions); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index 03084b5fcc3..181a76b7296 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -906,6 +906,7 @@ static ProtocolSpecBuilder verkleDefinition( .withdrawalsProcessor(new WithdrawalsProcessor(clearEmptyAccountStrategy)) .executionWitnessValidator(new ExecutionWitnessValidator.AllowedExecutionWitness()) .blockHashProcessor(new PragueBlockHashProcessor()) + .blockHeaderFunctions(new VerkleDevnetBlockHeaderFunctions()) .name("Verkle"); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/VerkleDevnetBlockHeaderFunctions.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/VerkleDevnetBlockHeaderFunctions.java new file mode 100644 index 00000000000..251e6935a0e --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/VerkleDevnetBlockHeaderFunctions.java @@ -0,0 +1,72 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; +import org.hyperledger.besu.ethereum.core.ParsedExtraData; +import org.hyperledger.besu.ethereum.rlp.RLP; +import org.hyperledger.besu.ethereum.rlp.RLPOutput; + +import org.apache.tuweni.bytes.Bytes; + +/** Implements the block hashing algorithm for MainNet as per the yellow paper. */ +public class VerkleDevnetBlockHeaderFunctions implements BlockHeaderFunctions { + + @Override + public Hash hash(final BlockHeader header) { + return createHash(header); + } + + public static Hash createHash(final BlockHeader header) { + final Bytes rlp = RLP.encode(rlpOutput -> serializeBlockHeader(header, rlpOutput)); + return Hash.hash(rlp); + } + + // TODO: Remove for mainnet, only needed for the current Verkle devnet. + protected static void serializeBlockHeader(final BlockHeader blockHeader, final RLPOutput out) { + out.startList(); + + out.writeBytes(blockHeader.getParentHash()); + out.writeBytes(blockHeader.getOmmersHash()); + out.writeBytes(blockHeader.getCoinbase()); + out.writeBytes(blockHeader.getStateRoot()); + out.writeBytes(blockHeader.getTransactionsRoot()); + out.writeBytes(blockHeader.getReceiptsRoot()); + out.writeBytes(blockHeader.getLogsBloom()); + out.writeUInt256Scalar(blockHeader.getDifficulty()); + out.writeLongScalar(blockHeader.getNumber()); + out.writeLongScalar(blockHeader.getGasLimit()); + out.writeLongScalar(blockHeader.getGasUsed()); + out.writeLongScalar(blockHeader.getTimestamp()); + out.writeBytes(blockHeader.getExtraData()); + out.writeBytes(blockHeader.getMixHashOrPrevRandao()); + out.writeLong(blockHeader.getNonce()); + do { + if (blockHeader.getBaseFee().isEmpty()) break; + out.writeUInt256Scalar(blockHeader.getBaseFee().get()); + + if (blockHeader.getWithdrawalsRoot().isEmpty()) break; + out.writeBytes(blockHeader.getWithdrawalsRoot().get()); + } while (false); + out.endList(); + } + + @Override + public ParsedExtraData parseExtraData(final BlockHeader header) { + return null; + } +} diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainTestSubCommand.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainTestSubCommand.java index 0d7ebb686c7..5e68773dad7 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainTestSubCommand.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainTestSubCommand.java @@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.referencetests.BlockchainReferenceTestCaseSpec; +import org.hyperledger.besu.ethereum.referencetests.CandidateBlock; import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules; import org.hyperledger.besu.ethereum.rlp.RLPException; import org.hyperledger.besu.evm.EVM; @@ -177,13 +178,13 @@ private void traceTestSpecs(final String test, final BlockchainReferenceTestCase final MutableBlockchain blockchain = spec.getBlockchain(); final ProtocolContext context = spec.getProtocolContext(); - for (final Block block : spec.getBlocks()) { - if (!spec.isExecutable(block)) { + for (final CandidateBlock candidateBlock : spec.getCandidateBlocks()) { + if (!candidateBlock.isExecutable()) { return; } try { - + final Block block = candidateBlock.getBlock(); final ProtocolSpec protocolSpec = schedule.getByBlockHeader(block.getHeader()); final BlockImporter blockImporter = protocolSpec.getBlockImporter(); @@ -196,7 +197,7 @@ private void traceTestSpecs(final String test, final BlockchainReferenceTestCase final BlockImportResult importResult = blockImporter.importBlock(context, block, validationMode, validationMode); - if (importResult.isImported() != spec.isValid(block)) { + if (importResult.isImported() != candidateBlock.isValid()) { parentCommand.out.printf( "Block %d (%s) %s%n", block.getHeader().getNumber(), @@ -210,10 +211,12 @@ private void traceTestSpecs(final String test, final BlockchainReferenceTestCase importResult.isImported() ? "Imported" : "Rejected (correctly)"); } } catch (final RLPException e) { - if (spec.isValid(block)) { + if (candidateBlock.isValid()) { parentCommand.out.printf( "Block %d (%s) should have imported but had an RLP exception %s%n", - block.getHeader().getNumber(), block.getHash(), e.getMessage()); + candidateBlock.getBlock().getHeader().getNumber(), + candidateBlock.getBlock().getHash(), + e.getMessage()); } } } diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index a4948597f75..a1d3a6ea8e9 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -119,7 +119,7 @@ def executionSpecTests = tasks.register("executionSpecTests") { generateTestFiles( fileTree(referenceTestsPath + "/fixtures/blockchain_tests"), file("src/reference-test/templates/BlockchainReferenceTest.java.template"), - "fixtures", + "all/fixtures", "$generatedTestsPath/org/hyperledger/besu/ethereum/vm/executionspec", "ExecutionSpecBlockchainTest", "org.hyperledger.besu.ethereum.vm.executionspec", @@ -129,7 +129,7 @@ def executionSpecTests = tasks.register("executionSpecTests") { generateTestFiles( fileTree(referenceTestsPath + "/fixtures/state_tests"), file("src/reference-test/templates/GeneralStateReferenceTest.java.template"), - "fixtures", + "all/fixtures", "$generatedTestsPath/org/hyperledger/besu/ethereum/vm/executionspec", "ExecutionSpecStateTest", "org.hyperledger.besu.ethereum.vm.executionspec", diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCase.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCase.java index 88feb3ad834..e8ca4d545fe 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCase.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCase.java @@ -16,7 +16,6 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; -import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; @@ -31,15 +30,9 @@ public interface BlockchainReferenceTestCase { ProtocolContext getProtocolContext(); - Iterable getBlocks(); + CandidateBlock[] getCandidateBlocks(); String getSealEngine(); Object getLastBlockHash(); - - boolean isExecutable(Block candidateBlock); - - boolean isValid(Block candidateBlock); - - boolean areAllTransactionsValid(final Block block); } diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java index 5f76f0ae1c9..329f87260ac 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java @@ -32,20 +32,12 @@ import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.ParsedExtraData; -import org.hyperledger.besu.ethereum.core.Transaction; -import org.hyperledger.besu.ethereum.core.Withdrawal; -import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; -import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; -import org.hyperledger.besu.ethereum.rlp.RLPInput; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.log.LogsBloomFilter; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Optional; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -58,7 +50,7 @@ public class BlockchainReferenceTestCaseSpec implements BlockchainReferenceTestC private final String network; - private final CandidateBlock[] candidateBlocks; + private final DefaultCandidateBlock[] candidateBlocks; private final ReferenceTestBlockHeader genesisBlockHeader; @@ -97,7 +89,7 @@ private static MutableBlockchain buildBlockchain(final BlockHeader genesisBlockH @JsonCreator public BlockchainReferenceTestCaseSpec( @JsonProperty("network") final String network, - @JsonProperty("blocks") final CandidateBlock[] candidateBlocks, + @JsonProperty("blocks") final DefaultCandidateBlock[] candidateBlocks, @JsonProperty("genesisBlockHeader") final ReferenceTestBlockHeader genesisBlockHeader, @SuppressWarnings("unused") @JsonProperty("genesisRLP") final String genesisRLP, @JsonProperty("pre") final Map accounts, @@ -124,35 +116,8 @@ public String getNetwork() { } @Override - public List getBlocks() { - return Arrays.stream(candidateBlocks).map(CandidateBlock::getBlock).toList(); - } - - @Override - public boolean isExecutable(final Block block) { - Optional candidateBlock = - Arrays.stream(candidateBlocks) - .filter(cb -> Objects.equals(cb.getBlock(), block)) - .findFirst(); - return candidateBlock.isPresent() && candidateBlock.get().rlp != null; - } - - @Override - public boolean isValid(final Block block) { - Optional candidateBlock = - Arrays.stream(candidateBlocks) - .filter(cb -> Objects.equals(cb.getBlock(), block)) - .findFirst(); - return candidateBlock.isPresent() && candidateBlock.get().valid; - } - - @Override - public boolean areAllTransactionsValid(final Block block) { - Optional candidateBlock = - Arrays.stream(candidateBlocks) - .filter(cb -> Objects.equals(cb.getBlock(), block)) - .findFirst(); - return candidateBlock.isPresent() && candidateBlock.get().areAllTransactionsValid(); + public CandidateBlock[] getCandidateBlocks() { + return candidateBlocks; } @Override @@ -270,15 +235,12 @@ public ParsedExtraData parseExtraData(final BlockHeader header) { "hasBigInt", "rlp_decoded" }) - public static class CandidateBlock { + public static class DefaultCandidateBlock extends CandidateBlock { - private final Bytes rlp; - - private final Boolean valid; private final List transactionSequence; @JsonCreator - public CandidateBlock( + public DefaultCandidateBlock( @JsonProperty("rlp") final String rlp, @JsonProperty("blockHeader") final Object blockHeader, @JsonProperty("transactions") final Object transactions, @@ -288,53 +250,21 @@ public CandidateBlock( @JsonProperty("withdrawalRequests") final Object withdrawalRequests, @JsonProperty("consolidationRequests") final Object consolidationRequests, @JsonProperty("transactionSequence") final List transactionSequence) { - boolean blockValid = true; - // The BLOCK__WrongCharAtRLP_0 test has an invalid character in its rlp string. - Bytes rlpAttempt = null; - try { - rlpAttempt = Bytes.fromHexString(rlp); - } catch (final IllegalArgumentException e) { - blockValid = false; - } - this.rlp = rlpAttempt; + super(rlp); if (blockHeader == null && transactions == null && uncleHeaders == null && withdrawals == null) { - blockValid = false; + this.valid = false; } - - this.valid = blockValid; this.transactionSequence = transactionSequence; } - public boolean isValid() { - return valid; - } - + @Override public boolean areAllTransactionsValid() { return transactionSequence == null || transactionSequence.stream().filter(t -> !t.valid()).count() == 0; } - - public boolean isExecutable() { - return rlp != null; - } - - public Block getBlock() { - final RLPInput input = new BytesValueRLPInput(rlp, false); - input.enterList(); - final MainnetBlockHeaderFunctions blockHeaderFunctions = new MainnetBlockHeaderFunctions(); - final BlockHeader header = BlockHeader.readFrom(input, blockHeaderFunctions); - final BlockBody body = - new BlockBody( - input.readList(Transaction::readFrom), - input.readList(inputData -> BlockHeader.readFrom(inputData, blockHeaderFunctions)), - input.isEndOfCurrentList() - ? Optional.empty() - : Optional.of(input.readList(Withdrawal::readFrom))); - return new Block(header, body); - } } } diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/CandidateBlock.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/CandidateBlock.java new file mode 100644 index 00000000000..fec94988c3c --- /dev/null +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/CandidateBlock.java @@ -0,0 +1,72 @@ +/* + * 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.referencetests; + +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockBody; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.core.Withdrawal; +import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; +import org.hyperledger.besu.ethereum.rlp.RLPInput; + +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; + +public abstract class CandidateBlock { + + protected final Bytes rlp; + protected Boolean valid; + + protected CandidateBlock(final String rlp) { + boolean blockValid = true; + // The BLOCK__WrongCharAtRLP_0 test has an invalid character in its rlp string. + Bytes rlpAttempt = null; + try { + rlpAttempt = Bytes.fromHexString(rlp); + } catch (final IllegalArgumentException e) { + blockValid = false; + } + this.rlp = rlpAttempt; + this.valid = blockValid; + } + + public Block getBlock() { + final RLPInput input = new BytesValueRLPInput(rlp, false); + input.enterList(); + final MainnetBlockHeaderFunctions blockHeaderFunctions = new MainnetBlockHeaderFunctions(); + final BlockHeader header = BlockHeader.readFrom(input, blockHeaderFunctions); + final BlockBody body = + new BlockBody( + input.readList(Transaction::readFrom), + input.readList(inputData -> BlockHeader.readFrom(inputData, blockHeaderFunctions)), + input.isEndOfCurrentList() + ? Optional.empty() + : Optional.of(input.readList(Withdrawal::readFrom))); + return new Block(header, body); + } + + public boolean isValid() { + return valid; + } + + public abstract boolean areAllTransactionsValid(); + + public boolean isExecutable() { + return rlp != null; + } +} diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/VerkleReferenceTestCaseSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/VerkleReferenceTestCaseSpec.java index d84a1beb102..7560ddaf687 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/VerkleReferenceTestCaseSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/VerkleReferenceTestCaseSpec.java @@ -30,20 +30,11 @@ import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.ParsedExtraData; -import org.hyperledger.besu.ethereum.core.Transaction; -import org.hyperledger.besu.ethereum.core.Withdrawal; -import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; -import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; -import org.hyperledger.besu.ethereum.rlp.RLPInput; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.evm.log.LogsBloomFilter; import org.hyperledger.besu.evm.worldstate.WorldUpdater; -import java.util.Arrays; -import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Optional; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -56,7 +47,7 @@ public class VerkleReferenceTestCaseSpec implements BlockchainReferenceTestCase private final String network; - private final CandidateBlock[] candidateBlocks; + private final VerkleCandidateBlock[] candidateBlocks; private final ReferenceTestBlockHeader genesisBlockHeader; @@ -97,7 +88,7 @@ private static MutableBlockchain buildBlockchain(final BlockHeader genesisBlockH @JsonCreator public VerkleReferenceTestCaseSpec( @JsonProperty("network") final String network, - @JsonProperty("blocks") final CandidateBlock[] candidateBlocks, + @JsonProperty("blocks") final VerkleCandidateBlock[] candidateBlocks, @JsonProperty("genesisBlockHeader") final ReferenceTestBlockHeader genesisBlockHeader, @SuppressWarnings("unused") @JsonProperty("genesisRLP") final String genesisRLP, @JsonProperty("pre") final Map accounts, @@ -120,8 +111,8 @@ public String getNetwork() { } @Override - public List getBlocks() { - return Arrays.stream(candidateBlocks).map(CandidateBlock::getBlock).toList(); + public CandidateBlock[] getCandidateBlocks() { + return candidateBlocks; } @Override @@ -149,29 +140,6 @@ public Hash getLastBlockHash() { return lastBlockHash; } - @Override - public boolean isExecutable(final Block block) { - Optional candidateBlock = - Arrays.stream(candidateBlocks) - .filter(cb -> Objects.equals(cb.getBlock(), block)) - .findFirst(); - return candidateBlock.isPresent() && candidateBlock.get().rlp != null; - } - - @Override - public boolean isValid(final Block block) { - Optional candidateBlock = - Arrays.stream(candidateBlocks) - .filter(cb -> Objects.equals(cb.getBlock(), block)) - .findFirst(); - return candidateBlock.isPresent() && candidateBlock.get().valid; - } - - @Override - public boolean areAllTransactionsValid(final Block block) { - throw new NotImplementedException("areAllTransactionsValid not available for verkle test"); - } - @Override public String getSealEngine() { return sealEngine; @@ -259,53 +227,27 @@ public ParsedExtraData parseExtraData(final BlockHeader header) { "rlp_decoded", "transactionSequence" }) - public static class CandidateBlock { - - private final Bytes rlp; + public static class VerkleCandidateBlock extends CandidateBlock { - private final Boolean valid; - - @JsonCreator - public CandidateBlock( + public VerkleCandidateBlock( @JsonProperty("rlp") final String rlp, @JsonProperty("blockHeader") final ReferenceTestBlockHeader blockHeader, @JsonProperty("transactions") final Object transactions, @JsonProperty("uncleHeaders") final Object uncleHeaders, @JsonProperty("withdrawals") final Object withdrawals, @SuppressWarnings("unused") @JsonProperty("witness") final Object witness) { - boolean blockValid = true; - // The BLOCK__WrongCharAtRLP_0 test has an invalid character in its rlp string. - Bytes rlpAttempt = null; - try { - rlpAttempt = Bytes.fromHexString(rlp); - } catch (final IllegalArgumentException e) { - blockValid = false; - } - this.rlp = rlpAttempt; - + super(rlp); if (blockHeader == null && transactions == null && uncleHeaders == null && withdrawals == null) { - blockValid = false; + this.valid = false; } - - this.valid = blockValid; } - public Block getBlock() { - final RLPInput input = new BytesValueRLPInput(rlp, false); - input.enterList(); - final MainnetBlockHeaderFunctions blockHeaderFunctions = new MainnetBlockHeaderFunctions(); - final BlockHeader header = BlockHeader.readFrom(input, blockHeaderFunctions); - final BlockBody body = - new BlockBody( - input.readList(Transaction::readFrom), - input.readList(inputData -> BlockHeader.readFrom(inputData, blockHeaderFunctions)), - input.isEndOfCurrentList() - ? Optional.empty() - : Optional.of(input.readList(Withdrawal::readFrom))); - return new Block(header, body); + @Override + public boolean areAllTransactionsValid() { + throw new NotImplementedException("areAllTransactionsValid not available for verkle test"); } } } diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpecTest.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpecTest.java index 07070375ad0..fe7e3cc59d7 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpecTest.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpecTest.java @@ -259,21 +259,21 @@ void getValidTxs() { throw new RuntimeException("Error parsing test case json.", e); } - BlockchainReferenceTestCaseSpec call1MB1024CalldepthD0g0v0London = blockchainReferenceTestCaseSpec - .testCaseSpecs - .get("Call1MB1024Calldepth_d0g0v0_London"); - Assertions.assertEquals(1, blockchainReferenceTestCaseSpec.testCaseSpecs.size()); Assertions.assertEquals( - 1, - call1MB1024CalldepthD0g0v0London - .getBlocks().size()); - - final Block block = call1MB1024CalldepthD0g0v0London - .getBlocks().getFirst(); - - Assertions.assertTrue(call1MB1024CalldepthD0g0v0London - .areAllTransactionsValid(block)); + 1, + blockchainReferenceTestCaseSpec + .testCaseSpecs + .get("Call1MB1024Calldepth_d0g0v0_London") + .getCandidateBlocks() + .length); + Assertions.assertEquals( + true, + blockchainReferenceTestCaseSpec + .testCaseSpecs + .get("Call1MB1024Calldepth_d0g0v0_London") + .getCandidateBlocks()[0] + .areAllTransactionsValid()); } @Test @@ -292,21 +292,21 @@ void getInValidTxs() { throw new RuntimeException("Error parsing test case json.", e); } - BlockchainReferenceTestCaseSpec valueOverflow_d0g0v0_EIP150 = blockchainReferenceTestCaseSpec - .testCaseSpecs - .get("ValueOverflow_d0g0v0_EIP150"); - Assertions.assertEquals(1, blockchainReferenceTestCaseSpec.testCaseSpecs.size()); Assertions.assertEquals( 1, - valueOverflow_d0g0v0_EIP150 - .getBlocks().size()); - - final Block block = valueOverflow_d0g0v0_EIP150 - .getBlocks().getFirst(); - - Assertions.assertFalse(valueOverflow_d0g0v0_EIP150 - .areAllTransactionsValid(block)); + blockchainReferenceTestCaseSpec + .testCaseSpecs + .get("ValueOverflow_d0g0v0_EIP150") + .getCandidateBlocks() + .length); + Assertions.assertEquals( + false, + blockchainReferenceTestCaseSpec + .testCaseSpecs + .get("ValueOverflow_d0g0v0_EIP150") + .getCandidateBlocks()[0] + .areAllTransactionsValid()); } } diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java index a6b2b8a6785..95cbc827a49 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTestTools.java @@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.referencetests.BlockchainReferenceTestCase; +import org.hyperledger.besu.ethereum.referencetests.CandidateBlock; import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules; import org.hyperledger.besu.ethereum.rlp.RLPException; import org.hyperledger.besu.evm.EVM; @@ -113,14 +114,16 @@ public static void executeTest(final BlockchainReferenceTestCase testCase) { final MutableBlockchain blockchain = testCase.getBlockchain(); final ProtocolContext context = testCase.getProtocolContext(); - for (final Block candidateBlock : - testCase.getBlocks()) { - if (!testCase.isExecutable(candidateBlock)) { + for (final CandidateBlock candidateBlock : + testCase.getCandidateBlocks()) { + if (!candidateBlock.isExecutable()) { return; } try { - final ProtocolSpec protocolSpec = schedule.getByBlockHeader(candidateBlock.getHeader()); + + final Block block = candidateBlock.getBlock(); + final ProtocolSpec protocolSpec = schedule.getByBlockHeader(block.getHeader()); final BlockImporter blockImporter = protocolSpec.getBlockImporter(); verifyJournaledEVMAccountCompatability(worldState, protocolSpec); @@ -130,11 +133,11 @@ public static void executeTest(final BlockchainReferenceTestCase testCase) { ? HeaderValidationMode.LIGHT : HeaderValidationMode.FULL; final BlockImportResult importResult = - blockImporter.importBlock(context, candidateBlock, validationMode, validationMode); + blockImporter.importBlock(context, block, validationMode, validationMode); - assertThat(importResult.isImported()).isEqualTo(testCase.isValid(candidateBlock)); + assertThat(importResult.isImported()).isEqualTo(candidateBlock.isValid()); } catch (final RLPException e) { - assertThat(testCase.isValid(candidateBlock)).isFalse(); + assertThat(candidateBlock.isValid()).isFalse(); } } 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 c0a884195f2..36218c0e3c4 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 @@ -49,6 +49,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Function; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; @@ -1172,25 +1173,26 @@ public EVMExecutor accessListWarmStorage(final Address address, final Bytes32... } /** - * Sets Message call processor. + * Sets the message call processor. * - * @param messageCallProcessor the message call processor - * @return the evm executor + * @param processorFactory the function to create the message call processor + * @return the EVM executor */ - public EVMExecutor messageCallProcessor(final MessageCallProcessor messageCallProcessor) { - this.messageCallProcessor = messageCallProcessor; + public EVMExecutor messageCallProcessor( + final Function processorFactory) { + this.messageCallProcessor = processorFactory.apply(evm); return this; } /** * Sets Contract call processor. * - * @param contractCreationProcessor the contract creation processor + * @param processorFactory the function to create the contract call processor * @return the evm executor */ public EVMExecutor contractCallProcessor( - final ContractCreationProcessor contractCreationProcessor) { - this.contractCreationProcessor = contractCreationProcessor; + final Function processorFactory) { + this.contractCreationProcessor = processorFactory.apply(evm); return this; } 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 eaa8dbc8c4b..6bd96b3a371 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 @@ -192,8 +192,8 @@ void giantExecuteStack() { .warmAddress(Address.ZERO) .accessListWarmStorage( Address.ZERO, Bytes32.ZERO, Bytes32.leftPad(Bytes.ofUnsignedLong(2L))) - .messageCallProcessor(new MessageCallProcessor(null, null)) - .contractCallProcessor(new ContractCreationProcessor(null, true, null, 1L)) + .messageCallProcessor(evm -> new MessageCallProcessor(evm, null)) + .contractCallProcessor(evm -> new ContractCreationProcessor(evm, true, null, 1L)) .execute(); assertThat(result).isNotNull(); } 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 d303cc27d80..aca7178189e 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 @@ -15,13 +15,16 @@ package org.hyperledger.besu.evm.operation; import static org.assertj.core.api.Assertions.assertThat; -import static org.hyperledger.besu.evm.operation.PushOperation.staticOperation; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.evm.code.CodeV0; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.EvmSpecVersion; +import org.hyperledger.besu.evm.code.CodeFactory; import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; import org.hyperledger.besu.evm.toy.ToyBlockValues; import org.hyperledger.besu.evm.toy.ToyWorld; @@ -29,55 +32,75 @@ import org.apache.tuweni.bytes.Bytes32; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) public class PushOperationTest { - private static final byte[] byteCode = new byte[] {0x00, 0x01, 0x02, 0x03}; - private static final MessageFrame frame = - MessageFrame.builder() - .worldUpdater(new ToyWorld()) - .originator(Address.ZERO) - .gasPrice(Wei.ONE) - .blobGasPrice(Wei.ONE) - .blockValues(new ToyBlockValues()) - .miningBeneficiary(Address.ZERO) - .blockHashLookup((l) -> Hash.ZERO) - .type(MessageFrame.Type.MESSAGE_CALL) - .initialGas(1) - .address(Address.ZERO) - .contract(Address.ZERO) - .inputData(Bytes32.ZERO) - .sender(Address.ZERO) - .value(Wei.ZERO) - .apparentValue(Wei.ZERO) - .code(CodeV0.EMPTY_CODE) - .completer(messageFrame -> {}) - .build(); - ; + private final GasCalculator gasCalculator = new PragueGasCalculator(); + private final CodeFactory codeFactory = + new CodeFactory( + EvmSpecVersion.PRAGUE.getMaxEofVersion(), EvmSpecVersion.PRAGUE.getMaxInitcodeSize()); + + private static final Bytes byteCode = Bytes.wrap(new byte[] {0x00, 0x01, 0x02, 0x03}); + + @Mock private EVM evm; + + private MessageFrame createMessageFrame(final int pc) { + MessageFrame frame = + MessageFrame.builder() + .worldUpdater(new ToyWorld()) + .originator(Address.ZERO) + .gasPrice(Wei.ONE) + .blobGasPrice(Wei.ONE) + .blockValues(new ToyBlockValues()) + .miningBeneficiary(Address.ZERO) + .blockHashLookup((l) -> Hash.ZERO) + .type(MessageFrame.Type.MESSAGE_CALL) + .initialGas(1) + .address(Address.ZERO) + .contract(Address.ZERO) + .inputData(Bytes32.ZERO) + .sender(Address.ZERO) + .value(Wei.ZERO) + .apparentValue(Wei.ZERO) + .code(codeFactory.createCode(byteCode)) + .completer(messageFrame -> {}) + .build(); + frame.setPC(pc); + return frame; + } @Test void unpaddedPushDoesntReachEndCode() { - staticOperation(frame, byteCode, 0, byteCode.length - 2); + MessageFrame frame = createMessageFrame(0); + final Operation operation = new PushOperation(byteCode.size() - 2, gasCalculator); + operation.execute(frame, evm); assertThat(frame.getStackItem(0).equals(Bytes.fromHexString("0x0102"))).isTrue(); } @Test void unpaddedPushUpReachesEndCode() { - staticOperation(frame, byteCode, 0, byteCode.length - 1); + MessageFrame frame = createMessageFrame(0); + final Operation operation = new PushOperation(byteCode.size() - 1, gasCalculator); + operation.execute(frame, evm); assertThat(frame.getStackItem(0).equals(Bytes.fromHexString("0x010203"))).isTrue(); } @Test void paddedPush() { - staticOperation(frame, byteCode, 1, byteCode.length - 1); + MessageFrame frame = createMessageFrame(1); + final Operation operation = new PushOperation(byteCode.size() - 1, gasCalculator); + operation.execute(frame, evm); assertThat(frame.getStackItem(0).equals(Bytes.fromHexString("0x020300"))).isTrue(); } @Test void oobPush() { - staticOperation(frame, byteCode, byteCode.length, byteCode.length - 1); + MessageFrame frame = createMessageFrame(byteCode.size()); + final Operation operation = new PushOperation(byteCode.size() - 1, gasCalculator); + operation.execute(frame, evm); assertThat(frame.getStackItem(0).equals(Bytes.EMPTY)).isTrue(); } }