use alloc::vec::Vec;
use miden_air::ProcessorAir;
use miden_core::{Felt, FieldElement, QuadFelt, ToElements, WORD_SIZE, Word};
use miden_processor::crypto::RpoRandomCoin;
use miden_utils_testing::{
MIN_STACK_DEPTH, VerifierError,
crypto::{MerkleStore, RandomCoin, Rpo256},
};
use winter_air::{
Air,
proof::{Proof, merge_ood_evaluations},
};
use winter_fri::VerifierChannel as FriVerifierChannel;
mod channel;
use channel::VerifierChannel;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct VerifierData {
pub initial_stack: Vec<u64>,
pub advice_stack: Vec<u64>,
pub store: MerkleStore,
pub advice_map: Vec<(Word, Vec<Felt>)>,
}
pub fn generate_advice_inputs(
proof: Proof,
pub_inputs: <ProcessorAir as Air>::PublicInputs,
) -> Result<VerifierData, VerifierError> {
let pub_inputs_elements = pub_inputs.to_elements();
let num_elements_pi = pub_inputs_elements.len();
let digests_elements = num_elements_pi - MIN_STACK_DEPTH * 2 - 2 * WORD_SIZE;
assert_eq!(digests_elements % WORD_SIZE, 0);
let num_kernel_procedures_digests = digests_elements / (2 * WORD_SIZE);
let initial_stack = vec![
proof.context.options().grinding_factor() as u64,
proof.context.options().num_queries() as u64,
proof.context.trace_info().length().ilog2() as u64,
];
let mut advice_stack = vec![digests_elements as u64];
let mut public_coin_seed = proof.context.to_elements();
public_coin_seed.extend_from_slice(&pub_inputs_elements);
let pub_inputs_int: Vec<u64> = pub_inputs_elements.iter().map(|a| a.as_int()).collect();
advice_stack.extend_from_slice(&pub_inputs_int);
let aux_rand_insertion_index = advice_stack.len();
advice_stack.extend_from_slice(&[0, 0, 0, 0]);
advice_stack.push(num_kernel_procedures_digests as u64);
let air = ProcessorAir::new(proof.trace_info().to_owned(), pub_inputs, proof.options().clone());
let seed_digest = Rpo256::hash_elements(&public_coin_seed);
let mut public_coin: RpoRandomCoin = RpoRandomCoin::new(seed_digest);
let mut channel = VerifierChannel::new(&air, proof)?;
let trace_commitments = channel.read_trace_commitments();
public_coin.reseed(trace_commitments[0]);
advice_stack.extend_from_slice(&digest_to_int_vec(trace_commitments));
let mut aux_trace_rand_elements = vec![];
for commitment in trace_commitments.iter().skip(1) {
let rand_elements: Vec<QuadFelt> = air
.get_aux_rand_elements(&mut public_coin)
.map_err(|_| VerifierError::RandomCoinError)?
.rand_elements()
.to_vec();
aux_trace_rand_elements.push(rand_elements);
public_coin.reseed(*commitment);
}
let alpha = aux_trace_rand_elements[0][0].to_owned();
let beta = aux_trace_rand_elements[0][2].to_owned();
advice_stack[aux_rand_insertion_index] = QuadFelt::base_element(&beta, 0).as_int();
advice_stack[aux_rand_insertion_index + 1] = QuadFelt::base_element(&beta, 1).as_int();
advice_stack[aux_rand_insertion_index + 2] = QuadFelt::base_element(&alpha, 0).as_int();
advice_stack[aux_rand_insertion_index + 3] = QuadFelt::base_element(&alpha, 1).as_int();
let _constraint_coeffs: winter_air::ConstraintCompositionCoefficients<QuadFelt> = air
.get_constraint_composition_coefficients(&mut public_coin)
.map_err(|_| VerifierError::RandomCoinError)?;
let constraint_commitment = channel.read_constraint_commitment();
advice_stack.extend_from_slice(&digest_to_int_vec(&[constraint_commitment]));
public_coin.reseed(constraint_commitment);
let _z: QuadFelt = public_coin.draw().unwrap();
let ood_trace_frame = channel.read_ood_trace_frame();
let ood_constraint_evaluations = channel.read_ood_constraint_evaluations();
let ood_evals = merge_ood_evaluations(&ood_trace_frame, &ood_constraint_evaluations);
let alpha_deep_index = advice_stack.len();
advice_stack.extend_from_slice(&[0, 0]);
advice_stack.extend_from_slice(&to_int_vec(&ood_evals));
let ood_digest = Rpo256::hash_elements(&ood_evals);
public_coin.reseed(ood_digest);
let fri_commitments_digests = channel.read_fri_layer_commitments();
let poly = channel.read_remainder().unwrap();
let fri_commitments: Vec<u64> = digest_to_int_vec(&fri_commitments_digests);
advice_stack.extend_from_slice(&fri_commitments);
advice_stack.extend_from_slice(&to_int_vec(&poly));
let deep_coefficients = air
.get_deep_composition_coefficients::<QuadFelt, RpoRandomCoin>(&mut public_coin)
.map_err(|_| VerifierError::RandomCoinError)?;
assert_eq!(
deep_coefficients.constraints[deep_coefficients.constraints.len() - 1],
QuadFelt::ONE
);
let alpha_deep = deep_coefficients.constraints[deep_coefficients.constraints.len() - 2];
advice_stack[alpha_deep_index] = alpha_deep.base_element(0).as_int();
advice_stack[alpha_deep_index + 1] = alpha_deep.base_element(1).as_int();
let layer_commitments = fri_commitments_digests.clone();
for commitment in layer_commitments.iter() {
public_coin.reseed(*commitment);
let _alpha: QuadFelt = public_coin.draw().expect("failed to draw random indices");
}
let pow_nonce = channel.read_pow_nonce();
let mut query_positions = public_coin
.draw_integers(air.options().num_queries(), air.lde_domain_size(), pow_nonce)
.map_err(|_| VerifierError::RandomCoinError)?;
advice_stack.extend_from_slice(&[pow_nonce]);
query_positions.sort();
query_positions.dedup();
let (mut main_aux_adv_map, mut partial_trees_traces) =
channel.read_queried_trace_states(&query_positions)?;
let (mut constraint_adv_map, partial_tree_constraint) =
channel.read_constraint_evaluations(&query_positions)?;
let (mut partial_trees_fri, mut fri_adv_map) = channel.unbatch_fri_layer_proofs::<4>(
&query_positions,
air.lde_domain_size(),
fri_commitments_digests,
);
main_aux_adv_map.append(&mut constraint_adv_map);
main_aux_adv_map.append(&mut fri_adv_map);
partial_trees_fri.append(&mut partial_trees_traces);
partial_trees_fri.push(partial_tree_constraint);
let mut store = MerkleStore::new();
for partial_tree in &partial_trees_fri {
store.extend(partial_tree.inner_nodes());
}
Ok(VerifierData {
initial_stack,
advice_stack,
store,
advice_map: main_aux_adv_map,
})
}
pub fn digest_to_int_vec(digest: &[Word]) -> Vec<u64> {
digest
.iter()
.flat_map(|digest| digest.as_elements().iter().map(|e| e.as_int()))
.collect()
}
pub fn to_int_vec(ext_felts: &[QuadFelt]) -> Vec<u64> {
QuadFelt::slice_as_base_elements(ext_felts).iter().map(|e| e.as_int()).collect()
}