Skip to content

Commit

Permalink
chore: updating to 2023 code :kek:
Browse files Browse the repository at this point in the history
  • Loading branch information
iam4x committed Aug 24, 2023
1 parent 4073cb1 commit 93eeffb
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 150 deletions.
11 changes: 3 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,10 @@ edition = "2021"
name = "vanitygen-bip39"
version = "0.1.0"

[profile.release]
codegen-units = 1
lto = "fat"
panic = "abort"

[dependencies]
bip0039 = "0.10.1"
clap = {version = "3.0.0-rc.10", features = ["derive"]}
hex = "*"
bip0039 = "0.11.0"
clap = {version = "4.3.24", features = ["derive"]}
hex = "0.4.3"
libsecp256k1 = "0.7.0"
num_cpus = "1.13.1"
reqwest = {version = "0.11.8", features = ["blocking", "json"]}
Expand Down
298 changes: 156 additions & 142 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
extern crate num_cpus;

use clap::Parser;
use hex::encode;
use std::collections::HashMap;
use std::str::FromStr;
use std::thread;
Expand All @@ -15,181 +16,194 @@ use tiny_keccak::{Hasher, Keccak};
#[derive(Parser, Debug)]
#[clap(about, version, author)]
struct Args {
#[clap(short, long, default_value_t = 400)]
score: i32,
#[clap(short, long, default_value_t = 400)]
score: i32,

#[clap(short, long, default_value_t = 0)]
words: i32,
#[clap(short, long, default_value_t = 0)]
words: i32,

#[clap(short, long, default_value_t = num_cpus::get())]
threads: usize,
#[clap(short, long, default_value_t = num_cpus::get())]
threads: usize,

#[clap(short = 'W', long, default_value = "")]
webhook: String,
#[clap(short = 'W', long, default_value = "")]
webhook: String,

#[clap(short, long)]
benchmark: bool,
#[clap(short, long)]
benchmark: bool,
}

fn main() {
let args = Args::parse();

println!("\n");
println!(" .__ __ ___. .__ ________ ________ ");
println!(" ___ _______ ____ |__|/ |_ ___.__. ____ ____ ____ \\_ |__ |__|_____\\_____ \\/ __ \\ ");
println!(" \\ \\/ /\\__ \\ / \\| \\ __< | |/ ___\\_/ __ \\ / \\ ______ | __ \\| \\____ \\ _(__ <\\____ /");
println!(" \\ / / __ \\| | \\ || | \\___ / /_/ > ___/| | \\ /_____/ | \\_\\ \\ | |_> > \\ / / ");
println!(" \\_/ (____ /___| /__||__| / ____\\___ / \\___ >___| / |___ /__| __/______ / /____/ ");
println!(" \\/ \\/ \\/ /_____/ \\/ \\/ \\/ |__| \\/ ");
println!("\n");

println!("Threads count: {}", args.threads);
println!("Minimum score shown: {}", args.score);

if args.words > 0 {
println!("Mnemonic words count: {}", args.words);
}
let args = Args::parse();

println!("\n");
println!(" .__ __ ___. .__ ________ ________ ");
println!(" ___ _______ ____ |__|/ |_ ___.__. ____ ____ ____ \\_ |__ |__|_____\\_____ \\/ __ \\ ");
println!(" \\ \\/ /\\__ \\ / \\| \\ __< | |/ ___\\_/ __ \\ / \\ ______ | __ \\| \\____ \\ _(__ <\\____ /");
println!(" \\ / / __ \\| | \\ || | \\___ / /_/ > ___/| | \\ /_____/ | \\_\\ \\ | |_> > \\ / / ");
println!(" \\_/ (____ /___| /__||__| / ____\\___ / \\___ >___| / |___ /__| __/______ / /____/ ");
println!(" \\/ \\/ \\/ /_____/ \\/ \\/ \\/ |__| \\/ ");
println!("\n");

println!("Threads count: {}", args.threads);
println!("Minimum score shown: {}", args.score);

if args.words > 0 {
println!("Mnemonic words count: {}", args.words);
}

if !args.webhook.is_empty() {
println!("Webhook: {}", args.webhook);
}
if !args.webhook.is_empty() {
println!("Webhook: {}", args.webhook);
}

if args.benchmark {
println!("Benchmark: true");
}
if args.benchmark {
println!("Benchmark: true");
}

println!("\n");
println!("\n");

let mut handles = vec![];
let mut handles = vec![];

for i in 0..args.threads {
handles.push(thread::spawn(move || {
find_vanity_address(i, args.threads, args.benchmark);
}));
}
for i in 0..args.threads {
handles.push(thread::spawn(move || {
find_vanity_address(i, args.threads, args.benchmark);
}));
}

for handle in handles {
handle.join().unwrap();
}
for handle in handles {
handle.join().unwrap();
}
}

fn find_vanity_address(thread: usize, threads_count: usize, bench: bool) {
let args = Args::parse();
let start = Instant::now();

let mut op_count: u128 = 0;
let mut op_start = Instant::now();

// default words to 12 and 24 depends on thread
// allow to search in different bip39 ranges for each thread
let mut words = if thread % 2 == 1 {
Count::Words12
} else {
Count::Words24
};

// respect user input if specified words count in args
if args.words == 12 {
words = Count::Words12;
} else if args.words == 24 {
words = Count::Words24;
}

loop {
let (mnemonic, address) = generate_address(words);
let score = calc_score(&address);

if score > args.score {
// Print the result
let duration = start.elapsed();
println!("\n");
println!("Time: {:?}", duration);
println!("BIP39: {}", mnemonic);
println!("Address: 0x{}", address);
println!("Score: {}", score);
println!("\n");

// Send to webhook
if !args.webhook.is_empty() {
let mut map = HashMap::new();
map.insert("duration", duration.as_secs().to_string());
map.insert("mnemonic", mnemonic.phrase().to_string());
map.insert("address", address.to_string());
map.insert("score", score.to_string());

let client = reqwest::blocking::Client::new();
let _res = client.post(&args.webhook).json(&map).send();
}
let args = Args::parse();
let start = Instant::now();

let mut op_count: u128 = 0;
let mut op_start = Instant::now();

// default words to 12 and 24 depends on thread
// allow to search in different bip39 ranges for each thread
let mut words = if thread % 2 == 1 {
Count::Words12
} else {
Count::Words24
};

// respect user input if specified words count in args
if args.words == 12 {
words = Count::Words12;
} else if args.words == 24 {
words = Count::Words24;
}

let mut output = [0u8; 32];

loop {
let (mnemonic, public) = generate_address(words);
let public = &public.serialize()[1..65];

keccak_hash_in_place(public, &mut output);
let score = calc_score(&output);

if score > args.score {
// Print the result
let address = &encode(&output[(output.len() - 20)..]);
let duration = start.elapsed();
println!("\n");
println!("Time: {:?}", duration);
println!("BIP39: {}", mnemonic);
println!("Address: 0x{}", address);
println!("Score: {}", score);
println!("\n");

// Send to webhook
if !args.webhook.is_empty() {
let mut map = HashMap::new();
map.insert("duration", duration.as_secs().to_string());
map.insert("mnemonic", mnemonic.phrase().to_string());
map.insert("address", address.to_string());
map.insert("score", score.to_string());

let client = reqwest::blocking::Client::new();
let _res = client.post(&args.webhook).json(&map).send();
}
}

if thread == 1 && bench {
op_count += 1;

if op_count == 10000 {
let duration = op_start.elapsed().as_millis();
let per_seconds = (1000 * op_count / duration) * threads_count as u128;

println!("~{} OP/S", per_seconds);

op_count = 0;
op_start = Instant::now();
}
}
}
}

if thread == 1 && bench {
op_count += 1;
const NIBBLE_MASK: u8 = 0x0F;
const SCORE_FOR_LEADING_ZERO: i32 = 100;
const SCORE_FOR_OTHER_ZEROS: i32 = 1;

if op_count == 10000 {
let duration = op_start.elapsed().as_millis();
let per_seconds = (1000 * op_count / duration) * threads_count as u128;
fn calc_score(address: &[u8]) -> i32 {
let mut score: i32 = 0;
let mut has_reached_non_zero = false;

println!("~{} OP/S", per_seconds);
for &byte in &address[(address.len() - 20)..] {
// Check first half-byte (or nibble)
let first_half = byte >> 4;

op_count = 0;
op_start = Instant::now();
}
}
}
}
if first_half != 0 {
has_reached_non_zero = true;
}

fn calc_score(address: &str) -> i32 {
let mut score: i32 = 0;
let mut has_reached_non_zero = false;
if first_half == 0 && !has_reached_non_zero {
score += SCORE_FOR_LEADING_ZERO;
} else if first_half == 0 && has_reached_non_zero {
score += SCORE_FOR_OTHER_ZEROS;
}

// calculate score of leading zeros into address
// +100 per leading 0
// +1 per non-zero leading 0
for c in address.chars() {
if c != '0' {
has_reached_non_zero = true;
}
// Check second half-byte (or nibble)
let second_half = byte & NIBBLE_MASK;

if c == '0' && !has_reached_non_zero {
score += 100;
}
if second_half != 0 {
has_reached_non_zero = true;
}

if c == '0' && has_reached_non_zero {
score += 1;
if second_half == 0 && !has_reached_non_zero {
score += SCORE_FOR_LEADING_ZERO;
} else if second_half == 0 && has_reached_non_zero {
score += SCORE_FOR_OTHER_ZEROS;
}
}
}

score
score
}

fn generate_address(words: Count) -> (Mnemonic, String) {
let mnemonic = Mnemonic::generate(words);
let seed = mnemonic.to_seed("");

let hdwallet = ExtendedPrivKey::derive(&seed, "m/44'/60'/0'/0").unwrap();
let account0 = hdwallet.child(ChildNumber::from_str("0").unwrap()).unwrap();
fn generate_address(words: Count) -> (Mnemonic, PublicKey) {
let mnemonic = Mnemonic::generate(words);
let seed = mnemonic.to_seed("");

let secret_key = SecretKey::parse(&account0.secret());
let secret_key = match secret_key {
Ok(sk) => sk,
Err(_) => panic!("Failed to parse secret key"),
};
let hdwallet = ExtendedPrivKey::derive(&seed, "m/44'/60'/0'/0").unwrap();
let account0 = hdwallet.child(ChildNumber::from_str("0").unwrap()).unwrap();

let public_key = PublicKey::from_secret_key(&secret_key);
let public_key = &public_key.serialize()[1..65];
let secret_key = SecretKey::parse(&account0.secret());
let secret_key = match secret_key {
Ok(sk) => sk,
Err(_) => panic!("Failed to parse secret key"),
};

let addr = &keccak_hash(public_key);
let addr = &addr[(addr.len() - 40)..];
let public_key = PublicKey::from_secret_key(&secret_key);

(mnemonic, addr.to_string())
(mnemonic, public_key)
}

fn keccak_hash(input: &[u8]) -> String {
let mut hasher = Keccak::v256();
let mut output = [0u8; 32];

hasher.update(input);
hasher.finalize(&mut output);

hex::encode(output)
fn keccak_hash_in_place(input: &[u8], output: &mut [u8; 32]) {
let mut hasher = Keccak::v256();
hasher.update(input);
hasher.finalize(output);
}

0 comments on commit 93eeffb

Please sign in to comment.