diff --git a/convex-cli/src/main/java/convex/cli/Main.java b/convex-cli/src/main/java/convex/cli/Main.java index c0453f5ff..ee83a6a65 100644 --- a/convex-cli/src/main/java/convex/cli/Main.java +++ b/convex-cli/src/main/java/convex/cli/Main.java @@ -19,7 +19,7 @@ import convex.api.Convex; import convex.cli.client.Query; import convex.cli.client.Status; -import convex.cli.client.Transaction; +import convex.cli.client.Transact; import convex.cli.key.Key; import convex.cli.local.Local; import convex.cli.output.RecordOutput; @@ -48,7 +48,7 @@ Peer.class, Query.class, Status.class, - Transaction.class, + Transact.class, CommandLine.HelpCommand.class }, usageHelpAutoWidth=true, @@ -67,13 +67,25 @@ public class Main implements Runnable { public CommandLine commandLine=new CommandLine(this); - @Option(names={"-k", "--keystore"}, - defaultValue="${env:CONVEX_KEYSTORE_PASSWORD:-" +Constants.KEYSTORE_FILENAME+"}", + @Option(names={"--keystore"}, + defaultValue="${env:CONVEX_KEYSTORE:-" +Constants.KEYSTORE_FILENAME+"}", scope = ScopeType.INHERIT, description="Keystore filename. Default: ${DEFAULT-VALUE}") private String keyStoreFilename; + + @Option(names={"--k","--key"}, + defaultValue="${env:CONVEX_KEY}", + scope = ScopeType.INHERIT, + description="Keystore filename. Default: ${DEFAULT-VALUE}") + private String keySpec; + + @Option(names={"--p","--password"}, + defaultValue="${env:CONVEX_KEY_PASSWORD}", + scope = ScopeType.INHERIT, + description="Keystore filename. Default: ${DEFAULT-VALUE}") + private String keyPassword; - @Option(names={"--store-password"}, + @Option(names={"--keystore-password"}, scope = ScopeType.INHERIT, defaultValue="${env:CONVEX_KEYSTORE_PASSWORD}", description="Password to read/write to the Keystore") @@ -241,25 +253,17 @@ public char[] getKeyPassword() { } else { if (!nonInteractive) { Console console = System.console(); - keypass= console.readPassword("Private Key Password: "); + keypass= console.readPassword("Private Key Encryption Password: "); } if (keypass==null) { log.warn("No password for key: defaulting to blank password"); keypass=new char[0]; } - } return keypass; } - /** - * Sets the currently defined keystore password - * @param password Password to use - */ - public void setPassword(String password) { - this.keystorePassword=password; - } /** * Gets the keystore file name currently used for the CLI diff --git a/convex-cli/src/main/java/convex/cli/client/AClientCommand.java b/convex-cli/src/main/java/convex/cli/client/AClientCommand.java index 24b7a76a2..1e35141c8 100644 --- a/convex-cli/src/main/java/convex/cli/client/AClientCommand.java +++ b/convex-cli/src/main/java/convex/cli/client/AClientCommand.java @@ -2,11 +2,17 @@ import java.io.IOException; import java.net.InetSocketAddress; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import convex.api.Convex; import convex.cli.ATopCommand; +import convex.cli.CLIError; import convex.cli.Constants; +import convex.core.Result; +import convex.core.crypto.AKeyPair; +import convex.core.data.ABlob; +import convex.core.data.ACell; import convex.core.data.Address; import picocli.CommandLine.Option; @@ -49,6 +55,33 @@ protected boolean ensureAddress(Convex convex) { } return false; } + + protected boolean ensureKeyPair(Convex convex) { + AKeyPair keyPair = convex.getKeyPair(); + if (keyPair!=null) return true; + + Address address=convex.getAddress(); + try { + // Try to identify the required keypair + Result ar=convex.query("*key*").get(1000,TimeUnit.MILLISECONDS); + if (ar.isError()) throw new CLIError("Unable to determine *key* for Address "+address+" : "+ar); + + ACell v=ar.getValue(); + if (v instanceof ABlob) { + String pk=((ABlob)v).toHexString(); + keyPair=mainParent.loadKeyFromStore(pk); + if (keyPair==null) { + // We didn't find required keypair + throw new CLIError("Unable to find keypair with public key "+v+" for Address "+address+" : "+ar); + } + convex.setKeyPair(keyPair); + return true; + } + } catch(Exception e) { + return false; + } + return false; + } protected convex.api.Convex connect() throws IOException,TimeoutException { if (port==null) port=convex.core.Constants.DEFAULT_PEER_PORT; diff --git a/convex-cli/src/main/java/convex/cli/client/Transaction.java b/convex-cli/src/main/java/convex/cli/client/Transact.java similarity index 61% rename from convex-cli/src/main/java/convex/cli/client/Transaction.java rename to convex-cli/src/main/java/convex/cli/client/Transact.java index 5600c9807..94b24908e 100644 --- a/convex-cli/src/main/java/convex/cli/client/Transaction.java +++ b/convex-cli/src/main/java/convex/cli/client/Transact.java @@ -1,15 +1,11 @@ package convex.cli.client; -import java.util.concurrent.TimeUnit; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import convex.api.Convex; import convex.cli.CLIError; import convex.core.Result; -import convex.core.crypto.AKeyPair; -import convex.core.data.ABlob; import convex.core.data.ACell; import convex.core.data.Address; import convex.core.lang.Reader; @@ -29,13 +25,12 @@ @Command(name="transact", mixinStandardHelpOptions=true, description="Execute a user transaction on the Convex network.") -public class Transaction extends AClientCommand { +public class Transact extends AClientCommand { - protected static final Logger log = LoggerFactory.getLogger(Transaction.class); + protected static final Logger log = LoggerFactory.getLogger(Transact.class); @Option(names={"--public-key"}, - defaultValue="", - description="Hex string of the public key in the Keystore to sign the transaction.%n" + description="Hex prefix of the public key in the Keystore to sign the transaction.%n" + "You only need to enter in the first distinct hex values of the public key.%n" + "For example: 0xf0234 or f0234") private String keystorePublicKey; @@ -49,28 +44,12 @@ public void run() { try { Convex convex = connect(); Address address=convex.getAddress(); - if (!ensureAddress(convex)) { - + if (!ensureAddress(convex)) { throw new CLIError("Must specify a valid address for transaction."); } - AKeyPair keyPair = convex.getKeyPair(); - - // If we don't already have keypair specified, attempt to find - // correct key pair for address from the network - if (keyPair==null) { - Result ar=convex.query("*key*").get(1000,TimeUnit.MILLISECONDS); - if (ar.isError()) throw new CLIError("Unable to get *key* for Address "+address+" : "+ar); - ACell v=ar.getValue(); - if (v instanceof ABlob) { - String pk=((ABlob)v).toHexString(); - keyPair=mainParent.loadKeyFromStore(pk); - if (keyPair==null) { - // We didn't find required keypair - throw new CLIError("Unable to get keypair "+pk+" for Address "+address+" : "+ar); - } - convex.setKeyPair(keyPair); - } + if (!ensureKeyPair(convex)) { + throw new CLIError("Must provide a key pair to sign transaction."); } log.debug("Executing transaction: '{}'\n", transactionCode); @@ -78,11 +57,11 @@ public void run() { ATransaction transaction = Invoke.create(address, -1, message); Result result = convex.transactSync(transaction, timeout); mainParent.printResult(result); + } catch (CLIError e) { + throw e; } catch (Exception e) { + // General catch all throw new CLIError("Error executing transation",e); } } - - - } diff --git a/convex-cli/src/main/java/convex/cli/key/KeyImport.java b/convex-cli/src/main/java/convex/cli/key/KeyImport.java index 3bc89a5ad..a578b564f 100644 --- a/convex-cli/src/main/java/convex/cli/key/KeyImport.java +++ b/convex-cli/src/main/java/convex/cli/key/KeyImport.java @@ -51,7 +51,6 @@ public class KeyImport extends AKeyCommand { @Override public void run() { - // sub command to generate keys Main mainParent = cli(); if (importFilename != null && importFilename.length() > 0) { Path path=Paths.get(importFilename); diff --git a/convex-cli/src/test/java/convex/cli/CLIHelpTest.java b/convex-cli/src/test/java/convex/cli/CLIHelpTest.java index 25db017ff..3ee85e6ec 100644 --- a/convex-cli/src/test/java/convex/cli/CLIHelpTest.java +++ b/convex-cli/src/test/java/convex/cli/CLIHelpTest.java @@ -2,7 +2,7 @@ import org.junit.jupiter.api.Test; -import static convex.cli.Helper.assertExecuteCommandLineResult; +import static convex.cli.HelperTest.assertExecuteCommandLineResult; public class CLIHelpTest { diff --git a/convex-cli/src/test/java/convex/cli/Helper.java b/convex-cli/src/test/java/convex/cli/HelperTest.java similarity index 98% rename from convex-cli/src/test/java/convex/cli/Helper.java rename to convex-cli/src/test/java/convex/cli/HelperTest.java index fe4cd9633..73ae11633 100644 --- a/convex-cli/src/test/java/convex/cli/Helper.java +++ b/convex-cli/src/test/java/convex/cli/HelperTest.java @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test; -public class Helper { +public class HelperTest { @Test public void testSplitArray() { diff --git a/convex-cli/src/test/java/convex/cli/key/KeyExportTest.java b/convex-cli/src/test/java/convex/cli/key/KeyExportTest.java index c905c2554..70ebdd076 100644 --- a/convex-cli/src/test/java/convex/cli/key/KeyExportTest.java +++ b/convex-cli/src/test/java/convex/cli/key/KeyExportTest.java @@ -17,6 +17,7 @@ public class KeyExportTest { private static final char[] KEYSTORE_PASSWORD = "testPassword".toCharArray(); + private static final char[] KEY_PASSWORD = "testKeytPassword".toCharArray(); private static final char[] EXPORT_PASSWORD = "testExportPassword".toCharArray(); private static final File TEMP_FILE; @@ -38,7 +39,8 @@ public void testKeyGenerateAndExport() { // command key.generate CLTester tester = CLTester.run( "key", "generate", - "--store-password", new String(KEYSTORE_PASSWORD), + "--keystore-password", new String(KEYSTORE_PASSWORD), + "--password", new String(KEY_PASSWORD), "--keystore", KEYSTORE_FILENAME ); assertEquals(ExitCodes.SUCCESS,tester.getResult()); @@ -58,9 +60,10 @@ public void testKeyGenerateAndExport() { tester = CLTester.run( "key", "export", - "--store-password", new String(KEYSTORE_PASSWORD), + "--keystore-password", new String(KEYSTORE_PASSWORD), + "--p", new String(KEY_PASSWORD), "--keystore", KEYSTORE_FILENAME, - "--public-key", publicKey, + "--key", publicKey, "--export-password", new String(EXPORT_PASSWORD) ); assertEquals(ExitCodes.SUCCESS,tester.getResult()); @@ -70,9 +73,9 @@ public void testKeyGenerateAndExport() { tester = CLTester.run( "key", "export", - "--store-password", new String(KEYSTORE_PASSWORD), + "--keystore-password", new String(KEYSTORE_PASSWORD), "--keystore", KEYSTORE_FILENAME, - "--public-key", "0x" + publicKey, + "--key", "0x" + publicKey, "--export-password", new String(EXPORT_PASSWORD) ); assertEquals(ExitCodes.SUCCESS,tester.getResult()); diff --git a/convex-cli/src/test/java/convex/cli/key/KeyImportTest.java b/convex-cli/src/test/java/convex/cli/key/KeyImportTest.java index 84d7b5bb3..2b4e0cdf0 100644 --- a/convex-cli/src/test/java/convex/cli/key/KeyImportTest.java +++ b/convex-cli/src/test/java/convex/cli/key/KeyImportTest.java @@ -39,7 +39,7 @@ public void testKeyImport() { "key", "import", "-n", - "--store-password", new String(KEYSTORE_PASSWORD), + "--keystore-password", new String(KEYSTORE_PASSWORD), "--keystore", KEYSTORE_FILENAME, "--import-text", pemText, "--import-password", new String(IMPORT_PASSWORD) @@ -50,7 +50,7 @@ public void testKeyImport() { CLTester t2=CLTester.run( "key" , "list", - "--store-password", new String(KEYSTORE_PASSWORD), + "--keystore-password", new String(KEYSTORE_PASSWORD), "--keystore", KEYSTORE_FILENAME); assertEquals(ExitCodes.SUCCESS,t2.getResult()); diff --git a/convex-cli/src/test/java/convex/cli/key/KeyTest.java b/convex-cli/src/test/java/convex/cli/key/KeyTest.java index f9ac75259..beebd3c47 100644 --- a/convex-cli/src/test/java/convex/cli/key/KeyTest.java +++ b/convex-cli/src/test/java/convex/cli/key/KeyTest.java @@ -26,6 +26,7 @@ public class KeyTest { TEMP_FILE.deleteOnExit(); } private static final String KEYSTORE_PASSWORD = "testPassword"; + private static final String KEY_PASSWORD = "testKeyPassword"; @Test public void testKeyGenerateAndUse() throws IOException { @@ -34,20 +35,21 @@ public void testKeyGenerateAndUse() throws IOException { String fileName =KEYSTORE_FILENAME; // command key.generate - CLTester tester = CLTester.run("key", "generate", "--store-password", KEYSTORE_PASSWORD, "--keystore", fileName); + CLTester tester = CLTester.run("key", "generate", "--p", KEY_PASSWORD, "--keystore-password", KEYSTORE_PASSWORD, "--keystore", fileName); assertEquals(0,tester.getResult()); String key = tester.getOutput().trim(); assertEquals(64,key.length()); + File fp = new File(fileName); assertTrue(fp.exists()); // command key.list - tester = CLTester.run("key", "list", "--store-password", KEYSTORE_PASSWORD, "--keystore", fileName); + tester = CLTester.run("key", "list", "--keystore-password", KEYSTORE_PASSWORD, "--keystore", fileName); //tester.assertOutputMatch("^Index Public Key\\s+1"); - // command key.list with non-existnt keystore - tester = CLTester.run("key", "list", "--store-password", KEYSTORE_PASSWORD, "--keystore","bad-keystore.pfx"); + // command key.list with non-existant keystore + tester = CLTester.run("key", "list", "--keystore-password", KEYSTORE_PASSWORD, "--keystore","bad-keystore.pfx"); assertNotEquals(ExitCodes.SUCCESS,tester.getResult()); }