use crate::{as_unsigned_byte_array_pub, gen_indexes, K};
use blake_bench::reference::blake2b256;
use num_bigint::BigUint;
use num_traits::Zero;
pub const ROW: usize = 31;
pub fn gen_element(idx: u32, h: &[u8], m: &[u8]) -> [u8; ROW] {
let mut concat = idx.to_be_bytes().to_vec();
concat.extend_from_slice(h);
concat.extend_from_slice(m);
let full = blake2b256(&concat);
let mut out = [0u8; ROW];
out.copy_from_slice(&full[1..]); out
}
pub struct Table {
pub n: u32,
pub rows: Vec<u8>, pub h: [u8; 4],
pub m: Vec<u8>,
}
impl Table {
pub fn build(height: u32, version: u8) -> Table {
let n = crate::calc_big_n(version, height);
let h = crate::height_bytes(height);
let m = crate::big_m();
let mut rows = vec![0u8; n as usize * ROW];
for idx in 0..n {
let e = gen_element(idx, &h, &m);
let o = idx as usize * ROW;
rows[o..o + ROW].copy_from_slice(&e);
}
Table { n, rows, h, m }
}
#[inline]
pub fn row(&self, idx: u32) -> &[u8] {
let o = idx as usize * ROW;
&self.rows[o..o + ROW]
}
}
pub fn pow_hit_via_table(table: &Table, msg: &[u8], nonce: &[u8]) -> BigUint {
let mut c = msg.to_vec();
c.extend_from_slice(nonce);
let h1 = blake2b256(&c);
let pre_i8 = BigUint::from_bytes_be(&h1[24..32]);
let i = (pre_i8 % table.n).to_u32_digits().first().copied().unwrap_or(0);
let f = table.row(i);
let mut c = f.to_vec();
c.extend_from_slice(msg);
c.extend_from_slice(nonce);
let seed = blake2b256(&c);
let indexes = gen_indexes(&seed, table.n);
let mut f2 = BigUint::zero();
for idx in indexes.into_iter().take(K) {
f2 += BigUint::from_bytes_be(table.row(idx));
}
let array = as_unsigned_byte_array_pub(32, &f2);
BigUint::from_bytes_be(&blake2b256(&array))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pow_hit;
#[test]
fn table_path_equals_recompute_path() {
let height = 614400; let m = crate::big_m();
let small_n = 4096u32;
let h = crate::height_bytes(height);
let rows: Vec<u8> = (0..small_n)
.flat_map(|idx| gen_element(idx, &h, &m).to_vec())
.collect();
let table = Table { n: small_n, rows, h, m: m.clone() };
for nonce_val in [0u64, 1, 42, 0x3105, 999_999] {
let nonce = nonce_val.to_be_bytes();
let msg = blake2b256(&nonce_val.to_le_bytes()); let via_table = pow_hit_via_table(&table, &msg, &nonce);
let recompute = pow_hit(&msg, &nonce, &table.h, small_n, &m);
assert_eq!(via_table, recompute, "table path must equal recompute at nonce {nonce_val}");
}
}
}