diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index e06aa8c9641..0c5f170931b 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -48,7 +48,7 @@ import org.hyperledger.besu.plugin.services.StorageService; import org.hyperledger.besu.plugin.services.TransactionSelectionService; import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBPlugin; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory; import org.hyperledger.besu.services.BesuConfigurationImpl; import org.hyperledger.besu.services.BesuEventsImpl; @@ -185,7 +185,7 @@ public void startNode(final BesuNode node) { final int maxPeers = 25; - final Optional transactionSelectorFactory = + final Optional transactionSelectorFactory = getTransactionSelectorFactory(besuPluginContext); final PluginTransactionValidatorFactory pluginTransactionValidatorFactory = @@ -323,7 +323,7 @@ public String getConsoleContents() { throw new RuntimeException("Console contents can only be captured in process execution"); } - private Optional getTransactionSelectorFactory( + private Optional getTransactionSelectorFactory( final BesuPluginContextImpl besuPluginContext) { final Optional txSelectionService = besuPluginContext.getService(TransactionSelectionService.class); 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 75c44198877..a1818d281f6 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -177,7 +177,7 @@ import org.hyperledger.besu.plugin.services.securitymodule.SecurityModule; import org.hyperledger.besu.plugin.services.storage.PrivacyKeyValueStorageFactory; import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBPlugin; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory; import org.hyperledger.besu.services.BesuEventsImpl; import org.hyperledger.besu.services.BesuPluginContextImpl; @@ -2294,7 +2294,7 @@ public BesuControllerBuilder getControllerBuilder() { } @NotNull - private Optional getTransactionSelectorFactory() { + private Optional getTransactionSelectorFactory() { final Optional txSelectionService = besuPluginContext.getService(TransactionSelectionService.class); return txSelectionService.isPresent() ? txSelectionService.get().get() : Optional.empty(); 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 d023cdd76da..c297bafde47 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -95,7 +95,7 @@ import org.hyperledger.besu.metrics.ObservableMetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory; import java.io.Closeable; @@ -182,7 +182,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides private NetworkingConfiguration networkingConfiguration; private Boolean randomPeerPriority; - private Optional transactionSelectorFactory = Optional.empty(); + private Optional transactionSelectorFactory = Optional.empty(); /** the Dagger configured context that can provide dependencies */ protected Optional besuComponent = Optional.empty(); @@ -535,7 +535,7 @@ public BesuControllerBuilder randomPeerPriority(final Boolean randomPeerPriority * @return the besu controller builder */ public BesuControllerBuilder transactionSelectorFactory( - final Optional transactionSelectorFactory) { + final Optional transactionSelectorFactory) { this.transactionSelectorFactory = transactionSelectorFactory; return this; } @@ -1035,7 +1035,7 @@ protected ProtocolContext createProtocolContext( final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, final ConsensusContextFactory consensusContextFactory, - final Optional transactionSelectorFactory) { + final Optional transactionSelectorFactory) { return ProtocolContext.init( blockchain, worldStateArchive, diff --git a/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java index 6d0d02b22f0..921289c66e5 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java @@ -62,7 +62,7 @@ import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.metrics.ObservableMetricsSystem; import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; import java.math.BigInteger; import java.nio.file.Path; @@ -176,7 +176,7 @@ protected ProtocolContext createProtocolContext( final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, final ConsensusContextFactory consensusContextFactory, - final Optional transactionSelectorFactory) { + final Optional transactionSelectorFactory) { return MigratingProtocolContext.init( blockchain, worldStateArchive, diff --git a/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java index 9323f3b81d6..6db71ef81ca 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java @@ -60,7 +60,7 @@ import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.metrics.ObservableMetricsSystem; import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; import java.math.BigInteger; import java.nio.file.Path; @@ -190,7 +190,7 @@ protected ProtocolContext createProtocolContext( final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, final ConsensusContextFactory consensusContextFactory, - final Optional transactionSelectorFactory) { + final Optional transactionSelectorFactory) { final ProtocolContext protocolContext = super.createProtocolContext( blockchain, diff --git a/besu/src/main/java/org/hyperledger/besu/services/TransactionSelectionServiceImpl.java b/besu/src/main/java/org/hyperledger/besu/services/TransactionSelectionServiceImpl.java index 42b1ae88a76..3175fe731f9 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/TransactionSelectionServiceImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/TransactionSelectionServiceImpl.java @@ -15,23 +15,23 @@ package org.hyperledger.besu.services; import org.hyperledger.besu.plugin.services.TransactionSelectionService; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; import java.util.Optional; /** The Transaction Selection service implementation. */ public class TransactionSelectionServiceImpl implements TransactionSelectionService { - private Optional factory = Optional.empty(); + private Optional factory = Optional.empty(); @Override - public Optional get() { + public Optional get() { return factory; } @Override public void registerTransactionSelectorFactory( - final TransactionSelectorFactory transactionSelectorFactory) { + final PluginTransactionSelectorFactory transactionSelectorFactory) { factory = Optional.ofNullable(transactionSelectorFactory); } } diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/MigratingProtocolContext.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/MigratingProtocolContext.java index e4304a3dbd0..32099e23fdf 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/MigratingProtocolContext.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/MigratingProtocolContext.java @@ -20,7 +20,7 @@ import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; import java.util.Optional; @@ -41,7 +41,7 @@ public MigratingProtocolContext( final MutableBlockchain blockchain, final WorldStateArchive worldStateArchive, final ForksSchedule consensusContextSchedule, - final Optional transactionSelectorFactory) { + final Optional transactionSelectorFactory) { super(blockchain, worldStateArchive, null, transactionSelectorFactory); this.consensusContextSchedule = consensusContextSchedule; } @@ -61,7 +61,7 @@ public static ProtocolContext init( final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, final ConsensusContextFactory consensusContextFactory, - final Optional transactionSelectorFactory) { + final Optional transactionSelectorFactory) { final ConsensusContext consensusContext = consensusContextFactory.create(blockchain, worldStateArchive, protocolSchedule); final MigratingContext migratingContext = consensusContext.as(MigratingContext.class); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java index b27c84bf04e..48ea3f9c069 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcErrorConverter.java @@ -77,6 +77,8 @@ public static RpcErrorType convertTransactionInvalidReason( return RpcErrorType.TOTAL_BLOB_GAS_TOO_HIGH; case TX_POOL_DISABLED: return RpcErrorType.TX_POOL_DISABLED; + case PLUGIN_TX_VALIDATOR: + return RpcErrorType.PLUGIN_TX_VALIDATOR; default: return RpcErrorType.INTERNAL_ERROR; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransaction.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransaction.java index 82cf4410a51..6f80a372ffe 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransaction.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthSendRawTransaction.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; @@ -33,6 +34,7 @@ import java.util.function.Supplier; import com.google.common.base.Suppliers; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -90,12 +92,29 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { () -> new JsonRpcSuccessResponse( requestContext.getRequest().getId(), transaction.getHash().toString()), - errorReason -> - sendEmptyHashOnInvalidBlock - ? new JsonRpcSuccessResponse( - requestContext.getRequest().getId(), Hash.EMPTY.toString()) - : new JsonRpcErrorResponse( - requestContext.getRequest().getId(), - JsonRpcErrorConverter.convertTransactionInvalidReason(errorReason))); + errorReason -> getJsonRpcResponse(requestContext, errorReason, validationResult)); + } + + @NotNull + private JsonRpcResponse getJsonRpcResponse( + final JsonRpcRequestContext requestContext, + final TransactionInvalidReason errorReason, + final ValidationResult validationResult) { + if (sendEmptyHashOnInvalidBlock) { + return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), Hash.EMPTY.toString()); + } else { + if (errorReason == TransactionInvalidReason.PLUGIN_TX_VALIDATOR) { + final RpcErrorType rpcErrorType = + JsonRpcErrorConverter.convertTransactionInvalidReason( + validationResult.getInvalidReason()); + return new JsonRpcErrorResponse( + requestContext.getRequest().getId(), + new JsonRpcError(rpcErrorType.getCode(), validationResult.getErrorMessage(), null)); + } else { + return new JsonRpcErrorResponse( + requestContext.getRequest().getId(), + JsonRpcErrorConverter.convertTransactionInvalidReason(errorReason)); + } + } } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcErrorResponse.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcErrorResponse.java index ed8f275c07a..3dffff4956a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcErrorResponse.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcErrorResponse.java @@ -107,7 +107,7 @@ private RpcErrorType findErrorType(final int code, final String message) { return Arrays.stream(RpcErrorType.values()) .filter(e -> e.getCode() == code && message.startsWith(e.getMessage())) .findFirst() - .get(); + .orElse(RpcErrorType.UNKNOWN); } @SuppressWarnings({"unchecked", "rawtypes"}) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java index c3ef46016d9..268af90a0cc 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java @@ -74,6 +74,7 @@ public enum RpcErrorType { LOWER_NONCE_INVALID_TRANSACTION_EXISTS( -32000, "An invalid transaction with a lower nonce exists"), TOTAL_BLOB_GAS_TOO_HIGH(-32000, "Total blob gas too high"), + PLUGIN_TX_VALIDATOR(-32000, "Plugin has marked the transaction as invalid"), // Execution engine failures UNKNOWN_PAYLOAD(-32001, "Payload does not exist / is not available"), @@ -210,7 +211,9 @@ public enum RpcErrorType { // Retesteth Errors BLOCK_RLP_IMPORT_ERROR(-32000, "Could not decode RLP for Block"), - BLOCK_IMPORT_ERROR(-32000, "Could not import Block"); + BLOCK_IMPORT_ERROR(-32000, "Could not import Block"), + + UNKNOWN(-32603, "Unknown internal error"); private final int code; private final String message; 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 7a86c928f5e..b89ce4d3a19 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 @@ -38,10 +38,11 @@ import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelector; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; import java.util.List; import java.util.Optional; @@ -84,7 +85,8 @@ public class BlockTransactionSelector { private final TransactionSelectionResults transactionSelectionResults = new TransactionSelectionResults(); private final List transactionSelectors; - private final TransactionSelector externalTransactionSelector; + private final PluginTransactionSelector pluginTransactionSelector; + private final OperationTracer pluginOperationTracer; public BlockTransactionSelector( final MainnetTransactionProcessor transactionProcessor, @@ -101,7 +103,7 @@ public BlockTransactionSelector( final FeeMarket feeMarket, final GasCalculator gasCalculator, final GasLimitCalculator gasLimitCalculator, - final Optional transactionSelectorFactory) { + final Optional transactionSelectorFactory) { this.transactionProcessor = transactionProcessor; this.blockchain = blockchain; this.worldState = worldState; @@ -119,10 +121,11 @@ public BlockTransactionSelector( miningBeneficiary, transactionPool); transactionSelectors = createTransactionSelectors(blockSelectionContext); - externalTransactionSelector = + pluginTransactionSelector = transactionSelectorFactory - .map(TransactionSelectorFactory::create) + .map(PluginTransactionSelectorFactory::create) .orElse(AllAcceptingTransactionSelector.INSTANCE); + pluginOperationTracer = pluginTransactionSelector.getOperationTracer(); } private List createTransactionSelectors( @@ -223,7 +226,7 @@ private TransactionSelectionResult evaluatePreProcessing( return result; } } - return externalTransactionSelector.evaluateTransactionPreProcessing(pendingTransaction); + return pluginTransactionSelector.evaluateTransactionPreProcessing(pendingTransaction); } /** @@ -248,7 +251,7 @@ private TransactionSelectionResult evaluatePostProcessing( return result; } } - return externalTransactionSelector.evaluateTransactionPostProcessing( + return pluginTransactionSelector.evaluateTransactionPostProcessing( pendingTransaction, processingResult); } @@ -269,6 +272,7 @@ private TransactionProcessingResult processTransaction( blockSelectionContext.processableBlockHeader(), pendingTransaction.getTransaction(), blockSelectionContext.miningBeneficiary(), + pluginOperationTracer, blockHashLookup, false, TransactionValidationParams.mining(), @@ -307,7 +311,7 @@ private TransactionSelectionResult handleTransactionSelected( transactionSelectionResults.updateSelected( pendingTransaction.getTransaction(), receipt, gasUsedByTransaction, blobGasUsed); - externalTransactionSelector.onTransactionSelected(pendingTransaction); + pluginTransactionSelector.onTransactionSelected(pendingTransaction, processingResult); return TransactionSelectionResult.SELECTED; } @@ -326,7 +330,7 @@ private TransactionSelectionResult handleTransactionNotSelected( final TransactionSelectionResult selectionResult) { transactionSelectionResults.updateNotSelected( pendingTransaction.getTransaction(), selectionResult); - externalTransactionSelector.onTransactionNotSelected(pendingTransaction, selectionResult); + pluginTransactionSelector.onTransactionNotSelected(pendingTransaction, selectionResult); return selectionResult; } diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AllAcceptingTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AllAcceptingTransactionSelector.java index 9d279afe73a..9032ddca575 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AllAcceptingTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/AllAcceptingTransactionSelector.java @@ -17,10 +17,10 @@ import org.hyperledger.besu.datatypes.PendingTransaction; import org.hyperledger.besu.plugin.data.TransactionProcessingResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelector; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; /** A TransactionSelector that unconditionally selects all transactions. */ -public class AllAcceptingTransactionSelector implements TransactionSelector { +public class AllAcceptingTransactionSelector implements PluginTransactionSelector { public static final AllAcceptingTransactionSelector INSTANCE = new AllAcceptingTransactionSelector(); diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java index 4c1f0018177..863d164df43 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java @@ -71,8 +71,8 @@ import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.plugin.services.MetricsSystem; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelector; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import java.math.BigInteger; @@ -557,9 +557,9 @@ public void transactionSelectionPluginShouldWork_PreProcessing() { final Transaction notSelectedInvalid = createTransaction(2, Wei.of(10), 21_000); ensureTransactionIsValid(notSelectedInvalid, 21_000, 0); - final TransactionSelectorFactory transactionSelectorFactory = + final PluginTransactionSelectorFactory transactionSelectorFactory = () -> - new TransactionSelector() { + new PluginTransactionSelector() { @Override public TransactionSelectionResult evaluateTransactionPreProcessing( final PendingTransaction pendingTransaction) { @@ -621,9 +621,9 @@ public void transactionSelectionPluginShouldWork_PostProcessing() { final Transaction selected3 = createTransaction(3, Wei.of(10), 21_000); ensureTransactionIsValid(selected3, maxGasUsedByTransaction, 0); - final TransactionSelectorFactory transactionSelectorFactory = + final PluginTransactionSelectorFactory transactionSelectorFactory = () -> - new TransactionSelector() { + new PluginTransactionSelector() { @Override public TransactionSelectionResult evaluateTransactionPreProcessing( final PendingTransaction pendingTransaction) { @@ -666,18 +666,17 @@ public TransactionSelectionResult evaluateTransactionPostProcessing( @Test public void transactionSelectionPluginShouldBeNotifiedWhenTransactionSelectionCompletes() { - final TransactionSelectorFactory transactionSelectorFactory = - mock(TransactionSelectorFactory.class); - TransactionSelector transactionSelector = spy(AllAcceptingTransactionSelector.INSTANCE); + final PluginTransactionSelectorFactory transactionSelectorFactory = + mock(PluginTransactionSelectorFactory.class); + PluginTransactionSelector transactionSelector = spy(AllAcceptingTransactionSelector.INSTANCE); when(transactionSelectorFactory.create()).thenReturn(transactionSelector); final Transaction transaction = createTransaction(0, Wei.of(10), 21_000); ensureTransactionIsValid(transaction, 21_000, 0); - final TransactionInvalidReason invalidReason = - TransactionInvalidReason.PLUGIN_TX_VALIDATOR_INVALIDATED; + final TransactionInvalidReason invalidReason = TransactionInvalidReason.PLUGIN_TX_VALIDATOR; final Transaction invalidTransaction = createTransaction(1, Wei.of(10), 21_000); - ensureTransactionIsInvalid(invalidTransaction, invalidReason); + ensureTransactionIsInvalid(invalidTransaction, TransactionInvalidReason.PLUGIN_TX_VALIDATOR); transactionPool.addRemoteTransactions(List.of(transaction, invalidTransaction)); createBlockSelectorWithTxSelPlugin( @@ -694,7 +693,8 @@ public void transactionSelectionPluginShouldBeNotifiedWhenTransactionSelectionCo ArgumentCaptor.forClass(PendingTransaction.class); // selected transaction must be notified to the selector - verify(transactionSelector).onTransactionSelected(argumentCaptor.capture()); + verify(transactionSelector) + .onTransactionSelected(argumentCaptor.capture(), any(TransactionProcessingResult.class)); PendingTransaction selected = argumentCaptor.getValue(); assertThat(selected.getTransaction()).isEqualTo(transaction); @@ -773,7 +773,7 @@ protected BlockTransactionSelector createBlockSelectorWithTxSelPlugin( final Address miningBeneficiary, final Wei blobGasPrice, final double minBlockOccupancyRatio, - final TransactionSelectorFactory transactionSelectorFactory) { + final PluginTransactionSelectorFactory transactionSelectorFactory) { final BlockTransactionSelector selector = new BlockTransactionSelector( transactionProcessor, @@ -854,7 +854,7 @@ protected void ensureTransactionIsValid(final Transaction tx) { protected void ensureTransactionIsValid( final Transaction tx, final long gasUsedByTransaction, final long gasRemaining) { when(transactionProcessor.processTransaction( - any(), any(), any(), eq(tx), any(), any(), anyBoolean(), any(), any())) + any(), any(), any(), eq(tx), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn( TransactionProcessingResult.successful( new ArrayList<>(), @@ -867,7 +867,7 @@ protected void ensureTransactionIsValid( protected void ensureTransactionIsInvalid( final Transaction tx, final TransactionInvalidReason invalidReason) { when(transactionProcessor.processTransaction( - any(), any(), any(), eq(tx), any(), any(), anyBoolean(), any(), any())) + any(), any(), any(), eq(tx), any(), any(), any(), anyBoolean(), any(), any())) .thenReturn(TransactionProcessingResult.invalid(ValidationResult.invalid(invalidReason))); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/ProtocolContext.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/ProtocolContext.java index 8c0fc460dc2..745c5640689 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/ProtocolContext.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/ProtocolContext.java @@ -18,7 +18,7 @@ import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; import java.util.Optional; @@ -31,7 +31,7 @@ public class ProtocolContext { private final MutableBlockchain blockchain; private final WorldStateArchive worldStateArchive; private final ConsensusContext consensusContext; - private final Optional transactionSelectorFactory; + private final Optional transactionSelectorFactory; private Optional synchronizer; @@ -46,7 +46,7 @@ public ProtocolContext( final MutableBlockchain blockchain, final WorldStateArchive worldStateArchive, final ConsensusContext consensusContext, - final Optional transactionSelectorFactory) { + final Optional transactionSelectorFactory) { this.blockchain = blockchain; this.worldStateArchive = worldStateArchive; this.consensusContext = consensusContext; @@ -59,7 +59,7 @@ public static ProtocolContext init( final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, final ConsensusContextFactory consensusContextFactory, - final Optional transactionSelectorFactory) { + final Optional transactionSelectorFactory) { return new ProtocolContext( blockchain, worldStateArchive, @@ -93,7 +93,7 @@ public Optional safeConsensusContext(final Class .map(klass::cast); } - public Optional getTransactionSelectorFactory() { + public Optional getTransactionSelectorFactory() { return transactionSelectorFactory; } } 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 3971b2f502b..6b9db98dea2 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 @@ -48,6 +48,7 @@ public enum TransactionInvalidReason { INTERNAL_ERROR, TX_POOL_DISABLED, INVALID_BLOBS, + PLUGIN_TX_VALIDATOR, // Private Transaction Invalid Reasons PRIVATE_TRANSACTION_INVALID, PRIVATE_TRANSACTION_FAILED, @@ -55,6 +56,5 @@ public enum TransactionInvalidReason { OFFCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST, PRIVATE_NONCE_TOO_HIGH, PRIVATE_VALUE_NOT_ZERO, - PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE, - PLUGIN_TX_VALIDATOR_INVALIDATED + PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java index 9b24f6cb539..f2aec4e7e33 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java @@ -476,11 +476,13 @@ && strictReplayProtectionShouldBeEnforcedLocally(chainHeadBlockHeader) } // Call the transaction validator plugin if one is available - if (pluginTransactionValidator != null - && !pluginTransactionValidator.validateTransaction(transaction)) { - return ValidationResultAndAccount.invalid( - TransactionInvalidReason.PLUGIN_TX_VALIDATOR_INVALIDATED, - "Plugin transaction vaildator returned false"); + if (pluginTransactionValidator != null) { + final Optional maybeError = + pluginTransactionValidator.validateTransaction(transaction); + if (maybeError.isPresent()) { + return ValidationResultAndAccount.invalid( + TransactionInvalidReason.PLUGIN_TX_VALIDATOR, maybeError.get()); + } } try (final var worldState = diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java index beeb2b1e43b..b5843001053 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java @@ -773,7 +773,7 @@ public void shouldRejectZeroGasPriceLocalTransactionWhenNotMining(final boolean @ValueSource(booleans = {true, false}) public void transactionNotRejectedByPluginShouldBeAdded(final boolean disableLocalTxs) { final PluginTransactionValidatorFactory pluginTransactionValidatorFactory = - getPluginTransactionValidatorFactoryReturning(true); + getPluginTransactionValidatorFactoryReturning(null); // null -> not rejecting !! this.transactionPool = createTransactionPool( b -> b.disableLocalTransactions(disableLocalTxs), pluginTransactionValidatorFactory); @@ -787,7 +787,7 @@ public void transactionNotRejectedByPluginShouldBeAdded(final boolean disableLoc @ValueSource(booleans = {true, false}) public void transactionRejectedByPluginShouldNotBeAdded(final boolean disableLocalTxs) { final PluginTransactionValidatorFactory pluginTransactionValidatorFactory = - getPluginTransactionValidatorFactoryReturning(false); + getPluginTransactionValidatorFactoryReturning("false"); this.transactionPool = createTransactionPool( b -> b.disableLocalTransactions(disableLocalTxs), pluginTransactionValidatorFactory); @@ -795,13 +795,13 @@ public void transactionRejectedByPluginShouldNotBeAdded(final boolean disableLoc givenTransactionIsValid(transaction0); addAndAssertTransactionViaApiInvalid( - transaction0, TransactionInvalidReason.PLUGIN_TX_VALIDATOR_INVALIDATED); + transaction0, TransactionInvalidReason.PLUGIN_TX_VALIDATOR); } @Test public void remoteTransactionRejectedByPluginShouldNotBeAdded() { final PluginTransactionValidatorFactory pluginTransactionValidatorFactory = - getPluginTransactionValidatorFactoryReturning(false); + getPluginTransactionValidatorFactoryReturning("false"); this.transactionPool = createTransactionPool(b -> {}, pluginTransactionValidatorFactory); givenTransactionIsValid(transaction0); @@ -1065,8 +1065,9 @@ public void addRemoteTransactionsShouldAllowDuplicates() { } private static PluginTransactionValidatorFactory getPluginTransactionValidatorFactoryReturning( - final boolean b) { - final PluginTransactionValidator pluginTransactionValidator = transaction -> b; + final String errorMessage) { + final PluginTransactionValidator pluginTransactionValidator = + transaction -> Optional.ofNullable(errorMessage); return () -> pluginTransactionValidator; } diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index 28973cc1f52..76b55919d0f 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -69,7 +69,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = 'Pfql+wKH6qarEvlb9TXZGVV/xwJuKWK5egKHt9uCpzE=' + knownHash = '5koUOxNHaYeuIFYH2PP/6RdMO1JvCm0ILdl/DyY297Y=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSelectionService.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSelectionService.java index 1a46e71f306..aab6e717bad 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSelectionService.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSelectionService.java @@ -16,7 +16,7 @@ package org.hyperledger.besu.plugin.services; import org.hyperledger.besu.plugin.Unstable; -import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; +import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory; import java.util.Optional; @@ -29,12 +29,13 @@ public interface TransactionSelectionService extends BesuService { * * @return the transaction selector factory */ - Optional get(); + Optional get(); /** * Registers the transaction selector factory with the service * * @param transactionSelectorFactory transaction selector factory to be used */ - void registerTransactionSelectorFactory(TransactionSelectorFactory transactionSelectorFactory); + void registerTransactionSelectorFactory( + PluginTransactionSelectorFactory transactionSelectorFactory); } diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/PluginTransactionSelector.java similarity index 79% rename from plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java rename to plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/PluginTransactionSelector.java index 1c124add998..daa93279606 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/PluginTransactionSelector.java @@ -16,13 +16,25 @@ package org.hyperledger.besu.plugin.services.txselection; import org.hyperledger.besu.datatypes.PendingTransaction; +import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.plugin.Unstable; import org.hyperledger.besu.plugin.data.TransactionProcessingResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; /** Interface for the transaction selector */ @Unstable -public interface TransactionSelector { +public interface PluginTransactionSelector { + + /** + * Method that returns an OperationTracer that will be used when executing transactions that are + * candidates to be added to a block. + * + * @return OperationTracer to be used to trace candidate transactions + */ + default OperationTracer getOperationTracer() { + return OperationTracer.NO_TRACING; + } + /** * Method called to decide whether a transaction is added to a block. The result can also indicate * that no further transactions can be added to the block. @@ -48,8 +60,11 @@ TransactionSelectionResult evaluateTransactionPostProcessing( * Method called when a transaction is selected to be added to a block. * * @param pendingTransaction The transaction that has been selected. + * @param processingResult The result of processing the selected transaction. */ - default void onTransactionSelected(final PendingTransaction pendingTransaction) {} + default void onTransactionSelected( + final PendingTransaction pendingTransaction, + final TransactionProcessingResult processingResult) {} /** * Method called when a transaction is not selected to be added to a block. * diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelectorFactory.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/PluginTransactionSelectorFactory.java similarity index 91% rename from plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelectorFactory.java rename to plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/PluginTransactionSelectorFactory.java index 1d4234acc9e..dc6311bb557 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelectorFactory.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/PluginTransactionSelectorFactory.java @@ -19,12 +19,12 @@ /** Interface for a factory that creates transaction selectors */ @Unstable -public interface TransactionSelectorFactory { +public interface PluginTransactionSelectorFactory { /** * Create a transaction selector * * @return the transaction selector */ - TransactionSelector create(); + PluginTransactionSelector create(); } diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txvalidator/PluginTransactionValidator.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txvalidator/PluginTransactionValidator.java index 60f04497199..ab47b31f0d7 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txvalidator/PluginTransactionValidator.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txvalidator/PluginTransactionValidator.java @@ -18,7 +18,9 @@ import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.plugin.Unstable; -/** Interface for the transaction validator */ +import java.util.Optional; + +/** Interface for the transaction validator plugin */ @Unstable public interface PluginTransactionValidator { @@ -26,7 +28,8 @@ public interface PluginTransactionValidator { * Method called to decide whether a transaction can be added to the transaction pool. * * @param transaction candidate transaction - * @return true if the transaction can be added, false otherwise + * @return Optional.empty() if the transaction is valid, an Optional containing an error message, + * if not */ - boolean validateTransaction(final Transaction transaction); + Optional validateTransaction(final Transaction transaction); }