diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java index 157421531bb..4e48be0401f 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java @@ -128,6 +128,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable private boolean useWsForJsonRpc = false; private String token = null; private final List plugins = new ArrayList<>(); + private final List requestedPlugins; private final List extraCLIOptions; private final List staticNodes; private boolean isDnsEnabled = false; @@ -163,6 +164,7 @@ public BesuNode( final boolean secp256k1Native, final boolean altbn128Native, final List plugins, + final List requestedPlugins, final List extraCLIOptions, final List staticNodes, final boolean isDnsEnabled, @@ -224,6 +226,7 @@ public BesuNode( LOG.error("Could not find plugin \"{}\" in resources", pluginName); } }); + this.requestedPlugins = requestedPlugins; engineRpcConfiguration.ifPresent( config -> MergeConfigOptions.setMergeEnabled(config.isEnabled())); this.extraCLIOptions = extraCLIOptions; @@ -738,6 +741,10 @@ public List getPlugins() { return plugins; } + public List getRequestedPlugins() { + return requestedPlugins; + } + @Override public List getExtraCLIOptions() { return extraCLIOptions; diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java index 6e00701ef2b..e08fb29f673 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java @@ -17,8 +17,8 @@ import static com.google.common.base.Preconditions.checkState; import static java.nio.charset.StandardCharsets.UTF_8; +import org.hyperledger.besu.cli.options.DataStorageOptions; import org.hyperledger.besu.cli.options.TransactionPoolOptions; -import org.hyperledger.besu.cli.options.stable.DataStorageOptions; import org.hyperledger.besu.cli.options.unstable.NetworkingOptions; import org.hyperledger.besu.ethereum.api.jsonrpc.ipc.JsonRpcIpcConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration; @@ -451,6 +451,11 @@ private List commandlineArgs(final BesuNode node, final Path dataDir) { params.add("--logging=" + level); } + if (!node.getRequestedPlugins().isEmpty()) { + params.add( + "--plugins=" + node.getRequestedPlugins().stream().collect(Collectors.joining(","))); + } + params.addAll(node.getRunCommand()); return params; } 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 90de0b0e952..d4334fd93cf 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 @@ -39,6 +39,7 @@ import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters; import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.plugins.PluginConfiguration; +import org.hyperledger.besu.ethereum.core.plugins.PluginInfo; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.BlobCacheModule; @@ -302,6 +303,12 @@ public List provideExtraCLIOptions() { return toProvide.getExtraCLIOptions(); } + @Provides + @Named("RequestedPlugins") + public List provideRequestedPlugins() { + return toProvide.getRequestedPlugins(); + } + @Provides Path provideDataDir() { return toProvide.homeDirectory(); @@ -469,7 +476,8 @@ public BesuPluginContextImpl providePluginContext( final RpcEndpointServiceImpl rpcEndpointServiceImpl, final BesuConfiguration commonPluginConfiguration, final PermissioningServiceImpl permissioningService, - final @Named("ExtraCLIOptions") List extraCLIOptions) { + final @Named("ExtraCLIOptions") List extraCLIOptions, + final @Named("RequestedPlugins") List requestedPlugins) { final CommandLine commandLine = new CommandLine(CommandSpec.create()); final BesuPluginContextImpl besuPluginContext = new BesuPluginContextImpl(); besuPluginContext.addService(StorageService.class, storageService); @@ -504,7 +512,10 @@ public BesuPluginContextImpl providePluginContext( besuPluginContext.addService(PrivacyPluginService.class, new PrivacyPluginServiceImpl()); besuPluginContext.initialize( - new PluginConfiguration.Builder().pluginsDir(pluginsPath).build()); + new PluginConfiguration.Builder() + .pluginsDir(pluginsPath) + .requestedPlugins(requestedPlugins.stream().map(PluginInfo::new).toList()) + .build()); besuPluginContext.registerPlugins(); commandLine.parseArgs(extraCLIOptions.toArray(new String[0])); diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java index c77a6c8ac7b..ba69e4ddd72 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java @@ -64,6 +64,7 @@ public class BesuNodeConfiguration { private final boolean secp256k1Native; private final boolean altbn128Native; private final List plugins; + private final List requestedPlugins; private final List extraCLIOptions; private final List staticNodes; private final boolean isDnsEnabled; @@ -102,6 +103,7 @@ public class BesuNodeConfiguration { final boolean secp256k1Native, final boolean altbn128Native, final List plugins, + final List requestedPlugins, final List extraCLIOptions, final List staticNodes, final boolean isDnsEnabled, @@ -137,6 +139,7 @@ public class BesuNodeConfiguration { this.secp256k1Native = secp256k1Native; this.altbn128Native = altbn128Native; this.plugins = plugins; + this.requestedPlugins = requestedPlugins; this.extraCLIOptions = extraCLIOptions; this.staticNodes = staticNodes; this.isDnsEnabled = isDnsEnabled; @@ -239,6 +242,10 @@ public List getPlugins() { return plugins; } + public List getRequestedPlugins() { + return requestedPlugins; + } + public List getExtraCLIOptions() { return extraCLIOptions; } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java index d8b5e0f9040..ead01ce97d2 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java @@ -93,6 +93,7 @@ public class BesuNodeConfigurationBuilder { private boolean secp256K1Native = true; private boolean altbn128Native = true; private final List plugins = new ArrayList<>(); + private final List requestedPlugins = new ArrayList<>(); private final List extraCLIOptions = new ArrayList<>(); private List staticNodes = new ArrayList<>(); private boolean isDnsEnabled = false; @@ -448,6 +449,12 @@ public BesuNodeConfigurationBuilder plugins(final List plugins) { return this; } + public BesuNodeConfigurationBuilder requestedPlugins(final List requestedPlugins) { + this.requestedPlugins.clear(); + this.requestedPlugins.addAll(requestedPlugins); + return this; + } + public BesuNodeConfigurationBuilder extraCLIOptions(final List extraCLIOptions) { this.extraCLIOptions.clear(); this.extraCLIOptions.addAll(extraCLIOptions); @@ -545,6 +552,7 @@ public BesuNodeConfiguration build() { secp256K1Native, altbn128Native, plugins, + requestedPlugins, extraCLIOptions, staticNodes, isDnsEnabled, diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java index 0451d2a7212..b39274c3d9c 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java @@ -77,6 +77,7 @@ public BesuNode create(final BesuNodeConfiguration config) throws IOException { config.isSecp256k1Native(), config.isAltbn128Native(), config.getPlugins(), + config.getRequestedPlugins(), config.getExtraCLIOptions(), config.getStaticNodes(), config.isDnsEnabled(), 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 e93518cad43..fa59f48dd7b 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -24,7 +24,6 @@ import static org.hyperledger.besu.cli.util.CommandLineUtils.DEPENDENCY_WARNING_MSG; import static org.hyperledger.besu.cli.util.CommandLineUtils.isOptionSet; import static org.hyperledger.besu.controller.BesuController.DATABASE_PATH; -import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration.DEFAULT_ENGINE_JSON_RPC_PORT; import static org.hyperledger.besu.ethereum.api.jsonrpc.authentication.EngineAuthService.EPHEMERAL_JWT_FILE; import static org.hyperledger.besu.nat.kubernetes.KubernetesNatManager.DEFAULT_BESU_SERVICE_NAME_FILTER; @@ -38,22 +37,22 @@ import org.hyperledger.besu.cli.config.NetworkName; import org.hyperledger.besu.cli.config.ProfilesCompletionCandidates; import org.hyperledger.besu.cli.converter.MetricCategoryConverter; -import org.hyperledger.besu.cli.converter.PercentageConverter; -import org.hyperledger.besu.cli.converter.SubnetInfoConverter; import org.hyperledger.besu.cli.custom.JsonRPCAllowlistHostsProperty; import org.hyperledger.besu.cli.error.BesuExecutionExceptionHandler; import org.hyperledger.besu.cli.error.BesuParameterExceptionHandler; +import org.hyperledger.besu.cli.options.DataStorageOptions; import org.hyperledger.besu.cli.options.MiningOptions; import org.hyperledger.besu.cli.options.TransactionPoolOptions; import org.hyperledger.besu.cli.options.stable.ApiConfigurationOptions; -import org.hyperledger.besu.cli.options.stable.DataStorageOptions; +import org.hyperledger.besu.cli.options.stable.EngineRPCConfiguration; +import org.hyperledger.besu.cli.options.stable.EngineRPCOptions; import org.hyperledger.besu.cli.options.stable.EthstatsOptions; import org.hyperledger.besu.cli.options.stable.GraphQlOptions; import org.hyperledger.besu.cli.options.stable.JsonRpcHttpOptions; import org.hyperledger.besu.cli.options.stable.LoggingLevelOption; import org.hyperledger.besu.cli.options.stable.MetricsOptionGroup; import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption; -import org.hyperledger.besu.cli.options.stable.P2PTLSConfigOptions; +import org.hyperledger.besu.cli.options.stable.P2PDiscoveryOptions; import org.hyperledger.besu.cli.options.stable.PermissionsOptions; import org.hyperledger.besu.cli.options.stable.PluginsConfigurationOptions; import org.hyperledger.besu.cli.options.stable.RpcWebsocketOptions; @@ -67,6 +66,7 @@ import org.hyperledger.besu.cli.options.unstable.NatOptions; import org.hyperledger.besu.cli.options.unstable.NativeLibraryOptions; import org.hyperledger.besu.cli.options.unstable.NetworkingOptions; +import org.hyperledger.besu.cli.options.unstable.P2PTLSConfigOptions; import org.hyperledger.besu.cli.options.unstable.PrivacyPluginOptions; import org.hyperledger.besu.cli.options.unstable.RPCOptions; import org.hyperledger.besu.cli.options.unstable.SynchronizerOptions; @@ -124,6 +124,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.mainnet.FrontierTargetingGasLimitCalculator; import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration; +import org.hyperledger.besu.ethereum.p2p.discovery.P2PDiscoveryConfiguration; import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.p2p.peers.StaticNodesParser; @@ -209,7 +210,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.math.BigInteger; -import java.net.InetAddress; import java.net.SocketException; import java.net.URI; import java.net.URL; @@ -247,7 +247,6 @@ import io.vertx.core.VertxOptions; import io.vertx.core.json.DecodeException; import io.vertx.core.metrics.MetricsOptions; -import org.apache.commons.net.util.SubnetUtils.SubnetInfo; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; import org.slf4j.Logger; @@ -416,7 +415,9 @@ void setUserName(final String userName) { // P2P Discovery Option Group @CommandLine.ArgGroup(validate = false, heading = "@|bold P2P Discovery Options|@%n") - P2PDiscoveryOptionGroup p2PDiscoveryOptionGroup = new P2PDiscoveryOptionGroup(); + P2PDiscoveryOptions p2PDiscoveryOptions = new P2PDiscoveryOptions(); + + P2PDiscoveryConfiguration p2PDiscoveryConfig; private final TransactionSelectionServiceImpl transactionSelectionServiceImpl; private final TransactionPoolValidatorServiceImpl transactionValidatorServiceImpl; @@ -424,163 +425,6 @@ void setUserName(final String userName) { private final BlockchainServiceImpl blockchainServiceImpl; private BesuComponent besuComponent; - static class P2PDiscoveryOptionGroup { - - // Public IP stored to prevent having to research it each time we need it. - private InetAddress autoDiscoveredDefaultIP = null; - - // Completely disables P2P within Besu. - @Option( - names = {"--p2p-enabled"}, - description = "Enable P2P functionality (default: ${DEFAULT-VALUE})", - arity = "1") - private final Boolean p2pEnabled = true; - - // Boolean option to indicate if peers should NOT be discovered, default to - // false indicates that - // the peers should be discovered by default. - // - // This negative option is required because of the nature of the option that is - // true when - // added on the command line. You can't do --option=false, so false is set as - // default - // and you have not to set the option at all if you want it false. - // This seems to be the only way it works with Picocli. - // Also many other software use the same negative option scheme for false - // defaults - // meaning that it's probably the right way to handle disabling options. - @Option( - names = {"--discovery-enabled"}, - description = "Enable P2P discovery (default: ${DEFAULT-VALUE})", - arity = "1") - private final Boolean peerDiscoveryEnabled = true; - - // A list of bootstrap nodes can be passed - // and a hardcoded list will be used otherwise by the Runner. - // NOTE: we have no control over default value here. - @Option( - names = {"--bootnodes"}, - paramLabel = "", - description = - "Comma separated enode URLs for P2P discovery bootstrap. " - + "Default is a predefined list.", - split = ",", - arity = "0..*") - private final List bootNodes = null; - - @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. - @Option( - names = {"--p2p-host"}, - paramLabel = MANDATORY_HOST_FORMAT_HELP, - description = "IP address this node advertises to its peers (default: ${DEFAULT-VALUE})", - arity = "1") - private String p2pHost = autoDiscoverDefaultIP().getHostAddress(); - - @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. - @Option( - names = {"--p2p-interface"}, - paramLabel = MANDATORY_HOST_FORMAT_HELP, - description = - "The network interface address on which this node listens for P2P communication (default: ${DEFAULT-VALUE})", - arity = "1") - private String p2pInterface = NetworkUtility.INADDR_ANY; - - @Option( - names = {"--p2p-port"}, - paramLabel = MANDATORY_PORT_FORMAT_HELP, - description = "Port on which to listen for P2P communication (default: ${DEFAULT-VALUE})", - arity = "1") - private final Integer p2pPort = EnodeURLImpl.DEFAULT_LISTENING_PORT; - - @Option( - names = {"--max-peers", "--p2p-peer-upper-bound"}, - paramLabel = MANDATORY_INTEGER_FORMAT_HELP, - description = "Maximum P2P connections that can be established (default: ${DEFAULT-VALUE})") - private final Integer maxPeers = DEFAULT_MAX_PEERS; - - @Option( - names = {"--remote-connections-limit-enabled"}, - description = - "Whether to limit the number of P2P connections initiated remotely. (default: ${DEFAULT-VALUE})") - private final Boolean isLimitRemoteWireConnectionsEnabled = true; - - @Option( - names = {"--remote-connections-max-percentage"}, - paramLabel = MANDATORY_DOUBLE_FORMAT_HELP, - description = - "The maximum percentage of P2P connections that can be initiated remotely. Must be between 0 and 100 inclusive. (default: ${DEFAULT-VALUE})", - arity = "1", - converter = PercentageConverter.class) - private final Percentage maxRemoteConnectionsPercentage = - Fraction.fromFloat(DEFAULT_FRACTION_REMOTE_WIRE_CONNECTIONS_ALLOWED).toPercentage(); - - @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. - @CommandLine.Option( - names = {"--discovery-dns-url"}, - description = "Specifies the URL to use for DNS discovery") - private String discoveryDnsUrl = null; - - @Option( - names = {"--random-peer-priority-enabled"}, - description = - "Allow for incoming connections to be prioritized randomly. This will prevent (typically small, stable) networks from forming impenetrable peer cliques. (default: ${DEFAULT-VALUE})") - private final Boolean randomPeerPriority = Boolean.FALSE; - - @Option( - names = {"--banned-node-ids", "--banned-node-id"}, - paramLabel = MANDATORY_NODE_ID_FORMAT_HELP, - description = "A list of node IDs to ban from the P2P network.", - split = ",", - arity = "1..*") - void setBannedNodeIds(final List values) { - try { - bannedNodeIds = - values.stream() - .filter(value -> !value.isEmpty()) - .map(EnodeURLImpl::parseNodeId) - .collect(Collectors.toList()); - } catch (final IllegalArgumentException e) { - throw new ParameterException( - new CommandLine(this), - "Invalid ids supplied to '--banned-node-ids'. " + e.getMessage()); - } - } - - // Boolean option to set that in a PoA network the bootnodes should always be queried during - // peer table refresh. If this flag is disabled bootnodes are only sent FINDN requests on first - // startup, meaning that an offline bootnode or network outage at the client can prevent it - // discovering any peers without a restart. - @Option( - names = {"--poa-discovery-retry-bootnodes"}, - description = - "Always use of bootnodes for discovery in PoA networks. Disabling this reverts " - + " to the same behaviour as non-PoA networks, where neighbours are only discovered from bootnodes on first startup." - + "(default: ${DEFAULT-VALUE})", - arity = "1") - private final Boolean poaDiscoveryRetryBootnodes = true; - - private Collection bannedNodeIds = new ArrayList<>(); - - // Used to discover the default IP of the client. - // Loopback IP is used by default as this is how smokeTests require it to be - // and it's probably a good security behaviour to default only on the localhost. - private InetAddress autoDiscoverDefaultIP() { - autoDiscoveredDefaultIP = - Optional.ofNullable(autoDiscoveredDefaultIP).orElseGet(InetAddress::getLoopbackAddress); - - return autoDiscoveredDefaultIP; - } - - @Option( - names = {"--net-restrict"}, - arity = "1..*", - split = ",", - converter = SubnetInfoConverter.class, - description = - "Comma-separated list of allowed IP subnets (e.g., '192.168.1.0/24,10.0.0.0/8').") - private List allowedSubnets; - } - @Option( names = {"--sync-mode"}, paramLabel = MANDATORY_MODE_FORMAT_HELP, @@ -647,42 +491,9 @@ private InetAddress autoDiscoverDefaultIP() { // Engine JSON-PRC Options @CommandLine.ArgGroup(validate = false, heading = "@|bold Engine JSON-RPC Options|@%n") - EngineRPCOptionGroup engineRPCOptionGroup = new EngineRPCOptionGroup(); - - static class EngineRPCOptionGroup { - @Option( - names = {"--engine-rpc-enabled"}, - description = - "enable the engine api, even in the absence of merge-specific configurations.") - private final Boolean overrideEngineRpcEnabled = false; - - @Option( - names = {"--engine-rpc-port", "--engine-rpc-http-port"}, - paramLabel = MANDATORY_PORT_FORMAT_HELP, - description = "Port to provide consensus client APIS on (default: ${DEFAULT-VALUE})", - arity = "1") - private final Integer engineRpcPort = DEFAULT_ENGINE_JSON_RPC_PORT; + EngineRPCOptions engineRPCOptions = new EngineRPCOptions(); - @Option( - names = {"--engine-jwt-secret"}, - paramLabel = MANDATORY_FILE_FORMAT_HELP, - description = "Path to file containing shared secret key for JWT signature verification") - private final Path engineJwtKeyFile = null; - - @Option( - names = {"--engine-jwt-disabled"}, - description = "Disable authentication for Engine APIs (default: ${DEFAULT-VALUE})") - private final Boolean isEngineAuthDisabled = false; - - @Option( - names = {"--engine-host-allowlist"}, - paramLabel = "[,...]... or * or all", - description = - "Comma separated list of hostnames to allow for ENGINE API access (applies to both HTTP and websockets), or * to accept any host (default: ${DEFAULT-VALUE})", - defaultValue = "localhost,127.0.0.1") - private final JsonRPCAllowlistHostsProperty engineHostsAllowlist = - new JsonRPCAllowlistHostsProperty(); - } + EngineRPCConfiguration engineRPCConfig = engineRPCOptions.toDomainObject(); // JSON-RPC HTTP Options @CommandLine.ArgGroup(validate = false, heading = "@|bold JSON-RPC HTTP Options|@%n") @@ -1409,13 +1220,13 @@ private void preSynchronization() { private Runner buildRunner() { return synchronize( besuController, - p2PDiscoveryOptionGroup.p2pEnabled, + p2PDiscoveryConfig.p2pEnabled(), p2pTLSConfiguration, - p2PDiscoveryOptionGroup.peerDiscoveryEnabled, + p2PDiscoveryConfig.peerDiscoveryEnabled(), ethNetworkConfig, - p2PDiscoveryOptionGroup.p2pHost, - p2PDiscoveryOptionGroup.p2pInterface, - p2PDiscoveryOptionGroup.p2pPort, + p2PDiscoveryConfig.p2pHost(), + p2PDiscoveryConfig.p2pInterface(), + p2PDiscoveryConfig.p2pPort(), graphQLConfiguration, jsonRpcConfiguration, engineJsonRpcConfiguration, @@ -1614,7 +1425,7 @@ private void configureNativeLibs() { private void validateOptions() { validateRequiredOptions(); issueOptionWarnings(); - validateP2PInterface(p2PDiscoveryOptionGroup.p2pInterface); + validateP2PInterface(p2PDiscoveryOptions.p2pInterface); validateMiningParams(); validateNatParams(); validateNetStatsParams(); @@ -1750,13 +1561,12 @@ private void validateDnsOptionsParams() { } private void ensureValidPeerBoundParams() { - maxPeers = p2PDiscoveryOptionGroup.maxPeers; + maxPeers = p2PDiscoveryOptions.maxPeers; final Boolean isLimitRemoteWireConnectionsEnabled = - p2PDiscoveryOptionGroup.isLimitRemoteWireConnectionsEnabled; + p2PDiscoveryOptions.isLimitRemoteWireConnectionsEnabled; if (isLimitRemoteWireConnectionsEnabled) { final float fraction = - Fraction.fromPercentage(p2PDiscoveryOptionGroup.maxRemoteConnectionsPercentage) - .getValue(); + Fraction.fromPercentage(p2PDiscoveryOptions.maxRemoteConnectionsPercentage).getValue(); checkState( fraction >= 0.0 && fraction <= 1.0, "Fraction of remote connections allowed must be between 0.0 and 1.0 (inclusive)."); @@ -1820,7 +1630,7 @@ private void issueOptionWarnings() { logger, commandLine, "--p2p-enabled", - !p2PDiscoveryOptionGroup.p2pEnabled, + !p2PDiscoveryOptions.p2pEnabled, asList( "--bootnodes", "--discovery-enabled", @@ -1861,6 +1671,8 @@ && isOptionSet(commandLine, "--sync-min-peers")) { } private void configure() throws Exception { + p2PDiscoveryConfig = p2PDiscoveryOptions.toDomainObject(); + engineRPCConfig = engineRPCOptions.toDomainObject(); checkPortClash(); checkIfRequiredPortsAreAvailable(); syncMode = getDefaultSyncModeIfNotSet(); @@ -1870,25 +1682,18 @@ private void configure() throws Exception { jsonRpcConfiguration = jsonRpcHttpOptions.jsonRpcConfiguration( - hostsAllowlist, - p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(), - unstableRPCOptions.getHttpTimeoutSec()); + hostsAllowlist, p2PDiscoveryOptions.p2pHost, unstableRPCOptions.getHttpTimeoutSec()); if (isEngineApiEnabled()) { - engineJsonRpcConfiguration = - createEngineJsonRpcConfiguration( - engineRPCOptionGroup.engineRpcPort, engineRPCOptionGroup.engineHostsAllowlist); + engineJsonRpcConfiguration = createEngineJsonRpcConfiguration(); } p2pTLSConfiguration = p2pTLSConfigOptions.p2pTLSConfiguration(commandLine); graphQLConfiguration = graphQlOptions.graphQLConfiguration( - hostsAllowlist, - p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(), - unstableRPCOptions.getHttpTimeoutSec()); + hostsAllowlist, p2PDiscoveryOptions.p2pHost, unstableRPCOptions.getHttpTimeoutSec()); + webSocketConfiguration = rpcWebsocketOptions.webSocketConfiguration( - hostsAllowlist, - p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(), - unstableRPCOptions.getWsTimeoutSec()); + hostsAllowlist, p2PDiscoveryConfig.p2pHost(), unstableRPCOptions.getWsTimeoutSec()); jsonRpcIpcConfiguration = jsonRpcIpcConfiguration( unstableIpcOptions.isEnabled(), @@ -2008,32 +1813,31 @@ public BesuControllerBuilder setupControllerBuilder() { .requiredBlocks(requiredBlocks) .reorgLoggingThreshold(reorgLoggingThreshold) .evmConfiguration(unstableEvmOptions.toDomainObject()) - .maxPeers(p2PDiscoveryOptionGroup.maxPeers) + .maxPeers(p2PDiscoveryOptions.maxPeers) .maxRemotelyInitiatedPeers(maxRemoteInitiatedPeers) - .randomPeerPriority(p2PDiscoveryOptionGroup.randomPeerPriority) + .randomPeerPriority(p2PDiscoveryOptions.randomPeerPriority) .chainPruningConfiguration(unstableChainPruningOptions.toDomainObject()) .cacheLastBlocks(numberOfblocksToCache) .genesisStateHashCacheEnabled(genesisStateHashCacheEnabled) .besuComponent(besuComponent); } - private JsonRpcConfiguration createEngineJsonRpcConfiguration( - final Integer engineListenPort, final List allowCallsFrom) { + private JsonRpcConfiguration createEngineJsonRpcConfiguration() { jsonRpcHttpOptions.checkDependencies(logger, commandLine); final JsonRpcConfiguration engineConfig = jsonRpcHttpOptions.jsonRpcConfiguration( - allowCallsFrom, - p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(), + engineRPCConfig.engineHostsAllowlist(), + p2PDiscoveryConfig.p2pHost(), unstableRPCOptions.getWsTimeoutSec()); - engineConfig.setPort(engineListenPort); + engineConfig.setPort(engineRPCConfig.engineRpcPort()); engineConfig.setRpcApis(Arrays.asList("ENGINE", "ETH")); engineConfig.setEnabled(isEngineApiEnabled()); - if (!engineRPCOptionGroup.isEngineAuthDisabled) { + if (!engineRPCConfig.isEngineAuthDisabled()) { engineConfig.setAuthenticationEnabled(true); engineConfig.setAuthenticationAlgorithm(JwtAlgorithm.HS256); - if (Objects.nonNull(engineRPCOptionGroup.engineJwtKeyFile) - && java.nio.file.Files.exists(engineRPCOptionGroup.engineJwtKeyFile)) { - engineConfig.setAuthenticationPublicKeyFile(engineRPCOptionGroup.engineJwtKeyFile.toFile()); + if (Objects.nonNull(engineRPCConfig.engineJwtKeyFile()) + && java.nio.file.Files.exists(engineRPCConfig.engineJwtKeyFile())) { + engineConfig.setAuthenticationPublicKeyFile(engineRPCConfig.engineJwtKeyFile().toFile()); } else { logger.warn( "Engine API authentication enabled without key file. Expect ephemeral jwt.hex file in datadir"); @@ -2090,7 +1894,7 @@ public MetricsConfiguration metricsConfiguration() { .enabled(metricsOptionGroup.getMetricsEnabled()) .host( Strings.isNullOrEmpty(metricsOptionGroup.getMetricsHost()) - ? p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress() + ? p2PDiscoveryOptions.p2pHost : metricsOptionGroup.getMetricsHost()) .port(metricsOptionGroup.getMetricsPort()) .protocol(metricsOptionGroup.getMetricsProtocol()) @@ -2098,7 +1902,7 @@ public MetricsConfiguration metricsConfiguration() { .pushEnabled(metricsOptionGroup.getMetricsPushEnabled()) .pushHost( Strings.isNullOrEmpty(metricsOptionGroup.getMetricsPushHost()) - ? p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress() + ? p2PDiscoveryOptions.autoDiscoverDefaultIP().getHostAddress() : metricsOptionGroup.getMetricsPushHost()) .pushPort(metricsOptionGroup.getMetricsPushPort()) .pushInterval(metricsOptionGroup.getMetricsPushInterval()) @@ -2441,7 +2245,7 @@ private Runner synchronize( .apiConfiguration(apiConfiguration) .pidPath(pidPath) .dataDir(dataDir()) - .bannedNodeIds(p2PDiscoveryOptionGroup.bannedNodeIds) + .bannedNodeIds(p2PDiscoveryConfig.bannedNodeIds()) .metricsSystem((ObservableMetricsSystem) besuComponent.getMetricsSystem()) .permissioningService(permissioningService) .metricsConfiguration(metricsConfiguration) @@ -2453,8 +2257,8 @@ private Runner synchronize( .storageProvider(keyValueStorageProvider(keyValueStorageName)) .rpcEndpointService(rpcEndpointServiceImpl) .enodeDnsConfiguration(getEnodeDnsConfiguration()) - .allowedSubnets(p2PDiscoveryOptionGroup.allowedSubnets) - .poaDiscoveryRetryBootnodes(p2PDiscoveryOptionGroup.poaDiscoveryRetryBootnodes) + .allowedSubnets(p2PDiscoveryConfig.allowedSubnets()) + .poaDiscoveryRetryBootnodes(p2PDiscoveryConfig.poaDiscoveryRetryBootnodes()) .build(); addShutdownHook(runner); @@ -2529,7 +2333,7 @@ private EthNetworkConfig updateNetworkConfig(final NetworkName network) { } } - if (p2PDiscoveryOptionGroup.bootNodes == null) { + if (p2PDiscoveryOptions.bootNodes == null) { builder.setBootNodes(new ArrayList<>()); } builder.setDnsDiscoveryUrl(null); @@ -2541,8 +2345,8 @@ private EthNetworkConfig updateNetworkConfig(final NetworkName network) { builder.setNetworkId(networkId); } - if (p2PDiscoveryOptionGroup.discoveryDnsUrl != null) { - builder.setDnsDiscoveryUrl(p2PDiscoveryOptionGroup.discoveryDnsUrl); + if (p2PDiscoveryOptions.discoveryDnsUrl != null) { + builder.setDnsDiscoveryUrl(p2PDiscoveryOptions.discoveryDnsUrl); } else { final Optional discoveryDnsUrlFromGenesis = genesisConfigOptionsSupplier.get().getDiscoveryOptions().getDiscoveryDnsUrl(); @@ -2550,9 +2354,9 @@ private EthNetworkConfig updateNetworkConfig(final NetworkName network) { } List listBootNodes = null; - if (p2PDiscoveryOptionGroup.bootNodes != null) { + if (p2PDiscoveryOptions.bootNodes != null) { try { - listBootNodes = buildEnodes(p2PDiscoveryOptionGroup.bootNodes, getEnodeDnsConfiguration()); + listBootNodes = buildEnodes(p2PDiscoveryOptions.bootNodes, getEnodeDnsConfiguration()); } catch (final IllegalArgumentException e) { throw new ParameterException(commandLine, e.getMessage()); } @@ -2564,7 +2368,7 @@ private EthNetworkConfig updateNetworkConfig(final NetworkName network) { } } if (listBootNodes != null) { - if (!p2PDiscoveryOptionGroup.peerDiscoveryEnabled) { + if (!p2PDiscoveryOptions.peerDiscoveryEnabled) { logger.warn("Discovery disabled: bootnodes will be ignored."); } DiscoveryConfiguration.assertValidBootnodes(listBootNodes); @@ -2699,12 +2503,12 @@ protected void checkIfRequiredPortsAreAvailable() { .filter(port -> port > 0) .forEach( port -> { - if (port.equals(p2PDiscoveryOptionGroup.p2pPort) + if (port.equals(p2PDiscoveryConfig.p2pPort()) && (NetworkUtility.isPortUnavailableForTcp(port) || NetworkUtility.isPortUnavailableForUdp(port))) { unavailablePorts.add(port); } - if (!port.equals(p2PDiscoveryOptionGroup.p2pPort) + if (!port.equals(p2PDiscoveryConfig.p2pPort()) && NetworkUtility.isPortUnavailableForTcp(port)) { unavailablePorts.add(port); } @@ -2724,15 +2528,14 @@ protected void checkIfRequiredPortsAreAvailable() { */ private List getEffectivePorts() { final List effectivePorts = new ArrayList<>(); - addPortIfEnabled( - effectivePorts, p2PDiscoveryOptionGroup.p2pPort, p2PDiscoveryOptionGroup.p2pEnabled); + addPortIfEnabled(effectivePorts, p2PDiscoveryOptions.p2pPort, p2PDiscoveryOptions.p2pEnabled); addPortIfEnabled( effectivePorts, graphQlOptions.getGraphQLHttpPort(), graphQlOptions.isGraphQLHttpEnabled()); addPortIfEnabled( effectivePorts, jsonRpcHttpOptions.getRpcHttpPort(), jsonRpcHttpOptions.isRpcHttpEnabled()); addPortIfEnabled( effectivePorts, rpcWebsocketOptions.getRpcWsPort(), rpcWebsocketOptions.isRpcWsEnabled()); - addPortIfEnabled(effectivePorts, engineRPCOptionGroup.engineRpcPort, isEngineApiEnabled()); + addPortIfEnabled(effectivePorts, engineRPCConfig.engineRpcPort(), isEngineApiEnabled()); addPortIfEnabled( effectivePorts, metricsOptionGroup.getMetricsPort(), @@ -2859,7 +2662,7 @@ private boolean isMergeEnabled() { } private boolean isEngineApiEnabled() { - return engineRPCOptionGroup.overrideEngineRpcEnabled || isMergeEnabled(); + return engineRPCConfig.overrideEngineRpcEnabled() || isMergeEnabled(); } private SyncMode getDefaultSyncModeIfNotSet() { diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/DataStorageOptions.java similarity index 99% rename from besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java rename to besu/src/main/java/org/hyperledger/besu/cli/options/DataStorageOptions.java index 3d53a595ec8..b442f6ee034 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/DataStorageOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/DataStorageOptions.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.cli.options.stable; +package org.hyperledger.besu.cli.options; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_LIMIT_TRIE_LOGS_ENABLED; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_MAX_LAYERS_TO_LOAD; @@ -22,7 +22,6 @@ import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_CODE_USING_CODE_HASH_ENABLED; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.Unstable.DEFAULT_BONSAI_FULL_FLAT_DB_ENABLED; -import org.hyperledger.besu.cli.options.CLIOptions; import org.hyperledger.besu.cli.util.CommandLineUtils; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration; diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/ApiConfigurationOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/ApiConfigurationOptions.java index fbed68de0cd..d6bc17026cd 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/ApiConfigurationOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/ApiConfigurationOptions.java @@ -28,6 +28,7 @@ * Handles configuration options for the API in Besu, including gas price settings, RPC log range, * and trace filter range. */ +// TODO: implement CLIOption public class ApiConfigurationOptions { /** Default constructor. */ public ApiConfigurationOptions() {} diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/EngineRPCConfiguration.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/EngineRPCConfiguration.java new file mode 100644 index 00000000000..c0eb9d1be08 --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/EngineRPCConfiguration.java @@ -0,0 +1,36 @@ +/* + * 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.cli.options.stable; + +import org.hyperledger.besu.cli.custom.JsonRPCAllowlistHostsProperty; + +import java.nio.file.Path; + +/** + * Command line options for configuring Engine RPC on the node. + * + * @param overrideEngineRpcEnabled enable the engine api, even in the absence of merge-specific + * configurations. + * @param engineRpcPort Port to provide consensus client APIS on + * @param engineJwtKeyFile Path to file containing shared secret key for JWT signature verification + * @param isEngineAuthDisabled Disable authentication for Engine APIs + * @param engineHostsAllowlist List of hosts to allowlist for Engine APIs + */ +public record EngineRPCConfiguration( + Boolean overrideEngineRpcEnabled, + Integer engineRpcPort, + Path engineJwtKeyFile, + Boolean isEngineAuthDisabled, + JsonRPCAllowlistHostsProperty engineHostsAllowlist) {} diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/EngineRPCOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/EngineRPCOptions.java new file mode 100644 index 00000000000..1aa5b3d3263 --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/EngineRPCOptions.java @@ -0,0 +1,81 @@ +/* + * 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.cli.options.stable; + +import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration.DEFAULT_ENGINE_JSON_RPC_PORT; + +import org.hyperledger.besu.cli.DefaultCommandValues; +import org.hyperledger.besu.cli.custom.JsonRPCAllowlistHostsProperty; +import org.hyperledger.besu.cli.options.CLIOptions; +import org.hyperledger.besu.cli.util.CommandLineUtils; + +import java.nio.file.Path; +import java.util.List; + +import picocli.CommandLine; + +/** Command line options for configuring Engine RPC on the node. */ +public class EngineRPCOptions implements CLIOptions { + + /** Default constructor */ + public EngineRPCOptions() {} + + @CommandLine.Option( + names = {"--engine-rpc-enabled"}, + description = "enable the engine api, even in the absence of merge-specific configurations.") + private final Boolean overrideEngineRpcEnabled = false; + + @CommandLine.Option( + names = {"--engine-rpc-port", "--engine-rpc-http-port"}, + paramLabel = DefaultCommandValues.MANDATORY_PORT_FORMAT_HELP, + description = "Port to provide consensus client APIS on (default: ${DEFAULT-VALUE})", + arity = "1") + private final Integer engineRpcPort = DEFAULT_ENGINE_JSON_RPC_PORT; + + @CommandLine.Option( + names = {"--engine-jwt-secret"}, + paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP, + description = "Path to file containing shared secret key for JWT signature verification") + private final Path engineJwtKeyFile = null; + + @CommandLine.Option( + names = {"--engine-jwt-disabled"}, + description = "Disable authentication for Engine APIs (default: ${DEFAULT-VALUE})") + private final Boolean isEngineAuthDisabled = false; + + @CommandLine.Option( + names = {"--engine-host-allowlist"}, + paramLabel = "[,...]... or * or all", + description = + "Comma separated list of hostnames to allow for ENGINE API access (applies to both HTTP and websockets), or * to accept any host (default: ${DEFAULT-VALUE})", + defaultValue = "localhost,127.0.0.1") + private final JsonRPCAllowlistHostsProperty engineHostsAllowlist = + new JsonRPCAllowlistHostsProperty(); + + @Override + public EngineRPCConfiguration toDomainObject() { + return new EngineRPCConfiguration( + overrideEngineRpcEnabled, + engineRpcPort, + engineJwtKeyFile, + isEngineAuthDisabled, + engineHostsAllowlist); + } + + @Override + public List getCLIOptions() { + return CommandLineUtils.getCLIOptions(this, new EngineRPCOptions()); + } +} diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/GraphQlOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/GraphQlOptions.java index 77cc1703052..6aac24a6fbc 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/GraphQlOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/GraphQlOptions.java @@ -29,6 +29,7 @@ import picocli.CommandLine; /** Handles configuration options for the GraphQL HTTP service in Besu. */ +// TODO: implement CLIOptions public class GraphQlOptions { @CommandLine.Option( names = {"--graphql-http-enabled"}, diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/JsonRpcHttpOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/JsonRpcHttpOptions.java index 026b83b5537..1c7e0f543e9 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/JsonRpcHttpOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/JsonRpcHttpOptions.java @@ -52,6 +52,7 @@ * Handles configuration options for the JSON-RPC HTTP service, including validation and creation of * a JSON-RPC configuration. */ +// TODO: implement CLIOption public class JsonRpcHttpOptions { @CommandLine.Option( names = {"--rpc-http-enabled"}, @@ -265,37 +266,50 @@ && rpcHttpAuthenticationCredentialsFile(commandLine) == null /** * Creates a JsonRpcConfiguration based on the provided options. * - * @param hostsAllowlist List of hosts allowed - * @param defaultHostAddress Default host address - * @param timoutSec timeout in seconds - * @return A JsonRpcConfiguration instance + * @return configuration populated from options or defaults */ - public JsonRpcConfiguration jsonRpcConfiguration( - final List hostsAllowlist, final String defaultHostAddress, final Long timoutSec) { + public JsonRpcConfiguration jsonRpcConfiguration() { final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault(); jsonRpcConfiguration.setEnabled(isRpcHttpEnabled); - jsonRpcConfiguration.setHost( - Strings.isNullOrEmpty(rpcHttpHost) ? defaultHostAddress : rpcHttpHost); jsonRpcConfiguration.setPort(rpcHttpPort); jsonRpcConfiguration.setMaxActiveConnections(rpcHttpMaxConnections); jsonRpcConfiguration.setCorsAllowedDomains(rpcHttpCorsAllowedOrigins); jsonRpcConfiguration.setRpcApis(rpcHttpApis.stream().distinct().collect(Collectors.toList())); jsonRpcConfiguration.setNoAuthRpcApis( rpcHttpApiMethodsNoAuth.stream().distinct().collect(Collectors.toList())); - jsonRpcConfiguration.setHostsAllowlist(hostsAllowlist); jsonRpcConfiguration.setAuthenticationEnabled(isRpcHttpAuthenticationEnabled); jsonRpcConfiguration.setAuthenticationCredentialsFile(rpcHttpAuthenticationCredentialsFile); jsonRpcConfiguration.setAuthenticationPublicKeyFile(rpcHttpAuthenticationPublicKeyFile); jsonRpcConfiguration.setAuthenticationAlgorithm(rpcHttpAuthenticationAlgorithm); jsonRpcConfiguration.setTlsConfiguration(rpcHttpTlsConfiguration()); - jsonRpcConfiguration.setHttpTimeoutSec(timoutSec); jsonRpcConfiguration.setMaxBatchSize(rpcHttpMaxBatchSize); jsonRpcConfiguration.setMaxRequestContentLength(rpcHttpMaxRequestContentLength); jsonRpcConfiguration.setPrettyJsonEnabled(prettyJsonEnabled); return jsonRpcConfiguration; } + /** + * Creates a JsonRpcConfiguration based on the provided options. + * + * @param hostsAllowlist List of hosts allowed + * @param defaultHostAddress Default host address + * @param timoutSec timeout in seconds + * @return A JsonRpcConfiguration instance + */ + public JsonRpcConfiguration jsonRpcConfiguration( + final List hostsAllowlist, final String defaultHostAddress, final Long timoutSec) { + + final JsonRpcConfiguration jsonRpcConfiguration = this.jsonRpcConfiguration(); + + jsonRpcConfiguration.setHost( + Strings.isNullOrEmpty(rpcHttpHost) ? defaultHostAddress : rpcHttpHost); + jsonRpcConfiguration.setHostsAllowlist(hostsAllowlist); + ; + jsonRpcConfiguration.setHttpTimeoutSec(timoutSec); + return jsonRpcConfiguration; + } + /** * Checks dependencies between options. * diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/MetricsOptionGroup.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/MetricsOptionGroup.java index add2bf16553..1c17e1cf9dd 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/MetricsOptionGroup.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/MetricsOptionGroup.java @@ -30,6 +30,7 @@ import picocli.CommandLine; /** Command line options for configuring metrics. */ +// TODO: implement CLIOption and rename to drop the Group public class MetricsOptionGroup { @CommandLine.Option( names = {"--metrics-enabled"}, diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/P2PDiscoveryOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/P2PDiscoveryOptions.java new file mode 100644 index 00000000000..21c24b3e278 --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/P2PDiscoveryOptions.java @@ -0,0 +1,238 @@ +/* + * 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.cli.options.stable; + +import org.hyperledger.besu.cli.DefaultCommandValues; +import org.hyperledger.besu.cli.converter.PercentageConverter; +import org.hyperledger.besu.cli.converter.SubnetInfoConverter; +import org.hyperledger.besu.cli.options.CLIOptions; +import org.hyperledger.besu.cli.util.CommandLineUtils; +import org.hyperledger.besu.ethereum.p2p.discovery.P2PDiscoveryConfiguration; +import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; +import org.hyperledger.besu.util.NetworkUtility; +import org.hyperledger.besu.util.number.Fraction; +import org.hyperledger.besu.util.number.Percentage; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.apache.commons.net.util.SubnetUtils; +import org.apache.tuweni.bytes.Bytes; +import picocli.CommandLine; + +/** Command line options for configuring P2P discovery on the node. */ +public class P2PDiscoveryOptions implements CLIOptions { + + /** Default constructor */ + public P2PDiscoveryOptions() {} + + // Public IP stored to prevent having to research it each time we need it. + private InetAddress autoDiscoveredDefaultIP = null; + + /** Completely disables P2P within Besu. */ + @CommandLine.Option( + names = {"--p2p-enabled"}, + description = "Enable P2P functionality (default: ${DEFAULT-VALUE})", + arity = "1") + public final Boolean p2pEnabled = true; + + /** + * Boolean option to indicate if peers should NOT be discovered, default to false indicates that + * the peers should be discovered by default. + */ + // + // This negative option is required because of the nature of the option that is + // true when + // added on the command line. You can't do --option=false, so false is set as + // default + // and you have not to set the option at all if you want it false. + // This seems to be the only way it works with Picocli. + // Also many other software use the same negative option scheme for false + // defaults + // meaning that it's probably the right way to handle disabling options. + @CommandLine.Option( + names = {"--discovery-enabled"}, + description = "Enable P2P discovery (default: ${DEFAULT-VALUE})", + arity = "1") + public final Boolean peerDiscoveryEnabled = true; + + /** + * A list of bootstrap nodes can be passed and a hardcoded list will be used otherwise by the + * Runner. + */ + // NOTE: we have no control over default value here. + @CommandLine.Option( + names = {"--bootnodes"}, + paramLabel = "", + description = + "Comma separated enode URLs for P2P discovery bootstrap. " + + "Default is a predefined list.", + split = ",", + arity = "0..*") + public final List bootNodes = null; + + /** The IP the node advertises to peers for P2P communication. */ + @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. + @CommandLine.Option( + names = {"--p2p-host"}, + paramLabel = DefaultCommandValues.MANDATORY_HOST_FORMAT_HELP, + description = "IP address this node advertises to its peers (default: ${DEFAULT-VALUE})", + arity = "1") + public String p2pHost = autoDiscoverDefaultIP().getHostAddress(); + + /** The network interface address on which this node listens for P2P communication. */ + @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. + @CommandLine.Option( + names = {"--p2p-interface"}, + paramLabel = DefaultCommandValues.MANDATORY_HOST_FORMAT_HELP, + description = + "The network interface address on which this node listens for P2P communication (default: ${DEFAULT-VALUE})", + arity = "1") + public String p2pInterface = NetworkUtility.INADDR_ANY; + + /** The port on which this node listens for P2P communication. */ + @CommandLine.Option( + names = {"--p2p-port"}, + paramLabel = DefaultCommandValues.MANDATORY_PORT_FORMAT_HELP, + description = "Port on which to listen for P2P communication (default: ${DEFAULT-VALUE})", + arity = "1") + public final Integer p2pPort = EnodeURLImpl.DEFAULT_LISTENING_PORT; + + /** The maximum number of peers this node can connect to. */ + @CommandLine.Option( + names = {"--max-peers", "--p2p-peer-upper-bound"}, + paramLabel = DefaultCommandValues.MANDATORY_INTEGER_FORMAT_HELP, + description = "Maximum P2P connections that can be established (default: ${DEFAULT-VALUE})") + public final Integer maxPeers = DefaultCommandValues.DEFAULT_MAX_PEERS; + + /** Boolean option to limit the number of P2P connections initiated remotely. */ + @CommandLine.Option( + names = {"--remote-connections-limit-enabled"}, + description = + "Whether to limit the number of P2P connections initiated remotely. (default: ${DEFAULT-VALUE})") + public final Boolean isLimitRemoteWireConnectionsEnabled = true; + + /** The maximum percentage of P2P connections that can be initiated remotely. */ + @CommandLine.Option( + names = {"--remote-connections-max-percentage"}, + paramLabel = DefaultCommandValues.MANDATORY_DOUBLE_FORMAT_HELP, + description = + "The maximum percentage of P2P connections that can be initiated remotely. Must be between 0 and 100 inclusive. (default: ${DEFAULT-VALUE})", + arity = "1", + converter = PercentageConverter.class) + public final Percentage maxRemoteConnectionsPercentage = + Fraction.fromFloat(DefaultCommandValues.DEFAULT_FRACTION_REMOTE_WIRE_CONNECTIONS_ALLOWED) + .toPercentage(); + + /** The URL to use for DNS discovery. */ + @SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings. + @CommandLine.Option( + names = {"--discovery-dns-url"}, + description = "Specifies the URL to use for DNS discovery") + public String discoveryDnsUrl = null; + + /** Boolean option to allow for incoming connections to be prioritized randomly. */ + @CommandLine.Option( + names = {"--random-peer-priority-enabled"}, + description = + "Allow for incoming connections to be prioritized randomly. This will prevent (typically small, stable) networks from forming impenetrable peer cliques. (default: ${DEFAULT-VALUE})") + public final Boolean randomPeerPriority = Boolean.FALSE; + + /** A list of node IDs to ban from the P2P network. */ + @CommandLine.Option( + names = {"--banned-node-ids", "--banned-node-id"}, + paramLabel = DefaultCommandValues.MANDATORY_NODE_ID_FORMAT_HELP, + description = "A list of node IDs to ban from the P2P network.", + split = ",", + arity = "1..*") + void setBannedNodeIds(final List values) { + try { + bannedNodeIds = + values.stream() + .filter(value -> !value.isEmpty()) + .map(EnodeURLImpl::parseNodeId) + .collect(Collectors.toList()); + } catch (final IllegalArgumentException e) { + throw new CommandLine.ParameterException( + new CommandLine(this), "Invalid ids supplied to '--banned-node-ids'. " + e.getMessage()); + } + } + + // Boolean option to set that in a PoA network the bootnodes should always be queried during + // peer table refresh. If this flag is disabled bootnodes are only sent FINDN requests on first + // startup, meaning that an offline bootnode or network outage at the client can prevent it + // discovering any peers without a restart. + @CommandLine.Option( + names = {"--poa-discovery-retry-bootnodes"}, + description = + "Always use of bootnodes for discovery in PoA networks. Disabling this reverts " + + " to the same behaviour as non-PoA networks, where neighbours are only discovered from bootnodes on first startup." + + "(default: ${DEFAULT-VALUE})", + arity = "1") + private final Boolean poaDiscoveryRetryBootnodes = true; + + private Collection bannedNodeIds = new ArrayList<>(); + + /** + * Auto-discovers the default IP of the client. + * + * @return machine loopback address + */ + // Loopback IP is used by default as this is how smokeTests require it to be + // and it's probably a good security behaviour to default only on the localhost. + public InetAddress autoDiscoverDefaultIP() { + autoDiscoveredDefaultIP = + Optional.ofNullable(autoDiscoveredDefaultIP).orElseGet(InetAddress::getLoopbackAddress); + + return autoDiscoveredDefaultIP; + } + + @CommandLine.Option( + names = {"--net-restrict"}, + arity = "1..*", + split = ",", + converter = SubnetInfoConverter.class, + description = + "Comma-separated list of allowed IP subnets (e.g., '192.168.1.0/24,10.0.0.0/8').") + private List allowedSubnets; + + @Override + public P2PDiscoveryConfiguration toDomainObject() { + return new P2PDiscoveryConfiguration( + p2pEnabled, + peerDiscoveryEnabled, + p2pHost, + p2pInterface, + p2pPort, + maxPeers, + isLimitRemoteWireConnectionsEnabled, + maxRemoteConnectionsPercentage, + randomPeerPriority, + bannedNodeIds, + allowedSubnets, + poaDiscoveryRetryBootnodes, + bootNodes, + discoveryDnsUrl); + } + + @Override + public List getCLIOptions() { + return CommandLineUtils.getCLIOptions(this, new P2PDiscoveryOptions()); + } +} diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/PermissionsOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/PermissionsOptions.java index f1eafaab165..4dc693d36ed 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/PermissionsOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/stable/PermissionsOptions.java @@ -30,6 +30,7 @@ import picocli.CommandLine; /** Handles configuration options for permissions in Besu. */ +// TODO: implement CLIOption public class PermissionsOptions { @CommandLine.Option( names = {"--permissions-nodes-config-file-enabled"}, diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/MetricsCLIOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/MetricsCLIOptions.java index 95164c31cc6..4149d9869e2 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/MetricsCLIOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/MetricsCLIOptions.java @@ -23,6 +23,7 @@ import picocli.CommandLine; /** The Metrics cli options. */ +// TODO: combine into MetricsOptionGroup, use Unstable inner class pattern (see MiningOptions) public class MetricsCLIOptions implements CLIOptions { private static final String TIMERS_ENABLED_FLAG = "--Xmetrics-timers-enabled"; private static final String IDLE_TIMEOUT_FLAG = "--Xmetrics-idle-timeout"; diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/P2PTLSConfigOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/P2PTLSConfigOptions.java similarity index 99% rename from besu/src/main/java/org/hyperledger/besu/cli/options/stable/P2PTLSConfigOptions.java rename to besu/src/main/java/org/hyperledger/besu/cli/options/unstable/P2PTLSConfigOptions.java index c3f8c56219f..5939eb3d390 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/options/stable/P2PTLSConfigOptions.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/P2PTLSConfigOptions.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.cli.options.stable; +package org.hyperledger.besu.cli.options.unstable; import static java.util.Arrays.asList; import static org.hyperledger.besu.cli.DefaultCommandValues.DEFAULT_KEYSTORE_TYPE; diff --git a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelper.java b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelper.java index 6bcfe4d3531..1ac309c430f 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelper.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/storage/TrieLogHelper.java @@ -15,11 +15,11 @@ package org.hyperledger.besu.cli.subcommands.storage; import static com.google.common.base.Preconditions.checkArgument; -import static org.hyperledger.besu.cli.options.stable.DataStorageOptions.BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD; +import static org.hyperledger.besu.cli.options.DataStorageOptions.BONSAI_STORAGE_FORMAT_MAX_LAYERS_TO_LOAD; import static org.hyperledger.besu.controller.BesuController.DATABASE_PATH; import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.DEFAULT_BONSAI_TRIE_LOG_PRUNING_WINDOW_SIZE; -import org.hyperledger.besu.cli.options.stable.DataStorageOptions; +import org.hyperledger.besu.cli.options.DataStorageOptions; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; diff --git a/besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java b/besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java index 59e2d60bf37..e757fd62a2b 100644 --- a/besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java +++ b/besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java @@ -20,7 +20,10 @@ import org.hyperledger.besu.chainimport.JsonBlockImporter; import org.hyperledger.besu.chainimport.RlpBlockImporter; import org.hyperledger.besu.cli.BesuCommand; +import org.hyperledger.besu.cli.options.stable.P2PDiscoveryOptions; +import org.hyperledger.besu.cli.options.unstable.RPCOptions; import org.hyperledger.besu.controller.BesuController; +import org.hyperledger.besu.ethereum.p2p.discovery.P2PDiscoveryConfiguration; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; import org.hyperledger.besu.services.BesuPluginContextImpl; @@ -53,7 +56,6 @@ BesuCommand provideBesuCommand(final @Named("besuCommandLogger") Logger commandL new BesuPluginContextImpl(), System.getenv(), commandLogger); - besuCommand.toCommandLine(); return besuCommand; } @@ -63,6 +65,18 @@ MetricsConfiguration provideMetricsConfiguration(final BesuCommand provideFrom) return provideFrom.metricsConfiguration(); } + @Provides + @Singleton + RPCOptions provideRPCOptions() { + return RPCOptions.create(); + } + + @Provides + @Singleton + P2PDiscoveryConfiguration provideP2PDiscoveryConfiguration() { + return new P2PDiscoveryOptions().toDomainObject(); + } + @Provides @Named("besuCommandLogger") @Singleton diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java index 65e51c92139..7657dcd26ac 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -34,9 +34,9 @@ import org.hyperledger.besu.chainimport.JsonBlockImporter; import org.hyperledger.besu.chainimport.RlpBlockImporter; import org.hyperledger.besu.cli.config.EthNetworkConfig; +import org.hyperledger.besu.cli.options.DataStorageOptions; import org.hyperledger.besu.cli.options.MiningOptions; import org.hyperledger.besu.cli.options.TransactionPoolOptions; -import org.hyperledger.besu.cli.options.stable.DataStorageOptions; import org.hyperledger.besu.cli.options.stable.EthstatsOptions; import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions; import org.hyperledger.besu.cli.options.unstable.MetricsCLIOptions; diff --git a/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java b/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java index 29fa3d66071..6be6429adb9 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/options/stable/DataStorageOptionsTest.java @@ -18,6 +18,7 @@ import static org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration.MINIMUM_BONSAI_TRIE_LOG_RETENTION_LIMIT; import org.hyperledger.besu.cli.options.AbstractCLIOptionsTest; +import org.hyperledger.besu.cli.options.DataStorageOptions; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration; import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; diff --git a/config/src/main/java/org/hyperledger/besu/config/MergeConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/MergeConfigOptions.java index 53faf690521..6ba865e113e 100644 --- a/config/src/main/java/org/hyperledger/besu/config/MergeConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/MergeConfigOptions.java @@ -17,6 +17,8 @@ import java.util.concurrent.atomic.AtomicBoolean; /** The Merge config options. */ +// TODO: naming this with Options as the suffix is misleading, it should be MergeConfig - doesn't +// use picocli public class MergeConfigOptions { private static final AtomicBoolean mergeEnabled = new AtomicBoolean(false); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/StorageRangeDataRequest.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/StorageRangeDataRequest.java index 14f92e2c3f9..4c50926303b 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/StorageRangeDataRequest.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/request/StorageRangeDataRequest.java @@ -16,7 +16,6 @@ import static org.hyperledger.besu.ethereum.eth.sync.snapsync.RequestType.STORAGE_RANGE; import static org.hyperledger.besu.ethereum.eth.sync.snapsync.StackTrie.FlatDatabaseUpdater.noop; -import static org.hyperledger.besu.ethereum.trie.RangeManager.MAX_RANGE; import static org.hyperledger.besu.ethereum.trie.RangeManager.MIN_RANGE; import static org.hyperledger.besu.ethereum.trie.RangeManager.findNewBeginElementInRange; import static org.hyperledger.besu.ethereum.trie.RangeManager.getRangeCount; @@ -192,12 +191,13 @@ public Stream getChildRequests( getRootHash(), accountHash, storageRoot, key, value); childRequests.add(storageRangeDataRequest); }); - if (startKeyHash.equals(MIN_RANGE) && endKeyHash.equals(MAX_RANGE)) { - // need to heal this account storage - downloadState.addAccountToHealingList(CompactEncoding.bytesToPath(accountHash)); - } }); + if (startKeyHash.equals(MIN_RANGE) && !taskElement.proofs().isEmpty()) { + // need to heal this account storage + downloadState.addAccountToHealingList(CompactEncoding.bytesToPath(accountHash)); + } + return childRequests.stream(); } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/AccountHealingTrackingTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/AccountHealingTrackingTest.java index 70bdbf8b801..8e4e40311f3 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/AccountHealingTrackingTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/AccountHealingTrackingTest.java @@ -64,9 +64,7 @@ public class AccountHealingTrackingTest { DataStorageConfiguration.DEFAULT_BONSAI_CONFIG); private WorldStateStorageCoordinator worldStateStorageCoordinator; - private WorldStateProofProvider worldStateProofProvider; - private MerkleTrie accountStateTrie; @Mock SnapWorldDownloadState snapWorldDownloadState; @@ -82,9 +80,7 @@ public void setup() { } @Test - void avoidMarkingAccountWhenStorageProofValid() { - - // generate valid proof + void shouldMarkAccountForHealingWhenStorageProofIsReceived() { final Hash accountHash = Hash.hash(accounts.get(0)); final StateTrieAccountValue stateTrieAccountValue = StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow())); @@ -108,7 +104,7 @@ void avoidMarkingAccountWhenStorageProofValid() { root -> RangeStorageEntriesCollector.collectEntries( collector, visitor, root, Hash.ZERO)); - // generate the proof + final List proofs = worldStateProofProvider.getStorageProofRelatedNodes( Hash.wrap(storageTrie.getRootHash()), accountHash, Hash.ZERO); @@ -127,11 +123,53 @@ void avoidMarkingAccountWhenStorageProofValid() { snapWorldDownloadState, worldStateProofProvider, slots, new ArrayDeque<>(proofs)); storageRangeDataRequest.getChildRequests( snapWorldDownloadState, worldStateStorageCoordinator, null); + + verify(snapWorldDownloadState).addAccountToHealingList(any(Bytes.class)); + } + + @Test + void shouldNotMarkAccountForHealingWhenAllStorageIsReceivedWithoutProof() { + final Hash accountHash = Hash.hash(accounts.get(0)); + final StateTrieAccountValue stateTrieAccountValue = + StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow())); + + final StoredMerklePatriciaTrie storageTrie = + new StoredMerklePatriciaTrie<>( + new StoredNodeFactory<>( + (location, hash) -> + worldStateKeyValueStorage.getAccountStorageTrieNode( + accountHash, location, hash), + Function.identity(), + Function.identity()), + stateTrieAccountValue.getStorageRoot()); + + final RangeStorageEntriesCollector collector = + RangeStorageEntriesCollector.createCollector(Hash.ZERO, MAX_RANGE, 10, Integer.MAX_VALUE); + final TrieIterator visitor = RangeStorageEntriesCollector.createVisitor(collector); + final TreeMap slots = + (TreeMap) + storageTrie.entriesFrom( + root -> + RangeStorageEntriesCollector.collectEntries( + collector, visitor, root, Hash.ZERO)); + + final StorageRangeDataRequest storageRangeDataRequest = + SnapDataRequest.createStorageRangeDataRequest( + Hash.wrap(accountStateTrie.getRootHash()), + accountHash, + storageTrie.getRootHash(), + Hash.ZERO, + MAX_RANGE); + storageRangeDataRequest.addResponse( + snapWorldDownloadState, worldStateProofProvider, slots, new ArrayDeque<>()); + storageRangeDataRequest.getChildRequests( + snapWorldDownloadState, worldStateStorageCoordinator, null); + verify(snapWorldDownloadState, never()).addAccountToHealingList(any(Bytes.class)); } @Test - void markAccountOnInvalidStorageProof() { + void shouldMarkAccountForHealingOnInvalidStorageProof() { final Hash accountHash = Hash.hash(accounts.get(0)); final StateTrieAccountValue stateTrieAccountValue = StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow())); @@ -157,8 +195,7 @@ void markAccountOnInvalidStorageProof() { } @Test - void markAccountOnPartialStorageRange() { - // generate valid proof + void shouldMarkAccountForHealingOnInvalidStorageWithoutProof() { final Hash accountHash = Hash.hash(accounts.get(0)); final StateTrieAccountValue stateTrieAccountValue = StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow())); @@ -174,11 +211,46 @@ void markAccountOnPartialStorageRange() { stateTrieAccountValue.getStorageRoot()); final RangeStorageEntriesCollector collector = - RangeStorageEntriesCollector.createCollector( + RangeStorageEntriesCollector.createCollector(Hash.ZERO, MAX_RANGE, 1, Integer.MAX_VALUE); + final TrieIterator visitor = RangeStorageEntriesCollector.createVisitor(collector); + final TreeMap slots = + (TreeMap) + storageTrie.entriesFrom( + root -> + RangeStorageEntriesCollector.collectEntries( + collector, visitor, root, Hash.ZERO)); + + final StorageRangeDataRequest storageRangeDataRequest = + SnapDataRequest.createStorageRangeDataRequest( + Hash.wrap(accountStateTrie.getRootHash()), + accountHash, + storageTrie.getRootHash(), Hash.ZERO, - MAX_RANGE, - 1, - Integer.MAX_VALUE); // limit to 1 in order to have a partial range + MAX_RANGE); + storageRangeDataRequest.addResponse( + snapWorldDownloadState, worldStateProofProvider, slots, new ArrayDeque<>()); + + verify(snapWorldDownloadState).addAccountToHealingList(any(Bytes.class)); + } + + @Test + void shouldMarkAccountForHealingOnPartialStorageRange() { + final Hash accountHash = Hash.hash(accounts.get(0)); + final StateTrieAccountValue stateTrieAccountValue = + StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow())); + + final StoredMerklePatriciaTrie storageTrie = + new StoredMerklePatriciaTrie<>( + new StoredNodeFactory<>( + (location, hash) -> + worldStateKeyValueStorage.getAccountStorageTrieNode( + accountHash, location, hash), + Function.identity(), + Function.identity()), + stateTrieAccountValue.getStorageRoot()); + + final RangeStorageEntriesCollector collector = + RangeStorageEntriesCollector.createCollector(Hash.ZERO, MAX_RANGE, 1, Integer.MAX_VALUE); final TrieIterator visitor = RangeStorageEntriesCollector.createVisitor(collector); final TreeMap slots = (TreeMap) @@ -186,7 +258,7 @@ void markAccountOnPartialStorageRange() { root -> RangeStorageEntriesCollector.collectEntries( collector, visitor, root, Hash.ZERO)); - // generate the proof + final List proofs = worldStateProofProvider.getStorageProofRelatedNodes( Hash.wrap(storageTrie.getRootHash()), accountHash, Hash.ZERO); @@ -205,14 +277,14 @@ void markAccountOnPartialStorageRange() { snapWorldDownloadState, worldStateProofProvider, slots, new ArrayDeque<>(proofs)); verify(snapWorldDownloadState, never()).addAccountToHealingList(any(Bytes.class)); - // should mark during the getchild request storageRangeDataRequest.getChildRequests( snapWorldDownloadState, worldStateStorageCoordinator, null); + verify(snapWorldDownloadState).addAccountToHealingList(any(Bytes.class)); } @Test - void avoidMarkingAccountOnValidStorageTrieNodeDetection() { + void shouldNotMarkAccountForHealingOnValidStorageTrieNodeDetection() { final Hash accountHash = Hash.hash(accounts.get(0)); final StateTrieAccountValue stateTrieAccountValue = StateTrieAccountValue.readFrom(RLP.input(accountStateTrie.get(accountHash).orElseThrow())); @@ -223,6 +295,7 @@ void avoidMarkingAccountOnValidStorageTrieNodeDetection() { Hash.wrap(accountStateTrie.getRootHash()), Bytes.EMPTY); storageTrieNodeHealingRequest.getExistingData(worldStateStorageCoordinator); + verify(snapWorldDownloadState, never()).addAccountToHealingList(any(Bytes.class)); } } diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/P2PDiscoveryConfiguration.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/P2PDiscoveryConfiguration.java new file mode 100644 index 00000000000..721a3100a07 --- /dev/null +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/P2PDiscoveryConfiguration.java @@ -0,0 +1,39 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.p2p.discovery; + +import org.hyperledger.besu.util.number.Percentage; + +import java.util.Collection; +import java.util.List; + +import org.apache.commons.net.util.SubnetUtils; +import org.apache.tuweni.bytes.Bytes; + +public record P2PDiscoveryConfiguration( + Boolean p2pEnabled, + Boolean peerDiscoveryEnabled, + String p2pHost, + String p2pInterface, + Integer p2pPort, + Integer maxPeers, + Boolean isLimitRemoteWireConnectionsEnabled, + Percentage maxRemoteConnectionsPercentage, + Boolean randomPeerPriority, + Collection bannedNodeIds, + List allowedSubnets, + Boolean poaDiscoveryRetryBootnodes, + List bootNodes, + String discoveryDnsUrl) {}