use sha2::Digest;
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::crypto;
use crate::error::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ChainCode {
Receive = 0,
Pay = 1,
Change = 2,
Mining = 3,
}
impl ChainCode {
pub fn from_u64(value: u64) -> Option<Self> {
match value {
0 => Some(ChainCode::Receive),
1 => Some(ChainCode::Pay),
2 => Some(ChainCode::Change),
3 => Some(ChainCode::Mining),
_ => None,
}
}
pub fn as_u64(self) -> u64 {
self as u64
}
pub fn as_str(self) -> &'static str {
match self {
ChainCode::Receive => "RECEIVE",
ChainCode::Pay => "PAY",
ChainCode::Change => "CHANGE",
ChainCode::Mining => "MINING",
}
}
}
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct HDWallet {
master_secret: [u8; 32],
}
impl HDWallet {
pub fn new() -> Result<Self> {
let secret = crypto::CryptoSecret::generate()
.map_err(|e| Error::crypto(format!("Failed to generate master secret: {}", e)))?;
Ok(HDWallet {
master_secret: secret.into_bytes(),
})
}
pub fn from_master_secret(master_secret: [u8; 32]) -> Self {
HDWallet { master_secret }
}
pub fn derive_secret(&self, chain_code: ChainCode, depth: u64) -> Result<String> {
let tag = crypto::sha256(b"webcashwalletv1");
let mut hasher = crypto::sha256_incremental();
hasher.update(tag);
hasher.update(tag);
hasher.update(self.master_secret);
hasher.update(chain_code.as_u64().to_be_bytes());
hasher.update(depth.to_be_bytes());
let final_hash = crypto::sha256_finalize(hasher);
Ok(hex::encode(final_hash))
}
pub fn master_secret(&self) -> &[u8; 32] {
&self.master_secret
}
pub fn master_secret_hex(&self) -> String {
hex::encode(self.master_secret)
}
}