From 4ee14eca1de3857af218311cf466970999cf0d40 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Thu, 4 Jul 2024 12:21:25 +1000 Subject: [PATCH 01/12] Implement System Calls (#7263) Signed-off-by: Gabriel-Trintinalia --- .../blockcreation/AbstractBlockCreator.java | 16 +- .../AbstractBlockCreatorTest.java | 4 +- .../mainnet/AbstractBlockProcessor.java | 14 +- .../mainnet/MainnetTransactionProcessor.java | 2 +- .../ethereum/mainnet/SystemCallProcessor.java | 141 ++++++++++++ .../WithdrawalRequestContractHelper.java | 197 ----------------- .../AbstractSystemCallRequestProcessor.java | 102 +++++++++ .../requests/DepositRequestProcessor.java | 7 +- .../requests/ProcessRequestContext.java | 32 +++ .../mainnet/requests/RequestProcessor.java | 6 +- .../requests/RequestProcessorCoordinator.java | 7 +- .../requests/WithdrawalRequestProcessor.java | 63 ++++-- .../requests/WithdrawalRequestValidator.java | 6 +- .../mainnet/SystemCallProcessorTest.java | 113 ++++++++++ .../WithdrawalRequestContractHelperTest.java | 205 ------------------ ...ithdrawalRequestValidatorTestFixtures.java | 2 +- .../hyperledger/besu/evmtool/T8nExecutor.java | 12 +- 17 files changed, 487 insertions(+), 442 deletions(-) create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/SystemCallProcessor.java delete mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/AbstractSystemCallRequestProcessor.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/ProcessRequestContext.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/SystemCallProcessorTest.java delete mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelperTest.java diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java index 15f3b16d89a..78c32842ff7 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java @@ -50,7 +50,9 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; +import org.hyperledger.besu.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; @@ -241,10 +243,18 @@ protected BlockCreationResult createBlock( // EIP-7685: process EL requests final Optional requestProcessor = newProtocolSpec.getRequestProcessorCoordinator(); + + ProcessRequestContext context = + new ProcessRequestContext( + processableBlockHeader, + disposableWorldState, + newProtocolSpec, + transactionResults.getReceipts(), + new CachingBlockHashLookup(processableBlockHeader, protocolContext.getBlockchain()), + operationTracer); + Optional> maybeRequests = - requestProcessor.flatMap( - processor -> - processor.process(disposableWorldState, transactionResults.getReceipts())); + requestProcessor.flatMap(processor -> processor.process(context)); throwIfStopped(); diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java index 856bf874b20..68d13987bb3 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java @@ -77,6 +77,7 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.CancunFeeMarket; import org.hyperledger.besu.ethereum.mainnet.requests.DepositRequestProcessor; import org.hyperledger.besu.ethereum.mainnet.requests.DepositRequestValidator; +import org.hyperledger.besu.ethereum.mainnet.requests.ProcessRequestContext; import org.hyperledger.besu.ethereum.mainnet.requests.RequestProcessorCoordinator; import org.hyperledger.besu.ethereum.mainnet.requests.RequestsValidatorCoordinator; import org.hyperledger.besu.evm.internal.EvmConfiguration; @@ -135,7 +136,8 @@ void findDepositRequestsFromReceipts() { final List expectedDepositRequests = List.of(expectedDepositRequest); var depositRequestsFromReceipts = - new DepositRequestProcessor(DEFAULT_DEPOSIT_CONTRACT_ADDRESS).process(null, receipts); + new DepositRequestProcessor(DEFAULT_DEPOSIT_CONTRACT_ADDRESS) + .process(new ProcessRequestContext(null, null, null, receipts, null, null)); assertThat(depositRequestsFromReceipts.get()).isEqualTo(expectedDepositRequests); } 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 888bc848f15..8ecfa453d9f 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 @@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.core.Withdrawal; +import org.hyperledger.besu.ethereum.mainnet.requests.ProcessRequestContext; import org.hyperledger.besu.ethereum.mainnet.requests.RequestProcessorCoordinator; import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; @@ -107,6 +108,7 @@ public BlockProcessingResult processBlock( final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(blockHeader); protocolSpec.getBlockHashProcessor().processBlockHashes(blockchain, worldState, blockHeader); + final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(blockHeader, blockchain); for (final Transaction transaction : transactions) { if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) { @@ -115,7 +117,6 @@ public BlockProcessingResult processBlock( final WorldUpdater worldStateUpdater = worldState.updater(); - final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(blockHeader, blockchain); final Address miningBeneficiary = miningBeneficiaryCalculator.calculateBeneficiary(blockHeader); @@ -197,7 +198,16 @@ public BlockProcessingResult processBlock( protocolSpec.getRequestProcessorCoordinator(); Optional> maybeRequests = Optional.empty(); if (requestProcessor.isPresent()) { - maybeRequests = requestProcessor.get().process(worldState, receipts); + ProcessRequestContext context = + new ProcessRequestContext( + blockHeader, + worldState, + protocolSpec, + receipts, + blockHashLookup, + OperationTracer.NO_TRACING); + + maybeRequests = requestProcessor.get().process(context); } if (!rewardCoinbase(worldState, blockHeader, ommers, skipZeroBlockRewards)) { 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 d982265f242..53801d14cd3 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 @@ -563,7 +563,7 @@ public void process(final MessageFrame frame, final OperationTracer operationTra executor.process(frame, operationTracer); } - private AbstractMessageProcessor getMessageProcessor(final MessageFrame.Type type) { + public AbstractMessageProcessor getMessageProcessor(final MessageFrame.Type type) { return switch (type) { case MESSAGE_CALL -> messageCallProcessor; case CONTRACT_CREATION -> contractCreationProcessor; 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 new file mode 100644 index 00000000000..f74de79442f --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/SystemCallProcessor.java @@ -0,0 +1,141 @@ +/* + * 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; + +import static org.hyperledger.besu.evm.frame.MessageFrame.DEFAULT_MAX_STACK_SIZE; + +import org.hyperledger.besu.datatypes.Address; +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.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; + +import java.util.Deque; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SystemCallProcessor { + private static final Logger LOG = LoggerFactory.getLogger(SystemCallProcessor.class); + + /** The system address */ + static final Address SYSTEM_ADDRESS = + Address.fromHexString("0xfffffffffffffffffffffffffffffffffffffffe"); + + private final MainnetTransactionProcessor mainnetTransactionProcessor; + + public SystemCallProcessor(final MainnetTransactionProcessor mainnetTransactionProcessor) { + this.mainnetTransactionProcessor = mainnetTransactionProcessor; + } + + /** + * Processes a system call to a specified address, using the provided world state, block header, + * operation tracer, and block hash lookup. + * + * @param callAddress the address to call. + * @param worldState the current world state. + * @param blockHeader the current block header. + * @param operationTracer the operation tracer for tracing EVM operations. + * @param blockHashLookup the block hash lookup function. + * @return the output data from the call + */ + public Bytes process( + final Address callAddress, + final WorldUpdater worldState, + final ProcessableBlockHeader blockHeader, + final OperationTracer operationTracer, + final BlockHashOperation.BlockHashLookup blockHashLookup) { + + // if no code exists at CALL_ADDRESS, the call must fail silently + final Account maybeContract = worldState.get(callAddress); + if (maybeContract == null) { + LOG.trace("System call address not found {}", callAddress); + return null; + } + + final AbstractMessageProcessor messageProcessor = + mainnetTransactionProcessor.getMessageProcessor(MessageFrame.Type.MESSAGE_CALL); + final MessageFrame initialFrame = + createCallFrame(callAddress, worldState, blockHeader, blockHashLookup); + + return processFrame(initialFrame, messageProcessor, operationTracer, worldState); + } + + private Bytes processFrame( + final MessageFrame frame, + final AbstractMessageProcessor processor, + final OperationTracer tracer, + final WorldUpdater updater) { + + if (!frame.getCode().isValid()) { + throw new RuntimeException("System call did not execute to completion - opcode invalid"); + } + + Deque stack = frame.getMessageFrameStack(); + while (!stack.isEmpty()) { + processor.process(stack.peekFirst(), tracer); + } + + if (frame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { + updater.commit(); + return frame.getOutputData(); + } + + // the call must execute to completion + throw new RuntimeException("System call did not execute to completion"); + } + + private MessageFrame createCallFrame( + final Address callAddress, + final WorldUpdater worldUpdater, + final ProcessableBlockHeader blockHeader, + final BlockHashOperation.BlockHashLookup blockHashLookup) { + + final Optional maybeContract = Optional.ofNullable(worldUpdater.get(callAddress)); + final AbstractMessageProcessor processor = + mainnetTransactionProcessor.getMessageProcessor(MessageFrame.Type.MESSAGE_CALL); + + return MessageFrame.builder() + .maxStackSize(DEFAULT_MAX_STACK_SIZE) + .worldUpdater(worldUpdater) + .initialGas(30_000_000L) + .originator(SYSTEM_ADDRESS) + .gasPrice(Wei.ZERO) + .blobGasPrice(Wei.ZERO) + .value(Wei.ZERO) + .apparentValue(Wei.ZERO) + .blockValues(blockHeader) + .completer(__ -> {}) + .miningBeneficiary(Address.ZERO) // Confirm this + .type(MessageFrame.Type.MESSAGE_CALL) + .address(callAddress) + .contract(callAddress) + .inputData(Bytes.EMPTY) + .sender(SYSTEM_ADDRESS) + .blockHashLookup(blockHashLookup) + .code( + maybeContract + .map(c -> processor.getCodeFromEVM(c.getCodeHash(), c.getCode())) + .orElse(CodeV0.EMPTY_CODE)) + .build(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java deleted file mode 100644 index c0b7302e867..00000000000 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelper.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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; - -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.BLSPublicKey; -import org.hyperledger.besu.datatypes.GWei; -import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.ethereum.core.MutableWorldState; -import org.hyperledger.besu.ethereum.core.WithdrawalRequest; -import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.account.MutableAccount; -import org.hyperledger.besu.evm.worldstate.WorldUpdater; - -import java.util.ArrayList; -import java.util.List; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.units.bigints.UInt256; -import org.apache.tuweni.units.bigints.UInt64; - -/** - * Helper for interacting with the Validator Withdrawal Request Contract - * (https://eips.ethereum.org/EIPS/eip-7002) - * - *

TODO: Please note that this is not the spec-way of interacting with the Validator Withdrawal - * Request contract. See https://github.com/hyperledger/besu/issues/6918 for more information. - */ -public class WithdrawalRequestContractHelper { - - public static final Address WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = - Address.fromHexString("0x00A3ca265EBcb825B45F985A16CEFB49958cE017"); - - /** private constructor to prevent instantiations */ - private WithdrawalRequestContractHelper() {} - - @VisibleForTesting - // Storage slot to store the difference between number of withdrawal requests since last block and - // target withdrawal requests - // per block - static final UInt256 EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT = UInt256.valueOf(0L); - - @VisibleForTesting - // Storage slot to store the number of withdrawal requests added since last block - static final UInt256 WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT = UInt256.valueOf(1L); - - @VisibleForTesting - static final UInt256 WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT = UInt256.valueOf(2L); - - @VisibleForTesting - static final UInt256 WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT = UInt256.valueOf(3L); - - private static final UInt256 WITHDRAWAL_REQUEST_QUEUE_STORAGE_OFFSET = UInt256.valueOf(4L); - - // How many slots each withdrawal request occupies in the account state - private static final int WITHDRAWAL_REQUEST_STORAGE_SLOT_SIZE = 3; - - public static final int MAX_WITHDRAWAL_REQUESTS_PER_BLOCK = 16; - - private static final int TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK = 2; - - private static final UInt256 INITIAL_EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT = - UInt256.valueOf(1181); - - // TODO-lucas Add MIN_WITHDRAWAL_REQUEST_FEE and WITHDRAWAL_REQUEST_FEE_UPDATE_FRACTION - - /* - Pop the expected list of withdrawal requests from the smart contract, updating the queue pointers and other - control variables in the contract state. - */ - public static List popWithdrawalRequestsFromQueue( - final MutableWorldState mutableWorldState) { - final WorldUpdater worldUpdater = mutableWorldState.updater(); - final MutableAccount account = worldUpdater.getAccount(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS); - if (account == null || Hash.EMPTY.equals(account.getCodeHash())) { - return List.of(); - } - - final List withdrawalRequests = dequeueWithdrawalRequests(account); - updateExcessWithdrawalRequests(account); - resetWithdrawalRequestsCount(account); - - worldUpdater.commit(); - - return withdrawalRequests; - } - - private static List dequeueWithdrawalRequests(final MutableAccount account) { - final UInt256 queueHeadIndex = - account.getStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT); - final UInt256 queueTailIndex = - account.getStorageValue(WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT); - - final List withdrawalRequests = - peekExpectedWithdrawalRequests(account, queueHeadIndex, queueTailIndex); - - final UInt256 newQueueHeadIndex = queueHeadIndex.plus(withdrawalRequests.size()); - if (newQueueHeadIndex.equals(queueTailIndex)) { - // Queue is empty, reset queue pointers - account.setStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT, UInt256.valueOf(0L)); - account.setStorageValue(WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT, UInt256.valueOf(0L)); - } else { - account.setStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT, newQueueHeadIndex); - } - - return withdrawalRequests; - } - - /* - ;; Each stack element has the following layout: - ;; - ;; A: addr - ;; 0x00 | 00 00 00 00 00 00 00 00 00 00 00 00 aa aa aa aa - ;; 0x10 | aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa - ;; - ;; B: pk[0:32] - ;; 0x00 | bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb - ;; 0x10 | bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb - ;; - ;; C: pk[32:48] ++ am[0:8] -> pk2_am - ;; 0x00 | cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc - ;; 0x10 | dd dd dd dd dd dd dd dd 00 00 00 00 00 00 00 00 - ;; - ;; To get these three stack elements into the correct contiguous format, it is - ;; necessary to combine them in the follow form: - ;; - ;; (A[12:32] ++ B[0:12], B[12:32] ++ C[0:12], C[12:24]) - */ - private static List peekExpectedWithdrawalRequests( - final Account account, final UInt256 queueHeadIndex, final UInt256 queueTailIndex) { - final long numRequestsInQueue = queueTailIndex.subtract(queueHeadIndex).toLong(); - final long numRequestsDequeued = - Long.min(numRequestsInQueue, MAX_WITHDRAWAL_REQUESTS_PER_BLOCK); - - final List withdrawalRequests = new ArrayList<>(); - - for (int i = 0; i < numRequestsDequeued; i++) { - final UInt256 queueStorageSlot = - WITHDRAWAL_REQUEST_QUEUE_STORAGE_OFFSET.plus( - queueHeadIndex.plus(i).multiply(WITHDRAWAL_REQUEST_STORAGE_SLOT_SIZE)); - final Address sourceAddress = - Address.wrap(account.getStorageValue(queueStorageSlot).toBytes().slice(12, 20)); - final BLSPublicKey validatorPubkey = - BLSPublicKey.wrap( - Bytes.concatenate( - account - .getStorageValue(queueStorageSlot.plus(1)) - .toBytes() - .slice(0, 32), // no need to slice - account.getStorageValue(queueStorageSlot.plus(2)).toBytes().slice(0, 16))); - final UInt64 amount = - UInt64.fromBytes(account.getStorageValue(queueStorageSlot.plus(2)).slice(16, 8)); - - withdrawalRequests.add( - new WithdrawalRequest(sourceAddress, validatorPubkey, GWei.of(amount))); - } - - return withdrawalRequests; - } - - private static void updateExcessWithdrawalRequests(final MutableAccount account) { - UInt256 previousExcessRequests = - account.getStorageValue(EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT); - - if (previousExcessRequests.equals(INITIAL_EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT)) { - previousExcessRequests = UInt256.ZERO; - } - - final UInt256 requestsCount = account.getStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT); - - UInt256 newExcessRequests = UInt256.valueOf(0L); - if (previousExcessRequests.plus(requestsCount).toLong() - > TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK) { - newExcessRequests = - previousExcessRequests.plus(requestsCount).subtract(TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK); - } - - account.setStorageValue(EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT, newExcessRequests); - } - - private static void resetWithdrawalRequestsCount(final MutableAccount account) { - account.setStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, UInt256.valueOf(0L)); - } -} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/AbstractSystemCallRequestProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/AbstractSystemCallRequestProcessor.java new file mode 100644 index 00000000000..a7d959f4b98 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/AbstractSystemCallRequestProcessor.java @@ -0,0 +1,102 @@ +/* + * 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.requests; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.ethereum.core.Request; +import org.hyperledger.besu.ethereum.mainnet.SystemCallProcessor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; + +/** + * Abstract base class for processing system call requests. + * + * @param The type of request to be processed. + */ +public abstract class AbstractSystemCallRequestProcessor + implements RequestProcessor { + + /** + * Processes a system call and converts the result into requests of type T. + * + * @param context The request context being processed. + * @return An {@link Optional} containing a list of {@link T} objects if any are found + */ + @Override + public Optional> process(final ProcessRequestContext context) { + + SystemCallProcessor systemCallProcessor = + new SystemCallProcessor(context.protocolSpec().getTransactionProcessor()); + + Bytes systemCallOutput = + systemCallProcessor.process( + getCallAddress(), + context.mutableWorldState().updater(), + context.blockHeader(), + context.operationTracer(), + context.blockHashLookup()); + + List requests = parseRequests(systemCallOutput); + return Optional.ofNullable(requests); + } + + /** + * Parses the provided bytes into a list of {@link T} objects. + * + * @param bytes The bytes representing requests. + * @return A list of parsed {@link T} objects. + */ + protected List parseRequests(final Bytes bytes) { + if (bytes == null) { + return null; + } + final List requests = new ArrayList<>(); + if (bytes.isEmpty()) { + return requests; + } + int count = bytes.size() / getRequestBytesSize(); + for (int i = 0; i < count; i++) { + Bytes requestBytes = bytes.slice(i * getRequestBytesSize(), getRequestBytesSize()); + requests.add(parseRequest(requestBytes)); + } + return requests; + } + + /** + * Parses a single request from the provided bytes. + * + * @param requestBytes The bytes representing a single request. + * @return A parsed {@link T} object. + */ + protected abstract T parseRequest(final Bytes requestBytes); + + /** + * Gets the call address for the specific request type. + * + * @return The call address. + */ + protected abstract Address getCallAddress(); + + /** + * Gets the size of the bytes representing a single request. + * + * @return The size of the bytes representing a single request. + */ + protected abstract int getRequestBytesSize(); +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/DepositRequestProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/DepositRequestProcessor.java index ce3ed6a5f65..8902ecc510a 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/DepositRequestProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/DepositRequestProcessor.java @@ -16,7 +16,6 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.core.DepositRequest; -import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Request; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.core.encoding.DepositRequestDecoder; @@ -39,12 +38,12 @@ public DepositRequestProcessor(final Address depositContractAddress) { } @Override - public Optional> process( - final MutableWorldState ignored, final List transactionReceipts) { + public Optional> process(final ProcessRequestContext context) { if (depositContractAddress.isEmpty()) { return Optional.empty(); } - List depositRequests = findDepositRequestsFromReceipts(transactionReceipts); + List depositRequests = + findDepositRequestsFromReceipts(context.transactionReceipts()); return Optional.of(depositRequests); } 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 new file mode 100644 index 00000000000..63f4a8d5144 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/ProcessRequestContext.java @@ -0,0 +1,32 @@ +/* + * 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.requests; + +import org.hyperledger.besu.ethereum.core.MutableWorldState; +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.tracing.OperationTracer; + +import java.util.List; + +public record ProcessRequestContext( + ProcessableBlockHeader blockHeader, + MutableWorldState mutableWorldState, + ProtocolSpec protocolSpec, + List transactionReceipts, + BlockHashLookup blockHashLookup, + OperationTracer operationTracer) {} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/RequestProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/RequestProcessor.java index d09b3c47d18..55f3cd41788 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/RequestProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/RequestProcessor.java @@ -14,15 +14,11 @@ */ package org.hyperledger.besu.ethereum.mainnet.requests; -import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Request; -import org.hyperledger.besu.ethereum.core.TransactionReceipt; import java.util.List; import java.util.Optional; public interface RequestProcessor { - Optional> process( - final MutableWorldState mutableWorldState, - final List transactionReceipts); + Optional> process(final ProcessRequestContext context); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/RequestProcessorCoordinator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/RequestProcessorCoordinator.java index b72674b4d24..b98274729d5 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/RequestProcessorCoordinator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/RequestProcessorCoordinator.java @@ -15,9 +15,7 @@ package org.hyperledger.besu.ethereum.mainnet.requests; import org.hyperledger.besu.datatypes.RequestType; -import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Request; -import org.hyperledger.besu.ethereum.core.TransactionReceipt; import java.util.ArrayList; import java.util.List; @@ -39,11 +37,10 @@ private RequestProcessorCoordinator( this.processors = processors; } - public Optional> process( - final MutableWorldState mutableWorldState, final List receipts) { + public Optional> process(final ProcessRequestContext context) { List requests = null; for (RequestProcessor requestProcessor : processors.values()) { - var r = requestProcessor.process(mutableWorldState, receipts); + var r = requestProcessor.process(context); if (r.isPresent()) { if (requests == null) { requests = new ArrayList<>(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/WithdrawalRequestProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/WithdrawalRequestProcessor.java index 9803f23f3f9..b230a6d6103 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/WithdrawalRequestProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/WithdrawalRequestProcessor.java @@ -14,25 +14,60 @@ */ package org.hyperledger.besu.ethereum.mainnet.requests; -import org.hyperledger.besu.ethereum.core.MutableWorldState; -import org.hyperledger.besu.ethereum.core.Request; -import org.hyperledger.besu.ethereum.core.TransactionReceipt; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.BLSPublicKey; +import org.hyperledger.besu.datatypes.GWei; import org.hyperledger.besu.ethereum.core.WithdrawalRequest; -import org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper; -import java.util.List; -import java.util.Optional; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt64; -public class WithdrawalRequestProcessor implements RequestProcessor { +/** Processor for handling withdrawal requests. */ +public class WithdrawalRequestProcessor + extends AbstractSystemCallRequestProcessor { + + public static final Address WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = + Address.fromHexString("0x00A3ca265EBcb825B45F985A16CEFB49958cE017"); + + private static final int ADDRESS_BYTES = 20; + private static final int PUBLIC_KEY_BYTES = 48; + private static final int AMOUNT_BYTES = 8; + private static final int WITHDRAWAL_REQUEST_BYTES_SIZE = + ADDRESS_BYTES + PUBLIC_KEY_BYTES + AMOUNT_BYTES; + + /** + * Gets the call address for withdrawal requests. + * + * @return The call address. + */ @Override - public Optional> process( - final MutableWorldState mutableWorldState, - final List transactionReceipts) { + protected Address getCallAddress() { + return WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS; + } - List withdrawalRequests = - WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(mutableWorldState).stream() - .toList(); + /** + * Gets the size of the bytes representing a single withdrawal request. + * + * @return The size of the bytes representing a single withdrawal request. + */ + @Override + protected int getRequestBytesSize() { + return WITHDRAWAL_REQUEST_BYTES_SIZE; + } - return Optional.of(withdrawalRequests); + /** + * Parses a single withdrawal request from the provided bytes. + * + * @param requestBytes The bytes representing a single withdrawal request. + * @return A parsed {@link WithdrawalRequest} object. + */ + @Override + protected WithdrawalRequest parseRequest(final Bytes requestBytes) { + final Address sourceAddress = Address.wrap(requestBytes.slice(0, ADDRESS_BYTES)); + final BLSPublicKey validatorPublicKey = + BLSPublicKey.wrap(requestBytes.slice(ADDRESS_BYTES, PUBLIC_KEY_BYTES)); + final UInt64 amount = + UInt64.fromBytes(requestBytes.slice(ADDRESS_BYTES + PUBLIC_KEY_BYTES, AMOUNT_BYTES)); + return new WithdrawalRequest(sourceAddress, validatorPublicKey, GWei.of(amount)); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/WithdrawalRequestValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/WithdrawalRequestValidator.java index d5f04e6ef74..fc108b798f3 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/WithdrawalRequestValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/WithdrawalRequestValidator.java @@ -21,7 +21,6 @@ import org.hyperledger.besu.ethereum.core.Request; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.core.WithdrawalRequest; -import org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper; import java.util.Collections; import java.util.List; @@ -32,6 +31,8 @@ public class WithdrawalRequestValidator implements RequestValidator { + public static final int MAX_WITHDRAWAL_REQUESTS_PER_BLOCK = 16; + private static final Logger LOG = LoggerFactory.getLogger(WithdrawalRequestValidator.class); private boolean validateWithdrawalRequestParameter( @@ -51,8 +52,7 @@ private boolean validateWithdrawalRequestsInBlock( .orElse(Collections.emptyList()); // TODO Do we need to allow for customization? (e.g. if the value changes in the next fork) - if (withdrawalRequestsInBlock.size() - > WithdrawalRequestContractHelper.MAX_WITHDRAWAL_REQUESTS_PER_BLOCK) { + if (withdrawalRequestsInBlock.size() > MAX_WITHDRAWAL_REQUESTS_PER_BLOCK) { LOG.warn( "Block {} has more than the allowed maximum number of withdrawal requests", blockHash); return false; 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 new file mode 100644 index 00000000000..e1d3906e734 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/SystemCallProcessorTest.java @@ -0,0 +1,113 @@ +/* + * 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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.datatypes.Address; +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.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; +import org.hyperledger.besu.evm.worldstate.WorldUpdater; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SystemCallProcessorTest { + private static final Address CALL_ADDRESS = Address.fromHexString("0x1"); + private static final Bytes EXPECTED_OUTPUT = Bytes.fromHexString("0x01"); + private ProcessableBlockHeader mockBlockHeader; + private MainnetTransactionProcessor mockTransactionProcessor; + private BlockHashOperation.BlockHashLookup mockBlockHashLookup; + private AbstractMessageProcessor mockMessageCallProcessor; + + @BeforeEach + public void setUp() { + mockBlockHeader = mock(ProcessableBlockHeader.class); + mockTransactionProcessor = mock(MainnetTransactionProcessor.class); + mockMessageCallProcessor = mock(MessageCallProcessor.class); + mockBlockHashLookup = mock(BlockHashOperation.BlockHashLookup.class); + when(mockTransactionProcessor.getMessageProcessor(any())).thenReturn(mockMessageCallProcessor); + } + + @Test + void shouldProcessSuccessfully() { + doAnswer( + invocation -> { + MessageFrame messageFrame = invocation.getArgument(0); + messageFrame.setOutputData(EXPECTED_OUTPUT); + messageFrame.getMessageFrameStack().pop(); + messageFrame.setState(MessageFrame.State.COMPLETED_SUCCESS); + return null; + }) + .when(mockMessageCallProcessor) + .process(any(), any()); + final MutableWorldState worldState = createWorldState(CALL_ADDRESS); + Bytes actualOutput = processSystemCall(worldState); + assertThat(actualOutput).isEqualTo(EXPECTED_OUTPUT); + } + + @Test + void shouldThrowExceptionOnFailedExecution() { + doAnswer( + invocation -> { + MessageFrame messageFrame = invocation.getArgument(0); + messageFrame.getMessageFrameStack().pop(); + messageFrame.setState(MessageFrame.State.COMPLETED_FAILED); + return null; + }) + .when(mockMessageCallProcessor) + .process(any(), any()); + final MutableWorldState worldState = createWorldState(CALL_ADDRESS); + var exception = assertThrows(RuntimeException.class, () -> processSystemCall(worldState)); + assertThat(exception.getMessage()).isEqualTo("System call did not execute to completion"); + } + + @Test + void shouldReturnNullWhenContractDoesNotExist() { + final MutableWorldState worldState = InMemoryKeyValueStorageProvider.createInMemoryWorldState(); + Bytes actualOutput = processSystemCall(worldState); + assertThat(actualOutput).isNull(); + } + + Bytes processSystemCall(final MutableWorldState worldState) { + SystemCallProcessor systemCallProcessor = new SystemCallProcessor(mockTransactionProcessor); + return systemCallProcessor.process( + CALL_ADDRESS, + worldState.updater(), + mockBlockHeader, + OperationTracer.NO_TRACING, + mockBlockHashLookup); + } + + private MutableWorldState createWorldState(final Address address) { + final MutableWorldState worldState = InMemoryKeyValueStorageProvider.createInMemoryWorldState(); + final WorldUpdater updater = worldState.updater(); + updater.getOrCreate(address); + updater.commit(); + return worldState; + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelperTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelperTest.java deleted file mode 100644 index 7e1e8571350..00000000000 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestContractHelperTest.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * 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; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryWorldStateArchive; -import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper.EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT; -import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper.WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT; -import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper.WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS; -import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper.WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT; -import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper.WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT; - -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.BLSPublicKey; -import org.hyperledger.besu.datatypes.GWei; -import org.hyperledger.besu.ethereum.core.MutableWorldState; -import org.hyperledger.besu.ethereum.core.WithdrawalRequest; -import org.hyperledger.besu.evm.account.MutableAccount; -import org.hyperledger.besu.evm.worldstate.WorldUpdater; - -import java.util.List; -import java.util.stream.IntStream; - -import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.bytes.Bytes32; -import org.apache.tuweni.bytes.Bytes48; -import org.apache.tuweni.units.bigints.UInt256; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class WithdrawalRequestContractHelperTest { - - private MutableWorldState worldState; - private MutableAccount contract; - - @BeforeEach - void setUp() { - worldState = createInMemoryWorldStateArchive().getMutable(); - } - - @Test - void popWithdrawalRequestsFromQueue_ReadWithdrawalRequestsCorrectly() { - final List validatorWithdrawalRequests = - List.of(createExit(), createExit(), createExit()); - loadContractStorage(worldState, validatorWithdrawalRequests); - - final List poppedWithdrawalRequests = - WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(worldState); - - assertThat(poppedWithdrawalRequests).isEqualTo(validatorWithdrawalRequests); - } - - @Test - void - popWithdrawalRequestsFromQueue_whenContractCodeIsEmpty_ReturnsEmptyListOfWithdrawalRequests() { - // Create account with empty code - final WorldUpdater updater = worldState.updater(); - updater.createAccount(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS); - updater.commit(); - - assertThat(WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(worldState)) - .isEmpty(); - } - - @Test - void popWithdrawalRequestsFromQueue_WhenMoreWithdrawalRequests_UpdatesQueuePointers() { - // Loading contract with more than 16 WithdrawalRequests - final List validatorWithdrawalRequests = - IntStream.range(0, 30).mapToObj(__ -> createExit()).toList(); - loadContractStorage(worldState, validatorWithdrawalRequests); - // After loading the contract, the WithdrawalRequests count since last block should match the - // size of the list - assertContractStorageValue( - WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, validatorWithdrawalRequests.size()); - - final List poppedWithdrawalRequests = - WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(worldState); - assertThat(poppedWithdrawalRequests).hasSize(16); - - // Check that queue pointers were updated successfully (head advanced to index 16) - assertContractStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT, 16); - assertContractStorageValue(WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT, 30); - - // We had 30 WithdrawalRequests in the queue, and target per block is 2, so we have 28 excess - assertContractStorageValue(EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT, 28); - - // We always reset the WithdrawalRequests count after processing the queue - assertContractStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, 0); - } - - @Test - void popWithdrawalRequestsFromQueue_WhenNoMoreWithdrawalRequests_ZeroQueuePointers() { - final List withdrawalRequests = - List.of(createExit(), createExit(), createExit()); - loadContractStorage(worldState, withdrawalRequests); - // After loading the contract, the exit count since last block should match the size of the list - assertContractStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, withdrawalRequests.size()); - - final List poppedWithdrawalRequests = - WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(worldState); - assertThat(poppedWithdrawalRequests).hasSize(3); - - // Check that queue pointers were updated successfully (head and tail zero because queue is - // empty) - assertContractStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT, 0); - assertContractStorageValue(WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT, 0); - - // We had 3 WithdrawalRequests in the queue, target per block is 2, so we have 1 excess - assertContractStorageValue(EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT, 1); - - // We always reset the WithdrawalRequests count after processing the queue - assertContractStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, 0); - } - - @Test - void popWithdrawalRequestsFromQueue_WhenNoWithdrawalRequests_DoesNothing() { - // Loading contract with 0 WithdrawalRequests - loadContractStorage(worldState, List.of()); - // After loading storage, we have the WithdrawalRequests count as zero because no - // WithdrawalRequests were added - assertContractStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, 0); - - final List poppedWithdrawalRequests = - WithdrawalRequestContractHelper.popWithdrawalRequestsFromQueue(worldState); - assertThat(poppedWithdrawalRequests).isEmpty(); - - // Check that queue pointers are correct (head and tail are zero) - assertContractStorageValue(WITHDRAWAL_REQUEST_QUEUE_HEAD_STORAGE_SLOT, 0); - assertContractStorageValue(WITHDRAWAL_REQUEST_QUEUE_TAIL_STORAGE_SLOT, 0); - - // We had 0 WithdrawalRequests in the queue, and target per block is 2, so we have 0 excess - assertContractStorageValue(EXCESS_WITHDRAWAL_REQUESTS_STORAGE_SLOT, 0); - - // We always reset the exit count after processing the queue - assertContractStorageValue(WITHDRAWAL_REQUEST_COUNT_STORAGE_SLOT, 0); - } - - private void assertContractStorageValue(final UInt256 slot, final int expectedValue) { - assertContractStorageValue(slot, UInt256.valueOf(expectedValue)); - } - - private void assertContractStorageValue(final UInt256 slot, final UInt256 expectedValue) { - assertThat(worldState.get(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS).getStorageValue(slot)) - .isEqualTo(expectedValue); - } - - private void loadContractStorage( - final MutableWorldState worldState, final List withdrawalRequests) { - final WorldUpdater updater = worldState.updater(); - contract = updater.getOrCreate(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS); - - contract.setCode( - Bytes.fromHexString( - "0x61013680600a5f395ff33373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b36603014156101325760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061013257600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460ed5780604402838201600302600401805490600101805490600101549160601b8160a01c17835260601b8160a01c17826020015260601b906040015260010160a6565b910180921460fe5790600255610109565b90505f6002555f6003555b5f546001546002828201116101205750505f610126565b01600290035b5f555f6001556044025ff35b5f5ffd")); - // excess requests - contract.setStorageValue(UInt256.valueOf(0), UInt256.valueOf(0)); - // requests count - contract.setStorageValue(UInt256.valueOf(1), UInt256.valueOf(withdrawalRequests.size())); - // requests queue head pointer - contract.setStorageValue(UInt256.valueOf(2), UInt256.valueOf(0)); - // requests queue tail pointer - contract.setStorageValue(UInt256.valueOf(3), UInt256.valueOf(withdrawalRequests.size())); - - int offset = 4; - for (int i = 0; i < withdrawalRequests.size(); i++) { - final WithdrawalRequest request = withdrawalRequests.get(i); - // source_account - contract.setStorageValue( - // set account to slot, with 12 bytes padding on the left - UInt256.valueOf(offset++), - UInt256.fromBytes( - Bytes.concatenate( - Bytes.fromHexString("0x000000000000000000000000"), request.getSourceAddress()))); - // validator_pubkey - contract.setStorageValue( - UInt256.valueOf(offset++), UInt256.fromBytes(request.getValidatorPubkey().slice(0, 32))); - contract.setStorageValue( - // set public key to slot, with 16 bytes padding on the right - UInt256.valueOf(offset++), - UInt256.fromBytes( - Bytes.concatenate( - request.getValidatorPubkey().slice(32, 16), - request.getAmount().toBytes(), // 8 bytes for amount - Bytes.fromHexString("0x0000000000000000")))); - } - updater.commit(); - } - - private WithdrawalRequest createExit() { - return new WithdrawalRequest( - Address.extract(Bytes32.random()), BLSPublicKey.wrap(Bytes48.random()), GWei.ONE); - } -} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestValidatorTestFixtures.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestValidatorTestFixtures.java index 7b8b440bb64..e719810c282 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestValidatorTestFixtures.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/WithdrawalRequestValidatorTestFixtures.java @@ -14,7 +14,7 @@ */ package org.hyperledger.besu.ethereum.mainnet; -import static org.hyperledger.besu.ethereum.mainnet.WithdrawalRequestContractHelper.MAX_WITHDRAWAL_REQUESTS_PER_BLOCK; +import static org.hyperledger.besu.ethereum.mainnet.requests.WithdrawalRequestValidator.MAX_WITHDRAWAL_REQUESTS_PER_BLOCK; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.BLSPublicKey; 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 d5239848821..32ffbe10934 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 @@ -42,6 +42,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.mainnet.requests.ProcessRequestContext; import org.hyperledger.besu.ethereum.mainnet.requests.RequestUtil; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.referencetests.BonsaiReferenceTestWorldState; @@ -52,6 +53,7 @@ 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.evm.account.Account; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.log.Log; @@ -511,7 +513,15 @@ static T8nResult runTest( var requestProcessorCoordinator = protocolSpec.getRequestProcessorCoordinator(); if (requestProcessorCoordinator.isPresent()) { var rpc = requestProcessorCoordinator.get(); - Optional> maybeRequests = rpc.process(worldState, receipts); + ProcessRequestContext context = + new ProcessRequestContext( + blockHeader, + worldState, + protocolSpec, + receipts, + new CachingBlockHashLookup(blockHeader, blockchain), + OperationTracer.NO_TRACING); + Optional> maybeRequests = rpc.process(context); Hash requestRoot = BodyValidation.requestsRoot(maybeRequests.orElse(List.of())); resultObject.put("requestsRoot", requestRoot.toHexString()); From d2b42d56d84e41e2be0b77c0ba4c842fba027455 Mon Sep 17 00:00:00 2001 From: Simon Dudley Date: Thu, 4 Jul 2024 21:10:44 +1000 Subject: [PATCH 02/12] Update limit trie logs validation message for sync-mode=FULL (#7279) Signed-off-by: Simon Dudley Co-authored-by: Sally MacFarlane --- .../besu/cli/options/stable/DataStorageOptions.java | 6 ++++-- .../besu/cli/options/stable/DataStorageOptionsTest.java | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java index d1cb4f721b2..69cf819cb4b 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java @@ -147,8 +147,10 @@ public void validate(final CommandLine commandLine, final SyncMode syncMode) { throw new CommandLine.ParameterException( commandLine, String.format( - "Cannot enable " + BONSAI_LIMIT_TRIE_LOGS_ENABLED + " with sync-mode %s", - syncMode)); + "Cannot enable %s with sync-mode %s. You must set %s or use a different sync-mode", + BONSAI_LIMIT_TRIE_LOGS_ENABLED, + SyncMode.FULL, + BONSAI_LIMIT_TRIE_LOGS_ENABLED + "=false")); } if (bonsaiMaxLayersToLoad < MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT) { throw new CommandLine.ParameterException( diff --git a/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java b/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java index 2086381825f..5ab2757f888 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java @@ -55,6 +55,14 @@ public void bonsaiTrieLogsEnabled_explicitlySetToFalse() { "--bonsai-limit-trie-logs-enabled=false"); } + @Test + public void bonsaiTrieLogPruningWindowSizeShouldBePositive2() { + internalTestFailure( + "Cannot enable --bonsai-limit-trie-logs-enabled with sync-mode FULL. You must set --bonsai-limit-trie-logs-enabled=false or use a different sync-mode", + "--sync-mode", + "FULL"); + } + @Test public void bonsaiTrieLogPruningWindowSizeShouldBePositive() { internalTestFailure( From 6673287b177308416f6b93d08b408736258b311c Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Sat, 6 Jul 2024 00:42:47 +1000 Subject: [PATCH 03/12] Execute requests before block persist (#7295) Signed-off-by: Gabriel-Trintinalia --- .../hyperledger/besu/evmtool/T8nExecutor.java | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) 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 32ffbe10934..429ad59444f 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 @@ -468,6 +468,44 @@ static T8nResult runTest( } } + var requestProcessorCoordinator = protocolSpec.getRequestProcessorCoordinator(); + if (requestProcessorCoordinator.isPresent()) { + var rpc = requestProcessorCoordinator.get(); + ProcessRequestContext context = + new ProcessRequestContext( + blockHeader, + worldState, + protocolSpec, + receipts, + new CachingBlockHashLookup(blockHeader, blockchain), + OperationTracer.NO_TRACING); + Optional> maybeRequests = rpc.process(context); + Hash requestRoot = BodyValidation.requestsRoot(maybeRequests.orElse(List.of())); + + resultObject.put("requestsRoot", requestRoot.toHexString()); + var deposits = resultObject.putArray("depositRequests"); + RequestUtil.filterRequestsOfType(maybeRequests.orElse(List.of()), DepositRequest.class) + .forEach( + deposit -> { + var obj = deposits.addObject(); + obj.put("pubkey", deposit.getPubkey().toHexString()); + obj.put("withdrawalCredentials", deposit.getWithdrawalCredentials().toHexString()); + obj.put("amount", deposit.getAmount().toHexString()); + obj.put("signature", deposit.getSignature().toHexString()); + obj.put("index", deposit.getIndex().toHexString()); + }); + + var withdrawlRequests = resultObject.putArray("withdrawalRequests"); + RequestUtil.filterRequestsOfType(maybeRequests.orElse(List.of()), WithdrawalRequest.class) + .forEach( + wr -> { + var obj = withdrawlRequests.addObject(); + obj.put("sourceAddress", wr.getSourceAddress().toHexString()); + obj.put("validatorPubkey", wr.getValidatorPubkey().toHexString()); + obj.put("amount", wr.getAmount().toHexString()); + }); + } + worldState.persist(blockHeader); resultObject.put("stateRoot", worldState.rootHash().toHexString()); @@ -510,44 +548,6 @@ static T8nResult runTest( resultObject.put("blobGasUsed", Bytes.ofUnsignedLong(blobGasUsed).toQuantityHexString()); } - var requestProcessorCoordinator = protocolSpec.getRequestProcessorCoordinator(); - if (requestProcessorCoordinator.isPresent()) { - var rpc = requestProcessorCoordinator.get(); - ProcessRequestContext context = - new ProcessRequestContext( - blockHeader, - worldState, - protocolSpec, - receipts, - new CachingBlockHashLookup(blockHeader, blockchain), - OperationTracer.NO_TRACING); - Optional> maybeRequests = rpc.process(context); - Hash requestRoot = BodyValidation.requestsRoot(maybeRequests.orElse(List.of())); - - resultObject.put("requestsRoot", requestRoot.toHexString()); - var deposits = resultObject.putArray("depositRequests"); - RequestUtil.filterRequestsOfType(maybeRequests.orElse(List.of()), DepositRequest.class) - .forEach( - deposit -> { - var obj = deposits.addObject(); - obj.put("pubkey", deposit.getPubkey().toHexString()); - obj.put("withdrawalCredentials", deposit.getWithdrawalCredentials().toHexString()); - obj.put("amount", deposit.getAmount().toHexString()); - obj.put("signature", deposit.getSignature().toHexString()); - obj.put("index", deposit.getIndex().toHexString()); - }); - - var withdrawlRequests = resultObject.putArray("withdrawalRequests"); - RequestUtil.filterRequestsOfType(maybeRequests.orElse(List.of()), WithdrawalRequest.class) - .forEach( - wr -> { - var obj = withdrawlRequests.addObject(); - obj.put("sourceAddress", wr.getSourceAddress().toHexString()); - obj.put("validatorPubkey", wr.getValidatorPubkey().toHexString()); - obj.put("amount", wr.getAmount().toHexString()); - }); - } - ObjectNode allocObject = objectMapper.createObjectNode(); worldState .streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE) From 33f2ae272619304243b860fc7e5fc07efce26763 Mon Sep 17 00:00:00 2001 From: gringsam Date: Tue, 9 Jul 2024 22:26:51 -0400 Subject: [PATCH 04/12] Fixed outdated tech redirect link. (#7297) * fix wiki link Signed-off-by: Snazzy * fix format Signed-off-by: Snazzy * change knownHash Signed-off-by: Snazzy --------- Signed-off-by: Snazzy --- plugin-api/build.gradle | 2 +- .../java/org/hyperledger/besu/plugin/services/BesuEvents.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index dbbb5e4f2bf..d55efa7fc99 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -70,7 +70,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = 'Q6EK5By3BNKNa/JYqYjFw43VXWL0KVBUV3dGEQBjZ70=' + knownHash = 'yH50m+z1tnzshJQPdwR86pb2EU3m6iZxwkqoy/5spcs=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuEvents.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuEvents.java index ab9c4eb782d..110a66dee54 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuEvents.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/BesuEvents.java @@ -258,7 +258,8 @@ interface LogListener { /** * Invoked for each log (both added and removed) when a new block is added to the blockchain. * - * @param logWithMetadata the log with associated metadata. see https://eth.wiki/json-rpc/API + * @param logWithMetadata the log with associated metadata. see + * https://ethereum.org/en/developers/docs/apis/json-rpc/ */ void onLogEmitted(LogWithMetadata logWithMetadata); } From 5660ebc1cecdd05333dd8675862281a9421a95a6 Mon Sep 17 00:00:00 2001 From: George Tebrean <99179176+gtebrean@users.noreply.github.com> Date: Wed, 10 Jul 2024 06:51:53 +0300 Subject: [PATCH 05/12] Increment private nonce even if transaction failed. (#6593) Increment private nonce even if transaction failed Signed-off-by: George Tebrean Signed-off-by: stefan.pingel@consensys.net Co-authored-by: stefan.pingel@consensys.net Co-authored-by: Stefan Pingel <16143240+pinges@users.noreply.github.com> --- CHANGELOG.md | 1 + .../dsl/node/ProcessBesuNodeRunner.java | 3 + .../node/configuration/BesuNodeFactory.java | 4 +- .../MultiTenancyAcceptanceTest.java | 1 + ...tiTenancyPrivateNonceIncrementingTest.java | 241 ++++++++++++++++++ ...tiTenancyValidationFailAcceptanceTest.java | 1 + .../org/hyperledger/besu/cli/BesuCommand.java | 9 + .../src/test/resources/everything_config.toml | 1 + ...acyPrecompiledContractIntegrationTest.java | 1 + .../besu/ethereum/core/PrivacyParameters.java | 18 ++ .../FlexiblePrivacyPrecompiledContract.java | 7 +- .../privacy/PrivacyPrecompiledContract.java | 29 ++- ...lexiblePrivacyPrecompiledContractTest.java | 6 +- .../PrivacyPrecompiledContractTest.java | 4 +- 14 files changed, 315 insertions(+), 11 deletions(-) create mode 100644 acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyPrivateNonceIncrementingTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d29b8f8af8..1cbfa4cbd3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ - Promote experimental `besu storage x-trie-log` subcommand to production-ready [#7278](https://github.com/hyperledger/besu/pull/7278) - Enhanced BFT round-change diagnostics [#7271](https://github.com/hyperledger/besu/pull/7271) - `--Xsnapsync-bft-enabled` option enables experimental support for snap sync with IBFT/QBFT permissioned Bonsai-DB chains [#7140](https://github.com/hyperledger/besu/pull/7140) +- `privacy-nonce-always-increments` option enables private transactions to always increment the nonce, even if the transaction is invalid [#6593](https://github.com/hyperledger/besu/pull/6593) ### Bug fixes - Validation errors ignored in accounts-allowlist and empty list [#7138](https://github.com/hyperledger/besu/issues/7138) diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java index 583992718aa..37906761d1a 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java @@ -166,6 +166,9 @@ public void startNode(final BesuNode node) { if (node.getPrivacyParameters().isPrivacyPluginEnabled()) { params.add("--Xprivacy-plugin-enabled"); } + if (node.getPrivacyParameters().isPrivateNonceAlwaysIncrementsEnabled()) { + params.add("privacy-nonce-always-increments"); + } } if (!node.getBootnodes().isEmpty()) { diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java index 96ab9cf6235..1ea29388bd5 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java @@ -287,7 +287,8 @@ public BesuNode createNodeWithMultiTenantedPrivacy( final String enclaveUrl, final String authFile, final String privTransactionSigningKey, - final boolean enableFlexiblePrivacy) + final boolean enableFlexiblePrivacy, + final boolean enablePrivateNonceAlwaysIncrements) throws IOException, URISyntaxException { final PrivacyParameters.Builder privacyParametersBuilder = new PrivacyParameters.Builder(); final PrivacyParameters privacyParameters = @@ -298,6 +299,7 @@ public BesuNode createNodeWithMultiTenantedPrivacy( .setStorageProvider(new InMemoryPrivacyStorageProvider()) .setEnclaveFactory(new EnclaveFactory(Vertx.vertx())) .setEnclaveUrl(URI.create(enclaveUrl)) + .setPrivateNonceAlwaysIncrementsEnabled(enablePrivateNonceAlwaysIncrements) .setPrivateKeyPath( Paths.get(ClassLoader.getSystemResource(privTransactionSigningKey).toURI())) .build(); diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java index e2fdd2188b1..b1c175f6461 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java @@ -104,6 +104,7 @@ public void setUp() throws Exception { "http://127.0.0.1:" + wireMockRule.port(), "authentication/auth_priv.toml", "authentication/auth_priv_key", + false, false); multiTenancyCluster.start(node); final String token = diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyPrivateNonceIncrementingTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyPrivateNonceIncrementingTest.java new file mode 100644 index 00000000000..11c405c37a8 --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyPrivateNonceIncrementingTest.java @@ -0,0 +1,241 @@ +/* + * 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.tests.acceptance.privacy.multitenancy; + +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static java.nio.charset.StandardCharsets.UTF_8; + +import org.hyperledger.besu.crypto.KeyPair; +import org.hyperledger.besu.crypto.SignatureAlgorithm; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.enclave.types.PrivacyGroup; +import org.hyperledger.besu.enclave.types.ReceiveResponse; +import org.hyperledger.besu.enclave.types.SendResponse; +import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; +import org.hyperledger.besu.plugin.data.Restriction; +import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; +import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; +import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.Cluster; +import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.ClusterConfiguration; +import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.ClusterConfigurationBuilder; + +import java.math.BigInteger; +import java.util.List; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import org.apache.tuweni.bytes.Bytes; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +public class MultiTenancyPrivateNonceIncrementingTest extends AcceptanceTestBase { + private BesuNode node; + private final ObjectMapper mapper = new ObjectMapper(); + private Cluster multiTenancyCluster; + + private static final Supplier SIGNATURE_ALGORITHM = + Suppliers.memoize(SignatureAlgorithmFactory::getInstance); + private static final KeyPair TEST_KEY = + SIGNATURE_ALGORITHM + .get() + .createKeyPair( + SIGNATURE_ALGORITHM + .get() + .createPrivateKey( + new BigInteger( + "853d7f0010fd86d0d7811c1f9d968ea89a24484a8127b4a483ddf5d2cfec766d", 16))); + private static final String PRIVACY_GROUP_ID = "B1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; + private static final String PARTICIPANT_ENCLAVE_KEY0 = + "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; + private static final String PARTICIPANT_ENCLAVE_KEY1 = + "sgFkVOyFndZe/5SAZJO5UYbrl7pezHetveriBBWWnE8="; + private final Address senderAddress = + Address.wrap(Bytes.fromHexString(accounts.getPrimaryBenefactor().getAddress())); + + @Rule public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort()); + + @Before + public void setUp() throws Exception { + final ClusterConfiguration clusterConfiguration = + new ClusterConfigurationBuilder().awaitPeerDiscovery(false).build(); + multiTenancyCluster = new Cluster(clusterConfiguration, net); + node = + besu.createNodeWithMultiTenantedPrivacy( + "node1", + "http://127.0.0.1:" + wireMockRule.port(), + "authentication/auth_priv.toml", + "authentication/auth_priv_key", + false, + true); + multiTenancyCluster.start(node); + final String token = + node.execute(permissioningTransactions.createSuccessfulLogin("user", "pegasys")); + node.useAuthenticationTokenInHeaderForJsonRpc(token); + } + + @After + public void tearDown() { + multiTenancyCluster.close(); + } + + @Test + public void validateUnsuccessfulPrivateTransactionsNonceIncrementation() + throws JsonProcessingException { + executePrivateFailingTransaction(0, 0, 1); + executePrivateValidTransaction(1, 1, 2); + executePrivateFailingTransaction(2, 2, 3); + executePrivateFailingTransaction(3, 3, 4); + executePrivateValidTransaction(4, 4, 5); + } + + private void executePrivateValidTransaction( + final int nonce, + final int expectedTransactionCountBeforeExecution, + final int expectedTransactionCountAfterExecution) + throws JsonProcessingException { + final PrivateTransaction validSignedPrivateTransaction = + getValidSignedPrivateTransaction(senderAddress, nonce); + + final String accountAddress = validSignedPrivateTransaction.getSender().toHexString(); + final BytesValueRLPOutput rlpOutput = getRLPOutput(validSignedPrivateTransaction); + + processEnclaveStub(validSignedPrivateTransaction); + + node.verify( + priv.getTransactionCount( + accountAddress, PRIVACY_GROUP_ID, expectedTransactionCountBeforeExecution)); + + final Hash transactionReceipt = + node.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString())); + + node.verify(priv.getSuccessfulTransactionReceipt(transactionReceipt)); + node.verify( + priv.getTransactionCount( + accountAddress, PRIVACY_GROUP_ID, expectedTransactionCountAfterExecution)); + } + + private void executePrivateFailingTransaction( + final int nonce, + final int expectedTransactionCountBeforeExecution, + final int expectedTransactionCountAfterExecution) + throws JsonProcessingException { + final PrivateTransaction invalidSignedPrivateTransaction = + getInvalidSignedPrivateTransaction(senderAddress, nonce); + final String accountAddress = invalidSignedPrivateTransaction.getSender().toHexString(); + final BytesValueRLPOutput invalidTxRlp = getRLPOutput(invalidSignedPrivateTransaction); + + processEnclaveStub(invalidSignedPrivateTransaction); + + node.verify( + priv.getTransactionCount( + accountAddress, PRIVACY_GROUP_ID, expectedTransactionCountBeforeExecution)); + final Hash invalidTransactionReceipt = + node.execute(privacyTransactions.sendRawTransaction(invalidTxRlp.encoded().toHexString())); + + node.verify(priv.getFailedTransactionReceipt(invalidTransactionReceipt)); + node.verify( + priv.getTransactionCount( + accountAddress, PRIVACY_GROUP_ID, expectedTransactionCountAfterExecution)); + } + + private void processEnclaveStub(final PrivateTransaction validSignedPrivateTransaction) + throws JsonProcessingException { + retrievePrivacyGroupEnclaveStub(); + sendEnclaveStub(); + receiveEnclaveStub(validSignedPrivateTransaction); + } + + private void retrievePrivacyGroupEnclaveStub() throws JsonProcessingException { + final String retrieveGroupResponse = + mapper.writeValueAsString( + createPrivacyGroup( + List.of(PARTICIPANT_ENCLAVE_KEY0, PARTICIPANT_ENCLAVE_KEY1), + PrivacyGroup.Type.PANTHEON)); + stubFor(post("/retrievePrivacyGroup").willReturn(ok(retrieveGroupResponse))); + } + + private void sendEnclaveStub() throws JsonProcessingException { + final String sendResponse = + mapper.writeValueAsString(new SendResponse(PARTICIPANT_ENCLAVE_KEY1)); + stubFor(post("/send").willReturn(ok(sendResponse))); + } + + private void receiveEnclaveStub(final PrivateTransaction privTx) throws JsonProcessingException { + final BytesValueRLPOutput rlpOutput = getRLPOutput(privTx); + final String senderKey = privTx.getPrivateFrom().toBase64String(); + final String receiveResponse = + mapper.writeValueAsString( + new ReceiveResponse( + rlpOutput.encoded().toBase64String().getBytes(UTF_8), PRIVACY_GROUP_ID, senderKey)); + stubFor(post("/receive").willReturn(ok(receiveResponse))); + } + + private BytesValueRLPOutput getRLPOutput(final PrivateTransaction privateTransaction) { + final BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput(); + privateTransaction.writeTo(bvrlpo); + return bvrlpo; + } + + private PrivacyGroup createPrivacyGroup( + final List groupMembers, final PrivacyGroup.Type groupType) { + return new PrivacyGroup(PRIVACY_GROUP_ID, groupType, "test", "testGroup", groupMembers); + } + + private static PrivateTransaction getInvalidSignedPrivateTransaction( + final Address senderAddress, final int nonce) { + return PrivateTransaction.builder() + .nonce(nonce) + .gasPrice(Wei.ZERO) + .gasLimit(3000000) + .to(null) + .value(Wei.ZERO) + .payload(Bytes.fromHexString("0x1234")) + .sender(senderAddress) + .chainId(BigInteger.valueOf(1337)) + .privateFrom(Bytes.fromBase64String(PARTICIPANT_ENCLAVE_KEY0)) + .restriction(Restriction.RESTRICTED) + .privacyGroupId(Bytes.fromBase64String(PRIVACY_GROUP_ID)) + .signAndBuild(TEST_KEY); + } + + private static PrivateTransaction getValidSignedPrivateTransaction( + final Address senderAddress, final int nonce) { + return PrivateTransaction.builder() + .nonce(nonce) + .gasPrice(Wei.ZERO) + .gasLimit(3000000) + .to(null) + .value(Wei.ZERO) + .payload(Bytes.wrap(new byte[] {})) + .sender(senderAddress) + .chainId(BigInteger.valueOf(1337)) + .privateFrom(Bytes.fromBase64String(PARTICIPANT_ENCLAVE_KEY0)) + .restriction(Restriction.RESTRICTED) + .privacyGroupId(Bytes.fromBase64String(PRIVACY_GROUP_ID)) + .signAndBuild(TEST_KEY); + } +} diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyValidationFailAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyValidationFailAcceptanceTest.java index 84e6fa7ea3e..28bf140bb36 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyValidationFailAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyValidationFailAcceptanceTest.java @@ -78,6 +78,7 @@ public void setUp() throws Exception { "http://127.0.0.1:" + wireMockRule.port(), "authentication/auth_priv.toml", "authentication/auth_priv_key", + false, false); multiTenancyCluster.start(node); diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 12752f2e9a1..8b123e70d36 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -715,6 +715,13 @@ static class PrivacyOptionGroup { names = {"--privacy-flexible-groups-enabled"}, description = "Enable flexible privacy groups (default: ${DEFAULT-VALUE})") private final Boolean isFlexiblePrivacyGroupsEnabled = false; + + @Option( + names = {"--privacy-nonce-always-increments"}, + description = + "Enable private nonce " + + "incrementation even if the transaction didn't succeeded (default: ${DEFAULT-VALUE})") + private final Boolean isPrivateNonceAlwaysIncrementsEnabled = false; } // Metrics Option Group @@ -2062,6 +2069,8 @@ private PrivacyParameters privacyParameters() { privacyOptionGroup.isFlexiblePrivacyGroupsEnabled); privacyParametersBuilder.setPrivacyPluginEnabled( unstablePrivacyPluginOptions.isPrivacyPluginEnabled()); + privacyParametersBuilder.setPrivateNonceAlwaysIncrementsEnabled( + privacyOptionGroup.isPrivateNonceAlwaysIncrementsEnabled); final boolean hasPrivacyPublicKey = privacyOptionGroup.privacyPublicKeyFile != null; diff --git a/besu/src/test/resources/everything_config.toml b/besu/src/test/resources/everything_config.toml index d5cc291237a..a5e03bd5990 100644 --- a/besu/src/test/resources/everything_config.toml +++ b/besu/src/test/resources/everything_config.toml @@ -172,6 +172,7 @@ privacy-multi-tenancy-enabled=true privacy-marker-transaction-signing-key-file="./signerKey" privacy-enable-database-migration=false privacy-flexible-groups-enabled=false +privacy-nonce-always-increments=false # Transaction Pool tx-pool="layered" diff --git a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java index 090c24e26e2..a5ee5f068ee 100644 --- a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java +++ b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java @@ -205,6 +205,7 @@ public void testSendAndReceive() { new PrivateStateRootResolver(privateStateStorage), new PrivateStateGenesisAllocator( false, (privacyGroupId, blockNumber) -> Collections::emptyList), + false, "IntegrationTest"); privacyPrecompiledContract.setPrivateTransactionProcessor(mockPrivateTxProcessor()); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java index 349ff8ffbee..775634a41a1 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/PrivacyParameters.java @@ -77,6 +77,7 @@ public class PrivacyParameters { private PrivateStateRootResolver privateStateRootResolver; private PrivateWorldStateReader privateWorldStateReader; private PrivacyPluginService privacyPluginService; + private boolean privateNonceAlwaysIncrementsEnabled; public Address getPrivacyAddress() { if (isPrivacyPluginEnabled()) { @@ -228,6 +229,15 @@ private PrivacyGroupGenesisProvider createPrivateGenesisProvider() { } } + public boolean isPrivateNonceAlwaysIncrementsEnabled() { + return privateNonceAlwaysIncrementsEnabled; + } + + public void setPrivateNonceAlwaysIncrementsEnabled( + final boolean privateNonceAlwaysIncrementsEnabled) { + this.privateNonceAlwaysIncrementsEnabled = privateNonceAlwaysIncrementsEnabled; + } + @Override public String toString() { return "PrivacyParameters{" @@ -263,6 +273,7 @@ public static class Builder { private boolean flexiblePrivacyGroupsEnabled; private boolean privacyPluginEnabled; private PrivacyPluginService privacyPluginService; + private boolean privateNonceAlwaysIncrementsEnabled; public Builder setEnclaveUrl(final URI enclaveUrl) { this.enclaveUrl = enclaveUrl; @@ -314,6 +325,12 @@ public Builder setFlexiblePrivacyGroupsEnabled(final boolean flexiblePrivacyGrou return this; } + public Builder setPrivateNonceAlwaysIncrementsEnabled( + final boolean isPrivateNonceAlwaysIncrementsEnabled) { + this.privateNonceAlwaysIncrementsEnabled = isPrivateNonceAlwaysIncrementsEnabled; + return this; + } + public Builder setPrivacyPluginEnabled(final boolean privacyPluginEnabled) { this.privacyPluginEnabled = privacyPluginEnabled; return this; @@ -382,6 +399,7 @@ public PrivacyParameters build() { config.setMultiTenancyEnabled(multiTenancyEnabled); config.setFlexiblePrivacyGroupsEnabled(flexiblePrivacyGroupsEnabled); config.setPrivacyPluginEnabled(privacyPluginEnabled); + config.setPrivateNonceAlwaysIncrementsEnabled(privateNonceAlwaysIncrementsEnabled); return config; } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/FlexiblePrivacyPrecompiledContract.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/FlexiblePrivacyPrecompiledContract.java index 0dd6863f94e..98709a4eaad 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/FlexiblePrivacyPrecompiledContract.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/FlexiblePrivacyPrecompiledContract.java @@ -70,13 +70,15 @@ public FlexiblePrivacyPrecompiledContract( final Enclave enclave, final WorldStateArchive worldStateArchive, final PrivateStateRootResolver privateStateRootResolver, - final PrivateStateGenesisAllocator privateStateGenesisAllocator) { + final PrivateStateGenesisAllocator privateStateGenesisAllocator, + final boolean alwaysIncrementPrivateNonce) { super( gasCalculator, enclave, worldStateArchive, privateStateRootResolver, privateStateGenesisAllocator, + alwaysIncrementPrivateNonce, "FlexiblePrivacy"); } @@ -87,7 +89,8 @@ public FlexiblePrivacyPrecompiledContract( privacyParameters.getEnclave(), privacyParameters.getPrivateWorldStateArchive(), privacyParameters.getPrivateStateRootResolver(), - privacyParameters.getPrivateStateGenesisAllocator()); + privacyParameters.getPrivateStateGenesisAllocator(), + privacyParameters.isPrivateNonceAlwaysIncrementsEnabled()); } public long addPrivateTransactionObserver(final PrivateTransactionObserver observer) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java index b1a754c9fe2..7e9bfa1ac4b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java @@ -19,6 +19,7 @@ import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION_HASH; import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH; +import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.enclave.Enclave; import org.hyperledger.besu.enclave.EnclaveClientException; @@ -40,6 +41,7 @@ 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.MutableAccount; import org.hyperledger.besu.evm.frame.BlockValues; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -61,6 +63,7 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract { final WorldStateArchive privateWorldStateArchive; final PrivateStateRootResolver privateStateRootResolver; private final PrivateStateGenesisAllocator privateStateGenesisAllocator; + final boolean alwaysIncrementPrivateNonce; PrivateTransactionProcessor privateTransactionProcessor; private static final Logger LOG = LoggerFactory.getLogger(PrivacyPrecompiledContract.class); @@ -79,6 +82,7 @@ public PrivacyPrecompiledContract( privacyParameters.getPrivateWorldStateArchive(), privacyParameters.getPrivateStateRootResolver(), privacyParameters.getPrivateStateGenesisAllocator(), + privacyParameters.isPrivateNonceAlwaysIncrementsEnabled(), name); } @@ -88,12 +92,14 @@ protected PrivacyPrecompiledContract( final WorldStateArchive worldStateArchive, final PrivateStateRootResolver privateStateRootResolver, final PrivateStateGenesisAllocator privateStateGenesisAllocator, + final boolean alwaysIncrementPrivateNonce, final String name) { super(name, gasCalculator); this.enclave = enclave; this.privateWorldStateArchive = worldStateArchive; this.privateStateRootResolver = privateStateRootResolver; this.privateStateGenesisAllocator = privateStateGenesisAllocator; + this.alwaysIncrementPrivateNonce = alwaysIncrementPrivateNonce; } public void setPrivateTransactionProcessor( @@ -181,18 +187,31 @@ public PrecompileContractResult computePrecompile( processPrivateTransaction( messageFrame, privateTransaction, privacyGroupId, privateWorldStateUpdater); - if (result.isInvalid() || !result.isSuccessful()) { + final Boolean isPersistingPrivateState = + messageFrame.getContextVariable(KEY_IS_PERSISTING_PRIVATE_STATE, false); + + if (!result.isSuccessful()) { LOG.error( "Failed to process private transaction {}: {}", pmtHash, result.getValidationResult().getErrorMessage()); - - privateMetadataUpdater.putTransactionReceipt(pmtHash, new PrivateTransactionReceipt(result)); - + if (isPersistingPrivateState && alwaysIncrementPrivateNonce) { + final Address senderAddress = privateTransaction.getSender(); + final MutableAccount senderAccount = privateWorldStateUpdater.getOrCreate(senderAddress); + senderAccount.incrementNonce(); + // we can safely commit the updater here, because it is only changed if the transaction is + // successful, + // so we can be sure that the only change is the incremented nonce + privateWorldStateUpdater.commit(); + disposablePrivateState.persist(null); + + storePrivateMetadata( + pmtHash, privacyGroupId, disposablePrivateState, privateMetadataUpdater, result); + } return NO_RESULT; } - if (messageFrame.getContextVariable(KEY_IS_PERSISTING_PRIVATE_STATE, false)) { + if (isPersistingPrivateState) { privateWorldStateUpdater.commit(); disposablePrivateState.persist(null); 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 3c4711a194a..fc08f519d3d 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 @@ -383,7 +383,8 @@ public void testInvalidPrivateTransaction() { enclave, worldStateArchive, privateStateRootResolver, - privateStateGenesisAllocator); + privateStateGenesisAllocator, + false); contract.setPrivateTransactionProcessor( mockPrivateTxProcessor( @@ -427,6 +428,7 @@ private FlexiblePrivacyPrecompiledContract buildPrivacyPrecompiledContract( enclave, worldStateArchive, privateStateRootResolver, - privateStateGenesisAllocator); + privateStateGenesisAllocator, + false); } } 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 134cfa9eac6..7d253057ac7 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 @@ -302,6 +302,7 @@ public void testInvalidPrivateTransaction() { worldStateArchive, privateStateRootResolver, privateStateGenesisAllocator, + false, "RestrictedPrivacyTest"); contract.setPrivateTransactionProcessor( @@ -328,7 +329,7 @@ public void testInvalidPrivateTransaction() { @Test public void testSimulatedPublicTransactionIsSkipped() { final PrivacyPrecompiledContract emptyContract = - new PrivacyPrecompiledContract(null, null, null, null, null, null); + new PrivacyPrecompiledContract(null, null, null, null, null, false, null); // A simulated public transaction doesn't contain a PrivateMetadataUpdater final MessageFrame frame = mock(MessageFrame.class); @@ -355,6 +356,7 @@ private PrivacyPrecompiledContract buildPrivacyPrecompiledContract(final Enclave worldStateArchive, privateStateRootResolver, privateStateGenesisAllocator, + false, "PrivacyTests"); } } From ae7ddd1c9adb750026d5314554e8366fda8a97a0 Mon Sep 17 00:00:00 2001 From: Usman Saleem Date: Wed, 10 Jul 2024 14:20:51 +1000 Subject: [PATCH 06/12] feat: Enhance --profile to load external profiles (#7292) * feat: --profile can load external profiles * fix external profile name method * fix ProfilesCompletionCandidate * test: Add unit tests * changelog: Update changelog * test: Fix TomlConfigurationDefaultProviderTest * test: Fix BesuCommandTest --------- Signed-off-by: Usman Saleem --- CHANGELOG.md | 1 + .../org/hyperledger/besu/cli/BesuCommand.java | 7 +- ...fileName.java => InternalProfileName.java} | 38 +++++++- .../config/ProfilesCompletionCandidates.java | 37 +++++++ .../subcommands/ValidateConfigSubCommand.java | 2 +- .../besu/cli/util/ProfileFinder.java | 94 +++++++++++++++--- .../TomlConfigurationDefaultProvider.java | 19 ++-- .../hyperledger/besu/cli/BesuCommandTest.java | 2 +- .../cli/ConfigurationOverviewBuilderTest.java | 4 +- .../hyperledger/besu/cli/ProfilesTest.java | 97 +++++++++++++++++-- .../TomlConfigurationDefaultProviderTest.java | 2 +- .../ProfilesCompletionCandidatesTest.java | 91 +++++++++++++++++ 12 files changed, 355 insertions(+), 39 deletions(-) rename besu/src/main/java/org/hyperledger/besu/cli/config/{ProfileName.java => InternalProfileName.java} (59%) create mode 100644 besu/src/main/java/org/hyperledger/besu/cli/config/ProfilesCompletionCandidates.java create mode 100644 besu/src/test/java/org/hyperledger/besu/cli/config/ProfilesCompletionCandidatesTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cbfa4cbd3a..1306de83dd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Breaking Changes ### Additions and Improvements +- Add support to load external profiles using `--profile` [#7265](https://github.com/hyperledger/besu/issues/7265) ### Bug fixes diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 8b123e70d36..427cac92480 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -39,7 +39,7 @@ import org.hyperledger.besu.chainimport.RlpBlockImporter; import org.hyperledger.besu.cli.config.EthNetworkConfig; import org.hyperledger.besu.cli.config.NetworkName; -import org.hyperledger.besu.cli.config.ProfileName; +import org.hyperledger.besu.cli.config.ProfilesCompletionCandidates; import org.hyperledger.besu.cli.converter.MetricCategoryConverter; import org.hyperledger.besu.cli.converter.PercentageConverter; import org.hyperledger.besu.cli.converter.SubnetInfoConverter; @@ -565,9 +565,10 @@ private InetAddress autoDiscoverDefaultIP() { @Option( names = {PROFILE_OPTION_NAME}, paramLabel = PROFILE_FORMAT_HELP, + completionCandidates = ProfilesCompletionCandidates.class, description = "Overwrite default settings. Possible values are ${COMPLETION-CANDIDATES}. (default: none)") - private final ProfileName profile = null; + private String profile = null; // don't set it as final due to picocli completion candidates @Option( names = {"--nat-method"}, @@ -2773,7 +2774,7 @@ private String generateConfigurationOverview() { } if (profile != null) { - builder.setProfile(profile.toString()); + builder.setProfile(profile); } builder.setHasCustomGenesis(genesisFile != null); diff --git a/besu/src/main/java/org/hyperledger/besu/cli/config/ProfileName.java b/besu/src/main/java/org/hyperledger/besu/cli/config/InternalProfileName.java similarity index 59% rename from besu/src/main/java/org/hyperledger/besu/cli/config/ProfileName.java rename to besu/src/main/java/org/hyperledger/besu/cli/config/InternalProfileName.java index 8c17dfb4f2c..efac7a52032 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/config/ProfileName.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/config/InternalProfileName.java @@ -14,10 +14,18 @@ */ package org.hyperledger.besu.cli.config; +import java.util.Arrays; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + import org.apache.commons.lang3.StringUtils; -/** Enum for profile names. Each profile corresponds to a configuration file. */ -public enum ProfileName { +/** + * Enum for profile names which are bundled. Each profile corresponds to a bundled configuration + * file. + */ +public enum InternalProfileName { /** The 'STAKER' profile */ STAKER("profiles/staker.toml"), /** The 'MINIMALIST_STAKER' profile */ @@ -31,12 +39,36 @@ public enum ProfileName { private final String configFile; + /** + * Returns the InternalProfileName that matches the given name, ignoring case. + * + * @param name The profile name + * @return Optional InternalProfileName if found, otherwise empty + */ + public static Optional valueOfIgnoreCase(final String name) { + return Arrays.stream(values()) + .filter(profile -> profile.name().equalsIgnoreCase(name)) + .findFirst(); + } + + /** + * Returns the set of internal profile names as lowercase. + * + * @return Set of internal profile names + */ + public static Set getInternalProfileNames() { + return Arrays.stream(InternalProfileName.values()) + .map(InternalProfileName::name) + .map(String::toLowerCase) + .collect(Collectors.toSet()); + } + /** * Constructs a new ProfileName. * * @param configFile the configuration file corresponding to the profile */ - ProfileName(final String configFile) { + InternalProfileName(final String configFile) { this.configFile = configFile; } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/config/ProfilesCompletionCandidates.java b/besu/src/main/java/org/hyperledger/besu/cli/config/ProfilesCompletionCandidates.java new file mode 100644 index 00000000000..51e9c49a0ab --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/cli/config/ProfilesCompletionCandidates.java @@ -0,0 +1,37 @@ +/* + * 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.cli.config; + +import org.hyperledger.besu.cli.util.ProfileFinder; + +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; + +/** Provides a list of profile names that can be used for command line completion. */ +public class ProfilesCompletionCandidates implements Iterable { + /** + * Create a new instance of ProfilesCompletionCandidates. This constructor is required for + * Picocli. + */ + public ProfilesCompletionCandidates() {} + + @Override + public Iterator iterator() { + final Set profileNames = new TreeSet<>(InternalProfileName.getInternalProfileNames()); + profileNames.addAll(ProfileFinder.getExternalProfileNames()); + return profileNames.iterator(); + } +} diff --git a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/ValidateConfigSubCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/ValidateConfigSubCommand.java index f17f1a09fc6..12500748ce7 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/ValidateConfigSubCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/ValidateConfigSubCommand.java @@ -70,7 +70,7 @@ public void run() { checkNotNull(parentCommand); try { TomlConfigurationDefaultProvider.fromFile(commandLine, dataPath.toFile()) - .loadConfigurationFromFile(); + .loadConfigurationIfNotLoaded(); } catch (Exception e) { this.out.println(e); return; diff --git a/besu/src/main/java/org/hyperledger/besu/cli/util/ProfileFinder.java b/besu/src/main/java/org/hyperledger/besu/cli/util/ProfileFinder.java index edb94f667af..ba35f6b69db 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/util/ProfileFinder.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/util/ProfileFinder.java @@ -16,11 +16,19 @@ import static org.hyperledger.besu.cli.DefaultCommandValues.PROFILE_OPTION_NAME; -import org.hyperledger.besu.cli.config.ProfileName; +import org.hyperledger.besu.cli.config.InternalProfileName; +import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import picocli.CommandLine; @@ -50,30 +58,94 @@ protected String getConfigEnvName() { @Override public Optional getFromOption( final CommandLine.ParseResult parseResult, final CommandLine commandLine) { + final String profileName; try { - return getProfile(parseResult.matchedOption(PROFILE_OPTION_NAME).getter().get(), commandLine); - } catch (Exception e) { - throw new RuntimeException(e); + profileName = parseResult.matchedOption(PROFILE_OPTION_NAME).getter().get(); + } catch (final Exception e) { + throw new CommandLine.ParameterException( + commandLine, "Unexpected error in obtaining value of --profile", e); } + return getProfile(profileName, commandLine); } @Override public Optional getFromEnvironment( final Map environment, final CommandLine commandLine) { - return getProfile(ProfileName.valueOf(environment.get(PROFILE_ENV_NAME)), commandLine); + return getProfile(environment.get(PROFILE_ENV_NAME), commandLine); } private static Optional getProfile( - final ProfileName profileName, final CommandLine commandLine) { - return Optional.of(getTomlFile(commandLine, profileName.getConfigFile())); + final String profileName, final CommandLine commandLine) { + final Optional internalProfileConfigPath = + InternalProfileName.valueOfIgnoreCase(profileName).map(InternalProfileName::getConfigFile); + if (internalProfileConfigPath.isPresent()) { + return Optional.of(getTomlFileFromClasspath(internalProfileConfigPath.get())); + } else { + final Path externalProfileFile = defaultProfilesDir().resolve(profileName + ".toml"); + if (Files.exists(externalProfileFile)) { + try { + return Optional.of(Files.newInputStream(externalProfileFile)); + } catch (IOException e) { + throw new CommandLine.ParameterException( + commandLine, "Error reading external profile: " + profileName); + } + } else { + throw new CommandLine.ParameterException( + commandLine, "Unable to load external profile: " + profileName); + } + } } - private static InputStream getTomlFile(final CommandLine commandLine, final String file) { - InputStream resourceUrl = ProfileFinder.class.getClassLoader().getResourceAsStream(file); + private static InputStream getTomlFileFromClasspath(final String profileConfigFile) { + InputStream resourceUrl = + ProfileFinder.class.getClassLoader().getResourceAsStream(profileConfigFile); + // this is not meant to happen, because for each InternalProfileName there is a corresponding + // TOML file in resources if (resourceUrl == null) { - throw new CommandLine.ParameterException( - commandLine, String.format("TOML file %s not found", file)); + throw new IllegalStateException( + String.format("Internal Profile TOML %s not found", profileConfigFile)); } return resourceUrl; } + + /** + * Returns the external profile names which are file names without extension in the default + * profiles directory. + * + * @return Set of external profile names + */ + public static Set getExternalProfileNames() { + final Path profilesDir = defaultProfilesDir(); + if (!Files.exists(profilesDir)) { + return Set.of(); + } + + try (Stream pathStream = Files.list(profilesDir)) { + return pathStream + .filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".toml")) + .map( + path -> + path.getFileName() + .toString() + .substring(0, path.getFileName().toString().length() - 5)) + .collect(Collectors.toSet()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Return default profiles directory location + * + * @return Path to default profiles directory + */ + private static Path defaultProfilesDir() { + final String profilesDir = System.getProperty("besu.profiles.dir"); + if (profilesDir == null) { + return Paths.get(System.getProperty("besu.home", "."), "profiles"); + } else { + return Paths.get(profilesDir); + } + } } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/util/TomlConfigurationDefaultProvider.java b/besu/src/main/java/org/hyperledger/besu/cli/util/TomlConfigurationDefaultProvider.java index 1747f461dc4..ca76b40b686 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/util/TomlConfigurationDefaultProvider.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/util/TomlConfigurationDefaultProvider.java @@ -96,7 +96,7 @@ public static TomlConfigurationDefaultProvider fromInputStream( @Override public String defaultValue(final ArgSpec argSpec) { - loadConfigurationFromFile(); + loadConfigurationIfNotLoaded(); // only options can be used in config because a name is needed for the key // so we skip default for positional params @@ -227,10 +227,10 @@ private String getNumericEntryAsString(final OptionSpec spec) { } private void checkConfigurationValidity() { - if (result == null || result.isEmpty()) + if (result == null || result.isEmpty()) { throw new ParameterException( - commandLine, - String.format("Unable to read TOML configuration file %s", configurationInputStream)); + commandLine, "Unable to read from empty TOML configuration file."); + } if (!isUnknownOptionsChecked && !commandLine.isUnmatchedArgumentsAllowed()) { checkUnknownOptions(result); @@ -239,8 +239,7 @@ private void checkConfigurationValidity() { } /** Load configuration from file. */ - public void loadConfigurationFromFile() { - + public void loadConfigurationIfNotLoaded() { if (result == null) { try { final TomlParseResult result = Toml.parse(configurationInputStream); @@ -289,12 +288,12 @@ private void checkUnknownOptions(final TomlParseResult result) { .collect(Collectors.toSet()); if (!unknownOptionsList.isEmpty()) { - final String options = unknownOptionsList.size() > 1 ? "options" : "option"; - final String csvUnknownOptions = - unknownOptionsList.stream().collect(Collectors.joining(", ")); + final String csvUnknownOptions = String.join(", ", unknownOptionsList); throw new ParameterException( commandLine, - String.format("Unknown %s in TOML configuration file: %s", options, csvUnknownOptions)); + String.format( + "Unknown option%s in TOML configuration file: %s", + unknownOptionsList.size() > 1 ? "s" : "", csvUnknownOptions)); } } } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index 641e69dba64..c6ffbdc1759 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -259,7 +259,7 @@ public void callingWithConfigOptionButNonExistingFileShouldDisplayHelp() throws final Path tempConfigFilePath = createTempFile("an-invalid-file-name-without-extension", ""); parseCommand("--config-file", tempConfigFilePath.toString()); - final String expectedOutputStart = "Unable to read TOML configuration file"; + final String expectedOutputStart = "Unable to read from empty TOML configuration file."; assertThat(commandErrorOutput.toString(UTF_8)).startsWith(expectedOutputStart); assertThat(commandOutput.toString(UTF_8)).isEmpty(); } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java b/besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java index 07e440430a5..841c6680007 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/ConfigurationOverviewBuilderTest.java @@ -20,7 +20,7 @@ import static org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration.Implementation.SEQUENCED; import static org.mockito.Mockito.mock; -import org.hyperledger.besu.cli.config.ProfileName; +import org.hyperledger.besu.cli.config.InternalProfileName; import org.hyperledger.besu.evm.internal.EvmConfiguration; import java.math.BigInteger; @@ -213,7 +213,7 @@ void setWorldStateUpdateModeJournaled() { @Test void setProfile() { - builder.setProfile(ProfileName.DEV.name()); + builder.setProfile(InternalProfileName.DEV.name()); final String profileSelected = builder.build(); assertThat(profileSelected).contains("Profile: DEV"); } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/ProfilesTest.java b/besu/src/test/java/org/hyperledger/besu/cli/ProfilesTest.java index cdfe09964ff..019ff7a7ed2 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/ProfilesTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/ProfilesTest.java @@ -17,21 +17,104 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; -import org.hyperledger.besu.cli.config.ProfileName; +import org.hyperledger.besu.cli.config.InternalProfileName; +import org.hyperledger.besu.cli.util.ProfileFinder; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; public class ProfilesTest extends CommandTestAbstract { + @TempDir private static Path tempProfilesDir; + private static String originalProfilesDirProperty; - /** Test if besu will validate the combination of options within the given profile. */ - @ParameterizedTest - @EnumSource(ProfileName.class) - public void testProfileWithNoOverrides_doesNotError(final ProfileName profileName) { + @BeforeAll + public static void copyExternalProfiles() throws IOException { + for (String internalProfileName : InternalProfileName.getInternalProfileNames()) { + final Path profilePath = tempProfilesDir.resolve(internalProfileName + "_external.toml"); + + String profileConfigFile = + InternalProfileName.valueOfIgnoreCase(internalProfileName).get().getConfigFile(); + try (InputStream resourceUrl = + ProfileFinder.class.getClassLoader().getResourceAsStream(profileConfigFile)) { + if (resourceUrl != null) { + Files.copy(resourceUrl, profilePath); + } + } + } + + // add an empty external profile + Files.createFile(tempProfilesDir.resolve("empty_external.toml")); + } - parseCommand("--profile", profileName.name()); + @BeforeAll + public static void setupSystemProperty() { + originalProfilesDirProperty = System.getProperty("besu.profiles.dir"); + // sets the system property for the test + System.setProperty("besu.profiles.dir", tempProfilesDir.toString()); + } + + static Stream profileNameProvider() { + final Set profileNames = new TreeSet<>(InternalProfileName.getInternalProfileNames()); + final Set externalProfileNames = + InternalProfileName.getInternalProfileNames().stream() + .map(name -> name + "_external") + .collect(Collectors.toSet()); + profileNames.addAll(externalProfileNames); + return profileNames.stream().map(Arguments::of); + } + + /** Test if besu will validate the combination of options within the given profile. */ + @ParameterizedTest(name = "{index} - Profile Name override: {0}") + @DisplayName("Valid Profile with overrides does not error") + @MethodSource("profileNameProvider") + public void testProfileWithNoOverrides_doesNotError(final String profileName) { + parseCommand("--profile", profileName); assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); } + + @Test + @DisplayName("Empty external profile file results in error") + public void emptyProfileFile_ShouldResultInError() { + parseCommand("--profile", "empty_external"); + + assertThat(commandOutput.toString(UTF_8)).isEmpty(); + assertThat(commandErrorOutput.toString(UTF_8)) + .contains("Unable to read from empty TOML configuration file."); + } + + @Test + @DisplayName("Non Existing profile results in error") + public void nonExistentProfileFile_ShouldResultInError() { + parseCommand("--profile", "non_existent_profile"); + + assertThat(commandOutput.toString(UTF_8)).isEmpty(); + assertThat(commandErrorOutput.toString(UTF_8)) + .contains("Unable to load external profile: non_existent_profile"); + } + + @AfterAll + public static void clearSystemProperty() { + if (originalProfilesDirProperty != null) { + System.setProperty("besu.profiles.dir", originalProfilesDirProperty); + } else { + System.clearProperty("besu.profiles.dir"); + } + } } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/TomlConfigurationDefaultProviderTest.java b/besu/src/test/java/org/hyperledger/besu/cli/TomlConfigurationDefaultProviderTest.java index 0b47654f58c..983e92cec17 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/TomlConfigurationDefaultProviderTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/TomlConfigurationDefaultProviderTest.java @@ -241,7 +241,7 @@ public void invalidConfigMustThrow(final @TempDir Path temp) throws IOException providerUnderTest.defaultValue( OptionSpec.builder("an-option").type(String.class).build())) .isInstanceOf(ParameterException.class) - .hasMessageContaining("Unable to read TOML configuration file"); + .hasMessageContaining("Unable to read from empty TOML configuration file."); } @Test diff --git a/besu/src/test/java/org/hyperledger/besu/cli/config/ProfilesCompletionCandidatesTest.java b/besu/src/test/java/org/hyperledger/besu/cli/config/ProfilesCompletionCandidatesTest.java new file mode 100644 index 00000000000..69cbf1de700 --- /dev/null +++ b/besu/src/test/java/org/hyperledger/besu/cli/config/ProfilesCompletionCandidatesTest.java @@ -0,0 +1,91 @@ +/* + * 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.cli.config; + +import org.hyperledger.besu.cli.util.ProfileFinder; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class ProfilesCompletionCandidatesTest { + @TempDir private static Path tempProfilesDir; + private static String originalProfilesDirProperty; + + @BeforeAll + public static void copyExternalProfiles() throws IOException { + for (String internalProfileName : InternalProfileName.getInternalProfileNames()) { + final Path profilePath = tempProfilesDir.resolve(internalProfileName + "_external.toml"); + + String profileConfigFile = + InternalProfileName.valueOfIgnoreCase(internalProfileName).get().getConfigFile(); + try (InputStream resourceUrl = + ProfileFinder.class.getClassLoader().getResourceAsStream(profileConfigFile)) { + if (resourceUrl != null) { + Files.copy(resourceUrl, profilePath); + } + } + } + } + + @BeforeAll + public static void setupSystemProperty() { + originalProfilesDirProperty = System.getProperty("besu.profiles.dir"); + // sets the system property for the test + System.setProperty("besu.profiles.dir", tempProfilesDir.toString()); + } + + @Test + void profileCompletionCandidates_shouldIncludeInternalAndExternalProfiles() { + Iterator candidates = new ProfilesCompletionCandidates().iterator(); + // convert Iterator to List + List candidatesList = new ArrayList<>(); + candidates.forEachRemaining(candidatesList::add); + + Assertions.assertThat(candidatesList).containsExactlyInAnyOrderElementsOf(allProfileNames()); + } + + static Set allProfileNames() { + final Set profileNames = new TreeSet<>(InternalProfileName.getInternalProfileNames()); + final Set externalProfileNames = + InternalProfileName.getInternalProfileNames().stream() + .map(name -> name + "_external") + .collect(Collectors.toSet()); + profileNames.addAll(externalProfileNames); + return profileNames; + } + + @AfterAll + public static void clearSystemProperty() { + if (originalProfilesDirProperty != null) { + System.setProperty("besu.profiles.dir", originalProfilesDirProperty); + } else { + System.clearProperty("besu.profiles.dir"); + } + } +} From 3117f15a3a3497fe8d4ccc553b739e5f390001ac Mon Sep 17 00:00:00 2001 From: Chaminda Divitotawela Date: Wed, 10 Jul 2024 18:43:07 +1000 Subject: [PATCH 07/12] Fix status badge for documentation (#7304) Documentation has been moved to GitHub pages and no longer use readthedocs. Updated the README status badge for docs with correct link Signed-off-by: Chaminda Divitotawela --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a2262086be..d54b6bb329f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Besu Ethereum Client [![CircleCI](https://circleci.com/gh/hyperledger/besu/tree/main.svg?style=svg)](https://circleci.com/gh/hyperledger/besu/tree/main) - [![Documentation Status](https://readthedocs.org/projects/hyperledger-besu/badge/?version=latest)](https://besu.hyperledger.org/en/latest/?badge=latest) + [![Documentation](https://img.shields.io/github/actions/workflow/status/hyperledger/besu-docs/publish-main-docs.yml?branch=main&label=docs)](https://github.com/hyperledger/besu-docs/actions/workflows/publish-main-docs.yml) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3174/badge)](https://bestpractices.coreinfrastructure.org/projects/3174) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/hyperledger/besu/blob/main/LICENSE) [![Discord](https://img.shields.io/discord/905194001349627914?logo=Hyperledger&style=plastic)](https://discord.gg/hyperledger) From 3f00bad598e99ea51d17456e9693abb3cd999b76 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 11 Jul 2024 09:49:37 +1000 Subject: [PATCH 08/12] [MINOR] Fixed some typos (#7299) * typos Signed-off-by: Sally MacFarlane --------- Signed-off-by: Sally MacFarlane --- .github/workflows/container-security-scan.yml | 2 +- .../besu/ethereum/mainnet/MainnetProtocolSpecs.java | 4 ++-- .../besu/ethereum/eth/manager/snap/SnapProtocolManager.java | 2 +- .../java/org/hyperledger/besu/ethereum/trie/MerkleTrie.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/container-security-scan.yml b/.github/workflows/container-security-scan.yml index f945d13220d..9d41e819b5f 100644 --- a/.github/workflows/container-security-scan.yml +++ b/.github/workflows/container-security-scan.yml @@ -8,7 +8,7 @@ on: required: false default: 'develop' schedule: - # Start of the hour is the busy time. Scheule it to run 8:17am UTC + # Start of the hour is the busy time. Schedule it to run 8:17am UTC - cron: '17 8 * * *' jobs: 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 b4a75c49c28..4ae30f8de9a 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 @@ -642,9 +642,9 @@ static ProtocolSpecBuilder pragueDefinition( return cancunDefinition( chainId, enableRevertReason, genesisConfigOptions, evmConfiguration, miningParameters) - // EIP-3074 AUTH and AUTCALL gas + // EIP-3074 AUTH and AUTHCALL gas .gasCalculator(PragueGasCalculator::new) - // EIP-3074 AUTH and AUTCALL + // EIP-3074 AUTH and AUTHCALL .evmBuilder( (gasCalculator, jdCacheConfig) -> MainnetEVMs.prague( diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapProtocolManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapProtocolManager.java index 2c717d77a16..c65a1c6b0cd 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapProtocolManager.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/SnapProtocolManager.java @@ -84,7 +84,7 @@ public void stop() {} public void awaitStop() throws InterruptedException {} /** - * This function is called by the P2P framework when an "SNAP message has been received. + * This function is called by the P2P framework when a SNAP message has been received. * * @param cap The capability under which the message was transmitted. * @param message The message to be decoded. diff --git a/ethereum/trie/src/main/java/org/hyperledger/besu/ethereum/trie/MerkleTrie.java b/ethereum/trie/src/main/java/org/hyperledger/besu/ethereum/trie/MerkleTrie.java index 2fdb494703f..c9442e26844 100644 --- a/ethereum/trie/src/main/java/org/hyperledger/besu/ethereum/trie/MerkleTrie.java +++ b/ethereum/trie/src/main/java/org/hyperledger/besu/ethereum/trie/MerkleTrie.java @@ -28,7 +28,7 @@ import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; -/** An Merkle Patricial Trie. */ +/** A Merkle Patricia Trie. */ public interface MerkleTrie { Bytes EMPTY_TRIE_NODE = RLP.NULL; From 812dc747424a0c984e483b2a01fdaab97d50a42c Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 11 Jul 2024 21:19:50 -0600 Subject: [PATCH 09/12] Add evmtool block-test subcommand (#7293) * Add evmtool block-test subcommand Add an evmtool subcommand that will run non-hive blockchain tests. Signed-off-by: Danno Ferrin --- CHANGELOG.md | 5 +- .../evmtool/BlockchainTestSubCommand.java | 254 ++++++++++++++++++ .../besu/evmtool/EvmToolCommand.java | 16 +- .../besu/evmtool/EvmToolSpecTests.java | 13 +- .../evmtool/block-test/prague-eof-rjump.json | 250 +++++++++++++++++ .../referencetests/EOFTestCaseSpec.java | 3 +- 6 files changed, 531 insertions(+), 10 deletions(-) create mode 100644 ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainTestSubCommand.java create mode 100644 ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/block-test/prague-eof-rjump.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 1306de83dd8..0b783b1dc3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,10 @@ ### Breaking Changes ### Additions and Improvements +- `--Xsnapsync-bft-enabled` option enables experimental support for snap sync with IBFT/QBFT permissioned Bonsai-DB chains [#7140](https://github.com/hyperledger/besu/pull/7140) - Add support to load external profiles using `--profile` [#7265](https://github.com/hyperledger/besu/issues/7265) +- `privacy-nonce-always-increments` option enables private transactions to always increment the nonce, even if the transaction is invalid [#6593](https://github.com/hyperledger/besu/pull/6593) +- Added `block-test` subcommand to the evmtool which runs blockchain reference tests [#7293](https://github.com/hyperledger/besu/pull/7293) ### Bug fixes @@ -34,8 +37,6 @@ - Nodes in a permissioned chain maintain (and retry) connections to bootnodes [#7257](https://github.com/hyperledger/besu/pull/7257) - Promote experimental `besu storage x-trie-log` subcommand to production-ready [#7278](https://github.com/hyperledger/besu/pull/7278) - Enhanced BFT round-change diagnostics [#7271](https://github.com/hyperledger/besu/pull/7271) -- `--Xsnapsync-bft-enabled` option enables experimental support for snap sync with IBFT/QBFT permissioned Bonsai-DB chains [#7140](https://github.com/hyperledger/besu/pull/7140) -- `privacy-nonce-always-increments` option enables private transactions to always increment the nonce, even if the transaction is invalid [#6593](https://github.com/hyperledger/besu/pull/6593) ### Bug fixes - Validation errors ignored in accounts-allowlist and empty list [#7138](https://github.com/hyperledger/besu/issues/7138) 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 new file mode 100644 index 00000000000..5b9bc2cdfe0 --- /dev/null +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/BlockchainTestSubCommand.java @@ -0,0 +1,254 @@ +/* + * 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.evmtool; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hyperledger.besu.evmtool.BlockchainTestSubCommand.COMMAND_NAME; + +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +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.core.BlockImporter; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.mainnet.BlockImportResult; +import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; +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.ReferenceTestProtocolSchedules; +import org.hyperledger.besu.ethereum.rlp.RLPException; +import org.hyperledger.besu.evm.EVM; +import org.hyperledger.besu.evm.EvmSpecVersion; +import org.hyperledger.besu.evm.account.AccountState; +import org.hyperledger.besu.evm.internal.EvmConfiguration.WorldUpdaterMode; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Suppliers; +import org.apache.tuweni.bytes.Bytes32; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; +import picocli.CommandLine.ParentCommand; + +/** + * This class, BlockchainTestSubCommand, is a command-line interface (CLI) command that executes an + * Ethereum State Test. It implements the Runnable interface, meaning it can be used in a thread of + * execution. + * + *

The class is annotated with @CommandLine.Command, which is a PicoCLI annotation that + * designates this class as a command-line command. The annotation parameters define the command's + * name, description, whether it includes standard help options, and the version provider. + * + *

The command's functionality is defined in the run() method, which is overridden from the + * Runnable interface. + */ +@Command( + name = COMMAND_NAME, + description = "Execute an Ethereum Blockchain Test.", + mixinStandardHelpOptions = true, + versionProvider = VersionProvider.class) +public class BlockchainTestSubCommand implements Runnable { + /** + * The name of the command for the BlockchainTestSubCommand. This constant is used as the name + * parameter in the @CommandLine.Command annotation. It defines the command name that users should + * enter on the command line to invoke this command. + */ + public static final String COMMAND_NAME = "block-test"; + + static final Supplier referenceTestProtocolSchedules = + Suppliers.memoize(ReferenceTestProtocolSchedules::create); + + @Option( + names = {"--test-name"}, + description = "Limit execution to one named test.") + private String testName = null; + + @ParentCommand private final EvmToolCommand parentCommand; + + // picocli does it magically + @Parameters private final List blockchainTestFiles = new ArrayList<>(); + + /** + * Default constructor for the BlockchainTestSubCommand class. This constructor doesn't take any + * arguments and initializes the parentCommand to null. PicoCLI requires this constructor. + */ + @SuppressWarnings("unused") + public BlockchainTestSubCommand() { + // PicoCLI requires this + this(null); + } + + BlockchainTestSubCommand(final EvmToolCommand parentCommand) { + this.parentCommand = parentCommand; + } + + @Override + public void run() { + // presume ethereum mainnet for reference and state tests + SignatureAlgorithmFactory.setDefaultInstance(); + final ObjectMapper blockchainTestMapper = JsonUtils.createObjectMapper(); + + final JavaType javaType = + blockchainTestMapper + .getTypeFactory() + .constructParametricType( + Map.class, String.class, BlockchainReferenceTestCaseSpec.class); + try { + if (blockchainTestFiles.isEmpty()) { + // if no state tests were specified, use standard input to get filenames + final BufferedReader in = + new BufferedReader(new InputStreamReader(parentCommand.in, UTF_8)); + while (true) { + final String fileName = in.readLine(); + if (fileName == null) { + // Reached end-of-file. Stop the loop. + break; + } + final File file = new File(fileName); + if (file.isFile()) { + final Map blockchainTests = + blockchainTestMapper.readValue(file, javaType); + executeBlockchainTest(blockchainTests); + } else { + parentCommand.out.println("File not found: " + fileName); + } + } + } else { + for (final Path blockchainTestFile : blockchainTestFiles) { + final Map blockchainTests; + if ("stdin".equals(blockchainTestFile.toString())) { + blockchainTests = blockchainTestMapper.readValue(parentCommand.in, javaType); + } else { + blockchainTests = blockchainTestMapper.readValue(blockchainTestFile.toFile(), javaType); + } + executeBlockchainTest(blockchainTests); + } + } + } catch (final JsonProcessingException jpe) { + parentCommand.out.println("File content error: " + jpe); + } catch (final IOException e) { + System.err.println("Unable to read state file"); + e.printStackTrace(System.err); + } + } + + private void executeBlockchainTest( + final Map blockchainTests) { + blockchainTests.forEach(this::traceTestSpecs); + } + + private void traceTestSpecs(final String test, final BlockchainReferenceTestCaseSpec spec) { + if (testName != null && !testName.equals(test)) { + parentCommand.out.println("Skipping test: " + test); + return; + } + parentCommand.out.println("Considering " + test); + + final BlockHeader genesisBlockHeader = spec.getGenesisBlockHeader(); + final MutableWorldState worldState = + spec.getWorldStateArchive() + .getMutable(genesisBlockHeader.getStateRoot(), genesisBlockHeader.getHash()) + .orElseThrow(); + + final ProtocolSchedule schedule = + referenceTestProtocolSchedules.get().getByName(spec.getNetwork()); + + final MutableBlockchain blockchain = spec.getBlockchain(); + final ProtocolContext context = spec.getProtocolContext(); + + for (final BlockchainReferenceTestCaseSpec.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(); + + verifyJournaledEVMAccountCompatability(worldState, protocolSpec); + + final HeaderValidationMode validationMode = + "NoProof".equalsIgnoreCase(spec.getSealEngine()) + ? HeaderValidationMode.LIGHT + : HeaderValidationMode.FULL; + final BlockImportResult importResult = + blockImporter.importBlock(context, block, validationMode, validationMode); + + if (importResult.isImported() != candidateBlock.isValid()) { + parentCommand.out.printf( + "Block %d (%s) %s%n", + block.getHeader().getNumber(), + block.getHash(), + importResult.isImported() ? "Failed to be rejected" : "Failed to import"); + } else { + parentCommand.out.printf( + "Block %d (%s) %s%n", + block.getHeader().getNumber(), + block.getHash(), + importResult.isImported() ? "Imported" : "Rejected (correctly)"); + } + } catch (final RLPException e) { + if (candidateBlock.isValid()) { + parentCommand.out.printf( + "Block %d (%s) should have imported but had an RLP exception %s%n", + candidateBlock.getBlock().getHeader().getNumber(), + candidateBlock.getBlock().getHash(), + e.getMessage()); + } + } + } + if (!blockchain.getChainHeadHash().equals(spec.getLastBlockHash())) { + parentCommand.out.printf( + "Chain header mismatch, have %s want %s - %s%n", + blockchain.getChainHeadHash(), spec.getLastBlockHash(), test); + } else { + parentCommand.out.println("Chain import successful - " + test); + } + } + + void verifyJournaledEVMAccountCompatability( + final MutableWorldState worldState, final ProtocolSpec protocolSpec) { + EVM evm = protocolSpec.getEvm(); + if (evm.getEvmConfiguration().worldUpdaterMode() == WorldUpdaterMode.JOURNALED) { + if (worldState + .streamAccounts(Bytes32.ZERO, Integer.MAX_VALUE) + .anyMatch(AccountState::isEmpty)) { + parentCommand.out.println("Journaled account configured and empty account detected"); + } + + if (EvmSpecVersion.SPURIOUS_DRAGON.compareTo(evm.getEvmVersion()) > 0) { + parentCommand.out.println( + "Journaled account configured and fork prior to the merge specified"); + } + } + } +} 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 a3bed6b54be..898f1dd4b6a 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 @@ -101,6 +101,7 @@ subcommands = { BenchmarkSubCommand.class, B11rSubCommand.class, + BlockchainTestSubCommand.class, CodeValidateSubCommand.class, EOFTestSubCommand.class, PrettyPrintSubCommand.class, @@ -370,15 +371,18 @@ public boolean hasFork() { public void run() { LogConfigurator.setLevel("", "OFF"); try { + GenesisFileModule genesisFileModule; + if (network != null) { + genesisFileModule = GenesisFileModule.createGenesisModule(network); + } else if (genesisFile != null) { + genesisFileModule = GenesisFileModule.createGenesisModule(genesisFile); + } else { + genesisFileModule = GenesisFileModule.createGenesisModule(NetworkName.DEV); + } final EvmToolComponent component = DaggerEvmToolComponent.builder() .dataStoreModule(new DataStoreModule()) - .genesisFileModule( - network == null - ? genesisFile == null - ? GenesisFileModule.createGenesisModule(NetworkName.DEV) - : GenesisFileModule.createGenesisModule(genesisFile) - : GenesisFileModule.createGenesisModule(network)) + .genesisFileModule(genesisFileModule) .evmToolCommandOptionsModule(daggerOptions) .metricsSystemModule(new MetricsSystemModule()) .build(); diff --git a/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/EvmToolSpecTests.java b/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/EvmToolSpecTests.java index 1892a472b02..66efb574fef 100644 --- a/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/EvmToolSpecTests.java +++ b/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/EvmToolSpecTests.java @@ -53,6 +53,10 @@ public class EvmToolSpecTests { static final ObjectMapper objectMapper = new ObjectMapper(); static final ObjectReader specReader = objectMapper.reader(); + public static Object[][] blocktestTests() { + return findSpecFiles(new String[] {"block-test"}); + } + public static Object[][] b11rTests() { return findSpecFiles(new String[] {"b11r"}); } @@ -114,7 +118,14 @@ private static Object[] pathToParams(final String subDir, final File file) { } @ParameterizedTest(name = "{0}") - @MethodSource({"b11rTests", "prettyPrintTests", "stateTestTests", "t8nTests", "traceTests"}) + @MethodSource({ + "blocktestTests", + "b11rTests", + "prettyPrintTests", + "stateTestTests", + "t8nTests", + "traceTests" + }) void testBySpec( final String file, final JsonNode cliNode, diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/block-test/prague-eof-rjump.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/block-test/prague-eof-rjump.json new file mode 100644 index 00000000000..b9d2e541795 --- /dev/null +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/block-test/prague-eof-rjump.json @@ -0,0 +1,250 @@ +{ + "cli": [ + "block-test", + "stdin" + ], + "stdin": { + "tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py::test_rjump_zero[fork_PragueEIP7692-blockchain_test]": { + "network": "Prague", + "genesisBlockHeader": { + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x0000000000000000000000000000000000000000", + "stateRoot": "0x34ccf8774a5b8833da9451a3f7f8a0af0147956c058f0831dab07c348d7ac0d9", + "transactionsTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x00", + "number": "0x00", + "gasLimit": "0x016345785d8a0000", + "gasUsed": "0x00", + "timestamp": "0x00", + "extraData": "0x00", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "blobGasUsed": "0x00", + "excessBlobGas": "0x00", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "requestsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "hash": "0xc9397e8a1b99cbb2b885852fde56de8fa686091a4f4430163f5237d7aaf33a14" + }, + "pre": { + "0x00000000219ab540356cbb839cbe05303d7705fa": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a2646970667358221220dceca8706b29e917dacf25fceef95acac8d90d765ac926663ce4096195952b6164736f6c634300060b0033", + "storage": { + "0x22": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", + "0x23": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "0x24": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + "0x25": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", + "0x26": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", + "0x27": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", + "0x28": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", + "0x29": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", + "0x2a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", + "0x2b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", + "0x2c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", + "0x2d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", + "0x2e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", + "0x2f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", + "0x30": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", + "0x31": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", + "0x32": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", + "0x33": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", + "0x34": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", + "0x35": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", + "0x36": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", + "0x37": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", + "0x38": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", + "0x39": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", + "0x3a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", + "0x3b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", + "0x3c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", + "0x3d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", + "0x3e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", + "0x3f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", + "0x40": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" + } + }, + "0x00a3ca265ebcb825b45f985a16cefb49958ce017": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b366038141561012e5760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061012e57600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460dd5780604c02838201600302600401805490600101805490600101549160601b83528260140152906034015260010160a6565b910180921460ed579060025560f8565b90505f6002555f6003555b5f548061049d141561010757505f5b60015460028282011161011c5750505f610122565b01600290035b5f555f600155604c025ff35b5f5ffd", + "storage": {} + }, + "0x0aae40965e6800cd9b1f4b05ff21581047e3f91e": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500", + "storage": {} + }, + "0x000f3df6d732807ef1319fb7b8bb8522d0beac02": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", + "storage": {} + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x00", + "balance": "0x3635c9adc5dea00000", + "code": "0x", + "storage": {} + }, + "0x0000000000000000000000000000000000001000": { + "nonce": "0x01", + "balance": "0x00", + "code": "0xef0001010004020001000a0400000000800002e0000061201560015500", + "storage": {} + } + }, + "postState": { + "0x0000000000000000000000000000000000001000": { + "nonce": "0x01", + "balance": "0x00", + "code": "0xef0001010004020001000a0400000000800002e0000061201560015500", + "storage": { + "0x01": "0x2015" + } + }, + "0x00000000219ab540356cbb839cbe05303d7705fa": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a2646970667358221220dceca8706b29e917dacf25fceef95acac8d90d765ac926663ce4096195952b6164736f6c634300060b0033", + "storage": { + "0x22": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", + "0x23": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "0x24": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + "0x25": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", + "0x26": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", + "0x27": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", + "0x28": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", + "0x29": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", + "0x2a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", + "0x2b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", + "0x2c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", + "0x2d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", + "0x2e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", + "0x2f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", + "0x30": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", + "0x31": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", + "0x32": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", + "0x33": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", + "0x34": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", + "0x35": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", + "0x36": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", + "0x37": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", + "0x38": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", + "0x39": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", + "0x3a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", + "0x3b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", + "0x3c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", + "0x3d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", + "0x3e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", + "0x3f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", + "0x40": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" + } + }, + "0x000f3df6d732807ef1319fb7b8bb8522d0beac02": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", + "storage": { + "0x03e8": "0x03e8" + } + }, + "0x00a3ca265ebcb825b45f985a16cefb49958ce017": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b366038141561012e5760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061012e57600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460dd5780604c02838201600302600401805490600101805490600101549160601b83528260140152906034015260010160a6565b910180921460ed579060025560f8565b90505f6002555f6003555b5f548061049d141561010757505f5b60015460028282011161011c5750505f610122565b01600290035b5f555f600155604c025ff35b5f5ffd", + "storage": {} + }, + "0x0aae40965e6800cd9b1f4b05ff21581047e3f91e": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500", + "storage": { + "0x00": "0xc9397e8a1b99cbb2b885852fde56de8fa686091a4f4430163f5237d7aaf33a14" + } + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "nonce": "0x00", + "balance": "0x01f92c", + "code": "0x", + "storage": {} + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x01", + "balance": "0x3635c9adc5de996c18", + "code": "0x", + "storage": {} + } + }, + "genesisRLP": "0xf90262f9025ba00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a034ccf8774a5b8833da9451a3f7f8a0af0147956c058f0831dab07c348d7ac0d9a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808088016345785d8a0000808000a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421c0c0c0c0", + "blocks": [ + { + "blockHeader": { + "parentHash": "0xc9397e8a1b99cbb2b885852fde56de8fa686091a4f4430163f5237d7aaf33a14", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "stateRoot": "0x1a4885d1f64bac16e5ab17fb13e23fb5d1a0ec2ca4519c81714683f69c8d9a84", + "transactionsTrie": "0xec9d10cff79619f2df45db8c66526ef3fbd32d283fdd2dcc9b55c0efe643d8c3", + "receiptTrie": "0x9593f56abf23bcbb26d27b0c6e46a56415d9103ed6b4d8ac7b4182f9f250cafa", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x00", + "number": "0x01", + "gasLimit": "0x016345785d8a0000", + "gasUsed": "0xa864", + "timestamp": "0x03e8", + "extraData": "0x00", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "blobGasUsed": "0x00", + "excessBlobGas": "0x00", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "requestsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "hash": "0x879cf54bf493b6585db592b1b24ec30a8a7fbe3b9146d3fb20ca36200f2aca87" + }, + "transactions": [ + { + "type": "0x00", + "chainId": "0x01", + "nonce": "0x00", + "gasPrice": "0x0a", + "gasLimit": "0x989680", + "to": "0x0000000000000000000000000000000000001000", + "value": "0x00", + "data": "0x", + "v": "0x26", + "r": "0xe5d462429669f661291a8dc4c49a092cfd4922b6f3f31c9189a2f4adf5ecd730", + "s": "0x01494afaf472fbb80bcb107ffeb918a2b9115f454027840615d6d20d63c69ac0", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } + ], + "uncleHeaders": [], + "withdrawals": [], + "depositRequests": [], + "withdrawalRequests": [], + "rlp": "0xf902c9f9025fa0c9397e8a1b99cbb2b885852fde56de8fa686091a4f4430163f5237d7aaf33a14a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa01a4885d1f64bac16e5ab17fb13e23fb5d1a0ec2ca4519c81714683f69c8d9a84a0ec9d10cff79619f2df45db8c66526ef3fbd32d283fdd2dcc9b55c0efe643d8c3a09593f56abf23bcbb26d27b0c6e46a56415d9103ed6b4d8ac7b4182f9f250cafab9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800188016345785d8a000082a8648203e800a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421f862f860800a83989680940000000000000000000000000000000000001000808026a0e5d462429669f661291a8dc4c49a092cfd4922b6f3f31c9189a2f4adf5ecd730a001494afaf472fbb80bcb107ffeb918a2b9115f454027840615d6d20d63c69ac0c0c0c0", + "blocknumber": "1" + } + ], + "lastblockhash": "0x879cf54bf493b6585db592b1b24ec30a8a7fbe3b9146d3fb20ca36200f2aca87", + "sealEngine": "NoProof", + "_info": { + "hash": "0xbfd1223f9b5b8dbf202178f7c1f18dc089cb24e54c9cb7fc9831907547e937c4", + "comment": "`execution-spec-tests` generated test", + "filling-transition-tool": "Hyperledger Besu evm 24.7-develop-8ca7129", + "description": "Test function documentation:\nEOF1V4200_0002 (Valid) EOF code containing RJUMP (Zero)", + "url": "https://github.com/ethereum/execution-spec-tests/blob/891a6111370c89d4ce89bf91589c6d5ff6785158/tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py#L63", + "reference-spec": "https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4200.md", + "reference-spec-version": "17d4a8d12d2b5e0f2985c866376c16c8c6df7cba" + } + } + }, + "stdout": "Considering tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py::test_rjump_zero[fork_PragueEIP7692-blockchain_test]\nBlock 1 (0x879cf54bf493b6585db592b1b24ec30a8a7fbe3b9146d3fb20ca36200f2aca87) Imported\nChain import successful - tests/prague/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py::test_rjump_zero[fork_PragueEIP7692-blockchain_test]\n" +} diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/EOFTestCaseSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/EOFTestCaseSpec.java index e67ec2091f0..48fae5277de 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/EOFTestCaseSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/EOFTestCaseSpec.java @@ -25,7 +25,8 @@ public class EOFTestCaseSpec { public record TestVector( @JsonProperty("code") String code, - @JsonProperty("results") NavigableMap results) {} + @JsonProperty("results") NavigableMap results, + @JsonProperty("containerKind") String containerKind) {} public record TestResult( @JsonProperty("exception") String exception, @JsonProperty("result") boolean result) { From d35c6d787521949f4991cb278217c3555adb1be6 Mon Sep 17 00:00:00 2001 From: Stefan Pingel <16143240+pinges@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:29:19 +1000 Subject: [PATCH 10/12] Make the retrying snap tasks switching (#7307) * make snap tasks switching Signed-off-by: stefan.pingel@consensys.net --- .../RetryingGetAccountRangeFromPeerTask.java | 15 +++++++-------- .../snap/RetryingGetBytecodeFromPeerTask.java | 15 ++++++++------- .../RetryingGetStorageRangeFromPeerTask.java | 19 ++++++++++++------- .../snap/RetryingGetTrieNodeFromPeerTask.java | 15 ++++++++------- .../pipeline/AsyncOperationProcessor.java | 6 +++++- 5 files changed, 40 insertions(+), 30 deletions(-) diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetAccountRangeFromPeerTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetAccountRangeFromPeerTask.java index 0624a90589c..ef48dcf952d 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetAccountRangeFromPeerTask.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetAccountRangeFromPeerTask.java @@ -17,18 +17,17 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; -import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingPeerTask; +import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingSwitchingPeerTask; import org.hyperledger.besu.ethereum.eth.manager.task.EthTask; import org.hyperledger.besu.ethereum.eth.messages.snap.AccountRangeMessage; import org.hyperledger.besu.plugin.services.MetricsSystem; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.apache.tuweni.bytes.Bytes32; public class RetryingGetAccountRangeFromPeerTask - extends AbstractRetryingPeerTask { + extends AbstractRetryingSwitchingPeerTask { public static final int MAX_RETRIES = 4; @@ -46,9 +45,9 @@ private RetryingGetAccountRangeFromPeerTask( final MetricsSystem metricsSystem) { super( ethContext, - MAX_RETRIES, + metricsSystem, data -> data.accounts().isEmpty() && data.proofs().isEmpty(), - metricsSystem); + MAX_RETRIES); this.ethContext = ethContext; this.startKeyHash = startKeyHash; this.endKeyHash = endKeyHash; @@ -67,12 +66,12 @@ public static EthTask forAccountRange( } @Override - protected CompletableFuture executePeerTask( - final Optional assignedPeer) { + protected CompletableFuture executeTaskOnCurrentPeer( + final EthPeer peer) { final GetAccountRangeFromPeerTask task = GetAccountRangeFromPeerTask.forAccountRange( ethContext, startKeyHash, endKeyHash, blockHeader, metricsSystem); - assignedPeer.ifPresent(task::assignPeer); + task.assignPeer(peer); return executeSubTask(task::run) .thenApply( peerResult -> { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetBytecodeFromPeerTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetBytecodeFromPeerTask.java index 3258298f2c7..e30d062597b 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetBytecodeFromPeerTask.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetBytecodeFromPeerTask.java @@ -17,19 +17,21 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; -import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingPeerTask; +import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingSwitchingPeerTask; import org.hyperledger.besu.ethereum.eth.manager.task.EthTask; import org.hyperledger.besu.plugin.services.MetricsSystem; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; -public class RetryingGetBytecodeFromPeerTask extends AbstractRetryingPeerTask> { +public class RetryingGetBytecodeFromPeerTask + extends AbstractRetryingSwitchingPeerTask> { + + public static final int MAX_RETRIES = 4; private final EthContext ethContext; private final List codeHashes; @@ -41,7 +43,7 @@ private RetryingGetBytecodeFromPeerTask( final List codeHashes, final BlockHeader blockHeader, final MetricsSystem metricsSystem) { - super(ethContext, 4, Map::isEmpty, metricsSystem); + super(ethContext, metricsSystem, Map::isEmpty, MAX_RETRIES); this.ethContext = ethContext; this.codeHashes = codeHashes; this.blockHeader = blockHeader; @@ -57,11 +59,10 @@ public static EthTask> forByteCode( } @Override - protected CompletableFuture> executePeerTask( - final Optional assignedPeer) { + protected CompletableFuture> executeTaskOnCurrentPeer(final EthPeer peer) { final GetBytecodeFromPeerTask task = GetBytecodeFromPeerTask.forBytecode(ethContext, codeHashes, blockHeader, metricsSystem); - assignedPeer.ifPresent(task::assignPeer); + task.assignPeer(peer); return executeSubTask(task::run) .thenApply( peerResult -> { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetStorageRangeFromPeerTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetStorageRangeFromPeerTask.java index 731b0b7623b..6e977bb4e79 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetStorageRangeFromPeerTask.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetStorageRangeFromPeerTask.java @@ -17,19 +17,20 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; -import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingPeerTask; +import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingSwitchingPeerTask; import org.hyperledger.besu.ethereum.eth.manager.task.EthTask; import org.hyperledger.besu.ethereum.eth.messages.snap.StorageRangeMessage; import org.hyperledger.besu.plugin.services.MetricsSystem; import java.util.List; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.apache.tuweni.bytes.Bytes32; public class RetryingGetStorageRangeFromPeerTask - extends AbstractRetryingPeerTask { + extends AbstractRetryingSwitchingPeerTask { + + public static final int MAX_RETRIES = 4; private final EthContext ethContext; private final List accountHashes; @@ -45,7 +46,11 @@ private RetryingGetStorageRangeFromPeerTask( final Bytes32 endKeyHash, final BlockHeader blockHeader, final MetricsSystem metricsSystem) { - super(ethContext, 4, data -> data.proofs().isEmpty() && data.slots().isEmpty(), metricsSystem); + super( + ethContext, + metricsSystem, + data -> data.proofs().isEmpty() && data.slots().isEmpty(), + MAX_RETRIES); this.ethContext = ethContext; this.accountHashes = accountHashes; this.startKeyHash = startKeyHash; @@ -66,12 +71,12 @@ public static EthTask forStorageRange( } @Override - protected CompletableFuture executePeerTask( - final Optional assignedPeer) { + protected CompletableFuture executeTaskOnCurrentPeer( + final EthPeer peer) { final GetStorageRangeFromPeerTask task = GetStorageRangeFromPeerTask.forStorageRange( ethContext, accountHashes, startKeyHash, endKeyHash, blockHeader, metricsSystem); - assignedPeer.ifPresent(task::assignPeer); + task.assignPeer(peer); return executeSubTask(task::run) .thenApply( peerResult -> { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetTrieNodeFromPeerTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetTrieNodeFromPeerTask.java index 1abf0d72302..152fd91712b 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetTrieNodeFromPeerTask.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/snap/RetryingGetTrieNodeFromPeerTask.java @@ -17,18 +17,20 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; -import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingPeerTask; +import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingSwitchingPeerTask; import org.hyperledger.besu.ethereum.eth.manager.task.EthTask; import org.hyperledger.besu.plugin.services.MetricsSystem; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.apache.tuweni.bytes.Bytes; -public class RetryingGetTrieNodeFromPeerTask extends AbstractRetryingPeerTask> { +public class RetryingGetTrieNodeFromPeerTask + extends AbstractRetryingSwitchingPeerTask> { + + public static final int MAX_RETRIES = 4; private final EthContext ethContext; private final Map> paths; @@ -40,7 +42,7 @@ private RetryingGetTrieNodeFromPeerTask( final Map> paths, final BlockHeader blockHeader, final MetricsSystem metricsSystem) { - super(ethContext, 4, Map::isEmpty, metricsSystem); + super(ethContext, metricsSystem, Map::isEmpty, MAX_RETRIES); this.ethContext = ethContext; this.paths = paths; this.blockHeader = blockHeader; @@ -56,11 +58,10 @@ public static EthTask> forTrieNodes( } @Override - protected CompletableFuture> executePeerTask( - final Optional assignedPeer) { + protected CompletableFuture> executeTaskOnCurrentPeer(final EthPeer peer) { final GetTrieNodeFromPeerTask task = GetTrieNodeFromPeerTask.forTrieNodes(ethContext, paths, blockHeader, metricsSystem); - assignedPeer.ifPresent(task::assignPeer); + task.assignPeer(peer); return executeSubTask(task::run) .thenApply( peerResult -> { diff --git a/services/pipeline/src/main/java/org/hyperledger/besu/services/pipeline/AsyncOperationProcessor.java b/services/pipeline/src/main/java/org/hyperledger/besu/services/pipeline/AsyncOperationProcessor.java index c8c7e5905db..e17296c026d 100644 --- a/services/pipeline/src/main/java/org/hyperledger/besu/services/pipeline/AsyncOperationProcessor.java +++ b/services/pipeline/src/main/java/org/hyperledger/besu/services/pipeline/AsyncOperationProcessor.java @@ -83,7 +83,11 @@ private void outputNextCompletedTask(final WritePipe outputPipe) { waitForAnyFutureToComplete(); outputCompletedTasks(outputPipe); } catch (final InterruptedException e) { - LOG.trace("Interrupted while waiting for processing to complete", e.getMessage()); + LOG.atTrace() + .setMessage("Interrupted while waiting for processing to complete: Message=({})") + .addArgument(e.getMessage()) + .setCause(e) + .log(); } catch (final ExecutionException e) { throw new AsyncOperationException("Async operation failed. " + e.getMessage(), e); } catch (final TimeoutException e) { From 41f007fb804e0ac8fb4fd11f2e2801035cce514c Mon Sep 17 00:00:00 2001 From: Matilda-Clerke Date: Fri, 12 Jul 2024 16:28:56 +1000 Subject: [PATCH 11/12] 6612: Remove deprecated sync modes and related helper methods (#7309) * 6612: Remove deprecated sync modes and related helper methods Signed-off-by: Matilda-Clerke --- .../org/hyperledger/besu/cli/BesuCommand.java | 10 +++---- .../besu/controller/BesuController.java | 4 +-- .../controller/BesuControllerBuilder.java | 6 ++--- .../hyperledger/besu/cli/BesuCommandTest.java | 2 +- .../besu/ethereum/eth/manager/EthPeers.java | 2 +- .../eth/sync/DefaultSynchronizer.java | 6 ++--- .../besu/ethereum/eth/sync/SyncMode.java | 26 ++----------------- 7 files changed, 16 insertions(+), 40 deletions(-) diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 427cac92480..118e586b178 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -1566,11 +1566,11 @@ private void validateConsensusSyncCompatibilityOptions() { || genesisConfigOptionsSupplier.get().isQbft()) && !unstableSynchronizerOptions.isSnapSyncBftEnabled()) { final String errorSuffix = "can't be used with BFT networks"; - if (SyncMode.CHECKPOINT.equals(syncMode) || SyncMode.X_CHECKPOINT.equals(syncMode)) { + if (SyncMode.CHECKPOINT.equals(syncMode)) { throw new ParameterException( commandLine, String.format("%s %s", "Checkpoint sync", errorSuffix)); } - if (syncMode == SyncMode.SNAP || syncMode == SyncMode.X_SNAP) { + if (syncMode == SyncMode.SNAP) { throw new ParameterException(commandLine, String.format("%s %s", "Snap sync", errorSuffix)); } } @@ -1753,7 +1753,7 @@ && isOptionSet(commandLine, "--sync-min-peers")) { CommandLineUtils.failIfOptionDoesntMeetRequirement( commandLine, "--Xcheckpoint-post-merge-enabled can only be used with CHECKPOINT sync-mode", - SyncMode.isCheckpointSync(getDefaultSyncModeIfNotSet()), + getDefaultSyncModeIfNotSet() == SyncMode.CHECKPOINT, singletonList("--Xcheckpoint-post-merge-enabled")); CommandLineUtils.failIfOptionDoesntMeetRequirement( @@ -2043,10 +2043,10 @@ private PrivacyParameters privacyParameters() { if (syncMode == SyncMode.FAST) { throw new ParameterException(commandLine, String.format("%s %s", "Fast sync", errorSuffix)); } - if (syncMode == SyncMode.SNAP || syncMode == SyncMode.X_SNAP) { + if (syncMode == SyncMode.SNAP) { throw new ParameterException(commandLine, String.format("%s %s", "Snap sync", errorSuffix)); } - if (syncMode == SyncMode.CHECKPOINT || syncMode == SyncMode.X_CHECKPOINT) { + if (syncMode == SyncMode.CHECKPOINT) { throw new ParameterException( commandLine, String.format("%s %s", "Checkpoint sync", errorSuffix)); } diff --git a/besu/src/main/java/org/hyperledger/besu/controller/BesuController.java b/besu/src/main/java/org/hyperledger/besu/controller/BesuController.java index 6942fda3166..fb99318905a 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuController.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuController.java @@ -14,8 +14,6 @@ */ package org.hyperledger.besu.controller; -import static org.hyperledger.besu.ethereum.eth.sync.SyncMode.isCheckpointSync; - import org.hyperledger.besu.cli.config.EthNetworkConfig; import org.hyperledger.besu.config.GenesisConfigFile; import org.hyperledger.besu.config.GenesisConfigOptions; @@ -361,7 +359,7 @@ public BesuControllerBuilder fromGenesisFile( // wrap with TransitionBesuControllerBuilder if we have a terminal total difficulty: if (configOptions.getTerminalTotalDifficulty().isPresent()) { // Enable start with vanilla MergeBesuControllerBuilder for PoS checkpoint block - if (isCheckpointSync(syncMode) && isCheckpointPoSBlock(configOptions)) { + if (syncMode == SyncMode.CHECKPOINT && isCheckpointPoSBlock(configOptions)) { return new MergeBesuControllerBuilder().genesisConfigFile(genesisConfigFile); } else { // TODO this should be changed to vanilla MergeBesuControllerBuilder and the Transition* diff --git a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java index 6123535f637..73bc76608b3 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -708,8 +708,8 @@ public BesuController build() { ethPeers.setTrailingPeerRequirementsSupplier(synchronizer::calculateTrailingPeerRequirements); - if (SyncMode.isSnapSync(syncConfig.getSyncMode()) - || SyncMode.isCheckpointSync(syncConfig.getSyncMode())) { + if (syncConfig.getSyncMode() == SyncMode.SNAP + || syncConfig.getSyncMode() == SyncMode.CHECKPOINT) { synchronizer.subscribeInSync((b) -> ethPeers.snapServerPeersNeeded(!b)); ethPeers.snapServerPeersNeeded(true); } else { @@ -1157,7 +1157,7 @@ protected List createPeerValidators(final ProtocolSchedule protoc final CheckpointConfigOptions checkpointConfigOptions = genesisConfigOptions.getCheckpointOptions(); - if (SyncMode.isCheckpointSync(syncConfig.getSyncMode()) && checkpointConfigOptions.isValid()) { + if (syncConfig.getSyncMode() == SyncMode.CHECKPOINT && checkpointConfigOptions.isValid()) { validators.add( new CheckpointBlocksPeerValidator( protocolSchedule, diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index c6ffbdc1759..efeff412d62 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -1107,7 +1107,7 @@ public void syncMode_invalid() { assertThat(commandOutput.toString(UTF_8)).isEmpty(); assertThat(commandErrorOutput.toString(UTF_8)) .contains( - "Invalid value for option '--sync-mode': expected one of [FULL, FAST, SNAP, CHECKPOINT, X_SNAP, X_CHECKPOINT] (case-insensitive) but was 'bogus'"); + "Invalid value for option '--sync-mode': expected one of [FULL, FAST, SNAP, CHECKPOINT] (case-insensitive) but was 'bogus'"); } @Test diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java index 851c075290e..d1a54d4d3d3 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeers.java @@ -525,7 +525,7 @@ private void ethPeerStatusExchanged(final EthPeer peer) { peer.chainState().updateHeightEstimate(peerHeadBlockHeader.getNumber()); CompletableFuture isServingSnapFuture; - if (SyncMode.isCheckpointSync(syncMode) || SyncMode.isSnapSync(syncMode)) { + if (syncMode == SyncMode.SNAP || syncMode == SyncMode.CHECKPOINT) { // even if we have finished the snap sync, we still want to know if the peer is a snap // server isServingSnapFuture = diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java index 7605754a65c..a41ad1812b5 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java @@ -99,8 +99,8 @@ public DefaultSynchronizer( this::calculateTrailingPeerRequirements, metricsSystem); - if (SyncMode.isSnapSync(syncConfig.getSyncMode()) - || SyncMode.isCheckpointSync(syncConfig.getSyncMode())) { + if (syncConfig.getSyncMode() == SyncMode.SNAP + || syncConfig.getSyncMode() == SyncMode.CHECKPOINT) { SnapServerChecker.createAndSetSnapServerChecker(ethContext, metricsSystem); } @@ -145,7 +145,7 @@ public DefaultSynchronizer( worldStateStorageCoordinator, syncState, clock); - } else if (SyncMode.isCheckpointSync(syncConfig.getSyncMode())) { + } else if (syncConfig.getSyncMode() == SyncMode.CHECKPOINT) { this.fastSyncFactory = () -> CheckpointDownloaderFactory.createCheckpointDownloader( diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/SyncMode.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/SyncMode.java index 4f3affbe555..c69a128c54d 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/SyncMode.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/SyncMode.java @@ -27,35 +27,13 @@ public enum SyncMode { // Perform snapsync SNAP, // Perform snapsync but starting from a checkpoint instead of starting from genesis - CHECKPOINT, - // Deprecated and will be removed in 24.4.0 (X_SNAP and X_CHECKPOINT) - X_SNAP, - X_CHECKPOINT; + CHECKPOINT; public String normalize() { - if (this.toString().startsWith("X_")) { - // removes X_ at the beginning - return StringUtils.capitalize(this.toString().substring(2).toLowerCase(Locale.ROOT)); - } - return StringUtils.capitalize(this.toString().toLowerCase(Locale.ROOT)); } public static boolean isFullSync(final SyncMode syncMode) { - return !EnumSet.of( - SyncMode.FAST, - SyncMode.SNAP, - SyncMode.X_SNAP, - SyncMode.CHECKPOINT, - SyncMode.X_CHECKPOINT) - .contains(syncMode); - } - - public static boolean isCheckpointSync(final SyncMode syncMode) { - return X_CHECKPOINT.equals(syncMode) || CHECKPOINT.equals(syncMode); - } - - public static boolean isSnapSync(final SyncMode syncMode) { - return X_SNAP.equals(syncMode) || SNAP.equals(syncMode); + return !EnumSet.of(SyncMode.FAST, SyncMode.SNAP, SyncMode.CHECKPOINT).contains(syncMode); } } From 965e757d81072f31d2a44bb5757ff46f7d102e36 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 12 Jul 2024 08:43:44 -0600 Subject: [PATCH 12/12] EOF Reference Test Fixes (#7306) Fix a number of issues found in reference tests and evmone tests. - Be tolerant of more nulls in json - Support ContainerKind in reference tests - re-order EXTCALL oeprands - correct return value for REVERT in EXT*CALL - re-order EOFCREATE code validation Signed-off-by: Danno Ferrin --- .../GeneralStateTestCaseSpec.java | 6 +- .../ReferenceTestProtocolSchedules.java | 5 ++ .../ethereum/eof/EOFReferenceTestTools.java | 72 +++++++++++++------ .../templates/EOFReferenceTest.java.template | 3 +- .../besu/evm/code/CodeFactory.java | 4 ++ .../org/hyperledger/besu/evm/code/CodeV1.java | 2 +- .../hyperledger/besu/evm/code/EOFLayout.java | 12 +++- .../operation/AbstractCreateOperation.java | 28 ++++---- .../operation/AbstractExtCallOperation.java | 6 ++ .../besu/evm/operation/ExtCallOperation.java | 4 +- .../operation/ExtDelegateCallOperation.java | 3 - .../evm/operation/ExtStaticCallOperation.java | 3 - .../evm/operations/ExtCallOperationTest.java | 2 +- .../besu/testutil/JsonTestParameters.java | 8 ++- 14 files changed, 107 insertions(+), 51 deletions(-) diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java index 73735fdb0e0..affc6e46010 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java @@ -56,10 +56,12 @@ private Map> generate( final ReferenceTestWorldState initialWorldState, final Map> postSections, final StateTestVersionedTransaction versionedTransaction) { - + if (initialWorldState == null) { + return Map.of(); + } initialWorldState.persist(null); final Map> res = - new LinkedHashMap<>(postSections.size()); + LinkedHashMap.newLinkedHashMap(postSections.size()); for (final Map.Entry> entry : postSections.entrySet()) { final String eip = entry.getKey(); final List post = entry.getValue(); diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java index 4d62c314ae3..8ac419f7a68 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java @@ -91,6 +91,11 @@ public static ReferenceTestProtocolSchedules create(final StubGenesisConfigOptio "CancunToPragueAtTime15k", createSchedule(genesisStub.clone().cancunTime(0).pragueTime(15000))); builder.put("Prague", createSchedule(genesisStub.clone().pragueEOFTime(0))); + builder.put("Osaka", createSchedule(genesisStub.clone().futureEipsTime(0))); + builder.put("Amsterdam", createSchedule(genesisStub.clone().futureEipsTime(0))); + builder.put("Bogota", createSchedule(genesisStub.clone().futureEipsTime(0))); + builder.put("Polis", createSchedule(genesisStub.clone().futureEipsTime(0))); + builder.put("Bangkok", createSchedule(genesisStub.clone().futureEipsTime(0))); builder.put("Future_EIPs", createSchedule(genesisStub.clone().futureEipsTime(0))); builder.put("Experimental_EIPs", createSchedule(genesisStub.clone().experimentalEipsTime(0))); return new ReferenceTestProtocolSchedules(builder.build()); diff --git a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/eof/EOFReferenceTestTools.java b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/eof/EOFReferenceTestTools.java index 58f8c025297..a15bcb24189 100644 --- a/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/eof/EOFReferenceTestTools.java +++ b/ethereum/referencetests/src/reference-test/java/org/hyperledger/besu/ethereum/eof/EOFReferenceTestTools.java @@ -21,13 +21,19 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Map.Entry; + import org.apache.tuweni.bytes.Bytes; + import org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec; +import org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec.TestResult; import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules; import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.code.CodeInvalid; +import org.hyperledger.besu.evm.code.CodeV1; import org.hyperledger.besu.evm.code.EOFLayout; +import org.hyperledger.besu.evm.code.EOFLayout.EOFContainerMode; import org.hyperledger.besu.testutil.JsonTestParameters; public class EOFReferenceTestTools { @@ -43,13 +49,18 @@ public class EOFReferenceTestTools { JsonTestParameters.create(EOFTestCaseSpec.class, EOFTestCaseSpec.TestResult.class) .generator( (testName, fullPath, eofSpec, collector) -> { + if (eofSpec.getVector() == null) { + return; + } final Path path = Path.of(fullPath).getParent().getFileName(); final String prefix = path + "/" + testName + "-"; for (final Map.Entry entry : eofSpec.getVector().entrySet()) { final String name = entry.getKey(); final Bytes code = Bytes.fromHexString(entry.getValue().code()); - for (final var result : entry.getValue().results().entrySet()) { + final String containerKind = entry.getValue().containerKind(); + for (final Entry result : + entry.getValue().results().entrySet()) { final String eip = result.getKey(); final boolean runTest = EIPS_TO_RUN.contains(eip); collector.add( @@ -57,6 +68,7 @@ public class EOFReferenceTestTools { fullPath, eip, code, + containerKind, result.getValue(), runTest); } @@ -72,7 +84,7 @@ public class EOFReferenceTestTools { params.ignore("EOF1_undefined_opcodes_186"); // embedded containers rules changed - params.ignore("efValidation/EOF1_embedded_container-Prague\\[EOF1_embedded_container_\\d+\\]"); + params.ignore("efValidation/EOF1_embedded_container-Prague\\[EOF1_embedded_container_\\d+\\]"); // truncated data is only allowed in embedded containers params.ignore("ori/validInvalid-Prague\\[validInvalid_48\\]"); @@ -101,7 +113,10 @@ public static Collection generateTestParametersForConfig(final String[ @SuppressWarnings("java:S5960") // This is not production code, this is testing code. public static void executeTest( - final String fork, final Bytes code, final EOFTestCaseSpec.TestResult expected) { + final String fork, + final Bytes code, + final String containerKind, + final EOFTestCaseSpec.TestResult expected) { EVM evm = ReferenceTestProtocolSchedules.create().geSpecByName(fork).getEvm(); assertThat(evm).isNotNull(); @@ -112,23 +127,40 @@ public static void executeTest( EOFLayout layout = EOFLayout.parseEOF(code); if (layout.isValid()) { - Code parsedCode = evm.getCodeUncached(code); - assertThat(parsedCode.isValid()) - .withFailMessage( - () -> - EOFLayout.parseEOF(code).prettyPrint() - + "\nExpected exception :" - + expected.exception() - + " actual exception :" - + (parsedCode.isValid() - ? null - : ((CodeInvalid) parsedCode).getInvalidReason())) - .isEqualTo(expected.result()); - - if (expected.result()) { - assertThat(code) - .withFailMessage("Container round trip failed") - .isEqualTo(layout.writeContainer(null)); + Code parsedCode; + if ("INITCODE".equals(containerKind)) { + parsedCode = evm.getCodeForCreation(code); + } else { + parsedCode = evm.getCodeUncached(code); + } + if ("EOF_IncompatibleContainerKind".equals(expected.exception()) && parsedCode.isValid()) { + EOFContainerMode expectedMode = + EOFContainerMode.valueOf(containerKind == null ? "RUNTIME" : containerKind); + EOFContainerMode containerMode = + ((CodeV1) parsedCode).getEofLayout().containerMode().get(); + EOFContainerMode actualMode = + containerMode == null ? EOFContainerMode.RUNTIME : containerMode; + assertThat(actualMode) + .withFailMessage("Code did not parse to valid containerKind of " + expectedMode) + .isNotEqualTo(expectedMode); + } else { + assertThat(parsedCode.isValid()) + .withFailMessage( + () -> + EOFLayout.parseEOF(code).prettyPrint() + + "\nExpected exception :" + + expected.exception() + + " actual exception :" + + (parsedCode.isValid() + ? null + : ((CodeInvalid) parsedCode).getInvalidReason())) + .isEqualTo(expected.result()); + + if (expected.result()) { + assertThat(code) + .withFailMessage("Container round trip failed") + .isEqualTo(layout.writeContainer(null)); + } } } else { assertThat(layout.isValid()) diff --git a/ethereum/referencetests/src/reference-test/templates/EOFReferenceTest.java.template b/ethereum/referencetests/src/reference-test/templates/EOFReferenceTest.java.template index 5f5cafd6760..d9e52403d78 100644 --- a/ethereum/referencetests/src/reference-test/templates/EOFReferenceTest.java.template +++ b/ethereum/referencetests/src/reference-test/templates/EOFReferenceTest.java.template @@ -34,9 +34,10 @@ public class %%TESTS_NAME%% { final String name, final String fork, final Bytes code, + final String containerKind, final EOFTestCaseSpec.TestResult results, final boolean runTest) { assumeTrue(runTest, "Test " + name + " was ignored"); - executeTest(fork, code, results); + executeTest(fork, code, containerKind, results); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeFactory.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeFactory.java index a0bba703718..18561001205 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeFactory.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeFactory.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.evm.code; import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.code.EOFLayout.EOFContainerMode; import javax.annotation.Nonnull; @@ -94,6 +95,9 @@ public Code createCode(final Bytes bytes, final boolean createTransaction) { } final EOFLayout layout = EOFLayout.parseEOF(bytes, !createTransaction); + if (createTransaction) { + layout.containerMode().set(EOFContainerMode.INITCODE); + } return createCode(layout); } else { return new CodeV0(bytes); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java index 50ffdd41061..a1517d1d7f4 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/CodeV1.java @@ -111,7 +111,7 @@ public Optional getSubContainer(final int index, final Bytes auxData, fina codeToLoad = subcontainerLayout.container(); } - Code subContainerCode = evm.getCodeForCreation(codeToLoad); + Code subContainerCode = evm.getCodeUncached(codeToLoad); return subContainerCode.isValid() && subContainerCode.getEofVersion() > 0 ? Optional.of(subContainerCode) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/code/EOFLayout.java b/evm/src/main/java/org/hyperledger/besu/evm/code/EOFLayout.java index 8b41a979231..401308715af 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/code/EOFLayout.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/code/EOFLayout.java @@ -57,9 +57,16 @@ public record EOFLayout( String invalidReason, AtomicReference containerMode) { - enum EOFContainerMode { + /** + * Enum tracking the useage mode of an EOF container. Detected either by opcode usage or + * determined by the source. + */ + public enum EOFContainerMode { + /** Usage mode is unknown */ UNKNOWN, + /** Usage mode is as init code */ INITCODE, + /** Usage mode is as deployed or runtime code */ RUNTIME } @@ -324,6 +331,9 @@ public static EOFLayout parseEOF(final Bytes container, final boolean strictSize Bytes subcontainer = container.slice(pos, subcontianerSize); pos += subcontianerSize; EOFLayout subLayout = EOFLayout.parseEOF(subcontainer, false); + if (subLayout.container.size() < subcontainer.size()) { + return invalidLayout(container, version, "excess data in subcontainer"); + } if (!subLayout.isValid()) { String invalidSubReason = subLayout.invalidReason; return invalidLayout( diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java index f026fd7110b..f20c2914a09 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java @@ -214,32 +214,32 @@ protected Bytes getInputData(final MessageFrame frame) { private void complete(final MessageFrame frame, final MessageFrame childFrame, final EVM evm) { frame.setState(MessageFrame.State.CODE_EXECUTING); - Code outputCode = - (childFrame.getCreatedCode() != null) - ? childFrame.getCreatedCode() - : evm.getCodeForCreation(childFrame.getOutputData()); + frame.incrementRemainingGas(childFrame.getRemainingGas()); + frame.addLogs(childFrame.getLogs()); + frame.addSelfDestructs(childFrame.getSelfDestructs()); + frame.addCreates(childFrame.getCreates()); frame.popStackItems(getStackItemsConsumed()); - if (outputCode.isValid()) { - frame.incrementRemainingGas(childFrame.getRemainingGas()); - frame.addLogs(childFrame.getLogs()); - frame.addSelfDestructs(childFrame.getSelfDestructs()); - frame.addCreates(childFrame.getCreates()); - - if (childFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { + if (childFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) { + Code outputCode = + (childFrame.getCreatedCode() != null) + ? childFrame.getCreatedCode() + : evm.getCodeForCreation(childFrame.getOutputData()); + if (outputCode.isValid()) { Address createdAddress = childFrame.getContractAddress(); frame.pushStackItem(Words.fromAddress(createdAddress)); + frame.setReturnData(Bytes.EMPTY); onSuccess(frame, createdAddress); } else { + frame.getWorldUpdater().deleteAccount(childFrame.getRecipientAddress()); frame.setReturnData(childFrame.getOutputData()); frame.pushStackItem(LEGACY_FAILURE_STACK_ITEM); - onFailure(frame, childFrame.getExceptionalHaltReason()); + onInvalid(frame, (CodeInvalid) outputCode); } } else { - frame.getWorldUpdater().deleteAccount(childFrame.getRecipientAddress()); frame.setReturnData(childFrame.getOutputData()); frame.pushStackItem(LEGACY_FAILURE_STACK_ITEM); - onInvalid(frame, (CodeInvalid) outputCode); + onFailure(frame, childFrame.getExceptionalHaltReason()); } final int currentPC = frame.getPC(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java index 2c0535b197b..43d7e343ff1 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractExtCallOperation.java @@ -38,6 +38,8 @@ public abstract class AbstractExtCallOperation extends AbstractCallOperation { static final int STACK_TO = 0; + static final int STACK_INPUT_OFFSET = 1; + static final int STACK_INPUT_LENGTH = 2; /** EXT*CALL response indicating success */ public static final Bytes EOF1_SUCCESS_STACK_ITEM = Bytes.EMPTY; @@ -195,6 +197,10 @@ Bytes getCallResultStackItem(final MessageFrame childFrame) { return switch (childFrame.getState()) { case COMPLETED_SUCCESS -> EOF1_SUCCESS_STACK_ITEM; case EXCEPTIONAL_HALT -> EOF1_EXCEPTION_STACK_ITEM; + case COMPLETED_FAILED -> + childFrame.getExceptionalHaltReason().isPresent() + ? EOF1_FAILURE_STACK_ITEM + : EOF1_EXCEPTION_STACK_ITEM; default -> EOF1_FAILURE_STACK_ITEM; }; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java index bf986a572e5..5914f0e966a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCallOperation.java @@ -24,9 +24,7 @@ /** The Call operation. */ public class ExtCallOperation extends AbstractExtCallOperation { - static final int STACK_VALUE = 1; - static final int STACK_INPUT_OFFSET = 2; - static final int STACK_INPUT_LENGTH = 3; + static final int STACK_VALUE = 3; /** * Instantiates a new Call operation. diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java index bddb337c73b..db851ee5a0b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtDelegateCallOperation.java @@ -24,9 +24,6 @@ /** The Delegate call operation. */ public class ExtDelegateCallOperation extends AbstractExtCallOperation { - static final int STACK_INPUT_OFFSET = 1; - static final int STACK_INPUT_LENGTH = 2; - /** * Instantiates a new Delegate call operation. * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java index 3a202f46e71..ef3dc042d9c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtStaticCallOperation.java @@ -24,9 +24,6 @@ /** The Static call operation. */ public class ExtStaticCallOperation extends AbstractExtCallOperation { - static final int STACK_INPUT_OFFSET = 1; - static final int STACK_INPUT_LENGTH = 2; - /** * Instantiates a new Static call operation. * diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCallOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCallOperationTest.java index 984ce28027f..946dd0b52eb 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCallOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/ExtCallOperationTest.java @@ -211,9 +211,9 @@ void callWithValueTest( new TestMessageFrameBuilder() .initialGas(parentGas) .pushStackItem(CONTRACT_ADDRESS) // canary for non-returning + .pushStackItem(valueSent) .pushStackItem(Bytes.EMPTY) .pushStackItem(Bytes.EMPTY) - .pushStackItem(valueSent) .pushStackItem(CONTRACT_ADDRESS) .worldUpdater(worldUpdater) .isStatic(isStatic) diff --git a/testutil/src/main/java/org/hyperledger/besu/testutil/JsonTestParameters.java b/testutil/src/main/java/org/hyperledger/besu/testutil/JsonTestParameters.java index 062e14cfc7f..f95ac9348ea 100644 --- a/testutil/src/main/java/org/hyperledger/besu/testutil/JsonTestParameters.java +++ b/testutil/src/main/java/org/hyperledger/besu/testutil/JsonTestParameters.java @@ -96,6 +96,7 @@ public void add( * @param fullPath the full path of the test * @param fork the fork to be tested * @param code the code to be tested + * @param containerKind the containerKind, if specified * @param value the value * @param runTest the run test */ @@ -104,10 +105,13 @@ public void add( final String fullPath, final String fork, final Bytes code, + final String containerKind, final S value, final boolean runTest) { testParameters.add( - new Object[] {name, fork, code, value, runTest && includes(name) && includes(fullPath)}); + new Object[] { + name, fork, code, containerKind, value, runTest && includes(name) && includes(fullPath) + }); } private boolean includes(final String name) { @@ -304,7 +308,7 @@ private Collection getFilteredFiles(final String[] paths) { final List files = new ArrayList<>(); for (final String path : paths) { final URL url = classLoader.getResource(path); - checkState(url != null, "Cannot find test directory " + path); + checkState(url != null, "Cannot find test directory %s", path); final Path dir; try { dir = Paths.get(url.toURI());