From d7ee7043bbe799b579c90507447e10214d9466a1 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Mon, 23 Sep 2024 17:09:21 +0200 Subject: [PATCH] Interrupt candidate tx execution on block creation timeout Signed-off-by: Fabio Di Fabio --- .../txselection/BlockTransactionSelector.java | 19 ++- .../InterruptibleOperationTracer.java | 143 ++++++++++++++++++ .../ProcessingResultTransactionSelector.java | 3 +- .../mainnet/MainnetTransactionProcessor.java | 6 + .../transaction/TransactionInvalidReason.java | 1 + 5 files changed, 164 insertions(+), 8 deletions(-) create mode 100644 ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/InterruptibleOperationTracer.java diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java index e07b43b9904..4f119a92b88 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java @@ -55,6 +55,7 @@ import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; @@ -97,7 +98,7 @@ public class BlockTransactionSelector { new TransactionSelectionResults(); private final List transactionSelectors; private final PluginTransactionSelector pluginTransactionSelector; - private final BlockAwareOperationTracer pluginOperationTracer; + private final BlockAwareOperationTracer operationTracer; private final EthScheduler ethScheduler; private final AtomicBoolean isTimeout = new AtomicBoolean(false); private final long blockTxsSelectionMaxTime; @@ -139,7 +140,8 @@ public BlockTransactionSelector( transactionPool); transactionSelectors = createTransactionSelectors(blockSelectionContext); this.pluginTransactionSelector = pluginTransactionSelector; - this.pluginOperationTracer = pluginTransactionSelector.getOperationTracer(); + this.operationTracer = + new InterruptibleOperationTracer(pluginTransactionSelector.getOperationTracer()); blockWorldStateUpdater = worldState.updater(); blockTxsSelectionMaxTime = miningParameters.getBlockTxsSelectionMaxTime(); } @@ -178,15 +180,17 @@ public TransactionSelectionResults buildTransactionListForBlock() { } private void timeLimitedSelection() { - final var txSelection = - ethScheduler.scheduleBlockCreationTask( + final var txSelectionTask = + new FutureTask( () -> blockSelectionContext .transactionPool() - .selectTransactions(this::evaluateTransaction)); + .selectTransactions(this::evaluateTransaction), + null); + ethScheduler.scheduleBlockCreationTask(txSelectionTask); try { - txSelection.get(blockTxsSelectionMaxTime, TimeUnit.MILLISECONDS); + txSelectionTask.get(blockTxsSelectionMaxTime, TimeUnit.MILLISECONDS); } catch (InterruptedException | ExecutionException e) { if (isCancelled.get()) { throw new CancellationException("Cancelled during transaction selection"); @@ -196,6 +200,7 @@ private void timeLimitedSelection() { // synchronize since we want to be sure that there is no concurrent state update synchronized (isTimeout) { isTimeout.set(true); + txSelectionTask.cancel(true); } LOG.warn( "Interrupting the selection of transactions for block inclusion as it exceeds the maximum configured duration of " @@ -337,7 +342,7 @@ private TransactionProcessingResult processTransaction( blockSelectionContext.pendingBlockHeader(), pendingTransaction.getTransaction(), blockSelectionContext.miningBeneficiary(), - pluginOperationTracer, + operationTracer, blockHashLookup, false, TransactionValidationParams.mining(), diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/InterruptibleOperationTracer.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/InterruptibleOperationTracer.java new file mode 100644 index 00000000000..eb7f34bd9a0 --- /dev/null +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/InterruptibleOperationTracer.java @@ -0,0 +1,143 @@ +/* + * 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.blockcreation.txselection; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.log.Log; +import org.hyperledger.besu.evm.operation.Operation; +import org.hyperledger.besu.evm.worldstate.WorldView; +import org.hyperledger.besu.plugin.data.BlockBody; +import org.hyperledger.besu.plugin.data.BlockHeader; +import org.hyperledger.besu.plugin.data.ProcessableBlockHeader; +import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.apache.tuweni.bytes.Bytes; + +public class InterruptibleOperationTracer implements BlockAwareOperationTracer { + private final BlockAwareOperationTracer delegate; + + public InterruptibleOperationTracer(final BlockAwareOperationTracer delegate) { + this.delegate = delegate; + } + + @Override + public void traceStartBlock(final BlockHeader blockHeader, final BlockBody blockBody) { + delegate.traceStartBlock(blockHeader, blockBody); + } + + @Override + public void traceEndBlock(final BlockHeader blockHeader, final BlockBody blockBody) { + delegate.traceEndBlock(blockHeader, blockBody); + } + + @Override + public void traceStartBlock(final ProcessableBlockHeader processableBlockHeader) { + delegate.traceStartBlock(processableBlockHeader); + } + + @Override + public boolean isExtendedTracing() { + return delegate.isExtendedTracing(); + } + + @Override + public void tracePreExecution(final MessageFrame frame) { + checkInterrupt(); + delegate.tracePreExecution(frame); + } + + @Override + public void tracePostExecution( + final MessageFrame frame, final Operation.OperationResult operationResult) { + checkInterrupt(); + delegate.tracePostExecution(frame, operationResult); + } + + @Override + public void tracePrecompileCall( + final MessageFrame frame, final long gasRequirement, final Bytes output) { + checkInterrupt(); + delegate.tracePrecompileCall(frame, gasRequirement, output); + } + + @Override + public void traceAccountCreationResult( + final MessageFrame frame, final Optional haltReason) { + checkInterrupt(); + delegate.traceAccountCreationResult(frame, haltReason); + } + + @Override + public void tracePrepareTransaction(final WorldView worldView, final Transaction transaction) { + delegate.tracePrepareTransaction(worldView, transaction); + } + + @Override + public void traceStartTransaction(final WorldView worldView, final Transaction transaction) { + delegate.traceStartTransaction(worldView, transaction); + } + + @Override + public void traceBeforeRewardTransaction( + final WorldView worldView, final Transaction tx, final Wei miningReward) { + delegate.traceBeforeRewardTransaction(worldView, tx, miningReward); + } + + @Override + public void traceEndTransaction( + final WorldView worldView, + final Transaction tx, + final boolean status, + final Bytes output, + final List logs, + final long gasUsed, + final Set
selfDestructs, + final long timeNs) { + delegate.traceEndTransaction( + worldView, tx, status, output, logs, gasUsed, selfDestructs, timeNs); + } + + @Override + public void traceContextEnter(final MessageFrame frame) { + checkInterrupt(); + delegate.traceContextEnter(frame); + } + + @Override + public void traceContextReEnter(final MessageFrame frame) { + checkInterrupt(); + delegate.traceContextReEnter(frame); + } + + @Override + public void traceContextExit(final MessageFrame frame) { + checkInterrupt(); + delegate.traceContextExit(frame); + } + + private void checkInterrupt() { + if (Thread.interrupted()) { + throw new RuntimeException(new InterruptedException("Transaction execution interrupted")); + } + } +} diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java index 9eed2bff09e..5a9fd6030cd 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java @@ -112,6 +112,7 @@ private TransactionSelectionResult transactionSelectionResultForInvalidResult( private boolean isTransientValidationError(final TransactionInvalidReason invalidReason) { return invalidReason.equals(TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE) || invalidReason.equals(TransactionInvalidReason.GAS_PRICE_BELOW_CURRENT_BASE_FEE) - || invalidReason.equals(TransactionInvalidReason.NONCE_TOO_HIGH); + || invalidReason.equals(TransactionInvalidReason.NONCE_TOO_HIGH) + || invalidReason.equals(TransactionInvalidReason.EXECUTION_INTERRUPTED); } } 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 91c964525e0..30a1ea8e762 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 @@ -599,6 +599,12 @@ public TransactionProcessingResult processTransaction( EMPTY_ADDRESS_SET, 0L); + final var cause = re.getCause(); + if (cause != null && cause instanceof InterruptedException) { + return TransactionProcessingResult.invalid( + ValidationResult.invalid(TransactionInvalidReason.EXECUTION_INTERRUPTED)); + } + LOG.error("Critical Exception Processing Transaction", re); return TransactionProcessingResult.invalid( ValidationResult.invalid( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java index bf5c6b3eb22..78802dce93b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java @@ -46,6 +46,7 @@ public enum TransactionInvalidReason { MAX_FEE_PER_GAS_BELOW_CURRENT_BASE_FEE, TX_FEECAP_EXCEEDED, INTERNAL_ERROR, + EXECUTION_INTERRUPTED, TX_POOL_DISABLED, INVALID_BLOBS, PLUGIN_TX_POOL_VALIDATOR,