[#536] Add support for custom private key file for public-key export and public-key export-address commands (#2801)

* Extract private key file option to mixin (#536)

Signed-off-by: Diego López León <dieguitoll@gmail.com>

* Method rename (#536)

Signed-off-by: Diego López León <dieguitoll@gmail.com>

* Use keypair to resolve node key in public-key subcommands (#536)

Signed-off-by: Diego López León <dieguitoll@gmail.com>

* Refactor common behavior for public key subcommands (#536)

Signed-off-by: Diego López León <dieguitoll@gmail.com>

* Write test private key to default location (#536)

This is to rely on actual file system resolution for the test context

Signed-off-by: Diego López León <dieguitoll@gmail.com>

* Add support for custom private key file on public-key subcommands (#536)

Signed-off-by: Diego López León <dieguitoll@gmail.com>

Co-authored-by: Sally MacFarlane <sally.macfarlane@consensys.net>
pull/2836/head
Diego López León 3 years ago committed by GitHub
parent babde1a231
commit 27a5e53f5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 34
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  3. 39
      besu/src/main/java/org/hyperledger/besu/cli/options/stable/NodePrivateKeyFileOption.java
  4. 97
      besu/src/main/java/org/hyperledger/besu/cli/subcommands/PublicKeySubCommand.java
  5. 35
      besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java
  6. 142
      besu/src/test/java/org/hyperledger/besu/cli/PublicKeySubCommandTest.java

@ -5,6 +5,7 @@
- The EVM has been factored out into a standalone module, suitable for inclusion as a library. [#2790](https://github.com/hyperledger/besu/pull/2790)
- Low level performance improvements changes to cut worst-case EVM performance in half. [#2796](https://github.com/hyperledger/besu/pull/2796)
- Migrate `ExceptionalHaltReason` from an enum to an interface to allow downstream users of the EVM to add new exceptional halt reasons. [#2810](https://github.com/hyperledger/besu/pull/2810)
- Add support for custom private key file for public-key export and public-key export-address commands [#2801](https://github.com/hyperledger/besu/pull/2801)
### Bug Fixes
- Allow BESU_CONFIG_FILE environment to specify TOML file [#2455](https://github.com/hyperledger/besu/issues/2455)

@ -49,6 +49,7 @@ import org.hyperledger.besu.cli.custom.JsonRPCAllowlistHostsProperty;
import org.hyperledger.besu.cli.custom.RpcAuthFileValidator;
import org.hyperledger.besu.cli.error.BesuExceptionHandler;
import org.hyperledger.besu.cli.options.stable.EthstatsOptions;
import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption;
import org.hyperledger.besu.cli.options.stable.P2PTLSConfigOptions;
import org.hyperledger.besu.cli.options.unstable.DataStorageOptions;
import org.hyperledger.besu.cli.options.unstable.DnsOptions;
@ -268,6 +269,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
// stable CLI options
private final EthstatsOptions ethstatsOptions = EthstatsOptions.create();
private final NodePrivateKeyFileOption nodePrivateKeyFileOption =
NodePrivateKeyFileOption.create();
private final RunnerBuilder runnerBuilder;
private final BesuController.Builder controllerBuilderFactory;
@ -321,13 +324,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
"Genesis file. Setting this option makes --network option ignored and requires --network-id to be set.")
private final File genesisFile = null;
@CommandLine.Option(
names = {"--node-private-key-file"},
paramLabel = MANDATORY_PATH_FORMAT_HELP,
description =
"The node's private key file (default: a file named \"key\" in the Besu data folder)")
private final File nodePrivateKeyFile = null;
@Option(
names = "--identity",
paramLabel = "<String>",
@ -1245,8 +1241,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
rlpBlockExporterFactory,
resultHandler.out()));
commandLine.addSubcommand(
PublicKeySubCommand.COMMAND_NAME,
new PublicKeySubCommand(resultHandler.out(), this::buildNodeKey));
PublicKeySubCommand.COMMAND_NAME, new PublicKeySubCommand(resultHandler.out()));
commandLine.addSubcommand(
PasswordSubCommand.COMMAND_NAME, new PasswordSubCommand(resultHandler.out()));
commandLine.addSubcommand(RetestethSubCommand.COMMAND_NAME, new RetestethSubCommand());
@ -1276,6 +1271,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private void handleStableOptions() {
commandLine.addMixin("Ethstats", ethstatsOptions);
commandLine.addMixin("Private key file", nodePrivateKeyFileOption);
}
private void handleUnstableOptions() {
@ -1325,12 +1321,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
}
private SecurityModule defaultSecurityModule() {
return new KeyPairSecurityModule(loadKeyPair());
return new KeyPairSecurityModule(loadKeyPair(nodePrivateKeyFileOption.getNodePrivateKeyFile()));
}
@VisibleForTesting
KeyPair loadKeyPair() {
return KeyPairUtil.loadKeyPair(nodePrivateKeyFile());
// loadKeyPair() is public because it is accessed by subcommands
public KeyPair loadKeyPair(final File nodePrivateKeyFile) {
return KeyPairUtil.loadKeyPair(resolveNodePrivateKeyFile(nodePrivateKeyFile));
}
private void parse(
@ -1568,7 +1564,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
!SyncMode.FAST.equals(syncMode),
singletonList("--fast-sync-min-peers"));
if (!securityModuleName.equals(DEFAULT_SECURITY_MODULE) && nodePrivateKeyFile != null) {
if (!securityModuleName.equals(DEFAULT_SECURITY_MODULE)
&& nodePrivateKeyFileOption.getNodePrivateKeyFile() != null) {
logger.warn(
DEPENDENCY_WARNING_MSG,
"--node-private-key-file",
@ -1717,7 +1714,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.maxOmmerDepth(unstableMiningOptions.getMaxOmmersDepth())
.build())
.transactionPoolConfiguration(buildTransactionPoolConfiguration())
.nodeKey(buildNodeKey())
.nodeKey(new NodeKey(securityModule()))
.metricsSystem(metricsSystem.get())
.messagePermissioningProviders(permissioningService.getMessagePermissioningProviders())
.privacyParameters(privacyParameters(storageProvider))
@ -2539,11 +2536,6 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
}
}
@VisibleForTesting
NodeKey buildNodeKey() {
return new NodeKey(securityModule());
}
private SecurityModule securityModule() {
return securityModuleService
.getByName(securityModuleName)
@ -2551,7 +2543,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.get();
}
private File nodePrivateKeyFile() {
private File resolveNodePrivateKeyFile(final File nodePrivateKeyFile) {
return Optional.ofNullable(nodePrivateKeyFile)
.orElseGet(() -> KeyPairUtil.getDefaultKeyFile(dataDir()));
}

@ -0,0 +1,39 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.cli.options.stable;
import static org.hyperledger.besu.cli.DefaultCommandValues.MANDATORY_PATH_FORMAT_HELP;
import java.io.File;
import picocli.CommandLine;
public class NodePrivateKeyFileOption {
public static NodePrivateKeyFileOption create() {
return new NodePrivateKeyFileOption();
}
@CommandLine.Option(
names = {"--node-private-key-file"},
paramLabel = MANDATORY_PATH_FORMAT_HELP,
description =
"The node's private key file (default: a file named \"key\" in the Besu data directory)")
private final File nodePrivateKeyFile = null;
public File getNodePrivateKeyFile() {
return nodePrivateKeyFile;
}
}

@ -20,10 +20,10 @@ import static org.hyperledger.besu.cli.subcommands.PublicKeySubCommand.COMMAND_N
import org.hyperledger.besu.cli.BesuCommand;
import org.hyperledger.besu.cli.DefaultCommandValues;
import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption;
import org.hyperledger.besu.cli.subcommands.PublicKeySubCommand.AddressSubCommand;
import org.hyperledger.besu.cli.subcommands.PublicKeySubCommand.ExportSubCommand;
import org.hyperledger.besu.crypto.NodeKey;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.ethereum.core.Util;
import java.io.BufferedWriter;
@ -32,12 +32,13 @@ import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.function.Function;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.ParentCommand;
@ -63,11 +64,9 @@ public class PublicKeySubCommand implements Runnable {
private CommandSpec spec; // Picocli injects reference to command spec
private final PrintStream out;
private final Supplier<NodeKey> nodeKey;
public PublicKeySubCommand(final PrintStream out, final Supplier<NodeKey> nodeKey) {
public PublicKeySubCommand(final PrintStream out) {
this.out = out;
this.nodeKey = nodeKey;
}
@Override
@ -75,10 +74,6 @@ public class PublicKeySubCommand implements Runnable {
spec.commandLine().usage(out);
}
private NodeKey getNodeKey() {
return nodeKey.get();
}
/**
* Public key export sub-command
*
@ -91,7 +86,7 @@ public class PublicKeySubCommand implements Runnable {
name = "export",
description = "This command outputs the node public key. Default output is standard output.",
mixinStandardHelpOptions = true)
static class ExportSubCommand implements Runnable {
static class ExportSubCommand extends KeyPairSubcommand implements Runnable {
@Option(
names = "--to",
@ -100,33 +95,9 @@ public class PublicKeySubCommand implements Runnable {
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 NodeKey nodeKey = parentCommand.getNodeKey();
Optional.ofNullable(nodeKey).ifPresent(this::outputPublicKey);
}
private void outputPublicKey(final NodeKey nodeKey) {
// if we have an output file defined, print to it
// otherwise print to standard output.
if (publicKeyExportFile != null) {
final Path path = publicKeyExportFile.toPath();
try (final BufferedWriter fileWriter = Files.newBufferedWriter(path, UTF_8)) {
fileWriter.write(nodeKey.getPublicKey().toString());
} catch (final IOException e) {
LOG.error("An error occurred while trying to write the public key", e);
}
} else {
parentCommand.out.println(nodeKey.getPublicKey().toString());
}
run(publicKeyExportFile, keyPair -> keyPair.getPublicKey().toString());
}
}
@ -144,7 +115,7 @@ public class PublicKeySubCommand implements Runnable {
"This command outputs the node's account address. "
+ "Default output is standard output.",
mixinStandardHelpOptions = true)
static class AddressSubCommand implements Runnable {
static class AddressSubCommand extends KeyPairSubcommand implements Runnable {
@Option(
names = "--to",
@ -153,34 +124,52 @@ public class PublicKeySubCommand implements Runnable {
arity = "1..1")
private final File addressExportFile = null;
@Override
public void run() {
run(addressExportFile, keyPair -> Util.publicKeyToAddress(keyPair.getPublicKey()).toString());
}
}
private static class KeyPairSubcommand {
@SuppressWarnings("unused")
@ParentCommand
private PublicKeySubCommand parentCommand; // Picocli injects reference to parent command
@Override
public void run() {
checkNotNull(parentCommand);
checkNotNull(parentCommand.parentCommand);
@Mixin private final NodePrivateKeyFileOption nodePrivateKeyFileOption = null;
final NodeKey nodeKey = parentCommand.getNodeKey();
Optional.ofNullable(nodeKey).ifPresent(this::outputAddress);
}
@Spec private final CommandSpec spec = null;
private void outputAddress(final NodeKey nodeKey) {
final Address address = Util.publicKeyToAddress(nodeKey.getPublicKey());
protected final void run(
final File exportFile, final Function<KeyPair, String> outputFunction) {
checkNotNull(parentCommand);
final BesuCommand besuCommand = parentCommand.parentCommand;
checkNotNull(besuCommand);
// if we have an output file defined, print to it
// otherwise print to standard output.
if (addressExportFile != null) {
final Path path = addressExportFile.toPath();
final File nodePrivateKeyFile = nodePrivateKeyFileOption.getNodePrivateKeyFile();
if (nodePrivateKeyFile != null && !nodePrivateKeyFile.exists()) {
throw new CommandLine.ParameterException(
spec.commandLine(), "Private key file doesn't exist");
}
final KeyPair keyPair;
try {
keyPair = besuCommand.loadKeyPair(nodePrivateKeyFileOption.getNodePrivateKeyFile());
} catch (IllegalArgumentException e) {
throw new CommandLine.ParameterException(
spec.commandLine(), "Private key cannot be loaded from file", e);
}
final String output = outputFunction.apply(keyPair);
if (exportFile != null) {
final Path path = exportFile.toPath();
try (final BufferedWriter fileWriter = Files.newBufferedWriter(path, UTF_8)) {
fileWriter.write(address.toString());
fileWriter.write(output);
} catch (final IOException e) {
LOG.error("An error occurred while trying to write the account address", e);
LOG.error("An error occurred while trying to write to output file", e);
}
} else {
parentCommand.out.println(address.toString());
parentCommand.out.println(output);
}
}
}

@ -43,6 +43,7 @@ import org.hyperledger.besu.controller.BesuController;
import org.hyperledger.besu.controller.BesuControllerBuilder;
import org.hyperledger.besu.controller.NoopPluginServiceFactory;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.KeyPairUtil;
import org.hyperledger.besu.crypto.NodeKey;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
@ -68,6 +69,7 @@ import org.hyperledger.besu.pki.config.PkiKeyStoreConfiguration;
import org.hyperledger.besu.plugin.services.BesuConfiguration;
import org.hyperledger.besu.plugin.services.PicoCLIOptions;
import org.hyperledger.besu.plugin.services.StorageService;
import org.hyperledger.besu.plugin.services.securitymodule.SecurityModule;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageFactory;
import org.hyperledger.besu.plugin.services.storage.PrivacyKeyValueStorageFactory;
import org.hyperledger.besu.services.BesuPluginContextImpl;
@ -78,9 +80,11 @@ import org.hyperledger.besu.services.StorageServiceImpl;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@ -145,6 +149,7 @@ public abstract class CommandTestAbstract {
@Mock protected RlpBlockImporter rlpBlockImporter;
@Mock protected StorageServiceImpl storageService;
@Mock protected SecurityModuleServiceImpl securityModuleService;
@Mock protected SecurityModule securityModule;
@Mock protected BesuConfiguration commonPluginConfiguration;
@Mock protected KeyValueStorageFactory rocksDBStorageFactory;
@Mock protected PrivacyKeyValueStorageFactory rocksDBSPrivacyStorageFactory;
@ -285,6 +290,9 @@ public abstract class CommandTestAbstract {
lenient()
.when(storageService.getByName(eq("rocksdb-privacy")))
.thenReturn(Optional.of(rocksDBSPrivacyStorageFactory));
lenient()
.when(securityModuleService.getByName(eq("localfile")))
.thenReturn(Optional.of(() -> securityModule));
lenient()
.when(rocksDBSPrivacyStorageFactory.create(any(), any(), any()))
.thenReturn(new InMemoryKeyValueStorage());
@ -339,8 +347,6 @@ public abstract class CommandTestAbstract {
final TestBesuCommand besuCommand =
new TestBesuCommand(
mockLogger,
nodeKey,
keyPair,
() -> rlpBlockImporter,
this::jsonBlockImporterFactory,
(blockchain) -> rlpBlockExporter,
@ -353,6 +359,13 @@ public abstract class CommandTestAbstract {
mockPkiBlockCreationConfigProvider);
besuCommands.add(besuCommand);
File defaultKeyFile =
KeyPairUtil.getDefaultKeyFile(DefaultCommandValues.getDefaultBesuDataPath(besuCommand));
try {
Files.writeString(defaultKeyFile.toPath(), keyPair.getPrivateKey().toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
besuCommand.setBesuConfiguration(commonPluginConfiguration);
// parse using Ansi.OFF to be able to assert on non formatted output results
@ -369,13 +382,9 @@ public abstract class CommandTestAbstract {
@CommandLine.Spec CommandLine.Model.CommandSpec spec;
private Vertx vertx;
private final NodeKey mockNodeKey;
private final KeyPair keyPair;
TestBesuCommand(
final Logger mockLogger,
final NodeKey mockNodeKey,
final KeyPair keyPair,
final Supplier<RlpBlockImporter> mockBlockImporter,
final Function<BesuController, JsonBlockImporter> jsonBlockImporterFactory,
final Function<Blockchain, RlpBlockExporter> rlpBlockExporterFactory,
@ -400,8 +409,6 @@ public abstract class CommandTestAbstract {
new PermissioningServiceImpl(),
new PrivacyPluginServiceImpl(),
pkiBlockCreationConfigProvider);
this.mockNodeKey = mockNodeKey;
this.keyPair = keyPair;
}
@Override
@ -415,18 +422,6 @@ public abstract class CommandTestAbstract {
return vertx;
}
@Override
NodeKey buildNodeKey() {
// for testing.
return mockNodeKey;
}
@Override
KeyPair loadKeyPair() {
// for testing.
return keyPair;
}
public CommandSpec getSpec() {
return spec;
}

@ -17,11 +17,22 @@ package org.hyperledger.besu.cli;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.contentOf;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.NodeKey;
import org.hyperledger.besu.crypto.SECPPrivateKey;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.ethereum.core.Util;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.tuweni.bytes.Bytes32;
import org.bouncycastle.asn1.sec.SECNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.junit.BeforeClass;
import org.junit.Test;
import picocli.CommandLine.Model.CommandSpec;
@ -48,19 +59,29 @@ public class PublicKeySubCommandTest extends CommandTestAbstract {
+ System.lineSeparator();
private static final String EXPECTED_PUBLIC_KEY_EXPORT_USAGE =
"Usage: besu public-key export [-hV] [--to=<FILE>]"
"Usage: besu public-key export [-hV] [--node-private-key-file=<PATH>]"
+ System.lineSeparator()
+ " [--to=<FILE>]"
+ System.lineSeparator()
+ "This command outputs the node public key. Default output is standard output."
+ System.lineSeparator()
+ " -h, --help Show this help message and exit."
+ System.lineSeparator()
+ " --node-private-key-file=<PATH>"
+ System.lineSeparator()
+ " The node's private key file (default: a file named \"key\" in"
+ System.lineSeparator()
+ " the Besu data directory)"
+ System.lineSeparator()
+ " --to=<FILE> File to write public key to instead of standard output"
+ System.lineSeparator()
+ " -V, --version Print version information and exit."
+ System.lineSeparator();
private static final String EXPECTED_PUBLIC_KEY_EXPORT_ADDRESS_USAGE =
"Usage: besu public-key export-address [-hV] [--to=<FILE>]"
"Usage: besu public-key export-address [-hV] [--node-private-key-file=<PATH>]"
+ System.lineSeparator()
+ " [--to=<FILE>]"
+ System.lineSeparator()
+ "This command outputs the node's account address. Default output is standard"
+ System.lineSeparator()
@ -68,6 +89,12 @@ public class PublicKeySubCommandTest extends CommandTestAbstract {
+ System.lineSeparator()
+ " -h, --help Show this help message and exit."
+ System.lineSeparator()
+ " --node-private-key-file=<PATH>"
+ System.lineSeparator()
+ " The node's private key file (default: a file named \"key\" in"
+ System.lineSeparator()
+ " the Besu data directory)"
+ System.lineSeparator()
+ " --to=<FILE> File to write address to instead of standard output"
+ System.lineSeparator()
+ " -V, --version Print version information and exit."
@ -76,6 +103,15 @@ public class PublicKeySubCommandTest extends CommandTestAbstract {
private static final String PUBLIC_KEY_SUBCOMMAND_NAME = "public-key";
private static final String PUBLIC_KEY_EXPORT_SUBCOMMAND_NAME = "export";
private static final String PUBLIC_KEY_EXPORT_ADDRESS_SUBCOMMAND_NAME = "export-address";
private static final String CURVE_NAME = "secp256k1";
private static final String ALGORITHM = SignatureAlgorithm.ALGORITHM;
private static ECDomainParameters curve;
@BeforeClass
public static void setUp() {
final X9ECParameters params = SECNamedCurves.getByName(CURVE_NAME);
curve = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH());
}
// public-key sub-command
@Test
@ -141,6 +177,55 @@ public class PublicKeySubCommandTest extends CommandTestAbstract {
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void callingPublicKeyExportSubCommandWithPrivateKeyFileMustWriteKeyToStandardOutput()
throws IOException {
final SECPPrivateKey privateKey =
SECPPrivateKey.create(
Bytes32.fromHexString(
"0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63"),
ALGORITHM);
final KeyPair keyPair = KeyPair.create(privateKey, curve, ALGORITHM);
final Path privateKeyFile = Files.createTempFile("private", "address");
Files.writeString(privateKeyFile, privateKey.toString());
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME,
PUBLIC_KEY_EXPORT_SUBCOMMAND_NAME,
"--node-private-key-file",
privateKeyFile.toString());
final String expectedOutputStart = keyPair.getPublicKey().toString();
assertThat(commandOutput.toString()).startsWith(expectedOutputStart);
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void callingPublicKeyExportSubCommandWithNonExistentMustDisplayError() {
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME,
PUBLIC_KEY_EXPORT_SUBCOMMAND_NAME,
"--node-private-key-file",
"/non/existent/file");
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).startsWith("Private key file doesn't exist");
}
@Test
public void callingPublicKeyExportSubCommandWithInvalidFileMustDisplayError() throws IOException {
final Path privateKeyFile = Files.createTempFile("private", "address");
Files.writeString(privateKeyFile, "invalid private key");
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME,
PUBLIC_KEY_EXPORT_SUBCOMMAND_NAME,
"--node-private-key-file",
privateKeyFile.toString());
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).startsWith("Private key cannot be loaded from file");
}
// Export address sub-sub-command
@Test
public void callingPublicKeyExportAddressSubCommandHelpMustDisplayUsage() {
@ -181,4 +266,55 @@ public class PublicKeySubCommandTest extends CommandTestAbstract {
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void
callingPublicKeyExportAddressSubCommandWithPrivateKeyFileMustWriteKeyToStandardOutput()
throws IOException {
final SECPPrivateKey privateKey =
SECPPrivateKey.create(
Bytes32.fromHexString(
"0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63"),
ALGORITHM);
final KeyPair keyPair = KeyPair.create(privateKey, curve, ALGORITHM);
final Path privateKeyFile = Files.createTempFile("private", "address");
Files.writeString(privateKeyFile, privateKey.toString());
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME,
PUBLIC_KEY_EXPORT_ADDRESS_SUBCOMMAND_NAME,
"--node-private-key-file",
privateKeyFile.toString());
final String expectedOutputStart = Util.publicKeyToAddress(keyPair.getPublicKey()).toString();
assertThat(commandOutput.toString()).startsWith(expectedOutputStart);
assertThat(commandErrorOutput.toString()).isEmpty();
}
@Test
public void callingPublicKeyExportAddressSubCommandWithNonExistentMustDisplayError() {
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME,
PUBLIC_KEY_EXPORT_ADDRESS_SUBCOMMAND_NAME,
"--node-private-key-file",
"/non/existent/file");
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).startsWith("Private key file doesn't exist");
}
@Test
public void callingPublicKeyExportAddressSubCommandWithInvalidFileMustDisplayError()
throws IOException {
final Path privateKeyFile = Files.createTempFile("private", "address");
Files.writeString(privateKeyFile, "invalid private key");
parseCommand(
PUBLIC_KEY_SUBCOMMAND_NAME,
PUBLIC_KEY_EXPORT_ADDRESS_SUBCOMMAND_NAME,
"--node-private-key-file",
privateKeyFile.toString());
assertThat(commandOutput.toString()).isEmpty();
assertThat(commandErrorOutput.toString()).startsWith("Private key cannot be loaded from file");
}
}

Loading…
Cancel
Save