use crate::chacha20_block;
use num_bigint::{BigUint, ToBigUint};
use num_traits::One;
use sha2::{Digest, Sha256};
use std::fmt::Write;
fn data_to_num3072(data: &[u8; 32]) -> BigUint {
let mut bytes384 = Vec::new();
for counter in 0..6 {
bytes384.extend(chacha20_block(data, &[0u8; 12], counter));
}
BigUint::from_bytes_le(&bytes384)
}
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct MuHash3072 {
numerator: BigUint,
denominator: BigUint,
modulus: BigUint,
}
impl Default for MuHash3072 {
fn default() -> Self {
Self::new()
}
}
impl MuHash3072 {
pub fn new() -> Self {
let modulus = (BigUint::one() << 3072) - 1103717u32.to_biguint().unwrap();
Self {
numerator: BigUint::one(),
denominator: BigUint::one(),
modulus,
}
}
pub fn insert(&mut self, data: &[u8]) {
let data_hash = Sha256::digest(data);
let num3072 = data_to_num3072(&data_hash.into());
self.numerator *= num3072;
self.numerator %= &self.modulus;
}
pub fn remove(&mut self, data: &[u8]) {
let data_hash = Sha256::digest(data);
let num3072 = data_to_num3072(&data_hash.into());
self.denominator *= num3072;
self.denominator %= &self.modulus;
}
pub fn digest(&self) -> Vec<u8> {
let denominator_inv = self
.denominator
.modpow(&(self.modulus.clone() - 2u32), &self.modulus);
let val = (&self.numerator * denominator_inv) % &self.modulus;
let mut bytes384 = val.to_bytes_le();
bytes384.resize(384, 0); Sha256::digest(&bytes384).to_vec()
}
pub fn txoutset_muhash(&self) -> String {
let finalized = self.digest();
finalized.iter().rev().fold(String::new(), |mut output, b| {
let _ = write!(output, "{b:02x}");
output
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_muhash() {
let mut muhash = MuHash3072::new();
muhash.insert(&[0x00; 32]);
let mut data = Vec::with_capacity(32);
data.push(0x01);
data.extend_from_slice(&[0x00; 31]);
muhash.insert(&data);
let mut data = Vec::with_capacity(32);
data.push(0x02);
data.extend_from_slice(&[0x00; 31]);
muhash.remove(&data);
let finalized = muhash.digest();
assert_eq!(
finalized
.iter()
.rev()
.map(|b| format!("{:02x}", b))
.collect::<String>(),
"10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863"
);
}
}