Skip to content

Commit

Permalink
Work towards full genesis testing
Browse files Browse the repository at this point in the history
  • Loading branch information
mikera committed Aug 5, 2024
1 parent d7ddb6f commit b0783cc
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 54 deletions.
7 changes: 5 additions & 2 deletions convex-cli/src/main/java/convex/cli/ACommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,11 @@ public String prompt(String string) {
public char[] readPassword(String prompt) {
Console c = System.console();
if (c == null) {
throw new CLIError(
"Unable to request password because console is unavaiable. Consider passing a password parameter, or running in interactive mode.");
if (verbose()>=3) {
informError("Can't give user prompt: "+prompt);
}
throw new CLIError(ExitCodes.USAGE,
"Unable to request password because console is unavailable. Consider passing a password parameter, or running in interactive mode.");
}

if (isColoured()) prompt = Coloured.blue(prompt);
Expand Down
5 changes: 5 additions & 0 deletions convex-cli/src/main/java/convex/cli/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ public int handleExecutionException(Exception ex, CommandLine commandLine, Parse
err.println("Underlying cause: ");
cause.printStackTrace(err);
}
} else if (ex.getClass().getSimpleName().equals("UserInterruptException")) {
informError("Operation cancelled by user");
if (verbose>=3) {
ex.printStackTrace(err);
}
} else {
if (verbose>=1) {
ex.printStackTrace(err);
Expand Down
8 changes: 4 additions & 4 deletions convex-cli/src/main/java/convex/cli/key/KeyImport.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ public AKeyPair importKeyPair() {
keyPair=AKeyPair.create(hex.toFlatBlob());
} else if ("bip39".equals(type)) {
if (hex==null) {
try {
hex=BIP39.getSeed(importText, importPassphrase);
} catch (Exception e) {
throw new CLIError("Error interpreting BIP39 seed",e);
// We attempt to interpret as BIP39 mnemonic
if (importPassphrase==null) {
importPassphrase=new String(readPassword("Enter passphrase for imported BIP39 memonic: "));
}
hex=BIP39.getSeed(importText, importPassphrase);
}
keyPair=BIP39.seedToKeyPair(hex.toFlatBlob());
} else if ("pem".equals(type)) {
Expand Down
3 changes: 2 additions & 1 deletion convex-cli/src/main/java/convex/cli/mixins/KeyMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ public char[] getKeyPassword() {

this.keyPassword=new String(keypass);
}

if (keypass.length == 0) {
paranoia("Cannot use an empty private key password");
paranoia("Cannot use an empty private key password in --strict-security mode");
}
return keypass;
}
Expand Down
57 changes: 57 additions & 0 deletions convex-cli/src/main/java/convex/cli/mixins/PeerKeyMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package convex.cli.mixins;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import picocli.CommandLine.Option;
import picocli.CommandLine.ScopeType;

public class PeerKeyMixin extends AMixin {

@Option(names = { "--peer-key" },
defaultValue = "${env:CONVEX_PEER_KEY}",
scope = ScopeType.INHERIT,
description = "Peer Key pair. Specifiy with a hex prefix of a public key. "+
"Can ALSO specify with CONVEX_PEER_KEY environment variable.")
protected String publicKey;

@Option(names = { "--peer-keypass" },
defaultValue = "${env:CONVEX_PEER_KEY_PASSWORD}",
scope = ScopeType.INHERIT,
description = "Peer Key pair password in key store. Can also specify with CONVEX_PEER_KEY_PASSWORD environment variable.")
protected String keyPassword;

public String getPublicKey() {
return publicKey;
}

static Logger log = LoggerFactory.getLogger(PeerKeyMixin.class);

/**
* Keys the password for the current key
*
* @return password
*/
public char[] getKeyPassword() {
char[] keypass = null;

if (this.keyPassword != null) {
keypass = this.keyPassword.toCharArray();
} else {
if (isInteractive()) {
keypass = readPassword("Private Key Encryption Password: ");
}

if (keypass == null) {
log.warn("No password for key: defaulting to blank password");
keypass = new char[0];
}

this.keyPassword=new String(keypass);
}
if (keypass.length == 0) {
paranoia("Cannot use an empty private key password");
}
return keypass;
}
}
41 changes: 41 additions & 0 deletions convex-cli/src/main/java/convex/cli/peer/APeerCommand.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package convex.cli.peer;

import convex.cli.ACommand;
import convex.cli.CLIError;
import convex.cli.ExitCodes;
import convex.cli.Main;
import convex.cli.mixins.EtchMixin;
import convex.cli.mixins.KeyMixin;
import convex.cli.mixins.PeerKeyMixin;
import convex.cli.mixins.StoreMixin;
import convex.core.crypto.AKeyPair;
import etch.EtchStore;
import picocli.CommandLine.Mixin;
import picocli.CommandLine.ParentCommand;
Expand All @@ -17,6 +21,9 @@ public abstract class APeerCommand extends ACommand {
@Mixin
protected KeyMixin keyMixin;

@Mixin
protected PeerKeyMixin peerKeyMixin;

@Mixin
protected StoreMixin storeMixin;

Expand All @@ -32,4 +39,38 @@ public Main cli() {
public EtchStore getEtchStore() {
return etchMixin.getEtchStore();
}

/**
* Get the keypair for the peer
*/
protected AKeyPair ensurePeerKey() {
String peerPublicKey=peerKeyMixin.getPublicKey();
if (peerPublicKey==null) {
if (!isInteractive()) {
throw new CLIError(ExitCodes.USAGE,"--peer-key must be specified in non-interactive mode");
}
}

char[] keyPass=peerKeyMixin.getKeyPassword();

AKeyPair result=storeMixin.loadKeyFromStore(peerPublicKey, keyPass);
return result;
}

/**
* Get the keypair for the peer controller account
*/
protected AKeyPair ensureControllerKey() {
String peerPublicKey=keyMixin.getPublicKey();
if (peerPublicKey==null) {
if (!isInteractive()) {
throw new CLIError(ExitCodes.USAGE,"Controller --key must be specified in non-interactive mode");
}
}

char[] keyPass=keyMixin.getKeyPassword();

AKeyPair result=storeMixin.loadKeyFromStore(peerPublicKey, keyPass);
return result;
}
}
7 changes: 0 additions & 7 deletions convex-cli/src/main/java/convex/cli/peer/PeerCreate.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,6 @@ public class PeerCreate extends APeerCommand {

@Spec CommandSpec spec;

@Option(names={"--peer"},
defaultValue="",
description="Hex string of the public key in the Keystore to use for the peer.%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;

@Mixin
RemotePeerMixin peerMixin;

Expand Down
32 changes: 6 additions & 26 deletions convex-cli/src/main/java/convex/cli/peer/PeerGenesis.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,42 +34,22 @@ public class PeerGenesis extends APeerCommand {
@Spec
CommandSpec spec;

@Option(names = {"--public-key" },
description = "Hex string of the public key in the Keystore to use for the genesis peer.%n"
+ "You only need to enter in the first distinct hex values of the public key.%n"
+ "For example: 0xf0234 or f0234")
private String genesisKey;

@Override
public void run() {

AKeyPair keyPair = null;
storeMixin.loadKeyStore();
if (genesisKey!=null) {
keyPair = storeMixin.loadKeyFromStore(genesisKey,keyMixin.getKeyPassword());
if (keyPair == null) {
throw new CLIError(ExitCodes.CONFIG,"Cannot find specified key pair to perform peer start: "+genesisKey);
}
} else {
// if (cli().prompt("No key pair specified. Continue by creating a new one? (Y/N)")) {
// throw new CLIError("Unable to obtain genesis key pair, aborting");
// }
if (isParanoid()) throw new CLIError("Aborting due to strict security: no key pair specified");
keyPair=AKeyPair.generate();
storeMixin.addKeyPairToStore(keyPair,keyMixin.getKeyPassword());
storeMixin.saveKeyStore();
inform("Generated new Keypair with public key: "+keyPair.getAccountKey());
}

AKeyPair peerKey = ensurePeerKey();
AKeyPair genesisKey=ensureControllerKey();

EtchStore store=getEtchStore();

State genesisState=Init.createState(List.of(keyPair.getAccountKey()));
inform("Created genersis state with hash: "+genesisState.getHash());
State genesisState=Init.createState(List.of(peerKey.getAccountKey()));
inform("Created genesis state with hash: "+genesisState.getHash());

HashMap<Keyword,Object> config=new HashMap<>();
config.put(Keywords.STORE, store);
config.put(Keywords.STATE, genesisState);
config.put(Keywords.KEYPAIR, keyPair);
config.put(Keywords.KEYPAIR, peerKey);
Server s=API.launchPeer(config);
s.close();
informSuccess("Convex genesis succeeded!");
Expand Down
16 changes: 9 additions & 7 deletions convex-cli/src/main/java/convex/cli/peer/PeerStart.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import convex.cli.CLIError;
import convex.cli.mixins.RemotePeerMixin;
import convex.core.crypto.AKeyPair;
import convex.core.exceptions.TODOException;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
Expand Down Expand Up @@ -34,12 +35,6 @@ public class PeerStart extends APeerCommand {
@Spec
CommandSpec spec;

@Option(names = {
"--peer" }, description = "Public key to use for the peer. Can specify a key in the cuurent store. "
+ "You only need to enter in the first distinct hex values of the public key.%n"
+ "For example: 0xf0234 or f0234")
private String peerPublicKey;

@Option(names = {"--reset" },
description = "Reset and delete the etch database if it exists. Default: ${DEFAULT-VALUE}")
private boolean isReset;
Expand All @@ -62,10 +57,15 @@ public class PeerStart extends APeerCommand {

@Option(names = { "-a", "--address" }, description = "Account address to use for the peer controller.")
private long addressNumber;



@Override
public void run() {
log.debug("Preparing to start peer: "+peerPublicKey);
storeMixin.loadKeyStore();
AKeyPair peerKey=ensurePeerKey();

log.debug("Preparing to start peer: "+peerKey.getAccountKey());

try {

Expand All @@ -77,4 +77,6 @@ public void run() {
throw new CLIError("Error starting peer: "+t.getMessage(),t);
}
}


}
35 changes: 28 additions & 7 deletions convex-cli/src/test/java/convex/cli/peer/GenesisTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package convex.cli.peer;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.File;

import org.junit.jupiter.api.Test;
Expand All @@ -16,6 +18,12 @@ public class GenesisTest {

private static final File TEMP_FILE;
private static final String KEYSTORE_FILENAME;

private static final String bip39="miracle source lizard gun neutral year dust recycle drama nephew infant enforce";
private static final String bipPassphrase="thisIsNotSecure";
private static final String expectedKey="09a5528c53579e1ee76a327ab8bc9db7b2853dd17391a6e3fe7f3052c6e8686a";


static {
try {
TEMP_FILE=Helpers.createTempFile("tempGenesisKeystore", ".pfx");
Expand All @@ -28,12 +36,25 @@ public class GenesisTest {
}

@Test public void testGenesisPeer() {
CLTester tester = CLTester.run(
"peer", "genesis",
"--storepass", new String(KEYSTORE_PASSWORD),
"--keypass", new String(KEY_PASSWORD),
"--keystore", KEYSTORE_FILENAME
);
tester.assertExitCode(ExitCodes.SUCCESS);
CLTester importTester = CLTester.run(
"key",
"import",
"--type","bip39",
"--storepass", new String(KEYSTORE_PASSWORD),
"--keystore", KEYSTORE_FILENAME,
"--keypass",new String(KEY_PASSWORD),
"--text", bip39,
"--passphrase", bipPassphrase
);
importTester.assertExitCode(ExitCodes.SUCCESS);
assertEquals(expectedKey,importTester.getOutput().trim());

// CLTester tester = CLTester.run(
// "peer", "genesis",
// "--storepass", new String(KEYSTORE_PASSWORD),
// "--keypass", new String(KEY_PASSWORD),
// "--keystore", KEYSTORE_FILENAME
// );
// tester.assertExitCode(ExitCodes.SUCCESS);
}
}

0 comments on commit b0783cc

Please sign in to comment.