use alloc::string::String;
use kobe_primitives::DeriveError;
use kobe_primitives::encoding::base58check_encode;
#[cfg(test)]
use kobe_primitives::encoding::double_sha256;
#[cfg(test)]
use zeroize::Zeroize;
use zeroize::Zeroizing;
use crate::Network;
#[allow(
clippy::indexing_slicing,
reason = "fixed-size WIF payload fields are checked by construction"
)]
#[allow(
clippy::unnecessary_wraps,
reason = "unified DeriveError surface with create_address / derive_*"
)]
pub(crate) fn encode_wif(
private_key: &Zeroizing<[u8; 32]>,
network: Network,
) -> Result<Zeroizing<String>, DeriveError> {
let mut body = Zeroizing::new([0u8; 34]);
body[0] = match network {
Network::Mainnet => 0x80,
Network::Testnet => 0xef,
};
body[1..33].copy_from_slice(private_key.as_ref());
body[33] = 0x01;
Ok(Zeroizing::new(base58check_encode(body.as_slice())))
}
#[cfg(test)]
#[allow(
clippy::indexing_slicing,
reason = "decoded WIF length is validated before fixed field access"
)]
pub(crate) fn decode_wif(wif: &str) -> Result<(Zeroizing<[u8; 32]>, Network), DeriveError> {
let mut decoded = bs58::decode(wif)
.into_vec()
.map_err(|_| DeriveError::Input("btc: invalid WIF base58".into()))?;
let result = (|| {
if decoded.len() != 38 {
return Err(DeriveError::Input(
"btc: invalid compressed WIF length".into(),
));
}
if decoded[33] != 0x01 {
return Err(DeriveError::Input(
"btc: WIF missing compressed flag".into(),
));
}
let checksum = double_sha256(&decoded[..34]);
if decoded[34..] != checksum[..4] {
return Err(DeriveError::Input("btc: invalid WIF checksum".into()));
}
let network = match decoded[0] {
0x80 => Network::Mainnet,
0xef => Network::Testnet,
_ => return Err(DeriveError::Input("btc: invalid WIF version".into())),
};
let mut key = Zeroizing::new([0u8; 32]);
key.copy_from_slice(&decoded[1..33]);
Ok((key, network))
})();
decoded.zeroize();
result
}