binius_ip/
fracaddcheck.rs1use binius_field::{Field, field::FieldOps};
9use binius_math::line::extrapolate_line;
10use binius_transcript::Error as TranscriptError;
11
12use crate::{
13 channel::IPVerifierChannel,
14 sumcheck::{self, BatchSumcheckOutput},
15};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct FracAddEvalClaim<F> {
19 pub num_eval: F,
21 pub den_eval: F,
22 pub point: Vec<F>,
24}
25
26pub fn verify<F, C>(
27 k: usize,
28 claim: FracAddEvalClaim<C::Elem>,
29 channel: &mut C,
30) -> Result<FracAddEvalClaim<C::Elem>, Error>
31where
32 F: Field,
33 C: IPVerifierChannel<F>,
34{
35 if k == 0 {
36 return Ok(claim);
37 }
38
39 let FracAddEvalClaim {
40 num_eval,
41 den_eval,
42 point,
43 } = claim;
44
45 let evals = [num_eval, den_eval];
46
47 let BatchSumcheckOutput {
49 batch_coeff,
50 eval,
51 mut challenges,
52 } = sumcheck::batch_verify_mle(&point, 2, &evals, channel)?;
53
54 let [num_0, num_1, den_0, den_1] = channel.recv_array()?;
56
57 challenges.reverse();
59 let reduced_eval_point = challenges;
60
61 let numerator_eval = num_0.clone() * den_1.clone() + num_1.clone() * den_0.clone();
62 let denominator_eval = den_0.clone() * den_1.clone();
63 let batched_eval = numerator_eval + denominator_eval * batch_coeff;
64
65 channel.assert_zero(batched_eval - eval)?;
66
67 let r = channel.sample();
69 let next_num = extrapolate_line(num_0, num_1, r.clone());
70 let next_den = extrapolate_line(den_0, den_1, r.clone());
71
72 let mut next_point = reduced_eval_point;
73 next_point.push(r);
74
75 verify(
76 k - 1,
77 FracAddEvalClaim {
78 num_eval: next_num,
79 den_eval: next_den,
80 point: next_point,
81 },
82 channel,
83 )
84}
85
86pub fn pad_leaf_fraction<E: FieldOps>(fraction: (E, E), pad_eq: E) -> (E, E) {
103 let (num, den) = fraction;
104 (num * pad_eq.clone(), E::one() + (den - E::one()) * pad_eq)
105}
106
107#[derive(Debug, thiserror::Error)]
108pub enum Error {
109 #[error("sumcheck error: {0}")]
110 Sumcheck(#[source] sumcheck::Error),
111 #[error("transcript error: {0}")]
112 Transcript(#[source] TranscriptError),
113 #[error("verification error: {0}")]
114 Verification(#[from] VerificationError),
115}
116
117impl From<sumcheck::Error> for Error {
118 fn from(err: sumcheck::Error) -> Self {
119 match err {
120 sumcheck::Error::Verification(err) => VerificationError::Sumcheck(err).into(),
121 _ => Error::Sumcheck(err),
122 }
123 }
124}
125
126impl From<TranscriptError> for Error {
127 fn from(err: TranscriptError) -> Self {
128 match err {
129 TranscriptError::NotEnoughBytes => VerificationError::TranscriptIsEmpty.into(),
130 _ => Error::Transcript(err),
131 }
132 }
133}
134
135impl From<crate::channel::Error> for Error {
136 fn from(err: crate::channel::Error) -> Self {
137 match err {
138 crate::channel::Error::ProofEmpty => VerificationError::TranscriptIsEmpty.into(),
139 crate::channel::Error::InvalidAssert => VerificationError::InvalidAssert.into(),
140 }
141 }
142}
143
144#[derive(Debug, thiserror::Error)]
145pub enum VerificationError {
146 #[error("sumcheck: {0}")]
147 Sumcheck(#[from] sumcheck::VerificationError),
148 #[error("incorrect layer fraction sum evaluation: {round}")]
149 IncorrectLayerFractionSumEvaluation { round: usize },
150 #[error("incorrect round evaluation: {round}")]
151 IncorrectRoundEvaluation { round: usize },
152 #[error("transcript is empty")]
153 TranscriptIsEmpty,
154 #[error("invalid assertion: value is not zero")]
155 InvalidAssert,
156}