Skip to content

Commit

Permalink
add plugin API to enable plugins to validate transaction before they …
Browse files Browse the repository at this point in the history
…are added to the transaction pool (hyperledger#5891)

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

Signed-off-by: Stefan <[email protected]>

* updated plugin version

Signed-off-by: Sally MacFarlane <[email protected]>

---------

Signed-off-by: Stefan <[email protected]>
Signed-off-by: Sally MacFarlane <[email protected]>
Co-authored-by: Sally MacFarlane <[email protected]>
  • Loading branch information
pinges and macfarla authored Sep 25, 2023
1 parent 52795b6 commit 1c261db
Show file tree
Hide file tree
Showing 37 changed files with 340 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,19 @@
import org.hyperledger.besu.plugin.services.BesuConfiguration;
import org.hyperledger.besu.plugin.services.BesuEvents;
import org.hyperledger.besu.plugin.services.PicoCLIOptions;
import org.hyperledger.besu.plugin.services.PluginTransactionValidatorService;
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.txvalidator.PluginTransactionValidatorFactory;
import org.hyperledger.besu.services.BesuConfigurationImpl;
import org.hyperledger.besu.services.BesuEventsImpl;
import org.hyperledger.besu.services.BesuPluginContextImpl;
import org.hyperledger.besu.services.PermissioningServiceImpl;
import org.hyperledger.besu.services.PicoCLIOptionsImpl;
import org.hyperledger.besu.services.PluginTransactionValidatorServiceImpl;
import org.hyperledger.besu.services.RpcEndpointServiceImpl;
import org.hyperledger.besu.services.SecurityModuleServiceImpl;
import org.hyperledger.besu.services.StorageServiceImpl;
Expand Down Expand Up @@ -97,7 +100,8 @@ private BesuPluginContextImpl buildPluginContext(
besuPluginContext.addService(PicoCLIOptions.class, new PicoCLIOptionsImpl(commandLine));
besuPluginContext.addService(
TransactionSelectionService.class, new TransactionSelectionServiceImpl());

besuPluginContext.addService(
PluginTransactionValidatorService.class, new PluginTransactionValidatorServiceImpl());
final Path pluginsPath;
final String pluginDir = System.getProperty("besu.plugins.dir");
if (pluginDir == null || pluginDir.isEmpty()) {
Expand Down Expand Up @@ -184,6 +188,8 @@ public void startNode(final BesuNode node) {
final Optional<TransactionSelectorFactory> transactionSelectorFactory =
getTransactionSelectorFactory(besuPluginContext);

final PluginTransactionValidatorFactory pluginTransactionValidatorFactory =
getPluginTransactionValidatorFactory(besuPluginContext);
builder
.synchronizerConfiguration(new SynchronizerConfiguration.Builder().build())
.dataDirectory(node.homeDirectory())
Expand All @@ -206,7 +212,8 @@ public void startNode(final BesuNode node) {
.maxRemotelyInitiatedPeers(15)
.networkConfiguration(node.getNetworkingConfiguration())
.randomPeerPriority(false)
.transactionSelectorFactory(transactionSelectorFactory);
.transactionSelectorFactory(transactionSelectorFactory)
.pluginTransactionValidatorFactory(pluginTransactionValidatorFactory);

node.getGenesisConfig()
.map(GenesisConfigFile::fromConfig)
Expand Down Expand Up @@ -322,4 +329,11 @@ private Optional<TransactionSelectorFactory> getTransactionSelectorFactory(
besuPluginContext.getService(TransactionSelectionService.class);
return txSelectionService.isPresent() ? txSelectionService.get().get() : Optional.empty();
}

private PluginTransactionValidatorFactory getPluginTransactionValidatorFactory(
final BesuPluginContextImpl besuPluginContext) {
final Optional<PluginTransactionValidatorService> txValidatorService =
besuPluginContext.getService(PluginTransactionValidatorService.class);
return txValidatorService.map(PluginTransactionValidatorService::get).orElse(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public BesuNode createMinerNodeWithExtraCliOptions(
.name(name)
.miningEnabled()
.jsonRpcEnabled()
.jsonRpcTxPool()
.webSocketEnabled()
.extraCLIOptions(extraCliOptions);
builder = configModifier.apply(builder);
Expand Down
27 changes: 21 additions & 6 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.PermissioningService;
import org.hyperledger.besu.plugin.services.PicoCLIOptions;
import org.hyperledger.besu.plugin.services.PluginTransactionValidatorService;
import org.hyperledger.besu.plugin.services.PrivacyPluginService;
import org.hyperledger.besu.plugin.services.RpcEndpointService;
import org.hyperledger.besu.plugin.services.SecurityModuleService;
Expand All @@ -177,11 +178,13 @@
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;
import org.hyperledger.besu.services.PermissioningServiceImpl;
import org.hyperledger.besu.services.PicoCLIOptionsImpl;
import org.hyperledger.besu.services.PluginTransactionValidatorServiceImpl;
import org.hyperledger.besu.services.PrivacyPluginServiceImpl;
import org.hyperledger.besu.services.RpcEndpointServiceImpl;
import org.hyperledger.besu.services.SecurityModuleServiceImpl;
Expand Down Expand Up @@ -372,6 +375,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
P2PDiscoveryOptionGroup p2PDiscoveryOptionGroup = new P2PDiscoveryOptionGroup();

private final TransactionSelectionServiceImpl transactionSelectionServiceImpl;
private final PluginTransactionValidatorServiceImpl transactionValidatorServiceImpl;

static class P2PDiscoveryOptionGroup {

Expand Down Expand Up @@ -1355,7 +1359,8 @@ public BesuCommand(
new PrivacyPluginServiceImpl(),
new PkiBlockCreationConfigurationProvider(),
new RpcEndpointServiceImpl(),
new TransactionSelectionServiceImpl());
new TransactionSelectionServiceImpl(),
new PluginTransactionValidatorServiceImpl());
}

/**
Expand All @@ -1376,6 +1381,7 @@ public BesuCommand(
* @param pkiBlockCreationConfigProvider instance of PkiBlockCreationConfigurationProvider
* @param rpcEndpointServiceImpl instance of RpcEndpointServiceImpl
* @param transactionSelectionServiceImpl instance of TransactionSelectionServiceImpl
* @param transactionValidatorServiceImpl instance of TransactionValidatorServiceImpl
*/
@VisibleForTesting
protected BesuCommand(
Expand All @@ -1393,7 +1399,8 @@ protected BesuCommand(
final PrivacyPluginServiceImpl privacyPluginService,
final PkiBlockCreationConfigurationProvider pkiBlockCreationConfigProvider,
final RpcEndpointServiceImpl rpcEndpointServiceImpl,
final TransactionSelectionServiceImpl transactionSelectionServiceImpl) {
final TransactionSelectionServiceImpl transactionSelectionServiceImpl,
final PluginTransactionValidatorServiceImpl transactionValidatorServiceImpl) {
this.besuComponent = besuComponent;
this.logger = besuComponent.getBesuCommandLogger();
this.rlpBlockImporter = rlpBlockImporter;
Expand All @@ -1412,6 +1419,7 @@ protected BesuCommand(
this.pkiBlockCreationConfigProvider = pkiBlockCreationConfigProvider;
this.rpcEndpointServiceImpl = rpcEndpointServiceImpl;
this.transactionSelectionServiceImpl = transactionSelectionServiceImpl;
this.transactionValidatorServiceImpl = transactionValidatorServiceImpl;
}

/**
Expand Down Expand Up @@ -1593,6 +1601,8 @@ private void preparePlugins() {
besuPluginContext.addService(RpcEndpointService.class, rpcEndpointServiceImpl);
besuPluginContext.addService(
TransactionSelectionService.class, transactionSelectionServiceImpl);
besuPluginContext.addService(
PluginTransactionValidatorService.class, transactionValidatorServiceImpl);

// register built-in plugins
rocksDBPlugin = new RocksDBPlugin();
Expand Down Expand Up @@ -2193,7 +2203,7 @@ private void ensureAllNodesAreInAllowlist(
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());
Expand Down Expand Up @@ -2226,15 +2236,14 @@ public BesuController buildController() {
*/
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())
.pluginTransactionValidatorFactory(getPluginTransactionValidatorFactory())
.dataDirectory(dataDir())
.miningParameters(
new MiningParameters.Builder()
Expand Down Expand Up @@ -2291,6 +2300,12 @@ private Optional<TransactionSelectorFactory> getTransactionSelectorFactory() {
return txSelectionService.isPresent() ? txSelectionService.get().get() : Optional.empty();
}

private PluginTransactionValidatorFactory getPluginTransactionValidatorFactory() {
final Optional<PluginTransactionValidatorService> txSValidatorService =
besuPluginContext.getService(PluginTransactionValidatorService.class);
return txSValidatorService.map(PluginTransactionValidatorService::get).orElse(null);
}

private GraphQLConfiguration graphQLConfiguration() {

CommandLineUtils.checkOptionDependencies(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
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;
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -537,6 +540,18 @@ public BesuControllerBuilder transactionSelectorFactory(
return this;
}

/**
* sets the pluginTransactionValidatorFactory
*
* @param pluginTransactionValidatorFactory factory that creates plugin transaction Validators
* @return the besu controller builder
*/
public BesuControllerBuilder pluginTransactionValidatorFactory(
final PluginTransactionValidatorFactory pluginTransactionValidatorFactory) {
this.pluginTransactionValidatorFactory = pluginTransactionValidatorFactory;
return this;
}

/**
* Build besu controller.
*
Expand Down Expand Up @@ -695,7 +710,8 @@ public BesuController build() {
metricsSystem,
syncState,
miningParameters,
transactionPoolConfiguration);
transactionPoolConfiguration,
pluginTransactionValidatorFactory);

final List<PeerValidator> peerValidators = createPeerValidators(protocolSchedule);

Expand Down
Original file line number Diff line number Diff line change
@@ -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.PluginTransactionValidatorService;
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory;

/** The Transaction Validation service implementation. */
public class PluginTransactionValidatorServiceImpl implements PluginTransactionValidatorService {

private PluginTransactionValidatorFactory factory;

@Override
public PluginTransactionValidatorFactory get() {
return factory;
}

@Override
public void registerTransactionValidatorFactory(
final PluginTransactionValidatorFactory transactionValidatorFactory) {
factory = transactionValidatorFactory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
import org.hyperledger.besu.plugin.services.storage.SegmentIdentifier;
import org.hyperledger.besu.services.BesuPluginContextImpl;
import org.hyperledger.besu.services.PermissioningServiceImpl;
import org.hyperledger.besu.services.PluginTransactionValidatorServiceImpl;
import org.hyperledger.besu.services.PrivacyPluginServiceImpl;
import org.hyperledger.besu.services.RpcEndpointServiceImpl;
import org.hyperledger.besu.services.SecurityModuleServiceImpl;
Expand Down Expand Up @@ -240,6 +241,8 @@ public void initMocks() throws Exception {
when(mockControllerBuilder.maxRemotelyInitiatedPeers(anyInt()))
.thenReturn(mockControllerBuilder);
when(mockControllerBuilder.transactionSelectorFactory(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.pluginTransactionValidatorFactory(any()))
.thenReturn(mockControllerBuilder);
when(mockControllerBuilder.besuComponent(any(BesuComponent.class)))
.thenReturn(mockControllerBuilder);
// doReturn used because of generic BesuController
Expand Down Expand Up @@ -489,7 +492,8 @@ public static class TestBesuCommand extends BesuCommand {
privacyPluginService,
pkiBlockCreationConfigProvider,
rpcEndpointServiceImpl,
new TransactionSelectionServiceImpl());
new TransactionSelectionServiceImpl(),
new PluginTransactionValidatorServiceImpl());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ public void setUp() {
new NoOpMetricsSystem(),
syncState,
new MiningParameters.Builder().minTransactionGasPrice(Wei.ZERO).build(),
txPoolConfig);
txPoolConfig,
null);

serviceImpl = new BesuEventsImpl(blockchain, blockBroadcaster, transactionPool, syncState);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public void sepoliaWithNodesAllowlistOptionWhichDoesIncludeRopstenBootnodesMustN
toml.toAbsolutePath().toString());

final List<EnodeURL> enodeURIs = ethNetworkConfig.getBootNodes();
PermissioningConfigurationValidator.areAllNodesAreInAllowlist(
PermissioningConfigurationValidator.areAllNodesInAllowlist(
enodeURIs, permissioningConfiguration);
}

Expand All @@ -92,7 +92,7 @@ public void nodesAllowlistOptionWhichDoesNotIncludeBootnodesMustError() throws E

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) {
Expand Down Expand Up @@ -142,7 +142,7 @@ public void nodeAllowlistCheckShouldIgnoreDiscoveryPortParam() throws Exception
// 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(
Expand Down Expand Up @@ -180,7 +180,7 @@ public void nodeAllowlistCheckShouldWorkWithHostnameIfDnsEnabled() throws Except
// 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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,8 @@ private TransactionPool createTransactionPool() {
ethContext,
mock(MiningParameters.class),
new TransactionPoolMetrics(metricsSystem),
conf);
conf,
null);
transactionPool.setEnabled();
return transactionPool;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ private TransactionPool createTransactionPool() {
cliqueEthContext,
mock(MiningParameters.class),
new TransactionPoolMetrics(metricsSystem),
conf);
conf,
null);

transactionPool.setEnabled();
return transactionPool;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,8 @@ private static ControllerAndState createControllerAndFinalState(
ethContext,
miningParams,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
null);

transactionPool.setEnabled();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ public BlockHeaderValidator.Builder createBlockHeaderRuleset(
ethContext,
mock(MiningParameters.class),
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
null);

transactionPool.setEnabled();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ public void setUp() {
ethContext,
miningParameters,
new TransactionPoolMetrics(metricsSystem),
poolConf);
poolConf,
null);

this.transactionPool.setEnabled();

Expand Down
Loading

0 comments on commit 1c261db

Please sign in to comment.