mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-07 00:03:10 +00:00
Merge pull request #5 from mir-protocol/validate_cosets
Validate that the cosets involved in Plonk's permutation argument are disjoint
This commit is contained in:
commit
2f54cedb5d
@ -10,7 +10,7 @@ use crate::gates::gate::{GateInstance, GateRef};
|
|||||||
use crate::gates::noop::NoopGate;
|
use crate::gates::noop::NoopGate;
|
||||||
use crate::generator::{CopyGenerator, WitnessGenerator};
|
use crate::generator::{CopyGenerator, WitnessGenerator};
|
||||||
use crate::hash::merkle_root_bit_rev_order;
|
use crate::hash::merkle_root_bit_rev_order;
|
||||||
use crate::partition::get_subgroup_shift;
|
use crate::field::cosets::get_unique_coset_shifts;
|
||||||
use crate::polynomial::polynomial::PolynomialValues;
|
use crate::polynomial::polynomial::PolynomialValues;
|
||||||
use crate::target::Target;
|
use crate::target::Target;
|
||||||
use crate::util::{log2_strict, transpose, transpose_poly_values};
|
use crate::util::{log2_strict, transpose, transpose_poly_values};
|
||||||
@ -188,13 +188,12 @@ impl<F: Field> CircuitBuilder<F> {
|
|||||||
.max()
|
.max()
|
||||||
.expect("No gates?");
|
.expect("No gates?");
|
||||||
|
|
||||||
let k_is = (0..self.config.num_routed_wires)
|
let degree_bits = log2_strict(degree);
|
||||||
.map(get_subgroup_shift)
|
let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires);
|
||||||
.collect();
|
|
||||||
|
|
||||||
let common = CommonCircuitData {
|
let common = CommonCircuitData {
|
||||||
config: self.config,
|
config: self.config,
|
||||||
degree_bits: log2_strict(degree),
|
degree_bits,
|
||||||
gates,
|
gates,
|
||||||
num_gate_constraints,
|
num_gate_constraints,
|
||||||
constants_root,
|
constants_root,
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
use crate::circuit_builder::CircuitBuilder;
|
|
||||||
use crate::field::field::Field;
|
use crate::field::field::Field;
|
||||||
use crate::gates::gate::GateRef;
|
use crate::gates::gate::GateRef;
|
||||||
use crate::generator::WitnessGenerator;
|
use crate::generator::WitnessGenerator;
|
||||||
use crate::proof::{Hash, Proof, HashTarget};
|
use crate::proof::{Hash, HashTarget, Proof};
|
||||||
use crate::prover::prove;
|
use crate::prover::prove;
|
||||||
use crate::target::Target;
|
|
||||||
use crate::verifier::verify;
|
use crate::verifier::verify;
|
||||||
use crate::witness::PartialWitness;
|
use crate::witness::PartialWitness;
|
||||||
|
|
||||||
@ -112,7 +110,7 @@ pub(crate) struct CommonCircuitData<F: Field> {
|
|||||||
/// A commitment to each permutation polynomial.
|
/// A commitment to each permutation polynomial.
|
||||||
pub(crate) sigmas_root: Hash<F>,
|
pub(crate) sigmas_root: Hash<F>,
|
||||||
|
|
||||||
/// {k_i}. See `get_subgroup_shift`.
|
/// The `{k_i}` valued used in `S_ID_i` in Plonk's permutation argument.
|
||||||
pub(crate) k_is: Vec<F>,
|
pub(crate) k_is: Vec<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
51
src/field/cosets.rs
Normal file
51
src/field/cosets.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use crate::field::field::Field;
|
||||||
|
|
||||||
|
/// Finds a set of shifts that result in unique cosets for the multiplicative subgroup of size
|
||||||
|
/// `2^subgroup_bits`.
|
||||||
|
pub(crate) fn get_unique_coset_shifts<F: Field>(
|
||||||
|
subgroup_size: usize,
|
||||||
|
num_shifts: usize,
|
||||||
|
) -> Vec<F> {
|
||||||
|
// From Lagrange's theorem.
|
||||||
|
let num_cosets = (F::ORDER - 1) / (subgroup_size as u64);
|
||||||
|
assert!(num_shifts as u64 <= num_cosets,
|
||||||
|
"The subgroup does not have enough distinct cosets");
|
||||||
|
|
||||||
|
// Let g be a generator of the entire multiplicative group. Let n be the order of the subgroup.
|
||||||
|
// The subgroup can be written as <g^(|F*| / n)>. We can use g^0, ..., g^(num_shifts - 1) as our
|
||||||
|
// shifts, since g^i <g^(|F*| / n)> are distinct cosets provided i < |F*| / n, which we checked.
|
||||||
|
F::MULTIPLICATIVE_GROUP_GENERATOR.powers()
|
||||||
|
.take(num_shifts)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use crate::field::cosets::get_unique_coset_shifts;
|
||||||
|
use crate::field::crandall_field::CrandallField;
|
||||||
|
use crate::field::field::Field;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn distinct_cosets() {
|
||||||
|
// TODO: Switch to a smaller test field so that collision rejection is likely to occur.
|
||||||
|
|
||||||
|
type F = CrandallField;
|
||||||
|
const SUBGROUP_BITS: usize = 5;
|
||||||
|
const NUM_SHIFTS: usize = 50;
|
||||||
|
|
||||||
|
let generator = F::primitive_root_of_unity(SUBGROUP_BITS);
|
||||||
|
let subgroup_size = 1 << SUBGROUP_BITS;
|
||||||
|
|
||||||
|
let shifts = get_unique_coset_shifts::<F>(SUBGROUP_BITS, NUM_SHIFTS);
|
||||||
|
|
||||||
|
let mut union = HashSet::new();
|
||||||
|
for shift in shifts {
|
||||||
|
let coset = F::cyclic_subgroup_coset_known_order(generator, shift, subgroup_size);
|
||||||
|
assert!(
|
||||||
|
coset.into_iter().all(|x| union.insert(x)),
|
||||||
|
"Duplicate element!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi
|
|||||||
use num::Integer;
|
use num::Integer;
|
||||||
|
|
||||||
use crate::field::field::Field;
|
use crate::field::field::Field;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
/// EPSILON = 9 * 2**28 - 1
|
/// EPSILON = 9 * 2**28 - 1
|
||||||
const EPSILON: u64 = 2415919103;
|
const EPSILON: u64 = 2415919103;
|
||||||
@ -17,7 +18,6 @@ const EPSILON: u64 = 2415919103;
|
|||||||
/// = 2**64 - 9 * 2**28 + 1
|
/// = 2**64 - 9 * 2**28 + 1
|
||||||
/// = 2**28 * (2**36 - 9) + 1
|
/// = 2**28 * (2**36 - 9) + 1
|
||||||
/// ```
|
/// ```
|
||||||
// TODO: [Partial]Eq should compare canonical representations.
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct CrandallField(pub u64);
|
pub struct CrandallField(pub u64);
|
||||||
|
|
||||||
@ -29,6 +29,12 @@ impl PartialEq for CrandallField {
|
|||||||
|
|
||||||
impl Eq for CrandallField {}
|
impl Eq for CrandallField {}
|
||||||
|
|
||||||
|
impl Hash for CrandallField {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
state.write_u64(self.to_canonical_u64())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for CrandallField {
|
impl Display for CrandallField {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
Display::fmt(&self.0, f)
|
Display::fmt(&self.0, f)
|
||||||
@ -64,59 +70,69 @@ impl Field for CrandallField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn try_inverse(&self) -> Option<Self> {
|
fn try_inverse(&self) -> Option<Self> {
|
||||||
if *self == Self::ZERO {
|
if self.is_zero() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on Algorithm 16 of "Efficient Software-Implementation of Finite Fields with
|
// Based on Algorithm 16 of "Efficient Software-Implementation of Finite Fields with
|
||||||
// Applications to Cryptography".
|
// Applications to Cryptography".
|
||||||
|
|
||||||
let mut u = self.0;
|
let p = Self::ORDER;
|
||||||
let mut v = Self::ORDER;
|
let mut u = self.to_canonical_u64();
|
||||||
let mut b = 1;
|
let mut v = p;
|
||||||
let mut c = 0;
|
let mut b = 1u64;
|
||||||
|
let mut c = 0u64;
|
||||||
|
|
||||||
while u != 1 && v != 1 {
|
while u != 1 && v != 1 {
|
||||||
while u.is_even() {
|
while u.is_even() {
|
||||||
u >>= 1;
|
u /= 2;
|
||||||
if b.is_even() {
|
if b.is_even() {
|
||||||
b >>= 1;
|
b /= 2;
|
||||||
} else {
|
} else {
|
||||||
// b = (b + p)/2, avoiding overflow
|
// b = (b + p)/2, avoiding overflow
|
||||||
b = (b >> 1) + (Self::ORDER >> 1) + 1;
|
b = (b / 2) + (p / 2) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while v.is_even() {
|
while v.is_even() {
|
||||||
v >>= 1;
|
v /= 2;
|
||||||
if c.is_even() {
|
if c.is_even() {
|
||||||
c >>= 1;
|
c /= 2;
|
||||||
} else {
|
} else {
|
||||||
// c = (c + p)/2, avoiding overflow
|
// c = (c + p)/2, avoiding overflow
|
||||||
c = (c >> 1) + (Self::ORDER >> 1) + 1;
|
c = (c / 2) + (p / 2) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u < v {
|
if u >= v {
|
||||||
v -= u;
|
|
||||||
if c < b {
|
|
||||||
c += Self::ORDER;
|
|
||||||
}
|
|
||||||
c -= b;
|
|
||||||
} else {
|
|
||||||
u -= v;
|
u -= v;
|
||||||
if b < c {
|
// b -= c
|
||||||
b += Self::ORDER;
|
let (mut diff, under) = b.overflowing_sub(c);
|
||||||
|
if under {
|
||||||
|
diff = diff.overflowing_add(p).0;
|
||||||
}
|
}
|
||||||
b -= c;
|
b = diff;
|
||||||
|
} else {
|
||||||
|
v -= u;
|
||||||
|
// c -= b
|
||||||
|
let (mut diff, under) = c.overflowing_sub(b);
|
||||||
|
if under {
|
||||||
|
diff = diff.overflowing_add(p).0;
|
||||||
|
}
|
||||||
|
c = diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Self(if u == 1 {
|
let inverse = Self(if u == 1 {
|
||||||
b
|
b
|
||||||
} else {
|
} else {
|
||||||
c
|
c
|
||||||
}))
|
});
|
||||||
|
|
||||||
|
// Should change to debug_assert_eq; using assert_eq as an extra precaution for now until
|
||||||
|
// we're more confident the impl is correct.
|
||||||
|
assert_eq!(*self * inverse, Self::ONE);
|
||||||
|
Some(inverse)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@ -3,11 +3,13 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi
|
|||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use crate::util::bits_u64;
|
use crate::util::bits_u64;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
/// A finite field with prime order less than 2^64.
|
/// A finite field with prime order less than 2^64.
|
||||||
pub trait Field: 'static
|
pub trait Field: 'static
|
||||||
+ Copy
|
+ Copy
|
||||||
+ Eq
|
+ Eq
|
||||||
|
+ Hash
|
||||||
+ Neg<Output=Self>
|
+ Neg<Output=Self>
|
||||||
+ Add<Self, Output=Self>
|
+ Add<Self, Output=Self>
|
||||||
+ AddAssign<Self>
|
+ AddAssign<Self>
|
||||||
@ -89,11 +91,13 @@ pub trait Field: 'static
|
|||||||
fn primitive_root_of_unity(n_power: usize) -> Self {
|
fn primitive_root_of_unity(n_power: usize) -> Self {
|
||||||
assert!(n_power <= Self::TWO_ADICITY);
|
assert!(n_power <= Self::TWO_ADICITY);
|
||||||
let base = Self::POWER_OF_TWO_GENERATOR;
|
let base = Self::POWER_OF_TWO_GENERATOR;
|
||||||
|
// TODO: Just repeated squaring should be a bit faster, to avoid conditionals.
|
||||||
base.exp(Self::from_canonical_u64(1u64 << (Self::TWO_ADICITY - n_power)))
|
base.exp(Self::from_canonical_u64(1u64 << (Self::TWO_ADICITY - n_power)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes a multiplicative subgroup whose order is known in advance.
|
||||||
fn cyclic_subgroup_known_order(generator: Self, order: usize) -> Vec<Self> {
|
fn cyclic_subgroup_known_order(generator: Self, order: usize) -> Vec<Self> {
|
||||||
let mut subgroup = Vec::new();
|
let mut subgroup = Vec::with_capacity(order);
|
||||||
let mut current = Self::ONE;
|
let mut current = Self::ONE;
|
||||||
for _i in 0..order {
|
for _i in 0..order {
|
||||||
subgroup.push(current);
|
subgroup.push(current);
|
||||||
@ -102,6 +106,14 @@ pub trait Field: 'static
|
|||||||
subgroup
|
subgroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes a coset of a multiplicative subgroup whose order is known in advance.
|
||||||
|
fn cyclic_subgroup_coset_known_order(generator: Self, shift: Self, order: usize) -> Vec<Self> {
|
||||||
|
let subgroup = Self::cyclic_subgroup_known_order(generator, order);
|
||||||
|
subgroup.into_iter()
|
||||||
|
.map(|x| x * shift)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn to_canonical_u64(&self) -> u64;
|
fn to_canonical_u64(&self) -> u64;
|
||||||
|
|
||||||
fn from_canonical_u64(n: u64) -> Self;
|
fn from_canonical_u64(n: u64) -> Self;
|
||||||
@ -131,6 +143,10 @@ pub trait Field: 'static
|
|||||||
self.exp(Self::from_canonical_usize(power))
|
self.exp(Self::from_canonical_usize(power))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn powers(&self) -> Powers<Self> {
|
||||||
|
Powers { base: *self, current: Self::ONE }
|
||||||
|
}
|
||||||
|
|
||||||
fn rand_from_rng<R: Rng>(rng: &mut R) -> Self {
|
fn rand_from_rng<R: Rng>(rng: &mut R) -> Self {
|
||||||
Self::from_canonical_u64(rng.gen_range(0, Self::ORDER))
|
Self::from_canonical_u64(rng.gen_range(0, Self::ORDER))
|
||||||
}
|
}
|
||||||
@ -139,3 +155,19 @@ pub trait Field: 'static
|
|||||||
Self::rand_from_rng(&mut OsRng)
|
Self::rand_from_rng(&mut OsRng)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`.
|
||||||
|
pub struct Powers<F: Field> {
|
||||||
|
base: F,
|
||||||
|
current: F,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Field> Iterator for Powers<F> {
|
||||||
|
type Item = F;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<F> {
|
||||||
|
let result = self.current;
|
||||||
|
self.current *= self.base;
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ pub(crate) mod crandall_field;
|
|||||||
pub(crate) mod field;
|
pub(crate) mod field;
|
||||||
pub(crate) mod field_search;
|
pub(crate) mod field_search;
|
||||||
pub(crate) mod fft;
|
pub(crate) mod fft;
|
||||||
|
pub(crate) mod cosets;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod field_testing;
|
mod field_testing;
|
||||||
|
|||||||
@ -26,7 +26,6 @@ mod gates;
|
|||||||
mod generator;
|
mod generator;
|
||||||
mod gmimc;
|
mod gmimc;
|
||||||
mod hash;
|
mod hash;
|
||||||
mod partition;
|
|
||||||
mod plonk_challenger;
|
mod plonk_challenger;
|
||||||
mod plonk_common;
|
mod plonk_common;
|
||||||
mod polynomial;
|
mod polynomial;
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
use rand::SeedableRng;
|
|
||||||
use rand_chacha::ChaCha8Rng;
|
|
||||||
|
|
||||||
use crate::field::field::Field;
|
|
||||||
|
|
||||||
/// Returns `k_i`, the multiplier used in `S_ID_i` in the context of Plonk's permutation argument.
|
|
||||||
// TODO: This is copied from plonky1, but may need revisiting. Is the random approach still OK now
|
|
||||||
// that our field is smaller?
|
|
||||||
pub(crate) fn get_subgroup_shift<F: Field>(i: usize) -> F {
|
|
||||||
// The optimized variant of Plonk's permutation argument calls for NUM_ROUTED_WIRES shifts,
|
|
||||||
// k_1, ..., k_n, which result in distinct cosets. The paper suggests a method which is
|
|
||||||
// fairly straightforward when only three shifts are needed, but seems a bit complex and
|
|
||||||
// expensive if more are needed.
|
|
||||||
|
|
||||||
// We will "cheat" and just use random field elements. Since our subgroup has |F*|/degree
|
|
||||||
// possible cosets, the probability of a collision is negligible for large fields.
|
|
||||||
|
|
||||||
// Unlike what's shown in the Plonk paper, we do not set k_1=1 to "randomize" the
|
|
||||||
// sigmas polynomials evaluations and making them fit in both fields with high probability.
|
|
||||||
// TODO: Go back to k_1=1 if we change the way we deal with values not fitting in both fields.
|
|
||||||
let mut rng = ChaCha8Rng::seed_from_u64(i as u64);
|
|
||||||
F::rand_from_rng(&mut rng)
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user