Use the node's configuration to decide if adding a peer with DNS in the enode is allowed (#5584)

* Use the node's configuration to decide if adding a peer with DNS in the enode is allowed

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Update command test mocks

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Update besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java

Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
Signed-off-by: Matt Whitehead <matthew1001@hotmail.com>

* Address PR comments. Add a reference in the change log. Fix failing integration test.

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Add the same DNS-config checking logic to admin_removePeer, along with a unit test file for it

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Tweak the change log

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Add clearer error messages for the cases where enode DNS is disabled (but a hostname one has been specified in the URL) or where DNS name resolution failed

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Spotless Java fixes

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* Fix copyright for new file

Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
Signed-off-by: Matt Whitehead <matthew1001@hotmail.com>

* Address PR comments (mainly copyright & constant renaming)

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>

* move changelog entry to 23.4.4

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>

---------

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matt Whitehead <matthew1001@hotmail.com>
Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>
Co-authored-by: Matthew Whitehead <matthew1001@gmail.com>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
pull/5627/head
Matt Whitehead 1 year ago committed by GitHub
parent 8f9882f768
commit 5bff76f107
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      CHANGELOG.md
  2. 17
      besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
  3. 1
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  4. 1
      besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java
  5. 1
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java
  6. 13
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminAddPeer.java
  7. 16
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminModifyPeer.java
  8. 13
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminRemovePeer.java
  9. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java
  10. 11
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/AdminJsonRpcMethods.java
  11. 9
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java
  12. 1
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/AbstractJsonRpcHttpServiceTest.java
  13. 1
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceHostAllowlistTest.java
  14. 1
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceLoginTest.java
  15. 2
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceRpcApisTest.java
  16. 1
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTestBase.java
  17. 1
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsClientAuthTest.java
  18. 1
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsMisconfigurationTest.java
  19. 1
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpServiceTlsTest.java
  20. 78
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminAddPeerTest.java
  21. 258
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminRemovePeerTest.java
  22. 1
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceLoginTest.java

@ -1,6 +1,19 @@
# Changelog # Changelog
## 23.4.2 ## 23.4.4
### Breaking Changes
### Additions and Improvements
### Bug Fixes
- Use the node's configuration to determine if DNS enode URLs are allowed in calls to `admin_addPeer` and `admin_removePeer` [#5584](https://github.com/hyperledger/besu/pull/5584)
### Download Links
---
## 23.4.3
### Breaking Changes ### Breaking Changes
- Move blockchain related variables in a dedicated storage, to pave the way to future optimizations [#5471](https://github.com/hyperledger/besu/pull/5471). The migration is performed automatically at startup, - Move blockchain related variables in a dedicated storage, to pave the way to future optimizations [#5471](https://github.com/hyperledger/besu/pull/5471). The migration is performed automatically at startup,
@ -24,6 +37,10 @@ and in case a rollback is needed, before installing a previous version, the migr
### Download Links ### Download Links
## 23.4.2
- Was not released (failed burn-in test)
--- ---
## 23.4.1 ## 23.4.1

@ -87,6 +87,7 @@ import org.hyperledger.besu.ethereum.p2p.network.NoopP2PNetwork;
import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork; import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork;
import org.hyperledger.besu.ethereum.p2p.network.ProtocolManager; import org.hyperledger.besu.ethereum.p2p.network.ProtocolManager;
import org.hyperledger.besu.ethereum.p2p.peers.DefaultPeer; import org.hyperledger.besu.ethereum.p2p.peers.DefaultPeer;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration;
import org.hyperledger.besu.ethereum.p2p.permissions.PeerPermissions; import org.hyperledger.besu.ethereum.p2p.permissions.PeerPermissions;
import org.hyperledger.besu.ethereum.p2p.permissions.PeerPermissionsDenylist; import org.hyperledger.besu.ethereum.p2p.permissions.PeerPermissionsDenylist;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.TLSConfiguration; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.netty.TLSConfiguration;
@ -191,6 +192,7 @@ public class RunnerBuilder {
private JsonRpcIpcConfiguration jsonRpcIpcConfiguration; private JsonRpcIpcConfiguration jsonRpcIpcConfiguration;
private boolean legacyForkIdEnabled; private boolean legacyForkIdEnabled;
private Optional<Long> rpcMaxLogsRange; private Optional<Long> rpcMaxLogsRange;
private Optional<EnodeDnsConfiguration> enodeDnsConfiguration;
/** /**
* Add Vertx. * Add Vertx.
@ -584,6 +586,18 @@ public class RunnerBuilder {
return this; return this;
} }
/**
* Add enode DNS configuration
*
* @param enodeDnsConfiguration the DNS configuration for enodes
* @return the runner builder
*/
public RunnerBuilder enodeDnsConfiguration(final EnodeDnsConfiguration enodeDnsConfiguration) {
this.enodeDnsConfiguration =
enodeDnsConfiguration != null ? Optional.of(enodeDnsConfiguration) : Optional.empty();
return this;
}
/** /**
* Build Runner instance. * Build Runner instance.
* *
@ -1224,7 +1238,8 @@ public class RunnerBuilder {
dataDir, dataDir,
besuController.getProtocolManager().ethContext().getEthPeers(), besuController.getProtocolManager().ethContext().getEthPeers(),
consensusEngineServer, consensusEngineServer,
rpcMaxLogsRange); rpcMaxLogsRange,
enodeDnsConfiguration);
methods.putAll(besuController.getAdditionalJsonRpcMethods(jsonRpcApis)); methods.putAll(besuController.getAdditionalJsonRpcMethods(jsonRpcApis));
final var pluginMethods = final var pluginMethods =

@ -3103,6 +3103,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.storageProvider(keyValueStorageProvider(keyValueStorageName)) .storageProvider(keyValueStorageProvider(keyValueStorageName))
.rpcEndpointService(rpcEndpointServiceImpl) .rpcEndpointService(rpcEndpointServiceImpl)
.rpcMaxLogsRange(rpcMaxLogsRange) .rpcMaxLogsRange(rpcMaxLogsRange)
.enodeDnsConfiguration(getEnodeDnsConfiguration())
.build(); .build();
addShutdownHook(runner); addShutdownHook(runner);

@ -291,6 +291,7 @@ public abstract class CommandTestAbstract {
when(mockRunnerBuilder.rpcEndpointService(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.rpcEndpointService(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.legacyForkId(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.legacyForkId(anyBoolean())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.rpcMaxLogsRange(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.rpcMaxLogsRange(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.enodeDnsConfiguration(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.build()).thenReturn(mockRunner); when(mockRunnerBuilder.build()).thenReturn(mockRunner);
final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance();

@ -184,6 +184,7 @@ public class JsonRpcTestMethodsFactory {
dataDir, dataDir,
ethPeers, ethPeers,
Vertx.vertx(new VertxOptions().setWorkerPoolSize(1)), Vertx.vertx(new VertxOptions().setWorkerPoolSize(1)),
Optional.empty(),
Optional.empty()); Optional.empty());
} }
} }

@ -19,10 +19,13 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcRespon
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork; import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork;
import org.hyperledger.besu.ethereum.p2p.peers.DefaultPeer; import org.hyperledger.besu.ethereum.p2p.peers.DefaultPeer;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.ethereum.p2p.peers.Peer; import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.data.EnodeURL;
import java.util.Optional;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -30,8 +33,9 @@ public class AdminAddPeer extends AdminModifyPeer {
private static final Logger LOG = LoggerFactory.getLogger(AdminAddPeer.class); private static final Logger LOG = LoggerFactory.getLogger(AdminAddPeer.class);
public AdminAddPeer(final P2PNetwork peerNetwork) { public AdminAddPeer(
super(peerNetwork); final P2PNetwork peerNetwork, final Optional<EnodeDnsConfiguration> enodeDnsConfiguration) {
super(peerNetwork, enodeDnsConfiguration);
} }
@Override @Override
@ -42,7 +46,10 @@ public class AdminAddPeer extends AdminModifyPeer {
@Override @Override
protected JsonRpcResponse performOperation(final Object id, final String enode) { protected JsonRpcResponse performOperation(final Object id, final String enode) {
LOG.debug("Adding ({}) to peers", enode); LOG.debug("Adding ({}) to peers", enode);
final EnodeURL enodeURL = EnodeURLImpl.fromString(enode); final EnodeURL enodeURL =
this.enodeDnsConfiguration.isEmpty()
? EnodeURLImpl.fromString(enode)
: EnodeURLImpl.fromString(enode, enodeDnsConfiguration.get());
final Peer peer = DefaultPeer.fromEnodeURL(enodeURL); final Peer peer = DefaultPeer.fromEnodeURL(enodeURL);
final boolean addedToNetwork = peerNetwork.addMaintainedConnectionPeer(peer); final boolean addedToNetwork = peerNetwork.addMaintainedConnectionPeer(peer);
return new JsonRpcSuccessResponse(id, addedToNetwork); return new JsonRpcSuccessResponse(id, addedToNetwork);

@ -21,13 +21,19 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorR
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork; import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork;
import org.hyperledger.besu.ethereum.p2p.network.exceptions.P2PDisabledException; import org.hyperledger.besu.ethereum.p2p.network.exceptions.P2PDisabledException;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration;
import java.util.Optional;
public abstract class AdminModifyPeer implements JsonRpcMethod { public abstract class AdminModifyPeer implements JsonRpcMethod {
protected final P2PNetwork peerNetwork; protected final P2PNetwork peerNetwork;
protected final Optional<EnodeDnsConfiguration> enodeDnsConfiguration;
protected AdminModifyPeer(final P2PNetwork peerNetwork) { protected AdminModifyPeer(
final P2PNetwork peerNetwork, final Optional<EnodeDnsConfiguration> enodeDnsConfiguration) {
this.peerNetwork = peerNetwork; this.peerNetwork = peerNetwork;
this.enodeDnsConfiguration = enodeDnsConfiguration;
} }
@Override @Override
@ -48,10 +54,18 @@ public abstract class AdminModifyPeer implements JsonRpcMethod {
"Invalid node ID: node ID must have exactly 128 hexadecimal characters and should not include any '0x' hex prefix.")) { "Invalid node ID: node ID must have exactly 128 hexadecimal characters and should not include any '0x' hex prefix.")) {
return new JsonRpcErrorResponse( return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.ENODE_ID_INVALID); requestContext.getRequest().getId(), JsonRpcError.ENODE_ID_INVALID);
} else {
if (e.getMessage().endsWith("Invalid ip address.")) {
return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.DNS_NOT_ENABLED);
} else if (e.getMessage().endsWith("dns-update-enabled flag is false.")) {
return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.CANT_RESOLVE_PEER_ENODE_DNS);
} else { } else {
return new JsonRpcErrorResponse( return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.PARSE_ERROR); requestContext.getRequest().getId(), JsonRpcError.PARSE_ERROR);
} }
}
} catch (final P2PDisabledException e) { } catch (final P2PDisabledException e) {
return new JsonRpcErrorResponse( return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.P2P_DISABLED); requestContext.getRequest().getId(), JsonRpcError.P2P_DISABLED);

@ -19,9 +19,12 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcRespon
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork; import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork;
import org.hyperledger.besu.ethereum.p2p.peers.DefaultPeer; import org.hyperledger.besu.ethereum.p2p.peers.DefaultPeer;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.data.EnodeURL;
import java.util.Optional;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -29,8 +32,9 @@ public class AdminRemovePeer extends AdminModifyPeer {
private static final Logger LOG = LoggerFactory.getLogger(AdminRemovePeer.class); private static final Logger LOG = LoggerFactory.getLogger(AdminRemovePeer.class);
public AdminRemovePeer(final P2PNetwork peerNetwork) { public AdminRemovePeer(
super(peerNetwork); final P2PNetwork peerNetwork, final Optional<EnodeDnsConfiguration> enodeDnsConfiguration) {
super(peerNetwork, enodeDnsConfiguration);
} }
@Override @Override
@ -41,7 +45,10 @@ public class AdminRemovePeer extends AdminModifyPeer {
@Override @Override
protected JsonRpcResponse performOperation(final Object id, final String enode) { protected JsonRpcResponse performOperation(final Object id, final String enode) {
LOG.debug("Remove ({}) from peer cache", enode); LOG.debug("Remove ({}) from peer cache", enode);
final EnodeURL enodeURL = EnodeURLImpl.fromString(enode); final EnodeURL enodeURL =
this.enodeDnsConfiguration.isEmpty()
? EnodeURLImpl.fromString(enode)
: EnodeURLImpl.fromString(enode, enodeDnsConfiguration.get());
final boolean result = final boolean result =
peerNetwork.removeMaintainedConnectionPeer(DefaultPeer.fromEnodeURL(enodeURL)); peerNetwork.removeMaintainedConnectionPeer(DefaultPeer.fromEnodeURL(enodeURL));
return new JsonRpcSuccessResponse(id, result); return new JsonRpcSuccessResponse(id, result);

@ -162,6 +162,8 @@ public enum JsonRpcError {
VALUE_NOT_ZERO(-50100, "We cannot transfer ether in a private transaction yet."), VALUE_NOT_ZERO(-50100, "We cannot transfer ether in a private transaction yet."),
CANT_CONNECT_TO_LOCAL_PEER(-32100, "Cannot add local node as peer."), CANT_CONNECT_TO_LOCAL_PEER(-32100, "Cannot add local node as peer."),
CANT_RESOLVE_PEER_ENODE_DNS(-32100, "Cannot resolve enode DNS hostname"),
DNS_NOT_ENABLED(-32100, "Enode DNS support is disabled"),
// Invalid input errors // Invalid input errors
ENODE_ID_INVALID( ENODE_ID_INVALID(

@ -29,11 +29,13 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.PluginsReloadC
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers; import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork; import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration;
import org.hyperledger.besu.nat.NatService; import org.hyperledger.besu.nat.NatService;
import org.hyperledger.besu.plugin.BesuPlugin; import org.hyperledger.besu.plugin.BesuPlugin;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Map; import java.util.Map;
import java.util.Optional;
public class AdminJsonRpcMethods extends ApiGroupJsonRpcMethods { public class AdminJsonRpcMethods extends ApiGroupJsonRpcMethods {
@ -45,6 +47,7 @@ public class AdminJsonRpcMethods extends ApiGroupJsonRpcMethods {
private final NatService natService; private final NatService natService;
private final Map<String, BesuPlugin> namedPlugins; private final Map<String, BesuPlugin> namedPlugins;
private final EthPeers ethPeers; private final EthPeers ethPeers;
private final Optional<EnodeDnsConfiguration> enodeDnsConfiguration;
public AdminJsonRpcMethods( public AdminJsonRpcMethods(
final String clientVersion, final String clientVersion,
@ -54,7 +57,8 @@ public class AdminJsonRpcMethods extends ApiGroupJsonRpcMethods {
final BlockchainQueries blockchainQueries, final BlockchainQueries blockchainQueries,
final Map<String, BesuPlugin> namedPlugins, final Map<String, BesuPlugin> namedPlugins,
final NatService natService, final NatService natService,
final EthPeers ethPeers) { final EthPeers ethPeers,
final Optional<EnodeDnsConfiguration> enodeDnsConfiguration) {
this.clientVersion = clientVersion; this.clientVersion = clientVersion;
this.networkId = networkId; this.networkId = networkId;
this.genesisConfigOptions = genesisConfigOptions; this.genesisConfigOptions = genesisConfigOptions;
@ -63,6 +67,7 @@ public class AdminJsonRpcMethods extends ApiGroupJsonRpcMethods {
this.namedPlugins = namedPlugins; this.namedPlugins = namedPlugins;
this.natService = natService; this.natService = natService;
this.ethPeers = ethPeers; this.ethPeers = ethPeers;
this.enodeDnsConfiguration = enodeDnsConfiguration;
} }
@Override @Override
@ -73,8 +78,8 @@ public class AdminJsonRpcMethods extends ApiGroupJsonRpcMethods {
@Override @Override
protected Map<String, JsonRpcMethod> create() { protected Map<String, JsonRpcMethod> create() {
return mapOf( return mapOf(
new AdminAddPeer(p2pNetwork), new AdminAddPeer(p2pNetwork, enodeDnsConfiguration),
new AdminRemovePeer(p2pNetwork), new AdminRemovePeer(p2pNetwork, enodeDnsConfiguration),
new AdminNodeInfo( new AdminNodeInfo(
clientVersion, clientVersion,
networkId, networkId,

@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork; import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeDnsConfiguration;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.permissioning.AccountLocalConfigPermissioningController; import org.hyperledger.besu.ethereum.permissioning.AccountLocalConfigPermissioningController;
import org.hyperledger.besu.ethereum.permissioning.NodeLocalConfigPermissioningController; import org.hyperledger.besu.ethereum.permissioning.NodeLocalConfigPermissioningController;
@ -76,13 +77,12 @@ public class JsonRpcMethodsFactory {
final Path dataDir, final Path dataDir,
final EthPeers ethPeers, final EthPeers ethPeers,
final Vertx consensusEngineServer, final Vertx consensusEngineServer,
final Optional<Long> maxLogRange) { final Optional<Long> maxLogRange,
final Optional<EnodeDnsConfiguration> enodeDnsConfiguration) {
final Map<String, JsonRpcMethod> enabled = new HashMap<>(); final Map<String, JsonRpcMethod> enabled = new HashMap<>();
if (!rpcApis.isEmpty()) { if (!rpcApis.isEmpty()) {
final JsonRpcMethod modules = new RpcModules(rpcApis); final JsonRpcMethod modules = new RpcModules(rpcApis);
enabled.put(modules.getName(), modules); enabled.put(modules.getName(), modules);
final List<JsonRpcMethods> availableApiGroups = final List<JsonRpcMethods> availableApiGroups =
List.of( List.of(
new AdminJsonRpcMethods( new AdminJsonRpcMethods(
@ -93,7 +93,8 @@ public class JsonRpcMethodsFactory {
blockchainQueries, blockchainQueries,
namedPlugins, namedPlugins,
natService, natService,
ethPeers), ethPeers,
enodeDnsConfiguration),
new DebugJsonRpcMethods( new DebugJsonRpcMethods(
blockchainQueries, blockchainQueries,
protocolContext, protocolContext,

@ -191,6 +191,7 @@ public abstract class AbstractJsonRpcHttpServiceTest {
folder.getRoot().toPath(), folder.getRoot().toPath(),
mock(EthPeers.class), mock(EthPeers.class),
syncVertx, syncVertx,
Optional.empty(),
Optional.empty()); Optional.empty());
} }

@ -124,6 +124,7 @@ public class JsonRpcHttpServiceHostAllowlistTest {
folder.getRoot().toPath(), folder.getRoot().toPath(),
mock(EthPeers.class), mock(EthPeers.class),
vertx, vertx,
Optional.empty(),
Optional.empty())); Optional.empty()));
service = createJsonRpcHttpService(); service = createJsonRpcHttpService();
service.start().join(); service.start().join();

@ -154,6 +154,7 @@ public class JsonRpcHttpServiceLoginTest {
folder.getRoot().toPath(), folder.getRoot().toPath(),
mock(EthPeers.class), mock(EthPeers.class),
vertx, vertx,
Optional.empty(),
Optional.empty())); Optional.empty()));
service = createJsonRpcHttpService(); service = createJsonRpcHttpService();
jwtAuth = service.authenticationService.get().getJwtAuthProvider(); jwtAuth = service.authenticationService.get().getJwtAuthProvider();

@ -227,6 +227,7 @@ public class JsonRpcHttpServiceRpcApisTest {
folder, folder,
mock(EthPeers.class), mock(EthPeers.class),
vertx, vertx,
Optional.empty(),
Optional.empty())); Optional.empty()));
final JsonRpcHttpService jsonRpcHttpService = final JsonRpcHttpService jsonRpcHttpService =
new JsonRpcHttpService( new JsonRpcHttpService(
@ -335,6 +336,7 @@ public class JsonRpcHttpServiceRpcApisTest {
folder, folder,
mock(EthPeers.class), mock(EthPeers.class),
vertx, vertx,
Optional.empty(),
Optional.empty())); Optional.empty()));
final JsonRpcHttpService jsonRpcHttpService = final JsonRpcHttpService jsonRpcHttpService =
new JsonRpcHttpService( new JsonRpcHttpService(

@ -133,6 +133,7 @@ public class JsonRpcHttpServiceTestBase {
folder.getRoot().toPath(), folder.getRoot().toPath(),
ethPeersMock, ethPeersMock,
vertx, vertx,
Optional.empty(),
Optional.empty())); Optional.empty()));
service = createJsonRpcHttpService(createLimitedJsonRpcConfig()); service = createJsonRpcHttpService(createLimitedJsonRpcConfig());
service.start().join(); service.start().join();

@ -139,6 +139,7 @@ public class JsonRpcHttpServiceTlsClientAuthTest {
folder.getRoot().toPath(), folder.getRoot().toPath(),
mock(EthPeers.class), mock(EthPeers.class),
vertx, vertx,
Optional.empty(),
Optional.empty())); Optional.empty()));
System.setProperty("javax.net.ssl.trustStore", CLIENT_AS_CA_CERT.getKeyStoreFile().toString()); System.setProperty("javax.net.ssl.trustStore", CLIENT_AS_CA_CERT.getKeyStoreFile().toString());

@ -127,6 +127,7 @@ class JsonRpcHttpServiceTlsMisconfigurationTest {
tempDir.getRoot(), tempDir.getRoot(),
mock(EthPeers.class), mock(EthPeers.class),
vertx, vertx,
Optional.empty(),
Optional.empty())); Optional.empty()));
} }

@ -129,6 +129,7 @@ public class JsonRpcHttpServiceTlsTest {
folder.getRoot().toPath(), folder.getRoot().toPath(),
mock(EthPeers.class), mock(EthPeers.class),
vertx, vertx,
Optional.empty(),
Optional.empty())); Optional.empty()));
service = createJsonRpcHttpService(createJsonRpcConfig()); service = createJsonRpcHttpService(createJsonRpcConfig());
service.start().join(); service.start().join();

@ -26,6 +26,9 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcRespon
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork; import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork;
import org.hyperledger.besu.ethereum.p2p.network.exceptions.P2PDisabledException; import org.hyperledger.besu.ethereum.p2p.network.exceptions.P2PDisabledException;
import org.hyperledger.besu.ethereum.p2p.peers.ImmutableEnodeDnsConfiguration;
import java.util.Optional;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -39,6 +42,8 @@ public class AdminAddPeerTest {
@Mock private P2PNetwork p2pNetwork; @Mock private P2PNetwork p2pNetwork;
private AdminAddPeer method; private AdminAddPeer method;
private AdminAddPeer methodDNSDisabled;
private AdminAddPeer methodDNSUpdateDisabled;
final String validEnode = final String validEnode =
"enode://" "enode://"
@ -48,13 +53,48 @@ public class AdminAddPeerTest {
+ "00000000000000000000000000000000" + "00000000000000000000000000000000"
+ "@127.0.0.1:30303"; + "@127.0.0.1:30303";
final String validDNSEnode =
"enode://"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "@node.acme.com:30303";
final JsonRpcRequestContext validRequest = final JsonRpcRequestContext validRequest =
new JsonRpcRequestContext( new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "admin_addPeer", new String[] {validEnode})); new JsonRpcRequest("2.0", "admin_addPeer", new String[] {validEnode}));
final JsonRpcRequestContext validDNSRequest =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "admin_addPeer", new String[] {validDNSEnode}));
@Before @Before
public void setup() { public void setup() {
method = new AdminAddPeer(p2pNetwork); method =
new AdminAddPeer(
p2pNetwork,
Optional.of(
ImmutableEnodeDnsConfiguration.builder()
.dnsEnabled(true)
.updateEnabled(true)
.build()));
methodDNSDisabled =
new AdminAddPeer(
p2pNetwork,
Optional.of(
ImmutableEnodeDnsConfiguration.builder()
.dnsEnabled(false)
.updateEnabled(true)
.build()));
methodDNSUpdateDisabled =
new AdminAddPeer(
p2pNetwork,
Optional.of(
ImmutableEnodeDnsConfiguration.builder()
.dnsEnabled(true)
.updateEnabled(false)
.build()));
} }
@Test @Test
@ -138,6 +178,42 @@ public class AdminAddPeerTest {
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse); assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
} }
@Test
public void requestAddsValidDNSEnode() {
when(p2pNetwork.addMaintainedConnectionPeer(any())).thenReturn(true);
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(
validRequest.getRequest().getId(),
true); // DNS is mapped to an IP address, so we expect the non-DNS response
final JsonRpcResponse actualResponse = method.response(validDNSRequest);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestAddsDNSEnodeButDNSDisabled() {
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(
validDNSRequest.getRequest().getId(), JsonRpcError.DNS_NOT_ENABLED);
final JsonRpcResponse actualResponse = methodDNSDisabled.response(validDNSRequest);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestAddsDNSEnodeButDNSNotResolved() {
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(
validDNSRequest.getRequest().getId(), JsonRpcError.CANT_RESOLVE_PEER_ENODE_DNS);
final JsonRpcResponse actualResponse = methodDNSUpdateDisabled.response(validDNSRequest);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test @Test
public void requestRefusesListOfNodes() { public void requestRefusesListOfNodes() {
final JsonRpcRequestContext request = final JsonRpcRequestContext request =

@ -0,0 +1,258 @@
/*
* Copyright Hyperledger Besu contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.p2p.network.P2PNetwork;
import org.hyperledger.besu.ethereum.p2p.network.exceptions.P2PDisabledException;
import org.hyperledger.besu.ethereum.p2p.peers.ImmutableEnodeDnsConfiguration;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class AdminRemovePeerTest {
@Mock private P2PNetwork p2pNetwork;
private AdminRemovePeer method;
private AdminRemovePeer methodDNSDisabled;
private AdminRemovePeer methodDNSUpdateDisabled;
final String validEnode =
"enode://"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "@127.0.0.1:30303";
final String validDNSEnode =
"enode://"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "@node.acme.com:30303";
final JsonRpcRequestContext validRequest =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "admin_removePeer", new String[] {validEnode}));
final JsonRpcRequestContext validDNSRequest =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "admin_removePeer", new String[] {validDNSEnode}));
@Before
public void setup() {
method =
new AdminRemovePeer(
p2pNetwork,
Optional.of(
ImmutableEnodeDnsConfiguration.builder()
.dnsEnabled(true)
.updateEnabled(true)
.build()));
methodDNSDisabled =
new AdminRemovePeer(
p2pNetwork,
Optional.of(
ImmutableEnodeDnsConfiguration.builder()
.dnsEnabled(false)
.updateEnabled(true)
.build()));
methodDNSUpdateDisabled =
new AdminRemovePeer(
p2pNetwork,
Optional.of(
ImmutableEnodeDnsConfiguration.builder()
.dnsEnabled(true)
.updateEnabled(false)
.build()));
}
@Test
public void requestIsMissingParameter() {
final JsonRpcRequestContext request =
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "admin_removePeer", new String[] {}));
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS);
final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestHasNullObjectParameter() {
final JsonRpcRequestContext request =
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "admin_removePeer", null));
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS);
final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestHasNullArrayParameter() {
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "admin_removePeer", new String[] {null}));
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS);
final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestHasInvalidEnode() {
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "admin_removePeer", new String[] {"asdf"}));
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.PARSE_ERROR);
final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestHasInvalidEnodeLength() {
String invalidLengthEnode =
"enode://"
+ "0000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "00000000000000000000000000000000"
+ "@127.0.0.1:30303";
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "admin_removePeer", new String[] {invalidLengthEnode}));
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.ENODE_ID_INVALID);
final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestRemovesValidEnode() {
when(p2pNetwork.removeMaintainedConnectionPeer(any())).thenReturn(true);
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(validRequest.getRequest().getId(), true);
final JsonRpcResponse actualResponse = method.response(validRequest);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestRemovesValidDNSEnode() {
when(p2pNetwork.removeMaintainedConnectionPeer(any())).thenReturn(true);
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(
validRequest.getRequest().getId(),
true); // DNS is mapped to an IP address, so we expect the non-DNS response
final JsonRpcResponse actualResponse = method.response(validDNSRequest);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestRemovesDNSEnodeButDNSDisabled() {
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(
validDNSRequest.getRequest().getId(), JsonRpcError.DNS_NOT_ENABLED);
final JsonRpcResponse actualResponse = methodDNSDisabled.response(validDNSRequest);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestRemovesDNSEnodeButDNSNotResolved() {
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(
validDNSRequest.getRequest().getId(), JsonRpcError.CANT_RESOLVE_PEER_ENODE_DNS);
final JsonRpcResponse actualResponse = methodDNSUpdateDisabled.response(validDNSRequest);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestRefusesListOfNodes() {
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "admin_removePeer", new String[] {validEnode, validEnode}));
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS);
final JsonRpcResponse actualResponse = method.response(request);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestReturnsFalseIfRemoveFails() {
when(p2pNetwork.removeMaintainedConnectionPeer(any())).thenReturn(false);
final JsonRpcResponse expectedResponse =
new JsonRpcSuccessResponse(validRequest.getRequest().getId(), false);
final JsonRpcResponse actualResponse = method.response(validRequest);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
@Test
public void requestReturnsErrorWhenP2pDisabled() {
when(p2pNetwork.removeMaintainedConnectionPeer(any()))
.thenThrow(
new P2PDisabledException(
"P2P networking disabled. Unable to connect to remove peer."));
final JsonRpcResponse expectedResponse =
new JsonRpcErrorResponse(validRequest.getRequest().getId(), JsonRpcError.P2P_DISABLED);
final JsonRpcResponse actualResponse = method.response(validRequest);
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}
}

@ -186,6 +186,7 @@ public class WebSocketServiceLoginTest {
folder.getRoot().toPath(), folder.getRoot().toPath(),
mock(EthPeers.class), mock(EthPeers.class),
vertx, vertx,
Optional.empty(),
Optional.empty())); Optional.empty()));
websocketMethods.putAll(rpcMethods); websocketMethods.putAll(rpcMethods);

Loading…
Cancel
Save