use crate::bitcoin::error::{BitcoinError, BitcoinResult};
use bitcoin::secp256k1::{Secp256k1, SecretKey};
use bitcoin::{
bip32::{DerivationPath, Xpriv, Xpub},
Network,
};
use rand::RngCore;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub struct ExtendedKey {
pub xpriv: Xpriv,
pub xpub: Xpub,
}
pub fn generate_seed(_password: &str) -> BitcoinResult<[u8; 64]> {
let mut seed = [0u8; 64];
rand::thread_rng().fill_bytes(&mut seed);
Ok(seed)
}
pub fn seed_from_mnemonic(mnemonic_phrase: &str, password: &str) -> BitcoinResult<[u8; 64]> {
let combined = format!("{mnemonic_phrase}{password}");
let mut seed = [0u8; 64];
let bytes = combined.as_bytes();
for (i, &byte) in bytes.iter().enumerate() {
if i >= 64 {
break;
}
seed[i] = byte;
}
Ok(seed)
}
pub fn derive_key_from_seed(seed: &[u8; 64], path: &str) -> BitcoinResult<SecretKey> {
let secp = Secp256k1::new();
let master_key = Xpriv::new_master(Network::Bitcoin, seed)
.map_err(|e| BitcoinError::KeyDerivation(format!("Failed to create master key: {e}")))?;
let derivation_path = DerivationPath::from_str(path)
.map_err(|e| BitcoinError::KeyDerivation(format!("Invalid derivation path: {e}")))?;
let derived_key = master_key
.derive_priv(&secp, &derivation_path)
.map_err(|e| BitcoinError::KeyDerivation(format!("Failed to derive key: {e}")))?;
Ok(derived_key.private_key)
}
pub fn derive_master_key(seed: &[u8], network: Network) -> BitcoinResult<ExtendedKey> {
let secp = Secp256k1::new();
let xpriv = Xpriv::new_master(network, seed)
.map_err(|e| BitcoinError::KeyDerivation(format!("Failed to create master key: {e}")))?;
let xpub = Xpub::from_priv(&secp, &xpriv);
Ok(ExtendedKey { xpriv, xpub })
}
pub fn derive_child_key(parent: &ExtendedKey, path: &DerivationPath) -> BitcoinResult<ExtendedKey> {
let secp = Secp256k1::new();
let xpriv = parent
.xpriv
.derive_priv(&secp, path)
.map_err(|e| BitcoinError::KeyDerivation(format!("Failed to derive child key: {e}")))?;
let xpub = Xpub::from_priv(&secp, &xpriv);
Ok(ExtendedKey { xpriv, xpub })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_seed_generation() {
let seed = generate_seed("test_password").unwrap();
assert_eq!(seed.len(), 64);
}
#[test]
fn test_derive_master_key() {
let seed = [0u8; 64];
let master = derive_master_key(&seed, Network::Bitcoin).unwrap();
assert!(master.xpriv.network == Network::Bitcoin.into());
}
#[test]
fn test_derive_child_key() {
let seed = [0u8; 64];
let master = derive_master_key(&seed, Network::Bitcoin).unwrap();
let path = DerivationPath::from_str("m/44'/0'/0'/0/0").unwrap();
let child = derive_child_key(&master, &path).unwrap();
assert!(child.xpriv.network == Network::Bitcoin.into());
}
}