[PAN-2362] permissioning cli smart contract (#1116)

* added smart contract CLI options for node permissioning

* throw ParameterException if contract address not specified

Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Sally MacFarlane 6 years ago committed by GitHub
parent d0b288a401
commit c2e6594453
  1. 6
      ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/SmartContractPermissioningConfiguration.java
  2. 73
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java
  3. 66
      pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java
  4. 2
      pantheon/src/test/resources/everything_config.toml

@ -18,6 +18,12 @@ public class SmartContractPermissioningConfiguration {
private boolean smartContractNodeWhitelistEnabled; private boolean smartContractNodeWhitelistEnabled;
private Address smartContractAddress; private Address smartContractAddress;
public SmartContractPermissioningConfiguration createDefault() {
final SmartContractPermissioningConfiguration smartContractPermissioningConfiguration =
new SmartContractPermissioningConfiguration();
return smartContractPermissioningConfiguration;
}
public boolean isSmartContractNodeWhitelistEnabled() { public boolean isSmartContractNodeWhitelistEnabled() {
return smartContractNodeWhitelistEnabled; return smartContractNodeWhitelistEnabled;
} }

@ -51,6 +51,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfigurationBuilder; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfigurationBuilder;
import tech.pegasys.pantheon.ethereum.permissioning.SmartContractPermissioningConfiguration;
import tech.pegasys.pantheon.metrics.MetricCategory; import tech.pegasys.pantheon.metrics.MetricCategory;
import tech.pegasys.pantheon.metrics.MetricsSystem; import tech.pegasys.pantheon.metrics.MetricsSystem;
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
@ -442,6 +443,17 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
arity = "1") arity = "1")
private final BytesValue extraData = DEFAULT_EXTRA_DATA; private final BytesValue extraData = DEFAULT_EXTRA_DATA;
@Option(
names = {"--permissions-nodes-contract-address"},
description = "Address of the node permissioning smart contract",
arity = "1")
private final Address permissionsNodesContractAddress = null;
@Option(
names = {"--permissions-nodes-contract-enabled"},
description = "Enable node level permissions via smart contract (default: ${DEFAULT-VALUE})")
private final Boolean permissionsNodesContractEnabled = false;
@Option( @Option(
names = {"--permissions-nodes-enabled"}, names = {"--permissions-nodes-enabled"},
description = "Enable node level permissions (default: ${DEFAULT-VALUE})") description = "Enable node level permissions (default: ${DEFAULT-VALUE})")
@ -603,13 +615,6 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
+ "or specify the beneficiary of mining (via --miner-coinbase <Address>)"); + "or specify the beneficiary of mining (via --miner-coinbase <Address>)");
} }
if (permissionsConfigFile() != null) {
if (!permissionsAccountsEnabled && !permissionsNodesEnabled) {
logger.warn(
"Permissions config file set {} but no permissions enabled", permissionsConfigFile());
}
}
final EthNetworkConfig ethNetworkConfig = updateNetworkConfig(getNetwork()); final EthNetworkConfig ethNetworkConfig = updateNetworkConfig(getNetwork());
try { try {
final JsonRpcConfiguration jsonRpcConfiguration = jsonRpcConfiguration(); final JsonRpcConfiguration jsonRpcConfiguration = jsonRpcConfiguration();
@ -791,7 +796,11 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
} }
private Optional<PermissioningConfiguration> permissioningConfiguration() throws Exception { private Optional<PermissioningConfiguration> permissioningConfiguration() throws Exception {
if (!permissionsAccountsEnabled && !permissionsNodesEnabled) { Optional<LocalPermissioningConfiguration> localPermissioningConfigurationOptional;
Optional<SmartContractPermissioningConfiguration>
smartContractPermissioningConfigurationOptional;
if (!(localPermissionsEnabled() || contractPermissionsEnabled())) {
if (rpcHttpApis.contains(RpcApis.PERM) || rpcWsApis.contains(RpcApis.PERM)) { if (rpcHttpApis.contains(RpcApis.PERM) || rpcWsApis.contains(RpcApis.PERM)) {
logger.warn( logger.warn(
"Permissions are disabled. Cannot enable PERM APIs when not using Permissions."); "Permissions are disabled. Cannot enable PERM APIs when not using Permissions.");
@ -799,17 +808,57 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
return Optional.empty(); return Optional.empty();
} }
final LocalPermissioningConfiguration localPermissioningConfiguration = if (localPermissionsEnabled()) {
PermissioningConfigurationBuilder.permissioningConfiguration( final LocalPermissioningConfiguration localPermissioningConfiguration =
getPermissionsConfigFile(), permissionsNodesEnabled, permissionsAccountsEnabled); PermissioningConfigurationBuilder.permissioningConfiguration(
getPermissionsConfigFile(), permissionsNodesEnabled, permissionsAccountsEnabled);
localPermissioningConfigurationOptional = Optional.of(localPermissioningConfiguration);
} else {
if (permissionsConfigFile() != null) {
logger.warn(
"Permissions config file set {} but no local permissions enabled",
permissionsConfigFile());
}
localPermissioningConfigurationOptional = Optional.empty();
}
if (contractPermissionsEnabled()) {
if (permissionsNodesContractAddress == null) {
throw new ParameterException(
this.commandLine,
"No contract address specified. Cannot enable contract based permissions.");
}
final SmartContractPermissioningConfiguration smartContractPermissioningConfiguration =
PermissioningConfigurationBuilder.smartContractPermissioningConfiguration(
permissionsNodesContractAddress, permissionsNodesContractEnabled);
smartContractPermissioningConfigurationOptional =
Optional.of(smartContractPermissioningConfiguration);
} else {
if (permissionsNodesContractAddress != null) {
logger.warn(
"Smart contract address set {} but no contract permissions enabled",
permissionsNodesContractAddress);
}
smartContractPermissioningConfigurationOptional = Optional.empty();
}
final PermissioningConfiguration permissioningConfiguration = final PermissioningConfiguration permissioningConfiguration =
new PermissioningConfiguration( new PermissioningConfiguration(
Optional.of(localPermissioningConfiguration), Optional.empty()); localPermissioningConfigurationOptional,
smartContractPermissioningConfigurationOptional);
return Optional.of(permissioningConfiguration); return Optional.of(permissioningConfiguration);
} }
private boolean localPermissionsEnabled() {
return permissionsAccountsEnabled || permissionsNodesEnabled;
}
private boolean contractPermissionsEnabled() {
// TODO add permissionsAccountsContractEnabled
return permissionsNodesContractEnabled;
}
private PrivacyParameters privacyParameters() throws IOException { private PrivacyParameters privacyParameters() throws IOException {
CommandLineUtils.checkOptionDependencies( CommandLineUtils.checkOptionDependencies(

@ -46,6 +46,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi;
import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration;
import tech.pegasys.pantheon.ethereum.permissioning.SmartContractPermissioningConfiguration;
import tech.pegasys.pantheon.metrics.MetricCategory; import tech.pegasys.pantheon.metrics.MetricCategory;
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.BytesValue;
@ -307,6 +308,67 @@ public class PantheonCommandTest extends CommandTestAbstract {
assertThat(commandErrorOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty();
} }
@Test
public void permissionsSmartContractWithoutOptionMustError() {
parseCommand("--permissions-nodes-contract-address");
verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandErrorOutput.toString())
.startsWith("Missing required parameter for option '--permissions-nodes-contract-address'");
assertThat(commandOutput.toString()).isEmpty();
}
@Test
public void permissionsEnabledWithoutContractAddressMustError() {
parseCommand("--permissions-nodes-contract-enabled");
verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandErrorOutput.toString()).contains("No contract address specified");
assertThat(commandOutput.toString()).isEmpty();
}
@Test
public void permissionsEnabledWithInvalidContractAddressMustError() {
parseCommand(
"--permissions-nodes-contract-enabled",
"--permissions-nodes-contract-address",
"invalid-smart-contract-address");
verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandErrorOutput.toString()).contains("Invalid value");
assertThat(commandOutput.toString()).isEmpty();
}
@Test
public void permissionsSmartContractMustUseOption() {
String smartContractAddress = "0x0000000000000000000000000000000000001234";
parseCommand(
"--permissions-nodes-contract-enabled",
"--permissions-nodes-contract-address",
smartContractAddress);
final SmartContractPermissioningConfiguration smartContractPermissioningConfiguration =
new SmartContractPermissioningConfiguration();
smartContractPermissioningConfiguration.setSmartContractAddress(
Address.fromHexString(smartContractAddress));
smartContractPermissioningConfiguration.setSmartContractNodeWhitelistEnabled(true);
verify(mockRunnerBuilder)
.permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
PermissioningConfiguration config = permissioningConfigurationArgumentCaptor.getValue();
assertThat(config.getSmartContractConfig().get())
.isEqualToComparingFieldByField(smartContractPermissioningConfiguration);
assertThat(commandErrorOutput.toString()).isEmpty();
assertThat(commandOutput.toString()).isEmpty();
}
@Test @Test
public void permissionsTomlPathWithoutOptionMustDisplayUsage() { public void permissionsTomlPathWithoutOptionMustDisplayUsage() {
parseCommand("--permissions-config-file"); parseCommand("--permissions-config-file");
@ -330,7 +392,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
} }
@Test @Test
public void permissionsTomlFileWithNoPermissionsEnabledMustError() throws IOException { public void permissionsTomlFileWithNoPermissionsEnabledMustNotError() throws IOException {
final URL configFile = Resources.getResource(PERMISSIONING_CONFIG_TOML); final URL configFile = Resources.getResource(PERMISSIONING_CONFIG_TOML);
final Path permToml = createTempFile("toml", Resources.toByteArray(configFile)); final Path permToml = createTempFile("toml", Resources.toByteArray(configFile));
@ -343,7 +405,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
} }
@Test @Test
public void defaultPermissionsTomlFileWithNoPermissionsEnabledMustError() { public void defaultPermissionsTomlFileWithNoPermissionsEnabledMustNotError() {
parseCommand("--p2p-enabled", "false"); parseCommand("--p2p-enabled", "false");
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();

@ -74,6 +74,8 @@ min-gas-price="1"
permissions-nodes-enabled=false permissions-nodes-enabled=false
permissions-accounts-enabled=false permissions-accounts-enabled=false
permissions-config-file="./permissions_config.toml" permissions-config-file="./permissions_config.toml"
permissions-nodes-contract-enabled=false
permissions-nodes-contract-address="0x0000000000000000000000000000000000001234"
# Privacy # Privacy
privacy-url="http://127.0.0.1:8888" privacy-url="http://127.0.0.1:8888"

Loading…
Cancel
Save