Skip to content

Commit

Permalink
Merge pull request #494 from aionick/pre-merge_fix_console
Browse files Browse the repository at this point in the history
fix null console bug and account exist checks
  • Loading branch information
AionJayT authored May 22, 2018
2 parents ba88ae9 + 289b3b3 commit e06cdb8
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 18 deletions.
80 changes: 70 additions & 10 deletions modAionImpl/src/org/aion/zero/impl/cli/Cli.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@

package org.aion.zero.impl.cli;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.aion.base.util.Hex;
import org.aion.crypto.ECKey;
import org.aion.crypto.ECKeyFac;
Expand Down Expand Up @@ -196,11 +199,17 @@ private void printHelp() {
/**
* Creates a new account.
*
* @return boolean
* @return true only if the new account was successfully created, otherwise false.
*/
private boolean createAccount() {
String password = readPassword("Please enter a password: ");
String password2 = readPassword("Please re-enter your password: ");
String password = null, password2 = null;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
password = readPassword("Please enter a password: ", reader);
password2 = readPassword("Please re-enter your password: ", reader);
} catch (IOException e) {
e.printStackTrace();
return false;
}

if (!password2.equals(password)) {
System.out.println("Passwords do not match!");
Expand Down Expand Up @@ -244,7 +253,13 @@ private boolean exportPrivateKey(String address) {
return false;
}

String password = readPassword("Please enter your password: ");
String password = null;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
password = readPassword("Please enter your password: ", reader);
} catch (IOException e) {
e.printStackTrace();
return false;
}
ECKey key = Keystore.getKey(address, password);

if (key != null) {
Expand Down Expand Up @@ -273,9 +288,21 @@ private boolean importPrivateKey(String privateKey) {
}

ECKey key = ECKeyFac.inst().fromPrivate(raw);
if (key == null) {
System.out.println("Unable to recover private key."
+ "Are you sure you did not import a public key?");
return false;
}

String password = null, password2 = null;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
password = readPassword("Please enter a password: ", reader);
password2 = readPassword("Please re-enter your password: ", reader);
} catch (IOException e) {
e.printStackTrace();
return false;
}

String password = readPassword("Please enter a password: ");
String password2 = readPassword("Please re-enter your password: ");
if (!password2.equals(password)) {
System.out.println("Passwords do not match!");
return false;
Expand All @@ -292,16 +319,49 @@ private boolean importPrivateKey(String privateKey) {
}

/**
* Reads a password from the console.
* Returns a password after prompting the user to enter it. This method attempts first to read
* user input from a console evironment and if one is not available it instead attempts to read
* from reader.
*
* @param prompt String
* @return boolean
* @throws NullPointerException if prompt is null or if console unavailable and reader is null.
* @param prompt The read-password prompt to display to the user.
* @return The user-entered password.
*/
public String readPassword(String prompt) {
public String readPassword(String prompt, BufferedReader reader) {
if (prompt == null) {
throw new NullPointerException("readPassword given null prompt.");
}

Console console = System.console();
if (console == null) {
return readPasswordFromReader(prompt, reader);
}
return new String(console.readPassword(prompt));
}

/**
* Returns a password after prompting the user to enter it from reader.
*
* @throws NullPointerException if reader is null.
* @param prompt The read-password prompt to display to the user.
* @param reader The BufferedReader to read input from.
* @return The user-entered password.
*/
private String readPasswordFromReader(String prompt, BufferedReader reader) {
if (reader == null) {
throw new NullPointerException("readPasswordFromReader given null reader.");
}
System.out.println(prompt);
try {
return reader.readLine();
} catch (IOException e) {
System.err.println("Error reading from BufferedReader: " + reader);
e.printStackTrace();
System.exit(1);
}
return null; // Make compiler happy; never get here.
}

private RecoveryUtils.Status revertTo(String blockNumber) {
// try to convert to long
long block;
Expand Down
59 changes: 53 additions & 6 deletions modAionImpl/test/org/aion/cli/CliTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.aion.cli;

import org.aion.base.type.Address;
import org.aion.mcf.account.Keystore;
import org.aion.base.util.Hex;
import org.aion.crypto.ECKey;
Expand All @@ -13,36 +12,51 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import org.aion.zero.impl.cli.Cli;;
import org.aion.zero.impl.cli.Cli;

public class CliTest {

private Cli cli;
private static final Cli cli = Mockito.spy(new Cli());

/**
* Sets up a spy Cli class that returns the String "password" when the cli.readPassword()
* is called using any two params.
*/
@Before
public void setup() {
cli = Mockito.spy(new Cli());
doReturn("password").when(cli).readPassword(any());
doReturn("password").when(cli).readPassword(any(), any());
}

/**
* Tests the -h argument does not fail.
*/
@Test
public void testHelp() {
String args[] = {"-h"};
assertEquals(0, cli.call(args, CfgAion.inst()));
}

/**
* Tests the -a create arguments do not fail.
*/
@Test
public void testCreateAccount() {
String args[] = {"-a", "create"};
assertEquals(0, cli.call(args, CfgAion.inst()));
}

/**
* Tests the -a list arguments do not fail.
*/
@Test
public void testListAccounts() {
String args[] = {"-a", "list"};
assertEquals(0, cli.call(args, CfgAion.inst()));
}

/**
* Tests the -a export arguments do not fail on a valid account.
*/
@Test
public void testExportPrivateKey() {
String account = Keystore.create("password");
Expand All @@ -51,6 +65,22 @@ public void testExportPrivateKey() {
assertEquals(0, cli.call(args, CfgAion.inst()));
}

/**
* Tests the -a export arguments fail when the suupplied account is a proper substring of a
* valid account.
*/
@Test
public void testExportSubstringOfAccount() {
String account = Keystore.create("password");
String substrAcc = account.substring(1);

String[] args = {"-a", "export", substrAcc};
assertEquals(1, cli.call(args, CfgAion.inst()));
}

/**
* Tests the -a import arguments do not fail on a fail import key.
*/
@Test
public void testImportPrivateKey() {
ECKey key = ECKeyFac.inst().create();
Expand All @@ -59,6 +89,20 @@ public void testImportPrivateKey() {
assertEquals(0, cli.call(args, CfgAion.inst()));
}

/**
* Tests the -a import arguments fail when a non-private key is supplied.
*/
@Test
public void testImportNonPrivateKey() {
String account = Keystore.create("password");

String[] args = {"-a", "import", account};
assertEquals(1, cli.call(args, CfgAion.inst()));
}

/**
* Tests the -a import arguments do not fail when a valid private key is supplied.
*/
@Test
public void testImportPrivateKey2() {
ECKey key = ECKeyFac.inst().create();
Expand All @@ -75,9 +119,12 @@ public void testImportPrivateKey2() {
System.out.println("Imported private key: " + Hex.toHexString(key2.getPrivKeyBytes()));
}

/**
* Tests the -a import arguments fail given an invalid private key.
*/
@Test
public void testImportPrivateKeyWrong() {
String[] args = {"-a", "import", "hello"};
assertEquals(1, cli.call(args, CfgAion.inst()));
}
}
}
13 changes: 11 additions & 2 deletions modMcf/src/org/aion/mcf/account/Keystore.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.aion.base.type.Address;
import org.aion.base.util.*;
Expand All @@ -65,6 +67,7 @@ public class Keystore {
private static final String KEYSTORE_PATH = System.getProperty("user.dir") + "/keystore";
private static final Path PATH = Paths.get(KEYSTORE_PATH);
private static final FileDateTimeComparator COMPARE = new FileDateTimeComparator();
private static final Pattern HEX_64 = Pattern.compile("^[\\p{XDigit}]{64}$");
private static final String ADDR_PREFIX = "0x";
private static final String AION_PREFIX = "a0";
private static final int IMPORT_LIMIT = 100;
Expand Down Expand Up @@ -231,7 +234,7 @@ public static ECKey getKey(String _address, String _password) {
if (_address.startsWith(AION_PREFIX)) {
List<File> files = getFiles();
for (File file : files) {
if (file.getName().contains(_address)) {
if (HEX_64.matcher(_address).find() && file.getName().contains(_address)) {
try {
byte[] content = Files.readAllBytes(file.toPath());
key = KeystoreFormat.fromKeystore(content, _password);
Expand All @@ -246,6 +249,12 @@ public static ECKey getKey(String _address, String _password) {
return key;
}

/**
* Returns true if the address _address exists, false otherwise.
*
* @param _address the address whose existence is to be tested.
* @return true only if _address exists.
*/
public static boolean exist(String _address) {
if (_address.startsWith(ADDR_PREFIX)) {
_address = _address.substring(2);
Expand All @@ -255,7 +264,7 @@ public static boolean exist(String _address) {
if (_address.startsWith(AION_PREFIX)) {
List<File> files = getFiles();
for (File file : files) {
if (file.getName().contains(_address)) {
if (HEX_64.matcher(_address).find() && file.getName().contains(_address)) {
flag = true;
break;
}
Expand Down

0 comments on commit e06cdb8

Please sign in to comment.