Skip to content

Commit

Permalink
forc-crypto: vanity address generation (#6661)
Browse files Browse the repository at this point in the history
## Description
Support for Bech32 case-insensitive vanity address generation via
`forc-crypto` CLI tool.

Usage:
```sh
forc-crypto vanity --help
```

CLI options:
```
Generate a vanity address

Usage: forc-crypto vanity [OPTIONS]

Options:
      --starts-with <HEX_STRING>  Desired hex string prefix for the address
      --ends-with <HEX_STRING>    Desired hex string suffix for the address
      --regex <PATTERN>           Desired regex pattern to match the entire address (case-insensitive)
      --timeout <SECONDS>         Timeout in seconds for address generation
      --mnemonic                  Return mnemonic with address (default false)
      --save-path <PATH>          Path to save the generated vanity address to
  -h, --help                      Print help
  -V, --version                   Print version

Generate vanity addresses for the Fuel blockchain
```

## Example outputs
```sh
# > forc-crypto vanity --starts-with 0 --ends-with F

Starting to generate vanity address...
Successfully found vanity address in 0.012 seconds.
Address: 0d3c399e756dee9f7312215882f92685fdae25449bc74f33c31063000d68afdf
PrivateKey: cec536d4f5aae64685856ad36818777ba0aed349bdef848b487d6ebb1cc2a0a4
```

```sh
# > forc-crypto vanity --starts-with 0 --ends-with F --mnemonic

Starting to generate vanity address...
Successfully found vanity address in 0.141 seconds.
Address: 030b8484305c9f3af7b662d9fdd88dc75bdceb29f42d0f5ea5f72d3dfaf9380f
Mnemonic: found broccoli trap left thought attack quality smooth patrol enrich fault flavor legend amused monitor shoulder legend blast elbow custom dirt cotton tackle much
PrivateKey: 269ee26dd810a4a2df72969ed8941fe92ddc0bab236715535b867448c3ffa25e
```

## Relevant issues
- #6664

---------

Co-authored-by: Sophie Dankel <47993817+sdankel@users.noreply.github.com>
Co-authored-by: Kaya Gökalp <kaya.gokalp@fuel.sh>
  • Loading branch information
3 people authored Oct 25, 2024
1 parent 31974b4 commit 058f4e2
Show file tree
Hide file tree
Showing 8 changed files with 595 additions and 45 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions forc-plugins/forc-crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,26 @@ forc-tracing.workspace = true
forc-util.workspace = true
fuel-core-types.workspace = true
fuel-crypto = { workspace = true, features = ["random"] }
fuels-accounts.workspace = true
fuels-core.workspace = true
futures.workspace = true
hex.workspace = true
libp2p-identity = { workspace = true, features = ["peerid", "secp256k1"] }
rand.workspace = true
rayon.workspace = true
regex.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_yaml.workspace = true
sha3.workspace = true
termion.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "process"] }
tracing.workspace = true

[dev-dependencies]
criterion = "0.5"
tempfile.workspace = true

[[bench]]
name = "bench_main"
harness = false
73 changes: 73 additions & 0 deletions forc-plugins/forc-crypto/benches/bench_main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use forc_crypto::keys::vanity::{find_vanity_address_with_timeout, HexMatcher, RegexMatcher};
use rayon::iter::Either;

fn benchmark_vanity_address(c: &mut Criterion) {
let mut group = c.benchmark_group("Vanity Address Generation");

// Benchmark HexMatcher with prefix
group.bench_function("HexMatcher (starts with 'a')", |b| {
b.iter(|| {
let matcher = Either::Right(HexMatcher::new("a", "").unwrap());
find_vanity_address_with_timeout(black_box(matcher), false, None)
})
});

// Benchmark HexMatcher with suffix
group.bench_function("HexMatcher (ends with 'f')", |b| {
b.iter(|| {
let matcher = Either::Right(HexMatcher::new("", "f").unwrap());
find_vanity_address_with_timeout(black_box(matcher), false, None)
})
});

// Benchmark HexMatcher with both prefix and suffix
group.bench_function("HexMatcher (starts with 'a' ends with 'f')", |b| {
b.iter(|| {
let matcher = Either::Right(HexMatcher::new("a", "f").unwrap());
find_vanity_address_with_timeout(black_box(matcher), false, None)
})
});

// Benchmark RegexMatcher with simple pattern
group.bench_function("RegexMatcher (starts with 'a')", |b| {
b.iter(|| {
let matcher = Either::Left(RegexMatcher::new("^a.*").unwrap());
find_vanity_address_with_timeout(black_box(matcher), false, None)
})
});

// Benchmark RegexMatcher with complex pattern
group.bench_function("RegexMatcher (contains two consecutive digits)", |b| {
b.iter(|| {
let matcher = Either::Left(RegexMatcher::new(r"[0-9]{2}").unwrap());
find_vanity_address_with_timeout(black_box(matcher), false, None)
})
});

// Benchmark with mnemonic generation
group.bench_function("HexMatcher with Mnemonic (starts with 'a')", |b| {
b.iter(|| {
let matcher = Either::Right(HexMatcher::new("a", "").unwrap());
find_vanity_address_with_timeout(black_box(matcher), true, None)
})
});

group.bench_function("RegexMatcher with Mnemonic (starts with 'a')", |b| {
b.iter(|| {
let matcher = Either::Left(RegexMatcher::new("^a.*").unwrap());
find_vanity_address_with_timeout(black_box(matcher), true, None)
})
});

group.finish();
}

criterion_group! {
name = benches;
config = Criterion::default()
.sample_size(10) // Reduced sample size due to potentially long-running benchmarks
.measurement_time(std::time::Duration::from_secs(20));
targets = benchmark_vanity_address
}
criterion_main!(benches);
16 changes: 8 additions & 8 deletions forc-plugins/forc-crypto/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ forc_util::cli_examples! {
pub struct HashArgs {
/// This argument is optional, it can be either:
///
/// 1. A path to a file. If that is the case, the content of the file is
/// loaded.
/// 1. A path to a file. If that is the case, the content of the file is
/// loaded
///
/// 2. A binary string encoded as a hex string. If that is the case, the
/// hex is decoded and passed as a Vec<u8>
/// 2. A binary string encoded as a hex string. If that is the case, the
/// hex is decoded and passed as a Vec<u8>
///
/// 3. A string. This is the last option, if the string is "-", `stdin`
/// is read instead. Otherwise the raw string is converted to a Vec<u8>
/// and passed
/// 3. A string. This is the last option, if the string is "-", "stdin"
/// is read instead. Otherwise the raw string is converted to a Vec<u8>
/// and passed
///
/// 4. If it nos not provided, `stdin` is read
/// 4. If it is not provided, "stdin" is read
content_or_filepath: Option<String>,
}

Expand Down
1 change: 1 addition & 0 deletions forc-plugins/forc-crypto/src/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use clap::ValueEnum;
pub mod get_public_key;
pub mod new_key;
pub mod parse_secret;
pub mod vanity;

#[derive(Clone, Debug, Default, ValueEnum)]
pub enum KeyType {
Expand Down
Loading

0 comments on commit 058f4e2

Please sign in to comment.