diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index eeb92fdbd60..311a7b40ba8 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -176,12 +176,6 @@ public class CommonParameter { @Setter public boolean solidityNode = false; - // If you are running KeystoreFactory, - // this flag is set to true - @Getter - @Setter - public boolean keystoreFactory = false; - // -- RPC / HTTP -- @Getter @Setter diff --git a/crypto/src/main/java/org/tron/keystore/WalletUtils.java b/crypto/src/main/java/org/tron/keystore/WalletUtils.java index 2ce100823d9..ff9f4dc88ee 100644 --- a/crypto/src/main/java/org/tron/keystore/WalletUtils.java +++ b/crypto/src/main/java/org/tron/keystore/WalletUtils.java @@ -201,13 +201,10 @@ public static boolean passwordValid(String password) { /** * Lazily-initialized Scanner shared across successive - * {@link #inputPassword()} calls on the non-TTY path so that - * {@link #inputPassword2Twice()} can read two lines in sequence - * without losing data. Each call to {@code new Scanner(System.in)} - * internally buffers bytes from the underlying {@link BufferedReader}; - * constructing a second Scanner after the first has been discarded - * drops any buffered bytes the first pulled from stdin, causing - * {@code NoSuchElementException}. + * {@link #inputPassword()} calls on the non-TTY path. Each call to + * {@code new Scanner(System.in)} buffers ahead from stdin; constructing + * a second Scanner after the first has been discarded drops any bytes + * the first pulled, causing {@code NoSuchElementException}. */ private static Scanner sharedStdinScanner; @@ -250,18 +247,4 @@ public static String inputPassword() { } } - public static String inputPassword2Twice() { - String password0; - while (true) { - System.out.println("Please input password."); - password0 = inputPassword(); - System.out.println("Please input password again."); - String password1 = inputPassword(); - if (password0.equals(password1)) { - break; - } - System.out.println("Two passwords do not match, please input again."); - } - return password0; - } } diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 0bca242606e..27107c66559 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -159,6 +159,20 @@ public static void setParam(final String[] args, final String confFileName) { Args.printHelp(jc); exit(0); } + // Check assignment, not the field value: JCommander toggles arity-0 booleans + // per occurrence, so a repeated flag parses back to false. + boolean keystoreFactoryPassed = jc.getParameters().stream() + .filter(pd -> "--keystore-factory".equals(pd.getLongestName())) + .anyMatch(ParameterDescription::isAssigned); + if (keystoreFactoryPassed) { + // stderr, not logger: the default logback config has no console appender + System.err.println("--keystore-factory was removed."); + System.err.println("Use: java -jar Toolkit.jar keystore "); + System.err.println("SM2 nodes (crypto.engine = 'sm2'): append --sm2 to commands that create " + + "or modify a keystore."); + throw new TronError("--keystore-factory was removed; use Toolkit.jar keystore", + TronError.ErrCode.PARAMETER_INIT); + } // Resolve config file path configFilePath = StringUtils.isNoneBlank(cmd.shellConfFileName) @@ -858,9 +872,6 @@ private static void applyCLIParams(CLIParameter cmd, JCommander jc) { if (assigned.contains("--solidity")) { PARAMETER.solidityNode = cmd.solidityNode; } - if (assigned.contains("--keystore-factory")) { - PARAMETER.keystoreFactory = cmd.keystoreFactory; - } if (assigned.contains("--rpc-thread")) { PARAMETER.rpcThreadNum = cmd.rpcThreadNum; } @@ -1292,7 +1303,7 @@ private static String getCommitIdAbbrev() { private static Map getOptionGroup() { String[] tronOption = new String[] {"version", "help", "shellConfFileName", "logbackPath", - "eventSubscribe", "solidityNode", "keystoreFactory"}; + "eventSubscribe", "solidityNode"}; String[] dbOption = new String[] {"outputDirectory"}; String[] witnessOption = new String[] {"witness", "privateKey"}; String[] vmOption = new String[] {"debug"}; diff --git a/framework/src/main/java/org/tron/core/config/args/CLIParameter.java b/framework/src/main/java/org/tron/core/config/args/CLIParameter.java index 4f056a32e3a..842724d45d0 100644 --- a/framework/src/main/java/org/tron/core/config/args/CLIParameter.java +++ b/framework/src/main/java/org/tron/core/config/args/CLIParameter.java @@ -50,7 +50,12 @@ public class CLIParameter { @Parameter(names = {"--solidity"}, description = "running a solidity node for java tron") public boolean solidityNode; - @Parameter(names = {"--keystore-factory"}, description = "running KeystoreFactory") + /** + * Tombstone for the removed --keystore-factory: Args.setParam exits with migration + * guidance. Keep declared — undeclared, JCommander parses the flag into seedNodes. + */ + @Deprecated + @Parameter(names = {"--keystore-factory"}, description = "removed; use Toolkit.jar keystore") public boolean keystoreFactory; @Deprecated diff --git a/framework/src/main/java/org/tron/core/config/args/WitnessInitializer.java b/framework/src/main/java/org/tron/core/config/args/WitnessInitializer.java index c2ce2ba0046..a143982c061 100644 --- a/framework/src/main/java/org/tron/core/config/args/WitnessInitializer.java +++ b/framework/src/main/java/org/tron/core/config/args/WitnessInitializer.java @@ -97,8 +97,8 @@ public static LocalWitnesses initFromKeystore( "Tip: keystores created via `FullNode.jar --keystore-factory` in " + "non-TTY mode were encrypted with only the first " + "whitespace-separated word of the password. Try restarting " - + "with only that first word as `-p`, then reset the password " - + "via `java -jar Toolkit.jar keystore update`."); + + "with only that first word as `--password`, then reset the " + + "password via `java -jar Toolkit.jar keystore update`."); } throw new TronError(e, TronError.ErrCode.WITNESS_KEYSTORE_LOAD); } diff --git a/framework/src/main/java/org/tron/program/FullNode.java b/framework/src/main/java/org/tron/program/FullNode.java index 96b9f73d577..46c525fd476 100644 --- a/framework/src/main/java/org/tron/program/FullNode.java +++ b/framework/src/main/java/org/tron/program/FullNode.java @@ -29,10 +29,6 @@ public static void main(String[] args) { LogService.load(parameter.getLogbackPath()); - if (parameter.isKeystoreFactory()) { - KeystoreFactory.start(); - return; - } if (parameter.isSolidityNode()) { logger.info("Solidity node is running."); if (StringUtils.isEmpty(parameter.getTrustNodeAddr())) { diff --git a/framework/src/main/java/org/tron/program/KeystoreFactory.java b/framework/src/main/java/org/tron/program/KeystoreFactory.java deleted file mode 100755 index f4e26afa145..00000000000 --- a/framework/src/main/java/org/tron/program/KeystoreFactory.java +++ /dev/null @@ -1,162 +0,0 @@ -package org.tron.program; - -import java.io.File; -import java.io.IOException; -import java.util.Locale; -import java.util.Scanner; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.tron.common.crypto.SignInterface; -import org.tron.common.crypto.SignUtils; -import org.tron.common.parameter.CommonParameter; -import org.tron.common.utils.ByteArray; -import org.tron.common.utils.Utils; -import org.tron.core.exception.CipherException; -import org.tron.keystore.Credentials; -import org.tron.keystore.WalletUtils; - -@Slf4j(topic = "app") -@Deprecated -public class KeystoreFactory { - - private static final String FilePath = "Wallet"; - - public static void start() { - System.err.println("WARNING: --keystore-factory is deprecated and will be removed " - + "in a future release."); - System.err.println("Please use: java -jar Toolkit.jar keystore "); - System.err.println(" keystore new - Generate a new keystore"); - System.err.println(" keystore import - Import a private key"); - System.err.println(" keystore list - List keystores"); - System.err.println(" keystore update - Change password"); - System.err.println(); - KeystoreFactory cli = new KeystoreFactory(); - cli.run(); - } - - private boolean priKeyValid(String priKey) { - if (StringUtils.isEmpty(priKey)) { - logger.warn("Warning: PrivateKey is empty!"); - return false; - } - if (priKey.length() != 64) { - logger.warn("Warning: PrivateKey length needs to be 64, but " + priKey.length() + "!"); - return false; - } - //Other rule; - return true; - } - - private void fileCheck(File file) throws IOException { - if (!file.exists()) { - if (!file.mkdir()) { - throw new IOException("Creating directory failed!"); - } - } else { - if (!file.isDirectory()) { - if (file.delete()) { - if (!file.mkdir()) { - throw new IOException("Creating directory failed!"); - } - } else { - throw new IOException("File is already existed and can not be deleted!"); - } - } - } - } - - - private void genKeystore() throws CipherException, IOException { - boolean ecKey = CommonParameter.getInstance().isECKeyCryptoEngine(); - String password = WalletUtils.inputPassword2Twice(); - - SignInterface eCkey = SignUtils.getGeneratedRandomSign(Utils.random, ecKey); - File file = new File(FilePath); - fileCheck(file); - String fileName = WalletUtils.generateWalletFile(password, eCkey, file, true); - System.out.println("Gen a keystore its name " + fileName); - Credentials credentials = WalletUtils.loadCredentials(password, new File(file, fileName), - ecKey); - System.out.println("Your address is " + credentials.getAddress()); - } - - private void importPrivateKey() throws CipherException, IOException { - Scanner in = new Scanner(System.in); - String privateKey; - System.out.println("Please input private key."); - while (true) { - String input = in.nextLine().trim(); - privateKey = input.split("\\s+")[0]; - if (priKeyValid(privateKey)) { - break; - } - System.out.println("Invalid private key, please input again."); - } - - String password = WalletUtils.inputPassword2Twice(); - - boolean ecKey = CommonParameter.getInstance().isECKeyCryptoEngine(); - SignInterface eCkey = SignUtils.fromPrivate(ByteArray.fromHexString(privateKey), ecKey); - File file = new File(FilePath); - fileCheck(file); - String fileName = WalletUtils.generateWalletFile(password, eCkey, file, true); - System.out.println("Gen a keystore its name " + fileName); - Credentials credentials = WalletUtils.loadCredentials(password, new File(file, fileName), - ecKey); - System.out.println("Your address is " + credentials.getAddress()); - } - - private void help() { - System.out.println("NOTE: --keystore-factory is deprecated. Use Toolkit.jar instead:"); - System.out.println(" java -jar Toolkit.jar keystore new|import|list|update"); - System.out.println(); - System.out.println("Legacy commands (will be removed):"); - System.out.println(" GenKeystore"); - System.out.println(" ImportPrivateKey"); - System.out.println(" Exit or Quit"); - } - - private void run() { - Scanner in = new Scanner(System.in); - help(); - while (in.hasNextLine()) { - try { - String cmdLine = in.nextLine().trim(); - String[] cmdArray = cmdLine.split("\\s+"); - // split on trim() string will always return at the minimum: [""] - String cmd = cmdArray[0]; - if ("".equals(cmd)) { - continue; - } - String cmdLowerCase = cmd.toLowerCase(Locale.ROOT); - - switch (cmdLowerCase) { - case "help": { - help(); - break; - } - case "genkeystore": { - genKeystore(); - break; - } - case "importprivatekey": { - importPrivateKey(); - break; - } - case "exit": - case "quit": { - System.out.println("Exit !!!"); - in.close(); - return; - } - default: { - System.out.println("Invalid cmd: " + cmd); - help(); - } - } - } catch (Exception e) { - logger.error(e.getMessage()); - } - } - } -} \ No newline at end of file diff --git a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java index 36b8a3269c1..68314a90f93 100644 --- a/framework/src/test/java/org/tron/core/config/args/ArgsTest.java +++ b/framework/src/test/java/org/tron/core/config/args/ArgsTest.java @@ -20,6 +20,8 @@ import com.typesafe.config.ConfigFactory; import io.grpc.internal.GrpcUtil; import io.grpc.netty.NettyServerBuilder; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetAddress; @@ -27,6 +29,7 @@ import java.util.HashMap; import java.util.Map; import lombok.extern.slf4j.Slf4j; +import org.junit.After; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -48,9 +51,47 @@ public class ArgsTest { @Rule public ExpectedException thrown = ExpectedException.none(); + @After + public void tearDown() { + Args.clearParam(); + } + + @Test + public void testRemovedKeystoreFactoryExitsWithMigrationGuidance() throws Exception { + ByteArrayOutputStream errorOutput = new ByteArrayOutputStream(); + PrintStream capturedError = new PrintStream(errorOutput, true, "UTF-8"); + PrintStream originalError = System.err; + + try { + System.setErr(capturedError); + + TronError exception = Assert.assertThrows(TronError.class, + () -> Args.setParam(new String[] {"--keystore-factory"}, TestConstants.TEST_CONF)); + + Assert.assertEquals(TronError.ErrCode.PARAMETER_INIT, exception.getErrCode()); + } finally { + System.setErr(originalError); + capturedError.close(); + } + + String errorMessage = errorOutput.toString("UTF-8"); + Assert.assertTrue(errorMessage.contains("--keystore-factory was removed.")); + Assert.assertTrue(errorMessage.contains("Toolkit.jar keystore ")); + Assert.assertTrue(errorMessage.contains( + "SM2 nodes (crypto.engine = 'sm2'): append --sm2 to commands that create or modify " + + "a keystore.")); + } + + @Test + public void testRemovedKeystoreFactoryRepeatedFlagStillExits() { + Assert.assertThrows(TronError.class, + () -> Args.setParam(new String[] {"--keystore-factory", "--keystore-factory"}, + TestConstants.TEST_CONF)); + } + @Test public void get() { - Args.setParam(new String[] {"--keystore-factory"}, TestConstants.TEST_CONF); + Args.setParam(new String[] {}, TestConstants.TEST_CONF); CommonParameter parameter = Args.getInstance(); @@ -122,8 +163,6 @@ public void get() { Assert.assertEquals(address, ByteArray.toHexString(Args.getLocalWitnesses() .getWitnessAccountAddress())); - - Assert.assertTrue(parameter.isKeystoreFactory()); } @Test diff --git a/framework/src/test/java/org/tron/keystore/WalletUtilsInputPasswordTest.java b/framework/src/test/java/org/tron/keystore/WalletUtilsInputPasswordTest.java index 64752b9ca49..ed1bc32b974 100644 --- a/framework/src/test/java/org/tron/keystore/WalletUtilsInputPasswordTest.java +++ b/framework/src/test/java/org/tron/keystore/WalletUtilsInputPasswordTest.java @@ -95,23 +95,6 @@ public void testInputPasswordPreservesLeadingAndTrailingSpaces() { " with spaces ", pw); } - @Test(timeout = 10000) - public void testInputPassword2TwicePipedPreservesInternalWhitespace() { - // M1: verifies the double-read path (inputPassword2Twice → inputPassword() - // called twice) works correctly when both lines arrive on the same - // piped stdin. Guards against regressions from Scanner lifecycle issues - // where a newly-constructed Scanner could miss bytes buffered by an - // earlier Scanner on the same InputStream. - System.setIn(new ByteArrayInputStream( - ("correct horse battery staple\n" - + "correct horse battery staple\n").getBytes(StandardCharsets.UTF_8))); - - String pw = WalletUtils.inputPassword2Twice(); - - assertEquals("Full passphrase must survive the double-read path", - "correct horse battery staple", pw); - } - // ---------- stripPasswordLine() direct unit tests (M3) ---------- @Test diff --git a/framework/src/test/java/org/tron/program/KeystoreFactoryDeprecationTest.java b/framework/src/test/java/org/tron/program/KeystoreFactoryDeprecationTest.java deleted file mode 100644 index 860980d21e5..00000000000 --- a/framework/src/test/java/org/tron/program/KeystoreFactoryDeprecationTest.java +++ /dev/null @@ -1,147 +0,0 @@ -package org.tron.program; - -import static org.junit.Assert.assertTrue; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.InputStream; -import java.io.PrintStream; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.tron.common.TestConstants; -import org.tron.core.config.args.Args; - -/** - * Verifies the deprecated --keystore-factory CLI. - */ -public class KeystoreFactoryDeprecationTest { - - private PrintStream originalOut; - private PrintStream originalErr; - private InputStream originalIn; - - @Before - public void setup() { - originalOut = System.out; - originalErr = System.err; - originalIn = System.in; - Args.setParam(new String[] {}, TestConstants.TEST_CONF); - } - - @After - public void teardown() throws Exception { - System.setOut(originalOut); - System.setErr(originalErr); - System.setIn(originalIn); - Args.clearParam(); - // Clean up Wallet dir - File wallet = new File("Wallet"); - if (wallet.exists()) { - if (wallet.isDirectory() && wallet.listFiles() != null) { - for (File f : wallet.listFiles()) { - f.delete(); - } - } - wallet.delete(); - } - } - - @Test(timeout = 10000) - public void testDeprecationWarningPrinted() throws Exception { - ByteArrayOutputStream errContent = new ByteArrayOutputStream(); - System.setErr(new PrintStream(errContent)); - System.setIn(new ByteArrayInputStream("exit\n".getBytes())); - - KeystoreFactory.start(); - - String errOutput = errContent.toString("UTF-8"); - assertTrue("Should contain deprecation warning", - errOutput.contains("--keystore-factory is deprecated")); - assertTrue("Should point to Toolkit.jar", - errOutput.contains("Toolkit.jar keystore")); - } - - @Test(timeout = 10000) - public void testHelpCommand() throws Exception { - ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outContent)); - System.setIn(new ByteArrayInputStream("help\nexit\n".getBytes())); - - KeystoreFactory.start(); - - String out = outContent.toString("UTF-8"); - assertTrue("Should show legacy commands", out.contains("GenKeystore")); - assertTrue("Should show ImportPrivateKey", out.contains("ImportPrivateKey")); - } - - @Test(timeout = 10000) - public void testInvalidCommand() throws Exception { - ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outContent)); - System.setIn(new ByteArrayInputStream("badcommand\nexit\n".getBytes())); - - KeystoreFactory.start(); - - String out = outContent.toString("UTF-8"); - assertTrue("Should report invalid cmd", - out.contains("Invalid cmd: badcommand")); - } - - @Test(timeout = 10000) - public void testEmptyLineSkipped() throws Exception { - ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outContent)); - System.setIn(new ByteArrayInputStream("\n\nexit\n".getBytes())); - - KeystoreFactory.start(); - - String out = outContent.toString("UTF-8"); - assertTrue("Should exit cleanly", out.contains("Exit")); - } - - @Test(timeout = 10000) - public void testQuitCommand() throws Exception { - ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outContent)); - System.setIn(new ByteArrayInputStream("quit\n".getBytes())); - - KeystoreFactory.start(); - - String out = outContent.toString("UTF-8"); - assertTrue("Quit should terminate", out.contains("Exit")); - } - - @Test(timeout = 10000) - public void testGenKeystoreTriggersError() throws Exception { - // genkeystore reads password via a nested Scanner, which conflicts - // with the outer Scanner and throws "No line found". The error is - // caught and logged, and the REPL continues. - ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outContent)); - System.setIn(new ByteArrayInputStream("genkeystore\nexit\n".getBytes())); - - KeystoreFactory.start(); - - String out = outContent.toString("UTF-8"); - assertTrue("genKeystore should prompt for password", - out.contains("Please input password")); - assertTrue("REPL should continue to exit", out.contains("Exit")); - } - - @Test(timeout = 10000) - public void testImportPrivateKeyTriggersPrompt() throws Exception { - // importprivatekey reads via nested Scanner — same limitation as above, - // but we at least hit the dispatch logic. - ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outContent)); - System.setIn(new ByteArrayInputStream("importprivatekey\nexit\n".getBytes())); - - KeystoreFactory.start(); - - String out = outContent.toString("UTF-8"); - assertTrue("importprivatekey should prompt for key", - out.contains("Please input private key")); - } -} diff --git a/plugins/README.md b/plugins/README.md index f14e070c01a..47a83472747 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -152,9 +152,10 @@ NOTE: large db may GC overhead limit exceeded. Keystore provides commands for managing account keystore files (Web3 Secret Storage format). -> **Migrating from `--keystore-factory`**: The legacy `FullNode.jar --keystore-factory` interactive mode is deprecated. Use the Toolkit keystore commands below instead. The mapping is: +> **Migrating from `--keystore-factory`**: The legacy `FullNode.jar --keystore-factory` interactive mode has been removed. Use the Toolkit keystore commands below instead. The mapping is: > - `GenKeystore` → `keystore new` > - `ImportPrivateKey` → `keystore import` +> - SM2 nodes (`crypto.engine = 'sm2'`): append `--sm2` — the legacy mode followed the node config, while Toolkit defaults to ECDSA > - (new) `keystore list` — list all keystores in a directory > - (new) `keystore update` — change the password of a keystore @@ -219,6 +220,6 @@ When using `--password-file` with `update`, the file must contain exactly two li - `--password-file`: Read password from a file instead of interactive prompt. For `keystore update`, the file must contain exactly two lines (current password, then new password). - `--key-file`: Read the private key (hex, with or without `0x` prefix) from a file instead of the interactive prompt (`keystore import` only). - `--force`: For `keystore import`, allow importing a private key whose address already has a keystore in the directory (creates an additional file). -- `--sm2`: Use SM2 algorithm instead of ECDSA (for `new` and `import`). +- `--sm2`: Use SM2 algorithm instead of ECDSA (for `new`, `import` and `update`). - `--json`: Output in JSON format for scripting. - `-h | --help`: Provide the help info.