1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use alloc::vec::Vec;
use risc0_core::field::{Elem, ExtElem, RootsOfUnity};
use super::VerifyHal;
use crate::{
core::{config::ConfigHash, log2_ceil},
verify::{merkle::MerkleTreeVerifier, read_iop::ReadIOP, VerificationError},
FRI_FOLD, FRI_FOLD_PO2, FRI_MIN_DEGREE, INV_RATE, QUERIES,
};
struct VerifyRoundInfo<'a, H: VerifyHal> {
domain: usize,
merkle: MerkleTreeVerifier<'a, H>,
mix: H::ExtElem,
}
impl<'a, H: VerifyHal> VerifyRoundInfo<'a, H> {
pub fn new(iop: &mut ReadIOP<'a, H::Field, H::Rng>, in_domain: usize) -> Self {
let domain = in_domain / FRI_FOLD;
VerifyRoundInfo {
domain,
merkle: MerkleTreeVerifier::new(iop, domain, FRI_FOLD * H::ExtElem::EXT_SIZE, QUERIES),
mix: iop.random_ext_elem(),
}
}
pub fn verify_query(
&mut self,
hal: &H,
iop: &mut ReadIOP<'a, H::Field, H::Rng>,
pos: &mut usize,
goal: &mut H::ExtElem,
) -> Result<(), VerificationError> {
let quot = *pos / self.domain;
let group = *pos % self.domain;
let data = self.merkle.verify(iop, group)?;
let data_ext: Vec<_> = (0..FRI_FOLD)
.map(|i| {
let mut inps = Vec::with_capacity(H::ExtElem::EXT_SIZE);
for j in 0..H::ExtElem::EXT_SIZE {
inps.push(data[j * FRI_FOLD + i]);
}
H::ExtElem::from_subelems(inps)
})
.collect();
if data_ext[quot] != *goal {
return Err(VerificationError::InvalidProof);
}
let root_po2 = log2_ceil(FRI_FOLD * self.domain);
let inv_wk = H::Elem::ROU_REV[root_po2].pow(group);
*goal = hal.fold_eval(&mut data_ext.try_into().unwrap(), self.mix * inv_wk);
*pos = group;
Ok(())
}
}
pub fn fri_verify<'a, H: VerifyHal + 'a, F>(
hal: &'a H,
iop: &mut ReadIOP<'a, H::Field, H::Rng>,
mut degree: usize,
mut inner: F,
) -> Result<(), VerificationError>
where
F: FnMut(&mut ReadIOP<'a, H::Field, H::Rng>, usize) -> Result<H::ExtElem, VerificationError>,
{
let orig_domain = INV_RATE * degree;
let mut domain = orig_domain;
let rounds_capacity =
(log2_ceil((degree + FRI_FOLD - 1) / FRI_FOLD) + FRI_FOLD_PO2 - 1) / FRI_FOLD_PO2;
let mut rounds = Vec::with_capacity(rounds_capacity);
while degree > FRI_MIN_DEGREE {
rounds.push(VerifyRoundInfo::new(iop, domain));
domain /= FRI_FOLD;
degree /= FRI_FOLD;
}
assert!(
rounds.len() < rounds_capacity,
"Did not allocate enough rounds; needed {} for degree {} but only allocated {}",
rounds.len(),
degree,
rounds_capacity
);
let final_coeffs = iop.read_field_elem_slice(H::ExtElem::EXT_SIZE * degree);
let final_digest = H::Hash::hash_elem_slice(final_coeffs);
iop.commit(&final_digest);
let gen = <H::Elem as RootsOfUnity>::ROU_FWD[log2_ceil(domain)];
let mut poly_buf: Vec<H::ExtElem> = Vec::with_capacity(degree);
for _ in 0..QUERIES {
let mut pos = iop.random_bits(log2_ceil(orig_domain)) as usize;
let mut goal = inner(iop, pos)?;
for round in &mut rounds {
round.verify_query(hal, iop, &mut pos, &mut goal)?;
}
let x = gen.pow(pos);
poly_buf.clear();
poly_buf.extend((0..degree).map(|i| {
H::ExtElem::from_subelems(
(0..H::ExtElem::EXT_SIZE).map(|j| final_coeffs[j * degree + i]),
)
}));
let fx = hal.poly_eval(poly_buf.as_slice(), H::ExtElem::from_subfield(&x));
if fx != goal {
return Err(VerificationError::InvalidProof);
}
}
Ok(())
}