diff --git a/modAionImpl/build.gradle b/modAionImpl/build.gradle index 51dbdb021f..a7afcc1d62 100644 --- a/modAionImpl/build.gradle +++ b/modAionImpl/build.gradle @@ -18,6 +18,7 @@ dependencies { compile project(':aion_api') compile project(':aion_fastvm/modFastVM') compile files('../lib/libJson.jar') + compile 'info.picocli:picocli:3.6.1' testCompile project(path: ':modDbImpl', configuration: 'testClassesOut') testCompile 'junit:junit:4.12' diff --git a/modAionImpl/build.xml b/modAionImpl/build.xml index 2c32d7274e..1867a6d295 100644 --- a/modAionImpl/build.xml +++ b/modAionImpl/build.xml @@ -32,6 +32,7 @@ + @@ -95,7 +96,7 @@ - + @@ -124,7 +125,7 @@ - + @@ -162,7 +163,7 @@ - + @@ -171,6 +172,7 @@ + diff --git a/modAionImpl/src/org/aion/zero/impl/cli/Arguments.java b/modAionImpl/src/org/aion/zero/impl/cli/Arguments.java new file mode 100644 index 0000000000..c801fc0263 --- /dev/null +++ b/modAionImpl/src/org/aion/zero/impl/cli/Arguments.java @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2017-2018 Aion foundation. + * + * This file is part of the aion network project. + * + * The aion network project is free software: you can redistribute it + * and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or any later version. + * + * The aion network project is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the aion network project source files. + * If not, see . + * + * Contributors: + * Aion foundation. + */ +package org.aion.zero.impl.cli; + +import java.util.ArrayList; +import java.util.List; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +/** + * Command line arguments for the Aion kernel. + * + * @author Alexandra Roatis + */ +@Command(name = "./aion.sh", separator = " ", sortOptions = false, abbreviateSynopsis = true) +public class Arguments { + + // usage information + @Option( + names = {"-h", "--help"}, + usageHelp = true, + description = "display help information") + private boolean help = false; + + // account management + @Option( + names = {"ac", "-a create", "--account create"}, + description = "create a new account") + private boolean createAccount = false; + + @Option( + names = {"al", "-a list", "--account list"}, + description = "list all existing accounts") + private boolean listAccounts = false; + + @Option( + names = {"ae", "-a export", "--account export"}, + paramLabel = "", + description = "export private key of an account") + private String exportAccount = null; + + @Option( + names = {"ai", "-a import", "--account import"}, + paramLabel = "", + description = "import private key") + private String importAccount = null; + + // config generation + @Option( + names = {"-c", "--config"}, + arity = "0..1", + paramLabel = "", + description = + "create config for the selected network\noptions: mainnet, conquest, mastery") + private String config = null; + + // get information and version + @Option( + names = {"-i", "--info"}, + description = "display information") + private boolean info = false; + + @Option( + names = {"-v"}, + description = "display version") + private boolean version = false; + + @Option( + names = {"--version"}, + description = "display version tag") + private boolean versionTag = false; + + // create ssl certificate + @Option( + names = {"sc", "-s create"}, + arity = "0..2", + paramLabel = " ", + description = + "create a ssl certificate for:\n - localhost (when no parameters are given), or" + + "\n - the given hostname and ip") + private String[] ssl = null; + + // offline block management + @Option( + names = {"pb", "--prune-blocks"}, + description = "remove blocks on side chains and update block info") + private boolean rebuildBlockInfo = false; + + @Option( + names = {"-r", "--revert"}, + arity = "1", + paramLabel = "", + description = "revert database state to given block number") + private String revertToBlock = null; + + // network and directory setup + @Option( + names = {"-n", "--network"}, + description = + "execute kernel with selected network\noptions: mainnet, conquest, mastery") + private String network = null; + + @Option( + names = {"-d", "--datadir"}, + description = "execute kernel with selected database directory") + private String directory = null; + + // offline database query and update + @Option( + names = {"ps", "--state"}, + paramLabel = "", + description = "reorganize the state storage\noptions: FULL, TOP, SPREAD") + private String pruntStateOption = null; + + // print info from db + @Option( + names = {"--dump-blocks"}, + arity = "0..1", + paramLabel = "", + description = "print top blocks from database") + private String dumpBlocksCount = null; + + @Option( + names = {"--dump-state-size"}, + arity = "0..1", + paramLabel = "", + description = "retrieves the state size (node count) for the top blocks") + private String dumpStateSizeCount = null; + + @Option( + names = {"--dump-state"}, + arity = "0..1", + paramLabel = "", + description = "retrieves the state for the top main chain blocks") + private String dumpStateCount = null; + + @Option( + names = {"--db-compact"}, + description = "if using leveldb, it triggers its database compaction processes") + private boolean dbCompact; + + /** Compacts the account options into specific commands. */ + public static String[] preProcess(String[] arguments) { + List list = new ArrayList<>(); + + int i = 0; + while (i < arguments.length) { + if (arguments[i].equals("-a") + || arguments[i].equals("--account") + || arguments[i].equals("-s")) { + if (i + 1 < arguments.length) { + list.add(arguments[i] + " " + arguments[i + 1]); + } else { + list.add(arguments[i]); + } + i++; + } else { + list.add(arguments[i]); + } + i++; + } + + return list.toArray(new String[list.size()]); + } + + public boolean isHelp() { + return help; + } + + public boolean isCreateAccount() { + return createAccount; + } + + public boolean isListAccounts() { + return listAccounts; + } + + public String getExportAccount() { + return exportAccount; + } + + public String getImportAccount() { + return importAccount; + } + + public String getConfig() { + return config; + } + + public boolean isInfo() { + return info; + } + + public boolean isVersion() { + return version; + } + + public boolean isVersionTag() { + return versionTag; + } + + public String[] getSsl() { + return ssl; + } + + public boolean isRebuildBlockInfo() { + return rebuildBlockInfo; + } + + public String getRevertToBlock() { + return revertToBlock; + } + + public String getNetwork() { + return network; + } + + public String getDirectory() { + return directory; + } + + public String getPruneStateOption() { + return pruntStateOption; + } + + public String getDumpBlocksCount() { + return dumpBlocksCount; + } + + public String getDumpStateSizeCount() { + return dumpStateSizeCount; + } + + public String getDumpStateCount() { + return dumpStateCount; + } + + public boolean isDbCompact() { + return dbCompact; + } +} diff --git a/modAionImpl/src/org/aion/zero/impl/cli/Cli.java b/modAionImpl/src/org/aion/zero/impl/cli/Cli.java index e9bc87d33f..cb0299723a 100644 --- a/modAionImpl/src/org/aion/zero/impl/cli/Cli.java +++ b/modAionImpl/src/org/aion/zero/impl/cli/Cli.java @@ -20,19 +20,24 @@ * Contributors: * Aion foundation. */ - - package org.aion.zero.impl.cli; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.*; +import static org.aion.zero.impl.cli.Cli.ReturnType.ERROR; +import static org.aion.zero.impl.cli.Cli.ReturnType.EXIT; +import static org.aion.zero.impl.cli.Cli.ReturnType.RUN; +import static org.aion.zero.impl.config.Network.determineNetwork; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.io.Files; +import java.io.BufferedReader; +import java.io.Console; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.aion.base.util.Hex; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; @@ -40,10 +45,10 @@ import org.aion.mcf.config.Cfg; import org.aion.mcf.config.CfgSsl; import org.aion.zero.impl.Version; -import org.aion.zero.impl.config.CfgAion; +import org.aion.zero.impl.config.Network; import org.aion.zero.impl.db.RecoveryUtils; - -import java.io.Console; +import org.apache.commons.lang3.ArrayUtils; +import picocli.CommandLine; /** * Command line interface. @@ -52,497 +57,520 @@ */ public class Cli { + // TODO-Ale: consider using initial path from cfg private final String BASE_PATH = System.getProperty("user.dir"); - private String BASE_PATH_WITH_NETWORK = BASE_PATH + "/config/" + CfgAion.getNetwork(); + private final File keystoreDir = + new File(System.getProperty("user.dir") + File.separator + CfgSsl.SSL_KEYSTORE_DIR); - private String dstConfig = BASE_PATH_WITH_NETWORK + "/config.xml"; + private final Arguments options = new Arguments(); + private final CommandLine parser = new CommandLine(options); - private String dstGenesis = BASE_PATH_WITH_NETWORK + "/genesis.json"; + public enum ReturnType { + RUN(2), + EXIT(0), + ERROR(1); + private final int value; - File keystoreDir = new File(System.getProperty("user.dir") + File.separator + CfgSsl.SSL_KEYSTORE_DIR); - - enum Network { - MAINNET, CONQUEST; + ReturnType(int _value) { + this.value = _value; + } - @Override - public String toString() { - switch(this) { - case MAINNET: return "mainnet"; - case CONQUEST: return "conquest"; - default: throw new IllegalArgumentException(); - } + public int getValue() { + return value; } } - private Network net = Network.MAINNET; + @SuppressWarnings("ResultOfMethodCallIgnored") + public ReturnType call(final String[] args, Cfg cfg) { + try { + // the pre-process method handles arguments that are separated by space + // parsing populates the options object + parser.parse(Arguments.preProcess(args)); + } catch (Exception e) { + System.out.println("Unable to parse the input arguments due to: "); + if (e.getMessage() != null) { + System.out.println(e.getMessage()); + } else { + e.printStackTrace(); + } - public int call(final String[] args, Cfg cfg) { - return call(args, cfg, BASE_PATH); - } + System.out.println(); + printHelp(); + return ERROR; + } - public int call(final String[] args, Cfg cfg, String path) { try { - cfg.fromXML(); - switch (args[0].toLowerCase()) { - case "-h": - printHelp(); - break; - case "-a": - - int index = 0; - boolean multi = false; - - if (args.length < 2) { - printHelp(); - return 1; - } else { - while (index < args.length) { - if(args[index].equals("-d")||args[index].equals("-n")||args[index].equals("--datadir")||args[index].equals("--network")) { - multi = true; - break; - } - index++; - } - } - - // Switches datadir && network - if(multi) { - String[] newArgs = Arrays.copyOfRange(args, index, args.length); - call(newArgs, cfg); - } + // 1. the first set of options don't mix with -d and -n + if (options.isHelp()) { + printHelp(); + return EXIT; + } - switch (args[1]) { - case "create": - if (!createAccount()) { - return 1; - } - break; - case "list": - if (!listAccounts()) { - return 1; - } - break; - case "export": - if (args.length < 3 || !exportPrivateKey(args[2])) { - return 1; - } - break; - case "import": - if (args.length < 3 || !importPrivateKey(args[2])) { - return 1; - } - break; - default: - printHelp(); - return 1; - } - break; - case "-c": - if (args.length == 2 && isValid(args[1])) { - - net = determineNetwork(args[1].toLowerCase()); - - switch (net) { - case MAINNET: - case CONQUEST: - CfgAion.setNetwork(net.toString()); - File dir = new File(BASE_PATH + "/config/" + net); - if(!dir.exists()) { - dir.mkdirs(); - } - CfgAion.setConfFilePath(BASE_PATH + "/config/" + args[1] + "/config.xml"); - System.out.println("\nNew config generated for " + args[1]); - break; - default: - System.out.println("\nInvalid network selected!"); - System.out.println("--- Available Networks ---"); - System.out.println(" mainnet, conquest"); - System.out.println("--------------------------"); - return 1; - } - } else if (args.length == 1) { - System.out.println("\nInvalid network selected!"); - System.out.println("--- Available Networks ---"); - System.out.println(" mainnet, conquest"); - System.out.println("--------------------------"); - return 1; - } - cfg.fromXML(); - cfg.setId(UUID.randomUUID().toString()); - cfg.toXML(null); - break; - case "-i": - cfg.fromXML(); - System.out.println("\nInformation"); + if (options.isVersion() || options.isVersionTag()) { + if (options.isVersion()) { + System.out.println("\nVersion"); System.out.println("--------------------------------------------"); - System.out.println( - "current: p2p://" + cfg.getId() + "@" + cfg.getNet().getP2p().getIp() + ":" - + cfg.getNet().getP2p().getPort()); - String[] nodes = cfg.getNet().getNodes(); - if (nodes != null && nodes.length > 0) { - System.out.println("boot nodes list:"); - for (String node : nodes) { - System.out.println(" " + node); - } - } else { - System.out.println("boot nodes list: 0"); - } - System.out.println( - "p2p: " + cfg.getNet().getP2p().getIp() + ":" + cfg.getNet().getP2p() - .getPort()); - break; - case "-s": - if ((args.length == 2 || args.length == 4) && (args[1].equals("create"))) { - createKeystoreDirIfMissing(); - Console console = System.console(); - checkConsoleExists(console); - - List scriptArgs = new ArrayList<>(); - scriptArgs.add("/bin/bash"); - scriptArgs.add("script/generateSslCert.sh"); - scriptArgs.add(getCertName(console)); - scriptArgs.add(getCertPass(console)); - // add the hostname and ip optionally passed in as cli args - scriptArgs.addAll(Arrays.asList(Arrays.copyOfRange(args, 2, args.length))); - new ProcessBuilder(scriptArgs).inheritIO().start().waitFor(); - } else { - System.out.println("Incorrect usage of -s create command.\n" + - "Command must enter both hostname AND ip or else neither one."); - return 1; - } - break; - case "-r": - if (args.length < 2) { - System.out.println("Starting database clean-up."); - RecoveryUtils.pruneAndCorrect(); - System.out.println("Finished database clean-up."); - } else { - switch (revertTo(args[1])) { - case SUCCESS: - System.out.println( - "Blockchain successfully reverted to block number " + args[1] - + "."); - break; - case FAILURE: - System.out - .println("Unable to revert to block number " + args[1] + "."); - return 1; - case ILLEGAL_ARGUMENT: - default: - return 1; - } - } - break; - - case "-n": - case "--network": - if ( (args.length == 2 || args.length == 4) && isValid(args[1])) { - - net = determineNetwork(args[1].toLowerCase()); - - switch (net) { - case MAINNET: - case CONQUEST: - - // -n [network] - if (args.length == 2) { - - CfgAion.setNetwork(net.toString()); - BASE_PATH_WITH_NETWORK = BASE_PATH + "/config/" + CfgAion.getNetwork(); - CfgAion.setConfFilePath(BASE_PATH_WITH_NETWORK + "/config.xml"); - CfgAion.setGenesisFilePath((BASE_PATH_WITH_NETWORK + "/genesis.json")); - - copyNetwork(path, net); - cfg.getLog().setLogPath(net.toString() + "/log"); - cfg.getDb().setDatabasePath(net.toString() + "/database"); - Keystore.setKeystorePath(path + "/" + net.toString() + "/keystore"); - return 2; - - } - - // -n [network] -d [directory] - else if ((args[2].equals("-d")||args[2].equals("--datadir")) && isValid(args[3])) { - - CfgAion.setNetwork(net.toString()); - BASE_PATH_WITH_NETWORK = BASE_PATH + "/config/" + CfgAion.getNetwork(); - CfgAion.setConfFilePath(BASE_PATH_WITH_NETWORK + "/config.xml"); - CfgAion.setGenesisFilePath((BASE_PATH_WITH_NETWORK + "/genesis.json")); - - String[] newArgs = Arrays.copyOfRange(args, 2, args.length); - call(newArgs, cfg); - return 2; - - } else if (!(args[2].equals("-d")||args[2].equals("--datadir"))) { - System.out.println("\nInvalid multi arguments!\n"); - printHelp(); - return 1; - - } else { - System.out.println("\nInvalid datadir selected!"); - System.out.println("Please choose valid directory name!\n"); - return 1; - } - - default: - System.out.println("\nInvalid network selected!\n"); - System.out.println("--- Available Networks ---"); - System.out.println(" mainnet, conquest"); - System.out.println("--------------------------\n"); - return 1; - } + } + System.out.println(Version.KERNEL_VERSION); + return EXIT; + } - } else { - System.out.println("\nInvalid network selected!"); - System.out.println("--- Available Networks ---"); - System.out.println(" mainnet , conquest"); - System.out.println("--------------------------\n"); - return 1; - } + // 2. determine the network configuration - // Determines database folder path - case "-d": - case "--datadir": - if ( (args.length == 2 || args.length == 4) && isValid(args[1])) { + if (options.getNetwork() != null + || (options.getConfig() != null && !options.getConfig().isEmpty())) { + String strNet = options.getNetwork(); + // the network given in config overwrites the -n option + if (options.getConfig() != null && !options.getConfig().isEmpty()) { + strNet = options.getConfig(); + } + setNetwork(strNet, cfg); + // no return -> allow for other parameters combined with -n + } - // -d [directory] - if (args.length == 2) { + // 3. determine the execution folder path; influenced by --network - copyNetwork(path + "/" + args[1], net); - cfg.getLog().setLogPath(args[1] + "/" + net + "/log"); - cfg.getDb().setDatabasePath(args[1] + "/" + net + "/database"); - Keystore.setKeystorePath(path + "/" + args[1] + "/" + net + "/keystore"); - return 2; + if (options.getDirectory() != null) { + if (!setDirectory(options.getDirectory(), cfg)) { + return ERROR; + } + // no return -> allow for other parameters combined with -d + } - } + // reading from correct config file + File configFile = cfg.getExecConfigFile(); + if (!configFile.exists()) { + configFile = cfg.getInitialConfigFile(); + } else { + // marks that the files were read from the execution path + cfg.setReadConfigFiles(configFile, cfg.getExecGenesisFile()); + } - // -d [directory] -n [network] - else if (isValid(args[3])) { - - String[] newArgs = Arrays.copyOfRange(args, 2, args.length); - call(newArgs, cfg); - - copyNetwork(path + "/" + args[1], net); - cfg.getLog().setLogPath(args[1] + "/" + net + "/log"); - cfg.getDb().setDatabasePath(args[1] + "/" + net + "/database"); - Keystore.setKeystorePath(path + "/" + args[1] + "/" + net + "/keystore"); - return 2; - - } else if (!(args[2].equals("-n")||args[2].equals("--network"))) { - System.out.println("\nInvalid multi arguments!\n"); - printHelp(); - return 1; - - } else { - System.out.println("\nInvalid network selected!"); - System.out.println("--- Available Networks ---"); - System.out.println(" mainnet , conquest"); - System.out.println("--------------------------\n"); - return 1; - } + // true means the UUID must be set + boolean overwrite = cfg.fromXML(configFile); - } else { - System.out.println("\nInvalid datadir selected!"); - System.out.println("Please choose valid directory name!\n"); - return 1; - } + // 4. can be influenced by the -d argument above - case "--state": { - String pruning_type = "full"; - if (args.length >= 2) { - pruning_type = args[1]; + if (options.getConfig() != null) { + // network was already set above + + // if the directory was set we generate a new file + if (options.getDirectory() != null) { + configFile = cfg.getExecConfigFile(); + + // ensure path exists + File dir = cfg.getExecConfigDirectory(); + if (!dir.exists()) { + if (!dir.mkdirs()) { + System.out.println( + "ERROR: Unable to create directory: " + + getRelativePath(dir.getAbsolutePath())); + return ERROR; + } } try { - RecoveryUtils.pruneOrRecoverState(pruning_type); - } catch (Throwable t) { - System.out.println("Reorganizing the state storage FAILED due to:"); - t.printStackTrace(); - return 1; + configFile.createNewFile(); + } catch (IOException e) { + System.out.println( + "ERROR: Unable to create file: " + + getRelativePath(configFile.getAbsolutePath())); + return ERROR; } - break; } - case "--dump-state-size": - long block_count = 2L; - if (args.length < 2) { - System.out - .println("Retrieving state size for top " + block_count + " blocks."); - RecoveryUtils.printStateTrieSize(block_count); - } else { - try { - block_count = Long.parseLong(args[1]); - } catch (NumberFormatException e) { - System.out.println("The given argument <" + args[1] - + "> cannot be converted to a number."); + // save to disk + cfg.toXML(null, configFile); + + System.out.println( + "\nNew config generated at: " + + getRelativePath(configFile.getAbsolutePath())); + return ReturnType.EXIT; + } + + // 5. options that can be influenced by the -d and -n arguments + + if (options.isInfo()) { + System.out.println( + "Reading config file from: " + + getRelativePath(configFile.getAbsolutePath())); + if (overwrite) { + // updating the file in case the user id was not set + cfg.toXML(new String[] {"--id=" + cfg.getId()}, configFile); + } + printInfo(cfg); + return ReturnType.EXIT; + } + + // make directories for kernel execution + makeDirs(configFile, cfg); + + if (overwrite) { + // only updating the file in case the user id was not set + cfg.toXML(new String[] {"--id=" + cfg.getId()}, cfg.getExecConfigFile()); + } + + // set correct keystore directory + Keystore.setKeystorePath(cfg.getKeystoreDir().getAbsolutePath()); + + if (options.isCreateAccount()) { + if (!createAccount()) { + return ERROR; + } else { + return EXIT; + } + } + + if (options.isListAccounts()) { + if (!listAccounts()) { + return ERROR; + } else { + return EXIT; + } + } + + if (options.getExportAccount() != null) { + if (!exportPrivateKey(options.getExportAccount())) { + return ERROR; + } else { + return EXIT; + } + } + + if (options.getImportAccount() != null) { + if (!importPrivateKey(options.getImportAccount())) { + return ERROR; + } else { + return EXIT; + } + } + + if (options.getSsl() != null) { + String[] parameters = options.getSsl(); + + if (parameters.length == 0 || parameters.length == 2) { + createKeystoreDirIfMissing(); + Console console = System.console(); + checkConsoleExists(console); + + List scriptArgs = new ArrayList<>(); + scriptArgs.add("/bin/bash"); + scriptArgs.add("script/generateSslCert.sh"); + scriptArgs.add(getCertName(console)); + scriptArgs.add(getCertPass(console)); + // add the hostname and ip optionally passed in as cli args + scriptArgs.addAll(Arrays.asList(parameters)); + new ProcessBuilder(scriptArgs).inheritIO().start().waitFor(); + return EXIT; + } else { + System.out.println( + "Incorrect usage of -s create command.\n" + + "Command must enter both hostname AND ip or else neither one."); + return ERROR; + } + } + + if (options.isRebuildBlockInfo()) { + System.out.println("Starting database clean-up."); + RecoveryUtils.pruneAndCorrect(); + System.out.println("Finished database clean-up."); + return EXIT; + } + + if (options.getRevertToBlock() != null) { + String block = options.getRevertToBlock(); + switch (revertTo(block)) { + case SUCCESS: + { + System.out.println( + "Blockchain successfully reverted to block number " + + block + + "."); + return EXIT; + } + case FAILURE: + { + System.out.println("Unable to revert to block number " + block + "."); + return ERROR; } - if (block_count < 1) { - System.out - .println("The given argument <" + args[1] + "> is not valid."); - block_count = 2L; + case ILLEGAL_ARGUMENT: + default: + { + return ERROR; } + } + } + + if (options.getPruneStateOption() != null) { + String pruning_type = options.getPruneStateOption(); + try { + RecoveryUtils.pruneOrRecoverState(pruning_type); + return EXIT; + } catch (Throwable t) { + System.out.println("Reorganizing the state storage FAILED due to:"); + t.printStackTrace(); + return ERROR; + } + } + + if (options.getDumpStateSizeCount() != null) { + long block_count = 2L; + String parameter = options.getDumpStateSizeCount(); - System.out - .println("Retrieving state size for top " + block_count + " blocks."); - RecoveryUtils.printStateTrieSize(block_count); + if (parameter.isEmpty()) { + System.out.println("Retrieving state size for top " + block_count + " blocks."); + RecoveryUtils.printStateTrieSize(block_count); + return EXIT; + } else { + try { + block_count = Long.parseLong(parameter); + } catch (NumberFormatException e) { + System.out.println( + "The given argument «" + + parameter + + "» cannot be converted to a number."); + return ERROR; + } + if (block_count < 1) { + System.out.println("The given argument «" + parameter + "» is not valid."); + block_count = 2L; } - break; - case "--dump-state": - long level = -1L; - if (args.length < 2) { + System.out.println("Retrieving state size for top " + block_count + " blocks."); + RecoveryUtils.printStateTrieSize(block_count); + return EXIT; + } + } + + if (options.getDumpStateCount() != null) { + long level = -1L; + String parameter = options.getDumpStateCount(); + + if (parameter.isEmpty()) { + System.out.println("Retrieving state for top main chain block..."); + RecoveryUtils.printStateTrieDump(level); + return EXIT; + } else { + try { + level = Long.parseLong(parameter); + } catch (NumberFormatException e) { + System.out.println( + "The given argument «" + + parameter + + "» cannot be converted to a number."); + return ERROR; + } + if (level == -1L) { System.out.println("Retrieving state for top main chain block..."); - RecoveryUtils.printStateTrieDump(level); } else { - try { - level = Long.parseLong(args[1]); - } catch (NumberFormatException e) { - System.out.println("The given argument <" + args[1] - + "> cannot be converted to a number."); - } - if (level == -1L) { - System.out.println("Retrieving state for top main chain block..."); - } else { - System.out.println( + System.out.println( "Retrieving state for main chain block at level " + level + "..."); - } - RecoveryUtils.printStateTrieDump(level); } - break; - case "--db-compact": - RecoveryUtils.dbCompact(); - break; - case "--dump-blocks": - long count = 10L; - - if (args.length < 2) { - System.out.println("Printing top " + count + " blocks from database."); - RecoveryUtils.dumpBlocks(count); - } else { - try { - count = Long.parseLong(args[1]); - } catch (NumberFormatException e) { - System.out.println("The given argument <" + args[1] - + "> cannot be converted to a number."); - } - if (count < 1) { - System.out - .println("The given argument <" + args[1] + "> is not valid."); - count = 10L; - } + RecoveryUtils.printStateTrieDump(level); + return EXIT; + } + } - System.out.println("Printing top " + count + " blocks from database."); - RecoveryUtils.dumpBlocks(count); + if (options.getDumpBlocksCount() != null) { + long count = 10L; + String parameter = options.getDumpBlocksCount(); + + if (parameter.isEmpty()) { + System.out.println("Printing top " + count + " blocks from database."); + RecoveryUtils.dumpBlocks(count); + return EXIT; + } else { + try { + count = Long.parseLong(parameter); + } catch (NumberFormatException e) { + System.out.println( + "The given argument «" + + parameter + + "» cannot be converted to a number."); + return ERROR; } - break; - case "-v": - System.out.println("\nVersion"); - System.out.println("--------------------------------------------"); - // Don't put break here!! - case "--version": - System.out.println(Version.KERNEL_VERSION); - break; - default: - System.out.println("Unable to parse the input arguments"); - printHelp(); - return 1; + if (count < 1) { + System.out.println("The given argument «" + parameter + "» is not valid."); + count = 10L; + } + + System.out.println("Printing top " + count + " blocks from database."); + RecoveryUtils.dumpBlocks(count); + return EXIT; + } } - System.out.println(""); + if (options.isDbCompact()) { + RecoveryUtils.dbCompact(); + return EXIT; + } + + // if no return happened earlier, run the kernel + return RUN; } catch (Throwable e) { + // TODO: should be moved to individual procedures System.out.println(""); - return 1; + e.printStackTrace(); + return ERROR; } - - return 0; } /** - * Print the CLI help info. + * Utility method for truncating absolute paths wrt the {@link #BASE_PATH}. + * + * @return the path without the {@link #BASE_PATH} prefix. */ + private String getRelativePath(String path) { + // if absolute paths are given with different prefix the replacement won't work + return path.replaceFirst(BASE_PATH, "."); + } + + /** Print the CLI help info. */ private void printHelp() { - System.out.println("Usage: ./aion.sh [options] [arguments]"); - System.out.println(); - System.out.println(" -h show help info"); - System.out.println(); - System.out.println(" -a create create a new account"); - System.out.println(" -a list list all existing accounts"); - System.out.println(" -a export [address] export private key of an account"); - System.out.println(" -a import [private_key] import private key"); - System.out.println(); - System.out.println(" -c [network] create config to selected network; mainnet, conquest"); - System.out.println(); - System.out.println(" -n, --network [network] execute kernel with selected network; mainnet, conquest"); - System.out.println(); - System.out.println(" -d, --datadir [directory] execute kernel with selected database directory"); - System.out.println(); - System.out.println(" -i show information"); - System.out.println(); - System.out.println(" -s create create an ssl certificate for localhost"); - System.out.println(" -s create [[hostname] [ip]] create an ssl certificate for a custom hostname and ip"); - System.out.println(); - System.out.println(" -r remove blocks on side chains and correct block info"); - System.out.println(" -r [block_number] revert db up to specific block number"); - System.out.println(); - System.out.println(" -v show version"); + String usage = parser.getUsageMessage(); + + usage = usage.replaceFirst("OPTIONS]", "OPTIONS] [ARGUMENTS]"); + + // the command line output has some styling characters in addition to the actual string + // making the use of a regular expression necessary here + usage = usage.replaceFirst(" \\[[^ ]* .*]", "]"); + + System.out.println(usage); + } + + private void printInfo(Cfg cfg) { + System.out.println("\nInformation"); + System.out.println( + "----------------------------------------------------------------------------"); + System.out.println( + "current: p2p://" + + cfg.getId() + + "@" + + cfg.getNet().getP2p().getIp() + + ":" + + cfg.getNet().getP2p().getPort()); + String[] nodes = cfg.getNet().getNodes(); + if (nodes != null && nodes.length > 0) { + System.out.println("boot nodes list:"); + for (String node : nodes) { + System.out.println(" " + node); + } + } else { + System.out.println("boot nodes list is empty"); + } + System.out.println( + "p2p: " + cfg.getNet().getP2p().getIp() + ":" + cfg.getNet().getP2p().getPort()); } /** - * Determines the correct network (mainnet / conquest) enum based on argument + * Sets the directory where the kernel will be executed. * - * @param arg CLI input of -n [network] - * @return Network + * @param directory the directory to be used + * @param cfg the configuration file containing the information + * @return {@code true} when the given directory is valid, {@code false} otherwise. */ - private Network determineNetwork(String arg) { - Network net; - switch(arg) { - case "mainnet": - net = Network.MAINNET; - break; - case "testnet": - net = Network.CONQUEST; - break; - case "conquest": - net = Network.CONQUEST; - break; - default: - net = null; - } - return net; + private boolean setDirectory(String directory, Cfg cfg) { + // use the path ignoring the current base path + File file = new File(directory); + if (!file.isAbsolute()) { + // add the directory to the base path + file = new File(BASE_PATH, directory); + } + + if (!file.exists()) { + if (!file.mkdirs()) { + return false; + } + } + + if (file.isDirectory() && file.canWrite()) { + cfg.setDataDirectory(file); + return true; + } else { + return false; + } + } + + private void setNetwork(String network, Cfg cfg) { + Network net = determineNetwork(network.toLowerCase()); + if (net == null) { + // print error message and set default value + printInvalidNetwork(); + net = Network.MAINNET; + } + cfg.setNetwork(net.toString()); + } + + private void printInvalidNetwork() { + System.out.println("\nInvalid network selected!\n"); + System.out.println("------ Available Networks ------"); + System.out.println(Network.valuesString()); + System.out.println("--------------------------------\n"); } /** - * Copies the config files (config && genesis) from root to [datadir]/[network] + * Creates the directories for persistence of the kernel data. Copies the config and genesis + * files from the initial path for the execution directory. * - * @param path input to append base directory to copy to - * @param net input to determine network to copy from + * @param cfg the configuration for the runtime kernel environment */ - private void copyNetwork(String path, Network net) { - - File dir1 = new File(path + "/" + net + "/config"); - File dir2 = new File(path + "/" + net + "/keystore"); - dir1.mkdirs(); - dir2.mkdirs(); - - File src1 = new File(BASE_PATH + "/config/" + net + "/config.xml"); - File src2 = new File(BASE_PATH + "/config/" + net + "/genesis.json"); - File dst1 = new File(path + "/" + net + "/config/config.xml"); - File dst2 = new File(path + "/" + net + "/config/genesis.json"); - - copyRecursively(src1, dst1); - copyRecursively(src2, dst2); - dstConfig = dst1.toString(); - dstGenesis = dst2.toString(); + @SuppressWarnings("ResultOfMethodCallIgnored") + private void makeDirs(File startConfigFile, Cfg cfg) { + File file = cfg.getExecDir(); + if (!file.exists()) { + file.mkdirs(); + } + + // create target config directory + file = cfg.getExecConfigDirectory(); + if (!file.exists()) { + file.mkdirs(); + } + + // copy config file + File initial = startConfigFile; + File target = cfg.getExecConfigFile(); + if (!initial.equals(target)) { + copyRecursively(initial, target); + + // copy genesis file + initial = cfg.getInitialGenesisFile(); + target = cfg.getExecGenesisFile(); + if (!initial.equals(target)) { + copyRecursively(initial, target); + } + } + + // create target log directory + file = cfg.getLogDir(); + if (!file.exists()) { + file.mkdirs(); + } + + // create target database directory + file = cfg.getDatabaseDir(); + if (!file.exists()) { + file.mkdirs(); + } + + // create target keystore directory + file = cfg.getKeystoreDir(); + if (!file.exists()) { + file.mkdirs(); + } } /** * Creates a new account. * - * @return true only if the new account was successfully created, otherwise false. + * @return {@code true} only if the new account was successfully created, {@code false} + * otherwise. */ private boolean createAccount() { - String password = null, password2 = null; - try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) { + String password, password2; + try (InputStreamReader isr = new InputStreamReader(System.in); + BufferedReader reader = new BufferedReader(isr)) { password = readPassword("Please enter a password: ", reader); password2 = readPassword("Please re-enter your password: ", reader); } catch (IOException e) { @@ -558,6 +586,8 @@ private boolean createAccount() { String address = Keystore.create(password); if (!address.equals("0x")) { System.out.println("A new account has been created: " + address); + System.out.println( + "The account was stored in: " + getRelativePath(Keystore.getKeystorePath())); return true; } else { System.out.println("Failed to create an account!"); @@ -565,17 +595,22 @@ private boolean createAccount() { } } - /** - * List all existing account. - * - * @return boolean - */ + /** List all existing accounts. */ + @SuppressWarnings("SameReturnValue") private boolean listAccounts() { String[] accounts = Keystore.list(); - for (String account : accounts) { - System.out.println(account); - } + if (ArrayUtils.isNotEmpty(accounts)) { + System.out.println( + "All accounts from: " + getRelativePath(Keystore.getKeystorePath()) + "\n"); + + for (String account : accounts) { + System.out.println("\t" + account); + } + } else { + System.out.println( + "No accounts found at: " + getRelativePath(Keystore.getKeystorePath())); + } return true; } @@ -583,16 +618,20 @@ private boolean listAccounts() { * Dumps the private of the given account. * * @param address address of the account - * @return boolean + * @return {@code true} if the operation was successful, {@code false} otherwise. */ private boolean exportPrivateKey(String address) { + System.out.println( + "Searching for account in: " + getRelativePath(Keystore.getKeystorePath())); + if (!Keystore.exist(address)) { System.out.println("The account does not exist!"); return false; } - String password = null; - try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) { + String password; + try (InputStreamReader isr = new InputStreamReader(System.in); + BufferedReader reader = new BufferedReader(isr)) { password = readPassword("Please enter your password: ", reader); } catch (IOException e) { e.printStackTrace(); @@ -613,7 +652,7 @@ private boolean exportPrivateKey(String address) { * Imports a private key. * * @param privateKey private key in hex string - * @return boolean + * @return {@code true} if the operation was successful, {@code false} otherwise. */ private boolean importPrivateKey(String privateKey) { // TODO: the Hex.decode() method catches all exceptions which may cause @@ -626,13 +665,15 @@ 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?"); + 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))) { + String password, password2; + try (InputStreamReader isr = new InputStreamReader(System.in); + BufferedReader reader = new BufferedReader(isr)) { password = readPassword("Please enter a password: ", reader); password2 = readPassword("Please re-enter your password: ", reader); } catch (IOException e) { @@ -647,17 +688,23 @@ private boolean importPrivateKey(String privateKey) { String address = Keystore.create(password, key); if (!address.equals("0x")) { - System.out.println("The private key was imported, the address is: " + address); + System.out.println( + "The private key was imported to: " + + getRelativePath(Keystore.getKeystorePath()) + + "\nThe address is: " + + address); return true; } else { - System.out.println("Failed to import the private key. Already exists?"); + System.out.println( + "Failed to import the private key. It may already exist in: " + + getRelativePath(Keystore.getKeystorePath())); return false; } } /** * 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 + * user input from a console environment and if one is not available it instead attempts to read * from reader. * * @param prompt The read-password prompt to display to the user. @@ -707,40 +754,30 @@ private RecoveryUtils.Status revertTo(String blockNumber) { block = Long.parseLong(blockNumber); } catch (NumberFormatException e) { System.out.println( - "The given argument <" + blockNumber + "> cannot be converted to a number."); + "The given argument «" + blockNumber + "» cannot be converted to a number."); return RecoveryUtils.Status.ILLEGAL_ARGUMENT; } return RecoveryUtils.revertTo(block); } - /** - * Checks for illegal inputs (for datadir && network names) - * @param value - * @return - */ - public static boolean isValid(String value) { - return !value.isEmpty() && !value.matches(".*[-=+,.?;:'!@#$%^&*].*"); - } - private void createKeystoreDirIfMissing() { if (!keystoreDir.isDirectory()) { if (!keystoreDir.mkdir()) { - System.out.println("Ssl keystore directory could not be created. " + - "Please check user permissions or create directory manually."); + System.out.println( + "Ssl keystore directory could not be created. " + + "Please check user permissions or create directory manually."); System.exit(1); } System.out.println(); } } - /** - * For security reasons we only want the ssl option to run in a console environment. - */ + /** For security reasons we only want the ssl option to run in a console environment. */ private void checkConsoleExists(Console console) { if (console == null) { System.out.println( - "No console found. This command can only be run interactively in a console environment."); + "No console found. This command can only be run interactively in a console environment."); System.exit(1); } } @@ -757,46 +794,41 @@ private String getCertName(Console console) { private String getCertPass(Console console) { int minPassLen = 7; - String certPass = String.valueOf(console.readPassword( - "Enter certificate password (at least " + minPassLen + " characters):\n")); - if ((certPass == null) || (certPass.isEmpty())) { + String certPass = + String.valueOf( + console.readPassword( + "Enter certificate password (at least " + + minPassLen + + " characters):\n")); + if (certPass.isEmpty()) { System.out.println("Error: no certificate password entered."); System.exit(1); } else if (certPass.length() < minPassLen) { System.out.println( - "Error: certificate password must be at least " + minPassLen + " characters long."); + "Error: certificate password must be at least " + + minPassLen + + " characters long."); System.exit(1); } return certPass; } - public String getDstConfig() { - return dstConfig; - } - - public String getDstGenesis() { - return dstGenesis; - } - // Methods below taken from FileUtils class - private static boolean copyRecursively(File src, File target) - { + public static boolean copyRecursively(File src, File target) { if (src.isDirectory()) { return copyDirectoryContents(src, target); - } - else { + } else { try { Files.copy(src, target); return true; - } - catch (IOException e) { + } catch (IOException e) { return false; } } } - private static boolean copyDirectoryContents(File src, File target) - { + @SuppressWarnings("ResultOfMethodCallIgnored") + private static boolean copyDirectoryContents(File src, File target) { Preconditions.checkArgument(src.isDirectory(), "Source dir is not a directory: %s", src); // Don't delete symbolic link directories @@ -814,31 +846,29 @@ private static boolean copyDirectoryContents(File src, File target) return success; } - private static boolean isSymbolicLink(File file) - { + private static boolean isSymbolicLink(File file) { try { File canonicalFile = file.getCanonicalFile(); File absoluteFile = file.getAbsoluteFile(); File parentFile = file.getParentFile(); // a symbolic link has a different name between the canonical and absolute path - return !canonicalFile.getName().equals(absoluteFile.getName()) || + return !canonicalFile.getName().equals(absoluteFile.getName()) + || // or the canonical parent path is not the same as the file's parent path, // provided the file has a parent path - parentFile != null && !parentFile.getCanonicalPath().equals(canonicalFile.getParent()); - } - catch (IOException e) { + parentFile != null + && !parentFile.getCanonicalPath().equals(canonicalFile.getParent()); + } catch (IOException e) { // error on the side of caution return true; } } - private static ImmutableList listFiles(File dir) - { + private static ImmutableList listFiles(File dir) { File[] files = dir.listFiles(); if (files == null) { return ImmutableList.of(); } return ImmutableList.copyOf(files); } - } diff --git a/modAionImpl/src/org/aion/zero/impl/config/CfgAion.java b/modAionImpl/src/org/aion/zero/impl/config/CfgAion.java index 2202ecbebc..52df09ab4a 100644 --- a/modAionImpl/src/org/aion/zero/impl/config/CfgAion.java +++ b/modAionImpl/src/org/aion/zero/impl/config/CfgAion.java @@ -25,30 +25,35 @@ package org.aion.zero.impl.config; -import java.io.*; - +import com.google.common.base.Objects; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import javax.xml.stream.*; - -import com.google.common.base.Objects; -import org.aion.mcf.config.*; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; +import org.aion.mcf.config.Cfg; +import org.aion.mcf.config.CfgApi; +import org.aion.mcf.config.CfgDb; +import org.aion.mcf.config.CfgGui; +import org.aion.mcf.config.CfgLog; +import org.aion.mcf.config.CfgNet; +import org.aion.mcf.config.CfgReports; +import org.aion.mcf.config.CfgSync; +import org.aion.mcf.config.CfgTx; import org.aion.zero.exceptions.HeaderStructureException; import org.aion.zero.impl.AionGenesis; import org.aion.zero.impl.GenesisBlockLoader; -/** - * @author chris - */ +/** @author chris */ public final class CfgAion extends Cfg { - private static String NETWORK = "mainnet"; - - private static String CONF_FILE_PATH = BASE_PATH + "/config/" + NETWORK + "/config.xml"; - - private static String GENESIS_FILE_PATH = BASE_PATH + "/config/" + NETWORK + "/genesis.json"; - protected AionGenesis genesis; protected static final int N = 210; @@ -69,6 +74,7 @@ public CfgAion() { this.tx = new CfgTx(); this.reports = new CfgReports(); this.gui = new CfgGui(); + initializeConfiguration(); } private static class CfgAionHolder { @@ -86,7 +92,7 @@ public static void setInst(CfgAion cfgAion) { @Override public void setGenesis() { try { - this.genesis = GenesisBlockLoader.loadJSON(GENESIS_FILE_PATH); + this.genesis = GenesisBlockLoader.loadJSON(getInitialGenesisFile().getAbsolutePath()); } catch (IOException | HeaderStructureException e) { System.out.println(String.format("Genesis load exception %s", e.getMessage())); System.out.println("defaulting to default AionGenesis configuration"); @@ -109,8 +115,7 @@ public CfgConsensusPow getConsensus() { } public synchronized AionGenesis getGenesis() { - if (this.genesis == null) - setGenesis(); + if (this.genesis == null) setGenesis(); return this.genesis; } @@ -122,7 +127,7 @@ public static int getK() { return K; } - private void closeFileInputStream(final FileInputStream fis){ + private void closeFileInputStream(final FileInputStream fis) { if (fis != null) { try { fis.close(); @@ -134,31 +139,30 @@ private void closeFileInputStream(final FileInputStream fis){ } public void dbFromXML() { - File cfgFile = new File(CONF_FILE_PATH); + File cfgFile = getInitialConfigFile(); XMLInputFactory input = XMLInputFactory.newInstance(); FileInputStream fis = null; try { fis = new FileInputStream(cfgFile); XMLStreamReader sr = input.createXMLStreamReader(fis); - loop: while (sr.hasNext()) { + loop: + while (sr.hasNext()) { int eventType = sr.next(); switch (eventType) { - case XMLStreamReader.START_ELEMENT: - String elementName = sr.getLocalName().toLowerCase(); - switch (elementName) { - case "db": - this.db.fromXML(sr); - break; - default: - skipElement(sr); - break; - } - break; - case XMLStreamReader.END_ELEMENT: - if (sr.getLocalName().toLowerCase().equals("aion")) - break loop; - else + case XMLStreamReader.START_ELEMENT: + String elementName = sr.getLocalName().toLowerCase(); + switch (elementName) { + case "db": + this.db.fromXML(sr); + break; + default: + skipElement(sr); + break; + } break; + case XMLStreamReader.END_ELEMENT: + if (sr.getLocalName().toLowerCase().equals("aion")) break loop; + else break; } } } catch (Exception e) { @@ -171,61 +175,60 @@ public void dbFromXML() { public boolean fromXML(final XMLStreamReader sr) throws XMLStreamException { boolean shouldWriteBackToFile = false; - loop: while (sr.hasNext()) { + loop: + while (sr.hasNext()) { int eventType = sr.next(); switch (eventType) { case XMLStreamReader.START_ELEMENT: String elementName = sr.getLocalName().toLowerCase(); switch (elementName) { - case "id": - String nodeId = readValue(sr); - if(NODE_ID_PLACEHOLDER.equals(nodeId)) { - this.id = UUID.randomUUID().toString(); - shouldWriteBackToFile = true; - } else { - this.id = nodeId; - } - break; - case "mode": - this.mode = readValue(sr); - break; - case "api": - this.api.fromXML(sr); - break; - case "net": - this.net.fromXML(sr); - break; - case "sync": - this.sync.fromXML(sr); - break; - case "consensus": - this.consensus.fromXML(sr); - break; - case "db": - this.db.fromXML(sr); - break; - case "log": - this.log.fromXML(sr); - break; - case "tx": - this.tx.fromXML(sr); - break; - case "reports": - this.reports.fromXML(sr); - break; - case "gui": - this.gui.fromXML(sr); - break; - default: - skipElement(sr); - break; + case "id": + String nodeId = readValue(sr); + if (NODE_ID_PLACEHOLDER.equals(nodeId)) { + this.id = UUID.randomUUID().toString(); + shouldWriteBackToFile = true; + } else { + this.id = nodeId; + } + break; + case "mode": + this.mode = readValue(sr); + break; + case "api": + this.api.fromXML(sr); + break; + case "net": + this.net.fromXML(sr); + break; + case "sync": + this.sync.fromXML(sr); + break; + case "consensus": + this.consensus.fromXML(sr); + break; + case "db": + this.db.fromXML(sr); + break; + case "log": + this.log.fromXML(sr); + break; + case "tx": + this.tx.fromXML(sr); + break; + case "reports": + this.reports.fromXML(sr); + break; + case "gui": + this.gui.fromXML(sr); + break; + default: + skipElement(sr); + break; } break; case XMLStreamReader.END_ELEMENT: - if (sr.getLocalName().toLowerCase().equals("aion")) - break loop; - else - break; + if (sr.getLocalName().toLowerCase().equals("aion")) break loop; + else break; } } return shouldWriteBackToFile; @@ -233,10 +236,15 @@ public boolean fromXML(final XMLStreamReader sr) throws XMLStreamException { @Override public boolean fromXML() { + return fromXML(getInitialConfigFile()); + } + + @Override + public boolean fromXML(File cfgFile) { boolean shouldWriteBackToFile = false; - File cfgFile = new File(CONF_FILE_PATH); - if(!cfgFile.exists()) + if (!cfgFile.exists()) { return false; + } XMLInputFactory input = XMLInputFactory.newInstance(); FileInputStream fis; try { @@ -248,11 +256,29 @@ public boolean fromXML() { System.out.println(""); System.exit(1); } + + // checks for absolute path for database + File db = new File(this.getDb().getPath()); + if (db.isAbsolute()) { + this.setDatabaseDir(db); + } + + // checks for absolute path for log + File log = new File(this.getLog().getLogPath()); + if (log.isAbsolute()) { + this.setLogDir(log); + } + return shouldWriteBackToFile; } @Override public void toXML(final String[] args) { + toXML(args, getExecConfigFile()); + } + + @Override + public void toXML(final String[] args, File file) { if (args != null) { boolean override = false; for (String arg : args) { @@ -273,8 +299,7 @@ public void toXML(final String[] args) { if (subArgsArr.length > 0) { List _nodes = new ArrayList<>(); for (String subArg : subArgsArr) { - if (!subArg.equals("")) - _nodes.add(subArg); + if (!subArg.equals("")) _nodes.add(subArg); } this.getNet().setNodes(_nodes.toArray(new String[0])); } @@ -300,8 +325,7 @@ public void toXML(final String[] args) { } } } - if (override) - System.out.println("Config Override"); + if (override) System.out.println("Config Override"); } XMLOutputFactory output = XMLOutputFactory.newInstance(); @@ -310,7 +334,7 @@ public void toXML(final String[] args) { try { - sw = output.createXMLStreamWriter(new FileWriter(CONF_FILE_PATH)); + sw = output.createXMLStreamWriter(new FileWriter(file)); sw.writeStartDocument("utf-8", "1.0"); sw.writeCharacters("\r\n"); sw.writeStartElement("aion"); @@ -355,22 +379,6 @@ public void toXML(final String[] args) { } } - public static String getNetwork() { - return NETWORK; - } - public static String getConfFilePath() { return CONF_FILE_PATH; } - public static String getGenesisFilePath() { return GENESIS_FILE_PATH; } - - public static void setNetwork(String value) { - NETWORK = value; - } - public static void setConfFilePath (String value) { - CONF_FILE_PATH = value; - } - public static void setGenesisFilePath (String value) { - GENESIS_FILE_PATH = value; - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/modAionImpl/src/org/aion/zero/impl/config/Network.java b/modAionImpl/src/org/aion/zero/impl/config/Network.java new file mode 100644 index 0000000000..f7ac622d05 --- /dev/null +++ b/modAionImpl/src/org/aion/zero/impl/config/Network.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017-2018 Aion foundation. + * + * This file is part of the aion network project. + * + * The aion network project is free software: you can redistribute it + * and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or any later version. + * + * The aion network project is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the aion network project source files. + * If not, see . + * + * Contributors: + * Aion foundation. + */ +package org.aion.zero.impl.config; + +import java.util.Arrays; + +/** + * Defines different Aion networks. + * + * @author Alexandra Roatis + */ +public enum Network { + MAINNET("mainnet", 256), + CONQUEST("conquest", 128), + MASTERY("mastery", 32), + CUSTOM("custom", 0); + + private final String name; + private int chainId; + + /** + * Constructor. + * + * @param _name network name + * @param _chainId chain identifier + */ + Network(String _name, int _chainId) { + this.name = _name; + this.chainId = _chainId; + } + + @Override + public String toString() { + return this.name; + } + + public int getChainId() { + return chainId; + } + + /** + * Utility method that determines the correct network based on the given name. + * + * @param network a string value representing the network name + * @return the network object corresponding to the string value or null when the value is not + * mapped to an object. + * @implNote There may be several test networks active. The term {@code testnet} is defined to + * correspond to the current default test network. + */ + public static Network determineNetwork(String network) { + String netStr = network.toLowerCase(); + + if (netStr.equals("testnet")) { + return Network.MASTERY; + } + + for (Network net : Network.values()) { + if (netStr.equals(net.toString())) { + return net; + } + } + + return null; + } + + /** + * Utility method that determines the correct network based on the given chain identifier. + * + * @param chainId a positive integer value representing the network chain identifier + * @return the network object corresponding to the int value or null when the value is not + * mapped to an object. + */ + public static Network determineNetwork(int chainId) { + if (chainId < 0) { + return null; + } + + for (Network net : Network.values()) { + if (chainId == net.getChainId()) { + return net; + } + } + + // custom networks may have any positive chainId not already taken by other defined networks + Network net = Network.CUSTOM; + net.chainId = chainId; + return net; + } + + public static String valuesString() { + String output = Arrays.toString(Network.values()); + return output.substring(1, output.length() - 1); + } + + /** + * Generates a custom network with the given chain identifier. + * + * @param chainId a positive integer value representing the network chain identifier + * @return a custom network object with the given chain identifier. + */ + public static Network getCustomNet(int chainId) { + if (chainId < 0) { + return null; + } + Network net = Network.CUSTOM; + net.chainId = chainId; + return net; + } +} diff --git a/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java b/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java index 7407c2041a..eda7467332 100644 --- a/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java +++ b/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java @@ -86,8 +86,7 @@ private static class AionRepositoryImplHolder { private static final AionRepositoryImpl inst = new AionRepositoryImpl( new RepositoryConfig( - new File(config.getBasePath(), config.getDb().getPath()) - .getAbsolutePath(), + config.getDatabasePath(), ContractDetailsAion.getInstance(), config.getDb())); } diff --git a/modAionImpl/test/org/aion/cli/CliTest.java b/modAionImpl/test/org/aion/cli/CliTest.java deleted file mode 100644 index 7da9a73294..0000000000 --- a/modAionImpl/test/org/aion/cli/CliTest.java +++ /dev/null @@ -1,449 +0,0 @@ -package org.aion.cli; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.common.io.Files; -import org.aion.mcf.account.Keystore; -import org.aion.base.util.Hex; -import org.aion.crypto.ECKey; -import org.aion.crypto.ECKeyFac; -import org.aion.mcf.config.Cfg; -import org.aion.zero.impl.config.CfgAion; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -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 java.io.File; -import java.io.IOException; -import java.nio.file.FileVisitResult; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; - -public class CliTest { - - private static final Cli cli = Mockito.spy(new Cli()); - - CfgAion cfg = CfgAion.inst(); - - String BASE_PATH = cfg.getBasePath(); - - /** - * 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() throws IOException { - doReturn("password").when(cli).readPassword(any(), any()); - - // Copies config folder recursively - File src = new File(BASE_PATH + "/../modBoot/resource"); - File dst = new File(BASE_PATH + "/config"); - copyRecursively(src, dst); - - CfgAion.setConfFilePath(BASE_PATH + "/config/mainnet/config.xml"); - CfgAion.setGenesisFilePath(BASE_PATH + "/config/mainnet/genesis.json"); - Keystore.setKeystorePath(BASE_PATH + "/keystore"); - } - - @After - public void shutdown() { - // Deletes created folders recursively - File path1 = new File(BASE_PATH + "/aaaaaaaa"); - File path2 = new File(BASE_PATH + "/abbbbbbb"); - File path3 = new File(BASE_PATH + "/abcccccc"); - File path4 = new File(BASE_PATH + "/keystore"); - File path5 = new File(BASE_PATH + "/config"); - if(path1.exists() || path2.exists() || path3.exists() || path4.exists() || path5.exists()) { - deleteRecursively(path1); - deleteRecursively(path2); - deleteRecursively(path3); - deleteRecursively(path4); - deleteRecursively(path5); - } - - CfgAion.setConfFilePath(BASE_PATH + "/config/mainnet/config.xml"); - CfgAion.setGenesisFilePath(BASE_PATH + "/config/mainnet/genesis.json"); - Keystore.setKeystorePath(BASE_PATH + "/keystore"); - } - - /** - * 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"); - - String[] args = {"-a", "export", account}; - 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(); - - String[] args = {"-a", "import", Hex.toHexString(key.getPrivKeyBytes())}; - 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(); - System.out.println("Original address : " + Hex.toHexString(key.getAddress())); - System.out.println("Original public key : " + Hex.toHexString(key.getPubKey())); - System.out.println("Original private key: " + Hex.toHexString(key.getPrivKeyBytes())); - - String[] args = {"-a", "import", Hex.toHexString(key.getPrivKeyBytes())}; - assertEquals(0, cli.call(args, CfgAion.inst())); - - ECKey key2 = Keystore.getKey(Hex.toHexString(key.getAddress()), "password"); - System.out.println("Imported address : " + Hex.toHexString(key2.getAddress())); - System.out.println("Imported public key : " + Hex.toHexString(key2.getPubKey())); - 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())); - } - - /** - * Test the -d | --datadir option to see if; - * 1. Access the correct dbPath - * 2. Defaults correctly to "database" - */ - @Test - public void testDatadir() { - - final String[][] networkArgs = new String[][] { - { "-d" , "" }, // Unspecified - { "-d" , "{@.@}" }, // Invalid (illegal characters) - { "-d" , "test_db" , "test"}, // Invalid (number of arguments) - { "-d" , "aaaaaaaa" }, // Valid - { "-d" , "abbbbbbb" }, // Valid - }; - - Cli cli = new Cli(); - - String logOG = cfg.getLog().getLogPath(); - String dbOG = cfg.getDb().getPath(); - System.out.println(logOG + " " + dbOG); - - System.out.println("Invalid Datadir:"); - assertEquals(1, cli.call(networkArgs[0], cfg)); - assertEquals(1, cli.call(networkArgs[1], cfg)); - assertEquals(1, cli.call(networkArgs[2], cfg)); - - cfg.getLog().setLogPath(logOG); - cfg.getDb().setPath(dbOG); - System.out.println(cfg.getLog().getLogPath() + " " + cfg.getDb().getPath()); - - System.out.println("\nValid Datadir 1: " + networkArgs[3][1]); - assertEquals(2, cli.call(networkArgs[3], cfg) ); - assertEquals("aaaaaaaa/mainnet/database", cfg.getDb().getPath() ); - assertEquals("aaaaaaaa/mainnet/log", cfg.getLog().getLogPath() ); - assertEquals(BASE_PATH + "/aaaaaaaa/mainnet/keystore", Keystore.getKeystorePath()); - printPaths(BASE_PATH, cfg, cli); - - System.out.println("\nValid Datadir 2: " + networkArgs[4][1]); - assertEquals(2, cli.call(networkArgs[4], cfg) ); - assertEquals("abbbbbbb/mainnet/database", cfg.getDb().getPath() ); - assertEquals("abbbbbbb/mainnet/log", cfg.getLog().getLogPath() ); - assertEquals(BASE_PATH + "/abbbbbbb/mainnet/keystore", Keystore.getKeystorePath()); - printPaths(BASE_PATH, cfg, cli); - - } - - /** - * Test the -n | --network option to see if; - * 1. Access the correct CONF_FILE_PATH - * 2. Access the correct GENESIS_FILE_PATH - * 3. Defaults correctly to "mainnet" - */ - @Test - public void testNetwork() { - - final String[][] networkArgs = new String[][] { - { "-n" , "" }, // Unspecified - { "-n" , "{@.@}" }, // Invalid (illegal characters) - { "-n" , "testnet", "test" }, // Invalid (number of arguments) - { "-n" , "mainnet" }, // Mainnet - { "-n" , "testnet" }, // Testnet - { "-n" , "conquest" }, // Conquest - }; - - Cli cli = new Cli(); - - System.out.println("Invalid Networks:"); - assertEquals(1, cli.call(networkArgs[0], cfg) ); - assertEquals(1, cli.call(networkArgs[1], cfg) ); - assertEquals(1, cli.call(networkArgs[2], cfg) ); - - System.out.println("\nValid Network 1: " + networkArgs[3][1]); - assertEquals(2, cli.call(networkArgs[3], cfg) ); - assertEquals("mainnet", CfgAion.getNetwork() ); - assertEquals(BASE_PATH + "/config/mainnet/config.xml", CfgAion.getConfFilePath() ); - assertEquals(BASE_PATH + "/config/mainnet/genesis.json", CfgAion.getGenesisFilePath() ); - printPaths(BASE_PATH, cfg, cli); - - System.out.println("\nValid Network 2: " + networkArgs[4][1]); - assertEquals(2, cli.call(networkArgs[4], cfg) ); - assertEquals("conquest", CfgAion.getNetwork() ); - assertEquals(BASE_PATH + "/config/conquest/config.xml", CfgAion.getConfFilePath() ); - assertEquals(BASE_PATH + "/config/conquest/genesis.json", CfgAion.getGenesisFilePath() ); - printPaths(BASE_PATH, cfg, cli); - - System.out.println("\nValid Network 3: " + networkArgs[5][1]); - assertEquals(2, cli.call(networkArgs[5], cfg) ); - assertEquals("conquest", CfgAion.getNetwork() ); - assertEquals(BASE_PATH + "/config/conquest/config.xml", CfgAion.getConfFilePath() ); - assertEquals(BASE_PATH + "/config/conquest/genesis.json", CfgAion.getGenesisFilePath() ); - printPaths(BASE_PATH, cfg, cli); - - } - - @Test - public void testMultiNetworkDatadir() { - - final String[][] multiArgs = new String[][] { - { "-d" , "aaaaaaaa" , "-n" , "mainnet" }, // New network - { "-d" , "abbbbbbb" , "-n" , "conquest" }, // New dir - { "-n" , "conquest" , "-d" , "abcccccc" }, // New dir - { "-d" , "aaaaaaaa" , "-n" , "conquest" }, // Existing dir / network - { "-n" , "conquest" , "-d" , "aaaaaaaa" }, // Exisitng network / dir - { "-d" , "{@.@}" , "-n" , "conquest" }, // Invalid dir - { "-n" , "conquest" , "-d" , "{@.@}" }, // Invalid dir - { "-d" , "aaaaaaaa" , "-n" , "{@.@}" }, // Invalid network - { "-n" , "{@.@}" , "-d" , "aaaaaaaa" }, // Invalid network - }; - - Cli cli = new Cli(); - Cfg cfg = CfgAion.inst(); - - System.out.println("\nNew 1: " + multiArgs[0][1] + " " + multiArgs[0][3]); - assertEquals(2, cli.call(multiArgs[0], cfg)); - assertEquals("mainnet", CfgAion.getNetwork() ); - assertEquals("aaaaaaaa/mainnet/log", cfg.getLog().getLogPath()); - assertEquals("aaaaaaaa/mainnet/database", cfg.getDb().getPath()); - assertEquals(BASE_PATH + "/aaaaaaaa/mainnet/keystore", Keystore.getKeystorePath()); - printPaths(BASE_PATH, cfg, cli); - - System.out.println("\nNew 2: " + multiArgs[1][1] + " " + multiArgs[1][3]); - assertEquals(2, cli.call(multiArgs[1], cfg)); - assertEquals("conquest", CfgAion.getNetwork() ); - assertEquals("abbbbbbb/conquest/log", cfg.getLog().getLogPath()); - assertEquals("abbbbbbb/conquest/database", cfg.getDb().getPath()); - assertEquals(BASE_PATH + "/abbbbbbb/conquest/keystore", Keystore.getKeystorePath()); - printPaths(BASE_PATH, cfg, cli); - - System.out.println("\nNew 3: " + multiArgs[2][1] + " " + multiArgs[2][3]); - assertEquals(2, cli.call(multiArgs[2], cfg)); - assertEquals("conquest", CfgAion.getNetwork() ); - assertEquals("abcccccc/conquest/log", cfg.getLog().getLogPath()); - assertEquals("abcccccc/conquest/database", cfg.getDb().getPath()); - assertEquals(BASE_PATH + "/abcccccc/conquest/keystore", Keystore.getKeystorePath()); - printPaths(BASE_PATH, cfg, cli); - - System.out.println("\n Exist 1: " + multiArgs[3][1] + " " + multiArgs[3][3]); - assertEquals(2, cli.call(multiArgs[3], cfg)); - assertEquals("conquest", CfgAion.getNetwork() ); - assertEquals("aaaaaaaa/conquest/log", cfg.getLog().getLogPath()); - assertEquals("aaaaaaaa/conquest/database", cfg.getDb().getPath()); - assertEquals(BASE_PATH + "/aaaaaaaa/conquest/keystore", Keystore.getKeystorePath()); - printPaths(BASE_PATH, cfg, cli); - - System.out.println("\nExist 2: " + multiArgs[4][1] + " " + multiArgs[4][3]); - assertEquals(2, cli.call(multiArgs[4], cfg)); - assertEquals("conquest", CfgAion.getNetwork() ); - assertEquals("aaaaaaaa/conquest/log", cfg.getLog().getLogPath()); - assertEquals("aaaaaaaa/conquest/database", cfg.getDb().getPath()); - assertEquals(BASE_PATH + "/aaaaaaaa/conquest/keystore", Keystore.getKeystorePath()); - printPaths(BASE_PATH, cfg, cli); - - // Invalid input - assertEquals(1, cli.call(multiArgs[5], cfg)); - assertEquals(1, cli.call(multiArgs[6], cfg)); - assertEquals(1, cli.call(multiArgs[7], cfg)); - assertEquals(1, cli.call(multiArgs[8], cfg)); - } - - private void printPaths(String BASE_PATH, Cfg cfg, Cli cli) { - System.out.println("\n-------------------------------- USED PATHS --------------------------------" + - "\n> Logger path: " + BASE_PATH + "/" + cfg.getLog().getLogPath() + - "\n> Database path: " + BASE_PATH + "/" + cfg.getDb().getPath() + - "\n> Keystore path: " + Keystore.getKeystorePath() + - "\n> Config write: " + cli.getDstConfig() + - "\n> Genesis write: " + cli.getDstGenesis() + - "\n----------------------------------------------------------------------------" + - "\n> Config read: " + CfgAion.getConfFilePath() + - "\n> Genesis read: " + CfgAion.getGenesisFilePath() + - "\n----------------------------------------------------------------------------\n\n"); - } - - // Methods below taken from FileUtils class - private static boolean copyRecursively(File src, File target) - { - if (src.isDirectory()) { - return copyDirectoryContents(src, target); - } - else { - try { - Files.copy(src, target); - return true; - } - catch (IOException e) { - return false; - } - } - } - - private static boolean copyDirectoryContents(File src, File target) - { - Preconditions.checkArgument(src.isDirectory(), "Source dir is not a directory: %s", src); - - // Don't delete symbolic link directories - if (isSymbolicLink(src)) { - return false; - } - - target.mkdirs(); - Preconditions.checkArgument(target.isDirectory(), "Target dir is not a directory: %s", src); - - boolean success = true; - for (File file : listFiles(src)) { - success = copyRecursively(file, new File(target, file.getName())) && success; - } - return success; - } - - private static boolean isSymbolicLink(File file) - { - try { - File canonicalFile = file.getCanonicalFile(); - File absoluteFile = file.getAbsoluteFile(); - File parentFile = file.getParentFile(); - // a symbolic link has a different name between the canonical and absolute path - return !canonicalFile.getName().equals(absoluteFile.getName()) || - // or the canonical parent path is not the same as the file's parent path, - // provided the file has a parent path - parentFile != null && !parentFile.getCanonicalPath().equals(canonicalFile.getParent()); - } - catch (IOException e) { - // error on the side of caution - return true; - } - } - - private static ImmutableList listFiles(File dir) - { - File[] files = dir.listFiles(); - if (files == null) { - return ImmutableList.of(); - } - return ImmutableList.copyOf(files); - } - - private static boolean deleteRecursively(File file) { - Path path = file.toPath(); - try { - java.nio.file.Files.walkFileTree(path, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { - java.nio.file.Files.delete(file); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFileFailed(final Path file, final IOException e) { - return handleException(e); - } - - private FileVisitResult handleException(final IOException e) { - // e.printStackTrace(); - return FileVisitResult.TERMINATE; - } - - @Override - public FileVisitResult postVisitDirectory(final Path dir, final IOException e) throws IOException { - if (e != null) - return handleException(e); - java.nio.file.Files.delete(dir); - return FileVisitResult.CONTINUE; - } - }); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - - return true; - } - -} \ No newline at end of file diff --git a/modAionImpl/test/org/aion/util/TestResources.java b/modAionImpl/test/org/aion/util/TestResources.java index fc0bed1b32..f7a64d5000 100644 --- a/modAionImpl/test/org/aion/util/TestResources.java +++ b/modAionImpl/test/org/aion/util/TestResources.java @@ -36,22 +36,21 @@ public class TestResources { private static final String userDir = System.getProperty("user.dir"); - private static final String module = "modAionImpl"; - private static final String rawDataFileWithRandomBlocks = "test_resources/raw-block-data.txt"; - private static final String rawDataFileWithConsecutiveBlocks = - "test_resources/consecutive-raw-block-data.txt"; + private static final String rawDataFileWithRandomBlocks = "raw-block-data.txt"; + private static final String rawDataFileWithConsecutiveBlocks = "consecutive-raw-block-data.txt"; + private static final String moduleDir = "modAionImpl"; + private static final String testResourceDir = "test_resources"; + + public static final File TEST_RESOURCE_DIR = + userDir.contains(moduleDir) + ? new File(userDir, testResourceDir) + : new File(userDir, moduleDir + File.separator + testResourceDir); /** Extracts raw block data from a test resource file. */ public static List rawBlockData(String rawDataFile) { List parameters = new ArrayList<>(); - File file; - - if (userDir.contains(module)) { - file = new File(userDir, rawDataFile); - } else { - file = new File(userDir, module + "/" + rawDataFile); - } + File file = new File(TEST_RESOURCE_DIR, rawDataFile); BufferedReader reader = null; @@ -83,13 +82,7 @@ public static List rawBlockData(String rawDataFile) { public static List rawBlockData(int limit, String rawDataFile) { List parameters = new ArrayList<>(); - File file; - - if (userDir.contains(module)) { - file = new File(userDir, rawDataFile); - } else { - file = new File(userDir, module + "/" + rawDataFile); - } + File file = new File(TEST_RESOURCE_DIR, rawDataFile); BufferedReader reader = null; diff --git a/modAionImpl/test/org/aion/zero/impl/GenesisTestNetJsonTest.java b/modAionImpl/test/org/aion/zero/impl/GenesisTestNetJsonTest.java index 08c5e5cb98..9c17983ad8 100644 --- a/modAionImpl/test/org/aion/zero/impl/GenesisTestNetJsonTest.java +++ b/modAionImpl/test/org/aion/zero/impl/GenesisTestNetJsonTest.java @@ -1,27 +1,45 @@ +/* + * Copyright (c) 2017-2018 Aion foundation. + * + * This file is part of the aion network project. + * + * The aion network project is free software: you can redistribute it + * and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or any later version. + * + * The aion network project is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the aion network project source files. + * If not, see . + * + * Contributors: + * Aion foundation. + */ package org.aion.zero.impl; +import static com.google.common.truth.Truth.assertThat; +import static org.aion.util.TestResources.TEST_RESOURCE_DIR; + +import java.io.File; +import java.io.IOException; +import java.math.BigInteger; +import java.util.Map; import org.aion.base.type.Address; import org.aion.base.util.ByteUtil; import org.aion.mcf.core.AccountState; import org.aion.zero.exceptions.HeaderStructureException; -import org.aion.zero.impl.AionGenesis; -import org.aion.zero.impl.GenesisBlockLoader; import org.junit.BeforeClass; import org.junit.Test; -import java.io.IOException; -import java.math.BigInteger; -import java.util.Map; - -import static com.google.common.truth.Truth.assertThat; - public class GenesisTestNetJsonTest { - // if we're running in the module itself - private static final String TESTNET_DEFAULT_LOCATION = "../modBoot/resource/conquest/genesis.json"; - - // if we're running in the project folder - private static final String TESTNET_ALT_LOCATION = "./modBoot/resource/conquest/genesis.json"; + private static final String TESTNET_DEFAULT_LOCATION = + new File(TEST_RESOURCE_DIR, "testnet.json").getAbsolutePath(); private static AionGenesis genesis; @@ -41,13 +59,6 @@ public static void loadFile() { } System.out.println(String.format("Failed to load genesis from: %s", TESTNET_DEFAULT_LOCATION)); - System.out.println("trying alternative"); - try { - genesis = GenesisBlockLoader.loadJSON(TESTNET_ALT_LOCATION); - } catch (IOException | HeaderStructureException e2) { - System.out.println("Failed to load from alternative location"); - e2.printStackTrace(); - } } } diff --git a/modAionImpl/test/org/aion/zero/impl/cli/ArgumentsTest.java b/modAionImpl/test/org/aion/zero/impl/cli/ArgumentsTest.java new file mode 100644 index 0000000000..d8dbb7c8d8 --- /dev/null +++ b/modAionImpl/test/org/aion/zero/impl/cli/ArgumentsTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2017-2018 Aion foundation. + * + * This file is part of the aion network project. + * + * The aion network project is free software: you can redistribute it + * and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or any later version. + * + * The aion network project is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the aion network project source files. + * If not, see . + * + * Contributors: + * Aion foundation. + */ +package org.aion.zero.impl.cli; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; + +/** @author Alexandra Roatis */ +public class ArgumentsTest { + + @Test + public void testPreprocess_wCreate() { + // first parameter + String[] input = + new String[] { + "-a", "create", "-n", "mainnet", "-d", "abc", + }; + String[] expected = + new String[] { + "-a create", "-n", "mainnet", "-d", "abc", + }; + + String[] actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + + // middle parameter + input = new String[] {"-d", "abc", "-a", "create", "-n", "mainnet"}; + expected = new String[] {"-d", "abc", "-a create", "-n", "mainnet"}; + + actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + + // last parameter + input = new String[] {"-n", "mainnet", "-d", "abc", "-a", "create"}; + expected = new String[] {"-n", "mainnet", "-d", "abc", "-a create"}; + + actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testPreprocess_wImport() { + // first parameter + String[] input = + new String[] { + "-a", "import", "0x123", "-n", "mainnet", "-d", "abc", + }; + String[] expected = + new String[] { + "-a import", "0x123", "-n", "mainnet", "-d", "abc", + }; + + String[] actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + + // middle parameter + input = new String[] {"-d", "abc", "-a", "import", "0x123", "-n", "mainnet"}; + expected = new String[] {"-d", "abc", "-a import", "0x123", "-n", "mainnet"}; + + actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + + // last parameter + input = new String[] {"-n", "mainnet", "-d", "abc", "-a", "import", "0x123"}; + expected = new String[] {"-n", "mainnet", "-d", "abc", "-a import", "0x123"}; + + actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testPreprocess_wExport() { + // first parameter + String[] input = + new String[] { + "-a", "export", "0x123", "-n", "mainnet", "-d", "abc", + }; + String[] expected = + new String[] { + "-a export", "0x123", "-n", "mainnet", "-d", "abc", + }; + + String[] actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + + // middle parameter + input = new String[] {"-d", "abc", "-a", "export", "0x123", "-n", "mainnet"}; + expected = new String[] {"-d", "abc", "-a export", "0x123", "-n", "mainnet"}; + + actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + + // last parameter + input = new String[] {"-n", "mainnet", "-d", "abc", "-a", "export", "0x123"}; + expected = new String[] {"-n", "mainnet", "-d", "abc", "-a export", "0x123"}; + + actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testPreprocess_wList() { + // first parameter + String[] input = + new String[] { + "-a", "list", "-n", "mainnet", "-d", "abc", + }; + String[] expected = + new String[] { + "-a list", "-n", "mainnet", "-d", "abc", + }; + + String[] actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + + // middle parameter + input = new String[] {"-d", "abc", "-a", "list", "-n", "mainnet"}; + expected = new String[] {"-d", "abc", "-a list", "-n", "mainnet"}; + + actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + + // last parameter + input = new String[] {"-n", "mainnet", "-d", "abc", "-a", "list"}; + expected = new String[] {"-n", "mainnet", "-d", "abc", "-a list"}; + + actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testPreprocess_wSsl() { + // first parameter + String[] input = + new String[] { + "-s", "create", "-n", "mainnet", "-d", "abc", + }; + String[] expected = + new String[] { + "-s create", "-n", "mainnet", "-d", "abc", + }; + + String[] actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + + // middle parameter + input = new String[] {"-d", "abc", "-s", "create", "-n", "mainnet"}; + expected = new String[] {"-d", "abc", "-s create", "-n", "mainnet"}; + + actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + + // last parameter + input = new String[] {"-n", "mainnet", "-d", "abc", "-s", "create"}; + expected = new String[] {"-n", "mainnet", "-d", "abc", "-s create"}; + + actual = Arguments.preProcess(input); + assertThat(actual).isEqualTo(expected); + } +} diff --git a/modAionImpl/test/org/aion/zero/impl/cli/CliTest.java b/modAionImpl/test/org/aion/zero/impl/cli/CliTest.java new file mode 100644 index 0000000000..90f2ead18f --- /dev/null +++ b/modAionImpl/test/org/aion/zero/impl/cli/CliTest.java @@ -0,0 +1,1167 @@ +/* + * Copyright (c) 2017-2018 Aion foundation. + * + * This file is part of the aion network project. + * + * The aion network project is free software: you can redistribute it + * and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or any later version. + * + * The aion network project is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the aion network project source files. + * If not, see . + * + * Contributors: + * Aion foundation. + */ +package org.aion.zero.impl.cli; + +import static com.google.common.truth.Truth.assertThat; +import static org.aion.util.TestResources.TEST_RESOURCE_DIR; +import static org.aion.zero.impl.cli.Cli.ReturnType.ERROR; +import static org.aion.zero.impl.cli.Cli.ReturnType.EXIT; +import static org.aion.zero.impl.cli.Cli.ReturnType.RUN; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.io.Files; +import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; +import org.aion.base.util.Hex; +import org.aion.crypto.ECKey; +import org.aion.crypto.ECKeyFac; +import org.aion.mcf.account.Keystore; +import org.aion.mcf.config.Cfg; +import org.aion.zero.impl.cli.Cli.ReturnType; +import org.aion.zero.impl.config.CfgAion; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** CliTest for new version with use of different networks. */ +@RunWith(JUnitParamsRunner.class) +public class CliTest { + + private final Cli cli = new Cli(); + private final CfgAion cfg = CfgAion.inst(); + private final Cli mockCli = spy(cli); + + // base paths + private static final String BASE_PATH = System.getProperty("user.dir"); + private static final File MAIN_BASE_PATH = new File(BASE_PATH, "mainnet"); + private static final File TEST_BASE_PATH = new File(BASE_PATH, "mastery"); + + // config paths + private static final File CONFIG_PATH = new File(BASE_PATH, "config"); + private static final File MAIN_CONFIG_PATH = new File(CONFIG_PATH, "mainnet"); + private static final File TEST_CONFIG_PATH = new File(CONFIG_PATH, "mastery"); + + private static final String module = "modAionImpl"; + + private static final String configFileName = "config.xml"; + private static final String genesisFileName = "genesis.json"; + + private static final String dataDirectory = "datadir"; + private static final String alternativeDirectory = "random"; + + private static final File path = new File(BASE_PATH, dataDirectory); + private static final File alternativePath = new File(BASE_PATH, alternativeDirectory); + + private static final File config = new File(TEST_RESOURCE_DIR, configFileName); + private static final File oldConfig = new File(CONFIG_PATH, configFileName); + private static final File mainnetConfig = new File(MAIN_CONFIG_PATH, configFileName); + private static final File testnetConfig = new File(TEST_CONFIG_PATH, configFileName); + + private static final File genesis = new File(TEST_RESOURCE_DIR, genesisFileName); + private static final File oldGenesis = new File(CONFIG_PATH, genesisFileName); + private static final File mainnetGenesis = new File(MAIN_CONFIG_PATH, genesisFileName); + private static final File testnetGenesis = new File(TEST_CONFIG_PATH, genesisFileName); + + /** @implNote set this to true to enable printing */ + private static final boolean verbose = false; + + @Before + public void setup() { + // reset config values + cfg.fromXML(config); + + if (BASE_PATH.contains(module) && !mainnetConfig.exists()) { + // save config to disk at expected location for new kernel + if (!MAIN_CONFIG_PATH.exists()) { + assertThat(MAIN_CONFIG_PATH.mkdirs()).isTrue(); + } + Cli.copyRecursively(config, mainnetConfig); + Cli.copyRecursively(genesis, mainnetGenesis); + } + + if (BASE_PATH.contains(module) && !testnetConfig.exists()) { + // save config to disk at expected location for new kernel + if (!TEST_CONFIG_PATH.exists()) { + assertThat(TEST_CONFIG_PATH.mkdirs()).isTrue(); + } + Cli.copyRecursively(config, mainnetConfig); + Cli.copyRecursively(genesis, testnetGenesis); + } + + cfg.resetInternal(); + + doReturn("password").when(mockCli).readPassword(any(), any()); + doCallRealMethod().when(mockCli).call(any(), any()); + } + + @After + public void shutdown() { + deleteRecursively(path); + deleteRecursively(alternativePath); + + // to avoid deleting config for all tests + if (BASE_PATH.contains(module)) { + deleteRecursively(CONFIG_PATH); + } + + deleteRecursively(MAIN_BASE_PATH); + deleteRecursively(TEST_BASE_PATH); + } + + /** + * Ensures that the { -h, --help, -v, --version } arguments work. + */ + @Test + @Parameters({"-h", "--help", "-v", "--version"}) + public void testHelpAndVersion(String option) { + assertThat(cli.call(new String[] {option}, cfg)).isEqualTo(EXIT); + } + + /** Parameters for testing {@link #testDirectoryAndNetwork(String[], ReturnType, String)}. */ + @SuppressWarnings("unused") + private Object parametersWithDirectoryAndNetwork() { + List parameters = new ArrayList<>(); + + String[] dir_options = new String[] {"-d", "--datadir"}; + String[] net_options = new String[] {"-n", "--network"}; + String expected = new File(path, "mainnet").getAbsolutePath(); + String expOnError = MAIN_BASE_PATH.getAbsolutePath(); + + // data directory alone + for (String op : dir_options) { + // with relative path + parameters.add(new Object[] {new String[] {op, dataDirectory}, RUN, expected}); + // with absolute path + parameters.add(new Object[] {new String[] {op, path.getAbsolutePath()}, RUN, expected}); + // without value + parameters.add(new Object[] {new String[] {op}, ERROR, expOnError}); + // with invalid characters (Linux & Win) + parameters.add(new Object[] {new String[] {op, "/\\<>:\"|?*"}, ERROR, expOnError}); + } + + // network and directory + String[] net_values = new String[] {"mainnet", "invalid"}; + for (String opDir : dir_options) { + for (String opNet : net_options) { + for (String valNet : net_values) { + // with relative path + parameters.add( + new Object[] { + new String[] {opDir, dataDirectory, opNet, valNet}, RUN, expected + }); + parameters.add( + new Object[] { + new String[] {opNet, valNet, opDir, dataDirectory}, RUN, expected + }); + // with absolute path + parameters.add( + new Object[] { + new String[] {opDir, path.getAbsolutePath(), opNet, valNet}, + RUN, + expected + }); + parameters.add( + new Object[] { + new String[] {opNet, valNet, opDir, path.getAbsolutePath()}, + RUN, + expected + }); + } + } + } + + // network alone + expected = MAIN_BASE_PATH.getAbsolutePath(); + for (String op : net_options) { + // without parameter + parameters.add(new Object[] {new String[] {op}, ERROR, expOnError}); + // with two parameters + parameters.add( + new Object[] {new String[] {op, "testnet", "mastery"}, ERROR, expOnError}); + // invalid parameter + parameters.add(new Object[] {new String[] {op, "invalid"}, RUN, expected}); + // mainnet as parameter + parameters.add(new Object[] {new String[] {op, "mainnet"}, RUN, expected}); + } + + // network alone with testnet + net_values = new String[] {"mastery", "testnet"}; + expected = TEST_BASE_PATH.getAbsolutePath(); + for (String op : net_options) { + for (String netVal : net_values) { + // mastery as parameter + parameters.add(new Object[] {new String[] {op, netVal}, RUN, expected}); + } + } + + // network and directory with testnet + expected = new File(path, "mastery").getAbsolutePath(); + for (String opDir : dir_options) { + for (String opNet : net_options) { + for (String netVal : net_values) { + // with relative path + parameters.add( + new Object[] { + new String[] {opDir, dataDirectory, opNet, netVal}, RUN, expected + }); + parameters.add( + new Object[] { + new String[] {opNet, netVal, opDir, dataDirectory}, RUN, expected + }); + // with absolute path + parameters.add( + new Object[] { + new String[] {opDir, path.getAbsolutePath(), opNet, netVal}, + RUN, + expected + }); + parameters.add( + new Object[] { + new String[] {opNet, netVal, opDir, path.getAbsolutePath()}, + RUN, + expected + }); + } + } + } + + // with subdirectories + String dir = dataDirectory + File.separator + "subfolder"; + File path = new File(BASE_PATH, dir); + expected = new File(path, "mainnet").getAbsolutePath(); + for (String op : dir_options) { + // with relative path with subdirectories + parameters.add(new Object[] {new String[] {op, dir}, RUN, expected}); + // with multiple values + parameters.add(new Object[] {new String[] {op, dataDirectory, dir}, ERROR, expOnError}); + } + + return parameters.toArray(); + } + + /** + * Ensures that the { -d, --datadir, -n, --network } arguments work. + */ + @Test + @Parameters(method = "parametersWithDirectoryAndNetwork") + public void testDirectoryAndNetwork( + String[] input, ReturnType expectedReturn, String expectedPath) { + assertThat(cli.call(input, cfg)).isEqualTo(expectedReturn); + assertThat(cfg.getBasePath()).isEqualTo(expectedPath); + assertThat(cfg.getExecConfigFile()) + .isEqualTo(new File(expectedPath, "config" + File.separator + configFileName)); + assertThat(cfg.getExecGenesisFile()) + .isEqualTo(new File(expectedPath, "config" + File.separator + genesisFileName)); + assertThat(cfg.getDatabaseDir()).isEqualTo(new File(expectedPath, "database")); + assertThat(cfg.getLogDir()).isEqualTo(new File(expectedPath, "log")); + assertThat(cfg.getKeystoreDir()).isEqualTo(new File(expectedPath, "keystore")); + + if (verbose) { + printPaths(cfg); + } + } + + private void printPaths(Cfg cfg) { + System.out.println( + "\n-------------------------------- USED PATHS --------------------------------" + + "\n> Logger path: " + + cfg.getLogDir().getAbsolutePath() + + "\n> Database path: " + + cfg.getDatabaseDir().getAbsolutePath() + + "\n> Keystore path: " + + cfg.getKeystoreDir().getAbsolutePath() + + "\n> Config write: " + + cfg.getExecConfigFile().getAbsolutePath() + + "\n> Genesis write: " + + cfg.getExecGenesisFile().getAbsolutePath() + + "\n----------------------------------------------------------------------------" + + "\n> Config read: " + + cfg.getInitialConfigFile().getAbsolutePath() + + "\n> Genesis read: " + + cfg.getInitialGenesisFile().getAbsolutePath() + + "\n----------------------------------------------------------------------------\n\n"); + } + + /** + * Ensures that the { -d, --datadir, -n, --network } arguments work + * with absolute paths for the database and log. The absolute path overwrites the datadir option + * location. + */ + @Test + @Parameters(method = "parametersWithDirectoryAndNetwork") + public void testDirectoryAndNetwork_wAbsoluteDbAndLogPath( + String[] input, ReturnType expectedReturn, String expectedPath) { + + File db = new File(alternativePath, "database"); + cfg.getDb().setPath(db.getAbsolutePath()); + File log = new File(alternativePath, "log"); + cfg.getLog().setLogPath(log.getAbsolutePath()); + + // save and reload for changes to take effect + cfg.toXML(null, cfg.getInitialConfigFile()); + cfg.fromXML(); + + assertThat(cli.call(input, cfg)).isEqualTo(expectedReturn); + assertThat(cfg.getBasePath()).isEqualTo(expectedPath); + + assertThat(cfg.getDatabaseDir()).isNotEqualTo(new File(expectedPath, "database")); + assertThat(cfg.getDatabaseDir()).isEqualTo(db); + + assertThat(cfg.getLogDir()).isNotEqualTo(new File(expectedPath, "log")); + assertThat(cfg.getLogDir()).isEqualTo(log); + + if (verbose) { + printPaths(cfg); + } + } + + /** Parameters for testing {@link #testConfig(String[], File, String)}. */ + @SuppressWarnings("unused") + private Object parametersWithConfig() { + List parameters = new ArrayList<>(); + + String[] options = new String[] {"-c", "--config"}; + String expected = MAIN_BASE_PATH.getAbsolutePath(); + + for (String op : options) { + // without parameter + parameters.add(new Object[] {new String[] {op}, mainnetConfig, expected}); + // invalid parameter + parameters.add(new Object[] {new String[] {op, "invalid"}, mainnetConfig, expected}); + // mainnet as parameter + parameters.add(new Object[] {new String[] {op, "mainnet"}, mainnetConfig, expected}); + } + + expected = TEST_BASE_PATH.getAbsolutePath(); + + for (String op : options) { + // mastery as parameter + parameters.add(new Object[] {new String[] {op, "mastery"}, testnetConfig, expected}); + // testnet as parameter + parameters.add(new Object[] {new String[] {op, "testnet"}, testnetConfig, expected}); + } + + // config and directory + String[] dir_options = new String[] {"-d", "--datadir"}; + File config = + new File( + path, + "mainnet" + File.separator + "config" + File.separator + configFileName); + expected = new File(path, "mainnet").getAbsolutePath(); + + String[] net_values = new String[] {"mainnet", "invalid"}; + for (String opDir : dir_options) { + for (String opCfg : options) { + for (String valNet : net_values) { + // with relative path + parameters.add( + new Object[] { + new String[] {opDir, dataDirectory, opCfg, valNet}, config, expected + }); + parameters.add( + new Object[] { + new String[] {opCfg, valNet, opDir, dataDirectory}, config, expected + }); + // with absolute path + parameters.add( + new Object[] { + new String[] {opDir, path.getAbsolutePath(), opCfg, valNet}, + config, + expected + }); + parameters.add( + new Object[] { + new String[] {opCfg, valNet, opDir, path.getAbsolutePath()}, + config, + expected + }); + } + } + } + + // config and directory with testnet + net_values = new String[] {"mastery", "testnet"}; + config = + new File( + path, + "mastery" + File.separator + "config" + File.separator + configFileName); + expected = new File(path, "mastery").getAbsolutePath(); + for (String opDir : dir_options) { + for (String opCfg : options) { + for (String netVal : net_values) { + // with relative path + parameters.add( + new Object[] { + new String[] {opDir, dataDirectory, opCfg, netVal}, config, expected + }); + parameters.add( + new Object[] { + new String[] {opCfg, netVal, opDir, dataDirectory}, config, expected + }); + // with absolute path + parameters.add( + new Object[] { + new String[] {opDir, path.getAbsolutePath(), opCfg, netVal}, + config, + expected + }); + parameters.add( + new Object[] { + new String[] {opCfg, netVal, opDir, path.getAbsolutePath()}, + config, + expected + }); + } + } + } + + return parameters.toArray(); + } + + /** Ensures that the { -c, --config } arguments work. */ + @Test + @Parameters(method = "parametersWithConfig") + public void testConfig(String[] input, File expectedFile, String expectedPath) { + if (expectedFile.exists()) { + assertThat(cfg.fromXML(expectedFile)).isTrue(); + } + + assertThat(cli.call(input, cfg)).isEqualTo(EXIT); + assertThat(cfg.getBasePath()).isEqualTo(expectedPath); + assertThat(cfg.getExecConfigFile()) + .isEqualTo(new File(expectedPath, "config" + File.separator + configFileName)); + assertThat(cfg.getExecGenesisFile()) + .isEqualTo(new File(expectedPath, "config" + File.separator + genesisFileName)); + + assertThat(expectedFile.exists()).isTrue(); + assertThat(cfg.fromXML(expectedFile)).isFalse(); + + if (verbose) { + printPaths(cfg); + } + } + + /** Parameters for testing {@link #testConfig_oldLocation(String[], String)}. */ + @SuppressWarnings("unused") + private Object parametersWithConfigForOldLocation() { + List parameters = new ArrayList<>(); + + String[] options = new String[] {"-c", "--config"}; + String expected = MAIN_BASE_PATH.getAbsolutePath(); + + for (String op : options) { + // without parameter + parameters.add(new Object[] {new String[] {op}, BASE_PATH}); + // invalid parameter + parameters.add(new Object[] {new String[] {op, "invalid"}, expected}); + // mainnet as parameter + parameters.add(new Object[] {new String[] {op, "mainnet"}, expected}); + } + + expected = TEST_BASE_PATH.getAbsolutePath(); + + for (String op : options) { + // mastery as parameter + parameters.add(new Object[] {new String[] {op, "mastery"}, expected}); + // testnet as parameter + parameters.add(new Object[] {new String[] {op, "testnet"}, expected}); + } + + return parameters.toArray(); + } + + /** + * Ensures that the { -c, --config } arguments work when using old config + * location. + */ + @Test + @Parameters(method = "parametersWithConfigForOldLocation") + public void testConfig_oldLocation(String[] input, String expectedPath) { + // ensure config exists on disk at expected location for old kernel + if (!oldConfig.exists()) { + File configPath = CONFIG_PATH; + if (!configPath.exists()) { + assertThat(configPath.mkdirs()).isTrue(); + } + cfg.toXML(null, oldConfig); + Cli.copyRecursively(genesis, oldGenesis); + } + + assertThat(cli.call(input, cfg)).isEqualTo(EXIT); + assertThat(cfg.getBasePath()).isEqualTo(expectedPath); + assertThat(cfg.getExecConfigFile()) + .isEqualTo(new File(expectedPath, "config" + File.separator + configFileName)); + assertThat(cfg.getExecGenesisFile()) + .isEqualTo(new File(expectedPath, "config" + File.separator + genesisFileName)); + + if (verbose) { + printPaths(cfg); + } + } + + /** Parameters for testing {@link #testInfo(String[], ReturnType, String)}. */ + @SuppressWarnings("unused") + private Object parametersWithInfo() { + List parameters = new ArrayList<>(); + + String[] options = new String[] {"-i", "--info"}; + String expected = MAIN_BASE_PATH.getAbsolutePath(); + String expOnError = expected; + + // only info + for (String op : options) { + // without parameter + parameters.add(new Object[] {new String[] {op}, EXIT, expected}); + // invalid parameter + parameters.add(new Object[] {new String[] {op, "value"}, ERROR, expOnError}); + } + + // with network + expected = TEST_BASE_PATH.getAbsolutePath(); + for (String op : options) { + // mastery as parameter + parameters.add(new Object[] {new String[] {op, "-n", "mastery"}, EXIT, expected}); + parameters.add(new Object[] {new String[] {"-n", "mastery", op}, EXIT, expected}); + // invalid parameter + parameters.add( + new Object[] {new String[] {op, "value", "-n", "mastery"}, ERROR, expOnError}); + } + + // with directory + expected = new File(path, "mainnet").getAbsolutePath(); + for (String op : options) { + // with relative path + parameters.add(new Object[] {new String[] {op, "-d", dataDirectory}, EXIT, expected}); + parameters.add(new Object[] {new String[] {"-d", dataDirectory, op}, EXIT, expected}); + // + invalid parameter + parameters.add( + new Object[] { + new String[] {op, "value", "-d", dataDirectory}, ERROR, expOnError + }); + // with absolute path + parameters.add( + new Object[] {new String[] {op, "-d", path.getAbsolutePath()}, EXIT, expected}); + parameters.add( + new Object[] {new String[] {"-d", path.getAbsolutePath(), op}, EXIT, expected}); + // + invalid parameter + parameters.add( + new Object[] { + new String[] {op, "value", "-d", path.getAbsolutePath()}, ERROR, expOnError + }); + } + + // with network and directory + expected = new File(path, "mastery").getAbsolutePath(); + for (String op : options) { + // with relative path + parameters.add( + new Object[] { + new String[] {op, "-d", dataDirectory, "-n", "mastery"}, EXIT, expected + }); + parameters.add( + new Object[] { + new String[] {"-n", "mastery", op, "-d", dataDirectory}, EXIT, expected + }); + parameters.add( + new Object[] { + new String[] {"-n", "mastery", "-d", dataDirectory, op}, EXIT, expected + }); + // with absolute path + parameters.add( + new Object[] { + new String[] {op, "-n", "mastery", "-d", path.getAbsolutePath()}, + EXIT, + expected + }); + parameters.add( + new Object[] { + new String[] {"-d", path.getAbsolutePath(), op, "-n", "mastery"}, + EXIT, + expected + }); + parameters.add( + new Object[] { + new String[] {"-d", path.getAbsolutePath(), "-n", "mastery", op}, + EXIT, + expected + }); + } + + return parameters.toArray(); + } + + /** Ensures that the { -i, --info } arguments work. */ + @Test + @Parameters(method = "parametersWithInfo") + public void testInfo(String[] input, ReturnType expectedReturn, String expectedPath) { + assertThat(cli.call(input, cfg)).isEqualTo(expectedReturn); + assertThat(cfg.getBasePath()).isEqualTo(expectedPath); + } + + /** + * Ensures that the { -i, --info } arguments work when using old config location. + */ + @Test + @Parameters({"-i", "--info"}) + public void testInfo_oldLocation(String option) { + // ensure config exists on disk at expected location for old kernel + if (!oldConfig.exists()) { + File configPath = CONFIG_PATH; + if (!configPath.exists()) { + assertThat(configPath.mkdirs()).isTrue(); + } + cfg.toXML(null, oldConfig); + Cli.copyRecursively(genesis, oldGenesis); + } + + assertThat(cli.call(new String[] {option}, cfg)).isEqualTo(EXIT); + assertThat(cfg.getBasePath()).isEqualTo(BASE_PATH); + } + + /** + * Ensures that the { -i, --info } arguments work when using the config location + * copied to the execution directory. + */ + @Test + @Parameters({"-i", "--info"}) + public void testInfo_execLocation(String option) { + // generates the config at the required destination + cli.call(new String[] {"-c", "-d", dataDirectory}, cfg); + + // ensure config exists on disk at expected location for old kernel + assertThat(cfg.getExecConfigFile().exists()).isTrue(); + + assertThat(cli.call(new String[] {option, "-d", dataDirectory}, cfg)).isEqualTo(EXIT); + } + + private List parametersWithAccount(String[] options) { + List parameters = new ArrayList<>(); + + // only info + for (String op : options) { + // without parameter + parameters.add(new Object[] {new String[] {op}, EXIT}); + // invalid parameter + parameters.add(new Object[] {new String[] {op, "value"}, ERROR}); + } + + // with network + for (String op : options) { + // mastery as parameter + parameters.add(new Object[] {new String[] {op, "-n", "mastery"}, EXIT}); + parameters.add(new Object[] {new String[] {"-n", "mastery", op}, EXIT}); + // invalid parameter + parameters.add(new Object[] {new String[] {op, "value", "-n", "mastery"}, ERROR}); + } + + // with directory + for (String op : options) { + // with relative path + parameters.add(new Object[] {new String[] {op, "-d", dataDirectory}, EXIT}); + parameters.add(new Object[] {new String[] {"-d", dataDirectory, op}, EXIT}); + // + invalid parameter + parameters.add(new Object[] {new String[] {op, "value", "-d", dataDirectory}, ERROR}); + // with absolute path + parameters.add(new Object[] {new String[] {op, "-d", path.getAbsolutePath()}, EXIT}); + parameters.add(new Object[] {new String[] {"-d", path.getAbsolutePath(), op}, EXIT}); + // + invalid parameter + parameters.add( + new Object[] {new String[] {op, "value", "-d", path.getAbsolutePath()}, ERROR}); + } + + // with network and directory + for (String op : options) { + // with relative path + parameters.add( + new Object[] {new String[] {op, "-d", dataDirectory, "-n", "mastery"}, EXIT}); + parameters.add( + new Object[] {new String[] {"-n", "mastery", op, "-d", dataDirectory}, EXIT}); + parameters.add( + new Object[] {new String[] {"-n", "mastery", "-d", dataDirectory, op}, EXIT}); + // with absolute path + parameters.add( + new Object[] { + new String[] {op, "-n", "mastery", "-d", path.getAbsolutePath()}, EXIT + }); + parameters.add( + new Object[] { + new String[] {"-d", path.getAbsolutePath(), op, "-n", "mastery"}, EXIT + }); + parameters.add( + new Object[] { + new String[] {"-d", path.getAbsolutePath(), "-n", "mastery", op}, EXIT + }); + } + + return parameters; + } + + /** Parameters for testing {@link #testCreateAccount(String[], ReturnType)}. */ + @SuppressWarnings("unused") + private Object parametersWithCreateAccount() { + return parametersWithAccount(new String[] {"-a create", "ac", "--account create"}) + .toArray(); + } + + /** Ensures that the { -a create, ac } arguments work. */ + @Test + @Parameters(method = "parametersWithCreateAccount") + public void testCreateAccount(String[] input, ReturnType expectedReturn) { + // number of accounts before create call + int count = Keystore.list().length; + assertThat(mockCli.call(input, cfg)).isEqualTo(expectedReturn); + if (expectedReturn == EXIT) { + // ensure number of accounts was incremented + assertThat(Keystore.list().length).isEqualTo(count + 1); + } else { + // ensure number of accounts is unchanged + assertThat(Keystore.list().length).isEqualTo(count); + } + } + + @Test + public void testCreateAccount_withSplitInput() { + // number of accounts before create call + int count = Keystore.list().length; + + assertThat(mockCli.call(new String[] {"-a"}, cfg)).isEqualTo(ERROR); + // ensure number of accounts is unchanged + assertThat(Keystore.list().length).isEqualTo(count); + + assertThat(mockCli.call(new String[] {"-a", "cre"}, cfg)).isEqualTo(ERROR); + // ensure number of accounts is unchanged + assertThat(Keystore.list().length).isEqualTo(count); + + assertThat(mockCli.call(new String[] {"--acc", "create"}, cfg)).isEqualTo(ERROR); + // ensure number of accounts is unchanged + assertThat(Keystore.list().length).isEqualTo(count); + + assertThat(mockCli.call(new String[] {"-a", "create"}, cfg)).isEqualTo(EXIT); + // ensure number of accounts was incremented + assertThat(Keystore.list().length).isEqualTo(count + 1); + + assertThat(mockCli.call(new String[] {"--account", "create"}, cfg)).isEqualTo(EXIT); + // ensure number of accounts was incremented + assertThat(Keystore.list().length).isEqualTo(count + 2); + } + + // TODO: add test for create account with old config location + + /** Parameters for testing {@link #testListAccounts(String[], ReturnType)}. */ + @SuppressWarnings("unused") + private Object parametersWithListAccount() { + return parametersWithAccount(new String[] {"-a list", "al", "--account list"}).toArray(); + } + + /** Ensures that the { -a list, al } arguments work. */ + @Test + @Parameters(method = "parametersWithListAccount") + public void testListAccounts(String[] input, ReturnType expectedReturn) { + assertThat(cli.call(input, cfg)).isEqualTo(expectedReturn); + } + + @Test + public void testListAccounts_withSplitInput() { + // used to ensure number of accounts is unchanged + int count = Keystore.list().length; + + assertThat(cli.call(new String[] {"-a"}, cfg)).isEqualTo(ERROR); + assertThat(Keystore.list().length).isEqualTo(count); + + assertThat(cli.call(new String[] {"-a", "lis"}, cfg)).isEqualTo(ERROR); + assertThat(Keystore.list().length).isEqualTo(count); + + assertThat(cli.call(new String[] {"--acc", "list"}, cfg)).isEqualTo(ERROR); + assertThat(Keystore.list().length).isEqualTo(count); + + assertThat(cli.call(new String[] {"-a", "list"}, cfg)).isEqualTo(EXIT); + assertThat(Keystore.list().length).isEqualTo(count); + + assertThat(cli.call(new String[] {"--account", "list"}, cfg)).isEqualTo(EXIT); + assertThat(Keystore.list().length).isEqualTo(count); + } + + /** Ensures that the { -a export, ae, --account export } arguments work. */ + @Test + @Parameters({"-a export", "ae", "--account export"}) + public void testExportPrivateKey(String option) { + // create account + assertThat(mockCli.call(new String[] {"ac"}, cfg)).isEqualTo(EXIT); + + String account = Keystore.list()[0]; + assertEquals(EXIT, mockCli.call(new String[] {option, account}, cfg)); + } + + /** + * Ensures that the { -a export, ae, --account export } arguments work in + * combination with the data directory option. + */ + @Test + @Parameters({"-a export", "ae", "--account export"}) + public void testExportPrivateKey_withDataDir(String option) { + // create account + assertThat(mockCli.call(new String[] {"ac", "-d", dataDirectory}, cfg)).isEqualTo(EXIT); + + String account = Keystore.list()[0]; + assertEquals(EXIT, mockCli.call(new String[] {option, account, "-d", dataDirectory}, cfg)); + assertEquals(EXIT, mockCli.call(new String[] {"-d", dataDirectory, option, account}, cfg)); + } + + /** + * Ensures that the { -a export, ae, --account export } arguments work in + * combination with the network option. + */ + @Test + @Parameters({"-a export", "ae", "--account export"}) + public void testExportPrivateKey_withNetwork(String option) { + // create account + assertThat(mockCli.call(new String[] {"ac", "-n", "mastery"}, cfg)).isEqualTo(EXIT); + + String account = Keystore.list()[0]; + assertEquals(EXIT, mockCli.call(new String[] {option, account, "-n", "mastery"}, cfg)); + assertEquals(EXIT, mockCli.call(new String[] {"-n", "mastery", option, account}, cfg)); + } + + /** + * Ensures that the { -a export, ae, --account export } arguments work in + * combination with the data directory and network options. + */ + @Test + @Parameters({"-a export", "ae", "--account export"}) + public void testExportPrivateKey_withDataDirAndNetwork(String option) { + // create account + assertThat(mockCli.call(new String[] {"ac", "-d", dataDirectory, "-n", "mastery"}, cfg)) + .isEqualTo(EXIT); + + String account = Keystore.list()[0]; + assertEquals( + EXIT, + mockCli.call( + new String[] {"-n", "mastery", option, account, "-d", dataDirectory}, cfg)); + assertEquals( + EXIT, + mockCli.call( + new String[] {"-n", "mastery", "-d", dataDirectory, option, account}, cfg)); + assertEquals( + EXIT, + mockCli.call( + new String[] {option, account, "-d", dataDirectory, "-n", "mastery"}, cfg)); + } + + /** + * Ensured that the { -a export, ae, --account export } arguments fail when + * the given an incorrect account value (proper substring of a valid account). + */ + @Test + @Parameters({"-a export", "ae", "--account export"}) + public void testExportPrivateKey_wSubstringOfAccount(String prefix) { + // create account + assertThat(mockCli.call(new String[] {"ac"}, cfg)).isEqualTo(EXIT); + + String subStrAcc = Keystore.list()[0].substring(1); + + assertEquals(ERROR, cli.call(new String[] {prefix, subStrAcc}, cfg)); + } + + @Test + public void testExportPrivateKey_withSplitInput() { + // create account + assertThat(mockCli.call(new String[] {"ac"}, cfg)).isEqualTo(EXIT); + String account = Keystore.list()[0]; + + assertThat(mockCli.call(new String[] {"-a", account}, cfg)).isEqualTo(ERROR); + + assertThat(mockCli.call(new String[] {"-a", "exp", account}, cfg)).isEqualTo(ERROR); + + assertThat(mockCli.call(new String[] {"--acc", "export", account}, cfg)).isEqualTo(ERROR); + + assertThat(mockCli.call(new String[] {"-a", "export", account}, cfg)).isEqualTo(EXIT); + + assertThat(mockCli.call(new String[] {"--account", "export", account}, cfg)) + .isEqualTo(EXIT); + } + + /** Ensures that the { -a import, a, --account import } arguments work. */ + @Test + @Parameters({"-a import", "ai", "--account import"}) + public void testImportPrivateKey(String option) { + // test 1: successful call + ECKey key = ECKeyFac.inst().create(); + String pKey = Hex.toHexString(key.getPrivKeyBytes()); + + assertEquals(EXIT, mockCli.call(new String[] {option, pKey}, cfg)); + assertThat(Keystore.list().length).isEqualTo(1); + + // test 2: error -> known key + assertEquals(ERROR, mockCli.call(new String[] {option, pKey}, cfg)); + assertThat(Keystore.list().length).isEqualTo(1); + } + + /** + * Ensures that the { -a import, a, --account import } arguments work in + * combination with the data directory option. + */ + @Test + @Parameters({"-a import", "ai", "--account import"}) + public void testImportPrivateKey_withDataDir(String option) { + // test 1: -d last + ECKey key = ECKeyFac.inst().create(); + String pKey = Hex.toHexString(key.getPrivKeyBytes()); + + assertEquals(EXIT, mockCli.call(new String[] {option, pKey, "-d", dataDirectory}, cfg)); + assertThat(Keystore.list().length).isEqualTo(1); + + // test 2: known key + assertEquals(ERROR, mockCli.call(new String[] {option, pKey, "-d", dataDirectory}, cfg)); + assertThat(Keystore.list().length).isEqualTo(1); + + // test 3: -d first + key = ECKeyFac.inst().create(); + pKey = Hex.toHexString(key.getPrivKeyBytes()); + + assertEquals(EXIT, mockCli.call(new String[] {"-d", dataDirectory, option, pKey}, cfg)); + assertThat(Keystore.list().length).isEqualTo(2); + } + + /** + * Ensures that the { -a import, a, --account import } arguments work in + * combination with the network option. + */ + @Test + @Parameters({"-a import", "ai", "--account import"}) + public void testImportPrivateKey_withNetwork(String option) { + // test 1: -n last + ECKey key = ECKeyFac.inst().create(); + String pKey = Hex.toHexString(key.getPrivKeyBytes()); + + assertEquals(EXIT, mockCli.call(new String[] {option, pKey, "-n", "mastery"}, cfg)); + assertThat(Keystore.list().length).isEqualTo(1); + + // test 2: known key + assertEquals(ERROR, mockCli.call(new String[] {option, pKey, "-n", "mastery"}, cfg)); + assertThat(Keystore.list().length).isEqualTo(1); + + // test 3: -n first + key = ECKeyFac.inst().create(); + pKey = Hex.toHexString(key.getPrivKeyBytes()); + + assertEquals(EXIT, mockCli.call(new String[] {"-n", "mastery", option, pKey}, cfg)); + assertThat(Keystore.list().length).isEqualTo(2); + } + + /** + * Ensures that the { -a import, a, --account import } arguments work in + * combination with the data directory and network options. + */ + @Test + @Parameters({"-a import", "ai", "--account import"}) + public void testImportPrivateKey_withDataDirAndNetwork(String option) { + // test 1: -d -n last + ECKey key = ECKeyFac.inst().create(); + String pKey = Hex.toHexString(key.getPrivKeyBytes()); + + assertEquals( + EXIT, + mockCli.call( + new String[] {option, pKey, "-d", dataDirectory, "-n", "mastery"}, cfg)); + assertThat(Keystore.list().length).isEqualTo(1); + + // test 2: known key + assertEquals( + ERROR, + mockCli.call( + new String[] {option, pKey, "-d", dataDirectory, "-n", "mastery"}, cfg)); + assertThat(Keystore.list().length).isEqualTo(1); + + // test 3: -d -n first + key = ECKeyFac.inst().create(); + pKey = Hex.toHexString(key.getPrivKeyBytes()); + + assertEquals( + EXIT, + mockCli.call( + new String[] {"-n", "mastery", "-d", dataDirectory, option, pKey}, cfg)); + assertThat(Keystore.list().length).isEqualTo(2); + + // test 4: ai middle + key = ECKeyFac.inst().create(); + pKey = Hex.toHexString(key.getPrivKeyBytes()); + + assertEquals( + EXIT, + mockCli.call( + new String[] {"-n", "mastery", option, pKey, "-d", dataDirectory}, cfg)); + assertThat(Keystore.list().length).isEqualTo(3); + } + + @Test + public void testImportPrivateKey_withSplitInput() { + // create account + ECKey key = ECKeyFac.inst().create(); + String pKey = Hex.toHexString(key.getPrivKeyBytes()); + + assertThat(mockCli.call(new String[] {"-a", pKey}, cfg)).isEqualTo(ERROR); + + assertThat(mockCli.call(new String[] {"-a", "imp", pKey}, cfg)).isEqualTo(ERROR); + + assertThat(mockCli.call(new String[] {"--acc", "import", pKey}, cfg)).isEqualTo(ERROR); + + assertThat(mockCli.call(new String[] {"-a", "import", pKey}, cfg)).isEqualTo(EXIT); + assertThat(Keystore.list().length).isEqualTo(1); + + key = ECKeyFac.inst().create(); + pKey = Hex.toHexString(key.getPrivKeyBytes()); + assertThat(mockCli.call(new String[] {"--account", "import", pKey}, cfg)).isEqualTo(EXIT); + assertThat(Keystore.list().length).isEqualTo(2); + } + + /** + * Ensures that the { -a import, a, --account import } arguments fail when + * a non-private key is supplied. + */ + @Test + @Parameters({"-a import", "ai", "--account import"}) + public void testImportNonPrivateKey(String option) { + String fakePKey = Hex.toHexString("random".getBytes()); + + assertThat(mockCli.call(new String[] {option, fakePKey}, cfg)).isEqualTo(ERROR); + + assertThat(mockCli.call(new String[] {option, "hello"}, cfg)).isEqualTo(ERROR); + } + + // Methods below taken from FileUtils class + private static boolean copyRecursively(File src, File target) { + if (src.isDirectory()) { + return copyDirectoryContents(src, target); + } else { + try { + Files.copy(src, target); + return true; + } catch (IOException e) { + return false; + } + } + } + + private static boolean copyDirectoryContents(File src, File target) { + Preconditions.checkArgument(src.isDirectory(), "Source dir is not a directory: %s", src); + + // Don't delete symbolic link directories + if (isSymbolicLink(src)) { + return false; + } + + target.mkdirs(); + Preconditions.checkArgument(target.isDirectory(), "Target dir is not a directory: %s", src); + + boolean success = true; + for (File file : listFiles(src)) { + success = copyRecursively(file, new File(target, file.getName())) && success; + } + return success; + } + + private static boolean isSymbolicLink(File file) { + try { + File canonicalFile = file.getCanonicalFile(); + File absoluteFile = file.getAbsoluteFile(); + File parentFile = file.getParentFile(); + // a symbolic link has a different name between the canonical and absolute path + return !canonicalFile.getName().equals(absoluteFile.getName()) + || + // or the canonical parent path is not the same as the file's parent path, + // provided the file has a parent path + parentFile != null + && !parentFile.getCanonicalPath().equals(canonicalFile.getParent()); + } catch (IOException e) { + // error on the side of caution + return true; + } + } + + private static ImmutableList listFiles(File dir) { + File[] files = dir.listFiles(); + if (files == null) { + return ImmutableList.of(); + } + return ImmutableList.copyOf(files); + } + + private static boolean deleteRecursively(File file) { + Path path = file.toPath(); + try { + java.nio.file.Files.walkFileTree( + path, + new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile( + final Path file, final BasicFileAttributes attrs) + throws IOException { + java.nio.file.Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed( + final Path file, final IOException e) { + return handleException(e); + } + + private FileVisitResult handleException(final IOException e) { + // e.printStackTrace(); + return FileVisitResult.TERMINATE; + } + + @Override + public FileVisitResult postVisitDirectory( + final Path dir, final IOException e) throws IOException { + if (e != null) { + return handleException(e); + } + java.nio.file.Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + + return true; + } +} diff --git a/modAionImpl/test/org/aion/zero/impl/config/NetworkTest.java b/modAionImpl/test/org/aion/zero/impl/config/NetworkTest.java new file mode 100644 index 0000000000..c21dfd5de8 --- /dev/null +++ b/modAionImpl/test/org/aion/zero/impl/config/NetworkTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017-2018 Aion foundation. + * + * This file is part of the aion network project. + * + * The aion network project is free software: you can redistribute it + * and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or any later version. + * + * The aion network project is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the aion network project source files. + * If not, see . + * + * Contributors: + * Aion foundation. + */ +package org.aion.zero.impl.config; + +import static com.google.common.truth.Truth.assertThat; + +import java.util.ArrayList; +import java.util.List; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test functionality from {@link Network}. + * + * @author Alexandra Roatis + */ +@RunWith(JUnitParamsRunner.class) +public class NetworkTest { + + /** Parameters for testing {@link #testDetermineNetwork(String, Network)}. */ + @SuppressWarnings("unused") + private Object stringToNetworkMappings() { + List parameters = new ArrayList<>(); + + for (Network net : Network.values()) { + parameters.add(new Object[] {net.toString(), net}); + parameters.add(new Object[] {net.toString().toUpperCase(), net}); + } + + parameters.add(new Object[] {"testnet", Network.MASTERY}); + parameters.add(new Object[] {"TESTNET", Network.MASTERY}); + + parameters.add(new Object[] {"custom", Network.CUSTOM}); + parameters.add(new Object[] {"CUSTOM", Network.CUSTOM}); + parameters.add(new Object[] {"custom", Network.getCustomNet(1000)}); + + parameters.add(new Object[] {"undefined", null}); + + return parameters.toArray(); + } + + @Test + @Parameters(method = "stringToNetworkMappings") + public void testDetermineNetwork(String input, Network expected) { + assertThat(Network.determineNetwork(input)).isEqualTo(expected); + } + + /** Parameters for testing {@link #testDetermineNetwork(int, Network)}. */ + @SuppressWarnings("unused") + private Object intToNetworkMappings() { + List parameters = new ArrayList<>(); + + for (Network net : Network.values()) { + parameters.add(new Object[] {net.getChainId(), net}); + } + + parameters.add(new Object[] {-1, null}); + parameters.add(new Object[] {1000, Network.getCustomNet(1000)}); + + return parameters.toArray(); + } + + @Test + @Parameters(method = "intToNetworkMappings") + public void testDetermineNetwork(int input, Network expected) { + assertThat(Network.determineNetwork(input)).isEqualTo(expected); + } +} diff --git a/modAionImpl/test_resources/config.xml b/modAionImpl/test_resources/config.xml new file mode 100644 index 0000000000..bedb9e6e89 --- /dev/null +++ b/modAionImpl/test_resources/config.xml @@ -0,0 +1,100 @@ + + + aion + [NODE-ID-PLACEHOLDER] + + + + false + + web3,eth,personal,stratum,ops + + + + + 10E9 + + 100E9 + + false + + + + 256 + + p2p://c33d1066-8c7e-496c-9c4e-c89318280274@13.92.155.115:30303 + p2p://c33d2207-729a-4584-86f1-e19ab97cf9ce@51.144.42.220:30303 + p2p://c33d302f-216b-47d4-ac44-5d8181b56e7e@52.231.187.227:30303 + p2p://c33d4c07-6a29-4ca6-8b06-b2781ba7f9bf@191.232.164.119:30303 + p2p://c33d5a94-20d8-49d9-97d6-284f88da5c21@13.89.244.125:30303 + p2p://741b979e-6a06-493a-a1f2-693cafd37083@66.207.217.190:30303 + + + 0.0.0.0 + 30303 + false + 128 + + + + + 32 + + false + + + false + 0000000000000000000000000000000000000000000000000000000000000000 + 2 + AION + + + + + + + + + + database + + true + + + + + FULL + + + leveldb + + false + + + + true + + log + WARN + INFO + ERROR + INFO + INFO + ERROR + INFO + INFO + INFO + + + + + true + + aion.sh + + /placeholder/for/aion_root_dir + + + + + diff --git a/modAionImpl/test_resources/genesis.json b/modAionImpl/test_resources/genesis.json new file mode 100644 index 0000000000..7e25773645 --- /dev/null +++ b/modAionImpl/test_resources/genesis.json @@ -0,0 +1,19 @@ +{ + "alloc": { + "0xa0eeaeabdbc92953b072afbd21f3e3fd8a4a4f5e6a6e22200db746ab75e9a99a": { + "balance": "465934586660000000000000000" + } + }, + "networkBalanceAlloc": { + "0": { + "balance": "465934586660000000000000000" + } + }, + "energyLimit": "15000000", + "nonce": "0x00", + "difficulty": "0x4000", + "coinbase": "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp": "1524528000", + "parentHash": "0x6a6d99a2ef14ab3b835dfc92fb918d76c37f6578a69825fbe19cd366485604b1", + "chainId": "256" +} \ No newline at end of file diff --git a/modAionImpl/test_resources/testnet.json b/modAionImpl/test_resources/testnet.json new file mode 100755 index 0000000000..ff86d9ca7b --- /dev/null +++ b/modAionImpl/test_resources/testnet.json @@ -0,0 +1,52 @@ +{ + "alloc": { + "0xa0483412e8c8e769df037231d336e96f7f6d843cf7224c3d8fbe0ec7cdc12ac6": { + "balance": "314159000000000000000000" + }, + "0xa0353561f8b6b5a8b56d535647a4ddd7278e80c2494e3314d1f0605470f56925": { + "balance": "314159000000000000000000" + }, + "0xa0274c1858ca50576f4d4d18b719787b76bb454c33749172758a620555bf4586": { + "balance": "314159000000000000000000" + }, + "0xa06691a968e8fe22dc79b6dd503d44cb96d9a523ae265b63c6752389be90185d": { + "balance": "314159000000000000000000" + }, + "0xa0a95b372efe55c77a75364407f0403dfefd3131519ca980b2d92b1d2d7297a7": { + "balance": "314159000000000000000000" + }, + "0xa07262e1d026fca027e5f4f38668303b74a2bba8bd07470fa39d0f3b03f882f1": { + "balance": "314159000000000000000000" + }, + "0xa08f92c80e34c95b8b2e8cb208d47e3e6048b7e0ea44d866e33e865c365b1417": { + "balance": "314159000000000000000000" + }, + "0xa022045f22772463c94e08d1fd32f7b251d4c9cfd1c92e039f1517b906776283": { + "balance": "314159000000000000000000" + }, + "0xa04c282f636feff4d6b35174fc2d8e05c23df4b1d59508712712627184dd8a93": { + "balance": "314159000000000000000000" + }, + "0xa0f8654c63ae53598cf42435f53b1ebd9b7df6cbceba10af235aa2393f03034c": { + "balance": "314159000000000000000000" + }, + "0xa0f3defb01b531c5a28680eb928e79ea18bc155f1060e1d923d660d74883518b": { + "balance": "314159000000000000000000" + }, + "0xa02198c9192bb89e9b2e8536d96cbf287fde80625afcf1a5e5632e23c1260d61": { + "balance": "465934586660000000000000000" + } + }, + "networkBalanceAlloc": { + "0": { + "balance": "465934586660000000000000000" + } + }, + "energyLimit": "10000000", + "nonce": "0x00", + "difficulty": "0x10", + "coinbase": "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp": "1497536993", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "chainId": "2" +} diff --git a/modBoot/resource/conquest/genesis.json b/modBoot/resource/conquest/genesis.json index ff86d9ca7b..b2cff36d2a 100644 --- a/modBoot/resource/conquest/genesis.json +++ b/modBoot/resource/conquest/genesis.json @@ -1,52 +1,49 @@ { "alloc": { - "0xa0483412e8c8e769df037231d336e96f7f6d843cf7224c3d8fbe0ec7cdc12ac6": { - "balance": "314159000000000000000000" + "0xa025f4fd54064e869f158c1b4eb0ed34820f67e60ee80a53b469f725efc06378": { + "balance": "465934586660000000000000000" }, - "0xa0353561f8b6b5a8b56d535647a4ddd7278e80c2494e3314d1f0605470f56925": { + "0xa0e6a0c9c85db355fdceccc44444618b3213b3d5c3c3e26dcfe039ed07f310cd": { "balance": "314159000000000000000000" }, - "0xa0274c1858ca50576f4d4d18b719787b76bb454c33749172758a620555bf4586": { + "0xa08849e680dbede69077b3be7d9c8c37f5849c46cce63eafb26bd2083ce32a48": { "balance": "314159000000000000000000" }, - "0xa06691a968e8fe22dc79b6dd503d44cb96d9a523ae265b63c6752389be90185d": { + "0xa00d29449981629c92658eaedb6b383e7d98ac2933d4d0783f6d60313a826f46": { "balance": "314159000000000000000000" }, - "0xa0a95b372efe55c77a75364407f0403dfefd3131519ca980b2d92b1d2d7297a7": { + "0xa04be405fe794146df4a6a0cac0009933323d65e29dedfaf80a1f880fa8cd329": { "balance": "314159000000000000000000" }, - "0xa07262e1d026fca027e5f4f38668303b74a2bba8bd07470fa39d0f3b03f882f1": { + "0xa0dd7205acbaad446e7bd4e1755a9d1e8dd74b793656cc7af5876cba0f616bab": { "balance": "314159000000000000000000" }, - "0xa08f92c80e34c95b8b2e8cb208d47e3e6048b7e0ea44d866e33e865c365b1417": { + "0xa076b66cb825ca43aab11aa807ced2586023e6a62d8d600b0f3e16445a8d3ced": { "balance": "314159000000000000000000" }, - "0xa022045f22772463c94e08d1fd32f7b251d4c9cfd1c92e039f1517b906776283": { + "0xa0438499953bab8e92399de99e1edab602119d75dd0e90da256efd461b0cfe29": { "balance": "314159000000000000000000" }, - "0xa04c282f636feff4d6b35174fc2d8e05c23df4b1d59508712712627184dd8a93": { + "0xa05765005030df3d38502f6d3144a321631bd8663b5a87bc12d69a1330c9481a": { "balance": "314159000000000000000000" }, - "0xa0f8654c63ae53598cf42435f53b1ebd9b7df6cbceba10af235aa2393f03034c": { + "0xa046cc48bcde4b0b2ce2dbefb318f3778946b6c0011f691ecc4025cc145a93d3": { "balance": "314159000000000000000000" }, - "0xa0f3defb01b531c5a28680eb928e79ea18bc155f1060e1d923d660d74883518b": { + "0xa07c95cc8729a0503c5ad50eb37ec8a27cd22d65de3bb225982ec55201366920": { "balance": "314159000000000000000000" - }, - "0xa02198c9192bb89e9b2e8536d96cbf287fde80625afcf1a5e5632e23c1260d61": { - "balance": "465934586660000000000000000" } }, - "networkBalanceAlloc": { + "networkBalanceAllocs": { "0": { "balance": "465934586660000000000000000" } }, - "energyLimit": "10000000", + "energyLimit": "15000000", "nonce": "0x00", - "difficulty": "0x10", + "difficulty": "0x0010", "coinbase": "0x0000000000000000000000000000000000000000000000000000000000000000", - "timestamp": "1497536993", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "chainId": "2" + "timestamp": "1525924800", + "parentHash": "0x10E71BF64DCB8C60766CCB491AB6E3ACEC1AB07D0D5A088FCF533CBFBC801295", + "chainId": "128" } diff --git a/modBoot/resource/custom/config.xml b/modBoot/resource/custom/config.xml new file mode 100644 index 0000000000..645f43f1ec --- /dev/null +++ b/modBoot/resource/custom/config.xml @@ -0,0 +1,82 @@ + + + aion + [NODE-ID-PLACEHOLDER] + + + + false + + web3,eth,personal,stratum + + 1 + + + + + 10E9 + + 100E9 + + false + + + + 0 + + + + 0.0.0.0 + 30303 + false + 128 + + + + + 32 + + false + + + true + 0000000000000000000000000000000000000000000000000000000000000000 + 2 + AION + + + + + + + + + + database + + false + + + + + FULL + + + leveldb + + false + + + + true + + log + INFO + ERROR + INFO + INFO + ERROR + INFO + INFO + + diff --git a/modBoot/resource/custom/genesis.json b/modBoot/resource/custom/genesis.json new file mode 100644 index 0000000000..f8b0b2249f --- /dev/null +++ b/modBoot/resource/custom/genesis.json @@ -0,0 +1,19 @@ +{ + "alloc": { + "0x0000000000000000000000000000000000000000000000000000000000000000": { + "balance": "465934586660000000000000000" + } + }, + "networkBalanceAllocs": { + "0": { + "balance": "465934586660000000000000000" + } + }, + "energyLimit": "15000000", + "nonce": "0x00", + "difficulty": "0x0010", + "coinbase": "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp": "1525924800", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "chainId": "0" +} diff --git a/modBoot/resource/mastery/config.xml b/modBoot/resource/mastery/config.xml new file mode 100755 index 0000000000..1f1ec44ce6 --- /dev/null +++ b/modBoot/resource/mastery/config.xml @@ -0,0 +1,97 @@ + + + aion + [NODE-ID-PLACEHOLDER] + + + + false + + web3,eth,personal,stratum,ops + + + + + 10E9 + + 100E9 + + false + + + + 32 + + p2p://a30d1000-8c7e-496c-9c4e-c89318280274@168.62.170.146:30303 + p2p://a30d2000-729a-4584-86f1-e19ab97cf9ce@23.96.22.19:30303 + p2p://a30d3000-216b-47d4-ac44-5d8181b56e7e@137.117.56.84:30303 + + + 0.0.0.0 + 30303 + false + 128 + + + + + 32 + + false + + + true + a08fc457b39b03c30dc71bdb89a4d0409dd4fa42f6539a5c3ee4054af9b71f23 + 1 + AION + + + + + + + + + + database + + true + + + + + FULL + + + leveldb + + true + + + + true + + log + WARN + INFO + ERROR + INFO + INFO + ERROR + INFO + INFO + INFO + + + + + true + + aion.sh + + /placeholder/for/aion_root_dir + + + + + diff --git a/modBoot/resource/mastery/genesis.json b/modBoot/resource/mastery/genesis.json new file mode 100755 index 0000000000..a489e2661b --- /dev/null +++ b/modBoot/resource/mastery/genesis.json @@ -0,0 +1,19 @@ +{ + "alloc": { + "0xa0b88269779d225510ca880ed742e445db0c70efb1ee3159b6d56479ae3501f9": { + "balance": "314000000000000000000000000" + } + }, + "networkBalanceAllocs": { + "0": { + "balance": "465934586660000000000000000" + } + }, + "energyLimit": "15000000", + "nonce": "0x00", + "difficulty": "0x0008", + "coinbase": "0xa08fc457b39b03c30dc71bdb89a4d0409dd4fa42f6539a5c3ee4054af9b71f23", + "timestamp": "1525924800", + "parentHash": "0x10E71BF64DCB8C60766CCB491AB6E3ACEC1AB07D0D5A088FCF533CBFBC801295", + "chainId": "32" +} diff --git a/modBoot/src/org/aion/Aion.java b/modBoot/src/org/aion/Aion.java index bffeda2b8b..7281620beb 100644 --- a/modBoot/src/org/aion/Aion.java +++ b/modBoot/src/org/aion/Aion.java @@ -27,6 +27,7 @@ import static org.aion.crypto.ECKeyFac.ECKeyType.ED25519; import static org.aion.crypto.HashUtil.H256Type.BLAKE2B_256; import static org.aion.zero.impl.Version.KERNEL_VERSION; +import static org.aion.zero.impl.cli.Cli.ReturnType; import java.io.Console; import java.io.IOException; @@ -56,6 +57,7 @@ import org.aion.zero.impl.blockchain.IAionChain; import org.aion.zero.impl.cli.Cli; import org.aion.zero.impl.config.CfgAion; +import org.aion.zero.impl.config.Network; import org.slf4j.Logger; public class Aion { @@ -78,26 +80,12 @@ public static void main(String args[]) { */ ECKeyFac.setType(ED25519); HashUtil.setType(BLAKE2B_256); - ServiceLoader.load(EventMgrModule.class); CfgAion cfg = CfgAion.inst(); - /* - * if in the config.xml id is set as default [NODE-ID-PLACEHOLDER] - * return true which means should save back to xml config - */ - if (cfg.fromXML()) { - if(args != null && args.length > 0 && !(args[0].equals("-v")||args[0].equals("--version"))) { - cfg.toXML(new String[]{"--id=" + cfg.getId()}); - } - } - - // Reads CLI (must be after the cfg.fromXML()) - if (args != null && args.length > 0) { - int ret = new Cli().call(args, cfg); - if (ret != 2) { - exit(ret); - } + ReturnType ret = new Cli().call(args, cfg); + if (ret != ReturnType.RUN) { + exit(ret.getValue()); } // UUID check @@ -107,6 +95,8 @@ public static void main(String args[]) { exit(-1); } + ServiceLoader.load(EventMgrModule.class); + try { ServiceLoader.load(AionLoggerFactory.class); } catch (Exception e) { @@ -121,29 +111,31 @@ public static void main(String args[]) { // from now on, all logging to console and file happens asynchronously - /* - * Logger initialize with LOGFILE and LOGPATH (user config inputs) - */ - AionLoggerFactory - .init(cfg.getLog().getModules(), cfg.getLog().getLogFile(), cfg.getLog().getLogPath()); - Logger genLog = AionLoggerFactory.getLogger(LogEnum.GEN.name()); - String[] filePath = new String[7]; // Log/Database path if (!cfg.getLog().getLogFile()) { - System.out.println("Logger disabled; to enable please check log settings in config.xml"); + System.out.println("Logger disabled; to enable please update log settings in config.xml and restart kernel."); + filePath[0] = "« disabled »"; } else if (!cfg.getLog().isValidPath() && cfg.getLog().getLogFile()) { - System.out.println("File path is invalid; please check log setting in config.xml"); - return; + System.out.println("Logger disabled due to invalid file path; to enable please update log setting in config.xml and restart kernel."); + cfg.getLog().disableLogging(); + filePath[0] = "« disabled »"; } else if (cfg.getLog().isValidPath() && cfg.getLog().getLogFile()) { - filePath[0] = cfg.getBasePath() + "/" + cfg.getLog().getLogPath(); + filePath[0] = cfg.getLogPath(); } - filePath[1] = cfg.getBasePath() + "/" + cfg.getDb().getPath(); + + // Logger initialize with LOGFILE and LOGPATH (user config inputs) + AionLoggerFactory + .init(cfg.getLog().getModules(), cfg.getLog().getLogFile(), cfg.getLogPath()); + Logger genLog = AionLoggerFactory.getLogger(LogEnum.GEN.name()); + + + filePath[1] = cfg.getDatabasePath(); filePath[2] = Keystore.getKeystorePath(); - filePath[3] = new Cli().getDstConfig(); - filePath[4] = new Cli().getDstGenesis(); - filePath[5] = CfgAion.getConfFilePath(); - filePath[6] = CfgAion.getGenesisFilePath(); + filePath[3] = cfg.getExecConfigFile().getAbsolutePath(); + filePath[4] = cfg.getExecGenesisFile().getAbsolutePath(); + filePath[5] = cfg.getInitialConfigFile().getAbsolutePath(); + filePath[6] = cfg.getInitialGenesisFile().getAbsolutePath(); String path = "\n-------------------------------- USED PATHS --------------------------------" + @@ -165,10 +157,16 @@ public static void main(String args[]) { ".' `. | `._____.' | ``|\n\n"; // always print the version string in the center of the Aion logo - String versionStr = "v"+KERNEL_VERSION; - String networkStr = CfgAion.getNetwork(); + String versionStr = "v" + KERNEL_VERSION; + String networkStr = cfg.getNetwork(); + // if using old kernel configuration + if (networkStr == null && cfg.getNet().getId() >= 0) { + networkStr = Network.determineNetwork(cfg.getNet().getId()).toString(); + } logo = appendLogo(logo, versionStr); - logo = appendLogo(logo, networkStr); + if (networkStr != null) { + logo = appendLogo(logo, networkStr); + } genLog.info(path); genLog.info(logo); diff --git a/modMcf/src/org/aion/mcf/config/Cfg.java b/modMcf/src/org/aion/mcf/config/Cfg.java index 449db98757..93b400ed23 100644 --- a/modMcf/src/org/aion/mcf/config/Cfg.java +++ b/modMcf/src/org/aion/mcf/config/Cfg.java @@ -1,42 +1,36 @@ /* * Copyright (c) 2017-2018 Aion foundation. * - * This file is part of the aion network project. + * This file is part of the aion network project. * - * The aion network project is free software: you can redistribute it - * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or any later version. + * The aion network project is free software: you can redistribute it + * and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or any later version. * - * The aion network project is distributed in the hope that it will - * be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. + * The aion network project is distributed in the hope that it will + * be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with the aion network project source files. - * If not, see . - * - * Contributors to the aion source files in decreasing order of code volume: - * - * Aion foundation. + * You should have received a copy of the GNU General Public License + * along with the aion network project source files. + * If not, see . * + * Contributors: + * Aion foundation. */ - package org.aion.mcf.config; -import org.aion.mcf.types.AbstractBlock; - +import com.google.common.annotations.VisibleForTesting; +import java.io.File; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; +import org.aion.mcf.types.AbstractBlock; -/** - * @author chris - */ +/** @author chris */ public abstract class Cfg { - protected static final String BASE_PATH = System.getProperty("user.dir"); - protected String mode; protected String id; @@ -59,7 +53,7 @@ public abstract class Cfg { protected CfgGui gui; - public void setId(final String _id){ + public void setId(final String _id) { this.id = _id; } @@ -127,7 +121,6 @@ public String[] getNodes() { return this.net.getNodes(); } - public CfgConsensus getConsensus() { return this.consensus; } @@ -136,11 +129,252 @@ public void setConsensus(CfgConsensus _consensus) { this.consensus = _consensus; } + /* ------------ execution path management ------------ */ + + // names + private final String configDirName = "config"; + private final String configFileName = "config.xml"; + private final String genesisFileName = "genesis.json"; + private final String keystoreDirName = "keystore"; + + // base path + private final File INITIAL_PATH = new File(System.getProperty("user.dir")); + + // directories containing the configuration files + private final File CONFIG_DIR = new File(INITIAL_PATH, configDirName); + private File networkConfigDir = null; + + // base configuration: old kernel OR using network config + private File baseConfigFile = null; + private File baseGenesisFile = null; + + // can be absolute in config file OR depend on execution path + private File logDir = null; + private File databaseDir = null; + private boolean absoluteLogDir = false; + private boolean absoluteDatabaseDir = false; + + // impact execution path + private String network = null; + private File dataDir = null; + + /** Data directory with network. */ + private File execDir = null; + + private File execConfigDir = null; + private File execConfigFile = null; + private File execGenesisFile = null; + + /** Resets internal data containing network and path. */ + @VisibleForTesting + public void resetInternal() { + networkConfigDir = null; + baseConfigFile = null; + baseGenesisFile = null; + logDir = null; + databaseDir = null; + absoluteLogDir = false; + absoluteDatabaseDir = false; + network = null; + dataDir = null; + execDir = null; + execConfigDir = null; + execConfigFile = null; + execGenesisFile = null; + } + + /** + * Determines the location of the initial configuration files ensuring compatibility with old + * kernels. + */ + protected void initializeConfiguration() { + // use old config location for compatibility with old kernels + baseConfigFile = new File(CONFIG_DIR, configFileName); + baseGenesisFile = new File(CONFIG_DIR, genesisFileName); + + if (!baseConfigFile.exists() || !baseGenesisFile.exists()) { + updateNetworkExecPaths(); + } else { + execDir = INITIAL_PATH; + execConfigDir = CONFIG_DIR; + execConfigFile = baseConfigFile; + execGenesisFile = baseGenesisFile; + updateStoragePaths(); + } + } + + /** + * Updates the base configuration and execution paths as is defined by new kernels where + * the configuration is placed in folders for each network type. + */ + private void updateNetworkExecPaths() { + if (network == null) { + network = "mainnet"; + } + networkConfigDir = new File(CONFIG_DIR, network); + baseConfigFile = new File(networkConfigDir, configFileName); + baseGenesisFile = new File(networkConfigDir, genesisFileName); + + if (dataDir == null) { + dataDir = INITIAL_PATH; + } + execDir = new File(dataDir, network); + execConfigDir = new File(execDir, configDirName); + execConfigFile = new File(execConfigDir, configFileName); + execGenesisFile = new File(execConfigDir, genesisFileName); + + updateStoragePaths(); + } + + /** Updates the path to the log, database directories. */ + private void updateStoragePaths() { + if (!absoluteLogDir) { + logDir = new File(execDir, getLog().getLogPath()); + } else if (logDir == null) { + logDir = new File(getLog().getLogPath()); + } + if (!absoluteDatabaseDir) { + databaseDir = new File(execDir, getDb().getPath()); + } else if (databaseDir == null) { + databaseDir = new File(getDb().getPath()); + } + } + + /** + * Sets the directory where the kernel data containing setup and execution information will be + * stored. + * + * @param _dataDir the directory chosen for execution + * @implNote Using this method overwrites the use of old kernel setup. + */ + public void setDataDirectory(File _dataDir) { + this.dataDir = _dataDir; + updateNetworkExecPaths(); + } + /** - * @return the base dir where all configuration + persistance is managed + * Sets the network to be used by the kernel. + * + * @param _network the network chosen for execution + * @implNote Using this method overwrites the use of old kernel setup. */ + public void setNetwork(String _network) { + this.network = _network; + updateNetworkExecPaths(); + } + + /** @return the base dir where all configuration + persistence is managed */ public String getBasePath() { - return BASE_PATH; + return getExecDir().getAbsolutePath(); + } + + /** Returns the directory location where the kernel configuration and persistence is managed. */ + public File getExecDir() { + if (execDir == null) { + initializeConfiguration(); + } + return execDir; + } + + public String getNetwork() { + return network; + } + + public String getLogPath() { + return getLogDir().getAbsolutePath(); + } + + public File getLogDir() { + if (logDir == null) { + // was not updated with absolute path + logDir = new File(getExecDir(), getLog().getLogPath()); + } + return logDir; + } + + /** + * Used to set an absolute path for the log directory. + * + * @param _logDirectory the path to be used for logging. + */ + public void setLogDir(File _logDirectory) { + this.logDir = _logDirectory; + this.absoluteLogDir = true; + } + + public String getDatabasePath() { + return getDatabaseDir().getAbsolutePath(); + } + + public File getDatabaseDir() { + if (databaseDir == null) { + // was not updated with absolute path + databaseDir = new File(getExecDir(), getDb().getPath()); + } + return databaseDir; + } + + /** + * Used to set an absolute path for the database directory. + * + * @param _databaseDirectory the path to be used for storing the database. + */ + public void setDatabaseDir(File _databaseDirectory) { + this.databaseDir = _databaseDirectory; + this.absoluteDatabaseDir = true; + } + + public File getKeystoreDir() { + return new File(getExecDir(), keystoreDirName); + } + + /** Returns the configuration directory location for the kernel execution. */ + public File getExecConfigDirectory() { + if (execConfigDir == null) { + initializeConfiguration(); + } + return execConfigDir; + } + + /** Returns the location where the config file is saved for kernel execution. */ + public File getExecConfigFile() { + if (execConfigFile == null) { + initializeConfiguration(); + } + return execConfigFile; + } + + /** Returns the location where the genesis file is saved for kernel execution. */ + public File getExecGenesisFile() { + if (execGenesisFile == null) { + initializeConfiguration(); + } + return execGenesisFile; + } + + /** @implNote Maintains the old setup if the config file is present in the old location. */ + public File getInitialConfigFile() { + if (baseConfigFile == null) { + initializeConfiguration(); + } + return baseConfigFile; + } + + /** + * Used to updated the initial configuration to using the execution configuration files when + * reading the initial configuration from those files. + */ + public void setReadConfigFiles(File configFile, File genesisFile) { + this.baseConfigFile = configFile; + this.baseGenesisFile = genesisFile; + } + + /** @implNote Maintains the old setup if the genesis file is present in the old location. */ + public File getInitialGenesisFile() { + if (baseGenesisFile == null) { + initializeConfiguration(); + } + return baseGenesisFile; } public static String readValue(final XMLStreamReader sr) throws XMLStreamException { @@ -149,11 +383,11 @@ public static String readValue(final XMLStreamReader sr) throws XMLStreamExcepti while (sr.hasNext()) { int eventType = sr.next(); switch (eventType) { - case XMLStreamReader.CHARACTERS: - str.append(sr.getText()); - break; - case XMLStreamReader.END_ELEMENT: - break readLoop; + case XMLStreamReader.CHARACTERS: + str.append(sr.getText()); + break; + case XMLStreamReader.END_ELEMENT: + break readLoop; } } return str.toString(); @@ -164,23 +398,37 @@ public static void skipElement(final XMLStreamReader sr) throws XMLStreamExcepti while (sr.hasNext()) { int eventType = sr.next(); switch (eventType) { - case XMLStreamReader.END_ELEMENT: - break skipLoop; + case XMLStreamReader.END_ELEMENT: + break skipLoop; } } } /** - * @return boolean - * use return to determine if also need to write back - * to file with current config + * Loads the configuration from the default config file. Returns a boolean value used to + * determine if the configuration needs to be saved back to disk with a valid peer identifier. + * + * @return {@code true} when the peer id read from the file is [NODE-ID-PLACEHOLDER] which needs + * to be replaced by a valid user ID on disk, {@code false} otherwise. */ public abstract boolean fromXML(); + /** + * Loads the configuration from the given config file. + * + * Returns a boolean value used to determine if the configuration needs to be saved back to + * disk with a valid peer identifier. + * + * @return {@code true} when the peer id read from the file is [NODE-ID-PLACEHOLDER] which needs + * to be replaced by a valid user ID on disk, {@code false} otherwise. + */ + public abstract boolean fromXML(File configFile); + public abstract void toXML(final String[] args); + public abstract void toXML(final String[] args, File file); + public abstract void setGenesis(); public abstract AbstractBlock, ?> getGenesis(); - -} \ No newline at end of file +} diff --git a/modMcf/src/org/aion/mcf/config/CfgLog.java b/modMcf/src/org/aion/mcf/config/CfgLog.java index 94be49f547..8fe2ef71f1 100644 --- a/modMcf/src/org/aion/mcf/config/CfgLog.java +++ b/modMcf/src/org/aion/mcf/config/CfgLog.java @@ -158,6 +158,11 @@ public boolean getLogFile() { return this.logFile; } + /** Used to turn off logging in case of incorrect configuration. */ + public void disableLogging() { + this.logFile = false; + } + /** Method returns user input folder path of logger */ public String getLogPath() { return logPath;
Returns a boolean value used to determine if the configuration needs to be saved back to + * disk with a valid peer identifier. + * + * @return {@code true} when the peer id read from the file is [NODE-ID-PLACEHOLDER] which needs + * to be replaced by a valid user ID on disk, {@code false} otherwise. + */ + public abstract boolean fromXML(File configFile); + public abstract void toXML(final String[] args); + public abstract void toXML(final String[] args, File file); + public abstract void setGenesis(); public abstract AbstractBlock, ?> getGenesis(); - -} \ No newline at end of file +} diff --git a/modMcf/src/org/aion/mcf/config/CfgLog.java b/modMcf/src/org/aion/mcf/config/CfgLog.java index 94be49f547..8fe2ef71f1 100644 --- a/modMcf/src/org/aion/mcf/config/CfgLog.java +++ b/modMcf/src/org/aion/mcf/config/CfgLog.java @@ -158,6 +158,11 @@ public boolean getLogFile() { return this.logFile; } + /** Used to turn off logging in case of incorrect configuration. */ + public void disableLogging() { + this.logFile = false; + } + /** Method returns user input folder path of logger */ public String getLogPath() { return logPath;