Skip to main content

sonobe_ivc/
lib.rs

1#![warn(missing_docs)]
2
3//! Incremental Verifiable Computation (IVC) abstractions.
4//!
5//! This crate provides the [`IVC`] trait, which describes the common
6//! interface for all IVC constructions, and [compilers] that turn a folding
7//! scheme into a full IVC scheme.
8//!
9//! # Example
10//!
11//! Proving a long hash chain with folding-based IVC. Each step folds one fresh
12//! execution of a user-defined step circuit into a single proof, whose size and
13//! verification cost do not grow with the number of steps.
14//!
15//! You can run this example with `cargo run --release --example hash_chain`.
16#![doc = concat!("```no_run\n", include_str!("../examples/hash_chain.rs"), "```")]
17
18use ark_ff::PrimeField;
19use ark_relations::gr1cs::SynthesisError;
20use ark_serialize::SerializationError;
21use ark_std::rand::RngCore;
22use sonobe_fs::Error as FoldingError;
23use sonobe_primitives::{arithmetizations::Error as ArithError, circuits::FCircuit, traits::Dummy};
24use thiserror::Error;
25
26pub mod compilers;
27
28/// [`enum@Error`] enumerates possible errors during the IVC operations.
29#[derive(Debug, Error)]
30pub enum Error {
31    /// [`Error::ArithError`] indicates an error from the underlying constraint
32    /// system.
33    #[error(transparent)]
34    ArithError(#[from] ArithError),
35    /// [`Error::SerializationError`] indicates an error during serialization.
36    #[error(transparent)]
37    SerializationError(#[from] SerializationError),
38    /// [`Error::FoldingError`] indicates an error from the underlying folding
39    /// scheme.
40    #[error(transparent)]
41    FoldingError(#[from] FoldingError),
42    /// [`Error::SynthesisError`] indicates an error during constraint
43    /// synthesis.
44    #[error(transparent)]
45    SynthesisError(#[from] SynthesisError),
46    /// [`Error::IVCVerificationFail`] indicates that the IVC verification has
47    /// failed.
48    #[error("IVC verification failed")]
49    IVCVerificationFail,
50}
51
52/// [`IVC`] defines the interface of Incremental Verifiable Computation schemes.
53/// It follows the general definition of proof/argument systems, with
54/// preprocessing, key generation, proving, and verification algorithms.
55pub trait IVC {
56    /// [`IVC::Field`] defines the field over which the IVC scheme operates.
57    type Field: PrimeField;
58
59    /// [`IVC::Config`] defines the configuration (e.g., the size of public
60    /// parameters) for the IVC scheme.
61    type Config;
62    /// [`IVC::PublicParam`] defines the public parameters produced by
63    /// preprocessing.
64    type PublicParam;
65    /// [`IVC::ProverKey`] defines the prover key type for the IVC scheme.
66    /// We parameterize it by the step circuit type `FC`, so that a prover key
67    /// for one step circuit cannot be used for another step circuit.
68    type ProverKey<FC: FCircuit>;
69    /// [`IVC::VerifierKey`] defines the verifier key type for the IVC scheme.
70    /// We parameterize it by the step circuit type `FC`, so that a verifier key
71    /// for one step circuit cannot be used for another step circuit.
72    type VerifierKey<FC: FCircuit>;
73    /// [`IVC::Proof`] defines the proof type for the IVC scheme.
74    /// We parameterize it by the step circuit type `FC`, so that a proof for
75    /// one step circuit cannot be used for another step circuit.
76    type Proof<FC: FCircuit>: for<'a> Dummy<&'a Self::ProverKey<FC>>;
77
78    /// [`IVC::preprocess`] defines the preprocessing algorithm, which is a
79    /// randomized algorithm that takes as input the config / parameterization
80    /// `config` of the IVC scheme and outputs the public parameters.
81    ///
82    /// Here, the randomness source is controlled by `rng`.
83    ///
84    /// The security parameter is implicitly specified by the size of underlying
85    /// fields and groups.
86    ///
87    /// This is usually called once for the given configuration and can be
88    /// reused for generating multiple keys for different step circuits, as long
89    /// as the step circuits conform to the configuration.
90    fn preprocess(config: Self::Config, rng: impl RngCore) -> Result<Self::PublicParam, Error>;
91
92    /// [`IVC::generate_keys`] defines the key generation algorithm, which is a
93    /// deterministic algorithm that takes as input the public parameters `pp`
94    /// and the step circuit `step_circuit`, and outputs a prover key and a
95    /// verifier key.
96    #[allow(clippy::type_complexity)]
97    fn generate_keys<FC: FCircuit<Field = Self::Field>>(
98        pp: Self::PublicParam,
99        step_circuit: &FC,
100    ) -> Result<(Self::ProverKey<FC>, Self::VerifierKey<FC>), Error>;
101
102    /// [`IVC::prove`] defines the proof updating algorithm, which is a
103    /// (probably) randomized algorithm that takes as input the prover key `pk`,
104    /// the step circuit `step_circuit`, the current step `i`, the initial state
105    /// `initial_state`, the current state `current_state`, the external inputs
106    /// `external_inputs`, and the current proof `current_proof`.
107    /// It executes the step circuit on the current state and external inputs,
108    /// and outputs its returned next state and external outputs, along with the
109    /// new proof.
110    ///
111    /// Here, `current_proof` attests that `current_state` is correctly derived
112    /// from `initial_state` after `i` steps of executing `step_circuit`, and
113    /// the returned next proof attests that the next state is correctly derived
114    /// from `initial_state` after `i+1` steps with the given `external_inputs`.
115    ///
116    /// The prover may further use `rng` as the randomness source.
117    #[allow(clippy::type_complexity, clippy::too_many_arguments)]
118    fn prove<FC: FCircuit<Field = Self::Field>>(
119        pk: &Self::ProverKey<FC>,
120        step_circuit: &FC,
121        i: usize,
122        initial_state: &FC::State,
123        current_state: &FC::State,
124        external_inputs: FC::ExternalInputs,
125        current_proof: &Self::Proof<FC>,
126        rng: impl RngCore,
127    ) -> Result<(FC::State, FC::ExternalOutputs, Self::Proof<FC>), Error>;
128
129    /// [`IVC::verify`] defines the proof verification algorithm, which is a
130    /// deterministic algorithm that takes as input the verifier key `vk`, the
131    /// current step `i`, the initial state `initial_state`, the current state
132    /// `current_state`, and the proof `proof`, and outputs `Ok(())` if the
133    /// proof is valid, or an error otherwise.
134    fn verify<FC: FCircuit<Field = Self::Field>>(
135        vk: &Self::VerifierKey<FC>,
136        i: usize,
137        initial_state: &FC::State,
138        current_state: &FC::State,
139        proof: &Self::Proof<FC>,
140    ) -> Result<(), Error>;
141}
142
143/// [`IVCStatefulProver`] is a convenience struct that implements a stateful IVC
144/// prover who maintains running state across iterations, so that the user does
145/// not need to manually track and pass in the current state and proof at each
146/// step.
147pub struct IVCStatefulProver<'a, FC: FCircuit<Field = I::Field>, I: IVC> {
148    pk: &'a I::ProverKey<FC>,
149    step_circuit: &'a FC,
150    /// [`IVCStatefulProver::i`] is the number of steps proved so far.
151    pub i: usize,
152    /// [`IVCStatefulProver::initial_state`] is the initial state of iterative
153    /// step circuit executions.
154    pub initial_state: FC::State,
155    /// [`IVCStatefulProver::current_state`] is the current state of iterative
156    /// step circuit executions, reached after [`Self::i`] steps.
157    pub current_state: FC::State,
158    /// [`IVCStatefulProver::current_proof`] is the current proof attesting that
159    /// [`Self::current_state`] is indeed derived from [`Self::initial_state`]
160    /// after executing the step circuit iteratively for [`Self::i`] steps.
161    pub current_proof: I::Proof<FC>,
162}
163
164impl<'a, FC: FCircuit<Field = I::Field>, I: IVC> IVCStatefulProver<'a, FC, I> {
165    /// [`IVCStatefulProver::new`] creates a new stateful IVC prover with the
166    /// given prover key `pk`, step circuit `step_circuit`, and initial state
167    /// `initial_state`.
168    pub fn new(
169        pk: &'a I::ProverKey<FC>,
170        step_circuit: &'a FC,
171        initial_state: FC::State,
172    ) -> Result<Self, Error> {
173        Ok(Self {
174            step_circuit,
175            i: 0,
176            current_state: initial_state.clone(),
177            initial_state,
178            current_proof: I::Proof::dummy(pk),
179            pk,
180        })
181    }
182
183    /// [`IVCStatefulProver::prove_step`] performs one step of proving, updating
184    /// the internal state and proof, and returning the external outputs.
185    pub fn prove_step(
186        &mut self,
187        external_inputs: FC::ExternalInputs,
188        rng: impl RngCore,
189    ) -> Result<FC::ExternalOutputs, Error> {
190        let (next_state, external_outputs, next_proof) = I::prove(
191            self.pk,
192            self.step_circuit,
193            self.i,
194            &self.initial_state,
195            &self.current_state,
196            external_inputs,
197            &self.current_proof,
198            rng,
199        )?;
200        self.i += 1;
201        self.current_state = next_state;
202        self.current_proof = next_proof;
203        Ok(external_outputs)
204    }
205}
206
207/// [`Decider`] defines a decider / proof-compression SNARK, which produces a
208/// final succinct zero-knowledge proof from an IVC proof.
209// TODO (@winderica): Still WIP
210pub trait Decider {
211    /// [`Decider::IVC`] defines the underlying IVC scheme that the decider
212    /// compiles.
213    type IVC: IVC;
214
215    /// [`Decider::ProverKey`] defines the prover key type for the decider.
216    type ProverKey;
217    /// [`Decider::VerifierKey`] defines the verifier key type for the decider.
218    type VerifierKey;
219    /// [`Decider::Instance`] defines the instance type for the decider.
220    type Instance;
221    /// [`Decider::Witness`] defines the witness type for the decider.
222    type Witness;
223    /// [`Decider::Proof`] defines the proof type for the decider.
224    type Proof;
225
226    /// [`Decider::preprocess_and_generate_keys`] preprocesses the IVC prover
227    /// key `ivc_pk` and generates the decider's prover key and verifier key.
228    ///
229    /// This can be seen as a SNARK with circuit-specific setup.
230    // TODO (@winderica): consider universal/transparent setup
231    fn preprocess_and_generate_keys<FC: FCircuit<Field = <Self::IVC as IVC>::Field>>(
232        ivc_pk: &<Self::IVC as IVC>::ProverKey<FC>,
233        rng: impl RngCore,
234    ) -> Result<(Self::ProverKey, Self::VerifierKey), Error>;
235
236    /// [`Decider::prove`] generates a decider proof from the given IVC proof
237    /// and instance/witness.
238    fn prove(
239        pk: &Self::ProverKey,
240        w: &Self::Witness,
241        x: &Self::Instance,
242        rng: impl RngCore,
243    ) -> Result<Self::Proof, Error>;
244
245    /// [`Decider::verify`] verifies the decider proof against the given
246    /// instance.
247    fn verify(vk: &Self::VerifierKey, x: &Self::Instance, proof: &Self::Proof)
248    -> Result<(), Error>;
249}
250
251#[cfg(test)]
252mod tests {
253    use ark_std::{error::Error, rand::Rng};
254
255    use super::*;
256
257    pub fn test_ivc<I: IVC, F: FCircuit<Field = I::Field>>(
258        config: I::Config,
259        step_circuit: F,
260        external_inputs_vec: Vec<F::ExternalInputs>,
261        mut rng: impl Rng,
262    ) -> Result<(), Box<dyn Error>> {
263        let pp = I::preprocess(config, &mut rng)?;
264
265        let (pk, vk) = I::generate_keys(pp, &step_circuit)?;
266
267        let initial_state = step_circuit.dummy_state();
268
269        let mut prover = IVCStatefulProver::<_, I>::new(&pk, &step_circuit, initial_state)?;
270
271        for external_inputs in external_inputs_vec {
272            prover.prove_step(external_inputs, &mut rng)?;
273
274            I::verify(
275                &vk,
276                prover.i,
277                &prover.initial_state,
278                &prover.current_state,
279                &prover.current_proof,
280            )?;
281        }
282
283        Ok(())
284    }
285}