plonky2/src/iop/challenger.rs

401 lines
13 KiB
Rust
Raw Normal View History

2021-06-23 19:15:52 +02:00
use std::convert::TryInto;
2021-11-05 10:56:23 +01:00
use std::marker::PhantomData;
2021-06-23 19:15:52 +02:00
2021-12-16 15:20:45 +01:00
use crate::field::extension_field::target::ExtensionTarget;
2021-05-18 15:22:06 +02:00
use crate::field::extension_field::{Extendable, FieldExtension};
use crate::field::field_types::RichField;
2021-08-10 13:33:44 +02:00
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget};
2021-11-05 10:56:23 +01:00
use crate::hash::hashing::{PlonkyPermutation, SPONGE_RATE, SPONGE_WIDTH};
2021-08-10 13:33:44 +02:00
use crate::hash::merkle_tree::MerkleCap;
use crate::iop::target::Target;
use crate::plonk::circuit_builder::CircuitBuilder;
2021-11-05 15:31:24 +01:00
use crate::plonk::config::{AlgebraicHasher, Hasher};
use crate::plonk::proof::{OpeningSet, OpeningSetTarget};
/// Observes prover messages, and generates challenges by hashing the transcript, a la Fiat-Shamir.
2021-03-31 21:15:24 -07:00
#[derive(Clone)]
2021-11-05 10:56:23 +01:00
pub struct Challenger<F: RichField, H: AlgebraicHasher<F>> {
2021-03-31 21:15:24 -07:00
sponge_state: [F; SPONGE_WIDTH],
input_buffer: Vec<F>,
output_buffer: Vec<F>,
2021-11-05 10:56:23 +01:00
_phantom: PhantomData<H>,
2021-03-31 21:15:24 -07:00
}
/// Observes prover messages, and generates verifier challenges based on the transcript.
///
/// The implementation is roughly based on a duplex sponge with a Rescue permutation. Note that in
/// each round, our sponge can absorb an arbitrary number of prover messages and generate an
/// arbitrary number of verifier challenges. This might appear to diverge from the duplex sponge
/// design, but it can be viewed as a duplex sponge whose inputs are sometimes zero (when we perform
/// multiple squeezes) and whose outputs are sometimes ignored (when we perform multiple
/// absorptions). Thus the security properties of a duplex sponge still apply to our design.
2021-11-05 10:56:23 +01:00
impl<F: RichField, H: AlgebraicHasher<F>> Challenger<F, H> {
pub fn new() -> Challenger<F, H> {
2021-03-31 21:15:24 -07:00
Challenger {
sponge_state: [F::ZERO; SPONGE_WIDTH],
input_buffer: Vec::new(),
output_buffer: Vec::new(),
2021-11-05 10:56:23 +01:00
_phantom: Default::default(),
2021-03-31 21:15:24 -07:00
}
}
pub fn observe_element(&mut self, element: F) {
// Any buffered outputs are now invalid, since they wouldn't reflect this input.
self.output_buffer.clear();
self.input_buffer.push(element);
}
2021-05-18 15:22:06 +02:00
pub fn observe_extension_element<const D: usize>(&mut self, element: &F::Extension)
where
F: Extendable<D>,
{
2021-06-14 13:26:22 +02:00
self.observe_elements(&element.to_basefield_array());
2021-05-18 15:22:06 +02:00
}
2021-03-31 21:15:24 -07:00
pub fn observe_elements(&mut self, elements: &[F]) {
for &element in elements {
self.observe_element(element);
}
}
2021-05-18 15:22:06 +02:00
pub fn observe_extension_elements<const D: usize>(&mut self, elements: &[F::Extension])
where
F: Extendable<D>,
{
for element in elements {
self.observe_extension_element(element);
}
}
pub fn observe_opening_set<const D: usize>(&mut self, os: &OpeningSet<F, D>)
where
F: Extendable<D>,
{
let OpeningSet {
constants,
2021-07-12 14:25:28 +02:00
plonk_sigmas,
wires,
plonk_zs,
plonk_zs_right,
2021-07-01 17:34:00 +02:00
partial_products,
quotient_polys,
} = os;
for v in &[
constants,
2021-07-12 14:25:28 +02:00
plonk_sigmas,
wires,
plonk_zs,
plonk_zs_right,
2021-07-01 17:34:00 +02:00
partial_products,
quotient_polys,
] {
self.observe_extension_elements(v);
}
}
2021-11-05 10:56:23 +01:00
pub fn observe_hash<OH: Hasher<F>>(&mut self, hash: OH::Hash) {
let felts: Vec<F> = hash.into();
self.observe_elements(&felts)
2021-03-31 21:15:24 -07:00
}
2021-11-05 10:56:23 +01:00
pub fn observe_cap<OH: Hasher<F>>(&mut self, cap: &MerkleCap<F, OH>) {
for &hash in &cap.0 {
self.observe_hash::<OH>(hash);
2021-08-10 13:33:44 +02:00
}
}
2021-11-05 12:02:33 +01:00
pub fn get_challenge(&mut self) -> F {
self.absorb_buffered_inputs();
2021-03-31 21:15:24 -07:00
if self.output_buffer.is_empty() {
// Evaluate the permutation to produce `r` new outputs.
2021-11-05 12:02:33 +01:00
self.sponge_state = H::Permutation::permute(self.sponge_state);
2021-03-31 21:15:24 -07:00
self.output_buffer = self.sponge_state[0..SPONGE_RATE].to_vec();
}
self.output_buffer
.pop()
.expect("Output buffer should be non-empty")
}
2021-11-05 12:02:33 +01:00
pub fn get_n_challenges(&mut self, n: usize) -> Vec<F> {
(0..n).map(|_| self.get_challenge()).collect()
2021-03-31 21:15:24 -07:00
}
2021-11-05 12:02:33 +01:00
pub fn get_hash(&mut self) -> HashOut<F> {
HashOut {
2021-04-22 22:21:24 +02:00
elements: [
2021-11-05 12:02:33 +01:00
self.get_challenge(),
self.get_challenge(),
self.get_challenge(),
self.get_challenge(),
2021-04-22 22:21:24 +02:00
],
}
}
2021-11-05 12:02:33 +01:00
pub fn get_extension_challenge<const D: usize>(&mut self) -> F::Extension
2021-05-18 15:22:06 +02:00
where
F: Extendable<D>,
{
let mut arr = [F::ZERO; D];
2021-11-05 12:02:33 +01:00
arr.copy_from_slice(&self.get_n_challenges(D));
2021-05-18 15:22:06 +02:00
F::Extension::from_basefield_array(arr)
}
2021-11-05 12:02:33 +01:00
pub fn get_n_extension_challenges<const D: usize>(&mut self, n: usize) -> Vec<F::Extension>
2021-05-18 16:23:44 +02:00
where
F: Extendable<D>,
{
2021-11-05 10:56:23 +01:00
(0..n)
2021-11-05 12:02:33 +01:00
.map(|_| self.get_extension_challenge::<D>())
2021-11-05 10:56:23 +01:00
.collect()
2021-05-18 16:23:44 +02:00
}
2021-03-31 21:15:24 -07:00
/// Absorb any buffered inputs. After calling this, the input buffer will be empty.
2021-11-05 12:02:33 +01:00
fn absorb_buffered_inputs(&mut self) {
if self.input_buffer.is_empty() {
return;
}
2021-03-31 21:15:24 -07:00
for input_chunk in self.input_buffer.chunks(SPONGE_RATE) {
2021-04-12 10:38:07 +02:00
// Overwrite the first r elements with the inputs. This differs from a standard sponge,
// where we would xor or add in the inputs. This is a well-known variant, though,
// sometimes called "overwrite mode".
2021-03-31 21:15:24 -07:00
for (i, &input) in input_chunk.iter().enumerate() {
2021-04-12 10:38:07 +02:00
self.sponge_state[i] = input;
2021-03-31 21:15:24 -07:00
}
// Apply the permutation.
2021-11-05 12:02:33 +01:00
self.sponge_state = H::Permutation::permute(self.sponge_state);
2021-03-31 21:15:24 -07:00
}
self.output_buffer = self.sponge_state[0..SPONGE_RATE].to_vec();
self.input_buffer.clear();
}
}
2021-11-05 10:56:23 +01:00
impl<F: RichField, H: AlgebraicHasher<F>> Default for Challenger<F, H> {
2021-04-23 12:35:19 -07:00
fn default() -> Self {
Self::new()
}
}
2021-03-31 21:15:24 -07:00
/// A recursive version of `Challenger`.
2021-11-05 10:56:23 +01:00
pub struct RecursiveChallenger<F: Extendable<D>, H: AlgebraicHasher<F>, const D: usize> {
2021-03-31 21:15:24 -07:00
sponge_state: [Target; SPONGE_WIDTH],
input_buffer: Vec<Target>,
output_buffer: Vec<Target>,
}
2021-11-05 10:56:23 +01:00
impl<F: Extendable<D>, H: AlgebraicHasher<F>, const D: usize> RecursiveChallenger<F, H, D> {
pub(crate) fn new(builder: &mut CircuitBuilder<F, D>) -> Self {
2021-03-31 21:15:24 -07:00
let zero = builder.zero();
RecursiveChallenger {
sponge_state: [zero; SPONGE_WIDTH],
input_buffer: Vec::new(),
output_buffer: Vec::new(),
}
}
pub(crate) fn observe_element(&mut self, target: Target) {
// Any buffered outputs are now invalid, since they wouldn't reflect this input.
self.output_buffer.clear();
self.input_buffer.push(target);
}
pub(crate) fn observe_elements(&mut self, targets: &[Target]) {
for &target in targets {
self.observe_element(target);
}
}
2021-11-05 10:56:23 +01:00
pub fn observe_opening_set(&mut self, os: &OpeningSetTarget<D>) {
2021-07-12 14:25:28 +02:00
let OpeningSetTarget {
constants,
plonk_sigmas,
wires,
plonk_zs,
plonk_zs_right,
partial_products,
quotient_polys,
} = os;
for v in &[
constants,
plonk_sigmas,
wires,
plonk_zs,
plonk_zs_right,
partial_products,
quotient_polys,
] {
self.observe_extension_elements(v);
}
}
pub fn observe_hash(&mut self, hash: &HashOutTarget) {
2021-03-31 21:15:24 -07:00
self.observe_elements(&hash.elements)
}
2021-08-10 13:33:44 +02:00
pub fn observe_cap(&mut self, cap: &MerkleCapTarget) {
for hash in &cap.0 {
self.observe_hash(hash)
}
}
2021-11-05 10:56:23 +01:00
pub fn observe_extension_element(&mut self, element: ExtensionTarget<D>) {
2021-06-14 13:26:22 +02:00
self.observe_elements(&element.0);
}
2021-11-05 10:56:23 +01:00
pub fn observe_extension_elements(&mut self, elements: &[ExtensionTarget<D>]) {
2021-06-14 13:26:22 +02:00
for &element in elements {
self.observe_extension_element(element);
}
}
2021-11-05 10:56:23 +01:00
pub(crate) fn get_challenge(&mut self, builder: &mut CircuitBuilder<F, D>) -> Target {
2021-03-31 21:15:24 -07:00
self.absorb_buffered_inputs(builder);
if self.output_buffer.is_empty() {
// Evaluate the permutation to produce `r` new outputs.
2021-11-05 10:56:23 +01:00
self.sponge_state = builder.permute::<H>(self.sponge_state);
2021-03-31 21:15:24 -07:00
self.output_buffer = self.sponge_state[0..SPONGE_RATE].to_vec();
}
self.output_buffer
.pop()
.expect("Output buffer should be non-empty")
}
2021-11-05 10:56:23 +01:00
pub(crate) fn get_n_challenges(
2021-03-31 21:15:24 -07:00
&mut self,
builder: &mut CircuitBuilder<F, D>,
2021-03-31 21:15:24 -07:00
n: usize,
) -> Vec<Target> {
(0..n).map(|_| self.get_challenge(builder)).collect()
}
2021-11-05 10:56:23 +01:00
pub fn get_hash(&mut self, builder: &mut CircuitBuilder<F, D>) -> HashOutTarget {
HashOutTarget {
2021-06-04 15:40:54 +02:00
elements: [
self.get_challenge(builder),
self.get_challenge(builder),
self.get_challenge(builder),
self.get_challenge(builder),
],
}
}
2021-11-05 10:56:23 +01:00
pub fn get_extension_challenge(
2021-06-14 13:26:22 +02:00
&mut self,
builder: &mut CircuitBuilder<F, D>,
) -> ExtensionTarget<D> {
self.get_n_challenges(builder, D).try_into().unwrap()
}
2021-03-31 21:15:24 -07:00
/// Absorb any buffered inputs. After calling this, the input buffer will be empty.
2021-11-05 10:56:23 +01:00
fn absorb_buffered_inputs(&mut self, builder: &mut CircuitBuilder<F, D>) {
if self.input_buffer.is_empty() {
return;
}
2021-03-31 21:15:24 -07:00
for input_chunk in self.input_buffer.chunks(SPONGE_RATE) {
2021-04-12 10:38:07 +02:00
// Overwrite the first r elements with the inputs. This differs from a standard sponge,
// where we would xor or add in the inputs. This is a well-known variant, though,
// sometimes called "overwrite mode".
2021-03-31 21:15:24 -07:00
for (i, &input) in input_chunk.iter().enumerate() {
2021-04-12 10:38:07 +02:00
self.sponge_state[i] = input;
2021-03-31 21:15:24 -07:00
}
// Apply the permutation.
2021-11-05 10:56:23 +01:00
self.sponge_state = builder.permute::<H>(self.sponge_state);
2021-03-31 21:15:24 -07:00
}
self.output_buffer = self.sponge_state[0..SPONGE_RATE].to_vec();
self.input_buffer.clear();
}
}
#[cfg(test)]
mod tests {
use crate::field::field_types::Field;
use crate::iop::challenger::{Challenger, RecursiveChallenger};
use crate::iop::generator::generate_partial_witness;
use crate::iop::target::Target;
use crate::iop::witness::{PartialWitness, Witness};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
2021-11-05 10:56:23 +01:00
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
2021-03-31 21:15:24 -07:00
#[test]
fn no_duplicate_challenges() {
2021-11-05 10:56:23 +01:00
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let mut challenger = Challenger::<F, <C as GenericConfig<D>>::InnerHasher>::new();
let mut challenges = Vec::new();
for i in 1..10 {
2021-11-05 12:02:33 +01:00
challenges.extend(challenger.get_n_challenges(i));
challenger.observe_element(F::rand());
}
let dedup_challenges = {
let mut dedup = challenges.clone();
dedup.dedup();
dedup
};
assert_eq!(dedup_challenges, challenges);
}
2021-03-31 21:15:24 -07:00
/// Tests for consistency between `Challenger` and `RecursiveChallenger`.
#[test]
fn test_consistency() {
2021-11-05 10:56:23 +01:00
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
2021-03-31 21:15:24 -07:00
// These are mostly arbitrary, but we want to test some rounds with enough inputs/outputs to
// trigger multiple absorptions/squeezes.
let num_inputs_per_round = vec![2, 5, 3];
let num_outputs_per_round = vec![1, 2, 4];
// Generate random input messages.
let inputs_per_round: Vec<Vec<F>> = num_inputs_per_round
.iter()
2021-05-06 15:14:43 +02:00
.map(|&n| F::rand_vec(n))
2021-03-31 21:15:24 -07:00
.collect();
2021-11-05 10:56:23 +01:00
let mut challenger = Challenger::<F, <C as GenericConfig<D>>::InnerHasher>::new();
2021-03-31 21:15:24 -07:00
let mut outputs_per_round: Vec<Vec<F>> = Vec::new();
for (r, inputs) in inputs_per_round.iter().enumerate() {
challenger.observe_elements(inputs);
2021-11-05 12:02:33 +01:00
outputs_per_round.push(challenger.get_n_challenges(num_outputs_per_round[r]));
2021-03-31 21:15:24 -07:00
}
let config = CircuitConfig::standard_recursion_config();
2021-11-05 10:56:23 +01:00
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut recursive_challenger =
RecursiveChallenger::<F, <C as GenericConfig<D>>::InnerHasher, D>::new(&mut builder);
2021-04-21 22:31:45 +02:00
let mut recursive_outputs_per_round: Vec<Vec<Target>> = Vec::new();
2021-03-31 21:15:24 -07:00
for (r, inputs) in inputs_per_round.iter().enumerate() {
2021-04-02 14:00:26 -07:00
recursive_challenger.observe_elements(&builder.constants(inputs));
2021-03-31 21:15:24 -07:00
recursive_outputs_per_round.push(
recursive_challenger.get_n_challenges(&mut builder, num_outputs_per_round[r]),
);
}
2021-11-05 10:56:23 +01:00
let circuit = builder.build::<C>();
let inputs = PartialWitness::new();
let witness = generate_partial_witness(inputs, &circuit.prover_only, &circuit.common);
2021-03-31 21:15:24 -07:00
let recursive_output_values_per_round: Vec<Vec<F>> = recursive_outputs_per_round
.iter()
.map(|outputs| witness.get_targets(outputs))
2021-03-31 21:15:24 -07:00
.collect();
assert_eq!(outputs_per_round, recursive_output_values_per_round);
}
}