Transaction Validation and Selection Plugin API update (#6020)

change Transaction Validation and Selection Plugin to make them more useful

Signed-off-by: Stefan <stefan.pingel@consensys.net>
Signed-off-by: Stefan Pingel <16143240+pinges@users.noreply.github.com>
Co-authored-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>
This commit is contained in:
Stefan Pingel
2023-10-12 17:52:32 +10:00
committed by GitHub
parent 4276a40a64
commit 1c293fed97
23 changed files with 136 additions and 86 deletions

View File

@@ -48,7 +48,7 @@ import org.hyperledger.besu.plugin.services.SecurityModuleService;
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 class ThreadBesuNodeRunner implements BesuNodeRunner {
final int maxPeers = 25;
final Optional<TransactionSelectorFactory> transactionSelectorFactory =
final Optional<PluginTransactionSelectorFactory> transactionSelectorFactory =
getTransactionSelectorFactory(besuPluginContext);
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory =
@@ -323,7 +323,7 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
throw new RuntimeException("Console contents can only be captured in process execution");
}
private Optional<TransactionSelectorFactory> getTransactionSelectorFactory(
private Optional<PluginTransactionSelectorFactory> getTransactionSelectorFactory(
final BesuPluginContextImpl besuPluginContext) {
final Optional<TransactionSelectionService> txSelectionService =
besuPluginContext.getService(TransactionSelectionService.class);

View File

@@ -177,7 +177,7 @@ import org.hyperledger.besu.plugin.services.metrics.MetricCategoryRegistry;
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 class BesuCommand implements DefaultCommandValues, Runnable {
}
@NotNull
private Optional<TransactionSelectorFactory> getTransactionSelectorFactory() {
private Optional<PluginTransactionSelectorFactory> getTransactionSelectorFactory() {
final Optional<TransactionSelectionService> txSelectionService =
besuPluginContext.getService(TransactionSelectionService.class);
return txSelectionService.isPresent() ? txSelectionService.get().get() : Optional.empty();

View File

@@ -95,7 +95,7 @@ import org.hyperledger.besu.evm.internal.EvmConfiguration;
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> transactionSelectorFactory = Optional.empty();
private Optional<PluginTransactionSelectorFactory> transactionSelectorFactory = Optional.empty();
/** the Dagger configured context that can provide dependencies */
protected Optional<BesuComponent> besuComponent = Optional.empty();
@@ -535,7 +535,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
* @return the besu controller builder
*/
public BesuControllerBuilder transactionSelectorFactory(
final Optional<TransactionSelectorFactory> transactionSelectorFactory) {
final Optional<PluginTransactionSelectorFactory> transactionSelectorFactory) {
this.transactionSelectorFactory = transactionSelectorFactory;
return this;
}
@@ -1035,7 +1035,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule,
final ConsensusContextFactory consensusContextFactory,
final Optional<TransactionSelectorFactory> transactionSelectorFactory) {
final Optional<PluginTransactionSelectorFactory> transactionSelectorFactory) {
return ProtocolContext.init(
blockchain,
worldStateArchive,

View File

@@ -62,7 +62,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
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 @@ public class ConsensusScheduleBesuControllerBuilder extends BesuControllerBuilde
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule,
final ConsensusContextFactory consensusContextFactory,
final Optional<TransactionSelectorFactory> transactionSelectorFactory) {
final Optional<PluginTransactionSelectorFactory> transactionSelectorFactory) {
return MigratingProtocolContext.init(
blockchain,
worldStateArchive,

View File

@@ -60,7 +60,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
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 @@ public class TransitionBesuControllerBuilder extends BesuControllerBuilder {
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule,
final ConsensusContextFactory consensusContextFactory,
final Optional<TransactionSelectorFactory> transactionSelectorFactory) {
final Optional<PluginTransactionSelectorFactory> transactionSelectorFactory) {
final ProtocolContext protocolContext =
super.createProtocolContext(
blockchain,

View File

@@ -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<TransactionSelectorFactory> factory = Optional.empty();
private Optional<PluginTransactionSelectorFactory> factory = Optional.empty();
@Override
public Optional<TransactionSelectorFactory> get() {
public Optional<PluginTransactionSelectorFactory> get() {
return factory;
}
@Override
public void registerTransactionSelectorFactory(
final TransactionSelectorFactory transactionSelectorFactory) {
final PluginTransactionSelectorFactory transactionSelectorFactory) {
factory = Optional.ofNullable(transactionSelectorFactory);
}
}

View File

@@ -20,7 +20,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext;
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 class MigratingProtocolContext extends ProtocolContext {
final MutableBlockchain blockchain,
final WorldStateArchive worldStateArchive,
final ForksSchedule<ConsensusContext> consensusContextSchedule,
final Optional<TransactionSelectorFactory> transactionSelectorFactory) {
final Optional<PluginTransactionSelectorFactory> transactionSelectorFactory) {
super(blockchain, worldStateArchive, null, transactionSelectorFactory);
this.consensusContextSchedule = consensusContextSchedule;
}
@@ -61,7 +61,7 @@ public class MigratingProtocolContext extends ProtocolContext {
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule,
final ConsensusContextFactory consensusContextFactory,
final Optional<TransactionSelectorFactory> transactionSelectorFactory) {
final Optional<PluginTransactionSelectorFactory> transactionSelectorFactory) {
final ConsensusContext consensusContext =
consensusContextFactory.create(blockchain, worldStateArchive, protocolSchedule);
final MigratingContext migratingContext = consensusContext.as(MigratingContext.class);

View File

@@ -77,6 +77,8 @@ public class JsonRpcErrorConverter {
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;
}

View File

@@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter;
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 org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
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 class EthSendRawTransaction implements JsonRpcMethod {
() ->
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<TransactionInvalidReason> 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));
}
}
}
}

View File

@@ -107,7 +107,7 @@ public class JsonRpcErrorResponse implements JsonRpcResponse {
return Arrays.stream(RpcErrorType.values())
.filter(e -> e.getCode() == code && message.startsWith(e.getMessage()))
.findFirst()
.get();
.orElse(RpcErrorType.UNKNOWN);
}
@SuppressWarnings({"unchecked", "rawtypes"})

View File

@@ -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;

View File

@@ -38,10 +38,11 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
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<AbstractTransactionSelector> transactionSelectors;
private final TransactionSelector externalTransactionSelector;
private final PluginTransactionSelector pluginTransactionSelector;
private final OperationTracer pluginOperationTracer;
public BlockTransactionSelector(
final MainnetTransactionProcessor transactionProcessor,
@@ -101,7 +103,7 @@ public class BlockTransactionSelector {
final FeeMarket feeMarket,
final GasCalculator gasCalculator,
final GasLimitCalculator gasLimitCalculator,
final Optional<TransactionSelectorFactory> transactionSelectorFactory) {
final Optional<PluginTransactionSelectorFactory> transactionSelectorFactory) {
this.transactionProcessor = transactionProcessor;
this.blockchain = blockchain;
this.worldState = worldState;
@@ -119,10 +121,11 @@ public class BlockTransactionSelector {
miningBeneficiary,
transactionPool);
transactionSelectors = createTransactionSelectors(blockSelectionContext);
externalTransactionSelector =
pluginTransactionSelector =
transactionSelectorFactory
.map(TransactionSelectorFactory::create)
.map(PluginTransactionSelectorFactory::create)
.orElse(AllAcceptingTransactionSelector.INSTANCE);
pluginOperationTracer = pluginTransactionSelector.getOperationTracer();
}
private List<AbstractTransactionSelector> createTransactionSelectors(
@@ -223,7 +226,7 @@ public class BlockTransactionSelector {
return result;
}
}
return externalTransactionSelector.evaluateTransactionPreProcessing(pendingTransaction);
return pluginTransactionSelector.evaluateTransactionPreProcessing(pendingTransaction);
}
/**
@@ -248,7 +251,7 @@ public class BlockTransactionSelector {
return result;
}
}
return externalTransactionSelector.evaluateTransactionPostProcessing(
return pluginTransactionSelector.evaluateTransactionPostProcessing(
pendingTransaction, processingResult);
}
@@ -269,6 +272,7 @@ public class BlockTransactionSelector {
blockSelectionContext.processableBlockHeader(),
pendingTransaction.getTransaction(),
blockSelectionContext.miningBeneficiary(),
pluginOperationTracer,
blockHashLookup,
false,
TransactionValidationParams.mining(),
@@ -307,7 +311,7 @@ public class BlockTransactionSelector {
transactionSelectionResults.updateSelected(
pendingTransaction.getTransaction(), receipt, gasUsedByTransaction, blobGasUsed);
externalTransactionSelector.onTransactionSelected(pendingTransaction);
pluginTransactionSelector.onTransactionSelected(pendingTransaction, processingResult);
return TransactionSelectionResult.SELECTED;
}
@@ -326,7 +330,7 @@ public class BlockTransactionSelector {
final TransactionSelectionResult selectionResult) {
transactionSelectionResults.updateNotSelected(
pendingTransaction.getTransaction(), selectionResult);
externalTransactionSelector.onTransactionNotSelected(pendingTransaction, selectionResult);
pluginTransactionSelector.onTransactionNotSelected(pendingTransaction, selectionResult);
return selectionResult;
}

View File

@@ -17,10 +17,10 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
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();

View File

@@ -71,8 +71,8 @@ import org.hyperledger.besu.evm.worldstate.WorldState;
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 abstract class AbstractBlockTransactionSelectorTest {
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 abstract class AbstractBlockTransactionSelectorTest {
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 abstract class AbstractBlockTransactionSelectorTest {
@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 abstract class AbstractBlockTransactionSelectorTest {
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 @@ public abstract class AbstractBlockTransactionSelectorTest {
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 @@ public abstract class AbstractBlockTransactionSelectorTest {
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 @@ public abstract class AbstractBlockTransactionSelectorTest {
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)));
}

View File

@@ -18,7 +18,7 @@ import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
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> transactionSelectorFactory;
private final Optional<PluginTransactionSelectorFactory> transactionSelectorFactory;
private Optional<Synchronizer> synchronizer;
@@ -46,7 +46,7 @@ public class ProtocolContext {
final MutableBlockchain blockchain,
final WorldStateArchive worldStateArchive,
final ConsensusContext consensusContext,
final Optional<TransactionSelectorFactory> transactionSelectorFactory) {
final Optional<PluginTransactionSelectorFactory> transactionSelectorFactory) {
this.blockchain = blockchain;
this.worldStateArchive = worldStateArchive;
this.consensusContext = consensusContext;
@@ -59,7 +59,7 @@ public class ProtocolContext {
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule,
final ConsensusContextFactory consensusContextFactory,
final Optional<TransactionSelectorFactory> transactionSelectorFactory) {
final Optional<PluginTransactionSelectorFactory> transactionSelectorFactory) {
return new ProtocolContext(
blockchain,
worldStateArchive,
@@ -93,7 +93,7 @@ public class ProtocolContext {
.map(klass::cast);
}
public Optional<TransactionSelectorFactory> getTransactionSelectorFactory() {
public Optional<PluginTransactionSelectorFactory> getTransactionSelectorFactory() {
return transactionSelectorFactory;
}
}

View File

@@ -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
}

View File

@@ -476,11 +476,13 @@ public class TransactionPool implements BlockAddedObserver {
}
// 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<String> maybeError =
pluginTransactionValidator.validateTransaction(transaction);
if (maybeError.isPresent()) {
return ValidationResultAndAccount.invalid(
TransactionInvalidReason.PLUGIN_TX_VALIDATOR, maybeError.get());
}
}
try (final var worldState =

View File

@@ -773,7 +773,7 @@ public abstract class AbstractTransactionPoolTest {
@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 abstract class AbstractTransactionPoolTest {
@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 abstract class AbstractTransactionPoolTest {
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 abstract class AbstractTransactionPoolTest {
}
private static PluginTransactionValidatorFactory getPluginTransactionValidatorFactoryReturning(
final boolean b) {
final PluginTransactionValidator pluginTransactionValidator = transaction -> b;
final String errorMessage) {
final PluginTransactionValidator pluginTransactionValidator =
transaction -> Optional.ofNullable(errorMessage);
return () -> pluginTransactionValidator;
}

View File

@@ -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')

View File

@@ -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<TransactionSelectorFactory> get();
Optional<PluginTransactionSelectorFactory> 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);
}

View File

@@ -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 @@ public interface TransactionSelector {
* 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.
*

View File

@@ -19,12 +19,12 @@ import org.hyperledger.besu.plugin.Unstable;
/** 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();
}

View File

@@ -18,7 +18,9 @@ package org.hyperledger.besu.plugin.services.txvalidator;
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<String> validateTransaction(final Transaction transaction);
}