mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-05 23:33:07 +00:00
Merge pull request #481 from mir-protocol/fix_hash_or_noop_merkle_proof
Use `hash_or_noop` for Merkle tree leaves
This commit is contained in:
commit
f4640bb5a1
@ -1,3 +1,5 @@
|
|||||||
|
#![feature(generic_const_exprs)]
|
||||||
|
|
||||||
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;
|
||||||
@ -9,7 +11,10 @@ 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, H: Hasher<F>>(c: &mut Criterion)
|
||||||
|
where
|
||||||
|
[(); H::HASH_SIZE]:,
|
||||||
|
{
|
||||||
let mut group = c.benchmark_group(&format!(
|
let mut group = c.benchmark_group(&format!(
|
||||||
"merkle-tree<{}, {}>",
|
"merkle-tree<{}, {}>",
|
||||||
type_name::<F>(),
|
type_name::<F>(),
|
||||||
|
|||||||
@ -12,7 +12,7 @@ use crate::fri::FriParams;
|
|||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
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, Hasher};
|
||||||
use crate::timed;
|
use crate::timed;
|
||||||
use crate::util::reducing::ReducingFactor;
|
use crate::util::reducing::ReducingFactor;
|
||||||
use crate::util::reverse_bits;
|
use crate::util::reverse_bits;
|
||||||
@ -43,7 +43,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
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
let coeffs = timed!(
|
let coeffs = timed!(
|
||||||
timing,
|
timing,
|
||||||
"IFFT",
|
"IFFT",
|
||||||
@ -68,7 +71,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
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
let degree = polynomials[0].len();
|
let degree = polynomials[0].len();
|
||||||
let lde_values = timed!(
|
let lde_values = timed!(
|
||||||
timing,
|
timing,
|
||||||
@ -133,7 +139,10 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
challenger: &mut Challenger<F, C::Hasher>,
|
challenger: &mut Challenger<F, C::Hasher>,
|
||||||
fri_params: &FriParams,
|
fri_params: &FriParams,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> FriProof<F, C::Hasher, D> {
|
) -> FriProof<F, C::Hasher, D>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
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);
|
||||||
|
|||||||
@ -245,7 +245,10 @@ impl<F: RichField + Extendable<D>, H: Hasher<F>, const D: usize> CompressedFriPr
|
|||||||
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, H, D>
|
||||||
|
where
|
||||||
|
[(); H::HASH_SIZE]:,
|
||||||
|
{
|
||||||
let CompressedFriProof {
|
let CompressedFriProof {
|
||||||
commit_phase_merkle_caps,
|
commit_phase_merkle_caps,
|
||||||
query_round_proofs,
|
query_round_proofs,
|
||||||
|
|||||||
@ -24,7 +24,10 @@ pub fn fri_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const
|
|||||||
challenger: &mut Challenger<F, C::Hasher>,
|
challenger: &mut Challenger<F, C::Hasher>,
|
||||||
fri_params: &FriParams,
|
fri_params: &FriParams,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> FriProof<F, C::Hasher, D> {
|
) -> FriProof<F, C::Hasher, D>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
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);
|
||||||
|
|
||||||
@ -68,7 +71,10 @@ fn fri_committed_trees<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>,
|
|||||||
) -> (
|
) -> (
|
||||||
Vec<MerkleTree<F, C::Hasher>>,
|
Vec<MerkleTree<F, C::Hasher>>,
|
||||||
PolynomialCoeffs<F::Extension>,
|
PolynomialCoeffs<F::Extension>,
|
||||||
) {
|
)
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
let mut trees = Vec::new();
|
let mut trees = Vec::new();
|
||||||
|
|
||||||
let mut shift = F::MULTIPLICATIVE_GROUP_GENERATOR;
|
let mut shift = F::MULTIPLICATIVE_GROUP_GENERATOR;
|
||||||
|
|||||||
@ -56,18 +56,17 @@ pub(crate) fn fri_verify_proof_of_work<F: RichField + Extendable<D>, const D: us
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_fri_proof<
|
pub fn verify_fri_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
C: GenericConfig<D, F = F>,
|
|
||||||
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, C::Hasher>],
|
||||||
proof: &FriProof<F, C::Hasher, D>,
|
proof: &FriProof<F, C::Hasher, D>,
|
||||||
params: &FriParams,
|
params: &FriParams,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
ensure!(
|
ensure!(
|
||||||
params.final_poly_len() == proof.final_poly.len(),
|
params.final_poly_len() == proof.final_poly.len(),
|
||||||
"Final polynomial has wrong degree."
|
"Final polynomial has wrong degree."
|
||||||
@ -112,7 +111,10 @@ fn fri_verify_initial_proof<F: RichField, H: Hasher<F>>(
|
|||||||
x_index: usize,
|
x_index: usize,
|
||||||
proof: &FriInitialTreeProof<F, H>,
|
proof: &FriInitialTreeProof<F, H>,
|
||||||
initial_merkle_caps: &[MerkleCap<F, H>],
|
initial_merkle_caps: &[MerkleCap<F, H>],
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); H::HASH_SIZE]:,
|
||||||
|
{
|
||||||
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::<F, H>(evals.clone(), x_index, cap, merkle_proof)?;
|
verify_merkle_proof::<F, H>(evals.clone(), x_index, cap, merkle_proof)?;
|
||||||
}
|
}
|
||||||
@ -177,7 +179,10 @@ fn fri_verifier_query_round<
|
|||||||
n: usize,
|
n: usize,
|
||||||
round_proof: &FriQueryRound<F, C::Hasher, D>,
|
round_proof: &FriQueryRound<F, C::Hasher, D>,
|
||||||
params: &FriParams,
|
params: &FriParams,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
fri_verify_initial_proof::<F, C::Hasher>(
|
fri_verify_initial_proof::<F, C::Hasher>(
|
||||||
x_index,
|
x_index,
|
||||||
&round_proof.initial_trees_proof,
|
&round_proof.initial_trees_proof,
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use crate::hash::hash_types::RichField;
|
|||||||
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;
|
use crate::plonk::config::{GenericConfig, Hasher};
|
||||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBaseBatch};
|
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBaseBatch};
|
||||||
use crate::plonk::verifier::verify;
|
use crate::plonk::verifier::verify;
|
||||||
use crate::util::transpose;
|
use crate::util::transpose;
|
||||||
@ -92,7 +92,10 @@ pub fn test_eval_fns<
|
|||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
gate: G,
|
gate: G,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
// 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());
|
||||||
|
|||||||
@ -12,16 +12,6 @@ pub(crate) const SPONGE_RATE: usize = 8;
|
|||||||
pub(crate) const SPONGE_CAPACITY: usize = 4;
|
pub(crate) const SPONGE_CAPACITY: usize = 4;
|
||||||
pub const SPONGE_WIDTH: usize = SPONGE_RATE + SPONGE_CAPACITY;
|
pub const SPONGE_WIDTH: usize = SPONGE_RATE + SPONGE_CAPACITY;
|
||||||
|
|
||||||
/// Hash the slice if necessary to reduce its length to ~256 bits. If it already fits, this is a
|
|
||||||
/// no-op.
|
|
||||||
pub fn hash_or_noop<F: RichField, P: PlonkyPermutation<F>>(inputs: &[F]) -> HashOut<F> {
|
|
||||||
if inputs.len() <= 4 {
|
|
||||||
HashOut::from_partial(inputs)
|
|
||||||
} else {
|
|
||||||
hash_n_to_hash_no_pad::<F, P>(inputs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<H: AlgebraicHasher<F>>(&mut self, inputs: Vec<Target>) -> HashOutTarget {
|
||||||
let zero = self.zero();
|
let zero = self.zero();
|
||||||
|
|||||||
@ -30,9 +30,12 @@ pub(crate) fn verify_merkle_proof<F: RichField, H: Hasher<F>>(
|
|||||||
leaf_index: usize,
|
leaf_index: usize,
|
||||||
merkle_cap: &MerkleCap<F, H>,
|
merkle_cap: &MerkleCap<F, H>,
|
||||||
proof: &MerkleProof<F, H>,
|
proof: &MerkleProof<F, H>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); H::HASH_SIZE]:,
|
||||||
|
{
|
||||||
let mut index = leaf_index;
|
let mut index = leaf_index;
|
||||||
let mut current_digest = H::hash_no_pad(&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() {
|
||||||
let bit = index & 1;
|
let bit = index & 1;
|
||||||
index >>= 1;
|
index >>= 1;
|
||||||
|
|||||||
@ -60,10 +60,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, H: Hasher<F>>(
|
||||||
digests_buf: &mut [MaybeUninit<H::Hash>],
|
digests_buf: &mut [MaybeUninit<H::Hash>],
|
||||||
leaves: &[Vec<F>],
|
leaves: &[Vec<F>],
|
||||||
) -> H::Hash {
|
) -> H::Hash
|
||||||
|
where
|
||||||
|
[(); H::HASH_SIZE]:,
|
||||||
|
{
|
||||||
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_no_pad(&leaves[0])
|
H::hash_or_noop(&leaves[0])
|
||||||
} else {
|
} else {
|
||||||
// Layout is: left recursive output || left child digest
|
// Layout is: left recursive output || left child digest
|
||||||
// || right child digest || right recursive output.
|
// || right child digest || right recursive output.
|
||||||
@ -89,7 +92,9 @@ fn fill_digests_buf<F: RichField, H: Hasher<F>>(
|
|||||||
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
|
||||||
|
[(); H::HASH_SIZE]:,
|
||||||
|
{
|
||||||
// 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`.)
|
||||||
@ -99,7 +104,7 @@ fn fill_digests_buf<F: RichField, H: Hasher<F>>(
|
|||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.zip(leaves)
|
.zip(leaves)
|
||||||
.for_each(|(cap_buf, leaf)| {
|
.for_each(|(cap_buf, leaf)| {
|
||||||
cap_buf.write(H::hash_no_pad(leaf));
|
cap_buf.write(H::hash_or_noop(leaf));
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -121,7 +126,10 @@ fn fill_digests_buf<F: RichField, H: Hasher<F>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<F: RichField, H: Hasher<F>> MerkleTree<F, H> {
|
impl<F: RichField, H: Hasher<F>> MerkleTree<F, H> {
|
||||||
pub fn new(leaves: Vec<Vec<F>>, cap_height: usize) -> Self {
|
pub fn new(leaves: Vec<Vec<F>>, cap_height: usize) -> Self
|
||||||
|
where
|
||||||
|
[(); H::HASH_SIZE]:,
|
||||||
|
{
|
||||||
let log2_leaves_len = log2_strict(leaves.len());
|
let log2_leaves_len = log2_strict(leaves.len());
|
||||||
assert!(
|
assert!(
|
||||||
cap_height <= log2_leaves_len,
|
cap_height <= log2_leaves_len,
|
||||||
@ -208,14 +216,13 @@ mod tests {
|
|||||||
(0..n).map(|_| F::rand_vec(k)).collect()
|
(0..n).map(|_| F::rand_vec(k)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_all_leaves<
|
fn verify_all_leaves<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
C: GenericConfig<D, F = F>,
|
|
||||||
const D: usize,
|
|
||||||
>(
|
|
||||||
leaves: Vec<Vec<F>>,
|
leaves: Vec<Vec<F>>,
|
||||||
cap_height: usize,
|
cap_height: usize,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
let tree = MerkleTree::<F, C::Hasher>::new(leaves.clone(), cap_height);
|
let tree = MerkleTree::<F, 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);
|
||||||
|
|||||||
@ -57,7 +57,10 @@ pub(crate) fn decompress_merkle_proofs<F: RichField, H: Hasher<F>>(
|
|||||||
compressed_proofs: &[MerkleProof<F, H>],
|
compressed_proofs: &[MerkleProof<F, H>],
|
||||||
height: usize,
|
height: usize,
|
||||||
cap_height: usize,
|
cap_height: usize,
|
||||||
) -> Vec<MerkleProof<F, H>> {
|
) -> Vec<MerkleProof<F, H>>
|
||||||
|
where
|
||||||
|
[(); H::HASH_SIZE]:,
|
||||||
|
{
|
||||||
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());
|
||||||
@ -66,7 +69,7 @@ pub(crate) fn decompress_merkle_proofs<F: RichField, H: Hasher<F>>(
|
|||||||
|
|
||||||
for (&i, v) in leaves_indices.iter().zip(leaves_data) {
|
for (&i, v) in leaves_indices.iter().zip(leaves_data) {
|
||||||
// Observe the leaves.
|
// Observe the leaves.
|
||||||
seen.insert(i + num_leaves, H::hash_no_pad(v));
|
seen.insert(i + num_leaves, H::hash_or_noop(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterators over the siblings.
|
// Iterators over the siblings.
|
||||||
|
|||||||
@ -610,7 +610,10 @@ 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<C: GenericConfig<D, F = F>>(mut self) -> CircuitData<F, C, D>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
let mut timing = TimingTree::new("preprocess", Level::Trace);
|
let mut timing = TimingTree::new("preprocess", Level::Trace);
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let rate_bits = self.config.fri_config.rate_bits;
|
let rate_bits = self.config.fri_config.rate_bits;
|
||||||
@ -776,7 +779,10 @@ 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<C: GenericConfig<D, F = F>>(self) -> ProverCircuitData<F, C, D>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
// TODO: Can skip parts of this.
|
// TODO: Can skip parts of this.
|
||||||
let CircuitData {
|
let CircuitData {
|
||||||
prover_only,
|
prover_only,
|
||||||
@ -790,7 +796,10 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 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<C: GenericConfig<D, F = F>>(self) -> VerifierCircuitData<F, C, D>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
// TODO: Can skip parts of this.
|
// TODO: Can skip parts of this.
|
||||||
let CircuitData {
|
let CircuitData {
|
||||||
verifier_only,
|
verifier_only,
|
||||||
|
|||||||
@ -104,7 +104,10 @@ pub struct CircuitData<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>,
|
|||||||
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>
|
||||||
CircuitData<F, C, D>
|
CircuitData<F, C, D>
|
||||||
{
|
{
|
||||||
pub fn prove(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>> {
|
pub fn prove(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
prove(
|
prove(
|
||||||
&self.prover_only,
|
&self.prover_only,
|
||||||
&self.common,
|
&self.common,
|
||||||
@ -113,14 +116,20 @@ 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, C, D>) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
verify(proof_with_pis, &self.verifier_only, &self.common)
|
verify(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, C, D>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
compressed_proof_with_pis.verify(&self.verifier_only, &self.common)
|
compressed_proof_with_pis.verify(&self.verifier_only, &self.common)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,7 +153,10 @@ pub struct ProverCircuitData<
|
|||||||
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>
|
||||||
ProverCircuitData<F, C, D>
|
ProverCircuitData<F, C, D>
|
||||||
{
|
{
|
||||||
pub fn prove(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>> {
|
pub fn prove(&self, inputs: PartialWitness<F>) -> Result<ProofWithPublicInputs<F, C, D>>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
prove(
|
prove(
|
||||||
&self.prover_only,
|
&self.prover_only,
|
||||||
&self.common,
|
&self.common,
|
||||||
@ -168,14 +180,20 @@ pub struct VerifierCircuitData<
|
|||||||
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>
|
||||||
VerifierCircuitData<F, C, D>
|
VerifierCircuitData<F, C, D>
|
||||||
{
|
{
|
||||||
pub fn verify(&self, proof_with_pis: ProofWithPublicInputs<F, C, D>) -> Result<()> {
|
pub fn verify(&self, proof_with_pis: ProofWithPublicInputs<F, C, D>) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
verify(proof_with_pis, &self.verifier_only, &self.common)
|
verify(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, C, D>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
compressed_proof_with_pis.verify(&self.verifier_only, &self.common)
|
compressed_proof_with_pis.verify(&self.verifier_only, &self.common)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,24 @@ pub trait Hasher<F: RichField>: Sized + Clone + Debug + Eq + PartialEq {
|
|||||||
Self::hash_no_pad(&padded_input)
|
Self::hash_no_pad(&padded_input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Hash the slice if necessary to reduce its length to ~256 bits. If it already fits, this is a
|
||||||
|
/// no-op.
|
||||||
|
fn hash_or_noop(inputs: &[F]) -> Self::Hash
|
||||||
|
where
|
||||||
|
[(); Self::HASH_SIZE]:,
|
||||||
|
{
|
||||||
|
if inputs.len() <= 4 {
|
||||||
|
let mut inputs_bytes = [0u8; Self::HASH_SIZE];
|
||||||
|
for i in 0..inputs.len() {
|
||||||
|
inputs_bytes[i * 8..(i + 1) * 8]
|
||||||
|
.copy_from_slice(&inputs[i].to_canonical_u64().to_le_bytes());
|
||||||
|
}
|
||||||
|
Self::Hash::from_bytes(&inputs_bytes)
|
||||||
|
} else {
|
||||||
|
Self::hash_no_pad(inputs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash;
|
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -138,7 +138,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, C, D>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
let CompressedProof {
|
let CompressedProof {
|
||||||
wires_cap,
|
wires_cap,
|
||||||
plonk_zs_partial_products_cap,
|
plonk_zs_partial_products_cap,
|
||||||
@ -174,7 +177,10 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
pub fn decompress(
|
pub fn decompress(
|
||||||
self,
|
self,
|
||||||
common_data: &CommonCircuitData<F, C, D>,
|
common_data: &CommonCircuitData<F, C, D>,
|
||||||
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>> {
|
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
let challenges = self.get_challenges(self.get_public_inputs_hash(), common_data)?;
|
let challenges = self.get_challenges(self.get_public_inputs_hash(), common_data)?;
|
||||||
let fri_inferred_elements = self.get_inferred_elements(&challenges, common_data);
|
let fri_inferred_elements = self.get_inferred_elements(&challenges, common_data);
|
||||||
let decompressed_proof =
|
let decompressed_proof =
|
||||||
@ -190,7 +196,10 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
self,
|
self,
|
||||||
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
||||||
common_data: &CommonCircuitData<F, C, D>,
|
common_data: &CommonCircuitData<F, C, D>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
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."
|
||||||
|
|||||||
@ -31,7 +31,10 @@ pub(crate) fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, co
|
|||||||
common_data: &CommonCircuitData<F, C, D>,
|
common_data: &CommonCircuitData<F, C, D>,
|
||||||
inputs: PartialWitness<F>,
|
inputs: PartialWitness<F>,
|
||||||
timing: &mut TimingTree,
|
timing: &mut TimingTree,
|
||||||
) -> Result<ProofWithPublicInputs<F, C, D>> {
|
) -> Result<ProofWithPublicInputs<F, C, D>>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
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();
|
||||||
|
|||||||
@ -187,7 +187,9 @@ mod tests {
|
|||||||
use crate::gates::noop::NoopGate;
|
use crate::gates::noop::NoopGate;
|
||||||
use crate::iop::witness::{PartialWitness, Witness};
|
use crate::iop::witness::{PartialWitness, Witness};
|
||||||
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, Hasher, KeccakGoldilocksConfig, PoseidonGoldilocksConfig,
|
||||||
|
};
|
||||||
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;
|
||||||
@ -322,7 +324,10 @@ mod tests {
|
|||||||
ProofWithPublicInputs<F, C, D>,
|
ProofWithPublicInputs<F, C, D>,
|
||||||
VerifierOnlyCircuitData<C, D>,
|
VerifierOnlyCircuitData<C, D>,
|
||||||
CommonCircuitData<F, C, D>,
|
CommonCircuitData<F, C, D>,
|
||||||
)> {
|
)>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
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![]);
|
||||||
@ -356,6 +361,7 @@ mod tests {
|
|||||||
)>
|
)>
|
||||||
where
|
where
|
||||||
InnerC::Hasher: AlgebraicHasher<F>,
|
InnerC::Hasher: AlgebraicHasher<F>,
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
{
|
{
|
||||||
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();
|
||||||
@ -407,7 +413,10 @@ mod tests {
|
|||||||
>(
|
>(
|
||||||
proof: &ProofWithPublicInputs<F, C, D>,
|
proof: &ProofWithPublicInputs<F, C, D>,
|
||||||
cd: &CommonCircuitData<F, C, D>,
|
cd: &CommonCircuitData<F, C, D>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
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)?;
|
||||||
|
|||||||
@ -15,7 +15,10 @@ pub(crate) fn verify<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, c
|
|||||||
proof_with_pis: ProofWithPublicInputs<F, C, D>,
|
proof_with_pis: ProofWithPublicInputs<F, C, D>,
|
||||||
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
||||||
common_data: &CommonCircuitData<F, C, D>,
|
common_data: &CommonCircuitData<F, C, D>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
ensure!(
|
ensure!(
|
||||||
proof_with_pis.public_inputs.len() == common_data.num_public_inputs,
|
proof_with_pis.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."
|
||||||
@ -42,7 +45,10 @@ pub(crate) fn verify_with_challenges<
|
|||||||
challenges: ProofChallenges<F, D>,
|
challenges: ProofChallenges<F, D>,
|
||||||
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
||||||
common_data: &CommonCircuitData<F, C, D>,
|
common_data: &CommonCircuitData<F, C, D>,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
{
|
||||||
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 {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ 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::iop::challenger::Challenger;
|
use plonky2::iop::challenger::Challenger;
|
||||||
use plonky2::plonk::config::GenericConfig;
|
use plonky2::plonk::config::{GenericConfig, Hasher};
|
||||||
use plonky2::timed;
|
use plonky2::timed;
|
||||||
use plonky2::util::timing::TimingTree;
|
use plonky2::util::timing::TimingTree;
|
||||||
use plonky2::util::transpose;
|
use plonky2::util::transpose;
|
||||||
@ -33,6 +33,7 @@ where
|
|||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); S::PUBLIC_INPUTS]:,
|
[(); S::PUBLIC_INPUTS]:,
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
{
|
{
|
||||||
let degree = trace.len();
|
let degree = trace.len();
|
||||||
let degree_bits = log2_strict(degree);
|
let degree_bits = log2_strict(degree);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use plonky2::field::extension_field::{Extendable, FieldExtension};
|
|||||||
use plonky2::field::field_types::Field;
|
use plonky2::field::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;
|
use plonky2::plonk::config::{GenericConfig, Hasher};
|
||||||
use plonky2::plonk::plonk_common::reduce_with_powers;
|
use plonky2::plonk::plonk_common::reduce_with_powers;
|
||||||
use plonky2_util::log2_strict;
|
use plonky2_util::log2_strict;
|
||||||
|
|
||||||
@ -26,6 +26,7 @@ pub fn verify<
|
|||||||
where
|
where
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); S::PUBLIC_INPUTS]:,
|
[(); S::PUBLIC_INPUTS]:,
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
{
|
{
|
||||||
let degree_bits = log2_strict(recover_degree(&proof_with_pis.proof, config));
|
let degree_bits = log2_strict(recover_degree(&proof_with_pis.proof, config));
|
||||||
let challenges = proof_with_pis.get_challenges(config, degree_bits)?;
|
let challenges = proof_with_pis.get_challenges(config, degree_bits)?;
|
||||||
@ -47,6 +48,7 @@ pub(crate) fn verify_with_challenges<
|
|||||||
where
|
where
|
||||||
[(); S::COLUMNS]:,
|
[(); S::COLUMNS]:,
|
||||||
[(); S::PUBLIC_INPUTS]:,
|
[(); S::PUBLIC_INPUTS]:,
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
{
|
{
|
||||||
let StarkProofWithPublicInputs {
|
let StarkProofWithPublicInputs {
|
||||||
proof,
|
proof,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user