remainder/prover/
helpers.rs

1#![allow(clippy::type_complexity)]
2
3use ark_std::{end_timer, start_timer};
4
5use itertools::Itertools;
6
7use serde_json;
8use sha3::Digest;
9use sha3::Sha3_256;
10use shared_types::circuit_hash::CircuitHashType;
11use shared_types::config::global_config::{
12    global_prover_circuit_description_hash_type, global_verifier_circuit_description_hash_type,
13};
14use shared_types::config::{GKRCircuitProverConfig, GKRCircuitVerifierConfig, ProofConfig};
15use shared_types::transcript::poseidon_sponge::PoseidonSponge;
16use shared_types::transcript::{TranscriptReader, TranscriptSponge, TranscriptWriter};
17use shared_types::{
18    perform_function_under_expected_configs, perform_function_under_prover_config,
19    perform_function_under_verifier_config, Field, Halo2FFTFriendlyField,
20};
21use std::fs::File;
22use std::hash::{DefaultHasher, Hash, Hasher};
23use std::io::BufWriter;
24use std::path::Path;
25
26use crate::provable_circuit::ProvableCircuit;
27use crate::verifiable_circuit::VerifiableCircuit;
28
29use super::GKRCircuitDescription;
30
31/// Writes the circuit description for the provided [GKRCircuitDescription]
32/// to the `circuit_description_path` specified, in JSON format.
33///
34/// ## Arguments
35/// * `circuit_description` - The [GKRCircuitDescription] to be written to file.
36/// * `circuit_description_path` - The filepath to which the JSON description
37///   will be saved.
38pub fn write_circuit_description_to_file<F: Field>(
39    circuit_description: &GKRCircuitDescription<F>,
40    circuit_description_path: &Path,
41) {
42    let f = File::create(circuit_description_path).unwrap();
43    let writer = BufWriter::new(f);
44    serde_json::to_writer(writer, circuit_description).unwrap();
45}
46
47/// Returns an equivalent set of field elements to be absorbed into a
48/// transcript, given a circuit description and the type of hash function
49/// to be used.
50///
51/// ## Arguments
52/// * `circuit_description` - The circuit description to be hashed and
53///   added to transcript.
54/// * `circuit_description_hash_type` - The type of hash function to be
55///   used.
56pub fn get_circuit_description_hash_as_field_elems<F: Field>(
57    circuit_description: &GKRCircuitDescription<F>,
58    circuit_description_hash_type: CircuitHashType,
59) -> Vec<F> {
60    match circuit_description_hash_type {
61        CircuitHashType::DefaultRustHash => {
62            let mut hasher = DefaultHasher::new();
63            circuit_description.hash(&mut hasher);
64            let hash_value = hasher.finish();
65            vec![F::from(hash_value)]
66        }
67        CircuitHashType::Sha3_256 => {
68            // First, serialize the circuit description to be hashed
69            let serialized = serde_json::to_vec(&circuit_description).expect("Failed to serialize");
70            let mut hasher = Sha3_256::new();
71            hasher.update(&serialized);
72            let circuit_description_hash_bytes = hasher.finalize();
73
74            // Since the output is 32 bytes and can be out of range,
75            // we split instead into two chunks of 16 bytes each and
76            // absorb two field elements.
77            // TODO(ryancao): Update this by using `REPR_NUM_BYTES` after merging with the testing branch
78            let mut circuit_description_hash_bytes_first_half = [0; 32];
79            let mut circuit_description_hash_bytes_second_half = [0; 32];
80
81            circuit_description_hash_bytes_first_half[..16]
82                .copy_from_slice(&circuit_description_hash_bytes.to_vec()[..16]);
83            circuit_description_hash_bytes_second_half[..16]
84                .copy_from_slice(&circuit_description_hash_bytes.to_vec()[16..]);
85
86            vec![
87                F::from_bytes_le(circuit_description_hash_bytes_first_half.as_ref()),
88                F::from_bytes_le(circuit_description_hash_bytes_second_half.as_ref()),
89            ]
90        }
91        CircuitHashType::Poseidon => {
92            // First, serialize the circuit description to be hashed
93            let serialized = serde_json::to_vec(&circuit_description).expect("Failed to serialize");
94            // Run through the bytes of `serialized` in chunks of 16 so we don't overflow
95            // TODO(ryancao): Update this by using `REPR_NUM_BYTES` after merging with the testing branch
96            let circuit_field_elem_desc = serialized
97                .chunks(16)
98                .map(|byte_chunk| F::from_bytes_le(byte_chunk))
99                .collect_vec();
100            let mut poseidon_sponge: PoseidonSponge<F> = PoseidonSponge::default();
101            poseidon_sponge.absorb_elements(&circuit_field_elem_desc);
102            vec![poseidon_sponge.squeeze()]
103        }
104    }
105}
106
107/// Function which calls `test_circuit_internal` with the appropriate expected
108/// prover/verifier config.
109pub fn test_circuit_with_config<F: Halo2FFTFriendlyField>(
110    provable_circuit: &ProvableCircuit<F>,
111    expected_prover_config: &GKRCircuitProverConfig,
112    expected_verifier_config: &GKRCircuitVerifierConfig,
113) {
114    perform_function_under_expected_configs!(
115        test_circuit_internal,
116        expected_prover_config,
117        expected_verifier_config,
118        provable_circuit
119    )
120}
121
122/// Function which calls `test_circuit_internal` with the appropriate expected
123/// prover/verifier config.
124pub fn test_circuit_with_runtime_optimized_config<F: Halo2FFTFriendlyField>(
125    provable_circuit: &ProvableCircuit<F>,
126) {
127    let expected_prover_config = GKRCircuitProverConfig::runtime_optimized_default();
128    let expected_verifier_config =
129        GKRCircuitVerifierConfig::new_from_prover_config(&expected_prover_config, false);
130    perform_function_under_expected_configs!(
131        test_circuit_internal,
132        &expected_prover_config,
133        &expected_verifier_config,
134        provable_circuit
135    )
136}
137
138/// Function which calls `test_circuit_internal` with a memory-optimized default.
139pub fn test_circuit_with_memory_optimized_config<F: Halo2FFTFriendlyField>(
140    provable_circuit: &ProvableCircuit<F>,
141) {
142    let expected_prover_config = GKRCircuitProverConfig::memory_optimized_default();
143    let expected_verifier_config =
144        GKRCircuitVerifierConfig::new_from_prover_config(&expected_prover_config, true);
145    perform_function_under_expected_configs!(
146        test_circuit_internal,
147        &expected_prover_config,
148        &expected_verifier_config,
149        provable_circuit
150    )
151}
152
153/// Function which instantiates a circuit description with the given inputs
154/// and precommits and both attempts to both prove and verify said circuit.
155///
156/// Note that this function assumes that the prover is providing _all_ of the
157/// circuit's public inputs. This should _not_ be the case in production, e.g.
158/// if there is a lookup argument involved, since the lookup table should be
159/// generated via a transparent setup and the verifier should know all the
160/// values (or be able to evaluate the MLE of such at a random challenge point).
161fn test_circuit_internal<F: Halo2FFTFriendlyField>(provable_circuit: &ProvableCircuit<F>) {
162    let verifiable_circuit = provable_circuit._gen_verifiable_circuit();
163    let (proof_config, proof_as_transcript_reader) =
164        prove_circuit_internal::<F, PoseidonSponge<F>>(provable_circuit);
165    verify_circuit_internal::<F, PoseidonSponge<F>>(
166        &verifiable_circuit,
167        &proof_config,
168        proof_as_transcript_reader,
169    );
170}
171
172/// Generates a GKR proof (in the form of a [TranscriptReader<F, Tr>]) and a
173/// [ProofConfig] (to be used in verification) for the given
174/// [`ProvableCircuit<F>`].
175fn prove_circuit_internal<F: Halo2FFTFriendlyField, Tr: TranscriptSponge<F>>(
176    provable_circuit: &ProvableCircuit<F>,
177) -> (ProofConfig, TranscriptReader<F, Tr>) {
178    let mut transcript_writer = TranscriptWriter::<F, Tr>::new("GKR Prover Transcript");
179    let prover_timer = start_timer!(|| "Proof generation");
180
181    match provable_circuit.prove(
182        global_prover_circuit_description_hash_type(),
183        &mut transcript_writer,
184    ) {
185        Ok(proof_config) => {
186            end_timer!(prover_timer);
187            let proof_transcript = transcript_writer.get_transcript();
188            (
189                proof_config,
190                TranscriptReader::<F, Tr>::new(proof_transcript),
191            )
192        }
193        Err(err) => {
194            println!("Proof failed! Error: {err}");
195            panic!();
196        }
197    }
198}
199
200/// Takes in a verifier-ready [`VerifiableCircuit<F>`], proof config [ProofConfig]
201/// and GKR proof in the form of a [`TranscriptReader<F, Tr>`], and attempts to
202/// verify the proof against the circuit.
203///
204/// Additionally, takes in a map of public inputs which are already known to
205/// the verifier. These will be checked against the values provided by the
206/// prover in transcript.
207fn verify_circuit_internal<F: Halo2FFTFriendlyField, Tr: TranscriptSponge<F>>(
208    verifiable_circuit: &VerifiableCircuit<F>,
209    proof_config: &ProofConfig,
210    mut proof_as_transcript: TranscriptReader<F, Tr>,
211) {
212    let verifier_timer = start_timer!(|| "Proof verification");
213
214    match verifiable_circuit.verify(
215        global_verifier_circuit_description_hash_type(),
216        &mut proof_as_transcript,
217        proof_config,
218    ) {
219        Ok(_) => {
220            end_timer!(verifier_timer);
221        }
222        Err(err) => {
223            println!("Verify failed! Error: {err}");
224            panic!();
225        }
226    }
227}
228
229/// Wrapper around `prove_circuit_internal()` which uses a runtime-optimized
230/// prover config.
231pub fn prove_circuit_with_runtime_optimized_config<
232    F: Halo2FFTFriendlyField,
233    Tr: TranscriptSponge<F>,
234>(
235    provable_circuit: &ProvableCircuit<F>,
236) -> (ProofConfig, TranscriptReader<F, Tr>) {
237    let runtime_optimized_config = GKRCircuitProverConfig::runtime_optimized_default();
238    perform_function_under_prover_config!(
239        prove_circuit_internal,
240        &runtime_optimized_config,
241        provable_circuit
242    )
243}
244
245/// Wrapper around `verify_circuit_internal()` which uses a verifier config
246/// corresponding to the provided prover config.
247pub fn verify_circuit_with_proof_config<F: Halo2FFTFriendlyField, Tr: TranscriptSponge<F>>(
248    verifiable_circuit: &VerifiableCircuit<F>,
249    proof_config: &ProofConfig,
250    proof_as_transcript: TranscriptReader<F, Tr>,
251) {
252    let verifier_runtime_optimized_config =
253        GKRCircuitVerifierConfig::new_from_proof_config(proof_config, false);
254    perform_function_under_verifier_config!(
255        verify_circuit_internal,
256        &verifier_runtime_optimized_config,
257        verifiable_circuit,
258        proof_config,
259        proof_as_transcript
260    )
261}