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 !!!note
Permissioning is under development and will be available in v1.0. Permissioning is under development and will be available in v1.0.
### banned-nodeids ### banned-node-ids
```bash tab="Syntax" ```bash tab="Syntax"
--banned-nodeids=<bannedNodeId>[,<bannedNodeId>...]... --banned-node-ids=<bannedNodeId>[,<bannedNodeId>...]...
``` ```
```bash tab="Example Command Line" ```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 !!!info
This option is only available from v0.8.2. 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 ### bootnodes
```bash tab="Syntax" ```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) 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). must be done on the command line not in a [configuration file](../Configuring-Pantheon/Using-Configuration-File.md).
### config ### config-file
```bash tab="Syntax" ```bash tab="Syntax"
--config=<PATH> --config-file=<FILE>
``` ```
```bash tab="Example Command Line" ```bash tab="Example Command Line"
@ -98,18 +102,18 @@ The default is `none`.
!!!note !!!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). 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" ```bash tab="Syntax"
--datadir=<PATH> --data-path=<PATH>
``` ```
```bash tab="Example Command Line" ```bash tab="Example Command Line"
--datadir=/home/me/me_node --data-path=/home/me/me_node
``` ```
```bash tab="Example Configuration File" ```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. 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 ### 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" ```bash tab="Syntax"
--dev-mode --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. 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" ```bash tab="Syntax"
--genesis=<PATH> --genesis-file=<FILE>
``` ```
```bash tab="Example Command Line" ```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" ```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. 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 ### 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" ```bash tab="Syntax"
--goerli --goerli
``` ```
@ -221,6 +233,10 @@ The default is 25.
### max-trailing-peers ### 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" ```bash tab="Syntax"
--max-trailing-peers=<INTEGER> --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). 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`. 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" ```bash tab="Syntax"
--metrics-listen=<HOST:PORT> --metrics-port=<PORT>
``` ```
```bash tab="Example Command Line" ```bash tab="Example Command Line"
--metrics-listen=127.0.0.1:6174 --metrics-port=6174
``` ```
```bash tab="Example Configuration File" ```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 Specifies the 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). metrics. The default is `9545`. The metrics server respects the [`--host-whitelist` option](#host-whitelist).
### miner-coinbase ### miner-coinbase
@ -300,35 +333,35 @@ miner-enabled=true
Enables mining when the node is started. Enables mining when the node is started.
Default is `false`. Default is `false`.
### miner-extraData ### miner-extra-data
```bash tab="Syntax" ```bash tab="Syntax"
--miner-extraData=<Extra data> --miner-extra-data=<Extra data>
``` ```
```bash tab="Example Command Line" ```bash tab="Example Command Line"
--miner-extraData=0x444F4E27542050414E4943202120484F444C2C20484F444C2C20484F444C2021 --miner-extra-data=0x444F4E27542050414E4943202120484F444C2C20484F444C2C20484F444C2021
``` ```
```bash tab="Example Configuration File" ```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. A hex string representing the 32 bytes to be included in the extra data field of a mined block.
The default is 0x. The default is 0x.
### miner-minTransactionGasPriceWei ### min-gas-price
```bash tab="Syntax" ```bash tab="Syntax"
--miner-minTransactionGasPriceWei=<minTransactionGasPrice> --min-gas-price=<minTransactionGasPrice>
``` ```
```bash tab="Example Command Line" ```bash tab="Example Command Line"
--miner-minTransactionGasPriceWei=1337 --min-gas-price=1337
``` ```
```bash tab="Example Configuration File" ```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. 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 ### 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" ```bash tab="Syntax"
--network-id=<INTEGER> --network-id=<INTEGER>
``` ```
@ -364,21 +401,21 @@ no-discovery=true
Disables P2P peer discovery. Disables P2P peer discovery.
The default is `false`. The default is `false`.
### node-private-key ### node-private-key-file
```bash tab="Syntax" ```bash tab="Syntax"
--node-private-key=<PATH> --node-private-key-file=<FILE>
``` ```
```bash tab="Example Command Line" ```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" ```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. 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; 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. otherwise, the existing key file specifies the node private key.
@ -416,6 +453,10 @@ Not intended for use with mainnet or public testnets.
### ottoman ### 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" ```bash tab="Syntax"
--ottoman --ottoman
``` ```
@ -429,29 +470,54 @@ Synchronize against the Ottoman test network. This is only useful if you are usi
!!!note !!!note
:construction: IBFT is not currently supported. Support for IBFT is in active development. :construction: IBFT is not currently supported. Support for IBFT is in active development.
### p2p-listen ### p2p-host
```bash tab="Syntax" ```bash tab="Syntax"
--p2p-listen=<HOST:PORT> --p2p-host=<HOST>
``` ```
```bash tab="Example Command Line" ```bash tab="Example Command Line"
# to listen on all interfaces on port 1789 # to listen on all interfaces
--p2p-listen=0.0.0.0:1789 --p2p-host=0.0.0.0
``` ```
```bash tab="Example Configuration File" ```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. Specifies the host on which P2P peer discovery listens.
The default is 127.0.0.1:30303. 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 !!!note
This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#exposing-ports). This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#exposing-ports).
### rinkeby ### 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" ```bash tab="Syntax"
--rinkeby --rinkeby
``` ```
@ -466,6 +532,10 @@ Default is `false`.
### ropsten ### 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" ```bash tab="Syntax"
--ropsten --ropsten
``` ```
@ -480,82 +550,107 @@ Default is `false`.
!!!note !!!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). 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" ```bash tab="Syntax"
--rpc-enabled --rpc-http-enabled
``` ```
```bash tab="Example Configuration File" ```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`. 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" ```bash tab="Syntax"
--rpc-listen=<HOST:PORT> --rpc-http-port=<PORT>
``` ```
```bash tab="Example Command Line" ```bash tab="Example Command Line"
# to listen on all interfaces on port 3435 # to listen on port 3435
--rpc-listen=0.0.0.0:3435 --rpc-http-port=3435
``` ```
```bash tab="Example Configuration File" ```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. Specifies the port on which HTTP JSON-RPC listens.
The default is 127.0.0.1:8545. The default is 8545.
!!!note !!!note
This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#exposing-ports). 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" ```bash tab="Syntax"
--rpc-api=<api name>[,<api name>...]... --rpc-http-api=<api name>[,<api name>...]...
``` ```
```bash tab="Example Command Line" ```bash tab="Example Command Line"
--rpc-api=ETH,NET,WEB3 --rpc-http-api=ETH,NET,WEB3
``` ```
```bash tab="Example Configuration File" ```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. Comma-separated APIs to enable on the HTTP JSON-RPC channel.
When you use this option, the `--rpc-enabled` option must also be specified. 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 available API options are: `ADMIN`, `ETH`, `NET`, `WEB3`, `CLIQUE`, `IBFT`, `DEBUG`, and `MINER`.
The default is: `ETH`, `NET`, `WEB3`, `CLIQUE`, `IBFT`. The default is: `ETH`, `NET`, `WEB3`, `CLIQUE`, `IBFT`.
!!!note !!!note
:construction: IBFT is not currently supported. Support for IBFT is in active development. :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" ```bash tab="Syntax"
--rpc-cors-origins=<rpcCorsAllowedOrigins> or all --rpc-http-cors-origins=<url>[,<url>...]... or all or *
``` ```
```bash tab="Example Command Line" ```bash tab="Example Command Line"
# You can whitelist one or more domains with a comma-separated list. # 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" ```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" ```bash tab="Remix IDE domain example"
# The following allows Remix to interact with your Pantheon node without using MetaMask. # 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. 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. If Remix is connecting to the node through MetaMask, it also does not require CORS validation.
!!!tip !!!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" ```bash tab="Syntax"
--ws-enabled --rpc-ws-enabled
``` ```
```bash tab="Example Configuration File" ```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`. The default is `false`.
### ws-api ### rpc-ws-api
```bash tab="Syntax" ```bash tab="Syntax"
--ws-api=<api name>[,<api name>...]... --rpc-ws-api=<api name>[,<api name>...]...
``` ```
```bash tab="Example Command Line" ```bash tab="Example Command Line"
--ws-api=ETH,NET,WEB3 --rpc-ws-api=ETH,NET,WEB3
``` ```
```bash tab="Example Configuration File" ```bash tab="Example Configuration File"
ws-api=["ETH","NET","WEB3"] rpc-ws-api=["ETH","NET","WEB3"]
``` ```
Comma-separated APIs to enable on Websockets channel. 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 available API options are: `ETH`, `NET`, `WEB3`, `CLIQUE`, `IBFT`, `DEBUG`, and `MINER`.
The default is: `ETH`, `NET`, `WEB3`, `CLIQUE`, `IBFT`. The default is: `ETH`, `NET`, `WEB3`, `CLIQUE`, `IBFT`.
!!!note !!!note
:construction: IBFT is not currently supported. Support for IBFT is in active development. :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" ```bash tab="Syntax"
--ws-listen=<HOST:PORT> --ws-host=<HOST>
``` ```
```bash tab="Example Command Line" ```bash tab="Example Command Line"
# to listen on all interfaces on port 6174 # to listen on all interfaces
--ws-listen=0.0.0.0:6174 --ws-host=0.0.0.0
``` ```
```bash tab="Example Configuration File" ```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. Host for Websocket WS-RPC to listen on.
The default is 127.0.0.1:8546. The default is 127.0.0.1.
!!!note !!!note
This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#exposing-ports). 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" ```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" ```bash tab="Example"
--ws-refresh-delay="10000" --rpc-ws-refresh-delay="10000"
``` ```
Refresh delay for Websocket synchronizing subscription in milliseconds. Refresh delay for Websocket synchronizing subscription in milliseconds.
@ -683,26 +804,34 @@ Print version information and exit.
Pantheon subcommands are: Pantheon subcommands are:
### import ### blocks
This command provides blocks related actions.
#### import
```bash tab="Syntax" ```bash tab="Syntax"
$ pantheon import <block-file> $ pantheon blocks import --from=<block-file>
``` ```
```bash tab="Example" ```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 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" ```bash tab="Syntax"
$ pantheon export-pub-key <key-file> $ pantheon public-key export --to=<key-file>
``` ```
```bash tab="Example" ```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. 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; 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.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileAlreadyExistsException;
@ -23,13 +27,30 @@ import java.nio.file.Paths;
import picocli.CommandLine; import picocli.CommandLine;
interface DefaultCommandValues { interface DefaultCommandValues {
String CONFIG_FILE_OPTION_NAME = "--config"; String CONFIG_FILE_OPTION_NAME = "--config-file";
String MANDATORY_PATH_FORMAT_HELP = "<PATH>"; String MANDATORY_PATH_FORMAT_HELP = "<PATH>";
String MANDATORY_FILE_FORMAT_HELP = "<FILE>";
String PANTHEON_HOME_PROPERTY_NAME = "pantheon.home"; String PANTHEON_HOME_PROPERTY_NAME = "pantheon.home";
String DEFAULT_DATA_DIR_PATH = "./build/data"; 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. // this property is retrieved from Gradle tasks or Pantheon running shell script.
final String pantheonHomeProperty = System.getProperty(PANTHEON_HOME_PROPERTY_NAME); final String pantheonHomeProperty = System.getProperty(PANTHEON_HOME_PROPERTY_NAME);
final Path pantheonHome; 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 com.google.common.base.Preconditions.checkNotNull;
import static java.nio.charset.StandardCharsets.UTF_8; 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.Runner;
import tech.pegasys.pantheon.RunnerBuilder; import tech.pegasys.pantheon.RunnerBuilder;
@ -62,6 +69,7 @@ import java.util.stream.Stream;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import com.google.common.net.HostAndPort; import com.google.common.net.HostAndPort;
import com.google.common.net.HostSpecifier;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.core.config.Configurator;
@ -90,24 +98,6 @@ import picocli.CommandLine.ParameterException;
) )
public class PantheonCommand implements DefaultCommandValues, Runnable { 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> { public static class RpcApisConverter implements ITypeConverter<RpcApi> {
@Override @Override
@ -149,7 +139,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
// Options parsing is done with CLI library Picocli https://picocli.info/ // Options parsing is done with CLI library Picocli https://picocli.info/
@Option( @Option(
names = {"--node-private-key"}, names = {"--node-private-key-file"},
paramLabel = MANDATORY_PATH_FORMAT_HELP, paramLabel = MANDATORY_PATH_FORMAT_HELP,
description = description =
"the path to the node's private key file (default: a file named \"key\" in the Pantheon data folder)" "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; 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( @Option(
names = {"--max-trailing-peers"}, names = {"--max-trailing-peers"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP, paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
@ -211,7 +207,8 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
private final Integer maxTrailingPeers = Integer.MAX_VALUE; private final Integer maxTrailingPeers = Integer.MAX_VALUE;
@Option( @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.", description = "A list of node IDs to ban from the p2p network.",
split = ",", split = ",",
arity = "1..*" arity = "1..*"
@ -228,6 +225,17 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
// ) // )
private final SyncMode syncMode = DEFAULT_SYNC_MODE; 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 // Boolean option to indicate if the client have to sync against the ottoman test network
// (see https://github.com/ethereum/EIPs/issues/650). // (see https://github.com/ethereum/EIPs/issues/650).
@Option( @Option(
@ -238,6 +246,8 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
) )
private final Boolean syncWithOttoman = false; private final Boolean syncWithOttoman = false;
/** @deprecated Deprecated in favour of --network option */
@Deprecated
@Option( @Option(
names = {"--rinkeby"}, names = {"--rinkeby"},
description = description =
@ -246,12 +256,16 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
) )
private final Boolean rinkeby = false; private final Boolean rinkeby = false;
/** @deprecated Deprecated in favour of --network option */
@Deprecated
@Option( @Option(
names = {"--ropsten"}, names = {"--ropsten"},
description = "Use the Ropsten test network (default: ${DEFAULT-VALUE})" description = "Use the Ropsten test network (default: ${DEFAULT-VALUE})"
) )
private final Boolean ropsten = false; private final Boolean ropsten = false;
/** @deprecated Deprecated in favour of --network option */
@Deprecated
@Option( @Option(
names = {"--goerli"}, names = {"--goerli"},
description = "Use the Goerli test network (default: ${DEFAULT-VALUE})" description = "Use the Goerli test network (default: ${DEFAULT-VALUE})"
@ -259,13 +273,24 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
private final Boolean goerli = false; private final Boolean goerli = false;
@Option( @Option(
names = {"--p2p-listen"}, names = {"--p2p-host"},
paramLabel = MANDATORY_HOST_AND_PORT_FORMAT_HELP, paramLabel = MANDATORY_HOST_FORMAT_HELP,
description = "Host and port for p2p peers discovery to listen on (default: ${DEFAULT-VALUE})", description = "Host for p2p peers discovery to listen on (default: ${DEFAULT-VALUE})",
arity = "1" 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( @Option(
names = {"--network-id"}, names = {"--network-id"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP, paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
@ -275,29 +300,38 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
private final Integer networkId = null; private final Integer networkId = null;
@Option( @Option(
names = {"--rpc-enabled"}, names = {"--rpc-http-enabled"},
description = "Set if the JSON-RPC service should be started (default: ${DEFAULT-VALUE})" 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( @Option(
names = {"--rpc-listen"}, names = {"--rpc-http-port"},
paramLabel = MANDATORY_HOST_AND_PORT_FORMAT_HELP, paramLabel = MANDATORY_PORT_FORMAT_HELP,
description = "Host and port for JSON-RPC to listen on (default: ${DEFAULT-VALUE})", description = "Port for HTTP JSON-RPC to listen on (default: ${DEFAULT-VALUE})",
arity = "1" arity = "1"
) )
private final HostAndPort rpcHostAndPort = private final Integer rpcHttpPort = DEFAULT_JSON_RPC_PORT;
getDefaultHostAndPort(JsonRpcConfiguration.DEFAULT_JSON_RPC_PORT);
// A list of origins URLs that are accepted by the JsonRpcHttpServer (CORS) // A list of origins URLs that are accepted by the JsonRpcHttpServer (CORS)
@Option( @Option(
names = {"--rpc-cors-origins"}, names = {"--rpc-http-cors-origins"},
description = "Comma separated origin domain URLs for CORS validation (default: none)" description = "Comma separated origin domain URLs for CORS validation (default: none)"
) )
private final CorsAllowedOriginsProperty rpcCorsAllowedOrigins = new CorsAllowedOriginsProperty(); private final CorsAllowedOriginsProperty rpcHttpCorsAllowedOrigins =
new CorsAllowedOriginsProperty();
@Option( @Option(
names = {"--rpc-api"}, names = {"--rpc-http-api", "--rpc-http-apis"},
paramLabel = "<api name>", paramLabel = "<api name>",
split = ",", split = ",",
arity = "1..*", 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}", description = "Comma separated APIs to enable on JSON-RPC channel. default: ${DEFAULT-VALUE}",
defaultValue = "ETH,NET,WEB3,CLIQUE,IBFT" defaultValue = "ETH,NET,WEB3,CLIQUE,IBFT"
) )
private final Collection<RpcApi> rpcApis = null; private final Collection<RpcApi> rpcHttpApis = null;
@Option( @Option(
names = {"--ws-enabled"}, names = {"--rpc-ws-enabled"},
description = description =
"Set if the WS-RPC (WebSocket) service should be started (default: ${DEFAULT-VALUE})" "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( @Option(
names = {"--ws-listen"}, names = {"--rpc-ws-port"},
paramLabel = MANDATORY_HOST_AND_PORT_FORMAT_HELP, paramLabel = MANDATORY_PORT_FORMAT_HELP,
description = "Host and port for WS-RPC (WebSocket) to listen on (default: ${DEFAULT-VALUE})", description = "Port for WebSocket JSON-RPC to listen on (default: ${DEFAULT-VALUE})",
arity = "1" arity = "1"
) )
private final HostAndPort wsHostAndPort = private final Integer rpcWsPort = DEFAULT_WEBSOCKET_PORT;
getDefaultHostAndPort(WebSocketConfiguration.DEFAULT_WEBSOCKET_PORT);
@Option( @Option(
names = {"--ws-api"}, names = {"--rpc-ws-api", "--rpc-ws-apis"},
paramLabel = "<api name>", paramLabel = "<api name>",
split = ",", split = ",",
arity = "1..*", arity = "1..*",
@ -332,29 +374,29 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
description = "Comma separated APIs to enable on WebSocket channel. default: ${DEFAULT-VALUE}", description = "Comma separated APIs to enable on WebSocket channel. default: ${DEFAULT-VALUE}",
defaultValue = "ETH,NET,WEB3,CLIQUE,IBFT" 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( @Option(
names = {"--ws-refresh-delay"}, names = {"--rpc-ws-refresh-delay"},
paramLabel = "<refresh delay>", paramLabel = "<refresh delay>",
arity = "1", arity = "1",
description = description =
"Refresh delay of websocket subscription sync in milliseconds. " "Refresh delay of websocket subscription sync in milliseconds. "
+ "default: ${DEFAULT-VALUE}", + "default: ${DEFAULT-VALUE}",
defaultValue = "" + WebSocketConfiguration.DEFAULT_WEBSOCKET_REFRESH_DELAY defaultValue = "" + DEFAULT_WEBSOCKET_REFRESH_DELAY
) )
private Long configureRefreshDelay(final Long refreshDelay) { private Long configureRefreshDelay(final Long refreshDelay) {
if (refreshDelay < DEFAULT_MIN_REFRESH_DELAY || refreshDelay > DEFAULT_MAX_REFRESH_DELAY) { if (refreshDelay < DEFAULT_MIN_REFRESH_DELAY || refreshDelay > DEFAULT_MAX_REFRESH_DELAY) {
throw new ParameterException( throw new ParameterException(
new CommandLine(this), new CommandLine(this),
String.format( 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_MIN_REFRESH_DELAY),
String.valueOf(DEFAULT_MAX_REFRESH_DELAY))); String.valueOf(DEFAULT_MAX_REFRESH_DELAY)));
} }
this.refreshDelay = refreshDelay; this.rpcWsRefreshDelay = refreshDelay;
return refreshDelay; return refreshDelay;
} }
@ -365,23 +407,33 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
private final Boolean isMetricsEnabled = false; private final Boolean isMetricsEnabled = false;
@Option( @Option(
names = {"--metrics-listen"}, names = {"--metrics-host"},
paramLabel = MANDATORY_HOST_AND_PORT_FORMAT_HELP, paramLabel = MANDATORY_HOST_FORMAT_HELP,
description = "Host and port for the metrics exporter to listen on (default: ${DEFAULT-VALUE})", description = "Host for the metrics exporter to listen on (default: ${DEFAULT-VALUE})",
arity = "1" arity = "1"
) )
private final HostAndPort metricsHostAndPort = private final HostSpecifier metricsHost =
getDefaultHostAndPort(MetricsConfiguration.DEFAULT_METRICS_PORT); 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( @Option(
names = {"--host-whitelist"}, names = {"--host-whitelist"},
paramLabel = "<hostname>", paramLabel = "<hostname>[,<hostname>...]... or * or all",
description = 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" defaultValue = "localhost"
) )
private final JsonRPCWhitelistHostsProperty hostsWhitelist = new JsonRPCWhitelistHostsProperty(); private final JsonRPCWhitelistHostsProperty hostsWhitelist = new JsonRPCWhitelistHostsProperty();
/** @deprecated Deprecated in favour of --network option */
@Deprecated
@Option( @Option(
names = {"--dev-mode"}, names = {"--dev-mode"},
description = description =
@ -392,7 +444,9 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
@Option( @Option(
names = {"--logging", "-l"}, 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; private final Level logLevel = null;
@ -405,23 +459,23 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
@Option( @Option(
names = {"--miner-coinbase"}, names = {"--miner-coinbase"},
description = description =
"the account to which mining rewards are to be paid, must be specified if " "account to which mining rewards are paid. You must specify a valid coinbase if "
+ "mining is enabled.", + "mining is enabled using --miner-enabled option.",
arity = "1" arity = "1"
) )
private final Address coinbase = null; private final Address coinbase = null;
@Option( @Option(
names = {"--miner-minTransactionGasPriceWei"}, names = {"--min-gas-price"},
description = 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}).", + "block (default: ${DEFAULT-VALUE}).",
arity = "1" arity = "1"
) )
private final Wei minTransactionGasPrice = DEFAULT_MIN_TRANSACTION_GAS_PRICE; private final Wei minTransactionGasPrice = DEFAULT_MIN_TRANSACTION_GAS_PRICE;
@Option( @Option(
names = {"--miner-extraData"}, names = {"--miner-extra-data"},
description = description =
"a hex string representing the (32) bytes to be included in the extra data " "a hex string representing the (32) bytes to be included in the extra data "
+ "field of a mined block. (default: ${DEFAULT-VALUE}).", + "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. // Permissioning: A list of whitelist nodes can be passed.
@Option( @Option(
names = {"--nodes-whitelist"}, names = {"--nodes-whitelist"},
paramLabel = "<enode://id@host:port>",
description = description =
"Comma separated enode URLs for permissioned networks. " "Comma separated enode URLs for permissioned networks. "
+ "Not intended to be used with mainnet or public testnets.", + "Not intended to be used with mainnet or public testnets.",
@ -479,13 +532,15 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
commandLine.addMixin("standaloneCommands", standaloneCommands); commandLine.addMixin("standaloneCommands", standaloneCommands);
} }
final ImportSubCommand importSubCommand = new ImportSubCommand(blockImporter); commandLine.addSubcommand(
commandLine.addSubcommand("import", importSubCommand); BlocksSubCommand.COMMAND_NAME, new BlocksSubCommand(blockImporter, resultHandler.out()));
commandLine.addSubcommand("export-pub-key", new ExportPublicKeySubCommand()); commandLine.addSubcommand(
PublicKeySubCommand.COMMAND_NAME, new PublicKeySubCommand(resultHandler.out()));
commandLine.registerConverter(Address.class, Address::fromHexString); commandLine.registerConverter(Address.class, Address::fromHexString);
commandLine.registerConverter(BytesValue.class, BytesValue::fromHexString); commandLine.registerConverter(BytesValue.class, BytesValue::fromHexString);
commandLine.registerConverter(HostAndPort.class, HostAndPort::fromString); commandLine.registerConverter(HostAndPort.class, HostAndPort::fromString);
commandLine.registerConverter(HostSpecifier.class, HostSpecifier::from);
commandLine.registerConverter(Level.class, Level::valueOf); commandLine.registerConverter(Level.class, Level::valueOf);
commandLine.registerConverter(SyncMode.class, SyncMode::fromString); commandLine.registerConverter(SyncMode.class, SyncMode::fromString);
commandLine.registerConverter(Wei.class, (arg) -> Wei.of(Long.parseUnsignedLong(arg))); commandLine.registerConverter(Wei.class, (arg) -> Wei.of(Long.parseUnsignedLong(arg)));
@ -534,7 +589,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
noPeerDiscovery, noPeerDiscovery,
ethNetworkConfig.getBootNodes(), ethNetworkConfig.getBootNodes(),
maxPeers, maxPeers,
p2pHostAndPort, HostAndPort.fromParts(p2pHost.toString(), p2pPort),
jsonRpcConfiguration(), jsonRpcConfiguration(),
webSocketConfiguration(), webSocketConfiguration(),
metricsConfiguration(), metricsConfiguration(),
@ -571,7 +626,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
PantheonController<?> buildController() { PantheonController<?> buildController() {
try { try {
return controllerBuilder return controllerBuilder
.synchronizerConfiguration(buildSyncConfig(syncMode)) .synchronizerConfiguration(buildSyncConfig())
.homePath(dataDir()) .homePath(dataDir())
.ethNetworkConfig(ethNetworkConfig()) .ethNetworkConfig(ethNetworkConfig())
.syncWithOttoman(syncWithOttoman) .syncWithOttoman(syncWithOttoman)
@ -596,30 +651,30 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
private JsonRpcConfiguration jsonRpcConfiguration() { private JsonRpcConfiguration jsonRpcConfiguration() {
final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault(); final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
jsonRpcConfiguration.setEnabled(isJsonRpcEnabled); jsonRpcConfiguration.setEnabled(isHttpRpcEnabled);
jsonRpcConfiguration.setHost(rpcHostAndPort.getHost()); jsonRpcConfiguration.setHost(rpcHttpHost.toString());
jsonRpcConfiguration.setPort(rpcHostAndPort.getPort()); jsonRpcConfiguration.setPort(rpcHttpPort);
jsonRpcConfiguration.setCorsAllowedDomains(rpcCorsAllowedOrigins); jsonRpcConfiguration.setCorsAllowedDomains(rpcHttpCorsAllowedOrigins);
jsonRpcConfiguration.setRpcApis(rpcApis); jsonRpcConfiguration.setRpcApis(rpcHttpApis);
jsonRpcConfiguration.setHostsWhitelist(hostsWhitelist); jsonRpcConfiguration.setHostsWhitelist(hostsWhitelist);
return jsonRpcConfiguration; return jsonRpcConfiguration;
} }
private WebSocketConfiguration webSocketConfiguration() { private WebSocketConfiguration webSocketConfiguration() {
final WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault(); final WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault();
webSocketConfiguration.setEnabled(isWsRpcEnabled); webSocketConfiguration.setEnabled(isRpcWsEnabled);
webSocketConfiguration.setHost(wsHostAndPort.getHost()); webSocketConfiguration.setHost(rpcWsHost.toString());
webSocketConfiguration.setPort(wsHostAndPort.getPort()); webSocketConfiguration.setPort(rpcWsPort);
webSocketConfiguration.setRpcApis(wsApis); webSocketConfiguration.setRpcApis(rpcWsApis);
webSocketConfiguration.setRefreshDelay(refreshDelay); webSocketConfiguration.setRefreshDelay(rpcWsRefreshDelay);
return webSocketConfiguration; return webSocketConfiguration;
} }
MetricsConfiguration metricsConfiguration() { MetricsConfiguration metricsConfiguration() {
final MetricsConfiguration metricsConfiguration = MetricsConfiguration.createDefault(); final MetricsConfiguration metricsConfiguration = createDefault();
metricsConfiguration.setEnabled(isMetricsEnabled); metricsConfiguration.setEnabled(isMetricsEnabled);
metricsConfiguration.setHost(metricsHostAndPort.getHost()); metricsConfiguration.setHost(metricsHost.toString());
metricsConfiguration.setPort(metricsHostAndPort.getPort()); metricsConfiguration.setPort(metricsPort);
metricsConfiguration.setHostsWhitelist(hostsWhitelist); metricsConfiguration.setHostsWhitelist(hostsWhitelist);
return metricsConfiguration; return metricsConfiguration;
} }
@ -632,8 +687,7 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
return permissioningConfiguration; return permissioningConfiguration;
} }
private SynchronizerConfiguration buildSyncConfig(final SyncMode syncMode) { private SynchronizerConfiguration buildSyncConfig() {
checkNotNull(syncMode);
synchronizerConfigurationBuilder.syncMode(syncMode); synchronizerConfigurationBuilder.syncMode(syncMode);
synchronizerConfigurationBuilder.maxTrailingPeers(maxTrailingPeers); synchronizerConfigurationBuilder.maxTrailingPeers(maxTrailingPeers);
return synchronizerConfigurationBuilder.build(); return synchronizerConfigurationBuilder.build();
@ -705,10 +759,6 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
return autoDiscoveredDefaultIP; return autoDiscoveredDefaultIP;
} }
private HostAndPort getDefaultHostAndPort(final int port) {
return HostAndPort.fromParts(autoDiscoverDefaultIP().getHostAddress(), port);
}
private EthNetworkConfig ethNetworkConfig() { private EthNetworkConfig ethNetworkConfig() {
final EthNetworkConfig predefinedNetworkConfig; final EthNetworkConfig predefinedNetworkConfig;
if (rinkeby) { if (rinkeby) {
@ -765,11 +815,11 @@ public class PantheonCommand implements DefaultCommandValues, Runnable {
private Path dataDir() { private Path dataDir() {
if (isFullInstantiation()) { if (isFullInstantiation()) {
return standaloneCommands.dataDir; return standaloneCommands.dataPath;
} else if (isDocker) { } else if (isDocker) {
return Paths.get(DOCKER_DATADIR_LOCATION); return Paths.get(DOCKER_DATADIR_LOCATION);
} else { } 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; 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.io.File;
import java.nio.file.Path; import java.nio.file.Path;
@ -23,26 +23,27 @@ class StandaloneCommand implements DefaultCommandValues {
@CommandLine.Option( @CommandLine.Option(
names = {CONFIG_FILE_OPTION_NAME}, names = {CONFIG_FILE_OPTION_NAME},
paramLabel = MANDATORY_PATH_FORMAT_HELP, paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "TOML config file (default: none)" description = "TOML config file (default: none)"
) )
private final File configFile = null; private final File configFile = null;
@CommandLine.Option( @CommandLine.Option(
names = {"--datadir"}, names = {"--data-path"},
paramLabel = MANDATORY_PATH_FORMAT_HELP, paramLabel = MANDATORY_PATH_FORMAT_HELP,
description = "The path to Pantheon data directory (default: ${DEFAULT-VALUE})" 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 // Genesis file path with null default option if the option
// is not defined on command line as this default is handled by Runner // is not defined on command line as this default is handled by Runner
// to use mainnet json file from resources // to use mainnet json file from resources
// NOTE: we have no control over default value here. // NOTE: we have no control over default value here.
@CommandLine.Option( @CommandLine.Option(
names = {"--genesis"}, names = {"--private-genesis-file"},
paramLabel = MANDATORY_PATH_FORMAT_HELP, paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "The path to genesis file (default: Pantheon embedded mainnet genesis file)" description =
"The path to genesis file. Setting this will also override --chain option to be CUSTOM"
) )
final File genesisFile = null; 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.any;
import static org.mockito.ArgumentMatchers.anyBoolean; 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 static org.mockito.Mockito.when;
import tech.pegasys.pantheon.Runner; import tech.pegasys.pantheon.Runner;
@ -38,6 +40,8 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Captor; import org.mockito.Captor;
@ -79,6 +83,14 @@ public abstract class CommandTestAbstract {
@Captor ArgumentCaptor<PermissioningConfiguration> permissioningConfigurationArgumentCaptor; @Captor ArgumentCaptor<PermissioningConfiguration> permissioningConfigurationArgumentCaptor;
@Captor ArgumentCaptor<Collection<URI>> uriListArgumentCaptor; @Captor ArgumentCaptor<Collection<URI>> uriListArgumentCaptor;
@Rule public final TemporaryFolder temp = new TemporaryFolder();
@Before
@After
public void resetSystemProps() {
System.setProperty("pantheon.docker", "false");
}
@Before @Before
public void initMocks() throws Exception { public void initMocks() throws Exception {
// doReturn used because of generic PantheonController // doReturn used because of generic PantheonController
@ -93,6 +105,23 @@ public abstract class CommandTestAbstract {
when(mockControllerBuilder.metricsSystem(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.metricsSystem(any())).thenReturn(mockControllerBuilder);
when(mockSyncConfBuilder.build()).thenReturn(mockSyncConf); 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 // 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.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue; 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.eq;
import static org.mockito.ArgumentMatchers.isNotNull; import static org.mockito.ArgumentMatchers.isNotNull;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions; 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 static tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration.MAINNET_BOOTSTRAP_NODES;
import tech.pegasys.pantheon.PantheonInfo; 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.Toml;
import net.consensys.cava.toml.TomlParseResult; import net.consensys.cava.toml.TomlParseResult;
import org.apache.commons.text.StringEscapeUtils; import org.apache.commons.text.StringEscapeUtils;
import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers; import org.mockito.ArgumentMatchers;
import picocli.CommandLine; import picocli.CommandLine;
@ -74,7 +66,6 @@ public class PantheonCommandTest extends CommandTestAbstract {
private final String VALID_NODE_ID = private final String VALID_NODE_ID =
"6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"; "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0";
@Rule public final TemporaryFolder temp = new TemporaryFolder();
private static final JsonRpcConfiguration defaultJsonRpcConfiguration; private static final JsonRpcConfiguration defaultJsonRpcConfiguration;
private static final WebSocketConfiguration defaultWebSocketConfiguration; private static final WebSocketConfiguration defaultWebSocketConfiguration;
private static final MetricsConfiguration defaultMetricsConfiguration; private static final MetricsConfiguration defaultMetricsConfiguration;
@ -105,34 +96,6 @@ public class PantheonCommandTest extends CommandTestAbstract {
defaultMetricsConfiguration = MetricsConfiguration.createDefault(); 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 @Test
public void callingHelpSubCommandMustDisplayUsage() { public void callingHelpSubCommandMustDisplayUsage() {
parseCommand("--help"); parseCommand("--help");
@ -198,9 +161,10 @@ public class PantheonCommandTest extends CommandTestAbstract {
public void callingWithConfigOptionButNoConfigFileShouldDisplayHelp() { public void callingWithConfigOptionButNoConfigFileShouldDisplayHelp() {
assumeTrue(isFullInstantiation()); 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(commandErrorOutput.toString()).startsWith(expectedOutputStart);
assertThat(commandOutput.toString()).isEmpty(); assertThat(commandOutput.toString()).isEmpty();
} }
@ -210,7 +174,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
assumeTrue(isFullInstantiation()); assumeTrue(isFullInstantiation());
final File tempConfigFile = temp.newFile("an-invalid-file-name-without-extension"); 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; final String expectedOutputStart = "Unable to read TOML configuration file " + tempConfigFile;
assertThat(commandErrorOutput.toString()).startsWith(expectedOutputStart); assertThat(commandErrorOutput.toString()).startsWith(expectedOutputStart);
@ -221,7 +185,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
public void callingWithConfigOptionButTomlFileNotFoundShouldDisplayHelp() { public void callingWithConfigOptionButTomlFileNotFoundShouldDisplayHelp() {
assumeTrue(isFullInstantiation()); 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."; final String expectedOutputStart = "Unable to read TOML configuration, file not found.";
assertThat(commandErrorOutput.toString()).startsWith(expectedOutputStart); assertThat(commandErrorOutput.toString()).startsWith(expectedOutputStart);
@ -240,7 +204,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
fileWriter.write("."); // an invalid toml content fileWriter.write("."); // an invalid toml content
fileWriter.flush(); fileWriter.flush();
parseCommand("--config", tempConfigFile.getPath()); parseCommand("--config-file", tempConfigFile.getPath());
final String expectedOutputStart = final String expectedOutputStart =
"Invalid TOML configuration : Unexpected '.', expected a-z, A-Z, 0-9, ', \", a table key, " "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.write("tester===========......."); // an invalid toml content
fileWriter.flush(); fileWriter.flush();
parseCommand("--config", tempConfigFile.getPath()); parseCommand("--config-file", tempConfigFile.getPath());
final String expectedOutputStart = final String expectedOutputStart =
"Invalid TOML configuration : Unexpected '=', expected ', \", ''', \"\"\", a number, " "Invalid TOML configuration : Unexpected '=', expected ', \", ''', \"\"\", a number, "
@ -306,7 +270,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
metricsConfiguration.setHost("8.6.7.5"); metricsConfiguration.setHost("8.6.7.5");
metricsConfiguration.setPort(309); metricsConfiguration.setPort(309);
parseCommand("--config", toml.toString()); parseCommand("--config-file", toml.toString());
verify(mockRunnerBuilder).discovery(eq(false)); verify(mockRunnerBuilder).discovery(eq(false));
verify(mockRunnerBuilder).bootstrapPeers(uriListArgumentCaptor.capture()); verify(mockRunnerBuilder).bootstrapPeers(uriListArgumentCaptor.capture());
@ -353,14 +317,14 @@ public class PantheonCommandTest extends CommandTestAbstract {
Files.write(toml, Resources.toByteArray(configFile)); Files.write(toml, Resources.toByteArray(configFile));
// Parse it. // 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); final TomlParseResult tomlResult = Toml.parse(toml);
// Verify we configured everything // Verify we configured everything
final HashSet<CommandLine.Model.OptionSpec> options = new HashSet<>(spec.options()); final HashSet<CommandLine.Model.OptionSpec> options = new HashSet<>(spec.options());
// Except for meta-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("--help"));
options.remove(spec.optionsMap().get("--version")); options.remove(spec.optionsMap().get("--version"));
@ -390,7 +354,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
final String configFile = Resources.getResource("partial_config.toml").getFile(); final String configFile = Resources.getResource("partial_config.toml").getFile();
parseCommand("--config", configFile); parseCommand("--config-file", configFile);
final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault(); final JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault();
jsonRpcConfiguration.addRpcApi(CliqueRpcApis.CLIQUE); jsonRpcConfiguration.addRpcApi(CliqueRpcApis.CLIQUE);
jsonRpcConfiguration.addRpcApi(IbftRpcApis.IBFT); jsonRpcConfiguration.addRpcApi(IbftRpcApis.IBFT);
@ -440,7 +404,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
public void nodekeyOptionMustBeUsed() throws Exception { public void nodekeyOptionMustBeUsed() throws Exception {
final File file = new File("./specific/key"); 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).homePath(isNotNull());
verify(mockControllerBuilder).syncWithOttoman(eq(false)); verify(mockControllerBuilder).syncWithOttoman(eq(false));
@ -459,7 +423,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
final Path path = Paths.get("."); final Path path = Paths.get(".");
parseCommand("--datadir", path.toString()); parseCommand("--data-path", path.toString());
verify(mockControllerBuilder).homePath(pathArgumentCaptor.capture()); verify(mockControllerBuilder).homePath(pathArgumentCaptor.capture());
verify(mockControllerBuilder).syncWithOttoman(eq(false)); verify(mockControllerBuilder).syncWithOttoman(eq(false));
@ -505,7 +469,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
final ArgumentCaptor<EthNetworkConfig> networkArg = final ArgumentCaptor<EthNetworkConfig> networkArg =
ArgumentCaptor.forClass(EthNetworkConfig.class); ArgumentCaptor.forClass(EthNetworkConfig.class);
parseCommand("--genesis", genesisFile.toString()); parseCommand("--private-genesis-file", genesisFile.toString());
verify(mockControllerBuilder).ethNetworkConfig(networkArg.capture()); verify(mockControllerBuilder).ethNetworkConfig(networkArg.capture());
verify(mockControllerBuilder).build(); verify(mockControllerBuilder).build();
@ -614,10 +578,10 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void callingWithBannedNodeidsOptionButNoValueMustDisplayErrorAndUsage() { public void callingWithBannedNodeidsOptionButNoValueMustDisplayErrorAndUsage() {
parseCommand("--banned-nodeids"); parseCommand("--banned-node-ids");
assertThat(commandOutput.toString()).isEmpty(); assertThat(commandOutput.toString()).isEmpty();
final String expectedErrorOutputStart = 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); assertThat(commandErrorOutput.toString()).startsWith(expectedErrorOutputStart);
} }
@ -637,7 +601,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void callingWithRefreshDelayWithValueMustUseValue() { public void callingWithRefreshDelayWithValueMustUseValue() {
parseCommand("--ws-refresh-delay", "2000"); parseCommand("--rpc-ws-refresh-delay", "2000");
verify(mockRunnerBuilder).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture()); verify(mockRunnerBuilder).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -650,10 +614,10 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void callingWithRefreshDelayWithNegativeValueMustError() { public void callingWithRefreshDelayWithNegativeValueMustError() {
parseCommand("--ws-refresh-delay", "-2000"); parseCommand("--rpc-ws-refresh-delay", "-2000");
assertThat(commandOutput.toString()).isEmpty(); 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); assertThat(commandErrorOutput.toString()).startsWith(expectedErrorMsg);
} }
@ -822,7 +786,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void bannedNodeIdsOptionMustBeUsed() { public void bannedNodeIdsOptionMustBeUsed() {
final String[] nodes = {"0001", "0002", "0003"}; 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).bannedNodeIds(stringListArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -838,7 +802,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
final String host = "1.2.3.4"; final String host = "1.2.3.4";
final int port = 1234; 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).discoveryHost(stringArgumentCaptor.capture());
verify(mockRunnerBuilder).discoveryPort(intArgumentCaptor.capture()); verify(mockRunnerBuilder).discoveryPort(intArgumentCaptor.capture());
@ -866,7 +830,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
assertThat(commandErrorOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty();
} }
@Ignore @Ignore("Ignored as we only have one mode available for now. See NC-1057/NC-1681")
@Test @Test
public void syncModeOptionMustBeUsed() { public void syncModeOptionMustBeUsed() {
@ -904,7 +868,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcEnabledPropertyMustBeUsed() { public void jsonRpcEnabledPropertyMustBeUsed() {
parseCommand("--rpc-enabled"); parseCommand("--rpc-http-enabled");
verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture()); verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -917,7 +881,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void rpcApisPropertyMustBeUsed() { public void rpcApisPropertyMustBeUsed() {
parseCommand("--rpc-api", "ETH,NET"); parseCommand("--rpc-http-api", "ETH,NET");
verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture()); verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -931,20 +895,22 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void rpcApisPropertyWithInvalidEntryMustDisplayError() { public void rpcApisPropertyWithInvalidEntryMustDisplayError() {
parseCommand("--rpc-api", "BOB"); parseCommand("--rpc-http-api", "BOB");
verifyZeroInteractions(mockRunnerBuilder); verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandOutput.toString()).isEmpty(); 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 @Test
public void jsonRpcHostAndPortOptionMustBeUsed() { public void jsonRpcHostAndPortOptionsMustBeUsed() {
final String host = "1.2.3.4"; final String host = "1.2.3.4";
final int port = 1234; 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).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -959,7 +925,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsTwoDomainsMustBuildListWithBothDomains() { public void jsonRpcCorsOriginsTwoDomainsMustBuildListWithBothDomains() {
final String[] origins = {"http://domain1.com", "https://domain2.com"}; 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).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -974,7 +940,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsDoubleCommaFilteredOut() { public void jsonRpcCorsOriginsDoubleCommaFilteredOut() {
final String[] origins = {"http://domain1.com", "https://domain2.com"}; 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).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -989,7 +955,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsWithWildcardMustBuildListWithWildcard() { public void jsonRpcCorsOriginsWithWildcardMustBuildListWithWildcard() {
final String[] origins = {"*"}; 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).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -1003,7 +969,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsWithAllMustBuildListWithWildcard() { public void jsonRpcCorsOriginsWithAllMustBuildListWithWildcard() {
parseCommand("--rpc-cors-origins", "all"); parseCommand("--rpc-http-cors-origins", "all");
verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture()); verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -1017,7 +983,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsWithNoneMustBuildEmptyList() { public void jsonRpcCorsOriginsWithNoneMustBuildEmptyList() {
final String[] origins = {"none"}; 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).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -1031,7 +997,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsNoneWithAnotherDomainMustFail() { public void jsonRpcCorsOriginsNoneWithAnotherDomainMustFail() {
final String[] origins = {"http://domain1.com", "none"}; final String[] origins = {"http://domain1.com", "none"};
parseCommand("--rpc-cors-origins", String.join(",", origins)); parseCommand("--rpc-http-cors-origins", String.join(",", origins));
verifyZeroInteractions(mockRunnerBuilder); verifyZeroInteractions(mockRunnerBuilder);
@ -1043,7 +1009,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsNoneWithAnotherDomainMustFailNoneFirst() { public void jsonRpcCorsOriginsNoneWithAnotherDomainMustFailNoneFirst() {
final String[] origins = {"none", "http://domain1.com"}; final String[] origins = {"none", "http://domain1.com"};
parseCommand("--rpc-cors-origins", String.join(",", origins)); parseCommand("--rpc-http-cors-origins", String.join(",", origins));
verifyZeroInteractions(mockRunnerBuilder); verifyZeroInteractions(mockRunnerBuilder);
@ -1054,7 +1020,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsAllWithAnotherDomainMustFail() { public void jsonRpcCorsOriginsAllWithAnotherDomainMustFail() {
parseCommand("--rpc-cors-origins=http://domain1.com,all"); parseCommand("--rpc-http-cors-origins=http://domain1.com,all");
verifyZeroInteractions(mockRunnerBuilder); verifyZeroInteractions(mockRunnerBuilder);
@ -1065,7 +1031,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsAllWithAnotherDomainMustFailAsFlags() { 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); verifyZeroInteractions(mockRunnerBuilder);
@ -1076,7 +1042,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsWildcardWithAnotherDomainMustFail() { public void jsonRpcCorsOriginsWildcardWithAnotherDomainMustFail() {
parseCommand("--rpc-cors-origins=http://domain1.com,*"); parseCommand("--rpc-http-cors-origins=http://domain1.com,*");
verifyZeroInteractions(mockRunnerBuilder); verifyZeroInteractions(mockRunnerBuilder);
@ -1087,7 +1053,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsWildcardWithAnotherDomainMustFailAsFlags() { 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); verifyZeroInteractions(mockRunnerBuilder);
@ -1099,7 +1065,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsInvalidRegexShouldFail() { public void jsonRpcCorsOriginsInvalidRegexShouldFail() {
final String[] origins = {"**"}; final String[] origins = {"**"};
parseCommand("--rpc-cors-origins", String.join(",", origins)); parseCommand("--rpc-http-cors-origins", String.join(",", origins));
verifyZeroInteractions(mockRunnerBuilder); verifyZeroInteractions(mockRunnerBuilder);
@ -1110,7 +1076,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void jsonRpcCorsOriginsEmtyValueFails() { public void jsonRpcCorsOriginsEmtyValueFails() {
parseCommand("--rpc-cors-origins="); parseCommand("--rpc-http-cors-origins=");
verifyZeroInteractions(mockRunnerBuilder); verifyZeroInteractions(mockRunnerBuilder);
@ -1283,7 +1249,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void wsRpcEnabledPropertyMustBeUsed() { public void wsRpcEnabledPropertyMustBeUsed() {
parseCommand("--ws-enabled"); parseCommand("--rpc-ws-enabled");
verify(mockRunnerBuilder).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture()); verify(mockRunnerBuilder).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -1296,7 +1262,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
@Test @Test
public void wsApiPropertyMustBeUsed() { public void wsApiPropertyMustBeUsed() {
parseCommand("--ws-api", "ETH, NET"); parseCommand("--rpc-ws-api", "ETH, NET");
verify(mockRunnerBuilder).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture()); verify(mockRunnerBuilder).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -1312,7 +1278,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
public void wsRpcHostAndPortOptionMustBeUsed() { public void wsRpcHostAndPortOptionMustBeUsed() {
final String host = "1.2.3.4"; final String host = "1.2.3.4";
final int port = 1234; 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).webSocketConfiguration(wsRpcConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -1354,7 +1320,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
public void metricsHostAndPortOptionMustBeUsed() { public void metricsHostAndPortOptionMustBeUsed() {
final String host = "1.2.3.4"; final String host = "1.2.3.4";
final int port = 1234; 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).metricsConfiguration(metricsConfigArgumentCaptor.capture());
verify(mockRunnerBuilder).build(); verify(mockRunnerBuilder).build();
@ -1398,8 +1364,8 @@ public class PantheonCommandTest extends CommandTestAbstract {
"0x1122334455667788990011223344556677889900112233445566778899001122"; "0x1122334455667788990011223344556677889900112233445566778899001122";
parseCommand( parseCommand(
"--miner-coinbase=" + requestedCoinbase.toString(), "--miner-coinbase=" + requestedCoinbase.toString(),
"--miner-minTransactionGasPriceWei=15", "--min-gas-price=15",
"--miner-extraData=" + extraDataString); "--miner-extra-data=" + extraDataString);
final ArgumentCaptor<MiningParameters> miningArg = final ArgumentCaptor<MiningParameters> miningArg =
ArgumentCaptor.forClass(MiningParameters.class); ArgumentCaptor.forClass(MiningParameters.class);
@ -1499,7 +1465,7 @@ public class PantheonCommandTest extends CommandTestAbstract {
"1", "1",
"--bootnodes", "--bootnodes",
String.join(",", validENodeStrings), String.join(",", validENodeStrings),
"--genesis", "--private-genesis-file",
genesisFile.toString()); genesisFile.toString());
final ArgumentCaptor<EthNetworkConfig> networkArg = final ArgumentCaptor<EthNetworkConfig> networkArg =
@ -1524,9 +1490,9 @@ public class PantheonCommandTest extends CommandTestAbstract {
verifyZeroInteractions(mockRunnerBuilder); verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandOutput.toString()).doesNotContain("--config"); assertThat(commandOutput.toString()).doesNotContain("--config-file");
assertThat(commandOutput.toString()).doesNotContain("--datadir"); assertThat(commandOutput.toString()).doesNotContain("--data-path");
assertThat(commandOutput.toString()).doesNotContain("--genesis"); assertThat(commandOutput.toString()).doesNotContain("--private-genesis-file");
assertThat(commandErrorOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty();
} }
@ -1536,9 +1502,9 @@ public class PantheonCommandTest extends CommandTestAbstract {
verifyZeroInteractions(mockRunnerBuilder); verifyZeroInteractions(mockRunnerBuilder);
assertThat(commandOutput.toString()).contains("--config"); assertThat(commandOutput.toString()).contains("--config-file");
assertThat(commandOutput.toString()).contains("--datadir"); assertThat(commandOutput.toString()).contains("--data-path");
assertThat(commandOutput.toString()).contains("--genesis"); assertThat(commandOutput.toString()).contains("--private-genesis-file");
assertThat(commandErrorOutput.toString()).isEmpty(); 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 # this is a valid TOML config file
datadir="~/pantheondata" # Path data-path="~/pantheondata" # Path
#invalid-option=true #invalid-option=true
@ -11,14 +11,18 @@ bootnodes=[
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567", "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567",
"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 max-peers=42
rpc-listen="5.6.7.8:5678" # IP:port rpc-http-host="5.6.7.8"
ws-listen="9.10.11.12:9101" # IP:port rpc-http-port=5678
metrics-listen="8.6.7.5:309" # IP:port rpc-ws-host="9.10.11.12"
rpc-ws-port=9101
metrics-host="8.6.7.5"
metrics-port=309
# chain # chain
genesis="~/genesis.json" # Path private-genesis-file="~/genesis.json" # Path
sync-mode="fast"# should be FAST or FULL (or fast or full) sync-mode="fast"# should be FAST or FULL (or fast or full)
ottoman=false # true means using ottoman testnet if genesys file uses iBFT ottoman=false # true means using ottoman testnet if genesys file uses iBFT

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

Loading…
Cancel
Save