diff --git a/build.gradle b/build.gradle index b54ce8c2..39864672 100644 --- a/build.gradle +++ b/build.gradle @@ -88,7 +88,9 @@ dependencies { implementation group: 'org.jline', name: 'jline', version: '3.25.0' implementation group: 'io.github.tronprotocol', name: 'zksnark-java-sdk', version: '1.0.0' - implementation group: 'org.web3j', name: 'crypto', version: '4.9.8' + implementation(group: 'org.web3j', name: 'crypto', version: '4.9.8-hotfix') { + exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on' + } implementation group: 'org.hid4java', name: 'hid4java', version: '0.8.0' //implementation 'javax.annotation:javax.annotation-api:1.3.2' } diff --git a/src/main/java/org/tron/common/utils/Utils.java b/src/main/java/org/tron/common/utils/Utils.java index b70124bf..64a2243a 100644 --- a/src/main/java/org/tron/common/utils/Utils.java +++ b/src/main/java/org/tron/common/utils/Utils.java @@ -287,6 +287,28 @@ public static char[] inputPassword(boolean checkStrength) throws IOException { } } + public static char[] inputPasswordWithoutCheck() throws IOException { + char[] password; + Console cons = System.console(); + if (cons != null) { + password = cons.readPassword("password: "); + } else { + byte[] passwd0 = new byte[64]; + int len = System.in.read(passwd0, 0, passwd0.length); + int i; + for (i = 0; i < len; i++) { + if (passwd0[i] == 0x09 || passwd0[i] == 0x0A) { + break; + } + } + byte[] passwd1 = Arrays.copyOfRange(passwd0, 0, i); + password = StringUtils.byte2Char(passwd1); + StringUtils.clear(passwd0); + StringUtils.clear(passwd1); + } + return password; + } + public static byte[] generateContractAddress(Transaction trx, byte[] ownerAddress) { // get tx hash byte[] txRawDataHash = Sha256Sm3Hash.of(trx.getRawData().toByteArray()).getBytes(); diff --git a/src/main/java/org/tron/mnemonic/MnemonicUtils.java b/src/main/java/org/tron/mnemonic/MnemonicUtils.java index dff4eaf5..17c11b18 100644 --- a/src/main/java/org/tron/mnemonic/MnemonicUtils.java +++ b/src/main/java/org/tron/mnemonic/MnemonicUtils.java @@ -14,8 +14,10 @@ import org.web3j.crypto.Bip32ECKeyPair; import org.web3j.crypto.Credentials; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; +import java.io.InputStreamReader; import java.nio.file.Path; import java.nio.file.Paths; import java.security.SecureRandom; @@ -155,8 +157,6 @@ public static void updateMnemonicFile( public static int inputMnemonicWordsNumber() { try { - Terminal terminal = TerminalBuilder.builder().system(true).build(); - LineReader lineReader = LineReaderBuilder.builder().terminal(terminal).build(); int attempts = 0; final int maxAttempts = 3; @@ -165,8 +165,10 @@ public static int inputMnemonicWordsNumber() { "\tDefault: 12 mnemonic words, \n" + "\tEnter 24 to use 24 mnemonic words\n" + "\tPress Enter to use the default (12). \n" + - "\tValid inputs are \"12\" or \"24\")\n\n"; - String input = lineReader.readLine(prompt).trim(); + "\tValid inputs are \"12\" or \"24\")\n"; + System.out.print(prompt); + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + String input = reader.readLine().trim(); if (input.isEmpty() || MNEMONIC_WORDS_LENGTH_12_STR.equals(input)) { return MNEMONIC_WORDS_LENGTH_12; } else if (MNEMONIC_WORDS_LENGTH_24_STR.equals(input)) { diff --git a/src/main/java/org/tron/mnemonic/SubAccount.java b/src/main/java/org/tron/mnemonic/SubAccount.java index 38296dec..8fab7745 100644 --- a/src/main/java/org/tron/mnemonic/SubAccount.java +++ b/src/main/java/org/tron/mnemonic/SubAccount.java @@ -114,52 +114,64 @@ private void generateAddresses(String mnemonic) { for (int i = 0; i <= 99; i++) { try { WalletAddress newAddress = generateWalletAddress(mnemonic, addresses.get(i).getPathIndex()); + if (newAddress == null) { + break; + } newAddress.setGenerated(MnemonicUtils.generatedAddress(newAddress.getAddress())); addresses.set(i, newAddress); } catch (Exception e) { - e.printStackTrace(); + System.out.println(e.getMessage()); } } } private WalletAddress generateWalletAddress(String mnemonic, int pathIndex) { - List words = MnemonicUtils.stringToMnemonicWords(mnemonic); - byte[] privateKey = MnemonicUtils.getPrivateKeyFromMnemonicByPath(words, pathIndex); - String address = ""; - if (isEckey) { - ECKey ecKey = ECKey.fromPrivate(privateKey); - address = WalletApi.encode58Check(ecKey.getAddress()); - } else { - SM2 sm2 = SM2.fromPrivate(privateKey); - address = WalletApi.encode58Check(sm2.getAddress()); + try { + List words = MnemonicUtils.stringToMnemonicWords(mnemonic); + byte[] privateKey = MnemonicUtils.getPrivateKeyFromMnemonicByPath(words, pathIndex); + String address = ""; + if (isEckey) { + ECKey ecKey = ECKey.fromPrivate(privateKey); + address = WalletApi.encode58Check(ecKey.getAddress()); + } else { + SM2 sm2 = SM2.fromPrivate(privateKey); + address = WalletApi.encode58Check(sm2.getAddress()); + } + return WalletAddress.builder() + .pathIndex(pathIndex) + .address(address) + .privateKey(privateKey) + .generated(false) + .build(); + } catch (Exception e) { + System.out.println(e.getMessage()); + return null; } - - return WalletAddress.builder() - .pathIndex(pathIndex) - .address(address) - .privateKey(privateKey) - .generated(false) - .build(); } private WalletAddress generateWalletAddressByCustomPath(String mnemonic, String pathFull) { - List words = MnemonicUtils.stringToMnemonicWords(mnemonic); - byte[] privateKey = MnemonicUtils.getPrivateKeyFromMnemonicByCustomPath(words, pathFull); - String address = ""; - if (isEckey) { - ECKey ecKey = ECKey.fromPrivate(privateKey); - address = WalletApi.encode58Check(ecKey.getAddress()); - } else { - SM2 sm2 = SM2.fromPrivate(privateKey); - address = WalletApi.encode58Check(sm2.getAddress()); - } + try { + List words = MnemonicUtils.stringToMnemonicWords(mnemonic); + byte[] privateKey = MnemonicUtils.getPrivateKeyFromMnemonicByCustomPath(words, pathFull); + String address = ""; + if (isEckey) { + ECKey ecKey = ECKey.fromPrivate(privateKey); + address = WalletApi.encode58Check(ecKey.getAddress()); + } else { + SM2 sm2 = SM2.fromPrivate(privateKey); + address = WalletApi.encode58Check(sm2.getAddress()); + } - return WalletAddress.builder() - .pathIndex(-1) - .address(address) - .privateKey(privateKey) - .generated(true) - .build(); + return WalletAddress.builder() + .pathIndex(-1) + .address(address) + .privateKey(privateKey) + .generated(true) + .build(); + } catch (Exception e) { + System.out.println("Generate wallet address failed: " + e.getMessage()); + return null; + } } private void printProgress(String message) { @@ -251,14 +263,23 @@ private void showError(String message) { public void start() throws Exception { while (true) { clearScreen(); + if (mnemonic == null || mnemonic.isEmpty()) { + System.out.println("MaKe sure your account has mnemonic words first!"); + break; + } Integer firstIndex = getFirstNonGeneratedPathIndex(); if (firstIndex == null) { System.out.println("All sub accounts have been generated!"); break; } String defaulFullPath = buildFullPath("0", firstIndex.toString()); + WalletAddress walletAddress = this.generateWalletAddressByCustomPath( mnemonic, defaulFullPath); + if (walletAddress==null) { + System.out.println("Generate wallet address error!"); + break; + } terminal.writer().println("\n=== Sub Account Generator ==="); @@ -367,8 +388,7 @@ public boolean generateByCustomPath() { } generateSubAccountByCustomPath(pathFull); } catch (Exception e) { - // debug - e.printStackTrace(); + System.out.println(e.getMessage()); return false; } return true; @@ -377,6 +397,10 @@ public boolean generateByCustomPath() { private void generateSubAccountByCustomPath(String path) throws CipherException, IOException { WalletAddress walletAddress = this.generateWalletAddressByCustomPath( mnemonic, path); + if (walletAddress == null) { + System.out.println("Generate Subaccount by Custom Path failed"); + return; + } if (MnemonicUtils.generatedAddress(walletAddress.getAddress())) { terminal.writer().println("The path is already generated..."); terminal.flush(); diff --git a/src/main/java/org/tron/walletcli/Client.java b/src/main/java/org/tron/walletcli/Client.java index 53be3f15..d67d554a 100755 --- a/src/main/java/org/tron/walletcli/Client.java +++ b/src/main/java/org/tron/walletcli/Client.java @@ -581,7 +581,7 @@ private void generateSubAccount() throws CipherException, IOException { } private void importWallet() throws CipherException, IOException { - System.out.println("((Note:This operation will overwrite the old keystore file and mnemonic file of the same address)"); + System.out.println("(Note:This operation will overwrite the old keystore file and mnemonic file of the same address)"); System.out.println("Please make sure to back up the old keystore files in the Wallet/Mnemonic directory if it is still needed!"); char[] password = Utils.inputPassword2Twice(); @@ -601,7 +601,7 @@ private void importWallet() throws CipherException, IOException { } private void importWalletByMnemonic() throws CipherException, IOException { - System.out.println("((Note:This operation will overwrite the old keystore file and mnemonic file of the same address)"); + System.out.println("(Note:This operation will overwrite the old keystore file and mnemonic file of the same address)"); System.out.println("Please make sure to back up the old keystore files in the Wallet/Mnemonic directory if it is still needed!"); char[] password = Utils.inputPassword2Twice(); @@ -624,7 +624,7 @@ private void importWalletByMnemonic() throws CipherException, IOException { } private void importWalletByLedger() throws CipherException, IOException { - System.out.println(ANSI_RED +"((Note:This will pair Ledger to user your hardward wallet)"); + System.out.println(ANSI_RED +"(Note:This will pair Ledger to user your hardward wallet)"); System.out.println("Only one Ledger device is supported. If you have multiple devices, please ensure only one is connected." + ANSI_RESET); @@ -666,7 +666,7 @@ private void importWalletByLedger() throws CipherException, IOException { } private void importWalletByBase64() throws CipherException, IOException { - System.out.println("((Note:This operation will overwrite the old keystore file and mnemonic file of the same address)"); + System.out.println("(Note:This operation will overwrite the old keystore file and mnemonic file of the same address)"); System.out.println("Please make sure to back up the old keystore files in the Wallet/Mnemonic directory if it is still needed!"); char[] password = Utils.inputPassword2Twice(); @@ -774,15 +774,22 @@ private void exportWalletKeystore(String[] parameters) throws CipherException, I if (!channel.equalsIgnoreCase("tronlink")) { System.out.println("exportWalletKeystore failed, channel error !!"); System.out.println("currrently only tronlink is supported!!"); + return; } String exportDirPath = parameters[1]; String exportFullDirPath = PathUtil.toAbsolutePath(exportDirPath); File exportFullDir = new File(exportFullDirPath); if (!exportFullDir.exists()) { - throw new IOException("Directory does not exist: " + exportFullDir.getAbsolutePath()); + System.out.println("exportWalletKeystore failed, directory does not exist !!"); + return; + } + if (!exportFullDir.isDirectory()) { + System.out.println("exportWalletKeystore failed, param 2 is not a directory!!"); + return; } if (!exportFullDir.canWrite()) { - throw new IOException("Directory is not writable: " + exportFullDir.getAbsolutePath()); + System.out.println("exportWalletKeystore failed, directory is not writable!!"); + return; } String exportFilePath = walletApiWrapper.exportKeystore(channel, exportFullDir); @@ -795,6 +802,9 @@ private void exportWalletKeystore(String[] parameters) throws CipherException, I } private void importWalletByKeystore(String[] parameters) throws CipherException, IOException { + System.out.println("(Note:This operation will overwrite the old keystore file and mnemonic file of the same address)"); + System.out.println("Please make sure to back up the old keystore files in the Wallet/Mnemonic directory if it is still needed!"); + if (parameters.length < 2) { System.out.println("Example usage: ImportWalletByKeystore tronlink tronlink-export-keystore.txt"); System.out.println("importWalletByKeystore failed, parameters error !!"); @@ -813,13 +823,18 @@ private void importWalletByKeystore(String[] parameters) throws CipherException, System.out.println("importWalletByKeystore failed, keystore file to import not exists !!"); return ; } + if (importFile.isDirectory()) { + System.out.println("importWalletByKeystore failed, parameters 2 is a directory!!"); + return ; + } - char[] password = Utils.inputPassword2Twice(); - byte[] passwdByte = StringUtils.char2Byte(password); + System.out.println("Please enter the password for the keystore file, enter it once."); + char[] keystorePassword = Utils.inputPasswordWithoutCheck(); + byte[] keystorepasswdByte = StringUtils.char2Byte(keystorePassword); try { - String fileName = walletApiWrapper.importWalletByKeystore(passwdByte, password, importFile); - if (fileName != null) { + String fileName = walletApiWrapper.importWalletByKeystore(keystorepasswdByte, importFile); + if (fileName != null && !fileName.isEmpty()) { System.out.println("fileName = " + fileName); System.out.println("importWalletByKeystore successful !!"); } else { @@ -828,10 +843,9 @@ private void importWalletByKeystore(String[] parameters) throws CipherException, } catch (Exception e) { System.out.println("importWalletByKeystore failed !!"); } finally { - StringUtils.clear(password); - StringUtils.clear(passwdByte); + StringUtils.clear(keystorePassword); + StringUtils.clear(keystorepasswdByte); } - } private char[] bytesToChars(byte[] bytes) { diff --git a/src/main/java/org/tron/walletcli/WalletApiWrapper.java b/src/main/java/org/tron/walletcli/WalletApiWrapper.java index 7308a730..586336d7 100644 --- a/src/main/java/org/tron/walletcli/WalletApiWrapper.java +++ b/src/main/java/org/tron/walletcli/WalletApiWrapper.java @@ -391,18 +391,25 @@ public boolean generateSubAccount() throws CipherException, IOException { char[] password = Utils.inputPassword(false); byte[] passwd = StringUtils.char2Byte(password); wallet.checkPassword(passwd); - - String ownerAddress = WalletApi.encode58Check(wallet.getAddress()); - byte[] mnemonic = MnemonicUtils.exportMnemonic(passwd, ownerAddress); + byte[] mnemonic = null; try { + String ownerAddress = WalletApi.encode58Check(wallet.getAddress()); + mnemonic = MnemonicUtils.exportMnemonic(passwd, ownerAddress); + if (mnemonic == null || mnemonic.length == 0) { + System.out.println("GenerateSubAccount failed. Your account does not have mnemonic."); + return false; + } SubAccount.getInstance(passwd, new String(mnemonic)).start(); } catch (Exception e) { - StringUtils.clear(password); System.out.println("Warning: GenerateSubAccount failed, e :" + e.getMessage()); e.printStackTrace(); return false; + } finally { + StringUtils.clear(mnemonic); + StringUtils.clear(password); + StringUtils.clear(passwd); } - StringUtils.clear(password); + return true; } @@ -464,13 +471,30 @@ public String exportKeystore(String walletChannel, File exportFullDir) return wallet.exportKeystore(walletChannel, exportFullDir); } - public String importWalletByKeystore(byte[] passwdByte, char[] password, File importFile) - throws IOException, CipherException { + public String importWalletByKeystore(byte[] passwdByte, File importFile) + throws IOException { WalletFile walletFile = WalletUtils.loadWalletFile(importFile); - byte[] priKey = Wallet.decrypt2PrivateBytes(passwdByte, walletFile); - String fileName = importWallet(password, priKey, null); - return fileName; + byte[] priKey = null; + try { + priKey = Wallet.decrypt2PrivateBytes(passwdByte, walletFile); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + + if (priKey !=null) { + System.out.println("Please enter the password for the new account after importing the keystore into wallet-cli, enter it twice."); + char[] password = Utils.inputPassword2Twice(); + try { + String fileName = importWallet(password, priKey, null); + return fileName; + } catch (Exception e) { + System.out.println(e.getMessage()); + } finally { + StringUtils.clear(password); + } + } + return ""; } public String getAddress() { diff --git a/src/main/java/org/tron/walletserver/WalletApi.java b/src/main/java/org/tron/walletserver/WalletApi.java index 299e1260..52e3f14d 100644 --- a/src/main/java/org/tron/walletserver/WalletApi.java +++ b/src/main/java/org/tron/walletserver/WalletApi.java @@ -2394,7 +2394,9 @@ public boolean clearWalletKeystore() { filePaths.addAll(mnemonicPath); } - LedgerFileUtil.removePathFromFile(this.path); + if (this.isLedgerUser && this.path != null && !this.path.isEmpty()) { + LedgerFileUtil.removePathFromFile(this.path); + } return ClearWalletUtils.confirmAndDeleteWallet(ownerAddress, filePaths); }