use core::str::FromStr;
use bee_common_derive::{SecretDebug, SecretDisplay, SecretDrop};
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::{
encoding::ternary::{Btrit, T1B1Buf, Trit, TritBuf, Trits, TryteBuf, T1B1},
hashes::ternary::{kerl::Kerl, Sponge, HASH_LENGTH},
};
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
InvalidLength(usize),
InvalidTrytes,
FailedSpongeOperation,
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Error::InvalidLength(length) => write!(f, "Invalid seed length, should be 243 trits, was {0}.", length),
Error::InvalidTrytes => write!(f, "Invalid seed trytes."),
Error::FailedSpongeOperation => write!(f, "Failed sponge operation."),
}
}
}
#[derive(SecretDebug, SecretDisplay, SecretDrop)]
pub struct Seed(TritBuf<T1B1Buf>);
impl Zeroize for Seed {
fn zeroize(&mut self) {
unsafe { self.0.as_i8_slice_mut().zeroize() }
}
}
impl ZeroizeOnDrop for Seed {}
impl Seed {
pub fn subseed(&self, index: usize) -> Self {
let mut subseed = self.0.clone();
for _ in 0..index {
for t in subseed.iter_mut() {
if let Some(ntrit) = t.checked_increment() {
*t = ntrit;
break;
} else {
*t = Btrit::NegOne;
}
}
}
Self(Kerl::default().digest(&subseed).unwrap())
}
pub fn from_trits(buf: TritBuf<T1B1Buf>) -> Result<Self, Error> {
if buf.len() != HASH_LENGTH {
return Err(Error::InvalidLength(buf.len()));
}
Ok(Self(buf))
}
pub fn as_trits(&self) -> &Trits<T1B1> {
&self.0
}
}
impl FromStr for Seed {
type Err = Error;
fn from_str(str: &str) -> Result<Self, Self::Err> {
if str.len() != HASH_LENGTH / 3 {
return Err(Error::InvalidLength(str.len() * 3));
}
Ok(Self(
TryteBuf::try_from_str(str)
.map_err(|_| Error::InvalidTrytes)?
.as_trits()
.encode::<T1B1Buf>(),
))
}
}