From c787dea877d969fa2a6e3b8d6b75729d9523677c Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 22 Jan 2019 12:51:02 +1000 Subject: [PATCH 01/31] typo --- pantheon/src/test/resources/complete_config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pantheon/src/test/resources/complete_config.toml b/pantheon/src/test/resources/complete_config.toml index 52b483f848..226f5b7e8d 100644 --- a/pantheon/src/test/resources/complete_config.toml +++ b/pantheon/src/test/resources/complete_config.toml @@ -20,7 +20,7 @@ metrics-listen="8.6.7.5:309" # IP:port # chain genesis="~/genesis.json" # Path sync-mode="fast"# should be FAST or FULL (or fast or full) -ottoman=false # true means using ottoman testnet if genesys file uses iBFT +ottoman=false # true means using ottoman testnet if genesis file uses iBFT #mining miner-coinbase="0x0000000000000000000000000000000000000002" \ No newline at end of file From 6ce96305aa6f3995745a4b014621819e1100e6ee Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 22 Jan 2019 12:55:12 +1000 Subject: [PATCH 02/31] typos --- pantheon/src/test/resources/everything_config.toml | 2 +- pantheon/src/test/resources/partial_config.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pantheon/src/test/resources/everything_config.toml b/pantheon/src/test/resources/everything_config.toml index 03e902f35c..19a01d41e3 100644 --- a/pantheon/src/test/resources/everything_config.toml +++ b/pantheon/src/test/resources/everything_config.toml @@ -2,7 +2,7 @@ # The odds are you are reading this because you added a CLI and didn't add it # here and a test broke. To fix the test add your CLI to this file. # -# Please use a plausable value, Pantheon has to at least be able to parse it. +# Please use a plausible value, Pantheon has to at least be able to parse it. # If it is a multi-valued CLI make it a TOML array. # If it is a number or boolean make it a number or boolean # All other config options are strings, and must be quoted. diff --git a/pantheon/src/test/resources/partial_config.toml b/pantheon/src/test/resources/partial_config.toml index 3de87f7931..f2d8eaa061 100644 --- a/pantheon/src/test/resources/partial_config.toml +++ b/pantheon/src/test/resources/partial_config.toml @@ -1,4 +1,4 @@ -# this is a valid prtial TOML config file +# this is a valid partial TOML config file #mining miner-coinbase="0x0000000000000000000000000000000000000002" \ No newline at end of file From a614ffacff90b5e27ddab7f36acfd409e776be28 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 22 Jan 2019 13:36:00 +1000 Subject: [PATCH 03/31] typo --- .../pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java index e805dd6368..784a7fdb71 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java @@ -41,7 +41,7 @@ class ConfigOptionSearchAndRunHandler extends AbstractParseResultHandler Date: Tue, 22 Jan 2019 16:20:14 +1000 Subject: [PATCH 04/31] create permissioning config from toml file --- .../pegasys/pantheon/cli/PantheonCommand.java | 30 +++++++++++++++++++ .../pantheon/cli/PantheonCommandTest.java | 23 ++++++++++++-- .../test/resources/permissioning_config.toml | 4 +++ 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 pantheon/src/test/resources/permissioning_config.toml diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index e16b3fafb0..e65b5db50b 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -63,6 +63,8 @@ import com.google.common.io.Resources; import com.google.common.net.HostAndPort; import io.vertx.core.Vertx; +import net.consensys.cava.toml.TomlArray; +import net.consensys.cava.toml.TomlParseResult; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; import picocli.CommandLine; @@ -617,6 +619,34 @@ private PermissioningConfiguration permissioningConfiguration() { return permissioningConfiguration; } + public static PermissioningConfiguration permissioningConfiguration( + final TomlParseResult permToml) throws IOException { + + TomlArray accountWhitelistTomlArray = permToml.getArray("accounts-whitelist"); + TomlArray nodeWhitelistTomlArray = permToml.getArray("nodes-whitelist"); + + final PermissioningConfiguration permissioningConfiguration = + PermissioningConfiguration.createDefault(); + List accountsWhitelistToml = + accountWhitelistTomlArray + .toList() + .stream() + .map(Object::toString) + .collect(Collectors.toList()); + permissioningConfiguration.setAccountWhitelist(accountsWhitelistToml); + + List nodesWhitelistToml = + nodeWhitelistTomlArray + .toList() + .stream() + .map(Object::toString) + .map(URI::create) + .collect(Collectors.toList()); + permissioningConfiguration.setNodeWhitelist(nodesWhitelistToml); + + return permissioningConfiguration; + } + private SynchronizerConfiguration buildSyncConfig(final SyncMode syncMode) { checkNotNull(syncMode); synchronizerConfigurationBuilder.syncMode(syncMode); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index 74b726cea9..426c5a1d00 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -38,6 +38,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.util.bytes.BytesValue; @@ -343,10 +344,10 @@ public void overrideDefaultValuesIfKeyIsPresentInConfigFile() throws IOException } @Test - public void tomlThatConfiguresEverything() throws IOException { + public void tomlThatConfiguresEverythingExceptPermissioning() throws IOException { assumeTrue(isFullInstantiation()); - // Load a TOML that configures literally everything + // Load a TOML that configures literally everything (except permissioning) final URL configFile = Resources.getResource("everything_config.toml"); final Path toml = Files.createTempFile("toml", ""); Files.write(toml, Resources.toByteArray(configFile)); @@ -383,6 +384,24 @@ public void tomlThatConfiguresEverything() throws IOException { assertThat(options.stream().map(CommandLine.Model.OptionSpec::longestName)).isEmpty(); } + @Test + public void permissioningToml() throws IOException { + final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; + + final URL configFile = Resources.getResource("permissioning_config.toml"); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + PermissioningConfiguration permissioningConfiguration = + PantheonCommand.permissioningConfiguration(tomlResult); + + assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getAccountWhitelist()) + .containsExactly("0x0000000000000000000000000000000000000009"); + assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); + } + @Test public void noOverrideDefaultValuesIfKeyIsNotPresentInConfigFile() throws IOException { assumeTrue(isFullInstantiation()); diff --git a/pantheon/src/test/resources/permissioning_config.toml b/pantheon/src/test/resources/permissioning_config.toml new file mode 100644 index 0000000000..786b14e0e8 --- /dev/null +++ b/pantheon/src/test/resources/permissioning_config.toml @@ -0,0 +1,4 @@ +# Permissioning TOML file + +accounts-whitelist=["0x0000000000000000000000000000000000000009"] +nodes-whitelist=["enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.9:4567"] From 58bfa1b2dcfbb2c6afe4e8b1508fc4a8b44aa895 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 23 Jan 2019 09:45:39 +1000 Subject: [PATCH 05/31] fixed merge issue --- .../main/java/tech/pegasys/pantheon/cli/PantheonCommand.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index bc3cab2c78..8e1b236360 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -717,8 +717,7 @@ public static PermissioningConfiguration permissioningConfiguration( return permissioningConfiguration; } - private SynchronizerConfiguration buildSyncConfig(final SyncMode syncMode) { - checkNotNull(syncMode); + private SynchronizerConfiguration buildSyncConfig() { synchronizerConfigurationBuilder.syncMode(syncMode); synchronizerConfigurationBuilder.maxTrailingPeers(maxTrailingPeers); return synchronizerConfigurationBuilder.build(); From cc38ef616949c4fd6800f366aac729e8c255ccc7 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 23 Jan 2019 09:52:07 +1000 Subject: [PATCH 06/31] don't throw --- .../main/java/tech/pegasys/pantheon/cli/PantheonCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 8e1b236360..b6f708da3f 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -690,7 +690,7 @@ private PermissioningConfiguration permissioningConfiguration() { } public static PermissioningConfiguration permissioningConfiguration( - final TomlParseResult permToml) throws IOException { + final TomlParseResult permToml) { TomlArray accountWhitelistTomlArray = permToml.getArray("accounts-whitelist"); TomlArray nodeWhitelistTomlArray = permToml.getArray("nodes-whitelist"); From 426b644ad15157d3a1338d0abbe87c40da7795a8 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 23 Jan 2019 15:44:48 +1000 Subject: [PATCH 07/31] parse TOML file for permissioning config --- .../pegasys/pantheon/cli/PantheonCommand.java | 7 ++++++- .../custom/EnodeToURIPropertyConverter.java | 10 ++++++--- .../pantheon/cli/PantheonCommandTest.java | 21 +++++++++++++++++++ .../permissioning_config_invalid_enode.toml | 4 ++++ 4 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 pantheon/src/test/resources/permissioning_config_invalid_enode.toml diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 7eb6388c50..f7fd8649f1 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -556,6 +556,11 @@ public void parse( @Override public void run() { + // TODO permToml = TomlConfigFileDefaultProvider.loadConfigurationFromFile + // but we don't have visibility to the TomlConfigFileDefaultProvider. + // see ConfigOptionSearchAndRunHandler + // TODO permissioningConfiguration(permToml) + // set log level per CLI flags if (logLevel != null) { System.out.println("Setting logging level to " + logLevel.name()); @@ -709,7 +714,7 @@ public static PermissioningConfiguration permissioningConfiguration( .toList() .stream() .map(Object::toString) - .map(URI::create) + .map(EnodeToURIPropertyConverter::convertToURI) .collect(Collectors.toList()); permissioningConfiguration.setNodeWhitelist(nodesWhitelistToml); diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/custom/EnodeToURIPropertyConverter.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/custom/EnodeToURIPropertyConverter.java index 83e0f09f4f..44e647e521 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/custom/EnodeToURIPropertyConverter.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/custom/EnodeToURIPropertyConverter.java @@ -38,6 +38,10 @@ public class EnodeToURIPropertyConverter implements ITypeConverter { @Override public URI convert(final String value) throws IllegalArgumentException { + return convertToURI(value); + } + + public static URI convertToURI(final String value) throws IllegalArgumentException { checkArgument( value != null && !value.isEmpty(), "Can't convert null/empty string to EnodeURLProperty."); @@ -85,7 +89,7 @@ public URI convert(final String value) throws IllegalArgumentException { } } - private String getAndValidateNodeId(final Matcher matcher) { + 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"); @@ -96,7 +100,7 @@ private String getAndValidateNodeId(final Matcher matcher) { return nodeId; } - private Integer getAndValidatePort(final Matcher matcher, final String portName) { + private static Integer getAndValidatePort(final Matcher matcher, final String portName) { int port = Integer.valueOf(matcher.group(portName)); checkArgument( NetworkUtility.isValidPort(port), @@ -104,7 +108,7 @@ private Integer getAndValidatePort(final Matcher matcher, final String portName) return port; } - private boolean containsDiscoveryPort(final String value) { + private static boolean containsDiscoveryPort(final String value) { return value.contains("discport"); } } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index ce07d9881e..d71f7973ec 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -15,6 +15,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.eq; @@ -351,6 +352,7 @@ public void tomlThatConfiguresEverythingExceptPermissioning() throws IOException @Test public void permissioningToml() throws IOException { + final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; final URL configFile = Resources.getResource("permissioning_config.toml"); @@ -367,6 +369,25 @@ public void permissioningToml() throws IOException { assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); } + @Test + public void permissioningTomlWithInvalidEnode() throws IOException { + + final URL configFile = Resources.getResource("permissioning_config_invalid_enode.toml"); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + try { + PantheonCommand.permissioningConfiguration(tomlResult); + fail("Enode URL contains an invalid node ID"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).startsWith("Enode URL contains an invalid node ID"); + } + // assertThat(commandErrorOutput.toString()).startsWith("Enode URL contains an invalid node + // ID"); + assertThat(commandOutput.toString()).isEmpty(); + } + @Test public void noOverrideDefaultValuesIfKeyIsNotPresentInConfigFile() throws IOException { assumeTrue(isFullInstantiation()); diff --git a/pantheon/src/test/resources/permissioning_config_invalid_enode.toml b/pantheon/src/test/resources/permissioning_config_invalid_enode.toml new file mode 100644 index 0000000000..89ad7ada23 --- /dev/null +++ b/pantheon/src/test/resources/permissioning_config_invalid_enode.toml @@ -0,0 +1,4 @@ +# Permissioning TOML file + +accounts-whitelist=["0x0000000000000000000000000000000000000009"] +nodes-whitelist=["enode://bob@192.168.0.9:4567"] From c5e55165a559fdf0f44aeba65d9c07435b1f0717 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 22 Jan 2019 12:51:02 +1000 Subject: [PATCH 08/31] typo --- pantheon/src/test/resources/complete_config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pantheon/src/test/resources/complete_config.toml b/pantheon/src/test/resources/complete_config.toml index 394f8217c5..3e72cb451f 100644 --- a/pantheon/src/test/resources/complete_config.toml +++ b/pantheon/src/test/resources/complete_config.toml @@ -24,7 +24,7 @@ metrics-port=309 # chain private-genesis-file="~/genesis.json" # Path sync-mode="fast"# should be FAST or FULL (or fast or full) -ottoman=false # true means using ottoman testnet if genesys file uses iBFT +ottoman=false # true means using ottoman testnet if genesis file uses iBFT #mining miner-coinbase="0x0000000000000000000000000000000000000002" \ No newline at end of file From 802279f84bbee74446e5fbf0dc35ca232effa599 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 22 Jan 2019 12:55:12 +1000 Subject: [PATCH 09/31] typos --- pantheon/src/test/resources/everything_config.toml | 2 +- pantheon/src/test/resources/partial_config.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pantheon/src/test/resources/everything_config.toml b/pantheon/src/test/resources/everything_config.toml index 3cbcb2ec03..5ea2916519 100644 --- a/pantheon/src/test/resources/everything_config.toml +++ b/pantheon/src/test/resources/everything_config.toml @@ -2,7 +2,7 @@ # The odds are you are reading this because you added a CLI and didn't add it # here and a test broke. To fix the test add your CLI to this file. # -# Please use a plausable value, Pantheon has to at least be able to parse it. +# Please use a plausible value, Pantheon has to at least be able to parse it. # If it is a multi-valued CLI make it a TOML array. # If it is a number or boolean make it a number or boolean # All other config options are strings, and must be quoted. diff --git a/pantheon/src/test/resources/partial_config.toml b/pantheon/src/test/resources/partial_config.toml index 3de87f7931..f2d8eaa061 100644 --- a/pantheon/src/test/resources/partial_config.toml +++ b/pantheon/src/test/resources/partial_config.toml @@ -1,4 +1,4 @@ -# this is a valid prtial TOML config file +# this is a valid partial TOML config file #mining miner-coinbase="0x0000000000000000000000000000000000000002" \ No newline at end of file From 6c8dcdb0eb9e338dabf65d6fa395c365c5229c1f Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 22 Jan 2019 13:36:00 +1000 Subject: [PATCH 10/31] typo --- .../pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java index e805dd6368..784a7fdb71 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java @@ -41,7 +41,7 @@ class ConfigOptionSearchAndRunHandler extends AbstractParseResultHandler Date: Tue, 22 Jan 2019 16:20:14 +1000 Subject: [PATCH 11/31] create permissioning config from toml file --- .../cli/ConfigOptionSearchAndRunHandler.java | 14 +++++++++ .../pegasys/pantheon/cli/PantheonCommand.java | 30 +++++++++++++++++++ .../pantheon/cli/PantheonCommandTest.java | 23 ++++++++++++-- .../test/resources/permissioning_config.toml | 4 +++ 4 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 pantheon/src/test/resources/permissioning_config.toml diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java index 784a7fdb71..f35cdc4c93 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java @@ -66,6 +66,20 @@ protected List handle(final ParseResult parseResult) throws ExecutionExc new TomlConfigFileDefaultProvider(commandLine, configFile); commandLine.setDefaultValueProvider(tomlConfigFileDefaultProvider); } + + if (parseResult.hasMatchedOption("--permissioning-config")) { + // TODO OR the --permissioning-config option is in the regular TOML + // OR the default file exists && --permissioning-enabled + // TODO we need a --permissioning-enabled flag + + // parse the permissioning toml file using same logic as above + + // or create directly the permissioningConfiguration object + // but how to pass it to the Runner? + } + commandLine.parseWithHandlers( + resultHandler, exceptionHandler, parseResult.originalArgs().toArray(new String[0])); + return new ArrayList<>(); } commandLine.parseWithHandlers( resultHandler, exceptionHandler, parseResult.originalArgs().toArray(new String[0])); diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 79abeb231a..a90f89a6d4 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -72,6 +72,8 @@ import com.google.common.net.HostAndPort; import com.google.common.net.HostSpecifier; import io.vertx.core.Vertx; +import net.consensys.cava.toml.TomlArray; +import net.consensys.cava.toml.TomlParseResult; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; import picocli.CommandLine; @@ -740,6 +742,34 @@ private PrivacyParameters orionConfiguration() { return privacyParameters; } + public static PermissioningConfiguration permissioningConfiguration( + final TomlParseResult permToml) throws IOException { + + TomlArray accountWhitelistTomlArray = permToml.getArray("accounts-whitelist"); + TomlArray nodeWhitelistTomlArray = permToml.getArray("nodes-whitelist"); + + final PermissioningConfiguration permissioningConfiguration = + PermissioningConfiguration.createDefault(); + List accountsWhitelistToml = + accountWhitelistTomlArray + .toList() + .stream() + .map(Object::toString) + .collect(Collectors.toList()); + permissioningConfiguration.setAccountWhitelist(accountsWhitelistToml); + + List nodesWhitelistToml = + nodeWhitelistTomlArray + .toList() + .stream() + .map(Object::toString) + .map(URI::create) + .collect(Collectors.toList()); + permissioningConfiguration.setNodeWhitelist(nodesWhitelistToml); + + return permissioningConfiguration; + } + private SynchronizerConfiguration buildSyncConfig() { synchronizerConfigurationBuilder.syncMode(syncMode); synchronizerConfigurationBuilder.maxTrailingPeers(maxTrailingPeers); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index a8a2fa3030..9381958138 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -34,6 +34,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.util.bytes.BytesValue; @@ -310,10 +311,10 @@ public void overrideDefaultValuesIfKeyIsPresentInConfigFile() throws IOException } @Test - public void tomlThatConfiguresEverything() throws IOException { + public void tomlThatConfiguresEverythingExceptPermissioning() throws IOException { assumeTrue(isFullInstantiation()); - // Load a TOML that configures literally everything + // Load a TOML that configures literally everything (except permissioning) final URL configFile = Resources.getResource("everything_config.toml"); final Path toml = Files.createTempFile("toml", ""); Files.write(toml, Resources.toByteArray(configFile)); @@ -350,6 +351,24 @@ public void tomlThatConfiguresEverything() throws IOException { assertThat(options.stream().map(CommandLine.Model.OptionSpec::longestName)).isEmpty(); } + @Test + public void permissioningToml() throws IOException { + final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; + + final URL configFile = Resources.getResource("permissioning_config.toml"); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + PermissioningConfiguration permissioningConfiguration = + PantheonCommand.permissioningConfiguration(tomlResult); + + assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getAccountWhitelist()) + .containsExactly("0x0000000000000000000000000000000000000009"); + assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); + } + @Test public void noOverrideDefaultValuesIfKeyIsNotPresentInConfigFile() throws IOException { assumeTrue(isFullInstantiation()); diff --git a/pantheon/src/test/resources/permissioning_config.toml b/pantheon/src/test/resources/permissioning_config.toml new file mode 100644 index 0000000000..786b14e0e8 --- /dev/null +++ b/pantheon/src/test/resources/permissioning_config.toml @@ -0,0 +1,4 @@ +# Permissioning TOML file + +accounts-whitelist=["0x0000000000000000000000000000000000000009"] +nodes-whitelist=["enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.9:4567"] From 3e77b527ac3e179fb8da81ccddc21dad182a16be Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 23 Jan 2019 09:52:07 +1000 Subject: [PATCH 12/31] don't throw --- .../main/java/tech/pegasys/pantheon/cli/PantheonCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index a90f89a6d4..972feec4c4 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -743,7 +743,7 @@ private PrivacyParameters orionConfiguration() { } public static PermissioningConfiguration permissioningConfiguration( - final TomlParseResult permToml) throws IOException { + final TomlParseResult permToml) { TomlArray accountWhitelistTomlArray = permToml.getArray("accounts-whitelist"); TomlArray nodeWhitelistTomlArray = permToml.getArray("nodes-whitelist"); From c89768f6fa41fd047ca872d71414709fc3dc2a01 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 23 Jan 2019 15:44:48 +1000 Subject: [PATCH 13/31] parse TOML file for permissioning config --- .../pegasys/pantheon/cli/PantheonCommand.java | 7 ++++++- .../custom/EnodeToURIPropertyConverter.java | 10 ++++++--- .../pantheon/cli/PantheonCommandTest.java | 21 +++++++++++++++++++ .../permissioning_config_invalid_enode.toml | 4 ++++ 4 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 pantheon/src/test/resources/permissioning_config_invalid_enode.toml diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 972feec4c4..15b29505db 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -598,6 +598,11 @@ public void parse( @Override public void run() { + // TODO permToml = TomlConfigFileDefaultProvider.loadConfigurationFromFile + // but we don't have visibility to the TomlConfigFileDefaultProvider. + // see ConfigOptionSearchAndRunHandler + // TODO permissioningConfiguration(permToml) + // set log level per CLI flags if (logLevel != null) { System.out.println("Setting logging level to " + logLevel.name()); @@ -763,7 +768,7 @@ public static PermissioningConfiguration permissioningConfiguration( .toList() .stream() .map(Object::toString) - .map(URI::create) + .map(EnodeToURIPropertyConverter::convertToURI) .collect(Collectors.toList()); permissioningConfiguration.setNodeWhitelist(nodesWhitelistToml); diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/custom/EnodeToURIPropertyConverter.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/custom/EnodeToURIPropertyConverter.java index 83e0f09f4f..44e647e521 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/custom/EnodeToURIPropertyConverter.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/custom/EnodeToURIPropertyConverter.java @@ -38,6 +38,10 @@ public class EnodeToURIPropertyConverter implements ITypeConverter { @Override public URI convert(final String value) throws IllegalArgumentException { + return convertToURI(value); + } + + public static URI convertToURI(final String value) throws IllegalArgumentException { checkArgument( value != null && !value.isEmpty(), "Can't convert null/empty string to EnodeURLProperty."); @@ -85,7 +89,7 @@ public URI convert(final String value) throws IllegalArgumentException { } } - private String getAndValidateNodeId(final Matcher matcher) { + 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"); @@ -96,7 +100,7 @@ private String getAndValidateNodeId(final Matcher matcher) { return nodeId; } - private Integer getAndValidatePort(final Matcher matcher, final String portName) { + private static Integer getAndValidatePort(final Matcher matcher, final String portName) { int port = Integer.valueOf(matcher.group(portName)); checkArgument( NetworkUtility.isValidPort(port), @@ -104,7 +108,7 @@ private Integer getAndValidatePort(final Matcher matcher, final String portName) return port; } - private boolean containsDiscoveryPort(final String value) { + private static boolean containsDiscoveryPort(final String value) { return value.contains("discport"); } } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index 9381958138..097cf5498e 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -15,6 +15,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.eq; @@ -353,6 +354,7 @@ public void tomlThatConfiguresEverythingExceptPermissioning() throws IOException @Test public void permissioningToml() throws IOException { + final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; final URL configFile = Resources.getResource("permissioning_config.toml"); @@ -369,6 +371,25 @@ public void permissioningToml() throws IOException { assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); } + @Test + public void permissioningTomlWithInvalidEnode() throws IOException { + + final URL configFile = Resources.getResource("permissioning_config_invalid_enode.toml"); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + try { + PantheonCommand.permissioningConfiguration(tomlResult); + fail("Enode URL contains an invalid node ID"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).startsWith("Enode URL contains an invalid node ID"); + } + // assertThat(commandErrorOutput.toString()).startsWith("Enode URL contains an invalid node + // ID"); + assertThat(commandOutput.toString()).isEmpty(); + } + @Test public void noOverrideDefaultValuesIfKeyIsNotPresentInConfigFile() throws IOException { assumeTrue(isFullInstantiation()); diff --git a/pantheon/src/test/resources/permissioning_config_invalid_enode.toml b/pantheon/src/test/resources/permissioning_config_invalid_enode.toml new file mode 100644 index 0000000000..89ad7ada23 --- /dev/null +++ b/pantheon/src/test/resources/permissioning_config_invalid_enode.toml @@ -0,0 +1,4 @@ +# Permissioning TOML file + +accounts-whitelist=["0x0000000000000000000000000000000000000009"] +nodes-whitelist=["enode://bob@192.168.0.9:4567"] From 3aeb71914669a514233d87b988beffaa29ab54e5 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 24 Jan 2019 14:37:30 +1000 Subject: [PATCH 14/31] added a separate non-CLI toml parser --- .../tech/pegasys/pantheon/RunnerBuilder.java | 73 +++++++++++++++++++ .../pantheon/TomlConfigFileParser.java | 48 ++++++++++++ .../cli/ConfigOptionSearchAndRunHandler.java | 11 --- .../pegasys/pantheon/cli/PantheonCommand.java | 35 --------- .../tech/pegasys/pantheon/RunnerTest.java | 42 +++++++++++ .../pantheon/cli/PantheonCommandTest.java | 40 ---------- 6 files changed, 163 insertions(+), 86 deletions(-) create mode 100644 pantheon/src/main/java/tech/pegasys/pantheon/TomlConfigFileParser.java diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index 21c729017d..523684d670 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -12,6 +12,9 @@ */ package tech.pegasys.pantheon; +import static java.nio.charset.StandardCharsets.UTF_8; + +import tech.pegasys.pantheon.cli.custom.EnodeToURIPropertyConverter; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.ProtocolContext; @@ -59,6 +62,9 @@ import tech.pegasys.pantheon.metrics.prometheus.MetricsService; import tech.pegasys.pantheon.util.bytes.BytesValue; +import java.io.File; +import java.io.IOException; +import java.net.URI; import java.nio.file.Path; import java.util.Collection; import java.util.List; @@ -68,10 +74,16 @@ import java.util.stream.Collectors; import com.google.common.base.Preconditions; +import com.google.common.io.Resources; import io.vertx.core.Vertx; +import net.consensys.cava.toml.TomlArray; +import net.consensys.cava.toml.TomlParseResult; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class RunnerBuilder { + private static final Logger LOG = LogManager.getLogger(); private Vertx vertx; private PantheonController pantheonController; private boolean p2pEnabled = true; @@ -138,12 +150,73 @@ public RunnerBuilder webSocketConfiguration(final WebSocketConfiguration webSock return this; } + // this one uses the CLI which will be deleted public RunnerBuilder permissioningConfiguration( final PermissioningConfiguration permissioningConfiguration) { this.permissioningConfiguration = permissioningConfiguration; return this; } + public RunnerBuilder permissioningConfigurationFromToml() { + String permToml = permissioningConfig(); + this.permissioningConfiguration = + permissioningConfig(TomlConfigFileParser.loadConfiguration(permToml)); + return this; + } + + // for testing + public static PermissioningConfiguration p() { + String permToml = permissioningConfig(); + return permissioningConfig(TomlConfigFileParser.loadConfiguration(permToml)); + } + + public static PermissioningConfiguration permissioningConfig(final TomlParseResult permToml) { + + TomlArray accountWhitelistTomlArray = permToml.getArray("accounts-whitelist"); + TomlArray nodeWhitelistTomlArray = permToml.getArray("nodes-whitelist"); + + final PermissioningConfiguration permissioningConfiguration = + PermissioningConfiguration.createDefault(); + List accountsWhitelistToml = + accountWhitelistTomlArray + .toList() + .stream() + .map(Object::toString) + .collect(Collectors.toList()); + permissioningConfiguration.setAccountWhitelist(accountsWhitelistToml); + + List nodesWhitelistToml = + nodeWhitelistTomlArray + .toList() + .stream() + .map(Object::toString) + .map(EnodeToURIPropertyConverter::convertToURI) + .collect(Collectors.toList()); + permissioningConfiguration.setNodeWhitelist(nodesWhitelistToml); + + return permissioningConfiguration; + } + + private static String permissioningConfig() { + try { + return Resources.toString(permissioningConfigFile().toURI().toURL(), UTF_8); + } catch (final IOException e) { + LOG.error("Unable to load permissioning config file %s.", permissioningConfigFile(), e); + return null; + } + } + + private static File permissioningConfigFile() { + String PERMISSIONING_CONFIG_LOCATION = "/Documents/workspace/permissioning_config.toml"; + + final File permissioningConfigFile = new File(PERMISSIONING_CONFIG_LOCATION); + if (permissioningConfigFile.exists()) { + return permissioningConfigFile; + } else { + return null; + } + } + public RunnerBuilder dataDir(final Path dataDir) { this.dataDir = dataDir; return this; diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/TomlConfigFileParser.java b/pantheon/src/main/java/tech/pegasys/pantheon/TomlConfigFileParser.java new file mode 100644 index 0000000000..ea6e4f8d9f --- /dev/null +++ b/pantheon/src/main/java/tech/pegasys/pantheon/TomlConfigFileParser.java @@ -0,0 +1,48 @@ +/* + * 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; + +import java.util.stream.Collectors; + +import net.consensys.cava.toml.Toml; +import net.consensys.cava.toml.TomlParseError; +import net.consensys.cava.toml.TomlParseResult; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class TomlConfigFileParser { + + private static final Logger LOG = LogManager.getLogger(); + + private static TomlParseResult checkConfigurationValidity( + final TomlParseResult result, final String toml) { + if (result == null || result.isEmpty()) { + LOG.error("Unable to read TOML configuration file %s", toml); + } + return result; + } + + public static TomlParseResult loadConfiguration(final String toml) { + TomlParseResult result; + result = Toml.parse(toml); + + if (result.hasErrors()) { + final String errors = + result.errors().stream().map(TomlParseError::toString).collect(Collectors.joining("%n")); + ; + LOG.error("Invalid TOML configuration : %s", errors); + } + + return checkConfigurationValidity(result, toml); + } +} diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java index f35cdc4c93..796e1b794c 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java @@ -66,17 +66,6 @@ protected List handle(final ParseResult parseResult) throws ExecutionExc new TomlConfigFileDefaultProvider(commandLine, configFile); commandLine.setDefaultValueProvider(tomlConfigFileDefaultProvider); } - - if (parseResult.hasMatchedOption("--permissioning-config")) { - // TODO OR the --permissioning-config option is in the regular TOML - // OR the default file exists && --permissioning-enabled - // TODO we need a --permissioning-enabled flag - - // parse the permissioning toml file using same logic as above - - // or create directly the permissioningConfiguration object - // but how to pass it to the Runner? - } commandLine.parseWithHandlers( resultHandler, exceptionHandler, parseResult.originalArgs().toArray(new String[0])); return new ArrayList<>(); diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 15b29505db..79abeb231a 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -72,8 +72,6 @@ import com.google.common.net.HostAndPort; import com.google.common.net.HostSpecifier; import io.vertx.core.Vertx; -import net.consensys.cava.toml.TomlArray; -import net.consensys.cava.toml.TomlParseResult; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; import picocli.CommandLine; @@ -598,11 +596,6 @@ public void parse( @Override public void run() { - // TODO permToml = TomlConfigFileDefaultProvider.loadConfigurationFromFile - // but we don't have visibility to the TomlConfigFileDefaultProvider. - // see ConfigOptionSearchAndRunHandler - // TODO permissioningConfiguration(permToml) - // set log level per CLI flags if (logLevel != null) { System.out.println("Setting logging level to " + logLevel.name()); @@ -747,34 +740,6 @@ private PrivacyParameters orionConfiguration() { return privacyParameters; } - public static PermissioningConfiguration permissioningConfiguration( - final TomlParseResult permToml) { - - TomlArray accountWhitelistTomlArray = permToml.getArray("accounts-whitelist"); - TomlArray nodeWhitelistTomlArray = permToml.getArray("nodes-whitelist"); - - final PermissioningConfiguration permissioningConfiguration = - PermissioningConfiguration.createDefault(); - List accountsWhitelistToml = - accountWhitelistTomlArray - .toList() - .stream() - .map(Object::toString) - .collect(Collectors.toList()); - permissioningConfiguration.setAccountWhitelist(accountsWhitelistToml); - - List nodesWhitelistToml = - nodeWhitelistTomlArray - .toList() - .stream() - .map(Object::toString) - .map(EnodeToURIPropertyConverter::convertToURI) - .collect(Collectors.toList()); - permissioningConfiguration.setNodeWhitelist(nodesWhitelistToml); - - return permissioningConfiguration; - } - private SynchronizerConfiguration buildSyncConfig() { synchronizerConfigurationBuilder.syncMode(syncMode); synchronizerConfigurationBuilder.maxTrailingPeers(maxTrailingPeers); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index a8dad2c2bf..3546f97c2e 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -15,6 +15,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static tech.pegasys.pantheon.controller.KeyPairUtil.loadKeyPair; +import com.google.common.io.Resources; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import net.consensys.cava.toml.Toml; +import net.consensys.cava.toml.TomlParseResult; import tech.pegasys.pantheon.config.GenesisConfigFile; import tech.pegasys.pantheon.controller.MainnetPantheonController; import tech.pegasys.pantheon.controller.PantheonController; @@ -73,6 +79,9 @@ /** Tests for {@link Runner}. */ public final class RunnerTest { + private final String VALID_NODE_ID = + "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"; + @Rule public final TemporaryFolder temp = new TemporaryFolder(); @Test @@ -86,6 +95,39 @@ public void fastSyncFromGenesis() throws Exception { syncFromGenesis(SyncMode.FAST); } + @Test + public void permissioningTomlFromResource() throws IOException { + + final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; + + final URL configFile = Resources.getResource("permissioning_config.toml"); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + PermissioningConfiguration permissioningConfiguration = + RunnerBuilder.permissioningConfig(tomlResult); + + assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getAccountWhitelist()) + .containsExactly("0x0000000000000000000000000000000000000009"); + assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); + } + + @Test + public void permissioningTomlFromDefaultFileLocation() throws IOException { + + final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; + + PermissioningConfiguration permissioningConfiguration = + RunnerBuilder.p(); + + assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getAccountWhitelist()) + .containsExactly("0x0000000000000000000000000000000000000009"); + assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); + } + private void syncFromGenesis(final SyncMode mode) throws Exception { final Path dbAhead = temp.newFolder().toPath(); final int blockCount = 500; diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index 097cf5498e..7ae473011d 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -15,7 +15,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.eq; @@ -35,7 +34,6 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; -import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.util.bytes.BytesValue; @@ -352,44 +350,6 @@ public void tomlThatConfiguresEverythingExceptPermissioning() throws IOException assertThat(options.stream().map(CommandLine.Model.OptionSpec::longestName)).isEmpty(); } - @Test - public void permissioningToml() throws IOException { - - final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; - - final URL configFile = Resources.getResource("permissioning_config.toml"); - final Path toml = Files.createTempFile("toml", ""); - Files.write(toml, Resources.toByteArray(configFile)); - final TomlParseResult tomlResult = Toml.parse(toml); - - PermissioningConfiguration permissioningConfiguration = - PantheonCommand.permissioningConfiguration(tomlResult); - - assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); - assertThat(permissioningConfiguration.getAccountWhitelist()) - .containsExactly("0x0000000000000000000000000000000000000009"); - assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); - } - - @Test - public void permissioningTomlWithInvalidEnode() throws IOException { - - final URL configFile = Resources.getResource("permissioning_config_invalid_enode.toml"); - final Path toml = Files.createTempFile("toml", ""); - Files.write(toml, Resources.toByteArray(configFile)); - final TomlParseResult tomlResult = Toml.parse(toml); - - try { - PantheonCommand.permissioningConfiguration(tomlResult); - fail("Enode URL contains an invalid node ID"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).startsWith("Enode URL contains an invalid node ID"); - } - // assertThat(commandErrorOutput.toString()).startsWith("Enode URL contains an invalid node - // ID"); - assertThat(commandOutput.toString()).isEmpty(); - } - @Test public void noOverrideDefaultValuesIfKeyIsNotPresentInConfigFile() throws IOException { assumeTrue(isFullInstantiation()); From 3aad74e1b7b3eac4aecb9da7f18da9b92bcd92e8 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 24 Jan 2019 15:44:18 +1000 Subject: [PATCH 15/31] formatting --- .../java/tech/pegasys/pantheon/RunnerTest.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index 3546f97c2e..5a5fa3e902 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -15,12 +15,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static tech.pegasys.pantheon.controller.KeyPairUtil.loadKeyPair; -import com.google.common.io.Resources; -import java.net.URI; -import java.net.URL; -import java.nio.file.Files; -import net.consensys.cava.toml.Toml; -import net.consensys.cava.toml.TomlParseResult; import tech.pegasys.pantheon.config.GenesisConfigFile; import tech.pegasys.pantheon.controller.MainnetPantheonController; import tech.pegasys.pantheon.controller.PantheonController; @@ -50,6 +44,9 @@ import java.io.IOException; import java.net.InetAddress; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -57,12 +54,15 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import com.google.common.io.Resources; import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClient; import io.vertx.core.json.Json; import io.vertx.core.json.JsonObject; +import net.consensys.cava.toml.Toml; +import net.consensys.cava.toml.TomlParseResult; import okhttp3.Call; import okhttp3.MediaType; import okhttp3.OkHttpClient; @@ -119,8 +119,7 @@ public void permissioningTomlFromDefaultFileLocation() throws IOException { final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; - PermissioningConfiguration permissioningConfiguration = - RunnerBuilder.p(); + PermissioningConfiguration permissioningConfiguration = RunnerBuilder.p(); assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); assertThat(permissioningConfiguration.getAccountWhitelist()) From 5fafd918ff31be9a2df27e04300897938b8461fe Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 24 Jan 2019 17:41:39 +1000 Subject: [PATCH 16/31] added tests --- .../tech/pegasys/pantheon/RunnerBuilder.java | 49 ++++++++++-------- .../tech/pegasys/pantheon/RunnerTest.java | 51 +++++++++++++++++-- ...sioning_config_account_whitelist_only.toml | 3 ++ ...missioning_config_node_whitelist_only.toml | 3 ++ 4 files changed, 80 insertions(+), 26 deletions(-) create mode 100644 pantheon/src/test/resources/permissioning_config_account_whitelist_only.toml create mode 100644 pantheon/src/test/resources/permissioning_config_node_whitelist_only.toml diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index 523684d670..c570662d96 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -159,8 +159,12 @@ public RunnerBuilder permissioningConfiguration( public RunnerBuilder permissioningConfigurationFromToml() { String permToml = permissioningConfig(); - this.permissioningConfiguration = - permissioningConfig(TomlConfigFileParser.loadConfiguration(permToml)); + if (permToml != null) { + this.permissioningConfiguration = + permissioningConfig(TomlConfigFileParser.loadConfiguration(permToml)); + } else { + this.permissioningConfiguration = PermissioningConfiguration.createDefault(); + } return this; } @@ -177,31 +181,33 @@ public static PermissioningConfiguration permissioningConfig(final TomlParseResu final PermissioningConfiguration permissioningConfiguration = PermissioningConfiguration.createDefault(); - List accountsWhitelistToml = - accountWhitelistTomlArray - .toList() - .stream() - .map(Object::toString) - .collect(Collectors.toList()); - permissioningConfiguration.setAccountWhitelist(accountsWhitelistToml); - - List nodesWhitelistToml = - nodeWhitelistTomlArray - .toList() - .stream() - .map(Object::toString) - .map(EnodeToURIPropertyConverter::convertToURI) - .collect(Collectors.toList()); - permissioningConfiguration.setNodeWhitelist(nodesWhitelistToml); - + if (accountWhitelistTomlArray != null) { + List accountsWhitelistToml = + accountWhitelistTomlArray + .toList() + .stream() + .map(Object::toString) + .collect(Collectors.toList()); + permissioningConfiguration.setAccountWhitelist(accountsWhitelistToml); + } + if (nodeWhitelistTomlArray != null) { + List nodesWhitelistToml = + nodeWhitelistTomlArray + .toList() + .stream() + .map(Object::toString) + .map(EnodeToURIPropertyConverter::convertToURI) + .collect(Collectors.toList()); + permissioningConfiguration.setNodeWhitelist(nodesWhitelistToml); + } return permissioningConfiguration; } private static String permissioningConfig() { try { return Resources.toString(permissioningConfigFile().toURI().toURL(), UTF_8); - } catch (final IOException e) { - LOG.error("Unable to load permissioning config file %s.", permissioningConfigFile(), e); + } catch (final Exception e) { + LOG.error("Unable to load permissioning config {}.", permissioningConfigFile(), e); return null; } } @@ -213,6 +219,7 @@ private static File permissioningConfigFile() { if (permissioningConfigFile.exists()) { return permissioningConfigFile; } else { + LOG.error("Unable to load permissioning config file from location {}.", PERMISSIONING_CONFIG_LOCATION); return null; } } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index 5a5fa3e902..26258a6bd6 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -13,6 +13,7 @@ package tech.pegasys.pantheon; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.fail; import static tech.pegasys.pantheon.controller.KeyPairUtil.loadKeyPair; import tech.pegasys.pantheon.config.GenesisConfigFile; @@ -111,20 +112,60 @@ public void permissioningTomlFromResource() throws IOException { assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); assertThat(permissioningConfiguration.getAccountWhitelist()) .containsExactly("0x0000000000000000000000000000000000000009"); + assertThat(permissioningConfiguration.isNodeWhitelistSet()).isTrue(); assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); } @Test - public void permissioningTomlFromDefaultFileLocation() throws IOException { + public void permissioningTomlFromResourceWithOnlyNodeWhitelistSet() throws IOException { final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; - PermissioningConfiguration permissioningConfiguration = RunnerBuilder.p(); + final URL configFile = Resources.getResource("permissioning_config_node_whitelist_only.toml"); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + PermissioningConfiguration permissioningConfiguration = + RunnerBuilder.permissioningConfig(tomlResult); + + assertThat(permissioningConfiguration.isAccountWhitelistSet()).isFalse(); + assertThat(permissioningConfiguration.isNodeWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); + } + + @Test + public void permissioningTomlFromResourceWithOnlyAccountWhitelistSet() throws IOException { + + + final URL configFile = Resources.getResource("permissioning_config_account_whitelist_only.toml"); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + PermissioningConfiguration permissioningConfiguration = + RunnerBuilder.permissioningConfig(tomlResult); + + assertThat(permissioningConfiguration.isNodeWhitelistSet()).isFalse(); assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); assertThat(permissioningConfiguration.getAccountWhitelist()) .containsExactly("0x0000000000000000000000000000000000000009"); - assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); + } + + @Test + public void permissioningTomlWithInvalidEnode() throws IOException { + + final URL configFile = Resources.getResource("permissioning_config_invalid_enode.toml"); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + try { + RunnerBuilder.permissioningConfig(tomlResult); + fail("Expecting IllegalArgumentException: Enode URL contains an invalid node ID"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).startsWith("Enode URL contains an invalid node ID"); + } } private void syncFromGenesis(final SyncMode mode) throws Exception { @@ -169,7 +210,7 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { final JsonRpcConfiguration aheadJsonRpcConfiguration = jsonRpcConfiguration(); final WebSocketConfiguration aheadWebSocketConfiguration = wsRpcConfiguration(); final MetricsConfiguration aheadMetricsConfiguration = metricsConfiguration(); - final PermissioningConfiguration aheadPermissioningConfiguration = permissioningConfiguration(); + final RunnerBuilder runnerBuilder = new RunnerBuilder() .vertx(Vertx.vertx()) @@ -188,7 +229,7 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { .webSocketConfiguration(aheadWebSocketConfiguration) .metricsConfiguration(aheadMetricsConfiguration) .dataDir(dbAhead) - .permissioningConfiguration(aheadPermissioningConfiguration) + .permissioningConfigurationFromToml() .build(); try { diff --git a/pantheon/src/test/resources/permissioning_config_account_whitelist_only.toml b/pantheon/src/test/resources/permissioning_config_account_whitelist_only.toml new file mode 100644 index 0000000000..c7333f2d77 --- /dev/null +++ b/pantheon/src/test/resources/permissioning_config_account_whitelist_only.toml @@ -0,0 +1,3 @@ +# Permissioning TOML file (account whitelist only) + +accounts-whitelist=["0x0000000000000000000000000000000000000009"] diff --git a/pantheon/src/test/resources/permissioning_config_node_whitelist_only.toml b/pantheon/src/test/resources/permissioning_config_node_whitelist_only.toml new file mode 100644 index 0000000000..ffd69e6437 --- /dev/null +++ b/pantheon/src/test/resources/permissioning_config_node_whitelist_only.toml @@ -0,0 +1,3 @@ +# Permissioning TOML file (node whitelist only) + +nodes-whitelist=["enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.9:4567"] From 6944ec17d52a1061902775f46d3ee0eea4818ba8 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 24 Jan 2019 17:54:39 +1000 Subject: [PATCH 17/31] formatting --- .../src/main/java/tech/pegasys/pantheon/RunnerBuilder.java | 5 +++-- pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index c570662d96..a2591bafbd 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -63,7 +63,6 @@ import tech.pegasys.pantheon.util.bytes.BytesValue; import java.io.File; -import java.io.IOException; import java.net.URI; import java.nio.file.Path; import java.util.Collection; @@ -219,7 +218,9 @@ private static File permissioningConfigFile() { if (permissioningConfigFile.exists()) { return permissioningConfigFile; } else { - LOG.error("Unable to load permissioning config file from location {}.", PERMISSIONING_CONFIG_LOCATION); + LOG.error( + "Unable to load permissioning config file from location {}.", + PERMISSIONING_CONFIG_LOCATION); return null; } } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index 26258a6bd6..05a1e1bfb4 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -137,8 +137,8 @@ public void permissioningTomlFromResourceWithOnlyNodeWhitelistSet() throws IOExc @Test public void permissioningTomlFromResourceWithOnlyAccountWhitelistSet() throws IOException { - - final URL configFile = Resources.getResource("permissioning_config_account_whitelist_only.toml"); + final URL configFile = + Resources.getResource("permissioning_config_account_whitelist_only.toml"); final Path toml = Files.createTempFile("toml", ""); Files.write(toml, Resources.toByteArray(configFile)); final TomlParseResult tomlResult = Toml.parse(toml); From fcfca804b16b8f6597d50c8df39e233a3f04fc55 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 24 Jan 2019 20:56:36 +1000 Subject: [PATCH 18/31] avoid 2 x log messages --- .../src/main/java/tech/pegasys/pantheon/RunnerBuilder.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index a2591bafbd..46098c52aa 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -203,10 +203,12 @@ public static PermissioningConfiguration permissioningConfig(final TomlParseResu } private static String permissioningConfig() { + File file = null; try { - return Resources.toString(permissioningConfigFile().toURI().toURL(), UTF_8); + file = permissioningConfigFile(); + return Resources.toString(file.toURI().toURL(), UTF_8); } catch (final Exception e) { - LOG.error("Unable to load permissioning config {}.", permissioningConfigFile(), e); + LOG.error("Unable to load permissioning config {}.", file, e); return null; } } From 8a6ee0f6de36f263de0f3c9b66c6653b28e17a38 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 24 Jan 2019 20:57:22 +1000 Subject: [PATCH 19/31] ignore tests --- .../java/tech/pegasys/pantheon/cli/PantheonCommand.java | 2 +- .../tech/pegasys/pantheon/cli/CommandTestAbstract.java | 2 +- .../tech/pegasys/pantheon/cli/PantheonCommandTest.java | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 79abeb231a..024f4dccbc 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -778,7 +778,7 @@ private void synchronize( .bannedNodeIds(bannedNodeIds) .metricsSystem(metricsSystem) .metricsConfiguration(metricsConfiguration) - .permissioningConfiguration(permissioningConfiguration) + .permissioningConfigurationFromToml() .build(); addShutdownHook(runner); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java index de45e2171e..cb8a1d60c3 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java @@ -117,7 +117,7 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.p2pEnabled(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.jsonRpcConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.webSocketConfiguration(any())).thenReturn(mockRunnerBuilder); - when(mockRunnerBuilder.permissioningConfiguration(any())).thenReturn(mockRunnerBuilder); + when(mockRunnerBuilder.permissioningConfigurationFromToml()).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.dataDir(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.bannedNodeIds(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.metricsSystem(any())).thenReturn(mockRunnerBuilder); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index 7ae473011d..ff8627a86e 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -632,6 +632,7 @@ public void callingWithRefreshDelayWithNegativeValueMustError() { } @Test + @Ignore public void callingWithInvalidNodesWhitelistMustDisplayErrorAndUsage() { parseCommand("--nodes-whitelist", "invalid_enode_url"); assertThat(commandOutput.toString()).isEmpty(); @@ -645,6 +646,7 @@ public void callingWithInvalidNodesWhitelistMustDisplayErrorAndUsage() { } @Test + @Ignore public void nodesWhitelistOptionMustBeUsed() { parseCommand( "--nodes-whitelist", @@ -672,6 +674,7 @@ public void nodesWhitelistOptionMustBeUsed() { } @Test + @Ignore public void callingWithAccountsWhitelistOptionButNoValueMustNotError() { parseCommand("--accounts-whitelist"); @@ -688,6 +691,7 @@ public void callingWithAccountsWhitelistOptionButNoValueMustNotError() { } @Test + @Ignore public void accountsWhitelistOptionMustBeUsed() { final String[] accounts = {"1111111111111111", "2222222222222222", "ffffffffffffffff"}; parseCommand("--accounts-whitelist", String.join(",", accounts)); @@ -706,6 +710,7 @@ public void accountsWhitelistOptionMustBeUsed() { } @Test + @Ignore public void nodesWhitelistOptionMustIncludeBootnodes() { parseCommand( "--bootnodes", @@ -733,6 +738,7 @@ public void nodesWhitelistOptionMustIncludeBootnodes() { } @Test + @Ignore public void nodesWhitelistOptionWhichDoesNotIncludeBootnodesMustDisplayError() { final String bootNodeNotWhitelisted = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; parseCommand( @@ -749,6 +755,7 @@ public void nodesWhitelistOptionWhichDoesNotIncludeBootnodesMustDisplayError() { } @Test + @Ignore public void ropstenWithNodesWhitelistOptionWhichDoesIncludeRopstenBootnodesMustNotDisplayError() { parseCommand("--ropsten", "--nodes-whitelist", String.join(",", ropstenBootnodes)); @@ -772,6 +779,7 @@ public void ropstenWithNodesWhitelistOptionWhichDoesIncludeRopstenBootnodesMustN } @Test + @Ignore public void ropstenWithNodesWhitelistOptionWhichDoesNotIncludeRopstenBootnodesMustDisplayError() { parseCommand("--ropsten", "--nodes-whitelist", String.join(",", validENodeStrings)); @@ -783,6 +791,7 @@ public void ropstenWithNodesWhitelistOptionWhichDoesNotIncludeRopstenBootnodesMu } @Test + @Ignore public void nodesWhitelistWithEmptyListAndNonEmptyBootnodesMustDisplayError() { parseCommand("--bootnodes", String.join(",", validENodeStrings[0]), "--nodes-whitelist"); From d5b773960759a1d513d67d2f51ca7cf2da166f67 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Fri, 25 Jan 2019 13:51:16 +1000 Subject: [PATCH 20/31] added CLI options for separately enabling node and account permissioning --- .../PermissioningConfigurationBuilder.java | 103 +++++++++++++++++ .../tech/pegasys/pantheon/RunnerBuilder.java | 80 ------------- .../pantheon/cli/DefaultCommandValues.java | 1 + .../pegasys/pantheon/cli/PantheonCommand.java | 22 +++- ...PermissioningConfigurationBuilderTest.java | 107 ++++++++++++++++++ .../tech/pegasys/pantheon/RunnerTest.java | 85 +------------- .../pantheon/cli/CommandTestAbstract.java | 2 +- .../src/test/resources/everything_config.toml | 2 + 8 files changed, 234 insertions(+), 168 deletions(-) create mode 100644 pantheon/src/main/java/tech/pegasys/pantheon/PermissioningConfigurationBuilder.java create mode 100644 pantheon/src/test/java/tech/pegasys/pantheon/PermissioningConfigurationBuilderTest.java diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/PermissioningConfigurationBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/PermissioningConfigurationBuilder.java new file mode 100644 index 0000000000..d00c0f9a59 --- /dev/null +++ b/pantheon/src/main/java/tech/pegasys/pantheon/PermissioningConfigurationBuilder.java @@ -0,0 +1,103 @@ +/* + * 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; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import tech.pegasys.pantheon.cli.custom.EnodeToURIPropertyConverter; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; + +import java.io.File; +import java.net.URI; +import java.util.List; +import java.util.stream.Collectors; + +import com.google.common.io.Resources; +import net.consensys.cava.toml.TomlArray; +import net.consensys.cava.toml.TomlParseResult; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PermissioningConfigurationBuilder { + + private static final Logger LOG = LogManager.getLogger(); + + // will be used to reload the config from a file while node is running + public static PermissioningConfiguration permissioningConfigurationFromToml( + final String configFilePath, + final boolean permissionedNodeEnabled, + final boolean permissionedAccountEnabled) { + + String permToml = permissioningConfigTomlAsString(permissioningConfigFile(configFilePath)); + return permissioningConfiguration( + TomlConfigFileParser.loadConfiguration(permToml), + permissionedNodeEnabled, + permissionedAccountEnabled); + } + + public static PermissioningConfiguration permissioningConfiguration( + final TomlParseResult permToml, + final boolean permissionedNodeEnabled, + final boolean permissionedAccountEnabled) { + + TomlArray accountWhitelistTomlArray = permToml.getArray("accounts-whitelist"); + TomlArray nodeWhitelistTomlArray = permToml.getArray("nodes-whitelist"); + + final PermissioningConfiguration permissioningConfiguration = + PermissioningConfiguration.createDefault(); + if (permissionedAccountEnabled) { + if (accountWhitelistTomlArray != null) { + List accountsWhitelistToml = + accountWhitelistTomlArray + .toList() + .stream() + .map(Object::toString) + .collect(Collectors.toList()); + permissioningConfiguration.setAccountWhitelist(accountsWhitelistToml); + } + } + if (permissionedNodeEnabled) { + if (nodeWhitelistTomlArray != null) { + List nodesWhitelistToml = + nodeWhitelistTomlArray + .toList() + .stream() + .map(Object::toString) + .map(EnodeToURIPropertyConverter::convertToURI) + .collect(Collectors.toList()); + permissioningConfiguration.setNodeWhitelist(nodesWhitelistToml); + } + } + return permissioningConfiguration; + } + + private static String permissioningConfigTomlAsString(final File file) { + try { + return Resources.toString(file.toURI().toURL(), UTF_8); + } catch (final Exception e) { + LOG.error("Unable to load permissioning config {}.", file, e); + return null; + } + } + + private static File permissioningConfigFile(final String filename) { + + final File permissioningConfigFile = new File(filename); + if (permissioningConfigFile.exists()) { + return permissioningConfigFile; + } else { + LOG.error("Unable to load permissioning config file from location {}.", filename); + return null; + } + } +} diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index 46098c52aa..7e7b46138f 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -12,9 +12,6 @@ */ package tech.pegasys.pantheon; -import static java.nio.charset.StandardCharsets.UTF_8; - -import tech.pegasys.pantheon.cli.custom.EnodeToURIPropertyConverter; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.ProtocolContext; @@ -62,8 +59,6 @@ import tech.pegasys.pantheon.metrics.prometheus.MetricsService; import tech.pegasys.pantheon.util.bytes.BytesValue; -import java.io.File; -import java.net.URI; import java.nio.file.Path; import java.util.Collection; import java.util.List; @@ -73,10 +68,7 @@ import java.util.stream.Collectors; import com.google.common.base.Preconditions; -import com.google.common.io.Resources; import io.vertx.core.Vertx; -import net.consensys.cava.toml.TomlArray; -import net.consensys.cava.toml.TomlParseResult; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -149,84 +141,12 @@ public RunnerBuilder webSocketConfiguration(final WebSocketConfiguration webSock return this; } - // this one uses the CLI which will be deleted public RunnerBuilder permissioningConfiguration( final PermissioningConfiguration permissioningConfiguration) { this.permissioningConfiguration = permissioningConfiguration; return this; } - public RunnerBuilder permissioningConfigurationFromToml() { - String permToml = permissioningConfig(); - if (permToml != null) { - this.permissioningConfiguration = - permissioningConfig(TomlConfigFileParser.loadConfiguration(permToml)); - } else { - this.permissioningConfiguration = PermissioningConfiguration.createDefault(); - } - return this; - } - - // for testing - public static PermissioningConfiguration p() { - String permToml = permissioningConfig(); - return permissioningConfig(TomlConfigFileParser.loadConfiguration(permToml)); - } - - public static PermissioningConfiguration permissioningConfig(final TomlParseResult permToml) { - - TomlArray accountWhitelistTomlArray = permToml.getArray("accounts-whitelist"); - TomlArray nodeWhitelistTomlArray = permToml.getArray("nodes-whitelist"); - - final PermissioningConfiguration permissioningConfiguration = - PermissioningConfiguration.createDefault(); - if (accountWhitelistTomlArray != null) { - List accountsWhitelistToml = - accountWhitelistTomlArray - .toList() - .stream() - .map(Object::toString) - .collect(Collectors.toList()); - permissioningConfiguration.setAccountWhitelist(accountsWhitelistToml); - } - if (nodeWhitelistTomlArray != null) { - List nodesWhitelistToml = - nodeWhitelistTomlArray - .toList() - .stream() - .map(Object::toString) - .map(EnodeToURIPropertyConverter::convertToURI) - .collect(Collectors.toList()); - permissioningConfiguration.setNodeWhitelist(nodesWhitelistToml); - } - return permissioningConfiguration; - } - - private static String permissioningConfig() { - File file = null; - try { - file = permissioningConfigFile(); - return Resources.toString(file.toURI().toURL(), UTF_8); - } catch (final Exception e) { - LOG.error("Unable to load permissioning config {}.", file, e); - return null; - } - } - - private static File permissioningConfigFile() { - String PERMISSIONING_CONFIG_LOCATION = "/Documents/workspace/permissioning_config.toml"; - - final File permissioningConfigFile = new File(PERMISSIONING_CONFIG_LOCATION); - if (permissioningConfigFile.exists()) { - return permissioningConfigFile; - } else { - LOG.error( - "Unable to load permissioning config file from location {}.", - PERMISSIONING_CONFIG_LOCATION); - return null; - } - } - public RunnerBuilder dataDir(final Path dataDir) { this.dataDir = dataDir; return this; diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/DefaultCommandValues.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/DefaultCommandValues.java index 1917063cca..3ba323c39a 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/DefaultCommandValues.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/DefaultCommandValues.java @@ -42,6 +42,7 @@ interface DefaultCommandValues { long DEFAULT_MAX_REFRESH_DELAY = 3600000; long DEFAULT_MIN_REFRESH_DELAY = 1; String DOCKER_GENESIS_LOCATION = "/etc/pantheon/genesis.json"; + String PERMISSIONING_CONFIG_LOCATION = "/etc/pantheon/permissioned_config.toml"; String DOCKER_DATADIR_LOCATION = "/var/lib/pantheon"; String MANDATORY_HOST_FORMAT_HELP = ""; String MANDATORY_PORT_FORMAT_HELP = ""; diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index a6e877f331..5c1b41bd5b 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -24,6 +24,7 @@ import static tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PORT; import static tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration.createDefault; +import tech.pegasys.pantheon.PermissioningConfigurationBuilder; import tech.pegasys.pantheon.Runner; import tech.pegasys.pantheon.RunnerBuilder; import tech.pegasys.pantheon.cli.custom.CorsAllowedOriginsProperty; @@ -505,6 +506,18 @@ private Long configureRefreshDelay(final Long refreshDelay) { ) private final BytesValue extraData = DEFAULT_EXTRA_DATA; + @Option( + names = {"--permissions-nodes-enabled"}, + description = "Set if node level permissions should be enabled (default: ${DEFAULT-VALUE})" + ) + private final Boolean permissionsNodesEnabled = false; + + @Option( + names = {"--permissions-accounts-enabled"}, + description = "Set if account level permissions should be enabled (default: ${DEFAULT-VALUE})" + ) + private final Boolean permissionsAccountsEnabled = false; + // Permissioning: A list of whitelist nodes can be passed. @Option( names = {"--nodes-whitelist"}, @@ -725,9 +738,10 @@ MetricsConfiguration metricsConfiguration() { private PermissioningConfiguration permissioningConfiguration() { final PermissioningConfiguration permissioningConfiguration = - PermissioningConfiguration.createDefault(); - permissioningConfiguration.setNodeWhitelist(nodesWhitelist); - permissioningConfiguration.setAccountWhitelist(accountsWhitelist); + PermissioningConfigurationBuilder.permissioningConfigurationFromToml( + DefaultCommandValues.PERMISSIONING_CONFIG_LOCATION, + permissionsNodesEnabled, + permissionsAccountsEnabled); return permissioningConfiguration; } @@ -777,7 +791,7 @@ private void synchronize( .bannedNodeIds(bannedNodeIds) .metricsSystem(metricsSystem) .metricsConfiguration(metricsConfiguration) - .permissioningConfigurationFromToml() + .permissioningConfiguration(permissioningConfiguration) .build(); addShutdownHook(runner); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/PermissioningConfigurationBuilderTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/PermissioningConfigurationBuilderTest.java new file mode 100644 index 0000000000..993ece85f6 --- /dev/null +++ b/pantheon/src/test/java/tech/pegasys/pantheon/PermissioningConfigurationBuilderTest.java @@ -0,0 +1,107 @@ +/* + * 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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.fail; + +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; + +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; + +import com.google.common.io.Resources; +import net.consensys.cava.toml.Toml; +import net.consensys.cava.toml.TomlParseResult; +import org.junit.Test; + +public class PermissioningConfigurationBuilderTest { + + private final String VALID_NODE_ID = + "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"; + + @Test + public void permissioningTomlFromResource() throws IOException { + + final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; + + final URL configFile = Resources.getResource("permissioning_config.toml"); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + PermissioningConfiguration permissioningConfiguration = + PermissioningConfigurationBuilder.permissioningConfiguration(tomlResult, true, true); + + assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getAccountWhitelist()) + .containsExactly("0x0000000000000000000000000000000000000009"); + assertThat(permissioningConfiguration.isNodeWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); + } + + @Test + public void permissioningTomlFromResourceWithOnlyNodeWhitelistSet() throws IOException { + + final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; + + final URL configFile = Resources.getResource("permissioning_config_node_whitelist_only.toml"); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + PermissioningConfiguration permissioningConfiguration = + PermissioningConfigurationBuilder.permissioningConfiguration(tomlResult, true, false); + + assertThat(permissioningConfiguration.isAccountWhitelistSet()).isFalse(); + assertThat(permissioningConfiguration.isNodeWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); + } + + @Test + public void permissioningTomlFromResourceWithOnlyAccountWhitelistSet() throws IOException { + + final URL configFile = + Resources.getResource("permissioning_config_account_whitelist_only.toml"); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + PermissioningConfiguration permissioningConfiguration = + PermissioningConfigurationBuilder.permissioningConfiguration(tomlResult, false, true); + + assertThat(permissioningConfiguration.isNodeWhitelistSet()).isFalse(); + assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getAccountWhitelist()) + .containsExactly("0x0000000000000000000000000000000000000009"); + } + + @Test + public void permissioningTomlWithInvalidEnode() throws IOException { + + final URL configFile = Resources.getResource("permissioning_config_invalid_enode.toml"); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + try { + PermissioningConfigurationBuilder.permissioningConfiguration(tomlResult, true, true); + fail("Expecting IllegalArgumentException: Enode URL contains an invalid node ID"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage()).startsWith("Enode URL contains an invalid node ID"); + } + } +} diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index 05a1e1bfb4..1c1d541fdb 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -13,7 +13,6 @@ package tech.pegasys.pantheon; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.fail; import static tech.pegasys.pantheon.controller.KeyPairUtil.loadKeyPair; import tech.pegasys.pantheon.config.GenesisConfigFile; @@ -45,9 +44,6 @@ import java.io.IOException; import java.net.InetAddress; -import java.net.URI; -import java.net.URL; -import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.List; @@ -55,15 +51,12 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import com.google.common.io.Resources; import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClient; import io.vertx.core.json.Json; import io.vertx.core.json.JsonObject; -import net.consensys.cava.toml.Toml; -import net.consensys.cava.toml.TomlParseResult; import okhttp3.Call; import okhttp3.MediaType; import okhttp3.OkHttpClient; @@ -80,9 +73,6 @@ /** Tests for {@link Runner}. */ public final class RunnerTest { - private final String VALID_NODE_ID = - "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"; - @Rule public final TemporaryFolder temp = new TemporaryFolder(); @Test @@ -96,78 +86,6 @@ public void fastSyncFromGenesis() throws Exception { syncFromGenesis(SyncMode.FAST); } - @Test - public void permissioningTomlFromResource() throws IOException { - - final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; - - final URL configFile = Resources.getResource("permissioning_config.toml"); - final Path toml = Files.createTempFile("toml", ""); - Files.write(toml, Resources.toByteArray(configFile)); - final TomlParseResult tomlResult = Toml.parse(toml); - - PermissioningConfiguration permissioningConfiguration = - RunnerBuilder.permissioningConfig(tomlResult); - - assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); - assertThat(permissioningConfiguration.getAccountWhitelist()) - .containsExactly("0x0000000000000000000000000000000000000009"); - assertThat(permissioningConfiguration.isNodeWhitelistSet()).isTrue(); - assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); - } - - @Test - public void permissioningTomlFromResourceWithOnlyNodeWhitelistSet() throws IOException { - - final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; - - final URL configFile = Resources.getResource("permissioning_config_node_whitelist_only.toml"); - final Path toml = Files.createTempFile("toml", ""); - Files.write(toml, Resources.toByteArray(configFile)); - final TomlParseResult tomlResult = Toml.parse(toml); - - PermissioningConfiguration permissioningConfiguration = - RunnerBuilder.permissioningConfig(tomlResult); - - assertThat(permissioningConfiguration.isAccountWhitelistSet()).isFalse(); - assertThat(permissioningConfiguration.isNodeWhitelistSet()).isTrue(); - assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); - } - - @Test - public void permissioningTomlFromResourceWithOnlyAccountWhitelistSet() throws IOException { - - final URL configFile = - Resources.getResource("permissioning_config_account_whitelist_only.toml"); - final Path toml = Files.createTempFile("toml", ""); - Files.write(toml, Resources.toByteArray(configFile)); - final TomlParseResult tomlResult = Toml.parse(toml); - - PermissioningConfiguration permissioningConfiguration = - RunnerBuilder.permissioningConfig(tomlResult); - - assertThat(permissioningConfiguration.isNodeWhitelistSet()).isFalse(); - assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); - assertThat(permissioningConfiguration.getAccountWhitelist()) - .containsExactly("0x0000000000000000000000000000000000000009"); - } - - @Test - public void permissioningTomlWithInvalidEnode() throws IOException { - - final URL configFile = Resources.getResource("permissioning_config_invalid_enode.toml"); - final Path toml = Files.createTempFile("toml", ""); - Files.write(toml, Resources.toByteArray(configFile)); - final TomlParseResult tomlResult = Toml.parse(toml); - - try { - RunnerBuilder.permissioningConfig(tomlResult); - fail("Expecting IllegalArgumentException: Enode URL contains an invalid node ID"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).startsWith("Enode URL contains an invalid node ID"); - } - } - private void syncFromGenesis(final SyncMode mode) throws Exception { final Path dbAhead = temp.newFolder().toPath(); final int blockCount = 500; @@ -210,6 +128,7 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { final JsonRpcConfiguration aheadJsonRpcConfiguration = jsonRpcConfiguration(); final WebSocketConfiguration aheadWebSocketConfiguration = wsRpcConfiguration(); final MetricsConfiguration aheadMetricsConfiguration = metricsConfiguration(); + final PermissioningConfiguration aheadPermissioningConfiguration = permissioningConfiguration(); final RunnerBuilder runnerBuilder = new RunnerBuilder() @@ -229,7 +148,7 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { .webSocketConfiguration(aheadWebSocketConfiguration) .metricsConfiguration(aheadMetricsConfiguration) .dataDir(dbAhead) - .permissioningConfigurationFromToml() + .permissioningConfiguration(aheadPermissioningConfiguration) .build(); try { diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java index cb8a1d60c3..de45e2171e 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java @@ -117,7 +117,7 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.p2pEnabled(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.jsonRpcConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.webSocketConfiguration(any())).thenReturn(mockRunnerBuilder); - when(mockRunnerBuilder.permissioningConfigurationFromToml()).thenReturn(mockRunnerBuilder); + when(mockRunnerBuilder.permissioningConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.dataDir(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.bannedNodeIds(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.metricsSystem(any())).thenReturn(mockRunnerBuilder); diff --git a/pantheon/src/test/resources/everything_config.toml b/pantheon/src/test/resources/everything_config.toml index 5ea2916519..d936a4cad3 100644 --- a/pantheon/src/test/resources/everything_config.toml +++ b/pantheon/src/test/resources/everything_config.toml @@ -72,6 +72,8 @@ min-gas-price="1" # Permissioning accounts-whitelist=["0x0000000000000000000000000000000000000009"] nodes-whitelist=["all"] +permissions-nodes-enabled=false +permissions-accounts-enabled=false # Privacy privacy-url="http://127.0.0.1:8888" From 5d9f8335593ebdcc1caf2c63bbcf12c3c47a96e3 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Fri, 25 Jan 2019 14:03:20 +1000 Subject: [PATCH 21/31] tidying up --- .../src/main/java/tech/pegasys/pantheon/RunnerBuilder.java | 3 --- .../pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java | 3 --- pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java | 1 - 3 files changed, 7 deletions(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index 7e7b46138f..21c729017d 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -69,12 +69,9 @@ import com.google.common.base.Preconditions; import io.vertx.core.Vertx; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; public class RunnerBuilder { - private static final Logger LOG = LogManager.getLogger(); private Vertx vertx; private PantheonController pantheonController; private boolean p2pEnabled = true; diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java index 796e1b794c..784a7fdb71 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/ConfigOptionSearchAndRunHandler.java @@ -66,9 +66,6 @@ protected List handle(final ParseResult parseResult) throws ExecutionExc new TomlConfigFileDefaultProvider(commandLine, configFile); commandLine.setDefaultValueProvider(tomlConfigFileDefaultProvider); } - commandLine.parseWithHandlers( - resultHandler, exceptionHandler, parseResult.originalArgs().toArray(new String[0])); - return new ArrayList<>(); } commandLine.parseWithHandlers( resultHandler, exceptionHandler, parseResult.originalArgs().toArray(new String[0])); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index 1c1d541fdb..a8dad2c2bf 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -129,7 +129,6 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { final WebSocketConfiguration aheadWebSocketConfiguration = wsRpcConfiguration(); final MetricsConfiguration aheadMetricsConfiguration = metricsConfiguration(); final PermissioningConfiguration aheadPermissioningConfiguration = permissioningConfiguration(); - final RunnerBuilder runnerBuilder = new RunnerBuilder() .vertx(Vertx.vertx()) From 73c8ed533a924513c872b0d3ad5d8886adf8d19c Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Fri, 25 Jan 2019 14:23:54 +1000 Subject: [PATCH 22/31] handle when default permissions config file does not exist --- .../java/tech/pegasys/pantheon/cli/PantheonCommand.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 5c1b41bd5b..a1b3614e2a 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -737,7 +737,12 @@ MetricsConfiguration metricsConfiguration() { } private PermissioningConfiguration permissioningConfiguration() { - final PermissioningConfiguration permissioningConfiguration = + + if (!permissionsAccountsEnabled && !permissionsNodesEnabled) { + return PermissioningConfiguration.createDefault(); + } + + final PermissioningConfiguration permissioningConfiguration = PermissioningConfigurationBuilder.permissioningConfigurationFromToml( DefaultCommandValues.PERMISSIONING_CONFIG_LOCATION, permissionsNodesEnabled, From b9dce8fc2e719266bb6b3baaba319befd94e0764 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Fri, 25 Jan 2019 14:24:07 +1000 Subject: [PATCH 23/31] handle when default permissions config file does not exist --- .../main/java/tech/pegasys/pantheon/cli/PantheonCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index a1b3614e2a..886574ab59 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -742,7 +742,7 @@ private PermissioningConfiguration permissioningConfiguration() { return PermissioningConfiguration.createDefault(); } - final PermissioningConfiguration permissioningConfiguration = + final PermissioningConfiguration permissioningConfiguration = PermissioningConfigurationBuilder.permissioningConfigurationFromToml( DefaultCommandValues.PERMISSIONING_CONFIG_LOCATION, permissionsNodesEnabled, From a46fc2f8a338bc9e79d0440250f3c6094ff13f4a Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Sat, 26 Jan 2019 07:21:55 +1000 Subject: [PATCH 24/31] added tests --- ...PermissioningConfigurationBuilderTest.java | 48 +++++++++++++++---- .../pantheon/cli/PantheonCommandTest.java | 36 -------------- .../test/resources/permissioning_config.toml | 2 +- ...permissioning_config_empty_whitelists.toml | 4 ++ 4 files changed, 43 insertions(+), 47 deletions(-) create mode 100644 pantheon/src/test/resources/permissioning_config_empty_whitelists.toml diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/PermissioningConfigurationBuilderTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/PermissioningConfigurationBuilderTest.java index 993ece85f6..7ebdf3b2a1 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/PermissioningConfigurationBuilderTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/PermissioningConfigurationBuilderTest.java @@ -30,15 +30,26 @@ public class PermissioningConfigurationBuilderTest { + static final String PERMISSIONING_CONFIG_TOML = "permissioning_config.toml"; + static final String PERMISSIONING_CONFIG_ACCOUNT_WHITELIST_ONLY = + "permissioning_config_account_whitelist_only.toml"; + static final String PERMISSIONING_CONFIG_NODE_WHITELIST_ONLY = + "permissioning_config_node_whitelist_only.toml"; + static final String PERMISSIONING_CONFIG_INVALID_ENODE = + "permissioning_config_invalid_enode.toml"; + static final String PERMISSIONING_CONFIG_EMPTY_WHITELISTS = + "permissioning_config_empty_whitelists.toml"; + private final String VALID_NODE_ID = "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"; @Test - public void permissioningTomlFromResource() throws IOException { + public void permissioningConfig() throws IOException { final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; + final String uri2 = "enode://" + VALID_NODE_ID + "@192.169.0.9:4568"; - final URL configFile = Resources.getResource("permissioning_config.toml"); + final URL configFile = Resources.getResource(PERMISSIONING_CONFIG_TOML); final Path toml = Files.createTempFile("toml", ""); Files.write(toml, Resources.toByteArray(configFile)); final TomlParseResult tomlResult = Toml.parse(toml); @@ -50,15 +61,16 @@ public void permissioningTomlFromResource() throws IOException { assertThat(permissioningConfiguration.getAccountWhitelist()) .containsExactly("0x0000000000000000000000000000000000000009"); assertThat(permissioningConfiguration.isNodeWhitelistSet()).isTrue(); - assertThat(permissioningConfiguration.getNodeWhitelist()).containsExactly(URI.create(uri)); + assertThat(permissioningConfiguration.getNodeWhitelist()) + .containsExactly(URI.create(uri), URI.create(uri2)); } @Test - public void permissioningTomlFromResourceWithOnlyNodeWhitelistSet() throws IOException { + public void permissioningConfigWithOnlyNodeWhitelistSet() throws IOException { final String uri = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; - final URL configFile = Resources.getResource("permissioning_config_node_whitelist_only.toml"); + final URL configFile = Resources.getResource(PERMISSIONING_CONFIG_NODE_WHITELIST_ONLY); final Path toml = Files.createTempFile("toml", ""); Files.write(toml, Resources.toByteArray(configFile)); final TomlParseResult tomlResult = Toml.parse(toml); @@ -72,10 +84,9 @@ public void permissioningTomlFromResourceWithOnlyNodeWhitelistSet() throws IOExc } @Test - public void permissioningTomlFromResourceWithOnlyAccountWhitelistSet() throws IOException { + public void permissioningConfigWithOnlyAccountWhitelistSet() throws IOException { - final URL configFile = - Resources.getResource("permissioning_config_account_whitelist_only.toml"); + final URL configFile = Resources.getResource(PERMISSIONING_CONFIG_ACCOUNT_WHITELIST_ONLY); final Path toml = Files.createTempFile("toml", ""); Files.write(toml, Resources.toByteArray(configFile)); final TomlParseResult tomlResult = Toml.parse(toml); @@ -90,9 +101,9 @@ public void permissioningTomlFromResourceWithOnlyAccountWhitelistSet() throws IO } @Test - public void permissioningTomlWithInvalidEnode() throws IOException { + public void permissioningConfigWithInvalidEnode() throws IOException { - final URL configFile = Resources.getResource("permissioning_config_invalid_enode.toml"); + final URL configFile = Resources.getResource(PERMISSIONING_CONFIG_INVALID_ENODE); final Path toml = Files.createTempFile("toml", ""); Files.write(toml, Resources.toByteArray(configFile)); final TomlParseResult tomlResult = Toml.parse(toml); @@ -104,4 +115,21 @@ public void permissioningTomlWithInvalidEnode() throws IOException { assertThat(e.getMessage()).startsWith("Enode URL contains an invalid node ID"); } } + + @Test + public void permissioningConfigWithEmptyWhitelistMustNotError() throws IOException { + + final URL configFile = Resources.getResource(PERMISSIONING_CONFIG_EMPTY_WHITELISTS); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + PermissioningConfiguration permissioningConfiguration = + PermissioningConfigurationBuilder.permissioningConfiguration(tomlResult, true, true); + + assertThat(permissioningConfiguration.isNodeWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getNodeWhitelist()).isEmpty(); + assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getAccountWhitelist()).isEmpty(); + } } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index 9a256a3c97..71991ead30 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -750,42 +750,6 @@ public void nodesWhitelistOptionMustBeUsed() { assertThat(commandErrorOutput.toString()).isEmpty(); } - @Test - @Ignore - public void callingWithAccountsWhitelistOptionButNoValueMustNotError() { - parseCommand("--accounts-whitelist"); - - verify(mockRunnerBuilder) - .permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); - verify(mockRunnerBuilder).build(); - - assertThat(permissioningConfigurationArgumentCaptor.getValue().getAccountWhitelist()).isEmpty(); - assertThat(permissioningConfigurationArgumentCaptor.getValue().isAccountWhitelistSet()) - .isTrue(); - - assertThat(commandOutput.toString()).isEmpty(); - assertThat(commandErrorOutput.toString()).isEmpty(); - } - - @Test - @Ignore - public void accountsWhitelistOptionMustBeUsed() { - final String[] accounts = {"1111111111111111", "2222222222222222", "ffffffffffffffff"}; - parseCommand("--accounts-whitelist", String.join(",", accounts)); - - verify(mockRunnerBuilder) - .permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); - verify(mockRunnerBuilder).build(); - - assertThat(permissioningConfigurationArgumentCaptor.getValue().getAccountWhitelist()) - .containsExactlyInAnyOrder(accounts); - assertThat(permissioningConfigurationArgumentCaptor.getValue().isAccountWhitelistSet()) - .isTrue(); - - assertThat(commandOutput.toString()).isEmpty(); - assertThat(commandErrorOutput.toString()).isEmpty(); - } - @Test @Ignore public void nodesWhitelistOptionMustIncludeBootnodes() { diff --git a/pantheon/src/test/resources/permissioning_config.toml b/pantheon/src/test/resources/permissioning_config.toml index 786b14e0e8..0457556cfd 100644 --- a/pantheon/src/test/resources/permissioning_config.toml +++ b/pantheon/src/test/resources/permissioning_config.toml @@ -1,4 +1,4 @@ # Permissioning TOML file accounts-whitelist=["0x0000000000000000000000000000000000000009"] -nodes-whitelist=["enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.9:4567"] +nodes-whitelist=["enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.9:4567","enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.169.0.9:4568"] diff --git a/pantheon/src/test/resources/permissioning_config_empty_whitelists.toml b/pantheon/src/test/resources/permissioning_config_empty_whitelists.toml new file mode 100644 index 0000000000..1e7eae0cf5 --- /dev/null +++ b/pantheon/src/test/resources/permissioning_config_empty_whitelists.toml @@ -0,0 +1,4 @@ +# Permissioning TOML file with empty lists + +accounts-whitelist=[] +nodes-whitelist=[] From cfb5531a1ab338d89024a1e2c33d5291e1436187 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Sat, 26 Jan 2019 16:17:15 +1000 Subject: [PATCH 25/31] moved check for bootnodes on whitelist out of CLI --- .../pegasys/pantheon/cli/PantheonCommand.java | 27 +--- .../PermissioningConfigurationValidator.java | 58 ++++++++ .../pantheon/cli/PantheonCommandTest.java | 136 ------------------ ...rmissioningConfigurationValidatorTest.java | 75 ++++++++++ ...ermissioning_config_ropsten_bootnodes.toml | 5 + 5 files changed, 144 insertions(+), 157 deletions(-) create mode 100644 pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java create mode 100644 pantheon/src/test/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidatorTest.java create mode 100644 pantheon/src/test/resources/permissioning_config_ropsten_bootnodes.toml diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 0860d07298..8db5857d7e 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -45,15 +45,13 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; -import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration; -import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; -import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.metrics.MetricsSystem; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.metrics.prometheus.PrometheusMetricsSystem; import tech.pegasys.pantheon.util.BlockImporter; import tech.pegasys.pantheon.util.InvalidConfigurationException; +import tech.pegasys.pantheon.util.PermissioningConfigurationValidator; import tech.pegasys.pantheon.util.bytes.BytesValue; import java.io.File; @@ -67,7 +65,6 @@ import java.util.List; import java.util.Optional; import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.Stream; import com.google.common.io.Resources; @@ -613,23 +610,11 @@ private NetworkName getNetwork() { private void ensureAllBootnodesAreInWhitelist( final EthNetworkConfig ethNetworkConfig, final PermissioningConfiguration permissioningConfiguration) { - final List bootnodes = - DiscoveryConfiguration.getBootstrapPeersFromGenericCollection( - ethNetworkConfig.getBootNodes()); - if (permissioningConfiguration.isNodeWhitelistSet() && bootnodes != null) { - final List whitelist = - permissioningConfiguration - .getNodeWhitelist() - .stream() - .map(DefaultPeer::fromURI) - .collect(Collectors.toList()); - for (final Peer bootnode : bootnodes) { - if (!whitelist.contains(bootnode)) { - throw new ParameterException( - new CommandLine(this), - "Cannot start node with bootnode(s) that are not in nodes-whitelist " + bootnode); - } - } + if (!PermissioningConfigurationValidator.areAllBootnodesAreInWhitelist( + ethNetworkConfig, permissioningConfiguration)) { + throw new ParameterException( + new CommandLine(this), + "Cannot start node with bootnode(s) that are not in nodes-whitelist "); } } diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java b/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java new file mode 100644 index 0000000000..3508970854 --- /dev/null +++ b/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java @@ -0,0 +1,58 @@ +/* + * 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; + +import tech.pegasys.pantheon.cli.EthNetworkConfig; +import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; +import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PermissioningConfigurationValidator { + + private static final Logger LOG = LogManager.getLogger(); + + public static boolean areAllBootnodesAreInWhitelist( + final EthNetworkConfig ethNetworkConfig, + final PermissioningConfiguration permissioningConfiguration) { + final List bootnodesNotInWhitelist = new ArrayList(); + final List bootnodes = + DiscoveryConfiguration.getBootstrapPeersFromGenericCollection( + ethNetworkConfig.getBootNodes()); + if (permissioningConfiguration.isNodeWhitelistSet() && bootnodes != null) { + final List whitelist = + permissioningConfiguration + .getNodeWhitelist() + .stream() + .map(DefaultPeer::fromURI) + .collect(Collectors.toList()); + for (final Peer bootnode : bootnodes) { + if (!whitelist.contains(bootnode)) { + bootnodesNotInWhitelist.add(bootnode); + } + } + if (!bootnodesNotInWhitelist.isEmpty()) { + LOG.error("Bootnode(s) not in nodes-whitelist " + bootnodesNotInWhitelist); + return false; + } + } + return true; + } +} diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index 71991ead30..070800cd35 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -52,7 +52,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -708,141 +707,6 @@ public void callingWithRefreshDelayWithNegativeValueMustError() { assertThat(commandErrorOutput.toString()).startsWith(expectedErrorMsg); } - @Test - @Ignore - public void callingWithInvalidNodesWhitelistMustDisplayErrorAndUsage() { - parseCommand("--nodes-whitelist", "invalid_enode_url"); - assertThat(commandOutput.toString()).isEmpty(); - /* - Because of the way Picocli handles errors parsing errors for lists with arity 0..*, we don't - get the nice error msg with that was wrong. It only shows to the user the values that weren't - parsed correctly. - */ - final String expectedErrorOutputStart = "Unmatched argument: invalid_enode_url"; - assertThat(commandErrorOutput.toString()).startsWith(expectedErrorOutputStart); - } - - @Test - @Ignore - public void nodesWhitelistOptionMustBeUsed() { - parseCommand( - "--nodes-whitelist", - String.join(",", validENodeStrings), - "--bootnodes", - validENodeStrings[0]); - - verify(mockRunnerBuilder) - .permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); - verify(mockRunnerBuilder).build(); - - final List enodeURLAsStringList = - permissioningConfigurationArgumentCaptor - .getValue() - .getNodeWhitelist() - .stream() - .map(URI::toString) - .collect(Collectors.toList()); - - assertThat(enodeURLAsStringList).containsExactlyInAnyOrder(validENodeStrings); - assertThat(permissioningConfigurationArgumentCaptor.getValue().isNodeWhitelistSet()).isTrue(); - - assertThat(commandOutput.toString()).isEmpty(); - assertThat(commandErrorOutput.toString()).isEmpty(); - } - - @Test - @Ignore - public void nodesWhitelistOptionMustIncludeBootnodes() { - parseCommand( - "--bootnodes", - String.join(",", validENodeStrings[0], validENodeStrings[1]), - "--nodes-whitelist", - String.join(",", validENodeStrings)); - - verify(mockRunnerBuilder) - .permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); - verify(mockRunnerBuilder).build(); - - final List enodeURLAsStringList = - permissioningConfigurationArgumentCaptor - .getValue() - .getNodeWhitelist() - .stream() - .map(URI::toString) - .collect(Collectors.toList()); - - assertThat(enodeURLAsStringList).containsExactlyInAnyOrder(validENodeStrings); - assertThat(permissioningConfigurationArgumentCaptor.getValue().isNodeWhitelistSet()).isTrue(); - - assertThat(commandOutput.toString()).isEmpty(); - assertThat(commandErrorOutput.toString()).isEmpty(); - } - - @Test - @Ignore - public void nodesWhitelistOptionWhichDoesNotIncludeBootnodesMustDisplayError() { - final String bootNodeNotWhitelisted = "enode://" + VALID_NODE_ID + "@192.168.0.9:4567"; - parseCommand( - "--bootnodes", - String.join(",", bootNodeNotWhitelisted, validENodeStrings[2]), - "--nodes-whitelist", - String.join(",", validENodeStrings)); - - verifyZeroInteractions(mockRunnerBuilder); - - assertThat(commandOutput.toString()).isEmpty(); - assertThat(commandErrorOutput.toString()) - .contains("Cannot start node with bootnode(s) that are not in nodes-whitelist"); - } - - @Test - @Ignore - public void ropstenWithNodesWhitelistOptionWhichDoesIncludeRopstenBootnodesMustNotDisplayError() { - parseCommand("--network", "ropsten", "--nodes-whitelist", String.join(",", ropstenBootnodes)); - - verify(mockRunnerBuilder) - .permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); - verify(mockRunnerBuilder).build(); - - final List enodeURLAsStringList = - permissioningConfigurationArgumentCaptor - .getValue() - .getNodeWhitelist() - .stream() - .map(URI::toString) - .collect(Collectors.toList()); - - assertThat(enodeURLAsStringList).containsExactlyInAnyOrder(ropstenBootnodes); - assertThat(permissioningConfigurationArgumentCaptor.getValue().isNodeWhitelistSet()).isTrue(); - - assertThat(commandOutput.toString()).isEmpty(); - assertThat(commandErrorOutput.toString()).isEmpty(); - } - - @Test - @Ignore - public void ropstenWithNodesWhitelistOptionWhichDoesNotIncludeRopstenBootnodesMustDisplayError() { - parseCommand("--network", "ropsten", "--nodes-whitelist", String.join(",", validENodeStrings)); - - verifyZeroInteractions(mockRunnerBuilder); - - assertThat(commandOutput.toString()).isEmpty(); - assertThat(commandErrorOutput.toString()) - .contains("Cannot start node with bootnode(s) that are not in nodes-whitelist"); - } - - @Test - @Ignore - public void nodesWhitelistWithEmptyListAndNonEmptyBootnodesMustDisplayError() { - parseCommand("--bootnodes", String.join(",", validENodeStrings[0]), "--nodes-whitelist"); - - verifyZeroInteractions(mockRunnerBuilder); - - assertThat(commandOutput.toString()).isEmpty(); - assertThat(commandErrorOutput.toString()) - .contains("Cannot start node with bootnode(s) that are not in nodes-whitelist"); - } - @Test public void bannedNodeIdsOptionMustBeUsed() { final String[] nodes = {"0001", "0002", "0003"}; diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidatorTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidatorTest.java new file mode 100644 index 0000000000..7d99230719 --- /dev/null +++ b/pantheon/src/test/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidatorTest.java @@ -0,0 +1,75 @@ +/* + * 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; + +import static org.assertj.core.api.Assertions.assertThat; + +import tech.pegasys.pantheon.PermissioningConfigurationBuilder; +import tech.pegasys.pantheon.cli.EthNetworkConfig; +import tech.pegasys.pantheon.cli.NetworkName; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; + +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; + +import com.google.common.io.Resources; +import net.consensys.cava.toml.Toml; +import net.consensys.cava.toml.TomlParseResult; +import org.junit.Test; + +public class PermissioningConfigurationValidatorTest { + + static final String PERMISSIONING_CONFIG_ROPSTEN_BOOTNODES = + "permissioning_config_ropsten_bootnodes.toml"; + static final String PERMISSIONING_CONFIG = "permissioning_config.toml"; + + @Test + public void ropstenWithNodesWhitelistOptionWhichDoesIncludeRopstenBootnodesMustNotError() + throws Exception { + + EthNetworkConfig ethNetworkConfig = EthNetworkConfig.getNetworkConfig(NetworkName.ROPSTEN); + + final URL configFile = Resources.getResource(PERMISSIONING_CONFIG_ROPSTEN_BOOTNODES); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + PermissioningConfiguration permissioningConfiguration = + PermissioningConfigurationBuilder.permissioningConfiguration(tomlResult, true, true); + + assertThat( + PermissioningConfigurationValidator.areAllBootnodesAreInWhitelist( + ethNetworkConfig, permissioningConfiguration)) + .isTrue(); + } + + @Test + public void nodesWhitelistOptionWhichDoesNotIncludeBootnodesMustError() throws Exception { + + EthNetworkConfig ethNetworkConfig = EthNetworkConfig.getNetworkConfig(NetworkName.ROPSTEN); + + final URL configFile = Resources.getResource(PERMISSIONING_CONFIG); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + PermissioningConfiguration permissioningConfiguration = + PermissioningConfigurationBuilder.permissioningConfiguration(tomlResult, true, true); + + assertThat( + PermissioningConfigurationValidator.areAllBootnodesAreInWhitelist( + ethNetworkConfig, permissioningConfiguration)) + .isFalse(); + } +} diff --git a/pantheon/src/test/resources/permissioning_config_ropsten_bootnodes.toml b/pantheon/src/test/resources/permissioning_config_ropsten_bootnodes.toml new file mode 100644 index 0000000000..aac5b1a75d --- /dev/null +++ b/pantheon/src/test/resources/permissioning_config_ropsten_bootnodes.toml @@ -0,0 +1,5 @@ +# Permissioning TOML file + +accounts-whitelist=["0x0000000000000000000000000000000000000009"] +nodes-whitelist=["enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303", + "enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303"] From 5d7296c58a81481355c11e6aeacb049f3d059567 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Sat, 26 Jan 2019 16:26:37 +1000 Subject: [PATCH 26/31] added tests --- .../pegasys/pantheon/cli/PantheonCommand.java | 10 +++++----- .../PermissioningConfigurationValidator.java | 14 ++++---------- ...ermissioningConfigurationValidatorTest.java | 18 ++++++++++-------- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 25a49dc60e..d1ca964667 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -610,11 +610,11 @@ private NetworkName getNetwork() { private void ensureAllBootnodesAreInWhitelist( final EthNetworkConfig ethNetworkConfig, final PermissioningConfiguration permissioningConfiguration) { - if (!PermissioningConfigurationValidator.areAllBootnodesAreInWhitelist( - ethNetworkConfig, permissioningConfiguration)) { - throw new ParameterException( - new CommandLine(this), - "Cannot start node with bootnode(s) that are not in nodes-whitelist "); + try { + PermissioningConfigurationValidator.areAllBootnodesAreInWhitelist( + ethNetworkConfig, permissioningConfiguration); + } catch (Exception e) { + throw new ParameterException(new CommandLine(this), e.getMessage()); } } diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java b/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java index 3508970854..cae5dbe08f 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java @@ -22,16 +22,12 @@ import java.util.List; import java.util.stream.Collectors; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - public class PermissioningConfigurationValidator { - private static final Logger LOG = LogManager.getLogger(); - - public static boolean areAllBootnodesAreInWhitelist( + public static void areAllBootnodesAreInWhitelist( final EthNetworkConfig ethNetworkConfig, - final PermissioningConfiguration permissioningConfiguration) { + final PermissioningConfiguration permissioningConfiguration) + throws Exception { final List bootnodesNotInWhitelist = new ArrayList(); final List bootnodes = DiscoveryConfiguration.getBootstrapPeersFromGenericCollection( @@ -49,10 +45,8 @@ public static boolean areAllBootnodesAreInWhitelist( } } if (!bootnodesNotInWhitelist.isEmpty()) { - LOG.error("Bootnode(s) not in nodes-whitelist " + bootnodesNotInWhitelist); - return false; + throw new Exception("Bootnode(s) not in nodes-whitelist " + bootnodesNotInWhitelist); } } - return true; } } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidatorTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidatorTest.java index 7d99230719..a84a3ddc07 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidatorTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidatorTest.java @@ -13,6 +13,7 @@ package tech.pegasys.pantheon.util; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import tech.pegasys.pantheon.PermissioningConfigurationBuilder; import tech.pegasys.pantheon.cli.EthNetworkConfig; @@ -48,10 +49,8 @@ public void ropstenWithNodesWhitelistOptionWhichDoesIncludeRopstenBootnodesMustN PermissioningConfiguration permissioningConfiguration = PermissioningConfigurationBuilder.permissioningConfiguration(tomlResult, true, true); - assertThat( - PermissioningConfigurationValidator.areAllBootnodesAreInWhitelist( - ethNetworkConfig, permissioningConfiguration)) - .isTrue(); + PermissioningConfigurationValidator.areAllBootnodesAreInWhitelist( + ethNetworkConfig, permissioningConfiguration); } @Test @@ -67,9 +66,12 @@ public void nodesWhitelistOptionWhichDoesNotIncludeBootnodesMustError() throws E PermissioningConfiguration permissioningConfiguration = PermissioningConfigurationBuilder.permissioningConfiguration(tomlResult, true, true); - assertThat( - PermissioningConfigurationValidator.areAllBootnodesAreInWhitelist( - ethNetworkConfig, permissioningConfiguration)) - .isFalse(); + try { + PermissioningConfigurationValidator.areAllBootnodesAreInWhitelist( + ethNetworkConfig, permissioningConfiguration); + fail("expected exception because ropsten bootnodes are not in node-whitelist"); + } catch (Exception e) { + assertThat(e.getMessage().startsWith("Bootnode")).isTrue(); + } } } From 5980e2aac64b7a9e55c32c433c1dbc82106aeeb0 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 29 Jan 2019 13:18:35 +1000 Subject: [PATCH 27/31] added test and set to empty list if toml key/value is absent --- .../PermissioningConfigurationBuilder.java | 5 +++++ ...PermissioningConfigurationBuilderTest.java | 19 +++++++++++++++++++ ...ermissioning_config_absent_whitelists.toml | 3 +++ 3 files changed, 27 insertions(+) create mode 100644 pantheon/src/test/resources/permissioning_config_absent_whitelists.toml diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/PermissioningConfigurationBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/PermissioningConfigurationBuilder.java index d00c0f9a59..c98c4103ab 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/PermissioningConfigurationBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/PermissioningConfigurationBuilder.java @@ -19,6 +19,7 @@ import java.io.File; import java.net.URI; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -64,6 +65,8 @@ public static PermissioningConfiguration permissioningConfiguration( .map(Object::toString) .collect(Collectors.toList()); permissioningConfiguration.setAccountWhitelist(accountsWhitelistToml); + } else { + permissioningConfiguration.setAccountWhitelist(new ArrayList<>()); } } if (permissionedNodeEnabled) { @@ -76,6 +79,8 @@ public static PermissioningConfiguration permissioningConfiguration( .map(EnodeToURIPropertyConverter::convertToURI) .collect(Collectors.toList()); permissioningConfiguration.setNodeWhitelist(nodesWhitelistToml); + } else { + permissioningConfiguration.setNodeWhitelist(new ArrayList<>()); } } return permissioningConfiguration; diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/PermissioningConfigurationBuilderTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/PermissioningConfigurationBuilderTest.java index 7ebdf3b2a1..22e8b7d6bc 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/PermissioningConfigurationBuilderTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/PermissioningConfigurationBuilderTest.java @@ -39,6 +39,8 @@ public class PermissioningConfigurationBuilderTest { "permissioning_config_invalid_enode.toml"; static final String PERMISSIONING_CONFIG_EMPTY_WHITELISTS = "permissioning_config_empty_whitelists.toml"; + static final String PERMISSIONING_CONFIG_ABSENT_WHITELISTS = + "permissioning_config_absent_whitelists.toml"; private final String VALID_NODE_ID = "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"; @@ -132,4 +134,21 @@ public void permissioningConfigWithEmptyWhitelistMustNotError() throws IOExcepti assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); assertThat(permissioningConfiguration.getAccountWhitelist()).isEmpty(); } + + @Test + public void permissioningConfigWithAbsentWhitelistMustSetValues() throws IOException { + + final URL configFile = Resources.getResource(PERMISSIONING_CONFIG_ABSENT_WHITELISTS); + final Path toml = Files.createTempFile("toml", ""); + Files.write(toml, Resources.toByteArray(configFile)); + final TomlParseResult tomlResult = Toml.parse(toml); + + PermissioningConfiguration permissioningConfiguration = + PermissioningConfigurationBuilder.permissioningConfiguration(tomlResult, true, true); + + assertThat(permissioningConfiguration.isNodeWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getNodeWhitelist()).isEmpty(); + assertThat(permissioningConfiguration.isAccountWhitelistSet()).isTrue(); + assertThat(permissioningConfiguration.getAccountWhitelist()).isEmpty(); + } } diff --git a/pantheon/src/test/resources/permissioning_config_absent_whitelists.toml b/pantheon/src/test/resources/permissioning_config_absent_whitelists.toml new file mode 100644 index 0000000000..566e717f0b --- /dev/null +++ b/pantheon/src/test/resources/permissioning_config_absent_whitelists.toml @@ -0,0 +1,3 @@ +# Permissioning TOML file with absent lists + +accounts-whitelist= From d45e2defdc08e47d889f99a9f2bba5bb402ba092 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 29 Jan 2019 13:19:46 +1000 Subject: [PATCH 28/31] PR comments --- .../main/java/tech/pegasys/pantheon/TomlConfigFileParser.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/TomlConfigFileParser.java b/pantheon/src/main/java/tech/pegasys/pantheon/TomlConfigFileParser.java index ea6e4f8d9f..65e3dd8f83 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/TomlConfigFileParser.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/TomlConfigFileParser.java @@ -33,8 +33,7 @@ private static TomlParseResult checkConfigurationValidity( } public static TomlParseResult loadConfiguration(final String toml) { - TomlParseResult result; - result = Toml.parse(toml); + TomlParseResult result = Toml.parse(toml); if (result.hasErrors()) { final String errors = From 586a8200e6040c6d8a9ed5159964db5bf1fc3339 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 29 Jan 2019 13:42:49 +1000 Subject: [PATCH 29/31] PR comments --- .../PermissioningConfigurationBuilder.java | 2 +- .../PermissioningConfigurationValidator.java | 23 ++++++++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/PermissioningConfigurationBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/PermissioningConfigurationBuilder.java index c98c4103ab..9d555edd65 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/PermissioningConfigurationBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/PermissioningConfigurationBuilder.java @@ -101,7 +101,7 @@ private static File permissioningConfigFile(final String filename) { if (permissioningConfigFile.exists()) { return permissioningConfigFile; } else { - LOG.error("Unable to load permissioning config file from location {}.", filename); + LOG.error("File does not exist: permissioning config path: {}.", filename); return null; } } diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java b/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java index cae5dbe08f..7e96bf3394 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java @@ -12,9 +12,9 @@ */ package tech.pegasys.pantheon.util; +import java.net.URI; import tech.pegasys.pantheon.cli.EthNetworkConfig; import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration; -import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; @@ -28,25 +28,20 @@ public static void areAllBootnodesAreInWhitelist( final EthNetworkConfig ethNetworkConfig, final PermissioningConfiguration permissioningConfiguration) throws Exception { - final List bootnodesNotInWhitelist = new ArrayList(); + List bootnodesNotInWhitelist = new ArrayList<>(); final List bootnodes = DiscoveryConfiguration.getBootstrapPeersFromGenericCollection( ethNetworkConfig.getBootNodes()); if (permissioningConfiguration.isNodeWhitelistSet() && bootnodes != null) { - final List whitelist = - permissioningConfiguration - .getNodeWhitelist() + bootnodesNotInWhitelist = + bootnodes .stream() - .map(DefaultPeer::fromURI) + .filter( + node -> permissioningConfiguration.getNodeWhitelist().contains(URI.create(node.toString()))) .collect(Collectors.toList()); - for (final Peer bootnode : bootnodes) { - if (!whitelist.contains(bootnode)) { - bootnodesNotInWhitelist.add(bootnode); - } - } - if (!bootnodesNotInWhitelist.isEmpty()) { - throw new Exception("Bootnode(s) not in nodes-whitelist " + bootnodesNotInWhitelist); - } + } + if (!bootnodesNotInWhitelist.isEmpty()) { + throw new Exception("Bootnode(s) not in nodes-whitelist " + bootnodesNotInWhitelist); } } } From 2395ea7481301ae0ef45565a8307bdfc06aa3d33 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 29 Jan 2019 13:51:18 +1000 Subject: [PATCH 30/31] copied code to convert from Peer to enode format --- .../PermissioningConfigurationValidator.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java b/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java index 7e96bf3394..fca4618138 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java @@ -12,16 +12,20 @@ */ package tech.pegasys.pantheon.util; -import java.net.URI; import tech.pegasys.pantheon.cli.EthNetworkConfig; import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.peers.Endpoint; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; +import java.net.URI; import java.util.ArrayList; import java.util.List; +import java.util.OptionalInt; import java.util.stream.Collectors; +import org.bouncycastle.util.encoders.Hex; + public class PermissioningConfigurationValidator { public static void areAllBootnodesAreInWhitelist( @@ -37,11 +41,29 @@ public static void areAllBootnodesAreInWhitelist( bootnodes .stream() .filter( - node -> permissioningConfiguration.getNodeWhitelist().contains(URI.create(node.toString()))) + node -> + permissioningConfiguration + .getNodeWhitelist() + .contains(URI.create(buildEnodeURI(node)))) .collect(Collectors.toList()); } if (!bootnodesNotInWhitelist.isEmpty()) { throw new Exception("Bootnode(s) not in nodes-whitelist " + bootnodesNotInWhitelist); } } + + private static String buildEnodeURI(final Peer s) { + String url = Hex.toHexString(s.getId().extractArray()); + Endpoint endpoint = s.getEndpoint(); + String nodeIp = endpoint.getHost(); + OptionalInt tcpPort = endpoint.getTcpPort(); + int udpPort = endpoint.getUdpPort(); + + if (tcpPort.isPresent() && (tcpPort.getAsInt() != udpPort)) { + return String.format( + "enode://%s@%s:%d?discport=%d", url, nodeIp, tcpPort.getAsInt(), udpPort); + } else { + return String.format("enode://%s@%s:%d", url, nodeIp, udpPort); + } + } } From 81fb4447f3bf4260e70eba6a9a533cc41751f873 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 29 Jan 2019 13:55:01 +1000 Subject: [PATCH 31/31] missed a --- .../pantheon/util/PermissioningConfigurationValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java b/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java index fca4618138..fcd3d1bad2 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/util/PermissioningConfigurationValidator.java @@ -42,7 +42,7 @@ public static void areAllBootnodesAreInWhitelist( .stream() .filter( node -> - permissioningConfiguration + !permissioningConfiguration .getNodeWhitelist() .contains(URI.create(buildEnodeURI(node)))) .collect(Collectors.toList());