remainder/
provable_circuit.rs1use std::collections::HashMap;
4use std::collections::HashSet;
5use std::fmt::Debug;
6
7use itertools::Itertools;
8use ligero::ligero_commit::{ligero_commit, ligero_eval_prove};
9use shared_types::circuit_hash::CircuitHashType;
10use shared_types::config::global_config::get_current_global_prover_config;
11use shared_types::config::ProofConfig;
12use shared_types::transcript::{ProverTranscript, TranscriptSponge, TranscriptWriter};
13use shared_types::Halo2FFTFriendlyField;
14
15use crate::input_layer::ligero_input_layer::{
16 LigeroCommitment, LigeroInputLayerDescriptionWithOptionalProverPrecommit,
17 LigeroInputLayerDescriptionWithOptionalVerifierPrecommit,
18};
19use crate::prover::helpers::get_circuit_description_hash_as_field_elems;
20use crate::prover::{prove_circuit, GKRCircuitDescription, GKRError};
21use crate::utils::debug::sanitycheck_input_layers_and_claims;
22use crate::verifiable_circuit::VerifiableCircuit;
23use crate::{layer::LayerId, mle::evals::MultilinearExtension};
24
25use anyhow::{anyhow, Result};
26
27#[derive(Clone, Debug)]
31pub struct ProvableCircuit<F: Halo2FFTFriendlyField> {
32 circuit_description: GKRCircuitDescription<F>,
33 inputs: HashMap<LayerId, MultilinearExtension<F>>,
34 committed_inputs: HashMap<LayerId, LigeroInputLayerDescriptionWithOptionalProverPrecommit<F>>,
35 layer_label_to_layer_id: HashMap<String, LayerId>,
36}
37
38impl<F: Halo2FFTFriendlyField> ProvableCircuit<F> {
39 pub fn new(
41 circuit_description: GKRCircuitDescription<F>,
42 inputs: HashMap<LayerId, MultilinearExtension<F>>,
43 committed_inputs: HashMap<
44 LayerId,
45 LigeroInputLayerDescriptionWithOptionalProverPrecommit<F>,
46 >,
47 layer_label_to_layer_id: HashMap<String, LayerId>,
48 ) -> Self {
49 Self {
50 circuit_description,
51 inputs,
52 committed_inputs,
53 layer_label_to_layer_id,
54 }
55 }
56
57 pub fn _gen_verifiable_circuit(&self) -> VerifiableCircuit<F> {
64 let public_ids: HashSet<LayerId> = self.get_public_input_layer_ids().into_iter().collect();
65 let predetermined_public_inputs: HashMap<LayerId, MultilinearExtension<F>> = self
66 .inputs
67 .clone()
68 .into_iter()
69 .filter(|(layer_id, _)| public_ids.contains(layer_id))
70 .collect();
71
72 let committed_inputs: HashMap<
73 LayerId,
74 LigeroInputLayerDescriptionWithOptionalVerifierPrecommit<F>,
75 > = self
76 .committed_inputs
77 .clone()
78 .into_iter()
79 .map(|(layer_id, (desc, opt_commit))| {
80 (layer_id, (desc, opt_commit.map(|commit| commit.get_root())))
81 })
82 .collect();
83
84 VerifiableCircuit::new(
85 self.circuit_description.clone(),
86 predetermined_public_inputs,
87 committed_inputs,
88 self.layer_label_to_layer_id.clone(),
89 )
90 }
91
92 pub fn get_gkr_circuit_description_ref(&self) -> &GKRCircuitDescription<F> {
96 &self.circuit_description
97 }
98
99 pub fn get_public_input_layer_ids(&self) -> Vec<LayerId> {
103 self.inputs
104 .keys()
105 .filter(|layer_id| !self.committed_inputs.contains_key(layer_id))
106 .cloned()
107 .collect()
108 }
109
110 pub fn get_committed_input_layer_ids(&self) -> Vec<LayerId> {
115 self.committed_inputs.keys().cloned().collect()
116 }
117
118 pub fn get_input_mle(&self, layer_id: LayerId) -> Result<MultilinearExtension<F>> {
121 self.inputs
122 .get(&layer_id)
123 .ok_or(anyhow!("Unrecognized Layer ID '{layer_id}'"))
124 .cloned()
125 }
126
127 pub fn get_committed_input_layer(
130 &self,
131 layer_id: LayerId,
132 ) -> Result<LigeroInputLayerDescriptionWithOptionalProverPrecommit<F>> {
133 self.committed_inputs
134 .get(&layer_id)
135 .ok_or(anyhow!("Unrecognized Layer ID '{layer_id}'"))
136 .cloned()
137 }
138
139 pub fn get_inputs_ref(&self) -> &HashMap<LayerId, MultilinearExtension<F>> {
145 &self.inputs
146 }
147
148 pub fn prove<Tr: TranscriptSponge<F>>(
154 &self,
155 circuit_description_hash_type: CircuitHashType,
156 transcript_writer: &mut TranscriptWriter<F, Tr>,
157 ) -> Result<ProofConfig> {
158 let proof_config = ProofConfig::new_from_prover_config(&get_current_global_prover_config());
160
161 let hash_value_as_field_elems = get_circuit_description_hash_as_field_elems(
163 self.get_gkr_circuit_description_ref(),
164 circuit_description_hash_type,
165 );
166 transcript_writer.append_elements("Circuit description hash", &hash_value_as_field_elems);
167
168 self.get_public_input_layer_ids()
172 .into_iter()
173 .sorted_by_key(|layer_id| layer_id.get_raw_input_layer_id())
174 .for_each(|layer_id| {
175 let mle = self.get_input_mle(layer_id).unwrap();
176 transcript_writer
177 .append_input_elements("Public input layer", &mle.iter().collect_vec());
178 });
179
180 let mut ligero_input_commitments = HashMap::<LayerId, LigeroCommitment<F>>::new();
183
184 self.get_committed_input_layer_ids()
185 .into_iter()
186 .sorted_by_key(|layer_id| layer_id.get_raw_input_layer_id())
187 .for_each(|layer_id| {
188 let (desc, maybe_precommitment) = self.get_committed_input_layer(layer_id).unwrap();
190 let commitment = if let Some(commitment) = maybe_precommitment {
191 commitment.clone()
192 } else {
193 let input_mle = self.get_input_mle(layer_id).unwrap();
194 let (commitment, _) = ligero_commit(&input_mle.iter().collect_vec(), &desc.aux);
195 commitment
196 };
197 let root = commitment.get_root();
199 transcript_writer.append_input_elements("Ligero commit", &[root.into_raw()]);
200 ligero_input_commitments.insert(layer_id, commitment);
202 });
203
204 let input_layer_claims = prove_circuit(self, transcript_writer).unwrap();
207
208 if cfg!(feature = "performance-debug") {
211 sanitycheck_input_layers_and_claims(
212 &input_layer_claims,
213 self.get_gkr_circuit_description_ref(),
214 );
215 }
216
217 if cfg!(debug_assertions) {
219 for claim in input_layer_claims.iter() {
220 let input_mle = self.get_input_mle(claim.get_to_layer_id()).unwrap();
221 let evaluation = input_mle.evaluate_at_point(claim.get_point());
222 if evaluation != claim.get_eval() {
223 return Err(anyhow!(GKRError::EvaluationMismatch(
224 claim.get_to_layer_id(),
225 claim.get_to_layer_id(),
226 )));
227 }
228 }
229 }
230
231 for claim in input_layer_claims.iter() {
233 let layer_id = claim.get_to_layer_id();
234 if let Ok((desc, _)) = self.get_committed_input_layer(layer_id) {
235 let mle = self.get_input_mle(layer_id).unwrap();
236 let commitment = ligero_input_commitments.get(&layer_id).unwrap();
237 ligero_eval_prove(
238 &mle.f.iter().collect_vec(),
239 claim.get_point(),
240 transcript_writer,
241 &desc.aux,
242 commitment,
243 )
244 .unwrap();
245 }
246 }
247
248 Ok(proof_config)
249 }
250}