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