diff --git a/src/main/java/org/qora/VanityGen.java b/src/main/java/org/qora/VanityGen.java index c975862f..917501b5 100644 --- a/src/main/java/org/qora/VanityGen.java +++ b/src/main/java/org/qora/VanityGen.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.security.SecureRandom; import java.security.Security; import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider; @@ -16,37 +18,87 @@ import com.google.common.primitives.Bytes; public class VanityGen { + // From utils.Base58: + private static final String ALPHABET_STR = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + + private static String prefix = null; + + private static void usage() { + System.err.println("Usage: Vanitygen [-t threads] "); + System.err.println("Example: VanityGen Qcat"); + System.exit(1); + } + + private static class Generator implements Runnable { + public void run() { + Random random = new SecureRandom(); + byte[] entropy = new byte[16]; + + while (true) { + // Generate entropy internally + random.nextBytes(entropy); + + // Use SHA256 to generate more bits + byte[] hash = Crypto.digest(entropy); + + // Append first 4 bits from hash to end. (Actually 8 bits but we only use 4). + byte checksum = (byte) (hash[0] & 0xf0); + byte[] entropy132 = Bytes.concat(entropy, new byte[] { checksum }); + + String mnemonic = BIP39.encode(entropy132, "en"); + + PrivateKeyAccount account = new PrivateKeyAccount(null, hash); + + if (!account.getAddress().startsWith(prefix)) + continue; + + System.out.println(String.format("Address: %s, public key: %s, private key: %s, mnemonic: %s", + account.getAddress(), Base58.encode(account.getPublicKey()), Base58.encode(hash), mnemonic)); + System.out.flush(); + } + } + } + public static void main(String argv[]) throws IOException { - if (argv.length != 1) { - System.err.println("Usage: Vanitygen "); - System.err.println("Example: VanityGen Qcat"); - return; + if (argv.length == 0) + usage(); + + int threadCount = 1; + + int argIndex = 0; + while (argIndex < argv.length) { + String arg = argv[argIndex++]; + + if (arg.equals("-t")) { + if (argIndex >= argv.length) + usage(); + + try { + threadCount = Integer.parseInt(argv[argIndex++]); + } catch (NumberFormatException e) { + usage(); + } + + continue; + } + + if (prefix != null) + usage(); + + prefix = arg; + if (!prefix.matches("[" + ALPHABET_STR + "]+")) { + System.err.println("Only the following characters are allowed:\n" + ALPHABET_STR); + System.exit(1); + } } Security.insertProviderAt(new BouncyCastleProvider(), 0); Security.insertProviderAt(new BouncyCastleJsseProvider(), 1); - Random random = new SecureRandom(); - byte[] entropy = new byte[16]; + ExecutorService executor = Executors.newFixedThreadPool(threadCount); - while (true) { - // Generate entropy internally - random.nextBytes(entropy); - - // Use SHA256 to generate more bits - byte[] hash = Crypto.digest(entropy); - - // Append first 4 bits from hash to end. (Actually 8 bits but we only use 4). - byte checksum = (byte) (hash[0] & 0xf0); - byte[] entropy132 = Bytes.concat(entropy, new byte[] { checksum }); - - String mnemonic = BIP39.encode(entropy132, "en"); - - PrivateKeyAccount account = new PrivateKeyAccount(null, hash); - - if (account.getAddress().startsWith(argv[0])) - System.out.println(String.format("Address: %s, public key: %s, private key: %s, mnemonic: %s", account.getAddress(), Base58.encode(account.getPublicKey()), Base58.encode(hash), mnemonic)); - } + for (int ti = 0; ti < threadCount; ++ti) + executor.execute(new Generator()); } }