add plugin API to enable plugins to validate transaction before they are added to the transaction pool

Signed-off-by: Stefan <stefan.pingel@consensys.net>
TransactionValidatorService
Stefan 1 year ago
parent 35385611ae
commit b0b5cf91db
  1. 5
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java
  2. 28
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  3. 12
      besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java
  4. 35
      besu/src/main/java/org/hyperledger/besu/services/TransactionValidatorServiceImpl.java
  5. 2
      besu/src/main/java/org/hyperledger/besu/util/PermissioningConfigurationValidator.java
  6. 6
      besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java
  7. 3
      besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java
  8. 8
      besu/src/test/java/org/hyperledger/besu/util/LocalPermissioningConfigurationValidatorTest.java
  9. 3
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java
  10. 3
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java
  11. 3
      consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java
  12. 3
      consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java
  13. 3
      consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java
  14. 3
      consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java
  15. 3
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/frontier/EthGetFilterChangesIntegrationTest.java
  16. 3
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/fork/london/EthGetFilterChangesIntegrationTest.java
  17. 3
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreatorTest.java
  18. 3
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LegacyFeeMarketBlockTransactionSelectorTest.java
  19. 3
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/LondonFeeMarketBlockTransactionSelectorTest.java
  20. 3
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java
  21. 3
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWMinerExecutorTest.java
  22. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java
  23. 3
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java
  24. 22
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java
  25. 13
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java
  26. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java
  27. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/AbstractMessageTaskTest.java
  28. 65
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionPoolTest.java
  29. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/AbstractTransactionsLayeredPendingTransactionsTest.java
  30. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java
  31. 6
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java
  32. 3
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java
  33. 2
      plugin-api/build.gradle
  34. 39
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionValidatorService.java
  35. 33
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txvalidator/PluginTransactionValidator.java
  36. 30
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txvalidator/PluginTransactionValidatorFactory.java

@ -46,6 +46,7 @@ import org.hyperledger.besu.plugin.services.PicoCLIOptions;
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.TransactionValidatorService;
import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBPlugin;
import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory;
import org.hyperledger.besu.services.BesuConfigurationImpl;
@ -57,6 +58,7 @@ import org.hyperledger.besu.services.RpcEndpointServiceImpl;
import org.hyperledger.besu.services.SecurityModuleServiceImpl;
import org.hyperledger.besu.services.StorageServiceImpl;
import org.hyperledger.besu.services.TransactionSelectionServiceImpl;
import org.hyperledger.besu.services.TransactionValidatorServiceImpl;
import java.io.File;
import java.nio.file.Path;
@ -97,7 +99,8 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
besuPluginContext.addService(PicoCLIOptions.class, new PicoCLIOptionsImpl(commandLine));
besuPluginContext.addService(
TransactionSelectionService.class, new TransactionSelectionServiceImpl());
besuPluginContext.addService(
TransactionValidatorService.class, new TransactionValidatorServiceImpl());
final Path pluginsPath;
final String pluginDir = System.getProperty("besu.plugins.dir");
if (pluginDir == null || pluginDir.isEmpty()) {

@ -170,6 +170,7 @@ import org.hyperledger.besu.plugin.services.SecurityModuleService;
import org.hyperledger.besu.plugin.services.StorageService;
import org.hyperledger.besu.plugin.services.TraceService;
import org.hyperledger.besu.plugin.services.TransactionSelectionService;
import org.hyperledger.besu.plugin.services.TransactionValidatorService;
import org.hyperledger.besu.plugin.services.exception.StorageException;
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
import org.hyperledger.besu.plugin.services.metrics.MetricCategoryRegistry;
@ -177,6 +178,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.txvalidator.PluginTransactionValidatorFactory;
import org.hyperledger.besu.services.BesuEventsImpl;
import org.hyperledger.besu.services.BesuPluginContextImpl;
import org.hyperledger.besu.services.BlockchainServiceImpl;
@ -188,6 +190,7 @@ import org.hyperledger.besu.services.SecurityModuleServiceImpl;
import org.hyperledger.besu.services.StorageServiceImpl;
import org.hyperledger.besu.services.TraceServiceImpl;
import org.hyperledger.besu.services.TransactionSelectionServiceImpl;
import org.hyperledger.besu.services.TransactionValidatorServiceImpl;
import org.hyperledger.besu.services.kvstore.InMemoryStoragePlugin;
import org.hyperledger.besu.util.InvalidConfigurationException;
import org.hyperledger.besu.util.LogConfigurator;
@ -372,6 +375,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
P2PDiscoveryOptionGroup p2PDiscoveryOptionGroup = new P2PDiscoveryOptionGroup();
private final TransactionSelectionServiceImpl transactionSelectionServiceImpl;
private final TransactionValidatorServiceImpl transactionValidatorServiceImpl;
static class P2PDiscoveryOptionGroup {
@ -1355,7 +1359,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
new PrivacyPluginServiceImpl(),
new PkiBlockCreationConfigurationProvider(),
new RpcEndpointServiceImpl(),
new TransactionSelectionServiceImpl());
new TransactionSelectionServiceImpl(),
new TransactionValidatorServiceImpl());
}
/**
@ -1376,6 +1381,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
* @param pkiBlockCreationConfigProvider instance of PkiBlockCreationConfigurationProvider
* @param rpcEndpointServiceImpl instance of RpcEndpointServiceImpl
* @param transactionSelectionServiceImpl instance of TransactionSelectionServiceImpl
* @param transactionValidatorServiceImpl instance of TransactionValidatorServiceImpl
*/
@VisibleForTesting
protected BesuCommand(
@ -1393,7 +1399,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final PrivacyPluginServiceImpl privacyPluginService,
final PkiBlockCreationConfigurationProvider pkiBlockCreationConfigProvider,
final RpcEndpointServiceImpl rpcEndpointServiceImpl,
final TransactionSelectionServiceImpl transactionSelectionServiceImpl) {
final TransactionSelectionServiceImpl transactionSelectionServiceImpl,
final TransactionValidatorServiceImpl transactionValidatorServiceImpl) {
this.besuComponent = besuComponent;
this.logger = besuComponent.getBesuCommandLogger();
this.rlpBlockImporter = rlpBlockImporter;
@ -1412,6 +1419,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
this.pkiBlockCreationConfigProvider = pkiBlockCreationConfigProvider;
this.rpcEndpointServiceImpl = rpcEndpointServiceImpl;
this.transactionSelectionServiceImpl = transactionSelectionServiceImpl;
this.transactionValidatorServiceImpl = transactionValidatorServiceImpl;
}
/**
@ -1593,6 +1601,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
besuPluginContext.addService(RpcEndpointService.class, rpcEndpointServiceImpl);
besuPluginContext.addService(
TransactionSelectionService.class, transactionSelectionServiceImpl);
besuPluginContext.addService(
TransactionValidatorService.class, transactionValidatorServiceImpl);
// register built-in plugins
rocksDBPlugin = new RocksDBPlugin();
@ -2193,7 +2203,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final Collection<EnodeURL> enodeAddresses,
final LocalPermissioningConfiguration permissioningConfiguration) {
try {
PermissioningConfigurationValidator.areAllNodesAreInAllowlist(
PermissioningConfigurationValidator.areAllNodesInAllowlist(
enodeAddresses, permissioningConfiguration);
} catch (final Exception e) {
throw new ParameterException(this.commandLine, e.getMessage());
@ -2226,15 +2236,14 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
*/
public BesuControllerBuilder getControllerBuilder() {
final KeyValueStorageProvider storageProvider = keyValueStorageProvider(keyValueStorageName);
final Optional<TransactionSelectorFactory> transactionSelectorFactory =
getTransactionSelectorFactory();
return controllerBuilderFactory
.fromEthNetworkConfig(
updateNetworkConfig(network), genesisConfigOverrides, getDefaultSyncModeIfNotSet())
.synchronizerConfiguration(buildSyncConfig())
.ethProtocolConfiguration(unstableEthProtocolOptions.toDomainObject())
.networkConfiguration(unstableNetworkingOptions.toDomainObject())
.transactionSelectorFactory(transactionSelectorFactory)
.transactionSelectorFactory(getTransactionSelectorFactory())
.pluginTransactionSelectorFactory(getPluginTransactionValidatorFactory())
.dataDirectory(dataDir())
.miningParameters(
new MiningParameters.Builder()
@ -2291,6 +2300,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
return txSelectionService.isPresent() ? txSelectionService.get().get() : Optional.empty();
}
private PluginTransactionValidatorFactory getPluginTransactionValidatorFactory() {
final Optional<TransactionValidatorService> txSValidatorService =
besuPluginContext.getService(TransactionValidatorService.class);
return txSValidatorService.map(TransactionValidatorService::get).orElse(null);
}
private GraphQLConfiguration graphQLConfiguration() {
CommandLineUtils.checkOptionDependencies(
@ -2920,6 +2935,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
}
private TransactionPoolConfiguration buildTransactionPoolConfiguration() {
final var stableTxPoolOption = stableTransactionPoolOptions.toDomainObject();
return ImmutableTransactionPoolConfiguration.builder()
.from(stableTxPoolOption)

@ -96,6 +96,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.txvalidator.PluginTransactionValidatorFactory;
import java.io.Closeable;
import java.math.BigInteger;
@ -185,6 +186,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
/** the Dagger configured context that can provide dependencies */
protected Optional<BesuComponent> besuComponent = Optional.empty();
private PluginTransactionValidatorFactory pluginTransactionValidatorFactory;
/**
* Provide a BesuComponent which can be used to get other dependencies
*
@ -537,6 +540,12 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
return this;
}
public BesuControllerBuilder pluginTransactionSelectorFactory(
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory) {
this.pluginTransactionValidatorFactory = pluginTransactionValidatorFactory;
return this;
}
/**
* Build besu controller.
*
@ -695,7 +704,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
metricsSystem,
syncState,
miningParameters,
transactionPoolConfiguration);
transactionPoolConfiguration,
pluginTransactionValidatorFactory);
final List<PeerValidator> peerValidators = createPeerValidators(protocolSchedule);

@ -0,0 +1,35 @@
/*
* 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.services;
import org.hyperledger.besu.plugin.services.TransactionValidatorService;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory;
/** The Transaction Selection service implementation. */
public class TransactionValidatorServiceImpl implements TransactionValidatorService {
private PluginTransactionValidatorFactory factory;
@Override
public PluginTransactionValidatorFactory get() {
return factory;
}
@Override
public void registerTransactionValidatorFactory(
final PluginTransactionValidatorFactory transactionValidatorFactory) {
factory = transactionValidatorFactory;
}
}

@ -34,7 +34,7 @@ public class PermissioningConfigurationValidator {
* @param permissioningConfiguration the permissioning configuration
* @throws Exception In case of nodes are not in allow list
*/
public static void areAllNodesAreInAllowlist(
public static void areAllNodesInAllowlist(
final Collection<EnodeURL> nodeURIs,
final LocalPermissioningConfiguration permissioningConfiguration)
throws Exception {

@ -81,6 +81,7 @@ import org.hyperledger.besu.services.RpcEndpointServiceImpl;
import org.hyperledger.besu.services.SecurityModuleServiceImpl;
import org.hyperledger.besu.services.StorageServiceImpl;
import org.hyperledger.besu.services.TransactionSelectionServiceImpl;
import org.hyperledger.besu.services.TransactionValidatorServiceImpl;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import java.io.ByteArrayOutputStream;
@ -240,6 +241,8 @@ public abstract class CommandTestAbstract {
when(mockControllerBuilder.maxRemotelyInitiatedPeers(anyInt()))
.thenReturn(mockControllerBuilder);
when(mockControllerBuilder.transactionSelectorFactory(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.pluginTransactionSelectorFactory(any()))
.thenReturn(mockControllerBuilder);
when(mockControllerBuilder.besuComponent(any(BesuComponent.class)))
.thenReturn(mockControllerBuilder);
// doReturn used because of generic BesuController
@ -489,7 +492,8 @@ public abstract class CommandTestAbstract {
privacyPluginService,
pkiBlockCreationConfigProvider,
rpcEndpointServiceImpl,
new TransactionSelectionServiceImpl());
new TransactionSelectionServiceImpl(),
new TransactionValidatorServiceImpl());
}
@Override

@ -156,7 +156,8 @@ public class BesuEventsImplTest {
new NoOpMetricsSystem(),
syncState,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
txPoolConfig);
txPoolConfig,
null);
serviceImpl = new BesuEventsImpl(blockchain, blockBroadcaster, transactionPool, syncState);
}

@ -68,7 +68,7 @@ public class LocalPermissioningConfigurationValidatorTest {
toml.toAbsolutePath().toString());
final List<EnodeURL> enodeURIs = ethNetworkConfig.getBootNodes();
PermissioningConfigurationValidator.areAllNodesAreInAllowlist(
PermissioningConfigurationValidator.areAllNodesInAllowlist(
enodeURIs, permissioningConfiguration);
}
@ -92,7 +92,7 @@ public class LocalPermissioningConfigurationValidatorTest {
try {
final List<EnodeURL> enodeURIs = ethNetworkConfig.getBootNodes();
PermissioningConfigurationValidator.areAllNodesAreInAllowlist(
PermissioningConfigurationValidator.areAllNodesInAllowlist(
enodeURIs, permissioningConfiguration);
fail("expected exception because sepolia bootnodes are not in node-allowlist");
} catch (Exception e) {
@ -142,7 +142,7 @@ public class LocalPermissioningConfigurationValidatorTest {
// However, for the allowlist validation, we should ignore the discovery port and don't throw an
// error
try {
PermissioningConfigurationValidator.areAllNodesAreInAllowlist(
PermissioningConfigurationValidator.areAllNodesInAllowlist(
Lists.newArrayList(enodeURL), permissioningConfiguration);
} catch (Exception e) {
fail(
@ -180,7 +180,7 @@ public class LocalPermissioningConfigurationValidatorTest {
// However, for the allowlist validation, we should ignore the discovery port and don't throw an
// error
try {
PermissioningConfigurationValidator.areAllNodesAreInAllowlist(
PermissioningConfigurationValidator.areAllNodesInAllowlist(
Lists.newArrayList(enodeURL), permissioningConfiguration);
} catch (Exception e) {
fail(

@ -235,7 +235,8 @@ public class CliqueBlockCreatorTest {
ethContext,
mock(MiningParameters.class),
new TransactionPoolMetrics(metricsSystem),
conf);
conf,
null);
transactionPool.setEnabled();
return transactionPool;
}

@ -222,7 +222,8 @@ public class CliqueMinerExecutorTest {
cliqueEthContext,
mock(MiningParameters.class),
new TransactionPoolMetrics(metricsSystem),
conf);
conf,
null);
transactionPool.setEnabled();
return transactionPool;

@ -357,7 +357,8 @@ public class TestContextBuilder {
ethContext,
miningParams,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
null);
transactionPool.setEnabled();

@ -145,7 +145,8 @@ public class BftBlockCreatorTest {
ethContext,
mock(MiningParameters.class),
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
null);
transactionPool.setEnabled();

@ -222,7 +222,8 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
ethContext,
miningParameters,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
null);
this.transactionPool.setEnabled();

@ -445,7 +445,8 @@ public class TestContextBuilder {
ethContext,
miningParams,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
null);
transactionPool.setEnabled();

@ -119,7 +119,8 @@ public class EthGetFilterChangesIntegrationTest {
ethContext,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
new TransactionPoolMetrics(metricsSystem),
TransactionPoolConfiguration.DEFAULT);
TransactionPoolConfiguration.DEFAULT,
null);
transactionPool.setEnabled();
final BlockchainQueries blockchainQueries =
new BlockchainQueries(blockchain, protocolContext.getWorldStateArchive());

@ -119,7 +119,8 @@ public class EthGetFilterChangesIntegrationTest {
ethContext,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
new TransactionPoolMetrics(metricsSystem),
TransactionPoolConfiguration.DEFAULT);
TransactionPoolConfiguration.DEFAULT,
null);
transactionPool.setEnabled();
final BlockchainQueries blockchainQueries =
new BlockchainQueries(blockchain, protocolContext.getWorldStateArchive());

@ -381,7 +381,8 @@ abstract class AbstractBlockCreatorTest {
ethContext,
mock(MiningParameters.class),
new TransactionPoolMetrics(new NoOpMetricsSystem()),
poolConf);
poolConf,
null);
transactionPool.setEnabled();
return new TestBlockCreator(

@ -87,7 +87,8 @@ public class LegacyFeeMarketBlockTransactionSelectorTest
ethContext,
miningParameters,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
null);
transactionPool.setEnabled();
return transactionPool;
}

@ -91,7 +91,8 @@ public class LondonFeeMarketBlockTransactionSelectorTest
ethContext,
miningParameters,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
null);
transactionPool.setEnabled();
return transactionPool;
}

@ -341,7 +341,8 @@ class PoWBlockCreatorTest extends AbstractBlockCreatorTest {
ethContext,
mock(MiningParameters.class),
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
null);
transactionPool.setEnabled();
return transactionPool;

@ -119,7 +119,8 @@ public class PoWMinerExecutorTest {
ethContext,
miningParameters,
new TransactionPoolMetrics(new NoOpMetricsSystem()),
poolConf);
poolConf,
null);
transactionPool.setEnabled();
return transactionPool;

@ -55,5 +55,6 @@ public enum TransactionInvalidReason {
OFFCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST,
PRIVATE_NONCE_TOO_HIGH,
PRIVATE_VALUE_NOT_ZERO,
PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE
PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE,
PLUGIN_TX_VALIDATOR_INVALIDATED
}

@ -162,7 +162,8 @@ public abstract class AbstractIsolationTests {
ethContext,
mock(MiningParameters.class),
txPoolMetrics,
poolConfiguration);
poolConfiguration,
null);
transactionPool.setEnabled();
}

@ -42,6 +42,8 @@ import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.fluent.SimpleAccount;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidator;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory;
import org.hyperledger.besu.util.Subscribers;
import java.io.BufferedReader;
@ -88,6 +90,7 @@ public class TransactionPool implements BlockAddedObserver {
private static final Logger LOG = LoggerFactory.getLogger(TransactionPool.class);
private static final Logger LOG_FOR_REPLAY = LoggerFactory.getLogger("LOG_FOR_REPLAY");
private final Supplier<PendingTransactions> pendingTransactionsSupplier;
private final PluginTransactionValidator pluginTransactionValidator;
private volatile PendingTransactions pendingTransactions;
private final ProtocolSchedule protocolSchedule;
private final ProtocolContext protocolContext;
@ -110,7 +113,8 @@ public class TransactionPool implements BlockAddedObserver {
final EthContext ethContext,
final MiningParameters miningParameters,
final TransactionPoolMetrics metrics,
final TransactionPoolConfiguration configuration) {
final TransactionPoolConfiguration configuration,
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory) {
this.pendingTransactionsSupplier = pendingTransactionsSupplier;
this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext;
@ -119,6 +123,10 @@ public class TransactionPool implements BlockAddedObserver {
this.miningParameters = miningParameters;
this.metrics = metrics;
this.configuration = configuration;
this.pluginTransactionValidator =
pluginTransactionValidatorFactory == null
? null
: pluginTransactionValidatorFactory.create();
initLogForReplay();
}
@ -409,7 +417,7 @@ public class TransactionPool implements BlockAddedObserver {
}
if (isLocal
&& strictReplayProtectionShouldBeEnforceLocally(chainHeadBlockHeader)
&& strictReplayProtectionShouldBeEnforcedLocally(chainHeadBlockHeader)
&& transaction.getChainId().isEmpty()) {
// Strict replay protection is enabled but the tx is not replay-protected
return ValidationResultAndAccount.invalid(
@ -428,6 +436,14 @@ public class TransactionPool implements BlockAddedObserver {
"EIP-1559 transaction are not allowed yet");
}
// 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");
}
try (final var worldState =
protocolContext
.getWorldStateArchive()
@ -477,7 +493,7 @@ public class TransactionPool implements BlockAddedObserver {
return null;
}
private boolean strictReplayProtectionShouldBeEnforceLocally(
private boolean strictReplayProtectionShouldBeEnforcedLocally(
final BlockHeader chainHeadBlockHeader) {
return configuration.getStrictTransactionReplayProtectionEnabled()
&& protocolSchedule.getChainId().isPresent()

@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.plugin.services.BesuEvents;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory;
import java.time.Clock;
import java.util.function.BiFunction;
@ -54,7 +55,8 @@ public class TransactionPoolFactory {
final MetricsSystem metricsSystem,
final SyncState syncState,
final MiningParameters miningParameters,
final TransactionPoolConfiguration transactionPoolConfiguration) {
final TransactionPoolConfiguration transactionPoolConfiguration,
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory) {
final TransactionPoolMetrics metrics = new TransactionPoolMetrics(metricsSystem);
@ -76,7 +78,8 @@ public class TransactionPoolFactory {
transactionPoolConfiguration,
transactionTracker,
transactionsMessageSender,
newPooledTransactionHashesMessageSender);
newPooledTransactionHashesMessageSender,
pluginTransactionValidatorFactory);
}
static TransactionPool createTransactionPool(
@ -90,7 +93,8 @@ public class TransactionPoolFactory {
final TransactionPoolConfiguration transactionPoolConfiguration,
final PeerTransactionTracker transactionTracker,
final TransactionsMessageSender transactionsMessageSender,
final NewPooledTransactionHashesMessageSender newPooledTransactionHashesMessageSender) {
final NewPooledTransactionHashesMessageSender newPooledTransactionHashesMessageSender,
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory) {
final TransactionPool transactionPool =
new TransactionPool(
@ -111,7 +115,8 @@ public class TransactionPoolFactory {
ethContext,
miningParameters,
metrics,
transactionPoolConfiguration);
transactionPoolConfiguration,
pluginTransactionValidatorFactory);
final TransactionsMessageHandler transactionsMessageHandler =
new TransactionsMessageHandler(

@ -1117,7 +1117,8 @@ public final class EthProtocolManagerTest {
metricsSystem,
new SyncState(blockchain, ethManager.ethContext().getEthPeers()),
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
TransactionPoolConfiguration.DEFAULT)
TransactionPoolConfiguration.DEFAULT,
null)
.setEnabled();
// Send just a transaction message.

@ -137,7 +137,8 @@ public abstract class AbstractMessageTaskTest<T, R> {
metricsSystem,
syncState,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ONE).build(),
TransactionPoolConfiguration.DEFAULT);
TransactionPoolConfiguration.DEFAULT,
null);
transactionPool.setEnabled();
ethProtocolManager =

@ -68,6 +68,8 @@ import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidator;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory;
import java.math.BigInteger;
import java.util.Collections;
@ -76,6 +78,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -173,6 +176,12 @@ public abstract class AbstractTransactionPoolTest {
protected TransactionPool createTransactionPool(
final Consumer<ImmutableTransactionPoolConfiguration.Builder> configConsumer) {
return createTransactionPool(configConsumer, null);
}
protected TransactionPool createTransactionPool(
final Consumer<ImmutableTransactionPoolConfiguration.Builder> configConsumer,
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory) {
final ImmutableTransactionPoolConfiguration.Builder configBuilder =
ImmutableTransactionPoolConfiguration.builder();
configConsumer.accept(configBuilder);
@ -187,7 +196,8 @@ public abstract class AbstractTransactionPoolTest {
ethContext,
miningParameters,
new TransactionPoolMetrics(metricsSystem),
config);
config,
pluginTransactionValidatorFactory);
txPool.setEnabled();
return txPool;
@ -647,6 +657,59 @@ public abstract class AbstractTransactionPoolTest {
assertTransactionViaApiInvalid(transaction, GAS_PRICE_TOO_LOW);
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void transactionNotRejectedByPluginShouldBeAdded(final boolean disableLocalTxs) {
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory =
getPluginTransactionValidatorFactoryReturning(true);
this.transactionPool =
createTransactionPool(
b -> b.disableLocalTransactions(disableLocalTxs), pluginTransactionValidatorFactory);
final Transaction transaction = createTransaction(0);
givenTransactionIsValid(transaction);
assertTransactionViaApiValid(transaction, disableLocalTxs);
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void transactionRejectedByPluginShouldNotBeAdded(final boolean disableLocalTxs) {
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory =
getPluginTransactionValidatorFactoryReturning(false);
this.transactionPool =
createTransactionPool(
b -> b.disableLocalTransactions(disableLocalTxs), pluginTransactionValidatorFactory);
final Transaction transaction = createTransaction(0);
givenTransactionIsValid(transaction);
assertTransactionViaApiInvalid(
transaction, TransactionInvalidReason.PLUGIN_TX_VALIDATOR_INVALIDATED);
}
@Test
public void remoteTransactionRejectedByPluginShouldNotBeAdded() {
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory =
getPluginTransactionValidatorFactoryReturning(false);
this.transactionPool = createTransactionPool(b -> {}, pluginTransactionValidatorFactory);
final Transaction transaction = createTransaction(0);
givenTransactionIsValid(transaction);
assertRemoteTransactionInvalid(transaction);
}
@NotNull
private static PluginTransactionValidatorFactory getPluginTransactionValidatorFactoryReturning(
final boolean b) {
final PluginTransactionValidator pluginTransactionValidator = transaction -> b;
return () -> pluginTransactionValidator;
}
private void assertTransactionPending(final Transaction t) {
assertThat(transactions.getTransactionByHash(t.getHash())).contains(t);
}

@ -200,7 +200,8 @@ public abstract class AbstractTransactionsLayeredPendingTransactionsTest {
ethContext,
miningParameters,
new TransactionPoolMetrics(metricsSystem),
poolConfig);
poolConfig,
null);
txPool.setEnabled();
return txPool;
}

@ -164,7 +164,8 @@ public class TestNode implements Closeable {
metricsSystem,
syncState,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
TransactionPoolConfiguration.DEFAULT);
TransactionPoolConfiguration.DEFAULT,
null);
final EthProtocolManager ethProtocolManager =
new EthProtocolManager(

@ -252,7 +252,8 @@ public class TransactionPoolFactoryTest {
.build(),
peerTransactionTracker,
transactionsMessageSender,
newPooledTransactionHashesMessageSender);
newPooledTransactionHashesMessageSender,
null);
ethProtocolManager =
new EthProtocolManager(
@ -358,7 +359,8 @@ public class TransactionPoolFactoryTest {
ImmutableTransactionPoolConfiguration.Unstable.builder()
.txMessageKeepAliveSeconds(1)
.build())
.build());
.build(),
null);
txPool.setEnabled();
return txPool;

@ -240,7 +240,8 @@ public class RetestethContext {
metricsSystem,
syncState,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
transactionPoolConfiguration);
transactionPoolConfiguration,
null);
if (LOG.isTraceEnabled()) {
LOG.trace("Genesis Block {} ", genesisState.getBlock());

@ -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 = 'fPOd/MnNB1PwfTV7HDTXc3oYRcoeUzMJrlzDUdg/HNk='
knownHash = 'efyGMh6uDklK9YBP8cAbXujTu4adsr/hG5Y6RUkj3P8='
}
check.dependsOn('checkAPIChanges')

@ -0,0 +1,39 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.plugin.services;
import org.hyperledger.besu.plugin.Unstable;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory;
/** Transaction validator for addition of transactions to the transaction pool */
@Unstable
public interface TransactionValidatorService extends BesuService {
/**
* Returns the transaction validator factory
*
* @return the transaction validator factory
*/
PluginTransactionValidatorFactory get();
/**
* Registers the transaction validator factory with the service
*
* @param transactionPoolFilterFactory transaction validator factory to be used
*/
void registerTransactionValidatorFactory(
PluginTransactionValidatorFactory transactionPoolFilterFactory);
}

@ -0,0 +1,33 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.plugin.services.txvalidator;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.plugin.Unstable;
/** Interface for the transaction validator */
@Unstable
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
*/
boolean validateTransaction(final Transaction transaction);
;
}

@ -0,0 +1,30 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.plugin.services.txvalidator;
import org.hyperledger.besu.plugin.Unstable;
/** Interface for a factory that creates transaction validators */
@Unstable
public interface PluginTransactionValidatorFactory {
/**
* Create a transaction validator
*
* @return the transaction validator
*/
PluginTransactionValidator create();
}
Loading…
Cancel
Save