use crate::field::{ExtField, Field, Fp, P};
pub type Digest = [u8; 32];
const LEAF_KEY: [u8; 32] = [0u8; 32];
const NODE_KEY: [u8; 32] = [1u8; 32];
fn hash_leaf<F: Field>(vals: &[F], salt: &[u8]) -> Digest {
let mut buf = Vec::with_capacity(8 * 4 * vals.len() + salt.len());
for v in vals {
v.to_bytes(&mut buf);
}
let mut h = blake3::Hasher::new_keyed(&LEAF_KEY);
h.update(&buf);
h.update(salt);
*h.finalize().as_bytes()
}
fn hash_node(l: &Digest, r: &Digest) -> Digest {
let mut h = blake3::Hasher::new_keyed(&NODE_KEY);
h.update(l);
h.update(r);
*h.finalize().as_bytes()
}
fn salt_stream(seed: &Digest, label: &[u8]) -> blake3::OutputReader {
let mut h = blake3::Hasher::new_keyed(seed);
h.update(b"kbfold/v0.3/leaf-salts");
h.update(&(label.len() as u64).to_le_bytes());
h.update(label);
h.finalize_xof()
}
pub struct MerkleTree {
layers: Vec<Vec<Digest>>,
salts: Vec<u8>,
salt_len: usize,
}
pub fn leaf_values<F: Field>(w: &[F], g: usize, t: usize) -> Vec<F> {
let leaves = w.len() >> g;
(0..1usize << g).map(|u| w[t + u * leaves]).collect()
}
pub fn fibre_leaf<F: Field>(w: &[F], g: usize, lambda: usize) -> Vec<F> {
let (t, u) = (lambda >> (g - 1), lambda & ((1 << (g - 1)) - 1));
let f = t + u * (w.len() >> g);
vec![w[f], w[f + w.len() / 2]]
}
impl MerkleTree {
pub fn new<F: Field>(w: &[F], g: usize, seed: &Digest, label: &[u8], salt_len: usize) -> Self {
let leaves = w.len() >> g;
assert!(g >= 1 && leaves >= 1 && leaves.is_power_of_two() && leaves << g == w.len());
Self::build(leaves, |t| leaf_values(w, g, t), seed, label, salt_len)
}
pub fn new_fibres<F: Field>(
w: &[F],
g: usize,
seed: &Digest,
label: &[u8],
salt_len: usize,
) -> Self {
let leaves = w.len() / 2;
assert!(g >= 1 && leaves.is_power_of_two() && w.len() >> g >= 1);
Self::build(leaves, |l| fibre_leaf(w, g, l), seed, label, salt_len)
}
fn build<F: Field>(
leaves: usize,
leaf: impl Fn(usize) -> Vec<F> + Sync + Send,
seed: &Digest,
label: &[u8],
salt_len: usize,
) -> Self {
let mut salts = vec![0u8; leaves * salt_len];
if salt_len > 0 {
salt_stream(seed, label).fill(&mut salts);
}
let hashes: Vec<Digest> = crate::par::map_range(leaves, |t| {
hash_leaf(&leaf(t), &salts[t * salt_len..(t + 1) * salt_len])
});
let mut layers = vec![hashes];
while layers.last().unwrap().len() > 1 {
let prev = layers.last().unwrap();
let next: Vec<Digest> = crate::par::map_range(prev.len() / 2, |i| {
hash_node(&prev[2 * i], &prev[2 * i + 1])
});
layers.push(next);
}
MerkleTree {
layers,
salts,
salt_len,
}
}
pub fn root(&self) -> Digest {
self.layers.last().unwrap()[0]
}
pub fn depth(&self) -> usize {
self.layers.len() - 1
}
pub fn salt(&self, t: usize) -> Vec<u8> {
self.salts[t * self.salt_len..(t + 1) * self.salt_len].to_vec()
}
pub fn multi_path(&self, positions: &[usize]) -> Vec<Digest> {
let mut nodes = Vec::new();
let mut cur: Vec<usize> = positions.to_vec();
for layer in &self.layers[..self.layers.len() - 1] {
let mut next = Vec::with_capacity(cur.len());
let mut i = 0;
while i < cur.len() {
let p = cur[i];
if i + 1 < cur.len() && cur[i + 1] == p ^ 1 {
i += 2;
} else {
nodes.push(layer[p ^ 1]);
i += 1;
}
next.push(p >> 1);
}
cur = next;
}
nodes
}
}
pub fn verify_multi<F: Field>(
root: &Digest,
depth: usize,
leaves: &[(usize, &[F], &[u8])],
nodes: &[Digest],
) -> bool {
if leaves.is_empty() || depth >= usize::BITS as usize {
return false;
}
if leaves.windows(2).any(|w| w[0].0 >= w[1].0) || leaves.last().unwrap().0 >> depth != 0 {
return false;
}
let mut cur: Vec<(usize, Digest)> = leaves
.iter()
.map(|&(p, v, s)| (p, hash_leaf(v, s)))
.collect();
let mut k = 0;
for _ in 0..depth {
let mut next = Vec::with_capacity(cur.len());
let mut i = 0;
while i < cur.len() {
let (p, h) = cur[i];
let parent = if i + 1 < cur.len() && cur[i + 1].0 == p ^ 1 {
i += 2;
hash_node(&h, &cur[i - 1].1)
} else {
let Some(sib) = nodes.get(k) else {
return false;
};
k += 1;
i += 1;
if p & 1 == 0 {
hash_node(&h, sib)
} else {
hash_node(sib, &h)
}
};
next.push((p >> 1, parent));
}
cur = next;
}
k == nodes.len() && cur.len() == 1 && cur[0].1 == *root
}
#[derive(Clone)]
pub struct Transcript {
state: Digest,
counter: u64,
}
impl Transcript {
pub fn new(label: &[u8]) -> Self {
Transcript {
state: *blake3::hash(label).as_bytes(),
counter: 0,
}
}
pub fn absorb(&mut self, tag: &[u8], data: &[u8]) {
let mut h = blake3::Hasher::new();
h.update(&self.state);
h.update(tag);
h.update(&(data.len() as u64).to_le_bytes());
h.update(data);
self.state = *h.finalize().as_bytes();
self.counter = 0;
}
pub fn absorb_field<F: Field>(&mut self, tag: &[u8], xs: &[F]) {
let mut buf = Vec::new();
for x in xs {
x.to_bytes(&mut buf);
}
self.absorb(tag, &buf);
}
pub fn squeeze(&mut self, len: usize) -> Vec<u8> {
let mut h = blake3::Hasher::new();
h.update(&self.state);
h.update(b"squeeze");
h.update(&self.counter.to_le_bytes());
self.counter += 1;
let mut out = vec![0u8; len];
h.finalize_xof().fill(&mut out);
out
}
pub fn challenge<E: ExtField>(&mut self) -> E {
let bytes = self.squeeze(8 * (E::DEGREE + 1));
let mut x: Vec<u64> = bytes
.chunks_exact(8)
.map(|c| u64::from_le_bytes(c.try_into().unwrap()))
.collect();
let mut digits = Vec::with_capacity(E::DEGREE);
for _ in 0..E::DEGREE {
let mut rem: u128 = 0;
for limb in x.iter_mut().rev() {
let cur = (rem << 64) | *limb as u128;
*limb = (cur / P as u128) as u64;
rem = cur % P as u128;
}
digits.push(Fp(rem as u64));
}
E::from_digits(&digits)
}
pub fn query_indices(&mut self, kappa: usize, bits: usize) -> Vec<usize> {
let bytes = self.squeeze((kappa * bits).div_ceil(8));
(0..kappa)
.map(|q| {
let mut idx = 0usize;
for b in 0..bits {
let pos = q * bits + b;
idx |= (((bytes[pos / 8] >> (pos % 8)) & 1) as usize) << b;
}
idx
})
.collect()
}
}
pub fn beta(degree: usize) -> usize {
64 * (degree + 1)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::field::{Fp2, Fp4};
#[test]
fn multi_openings() {
let w: Vec<Fp> = (0..256u64).map(Fp::new).collect();
for g in [1, 2, 4] {
for salt_len in [0, 32] {
let t = MerkleTree::new(&w, g, &[7u8; 32], b"t", salt_len);
let leaves = 256 >> g;
for pos in [
vec![0],
vec![3, 4],
vec![0, 1, 2, 3],
vec![1, 5, 6, leaves - 1],
] {
let vals: Vec<Vec<Fp>> = pos.iter().map(|&p| leaf_values(&w, g, p)).collect();
let salts: Vec<Vec<u8>> = pos.iter().map(|&p| t.salt(p)).collect();
let open: Vec<(usize, &[Fp], &[u8])> = pos
.iter()
.enumerate()
.map(|(i, &p)| (p, vals[i].as_slice(), salts[i].as_slice()))
.collect();
let nodes = t.multi_path(&pos);
assert!(verify_multi(&t.root(), t.depth(), &open, &nodes));
let mut more = nodes.clone();
more.push([0u8; 32]);
assert!(!verify_multi(&t.root(), t.depth(), &open, &more));
if !nodes.is_empty() {
assert!(!verify_multi(&t.root(), t.depth(), &open, &nodes[1..]));
let mut bad = nodes.clone();
bad[0][0] ^= 1;
assert!(!verify_multi(&t.root(), t.depth(), &open, &bad));
}
assert!(!verify_multi(&t.root(), t.depth() + 1, &open, &nodes));
let mut v2 = vals[0].clone();
v2[0] = v2[0] + Fp::ONE;
let mut open2 = open.clone();
open2[0].1 = &v2;
assert!(!verify_multi(&t.root(), t.depth(), &open2, &nodes));
}
}
}
let a = MerkleTree::new(&w, 1, &[1u8; 32], b"t", 32);
let b = MerkleTree::new(&w, 1, &[2u8; 32], b"t", 32);
assert_ne!(a.root(), b.root());
}
#[test]
fn challenge_map_digits() {
let mut tr = Transcript::new(b"t");
let mut copy = tr.clone();
let c: Fp4 = tr.challenge();
let bytes = copy.squeeze(40);
let mut x = num_bigint::BigUint::from_bytes_le(&bytes);
let p = num_bigint::BigUint::from(P);
let mut d = Vec::new();
for _ in 0..4 {
let r = &x % &p;
d.push(Fp(r.iter_u64_digits().next().unwrap_or(0)));
x /= &p;
}
assert_eq!(c, Fp4::from_digits(&d));
let c2: Fp2 = Transcript::new(b"u").challenge();
assert_ne!(c2, Fp2::ZERO);
assert_eq!(beta(2), 192);
assert_eq!(beta(4), 320);
}
}