use std::cmp::Reverse;
use std::collections::BTreeMap;
use itertools::Itertools;
use tracing::{span, Level};
use super::ops::MerkleOps;
use crate::core::fields::m31::BaseField;
use crate::core::utils::PeekableExt;
use crate::core::vcs::utils::{next_decommitment_node, option_flatten_peekable};
use crate::core::vcs::verifier::MerkleDecommitment;
use crate::core::vcs::MerkleHasher;
use crate::prover::backend::{Col, Column};
pub struct MerkleProver<B: MerkleOps<H>, H: MerkleHasher> {
pub layers: Vec<Col<B, H::Hash>>,
}
impl<B: MerkleOps<H>, H: MerkleHasher> MerkleProver<B, H> {
pub fn commit(columns: Vec<&Col<B, BaseField>>) -> Self {
let _span = span!(Level::TRACE, "Merkle", class = "MerkleCommitment").entered();
if columns.is_empty() {
return Self {
layers: vec![B::commit_on_layer(0, None, &[])],
};
}
let columns = &mut columns
.into_iter()
.sorted_by_key(|c| Reverse(c.len()))
.peekable();
let mut layers: Vec<Col<B, H::Hash>> = Vec::new();
let max_log_size = columns.peek().unwrap().len().ilog2();
for log_size in (0..=max_log_size).rev() {
let layer_columns = columns
.peek_take_while(|column| column.len().ilog2() == log_size)
.collect_vec();
layers.push(B::commit_on_layer(log_size, layers.last(), &layer_columns));
}
layers.reverse();
Self { layers }
}
pub fn decommit(
&self,
queries_per_log_size: &BTreeMap<u32, Vec<usize>>,
columns: Vec<&Col<B, BaseField>>,
) -> (Vec<BaseField>, MerkleDecommitment<H>) {
let mut queried_values = vec![];
let mut decommitment = MerkleDecommitment::empty();
let mut columns_by_layer = columns
.iter()
.sorted_by_key(|c| Reverse(c.len()))
.peekable();
let mut last_layer_queries = vec![];
for layer_log_size in (0..self.layers.len() as u32).rev() {
let mut layer_total_queries = vec![];
let layer_columns = columns_by_layer
.peek_take_while(|column| column.len().ilog2() == layer_log_size)
.collect_vec();
let previous_layer_hashes = self.layers.get(layer_log_size as usize + 1);
let mut prev_layer_queries = last_layer_queries.into_iter().peekable();
let mut layer_column_queries =
option_flatten_peekable(queries_per_log_size.get(&layer_log_size));
while let Some(node_index) =
next_decommitment_node(&mut prev_layer_queries, &mut layer_column_queries)
{
if let Some(previous_layer_hashes) = previous_layer_hashes {
if prev_layer_queries.next_if_eq(&(2 * node_index)).is_none() {
decommitment
.hash_witness
.push(previous_layer_hashes.at(2 * node_index));
}
if prev_layer_queries
.next_if_eq(&(2 * node_index + 1))
.is_none()
{
decommitment
.hash_witness
.push(previous_layer_hashes.at(2 * node_index + 1));
}
}
let node_values = layer_columns.iter().map(|c| c.at(node_index));
if layer_column_queries.next_if_eq(&node_index).is_some() {
queried_values.extend(node_values);
} else {
decommitment.column_witness.extend(node_values);
}
layer_total_queries.push(node_index);
}
last_layer_queries = layer_total_queries;
}
(queried_values, decommitment)
}
pub fn root(&self) -> H::Hash {
self.layers.first().unwrap().at(0)
}
}