keechain_core/command/
vanity.rs

1// Copyright (c) 2022 Yuki Kishimoto
2// Distributed under the MIT software license
3
4use std::time::Instant;
5
6use bitcoin::secp256k1::Secp256k1;
7use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey};
8use bitcoin::{Address, Network, PublicKey};
9
10use crate::error::{Error, Result};
11use crate::types::{Seed, MAX_INDEX};
12use crate::util::bip::bip32::{self, Bip32RootKey};
13
14const BECH32_CHARS: &str = "023456789acdefghjklmnpqrstuvwxyz";
15
16pub fn search_address(
17    seed: Seed,
18    prefixes: impl Into<Vec<String>>,
19    network: Network,
20) -> Result<(DerivationPath, Address)> {
21    let now = Instant::now();
22    let secp = Secp256k1::new();
23    let root = seed.to_bip32_root_key(network)?;
24    let prefixes = prefixes.into();
25
26    for prefix in prefixes.iter() {
27        for c in prefix.chars() {
28            if !BECH32_CHARS.contains(c) {
29                return Err(Error::Generic(format!("Unsupported char: {}", c)));
30            }
31        }
32    }
33
34    for account in 0..MAX_INDEX {
35        let path = bip32::account_extended_path(84, network, Some(account))?;
36        let derived_private_key: ExtendedPrivKey = root.derive_priv(&secp, &path)?;
37        let derived_public_key: ExtendedPubKey =
38            ExtendedPubKey::from_priv(&secp, &derived_private_key);
39
40        for index in 0..MAX_INDEX {
41            let derived_public_key: ExtendedPubKey =
42                derived_public_key.ckd_pub(&secp, ChildNumber::from_normal_idx(index)?)?;
43
44            for change in 0..=1 {
45                let derived_public_key: ExtendedPubKey =
46                    derived_public_key.ckd_pub(&secp, ChildNumber::from_normal_idx(change)?)?;
47                let pubkey = PublicKey::new(derived_public_key.public_key);
48                let address = Address::p2wpkh(&pubkey, network).unwrap();
49                let addr_str = address.to_string();
50                if prefixes.iter().any(|prefix| addr_str.contains(prefix)) {
51                    println!("{} ms", now.elapsed().as_millis());
52                    let path =
53                        bip32::get_path(84, network, Some(account), Some(index), change == 1)?;
54                    return Ok((path, address));
55                }
56            }
57        }
58    }
59
60    Err(Error::Generic(
61        "Failed to derive xpub at found path".to_string(),
62    ))
63}