Skip to content

Commit

Permalink
Merge branch 'main' into restrict-downgrade
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Whitehead <matthew.whitehead@kaleido.io>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
  • Loading branch information
matthew1001 committed Jan 25, 2024
2 parents 1d0c021 + 6ac419b commit ab7968b
Show file tree
Hide file tree
Showing 44 changed files with 992 additions and 603 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 24.1.2-SNAPSHOT

### Breaking Changes
- The `trace-filter` method in JSON-RPC API now has a default block range limit of 1000, adjustable with `--rpc-max-trace-filter-range` [#6446](https://github.com/hyperledger/besu/pull/6446)

### Deprecations

Expand Down
218 changes: 27 additions & 191 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.DEFAULT_RPC_APIS;
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.VALID_APIS;
import static org.hyperledger.besu.ethereum.api.jsonrpc.authentication.EngineAuthService.EPHEMERAL_JWT_FILE;
import static org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration.DEFAULT_WEBSOCKET_PORT;
import static org.hyperledger.besu.metrics.BesuMetricCategory.DEFAULT_METRIC_CATEGORIES;
import static org.hyperledger.besu.metrics.MetricsProtocol.PROMETHEUS;
import static org.hyperledger.besu.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PORT;
Expand Down Expand Up @@ -61,6 +60,7 @@
import org.hyperledger.besu.cli.options.stable.LoggingLevelOption;
import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption;
import org.hyperledger.besu.cli.options.stable.P2PTLSConfigOptions;
import org.hyperledger.besu.cli.options.stable.RpcWebsocketOptions;
import org.hyperledger.besu.cli.options.unstable.ChainPruningOptions;
import org.hyperledger.besu.cli.options.unstable.DnsOptions;
import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions;
Expand Down Expand Up @@ -813,93 +813,7 @@ static class JsonRPCHttpOptionGroup {

// JSON-RPC Websocket Options
@CommandLine.ArgGroup(validate = false, heading = "@|bold JSON-RPC Websocket Options|@%n")
JsonRPCWebsocketOptionGroup jsonRPCWebsocketOptionGroup = new JsonRPCWebsocketOptionGroup();

static class JsonRPCWebsocketOptionGroup {
@Option(
names = {"--rpc-ws-authentication-jwt-algorithm"},
description =
"Encryption algorithm used for Websockets JWT public key. Possible values are ${COMPLETION-CANDIDATES}"
+ " (default: ${DEFAULT-VALUE})",
arity = "1")
private final JwtAlgorithm rpcWebsocketsAuthenticationAlgorithm = DEFAULT_JWT_ALGORITHM;

@Option(
names = {"--rpc-ws-enabled"},
description = "Set to start the JSON-RPC WebSocket service (default: ${DEFAULT-VALUE})")
private final Boolean isRpcWsEnabled = false;

@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@Option(
names = {"--rpc-ws-host"},
paramLabel = MANDATORY_HOST_FORMAT_HELP,
description =
"Host for JSON-RPC WebSocket service to listen on (default: ${DEFAULT-VALUE})",
arity = "1")
private String rpcWsHost;

@Option(
names = {"--rpc-ws-port"},
paramLabel = MANDATORY_PORT_FORMAT_HELP,
description =
"Port for JSON-RPC WebSocket service to listen on (default: ${DEFAULT-VALUE})",
arity = "1")
private final Integer rpcWsPort = DEFAULT_WEBSOCKET_PORT;

@Option(
names = {"--rpc-ws-max-frame-size"},
description =
"Maximum size in bytes for JSON-RPC WebSocket frames (default: ${DEFAULT-VALUE}). If this limit is exceeded, the websocket will be disconnected.",
arity = "1")
private final Integer rpcWsMaxFrameSize = DEFAULT_WS_MAX_FRAME_SIZE;

@Option(
names = {"--rpc-ws-max-active-connections"},
description =
"Maximum number of WebSocket connections allowed for JSON-RPC (default: ${DEFAULT-VALUE}). Once this limit is reached, incoming connections will be rejected.",
arity = "1")
private final Integer rpcWsMaxConnections = DEFAULT_WS_MAX_CONNECTIONS;

@Option(
names = {"--rpc-ws-api", "--rpc-ws-apis"},
paramLabel = "<api name>",
split = " {0,1}, {0,1}",
arity = "1..*",
description =
"Comma separated list of APIs to enable on JSON-RPC WebSocket service (default: ${DEFAULT-VALUE})")
private final List<String> rpcWsApis = DEFAULT_RPC_APIS;

@Option(
names = {"--rpc-ws-api-methods-no-auth", "--rpc-ws-api-method-no-auth"},
paramLabel = "<api name>",
split = " {0,1}, {0,1}",
arity = "1..*",
description =
"Comma separated list of RPC methods to exclude from RPC authentication services, RPC WebSocket authentication must be enabled")
private final List<String> rpcWsApiMethodsNoAuth = new ArrayList<String>();

@Option(
names = {"--rpc-ws-authentication-enabled"},
description =
"Require authentication for the JSON-RPC WebSocket service (default: ${DEFAULT-VALUE})")
private final Boolean isRpcWsAuthenticationEnabled = false;

@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@CommandLine.Option(
names = {"--rpc-ws-authentication-credentials-file"},
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description =
"Storage file for JSON-RPC WebSocket authentication credentials (default: ${DEFAULT-VALUE})",
arity = "1")
private String rpcWsAuthenticationCredentialsFile = null;

@CommandLine.Option(
names = {"--rpc-ws-authentication-jwt-public-key-file"},
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "JWT public key file for JSON-RPC WebSocket authentication",
arity = "1")
private final File rpcWsAuthenticationPublicKeyFile = null;
}
RpcWebsocketOptions rpcWebsocketOptions = new RpcWebsocketOptions();

// Privacy Options Group
@CommandLine.ArgGroup(validate = false, heading = "@|bold Privacy Options|@%n")
Expand Down Expand Up @@ -1274,6 +1188,12 @@ static class PermissionsOptionGroup {
description = "Specifies the number of last blocks to cache (default: ${DEFAULT-VALUE})")
private final Integer numberOfblocksToCache = 0;

@Option(
names = {"--rpc-max-trace-filter-range"},
description =
"Specifies the maximum number of blocks for the trace_filter method. Must be >=0. 0 specifies no limit (default: $DEFAULT-VALUE)")
private final Long maxTraceFilterRange = 1000L;

@Mixin private P2PTLSConfigOptions p2pTLSConfigOptions;

@Mixin private PkiBlockCreationOptions pkiBlockCreationOptions;
Expand Down Expand Up @@ -1824,6 +1744,7 @@ private void validateOptions() {
validateDnsOptionsParams();
ensureValidPeerBoundParams();
validateRpcOptionsParams();
validateRpcWsOptions();
validateChainDataPruningParams();
validatePostMergeCheckpointBlockRequirements();
validateTransactionPoolOptions();
Expand Down Expand Up @@ -1966,15 +1887,6 @@ private void validateRpcOptionsParams() {
+ invalidHttpApis.toString());
}

if (!jsonRPCWebsocketOptionGroup.rpcWsApis.stream().allMatch(configuredApis)) {
final List<String> invalidWsApis =
new ArrayList<String>(jsonRPCWebsocketOptionGroup.rpcWsApis);
invalidWsApis.removeAll(VALID_APIS);
throw new ParameterException(
this.commandLine,
"Invalid value for option '--rpc-ws-api': invalid entries found " + invalidWsApis);
}

final boolean validHttpApiMethods =
jsonRPCHttpOptionGroup.rpcHttpApiMethodsNoAuth.stream()
.allMatch(RpcMethod::rpcMethodExists);
Expand All @@ -1984,16 +1896,15 @@ private void validateRpcOptionsParams() {
this.commandLine,
"Invalid value for option '--rpc-http-api-methods-no-auth', options must be valid RPC methods");
}
}

final boolean validWsApiMethods =
jsonRPCWebsocketOptionGroup.rpcWsApiMethodsNoAuth.stream()
.allMatch(RpcMethod::rpcMethodExists);

if (!validWsApiMethods) {
throw new ParameterException(
this.commandLine,
"Invalid value for option '--rpc-ws-api-methods-no-auth', options must be valid RPC methods");
}
private void validateRpcWsOptions() {
final Predicate<String> configuredApis =
apiName ->
Arrays.stream(RpcApis.values())
.anyMatch(builtInApi -> apiName.equals(builtInApi.name()))
|| rpcEndpointServiceImpl.hasNamespace(apiName);
rpcWebsocketOptions.validate(logger, commandLine, configuredApis);
}

private void validateChainDataPruningParams() {
Expand Down Expand Up @@ -2096,10 +2007,10 @@ private void configure() throws Exception {
p2pTLSConfiguration = p2pTLSConfigOptions.p2pTLSConfiguration(commandLine);
graphQLConfiguration = graphQLConfiguration();
webSocketConfiguration =
webSocketConfiguration(
jsonRPCWebsocketOptionGroup.rpcWsPort,
jsonRPCWebsocketOptionGroup.rpcWsApis,
hostsAllowlist);
rpcWebsocketOptions.webSocketConfiguration(
hostsAllowlist,
p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress(),
unstableRPCOptions.getWsTimeoutSec());
jsonRpcIpcConfiguration =
jsonRpcIpcConfiguration(
unstableIpcOptions.isEnabled(),
Expand Down Expand Up @@ -2468,71 +2379,6 @@ private boolean isRpcTlsConfigurationRequired() {
return jsonRPCHttpOptionGroup.isRpcHttpEnabled && jsonRPCHttpOptionGroup.isRpcHttpTlsEnabled;
}

private WebSocketConfiguration webSocketConfiguration(
final Integer listenPort, final List<String> apiGroups, final List<String> allowCallsFrom) {

CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--rpc-ws-enabled",
!jsonRPCWebsocketOptionGroup.isRpcWsEnabled,
asList(
"--rpc-ws-api",
"--rpc-ws-apis",
"--rpc-ws-api-method-no-auth",
"--rpc-ws-api-methods-no-auth",
"--rpc-ws-host",
"--rpc-ws-port",
"--rpc-ws-max-frame-size",
"--rpc-ws-max-active-connections",
"--rpc-ws-authentication-enabled",
"--rpc-ws-authentication-credentials-file",
"--rpc-ws-authentication-public-key-file",
"--rpc-ws-authentication-jwt-algorithm"));

if (jsonRPCWebsocketOptionGroup.isRpcWsAuthenticationEnabled) {
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--rpc-ws-authentication-public-key-file",
jsonRPCWebsocketOptionGroup.rpcWsAuthenticationPublicKeyFile == null,
asList("--rpc-ws-authentication-jwt-algorithm"));
}

if (jsonRPCWebsocketOptionGroup.isRpcWsAuthenticationEnabled
&& rpcWsAuthenticationCredentialsFile() == null
&& jsonRPCWebsocketOptionGroup.rpcWsAuthenticationPublicKeyFile == null) {
throw new ParameterException(
commandLine,
"Unable to authenticate JSON-RPC WebSocket endpoint without a supplied credentials file or authentication public key file");
}

final WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault();
webSocketConfiguration.setEnabled(jsonRPCWebsocketOptionGroup.isRpcWsEnabled);
webSocketConfiguration.setHost(
Strings.isNullOrEmpty(jsonRPCWebsocketOptionGroup.rpcWsHost)
? p2PDiscoveryOptionGroup.autoDiscoverDefaultIP().getHostAddress()
: jsonRPCWebsocketOptionGroup.rpcWsHost);
webSocketConfiguration.setPort(listenPort);
webSocketConfiguration.setMaxFrameSize(jsonRPCWebsocketOptionGroup.rpcWsMaxFrameSize);
webSocketConfiguration.setMaxActiveConnections(jsonRPCWebsocketOptionGroup.rpcWsMaxConnections);
webSocketConfiguration.setRpcApis(apiGroups);
webSocketConfiguration.setRpcApisNoAuth(
jsonRPCWebsocketOptionGroup.rpcWsApiMethodsNoAuth.stream()
.distinct()
.collect(Collectors.toList()));
webSocketConfiguration.setAuthenticationEnabled(
jsonRPCWebsocketOptionGroup.isRpcWsAuthenticationEnabled);
webSocketConfiguration.setAuthenticationCredentialsFile(rpcWsAuthenticationCredentialsFile());
webSocketConfiguration.setHostsAllowlist(allowCallsFrom);
webSocketConfiguration.setAuthenticationPublicKeyFile(
jsonRPCWebsocketOptionGroup.rpcWsAuthenticationPublicKeyFile);
webSocketConfiguration.setAuthenticationAlgorithm(
jsonRPCWebsocketOptionGroup.rpcWebsocketsAuthenticationAlgorithm);
webSocketConfiguration.setTimeoutSec(unstableRPCOptions.getWsTimeoutSec());
return webSocketConfiguration;
}

private ApiConfiguration apiConfiguration() {
checkApiOptionsDependencies();
var builder =
Expand All @@ -2544,7 +2390,8 @@ private ApiConfiguration apiConfiguration() {
.gasPriceMax(apiGasPriceMax)
.maxLogsRange(rpcMaxLogsRange)
.gasCap(rpcGasCap)
.isGasAndPriorityFeeLimitingEnabled(apiGasAndPriorityFeeLimitingEnabled);
.isGasAndPriorityFeeLimitingEnabled(apiGasAndPriorityFeeLimitingEnabled)
.maxTraceFilterRange(maxTraceFilterRange);
if (apiGasAndPriorityFeeLimitingEnabled) {
if (apiGasAndPriorityFeeLowerBoundCoefficient > apiGasAndPriorityFeeUpperBoundCoefficient) {
throw new ParameterException(
Expand Down Expand Up @@ -2614,7 +2461,7 @@ public MetricsConfiguration metricsConfiguration() {
private Optional<PermissioningConfiguration> permissioningConfiguration() throws Exception {
if (!(localPermissionsEnabled() || contractPermissionsEnabled())) {
if (jsonRPCHttpOptionGroup.rpcHttpApis.contains(RpcApis.PERM.name())
|| jsonRPCWebsocketOptionGroup.rpcWsApis.contains(RpcApis.PERM.name())) {
|| rpcWebsocketOptions.getRpcWsApis().contains(RpcApis.PERM.name())) {
logger.warn(
"Permissions are disabled. Cannot enable PERM APIs when not using Permissions.");
}
Expand Down Expand Up @@ -2817,9 +2664,9 @@ private PrivacyParameters privacyParameters() {

private boolean anyPrivacyApiEnabled() {
return jsonRPCHttpOptionGroup.rpcHttpApis.contains(RpcApis.EEA.name())
|| jsonRPCWebsocketOptionGroup.rpcWsApis.contains(RpcApis.EEA.name())
|| rpcWebsocketOptions.getRpcWsApis().contains(RpcApis.EEA.name())
|| jsonRPCHttpOptionGroup.rpcHttpApis.contains(RpcApis.PRIV.name())
|| jsonRPCWebsocketOptionGroup.rpcWsApis.contains(RpcApis.PRIV.name());
|| rpcWebsocketOptions.getRpcWsApis().contains(RpcApis.PRIV.name());
}

private PrivacyKeyValueStorageProvider privacyKeyStorageProvider(final String name) {
Expand Down Expand Up @@ -3194,15 +3041,6 @@ private String rpcHttpAuthenticationCredentialsFile() {
return filename;
}

private String rpcWsAuthenticationCredentialsFile() {
final String filename = jsonRPCWebsocketOptionGroup.rpcWsAuthenticationCredentialsFile;

if (filename != null) {
RpcAuthFileValidator.validate(commandLine, filename, "WS");
}
return filename;
}

private String getDefaultPermissioningFilePath() {
return dataDir()
+ System.getProperty("file.separator")
Expand Down Expand Up @@ -3340,9 +3178,7 @@ private List<Integer> getEffectivePorts() {
jsonRPCHttpOptionGroup.rpcHttpPort,
jsonRPCHttpOptionGroup.isRpcHttpEnabled);
addPortIfEnabled(
effectivePorts,
jsonRPCWebsocketOptionGroup.rpcWsPort,
jsonRPCWebsocketOptionGroup.isRpcWsEnabled);
effectivePorts, rpcWebsocketOptions.getRpcWsPort(), rpcWebsocketOptions.isRpcWsEnabled());
addPortIfEnabled(effectivePorts, engineRPCOptionGroup.engineRpcPort, isEngineApiEnabled());
addPortIfEnabled(
effectivePorts, metricsOptionGroup.metricsPort, metricsOptionGroup.isMetricsEnabled);
Expand Down
Loading

0 comments on commit ab7968b

Please sign in to comment.