CLI options and commands renaming (#618)

fixes Jira issues NC-2014, NC-2016, NC-2031, NC-2032, NC-2126 and NC-2127

Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Nicolas MASSART 6 years ago committed by GitHub
parent 14a67e8b82
commit ea4ca59bbb
  1. 301
      docs/Reference/Pantheon-CLI-Syntax.md
  2. 140
      pantheon/src/main/java/tech/pegasys/pantheon/cli/BlocksSubCommand.java
  3. 25
      pantheon/src/main/java/tech/pegasys/pantheon/cli/DefaultCommandValues.java
  4. 67
      pantheon/src/main/java/tech/pegasys/pantheon/cli/ExportPublicKeySubCommand.java
  5. 83
      pantheon/src/main/java/tech/pegasys/pantheon/cli/ImportSubCommand.java
  6. 21
      pantheon/src/main/java/tech/pegasys/pantheon/cli/NetworkName.java
  7. 230
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java
  8. 116
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PublicKeySubCommand.java
  9. 15
      pantheon/src/main/java/tech/pegasys/pantheon/cli/StandaloneCommand.java
  10. 109
      pantheon/src/test/java/tech/pegasys/pantheon/cli/BlockSubCommandTest.java
  11. 29
      pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java
  12. 60
      pantheon/src/test/java/tech/pegasys/pantheon/cli/ExportPublicKeySubCommandTest.java
  13. 53
      pantheon/src/test/java/tech/pegasys/pantheon/cli/ImportSubCommandTest.java
  14. 146
      pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java
  15. 117
      pantheon/src/test/java/tech/pegasys/pantheon/cli/PublicKeySubCommandTest.java
  16. 16
      pantheon/src/test/resources/complete_config.toml
  17. 39
      pantheon/src/test/resources/everything_config.toml

@ -36,10 +36,10 @@ Comma separated account public keys for permissioned transactions. You can speci
!!!note
Permissioning is under development and will be available in v1.0.
### banned-nodeids
### banned-node-ids
```bash tab="Syntax"
--banned-nodeids=<bannedNodeId>[,<bannedNodeId>...]...
--banned-node-ids=<bannedNodeId>[,<bannedNodeId>...]...
```
```bash tab="Example Command Line"
@ -55,6 +55,10 @@ List of node IDs with which this node will not peer. The node ID is the public k
!!!info
This option is only available from v0.8.2.
!!!tip
The singular `--banned-node-id` and plural `--banned-node-ids` are available and are just two
names for the same option.
### bootnodes
```bash tab="Syntax"
@ -82,10 +86,10 @@ Specify bootnodes when connecting to a [private network](../Configuring-Pantheon
Specifying a node is a [bootnode](../Configuring-Pantheon/Testing-Developing-Nodes.md#bootnodes)
must be done on the command line not in a [configuration file](../Configuring-Pantheon/Using-Configuration-File.md).
### config
### config-file
```bash tab="Syntax"
--config=<PATH>
--config-file=<FILE>
```
```bash tab="Example Command Line"
@ -98,18 +102,18 @@ The default is `none`.
!!!note
This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#custom-configuration-file) or in a [configuration file](../Configuring-Pantheon/Using-Configuration-File.md).
### datadir
### data-path
```bash tab="Syntax"
--datadir=<PATH>
--data-path=<PATH>
```
```bash tab="Example Command Line"
--datadir=/home/me/me_node
--data-path=/home/me/me_node
```
```bash tab="Example Configuration File"
datadir="/home/me/me_node"
data-path="/home/me/me_node"
```
The path to the Pantheon data directory. The default is the `/build/distributions/pantheon-<version>` directory in the Pantheon installation directory.
@ -120,6 +124,10 @@ The path to the Pantheon data directory. The default is the `/build/distribution
### dev-mode
!!!important
This option is deprecated in favor of the new `--network` option.
It will be completely removed in the 0.9 release.
```bash tab="Syntax"
--dev-mode
```
@ -139,18 +147,18 @@ Default is `false`.
The [`--dev-mode`](#dev-mode) option overrides the [`--genesis`](#genesis) option. If both are specified, the development mode configuration is used.
### genesis
### genesis-file
```bash tab="Syntax"
--genesis=<PATH>
--genesis-file=<FILE>
```
```bash tab="Example Command Line"
--genesis=/home/me/me_node/customGenesisFile.json
--genesis-file=/home/me/me_node/customGenesisFile.json
```
```bash tab="Example Configuration File"
genesis="/home/me/me_node/customGenesisFile.json"
genesis-file="/home/me/me_node/customGenesisFile.json"
```
The path to the genesis file. The default is the embedded genesis file for the Ethereum mainnet.
@ -166,6 +174,10 @@ When using this option, it is recommended to also set the [`--network-id`](#netw
### goerli
!!!important
This option is deprecated in favor of the new `--network` option.
It will be completely removed in the 0.9 release.
```bash tab="Syntax"
--goerli
```
@ -221,6 +233,10 @@ The default is 25.
### max-trailing-peers
!!!important
This option is deprecated in favor of a intelligent default setting and will be removed in 0.9
release.
```bash tab="Syntax"
--max-trailing-peers=<INTEGER>
```
@ -249,22 +265,39 @@ metrics-enabled=true
Set to `true` to enable the [Prometheus](https://prometheus.io/) monitoring service to access [Pantheon metrics](../Using-Pantheon/Debugging.md#monitor-node-performance-using-third-party-clients).
The default is `false`.
### metrics-listen
### metrics-host
```bash tab="Syntax"
--metrics-host=<HOST>
```
```bash tab="Example Command Line"
--metrics-host=127.0.0.1
```
```bash tab="Example Configuration File"
metrics-host="127.0.0.1"
```
Specifies the host on which the [Prometheus](https://prometheus.io/) monitoring service accesses Pantheon
metrics. The default is `127.0.0.1`. The metrics server respects the [`--host-whitelist` option](#host-whitelist).
### metrics-port
```bash tab="Syntax"
--metrics-listen=<HOST:PORT>
--metrics-port=<PORT>
```
```bash tab="Example Command Line"
--metrics-listen=127.0.0.1:6174
--metrics-port=6174
```
```bash tab="Example Configuration File"
metrics-listen="127.0.0.1:6174"
metrics-port="6174"
```
Specifies the host and port on which the [Prometheus](https://prometheus.io/) monitoring service accesses Pantheon
metrics. The default is `127.0.0.1:9545`. The metrics server respects the [`--host-whitelist` option](#host-whitelist).
Specifies the port on which the [Prometheus](https://prometheus.io/) monitoring service accesses Pantheon
metrics. The default is `9545`. The metrics server respects the [`--host-whitelist` option](#host-whitelist).
### miner-coinbase
@ -300,35 +333,35 @@ miner-enabled=true
Enables mining when the node is started.
Default is `false`.
### miner-extraData
### miner-extra-data
```bash tab="Syntax"
--miner-extraData=<Extra data>
--miner-extra-data=<Extra data>
```
```bash tab="Example Command Line"
--miner-extraData=0x444F4E27542050414E4943202120484F444C2C20484F444C2C20484F444C2021
--miner-extra-data=0x444F4E27542050414E4943202120484F444C2C20484F444C2C20484F444C2021
```
```bash tab="Example Configuration File"
miner-extraData="0x444F4E27542050414E4943202120484F444C2C20484F444C2C20484F444C2021"
miner-extra-data="0x444F4E27542050414E4943202120484F444C2C20484F444C2C20484F444C2021"
```
A hex string representing the 32 bytes to be included in the extra data field of a mined block.
The default is 0x.
### miner-minTransactionGasPriceWei
### min-gas-price
```bash tab="Syntax"
--miner-minTransactionGasPriceWei=<minTransactionGasPrice>
--min-gas-price=<minTransactionGasPrice>
```
```bash tab="Example Command Line"
--miner-minTransactionGasPriceWei=1337
--min-gas-price=1337
```
```bash tab="Example Configuration File"
miner-minTransactionGasPriceWei="1337"
min-gas-price="1337"
```
The minimum price that a transaction offers for it to be included in a mined block.
@ -336,6 +369,10 @@ The default is 1000.
### network-id
!!!important
This option is deprecated in favor of the new `--network` option.
It will be completely removed in the 0.9 release.
```bash tab="Syntax"
--network-id=<INTEGER>
```
@ -364,21 +401,21 @@ no-discovery=true
Disables P2P peer discovery.
The default is `false`.
### node-private-key
### node-private-key-file
```bash tab="Syntax"
--node-private-key=<PATH>
--node-private-key-file=<FILE>
```
```bash tab="Example Command Line"
--node-private-key=/home/me/me_node/myPrivateKey
--node-private-key-file=/home/me/me_node/myPrivateKey
```
```bash tab="Example Configuration File"
node-private-key="/home/me/me_node/myPrivateKey"
node-private-key-file="/home/me/me_node/myPrivateKey"
```
`<PATH>` is the path of the private key file of the node.
`<FILE>` is the path of the private key file of the node.
The default is the key file in the data directory.
If no key file exists, a key file containing the generated private key is created;
otherwise, the existing key file specifies the node private key.
@ -416,6 +453,10 @@ Not intended for use with mainnet or public testnets.
### ottoman
!!!important
This option is deprecated in favor of the new `--network` option.
It will be completely removed in the 0.9 release.
```bash tab="Syntax"
--ottoman
```
@ -429,29 +470,54 @@ Synchronize against the Ottoman test network. This is only useful if you are usi
!!!note
:construction: IBFT is not currently supported. Support for IBFT is in active development.
### p2p-listen
### p2p-host
```bash tab="Syntax"
--p2p-listen=<HOST:PORT>
--p2p-host=<HOST>
```
```bash tab="Example Command Line"
# to listen on all interfaces on port 1789
--p2p-listen=0.0.0.0:1789
# to listen on all interfaces
--p2p-host=0.0.0.0
```
```bash tab="Example Configuration File"
p2p-listen="0.0.0.0:1789"
p2p-host="0.0.0.0"
```
Specifies the host and port on which P2P peer discovery listens.
The default is 127.0.0.1:30303.
Specifies the host on which P2P peer discovery listens.
The default is 127.0.0.1.
!!!note
This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#exposing-ports).
### p2p-port
```bash tab="Syntax"
--p2p-port=<PORT>
```
```bash tab="Example Command Line"
# to listen on port 1789
--p2p-port=1789
```
```bash tab="Example Configuration File"
p2p-port="1789"
```
Specifies the port on which P2P peer discovery listens.
The default is 30303.
!!!note
This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#exposing-ports).
### rinkeby
!!!important
This option is deprecated in favor of the new `--network` option.
It will be completely removed in the 0.9 release.
```bash tab="Syntax"
--rinkeby
```
@ -466,6 +532,10 @@ Default is `false`.
### ropsten
!!!important
This option is deprecated in favor of the new `--network` option.
It will be completely removed in the 0.9 release.
```bash tab="Syntax"
--ropsten
```
@ -480,82 +550,107 @@ Default is `false`.
!!!note
This option is only available only from v0.8.2. For v0.8.1, refer to [Starting Pantheon](../Getting-Started/Starting-Pantheon.md#run-a-node-on-ropsten-testnet).
### rpc-enabled
### rpc-http-enabled
```bash tab="Syntax"
--rpc-enabled
--rpc-http-enabled
```
```bash tab="Example Configuration File"
rpc-enabled=true
rpc-http-enabled=true
```
Set to `true` to enable the JSON-RPC service (RPC over HTTP).
Set to `true` to enable the HTTP JSON-RPC service.
The default is `false`.
### rpc-listen
### rpc-http-host
```bash tab="Syntax"
--rpc-http-host=<HOST>
```
```bash tab="Example Command Line"
# to listen on all interfaces
--rpc-http-host=0.0.0.0
```
```bash tab="Example Configuration File"
rpc-http-host="0.0.0.0"
```
Specifies the host on which HTTP JSON-RPC listens.
The default is 127.0.0.1.
!!!note
This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#exposing-ports).
### rpc-http-port
```bash tab="Syntax"
--rpc-listen=<HOST:PORT>
--rpc-http-port=<PORT>
```
```bash tab="Example Command Line"
# to listen on all interfaces on port 3435
--rpc-listen=0.0.0.0:3435
# to listen on port 3435
--rpc-http-port=3435
```
```bash tab="Example Configuration File"
rpc-listen="0.0.0.0:3435"
rpc-http-port="3435"
```
Specifies the host and port on which JSON-RPC listens.
The default is 127.0.0.1:8545.
Specifies the port on which HTTP JSON-RPC listens.
The default is 8545.
!!!note
This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#exposing-ports).
### rpc-api
### rpc-http-api
```bash tab="Syntax"
--rpc-api=<api name>[,<api name>...]...
--rpc-http-api=<api name>[,<api name>...]...
```
```bash tab="Example Command Line"
--rpc-api=ETH,NET,WEB3
--rpc-http-api=ETH,NET,WEB3
```
```bash tab="Example Configuration File"
rpc-api=["ETH","NET","WEB3"]
rpc-http-api=["ETH","NET","WEB3"]
```
Comma-separated APIs to enable on the JSON-RPC channel.
When you use this option, the `--rpc-enabled` option must also be specified.
Comma-separated APIs to enable on the HTTP JSON-RPC channel.
When you use this option, the `--rpc-http-enabled` option must also be specified.
The available API options are: `ADMIN`, `ETH`, `NET`, `WEB3`, `CLIQUE`, `IBFT`, `DEBUG`, and `MINER`.
The default is: `ETH`, `NET`, `WEB3`, `CLIQUE`, `IBFT`.
!!!note
:construction: IBFT is not currently supported. Support for IBFT is in active development.
### rpc-cors-origins
!!!tip
The singular `--rpc-http-api` and plural `--rpc-http-apis` are available and are just two
names for the same option.
### rpc-http-cors-origins
```bash tab="Syntax"
--rpc-cors-origins=<rpcCorsAllowedOrigins> or all
--rpc-http-cors-origins=<url>[,<url>...]... or all or *
```
```bash tab="Example Command Line"
# You can whitelist one or more domains with a comma-separated list.
--rpc-cors-origins="http://medomain.com","https://meotherdomain.com"
--rpc-http-cors-origins="http://medomain.com","https://meotherdomain.com"
```
```bash tab="Example Configuration File"
rpc-cors-origins=["http://medomain.com","https://meotherdomain.com"]
rpc-http-cors-origins=["http://medomain.com","https://meotherdomain.com"]
```
```bash tab="Remix IDE domain example"
# The following allows Remix to interact with your Pantheon node without using MetaMask.
--rpc-cors-origins="http://remix.ethereum.org"
--rpc-http-cors-origins="http://remix.ethereum.org"
```
Specifies domain URLs for CORS validation.
@ -573,72 +668,98 @@ If you don't whitelist any domains, you won't be able to use webapps to interact
If Remix is connecting to the node through MetaMask, it also does not require CORS validation.
!!!tip
For development purposes, you can use `"all"` to accept requests from any domain, but we don't recommend this for production code.
For development purposes, you can use `"all"` or `"*"` to accept requests from any domain,
but we don't recommend this for production code.
### ws-enabled
### rpc-ws-enabled
```bash tab="Syntax"
--ws-enabled
--rpc-ws-enabled
```
```bash tab="Example Configuration File"
ws-enabled=true
rpc-ws-enabled=true
```
Set to `true` to enable the WS-RPC (WebSockets) service.
Set to `true` to enable the WebSockets JSON-RPC service.
The default is `false`.
### ws-api
### rpc-ws-api
```bash tab="Syntax"
--ws-api=<api name>[,<api name>...]...
--rpc-ws-api=<api name>[,<api name>...]...
```
```bash tab="Example Command Line"
--ws-api=ETH,NET,WEB3
--rpc-ws-api=ETH,NET,WEB3
```
```bash tab="Example Configuration File"
ws-api=["ETH","NET","WEB3"]
rpc-ws-api=["ETH","NET","WEB3"]
```
Comma-separated APIs to enable on Websockets channel.
When you use this option, the `--ws-enabled` option must also be specified.
When you use this option, the `--rpc-ws-enabled` option must also be specified.
The available API options are: `ETH`, `NET`, `WEB3`, `CLIQUE`, `IBFT`, `DEBUG`, and `MINER`.
The default is: `ETH`, `NET`, `WEB3`, `CLIQUE`, `IBFT`.
!!!note
:construction: IBFT is not currently supported. Support for IBFT is in active development.
### ws-listen
!!!tip
The singular `--rpc-ws-api` and plural `--rpc-ws-apis` are available and are just two
names for the same option.
### rpc-ws-host
```bash tab="Syntax"
--ws-listen=<HOST:PORT>
--ws-host=<HOST>
```
```bash tab="Example Command Line"
# to listen on all interfaces on port 6174
--ws-listen=0.0.0.0:6174
# to listen on all interfaces
--ws-host=0.0.0.0
```
```bash tab="Example Configuration File"
ws-listen="0.0.0.0:6174"
ws-host="0.0.0.0"
```
Host and port for WS-RPC (Websocket) to listen on.
The default is 127.0.0.1:8546.
Host for Websocket WS-RPC to listen on.
The default is 127.0.0.1.
!!!note
This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#exposing-ports).
### ws-refresh-delay
### rpc-ws-port
```bash tab="Syntax"
--ws-refresh-delay=<refresh delay>
--ws-port=<PORT>
```
```bash tab="Example Command Line"
# to listen on port 6174
--ws-port=6174
```
```bash tab="Example Configuration File"
ws-port="6174"
```
Port for Websocket WS-RPC to listen on.
The default is 8546.
!!!note
This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#exposing-ports).
### rpc-ws-refresh-delay
```bash tab="Syntax"
--rpc-ws-refresh-delay=<refresh delay>
```
```bash tab="Example"
--ws-refresh-delay="10000"
--rpc-ws-refresh-delay="10000"
```
Refresh delay for Websocket synchronizing subscription in milliseconds.
@ -683,26 +804,34 @@ Print version information and exit.
Pantheon subcommands are:
### import
### blocks
This command provides blocks related actions.
#### import
```bash tab="Syntax"
$ pantheon import <block-file>
$ pantheon blocks import --from=<block-file>
```
```bash tab="Example"
$ pantheon import /home/me/me_project/mainnet.blocks
$ pantheon blocks import --from=/home/me/me_project/mainnet.blocks
```
Imports blocks from the specified file into the blockchain database
### export-pub-key
### public-key
This command provides node public key related actions.
#### export
```bash tab="Syntax"
$ pantheon export-pub-key <key-file>
$ pantheon public-key export --to=<key-file>
```
```bash tab="Example"
$ pantheon export-pub-key /home/me/me_project/not_precious_pub_key
$ pantheon public-key export --to=/home/me/me_project/not_precious_pub_key
```
Exports node public key to the specified file.

@ -0,0 +1,140 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.cli;
import static com.google.common.base.Preconditions.checkNotNull;
import static tech.pegasys.pantheon.cli.BlocksSubCommand.COMMAND_NAME;
import static tech.pegasys.pantheon.cli.DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP;
import tech.pegasys.pantheon.cli.BlocksSubCommand.ImportSubCommand;
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
import tech.pegasys.pantheon.metrics.prometheus.MetricsHttpService;
import tech.pegasys.pantheon.util.BlockImporter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Path;
import java.util.Optional;
import io.vertx.core.Vertx;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.ExecutionException;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.ParentCommand;
import picocli.CommandLine.Spec;
/** Blocks related sub-command */
@Command(
name = COMMAND_NAME,
description = "This command provides blocks related actions.",
mixinStandardHelpOptions = true,
subcommands = {ImportSubCommand.class}
)
class BlocksSubCommand implements Runnable {
private static final Logger LOG = LogManager.getLogger();
static final String COMMAND_NAME = "blocks";
@SuppressWarnings("unused")
@ParentCommand
private PantheonCommand parentCommand; // Picocli injects reference to parent command
@SuppressWarnings("unused")
@Spec
private CommandSpec spec; // Picocli injects reference to command spec
private final BlockImporter blockImporter;
private final PrintStream out;
BlocksSubCommand(final BlockImporter blockImporter, final PrintStream out) {
this.blockImporter = blockImporter;
this.out = out;
}
@Override
public void run() {
spec.commandLine().usage(out);
}
/**
* blocks import sub-command
*
* <p>Imports blocks from a file into the database
*/
@Command(
name = "import",
description = "This command imports blocks from a file into the database.",
mixinStandardHelpOptions = true
)
static class ImportSubCommand implements Runnable {
@SuppressWarnings("unused")
@ParentCommand
private BlocksSubCommand parentCommand; // Picocli injects reference to parent command
@Option(
names = "--from",
required = true,
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "File containing blocks to import",
arity = "1..1"
)
private final File blocksImportFile = null;
@Override
public void run() {
LOG.info("Runs import sub command with blocksImportFile : {}", blocksImportFile);
checkNotNull(parentCommand);
checkNotNull(parentCommand.parentCommand);
checkNotNull(parentCommand.blockImporter);
Optional<MetricsHttpService> metricsHttpService = Optional.empty();
try {
final MetricsConfiguration metricsConfiguration =
parentCommand.parentCommand.metricsConfiguration();
if (metricsConfiguration.isEnabled()) {
metricsHttpService =
Optional.of(
new MetricsHttpService(
Vertx.vertx(),
metricsConfiguration,
parentCommand.parentCommand.getMetricsSystem()));
metricsHttpService.ifPresent(MetricsHttpService::start);
}
// As blocksImportFile even if initialized as null is injected by PicoCLI and param is
// mandatory
// So we are sure it's always not null, we can remove the warning
//noinspection ConstantConditions
Path path = blocksImportFile.toPath();
parentCommand.blockImporter.importBlockchain(
path, parentCommand.parentCommand.buildController());
} catch (final FileNotFoundException e) {
throw new ExecutionException(
new CommandLine(this), "Could not find file to import: " + blocksImportFile);
} catch (final IOException e) {
throw new ExecutionException(
new CommandLine(this), "Unable to import blocks from " + blocksImportFile, e);
} finally {
metricsHttpService.ifPresent(MetricsHttpService::stop);
}
}
}
}

@ -12,6 +12,10 @@
*/
package tech.pegasys.pantheon.cli;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.eth.sync.SyncMode;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
@ -23,13 +27,30 @@ import java.nio.file.Paths;
import picocli.CommandLine;
interface DefaultCommandValues {
String CONFIG_FILE_OPTION_NAME = "--config";
String CONFIG_FILE_OPTION_NAME = "--config-file";
String MANDATORY_PATH_FORMAT_HELP = "<PATH>";
String MANDATORY_FILE_FORMAT_HELP = "<FILE>";
String PANTHEON_HOME_PROPERTY_NAME = "pantheon.home";
String DEFAULT_DATA_DIR_PATH = "./build/data";
String MANDATORY_INTEGER_FORMAT_HELP = "<INTEGER>";
String MANDATORY_MODE_FORMAT_HELP = "<MODE>";
String MANDATORY_NETWORK_FORMAT_HELP = "<CHAIN>";
String MANDATORY_NODE_ID_FORMAT_HELP = "<NODEID>";
Wei DEFAULT_MIN_TRANSACTION_GAS_PRICE = Wei.of(1000);
BytesValue DEFAULT_EXTRA_DATA = BytesValue.EMPTY;
long DEFAULT_MAX_REFRESH_DELAY = 3600000;
long DEFAULT_MIN_REFRESH_DELAY = 1;
String DOCKER_GENESIS_LOCATION = "/etc/pantheon/genesis.json";
String DOCKER_DATADIR_LOCATION = "/var/lib/pantheon";
String MANDATORY_HOST_FORMAT_HELP = "<HOST>";
String MANDATORY_PORT_FORMAT_HELP = "<PORT>";
// Default should be FAST for the next release
// but we use FULL for the moment as Fast is still in progress
SyncMode DEFAULT_SYNC_MODE = SyncMode.FULL;
int DEFAULT_MAX_PEERS = 25;
static Path getDefaultPantheonDataDir(final Object command) {
static Path getDefaultPantheonDataPath(final Object command) {
// this property is retrieved from Gradle tasks or Pantheon running shell script.
final String pantheonHomeProperty = System.getProperty(PANTHEON_HOME_PROPERTY_NAME);
final Path pantheonHome;

@ -1,67 +0,0 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.cli;
import static java.nio.charset.StandardCharsets.UTF_8;
import tech.pegasys.pantheon.controller.PantheonController;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import picocli.CommandLine.Command;
import picocli.CommandLine.Parameters;
import picocli.CommandLine.ParentCommand;
// Export of the public key takes a file as parameter to export directly by writing the key in the
// file. A direct output of the key to sdt out is not done because we don't want the key value
// to be polluted by other information like logs that are in KeyPairUtil that is inevitable.
@Command(
name = "export-pub-key",
description = "This exports node public key in a file.",
mixinStandardHelpOptions = true
)
class ExportPublicKeySubCommand implements Runnable {
private static final Logger LOG = LogManager.getLogger();
@Parameters(arity = "1..1", paramLabel = "PATH", description = "File to write public key to")
private final File publicKeyExportFile = null;
@SuppressWarnings("unused")
@ParentCommand
private PantheonCommand parentCommand; // Picocli injects reference to parent command
@Override
public void run() {
final PantheonController<?> controller = parentCommand.buildController();
final KeyPair keyPair = controller.getLocalNodeKeyPair();
// this publicKeyExportFile can never be null because of Picocli arity requirement
//noinspection ConstantConditions
final Path path = publicKeyExportFile.toPath();
try (final BufferedWriter fileWriter = Files.newBufferedWriter(path, UTF_8)) {
fileWriter.write(keyPair.getPublicKey().toString());
} catch (final IOException e) {
LOG.error("An error occurred while trying to write the public key", e);
}
}
}

@ -1,83 +0,0 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.cli;
import static com.google.common.base.Preconditions.checkNotNull;
import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration;
import tech.pegasys.pantheon.metrics.prometheus.MetricsHttpService;
import tech.pegasys.pantheon.util.BlockImporter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import io.vertx.core.Vertx;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.ExecutionException;
import picocli.CommandLine.Parameters;
import picocli.CommandLine.ParentCommand;
@Command(
name = "import",
description = "This command imports blocks from a file into the database.",
mixinStandardHelpOptions = true
)
class ImportSubCommand implements Runnable {
private static final Logger LOG = LogManager.getLogger();
@ParentCommand
private PantheonCommand parentCommand; // Picocli injects reference to parent command
@Parameters(arity = "1..1", paramLabel = "PATH", description = "File containing blocks to import")
private final Path blocksImportPath = null;
private final BlockImporter blockImporter;
ImportSubCommand(final BlockImporter blockImporter) {
this.blockImporter = blockImporter;
}
@Override
public void run() {
LOG.info("Runs import sub command with blocksImportPath : {}", blocksImportPath);
checkNotNull(parentCommand);
checkNotNull(blockImporter);
Optional<MetricsHttpService> metricsHttpService = Optional.empty();
try {
final MetricsConfiguration metricsConfiguration = parentCommand.metricsConfiguration();
if (metricsConfiguration.isEnabled()) {
metricsHttpService =
Optional.of(
new MetricsHttpService(
Vertx.vertx(), metricsConfiguration, parentCommand.getMetricsSystem()));
metricsHttpService.ifPresent(MetricsHttpService::start);
}
blockImporter.importBlockchain(blocksImportPath, parentCommand.buildController());
} catch (final FileNotFoundException e) {
throw new ExecutionException(
new CommandLine(this), "Could not find file to import: " + blocksImportPath);
} catch (final IOException e) {
throw new ExecutionException(
new CommandLine(this), "Unable to import blocks from " + blocksImportPath, e);
} finally {
metricsHttpService.ifPresent(MetricsHttpService::stop);
}
}
}

@ -0,0 +1,21 @@
/*
* 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.cli;
public enum NetworkName {
MAINNET,
OTTOMAN,
RINKEBY,
ROPSTEN,
GOERLI
}

@ -14,7 +14,14 @@ package tech.pegasys.pantheon.cli;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.nio.charset.StandardCharsets.UTF_8;
import static tech.pegasys.pantheon.cli.DefaultCommandValues.getDefaultPantheonDataDir;
import static tech.pegasys.pantheon.cli.DefaultCommandValues.getDefaultPantheonDataPath;
import static tech.pegasys.pantheon.cli.NetworkName.MAINNET;
import static tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration.DEFAULT_JSON_RPC_PORT;
import static tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration.DEFAULT_WEBSOCKET_PORT;
import static tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration.DEFAULT_WEBSOCKET_REFRESH_DELAY;
import static tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer.DEFAULT_PORT;
import static tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PORT;
import static tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration.createDefault;
import tech.pegasys.pantheon.Runner;
import tech.pegasys.pantheon.RunnerBuilder;
@ -62,6 +69,7 @@ import java.util.stream.Stream;
import com.google.common.io.Resources;
import com.google.common.net.HostAndPort;
import com.google.common.net.HostSpecifier;
import io.vertx.core.Vertx;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator;
@ -90,24 +98,6 @@ import picocli.CommandLine.ParameterException;
)
public class PantheonCommand implements DefaultCommandValues, Runnable {
private static final int DEFAULT_MAX_PEERS = 25;
// Default should be FAST for the next release
// but we use FULL for the moment as Fast is still in progress
private static final SyncMode DEFAULT_SYNC_MODE = SyncMode.FULL;
private static final String MANDATORY_HOST_AND_PORT_FORMAT_HELP = "<HOST:PORT>";
private static final String MANDATORY_INTEGER_FORMAT_HELP = "<INTEGER>";
private static final String MANDATORY_MODE_FORMAT_HELP = "<MODE>";
private static final Wei DEFAULT_MIN_TRANSACTION_GAS_PRICE = Wei.of(1000);
private static final BytesValue DEFAULT_EXTRA_DATA = BytesValue.EMPTY;
private static final long DEFAULT_MAX_REFRESH_DELAY = 3600000;
private static final long DEFAULT_MIN_REFRESH_DELAY = 1;
private static final String DOCKER_GENESIS_LOCATION = "/etc/pantheon/genesis.json";
private static final String DOCKER_DATADIR_LOCATION = "/var/lib/pantheon";
public static class RpcApisConverter implements ITypeConverter<RpcApi> {
@Override
@ -149,7 +139,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
// Options parsing is done with CLI library Picocli https://picocli.info/
@Option(
names = {"--node-private-key"},
names = {"--node-private-key-file"},
paramLabel = MANDATORY_PATH_FORMAT_HELP,
description =
"the path to the node's private key file (default: a file named \"key\" in the Pantheon data folder)"
@ -202,6 +192,12 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
)
private final Integer maxPeers = DEFAULT_MAX_PEERS;
/**
* @deprecated This option is not exposed anymore even if still available as it's bounded by
* max-peers value. It's not useful enough to figure in the help. Will probably completely
* removed in next version.
*/
@Deprecated
@Option(
names = {"--max-trailing-peers"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
@ -211,7 +207,8 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
private final Integer maxTrailingPeers = Integer.MAX_VALUE;
@Option(
names = {"--banned-nodeids"},
names = {"--banned-node-ids", "--banned-node-id"},
paramLabel = MANDATORY_NODE_ID_FORMAT_HELP,
description = "A list of node IDs to ban from the p2p network.",
split = ",",
arity = "1..*"
@ -228,6 +225,17 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
// )
private final SyncMode syncMode = DEFAULT_SYNC_MODE;
@Option(
names = {"--network"},
paramLabel = MANDATORY_NETWORK_FORMAT_HELP,
description =
"Synchronize against the indicated network, possible values are ${COMPLETION-CANDIDATES}."
+ " (default: ${DEFAULT-VALUE})"
)
private final NetworkName network = MAINNET;
/** @deprecated Deprecated in favour of --network option */
@Deprecated
// Boolean option to indicate if the client have to sync against the ottoman test network
// (see https://github.com/ethereum/EIPs/issues/650).
@Option(
@ -238,6 +246,8 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
)
private final Boolean syncWithOttoman = false;
/** @deprecated Deprecated in favour of --network option */
@Deprecated
@Option(
names = {"--rinkeby"},
description =
@ -246,12 +256,16 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
)
private final Boolean rinkeby = false;
/** @deprecated Deprecated in favour of --network option */
@Deprecated
@Option(
names = {"--ropsten"},
description = "Use the Ropsten test network (default: ${DEFAULT-VALUE})"
)
private final Boolean ropsten = false;
/** @deprecated Deprecated in favour of --network option */
@Deprecated
@Option(
names = {"--goerli"},
description = "Use the Goerli test network (default: ${DEFAULT-VALUE})"
@ -259,13 +273,24 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
private final Boolean goerli = false;
@Option(
names = {"--p2p-listen"},
paramLabel = MANDATORY_HOST_AND_PORT_FORMAT_HELP,
description = "Host and port for p2p peers discovery to listen on (default: ${DEFAULT-VALUE})",
names = {"--p2p-host"},
paramLabel = MANDATORY_HOST_FORMAT_HELP,
description = "Host for p2p peers discovery to listen on (default: ${DEFAULT-VALUE})",
arity = "1"
)
private final HostAndPort p2pHostAndPort = getDefaultHostAndPort(DefaultPeer.DEFAULT_PORT);
private final HostSpecifier p2pHost =
HostSpecifier.fromValid(autoDiscoverDefaultIP().getHostAddress());
@Option(
names = {"--p2p-port"},
paramLabel = MANDATORY_PORT_FORMAT_HELP,
description = "Port for p2p peers discovery to listen on (default: ${DEFAULT-VALUE})",
arity = "1"
)
private final Integer p2pPort = DEFAULT_PORT;
/** @deprecated Deprecated in favour of --network option */
@Deprecated
@Option(
names = {"--network-id"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
@ -275,29 +300,38 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
private final Integer networkId = null;
@Option(
names = {"--rpc-enabled"},
names = {"--rpc-http-enabled"},
description = "Set if the JSON-RPC service should be started (default: ${DEFAULT-VALUE})"
)
private final Boolean isJsonRpcEnabled = false;
private final Boolean isHttpRpcEnabled = false;
@Option(
names = {"--rpc-http-host"},
paramLabel = MANDATORY_HOST_FORMAT_HELP,
description = "Host for HTTP JSON-RPC to listen on (default: ${DEFAULT-VALUE})",
arity = "1"
)
private final HostSpecifier rpcHttpHost =
HostSpecifier.fromValid(autoDiscoverDefaultIP().getHostAddress());
@Option(
names = {"--rpc-listen"},
paramLabel = MANDATORY_HOST_AND_PORT_FORMAT_HELP,
description = "Host and port for JSON-RPC to listen on (default: ${DEFAULT-VALUE})",
names = {"--rpc-http-port"},
paramLabel = MANDATORY_PORT_FORMAT_HELP,
description = "Port for HTTP JSON-RPC to listen on (default: ${DEFAULT-VALUE})",
arity = "1"
)
private final HostAndPort rpcHostAndPort =
getDefaultHostAndPort(JsonRpcConfiguration.DEFAULT_JSON_RPC_PORT);
private final Integer rpcHttpPort = DEFAULT_JSON_RPC_PORT;
// A list of origins URLs that are accepted by the JsonRpcHttpServer (CORS)
@Option(
names = {"--rpc-cors-origins"},
names = {"--rpc-http-cors-origins"},
description = "Comma separated origin domain URLs for CORS validation (default: none)"
)
private final CorsAllowedOriginsProperty rpcCorsAllowedOrigins = new CorsAllowedOriginsProperty();
private final CorsAllowedOriginsProperty rpcHttpCorsAllowedOrigins =
new CorsAllowedOriginsProperty();
@Option(
names = {"--rpc-api"},
names = {"--rpc-http-api", "--rpc-http-apis"},
paramLabel = "<api name>",
split = ",",
arity = "1..*",
@ -305,26 +339,34 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
description = "Comma separated APIs to enable on JSON-RPC channel. default: ${DEFAULT-VALUE}",
defaultValue = "ETH,NET,WEB3,CLIQUE,IBFT"
)
private final Collection<RpcApi> rpcApis = null;
private final Collection<RpcApi> rpcHttpApis = null;
@Option(
names = {"--ws-enabled"},
names = {"--rpc-ws-enabled"},
description =
"Set if the WS-RPC (WebSocket) service should be started (default: ${DEFAULT-VALUE})"
)
private final Boolean isWsRpcEnabled = false;
private final Boolean isRpcWsEnabled = false;
@Option(
names = {"--rpc-ws-host"},
paramLabel = MANDATORY_HOST_FORMAT_HELP,
description = "Host for WebSocket JSON-RPC to listen on (default: ${DEFAULT-VALUE})",
arity = "1"
)
private final HostSpecifier rpcWsHost =
HostSpecifier.fromValid(autoDiscoverDefaultIP().getHostAddress());
@Option(
names = {"--ws-listen"},
paramLabel = MANDATORY_HOST_AND_PORT_FORMAT_HELP,
description = "Host and port for WS-RPC (WebSocket) to listen on (default: ${DEFAULT-VALUE})",
names = {"--rpc-ws-port"},
paramLabel = MANDATORY_PORT_FORMAT_HELP,
description = "Port for WebSocket JSON-RPC to listen on (default: ${DEFAULT-VALUE})",
arity = "1"
)
private final HostAndPort wsHostAndPort =
getDefaultHostAndPort(WebSocketConfiguration.DEFAULT_WEBSOCKET_PORT);
private final Integer rpcWsPort = DEFAULT_WEBSOCKET_PORT;
@Option(
names = {"--ws-api"},
names = {"--rpc-ws-api", "--rpc-ws-apis"},
paramLabel = "<api name>",
split = ",",
arity = "1..*",
@ -332,29 +374,29 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
description = "Comma separated APIs to enable on WebSocket channel. default: ${DEFAULT-VALUE}",
defaultValue = "ETH,NET,WEB3,CLIQUE,IBFT"
)
private final Collection<RpcApi> wsApis = null;
private final Collection<RpcApi> rpcWsApis = null;
private Long refreshDelay;
private Long rpcWsRefreshDelay;
@Option(
names = {"--ws-refresh-delay"},
names = {"--rpc-ws-refresh-delay"},
paramLabel = "<refresh delay>",
arity = "1",
description =
"Refresh delay of websocket subscription sync in milliseconds. "
+ "default: ${DEFAULT-VALUE}",
defaultValue = "" + WebSocketConfiguration.DEFAULT_WEBSOCKET_REFRESH_DELAY
defaultValue = "" + DEFAULT_WEBSOCKET_REFRESH_DELAY
)
private Long configureRefreshDelay(final Long refreshDelay) {
if (refreshDelay < DEFAULT_MIN_REFRESH_DELAY || refreshDelay > DEFAULT_MAX_REFRESH_DELAY) {
throw new ParameterException(
new CommandLine(this),
String.format(
"refreshDelay must be a positive integer between %s and %s",
"Refresh delay must be a positive integer between %s and %s",
String.valueOf(DEFAULT_MIN_REFRESH_DELAY),
String.valueOf(DEFAULT_MAX_REFRESH_DELAY)));
}
this.refreshDelay = refreshDelay;
this.rpcWsRefreshDelay = refreshDelay;
return refreshDelay;
}
@ -365,23 +407,33 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
private final Boolean isMetricsEnabled = false;
@Option(
names = {"--metrics-listen"},
paramLabel = MANDATORY_HOST_AND_PORT_FORMAT_HELP,
description = "Host and port for the metrics exporter to listen on (default: ${DEFAULT-VALUE})",
names = {"--metrics-host"},
paramLabel = MANDATORY_HOST_FORMAT_HELP,
description = "Host for the metrics exporter to listen on (default: ${DEFAULT-VALUE})",
arity = "1"
)
private final HostAndPort metricsHostAndPort =
getDefaultHostAndPort(MetricsConfiguration.DEFAULT_METRICS_PORT);
private final HostSpecifier metricsHost =
HostSpecifier.fromValid(autoDiscoverDefaultIP().getHostAddress());
@Option(
names = {"--metrics-port"},
paramLabel = MANDATORY_PORT_FORMAT_HELP,
description = "Port for the metrics exporter to listen on (default: ${DEFAULT-VALUE})",
arity = "1"
)
private final Integer metricsPort = DEFAULT_METRICS_PORT;
@Option(
names = {"--host-whitelist"},
paramLabel = "<hostname>",
paramLabel = "<hostname>[,<hostname>...]... or * or all",
description =
"Comma separated list of hostnames to whitelist for RPC access. default: ${DEFAULT-VALUE}",
"Comma separated list of hostnames to whitelist for RPC access or * or all to accept any host. default: ${DEFAULT-VALUE}",
defaultValue = "localhost"
)
private final JsonRPCWhitelistHostsProperty hostsWhitelist = new JsonRPCWhitelistHostsProperty();
/** @deprecated Deprecated in favour of --network option */
@Deprecated
@Option(
names = {"--dev-mode"},
description =
@ -392,7 +444,9 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
@Option(
names = {"--logging", "-l"},
description = "Logging verbosity: OFF, FATAL, WARN, INFO, DEBUG, TRACE, ALL (default: INFO)."
paramLabel = "<LOG VERBOSITY LEVEL>",
description =
"Logging verbosity levels: OFF, FATAL, WARN, INFO, DEBUG, TRACE, ALL (default: INFO)."
)
private final Level logLevel = null;
@ -405,23 +459,23 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
@Option(
names = {"--miner-coinbase"},
description =
"the account to which mining rewards are to be paid, must be specified if "
+ "mining is enabled.",
"account to which mining rewards are paid. You must specify a valid coinbase if "
+ "mining is enabled using --miner-enabled option.",
arity = "1"
)
private final Address coinbase = null;
@Option(
names = {"--miner-minTransactionGasPriceWei"},
names = {"--min-gas-price"},
description =
"the minimum price offered by a transaction for it to be included in a mined "
"the minimum price (in Wei) offered by a transaction for it to be included in a mined "
+ "block (default: ${DEFAULT-VALUE}).",
arity = "1"
)
private final Wei minTransactionGasPrice = DEFAULT_MIN_TRANSACTION_GAS_PRICE;
@Option(
names = {"--miner-extraData"},
names = {"--miner-extra-data"},
description =
"a hex string representing the (32) bytes to be included in the extra data "
+ "field of a mined block. (default: ${DEFAULT-VALUE}).",
@ -432,7 +486,6 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
// Permissioning: A list of whitelist nodes can be passed.
@Option(
names = {"--nodes-whitelist"},
paramLabel = "<enode://id@host:port>",
description =
"Comma separated enode URLs for permissioned networks. "
+ "Not intended to be used with mainnet or public testnets.",
@ -479,13 +532,15 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
commandLine.addMixin("standaloneCommands", standaloneCommands);
}
final ImportSubCommand importSubCommand = new ImportSubCommand(blockImporter);
commandLine.addSubcommand("import", importSubCommand);
commandLine.addSubcommand("export-pub-key", new ExportPublicKeySubCommand());
commandLine.addSubcommand(
BlocksSubCommand.COMMAND_NAME, new BlocksSubCommand(blockImporter, resultHandler.out()));
commandLine.addSubcommand(
PublicKeySubCommand.COMMAND_NAME, new PublicKeySubCommand(resultHandler.out()));
commandLine.registerConverter(Address.class, Address::fromHexString);
commandLine.registerConverter(BytesValue.class, BytesValue::fromHexString);
commandLine.registerConverter(HostAndPort.class, HostAndPort::fromString);
commandLine.registerConverter(HostSpecifier.class, HostSpecifier::from);
commandLine.registerConverter(Level.class, Level::valueOf);
commandLine.registerConverter(SyncMode.class, SyncMode::fromString);
commandLine.registerConverter(Wei.class, (arg) -> Wei.of(Long.parseUnsignedLong(arg)));
@ -534,7 +589,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
noPeerDiscovery,
ethNetworkConfig.getBootNodes(),
maxPeers,
p2pHostAndPort,
HostAndPort.fromParts(p2pHost.toString(), p2pPort),
jsonRpcConfiguration(),
webSocketConfiguration(),
metricsConfiguration(),
@ -571,7 +626,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
PantheonController<?> buildController() {
try {
return controllerBuilder
.synchronizerConfiguration(buildSyncConfig(syncMode))
.synchronizerConfiguration(buildSyncConfig())
.homePath(dataDir())
.ethNetworkConfig(ethNetworkConfig())
.syncWithOttoman(syncWithOttoman)
@ -596,30 +651,30 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
private JsonRpcConfiguration jsonRpcConfiguration() {
final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
jsonRpcConfiguration.setEnabled(isJsonRpcEnabled);
jsonRpcConfiguration.setHost(rpcHostAndPort.getHost());
jsonRpcConfiguration.setPort(rpcHostAndPort.getPort());
jsonRpcConfiguration.setCorsAllowedDomains(rpcCorsAllowedOrigins);
jsonRpcConfiguration.setRpcApis(rpcApis);
jsonRpcConfiguration.setEnabled(isHttpRpcEnabled);
jsonRpcConfiguration.setHost(rpcHttpHost.toString());
jsonRpcConfiguration.setPort(rpcHttpPort);
jsonRpcConfiguration.setCorsAllowedDomains(rpcHttpCorsAllowedOrigins);
jsonRpcConfiguration.setRpcApis(rpcHttpApis);
jsonRpcConfiguration.setHostsWhitelist(hostsWhitelist);
return jsonRpcConfiguration;
}
private WebSocketConfiguration webSocketConfiguration() {
final WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault();
webSocketConfiguration.setEnabled(isWsRpcEnabled);
webSocketConfiguration.setHost(wsHostAndPort.getHost());
webSocketConfiguration.setPort(wsHostAndPort.getPort());
webSocketConfiguration.setRpcApis(wsApis);
webSocketConfiguration.setRefreshDelay(refreshDelay);
webSocketConfiguration.setEnabled(isRpcWsEnabled);
webSocketConfiguration.setHost(rpcWsHost.toString());
webSocketConfiguration.setPort(rpcWsPort);
webSocketConfiguration.setRpcApis(rpcWsApis);
webSocketConfiguration.setRefreshDelay(rpcWsRefreshDelay);
return webSocketConfiguration;
}
MetricsConfiguration metricsConfiguration() {
final MetricsConfiguration metricsConfiguration = MetricsConfiguration.createDefault();
final MetricsConfiguration metricsConfiguration = createDefault();
metricsConfiguration.setEnabled(isMetricsEnabled);
metricsConfiguration.setHost(metricsHostAndPort.getHost());
metricsConfiguration.setPort(metricsHostAndPort.getPort());
metricsConfiguration.setHost(metricsHost.toString());
metricsConfiguration.setPort(metricsPort);
metricsConfiguration.setHostsWhitelist(hostsWhitelist);
return metricsConfiguration;
}
@ -632,8 +687,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
return permissioningConfiguration;
}
private SynchronizerConfiguration buildSyncConfig(final SyncMode syncMode) {
checkNotNull(syncMode);
private SynchronizerConfiguration buildSyncConfig() {
synchronizerConfigurationBuilder.syncMode(syncMode);
synchronizerConfigurationBuilder.maxTrailingPeers(maxTrailingPeers);
return synchronizerConfigurationBuilder.build();
@ -705,10 +759,6 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
return autoDiscoveredDefaultIP;
}
private HostAndPort getDefaultHostAndPort(final int port) {
return HostAndPort.fromParts(autoDiscoverDefaultIP().getHostAddress(), port);
}
private EthNetworkConfig ethNetworkConfig() {
final EthNetworkConfig predefinedNetworkConfig;
if (rinkeby) {
@ -765,11 +815,11 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
private Path dataDir() {
if (isFullInstantiation()) {
return standaloneCommands.dataDir;
return standaloneCommands.dataPath;
} else if (isDocker) {
return Paths.get(DOCKER_DATADIR_LOCATION);
} else {
return getDefaultPantheonDataDir(this);
return getDefaultPantheonDataPath(this);
}
}

@ -0,0 +1,116 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.cli;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.nio.charset.StandardCharsets.UTF_8;
import static tech.pegasys.pantheon.cli.DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP;
import static tech.pegasys.pantheon.cli.PublicKeySubCommand.COMMAND_NAME;
import tech.pegasys.pantheon.cli.PublicKeySubCommand.ExportSubCommand;
import tech.pegasys.pantheon.controller.PantheonController;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import picocli.CommandLine.Command;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.ParentCommand;
import picocli.CommandLine.Spec;
/** Node's public key related sub-command */
@Command(
name = COMMAND_NAME,
description = "This command provides node public key related actions.",
mixinStandardHelpOptions = true,
subcommands = {ExportSubCommand.class}
)
class PublicKeySubCommand implements Runnable {
private static final Logger LOG = LogManager.getLogger();
static final String COMMAND_NAME = "public-key";
@SuppressWarnings("unused")
@ParentCommand
private PantheonCommand parentCommand; // Picocli injects reference to parent command
@SuppressWarnings("unused")
@Spec
private CommandSpec spec; // Picocli injects reference to command spec
private final PrintStream out;
PublicKeySubCommand(final PrintStream out) {
this.out = out;
}
@Override
public void run() {
spec.commandLine().usage(out);
}
/**
* Public key export sub-command
*
* <p>Export of the public key takes a file as parameter to export directly by writing the key in
* the file. A direct output of the key to standard out is not done because we don't want the key
* value to be polluted by other information like logs that are in KeyPairUtil that is inevitable.
*/
@Command(
name = "export",
description = "This command exports the node public key to a file.",
mixinStandardHelpOptions = true
)
static class ExportSubCommand implements Runnable {
@Option(
names = "--from",
required = true,
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "File to write public key to",
arity = "1..1"
)
private final File publicKeyExportFile = null;
@SuppressWarnings("unused")
@ParentCommand
private PublicKeySubCommand parentCommand; // Picocli injects reference to parent command
@Override
public void run() {
checkNotNull(parentCommand);
checkNotNull(parentCommand.parentCommand);
final PantheonController<?> controller = parentCommand.parentCommand.buildController();
final KeyPair keyPair = controller.getLocalNodeKeyPair();
// this publicKeyExportFile can never be null because of Picocli arity requirement
//noinspection ConstantConditions
final Path path = publicKeyExportFile.toPath();
try (final BufferedWriter fileWriter = Files.newBufferedWriter(path, UTF_8)) {
fileWriter.write(keyPair.getPublicKey().toString());
} catch (final IOException e) {
LOG.error("An error occurred while trying to write the public key", e);
}
}
}
}

@ -12,7 +12,7 @@
*/
package tech.pegasys.pantheon.cli;
import static tech.pegasys.pantheon.cli.DefaultCommandValues.getDefaultPantheonDataDir;
import static tech.pegasys.pantheon.cli.DefaultCommandValues.getDefaultPantheonDataPath;
import java.io.File;
import java.nio.file.Path;
@ -23,26 +23,27 @@ class StandaloneCommand implements DefaultCommandValues {
@CommandLine.Option(
names = {CONFIG_FILE_OPTION_NAME},
paramLabel = MANDATORY_PATH_FORMAT_HELP,
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "TOML config file (default: none)"
)
private final File configFile = null;
@CommandLine.Option(
names = {"--datadir"},
names = {"--data-path"},
paramLabel = MANDATORY_PATH_FORMAT_HELP,
description = "The path to Pantheon data directory (default: ${DEFAULT-VALUE})"
)
final Path dataDir = getDefaultPantheonDataDir(this);
final Path dataPath = getDefaultPantheonDataPath(this);
// Genesis file path with null default option if the option
// is not defined on command line as this default is handled by Runner
// to use mainnet json file from resources
// NOTE: we have no control over default value here.
@CommandLine.Option(
names = {"--genesis"},
paramLabel = MANDATORY_PATH_FORMAT_HELP,
description = "The path to genesis file (default: Pantheon embedded mainnet genesis file)"
names = {"--private-genesis-file"},
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description =
"The path to genesis file. Setting this will also override --chain option to be CUSTOM"
)
final File genesisFile = null;
}

@ -0,0 +1,109 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.cli;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import java.io.File;
import org.junit.Test;
import picocli.CommandLine.Model.CommandSpec;
public class BlockSubCommandTest extends CommandTestAbstract {
private static final String EXPECTED_BLOCK_USAGE =
"Usage: pantheon blocks [-hV] [COMMAND]"
+ System.lineSeparator()
+ "This command provides blocks related actions."
+ System.lineSeparator()
+ " -h, --help Show this help message and exit."
+ System.lineSeparator()
+ " -V, --version Print version information and exit."
+ System.lineSeparator()
+ "Commands:"
+ System.lineSeparator()
+ " import This command imports blocks from a file into the database."
+ System.lineSeparator();
private static final String EXPECTED_BLOCK_IMPORT_USAGE =
"Usage: pantheon blocks import [-hV] --from=<FILE>"
+ System.lineSeparator()
+ "This command imports blocks from a file into the database."
+ System.lineSeparator()
+ " --from=<FILE> File containing blocks to import"
+ System.lineSeparator()
+ " -h, --help Show this help message and exit."
+ System.lineSeparator()
+ " -V, --version Print version information and exit."
+ System.lineSeparator();
private static final String BLOCK_SUBCOMMAND_NAME = "blocks";
private static final String BLOCK_IMPORT_SUBCOMMAND_NAME = "import";
// Block sub-command
@Test
public void blockSubCommandExistAnbHaveSubCommands() {
CommandSpec spec = parseCommand();
assertThat(spec.subcommands()).containsKeys(BLOCK_SUBCOMMAND_NAME);
assertThat(spec.subcommands().get(BLOCK_SUBCOMMAND_NAME).getSubcommands())
.containsKeys(BLOCK_IMPORT_SUBCOMMAND_NAME);
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void callingBlockSubCommandWithoutSubSubcommandMustDisplayUsage() {
parseCommand(BLOCK_SUBCOMMAND_NAME);
assertThat(commandOutput.toString()).startsWith(EXPECTED_BLOCK_USAGE);
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void callingBlockSubCommandHelpMustDisplayUsage() {
parseCommand(BLOCK_SUBCOMMAND_NAME, "--help");
assertThat(commandOutput.toString()).startsWith(EXPECTED_BLOCK_USAGE);
assertThat(commandErrorOutput.toString()).isEmpty();
}
// Import sub-sub-command
@Test
public void callingBlockImportSubCommandWithoutPathMustDisplayErrorAndUsage() {
parseCommand(BLOCK_SUBCOMMAND_NAME, BLOCK_IMPORT_SUBCOMMAND_NAME);
final String expectedErrorOutputStart = "Missing required option '--from=<FILE>'";
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).startsWith(expectedErrorOutputStart);
}
@Test
public void callingBlockImportSubCommandHelpMustDisplayUsage() {
parseCommand(BLOCK_SUBCOMMAND_NAME, BLOCK_IMPORT_SUBCOMMAND_NAME, "--help");
assertThat(commandOutput.toString()).startsWith(EXPECTED_BLOCK_IMPORT_USAGE);
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void callingBlockImportSubCommandWithPathMustImportBlocksWithThisPath() throws Exception {
File fileToImport = temp.newFile("blocks.file");
parseCommand(
BLOCK_SUBCOMMAND_NAME, BLOCK_IMPORT_SUBCOMMAND_NAME, "--from", fileToImport.getPath());
verify(mockBlockImporter).importBlockchain(pathArgumentCaptor.capture(), any());
assertThat(pathArgumentCaptor.getValue()).isEqualByComparingTo(fileToImport.toPath());
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
}
}

@ -14,6 +14,8 @@ package tech.pegasys.pantheon.cli;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.Runner;
@ -38,6 +40,8 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
@ -79,6 +83,14 @@ public abstract class CommandTestAbstract {
@Captor ArgumentCaptor<PermissioningConfiguration> permissioningConfigurationArgumentCaptor;
@Captor ArgumentCaptor<Collection<URI>> uriListArgumentCaptor;
@Rule public final TemporaryFolder temp = new TemporaryFolder();
@Before
@After
public void resetSystemProps() {
System.setProperty("pantheon.docker", "false");
}
@Before
public void initMocks() throws Exception {
// doReturn used because of generic PantheonController
@ -93,6 +105,23 @@ public abstract class CommandTestAbstract {
when(mockControllerBuilder.metricsSystem(any())).thenReturn(mockControllerBuilder);
when(mockSyncConfBuilder.build()).thenReturn(mockSyncConf);
when(mockRunnerBuilder.vertx(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.pantheonController(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.discovery(anyBoolean())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.bootstrapPeers(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.discoveryHost(anyString())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.discoveryPort(anyInt())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.maxPeers(anyInt())).thenReturn(mockRunnerBuilder);
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.dataDir(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.bannedNodeIds(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.metricsSystem(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.metricsConfiguration(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.build()).thenReturn(mockRunner);
}
// Display outputs for debug purpose

@ -1,60 +0,0 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.cli;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.contentOf;
import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import java.io.File;
import org.junit.Test;
public class ExportPublicKeySubCommandTest extends CommandTestAbstract {
@Test
public void callingExportPublicKeySubCommandWithoutPathMustDisplayErrorAndUsage() {
parseCommand("export-pub-key");
final String expectedErrorOutputStart = "Missing required parameter: PATH";
assertThat(commandErrorOutput.toString()).startsWith(expectedErrorOutputStart);
}
@Test
public void callingExportPublicKeySubCommandHelpMustDisplayImportUsage() {
parseCommand("export-pub-key", "--help");
final String expectedOutputStart = "Usage: pantheon export-pub-key [-hV] PATH";
assertThat(commandOutput.toString()).startsWith(expectedOutputStart);
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void callingExportPublicKeySubCommandWithFilePathMustWritePublicKeyInThisFile()
throws Exception {
final KeyPair keyPair = KeyPair.generate();
when(mockController.getLocalNodeKeyPair()).thenReturn(keyPair);
final File file = File.createTempFile("public", "key");
parseCommand("export-pub-key", file.getPath());
assertThat(contentOf(file))
.startsWith(keyPair.getPublicKey().toString())
.endsWith(keyPair.getPublicKey().toString());
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
}
}

@ -1,53 +0,0 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.cli;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Test;
public class ImportSubCommandTest extends CommandTestAbstract {
@Test
public void callingImportSubCommandWithoutPathMustDisplayErrorAndUsage() {
parseCommand("import");
final String expectedErrorOutputStart = "Missing required parameter: PATH";
assertThat(commandErrorOutput.toString()).startsWith(expectedErrorOutputStart);
}
@Test
public void callingImportSubCommandHelpMustDisplayImportUsage() {
parseCommand("import", "--help");
final String expectedOutputStart = "Usage: pantheon import [-hV] PATH";
assertThat(commandOutput.toString()).startsWith(expectedOutputStart);
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void callingImportSubCommandWithPathMustImportBlocksWithThisPath() throws Exception {
final Path path = Paths.get(".");
parseCommand("import", path.toString());
verify(mockBlockImporter).importBlockchain(pathArgumentCaptor.capture(), any());
assertThat(pathArgumentCaptor.getValue()).isEqualByComparingTo(path);
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
}
}

@ -17,15 +17,10 @@ import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNotNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration.MAINNET_BOOTSTRAP_NODES;
import tech.pegasys.pantheon.PantheonInfo;
@ -61,11 +56,8 @@ import com.google.common.io.Resources;
import net.consensys.cava.toml.Toml;
import net.consensys.cava.toml.TomlParseResult;
import org.apache.commons.text.StringEscapeUtils;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import picocli.CommandLine;
@ -74,7 +66,6 @@ public class PantheonCommandTest extends CommandTestAbstract {
private final String VALID_NODE_ID =
"6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0";
@Rule public final TemporaryFolder temp = new TemporaryFolder();
private static final JsonRpcConfiguration defaultJsonRpcConfiguration;
private static final WebSocketConfiguration defaultWebSocketConfiguration;
private static final MetricsConfiguration defaultMetricsConfiguration;
@ -105,34 +96,6 @@ public class PantheonCommandTest extends CommandTestAbstract {
defaultMetricsConfiguration = MetricsConfiguration.createDefault();
}
@Before
public void resetSystemProps() {
System.setProperty("pantheon.docker", "false");
}
@Override
@Before
public void initMocks() throws Exception {
super.initMocks();
when(mockRunnerBuilder.vertx(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.pantheonController(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.discovery(anyBoolean())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.bootstrapPeers(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.discoveryHost(anyString())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.discoveryPort(anyInt())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.maxPeers(anyInt())).thenReturn(mockRunnerBuilder);
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.dataDir(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.bannedNodeIds(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.metricsSystem(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.metricsConfiguration(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.build()).thenReturn(mockRunner);
}
@Test
public void callingHelpSubCommandMustDisplayUsage() {
parseCommand("--help");
@ -198,9 +161,10 @@ public class PantheonCommandTest extends CommandTestAbstract {
public void callingWithConfigOptionButNoConfigFileShouldDisplayHelp() {
assumeTrue(isFullInstantiation());
parseCommand("--config");
parseCommand("--config-file");
final String expectedOutputStart = "Missing required parameter for option '--config' (<PATH>)";
final String expectedOutputStart =
"Missing required parameter for option '--config-file' (<FILE>)";
assertThat(commandErrorOutput.toString()).startsWith(expectedOutputStart);
assertThat(commandOutput.toString()).isEmpty();
}
@ -210,7 +174,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
assumeTrue(isFullInstantiation());
final File tempConfigFile = temp.newFile("an-invalid-file-name-without-extension");
parseCommand("--config", tempConfigFile.getPath());
parseCommand("--config-file", tempConfigFile.getPath());
final String expectedOutputStart = "Unable to read TOML configuration file " + tempConfigFile;
assertThat(commandErrorOutput.toString()).startsWith(expectedOutputStart);
@ -221,7 +185,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
public void callingWithConfigOptionButTomlFileNotFoundShouldDisplayHelp() {
assumeTrue(isFullInstantiation());
parseCommand("--config", "./an-invalid-file-name-sdsd87sjhqoi34io23.toml");
parseCommand("--config-file", "./an-invalid-file-name-sdsd87sjhqoi34io23.toml");
final String expectedOutputStart = "Unable to read TOML configuration, file not found.";
assertThat(commandErrorOutput.toString()).startsWith(expectedOutputStart);
@ -240,7 +204,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
fileWriter.write("."); // an invalid toml content
fileWriter.flush();
parseCommand("--config", tempConfigFile.getPath());
parseCommand("--config-file", tempConfigFile.getPath());
final String expectedOutputStart =
"Invalid TOML configuration : Unexpected '.', expected a-z, A-Z, 0-9, ', \", a table key, "
@ -262,7 +226,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
fileWriter.write("tester===========......."); // an invalid toml content
fileWriter.flush();
parseCommand("--config", tempConfigFile.getPath());
parseCommand("--config-file", tempConfigFile.getPath());
final String expectedOutputStart =
"Invalid TOML configuration : Unexpected '=', expected ', \", ''', \"\"\", a number, "
@ -306,7 +270,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
metricsConfiguration.setHost("8.6.7.5");
metricsConfiguration.setPort(309);
parseCommand("--config", toml.toString());
parseCommand("--config-file", toml.toString());
verify(mockRunnerBuilder).discovery(eq(false));
verify(mockRunnerBuilder).bootstrapPeers(uriListArgumentCaptor.capture());
@ -353,14 +317,14 @@ public class PantheonCommandTest extends CommandTestAbstract {
Files.write(toml, Resources.toByteArray(configFile));
// Parse it.
final CommandLine.Model.CommandSpec spec = parseCommand("--config", toml.toString());
final CommandLine.Model.CommandSpec spec = parseCommand("--config-file", toml.toString());
final TomlParseResult tomlResult = Toml.parse(toml);
// Verify we configured everything
final HashSet<CommandLine.Model.OptionSpec> options = new HashSet<>(spec.options());
// Except for meta-options
options.remove(spec.optionsMap().get("--config"));
options.remove(spec.optionsMap().get("--config-file"));
options.remove(spec.optionsMap().get("--help"));
options.remove(spec.optionsMap().get("--version"));
@ -390,7 +354,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
final String configFile = Resources.getResource("partial_config.toml").getFile();
parseCommand("--config", configFile);
parseCommand("--config-file", configFile);
final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
jsonRpcConfiguration.addRpcApi(CliqueRpcApis.CLIQUE);
jsonRpcConfiguration.addRpcApi(IbftRpcApis.IBFT);
@ -440,7 +404,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
public void nodekeyOptionMustBeUsed() throws Exception {
final File file = new File("./specific/key");
parseCommand("--node-private-key", file.getPath());
parseCommand("--node-private-key-file", file.getPath());
verify(mockControllerBuilder).homePath(isNotNull());
verify(mockControllerBuilder).syncWithOttoman(eq(false));
@ -459,7 +423,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
final Path path = Paths.get(".");
parseCommand("--datadir", path.toString());
parseCommand("--data-path", path.toString());
verify(mockControllerBuilder).homePath(pathArgumentCaptor.capture());
verify(mockControllerBuilder).syncWithOttoman(eq(false));
@ -505,7 +469,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
final ArgumentCaptor<EthNetworkConfig> networkArg =
ArgumentCaptor.forClass(EthNetworkConfig.class);
parseCommand("--genesis", genesisFile.toString());
parseCommand("--private-genesis-file", genesisFile.toString());
verify(mockControllerBuilder).ethNetworkConfig(networkArg.capture());
verify(mockControllerBuilder).build();
@ -614,10 +578,10 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void callingWithBannedNodeidsOptionButNoValueMustDisplayErrorAndUsage() {
parseCommand("--banned-nodeids");
parseCommand("--banned-node-ids");
assertThat(commandOutput.toString()).isEmpty();
final String expectedErrorOutputStart =
"Missing required parameter for option '--banned-nodeids' at index 0 (<bannedNodeIds>)";
"Missing required parameter for option '--banned-node-ids' at index 0 (<NODEID>)";
assertThat(commandErrorOutput.toString()).startsWith(expectedErrorOutputStart);
}
@ -637,7 +601,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void callingWithRefreshDelayWithValueMustUseValue() {
parseCommand("--ws-refresh-delay", "2000");
parseCommand("--rpc-ws-refresh-delay", "2000");
verify(mockRunnerBuilder).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -650,10 +614,10 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void callingWithRefreshDelayWithNegativeValueMustError() {
parseCommand("--ws-refresh-delay", "-2000");
parseCommand("--rpc-ws-refresh-delay", "-2000");
assertThat(commandOutput.toString()).isEmpty();
final String expectedErrorMsg = "refreshDelay must be a positive integer between";
final String expectedErrorMsg = "Refresh delay must be a positive integer between";
assertThat(commandErrorOutput.toString()).startsWith(expectedErrorMsg);
}
@ -822,7 +786,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void bannedNodeIdsOptionMustBeUsed() {
final String[] nodes = {"0001", "0002", "0003"};
parseCommand("--banned-nodeids", String.join(",", nodes));
parseCommand("--banned-node-ids", String.join(",", nodes));
verify(mockRunnerBuilder).bannedNodeIds(stringListArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -838,7 +802,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
final String host = "1.2.3.4";
final int port = 1234;
parseCommand("--p2p-listen", String.format("%1$s:%2$s", host, port));
parseCommand("--p2p-host", host, "--p2p-port", String.valueOf(port));
verify(mockRunnerBuilder).discoveryHost(stringArgumentCaptor.capture());
verify(mockRunnerBuilder).discoveryPort(intArgumentCaptor.capture());
@ -866,7 +830,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Ignore
@Ignore("Ignored as we only have one mode available for now. See NC-1057/NC-1681")
@Test
public void syncModeOptionMustBeUsed() {
@ -904,7 +868,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcEnabledPropertyMustBeUsed() {
parseCommand("--rpc-enabled");
parseCommand("--rpc-http-enabled");
verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -917,7 +881,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void rpcApisPropertyMustBeUsed() {
parseCommand("--rpc-api", "ETH,NET");
parseCommand("--rpc-http-api", "ETH,NET");
verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -931,20 +895,22 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void rpcApisPropertyWithInvalidEntryMustDisplayError() {
parseCommand("--rpc-api", "BOB");
parseCommand("--rpc-http-api", "BOB");
verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).contains("Invalid value for option '--rpc-api'");
// PicoCLI uses longest option name for message when option has multiple names, so here plural.
assertThat(commandErrorOutput.toString())
.contains("Invalid value for option '--rpc-http-apis'");
}
@Test
public void jsonRpcHostAndPortOptionMustBeUsed() {
public void jsonRpcHostAndPortOptionsMustBeUsed() {
final String host = "1.2.3.4";
final int port = 1234;
parseCommand("--rpc-listen", String.format("%1$s:%2$s", host, port));
parseCommand("--rpc-http-host", host, "--rpc-http-port", String.valueOf(port));
verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -959,7 +925,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsTwoDomainsMustBuildListWithBothDomains() {
final String[] origins = {"http://domain1.com", "https://domain2.com"};
parseCommand("--rpc-cors-origins", String.join(",", origins));
parseCommand("--rpc-http-cors-origins", String.join(",", origins));
verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -974,7 +940,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsDoubleCommaFilteredOut() {
final String[] origins = {"http://domain1.com", "https://domain2.com"};
parseCommand("--rpc-cors-origins", String.join(",,", origins));
parseCommand("--rpc-http-cors-origins", String.join(",,", origins));
verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -989,7 +955,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsWithWildcardMustBuildListWithWildcard() {
final String[] origins = {"*"};
parseCommand("--rpc-cors-origins", String.join(",", origins));
parseCommand("--rpc-http-cors-origins", String.join(",", origins));
verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -1003,7 +969,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsWithAllMustBuildListWithWildcard() {
parseCommand("--rpc-cors-origins", "all");
parseCommand("--rpc-http-cors-origins", "all");
verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -1017,7 +983,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsWithNoneMustBuildEmptyList() {
final String[] origins = {"none"};
parseCommand("--rpc-cors-origins", String.join(",", origins));
parseCommand("--rpc-http-cors-origins", String.join(",", origins));
verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -1031,7 +997,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsNoneWithAnotherDomainMustFail() {
final String[] origins = {"http://domain1.com", "none"};
parseCommand("--rpc-cors-origins", String.join(",", origins));
parseCommand("--rpc-http-cors-origins", String.join(",", origins));
verifyZeroInteractions(mockRunnerBuilder);
@ -1043,7 +1009,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsNoneWithAnotherDomainMustFailNoneFirst() {
final String[] origins = {"none", "http://domain1.com"};
parseCommand("--rpc-cors-origins", String.join(",", origins));
parseCommand("--rpc-http-cors-origins", String.join(",", origins));
verifyZeroInteractions(mockRunnerBuilder);
@ -1054,7 +1020,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsAllWithAnotherDomainMustFail() {
parseCommand("--rpc-cors-origins=http://domain1.com,all");
parseCommand("--rpc-http-cors-origins=http://domain1.com,all");
verifyZeroInteractions(mockRunnerBuilder);
@ -1065,7 +1031,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsAllWithAnotherDomainMustFailAsFlags() {
parseCommand("--rpc-cors-origins=http://domain1.com", "--rpc-cors-origins=all");
parseCommand("--rpc-http-cors-origins=http://domain1.com", "--rpc-http-cors-origins=all");
verifyZeroInteractions(mockRunnerBuilder);
@ -1076,7 +1042,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsWildcardWithAnotherDomainMustFail() {
parseCommand("--rpc-cors-origins=http://domain1.com,*");
parseCommand("--rpc-http-cors-origins=http://domain1.com,*");
verifyZeroInteractions(mockRunnerBuilder);
@ -1087,7 +1053,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsWildcardWithAnotherDomainMustFailAsFlags() {
parseCommand("--rpc-cors-origins=http://domain1.com", "--rpc-cors-origins=*");
parseCommand("--rpc-http-cors-origins=http://domain1.com", "--rpc-http-cors-origins=*");
verifyZeroInteractions(mockRunnerBuilder);
@ -1099,7 +1065,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsInvalidRegexShouldFail() {
final String[] origins = {"**"};
parseCommand("--rpc-cors-origins", String.join(",", origins));
parseCommand("--rpc-http-cors-origins", String.join(",", origins));
verifyZeroInteractions(mockRunnerBuilder);
@ -1110,7 +1076,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void jsonRpcCorsOriginsEmtyValueFails() {
parseCommand("--rpc-cors-origins=");
parseCommand("--rpc-http-cors-origins=");
verifyZeroInteractions(mockRunnerBuilder);
@ -1283,7 +1249,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void wsRpcEnabledPropertyMustBeUsed() {
parseCommand("--ws-enabled");
parseCommand("--rpc-ws-enabled");
verify(mockRunnerBuilder).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -1296,7 +1262,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test
public void wsApiPropertyMustBeUsed() {
parseCommand("--ws-api", "ETH, NET");
parseCommand("--rpc-ws-api", "ETH, NET");
verify(mockRunnerBuilder).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -1312,7 +1278,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
public void wsRpcHostAndPortOptionMustBeUsed() {
final String host = "1.2.3.4";
final int port = 1234;
parseCommand("--ws-listen", String.format("%1$s:%2$s", host, port));
parseCommand("--rpc-ws-host", host, "--rpc-ws-port", String.valueOf(port));
verify(mockRunnerBuilder).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -1354,7 +1320,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
public void metricsHostAndPortOptionMustBeUsed() {
final String host = "1.2.3.4";
final int port = 1234;
parseCommand("--metrics-listen", String.format("%1$s:%2$s", host, port));
parseCommand("--metrics-host", host, "--metrics-port", String.valueOf(port));
verify(mockRunnerBuilder).metricsConfiguration(metricsConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build();
@ -1398,8 +1364,8 @@ public class PantheonCommandTest extends CommandTestAbstract {
"0x1122334455667788990011223344556677889900112233445566778899001122";
parseCommand(
"--miner-coinbase=" + requestedCoinbase.toString(),
"--miner-minTransactionGasPriceWei=15",
"--miner-extraData=" + extraDataString);
"--min-gas-price=15",
"--miner-extra-data=" + extraDataString);
final ArgumentCaptor<MiningParameters> miningArg =
ArgumentCaptor.forClass(MiningParameters.class);
@ -1499,7 +1465,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
"1",
"--bootnodes",
String.join(",", validENodeStrings),
"--genesis",
"--private-genesis-file",
genesisFile.toString());
final ArgumentCaptor<EthNetworkConfig> networkArg =
@ -1524,9 +1490,9 @@ public class PantheonCommandTest extends CommandTestAbstract {
verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandOutput.toString()).doesNotContain("--config");
assertThat(commandOutput.toString()).doesNotContain("--datadir");
assertThat(commandOutput.toString()).doesNotContain("--genesis");
assertThat(commandOutput.toString()).doesNotContain("--config-file");
assertThat(commandOutput.toString()).doesNotContain("--data-path");
assertThat(commandOutput.toString()).doesNotContain("--private-genesis-file");
assertThat(commandErrorOutput.toString()).isEmpty();
}
@ -1536,9 +1502,9 @@ public class PantheonCommandTest extends CommandTestAbstract {
verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandOutput.toString()).contains("--config");
assertThat(commandOutput.toString()).contains("--datadir");
assertThat(commandOutput.toString()).contains("--genesis");
assertThat(commandOutput.toString()).contains("--config-file");
assertThat(commandOutput.toString()).contains("--data-path");
assertThat(commandOutput.toString()).contains("--private-genesis-file");
assertThat(commandErrorOutput.toString()).isEmpty();
}

@ -0,0 +1,117 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.cli;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.contentOf;
import static org.mockito.Mockito.when;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import java.io.File;
import org.junit.Test;
import picocli.CommandLine.Model.CommandSpec;
public class PublicKeySubCommandTest extends CommandTestAbstract {
private static final String EXPECTED_PUBLIC_KEY_USAGE =
"Usage: pantheon public-key [-hV] [COMMAND]"
+ System.lineSeparator()
+ "This command provides node public key related actions."
+ System.lineSeparator()
+ " -h, --help Show this help message and exit."
+ System.lineSeparator()
+ " -V, --version Print version information and exit."
+ System.lineSeparator()
+ "Commands:"
+ System.lineSeparator()
+ " export This command exports the node public key to a file."
+ System.lineSeparator();
private static final String EXPECTED_PUBLIC_KEY_EXPORT_USAGE =
"Usage: pantheon public-key export [-hV] --from=<FILE>"
+ System.lineSeparator()
+ "This command exports the node public key to a file."
+ System.lineSeparator()
+ " --from=<FILE> File to write public key to"
+ System.lineSeparator()
+ " -h, --help Show this help message and exit."
+ System.lineSeparator()
+ " -V, --version Print version information and exit."
+ System.lineSeparator();
private static final String PUBLIC_KEY_SUBCOMMAND_NAME = "public-key";
private static final String PUBLIC_KEY_EXPORT_SUBCOMMAND_NAME = "export";
// bublic-key sub-command
@Test
public void publicKeySubCommandExistAnbHaveSubCommands() {
CommandSpec spec = parseCommand();
assertThat(spec.subcommands()).containsKeys(PUBLIC_KEY_SUBCOMMAND_NAME);
assertThat(spec.subcommands().get(PUBLIC_KEY_SUBCOMMAND_NAME).getSubcommands())
.containsKeys(PUBLIC_KEY_EXPORT_SUBCOMMAND_NAME);
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void callingPublicKeySubCommandWithoutSubSubcommandMustDisplayUsage() {
parseCommand(PUBLIC_KEY_SUBCOMMAND_NAME);
assertThat(commandOutput.toString()).startsWith(EXPECTED_PUBLIC_KEY_USAGE);
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void callingPublicKeySubCommandHelpMustDisplayUsage() {
parseCommand(PUBLIC_KEY_SUBCOMMAND_NAME, "--help");
assertThat(commandOutput.toString()).startsWith(EXPECTED_PUBLIC_KEY_USAGE);
assertThat(commandErrorOutput.toString()).isEmpty();
}
// Export sub-sub-command
@Test
public void callingPublicKeyExportSubCommandWithoutPathMustDisplayErrorAndUsage() {
parseCommand(PUBLIC_KEY_SUBCOMMAND_NAME, PUBLIC_KEY_EXPORT_SUBCOMMAND_NAME);
final String expectedErrorOutputStart = "Missing required option '--from=<FILE>'";
assertThat(commandErrorOutput.toString()).startsWith(expectedErrorOutputStart);
}
@Test
public void callingPublicKeyExportSubCommandHelpMustDisplayUsage() {
parseCommand(PUBLIC_KEY_SUBCOMMAND_NAME, PUBLIC_KEY_EXPORT_SUBCOMMAND_NAME, "--help");
assertThat(commandOutput.toString()).startsWith(EXPECTED_PUBLIC_KEY_EXPORT_USAGE);
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void callingPublicKeyExportSubCommandWithFilePathMustWritePublicKeyInThisFile()
throws Exception {
final KeyPair keyPair = KeyPair.generate();
when(mockController.getLocalNodeKeyPair()).thenReturn(keyPair);
final File file = File.createTempFile("public", "key");
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME, PUBLIC_KEY_EXPORT_SUBCOMMAND_NAME, "--from", file.getPath());
assertThat(contentOf(file))
.startsWith(keyPair.getPublicKey().toString())
.endsWith(keyPair.getPublicKey().toString());
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
}
}

@ -1,6 +1,6 @@
# this is a valid TOML config file
datadir="~/pantheondata" # Path
data-path="~/pantheondata" # Path
#invalid-option=true
@ -11,14 +11,18 @@ bootnodes=[
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567",
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567"
]
p2p-listen="1.2.3.4:1234" # IP:port
p2p-host="1.2.3.4"
p2p-port=1234
max-peers=42
rpc-listen="5.6.7.8:5678" # IP:port
ws-listen="9.10.11.12:9101" # IP:port
metrics-listen="8.6.7.5:309" # IP:port
rpc-http-host="5.6.7.8"
rpc-http-port=5678
rpc-ws-host="9.10.11.12"
rpc-ws-port=9101
metrics-host="8.6.7.5"
metrics-port=309
# chain
genesis="~/genesis.json" # Path
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

@ -9,9 +9,9 @@
# Please provide some sensible grouping.
# Node Information
datadir="~/pantheondata"
data-path="~/pantheondata"
logging="INFO"
node-private-key="./path/to/privateKey"
node-private-key-file="./path/to/privateKey"
# P2P network
p2p-enabled=true
@ -21,14 +21,17 @@ bootnodes=[
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567",
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567"
]
banned-nodeids=["0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"]
p2p-listen="1.2.3.4:1234"
banned-node-ids=["0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0","0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"]
banned-node-id=["0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"]
p2p-host="1.2.3.4"
p2p-port=1234
max-peers=42
max-trailing-peers=5
host-whitelist=["all"]
# chain
genesis="~/genesis.json"
network="MAINNET"
private-genesis-file="~/genesis.json"
#sync-mode="fast"
ottoman=false
ropsten=false
@ -38,26 +41,30 @@ rinkeby=false
dev-mode=false
# JSON-RPC
rpc-enabled=false
rpc-listen="5.6.7.8:5678"
rpc-api=["DEBUG","ETH"]
rpc-cors-origins=["none"]
rpc-http-enabled=false
rpc-http-host="5.6.7.8"
rpc-http-port=5678
rpc-http-api=["DEBUG","ETH"]
rpc-http-apis=["DEBUG","ETH"]
rpc-http-cors-origins=["none"]
# WebSockets API
ws-enabled=false
ws-api=["DEBUG","ETH"]
ws-listen="9.10.11.12:9101"
ws-refresh-delay=500
rpc-ws-enabled=false
rpc-ws-api=["DEBUG","ETH"]
rpc-ws-host="9.10.11.12"
rpc-ws-port=9101
rpc-ws-refresh-delay=500
# Prometheus Metrics Endpoint
metrics-enabled=false
metrics-listen="8.6.7.5:309"
metrics-host="8.6.7.5"
metrics-port=309
# Mining
miner-enabled=false
miner-coinbase="0x0000000000000000000000000000000000000002"
miner-extraData="Protocol Engineering Group And SYStems"
miner-minTransactionGasPriceWei="1"
miner-extra-data="Protocol Engineering Group And SYStems"
min-gas-price="1"
# Permissioning
accounts-whitelist=["0x0000000000000000000000000000000000000009"]

Loading…
Cancel
Save