Skip to content

Commit 64639b0

Browse files
Merge branch 'main' into feat/fri-higher-arity-2
2 parents 621019d + 88d7f05 commit 64639b0

79 files changed

Lines changed: 1663 additions & 1211 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@ Many variations are possible, with different fields, hashes and so forth, which
6363

6464
For example, to prove 2^20 Poseidon2 permutations of width 16, using the `KoalaBear` field, `Radix2DitParallel` DFT and `KeccakF` as the Merkle tree hash:
6565
```
66-
RUSTFLAGS="-Ctarget-cpu=native" cargo run --example prove_monty_31 --release --features parallel -- --field koala-bear --objective poseidon-2-permutations --log-trace-length 17 --discrete-fourier-transform radix-2-dit-parallel --merkle-hash keccak-f
66+
RUSTFLAGS="-Ctarget-cpu=native" cargo run --example prove_prime_field_31 --release --features parallel -- --field koala-bear --objective poseidon-2-permutations --log-trace-length 17 --discrete-fourier-transform radix-2-dit-parallel --merkle-hash keccak-f
6767
```
6868

6969
Currently the options for the command line arguments are:
70-
- `--field` (`-f`): `koala-bear` or `baby-bear`.
70+
- `--field` (`-f`): `mersenne-31` or `koala-bear` or `baby-bear`.
7171
- `--objective` (`-o`): `blake-3-permutations, poseidon-2-permutations, keccak-f-permutations`.
7272
- `--log-trace-length` (`-l`): Accepts any integer between `0` and `255`. The number of permutations proven is `trace_length, 8*trace_length` and `trace_length/24` for `blake3, poseidon2` and `keccakf` respectively.
73-
- `--discrete-fourier-transform` (`-d`): `radix-2-dit-parallel, recursive-dft`.
73+
- `--discrete-fourier-transform` (`-d`): `radix-2-dit-parallel, recursive-dft`. This option should be omitted if the field choice is `mersenne-31` as the circle stark currently only supports a single discrete fourier transform.
7474
- `--merkle-hash` (`-m`): `poseidon-2, keccak-f`.
7575

7676
Extra speedups may be possible with some configuration changes:

air/src/utils.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,23 @@ pub fn checked_andn<F: Field>(x: F, y: F) -> F {
7676
/// The output array is in little-endian order.
7777
#[inline]
7878
pub fn u32_to_bits_le<FA: FieldAlgebra>(val: u32) -> [FA; 32] {
79-
// We do this over F::from_canonical_u32 as from_canonical_u32 can be slow
80-
// like in the case of monty field.
81-
array::from_fn(|i| {
82-
if val & (1 << i) != 0 {
83-
FA::ONE
84-
} else {
85-
FA::ZERO
86-
}
87-
})
79+
array::from_fn(|i| FA::from_bool(val & (1 << i) != 0))
80+
}
81+
82+
/// Convert a 64-bit integer into an array of 64 0 or 1 field elements.
83+
///
84+
/// The output array is in little-endian order.
85+
#[inline]
86+
pub fn u64_to_bits_le<FA: FieldAlgebra>(val: u64) -> [FA; 64] {
87+
array::from_fn(|i| FA::from_bool(val & (1 << i) != 0))
88+
}
89+
90+
/// Convert a 64-bit integer into an array of four field elements representing the 16 bit limb decomposition.
91+
///
92+
/// The output array is in little-endian order.
93+
#[inline]
94+
pub fn u64_to_16_bit_limbs<FA: FieldAlgebra>(val: u64) -> [FA; 4] {
95+
array::from_fn(|i| FA::from_canonical_u16((val >> (16 * i)) as u16))
8896
}
8997

9098
/// Verify that `a = b + c + d mod 2^32`

blake3-air/examples/prove_blake3_m31_keccak.rs

Lines changed: 0 additions & 73 deletions
This file was deleted.

blake3-air/examples/prove_blake3_m31_poseidon2.rs

Lines changed: 0 additions & 78 deletions
This file was deleted.

blake3-air/src/air.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ use crate::{generate_trace_rows, Blake3State, FullRound, QuarterRound};
1818
pub struct Blake3Air {}
1919

2020
impl Blake3Air {
21-
pub fn generate_trace_rows<F: PrimeField64>(&self, num_hashes: usize) -> RowMajorMatrix<F> {
21+
pub fn generate_trace_rows<F: PrimeField64>(
22+
&self,
23+
num_hashes: usize,
24+
extra_capacity_bits: usize,
25+
) -> RowMajorMatrix<F> {
2226
let inputs = (0..num_hashes).map(|_| random()).collect::<Vec<_>>();
23-
generate_trace_rows(inputs)
27+
generate_trace_rows(inputs, extra_capacity_bits)
2428
}
2529

2630
/// Verify that the quarter round function has been correctly computed.

blake3-air/src/generation.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,23 @@ use crate::{Blake3State, FullRound};
1313

1414
// TODO: Take generic iterable
1515
#[instrument(name = "generate Blake3 trace", skip_all)]
16-
pub fn generate_trace_rows<F: PrimeField64>(inputs: Vec<[u32; 24]>) -> RowMajorMatrix<F> {
16+
pub fn generate_trace_rows<F: PrimeField64>(
17+
inputs: Vec<[u32; 24]>,
18+
extra_capacity_bits: usize,
19+
) -> RowMajorMatrix<F> {
1720
let num_rows = inputs.len();
1821
assert!(
1922
num_rows.is_power_of_two(),
2023
"Callers expected to pad inputs to VECTOR_LEN times a power of two"
2124
);
2225

23-
let mut trace = RowMajorMatrix::new(F::zero_vec(num_rows * NUM_BLAKE3_COLS), NUM_BLAKE3_COLS);
26+
let trace_length = num_rows * NUM_BLAKE3_COLS;
27+
28+
// We allocate extra_capacity_bits now as this will be needed by the dft.
29+
let mut long_trace = F::zero_vec(trace_length << extra_capacity_bits);
30+
long_trace.truncate(trace_length);
31+
32+
let mut trace = RowMajorMatrix::new(long_trace, NUM_BLAKE3_COLS);
2433
let (prefix, rows, suffix) = unsafe { trace.values.align_to_mut::<Blake3Cols<F>>() };
2534
assert!(prefix.is_empty(), "Alignment should match");
2635
assert!(suffix.is_empty(), "Alignment should match");
@@ -205,14 +214,14 @@ fn verifiable_half_round(
205214
let (rot_1, rot_2) = if flag { (8, 7) } else { (16, 12) };
206215

207216
// The first summation:
208-
a += b;
209-
a += m;
217+
a = a.wrapping_add(b);
218+
a = a.wrapping_add(m);
210219

211220
// The first xor:
212221
d = (d ^ a).rotate_right(rot_1);
213222

214223
// The second summation:
215-
c += d;
224+
c = c.wrapping_add(d);
216225

217226
// The second xor:
218227
b = (b ^ c).rotate_right(rot_2);

bn254-fr/src/lib.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,6 @@ impl FieldAlgebra for Bn254Fr {
109109
f
110110
}
111111

112-
fn from_bool(b: bool) -> Self {
113-
Self::new(FFBn254Fr::from(b as u64))
114-
}
115-
116112
fn from_canonical_u8(n: u8) -> Self {
117113
Self::new(FFBn254Fr::from(n as u64))
118114
}

challenger/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ tracing.workspace = true
1313

1414
[dev-dependencies]
1515
p3-goldilocks.workspace = true
16+
p3-baby-bear.workspace = true

challenger/src/duplex_challenger.rs

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ where
148148
P: CryptographicPermutation<[F; WIDTH]>,
149149
{
150150
fn sample_bits(&mut self, bits: usize) -> usize {
151-
debug_assert!(bits < (usize::BITS as usize));
152-
debug_assert!((1 << bits) < F::ORDER_U64);
151+
assert!(bits < (usize::BITS as usize));
152+
assert!((1 << bits) < F::ORDER_U64);
153153
let rand_f: F = self.sample();
154154
let rand_usize = rand_f.as_canonical_u64() as usize;
155155
rand_usize & ((1 << bits) - 1)
@@ -160,45 +160,88 @@ where
160160
mod tests {
161161
use core::iter;
162162

163+
use p3_baby_bear::BabyBear;
163164
use p3_field::FieldAlgebra;
164165
use p3_goldilocks::Goldilocks;
165166
use p3_symmetric::Permutation;
166167

167168
use super::*;
169+
use crate::grinding_challenger::GrindingChallenger;
168170

169171
const WIDTH: usize = 24;
170172
const RATE: usize = 16;
171173

172-
type TestArray = [F; WIDTH];
173-
type F = Goldilocks;
174+
type G = Goldilocks;
175+
type BB = BabyBear;
174176

175177
#[derive(Clone)]
176178
struct TestPermutation {}
177179

178-
impl Permutation<TestArray> for TestPermutation {
179-
fn permute_mut(&self, input: &mut TestArray) {
180+
impl<F: Clone> Permutation<[F; WIDTH]> for TestPermutation {
181+
fn permute_mut(&self, input: &mut [F; WIDTH]) {
180182
input.reverse()
181183
}
182184
}
183185

184-
impl CryptographicPermutation<TestArray> for TestPermutation {}
186+
impl<F: Clone> CryptographicPermutation<[F; WIDTH]> for TestPermutation {}
185187

186188
#[test]
187189
fn test_duplex_challenger() {
188-
type Chal = DuplexChallenger<F, TestPermutation, WIDTH, RATE>;
190+
type Chal = DuplexChallenger<G, TestPermutation, WIDTH, RATE>;
189191
let permutation = TestPermutation {};
190192
let mut duplex_challenger = DuplexChallenger::new(permutation);
191193

192194
// Observe 12 elements.
193-
(0..12).for_each(|element| duplex_challenger.observe(F::from_canonical_u8(element as u8)));
195+
(0..12).for_each(|element| duplex_challenger.observe(G::from_canonical_u8(element as u8)));
194196

195-
let state_after_duplexing: Vec<_> = iter::repeat(F::ZERO)
197+
let state_after_duplexing: Vec<_> = iter::repeat(G::ZERO)
196198
.take(12)
197-
.chain((0..12).map(F::from_canonical_u8).rev())
199+
.chain((0..12).map(G::from_canonical_u8).rev())
198200
.collect();
199201

200-
let expected_samples: Vec<F> = state_after_duplexing[..16].iter().copied().rev().collect();
201-
let samples = <Chal as CanSample<F>>::sample_vec(&mut duplex_challenger, 16);
202+
let expected_samples: Vec<G> = state_after_duplexing[..16].iter().copied().rev().collect();
203+
let samples = <Chal as CanSample<G>>::sample_vec(&mut duplex_challenger, 16);
202204
assert_eq!(samples, expected_samples);
203205
}
206+
207+
#[test]
208+
#[should_panic]
209+
fn test_duplex_challenger_sample_bits_security() {
210+
type GoldilocksChal = DuplexChallenger<G, TestPermutation, WIDTH, RATE>;
211+
let permutation = TestPermutation {};
212+
let mut duplex_challenger = GoldilocksChal::new(permutation);
213+
214+
for _ in 0..100 {
215+
assert!(duplex_challenger.sample_bits(129) < 4);
216+
}
217+
}
218+
219+
#[test]
220+
#[should_panic]
221+
fn test_duplex_challenger_sample_bits_security_small_field() {
222+
type BabyBearChal = DuplexChallenger<BB, TestPermutation, WIDTH, RATE>;
223+
let permutation = TestPermutation {};
224+
let mut duplex_challenger = BabyBearChal::new(permutation);
225+
226+
for _ in 0..100 {
227+
assert!(duplex_challenger.sample_bits(40) < 1 << 31);
228+
}
229+
}
230+
231+
#[test]
232+
#[should_panic]
233+
fn test_duplex_challenger_grind_security() {
234+
type GoldilocksChal = DuplexChallenger<G, TestPermutation, WIDTH, RATE>;
235+
let permutation = TestPermutation {};
236+
let mut duplex_challenger = GoldilocksChal::new(permutation);
237+
238+
// This should cause sample_bits (and hence grind and check_witness) to
239+
// panic. If bit sizes were not constrained correctly inside the
240+
// challenger, (1 << too_many_bits) would loop around, incorrectly
241+
// grinding and accepting a 1-bit PoW.
242+
let too_many_bits = usize::BITS as usize;
243+
244+
let witness = duplex_challenger.grind(too_many_bits);
245+
assert!(duplex_challenger.check_witness(too_many_bits, witness));
246+
}
204247
}

0 commit comments

Comments
 (0)