mirror of https://github.com/hyperledger/besu
[NC-2118] Method to reload permissions file (#834)
* Extracting EnodeURL logic to specific object * Moving permissioning config builder to permissioning package * Validating accounts in permissions file * Implemented controller reload method * Reload whitelist from file API method * Spotless * Refactoring account validation * Errorprone * Fixing tests after rebase * Spotless * PR review Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
50c5baaf5a
commit
6551183a8d
@ -0,0 +1,57 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2018 ConsenSys AG. |
||||||
|
* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||||
|
import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; |
||||||
|
import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; |
||||||
|
|
||||||
|
import java.util.Optional; |
||||||
|
|
||||||
|
public class PermReloadPermissionsFromFile implements JsonRpcMethod { |
||||||
|
|
||||||
|
private final Optional<AccountWhitelistController> accountWhitelistController; |
||||||
|
private final Optional<NodeWhitelistController> nodesWhitelistController; |
||||||
|
|
||||||
|
public PermReloadPermissionsFromFile( |
||||||
|
final Optional<AccountWhitelistController> accountWhitelistController, |
||||||
|
final Optional<NodeWhitelistController> nodesWhitelistController) { |
||||||
|
this.accountWhitelistController = accountWhitelistController; |
||||||
|
this.nodesWhitelistController = nodesWhitelistController; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() { |
||||||
|
return "perm_reloadPermissionsFromFile"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public JsonRpcResponse response(final JsonRpcRequest request) { |
||||||
|
if (!accountWhitelistController.isPresent() && !nodesWhitelistController.isPresent()) { |
||||||
|
return new JsonRpcErrorResponse(request.getId(), JsonRpcError.PERMISSIONING_NOT_ENABLED); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
accountWhitelistController.ifPresent(AccountWhitelistController::reload); |
||||||
|
nodesWhitelistController.ifPresent(NodeWhitelistController::reload); |
||||||
|
return new JsonRpcSuccessResponse(request.getId()); |
||||||
|
} catch (Exception e) { |
||||||
|
return new JsonRpcErrorResponse(request.getId(), JsonRpcError.WHITELIST_RELOAD_ERROR); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2019 ConsenSys AG. |
||||||
|
* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.permissioning; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.mockito.Mockito.doThrow; |
||||||
|
import static org.mockito.Mockito.verify; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse; |
||||||
|
import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||||
|
import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; |
||||||
|
import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; |
||||||
|
|
||||||
|
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.class) |
||||||
|
public class PermReloadPermissionsFromFileTest { |
||||||
|
|
||||||
|
@Mock private AccountWhitelistController accountWhitelistController; |
||||||
|
@Mock private NodeWhitelistController nodeWhitelistController; |
||||||
|
private PermReloadPermissionsFromFile method; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void before() { |
||||||
|
method = |
||||||
|
new PermReloadPermissionsFromFile( |
||||||
|
Optional.of(accountWhitelistController), Optional.of(nodeWhitelistController)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void getNameShouldReturnExpectedName() { |
||||||
|
assertThat(method.getName()).isEqualTo("perm_reloadPermissionsFromFile"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenBothControllersAreNotPresentMethodShouldReturnPermissioningDisabled() { |
||||||
|
JsonRpcResponse expectedErrorResponse = |
||||||
|
new JsonRpcErrorResponse(null, JsonRpcError.PERMISSIONING_NOT_ENABLED); |
||||||
|
|
||||||
|
method = new PermReloadPermissionsFromFile(Optional.empty(), Optional.empty()); |
||||||
|
|
||||||
|
JsonRpcResponse response = method.response(reloadRequest()); |
||||||
|
|
||||||
|
assertThat(response).isEqualToComparingFieldByField(expectedErrorResponse); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenControllersReloadSucceedsMethodShouldReturnSuccess() { |
||||||
|
JsonRpcResponse response = method.response(reloadRequest()); |
||||||
|
|
||||||
|
verify(accountWhitelistController).reload(); |
||||||
|
verify(nodeWhitelistController).reload(); |
||||||
|
|
||||||
|
assertThat(response).isEqualToComparingFieldByField(successResponse()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void whenControllerReloadFailsMethodShouldReturnError() { |
||||||
|
doThrow(new RuntimeException()).when(accountWhitelistController).reload(); |
||||||
|
JsonRpcResponse expectedErrorResponse = |
||||||
|
new JsonRpcErrorResponse(null, JsonRpcError.WHITELIST_RELOAD_ERROR); |
||||||
|
|
||||||
|
JsonRpcResponse response = method.response(reloadRequest()); |
||||||
|
|
||||||
|
assertThat(response).isEqualToComparingFieldByField(expectedErrorResponse); |
||||||
|
} |
||||||
|
|
||||||
|
private JsonRpcSuccessResponse successResponse() { |
||||||
|
return new JsonRpcSuccessResponse(null); |
||||||
|
} |
||||||
|
|
||||||
|
private JsonRpcRequest reloadRequest() { |
||||||
|
return new JsonRpcRequest("2.0", "perm_reloadPermissionsFromFile", null); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,4 @@ |
|||||||
|
# Permissioning TOML file |
||||||
|
|
||||||
|
accounts-whitelist=["0x0000000000000000000000000000000000000009"] |
||||||
|
nodes-whitelist=["enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.9:4567","enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.169.0.9:4568"] |
@ -0,0 +1,3 @@ |
|||||||
|
# Permissioning TOML file (account whitelist only) |
||||||
|
|
||||||
|
accounts-whitelist=["0xfoo"] |
@ -0,0 +1,165 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2019 ConsenSys AG. |
||||||
|
* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
package tech.pegasys.pantheon.util.enode; |
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.util.NetworkUtility; |
||||||
|
|
||||||
|
import java.net.URI; |
||||||
|
import java.util.OptionalInt; |
||||||
|
import java.util.regex.Matcher; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
import com.google.common.base.Objects; |
||||||
|
|
||||||
|
public class EnodeURL { |
||||||
|
|
||||||
|
private static final String IP_REPLACE_MARKER = "$$IP_PATTERN$$"; |
||||||
|
private static final String IPV4_PATTERN = |
||||||
|
"(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}"; |
||||||
|
private static final String IPV6_PATTERN = "\\[(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\]"; |
||||||
|
private static final String IPV6_COMPACT_PATTERN = |
||||||
|
"\\[((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\]"; |
||||||
|
private static final String DISCOVERY_PORT_PATTERN = "\\?discport=(?<discovery>\\d+)"; |
||||||
|
private static final String HEX_STRING_PATTERN = "[0-9a-fA-F]+"; |
||||||
|
|
||||||
|
private static final String ENODE_URL_PATTERN = |
||||||
|
"enode://(?<nodeId>\\w+)@(?<ip>" + IP_REPLACE_MARKER + "):(?<listening>\\d+)"; |
||||||
|
|
||||||
|
private final String nodeId; |
||||||
|
private final String ip; |
||||||
|
private final Integer listeningPort; |
||||||
|
private final OptionalInt discoveryPort; |
||||||
|
|
||||||
|
public EnodeURL( |
||||||
|
final String nodeId, |
||||||
|
final String ip, |
||||||
|
final Integer listeningPort, |
||||||
|
final OptionalInt discoveryPort) { |
||||||
|
this.nodeId = nodeId; |
||||||
|
this.ip = ip; |
||||||
|
this.listeningPort = listeningPort; |
||||||
|
this.discoveryPort = discoveryPort; |
||||||
|
} |
||||||
|
|
||||||
|
public EnodeURL(final String nodeId, final String ip, final Integer listeningPort) { |
||||||
|
this.nodeId = nodeId; |
||||||
|
this.ip = ip; |
||||||
|
this.listeningPort = listeningPort; |
||||||
|
this.discoveryPort = OptionalInt.empty(); |
||||||
|
} |
||||||
|
|
||||||
|
public EnodeURL(final String value) { |
||||||
|
checkArgument( |
||||||
|
value != null && !value.isEmpty(), "Can't convert null/empty string to EnodeURLProperty."); |
||||||
|
|
||||||
|
final boolean containsDiscoveryPort = value.contains("discport"); |
||||||
|
final boolean isIPV4 = Pattern.compile(".*" + IPV4_PATTERN + ".*").matcher(value).matches(); |
||||||
|
final boolean isIPV6 = Pattern.compile(".*" + IPV6_PATTERN + ".*").matcher(value).matches(); |
||||||
|
final boolean isIPV6Compact = |
||||||
|
Pattern.compile(".*" + IPV6_COMPACT_PATTERN + ".*").matcher(value).matches(); |
||||||
|
|
||||||
|
String pattern = ENODE_URL_PATTERN; |
||||||
|
if (isIPV4) { |
||||||
|
pattern = pattern.replace(IP_REPLACE_MARKER, IPV4_PATTERN); |
||||||
|
} else if (isIPV6) { |
||||||
|
pattern = pattern.replace(IP_REPLACE_MARKER, IPV6_PATTERN); |
||||||
|
} else if (isIPV6Compact) { |
||||||
|
pattern = pattern.replace(IP_REPLACE_MARKER, IPV6_COMPACT_PATTERN); |
||||||
|
} else { |
||||||
|
throw new IllegalArgumentException("Invalid enode URL IP format."); |
||||||
|
} |
||||||
|
|
||||||
|
if (containsDiscoveryPort) { |
||||||
|
pattern += DISCOVERY_PORT_PATTERN; |
||||||
|
} |
||||||
|
if (isIPV6) { |
||||||
|
pattern = pattern.replace(IP_REPLACE_MARKER, IPV6_PATTERN); |
||||||
|
} else { |
||||||
|
pattern = pattern.replace(IP_REPLACE_MARKER, IPV4_PATTERN); |
||||||
|
} |
||||||
|
|
||||||
|
final Matcher matcher = Pattern.compile(pattern).matcher(value); |
||||||
|
checkArgument( |
||||||
|
matcher.matches(), |
||||||
|
"Invalid enode URL syntax. Enode URL should have the following format 'enode://<node_id>@<ip>:<listening_port>[?discport=<discovery_port>]'."); |
||||||
|
|
||||||
|
this.nodeId = getAndValidateNodeId(matcher); |
||||||
|
this.ip = matcher.group("ip"); |
||||||
|
this.listeningPort = getAndValidatePort(matcher, "listening"); |
||||||
|
|
||||||
|
if (containsDiscoveryPort(value)) { |
||||||
|
this.discoveryPort = OptionalInt.of(getAndValidatePort(matcher, "discovery")); |
||||||
|
} else { |
||||||
|
this.discoveryPort = OptionalInt.empty(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public URI toURI() { |
||||||
|
if (discoveryPort.isPresent()) { |
||||||
|
return URI.create( |
||||||
|
String.format( |
||||||
|
"enode://%s@%s:%d?discport=%d", nodeId, ip, listeningPort, discoveryPort.getAsInt())); |
||||||
|
} else { |
||||||
|
return URI.create(String.format("enode://%s@%s:%d", nodeId, ip, listeningPort)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static URI asURI(final String url) { |
||||||
|
return new EnodeURL(url).toURI(); |
||||||
|
} |
||||||
|
|
||||||
|
private static String getAndValidateNodeId(final Matcher matcher) { |
||||||
|
final String invalidNodeIdErrorMsg = |
||||||
|
"Enode URL contains an invalid node ID. Node ID must have 128 characters and shouldn't include the '0x' hex prefix."; |
||||||
|
final String nodeId = matcher.group("nodeId"); |
||||||
|
|
||||||
|
checkArgument(nodeId.matches(HEX_STRING_PATTERN), invalidNodeIdErrorMsg); |
||||||
|
checkArgument(nodeId.length() == 128, invalidNodeIdErrorMsg); |
||||||
|
|
||||||
|
return nodeId; |
||||||
|
} |
||||||
|
|
||||||
|
private static Integer getAndValidatePort(final Matcher matcher, final String portName) { |
||||||
|
int port = Integer.valueOf(matcher.group(portName)); |
||||||
|
checkArgument( |
||||||
|
NetworkUtility.isValidPort(port), |
||||||
|
"Invalid " + portName + " port range. Port should be between 0 - 65535"); |
||||||
|
return port; |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean containsDiscoveryPort(final String value) { |
||||||
|
return value.contains("discport"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(final Object o) { |
||||||
|
if (this == o) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
if (o == null || getClass() != o.getClass()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
EnodeURL enodeURL = (EnodeURL) o; |
||||||
|
return Objects.equal(nodeId, enodeURL.nodeId) |
||||||
|
&& Objects.equal(ip, enodeURL.ip) |
||||||
|
&& Objects.equal(listeningPort, enodeURL.listeningPort) |
||||||
|
&& Objects.equal(discoveryPort, enodeURL.discoveryPort); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int hashCode() { |
||||||
|
return Objects.hashCode(nodeId, ip, listeningPort, discoveryPort); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,230 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2019 ConsenSys AG. |
||||||
|
* |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
package tech.pegasys.pantheon.util.enode; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.assertj.core.api.Assertions.catchThrowable; |
||||||
|
|
||||||
|
import java.net.URI; |
||||||
|
import java.util.OptionalInt; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
public class EnodeURLTest { |
||||||
|
|
||||||
|
private final String VALID_NODE_ID = |
||||||
|
"6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"; |
||||||
|
private final String IPV4_ADDRESS = "192.168.0.1"; |
||||||
|
private final String IPV6_FULL_ADDRESS = "[2001:db8:85a3:0:0:8a2e:0370:7334]"; |
||||||
|
private final String IPV6_COMPACT_ADDRESS = "[2001:db8:85a3::8a2e:0370:7334]"; |
||||||
|
private final int P2P_PORT = 30303; |
||||||
|
private final int DISCOVERY_PORT = 30301; |
||||||
|
private final String DISCOVERY_QUERY = "discport=" + DISCOVERY_PORT; |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithDiscoveryPortShouldBuildExpectedEnodeURLObject() { |
||||||
|
final EnodeURL expectedEnodeURL = |
||||||
|
new EnodeURL(VALID_NODE_ID, IPV4_ADDRESS, P2P_PORT, OptionalInt.of(DISCOVERY_PORT)); |
||||||
|
final String enodeURLString = |
||||||
|
"enode://" + VALID_NODE_ID + "@" + IPV4_ADDRESS + ":" + P2P_PORT + "?" + DISCOVERY_QUERY; |
||||||
|
|
||||||
|
final EnodeURL enodeURL = new EnodeURL(enodeURLString); |
||||||
|
|
||||||
|
assertThat(enodeURL).isEqualTo(expectedEnodeURL); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithoutDiscoveryPortShouldBuildExpectedEnodeURLObject() { |
||||||
|
final EnodeURL expectedEnodeURL = new EnodeURL(VALID_NODE_ID, IPV4_ADDRESS, P2P_PORT); |
||||||
|
final String enodeURLString = "enode://" + VALID_NODE_ID + "@" + IPV4_ADDRESS + ":" + P2P_PORT; |
||||||
|
|
||||||
|
final EnodeURL enodeURL = new EnodeURL(enodeURLString); |
||||||
|
|
||||||
|
assertThat(enodeURL).isEqualTo(expectedEnodeURL); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithIPV6ShouldBuildExpectedEnodeURLObject() { |
||||||
|
final EnodeURL expectedEnodeURL = |
||||||
|
new EnodeURL(VALID_NODE_ID, IPV6_FULL_ADDRESS, P2P_PORT, OptionalInt.of(DISCOVERY_PORT)); |
||||||
|
final String enodeURLString = |
||||||
|
"enode://" |
||||||
|
+ VALID_NODE_ID |
||||||
|
+ "@" |
||||||
|
+ IPV6_FULL_ADDRESS |
||||||
|
+ ":" |
||||||
|
+ P2P_PORT |
||||||
|
+ "?" |
||||||
|
+ DISCOVERY_QUERY; |
||||||
|
|
||||||
|
final EnodeURL enodeURL = new EnodeURL(enodeURLString); |
||||||
|
|
||||||
|
assertThat(enodeURL).isEqualTo(expectedEnodeURL); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithIPV6InCompactFormShouldBuildExpectedEnodeURLObject() { |
||||||
|
final EnodeURL expectedEnodeURL = |
||||||
|
new EnodeURL(VALID_NODE_ID, IPV6_COMPACT_ADDRESS, P2P_PORT, OptionalInt.of(DISCOVERY_PORT)); |
||||||
|
final String enodeURLString = |
||||||
|
"enode://" |
||||||
|
+ VALID_NODE_ID |
||||||
|
+ "@" |
||||||
|
+ IPV6_COMPACT_ADDRESS |
||||||
|
+ ":" |
||||||
|
+ P2P_PORT |
||||||
|
+ "?" |
||||||
|
+ DISCOVERY_QUERY; |
||||||
|
|
||||||
|
final EnodeURL enodeURL = new EnodeURL(enodeURLString); |
||||||
|
|
||||||
|
assertThat(enodeURL).isEqualTo(expectedEnodeURL); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithoutNodeIdShouldFail() { |
||||||
|
final String enodeURLString = "enode://@" + IPV4_ADDRESS + ":" + P2P_PORT; |
||||||
|
final Throwable thrown = catchThrowable(() -> new EnodeURL(enodeURLString)); |
||||||
|
|
||||||
|
assertThat(thrown) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessage( |
||||||
|
"Invalid enode URL syntax. Enode URL should have the following format 'enode://<node_id>@<ip>:<listening_port>[?discport=<discovery_port>]'."); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithInvalidSizeNodeIdShouldFail() { |
||||||
|
final String enodeURLString = "enode://wrong_size_string@" + IPV4_ADDRESS + ":" + P2P_PORT; |
||||||
|
final Throwable thrown = catchThrowable(() -> new EnodeURL(enodeURLString)); |
||||||
|
|
||||||
|
assertThat(thrown) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessage( |
||||||
|
"Enode URL contains an invalid node ID. Node ID must have 128 characters and shouldn't include the '0x' hex prefix."); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithInvalidHexCharacterNodeIdShouldFail() { |
||||||
|
final String enodeURLString = |
||||||
|
"enode://0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000@" |
||||||
|
+ IPV4_ADDRESS |
||||||
|
+ ":" |
||||||
|
+ P2P_PORT; |
||||||
|
final Throwable thrown = catchThrowable(() -> new EnodeURL(enodeURLString)); |
||||||
|
|
||||||
|
assertThat(thrown) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessage( |
||||||
|
"Enode URL contains an invalid node ID. Node ID must have 128 characters and shouldn't include the '0x' hex prefix."); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithoutIpShouldFail() { |
||||||
|
final String enodeURLString = "enode://" + VALID_NODE_ID + "@:" + P2P_PORT; |
||||||
|
final Throwable thrown = catchThrowable(() -> new EnodeURL(enodeURLString)); |
||||||
|
|
||||||
|
assertThat(thrown) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessage("Invalid enode URL IP format."); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithInvalidIpFormatShouldFail() { |
||||||
|
final String enodeURLString = "enode://" + VALID_NODE_ID + "@192.0.1:" + P2P_PORT; |
||||||
|
final Throwable thrown = catchThrowable(() -> new EnodeURL(enodeURLString)); |
||||||
|
|
||||||
|
assertThat(thrown) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessage("Invalid enode URL IP format."); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithoutListeningPortShouldFail() { |
||||||
|
final String enodeURLString = "enode://" + VALID_NODE_ID + "@" + IPV4_ADDRESS + ":"; |
||||||
|
final Throwable thrown = catchThrowable(() -> new EnodeURL(enodeURLString)); |
||||||
|
|
||||||
|
assertThat(thrown) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessage( |
||||||
|
"Invalid enode URL syntax. Enode URL should have the following format 'enode://<node_id>@<ip>:<listening_port>[?discport=<discovery_port>]'."); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithoutListeningPortAndWithDiscoveryPortShouldFail() { |
||||||
|
final String enodeURLString = "enode://" + VALID_NODE_ID + "@" + IPV4_ADDRESS + ":?30301"; |
||||||
|
final Throwable thrown = catchThrowable(() -> new EnodeURL(enodeURLString)); |
||||||
|
|
||||||
|
assertThat(thrown) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessage( |
||||||
|
"Invalid enode URL syntax. Enode URL should have the following format 'enode://<node_id>@<ip>:<listening_port>[?discport=<discovery_port>]'."); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithAboveRangeListeningPortShouldFail() { |
||||||
|
final String enodeURLString = "enode://" + VALID_NODE_ID + "@" + IPV4_ADDRESS + ":98765"; |
||||||
|
final Throwable thrown = catchThrowable(() -> new EnodeURL(enodeURLString)); |
||||||
|
|
||||||
|
assertThat(thrown) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessage("Invalid listening port range. Port should be between 0 - 65535"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithAboveRangeDiscoveryPortShouldFail() { |
||||||
|
final String enodeURLString = |
||||||
|
"enode://" + VALID_NODE_ID + "@" + IPV4_ADDRESS + ":" + P2P_PORT + "?discport=98765"; |
||||||
|
final Throwable thrown = catchThrowable(() -> new EnodeURL(enodeURLString)); |
||||||
|
|
||||||
|
assertThat(thrown) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessage("Invalid discovery port range. Port should be between 0 - 65535"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithNullEnodeURLShouldFail() { |
||||||
|
final Throwable thrown = catchThrowable(() -> new EnodeURL(null)); |
||||||
|
|
||||||
|
assertThat(thrown) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessage("Can't convert null/empty string to EnodeURLProperty."); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createEnodeURLWithEmptyEnodeURLShouldFail() { |
||||||
|
final Throwable thrown = catchThrowable(() -> new EnodeURL("")); |
||||||
|
|
||||||
|
assertThat(thrown) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessage("Can't convert null/empty string to EnodeURLProperty."); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void toURIWithDiscoveryPortCreateExpectedURI() { |
||||||
|
final String enodeURLString = |
||||||
|
"enode://" + VALID_NODE_ID + "@" + IPV4_ADDRESS + ":" + P2P_PORT + "?" + DISCOVERY_QUERY; |
||||||
|
final URI expectedURI = URI.create(enodeURLString); |
||||||
|
final URI createdURI = new EnodeURL(enodeURLString).toURI(); |
||||||
|
|
||||||
|
assertThat(createdURI).isEqualTo(expectedURI); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void toURIWithoutDiscoveryPortCreateExpectedURI() { |
||||||
|
final String enodeURLString = "enode://" + VALID_NODE_ID + "@" + IPV4_ADDRESS + ":" + P2P_PORT; |
||||||
|
final URI expectedURI = URI.create(enodeURLString); |
||||||
|
final URI createdURI = new EnodeURL(enodeURLString).toURI(); |
||||||
|
|
||||||
|
assertThat(createdURI).isEqualTo(expectedURI); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue