Skip to main content

sonobe_primitives/arithmetizations/
mod.rs

1//! This module defines and implements traits for arithmetizations, also known
2//! as constraint systems.
3//!
4//! In Sonobe, we currently support two constraint systems: the Rank-1
5//! Constraint System (R1CS) and the Customizable Constraint System (CCS).
6//! However, user circuits are always synthesized into R1CS currently, since
7//! R1CS is the only supported constraint system by ark-relations.
8
9use ark_relations::gr1cs::SynthesisError;
10use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
11use ark_std::{fmt::Debug, log2};
12use thiserror::Error;
13
14use crate::relations::{Relation, RelationGadget};
15
16pub mod ccs;
17pub mod r1cs;
18
19/// [`enum@Error`] enumerates possible errors during arithmetization operations.
20#[derive(Error, Debug)]
21pub enum Error {
22    /// [`Error::MalformedAssignments`] indicates that the provided assignments
23    /// have incorrect shape.
24    #[error("The provided assignments have incorrect shape: {0}")]
25    MalformedAssignments(String),
26    /// [`Error::UnsatisfiedAssignments`] indicates that the provided
27    /// assignments do not satisfy the constraint system.
28    #[error("The provided assignments do not satisfy the constraint system: {0}")]
29    UnsatisfiedAssignments(String),
30    /// [`Error::InvalidNumberOfConstraints`] indicates that the constraint
31    /// system's number of constraints does not match the provided config.
32    #[error(
33        "The number of constraints in the constraint system configuration is invalid. Provided: {0}, expected: {1}"
34    )]
35    InvalidNumberOfConstraints(usize, usize),
36    /// [`Error::InvalidNumberOfVariables`] indicates that the constraint
37    /// system's number of variables does not match the provided config.
38    #[error(
39        "The number of variables in the constraint system configuration is invalid. Provided: {0}, expected: {1}"
40    )]
41    InvalidNumberOfVariables(usize, usize),
42    /// [`Error::SynthesisError`] indicates an error during constraint
43    /// synthesis.
44    #[error(transparent)]
45    SynthesisError(#[from] SynthesisError),
46}
47
48/// [`ArithConfig`] describes the configuration of a constraint system.
49#[derive(Clone, Debug, Default, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
50pub struct ArithConfig {
51    /// [`ArithConfig::degree`] specifies the degree of the constraint system.
52    pub degree: usize,
53
54    /// [`ArithConfig::n_constraints`] specifies the number of constraints in
55    /// the constraint system.
56    pub n_constraints: usize,
57
58    /// [`ArithConfig::n_variables`] specifies the number of variables in the
59    /// constraint system.
60    pub n_variables: usize,
61
62    /// [`ArithConfig::n_public_inputs`] specifies the number of public inputs
63    /// in the constraint system.
64    pub n_public_inputs: usize,
65
66    /// [`ArithConfig::n_witnesses`] specifies the number of witnesses in the
67    /// constraint system.
68    pub n_witnesses: usize,
69}
70
71impl ArithConfig {
72    /// [`ArithConfig::log_constraints`] returns the base-2 logarithm of the
73    /// number of constraints in the constraint system.
74    pub fn log_constraints(&self) -> usize {
75        log2(self.n_constraints) as usize
76    }
77}
78
79/// [`Arith`] is a trait for constraint systems (R1CS, CCS, etc.), where we
80/// define methods to get and set configuration about the constraint system.
81/// In addition to the configuration, the implementor of this trait may also
82/// store the actual constraints and other information.
83pub trait Arith: Clone + Default + Send + Sync + CanonicalSerialize + CanonicalDeserialize {
84    /// [`Arith::config`] returns the configuration of the constraint system.
85    fn config(&self) -> ArithConfig;
86}
87
88/// [`ArithRelation`] treats a constraint system as a relation between a witness
89/// of type `W` and an instance of type `U`, and in this trait, we separate the
90/// relation check into two steps: evaluating the constraint system and checking
91/// the evaluation result.
92///
93/// Note that `W` and `U` are part of the trait parameters instead of associated
94/// types, because the same constraint system may support different types of `W`
95/// and `U`, and the satisfiability check may vary.
96/// This "same constraint system, different witness-instance pair" abstraction
97/// turns out to be very flexible, as one constraint system struct now can have
98/// many different relation checks depending on the context.
99///
100/// For example, some folding schemes consider a variant of R1CS known as
101/// relaxed R1CS, which is also represented by the `A`, `B`, and `C` matrices
102/// but has a different relation check compared to plain R1CS.
103/// We handle their similarities and differences in the following way:
104/// - Since the structure of relaxed R1CS is exactly the same as plain R1CS, we
105///   use a single R1CS struct to represent both of them.
106/// - To distinguish their relation checks, we instead use distinct types of `W`
107///   and `U`.
108///     - For plain R1CS, we use plain witness `W = w` and instance `U = x` that
109///       are simply vectors of field elements.
110///       The implementation of `ArithRelation` for such `W` and `U` then checks
111///       if `Az ∘ Bz = Cz`, where `z = [1, x, w]`.
112///     - For relaxed R1CS, we use relaxed witness `W` and relaxed instance `U`
113///       that contain extra data such as the error or slack terms, e.g.,
114///         - In Nova, `W = (w, e, ...)`, `U = (u, x, ...)`.
115///           The implementation of `ArithRelation` for such `W` and `U` checks
116///           if `Az ∘ Bz = uCz + e`, where `z = [u, x, w]`.
117///         - In ProtoGalaxy, `W = (w, ...)`, `U = (x, e, β, ...)`.
118///           The implementation of `ArithRelation` for such `W` and `U` checks
119///           if `e = Σ pow_i(β) v_i`, where `v = Az ∘ Bz - Cz`,`z = [1, x, w]`.
120///
121/// This is also the case for CCS, where `W` and `U` may be vectors of field
122/// elements or running / incoming witness-instance pairs of different folding
123/// schemes such as HyperNova.
124pub trait ArithRelation<W: ?Sized, U: ?Sized>: Arith {
125    /// [`ArithRelation::Evaluation`] defines the type of the evaluation result
126    /// returned by [`ArithRelation::eval_relation`], and consumed by
127    /// [`ArithRelation::check_evaluation`].
128    ///
129    /// The evaluation result is usually a vector of field elements.
130    /// However, we use an associated type to represent the evaluation result
131    /// for future extensions.
132    type Evaluation;
133
134    /// [`ArithRelation::eval_relation`] evaluates the constraint system at
135    /// witness `w` and instance `u`. It returns the evaluation result.
136    ///
137    /// For instance:
138    /// - Evaluating the plain R1CS at `W = w` and `U = x` returns
139    ///   `Az ∘ Bz - Cz`, where `z = [1, x, w]`.
140    /// - Evaluating the relaxed R1CS in Nova at `W = (w, e, ...)` and
141    ///   `U = (u, x, ...)` returns `Az ∘ Bz - uCz`, where `z = [u, x, w]`.
142    /// - Evaluating the relaxed R1CS in ProtoGalaxy at `W = (w, ...)` and
143    ///   `U = (x, e, β, ...)` returns `Az ∘ Bz - Cz`, where `z = [1, x, w]`.
144    fn eval_relation(&self, w: &W, u: &U) -> Result<Self::Evaluation, Error>;
145
146    /// [`ArithRelation::check_evaluation`] checks if the evaluation result is
147    /// valid. The witness `w` and instance `u` are also parameters, because the
148    /// validity check may need information contained in `w` and/or `u`.
149    ///
150    /// For instance:
151    /// - The evaluation `v` of plain R1CS at satisfying `W` and `U` should be
152    ///   an all-zero vector.
153    /// - The evaluation `v` of relaxed R1CS in Nova at satisfying `W` and `U`
154    ///   should be equal to the error term `e` in `W`.
155    /// - The evaluation `v` of relaxed R1CS in ProtoGalaxy at satisfying `W`
156    ///   and `U` should satisfy `e = Σ pow_i(β) v_i`, where `e` is the error
157    ///   term in `U`.
158    fn check_evaluation(w: &W, u: &U, v: Self::Evaluation) -> Result<(), Error>;
159}
160
161impl<W, U, A: ArithRelation<W, U>> Relation<W, U> for A {
162    type Error = Error;
163
164    fn check_relation(&self, w: &W, u: &U) -> Result<(), Self::Error> {
165        // `check_relation` is implemented by combining `eval_relation` and
166        // `check_evaluation`.
167        let e = self.eval_relation(w, u)?;
168        Self::check_evaluation(w, u, e)
169    }
170}
171
172/// [`ArithRelationGadget`] defines the in-circuit gadget for constraint system
173/// operations in the same way as [`ArithRelation`].
174pub trait ArithRelationGadget<WVar, UVar> {
175    /// [`ArithRelationGadget::Evaluation`] defines the type of the evaluation
176    /// result returned by [`ArithRelationGadget::eval_relation`], and consumed
177    /// by [`ArithRelationGadget::check_evaluation`].
178    type Evaluation;
179
180    /// [`ArithRelationGadget::eval_relation`] evaluates the constraint system
181    /// at witness `w` and instance `u`. It returns the evaluation result.
182    fn eval_relation(&self, w: &WVar, u: &UVar) -> Result<Self::Evaluation, SynthesisError>;
183
184    /// [`ArithRelationGadget::check_evaluation`] checks if the evaluation
185    /// result is valid under the help of the witness `w` and instance `u`.
186    fn check_evaluation(w: &WVar, u: &UVar, e: Self::Evaluation) -> Result<(), SynthesisError>;
187}
188
189impl<WVar, UVar, A: ArithRelationGadget<WVar, UVar>> RelationGadget<WVar, UVar> for A {
190    fn check_relation(&self, w: &WVar, u: &UVar) -> Result<(), SynthesisError> {
191        // `check_relation` is implemented by combining `eval_relation` and
192        // `check_evaluation`.
193        let e = self.eval_relation(w, u)?;
194        Self::check_evaluation(w, u, e)
195    }
196}