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}