use bitcoin::key::Secp256k1;
use bitcoin::network::Network;
use bitcoin::secp256k1::constants::CURVE_ORDER;
use bitcoin::secp256k1::SecretKey;
use bitcoin::{Address, CompressedPublicKey, PrivateKey, PublicKey};
use num_bigint::BigUint;
#[derive(Debug, Clone)]
pub struct DerivedKey {
pub private_key_hex: String,
pub private_key_decimal: String,
pub private_key_binary: String,
pub bit_length: u16,
pub hamming_weight: u16,
pub leading_zeros: u8,
pub pubkey_compressed: String,
pub pubkey_uncompressed: String,
pub wif_compressed: String,
pub wif_uncompressed: String,
pub p2pkh_compressed: String,
pub p2pkh_uncompressed: String,
pub p2wpkh: String,
}
impl DerivedKey {
pub fn addresses(&self) -> [&str; 3] {
[
&self.p2pkh_compressed,
&self.p2pkh_uncompressed,
&self.p2wpkh,
]
}
}
pub struct KeyDeriver {
secp: Secp256k1<bitcoin::secp256k1::All>,
network: Network,
}
impl KeyDeriver {
pub fn new() -> Self {
Self {
secp: Secp256k1::new(),
network: Network::Bitcoin,
}
}
pub fn with_network(network: Network) -> Self {
Self {
secp: Secp256k1::new(),
network,
}
}
pub fn derive(&self, key: &[u8; 32]) -> DerivedKey {
let secret_key = SecretKey::from_slice(key).unwrap_or_else(|_| {
let mut int_val = BigUint::from_bytes_be(key);
let order = BigUint::from_bytes_be(&CURVE_ORDER);
int_val %= ℴ
if int_val == BigUint::from(0u8) {
int_val = BigUint::from(1u8);
}
let mut normalized = [0u8; 32];
let bytes = int_val.to_bytes_be();
let start = 32 - bytes.len();
normalized[start..].copy_from_slice(&bytes);
SecretKey::from_slice(&normalized).expect("normalized key")
});
let key_bytes = secret_key.secret_bytes();
let secp_pubkey = bitcoin::secp256k1::PublicKey::from_secret_key(&self.secp, &secret_key);
let pubkey_compressed = hex::encode(secp_pubkey.serialize());
let pubkey_uncompressed = hex::encode(secp_pubkey.serialize_uncompressed());
let mut priv_compressed = PrivateKey::new(secret_key, self.network);
priv_compressed.compressed = true;
let mut priv_uncompressed = PrivateKey::new(secret_key, self.network);
priv_uncompressed.compressed = false;
let pk_compressed = PublicKey::from_private_key(&self.secp, &priv_compressed);
let pk_uncompressed = PublicKey::from_private_key(&self.secp, &priv_uncompressed);
let p2pkh_compressed = Address::p2pkh(&pk_compressed, self.network).to_string();
let p2pkh_uncompressed = Address::p2pkh(&pk_uncompressed, self.network).to_string();
let compressed_pk = CompressedPublicKey::from_slice(&secp_pubkey.serialize())
.expect("valid compressed pubkey");
let p2wpkh = Address::p2wpkh(&compressed_pk, self.network).to_string();
let private_key_decimal = BigUint::from_bytes_be(&key_bytes).to_string();
let private_key_binary: String = key_bytes.iter()
.flat_map(|byte| (0..8).rev().map(move |i| if byte & (1 << i) != 0 { '1' } else { '0' }))
.collect();
let leading_zero_bits: u16 = key_bytes.iter()
.take_while(|&&b| b == 0)
.count() as u16 * 8
+ key_bytes.iter()
.find(|&&b| b != 0)
.map(|b| b.leading_zeros() as u16)
.unwrap_or(0);
let bit_length = 256 - leading_zero_bits;
let hamming_weight: u16 = key_bytes.iter().map(|b| b.count_ones() as u16).sum();
let hex_str = hex::encode(key_bytes);
let leading_zeros = hex_str.chars().take_while(|&c| c == '0').count() as u8;
DerivedKey {
private_key_hex: hex_str,
private_key_decimal,
private_key_binary,
bit_length,
hamming_weight,
leading_zeros,
pubkey_compressed,
pubkey_uncompressed,
wif_compressed: priv_compressed.to_wif(),
wif_uncompressed: priv_uncompressed.to_wif(),
p2pkh_compressed,
p2pkh_uncompressed,
p2wpkh,
}
}
}
impl Default for KeyDeriver {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_derive_known_key() {
let key: [u8; 32] = [
0xc4, 0xbb, 0xcb, 0x1f, 0xbe, 0xc9, 0x9d, 0x65,
0xbf, 0x59, 0xd8, 0x5c, 0x8c, 0xb6, 0x2e, 0xe2,
0xdb, 0x96, 0x3f, 0x0f, 0xe1, 0x06, 0xf4, 0x83,
0xd9, 0xaf, 0xa7, 0x3b, 0xd4, 0xe3, 0x9a, 0x8a,
];
let deriver = KeyDeriver::new();
let derived = deriver.derive(&key);
assert_eq!(
derived.wif_uncompressed,
"5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS"
);
assert_eq!(
derived.p2pkh_uncompressed,
"1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T"
);
assert!(derived.wif_compressed.starts_with('K') || derived.wif_compressed.starts_with('L'));
assert!(derived.p2wpkh.starts_with("bc1q"));
}
#[test]
fn test_addresses_returns_all() {
let key = [1u8; 32];
let deriver = KeyDeriver::new();
let derived = deriver.derive(&key);
let addrs = derived.addresses();
assert_eq!(addrs.len(), 3);
assert!(addrs[0].starts_with('1')); assert!(addrs[1].starts_with('1')); assert!(addrs[2].starts_with("bc1q")); }
#[test]
fn test_derive_normalizes_zero_key() {
let key = [0u8; 32];
let deriver = KeyDeriver::new();
let derived = deriver.derive(&key);
assert_eq!(derived.private_key_decimal, "1");
assert_eq!(derived.bit_length, 1);
}
}