use alloc::vec;
use alloc::vec::Vec;
use crate::PrimeField64;
pub fn linear_hash_seq<F: PrimeField64, H: crate::Hash<F>>(input: &[F]) -> H::State {
assert!(H::WIDTH > 4);
assert!(H::RATE > 0, "linear_hash_seq requires RATE > 0 (H::RATE = {})", H::RATE);
let mut state: H::State = H::State::default();
let size = input.len();
let mut remaining = size;
while remaining > 0 {
let s = state.as_mut();
if remaining != size {
let mut carry = [F::ZERO; 4];
carry.copy_from_slice(&s[..4]);
s[H::RATE..H::RATE + 4].copy_from_slice(&carry);
}
let n = if remaining < H::RATE { remaining } else { H::RATE };
for i in 0..(H::RATE - n) {
s[n + i] = F::ZERO;
}
for i in 0..n {
s[i] = input[size - remaining + i];
}
H::hash(&mut state);
remaining -= n;
}
state
}
pub fn calculate_root_from_proof<F: PrimeField64, H: crate::Hash<F>>(
value: &mut H::State,
mp: &[Vec<F>],
idx: &mut u64,
offset: u64,
arity: u64,
) {
debug_assert_eq!(arity as usize * 4, H::WIDTH, "arity ({}) * 4 must equal H::WIDTH ({})", arity, H::WIDTH);
if offset == mp.len() as u64 {
return;
}
let curr_idx = *idx % arity;
*idx /= arity;
let mut inputs: H::State = H::State::default();
{
let in_slot = inputs.as_mut();
let mut p = 0;
for i in 0..arity {
if i == curr_idx {
continue;
}
for j in 0..4 {
in_slot[(i * 4 + j) as usize] = mp[offset as usize][4 * p + j as usize];
}
p += 1;
}
for j in 0..4 {
in_slot[(curr_idx * 4 + j) as usize] = value.as_ref()[j as usize];
}
}
H::hash(&mut inputs);
value.as_mut()[..4].copy_from_slice(&inputs.as_ref()[..4]);
calculate_root_from_proof::<F, H>(value, mp, idx, offset + 1, arity);
}
pub fn partial_merkle_tree<F: PrimeField64, H: crate::Hash<F>>(input: &[F], num_elements: u64, arity: u64) -> [F; 4] {
assert_eq!(
arity as usize * H::CAPACITY,
H::WIDTH,
"arity ({}) * CAPACITY ({}) must equal H::WIDTH ({})",
arity,
H::CAPACITY,
H::WIDTH
);
let mut num_nodes = num_elements;
let mut nodes_level = num_elements;
while nodes_level > 1 {
let extra_zeros = (arity - (nodes_level % arity)) % arity;
num_nodes += extra_zeros;
let next_n = nodes_level.div_ceil(arity);
num_nodes += next_n;
nodes_level = next_n;
}
let cap = H::CAPACITY as u64;
let sponge_w = arity * cap;
let mut cursor = vec![F::ZERO; (num_nodes * cap) as usize];
cursor[..(num_elements * cap) as usize].copy_from_slice(&input[..(num_elements * cap) as usize]);
let mut pending = num_elements;
let mut next_n = pending.div_ceil(arity);
let mut next_index = 0;
while pending > 1 {
let extra_zeros = (arity - (pending % arity)) % arity;
if extra_zeros > 0 {
let start = (next_index + pending * cap) as usize;
let end = start + (extra_zeros * cap) as usize;
cursor[start..end].fill(F::ZERO);
}
for i in 0..next_n {
let mut pol_input: H::State = H::State::default();
{
let child_start = (next_index + i * sponge_w) as usize;
let slot = pol_input.as_mut();
slot[..(sponge_w as usize)].copy_from_slice(&cursor[child_start..child_start + sponge_w as usize]);
}
H::hash(&mut pol_input);
let parent_start = (next_index + (pending + extra_zeros + i) * cap) as usize;
cursor[parent_start..parent_start + H::CAPACITY].copy_from_slice(&pol_input.as_ref()[..H::CAPACITY]);
}
next_index += (pending + extra_zeros) * cap;
pending = pending.div_ceil(arity);
next_n = pending.div_ceil(arity);
}
let mut root = [F::ZERO; 4];
root.copy_from_slice(&cursor[next_index as usize..next_index as usize + 4]);
root
}
pub fn verify_mt<F, LH, NH>(
root: &[F],
last_level: &[F],
mp: &[Vec<F>],
idx: u64,
v: &[F],
arity: u64,
last_level_verification: u64,
) -> bool
where
F: PrimeField64,
LH: crate::Hash<F>,
NH: crate::Hash<F>,
{
let leaf_hash = linear_hash_seq::<F, LH>(v);
let mut value: NH::State = NH::State::default();
value.as_mut()[..4].copy_from_slice(&leaf_hash.as_ref()[..4]);
let mut query_idx = idx;
calculate_root_from_proof::<F, NH>(&mut value, mp, &mut query_idx, 0, arity);
if last_level_verification == 0 {
for (i, r) in root.iter().enumerate().take(4) {
if value.as_ref()[i] != *r {
return false;
}
}
} else {
for i in 0..4 {
if value.as_ref()[i] != last_level[query_idx as usize * 4 + i] {
return false;
}
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{poseidon2_hash, Goldilocks, Poseidon2_16};
#[test]
fn partial_merkle_tree_single_parent_matches_full_compression() {
let input: [Goldilocks; 16] = core::array::from_fn(|i| Goldilocks::new(i as u64 + 1));
let expected = poseidon2_hash::<Goldilocks, Poseidon2_16, 16>(&input);
let root = partial_merkle_tree::<Goldilocks, Poseidon2_16>(&input, 4, 4);
assert_eq!(root, [expected[0], expected[1], expected[2], expected[3]]);
}
}