Skip to content

Commit

Permalink
Change Merkle verify asserts to Result Errs (#251)
Browse files Browse the repository at this point in the history
Replace assertion-based error handling in our Merkle verification code with Result-based error handling.

The intent is for VerificationError::InvalidProof to be used when we attempt to verify an alleged proof that is not actually a true proof, and to use other error types for issues not directly related to the validity of the proof.
  • Loading branch information
tzerrell committed Aug 23, 2022
1 parent 69fbcd7 commit 0d360c7
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 44 deletions.
33 changes: 24 additions & 9 deletions risc0/zkp/rust/src/verify/fri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
sha::Sha,
},
field::Elem,
verify::{merkle::MerkleTreeVerifier, read_iop::ReadIOP},
verify::{merkle::MerkleTreeVerifier, read_iop::ReadIOP, VerificationError},
FRI_FOLD, FRI_MIN_DEGREE, INV_RATE, QUERIES,
};

Expand Down Expand Up @@ -65,11 +65,16 @@ impl VerifyRoundInfo {
}
}

pub fn verify_query<S: Sha>(&mut self, iop: &mut ReadIOP<S>, pos: &mut usize, goal: &mut Fp4) {
pub fn verify_query<S: Sha>(
&mut self,
iop: &mut ReadIOP<S>,
pos: &mut usize,
goal: &mut Fp4,
) -> Result<(), VerificationError> {
let quot = *pos / self.domain;
let group = *pos % self.domain;
// Get the column data
let data = self.merkle.verify(iop, group);
let data = self.merkle.verify(iop, group)?;
let mut data4 = vec![];
for i in 0..FRI_FOLD {
data4.push(Fp4::new(
Expand All @@ -80,16 +85,23 @@ impl VerifyRoundInfo {
));
}
// Check the existing goal
assert_eq!(data4[quot], *goal);
if data4[quot] != *goal {
return Err(VerificationError::InvalidProof);
}
// Compute the new goal + pos
*goal = fold_eval(&mut data4, self.mix, self.domain, group);
*pos = group;
Ok(())
}
}

pub fn fri_verify<S: Sha, F>(iop: &mut ReadIOP<S>, mut degree: usize, mut inner: F)
pub fn fri_verify<S: Sha, F>(
iop: &mut ReadIOP<S>,
mut degree: usize,
mut inner: F,
) -> Result<(), VerificationError>
where
F: FnMut(&mut ReadIOP<S>, usize) -> Fp4,
F: FnMut(&mut ReadIOP<S>, usize) -> Result<Fp4, VerificationError>,
{
let orig_domain = INV_RATE * degree;
let mut domain = orig_domain;
Expand All @@ -112,10 +124,10 @@ where
let rng = iop.next_u32();
let mut pos = rng as usize % orig_domain;
// Do the 'inner' verification for this index
let mut goal = inner(iop, pos);
let mut goal = inner(iop, pos)?;
// Verify the per-round proofs
for round in &mut rounds {
round.verify_query(iop, &mut pos, &mut goal);
round.verify_query(iop, &mut pos, &mut goal)?;
}
// Do final verification
let x = gen.pow(pos);
Expand All @@ -131,6 +143,9 @@ where
fx += cur * coeff;
cur *= x;
}
assert_eq!(fx, goal)
if fx != goal {
return Err(VerificationError::InvalidProof);
}
}
Ok(())
}
21 changes: 17 additions & 4 deletions risc0/zkp/rust/src/verify/merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use crate::{
field::Elem,
merkle::MerkleTreeParams,
verify::read_iop::ReadIOP,
verify::VerificationError,
};

/// A struct against which we verify merkle branches, consisting of the
Expand Down Expand Up @@ -63,8 +64,17 @@ impl MerkleTreeVerifier {
}

/// Verifies a branch provided by an IOP.
pub fn verify<S: Sha>(&self, iop: &mut ReadIOP<S>, mut idx: usize) -> Vec<Fp> {
assert!(idx < self.params.row_size);
pub fn verify<S: Sha>(
&self,
iop: &mut ReadIOP<S>,
mut idx: usize,
) -> Result<Vec<Fp>, VerificationError> {
if idx >= self.params.row_size {
return Err(VerificationError::MerkleQueryOutOfRange {
idx: idx,
rows: self.params.row_size,
});
}
// Initialize a vector to hold field elements.
let mut out = vec![Fp::ZERO; self.params.col_size];
// Read out field elements from IOP.
Expand All @@ -90,7 +100,10 @@ impl MerkleTreeVerifier {
}
// Once we reduce to an index for which we have the hash, check that it's
// correct.
assert_eq!(self.top[idx], cur);
out
if self.top[idx] == cur {
Ok(out)
} else {
Err(VerificationError::InvalidProof)
}
}
}
76 changes: 45 additions & 31 deletions risc0/zkp/rust/src/verify/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,21 @@ const CHECK_SIZE: usize = INV_RATE * EXT_SIZE;
pub enum VerificationError {
ReceiptFormatError,
MethodVerificationError,
MerkleQueryOutOfRange { idx: usize, rows: usize },
InvalidProof,
}

impl fmt::Display for VerificationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
VerificationError::ReceiptFormatError => write!(f, "invalid receipt format"),
VerificationError::MethodVerificationError => write!(f, "method verification failed"),
VerificationError::MerkleQueryOutOfRange { idx, rows } => write!(
f,
"Requested Merkle validation on row {}, but only {} rows exist",
idx, rows
),
VerificationError::InvalidProof => write!(f, "Verification indicates proof is invalid"),
}
}
}
Expand Down Expand Up @@ -153,7 +161,9 @@ where
}
check *= (Fp4::from_u32(3) * z).pow(size) - Fp4::ONE;
// debug!("Check = {check:?}");
assert_eq!(check, result);
if check != result {
return Err(VerificationError::InvalidProof);
}

// Set the mix mix value
let mix = Fp4::random(&mut iop);
Expand Down Expand Up @@ -185,37 +195,41 @@ where

let gen = Fp::new(ROU_FWD[log2_ceil(domain)]);
// debug!("FRI-verify, size = {size}");
fri_verify(&mut iop, size, |iop: &mut ReadIOP<S>, idx: usize| -> Fp4 {
let x = Fp4::from_fp(gen.pow(idx));
let mut rows = vec![];
rows.push(accum_merkle.verify(iop, idx));
rows.push(code_merkle.verify(iop, idx));
rows.push(data_merkle.verify(iop, idx));
let check_row = check_merkle.verify(iop, idx);
let mut cur = Fp4::ONE;
let mut tot = vec![Fp4::ZERO; combo_count + 1];
for reg in taps.regs() {
tot[reg.combo_id()] += cur * rows[reg.group() as usize][reg.offset()];
cur *= mix;
}
for i in 0..CHECK_SIZE {
tot[combo_count] += cur * check_row[i];
cur *= mix;
}
let mut ret = Fp4::ZERO;
for i in 0..combo_count {
let num = tot[i] - poly_eval(&combo_u[i], x);
let mut divisor = Fp4::ONE;
for back in taps.get_combo(i).slice() {
divisor *= x - z * back_one.pow(*back as usize);
fri_verify(
&mut iop,
size,
|iop: &mut ReadIOP<S>, idx: usize| -> Result<Fp4, VerificationError> {
let x = Fp4::from_fp(gen.pow(idx));
let mut rows = vec![];
rows.push(accum_merkle.verify(iop, idx)?);
rows.push(code_merkle.verify(iop, idx)?);
rows.push(data_merkle.verify(iop, idx)?);
let check_row = check_merkle.verify(iop, idx)?;
let mut cur = Fp4::ONE;
let mut tot = vec![Fp4::ZERO; combo_count + 1];
for reg in taps.regs() {
tot[reg.combo_id()] += cur * rows[reg.group() as usize][reg.offset()];
cur *= mix;
}
ret += num * divisor.inv();
}
let check_num = tot[combo_count] - combo_u[combo_count][0];
let check_div = x - z.pow(INV_RATE);
ret += check_num * check_div.inv();
ret
});
for i in 0..CHECK_SIZE {
tot[combo_count] += cur * check_row[i];
cur *= mix;
}
let mut ret = Fp4::ZERO;
for i in 0..combo_count {
let num = tot[i] - poly_eval(&combo_u[i], x);
let mut divisor = Fp4::ONE;
for back in taps.get_combo(i).slice() {
divisor *= x - z * back_one.pow(*back as usize);
}
ret += num * divisor.inv();
}
let check_num = tot[combo_count] - combo_u[combo_count][0];
let check_div = x - z.pow(INV_RATE);
ret += check_num * check_div.inv();
Ok(ret)
},
)?;
iop.verify_complete();
Ok(())
}

0 comments on commit 0d360c7

Please sign in to comment.