60 lines
2.2 KiB
Rust
60 lines
2.2 KiB
Rust
use bitcoin::network::constants::Network;
|
|
use bitcoin::util::address::Address;
|
|
use bitcoin::util::key::PrivateKey;
|
|
use bitcoin::secp256k1::{Secp256k1, SecretKey, PublicKey};
|
|
use sha2::{Digest, Sha256};
|
|
|
|
/// Simple Bitcoin on-chain wallet helper (minimal, safe-by-default)
|
|
///
|
|
/// This implements a deterministic derivation of a single private key from the core seed
|
|
/// purely for demonstration and testing. In production use **BIP32/BIP39** HD derivation
|
|
/// and robust UTXO management (e.g., `bdk`).
|
|
pub struct BtcWallet {
|
|
secret_key: SecretKey,
|
|
network: Network,
|
|
}
|
|
|
|
impl BtcWallet {
|
|
/// Derive a secret key deterministically from a seed and domain tag
|
|
pub fn derive_from_seed(seed: &[u8; 32], network: Network, tag: &[u8]) -> Self {
|
|
// Use SHA256(seed || tag) as a simple DRBG to get 32 bytes -> SecretKey
|
|
let mut hasher = Sha256::new();
|
|
hasher.update(seed);
|
|
hasher.update(tag);
|
|
let hash = hasher.finalize();
|
|
let sk = SecretKey::from_slice(&hash).expect("hash must be a valid secret key");
|
|
Self { secret_key: sk, network }
|
|
}
|
|
|
|
/// Return the P2WPKH bech32 address for this key
|
|
pub fn get_address(&self) -> Address {
|
|
let secp = Secp256k1::new();
|
|
let pk = PublicKey::from_secret_key(&secp, &self.secret_key);
|
|
let compressed = PublicKey::new(pk.key);
|
|
let pk_hash = bitcoin::hashes::hash160::Hash::hash(&compressed.serialize());
|
|
Address::p2wpkh(&pk, self.network).expect("address creation")
|
|
}
|
|
|
|
/// Return the private key in WIF format (useful for tests/examples only)
|
|
pub fn wif(&self) -> String {
|
|
let pk = PrivateKey { key: self.secret_key, network: self.network, compressed: true };
|
|
pk.to_wif()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::WalletCore;
|
|
|
|
#[test]
|
|
fn derive_and_address() {
|
|
let core = WalletCore::new();
|
|
let seed = core.seed; // NOTE: demo access to seed (in real code seed would be private)
|
|
let wallet = BtcWallet::derive_from_seed(&seed, Network::Testnet, b"cryptec-btc");
|
|
let addr = wallet.get_address();
|
|
assert!(addr.to_string().starts_with("tb1") || addr.to_string().starts_with("2") );
|
|
let wif = wallet.wif();
|
|
assert!(!wif.is_empty());
|
|
}
|
|
}
|