remainder/
verifiable_circuit.rs1use std::collections::HashMap;
4use std::fmt::Debug;
5
6use itertools::Itertools;
7use ligero::ligero_commit::ligero_verify;
8use shared_types::circuit_hash::CircuitHashType;
9use shared_types::config::global_config::get_current_global_verifier_config;
10use shared_types::config::ProofConfig;
11use shared_types::transcript::VerifierTranscript;
12use shared_types::Halo2FFTFriendlyField;
13
14use crate::input_layer::ligero_input_layer::{
15 LigeroInputLayerDescriptionWithOptionalVerifierPrecommit, LigeroRoot,
16};
17use crate::prover::helpers::get_circuit_description_hash_as_field_elems;
18use crate::prover::{GKRCircuitDescription, GKRError};
19use crate::{layer::LayerId, mle::evals::MultilinearExtension};
20
21use anyhow::{anyhow, Result};
22
23#[derive(Clone, Debug)]
26pub struct VerifiableCircuit<F: Halo2FFTFriendlyField> {
27 circuit_description: GKRCircuitDescription<F>,
28 predetermined_public_inputs: HashMap<LayerId, MultilinearExtension<F>>,
34 committed_inputs: HashMap<LayerId, LigeroInputLayerDescriptionWithOptionalVerifierPrecommit<F>>,
35 layer_label_to_layer_id: HashMap<String, LayerId>,
36}
37
38impl<F: Halo2FFTFriendlyField> VerifiableCircuit<F> {
39 pub fn new(
41 circuit_description: GKRCircuitDescription<F>,
42 predetermined_public_inputs: HashMap<LayerId, MultilinearExtension<F>>,
43 committed_inputs: HashMap<
44 LayerId,
45 LigeroInputLayerDescriptionWithOptionalVerifierPrecommit<F>,
46 >,
47 layer_label_to_layer_id: HashMap<String, LayerId>,
48 ) -> Self {
49 Self {
50 circuit_description,
51 predetermined_public_inputs,
52 committed_inputs,
53 layer_label_to_layer_id,
54 }
55 }
56
57 pub fn get_committed_inputs_ref(
63 &self,
64 ) -> &HashMap<LayerId, LigeroInputLayerDescriptionWithOptionalVerifierPrecommit<F>> {
65 &self.committed_inputs
66 }
67
68 pub fn get_gkr_circuit_description_ref(&self) -> &GKRCircuitDescription<F> {
72 &self.circuit_description
73 }
74
75 pub fn get_public_input_layer_ids(&self) -> Vec<LayerId> {
77 self.circuit_description
78 .input_layers
79 .iter()
80 .filter(|input_layer_description| {
81 !self
83 .committed_inputs
84 .contains_key(&input_layer_description.layer_id)
85 })
86 .map(|public_input_layer_description| public_input_layer_description.layer_id)
87 .collect()
88 }
89
90 pub fn get_committed_input_layer_ids(&self) -> Vec<LayerId> {
93 self.committed_inputs.keys().cloned().collect_vec()
94 }
95
96 pub fn get_predetermined_public_input_mle_ref(
99 &self,
100 layer_id: &LayerId,
101 ) -> Option<&MultilinearExtension<F>> {
102 self.predetermined_public_inputs.get(layer_id)
103 }
104
105 pub fn set_pre_commitment(
112 &mut self,
113 layer_label: &str,
114 commitment: LigeroRoot<F>,
115 ) -> Result<()> {
116 let layer_id = self.layer_label_to_layer_id.get(layer_label).unwrap();
117
118 let (_, optional_commitment) = self
119 .committed_inputs
120 .get_mut(layer_id)
121 .expect("Layer {layer_id} either does not exist, or is not a committed input layer.");
122
123 *optional_commitment = Some(commitment);
124
125 Ok(())
126 }
127
128 pub fn verify(
130 &self,
131 circuit_description_hash_type: CircuitHashType,
132 transcript: &mut impl VerifierTranscript<F>,
133 proof_config: &ProofConfig,
134 ) -> Result<()> {
135 if !get_current_global_verifier_config().matches_proof_config(proof_config) {
137 panic!("Error: Attempted to verify a GKR proof whose config doesn't match that of the verifier.");
138 }
139
140 assert_eq!(
144 self.get_public_input_layer_ids().len() + self.get_committed_inputs_ref().len(),
145 self.get_gkr_circuit_description_ref().input_layers.len()
146 );
147
148 let hash_value_as_field_elems = get_circuit_description_hash_as_field_elems(
150 self.get_gkr_circuit_description_ref(),
151 circuit_description_hash_type,
152 );
153 let prover_supplied_circuit_description_hash = transcript
154 .consume_elements("Circuit description hash", hash_value_as_field_elems.len())
155 .unwrap();
156 assert_eq!(
157 prover_supplied_circuit_description_hash,
158 hash_value_as_field_elems
159 );
160
161 let mut public_inputs = self.predetermined_public_inputs.clone();
162
163 self.get_public_input_layer_ids()
165 .into_iter()
166 .sorted_by_key(|layer_id| layer_id.get_raw_input_layer_id())
167 .map(|layer_id| {
168 let layer_desc = self
169 .get_gkr_circuit_description_ref()
170 .input_layers
171 .iter()
172 .find(|desc| desc.layer_id == layer_id)
173 .unwrap();
174 let (transcript_evaluations, _expected_input_hash_chain_digest) = transcript
175 .consume_input_elements("Public input layer", 1 << layer_desc.num_vars)
176 .unwrap();
177 match self.get_predetermined_public_input_mle_ref(&layer_id) {
178 Some(predetermined_public_input) => {
179 if predetermined_public_input.f.iter().collect_vec()
183 != transcript_evaluations
184 {
185 Err(anyhow!(GKRError::PublicInputLayerValuesMismatch(layer_id)))
186 } else {
187 Ok(())
188 }
189 }
190 None => {
191 public_inputs
194 .insert(layer_id, MultilinearExtension::new(transcript_evaluations));
195 Ok(())
196 }
197 }
198 })
199 .collect::<Result<Vec<_>>>()?;
200
201 assert_eq!(public_inputs.len(), self.get_public_input_layer_ids().len());
203
204 let mut ligero_commitments = HashMap::<LayerId, F>::new();
206 self.get_committed_input_layer_ids()
207 .into_iter()
208 .sorted_by_key(|layer_id| layer_id.get_raw_input_layer_id())
209 .for_each(|layer_id| {
210 let (commitment_as_vec, _expected_input_hash_chain_digest) = transcript
211 .consume_input_elements("Ligero commit", 1)
212 .unwrap();
213 assert_eq!(commitment_as_vec.len(), 1);
214 ligero_commitments.insert(layer_id, commitment_as_vec[0]);
215 });
216
217 let input_layer_claims = self
218 .get_gkr_circuit_description_ref()
219 .verify(transcript)
220 .unwrap();
221
222 let mut public_input_layer_claims = vec![];
224 let mut ligero_input_layer_claims = vec![];
225 input_layer_claims.into_iter().for_each(|claim| {
226 let layer_id = claim.get_to_layer_id();
227 if self.get_public_input_layer_ids().contains(&layer_id) {
228 public_input_layer_claims.push(claim);
229 } else if ligero_commitments.contains_key(&layer_id) {
230 ligero_input_layer_claims.push(claim);
231 } else {
232 panic!("Input layer {layer_id:?} has a claim but is not a public input layer nor a Ligero input layer.");
235 }
236 });
237
238 for claim in public_input_layer_claims.iter() {
240 let input_mle = public_inputs.get(&claim.get_to_layer_id()).unwrap();
241 let evaluation = input_mle.evaluate_at_point(claim.get_point());
242 if evaluation != claim.get_eval() {
243 return Err(anyhow!(GKRError::EvaluationMismatch(
244 claim.get_to_layer_id(),
245 claim.get_from_layer_id(),
246 )));
247 }
248 }
249
250 for claim in ligero_input_layer_claims.iter() {
252 let claim_layer_id = claim.get_to_layer_id();
253 let commitment = ligero_commitments.get(&claim_layer_id).unwrap();
254
255 let (_, (desc, opt_pre_commitment)) = self
256 .get_committed_inputs_ref()
257 .iter()
258 .find(|(layer_id, _)| **layer_id == claim_layer_id)
259 .unwrap();
260
261 if let Some(pre_commitment) = opt_pre_commitment {
262 assert_eq!(pre_commitment.root, *commitment);
263 }
264
265 ligero_verify::<F>(
266 commitment,
267 &desc.aux,
268 transcript,
269 claim.get_point(),
270 claim.get_eval(),
271 );
272 }
273
274 Ok(())
275 }
276}