mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-05-23 10:19:27 +00:00
Make hash functions generic
This commit is contained in:
parent
2ca00a9ad4
commit
e857c020bf
@ -171,7 +171,7 @@ mod tests {
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ethereum_types::U256;
|
use ethereum_types::U256;
|
||||||
use plonky2::field::types::{Field, PrimeField64};
|
use plonky2::field::types::{Field, PrimeField64};
|
||||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use rand::{Rng, SeedableRng};
|
use rand::{Rng, SeedableRng};
|
||||||
use rand_chacha::ChaCha8Rng;
|
use rand_chacha::ChaCha8Rng;
|
||||||
|
|
||||||
@ -183,7 +183,9 @@ mod tests {
|
|||||||
fn degree() -> Result<()> {
|
fn degree() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = ArithmeticStark<F, D>;
|
type S = ArithmeticStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
@ -196,20 +198,24 @@ mod tests {
|
|||||||
fn circuit() -> Result<()> {
|
fn circuit() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = ArithmeticStark<F, D>;
|
type S = ArithmeticStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
f: Default::default(),
|
f: Default::default(),
|
||||||
};
|
};
|
||||||
test_stark_circuit_constraints::<F, C, S, D>(stark)
|
test_stark_circuit_constraints::<F, HCO, HCI, C, S, D>(stark)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_trace() {
|
fn basic_trace() {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = ArithmeticStark<F, D>;
|
type S = ArithmeticStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
@ -295,7 +301,9 @@ mod tests {
|
|||||||
fn big_traces() {
|
fn big_traces() {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = ArithmeticStark<F, D>;
|
type S = ArithmeticStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
|
|||||||
@ -199,7 +199,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
use crate::cpu::cpu_stark::CpuStark;
|
use crate::cpu::cpu_stark::CpuStark;
|
||||||
use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree};
|
use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree};
|
||||||
@ -208,7 +208,9 @@ mod tests {
|
|||||||
fn test_stark_degree() -> Result<()> {
|
fn test_stark_degree() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = CpuStark<F, D>;
|
type S = CpuStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
@ -221,12 +223,14 @@ mod tests {
|
|||||||
fn test_stark_circuit() -> Result<()> {
|
fn test_stark_circuit() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = CpuStark<F, D>;
|
type S = CpuStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
f: Default::default(),
|
f: Default::default(),
|
||||||
};
|
};
|
||||||
test_stark_circuit_constraints::<F, C, S, D>(stark)
|
test_stark_circuit_constraints::<F, HCO, HCI, C, S, D>(stark)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ use plonky2::field::packed::PackedField;
|
|||||||
use plonky2::field::polynomial::PolynomialValues;
|
use plonky2::field::polynomial::PolynomialValues;
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::ext_target::ExtensionTarget;
|
use plonky2::iop::ext_target::ExtensionTarget;
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
@ -322,12 +323,19 @@ where
|
|||||||
impl<'a, F: RichField + Extendable<D>, const D: usize>
|
impl<'a, F: RichField + Extendable<D>, const D: usize>
|
||||||
CtlCheckVars<'a, F, F::Extension, F::Extension, D>
|
CtlCheckVars<'a, F, F::Extension, F::Extension, D>
|
||||||
{
|
{
|
||||||
pub(crate) fn from_proofs<C: GenericConfig<D, F = F>>(
|
pub(crate) fn from_proofs<
|
||||||
proofs: &[StarkProofWithMetadata<F, C, D>; NUM_TABLES],
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
>(
|
||||||
|
proofs: &[StarkProofWithMetadata<F, HCO, HCI, C, D>; NUM_TABLES],
|
||||||
cross_table_lookups: &'a [CrossTableLookup<F>],
|
cross_table_lookups: &'a [CrossTableLookup<F>],
|
||||||
ctl_challenges: &'a GrandProductChallengeSet<F>,
|
ctl_challenges: &'a GrandProductChallengeSet<F>,
|
||||||
num_permutation_zs: &[usize; NUM_TABLES],
|
num_permutation_zs: &[usize; NUM_TABLES],
|
||||||
) -> [Vec<Self>; NUM_TABLES] {
|
) -> [Vec<Self>; NUM_TABLES]
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
let mut ctl_zs = proofs
|
let mut ctl_zs = proofs
|
||||||
.iter()
|
.iter()
|
||||||
.zip(num_permutation_zs)
|
.zip(num_permutation_zs)
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use plonky2::field::extension::Extendable;
|
|||||||
use plonky2::fri::FriParams;
|
use plonky2::fri::FriParams;
|
||||||
use plonky2::gates::noop::NoopGate;
|
use plonky2::gates::noop::NoopGate;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::hash::hashing::SPONGE_WIDTH;
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::challenger::RecursiveChallenger;
|
use plonky2::iop::challenger::RecursiveChallenger;
|
||||||
use plonky2::iop::target::{BoolTarget, Target};
|
use plonky2::iop::target::{BoolTarget, Target};
|
||||||
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
@ -45,28 +45,33 @@ const THRESHOLD_DEGREE_BITS: usize = 13;
|
|||||||
/// `degree_bits`, this contains a chain of recursive circuits for shrinking that STARK from
|
/// `degree_bits`, this contains a chain of recursive circuits for shrinking that STARK from
|
||||||
/// `degree_bits` to a constant `THRESHOLD_DEGREE_BITS`. It also contains a special root circuit
|
/// `degree_bits` to a constant `THRESHOLD_DEGREE_BITS`. It also contains a special root circuit
|
||||||
/// for combining each STARK's shrunk wrapper proof into a single proof.
|
/// for combining each STARK's shrunk wrapper proof into a single proof.
|
||||||
pub struct AllRecursiveCircuits<F, C, const D: usize>
|
pub struct AllRecursiveCircuits<F, HCO, HCI, C, const D: usize>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
/// The EVM root circuit, which aggregates the (shrunk) per-table recursive proofs.
|
/// The EVM root circuit, which aggregates the (shrunk) per-table recursive proofs.
|
||||||
pub root: RootCircuitData<F, C, D>,
|
pub root: RootCircuitData<F, HCO, HCI, C, D>,
|
||||||
pub aggregation: AggregationCircuitData<F, C, D>,
|
pub aggregation: AggregationCircuitData<F, HCO, HCI, C, D>,
|
||||||
/// The block circuit, which verifies an aggregation root proof and a previous block proof.
|
/// The block circuit, which verifies an aggregation root proof and a previous block proof.
|
||||||
pub block: BlockCircuitData<F, C, D>,
|
pub block: BlockCircuitData<F, HCO, HCI, C, D>,
|
||||||
/// Holds chains of circuits for each table and for each initial `degree_bits`.
|
/// Holds chains of circuits for each table and for each initial `degree_bits`.
|
||||||
by_table: [RecursiveCircuitsForTable<F, C, D>; NUM_TABLES],
|
by_table: [RecursiveCircuitsForTable<F, HCO, HCI, C, D>; NUM_TABLES],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data for the EVM root circuit, which is used to combine each STARK's shrunk wrapper proof
|
/// Data for the EVM root circuit, which is used to combine each STARK's shrunk wrapper proof
|
||||||
/// into a single proof.
|
/// into a single proof.
|
||||||
pub struct RootCircuitData<F, C, const D: usize>
|
pub struct RootCircuitData<F, HCO, HCI, C, const D: usize>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
circuit: CircuitData<F, C, D>,
|
circuit: CircuitData<F, HCO, HCI, C, D>,
|
||||||
proof_with_pis: [ProofWithPublicInputsTarget<D>; NUM_TABLES],
|
proof_with_pis: [ProofWithPublicInputsTarget<D>; NUM_TABLES],
|
||||||
/// For each table, various inner circuits may be used depending on the initial table size.
|
/// For each table, various inner circuits may be used depending on the initial table size.
|
||||||
/// This target holds the index of the circuit (within `final_circuits()`) that was used.
|
/// This target holds the index of the circuit (within `final_circuits()`) that was used.
|
||||||
@ -78,12 +83,14 @@ where
|
|||||||
|
|
||||||
/// Data for the aggregation circuit, which is used to compress two proofs into one. Each inner
|
/// Data for the aggregation circuit, which is used to compress two proofs into one. Each inner
|
||||||
/// proof can be either an EVM root proof or another aggregation proof.
|
/// proof can be either an EVM root proof or another aggregation proof.
|
||||||
pub struct AggregationCircuitData<F, C, const D: usize>
|
pub struct AggregationCircuitData<F, HCO, HCI, C, const D: usize>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
circuit: CircuitData<F, C, D>,
|
circuit: CircuitData<F, HCO, HCI, C, D>,
|
||||||
lhs: AggregationChildTarget<D>,
|
lhs: AggregationChildTarget<D>,
|
||||||
rhs: AggregationChildTarget<D>,
|
rhs: AggregationChildTarget<D>,
|
||||||
cyclic_vk: VerifierCircuitTarget,
|
cyclic_vk: VerifierCircuitTarget,
|
||||||
@ -95,29 +102,35 @@ pub struct AggregationChildTarget<const D: usize> {
|
|||||||
evm_proof: ProofWithPublicInputsTarget<D>,
|
evm_proof: ProofWithPublicInputsTarget<D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlockCircuitData<F, C, const D: usize>
|
pub struct BlockCircuitData<F, HCO, HCI, C, const D: usize>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
circuit: CircuitData<F, C, D>,
|
circuit: CircuitData<F, HCO, HCI, C, D>,
|
||||||
has_parent_block: BoolTarget,
|
has_parent_block: BoolTarget,
|
||||||
parent_block_proof: ProofWithPublicInputsTarget<D>,
|
parent_block_proof: ProofWithPublicInputsTarget<D>,
|
||||||
agg_root_proof: ProofWithPublicInputsTarget<D>,
|
agg_root_proof: ProofWithPublicInputsTarget<D>,
|
||||||
cyclic_vk: VerifierCircuitTarget,
|
cyclic_vk: VerifierCircuitTarget,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, C, const D: usize> AllRecursiveCircuits<F, C, D>
|
impl<F, HCO, HCI, C, const D: usize> AllRecursiveCircuits<F, HCO, HCI, C, D>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F> + 'static,
|
HCO: HashConfig + 'static,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCI: HashConfig + 'static,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F> + 'static,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
[(); CpuStark::<F, D>::COLUMNS]:,
|
[(); CpuStark::<F, D>::COLUMNS]:,
|
||||||
[(); KeccakStark::<F, D>::COLUMNS]:,
|
[(); KeccakStark::<F, D>::COLUMNS]:,
|
||||||
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
||||||
[(); LogicStark::<F, D>::COLUMNS]:,
|
[(); LogicStark::<F, D>::COLUMNS]:,
|
||||||
[(); MemoryStark::<F, D>::COLUMNS]:,
|
[(); MemoryStark::<F, D>::COLUMNS]:,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
/// Preprocess all recursive circuits used by the system.
|
/// Preprocess all recursive circuits used by the system.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
@ -174,9 +187,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_root_circuit(
|
fn create_root_circuit(
|
||||||
by_table: &[RecursiveCircuitsForTable<F, C, D>; NUM_TABLES],
|
by_table: &[RecursiveCircuitsForTable<F, HCO, HCI, C, D>; NUM_TABLES],
|
||||||
stark_config: &StarkConfig,
|
stark_config: &StarkConfig,
|
||||||
) -> RootCircuitData<F, C, D> {
|
) -> RootCircuitData<F, HCO, HCI, C, D> {
|
||||||
let inner_common_data: [_; NUM_TABLES] =
|
let inner_common_data: [_; NUM_TABLES] =
|
||||||
core::array::from_fn(|i| &by_table[i].final_circuits()[0].common);
|
core::array::from_fn(|i| &by_table[i].final_circuits()[0].common);
|
||||||
|
|
||||||
@ -184,11 +197,11 @@ where
|
|||||||
let recursive_proofs =
|
let recursive_proofs =
|
||||||
core::array::from_fn(|i| builder.add_virtual_proof_with_pis(inner_common_data[i]));
|
core::array::from_fn(|i| builder.add_virtual_proof_with_pis(inner_common_data[i]));
|
||||||
let pis: [_; NUM_TABLES] = core::array::from_fn(|i| {
|
let pis: [_; NUM_TABLES] = core::array::from_fn(|i| {
|
||||||
PublicInputs::from_vec(&recursive_proofs[i].public_inputs, stark_config)
|
PublicInputs::<Target, HCO>::from_vec(&recursive_proofs[i].public_inputs, stark_config)
|
||||||
});
|
});
|
||||||
let index_verifier_data = core::array::from_fn(|_i| builder.add_virtual_target());
|
let index_verifier_data = core::array::from_fn(|_i| builder.add_virtual_target());
|
||||||
|
|
||||||
let mut challenger = RecursiveChallenger::<F, C::Hasher, D>::new(&mut builder);
|
let mut challenger = RecursiveChallenger::<F, HCO, C::Hasher, D>::new(&mut builder);
|
||||||
for pi in &pis {
|
for pi in &pis {
|
||||||
for h in &pi.trace_cap {
|
for h in &pi.trace_cap {
|
||||||
challenger.observe_elements(h);
|
challenger.observe_elements(h);
|
||||||
@ -214,12 +227,12 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let state = challenger.compact(&mut builder);
|
let state = challenger.compact(&mut builder);
|
||||||
for k in 0..SPONGE_WIDTH {
|
for k in 0..HCO::WIDTH {
|
||||||
builder.connect(state[k], pis[0].challenger_state_before[k]);
|
builder.connect(state[k], pis[0].challenger_state_before[k]);
|
||||||
}
|
}
|
||||||
// Check that the challenger state is consistent between proofs.
|
// Check that the challenger state is consistent between proofs.
|
||||||
for i in 1..NUM_TABLES {
|
for i in 1..NUM_TABLES {
|
||||||
for k in 0..SPONGE_WIDTH {
|
for k in 0..HCO::WIDTH {
|
||||||
builder.connect(
|
builder.connect(
|
||||||
pis[i].challenger_state_before[k],
|
pis[i].challenger_state_before[k],
|
||||||
pis[i - 1].challenger_state_after[k],
|
pis[i - 1].challenger_state_after[k],
|
||||||
@ -255,7 +268,7 @@ where
|
|||||||
let inner_verifier_data =
|
let inner_verifier_data =
|
||||||
builder.random_access_verifier_data(index_verifier_data[i], possible_vks);
|
builder.random_access_verifier_data(index_verifier_data[i], possible_vks);
|
||||||
|
|
||||||
builder.verify_proof::<C>(
|
builder.verify_proof::<HCO, HCI, C>(
|
||||||
&recursive_proofs[i],
|
&recursive_proofs[i],
|
||||||
&inner_verifier_data,
|
&inner_verifier_data,
|
||||||
inner_common_data[i],
|
inner_common_data[i],
|
||||||
@ -267,7 +280,7 @@ where
|
|||||||
let cyclic_vk = builder.add_verifier_data_public_inputs();
|
let cyclic_vk = builder.add_verifier_data_public_inputs();
|
||||||
|
|
||||||
RootCircuitData {
|
RootCircuitData {
|
||||||
circuit: builder.build(),
|
circuit: builder.build::<HCO, HCI, C>(),
|
||||||
proof_with_pis: recursive_proofs,
|
proof_with_pis: recursive_proofs,
|
||||||
index_verifier_data,
|
index_verifier_data,
|
||||||
cyclic_vk,
|
cyclic_vk,
|
||||||
@ -275,8 +288,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_aggregation_circuit(
|
fn create_aggregation_circuit(
|
||||||
root: &RootCircuitData<F, C, D>,
|
root: &RootCircuitData<F, HCO, HCI, C, D>,
|
||||||
) -> AggregationCircuitData<F, C, D> {
|
) -> AggregationCircuitData<F, HCO, HCI, C, D> {
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(root.circuit.common.config.clone());
|
let mut builder = CircuitBuilder::<F, D>::new(root.circuit.common.config.clone());
|
||||||
let cyclic_vk = builder.add_verifier_data_public_inputs();
|
let cyclic_vk = builder.add_verifier_data_public_inputs();
|
||||||
let lhs = Self::add_agg_child(&mut builder, root);
|
let lhs = Self::add_agg_child(&mut builder, root);
|
||||||
@ -287,7 +300,7 @@ where
|
|||||||
builder.add_gate(NoopGate, vec![]);
|
builder.add_gate(NoopGate, vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let circuit = builder.build::<C>();
|
let circuit = builder.build::<HCO, HCI, C>();
|
||||||
AggregationCircuitData {
|
AggregationCircuitData {
|
||||||
circuit,
|
circuit,
|
||||||
lhs,
|
lhs,
|
||||||
@ -298,7 +311,7 @@ where
|
|||||||
|
|
||||||
fn add_agg_child(
|
fn add_agg_child(
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
root: &RootCircuitData<F, C, D>,
|
root: &RootCircuitData<F, HCO, HCI, C, D>,
|
||||||
) -> AggregationChildTarget<D> {
|
) -> AggregationChildTarget<D> {
|
||||||
let common = &root.circuit.common;
|
let common = &root.circuit.common;
|
||||||
let root_vk = builder.constant_verifier_data(&root.circuit.verifier_only);
|
let root_vk = builder.constant_verifier_data(&root.circuit.verifier_only);
|
||||||
@ -306,7 +319,7 @@ where
|
|||||||
let agg_proof = builder.add_virtual_proof_with_pis(common);
|
let agg_proof = builder.add_virtual_proof_with_pis(common);
|
||||||
let evm_proof = builder.add_virtual_proof_with_pis(common);
|
let evm_proof = builder.add_virtual_proof_with_pis(common);
|
||||||
builder
|
builder
|
||||||
.conditionally_verify_cyclic_proof::<C>(
|
.conditionally_verify_cyclic_proof::<HCO, HCI, C>(
|
||||||
is_agg, &agg_proof, &evm_proof, &root_vk, common,
|
is_agg, &agg_proof, &evm_proof, &root_vk, common,
|
||||||
)
|
)
|
||||||
.expect("Failed to build cyclic recursion circuit");
|
.expect("Failed to build cyclic recursion circuit");
|
||||||
@ -317,7 +330,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_block_circuit(agg: &AggregationCircuitData<F, C, D>) -> BlockCircuitData<F, C, D> {
|
fn create_block_circuit(
|
||||||
|
agg: &AggregationCircuitData<F, HCO, HCI, C, D>,
|
||||||
|
) -> BlockCircuitData<F, HCO, HCI, C, D> {
|
||||||
// The block circuit is similar to the agg circuit; both verify two inner proofs.
|
// The block circuit is similar to the agg circuit; both verify two inner proofs.
|
||||||
// We need to adjust a few things, but it's easier than making a new CommonCircuitData.
|
// We need to adjust a few things, but it's easier than making a new CommonCircuitData.
|
||||||
let expected_common_data = CommonCircuitData {
|
let expected_common_data = CommonCircuitData {
|
||||||
@ -335,7 +350,7 @@ where
|
|||||||
|
|
||||||
let cyclic_vk = builder.add_verifier_data_public_inputs();
|
let cyclic_vk = builder.add_verifier_data_public_inputs();
|
||||||
builder
|
builder
|
||||||
.conditionally_verify_cyclic_proof_or_dummy::<C>(
|
.conditionally_verify_cyclic_proof_or_dummy::<HCO, HCI, C>(
|
||||||
has_parent_block,
|
has_parent_block,
|
||||||
&parent_block_proof,
|
&parent_block_proof,
|
||||||
&expected_common_data,
|
&expected_common_data,
|
||||||
@ -343,9 +358,13 @@ where
|
|||||||
.expect("Failed to build cyclic recursion circuit");
|
.expect("Failed to build cyclic recursion circuit");
|
||||||
|
|
||||||
let agg_verifier_data = builder.constant_verifier_data(&agg.circuit.verifier_only);
|
let agg_verifier_data = builder.constant_verifier_data(&agg.circuit.verifier_only);
|
||||||
builder.verify_proof::<C>(&agg_root_proof, &agg_verifier_data, &agg.circuit.common);
|
builder.verify_proof::<HCO, HCI, C>(
|
||||||
|
&agg_root_proof,
|
||||||
|
&agg_verifier_data,
|
||||||
|
&agg.circuit.common,
|
||||||
|
);
|
||||||
|
|
||||||
let circuit = builder.build::<C>();
|
let circuit = builder.build::<HCO, HCI, C>();
|
||||||
BlockCircuitData {
|
BlockCircuitData {
|
||||||
circuit,
|
circuit,
|
||||||
has_parent_block,
|
has_parent_block,
|
||||||
@ -362,8 +381,8 @@ where
|
|||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
generation_inputs: GenerationInputs,
|
generation_inputs: GenerationInputs,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>> {
|
) -> anyhow::Result<ProofWithPublicInputs<F, HCO, HCI, C, D>> {
|
||||||
let all_proof = prove::<F, C, D>(all_stark, config, generation_inputs, timing)?;
|
let all_proof = prove::<F, HCO, HCI, C, D>(all_stark, config, generation_inputs, timing)?;
|
||||||
let mut root_inputs = PartialWitness::new();
|
let mut root_inputs = PartialWitness::new();
|
||||||
|
|
||||||
for table in 0..NUM_TABLES {
|
for table in 0..NUM_TABLES {
|
||||||
@ -392,17 +411,20 @@ where
|
|||||||
self.root.circuit.prove(root_inputs)
|
self.root.circuit.prove(root_inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_root(&self, agg_proof: ProofWithPublicInputs<F, C, D>) -> anyhow::Result<()> {
|
pub fn verify_root(
|
||||||
|
&self,
|
||||||
|
agg_proof: ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
self.root.circuit.verify(agg_proof)
|
self.root.circuit.verify(agg_proof)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prove_aggregation(
|
pub fn prove_aggregation(
|
||||||
&self,
|
&self,
|
||||||
lhs_is_agg: bool,
|
lhs_is_agg: bool,
|
||||||
lhs_proof: &ProofWithPublicInputs<F, C, D>,
|
lhs_proof: &ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
rhs_is_agg: bool,
|
rhs_is_agg: bool,
|
||||||
rhs_proof: &ProofWithPublicInputs<F, C, D>,
|
rhs_proof: &ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>> {
|
) -> anyhow::Result<ProofWithPublicInputs<F, HCO, HCI, C, D>> {
|
||||||
let mut agg_inputs = PartialWitness::new();
|
let mut agg_inputs = PartialWitness::new();
|
||||||
|
|
||||||
agg_inputs.set_bool_target(self.aggregation.lhs.is_agg, lhs_is_agg);
|
agg_inputs.set_bool_target(self.aggregation.lhs.is_agg, lhs_is_agg);
|
||||||
@ -423,7 +445,7 @@ where
|
|||||||
|
|
||||||
pub fn verify_aggregation(
|
pub fn verify_aggregation(
|
||||||
&self,
|
&self,
|
||||||
agg_proof: &ProofWithPublicInputs<F, C, D>,
|
agg_proof: &ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
self.aggregation.circuit.verify(agg_proof.clone())?;
|
self.aggregation.circuit.verify(agg_proof.clone())?;
|
||||||
check_cyclic_proof_verifier_data(
|
check_cyclic_proof_verifier_data(
|
||||||
@ -435,9 +457,9 @@ where
|
|||||||
|
|
||||||
pub fn prove_block(
|
pub fn prove_block(
|
||||||
&self,
|
&self,
|
||||||
opt_parent_block_proof: Option<&ProofWithPublicInputs<F, C, D>>,
|
opt_parent_block_proof: Option<&ProofWithPublicInputs<F, HCO, HCI, C, D>>,
|
||||||
agg_root_proof: &ProofWithPublicInputs<F, C, D>,
|
agg_root_proof: &ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>> {
|
) -> anyhow::Result<ProofWithPublicInputs<F, HCO, HCI, C, D>> {
|
||||||
let mut block_inputs = PartialWitness::new();
|
let mut block_inputs = PartialWitness::new();
|
||||||
|
|
||||||
block_inputs.set_bool_target(
|
block_inputs.set_bool_target(
|
||||||
@ -457,7 +479,10 @@ where
|
|||||||
self.block.circuit.prove(block_inputs)
|
self.block.circuit.prove(block_inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_block(&self, block_proof: &ProofWithPublicInputs<F, C, D>) -> anyhow::Result<()> {
|
pub fn verify_block(
|
||||||
|
&self,
|
||||||
|
block_proof: &ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
self.block.circuit.verify(block_proof.clone())?;
|
self.block.circuit.verify(block_proof.clone())?;
|
||||||
check_cyclic_proof_verifier_data(
|
check_cyclic_proof_verifier_data(
|
||||||
block_proof,
|
block_proof,
|
||||||
@ -467,22 +492,29 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RecursiveCircuitsForTable<F, C, const D: usize>
|
struct RecursiveCircuitsForTable<F, HCO, HCI, C, const D: usize>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
/// A map from `log_2(height)` to a chain of shrinking recursion circuits starting at that
|
/// A map from `log_2(height)` to a chain of shrinking recursion circuits starting at that
|
||||||
/// height.
|
/// height.
|
||||||
by_stark_size: BTreeMap<usize, RecursiveCircuitsForTableSize<F, C, D>>,
|
by_stark_size: BTreeMap<usize, RecursiveCircuitsForTableSize<F, HCO, HCI, C, D>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, C, const D: usize> RecursiveCircuitsForTable<F, C, D>
|
impl<F, HCO, HCI, C, const D: usize> RecursiveCircuitsForTable<F, HCO, HCI, C, D>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
fn new<S: Stark<F, D>>(
|
fn new<S: Stark<F, D>>(
|
||||||
table: Table,
|
table: Table,
|
||||||
@ -513,7 +545,7 @@ where
|
|||||||
|
|
||||||
/// For each initial `degree_bits`, get the final circuit at the end of that shrinking chain.
|
/// For each initial `degree_bits`, get the final circuit at the end of that shrinking chain.
|
||||||
/// Each of these final circuits should have degree `THRESHOLD_DEGREE_BITS`.
|
/// Each of these final circuits should have degree `THRESHOLD_DEGREE_BITS`.
|
||||||
fn final_circuits(&self) -> Vec<&CircuitData<F, C, D>> {
|
fn final_circuits(&self) -> Vec<&CircuitData<F, HCO, HCI, C, D>> {
|
||||||
self.by_stark_size
|
self.by_stark_size
|
||||||
.values()
|
.values()
|
||||||
.map(|chain| {
|
.map(|chain| {
|
||||||
@ -529,21 +561,28 @@ where
|
|||||||
|
|
||||||
/// A chain of shrinking wrapper circuits, ending with a final circuit with `degree_bits`
|
/// A chain of shrinking wrapper circuits, ending with a final circuit with `degree_bits`
|
||||||
/// `THRESHOLD_DEGREE_BITS`.
|
/// `THRESHOLD_DEGREE_BITS`.
|
||||||
struct RecursiveCircuitsForTableSize<F, C, const D: usize>
|
struct RecursiveCircuitsForTableSize<F, HCO, HCI, C, const D: usize>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
initial_wrapper: StarkWrapperCircuit<F, C, D>,
|
initial_wrapper: StarkWrapperCircuit<F, HCO, HCI, C, D>,
|
||||||
shrinking_wrappers: Vec<PlonkWrapperCircuit<F, C, D>>,
|
shrinking_wrappers: Vec<PlonkWrapperCircuit<F, HCO, HCI, C, D>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, C, const D: usize> RecursiveCircuitsForTableSize<F, C, D>
|
impl<F, HCO, HCI, C, const D: usize> RecursiveCircuitsForTableSize<F, HCO, HCI, C, D>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
fn new<S: Stark<F, D>>(
|
fn new<S: Stark<F, D>>(
|
||||||
table: Table,
|
table: Table,
|
||||||
@ -570,7 +609,7 @@ where
|
|||||||
loop {
|
loop {
|
||||||
let last = shrinking_wrappers
|
let last = shrinking_wrappers
|
||||||
.last()
|
.last()
|
||||||
.map(|wrapper: &PlonkWrapperCircuit<F, C, D>| &wrapper.circuit)
|
.map(|wrapper: &PlonkWrapperCircuit<F, HCO, HCI, C, D>| &wrapper.circuit)
|
||||||
.unwrap_or(&initial_wrapper.circuit);
|
.unwrap_or(&initial_wrapper.circuit);
|
||||||
let last_degree_bits = last.common.degree_bits();
|
let last_degree_bits = last.common.degree_bits();
|
||||||
assert!(last_degree_bits >= THRESHOLD_DEGREE_BITS);
|
assert!(last_degree_bits >= THRESHOLD_DEGREE_BITS);
|
||||||
@ -581,10 +620,10 @@ where
|
|||||||
let mut builder = CircuitBuilder::new(shrinking_config());
|
let mut builder = CircuitBuilder::new(shrinking_config());
|
||||||
let proof_with_pis_target = builder.add_virtual_proof_with_pis(&last.common);
|
let proof_with_pis_target = builder.add_virtual_proof_with_pis(&last.common);
|
||||||
let last_vk = builder.constant_verifier_data(&last.verifier_only);
|
let last_vk = builder.constant_verifier_data(&last.verifier_only);
|
||||||
builder.verify_proof::<C>(&proof_with_pis_target, &last_vk, &last.common);
|
builder.verify_proof::<HCO, HCI, C>(&proof_with_pis_target, &last_vk, &last.common);
|
||||||
builder.register_public_inputs(&proof_with_pis_target.public_inputs); // carry PIs forward
|
builder.register_public_inputs(&proof_with_pis_target.public_inputs); // carry PIs forward
|
||||||
add_common_recursion_gates(&mut builder);
|
add_common_recursion_gates(&mut builder);
|
||||||
let circuit = builder.build();
|
let circuit = builder.build::<HCO, HCI, C>();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
circuit.common.degree_bits() < last_degree_bits,
|
circuit.common.degree_bits() < last_degree_bits,
|
||||||
@ -606,9 +645,9 @@ where
|
|||||||
|
|
||||||
fn shrink(
|
fn shrink(
|
||||||
&self,
|
&self,
|
||||||
stark_proof_with_metadata: &StarkProofWithMetadata<F, C, D>,
|
stark_proof_with_metadata: &StarkProofWithMetadata<F, HCO, HCI, C, D>,
|
||||||
ctl_challenges: &GrandProductChallengeSet<F>,
|
ctl_challenges: &GrandProductChallengeSet<F>,
|
||||||
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>> {
|
) -> anyhow::Result<ProofWithPublicInputs<F, HCO, HCI, C, D>> {
|
||||||
let mut proof = self
|
let mut proof = self
|
||||||
.initial_wrapper
|
.initial_wrapper
|
||||||
.prove(stark_proof_with_metadata, ctl_challenges)?;
|
.prove(stark_proof_with_metadata, ctl_challenges)?;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use plonky2::field::extension::Extendable;
|
use plonky2::field::extension::Extendable;
|
||||||
use plonky2::fri::proof::{FriProof, FriProofTarget};
|
use plonky2::fri::proof::{FriProof, FriProofTarget};
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::challenger::{Challenger, RecursiveChallenger};
|
use plonky2::iop::challenger::{Challenger, RecursiveChallenger};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
|
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||||
@ -13,14 +14,27 @@ use crate::permutation::{
|
|||||||
};
|
};
|
||||||
use crate::proof::*;
|
use crate::proof::*;
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> AllProof<F, C, D> {
|
impl<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> AllProof<F, HCO, HCI, C, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
/// Computes all Fiat-Shamir challenges used in the STARK proof.
|
/// Computes all Fiat-Shamir challenges used in the STARK proof.
|
||||||
pub(crate) fn get_challenges(
|
pub(crate) fn get_challenges(
|
||||||
&self,
|
&self,
|
||||||
all_stark: &AllStark<F, D>,
|
all_stark: &AllStark<F, D>,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> AllProofChallenges<F, D> {
|
) -> AllProofChallenges<F, D>
|
||||||
let mut challenger = Challenger::<F, C::Hasher>::new();
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
|
let mut challenger = Challenger::<F, HCO, C::Hasher>::new();
|
||||||
|
|
||||||
for proof in &self.stark_proofs {
|
for proof in &self.stark_proofs {
|
||||||
challenger.observe_cap(&proof.proof.trace_cap);
|
challenger.observe_cap(&proof.proof.trace_cap);
|
||||||
@ -53,8 +67,12 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> A
|
|||||||
&self,
|
&self,
|
||||||
all_stark: &AllStark<F, D>,
|
all_stark: &AllStark<F, D>,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> AllChallengerState<F, D> {
|
) -> AllChallengerState<F, HCO, D>
|
||||||
let mut challenger = Challenger::<F, C::Hasher>::new();
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
|
let mut challenger = Challenger::<F, HCO, C::Hasher>::new();
|
||||||
|
|
||||||
for proof in &self.stark_proofs {
|
for proof in &self.stark_proofs {
|
||||||
challenger.observe_cap(&proof.proof.trace_cap);
|
challenger.observe_cap(&proof.proof.trace_cap);
|
||||||
@ -86,19 +104,25 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> A
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, C, const D: usize> StarkProof<F, C, D>
|
impl<F, HCO, HCI, C, const D: usize> StarkProof<F, HCO, HCI, C, D>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
/// Computes all Fiat-Shamir challenges used in the STARK proof.
|
/// Computes all Fiat-Shamir challenges used in the STARK proof.
|
||||||
pub(crate) fn get_challenges(
|
pub(crate) fn get_challenges(
|
||||||
&self,
|
&self,
|
||||||
challenger: &mut Challenger<F, C::Hasher>,
|
challenger: &mut Challenger<F, HCO, C::Hasher>,
|
||||||
stark_use_permutation: bool,
|
stark_use_permutation: bool,
|
||||||
stark_permutation_batch_size: usize,
|
stark_permutation_batch_size: usize,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> StarkProofChallenges<F, D> {
|
) -> StarkProofChallenges<F, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let degree_bits = self.recover_degree_bits(config);
|
let degree_bits = self.recover_degree_bits(config);
|
||||||
|
|
||||||
let StarkProof {
|
let StarkProof {
|
||||||
@ -138,7 +162,7 @@ where
|
|||||||
permutation_challenge_sets,
|
permutation_challenge_sets,
|
||||||
stark_alphas,
|
stark_alphas,
|
||||||
stark_zeta,
|
stark_zeta,
|
||||||
fri_challenges: challenger.fri_challenges::<C, D>(
|
fri_challenges: challenger.fri_challenges::<HCI, C, D>(
|
||||||
commit_phase_merkle_caps,
|
commit_phase_merkle_caps,
|
||||||
final_poly,
|
final_poly,
|
||||||
*pow_witness,
|
*pow_witness,
|
||||||
@ -150,16 +174,23 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<const D: usize> StarkProofTarget<D> {
|
impl<const D: usize> StarkProofTarget<D> {
|
||||||
pub(crate) fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>>(
|
pub(crate) fn get_challenges<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
>(
|
||||||
&self,
|
&self,
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
challenger: &mut RecursiveChallenger<F, C::Hasher, D>,
|
challenger: &mut RecursiveChallenger<F, HCO, C::Hasher, D>,
|
||||||
stark_use_permutation: bool,
|
stark_use_permutation: bool,
|
||||||
stark_permutation_batch_size: usize,
|
stark_permutation_batch_size: usize,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> StarkProofChallengesTarget<D>
|
) -> StarkProofChallengesTarget<D>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let StarkProofTarget {
|
let StarkProofTarget {
|
||||||
permutation_ctl_zs_cap,
|
permutation_ctl_zs_cap,
|
||||||
|
|||||||
@ -607,7 +607,7 @@ mod tests {
|
|||||||
use plonky2::field::types::{Field, PrimeField64};
|
use plonky2::field::types::{Field, PrimeField64};
|
||||||
use plonky2::fri::oracle::PolynomialBatch;
|
use plonky2::fri::oracle::PolynomialBatch;
|
||||||
use plonky2::iop::challenger::Challenger;
|
use plonky2::iop::challenger::Challenger;
|
||||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use plonky2::timed;
|
use plonky2::timed;
|
||||||
use plonky2::util::timing::TimingTree;
|
use plonky2::util::timing::TimingTree;
|
||||||
use tiny_keccak::keccakf;
|
use tiny_keccak::keccakf;
|
||||||
@ -624,7 +624,9 @@ mod tests {
|
|||||||
fn test_stark_degree() -> Result<()> {
|
fn test_stark_degree() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = KeccakStark<F, D>;
|
type S = KeccakStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
@ -637,13 +639,15 @@ mod tests {
|
|||||||
fn test_stark_circuit() -> Result<()> {
|
fn test_stark_circuit() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = KeccakStark<F, D>;
|
type S = KeccakStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
f: Default::default(),
|
f: Default::default(),
|
||||||
};
|
};
|
||||||
test_stark_circuit_constraints::<F, C, S, D>(stark)
|
test_stark_circuit_constraints::<F, HCO, HCI, C, S, D>(stark)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -652,7 +656,9 @@ mod tests {
|
|||||||
|
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = KeccakStark<F, D>;
|
type S = KeccakStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
@ -685,7 +691,9 @@ mod tests {
|
|||||||
const NUM_PERMS: usize = 85;
|
const NUM_PERMS: usize = 85;
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = KeccakStark<F, D>;
|
type S = KeccakStark<F, D>;
|
||||||
let stark = S::default();
|
let stark = S::default();
|
||||||
let config = StarkConfig::standard_fast_config();
|
let config = StarkConfig::standard_fast_config();
|
||||||
@ -708,7 +716,7 @@ mod tests {
|
|||||||
let trace_commitments = timed!(
|
let trace_commitments = timed!(
|
||||||
timing,
|
timing,
|
||||||
"compute trace commitment",
|
"compute trace commitment",
|
||||||
PolynomialBatch::<F, C, D>::from_values(
|
PolynomialBatch::<F, HCO, HCI, C, D>::from_values(
|
||||||
cloned_trace_poly_values,
|
cloned_trace_poly_values,
|
||||||
config.fri_config.rate_bits,
|
config.fri_config.rate_bits,
|
||||||
false,
|
false,
|
||||||
|
|||||||
@ -411,7 +411,7 @@ mod tests {
|
|||||||
use keccak_hash::keccak;
|
use keccak_hash::keccak;
|
||||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||||
use plonky2::field::types::PrimeField64;
|
use plonky2::field::types::PrimeField64;
|
||||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
use crate::keccak_sponge::columns::KeccakSpongeColumnsView;
|
use crate::keccak_sponge::columns::KeccakSpongeColumnsView;
|
||||||
use crate::keccak_sponge::keccak_sponge_stark::{KeccakSpongeOp, KeccakSpongeStark};
|
use crate::keccak_sponge::keccak_sponge_stark::{KeccakSpongeOp, KeccakSpongeStark};
|
||||||
@ -423,7 +423,9 @@ mod tests {
|
|||||||
fn test_stark_degree() -> Result<()> {
|
fn test_stark_degree() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = KeccakSpongeStark<F, D>;
|
type S = KeccakSpongeStark<F, D>;
|
||||||
|
|
||||||
let stark = S::default();
|
let stark = S::default();
|
||||||
@ -434,11 +436,13 @@ mod tests {
|
|||||||
fn test_stark_circuit() -> Result<()> {
|
fn test_stark_circuit() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = KeccakSpongeStark<F, D>;
|
type S = KeccakSpongeStark<F, D>;
|
||||||
|
|
||||||
let stark = S::default();
|
let stark = S::default();
|
||||||
test_stark_circuit_constraints::<F, C, S, D>(stark)
|
test_stark_circuit_constraints::<F, HCO, HCI, C, S, D>(stark)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
#![allow(clippy::field_reassign_with_default)]
|
#![allow(clippy::field_reassign_with_default)]
|
||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![feature(generic_const_exprs)]
|
#![feature(generic_const_exprs)]
|
||||||
|
|
||||||
|
|||||||
@ -303,7 +303,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for LogicStark<F,
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
use crate::logic::LogicStark;
|
use crate::logic::LogicStark;
|
||||||
use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree};
|
use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree};
|
||||||
@ -312,7 +312,9 @@ mod tests {
|
|||||||
fn test_stark_degree() -> Result<()> {
|
fn test_stark_degree() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = LogicStark<F, D>;
|
type S = LogicStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
@ -325,12 +327,14 @@ mod tests {
|
|||||||
fn test_stark_circuit() -> Result<()> {
|
fn test_stark_circuit() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = LogicStark<F, D>;
|
type S = LogicStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
f: Default::default(),
|
f: Default::default(),
|
||||||
};
|
};
|
||||||
test_stark_circuit_constraints::<F, C, S, D>(stark)
|
test_stark_circuit_constraints::<F, HCO, HCI, C, S, D>(stark)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -462,7 +462,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for MemoryStark<F
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
use crate::memory::memory_stark::MemoryStark;
|
use crate::memory::memory_stark::MemoryStark;
|
||||||
use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree};
|
use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree};
|
||||||
@ -471,7 +471,9 @@ pub(crate) mod tests {
|
|||||||
fn test_stark_degree() -> Result<()> {
|
fn test_stark_degree() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = MemoryStark<F, D>;
|
type S = MemoryStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
@ -484,12 +486,14 @@ pub(crate) mod tests {
|
|||||||
fn test_stark_circuit() -> Result<()> {
|
fn test_stark_circuit() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = MemoryStark<F, D>;
|
type S = MemoryStark<F, D>;
|
||||||
|
|
||||||
let stark = S {
|
let stark = S {
|
||||||
f: Default::default(),
|
f: Default::default(),
|
||||||
};
|
};
|
||||||
test_stark_circuit_constraints::<F, C, S, D>(stark)
|
test_stark_circuit_constraints::<F, HCO, HCI, C, S, D>(stark)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ use plonky2::field::packed::PackedField;
|
|||||||
use plonky2::field::polynomial::PolynomialValues;
|
use plonky2::field::polynomial::PolynomialValues;
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::challenger::{Challenger, RecursiveChallenger};
|
use plonky2::iop::challenger::{Challenger, RecursiveChallenger};
|
||||||
use plonky2::iop::ext_target::ExtensionTarget;
|
use plonky2::iop::ext_target::ExtensionTarget;
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
@ -175,29 +176,38 @@ fn poly_product_elementwise<F: Field>(
|
|||||||
product
|
product
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_grand_product_challenge<F: RichField, H: Hasher<F>>(
|
fn get_grand_product_challenge<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
challenger: &mut Challenger<F, H>,
|
challenger: &mut Challenger<F, HC, H>,
|
||||||
) -> GrandProductChallenge<F> {
|
) -> GrandProductChallenge<F>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let beta = challenger.get_challenge();
|
let beta = challenger.get_challenge();
|
||||||
let gamma = challenger.get_challenge();
|
let gamma = challenger.get_challenge();
|
||||||
GrandProductChallenge { beta, gamma }
|
GrandProductChallenge { beta, gamma }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_grand_product_challenge_set<F: RichField, H: Hasher<F>>(
|
pub(crate) fn get_grand_product_challenge_set<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
challenger: &mut Challenger<F, H>,
|
challenger: &mut Challenger<F, HC, H>,
|
||||||
num_challenges: usize,
|
num_challenges: usize,
|
||||||
) -> GrandProductChallengeSet<F> {
|
) -> GrandProductChallengeSet<F>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let challenges = (0..num_challenges)
|
let challenges = (0..num_challenges)
|
||||||
.map(|_| get_grand_product_challenge(challenger))
|
.map(|_| get_grand_product_challenge(challenger))
|
||||||
.collect();
|
.collect();
|
||||||
GrandProductChallengeSet { challenges }
|
GrandProductChallengeSet { challenges }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_n_grand_product_challenge_sets<F: RichField, H: Hasher<F>>(
|
pub(crate) fn get_n_grand_product_challenge_sets<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
challenger: &mut Challenger<F, H>,
|
challenger: &mut Challenger<F, HC, H>,
|
||||||
num_challenges: usize,
|
num_challenges: usize,
|
||||||
num_sets: usize,
|
num_sets: usize,
|
||||||
) -> Vec<GrandProductChallengeSet<F>> {
|
) -> Vec<GrandProductChallengeSet<F>>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
(0..num_sets)
|
(0..num_sets)
|
||||||
.map(|_| get_grand_product_challenge_set(challenger, num_challenges))
|
.map(|_| get_grand_product_challenge_set(challenger, num_challenges))
|
||||||
.collect()
|
.collect()
|
||||||
@ -205,12 +215,16 @@ pub(crate) fn get_n_grand_product_challenge_sets<F: RichField, H: Hasher<F>>(
|
|||||||
|
|
||||||
fn get_grand_product_challenge_target<
|
fn get_grand_product_challenge_target<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
H: AlgebraicHasher<F>,
|
HC: HashConfig,
|
||||||
|
H: AlgebraicHasher<F, HC>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
challenger: &mut RecursiveChallenger<F, H, D>,
|
challenger: &mut RecursiveChallenger<F, HC, H, D>,
|
||||||
) -> GrandProductChallenge<Target> {
|
) -> GrandProductChallenge<Target>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let beta = challenger.get_challenge(builder);
|
let beta = challenger.get_challenge(builder);
|
||||||
let gamma = challenger.get_challenge(builder);
|
let gamma = challenger.get_challenge(builder);
|
||||||
GrandProductChallenge { beta, gamma }
|
GrandProductChallenge { beta, gamma }
|
||||||
@ -218,13 +232,17 @@ fn get_grand_product_challenge_target<
|
|||||||
|
|
||||||
pub(crate) fn get_grand_product_challenge_set_target<
|
pub(crate) fn get_grand_product_challenge_set_target<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
H: AlgebraicHasher<F>,
|
HC: HashConfig,
|
||||||
|
H: AlgebraicHasher<F, HC>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
challenger: &mut RecursiveChallenger<F, H, D>,
|
challenger: &mut RecursiveChallenger<F, HC, H, D>,
|
||||||
num_challenges: usize,
|
num_challenges: usize,
|
||||||
) -> GrandProductChallengeSet<Target> {
|
) -> GrandProductChallengeSet<Target>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let challenges = (0..num_challenges)
|
let challenges = (0..num_challenges)
|
||||||
.map(|_| get_grand_product_challenge_target(builder, challenger))
|
.map(|_| get_grand_product_challenge_target(builder, challenger))
|
||||||
.collect();
|
.collect();
|
||||||
@ -233,14 +251,18 @@ pub(crate) fn get_grand_product_challenge_set_target<
|
|||||||
|
|
||||||
pub(crate) fn get_n_grand_product_challenge_sets_target<
|
pub(crate) fn get_n_grand_product_challenge_sets_target<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
H: AlgebraicHasher<F>,
|
HC: HashConfig,
|
||||||
|
H: AlgebraicHasher<F, HC>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
challenger: &mut RecursiveChallenger<F, H, D>,
|
challenger: &mut RecursiveChallenger<F, HC, H, D>,
|
||||||
num_challenges: usize,
|
num_challenges: usize,
|
||||||
num_sets: usize,
|
num_sets: usize,
|
||||||
) -> Vec<GrandProductChallengeSet<Target>> {
|
) -> Vec<GrandProductChallengeSet<Target>>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
(0..num_sets)
|
(0..num_sets)
|
||||||
.map(|_| get_grand_product_challenge_set_target(builder, challenger, num_challenges))
|
.map(|_| get_grand_product_challenge_set_target(builder, challenger, num_challenges))
|
||||||
.collect()
|
.collect()
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use plonky2::fri::structure::{
|
|||||||
FriOpeningBatch, FriOpeningBatchTarget, FriOpenings, FriOpeningsTarget,
|
FriOpeningBatch, FriOpeningBatchTarget, FriOpenings, FriOpeningsTarget,
|
||||||
};
|
};
|
||||||
use plonky2::hash::hash_types::{MerkleCapTarget, RichField};
|
use plonky2::hash::hash_types::{MerkleCapTarget, RichField};
|
||||||
use plonky2::hash::hashing::SPONGE_WIDTH;
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::hash::merkle_tree::MerkleCap;
|
use plonky2::hash::merkle_tree::MerkleCap;
|
||||||
use plonky2::iop::ext_target::ExtensionTarget;
|
use plonky2::iop::ext_target::ExtensionTarget;
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
@ -21,13 +21,30 @@ use crate::permutation::GrandProductChallengeSet;
|
|||||||
|
|
||||||
/// A STARK proof for each table, plus some metadata used to create recursive wrapper proofs.
|
/// A STARK proof for each table, plus some metadata used to create recursive wrapper proofs.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AllProof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
|
pub struct AllProof<
|
||||||
pub stark_proofs: [StarkProofWithMetadata<F, C, D>; NUM_TABLES],
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
|
pub stark_proofs: [StarkProofWithMetadata<F, HCO, HCI, C, D>; NUM_TABLES],
|
||||||
pub(crate) ctl_challenges: GrandProductChallengeSet<F>,
|
pub(crate) ctl_challenges: GrandProductChallengeSet<F>,
|
||||||
pub public_values: PublicValues,
|
pub public_values: PublicValues,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> AllProof<F, C, D> {
|
impl<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> AllProof<F, HCO, HCI, C, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
pub fn degree_bits(&self, config: &StarkConfig) -> [usize; NUM_TABLES] {
|
pub fn degree_bits(&self, config: &StarkConfig) -> [usize; NUM_TABLES] {
|
||||||
core::array::from_fn(|i| self.stark_proofs[i].proof.recover_degree_bits(config))
|
core::array::from_fn(|i| self.stark_proofs[i].proof.recover_degree_bits(config))
|
||||||
}
|
}
|
||||||
@ -39,10 +56,13 @@ pub(crate) struct AllProofChallenges<F: RichField + Extendable<D>, const D: usiz
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)] // TODO: should be used soon
|
#[allow(unused)] // TODO: should be used soon
|
||||||
pub(crate) struct AllChallengerState<F: RichField + Extendable<D>, const D: usize> {
|
pub(crate) struct AllChallengerState<F: RichField + Extendable<D>, HC: HashConfig, const D: usize>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
/// Sponge state of the challenger before starting each proof,
|
/// Sponge state of the challenger before starting each proof,
|
||||||
/// along with the final state after all proofs are done. This final state isn't strictly needed.
|
/// along with the final state after all proofs are done. This final state isn't strictly needed.
|
||||||
pub states: [[F; SPONGE_WIDTH]; NUM_TABLES + 1],
|
pub states: [[F; HC::WIDTH]; NUM_TABLES + 1],
|
||||||
pub ctl_challenges: GrandProductChallengeSet<F>,
|
pub ctl_challenges: GrandProductChallengeSet<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,32 +117,48 @@ pub struct BlockMetadataTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StarkProof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
|
pub struct StarkProof<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> {
|
||||||
/// Merkle cap of LDEs of trace values.
|
/// Merkle cap of LDEs of trace values.
|
||||||
pub trace_cap: MerkleCap<F, C::Hasher>,
|
pub trace_cap: MerkleCap<F, HCO, C::Hasher>,
|
||||||
/// Merkle cap of LDEs of permutation Z values.
|
/// Merkle cap of LDEs of permutation Z values.
|
||||||
pub permutation_ctl_zs_cap: MerkleCap<F, C::Hasher>,
|
pub permutation_ctl_zs_cap: MerkleCap<F, HCO, C::Hasher>,
|
||||||
/// Merkle cap of LDEs of trace values.
|
/// Merkle cap of LDEs of trace values.
|
||||||
pub quotient_polys_cap: MerkleCap<F, C::Hasher>,
|
pub quotient_polys_cap: MerkleCap<F, HCO, C::Hasher>,
|
||||||
/// Purported values of each polynomial at the challenge point.
|
/// Purported values of each polynomial at the challenge point.
|
||||||
pub openings: StarkOpeningSet<F, D>,
|
pub openings: StarkOpeningSet<F, D>,
|
||||||
/// A batch FRI argument for all openings.
|
/// A batch FRI argument for all openings.
|
||||||
pub opening_proof: FriProof<F, C::Hasher, D>,
|
pub opening_proof: FriProof<F, HCO, C::Hasher, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `StarkProof` along with some metadata about the initial Fiat-Shamir state, which is used when
|
/// A `StarkProof` along with some metadata about the initial Fiat-Shamir state, which is used when
|
||||||
/// creating a recursive wrapper proof around a STARK proof.
|
/// creating a recursive wrapper proof around a STARK proof.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StarkProofWithMetadata<F, C, const D: usize>
|
pub struct StarkProofWithMetadata<F, HCO, HCI, C, const D: usize>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
pub(crate) init_challenger_state: [F; SPONGE_WIDTH],
|
pub(crate) init_challenger_state: [F; HCO::WIDTH],
|
||||||
pub(crate) proof: StarkProof<F, C, D>,
|
pub(crate) proof: StarkProof<F, HCO, HCI, C, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> StarkProof<F, C, D> {
|
impl<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> StarkProof<F, HCO, HCI, C, D>
|
||||||
|
{
|
||||||
/// Recover the length of the trace from a STARK proof and a STARK config.
|
/// Recover the length of the trace from a STARK proof and a STARK config.
|
||||||
pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize {
|
pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize {
|
||||||
let initial_merkle_proof = &self.opening_proof.query_round_proofs[0]
|
let initial_merkle_proof = &self.opening_proof.query_round_proofs[0]
|
||||||
@ -196,22 +232,22 @@ pub struct StarkOpeningSet<F: RichField + Extendable<D>, const D: usize> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
||||||
pub fn new<C: GenericConfig<D, F = F>>(
|
pub fn new<HCO: HashConfig, HCI: HashConfig, C: GenericConfig<HCO, HCI, D, F = F>>(
|
||||||
zeta: F::Extension,
|
zeta: F::Extension,
|
||||||
g: F,
|
g: F,
|
||||||
trace_commitment: &PolynomialBatch<F, C, D>,
|
trace_commitment: &PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
permutation_ctl_zs_commitment: &PolynomialBatch<F, C, D>,
|
permutation_ctl_zs_commitment: &PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
quotient_commitment: &PolynomialBatch<F, C, D>,
|
quotient_commitment: &PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
degree_bits: usize,
|
degree_bits: usize,
|
||||||
num_permutation_zs: usize,
|
num_permutation_zs: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let eval_commitment = |z: F::Extension, c: &PolynomialBatch<F, C, D>| {
|
let eval_commitment = |z: F::Extension, c: &PolynomialBatch<F, HCO, HCI, C, D>| {
|
||||||
c.polynomials
|
c.polynomials
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|p| p.to_extension().eval(z))
|
.map(|p| p.to_extension().eval(z))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
let eval_commitment_base = |z: F, c: &PolynomialBatch<F, C, D>| {
|
let eval_commitment_base = |z: F, c: &PolynomialBatch<F, HCO, HCI, C, D>| {
|
||||||
c.polynomials
|
c.polynomials
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|p| p.eval(z))
|
.map(|p| p.eval(z))
|
||||||
|
|||||||
@ -11,6 +11,7 @@ use plonky2::field::types::Field;
|
|||||||
use plonky2::field::zero_poly_coset::ZeroPolyOnCoset;
|
use plonky2::field::zero_poly_coset::ZeroPolyOnCoset;
|
||||||
use plonky2::fri::oracle::PolynomialBatch;
|
use plonky2::fri::oracle::PolynomialBatch;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::challenger::Challenger;
|
use plonky2::iop::challenger::Challenger;
|
||||||
use plonky2::plonk::config::{GenericConfig, Hasher};
|
use plonky2::plonk::config::{GenericConfig, Hasher};
|
||||||
use plonky2::timed;
|
use plonky2::timed;
|
||||||
@ -41,21 +42,25 @@ use crate::vanishing_poly::eval_vanishing_poly;
|
|||||||
use crate::vars::StarkEvaluationVars;
|
use crate::vars::StarkEvaluationVars;
|
||||||
|
|
||||||
/// Generate traces, then create all STARK proofs.
|
/// Generate traces, then create all STARK proofs.
|
||||||
pub fn prove<F, C, const D: usize>(
|
pub fn prove<F, HCO, HCI, C, const D: usize>(
|
||||||
all_stark: &AllStark<F, D>,
|
all_stark: &AllStark<F, D>,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
inputs: GenerationInputs,
|
inputs: GenerationInputs,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> Result<AllProof<F, C, D>>
|
) -> Result<AllProof<F, HCO, HCI, C, D>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
[(); CpuStark::<F, D>::COLUMNS]:,
|
[(); CpuStark::<F, D>::COLUMNS]:,
|
||||||
[(); KeccakStark::<F, D>::COLUMNS]:,
|
[(); KeccakStark::<F, D>::COLUMNS]:,
|
||||||
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
||||||
[(); LogicStark::<F, D>::COLUMNS]:,
|
[(); LogicStark::<F, D>::COLUMNS]:,
|
||||||
[(); MemoryStark::<F, D>::COLUMNS]:,
|
[(); MemoryStark::<F, D>::COLUMNS]:,
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let (proof, _outputs) = prove_with_outputs(all_stark, config, inputs, timing)?;
|
let (proof, _outputs) = prove_with_outputs(all_stark, config, inputs, timing)?;
|
||||||
Ok(proof)
|
Ok(proof)
|
||||||
@ -63,21 +68,25 @@ where
|
|||||||
|
|
||||||
/// Generate traces, then create all STARK proofs. Returns information about the post-state,
|
/// Generate traces, then create all STARK proofs. Returns information about the post-state,
|
||||||
/// intended for debugging, in addition to the proof.
|
/// intended for debugging, in addition to the proof.
|
||||||
pub fn prove_with_outputs<F, C, const D: usize>(
|
pub fn prove_with_outputs<F, HCO, HCI, C, const D: usize>(
|
||||||
all_stark: &AllStark<F, D>,
|
all_stark: &AllStark<F, D>,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
inputs: GenerationInputs,
|
inputs: GenerationInputs,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> Result<(AllProof<F, C, D>, GenerationOutputs)>
|
) -> Result<(AllProof<F, HCO, HCI, C, D>, GenerationOutputs)>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
[(); CpuStark::<F, D>::COLUMNS]:,
|
[(); CpuStark::<F, D>::COLUMNS]:,
|
||||||
[(); KeccakStark::<F, D>::COLUMNS]:,
|
[(); KeccakStark::<F, D>::COLUMNS]:,
|
||||||
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
||||||
[(); LogicStark::<F, D>::COLUMNS]:,
|
[(); LogicStark::<F, D>::COLUMNS]:,
|
||||||
[(); MemoryStark::<F, D>::COLUMNS]:,
|
[(); MemoryStark::<F, D>::COLUMNS]:,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
timed!(timing, "build kernel", Lazy::force(&KERNEL));
|
timed!(timing, "build kernel", Lazy::force(&KERNEL));
|
||||||
let (traces, public_values, outputs) = timed!(
|
let (traces, public_values, outputs) = timed!(
|
||||||
@ -90,22 +99,26 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compute all STARK proofs.
|
/// Compute all STARK proofs.
|
||||||
pub(crate) fn prove_with_traces<F, C, const D: usize>(
|
pub(crate) fn prove_with_traces<F, HCO, HCI, C, const D: usize>(
|
||||||
all_stark: &AllStark<F, D>,
|
all_stark: &AllStark<F, D>,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
trace_poly_values: [Vec<PolynomialValues<F>>; NUM_TABLES],
|
trace_poly_values: [Vec<PolynomialValues<F>>; NUM_TABLES],
|
||||||
public_values: PublicValues,
|
public_values: PublicValues,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> Result<AllProof<F, C, D>>
|
) -> Result<AllProof<F, HCO, HCI, C, D>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
[(); CpuStark::<F, D>::COLUMNS]:,
|
[(); CpuStark::<F, D>::COLUMNS]:,
|
||||||
[(); KeccakStark::<F, D>::COLUMNS]:,
|
[(); KeccakStark::<F, D>::COLUMNS]:,
|
||||||
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
||||||
[(); LogicStark::<F, D>::COLUMNS]:,
|
[(); LogicStark::<F, D>::COLUMNS]:,
|
||||||
[(); MemoryStark::<F, D>::COLUMNS]:,
|
[(); MemoryStark::<F, D>::COLUMNS]:,
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let rate_bits = config.fri_config.rate_bits;
|
let rate_bits = config.fri_config.rate_bits;
|
||||||
let cap_height = config.fri_config.cap_height;
|
let cap_height = config.fri_config.cap_height;
|
||||||
@ -120,7 +133,7 @@ where
|
|||||||
timed!(
|
timed!(
|
||||||
timing,
|
timing,
|
||||||
&format!("compute trace commitment for {:?}", table),
|
&format!("compute trace commitment for {:?}", table),
|
||||||
PolynomialBatch::<F, C, D>::from_values(
|
PolynomialBatch::<F, HCO, HCI, C, D>::from_values(
|
||||||
// TODO: Cloning this isn't great; consider having `from_values` accept a reference,
|
// TODO: Cloning this isn't great; consider having `from_values` accept a reference,
|
||||||
// or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`.
|
// or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`.
|
||||||
trace.clone(),
|
trace.clone(),
|
||||||
@ -139,7 +152,7 @@ where
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|c| c.merkle_tree.cap.clone())
|
.map(|c| c.merkle_tree.cap.clone())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let mut challenger = Challenger::<F, C::Hasher>::new();
|
let mut challenger = Challenger::<F, HCO, C::Hasher>::new();
|
||||||
for cap in &trace_caps {
|
for cap in &trace_caps {
|
||||||
challenger.observe_cap(cap);
|
challenger.observe_cap(cap);
|
||||||
}
|
}
|
||||||
@ -176,24 +189,28 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prove_with_commitments<F, C, const D: usize>(
|
fn prove_with_commitments<F, HCO, HCI, C, const D: usize>(
|
||||||
all_stark: &AllStark<F, D>,
|
all_stark: &AllStark<F, D>,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
trace_poly_values: [Vec<PolynomialValues<F>>; NUM_TABLES],
|
trace_poly_values: [Vec<PolynomialValues<F>>; NUM_TABLES],
|
||||||
trace_commitments: Vec<PolynomialBatch<F, C, D>>,
|
trace_commitments: Vec<PolynomialBatch<F, HCO, HCI, C, D>>,
|
||||||
ctl_data_per_table: [CtlData<F>; NUM_TABLES],
|
ctl_data_per_table: [CtlData<F>; NUM_TABLES],
|
||||||
challenger: &mut Challenger<F, C::Hasher>,
|
challenger: &mut Challenger<F, HCO, C::Hasher>,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> Result<[StarkProofWithMetadata<F, C, D>; NUM_TABLES]>
|
) -> Result<[StarkProofWithMetadata<F, HCO, HCI, C, D>; NUM_TABLES]>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
[(); CpuStark::<F, D>::COLUMNS]:,
|
[(); CpuStark::<F, D>::COLUMNS]:,
|
||||||
[(); KeccakStark::<F, D>::COLUMNS]:,
|
[(); KeccakStark::<F, D>::COLUMNS]:,
|
||||||
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
||||||
[(); LogicStark::<F, D>::COLUMNS]:,
|
[(); LogicStark::<F, D>::COLUMNS]:,
|
||||||
[(); MemoryStark::<F, D>::COLUMNS]:,
|
[(); MemoryStark::<F, D>::COLUMNS]:,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let cpu_proof = timed!(
|
let cpu_proof = timed!(
|
||||||
timing,
|
timing,
|
||||||
@ -270,21 +287,24 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compute proof for a single STARK table.
|
/// Compute proof for a single STARK table.
|
||||||
pub(crate) fn prove_single_table<F, C, S, const D: usize>(
|
pub(crate) fn prove_single_table<F, HCO, HCI, C, S, const D: usize>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
trace_poly_values: &[PolynomialValues<F>],
|
trace_poly_values: &[PolynomialValues<F>],
|
||||||
trace_commitment: &PolynomialBatch<F, C, D>,
|
trace_commitment: &PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
ctl_data: &CtlData<F>,
|
ctl_data: &CtlData<F>,
|
||||||
challenger: &mut Challenger<F, C::Hasher>,
|
challenger: &mut Challenger<F, HCO, C::Hasher>,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> Result<StarkProofWithMetadata<F, C, D>>
|
) -> Result<StarkProofWithMetadata<F, HCO, HCI, C, D>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let degree = trace_poly_values[0].len();
|
let degree = trace_poly_values[0].len();
|
||||||
let degree_bits = log2_strict(degree);
|
let degree_bits = log2_strict(degree);
|
||||||
@ -357,7 +377,7 @@ where
|
|||||||
let quotient_polys = timed!(
|
let quotient_polys = timed!(
|
||||||
timing,
|
timing,
|
||||||
"compute quotient polys",
|
"compute quotient polys",
|
||||||
compute_quotient_polys::<F, <F as Packable>::Packing, C, S, D>(
|
compute_quotient_polys::<F, <F as Packable>::Packing, HCO, HCI, C, S, D>(
|
||||||
stark,
|
stark,
|
||||||
trace_commitment,
|
trace_commitment,
|
||||||
&permutation_ctl_zs_commitment,
|
&permutation_ctl_zs_commitment,
|
||||||
@ -454,10 +474,10 @@ where
|
|||||||
|
|
||||||
/// Computes the quotient polynomials `(sum alpha^i C_i(x)) / Z_H(x)` for `alpha` in `alphas`,
|
/// Computes the quotient polynomials `(sum alpha^i C_i(x)) / Z_H(x)` for `alpha` in `alphas`,
|
||||||
/// where the `C_i`s are the Stark constraints.
|
/// where the `C_i`s are the Stark constraints.
|
||||||
fn compute_quotient_polys<'a, F, P, C, S, const D: usize>(
|
fn compute_quotient_polys<'a, F, P, HCO, HCI, C, S, const D: usize>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
trace_commitment: &'a PolynomialBatch<F, C, D>,
|
trace_commitment: &'a PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
permutation_ctl_zs_commitment: &'a PolynomialBatch<F, C, D>,
|
permutation_ctl_zs_commitment: &'a PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
permutation_challenges: Option<&'a Vec<GrandProductChallengeSet<F>>>,
|
permutation_challenges: Option<&'a Vec<GrandProductChallengeSet<F>>>,
|
||||||
ctl_data: &CtlData<F>,
|
ctl_data: &CtlData<F>,
|
||||||
alphas: Vec<F>,
|
alphas: Vec<F>,
|
||||||
@ -468,7 +488,9 @@ fn compute_quotient_polys<'a, F, P, C, S, const D: usize>(
|
|||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
P: PackedField<Scalar = F>,
|
P: PackedField<Scalar = F>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
{
|
{
|
||||||
@ -591,10 +613,10 @@ where
|
|||||||
|
|
||||||
/// Check that all constraints evaluate to zero on `H`.
|
/// Check that all constraints evaluate to zero on `H`.
|
||||||
/// Can also be used to check the degree of the constraints by evaluating on a larger subgroup.
|
/// Can also be used to check the degree of the constraints by evaluating on a larger subgroup.
|
||||||
fn check_constraints<'a, F, C, S, const D: usize>(
|
fn check_constraints<'a, F, HCO, HCI, C, S, const D: usize>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
trace_commitment: &'a PolynomialBatch<F, C, D>,
|
trace_commitment: &'a PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
permutation_ctl_zs_commitment: &'a PolynomialBatch<F, C, D>,
|
permutation_ctl_zs_commitment: &'a PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
permutation_challenges: Option<&'a Vec<GrandProductChallengeSet<F>>>,
|
permutation_challenges: Option<&'a Vec<GrandProductChallengeSet<F>>>,
|
||||||
ctl_data: &CtlData<F>,
|
ctl_data: &CtlData<F>,
|
||||||
alphas: Vec<F>,
|
alphas: Vec<F>,
|
||||||
@ -603,7 +625,9 @@ fn check_constraints<'a, F, C, S, const D: usize>(
|
|||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) where
|
) where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
{
|
{
|
||||||
@ -621,7 +645,7 @@ fn check_constraints<'a, F, C, S, const D: usize>(
|
|||||||
let subgroup = F::two_adic_subgroup(degree_bits + rate_bits);
|
let subgroup = F::two_adic_subgroup(degree_bits + rate_bits);
|
||||||
|
|
||||||
// Get the evaluations of a batch of polynomials over our subgroup.
|
// Get the evaluations of a batch of polynomials over our subgroup.
|
||||||
let get_subgroup_evals = |comm: &PolynomialBatch<F, C, D>| -> Vec<Vec<F>> {
|
let get_subgroup_evals = |comm: &PolynomialBatch<F, HCO, HCI, C, D>| -> Vec<Vec<F>> {
|
||||||
let values = comm
|
let values = comm
|
||||||
.polynomials
|
.polynomials
|
||||||
.par_iter()
|
.par_iter()
|
||||||
|
|||||||
@ -9,14 +9,14 @@ use plonky2::gates::exponentiation::ExponentiationGate;
|
|||||||
use plonky2::gates::gate::GateRef;
|
use plonky2::gates::gate::GateRef;
|
||||||
use plonky2::gates::noop::NoopGate;
|
use plonky2::gates::noop::NoopGate;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::hash::hashing::SPONGE_WIDTH;
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::challenger::{Challenger, RecursiveChallenger};
|
use plonky2::iop::challenger::{Challenger, RecursiveChallenger};
|
||||||
use plonky2::iop::ext_target::ExtensionTarget;
|
use plonky2::iop::ext_target::ExtensionTarget;
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
use plonky2::iop::witness::{PartialWitness, Witness, WitnessWrite};
|
use plonky2::iop::witness::{PartialWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, VerifierCircuitData};
|
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, VerifierCircuitData};
|
||||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher};
|
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||||
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||||
use plonky2::util::reducing::ReducingFactorTarget;
|
use plonky2::util::reducing::ReducingFactorTarget;
|
||||||
use plonky2::with_context;
|
use plonky2::with_context;
|
||||||
@ -43,18 +43,23 @@ use crate::vars::StarkEvaluationTargets;
|
|||||||
/// Table-wise recursive proofs of an `AllProof`.
|
/// Table-wise recursive proofs of an `AllProof`.
|
||||||
pub struct RecursiveAllProof<
|
pub struct RecursiveAllProof<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
> {
|
> {
|
||||||
pub recursive_proofs: [ProofWithPublicInputs<F, C, D>; NUM_TABLES],
|
pub recursive_proofs: [ProofWithPublicInputs<F, HCO, HCI, C, D>; NUM_TABLES],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct PublicInputs<T: Copy + Eq + PartialEq + Debug> {
|
pub(crate) struct PublicInputs<T: Copy + Eq + PartialEq + Debug, HC: HashConfig>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
pub(crate) trace_cap: Vec<Vec<T>>,
|
pub(crate) trace_cap: Vec<Vec<T>>,
|
||||||
pub(crate) ctl_zs_last: Vec<T>,
|
pub(crate) ctl_zs_last: Vec<T>,
|
||||||
pub(crate) ctl_challenges: GrandProductChallengeSet<T>,
|
pub(crate) ctl_challenges: GrandProductChallengeSet<T>,
|
||||||
pub(crate) challenger_state_before: [T; SPONGE_WIDTH],
|
pub(crate) challenger_state_before: [T; HC::WIDTH],
|
||||||
pub(crate) challenger_state_after: [T; SPONGE_WIDTH],
|
pub(crate) challenger_state_after: [T; HC::WIDTH],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to the unstable `Iterator::next_chunk`. Could be replaced with that when it's stable.
|
/// Similar to the unstable `Iterator::next_chunk`. Could be replaced with that when it's stable.
|
||||||
@ -66,7 +71,10 @@ fn next_chunk<T: Debug, const N: usize>(iter: &mut impl Iterator<Item = T>) -> [
|
|||||||
.expect("Not enough elements")
|
.expect("Not enough elements")
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy + Eq + PartialEq + Debug> PublicInputs<T> {
|
impl<T: Copy + Eq + PartialEq + Debug, HC: HashConfig> PublicInputs<T, HC>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
pub(crate) fn from_vec(v: &[T], config: &StarkConfig) -> Self {
|
pub(crate) fn from_vec(v: &[T], config: &StarkConfig) -> Self {
|
||||||
let mut iter = v.iter().copied();
|
let mut iter = v.iter().copied();
|
||||||
let trace_cap = (0..config.fri_config.num_cap_elements())
|
let trace_cap = (0..config.fri_config.num_cap_elements())
|
||||||
@ -94,24 +102,30 @@ impl<T: Copy + Eq + PartialEq + Debug> PublicInputs<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
impl<
|
||||||
RecursiveAllProof<F, C, D>
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> RecursiveAllProof<F, HCO, HCI, C, D>
|
||||||
{
|
{
|
||||||
/// Verify every recursive proof.
|
/// Verify every recursive proof.
|
||||||
pub fn verify(
|
pub fn verify(
|
||||||
self,
|
self,
|
||||||
verifier_data: &[VerifierCircuitData<F, C, D>; NUM_TABLES],
|
verifier_data: &[VerifierCircuitData<F, HCO, HCI, C, D>; NUM_TABLES],
|
||||||
cross_table_lookups: Vec<CrossTableLookup<F>>,
|
cross_table_lookups: Vec<CrossTableLookup<F>>,
|
||||||
inner_config: &StarkConfig,
|
inner_config: &StarkConfig,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let pis: [_; NUM_TABLES] = core::array::from_fn(|i| {
|
let pis: [_; NUM_TABLES] = core::array::from_fn(|i| {
|
||||||
PublicInputs::from_vec(&self.recursive_proofs[i].public_inputs, inner_config)
|
PublicInputs::<F, HCO>::from_vec(&self.recursive_proofs[i].public_inputs, inner_config)
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut challenger = Challenger::<F, C::Hasher>::new();
|
let mut challenger = Challenger::<F, HCO, C::Hasher>::new();
|
||||||
for pi in &pis {
|
for pi in &pis {
|
||||||
for h in &pi.trace_cap {
|
for h in &pi.trace_cap {
|
||||||
challenger.observe_elements(h);
|
challenger.observe_elements(h);
|
||||||
@ -147,29 +161,36 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a circuit which recursively verifies a STARK proof.
|
/// Represents a circuit which recursively verifies a STARK proof.
|
||||||
pub(crate) struct StarkWrapperCircuit<F, C, const D: usize>
|
pub(crate) struct StarkWrapperCircuit<F, HCO: HashConfig, HCI: HashConfig, C, const D: usize>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
pub(crate) circuit: CircuitData<F, C, D>,
|
pub(crate) circuit: CircuitData<F, HCO, HCI, C, D>,
|
||||||
pub(crate) stark_proof_target: StarkProofTarget<D>,
|
pub(crate) stark_proof_target: StarkProofTarget<D>,
|
||||||
pub(crate) ctl_challenges_target: GrandProductChallengeSet<Target>,
|
pub(crate) ctl_challenges_target: GrandProductChallengeSet<Target>,
|
||||||
pub(crate) init_challenger_state_target: [Target; SPONGE_WIDTH],
|
pub(crate) init_challenger_state_target: [Target; HCO::WIDTH],
|
||||||
pub(crate) zero_target: Target,
|
pub(crate) zero_target: Target,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, C, const D: usize> StarkWrapperCircuit<F, C, D>
|
impl<F, HCO, HCI, C, const D: usize> StarkWrapperCircuit<F, HCO, HCI, C, D>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
pub(crate) fn prove(
|
pub(crate) fn prove(
|
||||||
&self,
|
&self,
|
||||||
proof_with_metadata: &StarkProofWithMetadata<F, C, D>,
|
proof_with_metadata: &StarkProofWithMetadata<F, HCO, HCI, C, D>,
|
||||||
ctl_challenges: &GrandProductChallengeSet<F>,
|
ctl_challenges: &GrandProductChallengeSet<F>,
|
||||||
) -> Result<ProofWithPublicInputs<F, C, D>> {
|
) -> Result<ProofWithPublicInputs<F, HCO, HCI, C, D>> {
|
||||||
let mut inputs = PartialWitness::new();
|
let mut inputs = PartialWitness::new();
|
||||||
|
|
||||||
set_stark_proof_target(
|
set_stark_proof_target(
|
||||||
@ -199,25 +220,31 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a circuit which recursively verifies a PLONK proof.
|
/// Represents a circuit which recursively verifies a PLONK proof.
|
||||||
pub(crate) struct PlonkWrapperCircuit<F, C, const D: usize>
|
pub(crate) struct PlonkWrapperCircuit<F, HCO, HCI, C, const D: usize>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
pub(crate) circuit: CircuitData<F, C, D>,
|
pub(crate) circuit: CircuitData<F, HCO, HCI, C, D>,
|
||||||
pub(crate) proof_with_pis_target: ProofWithPublicInputsTarget<D>,
|
pub(crate) proof_with_pis_target: ProofWithPublicInputsTarget<D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, C, const D: usize> PlonkWrapperCircuit<F, C, D>
|
impl<F, HCO, HCI, C, const D: usize> PlonkWrapperCircuit<F, HCO, HCI, C, D>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
pub(crate) fn prove(
|
pub(crate) fn prove(
|
||||||
&self,
|
&self,
|
||||||
proof: &ProofWithPublicInputs<F, C, D>,
|
proof: &ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
) -> Result<ProofWithPublicInputs<F, C, D>> {
|
) -> Result<ProofWithPublicInputs<F, HCO, HCI, C, D>> {
|
||||||
let mut inputs = PartialWitness::new();
|
let mut inputs = PartialWitness::new();
|
||||||
inputs.set_proof_with_pis_target(&self.proof_with_pis_target, proof);
|
inputs.set_proof_with_pis_target(&self.proof_with_pis_target, proof);
|
||||||
self.circuit.prove(inputs)
|
self.circuit.prove(inputs)
|
||||||
@ -227,7 +254,9 @@ where
|
|||||||
/// Returns the recursive Stark circuit.
|
/// Returns the recursive Stark circuit.
|
||||||
pub(crate) fn recursive_stark_circuit<
|
pub(crate) fn recursive_stark_circuit<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
@ -238,11 +267,12 @@ pub(crate) fn recursive_stark_circuit<
|
|||||||
inner_config: &StarkConfig,
|
inner_config: &StarkConfig,
|
||||||
circuit_config: &CircuitConfig,
|
circuit_config: &CircuitConfig,
|
||||||
min_degree_bits: usize,
|
min_degree_bits: usize,
|
||||||
) -> StarkWrapperCircuit<F, C, D>
|
) -> StarkWrapperCircuit<F, HCO, HCI, C, D>
|
||||||
where
|
where
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(circuit_config.clone());
|
let mut builder = CircuitBuilder::<F, D>::new(circuit_config.clone());
|
||||||
let zero_target = builder.zero();
|
let zero_target = builder.zero();
|
||||||
@ -281,8 +311,8 @@ where
|
|||||||
|
|
||||||
let init_challenger_state_target = core::array::from_fn(|_| builder.add_virtual_public_input());
|
let init_challenger_state_target = core::array::from_fn(|_| builder.add_virtual_public_input());
|
||||||
let mut challenger =
|
let mut challenger =
|
||||||
RecursiveChallenger::<F, C::Hasher, D>::from_state(init_challenger_state_target);
|
RecursiveChallenger::<F, HCO, C::Hasher, D>::from_state(init_challenger_state_target);
|
||||||
let challenges = proof_target.get_challenges::<F, C>(
|
let challenges = proof_target.get_challenges::<F, HCO, HCI, C>(
|
||||||
&mut builder,
|
&mut builder,
|
||||||
&mut challenger,
|
&mut challenger,
|
||||||
num_permutation_zs > 0,
|
num_permutation_zs > 0,
|
||||||
@ -294,7 +324,7 @@ where
|
|||||||
|
|
||||||
builder.register_public_inputs(&proof_target.openings.ctl_zs_last);
|
builder.register_public_inputs(&proof_target.openings.ctl_zs_last);
|
||||||
|
|
||||||
verify_stark_proof_with_challenges_circuit::<F, C, _, D>(
|
verify_stark_proof_with_challenges_circuit::<F, HCO, HCI, C, _, D>(
|
||||||
&mut builder,
|
&mut builder,
|
||||||
stark,
|
stark,
|
||||||
&proof_target,
|
&proof_target,
|
||||||
@ -310,7 +340,7 @@ where
|
|||||||
builder.add_gate(NoopGate, vec![]);
|
builder.add_gate(NoopGate, vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let circuit = builder.build::<C>();
|
let circuit = builder.build::<HCO, HCI, C>();
|
||||||
StarkWrapperCircuit {
|
StarkWrapperCircuit {
|
||||||
circuit,
|
circuit,
|
||||||
stark_proof_target: proof_target,
|
stark_proof_target: proof_target,
|
||||||
@ -334,7 +364,9 @@ pub(crate) fn add_common_recursion_gates<F: RichField + Extendable<D>, const D:
|
|||||||
/// Recursively verifies an inner proof.
|
/// Recursively verifies an inner proof.
|
||||||
fn verify_stark_proof_with_challenges_circuit<
|
fn verify_stark_proof_with_challenges_circuit<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
@ -345,8 +377,9 @@ fn verify_stark_proof_with_challenges_circuit<
|
|||||||
ctl_vars: &[CtlCheckVarsTarget<F, D>],
|
ctl_vars: &[CtlCheckVarsTarget<F, D>],
|
||||||
inner_config: &StarkConfig,
|
inner_config: &StarkConfig,
|
||||||
) where
|
) where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
let zero = builder.zero();
|
let zero = builder.zero();
|
||||||
let one = builder.one_extension();
|
let one = builder.one_extension();
|
||||||
@ -430,7 +463,7 @@ fn verify_stark_proof_with_challenges_circuit<
|
|||||||
ctl_zs_last.len(),
|
ctl_zs_last.len(),
|
||||||
inner_config,
|
inner_config,
|
||||||
);
|
);
|
||||||
builder.verify_fri_proof::<C>(
|
builder.verify_fri_proof::<HCO, HCI, C>(
|
||||||
&fri_instance,
|
&fri_instance,
|
||||||
&proof.openings.to_fri_openings(zero),
|
&proof.openings.to_fri_openings(zero),
|
||||||
&challenges.fri_challenges,
|
&challenges.fri_challenges,
|
||||||
@ -558,14 +591,23 @@ fn add_virtual_stark_opening_set<F: RichField + Extendable<D>, S: Stark<F, D>, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_stark_proof_target<F, C: GenericConfig<D, F = F>, W, const D: usize>(
|
pub(crate) fn set_stark_proof_target<
|
||||||
|
F,
|
||||||
|
HCO,
|
||||||
|
HCI,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
W,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
witness: &mut W,
|
witness: &mut W,
|
||||||
proof_target: &StarkProofTarget<D>,
|
proof_target: &StarkProofTarget<D>,
|
||||||
proof: &StarkProof<F, C, D>,
|
proof: &StarkProof<F, HCO, HCI, C, D>,
|
||||||
zero: Target,
|
zero: Target,
|
||||||
) where
|
) where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
W: Witness<F>,
|
W: Witness<F>,
|
||||||
{
|
{
|
||||||
witness.set_cap_target(&proof_target.trace_cap, &proof.trace_cap);
|
witness.set_cap_target(&proof_target.trace_cap, &proof.trace_cap);
|
||||||
|
|||||||
@ -3,10 +3,11 @@ use plonky2::field::extension::{Extendable, FieldExtension};
|
|||||||
use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues};
|
use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues};
|
||||||
use plonky2::field::types::{Field, Sample};
|
use plonky2::field::types::{Field, Sample};
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::config::{GenericConfig, Hasher};
|
use plonky2::plonk::config::GenericConfig;
|
||||||
use plonky2::util::transpose;
|
use plonky2::util::transpose;
|
||||||
use plonky2_util::{log2_ceil, log2_strict};
|
use plonky2_util::{log2_ceil, log2_strict};
|
||||||
|
|
||||||
@ -78,7 +79,9 @@ where
|
|||||||
/// Tests that the circuit constraints imposed by the given STARK are coherent with the native constraints.
|
/// Tests that the circuit constraints imposed by the given STARK are coherent with the native constraints.
|
||||||
pub fn test_stark_circuit_constraints<
|
pub fn test_stark_circuit_constraints<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
@ -86,7 +89,8 @@ pub fn test_stark_circuit_constraints<
|
|||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
// Compute native constraint evaluation on random values.
|
// Compute native constraint evaluation on random values.
|
||||||
let vars = StarkEvaluationVars {
|
let vars = StarkEvaluationVars {
|
||||||
@ -144,7 +148,7 @@ where
|
|||||||
let native_eval_t = builder.constant_extension(native_eval);
|
let native_eval_t = builder.constant_extension(native_eval);
|
||||||
builder.connect_extension(circuit_eval, native_eval_t);
|
builder.connect_extension(circuit_eval, native_eval_t);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
data.verify(proof)
|
data.verify(proof)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,8 @@ use plonky2::field::extension::{Extendable, FieldExtension};
|
|||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use plonky2::fri::verifier::verify_fri_proof;
|
use plonky2::fri::verifier::verify_fri_proof;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::plonk::config::{GenericConfig, Hasher};
|
use plonky2::hash::hashing::HashConfig;
|
||||||
|
use plonky2::plonk::config::GenericConfig;
|
||||||
use plonky2::plonk::plonk_common::reduce_with_powers;
|
use plonky2::plonk::plonk_common::reduce_with_powers;
|
||||||
|
|
||||||
use crate::all_stark::{AllStark, Table};
|
use crate::all_stark::{AllStark, Table};
|
||||||
@ -25,9 +26,15 @@ use crate::stark::Stark;
|
|||||||
use crate::vanishing_poly::eval_vanishing_poly;
|
use crate::vanishing_poly::eval_vanishing_poly;
|
||||||
use crate::vars::StarkEvaluationVars;
|
use crate::vars::StarkEvaluationVars;
|
||||||
|
|
||||||
pub fn verify_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
pub fn verify_proof<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
all_stark: &AllStark<F, D>,
|
all_stark: &AllStark<F, D>,
|
||||||
all_proof: AllProof<F, C, D>,
|
all_proof: AllProof<F, HCO, HCI, C, D>,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
@ -36,7 +43,8 @@ where
|
|||||||
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
||||||
[(); LogicStark::<F, D>::COLUMNS]:,
|
[(); LogicStark::<F, D>::COLUMNS]:,
|
||||||
[(); MemoryStark::<F, D>::COLUMNS]:,
|
[(); MemoryStark::<F, D>::COLUMNS]:,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let AllProofChallenges {
|
let AllProofChallenges {
|
||||||
stark_challenges,
|
stark_challenges,
|
||||||
@ -106,19 +114,21 @@ where
|
|||||||
|
|
||||||
pub(crate) fn verify_stark_proof_with_challenges<
|
pub(crate) fn verify_stark_proof_with_challenges<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
proof: &StarkProof<F, C, D>,
|
proof: &StarkProof<F, HCO, HCI, C, D>,
|
||||||
challenges: &StarkProofChallenges<F, D>,
|
challenges: &StarkProofChallenges<F, D>,
|
||||||
ctl_vars: &[CtlCheckVars<F, F::Extension, F::Extension, D>],
|
ctl_vars: &[CtlCheckVars<F, F::Extension, F::Extension, D>],
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
log::debug!("Checking proof: {}", type_name::<S>());
|
log::debug!("Checking proof: {}", type_name::<S>());
|
||||||
validate_proof_shape(stark, proof, config, ctl_vars.len())?;
|
validate_proof_shape(stark, proof, config, ctl_vars.len())?;
|
||||||
@ -189,7 +199,7 @@ where
|
|||||||
proof.quotient_polys_cap.clone(),
|
proof.quotient_polys_cap.clone(),
|
||||||
];
|
];
|
||||||
|
|
||||||
verify_fri_proof::<F, C, D>(
|
verify_fri_proof::<F, HCO, HCI, C, D>(
|
||||||
&stark.fri_instance(
|
&stark.fri_instance(
|
||||||
challenges.stark_zeta,
|
challenges.stark_zeta,
|
||||||
F::primitive_root_of_unity(degree_bits),
|
F::primitive_root_of_unity(degree_bits),
|
||||||
@ -207,18 +217,19 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_proof_shape<F, C, S, const D: usize>(
|
fn validate_proof_shape<F, HCO, HCI, C, S, const D: usize>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
proof: &StarkProof<F, C, D>,
|
proof: &StarkProof<F, HCO, HCI, C, D>,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
num_ctl_zs: usize,
|
num_ctl_zs: usize,
|
||||||
) -> anyhow::Result<()>
|
) -> anyhow::Result<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
|
||||||
{
|
{
|
||||||
let StarkProof {
|
let StarkProof {
|
||||||
trace_cap,
|
trace_cap,
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -8,7 +10,7 @@ use ethereum_types::Address;
|
|||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use keccak_hash::keccak;
|
use keccak_hash::keccak;
|
||||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||||
use plonky2::plonk::config::PoseidonGoldilocksConfig;
|
use plonky2::plonk::config::{PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use plonky2::util::timing::TimingTree;
|
use plonky2::util::timing::TimingTree;
|
||||||
use plonky2_evm::all_stark::AllStark;
|
use plonky2_evm::all_stark::AllStark;
|
||||||
use plonky2_evm::config::StarkConfig;
|
use plonky2_evm::config::StarkConfig;
|
||||||
@ -22,6 +24,8 @@ use plonky2_evm::Node;
|
|||||||
type F = GoldilocksField;
|
type F = GoldilocksField;
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
|
||||||
/// The `add11_yml` test case from https://github.com/ethereum/tests
|
/// The `add11_yml` test case from https://github.com/ethereum/tests
|
||||||
#[test]
|
#[test]
|
||||||
@ -96,7 +100,7 @@ fn add11_yml() -> anyhow::Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut timing = TimingTree::new("prove", log::Level::Debug);
|
let mut timing = TimingTree::new("prove", log::Level::Debug);
|
||||||
let proof = prove::<F, C, D>(&all_stark, &config, inputs, &mut timing)?;
|
let proof = prove::<F, HCO, HCI, C, D>(&all_stark, &config, inputs, &mut timing)?;
|
||||||
timing.filter(Duration::from_millis(100)).print();
|
timing.filter(Duration::from_millis(100)).print();
|
||||||
|
|
||||||
let beneficiary_account_after = AccountRlp {
|
let beneficiary_account_after = AccountRlp {
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -8,7 +10,7 @@ use ethereum_types::{Address, U256};
|
|||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use keccak_hash::keccak;
|
use keccak_hash::keccak;
|
||||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||||
use plonky2::plonk::config::PoseidonGoldilocksConfig;
|
use plonky2::plonk::config::{PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use plonky2::util::timing::TimingTree;
|
use plonky2::util::timing::TimingTree;
|
||||||
use plonky2_evm::all_stark::AllStark;
|
use plonky2_evm::all_stark::AllStark;
|
||||||
use plonky2_evm::config::StarkConfig;
|
use plonky2_evm::config::StarkConfig;
|
||||||
@ -23,6 +25,8 @@ use plonky2_evm::Node;
|
|||||||
type F = GoldilocksField;
|
type F = GoldilocksField;
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
|
||||||
/// Test a simple token transfer to a new address.
|
/// Test a simple token transfer to a new address.
|
||||||
#[test]
|
#[test]
|
||||||
@ -110,7 +114,7 @@ fn test_basic_smart_contract() -> anyhow::Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut timing = TimingTree::new("prove", log::Level::Debug);
|
let mut timing = TimingTree::new("prove", log::Level::Debug);
|
||||||
let proof = prove::<F, C, D>(&all_stark, &config, inputs, &mut timing)?;
|
let proof = prove::<F, HCO, HCI, C, D>(&all_stark, &config, inputs, &mut timing)?;
|
||||||
timing.filter(Duration::from_millis(100)).print();
|
timing.filter(Duration::from_millis(100)).print();
|
||||||
|
|
||||||
let expected_state_trie_after: HashedPartialTrie = {
|
let expected_state_trie_after: HashedPartialTrie = {
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -5,7 +7,7 @@ use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV};
|
|||||||
use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie};
|
use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie};
|
||||||
use keccak_hash::keccak;
|
use keccak_hash::keccak;
|
||||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||||
use plonky2::plonk::config::PoseidonGoldilocksConfig;
|
use plonky2::plonk::config::{PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use plonky2::util::timing::TimingTree;
|
use plonky2::util::timing::TimingTree;
|
||||||
use plonky2_evm::all_stark::AllStark;
|
use plonky2_evm::all_stark::AllStark;
|
||||||
use plonky2_evm::config::StarkConfig;
|
use plonky2_evm::config::StarkConfig;
|
||||||
@ -19,6 +21,8 @@ use plonky2_evm::Node;
|
|||||||
type F = GoldilocksField;
|
type F = GoldilocksField;
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
|
||||||
/// Execute the empty list of transactions, i.e. a no-op.
|
/// Execute the empty list of transactions, i.e. a no-op.
|
||||||
#[test]
|
#[test]
|
||||||
@ -60,7 +64,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
|
|||||||
// TODO: This is redundant; prove_root below calls this prove method internally.
|
// TODO: This is redundant; prove_root below calls this prove method internally.
|
||||||
// Just keeping it for now because the root proof returned by prove_root doesn't contain public
|
// Just keeping it for now because the root proof returned by prove_root doesn't contain public
|
||||||
// values yet, and we want those for the assertions below.
|
// values yet, and we want those for the assertions below.
|
||||||
let proof = prove::<F, C, D>(&all_stark, &config, inputs.clone(), &mut timing)?;
|
let proof = prove::<F, HCO, HCI, C, D>(&all_stark, &config, inputs.clone(), &mut timing)?;
|
||||||
timing.filter(Duration::from_millis(100)).print();
|
timing.filter(Duration::from_millis(100)).print();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -90,7 +94,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
verify_proof(&all_stark, proof, &config)?;
|
verify_proof(&all_stark, proof, &config)?;
|
||||||
|
|
||||||
let all_circuits = AllRecursiveCircuits::<F, C, D>::new(&all_stark, 9..19, &config);
|
let all_circuits = AllRecursiveCircuits::<F, HCO, HCI, C, D>::new(&all_stark, 9..19, &config);
|
||||||
let root_proof = all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?;
|
let root_proof = all_circuits.prove_root(&all_stark, &config, inputs, &mut timing)?;
|
||||||
all_circuits.verify_root(root_proof.clone())?;
|
all_circuits.verify_root(root_proof.clone())?;
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -8,7 +10,7 @@ use ethereum_types::{Address, U256};
|
|||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use keccak_hash::keccak;
|
use keccak_hash::keccak;
|
||||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||||
use plonky2::plonk::config::PoseidonGoldilocksConfig;
|
use plonky2::plonk::config::{PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use plonky2::util::timing::TimingTree;
|
use plonky2::util::timing::TimingTree;
|
||||||
use plonky2_evm::all_stark::AllStark;
|
use plonky2_evm::all_stark::AllStark;
|
||||||
use plonky2_evm::config::StarkConfig;
|
use plonky2_evm::config::StarkConfig;
|
||||||
@ -22,6 +24,8 @@ use plonky2_evm::Node;
|
|||||||
type F = GoldilocksField;
|
type F = GoldilocksField;
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
|
||||||
/// Test a simple token transfer to a new address.
|
/// Test a simple token transfer to a new address.
|
||||||
#[test]
|
#[test]
|
||||||
@ -85,7 +89,7 @@ fn test_simple_transfer() -> anyhow::Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut timing = TimingTree::new("prove", log::Level::Debug);
|
let mut timing = TimingTree::new("prove", log::Level::Debug);
|
||||||
let proof = prove::<F, C, D>(&all_stark, &config, inputs, &mut timing)?;
|
let proof = prove::<F, HCO, HCI, C, D>(&all_stark, &config, inputs, &mut timing)?;
|
||||||
timing.filter(Duration::from_millis(100)).print();
|
timing.filter(Duration::from_millis(100)).print();
|
||||||
|
|
||||||
let expected_state_trie_after: HashedPartialTrie = {
|
let expected_state_trie_after: HashedPartialTrie = {
|
||||||
|
|||||||
@ -4,17 +4,18 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
|||||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||||
use plonky2::field::types::Sample;
|
use plonky2::field::types::Sample;
|
||||||
use plonky2::hash::hash_types::{BytesHash, RichField};
|
use plonky2::hash::hash_types::{BytesHash, RichField};
|
||||||
use plonky2::hash::hashing::SPONGE_WIDTH;
|
|
||||||
use plonky2::hash::keccak::KeccakHash;
|
use plonky2::hash::keccak::KeccakHash;
|
||||||
use plonky2::hash::poseidon::Poseidon;
|
use plonky2::hash::poseidon::{Poseidon, SPONGE_WIDTH};
|
||||||
use plonky2::plonk::config::Hasher;
|
use plonky2::plonk::config::{Hasher, KeccakHashConfig};
|
||||||
use tynm::type_name;
|
use tynm::type_name;
|
||||||
|
|
||||||
pub(crate) fn bench_keccak<F: RichField>(c: &mut Criterion) {
|
pub(crate) fn bench_keccak<F: RichField>(c: &mut Criterion) {
|
||||||
c.bench_function("keccak256", |b| {
|
c.bench_function("keccak256", |b| {
|
||||||
b.iter_batched(
|
b.iter_batched(
|
||||||
|| (BytesHash::<32>::rand(), BytesHash::<32>::rand()),
|
|| (BytesHash::<32>::rand(), BytesHash::<32>::rand()),
|
||||||
|(left, right)| <KeccakHash<32> as Hasher<F>>::two_to_one(left, right),
|
|(left, right)| {
|
||||||
|
<KeccakHash<32> as Hasher<F, KeccakHashConfig>>::two_to_one(left, right)
|
||||||
|
},
|
||||||
BatchSize::SmallInput,
|
BatchSize::SmallInput,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,17 +1,23 @@
|
|||||||
|
#![feature(generic_const_exprs)]
|
||||||
|
|
||||||
mod allocator;
|
mod allocator;
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::hash::keccak::KeccakHash;
|
use plonky2::hash::keccak::KeccakHash;
|
||||||
use plonky2::hash::merkle_tree::MerkleTree;
|
use plonky2::hash::merkle_tree::MerkleTree;
|
||||||
use plonky2::hash::poseidon::PoseidonHash;
|
use plonky2::hash::poseidon::PoseidonHash;
|
||||||
use plonky2::plonk::config::Hasher;
|
use plonky2::plonk::config::{Hasher, KeccakHashConfig, PoseidonHashConfig};
|
||||||
use tynm::type_name;
|
use tynm::type_name;
|
||||||
|
|
||||||
const ELEMS_PER_LEAF: usize = 135;
|
const ELEMS_PER_LEAF: usize = 135;
|
||||||
|
|
||||||
pub(crate) fn bench_merkle_tree<F: RichField, H: Hasher<F>>(c: &mut Criterion) {
|
pub(crate) fn bench_merkle_tree<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(c: &mut Criterion)
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let mut group = c.benchmark_group(&format!(
|
let mut group = c.benchmark_group(&format!(
|
||||||
"merkle-tree<{}, {}>",
|
"merkle-tree<{}, {}>",
|
||||||
type_name::<F>(),
|
type_name::<F>(),
|
||||||
@ -23,14 +29,14 @@ pub(crate) fn bench_merkle_tree<F: RichField, H: Hasher<F>>(c: &mut Criterion) {
|
|||||||
let size = 1 << size_log;
|
let size = 1 << size_log;
|
||||||
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| {
|
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, _| {
|
||||||
let leaves = vec![F::rand_vec(ELEMS_PER_LEAF); size];
|
let leaves = vec![F::rand_vec(ELEMS_PER_LEAF); size];
|
||||||
b.iter(|| MerkleTree::<F, H>::new(leaves.clone(), 0));
|
b.iter(|| MerkleTree::<F, HC, H>::new(leaves.clone(), 0));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn criterion_benchmark(c: &mut Criterion) {
|
fn criterion_benchmark(c: &mut Criterion) {
|
||||||
bench_merkle_tree::<GoldilocksField, PoseidonHash>(c);
|
bench_merkle_tree::<GoldilocksField, PoseidonHashConfig, PoseidonHash>(c);
|
||||||
bench_merkle_tree::<GoldilocksField, KeccakHash<25>>(c);
|
bench_merkle_tree::<GoldilocksField, KeccakHashConfig, KeccakHash<25>>(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!(benches, criterion_benchmark);
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
|||||||
@ -3,6 +3,9 @@
|
|||||||
// put it in `src/bin/`, but then we wouldn't have access to
|
// put it in `src/bin/`, but then we wouldn't have access to
|
||||||
// `[dev-dependencies]`.
|
// `[dev-dependencies]`.
|
||||||
|
|
||||||
|
#![feature(generic_const_exprs)]
|
||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use core::num::ParseIntError;
|
use core::num::ParseIntError;
|
||||||
use core::ops::RangeInclusive;
|
use core::ops::RangeInclusive;
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
@ -11,10 +14,13 @@ use anyhow::{anyhow, Context as _, Result};
|
|||||||
use log::{info, Level, LevelFilter};
|
use log::{info, Level, LevelFilter};
|
||||||
use plonky2::gates::noop::NoopGate;
|
use plonky2::gates::noop::NoopGate;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::{CircuitConfig, CommonCircuitData, VerifierOnlyCircuitData};
|
use plonky2::plonk::circuit_data::{CircuitConfig, CommonCircuitData, VerifierOnlyCircuitData};
|
||||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{
|
||||||
|
AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig,
|
||||||
|
};
|
||||||
use plonky2::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs};
|
use plonky2::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs};
|
||||||
use plonky2::plonk::prover::prove;
|
use plonky2::plonk::prover::prove;
|
||||||
use plonky2::util::timing::TimingTree;
|
use plonky2::util::timing::TimingTree;
|
||||||
@ -25,9 +31,9 @@ use rand::{RngCore, SeedableRng};
|
|||||||
use rand_chacha::ChaCha8Rng;
|
use rand_chacha::ChaCha8Rng;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
type ProofTuple<F, C, const D: usize> = (
|
type ProofTuple<F, HCO, HCI, C, const D: usize> = (
|
||||||
ProofWithPublicInputs<F, C, D>,
|
ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
VerifierOnlyCircuitData<C, D>,
|
VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
CommonCircuitData<F, D>,
|
CommonCircuitData<F, D>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -59,10 +65,20 @@ struct Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a dummy proof which should have `2 ** log2_size` rows.
|
/// Creates a dummy proof which should have `2 ** log2_size` rows.
|
||||||
fn dummy_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
fn dummy_proof<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
config: &CircuitConfig,
|
config: &CircuitConfig,
|
||||||
log2_size: usize,
|
log2_size: usize,
|
||||||
) -> Result<ProofTuple<F, C, D>> {
|
) -> Result<ProofTuple<F, HCO, HCI, C, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
// 'size' is in degree, but we want number of noop gates. A non-zero amount of padding will be added and size will be rounded to the next power of two. To hit our target size, we go just under the previous power of two and hope padding is less than half the proof.
|
// 'size' is in degree, but we want number of noop gates. A non-zero amount of padding will be added and size will be rounded to the next power of two. To hit our target size, we go just under the previous power of two and hope padding is less than half the proof.
|
||||||
let num_dummy_gates = match log2_size {
|
let num_dummy_gates = match log2_size {
|
||||||
0 => return Err(anyhow!("size must be at least 1")),
|
0 => return Err(anyhow!("size must be at least 1")),
|
||||||
@ -77,11 +93,11 @@ fn dummy_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D
|
|||||||
}
|
}
|
||||||
builder.print_gate_counts(0);
|
builder.print_gate_counts(0);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let inputs = PartialWitness::new();
|
let inputs = PartialWitness::new();
|
||||||
|
|
||||||
let mut timing = TimingTree::new("prove", Level::Debug);
|
let mut timing = TimingTree::new("prove", Level::Debug);
|
||||||
let proof = prove(&data.prover_only, &data.common, inputs, &mut timing)?;
|
let proof = prove::<F, HCO, HCI, C, D>(&data.prover_only, &data.common, inputs, &mut timing)?;
|
||||||
timing.print();
|
timing.print();
|
||||||
data.verify(proof.clone())?;
|
data.verify(proof.clone())?;
|
||||||
|
|
||||||
@ -90,16 +106,24 @@ fn dummy_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D
|
|||||||
|
|
||||||
fn recursive_proof<
|
fn recursive_proof<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCOO: HashConfig,
|
||||||
InnerC: GenericConfig<D, F = F>,
|
HCOI: HashConfig,
|
||||||
|
HCIO: HashConfig,
|
||||||
|
HCII: HashConfig,
|
||||||
|
C: GenericConfig<HCOO, HCOI, D, F = F>,
|
||||||
|
InnerC: GenericConfig<HCIO, HCII, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
inner: &ProofTuple<F, InnerC, D>,
|
inner: &ProofTuple<F, HCIO, HCII, InnerC, D>,
|
||||||
config: &CircuitConfig,
|
config: &CircuitConfig,
|
||||||
min_degree_bits: Option<usize>,
|
min_degree_bits: Option<usize>,
|
||||||
) -> Result<ProofTuple<F, C, D>>
|
) -> Result<ProofTuple<F, HCOO, HCOI, C, D>>
|
||||||
where
|
where
|
||||||
InnerC::Hasher: AlgebraicHasher<F>,
|
InnerC::Hasher: AlgebraicHasher<F, HCIO>,
|
||||||
|
[(); HCOO::WIDTH]:,
|
||||||
|
[(); HCOI::WIDTH]:,
|
||||||
|
[(); HCIO::WIDTH]:,
|
||||||
|
[(); HCII::WIDTH]:,
|
||||||
{
|
{
|
||||||
let (inner_proof, inner_vd, inner_cd) = inner;
|
let (inner_proof, inner_vd, inner_cd) = inner;
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
|
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
|
||||||
@ -107,7 +131,7 @@ where
|
|||||||
|
|
||||||
let inner_data = builder.add_virtual_verifier_data(inner_cd.config.fri_config.cap_height);
|
let inner_data = builder.add_virtual_verifier_data(inner_cd.config.fri_config.cap_height);
|
||||||
|
|
||||||
builder.verify_proof::<InnerC>(&pt, &inner_data, inner_cd);
|
builder.verify_proof::<HCIO, HCII, InnerC>(&pt, &inner_data, inner_cd);
|
||||||
builder.print_gate_counts(0);
|
builder.print_gate_counts(0);
|
||||||
|
|
||||||
if let Some(min_degree_bits) = min_degree_bits {
|
if let Some(min_degree_bits) = min_degree_bits {
|
||||||
@ -121,14 +145,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCOO, HCOI, C>();
|
||||||
|
|
||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::new();
|
||||||
pw.set_proof_with_pis_target(&pt, inner_proof);
|
pw.set_proof_with_pis_target(&pt, inner_proof);
|
||||||
pw.set_verifier_data_target(&inner_data, inner_vd);
|
pw.set_verifier_data_target(&inner_data, inner_vd);
|
||||||
|
|
||||||
let mut timing = TimingTree::new("prove", Level::Debug);
|
let mut timing = TimingTree::new("prove", Level::Debug);
|
||||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
let proof = prove::<F, HCOO, HCOI, C, D>(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||||
timing.print();
|
timing.print();
|
||||||
|
|
||||||
data.verify(proof.clone())?;
|
data.verify(proof.clone())?;
|
||||||
@ -137,11 +161,21 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Test serialization and print some size info.
|
/// Test serialization and print some size info.
|
||||||
fn test_serialization<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
fn test_serialization<
|
||||||
proof: &ProofWithPublicInputs<F, C, D>,
|
F: RichField + Extendable<D>,
|
||||||
vd: &VerifierOnlyCircuitData<C, D>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
|
proof: &ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
|
vd: &VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
cd: &CommonCircuitData<F, D>,
|
cd: &CommonCircuitData<F, D>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let proof_bytes = proof.to_bytes();
|
let proof_bytes = proof.to_bytes();
|
||||||
info!("Proof length: {} bytes", proof_bytes.len());
|
info!("Proof length: {} bytes", proof_bytes.len());
|
||||||
let proof_from_bytes = ProofWithPublicInputs::from_bytes(proof_bytes, cd)?;
|
let proof_from_bytes = ProofWithPublicInputs::from_bytes(proof_bytes, cd)?;
|
||||||
@ -170,10 +204,12 @@ fn test_serialization<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>,
|
|||||||
fn benchmark(config: &CircuitConfig, log2_inner_size: usize) -> Result<()> {
|
fn benchmark(config: &CircuitConfig, log2_inner_size: usize) -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
|
||||||
// Start with a dummy proof of specified size
|
// Start with a dummy proof of specified size
|
||||||
let inner = dummy_proof::<F, C, D>(config, log2_inner_size)?;
|
let inner = dummy_proof::<F, HCO, HCI, C, D>(config, log2_inner_size)?;
|
||||||
let (_, _, cd) = &inner;
|
let (_, _, cd) = &inner;
|
||||||
info!(
|
info!(
|
||||||
"Initial proof degree {} = 2^{}",
|
"Initial proof degree {} = 2^{}",
|
||||||
@ -182,7 +218,7 @@ fn benchmark(config: &CircuitConfig, log2_inner_size: usize) -> Result<()> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Recursively verify the proof
|
// Recursively verify the proof
|
||||||
let middle = recursive_proof::<F, C, C, D>(&inner, config, None)?;
|
let middle = recursive_proof::<F, HCO, HCI, HCO, HCI, C, C, D>(&inner, config, None)?;
|
||||||
let (_, _, cd) = &middle;
|
let (_, _, cd) = &middle;
|
||||||
info!(
|
info!(
|
||||||
"Single recursion proof degree {} = 2^{}",
|
"Single recursion proof degree {} = 2^{}",
|
||||||
@ -191,7 +227,7 @@ fn benchmark(config: &CircuitConfig, log2_inner_size: usize) -> Result<()> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Add a second layer of recursion to shrink the proof size further
|
// Add a second layer of recursion to shrink the proof size further
|
||||||
let outer = recursive_proof::<F, C, C, D>(&middle, config, None)?;
|
let outer = recursive_proof::<F, HCO, HCI, HCO, HCI, C, C, D>(&middle, config, None)?;
|
||||||
let (proof, vd, cd) = &outer;
|
let (proof, vd, cd) = &outer;
|
||||||
info!(
|
info!(
|
||||||
"Double recursion proof degree {} = 2^{}",
|
"Double recursion proof degree {} = 2^{}",
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
/// An example of using Plonky2 to prove a statement of the form
|
/// An example of using Plonky2 to prove a statement of the form
|
||||||
/// "I know n * (n + 1) * ... * (n + 99)".
|
/// "I know n * (n + 1) * ... * (n + 99)".
|
||||||
@ -11,7 +13,9 @@ use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
@ -31,7 +35,7 @@ fn main() -> Result<()> {
|
|||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::new();
|
||||||
pw.set_target(initial, F::ONE);
|
pw.set_target(initial, F::ONE);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
/// An example of using Plonky2 to prove a statement of the form
|
/// An example of using Plonky2 to prove a statement of the form
|
||||||
/// "I know the 100th element of the Fibonacci sequence, starting with constants a and b."
|
/// "I know the 100th element of the Fibonacci sequence, starting with constants a and b."
|
||||||
@ -11,7 +13,9 @@ use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
@ -37,7 +41,7 @@ fn main() -> Result<()> {
|
|||||||
pw.set_target(initial_a, F::ZERO);
|
pw.set_target(initial_a, F::ZERO);
|
||||||
pw.set_target(initial_b, F::ONE);
|
pw.set_target(initial_b, F::ONE);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
@ -8,7 +10,7 @@ use plonky2::iop::target::Target;
|
|||||||
use plonky2::iop::witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite};
|
use plonky2::iop::witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use plonky2_field::extension::Extendable;
|
use plonky2_field::extension::Extendable;
|
||||||
|
|
||||||
/// A generator used by the prover to calculate the square root (`x`) of a given value
|
/// A generator used by the prover to calculate the square root (`x`) of a given value
|
||||||
@ -42,7 +44,9 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
|
|
||||||
@ -71,7 +75,7 @@ fn main() -> Result<()> {
|
|||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::new();
|
||||||
pw.set_target(x_squared, x_squared_value);
|
pw.set_target(x_squared, x_squared_value);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw.clone())?;
|
let proof = data.prove(pw.clone())?;
|
||||||
|
|
||||||
let x_squared_actual = proof.public_inputs[0];
|
let x_squared_actual = proof.public_inputs[0];
|
||||||
|
|||||||
@ -5,25 +5,30 @@ use crate::fri::structure::{FriOpenings, FriOpeningsTarget};
|
|||||||
use crate::fri::FriConfig;
|
use crate::fri::FriConfig;
|
||||||
use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
|
use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
|
||||||
use crate::hash::hash_types::{MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{MerkleCapTarget, RichField};
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::iop::challenger::{Challenger, RecursiveChallenger};
|
use crate::iop::challenger::{Challenger, RecursiveChallenger};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::config::{AlgebraicHasher, GenericConfig, Hasher};
|
use crate::plonk::config::{AlgebraicHasher, GenericConfig, Hasher};
|
||||||
|
|
||||||
impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
impl<F: RichField, HCO: HashConfig, H: Hasher<F, HCO>> Challenger<F, HCO, H>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
pub fn observe_openings<const D: usize>(&mut self, openings: &FriOpenings<F, D>)
|
pub fn observe_openings<const D: usize>(&mut self, openings: &FriOpenings<F, D>)
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
for v in &openings.batches {
|
for v in &openings.batches {
|
||||||
self.observe_extension_elements(&v.values);
|
self.observe_extension_elements(&v.values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fri_challenges<C: GenericConfig<D, F = F>, const D: usize>(
|
pub fn fri_challenges<HCI: HashConfig, C: GenericConfig<HCO, HCI, D, F = F>, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
commit_phase_merkle_caps: &[MerkleCap<F, C::Hasher>],
|
commit_phase_merkle_caps: &[MerkleCap<F, HCO, C::Hasher>],
|
||||||
final_poly: &PolynomialCoeffs<F::Extension>,
|
final_poly: &PolynomialCoeffs<F::Extension>,
|
||||||
pow_witness: F,
|
pow_witness: F,
|
||||||
degree_bits: usize,
|
degree_bits: usize,
|
||||||
@ -31,6 +36,8 @@ impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
|||||||
) -> FriChallenges<F, D>
|
) -> FriChallenges<F, D>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let num_fri_queries = config.num_query_rounds;
|
let num_fri_queries = config.num_query_rounds;
|
||||||
let lde_size = 1 << (degree_bits + config.rate_bits);
|
let lde_size = 1 << (degree_bits + config.rate_bits);
|
||||||
@ -41,7 +48,7 @@ impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
|||||||
let fri_betas = commit_phase_merkle_caps
|
let fri_betas = commit_phase_merkle_caps
|
||||||
.iter()
|
.iter()
|
||||||
.map(|cap| {
|
.map(|cap| {
|
||||||
self.observe_cap(cap);
|
self.observe_cap::<HCO, C::Hasher>(cap);
|
||||||
self.get_extension_challenge::<D>()
|
self.get_extension_challenge::<D>()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -64,10 +71,15 @@ impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, H: AlgebraicHasher<F>, const D: usize>
|
impl<F: RichField + Extendable<D>, HCO: HashConfig, H: AlgebraicHasher<F, HCO>, const D: usize>
|
||||||
RecursiveChallenger<F, H, D>
|
RecursiveChallenger<F, HCO, H, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
pub fn observe_openings(&mut self, openings: &FriOpeningsTarget<D>) {
|
pub fn observe_openings(&mut self, openings: &FriOpeningsTarget<D>)
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
for v in &openings.batches {
|
for v in &openings.batches {
|
||||||
self.observe_extension_elements(&v.values);
|
self.observe_extension_elements(&v.values);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ use crate::fri::prover::fri_proof;
|
|||||||
use crate::fri::structure::{FriBatchInfo, FriInstanceInfo};
|
use crate::fri::structure::{FriBatchInfo, FriInstanceInfo};
|
||||||
use crate::fri::FriParams;
|
use crate::fri::FriParams;
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_tree::MerkleTree;
|
use crate::hash::merkle_tree::MerkleTree;
|
||||||
use crate::iop::challenger::Challenger;
|
use crate::iop::challenger::Challenger;
|
||||||
use crate::plonk::config::GenericConfig;
|
use crate::plonk::config::GenericConfig;
|
||||||
@ -26,17 +27,27 @@ use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place, transp
|
|||||||
pub const SALT_SIZE: usize = 4;
|
pub const SALT_SIZE: usize = 4;
|
||||||
|
|
||||||
/// Represents a FRI oracle, i.e. a batch of polynomials which have been Merklized.
|
/// Represents a FRI oracle, i.e. a batch of polynomials which have been Merklized.
|
||||||
pub struct PolynomialBatch<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
pub struct PolynomialBatch<
|
||||||
{
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> {
|
||||||
pub polynomials: Vec<PolynomialCoeffs<F>>,
|
pub polynomials: Vec<PolynomialCoeffs<F>>,
|
||||||
pub merkle_tree: MerkleTree<F, C::Hasher>,
|
pub merkle_tree: MerkleTree<F, HCO, C::Hasher>,
|
||||||
pub degree_log: usize,
|
pub degree_log: usize,
|
||||||
pub rate_bits: usize,
|
pub rate_bits: usize,
|
||||||
pub blinding: bool,
|
pub blinding: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
impl<
|
||||||
PolynomialBatch<F, C, D>
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> PolynomialBatch<F, HCO, HCI, C, D>
|
||||||
{
|
{
|
||||||
/// Creates a list polynomial commitment for the polynomials interpolating the values in `values`.
|
/// Creates a list polynomial commitment for the polynomials interpolating the values in `values`.
|
||||||
pub fn from_values(
|
pub fn from_values(
|
||||||
@ -46,7 +57,10 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
cap_height: usize,
|
cap_height: usize,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
fft_root_table: Option<&FftRootTable<F>>,
|
fft_root_table: Option<&FftRootTable<F>>,
|
||||||
) -> Self {
|
) -> Self
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
let coeffs = timed!(
|
let coeffs = timed!(
|
||||||
timing,
|
timing,
|
||||||
"IFFT",
|
"IFFT",
|
||||||
@ -71,7 +85,10 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
cap_height: usize,
|
cap_height: usize,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
fft_root_table: Option<&FftRootTable<F>>,
|
fft_root_table: Option<&FftRootTable<F>>,
|
||||||
) -> Self {
|
) -> Self
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
let degree = polynomials[0].len();
|
let degree = polynomials[0].len();
|
||||||
let lde_values = timed!(
|
let lde_values = timed!(
|
||||||
timing,
|
timing,
|
||||||
@ -161,10 +178,14 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
pub fn prove_openings(
|
pub fn prove_openings(
|
||||||
instance: &FriInstanceInfo<F, D>,
|
instance: &FriInstanceInfo<F, D>,
|
||||||
oracles: &[&Self],
|
oracles: &[&Self],
|
||||||
challenger: &mut Challenger<F, C::Hasher>,
|
challenger: &mut Challenger<F, HCO, C::Hasher>,
|
||||||
fri_params: &FriParams,
|
fri_params: &FriParams,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> FriProof<F, C::Hasher, D> {
|
) -> FriProof<F, HCO, C::Hasher, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
assert!(D > 1, "Not implemented for D=1.");
|
assert!(D > 1, "Not implemented for D=1.");
|
||||||
let alpha = challenger.get_extension_challenge::<D>();
|
let alpha = challenger.get_extension_challenge::<D>();
|
||||||
let mut alpha = ReducingFactor::new(alpha);
|
let mut alpha = ReducingFactor::new(alpha);
|
||||||
@ -202,7 +223,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
lde_final_poly.coset_fft(F::coset_shift().into())
|
lde_final_poly.coset_fft(F::coset_shift().into())
|
||||||
);
|
);
|
||||||
|
|
||||||
let fri_proof = fri_proof::<F, C, D>(
|
let fri_proof = fri_proof::<F, HCO, HCI, C, D>(
|
||||||
&oracles
|
&oracles
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|c| &c.merkle_tree)
|
.map(|c| &c.merkle_tree)
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use crate::field::polynomial::PolynomialCoeffs;
|
|||||||
use crate::fri::FriParams;
|
use crate::fri::FriParams;
|
||||||
use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
|
use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
|
||||||
use crate::hash::hash_types::{MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{MerkleCapTarget, RichField};
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_proofs::{MerkleProof, MerkleProofTarget};
|
use crate::hash::merkle_proofs::{MerkleProof, MerkleProofTarget};
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::hash::path_compression::{compress_merkle_proofs, decompress_merkle_proofs};
|
use crate::hash::path_compression::{compress_merkle_proofs, decompress_merkle_proofs};
|
||||||
@ -22,9 +23,14 @@ use crate::plonk::proof::{FriInferredElements, ProofChallenges};
|
|||||||
/// Evaluations and Merkle proof produced by the prover in a FRI query step.
|
/// Evaluations and Merkle proof produced by the prover in a FRI query step.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||||
#[serde(bound = "")]
|
#[serde(bound = "")]
|
||||||
pub struct FriQueryStep<F: RichField + Extendable<D>, H: Hasher<F>, const D: usize> {
|
pub struct FriQueryStep<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HC: HashConfig,
|
||||||
|
H: Hasher<F, HC>,
|
||||||
|
const D: usize,
|
||||||
|
> {
|
||||||
pub evals: Vec<F::Extension>,
|
pub evals: Vec<F::Extension>,
|
||||||
pub merkle_proof: MerkleProof<F, H>,
|
pub merkle_proof: MerkleProof<F, HC, H>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -37,11 +43,11 @@ pub struct FriQueryStepTarget<const D: usize> {
|
|||||||
/// before they are combined into a composition polynomial.
|
/// before they are combined into a composition polynomial.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||||
#[serde(bound = "")]
|
#[serde(bound = "")]
|
||||||
pub struct FriInitialTreeProof<F: RichField, H: Hasher<F>> {
|
pub struct FriInitialTreeProof<F: RichField, HC: HashConfig, H: Hasher<F, HC>> {
|
||||||
pub evals_proofs: Vec<(Vec<F>, MerkleProof<F, H>)>,
|
pub evals_proofs: Vec<(Vec<F>, MerkleProof<F, HC, H>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField, H: Hasher<F>> FriInitialTreeProof<F, H> {
|
impl<F: RichField, HC: HashConfig, H: Hasher<F, HC>> FriInitialTreeProof<F, HC, H> {
|
||||||
pub(crate) fn unsalted_eval(&self, oracle_index: usize, poly_index: usize, salted: bool) -> F {
|
pub(crate) fn unsalted_eval(&self, oracle_index: usize, poly_index: usize, salted: bool) -> F {
|
||||||
self.unsalted_evals(oracle_index, salted)[poly_index]
|
self.unsalted_evals(oracle_index, salted)[poly_index]
|
||||||
}
|
}
|
||||||
@ -76,9 +82,14 @@ impl FriInitialTreeProofTarget {
|
|||||||
/// Proof for a FRI query round.
|
/// Proof for a FRI query round.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||||
#[serde(bound = "")]
|
#[serde(bound = "")]
|
||||||
pub struct FriQueryRound<F: RichField + Extendable<D>, H: Hasher<F>, const D: usize> {
|
pub struct FriQueryRound<
|
||||||
pub initial_trees_proof: FriInitialTreeProof<F, H>,
|
F: RichField + Extendable<D>,
|
||||||
pub steps: Vec<FriQueryStep<F, H, D>>,
|
HC: HashConfig,
|
||||||
|
H: Hasher<F, HC>,
|
||||||
|
const D: usize,
|
||||||
|
> {
|
||||||
|
pub initial_trees_proof: FriInitialTreeProof<F, HC, H>,
|
||||||
|
pub steps: Vec<FriQueryStep<F, HC, H, D>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -90,22 +101,28 @@ pub struct FriQueryRoundTarget<const D: usize> {
|
|||||||
/// Compressed proof of the FRI query rounds.
|
/// Compressed proof of the FRI query rounds.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||||
#[serde(bound = "")]
|
#[serde(bound = "")]
|
||||||
pub struct CompressedFriQueryRounds<F: RichField + Extendable<D>, H: Hasher<F>, const D: usize> {
|
pub struct CompressedFriQueryRounds<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HC: HashConfig,
|
||||||
|
H: Hasher<F, HC>,
|
||||||
|
const D: usize,
|
||||||
|
> {
|
||||||
/// Query indices.
|
/// Query indices.
|
||||||
pub indices: Vec<usize>,
|
pub indices: Vec<usize>,
|
||||||
/// Map from initial indices `i` to the `FriInitialProof` for the `i`th leaf.
|
/// Map from initial indices `i` to the `FriInitialProof` for the `i`th leaf.
|
||||||
pub initial_trees_proofs: HashMap<usize, FriInitialTreeProof<F, H>>,
|
pub initial_trees_proofs: HashMap<usize, FriInitialTreeProof<F, HC, H>>,
|
||||||
/// For each FRI query step, a map from indices `i` to the `FriQueryStep` for the `i`th leaf.
|
/// For each FRI query step, a map from indices `i` to the `FriQueryStep` for the `i`th leaf.
|
||||||
pub steps: Vec<HashMap<usize, FriQueryStep<F, H, D>>>,
|
pub steps: Vec<HashMap<usize, FriQueryStep<F, HC, H, D>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||||
#[serde(bound = "")]
|
#[serde(bound = "")]
|
||||||
pub struct FriProof<F: RichField + Extendable<D>, H: Hasher<F>, const D: usize> {
|
pub struct FriProof<F: RichField + Extendable<D>, HC: HashConfig, H: Hasher<F, HC>, const D: usize>
|
||||||
|
{
|
||||||
/// A Merkle cap for each reduced polynomial in the commit phase.
|
/// A Merkle cap for each reduced polynomial in the commit phase.
|
||||||
pub commit_phase_merkle_caps: Vec<MerkleCap<F, H>>,
|
pub commit_phase_merkle_caps: Vec<MerkleCap<F, HC, H>>,
|
||||||
/// Query rounds proofs
|
/// Query rounds proofs
|
||||||
pub query_round_proofs: Vec<FriQueryRound<F, H, D>>,
|
pub query_round_proofs: Vec<FriQueryRound<F, HC, H, D>>,
|
||||||
/// The final polynomial in coefficient form.
|
/// The final polynomial in coefficient form.
|
||||||
pub final_poly: PolynomialCoeffs<F::Extension>,
|
pub final_poly: PolynomialCoeffs<F::Extension>,
|
||||||
/// Witness showing that the prover did PoW.
|
/// Witness showing that the prover did PoW.
|
||||||
@ -122,20 +139,31 @@ pub struct FriProofTarget<const D: usize> {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||||
#[serde(bound = "")]
|
#[serde(bound = "")]
|
||||||
pub struct CompressedFriProof<F: RichField + Extendable<D>, H: Hasher<F>, const D: usize> {
|
pub struct CompressedFriProof<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HC: HashConfig,
|
||||||
|
H: Hasher<F, HC>,
|
||||||
|
const D: usize,
|
||||||
|
> {
|
||||||
/// A Merkle cap for each reduced polynomial in the commit phase.
|
/// A Merkle cap for each reduced polynomial in the commit phase.
|
||||||
pub commit_phase_merkle_caps: Vec<MerkleCap<F, H>>,
|
pub commit_phase_merkle_caps: Vec<MerkleCap<F, HC, H>>,
|
||||||
/// Compressed query rounds proof.
|
/// Compressed query rounds proof.
|
||||||
pub query_round_proofs: CompressedFriQueryRounds<F, H, D>,
|
pub query_round_proofs: CompressedFriQueryRounds<F, HC, H, D>,
|
||||||
/// The final polynomial in coefficient form.
|
/// The final polynomial in coefficient form.
|
||||||
pub final_poly: PolynomialCoeffs<F::Extension>,
|
pub final_poly: PolynomialCoeffs<F::Extension>,
|
||||||
/// Witness showing that the prover did PoW.
|
/// Witness showing that the prover did PoW.
|
||||||
pub pow_witness: F,
|
pub pow_witness: F,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, H: Hasher<F>, const D: usize> FriProof<F, H, D> {
|
impl<F: RichField + Extendable<D>, HCO: HashConfig, H: Hasher<F, HCO>, const D: usize>
|
||||||
|
FriProof<F, HCO, H, D>
|
||||||
|
{
|
||||||
/// Compress all the Merkle paths in the FRI proof and remove duplicate indices.
|
/// Compress all the Merkle paths in the FRI proof and remove duplicate indices.
|
||||||
pub fn compress(self, indices: &[usize], params: &FriParams) -> CompressedFriProof<F, H, D> {
|
pub fn compress(
|
||||||
|
self,
|
||||||
|
indices: &[usize],
|
||||||
|
params: &FriParams,
|
||||||
|
) -> CompressedFriProof<F, HCO, H, D> {
|
||||||
let FriProof {
|
let FriProof {
|
||||||
commit_phase_merkle_caps,
|
commit_phase_merkle_caps,
|
||||||
query_round_proofs,
|
query_round_proofs,
|
||||||
@ -235,14 +263,19 @@ impl<F: RichField + Extendable<D>, H: Hasher<F>, const D: usize> FriProof<F, H,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, H: Hasher<F>, const D: usize> CompressedFriProof<F, H, D> {
|
impl<F: RichField + Extendable<D>, HCO: HashConfig, H: Hasher<F, HCO>, const D: usize>
|
||||||
|
CompressedFriProof<F, HCO, H, D>
|
||||||
|
{
|
||||||
/// Decompress all the Merkle paths in the FRI proof and reinsert duplicate indices.
|
/// Decompress all the Merkle paths in the FRI proof and reinsert duplicate indices.
|
||||||
pub(crate) fn decompress(
|
pub(crate) fn decompress(
|
||||||
self,
|
self,
|
||||||
challenges: &ProofChallenges<F, D>,
|
challenges: &ProofChallenges<F, D>,
|
||||||
fri_inferred_elements: FriInferredElements<F, D>,
|
fri_inferred_elements: FriInferredElements<F, D>,
|
||||||
params: &FriParams,
|
params: &FriParams,
|
||||||
) -> FriProof<F, H, D> {
|
) -> FriProof<F, HCO, H, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
let CompressedFriProof {
|
let CompressedFriProof {
|
||||||
commit_phase_merkle_caps,
|
commit_phase_merkle_caps,
|
||||||
query_round_proofs,
|
query_round_proofs,
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use crate::field::polynomial::{PolynomialCoeffs, PolynomialValues};
|
|||||||
use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep};
|
use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep};
|
||||||
use crate::fri::{FriConfig, FriParams};
|
use crate::fri::{FriConfig, FriParams};
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::hash::hashing::{PlonkyPermutation, SPONGE_RATE};
|
use crate::hash::hashing::{HashConfig, PlonkyPermutation};
|
||||||
use crate::hash::merkle_tree::MerkleTree;
|
use crate::hash::merkle_tree::MerkleTree;
|
||||||
use crate::iop::challenger::Challenger;
|
use crate::iop::challenger::Challenger;
|
||||||
use crate::plonk::config::{GenericConfig, Hasher};
|
use crate::plonk::config::{GenericConfig, Hasher};
|
||||||
@ -17,16 +17,26 @@ use crate::util::reverse_index_bits_in_place;
|
|||||||
use crate::util::timing::TimingTree;
|
use crate::util::timing::TimingTree;
|
||||||
|
|
||||||
/// Builds a FRI proof.
|
/// Builds a FRI proof.
|
||||||
pub fn fri_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
pub fn fri_proof<
|
||||||
initial_merkle_trees: &[&MerkleTree<F, C::Hasher>],
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
|
initial_merkle_trees: &[&MerkleTree<F, HCO, C::Hasher>],
|
||||||
// Coefficients of the polynomial on which the LDT is performed. Only the first `1/rate` coefficients are non-zero.
|
// Coefficients of the polynomial on which the LDT is performed. Only the first `1/rate` coefficients are non-zero.
|
||||||
lde_polynomial_coeffs: PolynomialCoeffs<F::Extension>,
|
lde_polynomial_coeffs: PolynomialCoeffs<F::Extension>,
|
||||||
// Evaluation of the polynomial on the large domain.
|
// Evaluation of the polynomial on the large domain.
|
||||||
lde_polynomial_values: PolynomialValues<F::Extension>,
|
lde_polynomial_values: PolynomialValues<F::Extension>,
|
||||||
challenger: &mut Challenger<F, C::Hasher>,
|
challenger: &mut Challenger<F, HCO, C::Hasher>,
|
||||||
fri_params: &FriParams,
|
fri_params: &FriParams,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> FriProof<F, C::Hasher, D> {
|
) -> FriProof<F, HCO, C::Hasher, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let n = lde_polynomial_values.len();
|
let n = lde_polynomial_values.len();
|
||||||
assert_eq!(lde_polynomial_coeffs.len(), n);
|
assert_eq!(lde_polynomial_coeffs.len(), n);
|
||||||
|
|
||||||
@ -34,7 +44,7 @@ pub fn fri_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const
|
|||||||
let (trees, final_coeffs) = timed!(
|
let (trees, final_coeffs) = timed!(
|
||||||
timing,
|
timing,
|
||||||
"fold codewords in the commitment phase",
|
"fold codewords in the commitment phase",
|
||||||
fri_committed_trees::<F, C, D>(
|
fri_committed_trees::<F, HCO, HCI, C, D>(
|
||||||
lde_polynomial_coeffs,
|
lde_polynomial_coeffs,
|
||||||
lde_polynomial_values,
|
lde_polynomial_values,
|
||||||
challenger,
|
challenger,
|
||||||
@ -46,12 +56,17 @@ pub fn fri_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const
|
|||||||
let pow_witness = timed!(
|
let pow_witness = timed!(
|
||||||
timing,
|
timing,
|
||||||
"find proof-of-work witness",
|
"find proof-of-work witness",
|
||||||
fri_proof_of_work::<F, C, D>(challenger, &fri_params.config)
|
fri_proof_of_work::<F, HCO, HCI, C, D>(challenger, &fri_params.config)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Query phase
|
// Query phase
|
||||||
let query_round_proofs =
|
let query_round_proofs = fri_prover_query_rounds::<F, HCO, HCI, C, D>(
|
||||||
fri_prover_query_rounds::<F, C, D>(initial_merkle_trees, &trees, challenger, n, fri_params);
|
initial_merkle_trees,
|
||||||
|
&trees,
|
||||||
|
challenger,
|
||||||
|
n,
|
||||||
|
fri_params,
|
||||||
|
);
|
||||||
|
|
||||||
FriProof {
|
FriProof {
|
||||||
commit_phase_merkle_caps: trees.iter().map(|t| t.cap.clone()).collect(),
|
commit_phase_merkle_caps: trees.iter().map(|t| t.cap.clone()).collect(),
|
||||||
@ -61,17 +76,26 @@ pub fn fri_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type FriCommitedTrees<F, C, const D: usize> = (
|
type FriCommitedTrees<F, HCO, HCI, C, const D: usize> = (
|
||||||
Vec<MerkleTree<F, <C as GenericConfig<D>>::Hasher>>,
|
Vec<MerkleTree<F, HCO, <C as GenericConfig<HCO, HCI, D>>::Hasher>>,
|
||||||
PolynomialCoeffs<<F as Extendable<D>>::Extension>,
|
PolynomialCoeffs<<F as Extendable<D>>::Extension>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn fri_committed_trees<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
fn fri_committed_trees<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
mut coeffs: PolynomialCoeffs<F::Extension>,
|
mut coeffs: PolynomialCoeffs<F::Extension>,
|
||||||
mut values: PolynomialValues<F::Extension>,
|
mut values: PolynomialValues<F::Extension>,
|
||||||
challenger: &mut Challenger<F, C::Hasher>,
|
challenger: &mut Challenger<F, HCO, C::Hasher>,
|
||||||
fri_params: &FriParams,
|
fri_params: &FriParams,
|
||||||
) -> FriCommitedTrees<F, C, D> {
|
) -> FriCommitedTrees<F, HCO, HCI, C, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
let mut trees = Vec::new();
|
let mut trees = Vec::new();
|
||||||
|
|
||||||
let mut shift = F::MULTIPLICATIVE_GROUP_GENERATOR;
|
let mut shift = F::MULTIPLICATIVE_GROUP_GENERATOR;
|
||||||
@ -84,7 +108,8 @@ fn fri_committed_trees<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>,
|
|||||||
.par_chunks(arity)
|
.par_chunks(arity)
|
||||||
.map(|chunk: &[F::Extension]| flatten(chunk))
|
.map(|chunk: &[F::Extension]| flatten(chunk))
|
||||||
.collect();
|
.collect();
|
||||||
let tree = MerkleTree::<F, C::Hasher>::new(chunked_values, fri_params.config.cap_height);
|
let tree =
|
||||||
|
MerkleTree::<F, HCO, C::Hasher>::new(chunked_values, fri_params.config.cap_height);
|
||||||
|
|
||||||
challenger.observe_cap(&tree.cap);
|
challenger.observe_cap(&tree.cap);
|
||||||
trees.push(tree);
|
trees.push(tree);
|
||||||
@ -112,10 +137,20 @@ fn fri_committed_trees<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Performs the proof-of-work (a.k.a. grinding) step of the FRI protocol. Returns the PoW witness.
|
/// Performs the proof-of-work (a.k.a. grinding) step of the FRI protocol. Returns the PoW witness.
|
||||||
fn fri_proof_of_work<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
fn fri_proof_of_work<
|
||||||
challenger: &mut Challenger<F, C::Hasher>,
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
|
challenger: &mut Challenger<F, HCO, C::Hasher>,
|
||||||
config: &FriConfig,
|
config: &FriConfig,
|
||||||
) -> F {
|
) -> F
|
||||||
|
where
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
let min_leading_zeros = config.proof_of_work_bits + (64 - F::order().bits()) as u32;
|
let min_leading_zeros = config.proof_of_work_bits + (64 - F::order().bits()) as u32;
|
||||||
|
|
||||||
// The easiest implementation would be repeatedly clone our Challenger. With each clone, we'd
|
// The easiest implementation would be repeatedly clone our Challenger. With each clone, we'd
|
||||||
@ -126,7 +161,7 @@ fn fri_proof_of_work<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, c
|
|||||||
// since it stores vectors, which means allocations. We'd like a more compact state to clone.
|
// since it stores vectors, which means allocations. We'd like a more compact state to clone.
|
||||||
//
|
//
|
||||||
// We know that a duplex will be performed right after we send the PoW witness, so we can ignore
|
// We know that a duplex will be performed right after we send the PoW witness, so we can ignore
|
||||||
// any output_buffer, which will be invalidated. We also know input_buffer.len() < SPONGE_WIDTH,
|
// any output_buffer, which will be invalidated. We also know input_buffer.len() < HCO::WIDTH,
|
||||||
// an invariant of Challenger.
|
// an invariant of Challenger.
|
||||||
//
|
//
|
||||||
// We separate the duplex operation into two steps, one which can be performed now, and the
|
// We separate the duplex operation into two steps, one which can be performed now, and the
|
||||||
@ -146,8 +181,10 @@ fn fri_proof_of_work<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, c
|
|||||||
let mut duplex_state = duplex_intermediate_state;
|
let mut duplex_state = duplex_intermediate_state;
|
||||||
duplex_state[witness_input_pos] = F::from_canonical_u64(candidate);
|
duplex_state[witness_input_pos] = F::from_canonical_u64(candidate);
|
||||||
duplex_state =
|
duplex_state =
|
||||||
<<C as GenericConfig<D>>::Hasher as Hasher<F>>::Permutation::permute(duplex_state);
|
<<C as GenericConfig<HCO, HCI, D>>::Hasher as Hasher<F, HCO>>::Permutation::permute(
|
||||||
let pow_response = duplex_state[SPONGE_RATE - 1];
|
duplex_state,
|
||||||
|
);
|
||||||
|
let pow_response = duplex_state[HCO::RATE - 1];
|
||||||
let leading_zeros = pow_response.to_canonical_u64().leading_zeros();
|
let leading_zeros = pow_response.to_canonical_u64().leading_zeros();
|
||||||
leading_zeros >= min_leading_zeros
|
leading_zeros >= min_leading_zeros
|
||||||
})
|
})
|
||||||
@ -164,35 +201,50 @@ fn fri_proof_of_work<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, c
|
|||||||
|
|
||||||
fn fri_prover_query_rounds<
|
fn fri_prover_query_rounds<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
initial_merkle_trees: &[&MerkleTree<F, C::Hasher>],
|
initial_merkle_trees: &[&MerkleTree<F, HCO, C::Hasher>],
|
||||||
trees: &[MerkleTree<F, C::Hasher>],
|
trees: &[MerkleTree<F, HCO, C::Hasher>],
|
||||||
challenger: &mut Challenger<F, C::Hasher>,
|
challenger: &mut Challenger<F, HCO, C::Hasher>,
|
||||||
n: usize,
|
n: usize,
|
||||||
fri_params: &FriParams,
|
fri_params: &FriParams,
|
||||||
) -> Vec<FriQueryRound<F, C::Hasher, D>> {
|
) -> Vec<FriQueryRound<F, HCO, C::Hasher, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
challenger
|
challenger
|
||||||
.get_n_challenges(fri_params.config.num_query_rounds)
|
.get_n_challenges(fri_params.config.num_query_rounds)
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(|rand| {
|
.map(|rand| {
|
||||||
let x_index = rand.to_canonical_u64() as usize % n;
|
let x_index = rand.to_canonical_u64() as usize % n;
|
||||||
fri_prover_query_round::<F, C, D>(initial_merkle_trees, trees, x_index, fri_params)
|
fri_prover_query_round::<F, HCO, HCI, C, D>(
|
||||||
|
initial_merkle_trees,
|
||||||
|
trees,
|
||||||
|
x_index,
|
||||||
|
fri_params,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fri_prover_query_round<
|
fn fri_prover_query_round<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
initial_merkle_trees: &[&MerkleTree<F, C::Hasher>],
|
initial_merkle_trees: &[&MerkleTree<F, HCO, C::Hasher>],
|
||||||
trees: &[MerkleTree<F, C::Hasher>],
|
trees: &[MerkleTree<F, HCO, C::Hasher>],
|
||||||
mut x_index: usize,
|
mut x_index: usize,
|
||||||
fri_params: &FriParams,
|
fri_params: &FriParams,
|
||||||
) -> FriQueryRound<F, C::Hasher, D> {
|
) -> FriQueryRound<F, HCO, C::Hasher, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
let mut query_steps = Vec::new();
|
let mut query_steps = Vec::new();
|
||||||
let initial_proof = initial_merkle_trees
|
let initial_proof = initial_merkle_trees
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@ -14,6 +14,7 @@ use crate::gates::coset_interpolation::CosetInterpolationGate;
|
|||||||
use crate::gates::gate::Gate;
|
use crate::gates::gate::Gate;
|
||||||
use crate::gates::random_access::RandomAccessGate;
|
use crate::gates::random_access::RandomAccessGate;
|
||||||
use crate::hash::hash_types::{MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{MerkleCapTarget, RichField};
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::iop::ext_target::{flatten_target, ExtensionTarget};
|
use crate::iop::ext_target::{flatten_target, ExtensionTarget};
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
@ -98,7 +99,11 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_fri_proof<C: GenericConfig<D, F = F>>(
|
pub fn verify_fri_proof<
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
instance: &FriInstanceInfoTarget<D>,
|
instance: &FriInstanceInfoTarget<D>,
|
||||||
openings: &FriOpeningsTarget<D>,
|
openings: &FriOpeningsTarget<D>,
|
||||||
@ -107,7 +112,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
proof: &FriProofTarget<D>,
|
proof: &FriProofTarget<D>,
|
||||||
params: &FriParams,
|
params: &FriParams,
|
||||||
) where
|
) where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
if let Some(max_arity_bits) = params.max_arity_bits() {
|
if let Some(max_arity_bits) = params.max_arity_bits() {
|
||||||
self.check_recursion_config(max_arity_bits);
|
self.check_recursion_config(max_arity_bits);
|
||||||
@ -160,7 +166,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
self,
|
self,
|
||||||
level,
|
level,
|
||||||
&format!("verify one (of {num_queries}) query rounds"),
|
&format!("verify one (of {num_queries}) query rounds"),
|
||||||
self.fri_verifier_query_round::<C>(
|
self.fri_verifier_query_round::<HCO, HCI, C>(
|
||||||
instance,
|
instance,
|
||||||
challenges,
|
challenges,
|
||||||
&precomputed_reduced_evals,
|
&precomputed_reduced_evals,
|
||||||
@ -175,13 +181,15 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fri_verify_initial_proof<H: AlgebraicHasher<F>>(
|
fn fri_verify_initial_proof<HC: HashConfig, H: AlgebraicHasher<F, HC>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
x_index_bits: &[BoolTarget],
|
x_index_bits: &[BoolTarget],
|
||||||
proof: &FriInitialTreeProofTarget,
|
proof: &FriInitialTreeProofTarget,
|
||||||
initial_merkle_caps: &[MerkleCapTarget],
|
initial_merkle_caps: &[MerkleCapTarget],
|
||||||
cap_index: Target,
|
cap_index: Target,
|
||||||
) {
|
) where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
for (i, ((evals, merkle_proof), cap)) in proof
|
for (i, ((evals, merkle_proof), cap)) in proof
|
||||||
.evals_proofs
|
.evals_proofs
|
||||||
.iter()
|
.iter()
|
||||||
@ -191,7 +199,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
with_context!(
|
with_context!(
|
||||||
self,
|
self,
|
||||||
&format!("verify {i}'th initial Merkle proof"),
|
&format!("verify {i}'th initial Merkle proof"),
|
||||||
self.verify_merkle_proof_to_cap_with_cap_index::<H>(
|
self.verify_merkle_proof_to_cap_with_cap_index::<HC, H>(
|
||||||
evals.clone(),
|
evals.clone(),
|
||||||
x_index_bits,
|
x_index_bits,
|
||||||
cap_index,
|
cap_index,
|
||||||
@ -246,7 +254,11 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
sum
|
sum
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fri_verifier_query_round<C: GenericConfig<D, F = F>>(
|
fn fri_verifier_query_round<
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
instance: &FriInstanceInfoTarget<D>,
|
instance: &FriInstanceInfoTarget<D>,
|
||||||
challenges: &FriChallengesTarget<D>,
|
challenges: &FriChallengesTarget<D>,
|
||||||
@ -258,7 +270,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
round_proof: &FriQueryRoundTarget<D>,
|
round_proof: &FriQueryRoundTarget<D>,
|
||||||
params: &FriParams,
|
params: &FriParams,
|
||||||
) where
|
) where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
let n_log = log2_strict(n);
|
let n_log = log2_strict(n);
|
||||||
|
|
||||||
@ -272,7 +285,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
with_context!(
|
with_context!(
|
||||||
self,
|
self,
|
||||||
"check FRI initial proof",
|
"check FRI initial proof",
|
||||||
self.fri_verify_initial_proof::<C::Hasher>(
|
self.fri_verify_initial_proof::<HCO, C::Hasher>(
|
||||||
&x_index_bits,
|
&x_index_bits,
|
||||||
&round_proof.initial_trees_proof,
|
&round_proof.initial_trees_proof,
|
||||||
initial_merkle_caps,
|
initial_merkle_caps,
|
||||||
@ -332,7 +345,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
with_context!(
|
with_context!(
|
||||||
self,
|
self,
|
||||||
"verify FRI round Merkle proof.",
|
"verify FRI round Merkle proof.",
|
||||||
self.verify_merkle_proof_to_cap_with_cap_index::<C::Hasher>(
|
self.verify_merkle_proof_to_cap_with_cap_index::<HCO, C::Hasher>(
|
||||||
flatten_target(evals),
|
flatten_target(evals),
|
||||||
&coset_index_bits,
|
&coset_index_bits,
|
||||||
cap_index,
|
cap_index,
|
||||||
|
|||||||
@ -5,17 +5,20 @@ use crate::fri::proof::{FriProof, FriQueryRound, FriQueryStep};
|
|||||||
use crate::fri::structure::FriInstanceInfo;
|
use crate::fri::structure::FriInstanceInfo;
|
||||||
use crate::fri::FriParams;
|
use crate::fri::FriParams;
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::plonk::config::GenericConfig;
|
use crate::plonk::config::GenericConfig;
|
||||||
use crate::plonk::plonk_common::salt_size;
|
use crate::plonk::plonk_common::salt_size;
|
||||||
|
|
||||||
pub(crate) fn validate_fri_proof_shape<F, C, const D: usize>(
|
pub(crate) fn validate_fri_proof_shape<F, HCO, HCI, C, const D: usize>(
|
||||||
proof: &FriProof<F, C::Hasher, D>,
|
proof: &FriProof<F, HCO, C::Hasher, D>,
|
||||||
instance: &FriInstanceInfo<F, D>,
|
instance: &FriInstanceInfo<F, D>,
|
||||||
params: &FriParams,
|
params: &FriParams,
|
||||||
) -> anyhow::Result<()>
|
) -> anyhow::Result<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let FriProof {
|
let FriProof {
|
||||||
commit_phase_merkle_caps,
|
commit_phase_merkle_caps,
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use crate::fri::structure::{FriBatchInfo, FriInstanceInfo, FriOpenings};
|
|||||||
use crate::fri::validate_shape::validate_fri_proof_shape;
|
use crate::fri::validate_shape::validate_fri_proof_shape;
|
||||||
use crate::fri::{FriConfig, FriParams};
|
use crate::fri::{FriConfig, FriParams};
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_proofs::verify_merkle_proof_to_cap;
|
use crate::hash::merkle_proofs::verify_merkle_proof_to_cap;
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::plonk::config::{GenericConfig, Hasher};
|
use crate::plonk::config::{GenericConfig, Hasher};
|
||||||
@ -60,17 +61,22 @@ pub(crate) fn fri_verify_proof_of_work<F: RichField + Extendable<D>, const D: us
|
|||||||
|
|
||||||
pub fn verify_fri_proof<
|
pub fn verify_fri_proof<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
instance: &FriInstanceInfo<F, D>,
|
instance: &FriInstanceInfo<F, D>,
|
||||||
openings: &FriOpenings<F, D>,
|
openings: &FriOpenings<F, D>,
|
||||||
challenges: &FriChallenges<F, D>,
|
challenges: &FriChallenges<F, D>,
|
||||||
initial_merkle_caps: &[MerkleCap<F, C::Hasher>],
|
initial_merkle_caps: &[MerkleCap<F, HCO, C::Hasher>],
|
||||||
proof: &FriProof<F, C::Hasher, D>,
|
proof: &FriProof<F, HCO, C::Hasher, D>,
|
||||||
params: &FriParams,
|
params: &FriParams,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
validate_fri_proof_shape::<F, C, D>(proof, instance, params)?;
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
|
validate_fri_proof_shape::<F, HCO, HCI, C, D>(proof, instance, params)?;
|
||||||
|
|
||||||
// Size of the LDE domain.
|
// Size of the LDE domain.
|
||||||
let n = params.lde_size();
|
let n = params.lde_size();
|
||||||
@ -91,7 +97,7 @@ pub fn verify_fri_proof<
|
|||||||
.iter()
|
.iter()
|
||||||
.zip(&proof.query_round_proofs)
|
.zip(&proof.query_round_proofs)
|
||||||
{
|
{
|
||||||
fri_verifier_query_round::<F, C, D>(
|
fri_verifier_query_round::<F, HCO, HCI, C, D>(
|
||||||
instance,
|
instance,
|
||||||
challenges,
|
challenges,
|
||||||
&precomputed_reduced_evals,
|
&precomputed_reduced_evals,
|
||||||
@ -107,13 +113,16 @@ pub fn verify_fri_proof<
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fri_verify_initial_proof<F: RichField, H: Hasher<F>>(
|
fn fri_verify_initial_proof<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
x_index: usize,
|
x_index: usize,
|
||||||
proof: &FriInitialTreeProof<F, H>,
|
proof: &FriInitialTreeProof<F, HC, H>,
|
||||||
initial_merkle_caps: &[MerkleCap<F, H>],
|
initial_merkle_caps: &[MerkleCap<F, HC, H>],
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
for ((evals, merkle_proof), cap) in proof.evals_proofs.iter().zip(initial_merkle_caps) {
|
for ((evals, merkle_proof), cap) in proof.evals_proofs.iter().zip(initial_merkle_caps) {
|
||||||
verify_merkle_proof_to_cap::<F, H>(evals.clone(), x_index, cap, merkle_proof)?;
|
verify_merkle_proof_to_cap::<F, HC, H>(evals.clone(), x_index, cap, merkle_proof)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -121,11 +130,13 @@ fn fri_verify_initial_proof<F: RichField, H: Hasher<F>>(
|
|||||||
|
|
||||||
pub(crate) fn fri_combine_initial<
|
pub(crate) fn fri_combine_initial<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
instance: &FriInstanceInfo<F, D>,
|
instance: &FriInstanceInfo<F, D>,
|
||||||
proof: &FriInitialTreeProof<F, C::Hasher>,
|
proof: &FriInitialTreeProof<F, HCO, C::Hasher>,
|
||||||
alpha: F::Extension,
|
alpha: F::Extension,
|
||||||
subgroup_x: F,
|
subgroup_x: F,
|
||||||
precomputed_reduced_evals: &PrecomputedReducedOpenings<F, D>,
|
precomputed_reduced_evals: &PrecomputedReducedOpenings<F, D>,
|
||||||
@ -162,20 +173,25 @@ pub(crate) fn fri_combine_initial<
|
|||||||
|
|
||||||
fn fri_verifier_query_round<
|
fn fri_verifier_query_round<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
instance: &FriInstanceInfo<F, D>,
|
instance: &FriInstanceInfo<F, D>,
|
||||||
challenges: &FriChallenges<F, D>,
|
challenges: &FriChallenges<F, D>,
|
||||||
precomputed_reduced_evals: &PrecomputedReducedOpenings<F, D>,
|
precomputed_reduced_evals: &PrecomputedReducedOpenings<F, D>,
|
||||||
initial_merkle_caps: &[MerkleCap<F, C::Hasher>],
|
initial_merkle_caps: &[MerkleCap<F, HCO, C::Hasher>],
|
||||||
proof: &FriProof<F, C::Hasher, D>,
|
proof: &FriProof<F, HCO, C::Hasher, D>,
|
||||||
mut x_index: usize,
|
mut x_index: usize,
|
||||||
n: usize,
|
n: usize,
|
||||||
round_proof: &FriQueryRound<F, C::Hasher, D>,
|
round_proof: &FriQueryRound<F, HCO, C::Hasher, D>,
|
||||||
params: &FriParams,
|
params: &FriParams,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
fri_verify_initial_proof::<F, C::Hasher>(
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
|
fri_verify_initial_proof::<F, HCO, C::Hasher>(
|
||||||
x_index,
|
x_index,
|
||||||
&round_proof.initial_trees_proof,
|
&round_proof.initial_trees_proof,
|
||||||
initial_merkle_caps,
|
initial_merkle_caps,
|
||||||
@ -187,7 +203,7 @@ fn fri_verifier_query_round<
|
|||||||
|
|
||||||
// old_eval is the last derived evaluation; it will be checked for consistency with its
|
// old_eval is the last derived evaluation; it will be checked for consistency with its
|
||||||
// committed "parent" value in the next iteration.
|
// committed "parent" value in the next iteration.
|
||||||
let mut old_eval = fri_combine_initial::<F, C, D>(
|
let mut old_eval = fri_combine_initial::<F, HCO, HCI, C, D>(
|
||||||
instance,
|
instance,
|
||||||
&round_proof.initial_trees_proof,
|
&round_proof.initial_trees_proof,
|
||||||
challenges.fri_alpha,
|
challenges.fri_alpha,
|
||||||
@ -216,7 +232,7 @@ fn fri_verifier_query_round<
|
|||||||
challenges.fri_betas[i],
|
challenges.fri_betas[i],
|
||||||
);
|
);
|
||||||
|
|
||||||
verify_merkle_proof_to_cap::<F, C::Hasher>(
|
verify_merkle_proof_to_cap::<F, HCO, C::Hasher>(
|
||||||
flatten(evals),
|
flatten(evals),
|
||||||
coset_index,
|
coset_index,
|
||||||
&proof.commit_phase_merkle_caps[i],
|
&proof.commit_phase_merkle_caps[i],
|
||||||
|
|||||||
@ -3,18 +3,20 @@ use itertools::Itertools;
|
|||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
use crate::fri::proof::{FriProof, FriProofTarget};
|
use crate::fri::proof::{FriProof, FriProofTarget};
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::iop::witness::WitnessWrite;
|
use crate::iop::witness::WitnessWrite;
|
||||||
use crate::plonk::config::AlgebraicHasher;
|
use crate::plonk::config::AlgebraicHasher;
|
||||||
|
|
||||||
/// Set the targets in a `FriProofTarget` to their corresponding values in a `FriProof`.
|
/// Set the targets in a `FriProofTarget` to their corresponding values in a `FriProof`.
|
||||||
pub fn set_fri_proof_target<F, W, H, const D: usize>(
|
pub fn set_fri_proof_target<F, W, HC, H, const D: usize>(
|
||||||
witness: &mut W,
|
witness: &mut W,
|
||||||
fri_proof_target: &FriProofTarget<D>,
|
fri_proof_target: &FriProofTarget<D>,
|
||||||
fri_proof: &FriProof<F, H, D>,
|
fri_proof: &FriProof<F, HC, H, D>,
|
||||||
) where
|
) where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
W: WitnessWrite<F> + ?Sized,
|
W: WitnessWrite<F> + ?Sized,
|
||||||
H: AlgebraicHasher<F>,
|
HC: HashConfig,
|
||||||
|
H: AlgebraicHasher<F, HC>,
|
||||||
{
|
{
|
||||||
witness.set_target(fri_proof_target.pow_witness, fri_proof.pow_witness);
|
witness.set_target(fri_proof_target.pow_witness, fri_proof.pow_witness);
|
||||||
|
|
||||||
|
|||||||
@ -576,15 +576,20 @@ mod tests {
|
|||||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, KeccakGoldilocksConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{
|
||||||
|
GenericConfig, KeccakGoldilocksConfig, KeccakHashConfig, PoseidonGoldilocksConfig,
|
||||||
|
PoseidonHashConfig,
|
||||||
|
};
|
||||||
use crate::plonk::verifier::verify;
|
use crate::plonk::verifier::verify;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mul_many() -> Result<()> {
|
fn test_mul_many() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
type FF = <C as GenericConfig<HCO, HCI, D>>::FE;
|
||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
|
|
||||||
@ -609,7 +614,7 @@ mod tests {
|
|||||||
builder.connect_extension(mul0, mul1);
|
builder.connect_extension(mul0, mul1);
|
||||||
builder.connect_extension(mul1, mul2);
|
builder.connect_extension(mul1, mul2);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
verify(proof, &data.verifier_only, &data.common)
|
verify(proof, &data.verifier_only, &data.common)
|
||||||
@ -619,8 +624,10 @@ mod tests {
|
|||||||
fn test_div_extension() -> Result<()> {
|
fn test_div_extension() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
type FF = <C as GenericConfig<HCO, HCI, D>>::FE;
|
||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_zk_config();
|
let config = CircuitConfig::standard_recursion_zk_config();
|
||||||
|
|
||||||
@ -636,7 +643,7 @@ mod tests {
|
|||||||
let comp_zt = builder.div_extension(xt, yt);
|
let comp_zt = builder.div_extension(xt, yt);
|
||||||
builder.connect_extension(zt, comp_zt);
|
builder.connect_extension(zt, comp_zt);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
verify(proof, &data.verifier_only, &data.common)
|
verify(proof, &data.verifier_only, &data.common)
|
||||||
@ -646,8 +653,10 @@ mod tests {
|
|||||||
fn test_mul_algebra() -> Result<()> {
|
fn test_mul_algebra() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = KeccakGoldilocksConfig;
|
type C = KeccakGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = KeccakHashConfig;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type HCI = PoseidonHashConfig;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
type FF = <C as GenericConfig<HCO, HCI, D>>::FE;
|
||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
|
|
||||||
@ -674,7 +683,7 @@ mod tests {
|
|||||||
pw.set_extension_target(zt.0[i], z.0[i]);
|
pw.set_extension_target(zt.0[i], z.0[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
verify(proof, &data.verifier_only, &data.common)
|
verify(proof, &data.verifier_only, &data.common)
|
||||||
|
|||||||
@ -1,27 +1,27 @@
|
|||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::hash::hashing::SPONGE_WIDTH;
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::config::AlgebraicHasher;
|
use crate::plonk::config::AlgebraicHasher;
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||||
pub fn permute<H: AlgebraicHasher<F>>(
|
pub fn permute<HC: HashConfig, H: AlgebraicHasher<F, HC>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
inputs: [Target; SPONGE_WIDTH],
|
inputs: [Target; HC::WIDTH],
|
||||||
) -> [Target; SPONGE_WIDTH] {
|
) -> [Target; HC::WIDTH] {
|
||||||
// We don't want to swap any inputs, so set that wire to 0.
|
// We don't want to swap any inputs, so set that wire to 0.
|
||||||
let _false = self._false();
|
let _false = self._false();
|
||||||
self.permute_swapped::<H>(inputs, _false)
|
self.permute_swapped::<HC, H>(inputs, _false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Conditionally swap two chunks of the inputs (useful in verifying Merkle proofs), then apply
|
/// Conditionally swap two chunks of the inputs (useful in verifying Merkle proofs), then apply
|
||||||
/// a cryptographic permutation.
|
/// a cryptographic permutation.
|
||||||
pub(crate) fn permute_swapped<H: AlgebraicHasher<F>>(
|
pub(crate) fn permute_swapped<HC: HashConfig, H: AlgebraicHasher<F, HC>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
inputs: [Target; SPONGE_WIDTH],
|
inputs: [Target; HC::WIDTH],
|
||||||
swap: BoolTarget,
|
swap: BoolTarget,
|
||||||
) -> [Target; SPONGE_WIDTH] {
|
) -> [Target; HC::WIDTH] {
|
||||||
H::permute_swapped(inputs, swap, self)
|
H::permute_swapped(inputs, swap, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,15 +47,17 @@ mod tests {
|
|||||||
use crate::iop::witness::PartialWitness;
|
use crate::iop::witness::PartialWitness;
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use crate::plonk::verifier::verify;
|
use crate::plonk::verifier::verify;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_interpolate() -> Result<()> {
|
fn test_interpolate() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
type FF = <C as GenericConfig<HCO, HCI, D>>::FE;
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let pw = PartialWitness::new();
|
let pw = PartialWitness::new();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
@ -102,7 +104,7 @@ mod tests {
|
|||||||
builder.connect_extension(eval_coset_gate, true_eval_target);
|
builder.connect_extension(eval_coset_gate, true_eval_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
verify(proof, &data.verifier_only, &data.common)
|
verify(proof, &data.verifier_only, &data.common)
|
||||||
|
|||||||
@ -107,14 +107,16 @@ mod tests {
|
|||||||
use crate::field::types::{Field, Sample};
|
use crate::field::types::{Field, Sample};
|
||||||
use crate::iop::witness::PartialWitness;
|
use crate::iop::witness::PartialWitness;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use crate::plonk::verifier::verify;
|
use crate::plonk::verifier::verify;
|
||||||
|
|
||||||
fn test_random_access_given_len(len_log: usize) -> Result<()> {
|
fn test_random_access_given_len(len_log: usize) -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
type FF = <C as GenericConfig<HCO, HCI, D>>::FE;
|
||||||
let len = 1 << len_log;
|
let len = 1 << len_log;
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let pw = PartialWitness::new();
|
let pw = PartialWitness::new();
|
||||||
@ -129,7 +131,7 @@ mod tests {
|
|||||||
builder.connect_extension(elem, res);
|
builder.connect_extension(elem, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
verify(proof, &data.verifier_only, &data.common)
|
verify(proof, &data.verifier_only, &data.common)
|
||||||
|
|||||||
@ -44,15 +44,17 @@ mod tests {
|
|||||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use crate::plonk::verifier::verify;
|
use crate::plonk::verifier::verify;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_select() -> Result<()> {
|
fn test_select() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
type FF = <C as GenericConfig<HCO, HCI, D>>::FE;
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let mut pw = PartialWitness::<F>::new();
|
let mut pw = PartialWitness::<F>::new();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
@ -72,7 +74,7 @@ mod tests {
|
|||||||
builder.connect_extension(should_be_x, xt);
|
builder.connect_extension(should_be_x, xt);
|
||||||
builder.connect_extension(should_be_y, yt);
|
builder.connect_extension(should_be_y, yt);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
verify(proof, &data.verifier_only, &data.common)
|
verify(proof, &data.verifier_only, &data.common)
|
||||||
|
|||||||
@ -113,14 +113,16 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::iop::witness::PartialWitness;
|
use crate::iop::witness::PartialWitness;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use crate::plonk::verifier::verify;
|
use crate::plonk::verifier::verify;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_split_base() -> Result<()> {
|
fn test_split_base() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let pw = PartialWitness::new();
|
let pw = PartialWitness::new();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
@ -137,7 +139,7 @@ mod tests {
|
|||||||
builder.connect(limbs[3], one);
|
builder.connect(limbs[3], one);
|
||||||
|
|
||||||
builder.assert_leading_zeros(xt, 64 - 9);
|
builder.assert_leading_zeros(xt, 64 - 9);
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
|
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
@ -148,7 +150,9 @@ mod tests {
|
|||||||
fn test_base_sum() -> Result<()> {
|
fn test_base_sum() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let pw = PartialWitness::new();
|
let pw = PartialWitness::new();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
@ -172,7 +176,7 @@ mod tests {
|
|||||||
|
|
||||||
builder.connect(x, y);
|
builder.connect(x, y);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
|
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
|
|||||||
@ -218,7 +218,7 @@ mod tests {
|
|||||||
use crate::gates::arithmetic_base::ArithmeticGate;
|
use crate::gates::arithmetic_base::ArithmeticGate;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn low_degree() {
|
fn low_degree() {
|
||||||
@ -230,8 +230,10 @@ mod tests {
|
|||||||
fn eval_fns() -> Result<()> {
|
fn eval_fns() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let gate = ArithmeticGate::new_from_config(&CircuitConfig::standard_recursion_config());
|
let gate = ArithmeticGate::new_from_config(&CircuitConfig::standard_recursion_config());
|
||||||
test_eval_fns::<F, C, _, D>(gate)
|
test_eval_fns::<F, HCO, HCI, C, _, D>(gate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -211,7 +211,7 @@ mod tests {
|
|||||||
use crate::gates::arithmetic_extension::ArithmeticExtensionGate;
|
use crate::gates::arithmetic_extension::ArithmeticExtensionGate;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn low_degree() {
|
fn low_degree() {
|
||||||
@ -224,9 +224,11 @@ mod tests {
|
|||||||
fn eval_fns() -> Result<()> {
|
fn eval_fns() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let gate =
|
let gate =
|
||||||
ArithmeticExtensionGate::new_from_config(&CircuitConfig::standard_recursion_config());
|
ArithmeticExtensionGate::new_from_config(&CircuitConfig::standard_recursion_config());
|
||||||
test_eval_fns::<F, C, _, D>(gate)
|
test_eval_fns::<F, HCO, HCI, C, _, D>(gate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -204,7 +204,7 @@ mod tests {
|
|||||||
use crate::field::goldilocks_field::GoldilocksField;
|
use crate::field::goldilocks_field::GoldilocksField;
|
||||||
use crate::gates::base_sum::BaseSumGate;
|
use crate::gates::base_sum::BaseSumGate;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn low_degree() {
|
fn low_degree() {
|
||||||
@ -215,7 +215,9 @@ mod tests {
|
|||||||
fn eval_fns() -> Result<()> {
|
fn eval_fns() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
test_eval_fns::<F, C, _, D>(BaseSumGate::<6>::new(11))
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
test_eval_fns::<F, HCO, HCI, C, _, D>(BaseSumGate::<6>::new(11))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -122,7 +122,7 @@ mod tests {
|
|||||||
use crate::gates::constant::ConstantGate;
|
use crate::gates::constant::ConstantGate;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn low_degree() {
|
fn low_degree() {
|
||||||
@ -135,9 +135,11 @@ mod tests {
|
|||||||
fn eval_fns() -> Result<()> {
|
fn eval_fns() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let num_consts = CircuitConfig::standard_recursion_config().num_constants;
|
let num_consts = CircuitConfig::standard_recursion_config().num_constants;
|
||||||
let gate = ConstantGate { num_consts };
|
let gate = ConstantGate { num_consts };
|
||||||
test_eval_fns::<F, C, _, D>(gate)
|
test_eval_fns::<F, HCO, HCI, C, _, D>(gate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -606,7 +606,7 @@ mod tests {
|
|||||||
use crate::field::types::{Field, Sample};
|
use crate::field::types::{Field, Sample};
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::hash::hash_types::HashOut;
|
use crate::hash::hash_types::HashOut;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_degree_and_wires_minimized() {
|
fn test_degree_and_wires_minimized() {
|
||||||
@ -747,9 +747,13 @@ mod tests {
|
|||||||
fn eval_fns() -> Result<()> {
|
fn eval_fns() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
for degree in 2..=4 {
|
for degree in 2..=4 {
|
||||||
test_eval_fns::<F, C, _, D>(CosetInterpolationGate::with_max_degree(2, degree))?;
|
test_eval_fns::<F, HCO, HCI, C, _, D>(CosetInterpolationGate::with_max_degree(
|
||||||
|
2, degree,
|
||||||
|
))?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -758,8 +762,10 @@ mod tests {
|
|||||||
fn test_gate_constraint() {
|
fn test_gate_constraint() {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
type FF = <C as GenericConfig<HCO, HCI, D>>::FE;
|
||||||
|
|
||||||
/// Returns the local wires for an interpolation gate for given coeffs, points and eval point.
|
/// Returns the local wires for an interpolation gate for given coeffs, points and eval point.
|
||||||
fn get_wires(shift: F, values: PolynomialValues<FF>, eval_point: FF) -> Vec<FF> {
|
fn get_wires(shift: F, values: PolynomialValues<FF>, eval_point: FF) -> Vec<FF> {
|
||||||
|
|||||||
@ -294,7 +294,7 @@ mod tests {
|
|||||||
use crate::field::types::Sample;
|
use crate::field::types::Sample;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::hash::hash_types::HashOut;
|
use crate::hash::hash_types::HashOut;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use crate::util::log2_ceil;
|
use crate::util::log2_ceil;
|
||||||
|
|
||||||
const MAX_POWER_BITS: usize = 17;
|
const MAX_POWER_BITS: usize = 17;
|
||||||
@ -329,8 +329,10 @@ mod tests {
|
|||||||
fn eval_fns() -> Result<()> {
|
fn eval_fns() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
test_eval_fns::<F, C, _, D>(ExponentiationGate::new_from_config(
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
test_eval_fns::<F, HCO, HCI, C, _, D>(ExponentiationGate::new_from_config(
|
||||||
&CircuitConfig::standard_recursion_config(),
|
&CircuitConfig::standard_recursion_config(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -339,8 +341,10 @@ mod tests {
|
|||||||
fn test_gate_constraint() {
|
fn test_gate_constraint() {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
type FF = <C as GenericConfig<HCO, HCI, D>>::FE;
|
||||||
|
|
||||||
/// Returns the local wires for an exponentiation gate given the base, power, and power bit
|
/// Returns the local wires for an exponentiation gate given the base, power, and power bit
|
||||||
/// values.
|
/// values.
|
||||||
|
|||||||
@ -8,6 +8,7 @@ use crate::field::polynomial::{PolynomialCoeffs, PolynomialValues};
|
|||||||
use crate::field::types::{Field, Sample};
|
use crate::field::types::{Field, Sample};
|
||||||
use crate::gates::gate::Gate;
|
use crate::gates::gate::Gate;
|
||||||
use crate::hash::hash_types::{HashOut, RichField};
|
use crate::hash::hash_types::{HashOut, RichField};
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
@ -88,12 +89,18 @@ fn random_low_degree_values<F: Field>(rate_bits: usize) -> Vec<F> {
|
|||||||
|
|
||||||
pub fn test_eval_fns<
|
pub fn test_eval_fns<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
G: Gate<F, D>,
|
G: Gate<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
gate: G,
|
gate: G,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
// Test that `eval_unfiltered` and `eval_unfiltered_base` are coherent.
|
// Test that `eval_unfiltered` and `eval_unfiltered_base` are coherent.
|
||||||
let wires_base = F::rand_vec(gate.num_wires());
|
let wires_base = F::rand_vec(gate.num_wires());
|
||||||
let constants_base = F::rand_vec(gate.num_constants());
|
let constants_base = F::rand_vec(gate.num_constants());
|
||||||
@ -157,7 +164,7 @@ pub fn test_eval_fns<
|
|||||||
let evals_t = gate.eval_unfiltered_circuit(&mut builder, vars_t);
|
let evals_t = gate.eval_unfiltered_circuit(&mut builder, vars_t);
|
||||||
pw.set_extension_targets(&evals_t, &evals);
|
pw.set_extension_targets(&evals_t, &evals);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
verify(proof, &data.verifier_only, &data.common)
|
verify::<F, HCO, HCI, C, D>(proof, &data.verifier_only, &data.common)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -187,7 +187,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::field::goldilocks_field::GoldilocksField;
|
use crate::field::goldilocks_field::GoldilocksField;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn low_degree() {
|
fn low_degree() {
|
||||||
@ -199,8 +199,10 @@ mod tests {
|
|||||||
fn eval_fns() -> Result<()> {
|
fn eval_fns() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let gate = MulExtensionGate::new_from_config(&CircuitConfig::standard_recursion_config());
|
let gate = MulExtensionGate::new_from_config(&CircuitConfig::standard_recursion_config());
|
||||||
test_eval_fns::<F, C, _, D>(gate)
|
test_eval_fns::<F, HCO, HCI, C, _, D>(gate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,7 +60,7 @@ mod tests {
|
|||||||
use crate::field::goldilocks_field::GoldilocksField;
|
use crate::field::goldilocks_field::GoldilocksField;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::gates::noop::NoopGate;
|
use crate::gates::noop::NoopGate;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn low_degree() {
|
fn low_degree() {
|
||||||
@ -71,7 +71,9 @@ mod tests {
|
|||||||
fn eval_fns() -> anyhow::Result<()> {
|
fn eval_fns() -> anyhow::Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
test_eval_fns::<F, C, _, D>(NoopGate)
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
test_eval_fns::<F, HCO, HCI, C, _, D>(NoopGate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,9 +10,8 @@ use crate::gates::gate::Gate;
|
|||||||
use crate::gates::poseidon_mds::PoseidonMdsGate;
|
use crate::gates::poseidon_mds::PoseidonMdsGate;
|
||||||
use crate::gates::util::StridedConstraintConsumer;
|
use crate::gates::util::StridedConstraintConsumer;
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::hash::hashing::SPONGE_WIDTH;
|
|
||||||
use crate::hash::poseidon;
|
use crate::hash::poseidon;
|
||||||
use crate::hash::poseidon::Poseidon;
|
use crate::hash::poseidon::{Poseidon, SPONGE_WIDTH};
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
@ -510,14 +509,13 @@ mod tests {
|
|||||||
use crate::field::types::Field;
|
use crate::field::types::Field;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::gates::poseidon::PoseidonGate;
|
use crate::gates::poseidon::PoseidonGate;
|
||||||
use crate::hash::hashing::SPONGE_WIDTH;
|
use crate::hash::poseidon::{Poseidon, SPONGE_WIDTH};
|
||||||
use crate::hash::poseidon::Poseidon;
|
|
||||||
use crate::iop::generator::generate_partial_witness;
|
use crate::iop::generator::generate_partial_witness;
|
||||||
use crate::iop::wire::Wire;
|
use crate::iop::wire::Wire;
|
||||||
use crate::iop::witness::{PartialWitness, Witness, WitnessWrite};
|
use crate::iop::witness::{PartialWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wire_indices() {
|
fn wire_indices() {
|
||||||
@ -545,7 +543,9 @@ mod tests {
|
|||||||
fn generated_output() {
|
fn generated_output() {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
|
||||||
let config = CircuitConfig {
|
let config = CircuitConfig {
|
||||||
num_wires: 143,
|
num_wires: 143,
|
||||||
@ -555,7 +555,7 @@ mod tests {
|
|||||||
type Gate = PoseidonGate<F, D>;
|
type Gate = PoseidonGate<F, D>;
|
||||||
let gate = Gate::new();
|
let gate = Gate::new();
|
||||||
let row = builder.add_gate(gate, vec![]);
|
let row = builder.add_gate(gate, vec![]);
|
||||||
let circuit = builder.build_prover::<C>();
|
let circuit = builder.build_prover::<HCO, HCI, C>();
|
||||||
|
|
||||||
let permutation_inputs = (0..SPONGE_WIDTH)
|
let permutation_inputs = (0..SPONGE_WIDTH)
|
||||||
.map(F::from_canonical_usize)
|
.map(F::from_canonical_usize)
|
||||||
@ -603,8 +603,10 @@ mod tests {
|
|||||||
fn eval_fns() -> Result<()> {
|
fn eval_fns() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let gate = PoseidonGate::<F, 2>::new();
|
let gate = PoseidonGate::<F, 2>::new();
|
||||||
test_eval_fns::<F, C, _, D>(gate)
|
test_eval_fns::<F, HCO, HCI, C, _, D>(gate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,8 +11,7 @@ use crate::field::types::Field;
|
|||||||
use crate::gates::gate::Gate;
|
use crate::gates::gate::Gate;
|
||||||
use crate::gates::util::StridedConstraintConsumer;
|
use crate::gates::util::StridedConstraintConsumer;
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::hash::hashing::SPONGE_WIDTH;
|
use crate::hash::poseidon::{Poseidon, SPONGE_WIDTH};
|
||||||
use crate::hash::poseidon::Poseidon;
|
|
||||||
use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget};
|
use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget};
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
@ -244,13 +243,15 @@ impl<F: RichField + Extendable<D> + Poseidon, const D: usize> SimpleGenerator<F>
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::gates::poseidon_mds::PoseidonMdsGate;
|
use crate::gates::poseidon_mds::PoseidonMdsGate;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn low_degree() {
|
fn low_degree() {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let gate = PoseidonMdsGate::<F, D>::new();
|
let gate = PoseidonMdsGate::<F, D>::new();
|
||||||
test_low_degree(gate)
|
test_low_degree(gate)
|
||||||
}
|
}
|
||||||
@ -259,8 +260,10 @@ mod tests {
|
|||||||
fn eval_fns() -> anyhow::Result<()> {
|
fn eval_fns() -> anyhow::Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let gate = PoseidonMdsGate::<F, D>::new();
|
let gate = PoseidonMdsGate::<F, D>::new();
|
||||||
test_eval_fns::<F, C, _, D>(gate)
|
test_eval_fns::<F, HCO, HCI, C, _, D>(gate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,7 +104,7 @@ mod tests {
|
|||||||
use crate::field::goldilocks_field::GoldilocksField;
|
use crate::field::goldilocks_field::GoldilocksField;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::gates::public_input::PublicInputGate;
|
use crate::gates::public_input::PublicInputGate;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn low_degree() {
|
fn low_degree() {
|
||||||
@ -115,7 +115,9 @@ mod tests {
|
|||||||
fn eval_fns() -> anyhow::Result<()> {
|
fn eval_fns() -> anyhow::Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
test_eval_fns::<F, C, _, D>(PublicInputGate)
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
test_eval_fns::<F, HCO, HCI, C, _, D>(PublicInputGate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -389,7 +389,7 @@ mod tests {
|
|||||||
use crate::field::types::Sample;
|
use crate::field::types::Sample;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::hash::hash_types::HashOut;
|
use crate::hash::hash_types::HashOut;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn low_degree() {
|
fn low_degree() {
|
||||||
@ -400,16 +400,20 @@ mod tests {
|
|||||||
fn eval_fns() -> Result<()> {
|
fn eval_fns() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
test_eval_fns::<F, C, _, D>(RandomAccessGate::new(4, 4, 1))
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
test_eval_fns::<F, HCO, HCI, C, _, D>(RandomAccessGate::new(4, 4, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_gate_constraint() {
|
fn test_gate_constraint() {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
type FF = <C as GenericConfig<HCO, HCI, D>>::FE;
|
||||||
|
|
||||||
/// Returns the local wires for a random access gate given the vectors, elements to compare,
|
/// Returns the local wires for a random access gate given the vectors, elements to compare,
|
||||||
/// and indices.
|
/// and indices.
|
||||||
|
|||||||
@ -216,7 +216,7 @@ mod tests {
|
|||||||
use crate::field::goldilocks_field::GoldilocksField;
|
use crate::field::goldilocks_field::GoldilocksField;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::gates::reducing::ReducingGate;
|
use crate::gates::reducing::ReducingGate;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn low_degree() {
|
fn low_degree() {
|
||||||
@ -227,7 +227,9 @@ mod tests {
|
|||||||
fn eval_fns() -> Result<()> {
|
fn eval_fns() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
test_eval_fns::<F, C, _, D>(ReducingGate::new(22))
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
test_eval_fns::<F, HCO, HCI, C, _, D>(ReducingGate::new(22))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -210,7 +210,7 @@ mod tests {
|
|||||||
use crate::field::goldilocks_field::GoldilocksField;
|
use crate::field::goldilocks_field::GoldilocksField;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
use crate::gates::reducing_extension::ReducingExtensionGate;
|
use crate::gates::reducing_extension::ReducingExtensionGate;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn low_degree() {
|
fn low_degree() {
|
||||||
@ -221,7 +221,9 @@ mod tests {
|
|||||||
fn eval_fns() -> Result<()> {
|
fn eval_fns() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
test_eval_fns::<F, C, _, D>(ReducingExtensionGate::new(22))
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
test_eval_fns::<F, HCO, HCI, C, _, D>(ReducingExtensionGate::new(22))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
//! Concrete instantiation of a hash function.
|
//! Concrete instantiation of a hash function.
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
use crate::hash::hash_types::{HashOut, HashOutTarget, RichField};
|
use crate::hash::hash_types::{HashOut, HashOutTarget, RichField};
|
||||||
@ -8,62 +9,81 @@ use crate::iop::target::Target;
|
|||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::config::AlgebraicHasher;
|
use crate::plonk::config::AlgebraicHasher;
|
||||||
|
|
||||||
pub(crate) const SPONGE_RATE: usize = 8;
|
pub trait HashConfig: Clone + Debug + Eq + PartialEq {
|
||||||
pub(crate) const SPONGE_CAPACITY: usize = 4;
|
const RATE: usize;
|
||||||
pub const SPONGE_WIDTH: usize = SPONGE_RATE + SPONGE_CAPACITY;
|
const WIDTH: usize;
|
||||||
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||||
pub fn hash_or_noop<H: AlgebraicHasher<F>>(&mut self, inputs: Vec<Target>) -> HashOutTarget {
|
pub fn hash_or_noop<HC: HashConfig, H: AlgebraicHasher<F, HC>>(
|
||||||
|
&mut self,
|
||||||
|
inputs: Vec<Target>,
|
||||||
|
) -> HashOutTarget
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let zero = self.zero();
|
let zero = self.zero();
|
||||||
if inputs.len() <= 4 {
|
if inputs.len() <= 4 {
|
||||||
HashOutTarget::from_partial(&inputs, zero)
|
HashOutTarget::from_partial(&inputs, zero)
|
||||||
} else {
|
} else {
|
||||||
self.hash_n_to_hash_no_pad::<H>(inputs)
|
self.hash_n_to_hash_no_pad::<HC, H>(inputs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_n_to_hash_no_pad<H: AlgebraicHasher<F>>(
|
pub fn hash_n_to_hash_no_pad<HC: HashConfig, H: AlgebraicHasher<F, HC>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
inputs: Vec<Target>,
|
inputs: Vec<Target>,
|
||||||
) -> HashOutTarget {
|
) -> HashOutTarget
|
||||||
HashOutTarget::from_vec(self.hash_n_to_m_no_pad::<H>(inputs, 4))
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
|
HashOutTarget::from_vec(self.hash_n_to_m_no_pad::<HC, H>(inputs, 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_n_to_m_no_pad<H: AlgebraicHasher<F>>(
|
pub fn hash_n_to_m_no_pad<HC: HashConfig, H: AlgebraicHasher<F, HC>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
inputs: Vec<Target>,
|
inputs: Vec<Target>,
|
||||||
num_outputs: usize,
|
num_outputs: usize,
|
||||||
) -> Vec<Target> {
|
) -> Vec<Target>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let zero = self.zero();
|
let zero = self.zero();
|
||||||
|
|
||||||
let mut state = [zero; SPONGE_WIDTH];
|
let mut state = [zero; HC::WIDTH];
|
||||||
|
|
||||||
// Absorb all input chunks.
|
// Absorb all input chunks.
|
||||||
for input_chunk in inputs.chunks(SPONGE_RATE) {
|
for input_chunk in inputs.chunks(HC::RATE) {
|
||||||
// Overwrite the first r elements with the inputs. This differs from a standard sponge,
|
// 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,
|
// where we would xor or add in the inputs. This is a well-known variant, though,
|
||||||
// sometimes called "overwrite mode".
|
// sometimes called "overwrite mode".
|
||||||
state[..input_chunk.len()].copy_from_slice(input_chunk);
|
state[..input_chunk.len()].copy_from_slice(input_chunk);
|
||||||
state = self.permute::<H>(state);
|
state = self.permute::<HC, H>(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Squeeze until we have the desired number of outputs.
|
// Squeeze until we have the desired number of outputs.
|
||||||
let mut outputs = Vec::with_capacity(num_outputs);
|
let mut outputs = Vec::with_capacity(num_outputs);
|
||||||
loop {
|
loop {
|
||||||
for i in 0..SPONGE_RATE {
|
for i in 0..HC::RATE {
|
||||||
outputs.push(state[i]);
|
outputs.push(state[i]);
|
||||||
if outputs.len() == num_outputs {
|
if outputs.len() == num_outputs {
|
||||||
return outputs;
|
return outputs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state = self.permute::<H>(state);
|
state = self.permute::<HC, H>(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A one-way compression function which takes two ~256 bit inputs and returns a ~256 bit output.
|
/// A one-way compression function which takes two ~256 bit inputs and returns a ~256 bit output.
|
||||||
pub fn compress<F: RichField, P: PlonkyPermutation<F>>(x: HashOut<F>, y: HashOut<F>) -> HashOut<F> {
|
pub fn compress<F: RichField, HC: HashConfig, P: PlonkyPermutation<F, HC>>(
|
||||||
let mut perm_inputs = [F::ZERO; SPONGE_WIDTH];
|
x: HashOut<F>,
|
||||||
|
y: HashOut<F>,
|
||||||
|
) -> HashOut<F>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
|
let mut perm_inputs = [F::ZERO; HC::WIDTH];
|
||||||
perm_inputs[..4].copy_from_slice(&x.elements);
|
perm_inputs[..4].copy_from_slice(&x.elements);
|
||||||
perm_inputs[4..8].copy_from_slice(&y.elements);
|
perm_inputs[4..8].copy_from_slice(&y.elements);
|
||||||
HashOut {
|
HashOut {
|
||||||
@ -72,20 +92,25 @@ pub fn compress<F: RichField, P: PlonkyPermutation<F>>(x: HashOut<F>, y: HashOut
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Permutation that can be used in the sponge construction for an algebraic hash.
|
/// Permutation that can be used in the sponge construction for an algebraic hash.
|
||||||
pub trait PlonkyPermutation<F: RichField> {
|
pub trait PlonkyPermutation<F: RichField, HC: HashConfig> {
|
||||||
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH];
|
fn permute(input: [F; HC::WIDTH]) -> [F; HC::WIDTH]
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hash a message without any padding step. Note that this can enable length-extension attacks.
|
/// Hash a message without any padding step. Note that this can enable length-extension attacks.
|
||||||
/// However, it is still collision-resistant in cases where the input has a fixed length.
|
/// However, it is still collision-resistant in cases where the input has a fixed length.
|
||||||
pub fn hash_n_to_m_no_pad<F: RichField, P: PlonkyPermutation<F>>(
|
pub fn hash_n_to_m_no_pad<F: RichField, HC: HashConfig, P: PlonkyPermutation<F, HC>>(
|
||||||
inputs: &[F],
|
inputs: &[F],
|
||||||
num_outputs: usize,
|
num_outputs: usize,
|
||||||
) -> Vec<F> {
|
) -> Vec<F>
|
||||||
let mut state = [F::ZERO; SPONGE_WIDTH];
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
|
let mut state = [F::ZERO; HC::WIDTH];
|
||||||
|
|
||||||
// Absorb all input chunks.
|
// Absorb all input chunks.
|
||||||
for input_chunk in inputs.chunks(SPONGE_RATE) {
|
for input_chunk in inputs.chunks(HC::RATE) {
|
||||||
state[..input_chunk.len()].copy_from_slice(input_chunk);
|
state[..input_chunk.len()].copy_from_slice(input_chunk);
|
||||||
state = P::permute(state);
|
state = P::permute(state);
|
||||||
}
|
}
|
||||||
@ -93,7 +118,7 @@ pub fn hash_n_to_m_no_pad<F: RichField, P: PlonkyPermutation<F>>(
|
|||||||
// Squeeze until we have the desired number of outputs.
|
// Squeeze until we have the desired number of outputs.
|
||||||
let mut outputs = Vec::new();
|
let mut outputs = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
for &item in state.iter().take(SPONGE_RATE) {
|
for &item in state.iter().take(HC::RATE) {
|
||||||
outputs.push(item);
|
outputs.push(item);
|
||||||
if outputs.len() == num_outputs {
|
if outputs.len() == num_outputs {
|
||||||
return outputs;
|
return outputs;
|
||||||
@ -103,6 +128,11 @@ pub fn hash_n_to_m_no_pad<F: RichField, P: PlonkyPermutation<F>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_n_to_hash_no_pad<F: RichField, P: PlonkyPermutation<F>>(inputs: &[F]) -> HashOut<F> {
|
pub fn hash_n_to_hash_no_pad<F: RichField, HC: HashConfig, P: PlonkyPermutation<F, HC>>(
|
||||||
HashOut::from_vec(hash_n_to_m_no_pad::<F, P>(inputs, 4))
|
inputs: &[F],
|
||||||
|
) -> HashOut<F>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
|
HashOut::from_vec(hash_n_to_m_no_pad::<F, HC, P>(inputs, 4))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,16 +7,23 @@ use itertools::Itertools;
|
|||||||
use keccak_hash::keccak;
|
use keccak_hash::keccak;
|
||||||
|
|
||||||
use crate::hash::hash_types::{BytesHash, RichField};
|
use crate::hash::hash_types::{BytesHash, RichField};
|
||||||
use crate::hash::hashing::{PlonkyPermutation, SPONGE_WIDTH};
|
use crate::hash::hashing::PlonkyPermutation;
|
||||||
use crate::plonk::config::Hasher;
|
use crate::plonk::config::{Hasher, KeccakHashConfig};
|
||||||
use crate::util::serialization::Write;
|
use crate::util::serialization::Write;
|
||||||
|
|
||||||
|
pub const SPONGE_RATE: usize = 8;
|
||||||
|
pub const SPONGE_CAPACITY: usize = 4;
|
||||||
|
pub const SPONGE_WIDTH: usize = SPONGE_RATE + SPONGE_CAPACITY;
|
||||||
|
|
||||||
/// Keccak-256 pseudo-permutation (not necessarily one-to-one) used in the challenger.
|
/// Keccak-256 pseudo-permutation (not necessarily one-to-one) used in the challenger.
|
||||||
/// A state `input: [F; 12]` is sent to the field representation of `H(input) || H(H(input)) || H(H(H(input)))`
|
/// A state `input: [F; 12]` is sent to the field representation of `H(input) || H(H(input)) || H(H(H(input)))`
|
||||||
/// where `H` is the Keccak-256 hash.
|
/// where `H` is the Keccak-256 hash.
|
||||||
pub struct KeccakPermutation;
|
pub struct KeccakPermutation;
|
||||||
impl<F: RichField> PlonkyPermutation<F> for KeccakPermutation {
|
impl<F: RichField> PlonkyPermutation<F, KeccakHashConfig> for KeccakPermutation {
|
||||||
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] {
|
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH]
|
||||||
|
where
|
||||||
|
[(); SPONGE_WIDTH]:,
|
||||||
|
{
|
||||||
let mut state = vec![0u8; SPONGE_WIDTH * size_of::<u64>()];
|
let mut state = vec![0u8; SPONGE_WIDTH * size_of::<u64>()];
|
||||||
for i in 0..SPONGE_WIDTH {
|
for i in 0..SPONGE_WIDTH {
|
||||||
state[i * size_of::<u64>()..(i + 1) * size_of::<u64>()]
|
state[i * size_of::<u64>()..(i + 1) * size_of::<u64>()]
|
||||||
@ -53,8 +60,7 @@ impl<F: RichField> PlonkyPermutation<F> for KeccakPermutation {
|
|||||||
/// Keccak-256 hash function.
|
/// Keccak-256 hash function.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct KeccakHash<const N: usize>;
|
pub struct KeccakHash<const N: usize>;
|
||||||
|
impl<F: RichField, const N: usize> Hasher<F, KeccakHashConfig> for KeccakHash<N> {
|
||||||
impl<F: RichField, const N: usize> Hasher<F> for KeccakHash<N> {
|
|
||||||
const HASH_SIZE: usize = N;
|
const HASH_SIZE: usize = N;
|
||||||
type Hash = BytesHash<N>;
|
type Hash = BytesHash<N>;
|
||||||
type Permutation = KeccakPermutation;
|
type Permutation = KeccakPermutation;
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
|
||||||
use crate::hash::hashing::SPONGE_WIDTH;
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
@ -16,12 +16,12 @@ use crate::plonk::config::{AlgebraicHasher, Hasher};
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
#[serde(bound = "")]
|
#[serde(bound = "")]
|
||||||
pub struct MerkleProof<F: RichField, H: Hasher<F>> {
|
pub struct MerkleProof<F: RichField, HC: HashConfig, H: Hasher<F, HC>> {
|
||||||
/// The Merkle digest of each sibling subtree, staying from the bottommost layer.
|
/// The Merkle digest of each sibling subtree, staying from the bottommost layer.
|
||||||
pub siblings: Vec<H::Hash>,
|
pub siblings: Vec<H::Hash>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField, H: Hasher<F>> MerkleProof<F, H> {
|
impl<F: RichField, HC: HashConfig, H: Hasher<F, HC>> MerkleProof<F, HC, H> {
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.siblings.len()
|
self.siblings.len()
|
||||||
}
|
}
|
||||||
@ -39,24 +39,30 @@ pub struct MerkleProofTarget {
|
|||||||
|
|
||||||
/// Verifies that the given leaf data is present at the given index in the Merkle tree with the
|
/// Verifies that the given leaf data is present at the given index in the Merkle tree with the
|
||||||
/// given root.
|
/// given root.
|
||||||
pub fn verify_merkle_proof<F: RichField, H: Hasher<F>>(
|
pub fn verify_merkle_proof<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
leaf_data: Vec<F>,
|
leaf_data: Vec<F>,
|
||||||
leaf_index: usize,
|
leaf_index: usize,
|
||||||
merkle_root: H::Hash,
|
merkle_root: H::Hash,
|
||||||
proof: &MerkleProof<F, H>,
|
proof: &MerkleProof<F, HC, H>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let merkle_cap = MerkleCap(vec![merkle_root]);
|
let merkle_cap = MerkleCap(vec![merkle_root]);
|
||||||
verify_merkle_proof_to_cap(leaf_data, leaf_index, &merkle_cap, proof)
|
verify_merkle_proof_to_cap(leaf_data, leaf_index, &merkle_cap, proof)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verifies that the given leaf data is present at the given index in the Merkle tree with the
|
/// Verifies that the given leaf data is present at the given index in the Merkle tree with the
|
||||||
/// given cap.
|
/// given cap.
|
||||||
pub fn verify_merkle_proof_to_cap<F: RichField, H: Hasher<F>>(
|
pub fn verify_merkle_proof_to_cap<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
leaf_data: Vec<F>,
|
leaf_data: Vec<F>,
|
||||||
leaf_index: usize,
|
leaf_index: usize,
|
||||||
merkle_cap: &MerkleCap<F, H>,
|
merkle_cap: &MerkleCap<F, HC, H>,
|
||||||
proof: &MerkleProof<F, H>,
|
proof: &MerkleProof<F, HC, H>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let mut index = leaf_index;
|
let mut index = leaf_index;
|
||||||
let mut current_digest = H::hash_or_noop(&leaf_data);
|
let mut current_digest = H::hash_or_noop(&leaf_data);
|
||||||
for &sibling_digest in proof.siblings.iter() {
|
for &sibling_digest in proof.siblings.iter() {
|
||||||
@ -79,28 +85,32 @@ pub fn verify_merkle_proof_to_cap<F: RichField, H: Hasher<F>>(
|
|||||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||||
/// Verifies that the given leaf data is present at the given index in the Merkle tree with the
|
/// Verifies that the given leaf data is present at the given index in the Merkle tree with the
|
||||||
/// given root. The index is given by its little-endian bits.
|
/// given root. The index is given by its little-endian bits.
|
||||||
pub fn verify_merkle_proof<H: AlgebraicHasher<F>>(
|
pub fn verify_merkle_proof<HC: HashConfig, H: AlgebraicHasher<F, HC>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
leaf_data: Vec<Target>,
|
leaf_data: Vec<Target>,
|
||||||
leaf_index_bits: &[BoolTarget],
|
leaf_index_bits: &[BoolTarget],
|
||||||
merkle_root: HashOutTarget,
|
merkle_root: HashOutTarget,
|
||||||
proof: &MerkleProofTarget,
|
proof: &MerkleProofTarget,
|
||||||
) {
|
) where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let merkle_cap = MerkleCapTarget(vec![merkle_root]);
|
let merkle_cap = MerkleCapTarget(vec![merkle_root]);
|
||||||
self.verify_merkle_proof_to_cap::<H>(leaf_data, leaf_index_bits, &merkle_cap, proof);
|
self.verify_merkle_proof_to_cap::<HC, H>(leaf_data, leaf_index_bits, &merkle_cap, proof);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verifies that the given leaf data is present at the given index in the Merkle tree with the
|
/// Verifies that the given leaf data is present at the given index in the Merkle tree with the
|
||||||
/// given cap. The index is given by its little-endian bits.
|
/// given cap. The index is given by its little-endian bits.
|
||||||
pub fn verify_merkle_proof_to_cap<H: AlgebraicHasher<F>>(
|
pub fn verify_merkle_proof_to_cap<HC: HashConfig, H: AlgebraicHasher<F, HC>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
leaf_data: Vec<Target>,
|
leaf_data: Vec<Target>,
|
||||||
leaf_index_bits: &[BoolTarget],
|
leaf_index_bits: &[BoolTarget],
|
||||||
merkle_cap: &MerkleCapTarget,
|
merkle_cap: &MerkleCapTarget,
|
||||||
proof: &MerkleProofTarget,
|
proof: &MerkleProofTarget,
|
||||||
) {
|
) where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let cap_index = self.le_sum(leaf_index_bits[proof.siblings.len()..].iter().copied());
|
let cap_index = self.le_sum(leaf_index_bits[proof.siblings.len()..].iter().copied());
|
||||||
self.verify_merkle_proof_to_cap_with_cap_index::<H>(
|
self.verify_merkle_proof_to_cap_with_cap_index::<HC, H>(
|
||||||
leaf_data,
|
leaf_data,
|
||||||
leaf_index_bits,
|
leaf_index_bits,
|
||||||
cap_index,
|
cap_index,
|
||||||
@ -111,22 +121,27 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
|
|
||||||
/// Same as `verify_merkle_proof_to_cap`, except with the final "cap index" as separate parameter,
|
/// Same as `verify_merkle_proof_to_cap`, except with the final "cap index" as separate parameter,
|
||||||
/// rather than being contained in `leaf_index_bits`.
|
/// rather than being contained in `leaf_index_bits`.
|
||||||
pub(crate) fn verify_merkle_proof_to_cap_with_cap_index<H: AlgebraicHasher<F>>(
|
pub(crate) fn verify_merkle_proof_to_cap_with_cap_index<
|
||||||
|
HC: HashConfig,
|
||||||
|
H: AlgebraicHasher<F, HC>,
|
||||||
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
leaf_data: Vec<Target>,
|
leaf_data: Vec<Target>,
|
||||||
leaf_index_bits: &[BoolTarget],
|
leaf_index_bits: &[BoolTarget],
|
||||||
cap_index: Target,
|
cap_index: Target,
|
||||||
merkle_cap: &MerkleCapTarget,
|
merkle_cap: &MerkleCapTarget,
|
||||||
proof: &MerkleProofTarget,
|
proof: &MerkleProofTarget,
|
||||||
) {
|
) where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let zero = self.zero();
|
let zero = self.zero();
|
||||||
let mut state: HashOutTarget = self.hash_or_noop::<H>(leaf_data);
|
let mut state: HashOutTarget = self.hash_or_noop::<HC, H>(leaf_data);
|
||||||
|
|
||||||
for (&bit, &sibling) in leaf_index_bits.iter().zip(&proof.siblings) {
|
for (&bit, &sibling) in leaf_index_bits.iter().zip(&proof.siblings) {
|
||||||
let mut perm_inputs = [zero; SPONGE_WIDTH];
|
let mut perm_inputs = [zero; HC::WIDTH];
|
||||||
perm_inputs[..4].copy_from_slice(&state.elements);
|
perm_inputs[..4].copy_from_slice(&state.elements);
|
||||||
perm_inputs[4..8].copy_from_slice(&sibling.elements);
|
perm_inputs[4..8].copy_from_slice(&sibling.elements);
|
||||||
let perm_outs = self.permute_swapped::<H>(perm_inputs, bit);
|
let perm_outs = self.permute_swapped::<HC, H>(perm_inputs, bit);
|
||||||
let hash_outs = perm_outs[0..4].try_into().unwrap();
|
let hash_outs = perm_outs[0..4].try_into().unwrap();
|
||||||
state = HashOutTarget {
|
state = HashOutTarget {
|
||||||
elements: hash_outs,
|
elements: hash_outs,
|
||||||
@ -172,7 +187,7 @@ mod tests {
|
|||||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use crate::plonk::verifier::verify;
|
use crate::plonk::verifier::verify;
|
||||||
|
|
||||||
fn random_data<F: Field>(n: usize, k: usize) -> Vec<Vec<F>> {
|
fn random_data<F: Field>(n: usize, k: usize) -> Vec<Vec<F>> {
|
||||||
@ -183,7 +198,9 @@ mod tests {
|
|||||||
fn test_recursive_merkle_proof() -> Result<()> {
|
fn test_recursive_merkle_proof() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::new();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
@ -192,7 +209,9 @@ mod tests {
|
|||||||
let n = 1 << log_n;
|
let n = 1 << log_n;
|
||||||
let cap_height = 1;
|
let cap_height = 1;
|
||||||
let leaves = random_data::<F>(n, 7);
|
let leaves = random_data::<F>(n, 7);
|
||||||
let tree = MerkleTree::<F, <C as GenericConfig<D>>::Hasher>::new(leaves, cap_height);
|
let tree = MerkleTree::<F, HCO, <C as GenericConfig<HCO, HCI, D>>::Hasher>::new(
|
||||||
|
leaves, cap_height,
|
||||||
|
);
|
||||||
let i: usize = OsRng.gen_range(0..n);
|
let i: usize = OsRng.gen_range(0..n);
|
||||||
let proof = tree.prove(i);
|
let proof = tree.prove(i);
|
||||||
|
|
||||||
@ -214,11 +233,11 @@ mod tests {
|
|||||||
pw.set_target(data[j], tree.leaves[i][j]);
|
pw.set_target(data[j], tree.leaves[i][j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.verify_merkle_proof_to_cap::<<C as GenericConfig<D>>::InnerHasher>(
|
builder.verify_merkle_proof_to_cap::<HCI, <C as GenericConfig<HCO, HCI, D>>::InnerHasher>(
|
||||||
data, &i_bits, &cap_t, &proof_t,
|
data, &i_bits, &cap_t, &proof_t,
|
||||||
);
|
);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
verify(proof, &data.verifier_only, &data.common)
|
verify(proof, &data.verifier_only, &data.common)
|
||||||
|
|||||||
@ -6,6 +6,7 @@ use plonky2_maybe_rayon::*;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_proofs::MerkleProof;
|
use crate::hash::merkle_proofs::MerkleProof;
|
||||||
use crate::plonk::config::{GenericHashOut, Hasher};
|
use crate::plonk::config::{GenericHashOut, Hasher};
|
||||||
use crate::util::log2_strict;
|
use crate::util::log2_strict;
|
||||||
@ -15,9 +16,9 @@ use crate::util::log2_strict;
|
|||||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
#[serde(bound = "")]
|
#[serde(bound = "")]
|
||||||
// TODO: Change H to GenericHashOut<F>, since this only cares about the hash, not the hasher.
|
// TODO: Change H to GenericHashOut<F>, since this only cares about the hash, not the hasher.
|
||||||
pub struct MerkleCap<F: RichField, H: Hasher<F>>(pub Vec<H::Hash>);
|
pub struct MerkleCap<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(pub Vec<H::Hash>);
|
||||||
|
|
||||||
impl<F: RichField, H: Hasher<F>> MerkleCap<F, H> {
|
impl<F: RichField, HC: HashConfig, H: Hasher<F, HC>> MerkleCap<F, HC, H> {
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
@ -36,7 +37,7 @@ impl<F: RichField, H: Hasher<F>> MerkleCap<F, H> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MerkleTree<F: RichField, H: Hasher<F>> {
|
pub struct MerkleTree<F: RichField, HC: HashConfig, H: Hasher<F, HC>> {
|
||||||
/// The data in the leaves of the Merkle tree.
|
/// The data in the leaves of the Merkle tree.
|
||||||
pub leaves: Vec<Vec<F>>,
|
pub leaves: Vec<Vec<F>>,
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ pub struct MerkleTree<F: RichField, H: Hasher<F>> {
|
|||||||
pub digests: Vec<H::Hash>,
|
pub digests: Vec<H::Hash>,
|
||||||
|
|
||||||
/// The Merkle cap.
|
/// The Merkle cap.
|
||||||
pub cap: MerkleCap<F, H>,
|
pub cap: MerkleCap<F, HC, H>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capacity_up_to_mut<T>(v: &mut Vec<T>, len: usize) -> &mut [MaybeUninit<T>] {
|
fn capacity_up_to_mut<T>(v: &mut Vec<T>, len: usize) -> &mut [MaybeUninit<T>] {
|
||||||
@ -66,10 +67,13 @@ fn capacity_up_to_mut<T>(v: &mut Vec<T>, len: usize) -> &mut [MaybeUninit<T>] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_subtree<F: RichField, H: Hasher<F>>(
|
fn fill_subtree<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
digests_buf: &mut [MaybeUninit<H::Hash>],
|
digests_buf: &mut [MaybeUninit<H::Hash>],
|
||||||
leaves: &[Vec<F>],
|
leaves: &[Vec<F>],
|
||||||
) -> H::Hash {
|
) -> H::Hash
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
assert_eq!(leaves.len(), digests_buf.len() / 2 + 1);
|
assert_eq!(leaves.len(), digests_buf.len() / 2 + 1);
|
||||||
if digests_buf.is_empty() {
|
if digests_buf.is_empty() {
|
||||||
H::hash_or_noop(&leaves[0])
|
H::hash_or_noop(&leaves[0])
|
||||||
@ -85,8 +89,8 @@ fn fill_subtree<F: RichField, H: Hasher<F>>(
|
|||||||
let (left_leaves, right_leaves) = leaves.split_at(leaves.len() / 2);
|
let (left_leaves, right_leaves) = leaves.split_at(leaves.len() / 2);
|
||||||
|
|
||||||
let (left_digest, right_digest) = plonky2_maybe_rayon::join(
|
let (left_digest, right_digest) = plonky2_maybe_rayon::join(
|
||||||
|| fill_subtree::<F, H>(left_digests_buf, left_leaves),
|
|| fill_subtree::<F, HC, H>(left_digests_buf, left_leaves),
|
||||||
|| fill_subtree::<F, H>(right_digests_buf, right_leaves),
|
|| fill_subtree::<F, HC, H>(right_digests_buf, right_leaves),
|
||||||
);
|
);
|
||||||
|
|
||||||
left_digest_mem.write(left_digest);
|
left_digest_mem.write(left_digest);
|
||||||
@ -95,12 +99,14 @@ fn fill_subtree<F: RichField, H: Hasher<F>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_digests_buf<F: RichField, H: Hasher<F>>(
|
fn fill_digests_buf<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
digests_buf: &mut [MaybeUninit<H::Hash>],
|
digests_buf: &mut [MaybeUninit<H::Hash>],
|
||||||
cap_buf: &mut [MaybeUninit<H::Hash>],
|
cap_buf: &mut [MaybeUninit<H::Hash>],
|
||||||
leaves: &[Vec<F>],
|
leaves: &[Vec<F>],
|
||||||
cap_height: usize,
|
cap_height: usize,
|
||||||
) {
|
) where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
// Special case of a tree that's all cap. The usual case will panic because we'll try to split
|
// Special case of a tree that's all cap. The usual case will panic because we'll try to split
|
||||||
// an empty slice into chunks of `0`. (We would not need this if there was a way to split into
|
// an empty slice into chunks of `0`. (We would not need this if there was a way to split into
|
||||||
// `blah` chunks as opposed to chunks _of_ `blah`.)
|
// `blah` chunks as opposed to chunks _of_ `blah`.)
|
||||||
@ -126,12 +132,15 @@ fn fill_digests_buf<F: RichField, H: Hasher<F>>(
|
|||||||
// We have `1 << cap_height` sub-trees, one for each entry in `cap`. They are totally
|
// We have `1 << cap_height` sub-trees, one for each entry in `cap`. They are totally
|
||||||
// independent, so we schedule one task for each. `digests_buf` and `leaves` are split
|
// independent, so we schedule one task for each. `digests_buf` and `leaves` are split
|
||||||
// into `1 << cap_height` slices, one for each sub-tree.
|
// into `1 << cap_height` slices, one for each sub-tree.
|
||||||
subtree_cap.write(fill_subtree::<F, H>(subtree_digests, subtree_leaves));
|
subtree_cap.write(fill_subtree::<F, HC, H>(subtree_digests, subtree_leaves));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField, H: Hasher<F>> MerkleTree<F, H> {
|
impl<F: RichField, HC: HashConfig, H: Hasher<F, HC>> MerkleTree<F, HC, H>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
pub fn new(leaves: Vec<Vec<F>>, cap_height: usize) -> Self {
|
pub fn new(leaves: Vec<Vec<F>>, cap_height: usize) -> Self {
|
||||||
let log2_leaves_len = log2_strict(leaves.len());
|
let log2_leaves_len = log2_strict(leaves.len());
|
||||||
assert!(
|
assert!(
|
||||||
@ -149,7 +158,7 @@ impl<F: RichField, H: Hasher<F>> MerkleTree<F, H> {
|
|||||||
|
|
||||||
let digests_buf = capacity_up_to_mut(&mut digests, num_digests);
|
let digests_buf = capacity_up_to_mut(&mut digests, num_digests);
|
||||||
let cap_buf = capacity_up_to_mut(&mut cap, len_cap);
|
let cap_buf = capacity_up_to_mut(&mut cap, len_cap);
|
||||||
fill_digests_buf::<F, H>(digests_buf, cap_buf, &leaves[..], cap_height);
|
fill_digests_buf::<F, HC, H>(digests_buf, cap_buf, &leaves[..], cap_height);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// SAFETY: `fill_digests_buf` and `cap` initialized the spare capacity up to
|
// SAFETY: `fill_digests_buf` and `cap` initialized the spare capacity up to
|
||||||
@ -170,7 +179,7 @@ impl<F: RichField, H: Hasher<F>> MerkleTree<F, H> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a Merkle proof from a leaf index.
|
/// Create a Merkle proof from a leaf index.
|
||||||
pub fn prove(&self, leaf_index: usize) -> MerkleProof<F, H> {
|
pub fn prove(&self, leaf_index: usize) -> MerkleProof<F, HC, H> {
|
||||||
let cap_height = log2_strict(self.cap.len());
|
let cap_height = log2_strict(self.cap.len());
|
||||||
let num_layers = log2_strict(self.leaves.len()) - cap_height;
|
let num_layers = log2_strict(self.leaves.len()) - cap_height;
|
||||||
debug_assert_eq!(leaf_index >> (cap_height + num_layers), 0);
|
debug_assert_eq!(leaf_index >> (cap_height + num_layers), 0);
|
||||||
@ -214,7 +223,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
use crate::hash::merkle_proofs::verify_merkle_proof_to_cap;
|
use crate::hash::merkle_proofs::verify_merkle_proof_to_cap;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
fn random_data<F: RichField>(n: usize, k: usize) -> Vec<Vec<F>> {
|
fn random_data<F: RichField>(n: usize, k: usize) -> Vec<Vec<F>> {
|
||||||
(0..n).map(|_| F::rand_vec(k)).collect()
|
(0..n).map(|_| F::rand_vec(k)).collect()
|
||||||
@ -222,13 +231,18 @@ mod tests {
|
|||||||
|
|
||||||
fn verify_all_leaves<
|
fn verify_all_leaves<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
leaves: Vec<Vec<F>>,
|
leaves: Vec<Vec<F>>,
|
||||||
cap_height: usize,
|
cap_height: usize,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
let tree = MerkleTree::<F, C::Hasher>::new(leaves.clone(), cap_height);
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
|
let tree = MerkleTree::<F, HCO, C::Hasher>::new(leaves.clone(), cap_height);
|
||||||
for (i, leaf) in leaves.into_iter().enumerate() {
|
for (i, leaf) in leaves.into_iter().enumerate() {
|
||||||
let proof = tree.prove(i);
|
let proof = tree.prove(i);
|
||||||
verify_merkle_proof_to_cap(leaf, i, &tree.cap, &proof)?;
|
verify_merkle_proof_to_cap(leaf, i, &tree.cap, &proof)?;
|
||||||
@ -241,26 +255,32 @@ mod tests {
|
|||||||
fn test_cap_height_too_big() {
|
fn test_cap_height_too_big() {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
|
||||||
let log_n = 8;
|
let log_n = 8;
|
||||||
let cap_height = log_n + 1; // Should panic if `cap_height > len_n`.
|
let cap_height = log_n + 1; // Should panic if `cap_height > len_n`.
|
||||||
|
|
||||||
let leaves = random_data::<F>(1 << log_n, 7);
|
let leaves = random_data::<F>(1 << log_n, 7);
|
||||||
let _ = MerkleTree::<F, <C as GenericConfig<D>>::Hasher>::new(leaves, cap_height);
|
let _ = MerkleTree::<F, HCO, <C as GenericConfig<HCO, HCI, D>>::Hasher>::new(
|
||||||
|
leaves, cap_height,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cap_height_eq_log2_len() -> Result<()> {
|
fn test_cap_height_eq_log2_len() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
|
||||||
let log_n = 8;
|
let log_n = 8;
|
||||||
let n = 1 << log_n;
|
let n = 1 << log_n;
|
||||||
let leaves = random_data::<F>(n, 7);
|
let leaves = random_data::<F>(n, 7);
|
||||||
|
|
||||||
verify_all_leaves::<F, C, D>(leaves, log_n)?;
|
verify_all_leaves::<F, HCO, HCI, C, D>(leaves, log_n)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -269,13 +289,15 @@ mod tests {
|
|||||||
fn test_merkle_trees() -> Result<()> {
|
fn test_merkle_trees() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
|
||||||
let log_n = 8;
|
let log_n = 8;
|
||||||
let n = 1 << log_n;
|
let n = 1 << log_n;
|
||||||
let leaves = random_data::<F>(n, 7);
|
let leaves = random_data::<F>(n, 7);
|
||||||
|
|
||||||
verify_all_leaves::<F, C, D>(leaves, 1)?;
|
verify_all_leaves::<F, HCO, HCI, C, D>(leaves, 1)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,15 +5,16 @@ use hashbrown::HashMap;
|
|||||||
use num::Integer;
|
use num::Integer;
|
||||||
|
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_proofs::MerkleProof;
|
use crate::hash::merkle_proofs::MerkleProof;
|
||||||
use crate::plonk::config::Hasher;
|
use crate::plonk::config::Hasher;
|
||||||
|
|
||||||
/// Compress multiple Merkle proofs on the same tree by removing redundancy in the Merkle paths.
|
/// Compress multiple Merkle proofs on the same tree by removing redundancy in the Merkle paths.
|
||||||
pub(crate) fn compress_merkle_proofs<F: RichField, H: Hasher<F>>(
|
pub(crate) fn compress_merkle_proofs<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
cap_height: usize,
|
cap_height: usize,
|
||||||
indices: &[usize],
|
indices: &[usize],
|
||||||
proofs: &[MerkleProof<F, H>],
|
proofs: &[MerkleProof<F, HC, H>],
|
||||||
) -> Vec<MerkleProof<F, H>> {
|
) -> Vec<MerkleProof<F, HC, H>> {
|
||||||
assert!(!proofs.is_empty());
|
assert!(!proofs.is_empty());
|
||||||
let height = cap_height + proofs[0].siblings.len();
|
let height = cap_height + proofs[0].siblings.len();
|
||||||
let num_leaves = 1 << height;
|
let num_leaves = 1 << height;
|
||||||
@ -53,13 +54,16 @@ pub(crate) fn compress_merkle_proofs<F: RichField, H: Hasher<F>>(
|
|||||||
|
|
||||||
/// Decompress compressed Merkle proofs.
|
/// Decompress compressed Merkle proofs.
|
||||||
/// Note: The data and indices must be in the same order as in `compress_merkle_proofs`.
|
/// Note: The data and indices must be in the same order as in `compress_merkle_proofs`.
|
||||||
pub(crate) fn decompress_merkle_proofs<F: RichField, H: Hasher<F>>(
|
pub(crate) fn decompress_merkle_proofs<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
leaves_data: &[Vec<F>],
|
leaves_data: &[Vec<F>],
|
||||||
leaves_indices: &[usize],
|
leaves_indices: &[usize],
|
||||||
compressed_proofs: &[MerkleProof<F, H>],
|
compressed_proofs: &[MerkleProof<F, HC, H>],
|
||||||
height: usize,
|
height: usize,
|
||||||
cap_height: usize,
|
cap_height: usize,
|
||||||
) -> Vec<MerkleProof<F, H>> {
|
) -> Vec<MerkleProof<F, HC, H>>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let num_leaves = 1 << height;
|
let num_leaves = 1 << height;
|
||||||
let compressed_proofs = compressed_proofs.to_vec();
|
let compressed_proofs = compressed_proofs.to_vec();
|
||||||
let mut decompressed_proofs = Vec::with_capacity(compressed_proofs.len());
|
let mut decompressed_proofs = Vec::with_capacity(compressed_proofs.len());
|
||||||
@ -120,17 +124,22 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::field::types::Sample;
|
use crate::field::types::Sample;
|
||||||
use crate::hash::merkle_tree::MerkleTree;
|
use crate::hash::merkle_tree::MerkleTree;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_path_compression() {
|
fn test_path_compression() {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let h = 10;
|
let h = 10;
|
||||||
let cap_height = 3;
|
let cap_height = 3;
|
||||||
let vs = (0..1 << h).map(|_| vec![F::rand()]).collect::<Vec<_>>();
|
let vs = (0..1 << h).map(|_| vec![F::rand()]).collect::<Vec<_>>();
|
||||||
let mt = MerkleTree::<F, <C as GenericConfig<D>>::Hasher>::new(vs.clone(), cap_height);
|
let mt = MerkleTree::<F, HCO, <C as GenericConfig<HCO, HCI, D>>::Hasher>::new(
|
||||||
|
vs.clone(),
|
||||||
|
cap_height,
|
||||||
|
);
|
||||||
|
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let k = rng.gen_range(1..=1 << h);
|
let k = rng.gen_range(1..=1 << h);
|
||||||
|
|||||||
@ -12,11 +12,15 @@ use crate::gates::gate::Gate;
|
|||||||
use crate::gates::poseidon::PoseidonGate;
|
use crate::gates::poseidon::PoseidonGate;
|
||||||
use crate::gates::poseidon_mds::PoseidonMdsGate;
|
use crate::gates::poseidon_mds::PoseidonMdsGate;
|
||||||
use crate::hash::hash_types::{HashOut, RichField};
|
use crate::hash::hash_types::{HashOut, RichField};
|
||||||
use crate::hash::hashing::{compress, hash_n_to_hash_no_pad, PlonkyPermutation, SPONGE_WIDTH};
|
use crate::hash::hashing::{compress, hash_n_to_hash_no_pad, PlonkyPermutation};
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::config::{AlgebraicHasher, Hasher};
|
use crate::plonk::config::{AlgebraicHasher, Hasher, PoseidonHashConfig};
|
||||||
|
|
||||||
|
pub const SPONGE_RATE: usize = 8;
|
||||||
|
pub const SPONGE_CAPACITY: usize = 4;
|
||||||
|
pub const SPONGE_WIDTH: usize = SPONGE_RATE + SPONGE_CAPACITY;
|
||||||
|
|
||||||
// The number of full rounds and partial rounds is given by the
|
// The number of full rounds and partial rounds is given by the
|
||||||
// calc_round_numbers.py script. They happen to be the same for both
|
// calc_round_numbers.py script. They happen to be the same for both
|
||||||
@ -47,7 +51,7 @@ fn reduce_u160<F: PrimeField64>((n_lo, n_hi): (u128, u32)) -> F {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Note that these work for the Goldilocks field, but not necessarily others. See
|
/// Note that these work for the Goldilocks field, but not necessarily others. See
|
||||||
/// `generate_constants` about how these were generated. We include enough for a WIDTH of 12;
|
/// `generate_constants` about how these were generated. We include enough for a width of 12;
|
||||||
/// smaller widths just use a subset.
|
/// smaller widths just use a subset.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub const ALL_ROUND_CONSTANTS: [u64; MAX_WIDTH * N_ROUNDS] = [
|
pub const ALL_ROUND_CONSTANTS: [u64; MAX_WIDTH * N_ROUNDS] = [
|
||||||
@ -150,29 +154,28 @@ pub const ALL_ROUND_CONSTANTS: [u64; MAX_WIDTH * N_ROUNDS] = [
|
|||||||
0x4543d9df5476d3cb, 0xf172d73e004fc90d, 0xdfd1c4febcc81238, 0xbc8dfb627fe558fc,
|
0x4543d9df5476d3cb, 0xf172d73e004fc90d, 0xdfd1c4febcc81238, 0xbc8dfb627fe558fc,
|
||||||
];
|
];
|
||||||
|
|
||||||
const WIDTH: usize = SPONGE_WIDTH;
|
|
||||||
pub trait Poseidon: PrimeField64 {
|
pub trait Poseidon: PrimeField64 {
|
||||||
// Total number of round constants required: width of the input
|
// Total number of round constants required: width of the input
|
||||||
// times number of rounds.
|
// times number of rounds.
|
||||||
const N_ROUND_CONSTANTS: usize = WIDTH * N_ROUNDS;
|
const N_ROUND_CONSTANTS: usize = SPONGE_WIDTH * N_ROUNDS;
|
||||||
|
|
||||||
// The MDS matrix we use is C + D, where C is the circulant matrix whose first row is given by
|
// The MDS matrix we use is C + D, where C is the circulant matrix whose first row is given by
|
||||||
// `MDS_MATRIX_CIRC`, and D is the diagonal matrix whose diagonal is given by `MDS_MATRIX_DIAG`.
|
// `MDS_MATRIX_CIRC`, and D is the diagonal matrix whose diagonal is given by `MDS_MATRIX_DIAG`.
|
||||||
const MDS_MATRIX_CIRC: [u64; WIDTH];
|
const MDS_MATRIX_CIRC: [u64; SPONGE_WIDTH];
|
||||||
const MDS_MATRIX_DIAG: [u64; WIDTH];
|
const MDS_MATRIX_DIAG: [u64; SPONGE_WIDTH];
|
||||||
|
|
||||||
// Precomputed constants for the fast Poseidon calculation. See
|
// Precomputed constants for the fast Poseidon calculation. See
|
||||||
// the paper.
|
// the paper.
|
||||||
const FAST_PARTIAL_FIRST_ROUND_CONSTANT: [u64; WIDTH];
|
const FAST_PARTIAL_FIRST_ROUND_CONSTANT: [u64; SPONGE_WIDTH];
|
||||||
const FAST_PARTIAL_ROUND_CONSTANTS: [u64; N_PARTIAL_ROUNDS];
|
const FAST_PARTIAL_ROUND_CONSTANTS: [u64; N_PARTIAL_ROUNDS];
|
||||||
const FAST_PARTIAL_ROUND_VS: [[u64; WIDTH - 1]; N_PARTIAL_ROUNDS];
|
const FAST_PARTIAL_ROUND_VS: [[u64; SPONGE_WIDTH - 1]; N_PARTIAL_ROUNDS];
|
||||||
const FAST_PARTIAL_ROUND_W_HATS: [[u64; WIDTH - 1]; N_PARTIAL_ROUNDS];
|
const FAST_PARTIAL_ROUND_W_HATS: [[u64; SPONGE_WIDTH - 1]; N_PARTIAL_ROUNDS];
|
||||||
const FAST_PARTIAL_ROUND_INITIAL_MATRIX: [[u64; WIDTH - 1]; WIDTH - 1];
|
const FAST_PARTIAL_ROUND_INITIAL_MATRIX: [[u64; SPONGE_WIDTH - 1]; SPONGE_WIDTH - 1];
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[unroll_for_loops]
|
#[unroll_for_loops]
|
||||||
fn mds_row_shf(r: usize, v: &[u64; WIDTH]) -> u128 {
|
fn mds_row_shf(r: usize, v: &[u64; SPONGE_WIDTH]) -> u128 {
|
||||||
debug_assert!(r < WIDTH);
|
debug_assert!(r < SPONGE_WIDTH);
|
||||||
// The values of `MDS_MATRIX_CIRC` and `MDS_MATRIX_DIAG` are
|
// The values of `MDS_MATRIX_CIRC` and `MDS_MATRIX_DIAG` are
|
||||||
// known to be small, so we can accumulate all the products for
|
// known to be small, so we can accumulate all the products for
|
||||||
// each row and reduce just once at the end (done by the
|
// each row and reduce just once at the end (done by the
|
||||||
@ -184,8 +187,8 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
|
|
||||||
// This is a hacky way of fully unrolling the loop.
|
// This is a hacky way of fully unrolling the loop.
|
||||||
for i in 0..12 {
|
for i in 0..12 {
|
||||||
if i < WIDTH {
|
if i < SPONGE_WIDTH {
|
||||||
res += (v[(i + r) % WIDTH] as u128) * (Self::MDS_MATRIX_CIRC[i] as u128);
|
res += (v[(i + r) % SPONGE_WIDTH] as u128) * (Self::MDS_MATRIX_CIRC[i] as u128);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res += (v[r] as u128) * (Self::MDS_MATRIX_DIAG[r] as u128);
|
res += (v[r] as u128) * (Self::MDS_MATRIX_DIAG[r] as u128);
|
||||||
@ -196,13 +199,13 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
/// Same as `mds_row_shf` for field extensions of `Self`.
|
/// Same as `mds_row_shf` for field extensions of `Self`.
|
||||||
fn mds_row_shf_field<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
fn mds_row_shf_field<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
||||||
r: usize,
|
r: usize,
|
||||||
v: &[F; WIDTH],
|
v: &[F; SPONGE_WIDTH],
|
||||||
) -> F {
|
) -> F {
|
||||||
debug_assert!(r < WIDTH);
|
debug_assert!(r < SPONGE_WIDTH);
|
||||||
let mut res = F::ZERO;
|
let mut res = F::ZERO;
|
||||||
|
|
||||||
for i in 0..WIDTH {
|
for i in 0..SPONGE_WIDTH {
|
||||||
res += v[(i + r) % WIDTH] * F::from_canonical_u64(Self::MDS_MATRIX_CIRC[i]);
|
res += v[(i + r) % SPONGE_WIDTH] * F::from_canonical_u64(Self::MDS_MATRIX_CIRC[i]);
|
||||||
}
|
}
|
||||||
res += v[r] * F::from_canonical_u64(Self::MDS_MATRIX_DIAG[r]);
|
res += v[r] * F::from_canonical_u64(Self::MDS_MATRIX_DIAG[r]);
|
||||||
|
|
||||||
@ -213,17 +216,17 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
fn mds_row_shf_circuit<const D: usize>(
|
fn mds_row_shf_circuit<const D: usize>(
|
||||||
builder: &mut CircuitBuilder<Self, D>,
|
builder: &mut CircuitBuilder<Self, D>,
|
||||||
r: usize,
|
r: usize,
|
||||||
v: &[ExtensionTarget<D>; WIDTH],
|
v: &[ExtensionTarget<D>; SPONGE_WIDTH],
|
||||||
) -> ExtensionTarget<D>
|
) -> ExtensionTarget<D>
|
||||||
where
|
where
|
||||||
Self: RichField + Extendable<D>,
|
Self: RichField + Extendable<D>,
|
||||||
{
|
{
|
||||||
debug_assert!(r < WIDTH);
|
debug_assert!(r < SPONGE_WIDTH);
|
||||||
let mut res = builder.zero_extension();
|
let mut res = builder.zero_extension();
|
||||||
|
|
||||||
for i in 0..WIDTH {
|
for i in 0..SPONGE_WIDTH {
|
||||||
let c = Self::from_canonical_u64(<Self as Poseidon>::MDS_MATRIX_CIRC[i]);
|
let c = Self::from_canonical_u64(<Self as Poseidon>::MDS_MATRIX_CIRC[i]);
|
||||||
res = builder.mul_const_add_extension(c, v[(i + r) % WIDTH], res);
|
res = builder.mul_const_add_extension(c, v[(i + r) % SPONGE_WIDTH], res);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let c = Self::from_canonical_u64(<Self as Poseidon>::MDS_MATRIX_DIAG[r]);
|
let c = Self::from_canonical_u64(<Self as Poseidon>::MDS_MATRIX_DIAG[r]);
|
||||||
@ -235,17 +238,17 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[unroll_for_loops]
|
#[unroll_for_loops]
|
||||||
fn mds_layer(state_: &[Self; WIDTH]) -> [Self; WIDTH] {
|
fn mds_layer(state_: &[Self; SPONGE_WIDTH]) -> [Self; SPONGE_WIDTH] {
|
||||||
let mut result = [Self::ZERO; WIDTH];
|
let mut result = [Self::ZERO; SPONGE_WIDTH];
|
||||||
|
|
||||||
let mut state = [0u64; WIDTH];
|
let mut state = [0u64; SPONGE_WIDTH];
|
||||||
for r in 0..WIDTH {
|
for r in 0..SPONGE_WIDTH {
|
||||||
state[r] = state_[r].to_noncanonical_u64();
|
state[r] = state_[r].to_noncanonical_u64();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a hacky way of fully unrolling the loop.
|
// This is a hacky way of fully unrolling the loop.
|
||||||
for r in 0..12 {
|
for r in 0..12 {
|
||||||
if r < WIDTH {
|
if r < SPONGE_WIDTH {
|
||||||
let sum = Self::mds_row_shf(r, &state);
|
let sum = Self::mds_row_shf(r, &state);
|
||||||
let sum_lo = sum as u64;
|
let sum_lo = sum as u64;
|
||||||
let sum_hi = (sum >> 64) as u32;
|
let sum_hi = (sum >> 64) as u32;
|
||||||
@ -258,11 +261,11 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
|
|
||||||
/// Same as `mds_layer` for field extensions of `Self`.
|
/// Same as `mds_layer` for field extensions of `Self`.
|
||||||
fn mds_layer_field<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
fn mds_layer_field<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
||||||
state: &[F; WIDTH],
|
state: &[F; SPONGE_WIDTH],
|
||||||
) -> [F; WIDTH] {
|
) -> [F; SPONGE_WIDTH] {
|
||||||
let mut result = [F::ZERO; WIDTH];
|
let mut result = [F::ZERO; SPONGE_WIDTH];
|
||||||
|
|
||||||
for r in 0..WIDTH {
|
for r in 0..SPONGE_WIDTH {
|
||||||
result[r] = Self::mds_row_shf_field(r, state);
|
result[r] = Self::mds_row_shf_field(r, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,8 +275,8 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
/// Recursive version of `mds_layer`.
|
/// Recursive version of `mds_layer`.
|
||||||
fn mds_layer_circuit<const D: usize>(
|
fn mds_layer_circuit<const D: usize>(
|
||||||
builder: &mut CircuitBuilder<Self, D>,
|
builder: &mut CircuitBuilder<Self, D>,
|
||||||
state: &[ExtensionTarget<D>; WIDTH],
|
state: &[ExtensionTarget<D>; SPONGE_WIDTH],
|
||||||
) -> [ExtensionTarget<D>; WIDTH]
|
) -> [ExtensionTarget<D>; SPONGE_WIDTH]
|
||||||
where
|
where
|
||||||
Self: RichField + Extendable<D>,
|
Self: RichField + Extendable<D>,
|
||||||
{
|
{
|
||||||
@ -281,11 +284,11 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
let mds_gate = PoseidonMdsGate::<Self, D>::new();
|
let mds_gate = PoseidonMdsGate::<Self, D>::new();
|
||||||
if builder.config.num_routed_wires >= mds_gate.num_wires() {
|
if builder.config.num_routed_wires >= mds_gate.num_wires() {
|
||||||
let index = builder.add_gate(mds_gate, vec![]);
|
let index = builder.add_gate(mds_gate, vec![]);
|
||||||
for i in 0..WIDTH {
|
for i in 0..SPONGE_WIDTH {
|
||||||
let input_wire = PoseidonMdsGate::<Self, D>::wires_input(i);
|
let input_wire = PoseidonMdsGate::<Self, D>::wires_input(i);
|
||||||
builder.connect_extension(state[i], ExtensionTarget::from_range(index, input_wire));
|
builder.connect_extension(state[i], ExtensionTarget::from_range(index, input_wire));
|
||||||
}
|
}
|
||||||
(0..WIDTH)
|
(0..SPONGE_WIDTH)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let output_wire = PoseidonMdsGate::<Self, D>::wires_output(i);
|
let output_wire = PoseidonMdsGate::<Self, D>::wires_output(i);
|
||||||
ExtensionTarget::from_range(index, output_wire)
|
ExtensionTarget::from_range(index, output_wire)
|
||||||
@ -294,9 +297,9 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
.try_into()
|
.try_into()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
} else {
|
} else {
|
||||||
let mut result = [builder.zero_extension(); WIDTH];
|
let mut result = [builder.zero_extension(); SPONGE_WIDTH];
|
||||||
|
|
||||||
for r in 0..WIDTH {
|
for r in 0..SPONGE_WIDTH {
|
||||||
result[r] = Self::mds_row_shf_circuit(builder, r, state);
|
result[r] = Self::mds_row_shf_circuit(builder, r, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,10 +310,10 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[unroll_for_loops]
|
#[unroll_for_loops]
|
||||||
fn partial_first_constant_layer<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
fn partial_first_constant_layer<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
||||||
state: &mut [F; WIDTH],
|
state: &mut [F; SPONGE_WIDTH],
|
||||||
) {
|
) {
|
||||||
for i in 0..12 {
|
for i in 0..12 {
|
||||||
if i < WIDTH {
|
if i < SPONGE_WIDTH {
|
||||||
state[i] += F::from_canonical_u64(Self::FAST_PARTIAL_FIRST_ROUND_CONSTANT[i]);
|
state[i] += F::from_canonical_u64(Self::FAST_PARTIAL_FIRST_ROUND_CONSTANT[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,11 +322,11 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
/// Recursive version of `partial_first_constant_layer`.
|
/// Recursive version of `partial_first_constant_layer`.
|
||||||
fn partial_first_constant_layer_circuit<const D: usize>(
|
fn partial_first_constant_layer_circuit<const D: usize>(
|
||||||
builder: &mut CircuitBuilder<Self, D>,
|
builder: &mut CircuitBuilder<Self, D>,
|
||||||
state: &mut [ExtensionTarget<D>; WIDTH],
|
state: &mut [ExtensionTarget<D>; SPONGE_WIDTH],
|
||||||
) where
|
) where
|
||||||
Self: RichField + Extendable<D>,
|
Self: RichField + Extendable<D>,
|
||||||
{
|
{
|
||||||
for i in 0..WIDTH {
|
for i in 0..SPONGE_WIDTH {
|
||||||
let c = <Self as Poseidon>::FAST_PARTIAL_FIRST_ROUND_CONSTANT[i];
|
let c = <Self as Poseidon>::FAST_PARTIAL_FIRST_ROUND_CONSTANT[i];
|
||||||
let c = Self::Extension::from_canonical_u64(c);
|
let c = Self::Extension::from_canonical_u64(c);
|
||||||
let c = builder.constant_extension(c);
|
let c = builder.constant_extension(c);
|
||||||
@ -334,9 +337,9 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[unroll_for_loops]
|
#[unroll_for_loops]
|
||||||
fn mds_partial_layer_init<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
fn mds_partial_layer_init<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
||||||
state: &[F; WIDTH],
|
state: &[F; SPONGE_WIDTH],
|
||||||
) -> [F; WIDTH] {
|
) -> [F; SPONGE_WIDTH] {
|
||||||
let mut result = [F::ZERO; WIDTH];
|
let mut result = [F::ZERO; SPONGE_WIDTH];
|
||||||
|
|
||||||
// Initial matrix has first row/column = [1, 0, ..., 0];
|
// Initial matrix has first row/column = [1, 0, ..., 0];
|
||||||
|
|
||||||
@ -344,9 +347,9 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
result[0] = state[0];
|
result[0] = state[0];
|
||||||
|
|
||||||
for r in 1..12 {
|
for r in 1..12 {
|
||||||
if r < WIDTH {
|
if r < SPONGE_WIDTH {
|
||||||
for c in 1..12 {
|
for c in 1..12 {
|
||||||
if c < WIDTH {
|
if c < SPONGE_WIDTH {
|
||||||
// NB: FAST_PARTIAL_ROUND_INITIAL_MATRIX is stored in
|
// NB: FAST_PARTIAL_ROUND_INITIAL_MATRIX is stored in
|
||||||
// row-major order so that this dot product is cache
|
// row-major order so that this dot product is cache
|
||||||
// friendly.
|
// friendly.
|
||||||
@ -364,17 +367,17 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
/// Recursive version of `mds_partial_layer_init`.
|
/// Recursive version of `mds_partial_layer_init`.
|
||||||
fn mds_partial_layer_init_circuit<const D: usize>(
|
fn mds_partial_layer_init_circuit<const D: usize>(
|
||||||
builder: &mut CircuitBuilder<Self, D>,
|
builder: &mut CircuitBuilder<Self, D>,
|
||||||
state: &[ExtensionTarget<D>; WIDTH],
|
state: &[ExtensionTarget<D>; SPONGE_WIDTH],
|
||||||
) -> [ExtensionTarget<D>; WIDTH]
|
) -> [ExtensionTarget<D>; SPONGE_WIDTH]
|
||||||
where
|
where
|
||||||
Self: RichField + Extendable<D>,
|
Self: RichField + Extendable<D>,
|
||||||
{
|
{
|
||||||
let mut result = [builder.zero_extension(); WIDTH];
|
let mut result = [builder.zero_extension(); SPONGE_WIDTH];
|
||||||
|
|
||||||
result[0] = state[0];
|
result[0] = state[0];
|
||||||
|
|
||||||
for r in 1..WIDTH {
|
for r in 1..SPONGE_WIDTH {
|
||||||
for c in 1..WIDTH {
|
for c in 1..SPONGE_WIDTH {
|
||||||
let t = <Self as Poseidon>::FAST_PARTIAL_ROUND_INITIAL_MATRIX[r - 1][c - 1];
|
let t = <Self as Poseidon>::FAST_PARTIAL_ROUND_INITIAL_MATRIX[r - 1][c - 1];
|
||||||
let t = Self::Extension::from_canonical_u64(t);
|
let t = Self::Extension::from_canonical_u64(t);
|
||||||
let t = builder.constant_extension(t);
|
let t = builder.constant_extension(t);
|
||||||
@ -394,12 +397,12 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
/// (t-1)x(t-1) identity matrix.
|
/// (t-1)x(t-1) identity matrix.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[unroll_for_loops]
|
#[unroll_for_loops]
|
||||||
fn mds_partial_layer_fast(state: &[Self; WIDTH], r: usize) -> [Self; WIDTH] {
|
fn mds_partial_layer_fast(state: &[Self; SPONGE_WIDTH], r: usize) -> [Self; SPONGE_WIDTH] {
|
||||||
// Set d = [M_00 | w^] dot [state]
|
// Set d = [M_00 | w^] dot [state]
|
||||||
|
|
||||||
let mut d_sum = (0u128, 0u32); // u160 accumulator
|
let mut d_sum = (0u128, 0u32); // u160 accumulator
|
||||||
for i in 1..12 {
|
for i in 1..12 {
|
||||||
if i < WIDTH {
|
if i < SPONGE_WIDTH {
|
||||||
let t = Self::FAST_PARTIAL_ROUND_W_HATS[r][i - 1] as u128;
|
let t = Self::FAST_PARTIAL_ROUND_W_HATS[r][i - 1] as u128;
|
||||||
let si = state[i].to_noncanonical_u64() as u128;
|
let si = state[i].to_noncanonical_u64() as u128;
|
||||||
d_sum = add_u160_u128(d_sum, si * t);
|
d_sum = add_u160_u128(d_sum, si * t);
|
||||||
@ -411,10 +414,10 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
let d = reduce_u160::<Self>(d_sum);
|
let d = reduce_u160::<Self>(d_sum);
|
||||||
|
|
||||||
// result = [d] concat [state[0] * v + state[shift up by 1]]
|
// result = [d] concat [state[0] * v + state[shift up by 1]]
|
||||||
let mut result = [Self::ZERO; WIDTH];
|
let mut result = [Self::ZERO; SPONGE_WIDTH];
|
||||||
result[0] = d;
|
result[0] = d;
|
||||||
for i in 1..12 {
|
for i in 1..12 {
|
||||||
if i < WIDTH {
|
if i < SPONGE_WIDTH {
|
||||||
let t = Self::from_canonical_u64(Self::FAST_PARTIAL_ROUND_VS[r][i - 1]);
|
let t = Self::from_canonical_u64(Self::FAST_PARTIAL_ROUND_VS[r][i - 1]);
|
||||||
result[i] = state[i].multiply_accumulate(state[0], t);
|
result[i] = state[i].multiply_accumulate(state[0], t);
|
||||||
}
|
}
|
||||||
@ -424,21 +427,21 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
|
|
||||||
/// Same as `mds_partial_layer_fast` for field extensions of `Self`.
|
/// Same as `mds_partial_layer_fast` for field extensions of `Self`.
|
||||||
fn mds_partial_layer_fast_field<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
fn mds_partial_layer_fast_field<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
||||||
state: &[F; WIDTH],
|
state: &[F; SPONGE_WIDTH],
|
||||||
r: usize,
|
r: usize,
|
||||||
) -> [F; WIDTH] {
|
) -> [F; SPONGE_WIDTH] {
|
||||||
let s0 = state[0];
|
let s0 = state[0];
|
||||||
let mds0to0 = Self::MDS_MATRIX_CIRC[0] + Self::MDS_MATRIX_DIAG[0];
|
let mds0to0 = Self::MDS_MATRIX_CIRC[0] + Self::MDS_MATRIX_DIAG[0];
|
||||||
let mut d = s0 * F::from_canonical_u64(mds0to0);
|
let mut d = s0 * F::from_canonical_u64(mds0to0);
|
||||||
for i in 1..WIDTH {
|
for i in 1..SPONGE_WIDTH {
|
||||||
let t = F::from_canonical_u64(Self::FAST_PARTIAL_ROUND_W_HATS[r][i - 1]);
|
let t = F::from_canonical_u64(Self::FAST_PARTIAL_ROUND_W_HATS[r][i - 1]);
|
||||||
d += state[i] * t;
|
d += state[i] * t;
|
||||||
}
|
}
|
||||||
|
|
||||||
// result = [d] concat [state[0] * v + state[shift up by 1]]
|
// result = [d] concat [state[0] * v + state[shift up by 1]]
|
||||||
let mut result = [F::ZERO; WIDTH];
|
let mut result = [F::ZERO; SPONGE_WIDTH];
|
||||||
result[0] = d;
|
result[0] = d;
|
||||||
for i in 1..WIDTH {
|
for i in 1..SPONGE_WIDTH {
|
||||||
let t = F::from_canonical_u64(Self::FAST_PARTIAL_ROUND_VS[r][i - 1]);
|
let t = F::from_canonical_u64(Self::FAST_PARTIAL_ROUND_VS[r][i - 1]);
|
||||||
result[i] = state[0] * t + state[i];
|
result[i] = state[0] * t + state[i];
|
||||||
}
|
}
|
||||||
@ -448,25 +451,25 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
/// Recursive version of `mds_partial_layer_fast`.
|
/// Recursive version of `mds_partial_layer_fast`.
|
||||||
fn mds_partial_layer_fast_circuit<const D: usize>(
|
fn mds_partial_layer_fast_circuit<const D: usize>(
|
||||||
builder: &mut CircuitBuilder<Self, D>,
|
builder: &mut CircuitBuilder<Self, D>,
|
||||||
state: &[ExtensionTarget<D>; WIDTH],
|
state: &[ExtensionTarget<D>; SPONGE_WIDTH],
|
||||||
r: usize,
|
r: usize,
|
||||||
) -> [ExtensionTarget<D>; WIDTH]
|
) -> [ExtensionTarget<D>; SPONGE_WIDTH]
|
||||||
where
|
where
|
||||||
Self: RichField + Extendable<D>,
|
Self: RichField + Extendable<D>,
|
||||||
{
|
{
|
||||||
let s0 = state[0];
|
let s0 = state[0];
|
||||||
let mds0to0 = Self::MDS_MATRIX_CIRC[0] + Self::MDS_MATRIX_DIAG[0];
|
let mds0to0 = Self::MDS_MATRIX_CIRC[0] + Self::MDS_MATRIX_DIAG[0];
|
||||||
let mut d = builder.mul_const_extension(Self::from_canonical_u64(mds0to0), s0);
|
let mut d = builder.mul_const_extension(Self::from_canonical_u64(mds0to0), s0);
|
||||||
for i in 1..WIDTH {
|
for i in 1..SPONGE_WIDTH {
|
||||||
let t = <Self as Poseidon>::FAST_PARTIAL_ROUND_W_HATS[r][i - 1];
|
let t = <Self as Poseidon>::FAST_PARTIAL_ROUND_W_HATS[r][i - 1];
|
||||||
let t = Self::Extension::from_canonical_u64(t);
|
let t = Self::Extension::from_canonical_u64(t);
|
||||||
let t = builder.constant_extension(t);
|
let t = builder.constant_extension(t);
|
||||||
d = builder.mul_add_extension(t, state[i], d);
|
d = builder.mul_add_extension(t, state[i], d);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = [builder.zero_extension(); WIDTH];
|
let mut result = [builder.zero_extension(); SPONGE_WIDTH];
|
||||||
result[0] = d;
|
result[0] = d;
|
||||||
for i in 1..WIDTH {
|
for i in 1..SPONGE_WIDTH {
|
||||||
let t = <Self as Poseidon>::FAST_PARTIAL_ROUND_VS[r][i - 1];
|
let t = <Self as Poseidon>::FAST_PARTIAL_ROUND_VS[r][i - 1];
|
||||||
let t = Self::Extension::from_canonical_u64(t);
|
let t = Self::Extension::from_canonical_u64(t);
|
||||||
let t = builder.constant_extension(t);
|
let t = builder.constant_extension(t);
|
||||||
@ -477,10 +480,10 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[unroll_for_loops]
|
#[unroll_for_loops]
|
||||||
fn constant_layer(state: &mut [Self; WIDTH], round_ctr: usize) {
|
fn constant_layer(state: &mut [Self; SPONGE_WIDTH], round_ctr: usize) {
|
||||||
for i in 0..12 {
|
for i in 0..12 {
|
||||||
if i < WIDTH {
|
if i < SPONGE_WIDTH {
|
||||||
let round_constant = ALL_ROUND_CONSTANTS[i + WIDTH * round_ctr];
|
let round_constant = ALL_ROUND_CONSTANTS[i + SPONGE_WIDTH * round_ctr];
|
||||||
unsafe {
|
unsafe {
|
||||||
state[i] = state[i].add_canonical_u64(round_constant);
|
state[i] = state[i].add_canonical_u64(round_constant);
|
||||||
}
|
}
|
||||||
@ -490,24 +493,24 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
|
|
||||||
/// Same as `constant_layer` for field extensions of `Self`.
|
/// Same as `constant_layer` for field extensions of `Self`.
|
||||||
fn constant_layer_field<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
fn constant_layer_field<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
||||||
state: &mut [F; WIDTH],
|
state: &mut [F; SPONGE_WIDTH],
|
||||||
round_ctr: usize,
|
round_ctr: usize,
|
||||||
) {
|
) {
|
||||||
for i in 0..WIDTH {
|
for i in 0..SPONGE_WIDTH {
|
||||||
state[i] += F::from_canonical_u64(ALL_ROUND_CONSTANTS[i + WIDTH * round_ctr]);
|
state[i] += F::from_canonical_u64(ALL_ROUND_CONSTANTS[i + SPONGE_WIDTH * round_ctr]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursive version of `constant_layer`.
|
/// Recursive version of `constant_layer`.
|
||||||
fn constant_layer_circuit<const D: usize>(
|
fn constant_layer_circuit<const D: usize>(
|
||||||
builder: &mut CircuitBuilder<Self, D>,
|
builder: &mut CircuitBuilder<Self, D>,
|
||||||
state: &mut [ExtensionTarget<D>; WIDTH],
|
state: &mut [ExtensionTarget<D>; SPONGE_WIDTH],
|
||||||
round_ctr: usize,
|
round_ctr: usize,
|
||||||
) where
|
) where
|
||||||
Self: RichField + Extendable<D>,
|
Self: RichField + Extendable<D>,
|
||||||
{
|
{
|
||||||
for i in 0..WIDTH {
|
for i in 0..SPONGE_WIDTH {
|
||||||
let c = ALL_ROUND_CONSTANTS[i + WIDTH * round_ctr];
|
let c = ALL_ROUND_CONSTANTS[i + SPONGE_WIDTH * round_ctr];
|
||||||
let c = Self::Extension::from_canonical_u64(c);
|
let c = Self::Extension::from_canonical_u64(c);
|
||||||
let c = builder.constant_extension(c);
|
let c = builder.constant_extension(c);
|
||||||
state[i] = builder.add_extension(state[i], c);
|
state[i] = builder.add_extension(state[i], c);
|
||||||
@ -537,9 +540,9 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[unroll_for_loops]
|
#[unroll_for_loops]
|
||||||
fn sbox_layer(state: &mut [Self; WIDTH]) {
|
fn sbox_layer(state: &mut [Self; SPONGE_WIDTH]) {
|
||||||
for i in 0..12 {
|
for i in 0..12 {
|
||||||
if i < WIDTH {
|
if i < SPONGE_WIDTH {
|
||||||
state[i] = Self::sbox_monomial(state[i]);
|
state[i] = Self::sbox_monomial(state[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -547,9 +550,9 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
|
|
||||||
/// Same as `sbox_layer` for field extensions of `Self`.
|
/// Same as `sbox_layer` for field extensions of `Self`.
|
||||||
fn sbox_layer_field<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
fn sbox_layer_field<F: FieldExtension<D, BaseField = Self>, const D: usize>(
|
||||||
state: &mut [F; WIDTH],
|
state: &mut [F; SPONGE_WIDTH],
|
||||||
) {
|
) {
|
||||||
for i in 0..WIDTH {
|
for i in 0..SPONGE_WIDTH {
|
||||||
state[i] = Self::sbox_monomial(state[i]);
|
state[i] = Self::sbox_monomial(state[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -557,17 +560,17 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
/// Recursive version of `sbox_layer`.
|
/// Recursive version of `sbox_layer`.
|
||||||
fn sbox_layer_circuit<const D: usize>(
|
fn sbox_layer_circuit<const D: usize>(
|
||||||
builder: &mut CircuitBuilder<Self, D>,
|
builder: &mut CircuitBuilder<Self, D>,
|
||||||
state: &mut [ExtensionTarget<D>; WIDTH],
|
state: &mut [ExtensionTarget<D>; SPONGE_WIDTH],
|
||||||
) where
|
) where
|
||||||
Self: RichField + Extendable<D>,
|
Self: RichField + Extendable<D>,
|
||||||
{
|
{
|
||||||
for i in 0..WIDTH {
|
for i in 0..SPONGE_WIDTH {
|
||||||
state[i] = <Self as Poseidon>::sbox_monomial_circuit(builder, state[i]);
|
state[i] = <Self as Poseidon>::sbox_monomial_circuit(builder, state[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn full_rounds(state: &mut [Self; WIDTH], round_ctr: &mut usize) {
|
fn full_rounds(state: &mut [Self; SPONGE_WIDTH], round_ctr: &mut usize) {
|
||||||
for _ in 0..HALF_N_FULL_ROUNDS {
|
for _ in 0..HALF_N_FULL_ROUNDS {
|
||||||
Self::constant_layer(state, *round_ctr);
|
Self::constant_layer(state, *round_ctr);
|
||||||
Self::sbox_layer(state);
|
Self::sbox_layer(state);
|
||||||
@ -577,7 +580,7 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn partial_rounds(state: &mut [Self; WIDTH], round_ctr: &mut usize) {
|
fn partial_rounds(state: &mut [Self; SPONGE_WIDTH], round_ctr: &mut usize) {
|
||||||
Self::partial_first_constant_layer(state);
|
Self::partial_first_constant_layer(state);
|
||||||
*state = Self::mds_partial_layer_init(state);
|
*state = Self::mds_partial_layer_init(state);
|
||||||
|
|
||||||
@ -592,7 +595,7 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poseidon(input: [Self; WIDTH]) -> [Self; WIDTH] {
|
fn poseidon(input: [Self; SPONGE_WIDTH]) -> [Self; SPONGE_WIDTH] {
|
||||||
let mut state = input;
|
let mut state = input;
|
||||||
let mut round_ctr = 0;
|
let mut round_ctr = 0;
|
||||||
|
|
||||||
@ -606,7 +609,7 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
|
|
||||||
// For testing only, to ensure that various tricks are correct.
|
// For testing only, to ensure that various tricks are correct.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn partial_rounds_naive(state: &mut [Self; WIDTH], round_ctr: &mut usize) {
|
fn partial_rounds_naive(state: &mut [Self; SPONGE_WIDTH], round_ctr: &mut usize) {
|
||||||
for _ in 0..N_PARTIAL_ROUNDS {
|
for _ in 0..N_PARTIAL_ROUNDS {
|
||||||
Self::constant_layer(state, *round_ctr);
|
Self::constant_layer(state, *round_ctr);
|
||||||
state[0] = Self::sbox_monomial(state[0]);
|
state[0] = Self::sbox_monomial(state[0]);
|
||||||
@ -616,7 +619,7 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poseidon_naive(input: [Self; WIDTH]) -> [Self; WIDTH] {
|
fn poseidon_naive(input: [Self; SPONGE_WIDTH]) -> [Self; SPONGE_WIDTH] {
|
||||||
let mut state = input;
|
let mut state = input;
|
||||||
let mut round_ctr = 0;
|
let mut round_ctr = 0;
|
||||||
|
|
||||||
@ -630,7 +633,7 @@ pub trait Poseidon: PrimeField64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct PoseidonPermutation;
|
pub struct PoseidonPermutation;
|
||||||
impl<F: RichField> PlonkyPermutation<F> for PoseidonPermutation {
|
impl<F: RichField> PlonkyPermutation<F, PoseidonHashConfig> for PoseidonPermutation {
|
||||||
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] {
|
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] {
|
||||||
F::poseidon(input)
|
F::poseidon(input)
|
||||||
}
|
}
|
||||||
@ -639,21 +642,21 @@ impl<F: RichField> PlonkyPermutation<F> for PoseidonPermutation {
|
|||||||
/// Poseidon hash function.
|
/// Poseidon hash function.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct PoseidonHash;
|
pub struct PoseidonHash;
|
||||||
impl<F: RichField> Hasher<F> for PoseidonHash {
|
impl<F: RichField> Hasher<F, PoseidonHashConfig> for PoseidonHash {
|
||||||
const HASH_SIZE: usize = 4 * 8;
|
const HASH_SIZE: usize = 4 * 8;
|
||||||
type Hash = HashOut<F>;
|
type Hash = HashOut<F>;
|
||||||
type Permutation = PoseidonPermutation;
|
type Permutation = PoseidonPermutation;
|
||||||
|
|
||||||
fn hash_no_pad(input: &[F]) -> Self::Hash {
|
fn hash_no_pad(input: &[F]) -> Self::Hash {
|
||||||
hash_n_to_hash_no_pad::<F, Self::Permutation>(input)
|
hash_n_to_hash_no_pad::<F, PoseidonHashConfig, Self::Permutation>(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash {
|
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash {
|
||||||
compress::<F, Self::Permutation>(left, right)
|
compress::<F, PoseidonHashConfig, Self::Permutation>(left, right)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField> AlgebraicHasher<F> for PoseidonHash {
|
impl<F: RichField> AlgebraicHasher<F, PoseidonHashConfig> for PoseidonHash {
|
||||||
fn permute_swapped<const D: usize>(
|
fn permute_swapped<const D: usize>(
|
||||||
inputs: [Target; SPONGE_WIDTH],
|
inputs: [Target; SPONGE_WIDTH],
|
||||||
swap: BoolTarget,
|
swap: BoolTarget,
|
||||||
@ -688,8 +691,7 @@ impl<F: RichField> AlgebraicHasher<F> for PoseidonHash {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod test_helpers {
|
pub(crate) mod test_helpers {
|
||||||
use crate::field::types::Field;
|
use crate::field::types::Field;
|
||||||
use crate::hash::hashing::SPONGE_WIDTH;
|
use crate::hash::poseidon::{Poseidon, SPONGE_WIDTH};
|
||||||
use crate::hash::poseidon::Poseidon;
|
|
||||||
|
|
||||||
pub(crate) fn check_test_vectors<F: Field>(
|
pub(crate) fn check_test_vectors<F: Field>(
|
||||||
test_vectors: Vec<([u64; SPONGE_WIDTH], [u64; SPONGE_WIDTH])>,
|
test_vectors: Vec<([u64; SPONGE_WIDTH], [u64; SPONGE_WIDTH])>,
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use core::marker::PhantomData;
|
|||||||
|
|
||||||
use crate::field::extension::{Extendable, FieldExtension};
|
use crate::field::extension::{Extendable, FieldExtension};
|
||||||
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField};
|
||||||
use crate::hash::hashing::{PlonkyPermutation, SPONGE_RATE, SPONGE_WIDTH};
|
use crate::hash::hashing::{HashConfig, PlonkyPermutation};
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
@ -13,8 +13,11 @@ use crate::plonk::config::{AlgebraicHasher, GenericHashOut, Hasher};
|
|||||||
|
|
||||||
/// Observes prover messages, and generates challenges by hashing the transcript, a la Fiat-Shamir.
|
/// Observes prover messages, and generates challenges by hashing the transcript, a la Fiat-Shamir.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Challenger<F: RichField, H: Hasher<F>> {
|
pub struct Challenger<F: RichField, HC: HashConfig, H: Hasher<F, HC>>
|
||||||
pub(crate) sponge_state: [F; SPONGE_WIDTH],
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
|
pub(crate) sponge_state: [F; HC::WIDTH],
|
||||||
pub(crate) input_buffer: Vec<F>,
|
pub(crate) input_buffer: Vec<F>,
|
||||||
output_buffer: Vec<F>,
|
output_buffer: Vec<F>,
|
||||||
_phantom: PhantomData<H>,
|
_phantom: PhantomData<H>,
|
||||||
@ -28,23 +31,32 @@ pub struct Challenger<F: RichField, H: Hasher<F>> {
|
|||||||
/// design, but it can be viewed as a duplex sponge whose inputs are sometimes zero (when we perform
|
/// 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
|
/// 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.
|
/// absorptions). Thus the security properties of a duplex sponge still apply to our design.
|
||||||
impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
impl<F: RichField, HC: HashConfig, H: Hasher<F, HC>> Challenger<F, HC, H>
|
||||||
pub fn new() -> Challenger<F, H> {
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
|
pub fn new() -> Challenger<F, HC, H>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
Challenger {
|
Challenger {
|
||||||
sponge_state: [F::ZERO; SPONGE_WIDTH],
|
sponge_state: [F::ZERO; HC::WIDTH],
|
||||||
input_buffer: Vec::with_capacity(SPONGE_RATE),
|
input_buffer: Vec::with_capacity(HC::RATE),
|
||||||
output_buffer: Vec::with_capacity(SPONGE_RATE),
|
output_buffer: Vec::with_capacity(HC::RATE),
|
||||||
_phantom: Default::default(),
|
_phantom: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe_element(&mut self, element: F) {
|
pub fn observe_element(&mut self, element: F)
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
// Any buffered outputs are now invalid, since they wouldn't reflect this input.
|
// Any buffered outputs are now invalid, since they wouldn't reflect this input.
|
||||||
self.output_buffer.clear();
|
self.output_buffer.clear();
|
||||||
|
|
||||||
self.input_buffer.push(element);
|
self.input_buffer.push(element);
|
||||||
|
|
||||||
if self.input_buffer.len() == SPONGE_RATE {
|
if self.input_buffer.len() == HC::RATE {
|
||||||
self.duplexing();
|
self.duplexing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,11 +64,15 @@ impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
|||||||
pub fn observe_extension_element<const D: usize>(&mut self, element: &F::Extension)
|
pub fn observe_extension_element<const D: usize>(&mut self, element: &F::Extension)
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
{
|
{
|
||||||
self.observe_elements(&element.to_basefield_array());
|
self.observe_elements(&element.to_basefield_array());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe_elements(&mut self, elements: &[F]) {
|
pub fn observe_elements(&mut self, elements: &[F])
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
for &element in elements {
|
for &element in elements {
|
||||||
self.observe_element(element);
|
self.observe_element(element);
|
||||||
}
|
}
|
||||||
@ -65,23 +81,33 @@ impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
|||||||
pub fn observe_extension_elements<const D: usize>(&mut self, elements: &[F::Extension])
|
pub fn observe_extension_elements<const D: usize>(&mut self, elements: &[F::Extension])
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
{
|
{
|
||||||
for element in elements {
|
for element in elements {
|
||||||
self.observe_extension_element(element);
|
self.observe_extension_element(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe_hash<OH: Hasher<F>>(&mut self, hash: OH::Hash) {
|
pub fn observe_hash<OHC: HashConfig, OH: Hasher<F, OHC>>(&mut self, hash: OH::Hash)
|
||||||
|
where
|
||||||
|
[(); OHC::WIDTH]:,
|
||||||
|
{
|
||||||
self.observe_elements(&hash.to_vec())
|
self.observe_elements(&hash.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe_cap<OH: Hasher<F>>(&mut self, cap: &MerkleCap<F, OH>) {
|
pub fn observe_cap<OHC: HashConfig, OH: Hasher<F, OHC>>(&mut self, cap: &MerkleCap<F, OHC, OH>)
|
||||||
|
where
|
||||||
|
[(); OHC::WIDTH]:,
|
||||||
|
{
|
||||||
for &hash in &cap.0 {
|
for &hash in &cap.0 {
|
||||||
self.observe_hash::<OH>(hash);
|
self.observe_hash::<OHC, OH>(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_challenge(&mut self) -> F {
|
pub fn get_challenge(&mut self) -> F
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
// If we have buffered inputs, we must perform a duplexing so that the challenge will
|
// If we have buffered inputs, we must perform a duplexing so that the challenge will
|
||||||
// reflect them. Or if we've run out of outputs, we must perform a duplexing to get more.
|
// reflect them. Or if we've run out of outputs, we must perform a duplexing to get more.
|
||||||
if !self.input_buffer.is_empty() || self.output_buffer.is_empty() {
|
if !self.input_buffer.is_empty() || self.output_buffer.is_empty() {
|
||||||
@ -93,11 +119,17 @@ impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
|||||||
.expect("Output buffer should be non-empty")
|
.expect("Output buffer should be non-empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_n_challenges(&mut self, n: usize) -> Vec<F> {
|
pub fn get_n_challenges(&mut self, n: usize) -> Vec<F>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
(0..n).map(|_| self.get_challenge()).collect()
|
(0..n).map(|_| self.get_challenge()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_hash(&mut self) -> HashOut<F> {
|
pub fn get_hash(&mut self) -> HashOut<F>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
HashOut {
|
HashOut {
|
||||||
elements: [
|
elements: [
|
||||||
self.get_challenge(),
|
self.get_challenge(),
|
||||||
@ -111,6 +143,7 @@ impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
|||||||
pub fn get_extension_challenge<const D: usize>(&mut self) -> F::Extension
|
pub fn get_extension_challenge<const D: usize>(&mut self) -> F::Extension
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
{
|
{
|
||||||
let mut arr = [F::ZERO; D];
|
let mut arr = [F::ZERO; D];
|
||||||
arr.copy_from_slice(&self.get_n_challenges(D));
|
arr.copy_from_slice(&self.get_n_challenges(D));
|
||||||
@ -120,6 +153,7 @@ impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
|||||||
pub fn get_n_extension_challenges<const D: usize>(&mut self, n: usize) -> Vec<F::Extension>
|
pub fn get_n_extension_challenges<const D: usize>(&mut self, n: usize) -> Vec<F::Extension>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
{
|
{
|
||||||
(0..n)
|
(0..n)
|
||||||
.map(|_| self.get_extension_challenge::<D>())
|
.map(|_| self.get_extension_challenge::<D>())
|
||||||
@ -128,8 +162,11 @@ impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
|||||||
|
|
||||||
/// Absorb any buffered inputs. After calling this, the input buffer will be empty, and the
|
/// Absorb any buffered inputs. After calling this, the input buffer will be empty, and the
|
||||||
/// output buffer will be full.
|
/// output buffer will be full.
|
||||||
fn duplexing(&mut self) {
|
fn duplexing(&mut self)
|
||||||
assert!(self.input_buffer.len() <= SPONGE_RATE);
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
|
assert!(self.input_buffer.len() <= HC::RATE);
|
||||||
|
|
||||||
// Overwrite the first r elements with the inputs. This differs from a standard sponge,
|
// 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,
|
// where we would xor or add in the inputs. This is a well-known variant, though,
|
||||||
@ -143,10 +180,10 @@ impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
|||||||
|
|
||||||
self.output_buffer.clear();
|
self.output_buffer.clear();
|
||||||
self.output_buffer
|
self.output_buffer
|
||||||
.extend_from_slice(&self.sponge_state[0..SPONGE_RATE]);
|
.extend_from_slice(&self.sponge_state[0..HC::RATE]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compact(&mut self) -> [F; SPONGE_WIDTH] {
|
pub fn compact(&mut self) -> [F; HC::WIDTH] {
|
||||||
if !self.input_buffer.is_empty() {
|
if !self.input_buffer.is_empty() {
|
||||||
self.duplexing();
|
self.duplexing();
|
||||||
}
|
}
|
||||||
@ -155,37 +192,54 @@ impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField, H: AlgebraicHasher<F>> Default for Challenger<F, H> {
|
impl<F: RichField, HC: HashConfig, H: AlgebraicHasher<F, HC>> Default for Challenger<F, HC, H>
|
||||||
fn default() -> Self {
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
|
fn default() -> Self
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A recursive version of `Challenger`. The main difference is that `RecursiveChallenger`'s input
|
/// A recursive version of `Challenger`. The main difference is that `RecursiveChallenger`'s input
|
||||||
/// buffer can grow beyond `SPONGE_RATE`. This is so that `observe_element` etc do not need access
|
/// buffer can grow beyond `HC::RATE`. This is so that `observe_element` etc do not need access
|
||||||
/// to the `CircuitBuilder`.
|
/// to the `CircuitBuilder`.
|
||||||
pub struct RecursiveChallenger<F: RichField + Extendable<D>, H: AlgebraicHasher<F>, const D: usize>
|
pub struct RecursiveChallenger<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HC: HashConfig,
|
||||||
|
H: AlgebraicHasher<F, HC>,
|
||||||
|
const D: usize,
|
||||||
|
> where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
{
|
{
|
||||||
sponge_state: [Target; SPONGE_WIDTH],
|
sponge_state: [Target; HC::WIDTH],
|
||||||
input_buffer: Vec<Target>,
|
input_buffer: Vec<Target>,
|
||||||
output_buffer: Vec<Target>,
|
output_buffer: Vec<Target>,
|
||||||
__: PhantomData<(F, H)>,
|
__: PhantomData<(F, H)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, H: AlgebraicHasher<F>, const D: usize>
|
impl<F: RichField + Extendable<D>, HC: HashConfig, H: AlgebraicHasher<F, HC>, const D: usize>
|
||||||
RecursiveChallenger<F, H, D>
|
RecursiveChallenger<F, HC, H, D>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
{
|
{
|
||||||
pub fn new(builder: &mut CircuitBuilder<F, D>) -> Self {
|
pub fn new(builder: &mut CircuitBuilder<F, D>) -> Self
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let zero = builder.zero();
|
let zero = builder.zero();
|
||||||
Self {
|
Self {
|
||||||
sponge_state: [zero; SPONGE_WIDTH],
|
sponge_state: [zero; HC::WIDTH],
|
||||||
input_buffer: Vec::new(),
|
input_buffer: Vec::new(),
|
||||||
output_buffer: Vec::new(),
|
output_buffer: Vec::new(),
|
||||||
__: PhantomData,
|
__: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_state(sponge_state: [Target; SPONGE_WIDTH]) -> Self {
|
pub fn from_state(sponge_state: [Target; HC::WIDTH]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sponge_state,
|
sponge_state,
|
||||||
input_buffer: vec![],
|
input_buffer: vec![],
|
||||||
@ -194,46 +248,67 @@ impl<F: RichField + Extendable<D>, H: AlgebraicHasher<F>, const D: usize>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn observe_element(&mut self, target: Target) {
|
pub(crate) fn observe_element(&mut self, target: Target)
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
// Any buffered outputs are now invalid, since they wouldn't reflect this input.
|
// Any buffered outputs are now invalid, since they wouldn't reflect this input.
|
||||||
self.output_buffer.clear();
|
self.output_buffer.clear();
|
||||||
|
|
||||||
self.input_buffer.push(target);
|
self.input_buffer.push(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe_elements(&mut self, targets: &[Target]) {
|
pub fn observe_elements(&mut self, targets: &[Target])
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
for &target in targets {
|
for &target in targets {
|
||||||
self.observe_element(target);
|
self.observe_element(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe_hash(&mut self, hash: &HashOutTarget) {
|
pub fn observe_hash(&mut self, hash: &HashOutTarget)
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
self.observe_elements(&hash.elements)
|
self.observe_elements(&hash.elements)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe_cap(&mut self, cap: &MerkleCapTarget) {
|
pub fn observe_cap(&mut self, cap: &MerkleCapTarget)
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
for hash in &cap.0 {
|
for hash in &cap.0 {
|
||||||
self.observe_hash(hash)
|
self.observe_hash(hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe_extension_element(&mut self, element: ExtensionTarget<D>) {
|
pub fn observe_extension_element(&mut self, element: ExtensionTarget<D>)
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
self.observe_elements(&element.0);
|
self.observe_elements(&element.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe_extension_elements(&mut self, elements: &[ExtensionTarget<D>]) {
|
pub fn observe_extension_elements(&mut self, elements: &[ExtensionTarget<D>])
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
for &element in elements {
|
for &element in elements {
|
||||||
self.observe_extension_element(element);
|
self.observe_extension_element(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_challenge(&mut self, builder: &mut CircuitBuilder<F, D>) -> Target {
|
pub fn get_challenge(&mut self, builder: &mut CircuitBuilder<F, D>) -> Target
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
self.absorb_buffered_inputs(builder);
|
self.absorb_buffered_inputs(builder);
|
||||||
|
|
||||||
if self.output_buffer.is_empty() {
|
if self.output_buffer.is_empty() {
|
||||||
// Evaluate the permutation to produce `r` new outputs.
|
// Evaluate the permutation to produce `r` new outputs.
|
||||||
self.sponge_state = builder.permute::<H>(self.sponge_state);
|
self.sponge_state = builder.permute::<HC, H>(self.sponge_state);
|
||||||
self.output_buffer = self.sponge_state[0..SPONGE_RATE].to_vec();
|
self.output_buffer = self.sponge_state[0..HC::RATE].to_vec();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.output_buffer
|
self.output_buffer
|
||||||
@ -241,15 +316,17 @@ impl<F: RichField + Extendable<D>, H: AlgebraicHasher<F>, const D: usize>
|
|||||||
.expect("Output buffer should be non-empty")
|
.expect("Output buffer should be non-empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_n_challenges(
|
pub fn get_n_challenges(&mut self, builder: &mut CircuitBuilder<F, D>, n: usize) -> Vec<Target>
|
||||||
&mut self,
|
where
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
[(); HC::WIDTH]:,
|
||||||
n: usize,
|
{
|
||||||
) -> Vec<Target> {
|
|
||||||
(0..n).map(|_| self.get_challenge(builder)).collect()
|
(0..n).map(|_| self.get_challenge(builder)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_hash(&mut self, builder: &mut CircuitBuilder<F, D>) -> HashOutTarget {
|
pub fn get_hash(&mut self, builder: &mut CircuitBuilder<F, D>) -> HashOutTarget
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
HashOutTarget {
|
HashOutTarget {
|
||||||
elements: [
|
elements: [
|
||||||
self.get_challenge(builder),
|
self.get_challenge(builder),
|
||||||
@ -263,18 +340,24 @@ impl<F: RichField + Extendable<D>, H: AlgebraicHasher<F>, const D: usize>
|
|||||||
pub fn get_extension_challenge(
|
pub fn get_extension_challenge(
|
||||||
&mut self,
|
&mut self,
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
) -> ExtensionTarget<D> {
|
) -> ExtensionTarget<D>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
self.get_n_challenges(builder, D).try_into().unwrap()
|
self.get_n_challenges(builder, D).try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Absorb any buffered inputs. After calling this, the input buffer will be empty, and the
|
/// Absorb any buffered inputs. After calling this, the input buffer will be empty, and the
|
||||||
/// output buffer will be full.
|
/// output buffer will be full.
|
||||||
fn absorb_buffered_inputs(&mut self, builder: &mut CircuitBuilder<F, D>) {
|
fn absorb_buffered_inputs(&mut self, builder: &mut CircuitBuilder<F, D>)
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
if self.input_buffer.is_empty() {
|
if self.input_buffer.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for input_chunk in self.input_buffer.chunks(SPONGE_RATE) {
|
for input_chunk in self.input_buffer.chunks(HC::RATE) {
|
||||||
// Overwrite the first r elements with the inputs. This differs from a standard sponge,
|
// 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,
|
// where we would xor or add in the inputs. This is a well-known variant, though,
|
||||||
// sometimes called "overwrite mode".
|
// sometimes called "overwrite mode".
|
||||||
@ -283,15 +366,15 @@ impl<F: RichField + Extendable<D>, H: AlgebraicHasher<F>, const D: usize>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply the permutation.
|
// Apply the permutation.
|
||||||
self.sponge_state = builder.permute::<H>(self.sponge_state);
|
self.sponge_state = builder.permute::<HC, H>(self.sponge_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.output_buffer = self.sponge_state[0..SPONGE_RATE].to_vec();
|
self.output_buffer = self.sponge_state[0..HC::RATE].to_vec();
|
||||||
|
|
||||||
self.input_buffer.clear();
|
self.input_buffer.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compact(&mut self, builder: &mut CircuitBuilder<F, D>) -> [Target; SPONGE_WIDTH] {
|
pub fn compact(&mut self, builder: &mut CircuitBuilder<F, D>) -> [Target; HC::WIDTH] {
|
||||||
self.absorb_buffered_inputs(builder);
|
self.absorb_buffered_inputs(builder);
|
||||||
self.output_buffer.clear();
|
self.output_buffer.clear();
|
||||||
self.sponge_state
|
self.sponge_state
|
||||||
@ -307,14 +390,17 @@ mod tests {
|
|||||||
use crate::iop::witness::{PartialWitness, Witness};
|
use crate::iop::witness::{PartialWitness, Witness};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_duplicate_challenges() {
|
fn no_duplicate_challenges() {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
let mut challenger = Challenger::<F, <C as GenericConfig<D>>::InnerHasher>::new();
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
let mut challenger =
|
||||||
|
Challenger::<F, HCI, <C as GenericConfig<HCO, HCI, D>>::InnerHasher>::new();
|
||||||
let mut challenges = Vec::new();
|
let mut challenges = Vec::new();
|
||||||
|
|
||||||
for i in 1..10 {
|
for i in 1..10 {
|
||||||
@ -335,7 +421,9 @@ mod tests {
|
|||||||
fn test_consistency() {
|
fn test_consistency() {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
|
||||||
// These are mostly arbitrary, but we want to test some rounds with enough inputs/outputs to
|
// These are mostly arbitrary, but we want to test some rounds with enough inputs/outputs to
|
||||||
// trigger multiple absorptions/squeezes.
|
// trigger multiple absorptions/squeezes.
|
||||||
@ -348,7 +436,8 @@ mod tests {
|
|||||||
.map(|&n| F::rand_vec(n))
|
.map(|&n| F::rand_vec(n))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut challenger = Challenger::<F, <C as GenericConfig<D>>::InnerHasher>::new();
|
let mut challenger =
|
||||||
|
Challenger::<F, HCI, <C as GenericConfig<HCO, HCI, D>>::InnerHasher>::new();
|
||||||
let mut outputs_per_round: Vec<Vec<F>> = Vec::new();
|
let mut outputs_per_round: Vec<Vec<F>> = Vec::new();
|
||||||
for (r, inputs) in inputs_per_round.iter().enumerate() {
|
for (r, inputs) in inputs_per_round.iter().enumerate() {
|
||||||
challenger.observe_elements(inputs);
|
challenger.observe_elements(inputs);
|
||||||
@ -358,7 +447,9 @@ mod tests {
|
|||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
let mut recursive_challenger =
|
let mut recursive_challenger =
|
||||||
RecursiveChallenger::<F, <C as GenericConfig<D>>::InnerHasher, D>::new(&mut builder);
|
RecursiveChallenger::<F, HCI, <C as GenericConfig<HCO, HCI, D>>::InnerHasher, D>::new(
|
||||||
|
&mut builder,
|
||||||
|
);
|
||||||
let mut recursive_outputs_per_round: Vec<Vec<Target>> = Vec::new();
|
let mut recursive_outputs_per_round: Vec<Vec<Target>> = Vec::new();
|
||||||
for (r, inputs) in inputs_per_round.iter().enumerate() {
|
for (r, inputs) in inputs_per_round.iter().enumerate() {
|
||||||
recursive_challenger.observe_elements(&builder.constants(inputs));
|
recursive_challenger.observe_elements(&builder.constants(inputs));
|
||||||
@ -366,7 +457,7 @@ mod tests {
|
|||||||
recursive_challenger.get_n_challenges(&mut builder, num_outputs_per_round[r]),
|
recursive_challenger.get_n_challenges(&mut builder, num_outputs_per_round[r]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let circuit = builder.build::<C>();
|
let circuit = builder.build::<HCO, HCI, C>();
|
||||||
let inputs = PartialWitness::new();
|
let inputs = PartialWitness::new();
|
||||||
let witness = generate_partial_witness(inputs, &circuit.prover_only, &circuit.common);
|
let witness = generate_partial_witness(inputs, &circuit.prover_only, &circuit.common);
|
||||||
let recursive_output_values_per_round: Vec<Vec<F>> = recursive_outputs_per_round
|
let recursive_output_values_per_round: Vec<Vec<F>> = recursive_outputs_per_round
|
||||||
|
|||||||
@ -6,6 +6,7 @@ use core::marker::PhantomData;
|
|||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
use crate::field::types::Field;
|
use crate::field::types::Field;
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::wire::Wire;
|
use crate::iop::wire::Wire;
|
||||||
@ -18,11 +19,13 @@ use crate::plonk::config::GenericConfig;
|
|||||||
pub(crate) fn generate_partial_witness<
|
pub(crate) fn generate_partial_witness<
|
||||||
'a,
|
'a,
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
inputs: PartialWitness<F>,
|
inputs: PartialWitness<F>,
|
||||||
prover_data: &'a ProverOnlyCircuitData<F, C, D>,
|
prover_data: &'a ProverOnlyCircuitData<F, HCO, HCI, C, D>,
|
||||||
common_data: &'a CommonCircuitData<F, D>,
|
common_data: &'a CommonCircuitData<F, D>,
|
||||||
) -> PartitionWitness<'a, F> {
|
) -> PartitionWitness<'a, F> {
|
||||||
let config = &common_data.config;
|
let config = &common_data.config;
|
||||||
|
|||||||
@ -9,6 +9,7 @@ use crate::field::types::Field;
|
|||||||
use crate::fri::structure::{FriOpenings, FriOpeningsTarget};
|
use crate::fri::structure::{FriOpenings, FriOpeningsTarget};
|
||||||
use crate::fri::witness_util::set_fri_proof_target;
|
use crate::fri::witness_util::set_fri_proof_target;
|
||||||
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField};
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
@ -27,10 +28,10 @@ pub trait WitnessWrite<F: Field> {
|
|||||||
.for_each(|(&t, x)| self.set_target(t, x));
|
.for_each(|(&t, x)| self.set_target(t, x));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cap_target<H: AlgebraicHasher<F>>(
|
fn set_cap_target<HC: HashConfig, H: AlgebraicHasher<F, HC>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
ct: &MerkleCapTarget,
|
ct: &MerkleCapTarget,
|
||||||
value: &MerkleCap<F, H>,
|
value: &MerkleCap<F, HC, H>,
|
||||||
) where
|
) where
|
||||||
F: RichField,
|
F: RichField,
|
||||||
{
|
{
|
||||||
@ -71,13 +72,18 @@ pub trait WitnessWrite<F: Field> {
|
|||||||
|
|
||||||
/// Set the targets in a `ProofWithPublicInputsTarget` to their corresponding values in a
|
/// Set the targets in a `ProofWithPublicInputsTarget` to their corresponding values in a
|
||||||
/// `ProofWithPublicInputs`.
|
/// `ProofWithPublicInputs`.
|
||||||
fn set_proof_with_pis_target<C: GenericConfig<D, F = F>, const D: usize>(
|
fn set_proof_with_pis_target<
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
proof_with_pis_target: &ProofWithPublicInputsTarget<D>,
|
proof_with_pis_target: &ProofWithPublicInputsTarget<D>,
|
||||||
proof_with_pis: &ProofWithPublicInputs<F, C, D>,
|
proof_with_pis: &ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
) where
|
) where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
{
|
{
|
||||||
let ProofWithPublicInputs {
|
let ProofWithPublicInputs {
|
||||||
proof,
|
proof,
|
||||||
@ -97,13 +103,18 @@ pub trait WitnessWrite<F: Field> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set the targets in a `ProofTarget` to their corresponding values in a `Proof`.
|
/// Set the targets in a `ProofTarget` to their corresponding values in a `Proof`.
|
||||||
fn set_proof_target<C: GenericConfig<D, F = F>, const D: usize>(
|
fn set_proof_target<
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
proof_target: &ProofTarget<D>,
|
proof_target: &ProofTarget<D>,
|
||||||
proof: &Proof<F, C, D>,
|
proof: &Proof<F, HCO, HCI, C, D>,
|
||||||
) where
|
) where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
{
|
{
|
||||||
self.set_cap_target(&proof_target.wires_cap, &proof.wires_cap);
|
self.set_cap_target(&proof_target.wires_cap, &proof.wires_cap);
|
||||||
self.set_cap_target(
|
self.set_cap_target(
|
||||||
@ -136,13 +147,18 @@ pub trait WitnessWrite<F: Field> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_verifier_data_target<C: GenericConfig<D, F = F>, const D: usize>(
|
fn set_verifier_data_target<
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
vdt: &VerifierCircuitTarget,
|
vdt: &VerifierCircuitTarget,
|
||||||
vd: &VerifierOnlyCircuitData<C, D>,
|
vd: &VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
) where
|
) where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
{
|
{
|
||||||
self.set_cap_target(&vdt.constants_sigmas_cap, &vd.constants_sigmas_cap);
|
self.set_cap_target(&vdt.constants_sigmas_cap, &vd.constants_sigmas_cap);
|
||||||
self.set_hash_target(vdt.circuit_digest, vd.circuit_digest);
|
self.set_hash_target(vdt.circuit_digest, vd.circuit_digest);
|
||||||
@ -224,10 +240,14 @@ pub trait Witness<F: Field>: WitnessWrite<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_merkle_cap_target<H: Hasher<F>>(&self, cap_target: MerkleCapTarget) -> MerkleCap<F, H>
|
fn get_merkle_cap_target<HC, H: Hasher<F, HC>>(
|
||||||
|
&self,
|
||||||
|
cap_target: MerkleCapTarget,
|
||||||
|
) -> MerkleCap<F, HC, H>
|
||||||
where
|
where
|
||||||
F: RichField,
|
F: RichField,
|
||||||
H: AlgebraicHasher<F>,
|
HC: HashConfig,
|
||||||
|
H: AlgebraicHasher<F, HC>,
|
||||||
{
|
{
|
||||||
let cap = cap_target
|
let cap = cap_target
|
||||||
.0
|
.0
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
#![allow(clippy::needless_range_loop)]
|
#![allow(clippy::needless_range_loop)]
|
||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
#![feature(generic_const_exprs)]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|||||||
@ -28,6 +28,7 @@ use crate::gates::noop::NoopGate;
|
|||||||
use crate::gates::public_input::PublicInputGate;
|
use crate::gates::public_input::PublicInputGate;
|
||||||
use crate::gates::selectors::selector_polynomials;
|
use crate::gates::selectors::selector_polynomials;
|
||||||
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField};
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_proofs::MerkleProofTarget;
|
use crate::hash::merkle_proofs::MerkleProofTarget;
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
@ -434,19 +435,21 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constant_merkle_cap<H: Hasher<F, Hash = HashOut<F>>>(
|
pub fn constant_merkle_cap<HC: HashConfig, H: Hasher<F, HC, Hash = HashOut<F>>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
cap: &MerkleCap<F, H>,
|
cap: &MerkleCap<F, HC, H>,
|
||||||
) -> MerkleCapTarget {
|
) -> MerkleCapTarget {
|
||||||
MerkleCapTarget(cap.0.iter().map(|h| self.constant_hash(*h)).collect())
|
MerkleCapTarget(cap.0.iter().map(|h| self.constant_hash(*h)).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constant_verifier_data<C: GenericConfig<D, F = F>>(
|
pub fn constant_verifier_data<HCO, HCI, C: GenericConfig<HCO, HCI, D, F = F>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
verifier_data: &VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
) -> VerifierCircuitTarget
|
) -> VerifierCircuitTarget
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
{
|
{
|
||||||
VerifierCircuitTarget {
|
VerifierCircuitTarget {
|
||||||
constants_sigmas_cap: self.constant_merkle_cap(&verifier_data.constants_sigmas_cap),
|
constants_sigmas_cap: self.constant_merkle_cap(&verifier_data.constants_sigmas_cap),
|
||||||
@ -737,7 +740,13 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a "full circuit", with both prover and verifier data.
|
/// Builds a "full circuit", with both prover and verifier data.
|
||||||
pub fn build<C: GenericConfig<D, F = F>>(mut self) -> CircuitData<F, C, D> {
|
pub fn build<HCO: HashConfig, HCI: HashConfig, C: GenericConfig<HCO, HCI, D, F = F>>(
|
||||||
|
mut self,
|
||||||
|
) -> CircuitData<F, HCO, HCI, C, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let mut timing = TimingTree::new("preprocess", Level::Trace);
|
let mut timing = TimingTree::new("preprocess", Level::Trace);
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
@ -748,7 +757,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
// those hash wires match the claimed public inputs.
|
// those hash wires match the claimed public inputs.
|
||||||
let num_public_inputs = self.public_inputs.len();
|
let num_public_inputs = self.public_inputs.len();
|
||||||
let public_inputs_hash =
|
let public_inputs_hash =
|
||||||
self.hash_n_to_hash_no_pad::<C::InnerHasher>(self.public_inputs.clone());
|
self.hash_n_to_hash_no_pad::<HCI, C::InnerHasher>(self.public_inputs.clone());
|
||||||
let pi_gate = self.add_gate(PublicInputGate, vec![]);
|
let pi_gate = self.add_gate(PublicInputGate, vec![]);
|
||||||
for (&hash_part, wire) in public_inputs_hash
|
for (&hash_part, wire) in public_inputs_hash
|
||||||
.elements
|
.elements
|
||||||
@ -825,7 +834,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
let fft_root_table = fft_root_table(max_fft_points);
|
let fft_root_table = fft_root_table(max_fft_points);
|
||||||
|
|
||||||
let constants_sigmas_vecs = [constant_vecs, sigma_vecs.clone()].concat();
|
let constants_sigmas_vecs = [constant_vecs, sigma_vecs.clone()].concat();
|
||||||
let constants_sigmas_commitment = PolynomialBatch::from_values(
|
let constants_sigmas_commitment = PolynomialBatch::<F, HCO, HCI, C, D>::from_values(
|
||||||
constants_sigmas_vecs,
|
constants_sigmas_vecs,
|
||||||
rate_bits,
|
rate_bits,
|
||||||
PlonkOracle::CONSTANTS_SIGMAS.blinding,
|
PlonkOracle::CONSTANTS_SIGMAS.blinding,
|
||||||
@ -913,7 +922,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
assert_eq!(goal_data, common, "The expected circuit data passed to cyclic recursion method did not match the actual circuit");
|
assert_eq!(goal_data, common, "The expected circuit data passed to cyclic recursion method did not match the actual circuit");
|
||||||
}
|
}
|
||||||
|
|
||||||
let prover_only = ProverOnlyCircuitData {
|
let prover_only = ProverOnlyCircuitData::<F, HCO, HCI, C, D> {
|
||||||
generators: self.generators,
|
generators: self.generators,
|
||||||
generator_indices_by_watches,
|
generator_indices_by_watches,
|
||||||
constants_sigmas_commitment,
|
constants_sigmas_commitment,
|
||||||
@ -925,7 +934,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
circuit_digest,
|
circuit_digest,
|
||||||
};
|
};
|
||||||
|
|
||||||
let verifier_only = VerifierOnlyCircuitData {
|
let verifier_only = VerifierOnlyCircuitData::<HCO, HCI, C, D> {
|
||||||
constants_sigmas_cap,
|
constants_sigmas_cap,
|
||||||
circuit_digest,
|
circuit_digest,
|
||||||
};
|
};
|
||||||
@ -941,16 +950,28 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a "prover circuit", with data needed to generate proofs but not verify them.
|
/// Builds a "prover circuit", with data needed to generate proofs but not verify them.
|
||||||
pub fn build_prover<C: GenericConfig<D, F = F>>(self) -> ProverCircuitData<F, C, D> {
|
pub fn build_prover<HCO: HashConfig, HCI: HashConfig, C: GenericConfig<HCO, HCI, D, F = F>>(
|
||||||
|
self,
|
||||||
|
) -> ProverCircuitData<F, HCO, HCI, C, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
// TODO: Can skip parts of this.
|
// TODO: Can skip parts of this.
|
||||||
let circuit_data = self.build();
|
let circuit_data = self.build::<HCO, HCI, C>();
|
||||||
circuit_data.prover_data()
|
circuit_data.prover_data()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a "verifier circuit", with data needed to verify proofs but not generate them.
|
/// Builds a "verifier circuit", with data needed to verify proofs but not generate them.
|
||||||
pub fn build_verifier<C: GenericConfig<D, F = F>>(self) -> VerifierCircuitData<F, C, D> {
|
pub fn build_verifier<HCO: HashConfig, HCI: HashConfig, C: GenericConfig<HCO, HCI, D, F = F>>(
|
||||||
|
self,
|
||||||
|
) -> VerifierCircuitData<F, HCO, HCI, C, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
// TODO: Can skip parts of this.
|
// TODO: Can skip parts of this.
|
||||||
let circuit_data = self.build();
|
let circuit_data = self.build::<HCO, HCI, C>();
|
||||||
circuit_data.verifier_data()
|
circuit_data.verifier_data()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ use crate::fri::{FriConfig, FriParams};
|
|||||||
use crate::gates::gate::GateRef;
|
use crate::gates::gate::GateRef;
|
||||||
use crate::gates::selectors::SelectorsInfo;
|
use crate::gates::selectors::SelectorsInfo;
|
||||||
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::generator::WitnessGenerator;
|
use crate::iop::generator::WitnessGenerator;
|
||||||
@ -106,17 +107,35 @@ impl CircuitConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Circuit data required by the prover or the verifier.
|
/// Circuit data required by the prover or the verifier.
|
||||||
pub struct CircuitData<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
|
pub struct CircuitData<
|
||||||
pub prover_only: ProverOnlyCircuitData<F, C, D>,
|
F: RichField + Extendable<D>,
|
||||||
pub verifier_only: VerifierOnlyCircuitData<C, D>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> {
|
||||||
|
pub prover_only: ProverOnlyCircuitData<F, HCO, HCI, C, D>,
|
||||||
|
pub verifier_only: VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
pub common: CommonCircuitData<F, D>,
|
pub common: CommonCircuitData<F, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
impl<
|
||||||
CircuitData<F, C, D>
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> CircuitData<F, HCO, HCI, C, D>
|
||||||
{
|
{
|
||||||
pub fn prove(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>> {
|
pub fn prove(
|
||||||
prove(
|
&self,
|
||||||
|
inputs: PartialWitness<F>,
|
||||||
|
) -> Result<ProofWithPublicInputs<F, HCO, HCI, C, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
|
prove::<F, HCO, HCI, C, D>(
|
||||||
&self.prover_only,
|
&self.prover_only,
|
||||||
&self.common,
|
&self.common,
|
||||||
inputs,
|
inputs,
|
||||||
@ -124,32 +143,48 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(&self, proof_with_pis: ProofWithPublicInputs<F, C, D>) -> Result<()> {
|
pub fn verify(&self, proof_with_pis: ProofWithPublicInputs<F, HCO, HCI, C, D>) -> Result<()>
|
||||||
verify(proof_with_pis, &self.verifier_only, &self.common)
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
|
verify::<F, HCO, HCI, C, D>(proof_with_pis, &self.verifier_only, &self.common)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_compressed(
|
pub fn verify_compressed(
|
||||||
&self,
|
&self,
|
||||||
compressed_proof_with_pis: CompressedProofWithPublicInputs<F, C, D>,
|
compressed_proof_with_pis: CompressedProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
compressed_proof_with_pis.verify(&self.verifier_only, &self.common)
|
compressed_proof_with_pis.verify(&self.verifier_only, &self.common)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compress(
|
pub fn compress(
|
||||||
&self,
|
&self,
|
||||||
proof: ProofWithPublicInputs<F, C, D>,
|
proof: ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
) -> Result<CompressedProofWithPublicInputs<F, C, D>> {
|
) -> Result<CompressedProofWithPublicInputs<F, HCO, HCI, C, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
proof.compress(&self.verifier_only.circuit_digest, &self.common)
|
proof.compress(&self.verifier_only.circuit_digest, &self.common)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decompress(
|
pub fn decompress(
|
||||||
&self,
|
&self,
|
||||||
proof: CompressedProofWithPublicInputs<F, C, D>,
|
proof: CompressedProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
) -> Result<ProofWithPublicInputs<F, C, D>> {
|
) -> Result<ProofWithPublicInputs<F, HCO, HCI, C, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
proof.decompress(&self.verifier_only.circuit_digest, &self.common)
|
proof.decompress(&self.verifier_only.circuit_digest, &self.common)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verifier_data(&self) -> VerifierCircuitData<F, C, D> {
|
pub fn verifier_data(&self) -> VerifierCircuitData<F, HCO, HCI, C, D> {
|
||||||
let CircuitData {
|
let CircuitData {
|
||||||
verifier_only,
|
verifier_only,
|
||||||
common,
|
common,
|
||||||
@ -161,7 +196,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prover_data(self) -> ProverCircuitData<F, C, D> {
|
pub fn prover_data(self) -> ProverCircuitData<F, HCO, HCI, C, D> {
|
||||||
let CircuitData {
|
let CircuitData {
|
||||||
prover_only,
|
prover_only,
|
||||||
common,
|
common,
|
||||||
@ -183,18 +218,32 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
/// construct a more minimal prover structure and convert back and forth.
|
/// construct a more minimal prover structure and convert back and forth.
|
||||||
pub struct ProverCircuitData<
|
pub struct ProverCircuitData<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
> {
|
> {
|
||||||
pub prover_only: ProverOnlyCircuitData<F, C, D>,
|
pub prover_only: ProverOnlyCircuitData<F, HCO, HCI, C, D>,
|
||||||
pub common: CommonCircuitData<F, D>,
|
pub common: CommonCircuitData<F, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
impl<
|
||||||
ProverCircuitData<F, C, D>
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> ProverCircuitData<F, HCO, HCI, C, D>
|
||||||
{
|
{
|
||||||
pub fn prove(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>> {
|
pub fn prove(
|
||||||
prove(
|
&self,
|
||||||
|
inputs: PartialWitness<F>,
|
||||||
|
) -> Result<ProofWithPublicInputs<F, HCO, HCI, C, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
|
prove::<F, HCO, HCI, C, D>(
|
||||||
&self.prover_only,
|
&self.prover_only,
|
||||||
&self.common,
|
&self.common,
|
||||||
inputs,
|
inputs,
|
||||||
@ -207,24 +256,39 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VerifierCircuitData<
|
pub struct VerifierCircuitData<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
> {
|
> {
|
||||||
pub verifier_only: VerifierOnlyCircuitData<C, D>,
|
pub verifier_only: VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
pub common: CommonCircuitData<F, D>,
|
pub common: CommonCircuitData<F, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
impl<
|
||||||
VerifierCircuitData<F, C, D>
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> VerifierCircuitData<F, HCO, HCI, C, D>
|
||||||
{
|
{
|
||||||
pub fn verify(&self, proof_with_pis: ProofWithPublicInputs<F, C, D>) -> Result<()> {
|
pub fn verify(&self, proof_with_pis: ProofWithPublicInputs<F, HCO, HCI, C, D>) -> Result<()>
|
||||||
verify(proof_with_pis, &self.verifier_only, &self.common)
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
|
verify::<F, HCO, HCI, C, D>(proof_with_pis, &self.verifier_only, &self.common)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_compressed(
|
pub fn verify_compressed(
|
||||||
&self,
|
&self,
|
||||||
compressed_proof_with_pis: CompressedProofWithPublicInputs<F, C, D>,
|
compressed_proof_with_pis: CompressedProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
compressed_proof_with_pis.verify(&self.verifier_only, &self.common)
|
compressed_proof_with_pis.verify(&self.verifier_only, &self.common)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,7 +296,9 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
/// Circuit data required by the prover, but not the verifier.
|
/// Circuit data required by the prover, but not the verifier.
|
||||||
pub struct ProverOnlyCircuitData<
|
pub struct ProverOnlyCircuitData<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
> {
|
> {
|
||||||
pub generators: Vec<Box<dyn WitnessGenerator<F>>>,
|
pub generators: Vec<Box<dyn WitnessGenerator<F>>>,
|
||||||
@ -240,7 +306,7 @@ pub struct ProverOnlyCircuitData<
|
|||||||
/// they watch.
|
/// they watch.
|
||||||
pub generator_indices_by_watches: BTreeMap<usize, Vec<usize>>,
|
pub generator_indices_by_watches: BTreeMap<usize, Vec<usize>>,
|
||||||
/// Commitments to the constants polynomials and sigma polynomials.
|
/// Commitments to the constants polynomials and sigma polynomials.
|
||||||
pub constants_sigmas_commitment: PolynomialBatch<F, C, D>,
|
pub constants_sigmas_commitment: PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
/// The transpose of the list of sigma polynomials.
|
/// The transpose of the list of sigma polynomials.
|
||||||
pub sigmas: Vec<Vec<F>>,
|
pub sigmas: Vec<Vec<F>>,
|
||||||
/// Subgroup of order `degree`.
|
/// Subgroup of order `degree`.
|
||||||
@ -254,17 +320,22 @@ pub struct ProverOnlyCircuitData<
|
|||||||
pub fft_root_table: Option<FftRootTable<F>>,
|
pub fft_root_table: Option<FftRootTable<F>>,
|
||||||
/// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to
|
/// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to
|
||||||
/// seed Fiat-Shamir.
|
/// seed Fiat-Shamir.
|
||||||
pub circuit_digest: <<C as GenericConfig<D>>::Hasher as Hasher<F>>::Hash,
|
pub circuit_digest: <<C as GenericConfig<HCO, HCI, D>>::Hasher as Hasher<F, HCO>>::Hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Circuit data required by the verifier, but not the prover.
|
/// Circuit data required by the verifier, but not the prover.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct VerifierOnlyCircuitData<C: GenericConfig<D>, const D: usize> {
|
pub struct VerifierOnlyCircuitData<
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D>,
|
||||||
|
const D: usize,
|
||||||
|
> {
|
||||||
/// A commitment to each constant polynomial and each permutation polynomial.
|
/// A commitment to each constant polynomial and each permutation polynomial.
|
||||||
pub constants_sigmas_cap: MerkleCap<C::F, C::Hasher>,
|
pub constants_sigmas_cap: MerkleCap<C::F, HCO, C::Hasher>,
|
||||||
/// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to
|
/// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to
|
||||||
/// seed Fiat-Shamir.
|
/// seed Fiat-Shamir.
|
||||||
pub circuit_digest: <<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
|
pub circuit_digest: <<C as GenericConfig<HCO, HCI, D>>::Hasher as Hasher<C::F, HCO>>::Hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Circuit data required by both the prover and the verifier.
|
/// Circuit data required by both the prover and the verifier.
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use crate::field::extension::quadratic::QuadraticExtension;
|
|||||||
use crate::field::extension::{Extendable, FieldExtension};
|
use crate::field::extension::{Extendable, FieldExtension};
|
||||||
use crate::field::goldilocks_field::GoldilocksField;
|
use crate::field::goldilocks_field::GoldilocksField;
|
||||||
use crate::hash::hash_types::{HashOut, RichField};
|
use crate::hash::hash_types::{HashOut, RichField};
|
||||||
use crate::hash::hashing::{PlonkyPermutation, SPONGE_WIDTH};
|
use crate::hash::hashing::{HashConfig, PlonkyPermutation};
|
||||||
use crate::hash::keccak::KeccakHash;
|
use crate::hash::keccak::KeccakHash;
|
||||||
use crate::hash::poseidon::PoseidonHash;
|
use crate::hash::poseidon::PoseidonHash;
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
@ -25,7 +25,7 @@ pub trait GenericHashOut<F: RichField>:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for hash functions.
|
/// Trait for hash functions.
|
||||||
pub trait Hasher<F: RichField>: Sized + Clone + Debug + Eq + PartialEq {
|
pub trait Hasher<F: RichField, HC: HashConfig>: Sized + Clone + Debug + Eq + PartialEq {
|
||||||
/// Size of `Hash` in bytes.
|
/// Size of `Hash` in bytes.
|
||||||
const HASH_SIZE: usize;
|
const HASH_SIZE: usize;
|
||||||
|
|
||||||
@ -33,17 +33,22 @@ pub trait Hasher<F: RichField>: Sized + Clone + Debug + Eq + PartialEq {
|
|||||||
type Hash: GenericHashOut<F>;
|
type Hash: GenericHashOut<F>;
|
||||||
|
|
||||||
/// Permutation used in the sponge construction.
|
/// Permutation used in the sponge construction.
|
||||||
type Permutation: PlonkyPermutation<F>;
|
type Permutation: PlonkyPermutation<F, HC>;
|
||||||
|
|
||||||
/// Hash a message without any padding step. Note that this can enable length-extension attacks.
|
/// Hash a message without any padding step. Note that this can enable length-extension attacks.
|
||||||
/// However, it is still collision-resistant in cases where the input has a fixed length.
|
/// However, it is still collision-resistant in cases where the input has a fixed length.
|
||||||
fn hash_no_pad(input: &[F]) -> Self::Hash;
|
fn hash_no_pad(input: &[F]) -> Self::Hash
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:;
|
||||||
|
|
||||||
/// Pad the message using the `pad10*1` rule, then hash it.
|
/// Pad the message using the `pad10*1` rule, then hash it.
|
||||||
fn hash_pad(input: &[F]) -> Self::Hash {
|
fn hash_pad(input: &[F]) -> Self::Hash
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let mut padded_input = input.to_vec();
|
let mut padded_input = input.to_vec();
|
||||||
padded_input.push(F::ONE);
|
padded_input.push(F::ONE);
|
||||||
while (padded_input.len() + 1) % SPONGE_WIDTH != 0 {
|
while (padded_input.len() + 1) % HC::WIDTH != 0 {
|
||||||
padded_input.push(F::ZERO);
|
padded_input.push(F::ZERO);
|
||||||
}
|
}
|
||||||
padded_input.push(F::ONE);
|
padded_input.push(F::ONE);
|
||||||
@ -52,7 +57,10 @@ pub trait Hasher<F: RichField>: Sized + Clone + Debug + Eq + PartialEq {
|
|||||||
|
|
||||||
/// Hash the slice if necessary to reduce its length to ~256 bits. If it already fits, this is a
|
/// Hash the slice if necessary to reduce its length to ~256 bits. If it already fits, this is a
|
||||||
/// no-op.
|
/// no-op.
|
||||||
fn hash_or_noop(inputs: &[F]) -> Self::Hash {
|
fn hash_or_noop(inputs: &[F]) -> Self::Hash
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
if inputs.len() * 8 <= Self::HASH_SIZE {
|
if inputs.len() * 8 <= Self::HASH_SIZE {
|
||||||
let mut inputs_bytes = vec![0u8; Self::HASH_SIZE];
|
let mut inputs_bytes = vec![0u8; Self::HASH_SIZE];
|
||||||
for i in 0..inputs.len() {
|
for i in 0..inputs.len() {
|
||||||
@ -65,27 +73,27 @@ pub trait Hasher<F: RichField>: Sized + Clone + Debug + Eq + PartialEq {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash;
|
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for algebraic hash functions, built from a permutation using the sponge construction.
|
/// Trait for algebraic hash functions, built from a permutation using the sponge construction.
|
||||||
pub trait AlgebraicHasher<F: RichField>: Hasher<F, Hash = HashOut<F>> {
|
pub trait AlgebraicHasher<F: RichField, HC: HashConfig>: Hasher<F, HC, Hash = HashOut<F>> {
|
||||||
// TODO: Adding a `const WIDTH: usize` here yields a compiler error down the line.
|
|
||||||
// Maybe try again in a while.
|
|
||||||
|
|
||||||
/// Circuit to conditionally swap two chunks of the inputs (useful in verifying Merkle proofs),
|
/// Circuit to conditionally swap two chunks of the inputs (useful in verifying Merkle proofs),
|
||||||
/// then apply the permutation.
|
/// then apply the permutation.
|
||||||
fn permute_swapped<const D: usize>(
|
fn permute_swapped<const D: usize>(
|
||||||
inputs: [Target; SPONGE_WIDTH],
|
inputs: [Target; HC::WIDTH],
|
||||||
swap: BoolTarget,
|
swap: BoolTarget,
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
) -> [Target; SPONGE_WIDTH]
|
) -> [Target; HC::WIDTH]
|
||||||
where
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
F: RichField + Extendable<D>;
|
F: RichField + Extendable<D>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic configuration trait.
|
/// Generic configuration trait.
|
||||||
pub trait GenericConfig<const D: usize>:
|
pub trait GenericConfig<HCO: HashConfig, HCI: HashConfig, const D: usize>:
|
||||||
Debug + Clone + Sync + Sized + Send + Eq + PartialEq
|
Debug + Clone + Sync + Sized + Send + Eq + PartialEq
|
||||||
{
|
{
|
||||||
/// Main field.
|
/// Main field.
|
||||||
@ -93,25 +101,37 @@ pub trait GenericConfig<const D: usize>:
|
|||||||
/// Field extension of degree D of the main field.
|
/// Field extension of degree D of the main field.
|
||||||
type FE: FieldExtension<D, BaseField = Self::F>;
|
type FE: FieldExtension<D, BaseField = Self::F>;
|
||||||
/// Hash function used for building Merkle trees.
|
/// Hash function used for building Merkle trees.
|
||||||
type Hasher: Hasher<Self::F>;
|
type Hasher: Hasher<Self::F, HCO>;
|
||||||
/// Algebraic hash function used for the challenger and hashing public inputs.
|
/// Algebraic hash function used for the challenger and hashing public inputs.
|
||||||
type InnerHasher: AlgebraicHasher<Self::F>;
|
type InnerHasher: AlgebraicHasher<Self::F, HCI>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct PoseidonHashConfig;
|
||||||
|
impl HashConfig for PoseidonHashConfig {
|
||||||
|
const RATE: usize = 8;
|
||||||
|
const WIDTH: usize = 12;
|
||||||
|
}
|
||||||
/// Configuration using Poseidon over the Goldilocks field.
|
/// Configuration using Poseidon over the Goldilocks field.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub struct PoseidonGoldilocksConfig;
|
pub struct PoseidonGoldilocksConfig;
|
||||||
impl GenericConfig<2> for PoseidonGoldilocksConfig {
|
impl GenericConfig<PoseidonHashConfig, PoseidonHashConfig, 2> for PoseidonGoldilocksConfig {
|
||||||
type F = GoldilocksField;
|
type F = GoldilocksField;
|
||||||
type FE = QuadraticExtension<Self::F>;
|
type FE = QuadraticExtension<Self::F>;
|
||||||
type Hasher = PoseidonHash;
|
type Hasher = PoseidonHash;
|
||||||
type InnerHasher = PoseidonHash;
|
type InnerHasher = PoseidonHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct KeccakHashConfig;
|
||||||
|
impl HashConfig for KeccakHashConfig {
|
||||||
|
const RATE: usize = 8;
|
||||||
|
const WIDTH: usize = 12;
|
||||||
|
}
|
||||||
/// Configuration using truncated Keccak over the Goldilocks field.
|
/// Configuration using truncated Keccak over the Goldilocks field.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub struct KeccakGoldilocksConfig;
|
pub struct KeccakGoldilocksConfig;
|
||||||
impl GenericConfig<2> for KeccakGoldilocksConfig {
|
impl GenericConfig<KeccakHashConfig, PoseidonHashConfig, 2> for KeccakGoldilocksConfig {
|
||||||
type F = GoldilocksField;
|
type F = GoldilocksField;
|
||||||
type FE = QuadraticExtension<Self::F>;
|
type FE = QuadraticExtension<Self::F>;
|
||||||
type Hasher = KeccakHash<25>;
|
type Hasher = KeccakHash<25>;
|
||||||
|
|||||||
@ -9,6 +9,7 @@ use crate::fri::proof::{CompressedFriProof, FriChallenges, FriProof, FriProofTar
|
|||||||
use crate::fri::verifier::{compute_evaluation, fri_combine_initial, PrecomputedReducedOpenings};
|
use crate::fri::verifier::{compute_evaluation, fri_combine_initial, PrecomputedReducedOpenings};
|
||||||
use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
|
use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
|
||||||
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::iop::challenger::{Challenger, RecursiveChallenger};
|
use crate::iop::challenger::{Challenger, RecursiveChallenger};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
@ -22,35 +23,45 @@ use crate::plonk::proof::{
|
|||||||
};
|
};
|
||||||
use crate::util::reverse_bits;
|
use crate::util::reverse_bits;
|
||||||
|
|
||||||
fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
fn get_challenges<
|
||||||
public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
|
F: RichField + Extendable<D>,
|
||||||
wires_cap: &MerkleCap<F, C::Hasher>,
|
HCO: HashConfig,
|
||||||
plonk_zs_partial_products_cap: &MerkleCap<F, C::Hasher>,
|
HCI: HashConfig,
|
||||||
quotient_polys_cap: &MerkleCap<F, C::Hasher>,
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
|
public_inputs_hash: <<C as GenericConfig<HCO, HCI, D>>::InnerHasher as Hasher<F, HCI>>::Hash,
|
||||||
|
wires_cap: &MerkleCap<F, HCO, C::Hasher>,
|
||||||
|
plonk_zs_partial_products_cap: &MerkleCap<F, HCO, C::Hasher>,
|
||||||
|
quotient_polys_cap: &MerkleCap<F, HCO, C::Hasher>,
|
||||||
openings: &OpeningSet<F, D>,
|
openings: &OpeningSet<F, D>,
|
||||||
commit_phase_merkle_caps: &[MerkleCap<F, C::Hasher>],
|
commit_phase_merkle_caps: &[MerkleCap<F, HCO, C::Hasher>],
|
||||||
final_poly: &PolynomialCoeffs<F::Extension>,
|
final_poly: &PolynomialCoeffs<F::Extension>,
|
||||||
pow_witness: F,
|
pow_witness: F,
|
||||||
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
|
circuit_digest: &<<C as GenericConfig<HCO, HCI, D>>::Hasher as Hasher<C::F, HCO>>::Hash,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> anyhow::Result<ProofChallenges<F, D>> {
|
) -> anyhow::Result<ProofChallenges<F, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let config = &common_data.config;
|
let config = &common_data.config;
|
||||||
let num_challenges = config.num_challenges;
|
let num_challenges = config.num_challenges;
|
||||||
|
|
||||||
let mut challenger = Challenger::<F, C::Hasher>::new();
|
let mut challenger = Challenger::<F, HCO, C::Hasher>::new();
|
||||||
|
|
||||||
// Observe the instance.
|
// Observe the instance.
|
||||||
challenger.observe_hash::<C::Hasher>(*circuit_digest);
|
challenger.observe_hash::<HCO, C::Hasher>(*circuit_digest);
|
||||||
challenger.observe_hash::<C::InnerHasher>(public_inputs_hash);
|
challenger.observe_hash::<HCI, C::InnerHasher>(public_inputs_hash);
|
||||||
|
|
||||||
challenger.observe_cap(wires_cap);
|
challenger.observe_cap::<HCO, C::Hasher>(wires_cap);
|
||||||
let plonk_betas = challenger.get_n_challenges(num_challenges);
|
let plonk_betas = challenger.get_n_challenges(num_challenges);
|
||||||
let plonk_gammas = challenger.get_n_challenges(num_challenges);
|
let plonk_gammas = challenger.get_n_challenges(num_challenges);
|
||||||
|
|
||||||
challenger.observe_cap(plonk_zs_partial_products_cap);
|
challenger.observe_cap::<HCO, C::Hasher>(plonk_zs_partial_products_cap);
|
||||||
let plonk_alphas = challenger.get_n_challenges(num_challenges);
|
let plonk_alphas = challenger.get_n_challenges(num_challenges);
|
||||||
|
|
||||||
challenger.observe_cap(quotient_polys_cap);
|
challenger.observe_cap::<HCO, C::Hasher>(quotient_polys_cap);
|
||||||
let plonk_zeta = challenger.get_extension_challenge::<D>();
|
let plonk_zeta = challenger.get_extension_challenge::<D>();
|
||||||
|
|
||||||
challenger.observe_openings(&openings.to_fri_openings());
|
challenger.observe_openings(&openings.to_fri_openings());
|
||||||
@ -60,7 +71,7 @@ fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, cons
|
|||||||
plonk_gammas,
|
plonk_gammas,
|
||||||
plonk_alphas,
|
plonk_alphas,
|
||||||
plonk_zeta,
|
plonk_zeta,
|
||||||
fri_challenges: challenger.fri_challenges::<C, D>(
|
fri_challenges: challenger.fri_challenges::<HCI, C, D>(
|
||||||
commit_phase_merkle_caps,
|
commit_phase_merkle_caps,
|
||||||
final_poly,
|
final_poly,
|
||||||
pow_witness,
|
pow_witness,
|
||||||
@ -70,14 +81,23 @@ fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, cons
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
impl<
|
||||||
ProofWithPublicInputs<F, C, D>
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> ProofWithPublicInputs<F, HCO, HCI, C, D>
|
||||||
{
|
{
|
||||||
pub(crate) fn fri_query_indices(
|
pub(crate) fn fri_query_indices(
|
||||||
&self,
|
&self,
|
||||||
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
|
circuit_digest: &<<C as GenericConfig<HCO, HCI, D>>::Hasher as Hasher<C::F, HCO>>::Hash,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> anyhow::Result<Vec<usize>> {
|
) -> anyhow::Result<Vec<usize>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
Ok(self
|
Ok(self
|
||||||
.get_challenges(self.get_public_inputs_hash(), circuit_digest, common_data)?
|
.get_challenges(self.get_public_inputs_hash(), circuit_digest, common_data)?
|
||||||
.fri_challenges
|
.fri_challenges
|
||||||
@ -87,10 +107,14 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
/// Computes all Fiat-Shamir challenges used in the Plonk proof.
|
/// Computes all Fiat-Shamir challenges used in the Plonk proof.
|
||||||
pub(crate) fn get_challenges(
|
pub(crate) fn get_challenges(
|
||||||
&self,
|
&self,
|
||||||
public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
|
public_inputs_hash: <<C as GenericConfig<HCO, HCI, D>>::InnerHasher as Hasher<F, HCI>>::Hash,
|
||||||
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
|
circuit_digest: &<<C as GenericConfig<HCO, HCI, D>>::Hasher as Hasher<C::F, HCO>>::Hash,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> anyhow::Result<ProofChallenges<F, D>> {
|
) -> anyhow::Result<ProofChallenges<F, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let Proof {
|
let Proof {
|
||||||
wires_cap,
|
wires_cap,
|
||||||
plonk_zs_partial_products_cap,
|
plonk_zs_partial_products_cap,
|
||||||
@ -105,7 +129,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
},
|
},
|
||||||
} = &self.proof;
|
} = &self.proof;
|
||||||
|
|
||||||
get_challenges::<F, C, D>(
|
get_challenges::<F, HCO, HCI, C, D>(
|
||||||
public_inputs_hash,
|
public_inputs_hash,
|
||||||
wires_cap,
|
wires_cap,
|
||||||
plonk_zs_partial_products_cap,
|
plonk_zs_partial_products_cap,
|
||||||
@ -120,16 +144,25 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
impl<
|
||||||
CompressedProofWithPublicInputs<F, C, D>
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> CompressedProofWithPublicInputs<F, HCO, HCI, C, D>
|
||||||
{
|
{
|
||||||
/// Computes all Fiat-Shamir challenges used in the Plonk proof.
|
/// Computes all Fiat-Shamir challenges used in the Plonk proof.
|
||||||
pub(crate) fn get_challenges(
|
pub(crate) fn get_challenges(
|
||||||
&self,
|
&self,
|
||||||
public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
|
public_inputs_hash: <<C as GenericConfig<HCO, HCI, D>>::InnerHasher as Hasher<F, HCI>>::Hash,
|
||||||
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
|
circuit_digest: &<<C as GenericConfig<HCO, HCI, D>>::Hasher as Hasher<C::F, HCO>>::Hash,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> anyhow::Result<ProofChallenges<F, D>> {
|
) -> anyhow::Result<ProofChallenges<F, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let CompressedProof {
|
let CompressedProof {
|
||||||
wires_cap,
|
wires_cap,
|
||||||
plonk_zs_partial_products_cap,
|
plonk_zs_partial_products_cap,
|
||||||
@ -144,7 +177,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
},
|
},
|
||||||
} = &self.proof;
|
} = &self.proof;
|
||||||
|
|
||||||
get_challenges::<F, C, D>(
|
get_challenges::<F, HCO, HCI, C, D>(
|
||||||
public_inputs_hash,
|
public_inputs_hash,
|
||||||
wires_cap,
|
wires_cap,
|
||||||
plonk_zs_partial_products_cap,
|
plonk_zs_partial_products_cap,
|
||||||
@ -189,7 +222,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
for &(mut x_index) in fri_query_indices {
|
for &(mut x_index) in fri_query_indices {
|
||||||
let mut subgroup_x = F::MULTIPLICATIVE_GROUP_GENERATOR
|
let mut subgroup_x = F::MULTIPLICATIVE_GROUP_GENERATOR
|
||||||
* F::primitive_root_of_unity(log_n).exp_u64(reverse_bits(x_index, log_n) as u64);
|
* F::primitive_root_of_unity(log_n).exp_u64(reverse_bits(x_index, log_n) as u64);
|
||||||
let mut old_eval = fri_combine_initial::<F, C, D>(
|
let mut old_eval = fri_combine_initial::<F, HCO, HCI, C, D>(
|
||||||
&common_data.get_fri_instance(*plonk_zeta),
|
&common_data.get_fri_instance(*plonk_zeta),
|
||||||
&self
|
&self
|
||||||
.proof
|
.proof
|
||||||
@ -235,7 +268,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||||
fn get_challenges<C: GenericConfig<D, F = F>>(
|
fn get_challenges<HCO: HashConfig, HCI: HashConfig, C: GenericConfig<HCO, HCI, D, F = F>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
public_inputs_hash: HashOutTarget,
|
public_inputs_hash: HashOutTarget,
|
||||||
wires_cap: &MerkleCapTarget,
|
wires_cap: &MerkleCapTarget,
|
||||||
@ -249,12 +282,14 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
inner_common_data: &CommonCircuitData<F, D>,
|
inner_common_data: &CommonCircuitData<F, D>,
|
||||||
) -> ProofChallengesTarget<D>
|
) -> ProofChallengesTarget<D>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let config = &inner_common_data.config;
|
let config = &inner_common_data.config;
|
||||||
let num_challenges = config.num_challenges;
|
let num_challenges = config.num_challenges;
|
||||||
|
|
||||||
let mut challenger = RecursiveChallenger::<F, C::Hasher, D>::new(self);
|
let mut challenger = RecursiveChallenger::<F, HCO, C::Hasher, D>::new(self);
|
||||||
|
|
||||||
// Observe the instance.
|
// Observe the instance.
|
||||||
challenger.observe_hash(&inner_circuit_digest);
|
challenger.observe_hash(&inner_circuit_digest);
|
||||||
@ -289,7 +324,12 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<const D: usize> ProofWithPublicInputsTarget<D> {
|
impl<const D: usize> ProofWithPublicInputsTarget<D> {
|
||||||
pub(crate) fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>>(
|
pub(crate) fn get_challenges<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
>(
|
||||||
&self,
|
&self,
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
public_inputs_hash: HashOutTarget,
|
public_inputs_hash: HashOutTarget,
|
||||||
@ -297,7 +337,9 @@ impl<const D: usize> ProofWithPublicInputsTarget<D> {
|
|||||||
inner_common_data: &CommonCircuitData<F, D>,
|
inner_common_data: &CommonCircuitData<F, D>,
|
||||||
) -> ProofChallengesTarget<D>
|
) -> ProofChallengesTarget<D>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let ProofTarget {
|
let ProofTarget {
|
||||||
wires_cap,
|
wires_cap,
|
||||||
@ -313,7 +355,7 @@ impl<const D: usize> ProofWithPublicInputsTarget<D> {
|
|||||||
},
|
},
|
||||||
} = &self.proof;
|
} = &self.proof;
|
||||||
|
|
||||||
builder.get_challenges::<C>(
|
builder.get_challenges::<HCO, HCI, C>(
|
||||||
public_inputs_hash,
|
public_inputs_hash,
|
||||||
wires_cap,
|
wires_cap,
|
||||||
plonk_zs_partial_products_cap,
|
plonk_zs_partial_products_cap,
|
||||||
|
|||||||
@ -15,6 +15,7 @@ use crate::fri::structure::{
|
|||||||
};
|
};
|
||||||
use crate::fri::FriParams;
|
use crate::fri::FriParams;
|
||||||
use crate::hash::hash_types::{MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{MerkleCapTarget, RichField};
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
@ -27,17 +28,23 @@ use crate::util::serialization::{Buffer, Read};
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||||
#[serde(bound = "")]
|
#[serde(bound = "")]
|
||||||
pub struct Proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
|
pub struct Proof<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> {
|
||||||
/// Merkle cap of LDEs of wire values.
|
/// Merkle cap of LDEs of wire values.
|
||||||
pub wires_cap: MerkleCap<F, C::Hasher>,
|
pub wires_cap: MerkleCap<F, HCO, C::Hasher>,
|
||||||
/// Merkle cap of LDEs of Z, in the context of Plonk's permutation argument.
|
/// Merkle cap of LDEs of Z, in the context of Plonk's permutation argument.
|
||||||
pub plonk_zs_partial_products_cap: MerkleCap<F, C::Hasher>,
|
pub plonk_zs_partial_products_cap: MerkleCap<F, HCO, C::Hasher>,
|
||||||
/// Merkle cap of LDEs of the quotient polynomial components.
|
/// Merkle cap of LDEs of the quotient polynomial components.
|
||||||
pub quotient_polys_cap: MerkleCap<F, C::Hasher>,
|
pub quotient_polys_cap: MerkleCap<F, HCO, C::Hasher>,
|
||||||
/// Purported values of each polynomial at the challenge point.
|
/// Purported values of each polynomial at the challenge point.
|
||||||
pub openings: OpeningSet<F, D>,
|
pub openings: OpeningSet<F, D>,
|
||||||
/// A batch FRI argument for all openings.
|
/// A batch FRI argument for all openings.
|
||||||
pub opening_proof: FriProof<F, C::Hasher, D>,
|
pub opening_proof: FriProof<F, HCO, C::Hasher, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -49,9 +56,20 @@ pub struct ProofTarget<const D: usize> {
|
|||||||
pub opening_proof: FriProofTarget<D>,
|
pub opening_proof: FriProofTarget<D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> Proof<F, C, D> {
|
impl<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> Proof<F, HCO, HCI, C, D>
|
||||||
|
{
|
||||||
/// Compress the proof.
|
/// Compress the proof.
|
||||||
pub fn compress(self, indices: &[usize], params: &FriParams) -> CompressedProof<F, C, D> {
|
pub fn compress(
|
||||||
|
self,
|
||||||
|
indices: &[usize],
|
||||||
|
params: &FriParams,
|
||||||
|
) -> CompressedProof<F, HCO, HCI, C, D> {
|
||||||
let Proof {
|
let Proof {
|
||||||
wires_cap,
|
wires_cap,
|
||||||
plonk_zs_partial_products_cap,
|
plonk_zs_partial_products_cap,
|
||||||
@ -74,21 +92,32 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> P
|
|||||||
#[serde(bound = "")]
|
#[serde(bound = "")]
|
||||||
pub struct ProofWithPublicInputs<
|
pub struct ProofWithPublicInputs<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
> {
|
> {
|
||||||
pub proof: Proof<F, C, D>,
|
pub proof: Proof<F, HCO, HCI, C, D>,
|
||||||
pub public_inputs: Vec<F>,
|
pub public_inputs: Vec<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
impl<
|
||||||
ProofWithPublicInputs<F, C, D>
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> ProofWithPublicInputs<F, HCO, HCI, C, D>
|
||||||
{
|
{
|
||||||
pub fn compress(
|
pub fn compress(
|
||||||
self,
|
self,
|
||||||
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
|
circuit_digest: &<<C as GenericConfig<HCO, HCI, D>>::Hasher as Hasher<C::F, HCO>>::Hash,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> anyhow::Result<CompressedProofWithPublicInputs<F, C, D>> {
|
) -> anyhow::Result<CompressedProofWithPublicInputs<F, HCO, HCI, C, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let indices = self.fri_query_indices(circuit_digest, common_data)?;
|
let indices = self.fri_query_indices(circuit_digest, common_data)?;
|
||||||
let compressed_proof = self.proof.compress(&indices, &common_data.fri_params);
|
let compressed_proof = self.proof.compress(&indices, &common_data.fri_params);
|
||||||
Ok(CompressedProofWithPublicInputs {
|
Ok(CompressedProofWithPublicInputs {
|
||||||
@ -99,7 +128,10 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
|
|
||||||
pub(crate) fn get_public_inputs_hash(
|
pub(crate) fn get_public_inputs_hash(
|
||||||
&self,
|
&self,
|
||||||
) -> <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash {
|
) -> <<C as GenericConfig<HCO, HCI, D>>::InnerHasher as Hasher<F, HCI>>::Hash
|
||||||
|
where
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
C::InnerHasher::hash_no_pad(&self.public_inputs)
|
C::InnerHasher::hash_no_pad(&self.public_inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,22 +158,32 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||||
#[serde(bound = "")]
|
#[serde(bound = "")]
|
||||||
pub struct CompressedProof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
pub struct CompressedProof<
|
||||||
{
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> {
|
||||||
/// Merkle cap of LDEs of wire values.
|
/// Merkle cap of LDEs of wire values.
|
||||||
pub wires_cap: MerkleCap<F, C::Hasher>,
|
pub wires_cap: MerkleCap<F, HCO, C::Hasher>,
|
||||||
/// Merkle cap of LDEs of Z, in the context of Plonk's permutation argument.
|
/// Merkle cap of LDEs of Z, in the context of Plonk's permutation argument.
|
||||||
pub plonk_zs_partial_products_cap: MerkleCap<F, C::Hasher>,
|
pub plonk_zs_partial_products_cap: MerkleCap<F, HCO, C::Hasher>,
|
||||||
/// Merkle cap of LDEs of the quotient polynomial components.
|
/// Merkle cap of LDEs of the quotient polynomial components.
|
||||||
pub quotient_polys_cap: MerkleCap<F, C::Hasher>,
|
pub quotient_polys_cap: MerkleCap<F, HCO, C::Hasher>,
|
||||||
/// Purported values of each polynomial at the challenge point.
|
/// Purported values of each polynomial at the challenge point.
|
||||||
pub openings: OpeningSet<F, D>,
|
pub openings: OpeningSet<F, D>,
|
||||||
/// A compressed batch FRI argument for all openings.
|
/// A compressed batch FRI argument for all openings.
|
||||||
pub opening_proof: CompressedFriProof<F, C::Hasher, D>,
|
pub opening_proof: CompressedFriProof<F, HCO, C::Hasher, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
impl<
|
||||||
CompressedProof<F, C, D>
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> CompressedProof<F, HCO, HCI, C, D>
|
||||||
{
|
{
|
||||||
/// Decompress the proof.
|
/// Decompress the proof.
|
||||||
pub(crate) fn decompress(
|
pub(crate) fn decompress(
|
||||||
@ -149,7 +191,10 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
challenges: &ProofChallenges<F, D>,
|
challenges: &ProofChallenges<F, D>,
|
||||||
fri_inferred_elements: FriInferredElements<F, D>,
|
fri_inferred_elements: FriInferredElements<F, D>,
|
||||||
params: &FriParams,
|
params: &FriParams,
|
||||||
) -> Proof<F, C, D> {
|
) -> Proof<F, HCO, HCI, C, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
let CompressedProof {
|
let CompressedProof {
|
||||||
wires_cap,
|
wires_cap,
|
||||||
plonk_zs_partial_products_cap,
|
plonk_zs_partial_products_cap,
|
||||||
@ -172,21 +217,32 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
#[serde(bound = "")]
|
#[serde(bound = "")]
|
||||||
pub struct CompressedProofWithPublicInputs<
|
pub struct CompressedProofWithPublicInputs<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
> {
|
> {
|
||||||
pub proof: CompressedProof<F, C, D>,
|
pub proof: CompressedProof<F, HCO, HCI, C, D>,
|
||||||
pub public_inputs: Vec<F>,
|
pub public_inputs: Vec<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
impl<
|
||||||
CompressedProofWithPublicInputs<F, C, D>
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> CompressedProofWithPublicInputs<F, HCO, HCI, C, D>
|
||||||
{
|
{
|
||||||
pub fn decompress(
|
pub fn decompress(
|
||||||
self,
|
self,
|
||||||
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
|
circuit_digest: &<<C as GenericConfig<HCO, HCI, D>>::Hasher as Hasher<C::F, HCO>>::Hash,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>> {
|
) -> anyhow::Result<ProofWithPublicInputs<F, HCO, HCI, C, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let challenges =
|
let challenges =
|
||||||
self.get_challenges(self.get_public_inputs_hash(), circuit_digest, common_data)?;
|
self.get_challenges(self.get_public_inputs_hash(), circuit_digest, common_data)?;
|
||||||
let fri_inferred_elements = self.get_inferred_elements(&challenges, common_data);
|
let fri_inferred_elements = self.get_inferred_elements(&challenges, common_data);
|
||||||
@ -201,9 +257,13 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
|
|
||||||
pub(crate) fn verify(
|
pub(crate) fn verify(
|
||||||
self,
|
self,
|
||||||
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
verifier_data: &VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
ensure!(
|
ensure!(
|
||||||
self.public_inputs.len() == common_data.num_public_inputs,
|
self.public_inputs.len() == common_data.num_public_inputs,
|
||||||
"Number of public inputs doesn't match circuit data."
|
"Number of public inputs doesn't match circuit data."
|
||||||
@ -218,7 +278,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
let decompressed_proof =
|
let decompressed_proof =
|
||||||
self.proof
|
self.proof
|
||||||
.decompress(&challenges, fri_inferred_elements, &common_data.fri_params);
|
.decompress(&challenges, fri_inferred_elements, &common_data.fri_params);
|
||||||
verify_with_challenges(
|
verify_with_challenges::<F, HCO, HCI, C, D>(
|
||||||
decompressed_proof,
|
decompressed_proof,
|
||||||
public_inputs_hash,
|
public_inputs_hash,
|
||||||
challenges,
|
challenges,
|
||||||
@ -229,7 +289,10 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
|
|
||||||
pub(crate) fn get_public_inputs_hash(
|
pub(crate) fn get_public_inputs_hash(
|
||||||
&self,
|
&self,
|
||||||
) -> <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash {
|
) -> <<C as GenericConfig<HCO, HCI, D>>::InnerHasher as Hasher<F, HCI>>::Hash
|
||||||
|
where
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
C::InnerHasher::hash_no_pad(&self.public_inputs)
|
C::InnerHasher::hash_no_pad(&self.public_inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,16 +365,16 @@ pub struct OpeningSet<F: RichField + Extendable<D>, const D: usize> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> OpeningSet<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> OpeningSet<F, D> {
|
||||||
pub fn new<C: GenericConfig<D, F = F>>(
|
pub fn new<HCO: HashConfig, HCI: HashConfig, C: GenericConfig<HCO, HCI, D, F = F>>(
|
||||||
zeta: F::Extension,
|
zeta: F::Extension,
|
||||||
g: F::Extension,
|
g: F::Extension,
|
||||||
constants_sigmas_commitment: &PolynomialBatch<F, C, D>,
|
constants_sigmas_commitment: &PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
wires_commitment: &PolynomialBatch<F, C, D>,
|
wires_commitment: &PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
zs_partial_products_commitment: &PolynomialBatch<F, C, D>,
|
zs_partial_products_commitment: &PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
quotient_polys_commitment: &PolynomialBatch<F, C, D>,
|
quotient_polys_commitment: &PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let eval_commitment = |z: F::Extension, c: &PolynomialBatch<F, C, D>| {
|
let eval_commitment = |z: F::Extension, c: &PolynomialBatch<F, HCO, HCI, C, D>| {
|
||||||
c.polynomials
|
c.polynomials
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|p| p.to_extension().eval(z))
|
.map(|p| p.to_extension().eval(z))
|
||||||
@ -398,14 +461,16 @@ mod tests {
|
|||||||
use crate::iop::witness::PartialWitness;
|
use crate::iop::witness::PartialWitness;
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use crate::plonk::verifier::verify;
|
use crate::plonk::verifier::verify;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_proof_compression() -> Result<()> {
|
fn test_proof_compression() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
|
||||||
let mut config = CircuitConfig::standard_recursion_config();
|
let mut config = CircuitConfig::standard_recursion_config();
|
||||||
config.fri_config.reduction_strategy = FriReductionStrategy::Fixed(vec![1, 1]);
|
config.fri_config.reduction_strategy = FriReductionStrategy::Fixed(vec![1, 1]);
|
||||||
@ -426,7 +491,7 @@ mod tests {
|
|||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
builder.add_gate(NoopGate, vec![]);
|
builder.add_gate(NoopGate, vec![]);
|
||||||
}
|
}
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
verify(proof.clone(), &data.verifier_only, &data.common)?;
|
verify(proof.clone(), &data.verifier_only, &data.common)?;
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ use crate::field::types::Field;
|
|||||||
use crate::field::zero_poly_coset::ZeroPolyOnCoset;
|
use crate::field::zero_poly_coset::ZeroPolyOnCoset;
|
||||||
use crate::fri::oracle::PolynomialBatch;
|
use crate::fri::oracle::PolynomialBatch;
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::iop::challenger::Challenger;
|
use crate::iop::challenger::Challenger;
|
||||||
use crate::iop::generator::generate_partial_witness;
|
use crate::iop::generator::generate_partial_witness;
|
||||||
use crate::iop::witness::{MatrixWitness, PartialWitness, Witness};
|
use crate::iop::witness::{MatrixWitness, PartialWitness, Witness};
|
||||||
@ -25,12 +26,24 @@ use crate::util::partial_products::{partial_products_and_z_gx, quotient_chunk_pr
|
|||||||
use crate::util::timing::TimingTree;
|
use crate::util::timing::TimingTree;
|
||||||
use crate::util::{ceil_div_usize, log2_ceil, transpose};
|
use crate::util::{ceil_div_usize, log2_ceil, transpose};
|
||||||
|
|
||||||
pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
pub fn prove<
|
||||||
prover_data: &ProverOnlyCircuitData<F, C, D>,
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
|
prover_data: &ProverOnlyCircuitData<F, HCO, HCI, C, D>,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
inputs: PartialWitness<F>,
|
inputs: PartialWitness<F>,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> Result<ProofWithPublicInputs<F, C, D>> {
|
) -> Result<ProofWithPublicInputs<F, HCO, HCI, C, D>>
|
||||||
|
where
|
||||||
|
C::Hasher: Hasher<F, HCO>,
|
||||||
|
C::InnerHasher: Hasher<F, HCI>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let config = &common_data.config;
|
let config = &common_data.config;
|
||||||
let num_challenges = config.num_challenges;
|
let num_challenges = config.num_challenges;
|
||||||
let quotient_degree = common_data.quotient_degree();
|
let quotient_degree = common_data.quotient_degree();
|
||||||
@ -64,7 +77,7 @@ pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D:
|
|||||||
let wires_commitment = timed!(
|
let wires_commitment = timed!(
|
||||||
timing,
|
timing,
|
||||||
"compute wires commitment",
|
"compute wires commitment",
|
||||||
PolynomialBatch::from_values(
|
PolynomialBatch::<F, HCO, HCI, C, D>::from_values(
|
||||||
wires_values,
|
wires_values,
|
||||||
config.fri_config.rate_bits,
|
config.fri_config.rate_bits,
|
||||||
config.zero_knowledge && PlonkOracle::WIRES.blinding,
|
config.zero_knowledge && PlonkOracle::WIRES.blinding,
|
||||||
@ -74,13 +87,13 @@ pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D:
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut challenger = Challenger::<F, C::Hasher>::new();
|
let mut challenger = Challenger::<F, HCO, C::Hasher>::new();
|
||||||
|
|
||||||
// Observe the instance.
|
// Observe the instance.
|
||||||
challenger.observe_hash::<C::Hasher>(prover_data.circuit_digest);
|
challenger.observe_hash::<HCO, C::Hasher>(prover_data.circuit_digest);
|
||||||
challenger.observe_hash::<C::InnerHasher>(public_inputs_hash);
|
challenger.observe_hash::<HCI, C::InnerHasher>(public_inputs_hash);
|
||||||
|
|
||||||
challenger.observe_cap(&wires_commitment.merkle_tree.cap);
|
challenger.observe_cap::<HCO, C::Hasher>(&wires_commitment.merkle_tree.cap);
|
||||||
let betas = challenger.get_n_challenges(num_challenges);
|
let betas = challenger.get_n_challenges(num_challenges);
|
||||||
let gammas = challenger.get_n_challenges(num_challenges);
|
let gammas = challenger.get_n_challenges(num_challenges);
|
||||||
|
|
||||||
@ -104,7 +117,7 @@ pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D:
|
|||||||
let partial_products_and_zs_commitment = timed!(
|
let partial_products_and_zs_commitment = timed!(
|
||||||
timing,
|
timing,
|
||||||
"commit to partial products and Z's",
|
"commit to partial products and Z's",
|
||||||
PolynomialBatch::from_values(
|
PolynomialBatch::<F, HCO, HCI, C, D>::from_values(
|
||||||
zs_partial_products,
|
zs_partial_products,
|
||||||
config.fri_config.rate_bits,
|
config.fri_config.rate_bits,
|
||||||
config.zero_knowledge && PlonkOracle::ZS_PARTIAL_PRODUCTS.blinding,
|
config.zero_knowledge && PlonkOracle::ZS_PARTIAL_PRODUCTS.blinding,
|
||||||
@ -114,14 +127,14 @@ pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D:
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
challenger.observe_cap(&partial_products_and_zs_commitment.merkle_tree.cap);
|
challenger.observe_cap::<HCO, C::Hasher>(&partial_products_and_zs_commitment.merkle_tree.cap);
|
||||||
|
|
||||||
let alphas = challenger.get_n_challenges(num_challenges);
|
let alphas = challenger.get_n_challenges(num_challenges);
|
||||||
|
|
||||||
let quotient_polys = timed!(
|
let quotient_polys = timed!(
|
||||||
timing,
|
timing,
|
||||||
"compute quotient polys",
|
"compute quotient polys",
|
||||||
compute_quotient_polys(
|
compute_quotient_polys::<F, HCO, HCI, C, D>(
|
||||||
common_data,
|
common_data,
|
||||||
prover_data,
|
prover_data,
|
||||||
&public_inputs_hash,
|
&public_inputs_hash,
|
||||||
@ -152,7 +165,7 @@ pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D:
|
|||||||
let quotient_polys_commitment = timed!(
|
let quotient_polys_commitment = timed!(
|
||||||
timing,
|
timing,
|
||||||
"commit to quotient polys",
|
"commit to quotient polys",
|
||||||
PolynomialBatch::from_coeffs(
|
PolynomialBatch::<F, HCO, HCI, C, D>::from_coeffs(
|
||||||
all_quotient_poly_chunks,
|
all_quotient_poly_chunks,
|
||||||
config.fri_config.rate_bits,
|
config.fri_config.rate_bits,
|
||||||
config.zero_knowledge && PlonkOracle::QUOTIENT.blinding,
|
config.zero_knowledge && PlonkOracle::QUOTIENT.blinding,
|
||||||
@ -162,7 +175,7 @@ pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D:
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
challenger.observe_cap("ient_polys_commitment.merkle_tree.cap);
|
challenger.observe_cap::<HCO, C::Hasher>("ient_polys_commitment.merkle_tree.cap);
|
||||||
|
|
||||||
let zeta = challenger.get_extension_challenge::<D>();
|
let zeta = challenger.get_extension_challenge::<D>();
|
||||||
// To avoid leaking witness data, we want to ensure that our opening locations, `zeta` and
|
// To avoid leaking witness data, we want to ensure that our opening locations, `zeta` and
|
||||||
@ -177,7 +190,7 @@ pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D:
|
|||||||
let openings = timed!(
|
let openings = timed!(
|
||||||
timing,
|
timing,
|
||||||
"construct the opening set",
|
"construct the opening set",
|
||||||
OpeningSet::new(
|
OpeningSet::new::<HCO, HCI, C>(
|
||||||
zeta,
|
zeta,
|
||||||
g,
|
g,
|
||||||
&prover_data.constants_sigmas_commitment,
|
&prover_data.constants_sigmas_commitment,
|
||||||
@ -192,7 +205,7 @@ pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D:
|
|||||||
let opening_proof = timed!(
|
let opening_proof = timed!(
|
||||||
timing,
|
timing,
|
||||||
"compute opening proofs",
|
"compute opening proofs",
|
||||||
PolynomialBatch::prove_openings(
|
PolynomialBatch::<F, HCO, HCI, C, D>::prove_openings(
|
||||||
&common_data.get_fri_instance(zeta),
|
&common_data.get_fri_instance(zeta),
|
||||||
&[
|
&[
|
||||||
&prover_data.constants_sigmas_commitment,
|
&prover_data.constants_sigmas_commitment,
|
||||||
@ -206,14 +219,14 @@ pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D:
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let proof = Proof {
|
let proof = Proof::<F, HCO, HCI, C, D> {
|
||||||
wires_cap: wires_commitment.merkle_tree.cap,
|
wires_cap: wires_commitment.merkle_tree.cap,
|
||||||
plonk_zs_partial_products_cap: partial_products_and_zs_commitment.merkle_tree.cap,
|
plonk_zs_partial_products_cap: partial_products_and_zs_commitment.merkle_tree.cap,
|
||||||
quotient_polys_cap: quotient_polys_commitment.merkle_tree.cap,
|
quotient_polys_cap: quotient_polys_commitment.merkle_tree.cap,
|
||||||
openings,
|
openings,
|
||||||
opening_proof,
|
opening_proof,
|
||||||
};
|
};
|
||||||
Ok(ProofWithPublicInputs {
|
Ok(ProofWithPublicInputs::<F, HCO, HCI, C, D> {
|
||||||
proof,
|
proof,
|
||||||
public_inputs,
|
public_inputs,
|
||||||
})
|
})
|
||||||
@ -222,13 +235,15 @@ pub fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D:
|
|||||||
/// Compute the partial products used in the `Z` polynomials.
|
/// Compute the partial products used in the `Z` polynomials.
|
||||||
fn all_wires_permutation_partial_products<
|
fn all_wires_permutation_partial_products<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
witness: &MatrixWitness<F>,
|
witness: &MatrixWitness<F>,
|
||||||
betas: &[F],
|
betas: &[F],
|
||||||
gammas: &[F],
|
gammas: &[F],
|
||||||
prover_data: &ProverOnlyCircuitData<F, C, D>,
|
prover_data: &ProverOnlyCircuitData<F, HCO, HCI, C, D>,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> Vec<Vec<PolynomialValues<F>>> {
|
) -> Vec<Vec<PolynomialValues<F>>> {
|
||||||
(0..common_data.config.num_challenges)
|
(0..common_data.config.num_challenges)
|
||||||
@ -249,13 +264,15 @@ fn all_wires_permutation_partial_products<
|
|||||||
/// where `f, g` are the products in the definition of `Z`: `Z(g^i) = f / g`.
|
/// where `f, g` are the products in the definition of `Z`: `Z(g^i) = f / g`.
|
||||||
fn wires_permutation_partial_products_and_zs<
|
fn wires_permutation_partial_products_and_zs<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
witness: &MatrixWitness<F>,
|
witness: &MatrixWitness<F>,
|
||||||
beta: F,
|
beta: F,
|
||||||
gamma: F,
|
gamma: F,
|
||||||
prover_data: &ProverOnlyCircuitData<F, C, D>,
|
prover_data: &ProverOnlyCircuitData<F, HCO, HCI, C, D>,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> Vec<PolynomialValues<F>> {
|
) -> Vec<PolynomialValues<F>> {
|
||||||
let degree = common_data.quotient_degree_factor;
|
let degree = common_data.quotient_degree_factor;
|
||||||
@ -311,14 +328,16 @@ const BATCH_SIZE: usize = 32;
|
|||||||
fn compute_quotient_polys<
|
fn compute_quotient_polys<
|
||||||
'a,
|
'a,
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
prover_data: &'a ProverOnlyCircuitData<F, C, D>,
|
prover_data: &'a ProverOnlyCircuitData<F, HCO, HCI, C, D>,
|
||||||
public_inputs_hash: &<<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
|
public_inputs_hash: &<<C as GenericConfig<HCO, HCI, D>>::InnerHasher as Hasher<F, HCI>>::Hash,
|
||||||
wires_commitment: &'a PolynomialBatch<F, C, D>,
|
wires_commitment: &'a PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
zs_partial_products_commitment: &'a PolynomialBatch<F, C, D>,
|
zs_partial_products_commitment: &'a PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
betas: &[F],
|
betas: &[F],
|
||||||
gammas: &[F],
|
gammas: &[F],
|
||||||
alphas: &[F],
|
alphas: &[F],
|
||||||
|
|||||||
@ -2,17 +2,20 @@ use anyhow::ensure;
|
|||||||
|
|
||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::plonk::circuit_data::CommonCircuitData;
|
use crate::plonk::circuit_data::CommonCircuitData;
|
||||||
use crate::plonk::config::GenericConfig;
|
use crate::plonk::config::GenericConfig;
|
||||||
use crate::plonk::proof::{OpeningSet, Proof, ProofWithPublicInputs};
|
use crate::plonk::proof::{OpeningSet, Proof, ProofWithPublicInputs};
|
||||||
|
|
||||||
pub(crate) fn validate_proof_with_pis_shape<F, C, const D: usize>(
|
pub(crate) fn validate_proof_with_pis_shape<F, HCO, HCI, C, const D: usize>(
|
||||||
proof_with_pis: &ProofWithPublicInputs<F, C, D>,
|
proof_with_pis: &ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> anyhow::Result<()>
|
) -> anyhow::Result<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let ProofWithPublicInputs {
|
let ProofWithPublicInputs {
|
||||||
proof,
|
proof,
|
||||||
@ -26,13 +29,15 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_proof_shape<F, C, const D: usize>(
|
fn validate_proof_shape<F, HCO, HCI, C, const D: usize>(
|
||||||
proof: &Proof<F, C, D>,
|
proof: &Proof<F, HCO, HCI, C, D>,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> anyhow::Result<()>
|
) -> anyhow::Result<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let config = &common_data.config;
|
let config = &common_data.config;
|
||||||
let Proof {
|
let Proof {
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use crate::field::extension::Extendable;
|
|||||||
use crate::field::types::Field;
|
use crate::field::types::Field;
|
||||||
use crate::fri::verifier::verify_fri_proof;
|
use crate::fri::verifier::verify_fri_proof;
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData};
|
use crate::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData};
|
||||||
use crate::plonk::config::{GenericConfig, Hasher};
|
use crate::plonk::config::{GenericConfig, Hasher};
|
||||||
use crate::plonk::plonk_common::reduce_with_powers;
|
use crate::plonk::plonk_common::reduce_with_powers;
|
||||||
@ -12,11 +13,21 @@ use crate::plonk::validate_shape::validate_proof_with_pis_shape;
|
|||||||
use crate::plonk::vanishing_poly::eval_vanishing_poly;
|
use crate::plonk::vanishing_poly::eval_vanishing_poly;
|
||||||
use crate::plonk::vars::EvaluationVars;
|
use crate::plonk::vars::EvaluationVars;
|
||||||
|
|
||||||
pub(crate) fn verify<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
pub(crate) fn verify<
|
||||||
proof_with_pis: ProofWithPublicInputs<F, C, D>,
|
F: RichField + Extendable<D>,
|
||||||
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
|
proof_with_pis: ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
|
verifier_data: &VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
validate_proof_with_pis_shape(&proof_with_pis, common_data)?;
|
validate_proof_with_pis_shape(&proof_with_pis, common_data)?;
|
||||||
|
|
||||||
let public_inputs_hash = proof_with_pis.get_public_inputs_hash();
|
let public_inputs_hash = proof_with_pis.get_public_inputs_hash();
|
||||||
@ -26,7 +37,7 @@ pub(crate) fn verify<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, c
|
|||||||
common_data,
|
common_data,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
verify_with_challenges(
|
verify_with_challenges::<F, HCO, HCI, C, D>(
|
||||||
proof_with_pis.proof,
|
proof_with_pis.proof,
|
||||||
public_inputs_hash,
|
public_inputs_hash,
|
||||||
challenges,
|
challenges,
|
||||||
@ -37,15 +48,20 @@ pub(crate) fn verify<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, c
|
|||||||
|
|
||||||
pub(crate) fn verify_with_challenges<
|
pub(crate) fn verify_with_challenges<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
proof: Proof<F, C, D>,
|
proof: Proof<F, HCO, HCI, C, D>,
|
||||||
public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
|
public_inputs_hash: <<C as GenericConfig<HCO, HCI, D>>::InnerHasher as Hasher<F, HCI>>::Hash,
|
||||||
challenges: ProofChallenges<F, D>,
|
challenges: ProofChallenges<F, D>,
|
||||||
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
verifier_data: &VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
{
|
||||||
let local_constants = &proof.openings.constants;
|
let local_constants = &proof.openings.constants;
|
||||||
let local_wires = &proof.openings.wires;
|
let local_wires = &proof.openings.wires;
|
||||||
let vars = EvaluationVars {
|
let vars = EvaluationVars {
|
||||||
@ -97,7 +113,7 @@ pub(crate) fn verify_with_challenges<
|
|||||||
proof.quotient_polys_cap,
|
proof.quotient_polys_cap,
|
||||||
];
|
];
|
||||||
|
|
||||||
verify_fri_proof::<F, C, D>(
|
verify_fri_proof::<F, HCO, HCI, C, D>(
|
||||||
&common_data.get_fri_instance(challenges.plonk_zeta),
|
&common_data.get_fri_instance(challenges.plonk_zeta),
|
||||||
&proof.openings.to_fri_openings(),
|
&proof.openings.to_fri_openings(),
|
||||||
&challenges.fri_challenges,
|
&challenges.fri_challenges,
|
||||||
|
|||||||
@ -8,6 +8,7 @@ use crate::fri::proof::{
|
|||||||
};
|
};
|
||||||
use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
|
use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
|
||||||
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_proofs::MerkleProofTarget;
|
use crate::hash::merkle_proofs::MerkleProofTarget;
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
@ -20,7 +21,11 @@ use crate::with_context;
|
|||||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||||
/// Verify `proof0` if `condition` else verify `proof1`.
|
/// Verify `proof0` if `condition` else verify `proof1`.
|
||||||
/// `proof0` and `proof1` are assumed to use the same `CommonCircuitData`.
|
/// `proof0` and `proof1` are assumed to use the same `CommonCircuitData`.
|
||||||
pub fn conditionally_verify_proof<C: GenericConfig<D, F = F>>(
|
pub fn conditionally_verify_proof<
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
condition: BoolTarget,
|
condition: BoolTarget,
|
||||||
proof_with_pis0: &ProofWithPublicInputsTarget<D>,
|
proof_with_pis0: &ProofWithPublicInputsTarget<D>,
|
||||||
@ -29,7 +34,9 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
inner_verifier_data1: &VerifierCircuitTarget,
|
inner_verifier_data1: &VerifierCircuitTarget,
|
||||||
inner_common_data: &CommonCircuitData<F, D>,
|
inner_common_data: &CommonCircuitData<F, D>,
|
||||||
) where
|
) where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let selected_proof =
|
let selected_proof =
|
||||||
self.select_proof_with_pis(condition, proof_with_pis0, proof_with_pis1);
|
self.select_proof_with_pis(condition, proof_with_pis0, proof_with_pis1);
|
||||||
@ -46,11 +53,19 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.verify_proof::<C>(&selected_proof, &selected_verifier_data, inner_common_data);
|
self.verify_proof::<HCO, HCI, C>(
|
||||||
|
&selected_proof,
|
||||||
|
&selected_verifier_data,
|
||||||
|
inner_common_data,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Conditionally verify a proof with a new generated dummy proof.
|
/// Conditionally verify a proof with a new generated dummy proof.
|
||||||
pub fn conditionally_verify_proof_or_dummy<C: GenericConfig<D, F = F> + 'static>(
|
pub fn conditionally_verify_proof_or_dummy<
|
||||||
|
HCO,
|
||||||
|
HCI,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F> + 'static,
|
||||||
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
condition: BoolTarget,
|
condition: BoolTarget,
|
||||||
proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
||||||
@ -58,11 +73,15 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
inner_common_data: &CommonCircuitData<F, D>,
|
inner_common_data: &CommonCircuitData<F, D>,
|
||||||
) -> anyhow::Result<()>
|
) -> anyhow::Result<()>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCO: HashConfig + 'static,
|
||||||
|
HCI: HashConfig + 'static,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let (dummy_proof_with_pis_target, dummy_verifier_data_target) =
|
let (dummy_proof_with_pis_target, dummy_verifier_data_target) =
|
||||||
self.dummy_proof_and_vk::<C>(inner_common_data)?;
|
self.dummy_proof_and_vk::<HCO, HCI, C>(inner_common_data)?;
|
||||||
self.conditionally_verify_proof::<C>(
|
self.conditionally_verify_proof::<HCO, HCI, C>(
|
||||||
condition,
|
condition,
|
||||||
proof_with_pis,
|
proof_with_pis,
|
||||||
inner_verifier_data,
|
inner_verifier_data,
|
||||||
@ -342,7 +361,7 @@ mod tests {
|
|||||||
use crate::gates::noop::NoopGate;
|
use crate::gates::noop::NoopGate;
|
||||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use crate::recursion::dummy_circuit::{dummy_circuit, dummy_proof};
|
use crate::recursion::dummy_circuit::{dummy_circuit, dummy_proof};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -350,7 +369,9 @@ mod tests {
|
|||||||
init_logger();
|
init_logger();
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
|
|
||||||
// Generate proof.
|
// Generate proof.
|
||||||
@ -363,7 +384,7 @@ mod tests {
|
|||||||
for _ in 0..64 {
|
for _ in 0..64 {
|
||||||
builder.add_gate(NoopGate, vec![]);
|
builder.add_gate(NoopGate, vec![]);
|
||||||
}
|
}
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
data.verify(proof.clone())?;
|
data.verify(proof.clone())?;
|
||||||
|
|
||||||
@ -377,7 +398,7 @@ mod tests {
|
|||||||
let pt = builder.add_virtual_proof_with_pis(&data.common);
|
let pt = builder.add_virtual_proof_with_pis(&data.common);
|
||||||
pw.set_proof_with_pis_target(&pt, &proof);
|
pw.set_proof_with_pis_target(&pt, &proof);
|
||||||
let dummy_pt = builder.add_virtual_proof_with_pis(&data.common);
|
let dummy_pt = builder.add_virtual_proof_with_pis(&data.common);
|
||||||
pw.set_proof_with_pis_target::<C, D>(&dummy_pt, &dummy_proof);
|
pw.set_proof_with_pis_target::<HCO, HCI, C, D>(&dummy_pt, &dummy_proof);
|
||||||
let inner_data =
|
let inner_data =
|
||||||
builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height);
|
builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height);
|
||||||
pw.set_verifier_data_target(&inner_data, &data.verifier_only);
|
pw.set_verifier_data_target(&inner_data, &data.verifier_only);
|
||||||
@ -385,7 +406,7 @@ mod tests {
|
|||||||
builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height);
|
builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height);
|
||||||
pw.set_verifier_data_target(&dummy_inner_data, &dummy_data.verifier_only);
|
pw.set_verifier_data_target(&dummy_inner_data, &dummy_data.verifier_only);
|
||||||
let b = builder.constant_bool(F::rand().0 % 2 == 0);
|
let b = builder.constant_bool(F::rand().0 % 2 == 0);
|
||||||
builder.conditionally_verify_proof::<C>(
|
builder.conditionally_verify_proof::<HCO, HCI, C>(
|
||||||
b,
|
b,
|
||||||
&pt,
|
&pt,
|
||||||
&inner_data,
|
&inner_data,
|
||||||
@ -395,7 +416,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
builder.print_gate_counts(100);
|
builder.print_gate_counts(100);
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
data.verify(proof)
|
data.verify(proof)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use anyhow::{ensure, Result};
|
|||||||
|
|
||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField};
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
@ -13,10 +14,12 @@ use crate::plonk::circuit_data::{
|
|||||||
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
|
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||||
use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||||
|
|
||||||
impl<C: GenericConfig<D>, const D: usize> VerifierOnlyCircuitData<C, D> {
|
impl<HCO: HashConfig, HCI: HashConfig, C: GenericConfig<HCO, HCI, D>, const D: usize>
|
||||||
|
VerifierOnlyCircuitData<HCO, HCI, C, D>
|
||||||
|
{
|
||||||
fn from_slice(slice: &[C::F], common_data: &CommonCircuitData<C::F, D>) -> Result<Self>
|
fn from_slice(slice: &[C::F], common_data: &CommonCircuitData<C::F, D>) -> Result<Self>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<C::F>,
|
C::Hasher: AlgebraicHasher<C::F, HCO>,
|
||||||
{
|
{
|
||||||
// The structure of the public inputs is `[..., circuit_digest, constants_sigmas_cap]`.
|
// The structure of the public inputs is `[..., circuit_digest, constants_sigmas_cap]`.
|
||||||
let cap_len = common_data.config.fri_config.num_cap_elements();
|
let cap_len = common_data.config.fri_config.num_cap_elements();
|
||||||
@ -79,7 +82,11 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
/// that the verification key matches.
|
/// that the verification key matches.
|
||||||
///
|
///
|
||||||
/// WARNING: Do not register any public input after calling this! TODO: relax this
|
/// WARNING: Do not register any public input after calling this! TODO: relax this
|
||||||
pub fn conditionally_verify_cyclic_proof<C: GenericConfig<D, F = F>>(
|
pub fn conditionally_verify_cyclic_proof<
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
condition: BoolTarget,
|
condition: BoolTarget,
|
||||||
cyclic_proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
cyclic_proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
||||||
@ -88,7 +95,9 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let verifier_data = self
|
let verifier_data = self
|
||||||
.verifier_data_public_input
|
.verifier_data_public_input
|
||||||
@ -116,7 +125,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Verify the cyclic proof if `condition` is set to true, otherwise verify the other proof.
|
// Verify the cyclic proof if `condition` is set to true, otherwise verify the other proof.
|
||||||
self.conditionally_verify_proof::<C>(
|
self.conditionally_verify_proof::<HCO, HCI, C>(
|
||||||
condition,
|
condition,
|
||||||
cyclic_proof_with_pis,
|
cyclic_proof_with_pis,
|
||||||
&verifier_data,
|
&verifier_data,
|
||||||
@ -133,18 +142,26 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn conditionally_verify_cyclic_proof_or_dummy<C: GenericConfig<D, F = F> + 'static>(
|
pub fn conditionally_verify_cyclic_proof_or_dummy<
|
||||||
|
HCO,
|
||||||
|
HCI,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F> + 'static,
|
||||||
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
condition: BoolTarget,
|
condition: BoolTarget,
|
||||||
cyclic_proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
cyclic_proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCO: HashConfig + 'static,
|
||||||
|
HCI: HashConfig + 'static,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let (dummy_proof_with_pis_target, dummy_verifier_data_target) =
|
let (dummy_proof_with_pis_target, dummy_verifier_data_target) =
|
||||||
self.dummy_proof_and_vk::<C>(common_data)?;
|
self.dummy_proof_and_vk::<HCO, HCI, C>(common_data)?;
|
||||||
self.conditionally_verify_cyclic_proof::<C>(
|
self.conditionally_verify_cyclic_proof::<HCO, HCI, C>(
|
||||||
condition,
|
condition,
|
||||||
cyclic_proof_with_pis,
|
cyclic_proof_with_pis,
|
||||||
&dummy_proof_with_pis_target,
|
&dummy_proof_with_pis_target,
|
||||||
@ -159,17 +176,22 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
/// Checks that the purported verifier data in the public inputs match the real verifier data.
|
/// Checks that the purported verifier data in the public inputs match the real verifier data.
|
||||||
pub fn check_cyclic_proof_verifier_data<
|
pub fn check_cyclic_proof_verifier_data<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
proof: &ProofWithPublicInputs<F, C, D>,
|
proof: &ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
verifier_data: &VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let pis = VerifierOnlyCircuitData::<C, D>::from_slice(&proof.public_inputs, common_data)?;
|
let pis =
|
||||||
|
VerifierOnlyCircuitData::<HCO, HCI, C, D>::from_slice(&proof.public_inputs, common_data)?;
|
||||||
ensure!(verifier_data.constants_sigmas_cap == pis.constants_sigmas_cap);
|
ensure!(verifier_data.constants_sigmas_cap == pis.constants_sigmas_cap);
|
||||||
ensure!(verifier_data.circuit_digest == pis.circuit_digest);
|
ensure!(verifier_data.circuit_digest == pis.circuit_digest);
|
||||||
|
|
||||||
@ -184,45 +206,51 @@ mod tests {
|
|||||||
use crate::field::types::{Field, PrimeField64};
|
use crate::field::types::{Field, PrimeField64};
|
||||||
use crate::gates::noop::NoopGate;
|
use crate::gates::noop::NoopGate;
|
||||||
use crate::hash::hash_types::{HashOutTarget, RichField};
|
use crate::hash::hash_types::{HashOutTarget, RichField};
|
||||||
use crate::hash::hashing::hash_n_to_hash_no_pad;
|
use crate::hash::hashing::{hash_n_to_hash_no_pad, HashConfig};
|
||||||
use crate::hash::poseidon::{PoseidonHash, PoseidonPermutation};
|
use crate::hash::poseidon::{PoseidonHash, PoseidonPermutation};
|
||||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData};
|
use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData};
|
||||||
use crate::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{
|
||||||
|
AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig,
|
||||||
|
};
|
||||||
use crate::recursion::cyclic_recursion::check_cyclic_proof_verifier_data;
|
use crate::recursion::cyclic_recursion::check_cyclic_proof_verifier_data;
|
||||||
use crate::recursion::dummy_circuit::cyclic_base_proof;
|
use crate::recursion::dummy_circuit::cyclic_base_proof;
|
||||||
|
|
||||||
// Generates `CommonCircuitData` usable for recursion.
|
// Generates `CommonCircuitData` usable for recursion.
|
||||||
fn common_data_for_recursion<
|
fn common_data_for_recursion<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>() -> CommonCircuitData<F, D>
|
>() -> CommonCircuitData<F, D>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let builder = CircuitBuilder::<F, D>::new(config);
|
let builder = CircuitBuilder::<F, D>::new(config);
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
let proof = builder.add_virtual_proof_with_pis(&data.common);
|
let proof = builder.add_virtual_proof_with_pis(&data.common);
|
||||||
let verifier_data =
|
let verifier_data =
|
||||||
builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height);
|
builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height);
|
||||||
builder.verify_proof::<C>(&proof, &verifier_data, &data.common);
|
builder.verify_proof::<HCO, HCI, C>(&proof, &verifier_data, &data.common);
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
let proof = builder.add_virtual_proof_with_pis(&data.common);
|
let proof = builder.add_virtual_proof_with_pis(&data.common);
|
||||||
let verifier_data =
|
let verifier_data =
|
||||||
builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height);
|
builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height);
|
||||||
builder.verify_proof::<C>(&proof, &verifier_data, &data.common);
|
builder.verify_proof::<HCO, HCI, C>(&proof, &verifier_data, &data.common);
|
||||||
while builder.num_gates() < 1 << 12 {
|
while builder.num_gates() < 1 << 12 {
|
||||||
builder.add_gate(NoopGate, vec![]);
|
builder.add_gate(NoopGate, vec![]);
|
||||||
}
|
}
|
||||||
builder.build::<C>().common
|
builder.build::<HCO, HCI, C>().common
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uses cyclic recursion to build a hash chain.
|
/// Uses cyclic recursion to build a hash chain.
|
||||||
@ -235,7 +263,9 @@ mod tests {
|
|||||||
fn test_cyclic_recursion() -> Result<()> {
|
fn test_cyclic_recursion() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
@ -245,12 +275,13 @@ mod tests {
|
|||||||
let initial_hash_target = builder.add_virtual_hash();
|
let initial_hash_target = builder.add_virtual_hash();
|
||||||
builder.register_public_inputs(&initial_hash_target.elements);
|
builder.register_public_inputs(&initial_hash_target.elements);
|
||||||
let current_hash_in = builder.add_virtual_hash();
|
let current_hash_in = builder.add_virtual_hash();
|
||||||
let current_hash_out =
|
let current_hash_out = builder.hash_n_to_hash_no_pad::<PoseidonHashConfig, PoseidonHash>(
|
||||||
builder.hash_n_to_hash_no_pad::<PoseidonHash>(current_hash_in.elements.to_vec());
|
current_hash_in.elements.to_vec(),
|
||||||
|
);
|
||||||
builder.register_public_inputs(¤t_hash_out.elements);
|
builder.register_public_inputs(¤t_hash_out.elements);
|
||||||
let counter = builder.add_virtual_public_input();
|
let counter = builder.add_virtual_public_input();
|
||||||
|
|
||||||
let mut common_data = common_data_for_recursion::<F, C, D>();
|
let mut common_data = common_data_for_recursion::<F, HCO, HCI, C, D>();
|
||||||
let verifier_data_target = builder.add_verifier_data_public_inputs();
|
let verifier_data_target = builder.add_verifier_data_public_inputs();
|
||||||
common_data.num_public_inputs = builder.num_public_inputs();
|
common_data.num_public_inputs = builder.num_public_inputs();
|
||||||
|
|
||||||
@ -277,19 +308,19 @@ mod tests {
|
|||||||
let new_counter = builder.mul_add(condition.target, inner_cyclic_counter, one);
|
let new_counter = builder.mul_add(condition.target, inner_cyclic_counter, one);
|
||||||
builder.connect(counter, new_counter);
|
builder.connect(counter, new_counter);
|
||||||
|
|
||||||
builder.conditionally_verify_cyclic_proof_or_dummy::<C>(
|
builder.conditionally_verify_cyclic_proof_or_dummy::<HCO, HCI, C>(
|
||||||
condition,
|
condition,
|
||||||
&inner_cyclic_proof_with_pis,
|
&inner_cyclic_proof_with_pis,
|
||||||
&common_data,
|
&common_data,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let cyclic_circuit_data = builder.build::<C>();
|
let cyclic_circuit_data = builder.build::<HCO, HCI, C>();
|
||||||
|
|
||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::new();
|
||||||
let initial_hash = [F::ZERO, F::ONE, F::TWO, F::from_canonical_usize(3)];
|
let initial_hash = [F::ZERO, F::ONE, F::TWO, F::from_canonical_usize(3)];
|
||||||
let initial_hash_pis = initial_hash.into_iter().enumerate().collect();
|
let initial_hash_pis = initial_hash.into_iter().enumerate().collect();
|
||||||
pw.set_bool_target(condition, false);
|
pw.set_bool_target(condition, false);
|
||||||
pw.set_proof_with_pis_target::<C, D>(
|
pw.set_proof_with_pis_target::<HCO, HCI, C, D>(
|
||||||
&inner_cyclic_proof_with_pis,
|
&inner_cyclic_proof_with_pis,
|
||||||
&cyclic_base_proof(
|
&cyclic_base_proof(
|
||||||
&common_data,
|
&common_data,
|
||||||
@ -347,7 +378,8 @@ mod tests {
|
|||||||
fn iterate_poseidon<F: RichField>(initial_state: [F; 4], n: usize) -> [F; 4] {
|
fn iterate_poseidon<F: RichField>(initial_state: [F; 4], n: usize) -> [F; 4] {
|
||||||
let mut current = initial_state;
|
let mut current = initial_state;
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
current = hash_n_to_hash_no_pad::<F, PoseidonPermutation>(¤t).elements;
|
current = hash_n_to_hash_no_pad::<F, PoseidonHashConfig, PoseidonPermutation>(¤t)
|
||||||
|
.elements;
|
||||||
}
|
}
|
||||||
current
|
current
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use plonky2_util::ceil_div_usize;
|
|||||||
|
|
||||||
use crate::gates::noop::NoopGate;
|
use crate::gates::noop::NoopGate;
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::witness::{PartialWitness, PartitionWitness, WitnessWrite};
|
use crate::iop::witness::{PartialWitness, PartitionWitness, WitnessWrite};
|
||||||
@ -22,15 +23,19 @@ use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
|||||||
/// public inputs which encode the cyclic verification key must be set properly, and this method
|
/// public inputs which encode the cyclic verification key must be set properly, and this method
|
||||||
/// takes care of that. It also allows the user to specify any other public inputs which should be
|
/// takes care of that. It also allows the user to specify any other public inputs which should be
|
||||||
/// set in this base proof.
|
/// set in this base proof.
|
||||||
pub fn cyclic_base_proof<F, C, const D: usize>(
|
pub fn cyclic_base_proof<F, HCO, HCI, C, const D: usize>(
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
verifier_data: &VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
mut nonzero_public_inputs: HashMap<usize, F>,
|
mut nonzero_public_inputs: HashMap<usize, F>,
|
||||||
) -> ProofWithPublicInputs<F, C, D>
|
) -> ProofWithPublicInputs<F, HCO, HCI, C, D>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
C::Hasher: AlgebraicHasher<C::F>,
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
C::Hasher: AlgebraicHasher<C::F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let pis_len = common_data.num_public_inputs;
|
let pis_len = common_data.num_public_inputs;
|
||||||
let cap_elements = common_data.config.fri_config.num_cap_elements();
|
let cap_elements = common_data.config.fri_config.num_cap_elements();
|
||||||
@ -46,19 +51,29 @@ where
|
|||||||
|
|
||||||
// TODO: A bit wasteful to build a dummy circuit here. We could potentially use a proof that
|
// TODO: A bit wasteful to build a dummy circuit here. We could potentially use a proof that
|
||||||
// just consists of zeros, apart from public inputs.
|
// just consists of zeros, apart from public inputs.
|
||||||
dummy_proof(&dummy_circuit(common_data), nonzero_public_inputs).unwrap()
|
dummy_proof::<F, HCO, HCI, C, D>(
|
||||||
|
&dummy_circuit::<F, HCO, HCI, C, D>(common_data),
|
||||||
|
nonzero_public_inputs,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a proof for a dummy circuit. The `public_inputs` parameter let the caller specify
|
/// Generate a proof for a dummy circuit. The `public_inputs` parameter let the caller specify
|
||||||
/// certain public inputs (identified by their indices) which should be given specific values.
|
/// certain public inputs (identified by their indices) which should be given specific values.
|
||||||
/// The rest will default to zero.
|
/// The rest will default to zero.
|
||||||
pub(crate) fn dummy_proof<F, C, const D: usize>(
|
pub(crate) fn dummy_proof<
|
||||||
circuit: &CircuitData<F, C, D>,
|
|
||||||
nonzero_public_inputs: HashMap<usize, F>,
|
|
||||||
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>>
|
|
||||||
where
|
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
|
circuit: &CircuitData<F, HCO, HCI, C, D>,
|
||||||
|
nonzero_public_inputs: HashMap<usize, F>,
|
||||||
|
) -> anyhow::Result<ProofWithPublicInputs<F, HCO, HCI, C, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::new();
|
||||||
for i in 0..circuit.common.num_public_inputs {
|
for i in 0..circuit.common.num_public_inputs {
|
||||||
@ -71,11 +86,17 @@ where
|
|||||||
/// Generate a circuit matching a given `CommonCircuitData`.
|
/// Generate a circuit matching a given `CommonCircuitData`.
|
||||||
pub(crate) fn dummy_circuit<
|
pub(crate) fn dummy_circuit<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> CircuitData<F, C, D> {
|
) -> CircuitData<F, HCO, HCI, C, D>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let config = common_data.config.clone();
|
let config = common_data.config.clone();
|
||||||
assert!(
|
assert!(
|
||||||
!common_data.config.zero_knowledge,
|
!common_data.config.zero_knowledge,
|
||||||
@ -98,21 +119,26 @@ pub(crate) fn dummy_circuit<
|
|||||||
builder.add_virtual_public_input();
|
builder.add_virtual_public_input();
|
||||||
}
|
}
|
||||||
|
|
||||||
let circuit = builder.build::<C>();
|
let circuit = builder.build::<HCO, HCI, C>();
|
||||||
assert_eq!(&circuit.common, common_data);
|
assert_eq!(&circuit.common, common_data);
|
||||||
circuit
|
circuit
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||||
pub(crate) fn dummy_proof_and_vk<C: GenericConfig<D, F = F> + 'static>(
|
pub(crate) fn dummy_proof_and_vk<HCO, HCI, C: GenericConfig<HCO, HCI, D, F = F> + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> anyhow::Result<(ProofWithPublicInputsTarget<D>, VerifierCircuitTarget)>
|
) -> anyhow::Result<(ProofWithPublicInputsTarget<D>, VerifierCircuitTarget)>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCO: HashConfig + 'static,
|
||||||
|
HCI: HashConfig + 'static,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let dummy_circuit = dummy_circuit::<F, C, D>(common_data);
|
let dummy_circuit = dummy_circuit::<F, HCO, HCI, C, D>(common_data);
|
||||||
let dummy_proof_with_pis = dummy_proof(&dummy_circuit, HashMap::new())?;
|
let dummy_proof_with_pis =
|
||||||
|
dummy_proof::<F, HCO, HCI, C, D>(&dummy_circuit, HashMap::new())?;
|
||||||
let dummy_proof_with_pis_target = self.add_virtual_proof_with_pis(common_data);
|
let dummy_proof_with_pis_target = self.add_virtual_proof_with_pis(common_data);
|
||||||
let dummy_verifier_data_target =
|
let dummy_verifier_data_target =
|
||||||
self.add_virtual_verifier_data(self.config.fri_config.cap_height);
|
self.add_virtual_verifier_data(self.config.fri_config.cap_height);
|
||||||
@ -129,22 +155,26 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct DummyProofGenerator<F, C, const D: usize>
|
pub(crate) struct DummyProofGenerator<F, HCO, HCI, C, const D: usize>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
pub(crate) proof_with_pis_target: ProofWithPublicInputsTarget<D>,
|
pub(crate) proof_with_pis_target: ProofWithPublicInputsTarget<D>,
|
||||||
pub(crate) proof_with_pis: ProofWithPublicInputs<F, C, D>,
|
pub(crate) proof_with_pis: ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
pub(crate) verifier_data_target: VerifierCircuitTarget,
|
pub(crate) verifier_data_target: VerifierCircuitTarget,
|
||||||
pub(crate) verifier_data: VerifierOnlyCircuitData<C, D>,
|
pub(crate) verifier_data: VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, C, const D: usize> SimpleGenerator<F> for DummyProofGenerator<F, C, D>
|
impl<F, HCO, HCI, C, const D: usize> SimpleGenerator<F> for DummyProofGenerator<F, HCO, HCI, C, D>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F> + 'static,
|
HCO: HashConfig + 'static,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCI: HashConfig + 'static,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F> + 'static,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
{
|
{
|
||||||
fn dependencies(&self) -> Vec<Target> {
|
fn dependencies(&self) -> Vec<Target> {
|
||||||
vec![]
|
vec![]
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
use crate::hash::hash_types::{HashOutTarget, RichField};
|
use crate::hash::hash_types::{HashOutTarget, RichField};
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::{CommonCircuitData, VerifierCircuitTarget};
|
use crate::plonk::circuit_data::{CommonCircuitData, VerifierCircuitTarget};
|
||||||
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
|
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||||
@ -14,28 +15,30 @@ use crate::with_context;
|
|||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||||
/// Recursively verifies an inner proof.
|
/// Recursively verifies an inner proof.
|
||||||
pub fn verify_proof<C: GenericConfig<D, F = F>>(
|
pub fn verify_proof<HCO: HashConfig, HCI: HashConfig, C: GenericConfig<HCO, HCI, D, F = F>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
||||||
inner_verifier_data: &VerifierCircuitTarget,
|
inner_verifier_data: &VerifierCircuitTarget,
|
||||||
inner_common_data: &CommonCircuitData<F, D>,
|
inner_common_data: &CommonCircuitData<F, D>,
|
||||||
) where
|
) where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
proof_with_pis.public_inputs.len(),
|
proof_with_pis.public_inputs.len(),
|
||||||
inner_common_data.num_public_inputs
|
inner_common_data.num_public_inputs
|
||||||
);
|
);
|
||||||
let public_inputs_hash =
|
let public_inputs_hash =
|
||||||
self.hash_n_to_hash_no_pad::<C::InnerHasher>(proof_with_pis.public_inputs.clone());
|
self.hash_n_to_hash_no_pad::<HCI, C::InnerHasher>(proof_with_pis.public_inputs.clone());
|
||||||
let challenges = proof_with_pis.get_challenges::<F, C>(
|
let challenges = proof_with_pis.get_challenges::<F, HCO, HCI, C>(
|
||||||
self,
|
self,
|
||||||
public_inputs_hash,
|
public_inputs_hash,
|
||||||
inner_verifier_data.circuit_digest,
|
inner_verifier_data.circuit_digest,
|
||||||
inner_common_data,
|
inner_common_data,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.verify_proof_with_challenges::<C>(
|
self.verify_proof_with_challenges::<HCO, HCI, C>(
|
||||||
&proof_with_pis.proof,
|
&proof_with_pis.proof,
|
||||||
public_inputs_hash,
|
public_inputs_hash,
|
||||||
challenges,
|
challenges,
|
||||||
@ -45,7 +48,11 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively verifies an inner proof.
|
/// Recursively verifies an inner proof.
|
||||||
fn verify_proof_with_challenges<C: GenericConfig<D, F = F>>(
|
fn verify_proof_with_challenges<
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
proof: &ProofTarget<D>,
|
proof: &ProofTarget<D>,
|
||||||
public_inputs_hash: HashOutTarget,
|
public_inputs_hash: HashOutTarget,
|
||||||
@ -53,7 +60,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
inner_verifier_data: &VerifierCircuitTarget,
|
inner_verifier_data: &VerifierCircuitTarget,
|
||||||
inner_common_data: &CommonCircuitData<F, D>,
|
inner_common_data: &CommonCircuitData<F, D>,
|
||||||
) where
|
) where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
let one = self.one_extension();
|
let one = self.one_extension();
|
||||||
|
|
||||||
@ -115,7 +123,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
with_context!(
|
with_context!(
|
||||||
self,
|
self,
|
||||||
"verify FRI proof",
|
"verify FRI proof",
|
||||||
self.verify_fri_proof::<C>(
|
self.verify_fri_proof::<HCO, HCI, C>(
|
||||||
&fri_instance,
|
&fri_instance,
|
||||||
&proof.openings.to_fri_openings(),
|
&proof.openings.to_fri_openings(),
|
||||||
&challenges.fri_challenges,
|
&challenges.fri_challenges,
|
||||||
@ -187,7 +195,10 @@ mod tests {
|
|||||||
use crate::gates::noop::NoopGate;
|
use crate::gates::noop::NoopGate;
|
||||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_data::{CircuitConfig, VerifierOnlyCircuitData};
|
use crate::plonk::circuit_data::{CircuitConfig, VerifierOnlyCircuitData};
|
||||||
use crate::plonk::config::{GenericConfig, KeccakGoldilocksConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{
|
||||||
|
GenericConfig, KeccakGoldilocksConfig, KeccakHashConfig, PoseidonGoldilocksConfig,
|
||||||
|
PoseidonHashConfig,
|
||||||
|
};
|
||||||
use crate::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs};
|
use crate::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs};
|
||||||
use crate::plonk::prover::prove;
|
use crate::plonk::prover::prove;
|
||||||
use crate::util::timing::TimingTree;
|
use crate::util::timing::TimingTree;
|
||||||
@ -197,12 +208,15 @@ mod tests {
|
|||||||
init_logger();
|
init_logger();
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
let config = CircuitConfig::standard_recursion_zk_config();
|
let config = CircuitConfig::standard_recursion_zk_config();
|
||||||
|
|
||||||
let (proof, vd, cd) = dummy_proof::<F, C, D>(&config, 4_000)?;
|
let (proof, vd, cd) = dummy_proof::<F, HCO, HCI, C, D>(&config, 4_000)?;
|
||||||
let (proof, vd, cd) =
|
let (proof, vd, cd) = recursive_proof::<F, HCO, HCI, HCO, HCI, C, C, D>(
|
||||||
recursive_proof::<F, C, C, D>(proof, vd, cd, &config, None, true, true)?;
|
proof, vd, cd, &config, None, true, true,
|
||||||
|
)?;
|
||||||
test_serialization(&proof, &vd, &cd)?;
|
test_serialization(&proof, &vd, &cd)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -213,22 +227,32 @@ mod tests {
|
|||||||
init_logger();
|
init_logger();
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
|
|
||||||
// Start with a degree 2^14 proof
|
// Start with a degree 2^14 proof
|
||||||
let (proof, vd, cd) = dummy_proof::<F, C, D>(&config, 16_000)?;
|
let (proof, vd, cd) = dummy_proof::<F, HCO, HCI, C, D>(&config, 16_000)?;
|
||||||
assert_eq!(cd.degree_bits(), 14);
|
assert_eq!(cd.degree_bits(), 14);
|
||||||
|
|
||||||
// Shrink it to 2^13.
|
// Shrink it to 2^13.
|
||||||
let (proof, vd, cd) =
|
let (proof, vd, cd) = recursive_proof::<F, HCO, HCI, HCO, HCI, C, C, D>(
|
||||||
recursive_proof::<F, C, C, D>(proof, vd, cd, &config, Some(13), false, false)?;
|
proof,
|
||||||
|
vd,
|
||||||
|
cd,
|
||||||
|
&config,
|
||||||
|
Some(13),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
assert_eq!(cd.degree_bits(), 13);
|
assert_eq!(cd.degree_bits(), 13);
|
||||||
|
|
||||||
// Shrink it to 2^12.
|
// Shrink it to 2^12.
|
||||||
let (proof, vd, cd) =
|
let (proof, vd, cd) = recursive_proof::<F, HCO, HCI, HCO, HCI, C, C, D>(
|
||||||
recursive_proof::<F, C, C, D>(proof, vd, cd, &config, None, true, true)?;
|
proof, vd, cd, &config, None, true, true,
|
||||||
|
)?;
|
||||||
assert_eq!(cd.degree_bits(), 12);
|
assert_eq!(cd.degree_bits(), 12);
|
||||||
|
|
||||||
test_serialization(&proof, &vd, &cd)?;
|
test_serialization(&proof, &vd, &cd)?;
|
||||||
@ -245,16 +269,28 @@ mod tests {
|
|||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type KC = KeccakGoldilocksConfig;
|
type KC = KeccakGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCCO = PoseidonHashConfig;
|
||||||
|
type HCCI = HCCO;
|
||||||
|
type HCKCO = KeccakHashConfig;
|
||||||
|
type HCKCI = HCCI;
|
||||||
|
type F = <C as GenericConfig<HCCO, HCCI, D>>::F;
|
||||||
|
|
||||||
let standard_config = CircuitConfig::standard_recursion_config();
|
let standard_config = CircuitConfig::standard_recursion_config();
|
||||||
|
|
||||||
// An initial dummy proof.
|
// An initial dummy proof.
|
||||||
let (proof, vd, cd) = dummy_proof::<F, C, D>(&standard_config, 4_000)?;
|
let (proof, vd, cd) = dummy_proof::<F, HCCO, HCCI, C, D>(&standard_config, 4_000)?;
|
||||||
assert_eq!(cd.degree_bits(), 12);
|
assert_eq!(cd.degree_bits(), 12);
|
||||||
|
|
||||||
// A standard recursive proof.
|
// A standard recursive proof.
|
||||||
let (proof, vd, cd) = recursive_proof(proof, vd, cd, &standard_config, None, false, false)?;
|
let (proof, vd, cd) = recursive_proof::<F, HCCO, HCCI, HCCO, HCCI, C, C, D>(
|
||||||
|
proof,
|
||||||
|
vd,
|
||||||
|
cd,
|
||||||
|
&standard_config,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
assert_eq!(cd.degree_bits(), 12);
|
assert_eq!(cd.degree_bits(), 12);
|
||||||
|
|
||||||
// A high-rate recursive proof, designed to be verifiable with fewer routed wires.
|
// A high-rate recursive proof, designed to be verifiable with fewer routed wires.
|
||||||
@ -267,8 +303,15 @@ mod tests {
|
|||||||
},
|
},
|
||||||
..standard_config
|
..standard_config
|
||||||
};
|
};
|
||||||
let (proof, vd, cd) =
|
let (proof, vd, cd) = recursive_proof::<F, HCCO, HCCI, HCCO, HCCI, C, C, D>(
|
||||||
recursive_proof::<F, C, C, D>(proof, vd, cd, &high_rate_config, None, true, true)?;
|
proof,
|
||||||
|
vd,
|
||||||
|
cd,
|
||||||
|
&high_rate_config,
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
assert_eq!(cd.degree_bits(), 12);
|
assert_eq!(cd.degree_bits(), 12);
|
||||||
|
|
||||||
// A final proof, optimized for size.
|
// A final proof, optimized for size.
|
||||||
@ -283,8 +326,15 @@ mod tests {
|
|||||||
},
|
},
|
||||||
..high_rate_config
|
..high_rate_config
|
||||||
};
|
};
|
||||||
let (proof, vd, cd) =
|
let (proof, vd, cd) = recursive_proof::<F, HCKCO, HCKCI, HCCO, HCCI, KC, C, D>(
|
||||||
recursive_proof::<F, KC, C, D>(proof, vd, cd, &final_config, None, true, true)?;
|
proof,
|
||||||
|
vd,
|
||||||
|
cd,
|
||||||
|
&final_config,
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
assert_eq!(cd.degree_bits(), 12, "final proof too large");
|
assert_eq!(cd.degree_bits(), 12, "final proof too large");
|
||||||
|
|
||||||
test_serialization(&proof, &vd, &cd)?;
|
test_serialization(&proof, &vd, &cd)?;
|
||||||
@ -298,39 +348,55 @@ mod tests {
|
|||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type PC = PoseidonGoldilocksConfig;
|
type PC = PoseidonGoldilocksConfig;
|
||||||
type KC = KeccakGoldilocksConfig;
|
type KC = KeccakGoldilocksConfig;
|
||||||
type F = <PC as GenericConfig<D>>::F;
|
type HCCO = PoseidonHashConfig;
|
||||||
|
type HCCI = HCCO;
|
||||||
|
type HCKCO = KeccakHashConfig;
|
||||||
|
type HCKCI = HCCI;
|
||||||
|
type F = <PC as GenericConfig<HCCO, HCCI, D>>::F;
|
||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let (proof, vd, cd) = dummy_proof::<F, PC, D>(&config, 4_000)?;
|
let (proof, vd, cd) = dummy_proof::<F, HCCO, HCCI, PC, D>(&config, 4_000)?;
|
||||||
|
|
||||||
let (proof, vd, cd) =
|
let (proof, vd, cd) = recursive_proof::<F, HCCO, HCCI, HCCO, HCCI, PC, PC, D>(
|
||||||
recursive_proof::<F, PC, PC, D>(proof, vd, cd, &config, None, false, false)?;
|
proof, vd, cd, &config, None, false, false,
|
||||||
|
)?;
|
||||||
test_serialization(&proof, &vd, &cd)?;
|
test_serialization(&proof, &vd, &cd)?;
|
||||||
|
|
||||||
let (proof, vd, cd) =
|
let (proof, vd, cd) = recursive_proof::<F, HCKCO, HCKCI, HCCO, HCCI, KC, PC, D>(
|
||||||
recursive_proof::<F, KC, PC, D>(proof, vd, cd, &config, None, false, false)?;
|
proof, vd, cd, &config, None, false, false,
|
||||||
|
)?;
|
||||||
test_serialization(&proof, &vd, &cd)?;
|
test_serialization(&proof, &vd, &cd)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
type Proof<F, C, const D: usize> = (
|
type Proof<F, HCO, HCI, C, const D: usize> = (
|
||||||
ProofWithPublicInputs<F, C, D>,
|
ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
VerifierOnlyCircuitData<C, D>,
|
VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
CommonCircuitData<F, D>,
|
CommonCircuitData<F, D>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Creates a dummy proof which should have roughly `num_dummy_gates` gates.
|
/// Creates a dummy proof which should have roughly `num_dummy_gates` gates.
|
||||||
fn dummy_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
fn dummy_proof<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
config: &CircuitConfig,
|
config: &CircuitConfig,
|
||||||
num_dummy_gates: u64,
|
num_dummy_gates: u64,
|
||||||
) -> Result<Proof<F, C, D>> {
|
) -> Result<Proof<F, HCO, HCI, C, D>>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
|
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
|
||||||
for _ in 0..num_dummy_gates {
|
for _ in 0..num_dummy_gates {
|
||||||
builder.add_gate(NoopGate, vec![]);
|
builder.add_gate(NoopGate, vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let inputs = PartialWitness::new();
|
let inputs = PartialWitness::new();
|
||||||
let proof = data.prove(inputs)?;
|
let proof = data.prove(inputs)?;
|
||||||
data.verify(proof.clone())?;
|
data.verify(proof.clone())?;
|
||||||
@ -340,20 +406,28 @@ mod tests {
|
|||||||
|
|
||||||
fn recursive_proof<
|
fn recursive_proof<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCOO: HashConfig,
|
||||||
InnerC: GenericConfig<D, F = F>,
|
HCOI: HashConfig,
|
||||||
|
HCIO: HashConfig,
|
||||||
|
HCII: HashConfig,
|
||||||
|
C: GenericConfig<HCOO, HCOI, D, F = F>,
|
||||||
|
InnerC: GenericConfig<HCIO, HCII, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
inner_proof: ProofWithPublicInputs<F, InnerC, D>,
|
inner_proof: ProofWithPublicInputs<F, HCIO, HCII, InnerC, D>,
|
||||||
inner_vd: VerifierOnlyCircuitData<InnerC, D>,
|
inner_vd: VerifierOnlyCircuitData<HCIO, HCII, InnerC, D>,
|
||||||
inner_cd: CommonCircuitData<F, D>,
|
inner_cd: CommonCircuitData<F, D>,
|
||||||
config: &CircuitConfig,
|
config: &CircuitConfig,
|
||||||
min_degree_bits: Option<usize>,
|
min_degree_bits: Option<usize>,
|
||||||
print_gate_counts: bool,
|
print_gate_counts: bool,
|
||||||
print_timing: bool,
|
print_timing: bool,
|
||||||
) -> Result<Proof<F, C, D>>
|
) -> Result<Proof<F, HCOO, HCOI, C, D>>
|
||||||
where
|
where
|
||||||
InnerC::Hasher: AlgebraicHasher<F>,
|
InnerC::Hasher: AlgebraicHasher<F, HCIO>,
|
||||||
|
[(); HCOO::WIDTH]:,
|
||||||
|
[(); HCOI::WIDTH]:,
|
||||||
|
[(); HCIO::WIDTH]:,
|
||||||
|
[(); HCII::WIDTH]:,
|
||||||
{
|
{
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
|
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
|
||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::new();
|
||||||
@ -367,7 +441,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
pw.set_hash_target(inner_data.circuit_digest, inner_vd.circuit_digest);
|
pw.set_hash_target(inner_data.circuit_digest, inner_vd.circuit_digest);
|
||||||
|
|
||||||
builder.verify_proof::<InnerC>(&pt, &inner_data, &inner_cd);
|
builder.verify_proof::<HCIO, HCII, InnerC>(&pt, &inner_data, &inner_cd);
|
||||||
|
|
||||||
if print_gate_counts {
|
if print_gate_counts {
|
||||||
builder.print_gate_counts(0);
|
builder.print_gate_counts(0);
|
||||||
@ -383,7 +457,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCOO, HCOI, C>();
|
||||||
|
|
||||||
let mut timing = TimingTree::new("prove", Level::Debug);
|
let mut timing = TimingTree::new("prove", Level::Debug);
|
||||||
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
let proof = prove(&data.prover_only, &data.common, pw, &mut timing)?;
|
||||||
@ -399,13 +473,19 @@ mod tests {
|
|||||||
/// Test serialization and print some size info.
|
/// Test serialization and print some size info.
|
||||||
fn test_serialization<
|
fn test_serialization<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
proof: &ProofWithPublicInputs<F, C, D>,
|
proof: &ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
vd: &VerifierOnlyCircuitData<C, D>,
|
vd: &VerifierOnlyCircuitData<HCO, HCI, C, D>,
|
||||||
cd: &CommonCircuitData<F, D>,
|
cd: &CommonCircuitData<F, D>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
|
{
|
||||||
let proof_bytes = proof.to_bytes();
|
let proof_bytes = proof.to_bytes();
|
||||||
info!("Proof length: {} bytes", proof_bytes.len());
|
info!("Proof length: {} bytes", proof_bytes.len());
|
||||||
let proof_from_bytes = ProofWithPublicInputs::from_bytes(proof_bytes, cd)?;
|
let proof_from_bytes = ProofWithPublicInputs::from_bytes(proof_bytes, cd)?;
|
||||||
|
|||||||
@ -280,14 +280,16 @@ mod tests {
|
|||||||
use crate::field::types::Sample;
|
use crate::field::types::Sample;
|
||||||
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig};
|
||||||
use crate::plonk::verifier::verify;
|
use crate::plonk::verifier::verify;
|
||||||
|
|
||||||
fn test_reduce_gadget_base(n: usize) -> Result<()> {
|
fn test_reduce_gadget_base(n: usize) -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
type FF = <C as GenericConfig<HCO, HCI, D>>::FE;
|
||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
|
|
||||||
@ -309,7 +311,7 @@ mod tests {
|
|||||||
|
|
||||||
builder.connect_extension(manual_reduce, circuit_reduce);
|
builder.connect_extension(manual_reduce, circuit_reduce);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
verify(proof, &data.verifier_only, &data.common)
|
verify(proof, &data.verifier_only, &data.common)
|
||||||
@ -318,8 +320,10 @@ mod tests {
|
|||||||
fn test_reduce_gadget(n: usize) -> Result<()> {
|
fn test_reduce_gadget(n: usize) -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
|
type FF = <C as GenericConfig<HCO, HCI, D>>::FE;
|
||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
|
|
||||||
@ -339,7 +343,7 @@ mod tests {
|
|||||||
|
|
||||||
builder.connect_extension(manual_reduce, circuit_reduce);
|
builder.connect_extension(manual_reduce, circuit_reduce);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
verify(proof, &data.verifier_only, &data.common)
|
verify(proof, &data.verifier_only, &data.common)
|
||||||
|
|||||||
@ -14,6 +14,7 @@ use crate::fri::proof::{
|
|||||||
FriQueryStep,
|
FriQueryStep,
|
||||||
};
|
};
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::hash::hashing::HashConfig;
|
||||||
use crate::hash::merkle_proofs::MerkleProof;
|
use crate::hash::merkle_proofs::MerkleProof;
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::plonk::circuit_data::CommonCircuitData;
|
use crate::plonk::circuit_data::CommonCircuitData;
|
||||||
@ -119,10 +120,11 @@ pub trait Read {
|
|||||||
|
|
||||||
/// Reads a hash value from `self`.
|
/// Reads a hash value from `self`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_hash<F, H>(&mut self) -> IoResult<H::Hash>
|
fn read_hash<F, HC, H>(&mut self) -> IoResult<H::Hash>
|
||||||
where
|
where
|
||||||
F: RichField,
|
F: RichField,
|
||||||
H: Hasher<F>,
|
HC: HashConfig,
|
||||||
|
H: Hasher<F, HC>,
|
||||||
{
|
{
|
||||||
let mut buf = vec![0; H::HASH_SIZE];
|
let mut buf = vec![0; H::HASH_SIZE];
|
||||||
self.read_exact(&mut buf)?;
|
self.read_exact(&mut buf)?;
|
||||||
@ -131,28 +133,31 @@ pub trait Read {
|
|||||||
|
|
||||||
/// Reads a value of type [`MerkleCap`] from `self` with the given `cap_height`.
|
/// Reads a value of type [`MerkleCap`] from `self` with the given `cap_height`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_merkle_cap<F, H>(&mut self, cap_height: usize) -> IoResult<MerkleCap<F, H>>
|
fn read_merkle_cap<F, HC, H>(&mut self, cap_height: usize) -> IoResult<MerkleCap<F, HC, H>>
|
||||||
where
|
where
|
||||||
F: RichField,
|
F: RichField,
|
||||||
H: Hasher<F>,
|
HC: HashConfig,
|
||||||
|
H: Hasher<F, HC>,
|
||||||
{
|
{
|
||||||
let cap_length = 1 << cap_height;
|
let cap_length = 1 << cap_height;
|
||||||
Ok(MerkleCap(
|
Ok(MerkleCap(
|
||||||
(0..cap_length)
|
(0..cap_length)
|
||||||
.map(|_| self.read_hash::<F, H>())
|
.map(|_| self.read_hash::<F, HC, H>())
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads a value of type [`OpeningSet`] from `self` with the given `common_data`.
|
/// Reads a value of type [`OpeningSet`] from `self` with the given `common_data`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_opening_set<F, C, const D: usize>(
|
fn read_opening_set<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> IoResult<OpeningSet<F, D>>
|
) -> IoResult<OpeningSet<F, D>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let config = &common_data.config;
|
let config = &common_data.config;
|
||||||
let constants = self.read_field_ext_vec::<F, D>(common_data.num_constants)?;
|
let constants = self.read_field_ext_vec::<F, D>(common_data.num_constants)?;
|
||||||
@ -178,28 +183,31 @@ pub trait Read {
|
|||||||
|
|
||||||
/// Reads a value of type [`MerkleProof`] from `self`.
|
/// Reads a value of type [`MerkleProof`] from `self`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_merkle_proof<F, H>(&mut self) -> IoResult<MerkleProof<F, H>>
|
fn read_merkle_proof<F, HC, H>(&mut self) -> IoResult<MerkleProof<F, HC, H>>
|
||||||
where
|
where
|
||||||
F: RichField,
|
F: RichField,
|
||||||
H: Hasher<F>,
|
HC: HashConfig,
|
||||||
|
H: Hasher<F, HC>,
|
||||||
{
|
{
|
||||||
let length = self.read_u8()?;
|
let length = self.read_u8()?;
|
||||||
Ok(MerkleProof {
|
Ok(MerkleProof {
|
||||||
siblings: (0..length)
|
siblings: (0..length)
|
||||||
.map(|_| self.read_hash::<F, H>())
|
.map(|_| self.read_hash::<F, HC, H>())
|
||||||
.collect::<Result<_, _>>()?,
|
.collect::<Result<_, _>>()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads a value of type [`FriInitialTreeProof`] from `self` with the given `common_data`.
|
/// Reads a value of type [`FriInitialTreeProof`] from `self` with the given `common_data`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_fri_initial_proof<F, C, const D: usize>(
|
fn read_fri_initial_proof<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> IoResult<FriInitialTreeProof<F, C::Hasher>>
|
) -> IoResult<FriInitialTreeProof<F, HCO, C::Hasher>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let config = &common_data.config;
|
let config = &common_data.config;
|
||||||
let salt = salt_size(common_data.fri_params.hiding);
|
let salt = salt_size(common_data.fri_params.hiding);
|
||||||
@ -231,14 +239,16 @@ pub trait Read {
|
|||||||
/// Reads a value of type [`FriQueryStep`] from `self` with the given `arity` and `compressed`
|
/// Reads a value of type [`FriQueryStep`] from `self` with the given `arity` and `compressed`
|
||||||
/// flag.
|
/// flag.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_fri_query_step<F, C, const D: usize>(
|
fn read_fri_query_step<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
arity: usize,
|
arity: usize,
|
||||||
compressed: bool,
|
compressed: bool,
|
||||||
) -> IoResult<FriQueryStep<F, C::Hasher, D>>
|
) -> IoResult<FriQueryStep<F, HCO, C::Hasher, D>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let evals = self.read_field_ext_vec::<F, D>(arity - usize::from(compressed))?;
|
let evals = self.read_field_ext_vec::<F, D>(arity - usize::from(compressed))?;
|
||||||
let merkle_proof = self.read_merkle_proof()?;
|
let merkle_proof = self.read_merkle_proof()?;
|
||||||
@ -250,23 +260,26 @@ pub trait Read {
|
|||||||
|
|
||||||
/// Reads a vector of [`FriQueryRound`]s from `self` with `common_data`.
|
/// Reads a vector of [`FriQueryRound`]s from `self` with `common_data`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_fri_query_rounds<F, C, const D: usize>(
|
fn read_fri_query_rounds<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> IoResult<Vec<FriQueryRound<F, C::Hasher, D>>>
|
) -> IoResult<Vec<FriQueryRound<F, HCO, C::Hasher, D>>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let config = &common_data.config;
|
let config = &common_data.config;
|
||||||
let mut fqrs = Vec::with_capacity(config.fri_config.num_query_rounds);
|
let mut fqrs = Vec::with_capacity(config.fri_config.num_query_rounds);
|
||||||
for _ in 0..config.fri_config.num_query_rounds {
|
for _ in 0..config.fri_config.num_query_rounds {
|
||||||
let initial_trees_proof = self.read_fri_initial_proof::<F, C, D>(common_data)?;
|
let initial_trees_proof =
|
||||||
|
self.read_fri_initial_proof::<F, HCO, HCI, C, D>(common_data)?;
|
||||||
let steps = common_data
|
let steps = common_data
|
||||||
.fri_params
|
.fri_params
|
||||||
.reduction_arity_bits
|
.reduction_arity_bits
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&ar| self.read_fri_query_step::<F, C, D>(1 << ar, false))
|
.map(|&ar| self.read_fri_query_step::<F, HCO, HCI, C, D>(1 << ar, false))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
fqrs.push(FriQueryRound {
|
fqrs.push(FriQueryRound {
|
||||||
initial_trees_proof,
|
initial_trees_proof,
|
||||||
@ -278,19 +291,21 @@ pub trait Read {
|
|||||||
|
|
||||||
/// Reads a value of type [`FriProof`] from `self` with `common_data`.
|
/// Reads a value of type [`FriProof`] from `self` with `common_data`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_fri_proof<F, C, const D: usize>(
|
fn read_fri_proof<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> IoResult<FriProof<F, C::Hasher, D>>
|
) -> IoResult<FriProof<F, HCO, C::Hasher, D>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let config = &common_data.config;
|
let config = &common_data.config;
|
||||||
let commit_phase_merkle_caps = (0..common_data.fri_params.reduction_arity_bits.len())
|
let commit_phase_merkle_caps = (0..common_data.fri_params.reduction_arity_bits.len())
|
||||||
.map(|_| self.read_merkle_cap(config.fri_config.cap_height))
|
.map(|_| self.read_merkle_cap(config.fri_config.cap_height))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let query_round_proofs = self.read_fri_query_rounds::<F, C, D>(common_data)?;
|
let query_round_proofs = self.read_fri_query_rounds::<F, HCO, HCI, C, D>(common_data)?;
|
||||||
let final_poly = PolynomialCoeffs::new(
|
let final_poly = PolynomialCoeffs::new(
|
||||||
self.read_field_ext_vec::<F, D>(common_data.fri_params.final_poly_len())?,
|
self.read_field_ext_vec::<F, D>(common_data.fri_params.final_poly_len())?,
|
||||||
);
|
);
|
||||||
@ -305,20 +320,22 @@ pub trait Read {
|
|||||||
|
|
||||||
/// Reads a value of type [`Proof`] from `self` with `common_data`.
|
/// Reads a value of type [`Proof`] from `self` with `common_data`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_proof<F, C, const D: usize>(
|
fn read_proof<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> IoResult<Proof<F, C, D>>
|
) -> IoResult<Proof<F, HCO, HCI, C, D>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let config = &common_data.config;
|
let config = &common_data.config;
|
||||||
let wires_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
let wires_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
||||||
let plonk_zs_partial_products_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
let plonk_zs_partial_products_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
||||||
let quotient_polys_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
let quotient_polys_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
||||||
let openings = self.read_opening_set::<F, C, D>(common_data)?;
|
let openings = self.read_opening_set::<F, HCO, HCI, C, D>(common_data)?;
|
||||||
let opening_proof = self.read_fri_proof::<F, C, D>(common_data)?;
|
let opening_proof = self.read_fri_proof::<F, HCO, HCI, C, D>(common_data)?;
|
||||||
Ok(Proof {
|
Ok(Proof {
|
||||||
wires_cap,
|
wires_cap,
|
||||||
plonk_zs_partial_products_cap,
|
plonk_zs_partial_products_cap,
|
||||||
@ -330,14 +347,16 @@ pub trait Read {
|
|||||||
|
|
||||||
/// Reads a value of type [`ProofWithPublicInputs`] from `self` with `common_data`.
|
/// Reads a value of type [`ProofWithPublicInputs`] from `self` with `common_data`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_proof_with_public_inputs<F, C, const D: usize>(
|
fn read_proof_with_public_inputs<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> IoResult<ProofWithPublicInputs<F, C, D>>
|
) -> IoResult<ProofWithPublicInputs<F, HCO, HCI, C, D>>
|
||||||
where
|
where
|
||||||
Self: Remaining,
|
Self: Remaining,
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let proof = self.read_proof(common_data)?;
|
let proof = self.read_proof(common_data)?;
|
||||||
let public_inputs = self.read_field_vec(self.remaining() / size_of::<u64>())?;
|
let public_inputs = self.read_field_vec(self.remaining() / size_of::<u64>())?;
|
||||||
@ -349,13 +368,15 @@ pub trait Read {
|
|||||||
|
|
||||||
/// Reads a value of type [`CompressedFriQueryRounds`] from `self` with `common_data`.
|
/// Reads a value of type [`CompressedFriQueryRounds`] from `self` with `common_data`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_compressed_fri_query_rounds<F, C, const D: usize>(
|
fn read_compressed_fri_query_rounds<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> IoResult<CompressedFriQueryRounds<F, C::Hasher, D>>
|
) -> IoResult<CompressedFriQueryRounds<F, HCO, C::Hasher, D>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let config = &common_data.config;
|
let config = &common_data.config;
|
||||||
let original_indices = (0..config.fri_config.num_query_rounds)
|
let original_indices = (0..config.fri_config.num_query_rounds)
|
||||||
@ -366,7 +387,10 @@ pub trait Read {
|
|||||||
indices.dedup();
|
indices.dedup();
|
||||||
let mut pairs = Vec::new();
|
let mut pairs = Vec::new();
|
||||||
for &i in &indices {
|
for &i in &indices {
|
||||||
pairs.push((i, self.read_fri_initial_proof::<F, C, D>(common_data)?));
|
pairs.push((
|
||||||
|
i,
|
||||||
|
self.read_fri_initial_proof::<F, HCO, HCI, C, D>(common_data)?,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
let initial_trees_proofs = HashMap::from_iter(pairs);
|
let initial_trees_proofs = HashMap::from_iter(pairs);
|
||||||
|
|
||||||
@ -377,7 +401,7 @@ pub trait Read {
|
|||||||
});
|
});
|
||||||
indices.dedup();
|
indices.dedup();
|
||||||
let query_steps = (0..indices.len())
|
let query_steps = (0..indices.len())
|
||||||
.map(|_| self.read_fri_query_step::<F, C, D>(1 << a, true))
|
.map(|_| self.read_fri_query_step::<F, HCO, HCI, C, D>(1 << a, true))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
steps.push(
|
steps.push(
|
||||||
indices
|
indices
|
||||||
@ -397,19 +421,22 @@ pub trait Read {
|
|||||||
|
|
||||||
/// Reads a value of type [`CompressedFriProof`] from `self` with `common_data`.
|
/// Reads a value of type [`CompressedFriProof`] from `self` with `common_data`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_compressed_fri_proof<F, C, const D: usize>(
|
fn read_compressed_fri_proof<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> IoResult<CompressedFriProof<F, C::Hasher, D>>
|
) -> IoResult<CompressedFriProof<F, HCO, C::Hasher, D>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let config = &common_data.config;
|
let config = &common_data.config;
|
||||||
let commit_phase_merkle_caps = (0..common_data.fri_params.reduction_arity_bits.len())
|
let commit_phase_merkle_caps = (0..common_data.fri_params.reduction_arity_bits.len())
|
||||||
.map(|_| self.read_merkle_cap(config.fri_config.cap_height))
|
.map(|_| self.read_merkle_cap(config.fri_config.cap_height))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let query_round_proofs = self.read_compressed_fri_query_rounds::<F, C, D>(common_data)?;
|
let query_round_proofs =
|
||||||
|
self.read_compressed_fri_query_rounds::<F, HCO, HCI, C, D>(common_data)?;
|
||||||
let final_poly = PolynomialCoeffs::new(
|
let final_poly = PolynomialCoeffs::new(
|
||||||
self.read_field_ext_vec::<F, D>(common_data.fri_params.final_poly_len())?,
|
self.read_field_ext_vec::<F, D>(common_data.fri_params.final_poly_len())?,
|
||||||
);
|
);
|
||||||
@ -424,20 +451,22 @@ pub trait Read {
|
|||||||
|
|
||||||
/// Reads a value of type [`CompressedProof`] from `self` with `common_data`.
|
/// Reads a value of type [`CompressedProof`] from `self` with `common_data`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_compressed_proof<F, C, const D: usize>(
|
fn read_compressed_proof<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> IoResult<CompressedProof<F, C, D>>
|
) -> IoResult<CompressedProof<F, HCO, HCI, C, D>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let config = &common_data.config;
|
let config = &common_data.config;
|
||||||
let wires_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
let wires_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
||||||
let plonk_zs_partial_products_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
let plonk_zs_partial_products_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
||||||
let quotient_polys_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
let quotient_polys_cap = self.read_merkle_cap(config.fri_config.cap_height)?;
|
||||||
let openings = self.read_opening_set::<F, C, D>(common_data)?;
|
let openings = self.read_opening_set::<F, HCO, HCI, C, D>(common_data)?;
|
||||||
let opening_proof = self.read_compressed_fri_proof::<F, C, D>(common_data)?;
|
let opening_proof = self.read_compressed_fri_proof::<F, HCO, HCI, C, D>(common_data)?;
|
||||||
Ok(CompressedProof {
|
Ok(CompressedProof {
|
||||||
wires_cap,
|
wires_cap,
|
||||||
plonk_zs_partial_products_cap,
|
plonk_zs_partial_products_cap,
|
||||||
@ -449,14 +478,16 @@ pub trait Read {
|
|||||||
|
|
||||||
/// Reads a value of type [`CompressedProofWithPublicInputs`] from `self` with `common_data`.
|
/// Reads a value of type [`CompressedProofWithPublicInputs`] from `self` with `common_data`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_compressed_proof_with_public_inputs<F, C, const D: usize>(
|
fn read_compressed_proof_with_public_inputs<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> IoResult<CompressedProofWithPublicInputs<F, C, D>>
|
) -> IoResult<CompressedProofWithPublicInputs<F, HCO, HCI, C, D>>
|
||||||
where
|
where
|
||||||
Self: Remaining,
|
Self: Remaining,
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let proof = self.read_compressed_proof(common_data)?;
|
let proof = self.read_compressed_proof(common_data)?;
|
||||||
let public_inputs = self.read_field_vec(self.remaining() / size_of::<u64>())?;
|
let public_inputs = self.read_field_vec(self.remaining() / size_of::<u64>())?;
|
||||||
@ -534,23 +565,25 @@ pub trait Write {
|
|||||||
|
|
||||||
/// Writes a hash `h` to `self`.
|
/// Writes a hash `h` to `self`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_hash<F, H>(&mut self, h: H::Hash) -> IoResult<()>
|
fn write_hash<F, HC, H>(&mut self, h: H::Hash) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField,
|
F: RichField,
|
||||||
H: Hasher<F>,
|
HC: HashConfig,
|
||||||
|
H: Hasher<F, HC>,
|
||||||
{
|
{
|
||||||
self.write_all(&h.to_bytes())
|
self.write_all(&h.to_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes `cap`, a value of type [`MerkleCap`], to `self`.
|
/// Writes `cap`, a value of type [`MerkleCap`], to `self`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_merkle_cap<F, H>(&mut self, cap: &MerkleCap<F, H>) -> IoResult<()>
|
fn write_merkle_cap<F, HC, H>(&mut self, cap: &MerkleCap<F, HC, H>) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField,
|
F: RichField,
|
||||||
H: Hasher<F>,
|
HC: HashConfig,
|
||||||
|
H: Hasher<F, HC>,
|
||||||
{
|
{
|
||||||
for &a in &cap.0 {
|
for &a in &cap.0 {
|
||||||
self.write_hash::<F, H>(a)?;
|
self.write_hash::<F, HC, H>(a)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -572,10 +605,11 @@ pub trait Write {
|
|||||||
|
|
||||||
/// Writes a value `p` of type [`MerkleProof`] to `self.`
|
/// Writes a value `p` of type [`MerkleProof`] to `self.`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_merkle_proof<F, H>(&mut self, p: &MerkleProof<F, H>) -> IoResult<()>
|
fn write_merkle_proof<F, HC, H>(&mut self, p: &MerkleProof<F, HC, H>) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField,
|
F: RichField,
|
||||||
H: Hasher<F>,
|
HC: HashConfig,
|
||||||
|
H: Hasher<F, HC>,
|
||||||
{
|
{
|
||||||
let length = p.siblings.len();
|
let length = p.siblings.len();
|
||||||
self.write_u8(
|
self.write_u8(
|
||||||
@ -584,20 +618,22 @@ pub trait Write {
|
|||||||
.expect("Merkle proof length must fit in u8."),
|
.expect("Merkle proof length must fit in u8."),
|
||||||
)?;
|
)?;
|
||||||
for &h in &p.siblings {
|
for &h in &p.siblings {
|
||||||
self.write_hash::<F, H>(h)?;
|
self.write_hash::<F, HC, H>(h)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes a value `fitp` of type [`FriInitialTreeProof`] to `self.`
|
/// Writes a value `fitp` of type [`FriInitialTreeProof`] to `self.`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_fri_initial_proof<F, C, const D: usize>(
|
fn write_fri_initial_proof<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fitp: &FriInitialTreeProof<F, C::Hasher>,
|
fitp: &FriInitialTreeProof<F, HCO, C::Hasher>,
|
||||||
) -> IoResult<()>
|
) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
for (v, p) in &fitp.evals_proofs {
|
for (v, p) in &fitp.evals_proofs {
|
||||||
self.write_field_vec(v)?;
|
self.write_field_vec(v)?;
|
||||||
@ -608,13 +644,15 @@ pub trait Write {
|
|||||||
|
|
||||||
/// Writes a value `fqs` of type [`FriQueryStep`] to `self.`
|
/// Writes a value `fqs` of type [`FriQueryStep`] to `self.`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_fri_query_step<F, C, const D: usize>(
|
fn write_fri_query_step<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fqs: &FriQueryStep<F, C::Hasher, D>,
|
fqs: &FriQueryStep<F, HCO, C::Hasher, D>,
|
||||||
) -> IoResult<()>
|
) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
self.write_field_ext_vec::<F, D>(&fqs.evals)?;
|
self.write_field_ext_vec::<F, D>(&fqs.evals)?;
|
||||||
self.write_merkle_proof(&fqs.merkle_proof)
|
self.write_merkle_proof(&fqs.merkle_proof)
|
||||||
@ -622,18 +660,20 @@ pub trait Write {
|
|||||||
|
|
||||||
/// Writes a value `fqrs` of type [`FriQueryRound`] to `self.`
|
/// Writes a value `fqrs` of type [`FriQueryRound`] to `self.`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_fri_query_rounds<F, C, const D: usize>(
|
fn write_fri_query_rounds<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fqrs: &[FriQueryRound<F, C::Hasher, D>],
|
fqrs: &[FriQueryRound<F, HCO, C::Hasher, D>],
|
||||||
) -> IoResult<()>
|
) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
for fqr in fqrs {
|
for fqr in fqrs {
|
||||||
self.write_fri_initial_proof::<F, C, D>(&fqr.initial_trees_proof)?;
|
self.write_fri_initial_proof::<F, HCO, HCI, C, D>(&fqr.initial_trees_proof)?;
|
||||||
for fqs in &fqr.steps {
|
for fqs in &fqr.steps {
|
||||||
self.write_fri_query_step::<F, C, D>(fqs)?;
|
self.write_fri_query_step::<F, HCO, HCI, C, D>(fqs)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -641,45 +681,54 @@ pub trait Write {
|
|||||||
|
|
||||||
/// Writes a value `fq` of type [`FriProof`] to `self.`
|
/// Writes a value `fq` of type [`FriProof`] to `self.`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_fri_proof<F, C, const D: usize>(
|
fn write_fri_proof<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fp: &FriProof<F, C::Hasher, D>,
|
fp: &FriProof<F, HCO, C::Hasher, D>,
|
||||||
) -> IoResult<()>
|
) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
for cap in &fp.commit_phase_merkle_caps {
|
for cap in &fp.commit_phase_merkle_caps {
|
||||||
self.write_merkle_cap(cap)?;
|
self.write_merkle_cap(cap)?;
|
||||||
}
|
}
|
||||||
self.write_fri_query_rounds::<F, C, D>(&fp.query_round_proofs)?;
|
self.write_fri_query_rounds::<F, HCO, HCI, C, D>(&fp.query_round_proofs)?;
|
||||||
self.write_field_ext_vec::<F, D>(&fp.final_poly.coeffs)?;
|
self.write_field_ext_vec::<F, D>(&fp.final_poly.coeffs)?;
|
||||||
self.write_field(fp.pow_witness)
|
self.write_field(fp.pow_witness)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes a value `proof` of type [`Proof`] to `self.`
|
/// Writes a value `proof` of type [`Proof`] to `self.`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_proof<F, C, const D: usize>(&mut self, proof: &Proof<F, C, D>) -> IoResult<()>
|
fn write_proof<F, HCO, HCI, C, const D: usize>(
|
||||||
|
&mut self,
|
||||||
|
proof: &Proof<F, HCO, HCI, C, D>,
|
||||||
|
) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
self.write_merkle_cap(&proof.wires_cap)?;
|
self.write_merkle_cap(&proof.wires_cap)?;
|
||||||
self.write_merkle_cap(&proof.plonk_zs_partial_products_cap)?;
|
self.write_merkle_cap(&proof.plonk_zs_partial_products_cap)?;
|
||||||
self.write_merkle_cap(&proof.quotient_polys_cap)?;
|
self.write_merkle_cap(&proof.quotient_polys_cap)?;
|
||||||
self.write_opening_set(&proof.openings)?;
|
self.write_opening_set(&proof.openings)?;
|
||||||
self.write_fri_proof::<F, C, D>(&proof.opening_proof)
|
self.write_fri_proof::<F, HCO, HCI, C, D>(&proof.opening_proof)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes a value `proof_with_pis` of type [`ProofWithPublicInputs`] to `self.`
|
/// Writes a value `proof_with_pis` of type [`ProofWithPublicInputs`] to `self.`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_proof_with_public_inputs<F, C, const D: usize>(
|
fn write_proof_with_public_inputs<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
proof_with_pis: &ProofWithPublicInputs<F, C, D>,
|
proof_with_pis: &ProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
) -> IoResult<()>
|
) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let ProofWithPublicInputs {
|
let ProofWithPublicInputs {
|
||||||
proof,
|
proof,
|
||||||
@ -691,13 +740,15 @@ pub trait Write {
|
|||||||
|
|
||||||
/// Writes a value `cfqrs` of type [`CompressedFriQueryRounds`] to `self.`
|
/// Writes a value `cfqrs` of type [`CompressedFriQueryRounds`] to `self.`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_compressed_fri_query_rounds<F, C, const D: usize>(
|
fn write_compressed_fri_query_rounds<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
cfqrs: &CompressedFriQueryRounds<F, C::Hasher, D>,
|
cfqrs: &CompressedFriQueryRounds<F, HCO, C::Hasher, D>,
|
||||||
) -> IoResult<()>
|
) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
for &i in &cfqrs.indices {
|
for &i in &cfqrs.indices {
|
||||||
self.write_u32(i as u32)?;
|
self.write_u32(i as u32)?;
|
||||||
@ -705,13 +756,13 @@ pub trait Write {
|
|||||||
let mut initial_trees_proofs = cfqrs.initial_trees_proofs.iter().collect::<Vec<_>>();
|
let mut initial_trees_proofs = cfqrs.initial_trees_proofs.iter().collect::<Vec<_>>();
|
||||||
initial_trees_proofs.sort_by_key(|&x| x.0);
|
initial_trees_proofs.sort_by_key(|&x| x.0);
|
||||||
for (_, itp) in initial_trees_proofs {
|
for (_, itp) in initial_trees_proofs {
|
||||||
self.write_fri_initial_proof::<F, C, D>(itp)?;
|
self.write_fri_initial_proof::<F, HCO, HCI, C, D>(itp)?;
|
||||||
}
|
}
|
||||||
for h in &cfqrs.steps {
|
for h in &cfqrs.steps {
|
||||||
let mut fri_query_steps = h.iter().collect::<Vec<_>>();
|
let mut fri_query_steps = h.iter().collect::<Vec<_>>();
|
||||||
fri_query_steps.sort_by_key(|&x| x.0);
|
fri_query_steps.sort_by_key(|&x| x.0);
|
||||||
for (_, fqs) in fri_query_steps {
|
for (_, fqs) in fri_query_steps {
|
||||||
self.write_fri_query_step::<F, C, D>(fqs)?;
|
self.write_fri_query_step::<F, HCO, HCI, C, D>(fqs)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -719,48 +770,54 @@ pub trait Write {
|
|||||||
|
|
||||||
/// Writes a value `fq` of type [`CompressedFriProof`] to `self.`
|
/// Writes a value `fq` of type [`CompressedFriProof`] to `self.`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_compressed_fri_proof<F, C, const D: usize>(
|
fn write_compressed_fri_proof<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fp: &CompressedFriProof<F, C::Hasher, D>,
|
fp: &CompressedFriProof<F, HCO, C::Hasher, D>,
|
||||||
) -> IoResult<()>
|
) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
for cap in &fp.commit_phase_merkle_caps {
|
for cap in &fp.commit_phase_merkle_caps {
|
||||||
self.write_merkle_cap(cap)?;
|
self.write_merkle_cap(cap)?;
|
||||||
}
|
}
|
||||||
self.write_compressed_fri_query_rounds::<F, C, D>(&fp.query_round_proofs)?;
|
self.write_compressed_fri_query_rounds::<F, HCO, HCI, C, D>(&fp.query_round_proofs)?;
|
||||||
self.write_field_ext_vec::<F, D>(&fp.final_poly.coeffs)?;
|
self.write_field_ext_vec::<F, D>(&fp.final_poly.coeffs)?;
|
||||||
self.write_field(fp.pow_witness)
|
self.write_field(fp.pow_witness)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes a value `proof` of type [`CompressedProof`] to `self.`
|
/// Writes a value `proof` of type [`CompressedProof`] to `self.`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_compressed_proof<F, C, const D: usize>(
|
fn write_compressed_proof<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
proof: &CompressedProof<F, C, D>,
|
proof: &CompressedProof<F, HCO, HCI, C, D>,
|
||||||
) -> IoResult<()>
|
) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
self.write_merkle_cap(&proof.wires_cap)?;
|
self.write_merkle_cap(&proof.wires_cap)?;
|
||||||
self.write_merkle_cap(&proof.plonk_zs_partial_products_cap)?;
|
self.write_merkle_cap(&proof.plonk_zs_partial_products_cap)?;
|
||||||
self.write_merkle_cap(&proof.quotient_polys_cap)?;
|
self.write_merkle_cap(&proof.quotient_polys_cap)?;
|
||||||
self.write_opening_set(&proof.openings)?;
|
self.write_opening_set(&proof.openings)?;
|
||||||
self.write_compressed_fri_proof::<F, C, D>(&proof.opening_proof)
|
self.write_compressed_fri_proof::<F, HCO, HCI, C, D>(&proof.opening_proof)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes a value `proof_with_pis` of type [`CompressedProofWithPublicInputs`] to `self.`
|
/// Writes a value `proof_with_pis` of type [`CompressedProofWithPublicInputs`] to `self.`
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_compressed_proof_with_public_inputs<F, C, const D: usize>(
|
fn write_compressed_proof_with_public_inputs<F, HCO, HCI, C, const D: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
proof_with_pis: &CompressedProofWithPublicInputs<F, C, D>,
|
proof_with_pis: &CompressedProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
) -> IoResult<()>
|
) -> IoResult<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
{
|
{
|
||||||
let CompressedProofWithPublicInputs {
|
let CompressedProofWithPublicInputs {
|
||||||
proof,
|
proof,
|
||||||
|
|||||||
@ -127,11 +127,12 @@ mod tests {
|
|||||||
use plonky2::field::extension::Extendable;
|
use plonky2::field::extension::Extendable;
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::witness::PartialWitness;
|
use plonky2::iop::witness::PartialWitness;
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::config::{
|
use plonky2::plonk::config::{
|
||||||
AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig,
|
AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig, PoseidonHashConfig,
|
||||||
};
|
};
|
||||||
use plonky2::util::timing::TimingTree;
|
use plonky2::util::timing::TimingTree;
|
||||||
|
|
||||||
@ -155,7 +156,9 @@ mod tests {
|
|||||||
fn test_fibonacci_stark() -> Result<()> {
|
fn test_fibonacci_stark() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = FibonacciStark<F, D>;
|
type S = FibonacciStark<F, D>;
|
||||||
|
|
||||||
let config = StarkConfig::standard_fast_config();
|
let config = StarkConfig::standard_fast_config();
|
||||||
@ -163,7 +166,7 @@ mod tests {
|
|||||||
let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)];
|
let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)];
|
||||||
let stark = S::new(num_rows);
|
let stark = S::new(num_rows);
|
||||||
let trace = stark.generate_trace(public_inputs[0], public_inputs[1]);
|
let trace = stark.generate_trace(public_inputs[0], public_inputs[1]);
|
||||||
let proof = prove::<F, C, S, D>(
|
let proof = prove::<F, HCO, HCI, C, S, D>(
|
||||||
stark,
|
stark,
|
||||||
&config,
|
&config,
|
||||||
trace,
|
trace,
|
||||||
@ -178,7 +181,9 @@ mod tests {
|
|||||||
fn test_fibonacci_stark_degree() -> Result<()> {
|
fn test_fibonacci_stark_degree() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = FibonacciStark<F, D>;
|
type S = FibonacciStark<F, D>;
|
||||||
|
|
||||||
let num_rows = 1 << 5;
|
let num_rows = 1 << 5;
|
||||||
@ -190,12 +195,14 @@ mod tests {
|
|||||||
fn test_fibonacci_stark_circuit() -> Result<()> {
|
fn test_fibonacci_stark_circuit() -> Result<()> {
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = FibonacciStark<F, D>;
|
type S = FibonacciStark<F, D>;
|
||||||
|
|
||||||
let num_rows = 1 << 5;
|
let num_rows = 1 << 5;
|
||||||
let stark = S::new(num_rows);
|
let stark = S::new(num_rows);
|
||||||
test_stark_circuit_constraints::<F, C, S, D>(stark)
|
test_stark_circuit_constraints::<F, HCO, HCI, C, S, D>(stark)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -203,7 +210,9 @@ mod tests {
|
|||||||
init_logger();
|
init_logger();
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type HCO = PoseidonHashConfig;
|
||||||
|
type HCI = HCO;
|
||||||
|
type F = <C as GenericConfig<HCO, HCI, D>>::F;
|
||||||
type S = FibonacciStark<F, D>;
|
type S = FibonacciStark<F, D>;
|
||||||
|
|
||||||
let config = StarkConfig::standard_fast_config();
|
let config = StarkConfig::standard_fast_config();
|
||||||
@ -211,7 +220,7 @@ mod tests {
|
|||||||
let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)];
|
let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)];
|
||||||
let stark = S::new(num_rows);
|
let stark = S::new(num_rows);
|
||||||
let trace = stark.generate_trace(public_inputs[0], public_inputs[1]);
|
let trace = stark.generate_trace(public_inputs[0], public_inputs[1]);
|
||||||
let proof = prove::<F, C, S, D>(
|
let proof = prove::<F, HCO, HCI, C, S, D>(
|
||||||
stark,
|
stark,
|
||||||
&config,
|
&config,
|
||||||
trace,
|
trace,
|
||||||
@ -220,26 +229,33 @@ mod tests {
|
|||||||
)?;
|
)?;
|
||||||
verify_stark_proof(stark, proof.clone(), &config)?;
|
verify_stark_proof(stark, proof.clone(), &config)?;
|
||||||
|
|
||||||
recursive_proof::<F, C, S, C, D>(stark, proof, &config, true)
|
recursive_proof::<F, HCO, HCI, HCO, HCI, C, S, C, D>(stark, proof, &config, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recursive_proof<
|
fn recursive_proof<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCOO: HashConfig,
|
||||||
|
HCOI: HashConfig,
|
||||||
|
HCIO: HashConfig,
|
||||||
|
HCII: HashConfig,
|
||||||
|
C: GenericConfig<HCOO, HCOI, D, F = F>,
|
||||||
S: Stark<F, D> + Copy,
|
S: Stark<F, D> + Copy,
|
||||||
InnerC: GenericConfig<D, F = F>,
|
InnerC: GenericConfig<HCIO, HCII, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
stark: S,
|
stark: S,
|
||||||
inner_proof: StarkProofWithPublicInputs<F, InnerC, D>,
|
inner_proof: StarkProofWithPublicInputs<F, HCIO, HCII, InnerC, D>,
|
||||||
inner_config: &StarkConfig,
|
inner_config: &StarkConfig,
|
||||||
print_gate_counts: bool,
|
print_gate_counts: bool,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
InnerC::Hasher: AlgebraicHasher<F>,
|
InnerC::Hasher: AlgebraicHasher<F, HCIO>,
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); S::PUBLIC_INPUTS]:,
|
[(); S::PUBLIC_INPUTS]:,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); HCOO::WIDTH]:,
|
||||||
|
[(); HCOI::WIDTH]:,
|
||||||
|
[(); HCIO::WIDTH]:,
|
||||||
|
[(); HCII::WIDTH]:,
|
||||||
{
|
{
|
||||||
let circuit_config = CircuitConfig::standard_recursion_config();
|
let circuit_config = CircuitConfig::standard_recursion_config();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(circuit_config);
|
let mut builder = CircuitBuilder::<F, D>::new(circuit_config);
|
||||||
@ -248,13 +264,18 @@ mod tests {
|
|||||||
let pt = add_virtual_stark_proof_with_pis(&mut builder, stark, inner_config, degree_bits);
|
let pt = add_virtual_stark_proof_with_pis(&mut builder, stark, inner_config, degree_bits);
|
||||||
set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof);
|
set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof);
|
||||||
|
|
||||||
verify_stark_proof_circuit::<F, InnerC, S, D>(&mut builder, stark, pt, inner_config);
|
verify_stark_proof_circuit::<F, HCIO, HCII, InnerC, S, D>(
|
||||||
|
&mut builder,
|
||||||
|
stark,
|
||||||
|
pt,
|
||||||
|
inner_config,
|
||||||
|
);
|
||||||
|
|
||||||
if print_gate_counts {
|
if print_gate_counts {
|
||||||
builder.print_gate_counts(0);
|
builder.print_gate_counts(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCOO, HCOI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
data.verify(proof)
|
data.verify(proof)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use plonky2::field::polynomial::PolynomialCoeffs;
|
|||||||
use plonky2::fri::proof::{FriProof, FriProofTarget};
|
use plonky2::fri::proof::{FriProof, FriProofTarget};
|
||||||
use plonky2::gadgets::polynomial::PolynomialCoeffsExtTarget;
|
use plonky2::gadgets::polynomial::PolynomialCoeffsExtTarget;
|
||||||
use plonky2::hash::hash_types::{MerkleCapTarget, RichField};
|
use plonky2::hash::hash_types::{MerkleCapTarget, RichField};
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::hash::merkle_tree::MerkleCap;
|
use plonky2::hash::merkle_tree::MerkleCap;
|
||||||
use plonky2::iop::challenger::{Challenger, RecursiveChallenger};
|
use plonky2::iop::challenger::{Challenger, RecursiveChallenger};
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
@ -18,13 +19,13 @@ use crate::permutation::{
|
|||||||
use crate::proof::*;
|
use crate::proof::*;
|
||||||
use crate::stark::Stark;
|
use crate::stark::Stark;
|
||||||
|
|
||||||
fn get_challenges<F, C, S, const D: usize>(
|
fn get_challenges<F, HCO, HCI, C, S, const D: usize>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
trace_cap: &MerkleCap<F, C::Hasher>,
|
trace_cap: &MerkleCap<F, HCO, C::Hasher>,
|
||||||
permutation_zs_cap: Option<&MerkleCap<F, C::Hasher>>,
|
permutation_zs_cap: Option<&MerkleCap<F, HCO, C::Hasher>>,
|
||||||
quotient_polys_cap: &MerkleCap<F, C::Hasher>,
|
quotient_polys_cap: &MerkleCap<F, HCO, C::Hasher>,
|
||||||
openings: &StarkOpeningSet<F, D>,
|
openings: &StarkOpeningSet<F, D>,
|
||||||
commit_phase_merkle_caps: &[MerkleCap<F, C::Hasher>],
|
commit_phase_merkle_caps: &[MerkleCap<F, HCO, C::Hasher>],
|
||||||
final_poly: &PolynomialCoeffs<F::Extension>,
|
final_poly: &PolynomialCoeffs<F::Extension>,
|
||||||
pow_witness: F,
|
pow_witness: F,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
@ -32,12 +33,16 @@ fn get_challenges<F, C, S, const D: usize>(
|
|||||||
) -> StarkProofChallenges<F, D>
|
) -> StarkProofChallenges<F, D>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let num_challenges = config.num_challenges;
|
let num_challenges = config.num_challenges;
|
||||||
|
|
||||||
let mut challenger = Challenger::<F, C::Hasher>::new();
|
let mut challenger = Challenger::<F, HCO, C::Hasher>::new();
|
||||||
|
|
||||||
challenger.observe_cap(trace_cap);
|
challenger.observe_cap(trace_cap);
|
||||||
|
|
||||||
@ -62,7 +67,7 @@ where
|
|||||||
permutation_challenge_sets,
|
permutation_challenge_sets,
|
||||||
stark_alphas,
|
stark_alphas,
|
||||||
stark_zeta,
|
stark_zeta,
|
||||||
fri_challenges: challenger.fri_challenges::<C, D>(
|
fri_challenges: challenger.fri_challenges::<HCI, C, D>(
|
||||||
commit_phase_merkle_caps,
|
commit_phase_merkle_caps,
|
||||||
final_poly,
|
final_poly,
|
||||||
pow_witness,
|
pow_witness,
|
||||||
@ -72,10 +77,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, C, const D: usize> StarkProofWithPublicInputs<F, C, D>
|
impl<F, HCO, HCI, C, const D: usize> StarkProofWithPublicInputs<F, HCO, HCI, C, D>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
// TODO: Should be used later in compression?
|
// TODO: Should be used later in compression?
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
@ -111,7 +120,7 @@ where
|
|||||||
},
|
},
|
||||||
} = &self.proof;
|
} = &self.proof;
|
||||||
|
|
||||||
get_challenges::<F, C, S, D>(
|
get_challenges::<F, HCO, HCI, C, S, D>(
|
||||||
stark,
|
stark,
|
||||||
trace_cap,
|
trace_cap,
|
||||||
permutation_zs_cap.as_ref(),
|
permutation_zs_cap.as_ref(),
|
||||||
@ -129,7 +138,9 @@ where
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn get_challenges_target<
|
pub(crate) fn get_challenges_target<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
@ -145,11 +156,13 @@ pub(crate) fn get_challenges_target<
|
|||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> StarkProofChallengesTarget<D>
|
) -> StarkProofChallengesTarget<D>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let num_challenges = config.num_challenges;
|
let num_challenges = config.num_challenges;
|
||||||
|
|
||||||
let mut challenger = RecursiveChallenger::<F, C::Hasher, D>::new(builder);
|
let mut challenger = RecursiveChallenger::<F, HCO, C::Hasher, D>::new(builder);
|
||||||
|
|
||||||
challenger.observe_cap(trace_cap);
|
challenger.observe_cap(trace_cap);
|
||||||
|
|
||||||
@ -188,7 +201,9 @@ where
|
|||||||
impl<const D: usize> StarkProofWithPublicInputsTarget<D> {
|
impl<const D: usize> StarkProofWithPublicInputsTarget<D> {
|
||||||
pub(crate) fn get_challenges<
|
pub(crate) fn get_challenges<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
>(
|
>(
|
||||||
&self,
|
&self,
|
||||||
@ -197,7 +212,9 @@ impl<const D: usize> StarkProofWithPublicInputsTarget<D> {
|
|||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> StarkProofChallengesTarget<D>
|
) -> StarkProofChallengesTarget<D>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let StarkProofTarget {
|
let StarkProofTarget {
|
||||||
trace_cap,
|
trace_cap,
|
||||||
@ -213,7 +230,7 @@ impl<const D: usize> StarkProofWithPublicInputsTarget<D> {
|
|||||||
},
|
},
|
||||||
} = &self.proof;
|
} = &self.proof;
|
||||||
|
|
||||||
get_challenges_target::<F, C, S, D>(
|
get_challenges_target::<F, HCO, HCI, C, S, D>(
|
||||||
builder,
|
builder,
|
||||||
stark,
|
stark,
|
||||||
trace_cap,
|
trace_cap,
|
||||||
@ -229,13 +246,13 @@ impl<const D: usize> StarkProofWithPublicInputsTarget<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Deal with the compressed stuff.
|
// TODO: Deal with the compressed stuff.
|
||||||
// impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
// impl<F: RichField + Extendable<D>, C: GenericConfig<HCO, HCI, D, F = F>, const D: usize>
|
||||||
// CompressedProofWithPublicInputs<F, C, D>
|
// CompressedProofWithPublicInputs<F, HCO, HCI, C, D>
|
||||||
// {
|
// {
|
||||||
// /// Computes all Fiat-Shamir challenges used in the Plonk proof.
|
// /// Computes all Fiat-Shamir challenges used in the Plonk proof.
|
||||||
// pub(crate) fn get_challenges(
|
// pub(crate) fn get_challenges(
|
||||||
// &self,
|
// &self,
|
||||||
// common_data: &CommonCircuitData<F, C, D>,
|
// common_data: &CommonCircuitData<F, HCO, HCI, C, D>,
|
||||||
// ) -> anyhow::Result<ProofChallenges<F, D>> {
|
// ) -> anyhow::Result<ProofChallenges<F, D>> {
|
||||||
// let CompressedProof {
|
// let CompressedProof {
|
||||||
// wires_cap,
|
// wires_cap,
|
||||||
@ -268,7 +285,7 @@ impl<const D: usize> StarkProofWithPublicInputsTarget<D> {
|
|||||||
// pub(crate) fn get_inferred_elements(
|
// pub(crate) fn get_inferred_elements(
|
||||||
// &self,
|
// &self,
|
||||||
// challenges: &ProofChallenges<F, D>,
|
// challenges: &ProofChallenges<F, D>,
|
||||||
// common_data: &CommonCircuitData<F, C, D>,
|
// common_data: &CommonCircuitData<F, HCO, HCI, C, D>,
|
||||||
// ) -> FriInferredElements<F, D> {
|
// ) -> FriInferredElements<F, D> {
|
||||||
// let ProofChallenges {
|
// let ProofChallenges {
|
||||||
// plonk_zeta,
|
// plonk_zeta,
|
||||||
@ -291,7 +308,7 @@ impl<const D: usize> StarkProofWithPublicInputsTarget<D> {
|
|||||||
// for &(mut x_index) in fri_query_indices {
|
// for &(mut x_index) in fri_query_indices {
|
||||||
// let mut subgroup_x = F::MULTIPLICATIVE_GROUP_GENERATOR
|
// let mut subgroup_x = F::MULTIPLICATIVE_GROUP_GENERATOR
|
||||||
// * F::primitive_root_of_unity(log_n).exp_u64(reverse_bits(x_index, log_n) as u64);
|
// * F::primitive_root_of_unity(log_n).exp_u64(reverse_bits(x_index, log_n) as u64);
|
||||||
// let mut old_eval = fri_combine_initial::<F, C, D>(
|
// let mut old_eval = fri_combine_initial::<F, HCO, HCI, C, D>(
|
||||||
// &common_data.get_fri_instance(*plonk_zeta),
|
// &common_data.get_fri_instance(*plonk_zeta),
|
||||||
// &self
|
// &self
|
||||||
// .proof
|
// .proof
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
#![feature(generic_const_exprs)]
|
#![feature(generic_const_exprs)]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use plonky2::field::packed::PackedField;
|
|||||||
use plonky2::field::polynomial::PolynomialValues;
|
use plonky2::field::polynomial::PolynomialValues;
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::challenger::{Challenger, RecursiveChallenger};
|
use plonky2::iop::challenger::{Challenger, RecursiveChallenger};
|
||||||
use plonky2::iop::ext_target::ExtensionTarget;
|
use plonky2::iop::ext_target::ExtensionTarget;
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
@ -149,29 +150,38 @@ fn poly_product_elementwise<F: Field>(
|
|||||||
product
|
product
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_permutation_challenge<F: RichField, H: Hasher<F>>(
|
fn get_permutation_challenge<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
challenger: &mut Challenger<F, H>,
|
challenger: &mut Challenger<F, HC, H>,
|
||||||
) -> PermutationChallenge<F> {
|
) -> PermutationChallenge<F>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let beta = challenger.get_challenge();
|
let beta = challenger.get_challenge();
|
||||||
let gamma = challenger.get_challenge();
|
let gamma = challenger.get_challenge();
|
||||||
PermutationChallenge { beta, gamma }
|
PermutationChallenge { beta, gamma }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_permutation_challenge_set<F: RichField, H: Hasher<F>>(
|
fn get_permutation_challenge_set<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
challenger: &mut Challenger<F, H>,
|
challenger: &mut Challenger<F, HC, H>,
|
||||||
num_challenges: usize,
|
num_challenges: usize,
|
||||||
) -> PermutationChallengeSet<F> {
|
) -> PermutationChallengeSet<F>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let challenges = (0..num_challenges)
|
let challenges = (0..num_challenges)
|
||||||
.map(|_| get_permutation_challenge(challenger))
|
.map(|_| get_permutation_challenge(challenger))
|
||||||
.collect();
|
.collect();
|
||||||
PermutationChallengeSet { challenges }
|
PermutationChallengeSet { challenges }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_n_permutation_challenge_sets<F: RichField, H: Hasher<F>>(
|
pub(crate) fn get_n_permutation_challenge_sets<F: RichField, HC: HashConfig, H: Hasher<F, HC>>(
|
||||||
challenger: &mut Challenger<F, H>,
|
challenger: &mut Challenger<F, HC, H>,
|
||||||
num_challenges: usize,
|
num_challenges: usize,
|
||||||
num_sets: usize,
|
num_sets: usize,
|
||||||
) -> Vec<PermutationChallengeSet<F>> {
|
) -> Vec<PermutationChallengeSet<F>>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
(0..num_sets)
|
(0..num_sets)
|
||||||
.map(|_| get_permutation_challenge_set(challenger, num_challenges))
|
.map(|_| get_permutation_challenge_set(challenger, num_challenges))
|
||||||
.collect()
|
.collect()
|
||||||
@ -179,12 +189,16 @@ pub(crate) fn get_n_permutation_challenge_sets<F: RichField, H: Hasher<F>>(
|
|||||||
|
|
||||||
fn get_permutation_challenge_target<
|
fn get_permutation_challenge_target<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
H: AlgebraicHasher<F>,
|
HC: HashConfig,
|
||||||
|
H: AlgebraicHasher<F, HC>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
challenger: &mut RecursiveChallenger<F, H, D>,
|
challenger: &mut RecursiveChallenger<F, HC, H, D>,
|
||||||
) -> PermutationChallenge<Target> {
|
) -> PermutationChallenge<Target>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let beta = challenger.get_challenge(builder);
|
let beta = challenger.get_challenge(builder);
|
||||||
let gamma = challenger.get_challenge(builder);
|
let gamma = challenger.get_challenge(builder);
|
||||||
PermutationChallenge { beta, gamma }
|
PermutationChallenge { beta, gamma }
|
||||||
@ -192,13 +206,17 @@ fn get_permutation_challenge_target<
|
|||||||
|
|
||||||
fn get_permutation_challenge_set_target<
|
fn get_permutation_challenge_set_target<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
H: AlgebraicHasher<F>,
|
HC: HashConfig,
|
||||||
|
H: AlgebraicHasher<F, HC>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
challenger: &mut RecursiveChallenger<F, H, D>,
|
challenger: &mut RecursiveChallenger<F, HC, H, D>,
|
||||||
num_challenges: usize,
|
num_challenges: usize,
|
||||||
) -> PermutationChallengeSet<Target> {
|
) -> PermutationChallengeSet<Target>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
let challenges = (0..num_challenges)
|
let challenges = (0..num_challenges)
|
||||||
.map(|_| get_permutation_challenge_target(builder, challenger))
|
.map(|_| get_permutation_challenge_target(builder, challenger))
|
||||||
.collect();
|
.collect();
|
||||||
@ -207,14 +225,18 @@ fn get_permutation_challenge_set_target<
|
|||||||
|
|
||||||
pub(crate) fn get_n_permutation_challenge_sets_target<
|
pub(crate) fn get_n_permutation_challenge_sets_target<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
H: AlgebraicHasher<F>,
|
HC: HashConfig,
|
||||||
|
H: AlgebraicHasher<F, HC>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
challenger: &mut RecursiveChallenger<F, H, D>,
|
challenger: &mut RecursiveChallenger<F, HC, H, D>,
|
||||||
num_challenges: usize,
|
num_challenges: usize,
|
||||||
num_sets: usize,
|
num_sets: usize,
|
||||||
) -> Vec<PermutationChallengeSet<Target>> {
|
) -> Vec<PermutationChallengeSet<Target>>
|
||||||
|
where
|
||||||
|
[(); HC::WIDTH]:,
|
||||||
|
{
|
||||||
(0..num_sets)
|
(0..num_sets)
|
||||||
.map(|_| get_permutation_challenge_set_target(builder, challenger, num_challenges))
|
.map(|_| get_permutation_challenge_set_target(builder, challenger, num_challenges))
|
||||||
.collect()
|
.collect()
|
||||||
|
|||||||
@ -11,6 +11,7 @@ use plonky2::fri::structure::{
|
|||||||
FriOpeningBatch, FriOpeningBatchTarget, FriOpenings, FriOpeningsTarget,
|
FriOpeningBatch, FriOpeningBatchTarget, FriOpenings, FriOpeningsTarget,
|
||||||
};
|
};
|
||||||
use plonky2::hash::hash_types::{MerkleCapTarget, RichField};
|
use plonky2::hash::hash_types::{MerkleCapTarget, RichField};
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::hash::merkle_tree::MerkleCap;
|
use plonky2::hash::merkle_tree::MerkleCap;
|
||||||
use plonky2::iop::ext_target::ExtensionTarget;
|
use plonky2::iop::ext_target::ExtensionTarget;
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
@ -21,20 +22,33 @@ use crate::config::StarkConfig;
|
|||||||
use crate::permutation::PermutationChallengeSet;
|
use crate::permutation::PermutationChallengeSet;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StarkProof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
|
pub struct StarkProof<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> {
|
||||||
/// Merkle cap of LDEs of trace values.
|
/// Merkle cap of LDEs of trace values.
|
||||||
pub trace_cap: MerkleCap<F, C::Hasher>,
|
pub trace_cap: MerkleCap<F, HCO, C::Hasher>,
|
||||||
/// Merkle cap of LDEs of permutation Z values.
|
/// Merkle cap of LDEs of permutation Z values.
|
||||||
pub permutation_zs_cap: Option<MerkleCap<F, C::Hasher>>,
|
pub permutation_zs_cap: Option<MerkleCap<F, HCO, C::Hasher>>,
|
||||||
/// Merkle cap of LDEs of trace values.
|
/// Merkle cap of LDEs of trace values.
|
||||||
pub quotient_polys_cap: MerkleCap<F, C::Hasher>,
|
pub quotient_polys_cap: MerkleCap<F, HCO, C::Hasher>,
|
||||||
/// Purported values of each polynomial at the challenge point.
|
/// Purported values of each polynomial at the challenge point.
|
||||||
pub openings: StarkOpeningSet<F, D>,
|
pub openings: StarkOpeningSet<F, D>,
|
||||||
/// A batch FRI argument for all openings.
|
/// A batch FRI argument for all openings.
|
||||||
pub opening_proof: FriProof<F, C::Hasher, D>,
|
pub opening_proof: FriProof<F, HCO, C::Hasher, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> StarkProof<F, C, D> {
|
impl<
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
const D: usize,
|
||||||
|
> StarkProof<F, HCO, HCI, C, D>
|
||||||
|
{
|
||||||
/// Recover the length of the trace from a STARK proof and a STARK config.
|
/// Recover the length of the trace from a STARK proof and a STARK config.
|
||||||
pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize {
|
pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize {
|
||||||
let initial_merkle_proof = &self.opening_proof.query_round_proofs[0]
|
let initial_merkle_proof = &self.opening_proof.query_round_proofs[0]
|
||||||
@ -69,10 +83,12 @@ impl<const D: usize> StarkProofTarget<D> {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StarkProofWithPublicInputs<
|
pub struct StarkProofWithPublicInputs<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
> {
|
> {
|
||||||
pub proof: StarkProof<F, C, D>,
|
pub proof: StarkProof<F, HCO, HCI, C, D>,
|
||||||
// TODO: Maybe make it generic over a `S: Stark` and replace with `[F; S::PUBLIC_INPUTS]`.
|
// TODO: Maybe make it generic over a `S: Stark` and replace with `[F; S::PUBLIC_INPUTS]`.
|
||||||
pub public_inputs: Vec<F>,
|
pub public_inputs: Vec<F>,
|
||||||
}
|
}
|
||||||
@ -84,23 +100,27 @@ pub struct StarkProofWithPublicInputsTarget<const D: usize> {
|
|||||||
|
|
||||||
pub struct CompressedStarkProof<
|
pub struct CompressedStarkProof<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
> {
|
> {
|
||||||
/// Merkle cap of LDEs of trace values.
|
/// Merkle cap of LDEs of trace values.
|
||||||
pub trace_cap: MerkleCap<F, C::Hasher>,
|
pub trace_cap: MerkleCap<F, HCO, C::Hasher>,
|
||||||
/// Purported values of each polynomial at the challenge point.
|
/// Purported values of each polynomial at the challenge point.
|
||||||
pub openings: StarkOpeningSet<F, D>,
|
pub openings: StarkOpeningSet<F, D>,
|
||||||
/// A batch FRI argument for all openings.
|
/// A batch FRI argument for all openings.
|
||||||
pub opening_proof: CompressedFriProof<F, C::Hasher, D>,
|
pub opening_proof: CompressedFriProof<F, HCO, C::Hasher, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CompressedStarkProofWithPublicInputs<
|
pub struct CompressedStarkProofWithPublicInputs<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
> {
|
> {
|
||||||
pub proof: CompressedStarkProof<F, C, D>,
|
pub proof: CompressedStarkProof<F, HCO, HCI, C, D>,
|
||||||
pub public_inputs: Vec<F>,
|
pub public_inputs: Vec<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,14 +155,14 @@ pub struct StarkOpeningSet<F: RichField + Extendable<D>, const D: usize> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
||||||
pub fn new<C: GenericConfig<D, F = F>>(
|
pub fn new<HCO: HashConfig, HCI: HashConfig, C: GenericConfig<HCO, HCI, D, F = F>>(
|
||||||
zeta: F::Extension,
|
zeta: F::Extension,
|
||||||
g: F,
|
g: F,
|
||||||
trace_commitment: &PolynomialBatch<F, C, D>,
|
trace_commitment: &PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
permutation_zs_commitment: Option<&PolynomialBatch<F, C, D>>,
|
permutation_zs_commitment: Option<&PolynomialBatch<F, HCO, HCI, C, D>>,
|
||||||
quotient_commitment: &PolynomialBatch<F, C, D>,
|
quotient_commitment: &PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let eval_commitment = |z: F::Extension, c: &PolynomialBatch<F, C, D>| {
|
let eval_commitment = |z: F::Extension, c: &PolynomialBatch<F, HCO, HCI, C, D>| {
|
||||||
c.polynomials
|
c.polynomials
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|p| p.to_extension().eval(z))
|
.map(|p| p.to_extension().eval(z))
|
||||||
|
|||||||
@ -11,8 +11,9 @@ use plonky2::field::types::Field;
|
|||||||
use plonky2::field::zero_poly_coset::ZeroPolyOnCoset;
|
use plonky2::field::zero_poly_coset::ZeroPolyOnCoset;
|
||||||
use plonky2::fri::oracle::PolynomialBatch;
|
use plonky2::fri::oracle::PolynomialBatch;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::challenger::Challenger;
|
use plonky2::iop::challenger::Challenger;
|
||||||
use plonky2::plonk::config::{GenericConfig, Hasher};
|
use plonky2::plonk::config::GenericConfig;
|
||||||
use plonky2::timed;
|
use plonky2::timed;
|
||||||
use plonky2::util::timing::TimingTree;
|
use plonky2::util::timing::TimingTree;
|
||||||
use plonky2::util::{log2_ceil, log2_strict, transpose};
|
use plonky2::util::{log2_ceil, log2_strict, transpose};
|
||||||
@ -29,20 +30,23 @@ use crate::stark::Stark;
|
|||||||
use crate::vanishing_poly::eval_vanishing_poly;
|
use crate::vanishing_poly::eval_vanishing_poly;
|
||||||
use crate::vars::StarkEvaluationVars;
|
use crate::vars::StarkEvaluationVars;
|
||||||
|
|
||||||
pub fn prove<F, C, S, const D: usize>(
|
pub fn prove<F, HCO, HCI, C, S, const D: usize>(
|
||||||
stark: S,
|
stark: S,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
trace_poly_values: Vec<PolynomialValues<F>>,
|
trace_poly_values: Vec<PolynomialValues<F>>,
|
||||||
public_inputs: [F; S::PUBLIC_INPUTS],
|
public_inputs: [F; S::PUBLIC_INPUTS],
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> Result<StarkProofWithPublicInputs<F, C, D>>
|
) -> Result<StarkProofWithPublicInputs<F, HCO, HCI, C, D>>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); S::PUBLIC_INPUTS]:,
|
[(); S::PUBLIC_INPUTS]:,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
let degree = trace_poly_values[0].len();
|
let degree = trace_poly_values[0].len();
|
||||||
let degree_bits = log2_strict(degree);
|
let degree_bits = log2_strict(degree);
|
||||||
@ -57,7 +61,7 @@ where
|
|||||||
let trace_commitment = timed!(
|
let trace_commitment = timed!(
|
||||||
timing,
|
timing,
|
||||||
"compute trace commitment",
|
"compute trace commitment",
|
||||||
PolynomialBatch::<F, C, D>::from_values(
|
PolynomialBatch::<F, HCO, HCI, C, D>::from_values(
|
||||||
// TODO: Cloning this isn't great; consider having `from_values` accept a reference,
|
// TODO: Cloning this isn't great; consider having `from_values` accept a reference,
|
||||||
// or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`.
|
// or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`.
|
||||||
trace_poly_values.clone(),
|
trace_poly_values.clone(),
|
||||||
@ -112,7 +116,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let alphas = challenger.get_n_challenges(config.num_challenges);
|
let alphas = challenger.get_n_challenges(config.num_challenges);
|
||||||
let quotient_polys = compute_quotient_polys::<F, <F as Packable>::Packing, C, S, D>(
|
let quotient_polys = compute_quotient_polys::<F, <F as Packable>::Packing, HCO, HCI, C, S, D>(
|
||||||
&stark,
|
&stark,
|
||||||
&trace_commitment,
|
&trace_commitment,
|
||||||
&permutation_zs_commitment_challenges,
|
&permutation_zs_commitment_challenges,
|
||||||
@ -196,11 +200,11 @@ where
|
|||||||
|
|
||||||
/// Computes the quotient polynomials `(sum alpha^i C_i(x)) / Z_H(x)` for `alpha` in `alphas`,
|
/// Computes the quotient polynomials `(sum alpha^i C_i(x)) / Z_H(x)` for `alpha` in `alphas`,
|
||||||
/// where the `C_i`s are the Stark constraints.
|
/// where the `C_i`s are the Stark constraints.
|
||||||
fn compute_quotient_polys<'a, F, P, C, S, const D: usize>(
|
fn compute_quotient_polys<'a, F, P, HCO, HCI, C, S, const D: usize>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
trace_commitment: &'a PolynomialBatch<F, C, D>,
|
trace_commitment: &'a PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
permutation_zs_commitment_challenges: &'a Option<(
|
permutation_zs_commitment_challenges: &'a Option<(
|
||||||
PolynomialBatch<F, C, D>,
|
PolynomialBatch<F, HCO, HCI, C, D>,
|
||||||
Vec<PermutationChallengeSet<F>>,
|
Vec<PermutationChallengeSet<F>>,
|
||||||
)>,
|
)>,
|
||||||
public_inputs: [F; S::PUBLIC_INPUTS],
|
public_inputs: [F; S::PUBLIC_INPUTS],
|
||||||
@ -211,7 +215,9 @@ fn compute_quotient_polys<'a, F, P, C, S, const D: usize>(
|
|||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
P: PackedField<Scalar = F>,
|
P: PackedField<Scalar = F>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); S::PUBLIC_INPUTS]:,
|
[(); S::PUBLIC_INPUTS]:,
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use plonky2::field::extension::Extendable;
|
|||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use plonky2::fri::witness_util::set_fri_proof_target;
|
use plonky2::fri::witness_util::set_fri_proof_target;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::ext_target::ExtensionTarget;
|
use plonky2::iop::ext_target::ExtensionTarget;
|
||||||
use plonky2::iop::witness::Witness;
|
use plonky2::iop::witness::Witness;
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
@ -27,7 +28,9 @@ use crate::vars::StarkEvaluationTargets;
|
|||||||
|
|
||||||
pub fn verify_stark_proof_circuit<
|
pub fn verify_stark_proof_circuit<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
@ -36,19 +39,21 @@ pub fn verify_stark_proof_circuit<
|
|||||||
proof_with_pis: StarkProofWithPublicInputsTarget<D>,
|
proof_with_pis: StarkProofWithPublicInputsTarget<D>,
|
||||||
inner_config: &StarkConfig,
|
inner_config: &StarkConfig,
|
||||||
) where
|
) where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); S::PUBLIC_INPUTS]:,
|
[(); S::PUBLIC_INPUTS]:,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
assert_eq!(proof_with_pis.public_inputs.len(), S::PUBLIC_INPUTS);
|
assert_eq!(proof_with_pis.public_inputs.len(), S::PUBLIC_INPUTS);
|
||||||
let degree_bits = proof_with_pis.proof.recover_degree_bits(inner_config);
|
let degree_bits = proof_with_pis.proof.recover_degree_bits(inner_config);
|
||||||
let challenges = with_context!(
|
let challenges = with_context!(
|
||||||
builder,
|
builder,
|
||||||
"compute challenges",
|
"compute challenges",
|
||||||
proof_with_pis.get_challenges::<F, C, S>(builder, &stark, inner_config)
|
proof_with_pis.get_challenges::<F, HCO, HCI, C, S>(builder, &stark, inner_config)
|
||||||
);
|
);
|
||||||
|
|
||||||
verify_stark_proof_with_challenges_circuit::<F, C, S, D>(
|
verify_stark_proof_with_challenges_circuit::<F, HCO, HCI, C, S, D>(
|
||||||
builder,
|
builder,
|
||||||
stark,
|
stark,
|
||||||
proof_with_pis,
|
proof_with_pis,
|
||||||
@ -61,7 +66,9 @@ pub fn verify_stark_proof_circuit<
|
|||||||
/// Recursively verifies an inner proof.
|
/// Recursively verifies an inner proof.
|
||||||
fn verify_stark_proof_with_challenges_circuit<
|
fn verify_stark_proof_with_challenges_circuit<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
@ -72,9 +79,10 @@ fn verify_stark_proof_with_challenges_circuit<
|
|||||||
inner_config: &StarkConfig,
|
inner_config: &StarkConfig,
|
||||||
degree_bits: usize,
|
degree_bits: usize,
|
||||||
) where
|
) where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); S::PUBLIC_INPUTS]:,
|
[(); S::PUBLIC_INPUTS]:,
|
||||||
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
check_permutation_options(&stark, &proof_with_pis, &challenges).unwrap();
|
check_permutation_options(&stark, &proof_with_pis, &challenges).unwrap();
|
||||||
let one = builder.one_extension();
|
let one = builder.one_extension();
|
||||||
@ -161,7 +169,7 @@ fn verify_stark_proof_with_challenges_circuit<
|
|||||||
F::primitive_root_of_unity(degree_bits),
|
F::primitive_root_of_unity(degree_bits),
|
||||||
inner_config,
|
inner_config,
|
||||||
);
|
);
|
||||||
builder.verify_fri_proof::<C>(
|
builder.verify_fri_proof::<HCO, HCI, C>(
|
||||||
&fri_instance,
|
&fri_instance,
|
||||||
&proof.openings.to_fri_openings(),
|
&proof.openings.to_fri_openings(),
|
||||||
&challenges.fri_challenges,
|
&challenges.fri_challenges,
|
||||||
@ -259,13 +267,22 @@ fn add_stark_opening_set_target<F: RichField + Extendable<D>, S: Stark<F, D>, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_stark_proof_with_pis_target<F, C: GenericConfig<D, F = F>, W, const D: usize>(
|
pub fn set_stark_proof_with_pis_target<
|
||||||
|
F,
|
||||||
|
HCO,
|
||||||
|
HCI,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
W,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
witness: &mut W,
|
witness: &mut W,
|
||||||
stark_proof_with_pis_target: &StarkProofWithPublicInputsTarget<D>,
|
stark_proof_with_pis_target: &StarkProofWithPublicInputsTarget<D>,
|
||||||
stark_proof_with_pis: &StarkProofWithPublicInputs<F, C, D>,
|
stark_proof_with_pis: &StarkProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
) where
|
) where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
W: Witness<F>,
|
W: Witness<F>,
|
||||||
{
|
{
|
||||||
let StarkProofWithPublicInputs {
|
let StarkProofWithPublicInputs {
|
||||||
@ -285,13 +302,22 @@ pub fn set_stark_proof_with_pis_target<F, C: GenericConfig<D, F = F>, W, const D
|
|||||||
set_stark_proof_target(witness, pt, proof);
|
set_stark_proof_target(witness, pt, proof);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_stark_proof_target<F, C: GenericConfig<D, F = F>, W, const D: usize>(
|
pub fn set_stark_proof_target<
|
||||||
|
F,
|
||||||
|
HCO,
|
||||||
|
HCI,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
|
W,
|
||||||
|
const D: usize,
|
||||||
|
>(
|
||||||
witness: &mut W,
|
witness: &mut W,
|
||||||
proof_target: &StarkProofTarget<D>,
|
proof_target: &StarkProofTarget<D>,
|
||||||
proof: &StarkProof<F, C, D>,
|
proof: &StarkProof<F, HCO, HCI, C, D>,
|
||||||
) where
|
) where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C::Hasher: AlgebraicHasher<F, HCO>,
|
||||||
W: Witness<F>,
|
W: Witness<F>,
|
||||||
{
|
{
|
||||||
witness.set_cap_target(&proof_target.trace_cap, &proof.trace_cap);
|
witness.set_cap_target(&proof_target.trace_cap, &proof.trace_cap);
|
||||||
|
|||||||
@ -6,10 +6,11 @@ use plonky2::field::extension::{Extendable, FieldExtension};
|
|||||||
use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues};
|
use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues};
|
||||||
use plonky2::field::types::{Field, Sample};
|
use plonky2::field::types::{Field, Sample};
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::hashing::HashConfig;
|
||||||
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::config::{GenericConfig, Hasher};
|
use plonky2::plonk::config::GenericConfig;
|
||||||
use plonky2::util::{log2_ceil, log2_strict, transpose};
|
use plonky2::util::{log2_ceil, log2_strict, transpose};
|
||||||
|
|
||||||
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||||
@ -80,7 +81,9 @@ where
|
|||||||
/// Tests that the circuit constraints imposed by the given STARK are coherent with the native constraints.
|
/// Tests that the circuit constraints imposed by the given STARK are coherent with the native constraints.
|
||||||
pub fn test_stark_circuit_constraints<
|
pub fn test_stark_circuit_constraints<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
@ -89,7 +92,8 @@ pub fn test_stark_circuit_constraints<
|
|||||||
where
|
where
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); S::PUBLIC_INPUTS]:,
|
[(); S::PUBLIC_INPUTS]:,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
// Compute native constraint evaluation on random values.
|
// Compute native constraint evaluation on random values.
|
||||||
let vars = StarkEvaluationVars {
|
let vars = StarkEvaluationVars {
|
||||||
@ -151,7 +155,7 @@ where
|
|||||||
let native_eval_t = builder.constant_extension(native_eval);
|
let native_eval_t = builder.constant_extension(native_eval);
|
||||||
builder.connect_extension(circuit_eval, native_eval_t);
|
builder.connect_extension(circuit_eval, native_eval_t);
|
||||||
|
|
||||||
let data = builder.build::<C>();
|
let data = builder.build::<HCO, HCI, C>();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
data.verify(proof)
|
data.verify(proof)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,8 @@ use plonky2::field::extension::{Extendable, FieldExtension};
|
|||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use plonky2::fri::verifier::verify_fri_proof;
|
use plonky2::fri::verifier::verify_fri_proof;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::plonk::config::{GenericConfig, Hasher};
|
use plonky2::hash::hashing::HashConfig;
|
||||||
|
use plonky2::plonk::config::GenericConfig;
|
||||||
use plonky2::plonk::plonk_common::reduce_with_powers;
|
use plonky2::plonk::plonk_common::reduce_with_powers;
|
||||||
|
|
||||||
use crate::config::StarkConfig;
|
use crate::config::StarkConfig;
|
||||||
@ -20,18 +21,21 @@ use crate::vars::StarkEvaluationVars;
|
|||||||
|
|
||||||
pub fn verify_stark_proof<
|
pub fn verify_stark_proof<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
stark: S,
|
stark: S,
|
||||||
proof_with_pis: StarkProofWithPublicInputs<F, C, D>,
|
proof_with_pis: StarkProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); S::PUBLIC_INPUTS]:,
|
[(); S::PUBLIC_INPUTS]:,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); HCO::WIDTH]:,
|
||||||
|
[(); HCI::WIDTH]:,
|
||||||
{
|
{
|
||||||
ensure!(proof_with_pis.public_inputs.len() == S::PUBLIC_INPUTS);
|
ensure!(proof_with_pis.public_inputs.len() == S::PUBLIC_INPUTS);
|
||||||
let degree_bits = proof_with_pis.proof.recover_degree_bits(config);
|
let degree_bits = proof_with_pis.proof.recover_degree_bits(config);
|
||||||
@ -41,12 +45,14 @@ where
|
|||||||
|
|
||||||
pub(crate) fn verify_stark_proof_with_challenges<
|
pub(crate) fn verify_stark_proof_with_challenges<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
stark: S,
|
stark: S,
|
||||||
proof_with_pis: StarkProofWithPublicInputs<F, C, D>,
|
proof_with_pis: StarkProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
challenges: StarkProofChallenges<F, D>,
|
challenges: StarkProofChallenges<F, D>,
|
||||||
degree_bits: usize,
|
degree_bits: usize,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
@ -54,7 +60,7 @@ pub(crate) fn verify_stark_proof_with_challenges<
|
|||||||
where
|
where
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); S::PUBLIC_INPUTS]:,
|
[(); S::PUBLIC_INPUTS]:,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); HCO::WIDTH]:,
|
||||||
{
|
{
|
||||||
validate_proof_shape(&stark, &proof_with_pis, config)?;
|
validate_proof_shape(&stark, &proof_with_pis, config)?;
|
||||||
check_permutation_options(&stark, &proof_with_pis, &challenges)?;
|
check_permutation_options(&stark, &proof_with_pis, &challenges)?;
|
||||||
@ -130,7 +136,7 @@ where
|
|||||||
.chain(once(proof.quotient_polys_cap))
|
.chain(once(proof.quotient_polys_cap))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
verify_fri_proof::<F, C, D>(
|
verify_fri_proof::<F, HCO, HCI, C, D>(
|
||||||
&stark.fri_instance(
|
&stark.fri_instance(
|
||||||
challenges.stark_zeta,
|
challenges.stark_zeta,
|
||||||
F::primitive_root_of_unity(degree_bits),
|
F::primitive_root_of_unity(degree_bits),
|
||||||
@ -146,17 +152,18 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_proof_shape<F, C, S, const D: usize>(
|
fn validate_proof_shape<F, HCO, HCI, C, S, const D: usize>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
proof_with_pis: &StarkProofWithPublicInputs<F, C, D>,
|
proof_with_pis: &StarkProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> anyhow::Result<()>
|
) -> anyhow::Result<()>
|
||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
|
||||||
{
|
{
|
||||||
let StarkProofWithPublicInputs {
|
let StarkProofWithPublicInputs {
|
||||||
proof,
|
proof,
|
||||||
@ -234,12 +241,14 @@ fn eval_l_0_and_l_last<F: Field>(log_n: usize, x: F) -> (F, F) {
|
|||||||
/// the Stark uses a permutation argument.
|
/// the Stark uses a permutation argument.
|
||||||
fn check_permutation_options<
|
fn check_permutation_options<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
HCO: HashConfig,
|
||||||
|
HCI: HashConfig,
|
||||||
|
C: GenericConfig<HCO, HCI, D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
proof_with_pis: &StarkProofWithPublicInputs<F, C, D>,
|
proof_with_pis: &StarkProofWithPublicInputs<F, HCO, HCI, C, D>,
|
||||||
challenges: &StarkProofChallenges<F, D>,
|
challenges: &StarkProofChallenges<F, D>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let options_is_some = [
|
let options_is_some = [
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user