Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/org/tron/common/utils/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
10 changes: 6 additions & 4 deletions src/main/java/org/tron/mnemonic/MnemonicUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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)) {
Expand Down
94 changes: 59 additions & 35 deletions src/main/java/org/tron/mnemonic/SubAccount.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> 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<String> 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<String> 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<String> 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) {
Expand Down Expand Up @@ -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 ===");

Expand Down Expand Up @@ -367,8 +388,7 @@ public boolean generateByCustomPath() {
}
generateSubAccountByCustomPath(pathFull);
} catch (Exception e) {
// debug
e.printStackTrace();
System.out.println(e.getMessage());
return false;
}
return true;
Expand All @@ -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();
Expand Down
40 changes: 27 additions & 13 deletions src/main/java/org/tron/walletcli/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -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);

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand All @@ -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 !!");
Expand All @@ -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 {
Expand All @@ -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) {
Expand Down
44 changes: 34 additions & 10 deletions src/main/java/org/tron/walletcli/WalletApiWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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() {
Expand Down
Loading