This commit is contained in:
Nicholas Ward 2021-07-20 15:42:27 -07:00
parent 8a51e6a323
commit b103c0774f
11 changed files with 64 additions and 77 deletions

View File

@ -23,7 +23,7 @@ unroll = "0.1.5"
anyhow = "1.0.40"
serde = { version = "1.0", features = ["derive"] }
serde_cbor = "0.11.1"
num_bigint = "0.2.3"
num-bigint = "0.2.3"
[profile.release]
opt-level = 3

View File

@ -1,27 +1,15 @@
use num_bigint::BigUInt;
use crate::field::field::{Field, FieldOrder};
use num_bigint::BigUint;
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> {
match F::ORDER {
FieldOrder::U64(order) => {
// From Lagrange's theorem.
let num_cosets = (order - 1) / (subgroup_size as u64);
let num_cosets = (F::ORDER - 1u32) / (subgroup_size as u32);
assert!(
num_shifts as u64 <= num_cosets,
BigUint::from(num_shifts) <= num_cosets,
"The subgroup does not have enough distinct cosets"
);
},
FieldOrder::Big(order) => {
// From Lagrange's theorem.
let num_cosets = (order - 1) / (subgroup_size as BigUInt);
assert!(
num_shifts as BigUInt <= 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

View File

@ -4,6 +4,7 @@ use std::hash::{Hash, Hasher};
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use num_bigint::BigUint;
use num::Integer;
use serde::{Deserialize, Serialize};
@ -12,6 +13,8 @@ use crate::field::extension_field::quartic::QuarticCrandallField;
use crate::field::extension_field::{Extendable, Frobenius};
use crate::field::field::Field;
const FIELD_ORDER: u64 = 18446744071293632513;
/// EPSILON = 9 * 2**28 - 1
const EPSILON: u64 = 2415919103;
@ -142,11 +145,11 @@ impl Field for CrandallField {
const ZERO: Self = Self(0);
const ONE: Self = Self(1);
const TWO: Self = Self(2);
const NEG_ONE: Self = Self(Self::ORDER - 1);
const NEG_ONE: Self = Self(FIELD_ORDER - 1);
const ORDER: u64 = 18446744071293632513;
const ORDER : BigUint = BigUint::from(FIELD_ORDER);
const TWO_ADICITY: usize = 28;
const CHARACTERISTIC: u64 = Self::ORDER;
const CHARACTERISTIC: u64 = FIELD_ORDER;
const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self(5);
const POWER_OF_TWO_GENERATOR: Self = Self(10281950781551402419);
@ -170,7 +173,7 @@ impl Field for CrandallField {
// Based on Algorithm 16 of "Efficient Software-Implementation of Finite Fields with
// Applications to Cryptography".
let p = Self::ORDER;
let p = FIELD_ORDER;
let mut u = self.to_canonical_u64();
let mut v = p;
let mut b = 1u64;
@ -228,8 +231,8 @@ impl Field for CrandallField {
fn to_canonical_u64(&self) -> u64 {
let mut c = self.0;
// We only need one condition subtraction, since 2 * ORDER would not fit in a u64.
if c >= Self::ORDER {
c -= Self::ORDER;
if c >= FIELD_ORDER {
c -= FIELD_ORDER;
}
c
}
@ -336,7 +339,7 @@ impl Neg for CrandallField {
if self.is_zero() {
Self::ZERO
} else {
Self(Self::ORDER - self.to_canonical_u64())
Self(FIELD_ORDER - self.to_canonical_u64())
}
}
}
@ -348,7 +351,7 @@ impl Add for CrandallField {
#[allow(clippy::suspicious_arithmetic_impl)]
fn add(self, rhs: Self) -> Self {
let (sum, over) = self.0.overflowing_add(rhs.0);
Self(sum.overflowing_sub((over as u64) * Self::ORDER).0)
Self(sum.overflowing_sub((over as u64) * FIELD_ORDER).0)
}
}
@ -371,7 +374,7 @@ impl Sub for CrandallField {
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, rhs: Self) -> Self {
let (diff, under) = self.0.overflowing_sub(rhs.to_canonical_u64());
Self(diff.overflowing_add((under as u64) * Self::ORDER).0)
Self(diff.overflowing_add((under as u64) * FIELD_ORDER).0)
}
}

View File

@ -3,6 +3,7 @@ use std::hash::Hash;
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use num_bigint::BigUint;
use rand::Rng;
use serde::{Deserialize, Serialize};
@ -51,9 +52,8 @@ impl Field for QuadraticCrandallField {
const TWO: Self = Self([CrandallField::TWO, CrandallField::ZERO]);
const NEG_ONE: Self = Self([CrandallField::NEG_ONE, CrandallField::ZERO]);
const CHARACTERISTIC: u64 = CrandallField::ORDER;
// Does not fit in 64-bits.
const ORDER: u64 = 0;
const CHARACTERISTIC: u64 = CrandallField::CHARACTERISTIC;
const ORDER: BigUint = CrandallField::ORDER * CrandallField::ORDER;
const TWO_ADICITY: usize = 29;
const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([
CrandallField(6483724566312148654),

View File

@ -3,6 +3,8 @@ use std::hash::Hash;
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use num::traits::Pow;
use num_bigint::BigUint;
use rand::Rng;
use serde::{Deserialize, Serialize};
@ -73,9 +75,9 @@ impl Field for QuarticCrandallField {
CrandallField::ZERO,
]);
const CHARACTERISTIC: u64 = CrandallField::ORDER;
const CHARACTERISTIC: u64 = CrandallField::CHARACTERISTIC;
// Does not fit in 64-bits.
const ORDER: u64 = 0;
const ORDER: BigUint = CrandallField::ORDER.pow(4);
const TWO_ADICITY: usize = 30;
const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([
CrandallField(12476589904174392631),

View File

@ -4,8 +4,8 @@ use std::hash::Hash;
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use num_bigint::BigUInt;
use num::Integer;
use num_bigint::BigUint;
use num::{Integer, Zero};
use rand::Rng;
use serde::de::DeserializeOwned;
use serde::Serialize;
@ -13,11 +13,6 @@ use serde::Serialize;
use crate::field::extension_field::Frobenius;
use crate::util::bits_u64;
pub enum FieldOrder {
U64(u64),
Big(BigUInt)
}
/// A finite field with prime order less than 2^64.
pub trait Field:
'static
@ -50,7 +45,7 @@ pub trait Field:
const NEG_ONE: Self;
const CHARACTERISTIC: u64;
const ORDER: FieldOrder;
const ORDER: BigUint;
const TWO_ADICITY: usize;
/// Generator of the entire multiplicative group, i.e. all non-zero elements.
@ -218,18 +213,33 @@ pub trait Field:
self.exp(power as u64)
}
fn exp_bigint(&self, power: BigUint) -> Self {
let digits = power.to_u32_digits();
let radix = 1u64 << 32;
let mut result = Self::ONE;
for (radix_power, &digit) in digits.iter().enumerate() {
let mut current = self.exp_u32(digit);
for _ in 0..radix_power {
current = current.exp(radix);
}
result *= current;
}
result
}
/// Returns whether `x^power` is a permutation of this field.
fn is_monomial_permutation(power: u64) -> bool {
match power {
0 => false,
1 => true,
_ => (Self::ORDER - 1).gcd(&power) == 1,
_ => (Self::ORDER - 1u32).gcd(&BigUint::from(power)) == BigUint::from(1u32),
}
}
fn kth_root(&self, k: u64) -> Self {
let p = Self::ORDER;
let p_minus_1 = p - 1;
let p_minus_1 = p - 1u32;
debug_assert!(
Self::is_monomial_permutation(k),
"Not a permutation of this field"
@ -242,10 +252,10 @@ pub trait Field:
// x^((p + n(p - 1))/k)^k = x,
// implying that x^((p + n(p - 1))/k) is a k'th root of x.
for n in 0..k {
let numerator = p as u128 + n as u128 * p_minus_1 as u128;
if numerator % k as u128 == 0 {
let power = (numerator / k as u128) as u64 % p_minus_1;
return self.exp(power);
let numerator = p + p_minus_1 * n;
if numerator % k == BigUint::zero() {
let power = (numerator / k) % p_minus_1;
return self.exp_bigint(power);
}
}
panic!(

View File

@ -1,3 +1,5 @@
use num_bigint::BigUint;
use crate::field::field::Field;
use crate::util::{bits_u64, ceil_div_usize};
@ -5,9 +7,9 @@ use crate::util::{bits_u64, ceil_div_usize};
/// `modulus` which cover a range of values and which will
/// generate lots of carries, especially at `word_bits` word
/// boundaries.
pub fn test_inputs(modulus: u64, word_bits: usize) -> Vec<u64> {
pub fn test_inputs(modulus: BigUint, word_bits: usize) -> Vec<u64> {
assert!(word_bits == 32 || word_bits == 64);
let modwords = ceil_div_usize(bits_u64(modulus), word_bits);
let modwords = ceil_div_usize(modulus.bits(), word_bits);
// Start with basic set close to zero: 0 .. 10
const BIGGEST_SMALL: u32 = 10;
let smalls: Vec<_> = (0..BIGGEST_SMALL).map(u64::from).collect();
@ -32,17 +34,17 @@ pub fn test_inputs(modulus: u64, word_bits: usize) -> Vec<u64> {
let diff_max = basic_inputs
.iter()
.map(|&x| u64::MAX - x)
.filter(|&x| x < modulus)
.filter(|&x| BigUint::from(x) < modulus)
.collect();
// Inputs 'difference from' modulus value
let diff_mod = basic_inputs
.iter()
.filter(|&&x| x < modulus && x != 0)
.filter(|&&x| BigUint::from(x) < modulus && x != 0)
.map(|&x| modulus - x)
.collect();
let basics = basic_inputs
.into_iter()
.filter(|&x| x < modulus)
.filter(|&x| BigUint::from(x) < modulus)
.collect::<Vec<u64>>();
[basics, diff_max, diff_mod].concat()
@ -59,7 +61,7 @@ pub fn test_inputs(modulus: u64, word_bits: usize) -> Vec<u64> {
/// coordinate-wise to the inputs from `test_inputs(modulus,
/// word_bits)` and panic if the two resulting vectors differ.
pub fn run_unaryop_test_cases<F, UnaryOp, ExpectedOp>(
modulus: u64,
modulus: BigUint,
word_bits: usize,
op: UnaryOp,
expected_op: ExpectedOp,
@ -90,7 +92,7 @@ pub fn run_unaryop_test_cases<F, UnaryOp, ExpectedOp>(
/// `inputs.len()`. Panic if the two functions ever give
/// different answers.
pub fn run_binaryop_test_cases<F, BinaryOp, ExpectedOp>(
modulus: u64,
modulus: BigUint,
word_bits: usize,
op: BinaryOp,
expected_op: ExpectedOp,

View File

@ -44,21 +44,3 @@ fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 {
1.0 / (2.0 * EPSILON * rate.sqrt())
}
}
fn fri_soundness(
num_functions: usize,
rate_bits: usize,
codeword_size_bits: usize,
m: usize,
arity_bits: usize,
num_rounds: usize,
field_size_bits: usize,
num_queries: usize,
) {
let rho = 1.0 / ((1 >> rate_bits) as f32);
let alpha = rho.sqrt() * (1.0 + 1.0 / (2.0 * m as f32));
let term_1 = (m as f32 + 0.5).powi(7) / (rho.powf(1.5) * (1 << (field_size_bits - 2 * codeword_size_bits + 1)) as f32);
let term_2 = (2.0 * m + 1.0) * ((1 << codeword_size_bits) as f32 + 1.0)
}

View File

@ -115,7 +115,7 @@ fn fri_proof_of_work<F: Field>(current_hash: Hash<F>, config: &FriConfig) -> F {
)
.to_canonical_u64()
.leading_zeros()
>= config.proof_of_work_bits + F::ORDER.leading_zeros()
>= config.proof_of_work_bits + (64 - F::ORDER.bits()) as u32
})
.map(F::from_canonical_u64)
.expect("Proof of work failed. This is highly unlikely!")

View File

@ -59,7 +59,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
inputs.push(proof.pow_witness);
let hash = self.hash_n_to_m(inputs, 1, false)[0];
self.assert_leading_zeros(hash, config.proof_of_work_bits + F::ORDER.leading_zeros());
self.assert_leading_zeros(hash, config.proof_of_work_bits + (64 - F::ORDER.bits()) as u32);
}
pub fn verify_fri_proof(

View File

@ -58,7 +58,7 @@ fn fri_verify_proof_of_work<F: Field + Extendable<D>, const D: usize>(
);
ensure!(
hash.to_canonical_u64().leading_zeros()
>= config.proof_of_work_bits + F::ORDER.leading_zeros(),
>= config.proof_of_work_bits + (64 - F::ORDER.bits()) as u32,
"Invalid proof of work witness."
);