Skip to main content

sonobe_primitives/algebra/field/
mod.rs

1//! This module defines extension traits for field elements and their in-circuit
2//! counterparts, along with some common implementations.
3
4use ark_ff::{BigInteger, Fp, FpConfig, PrimeField};
5use ark_r1cs_std::{
6    GR1CSVar,
7    alloc::AllocVar,
8    eq::EqGadget,
9    fields::{FieldVar, fp::FpVar},
10};
11use ark_relations::gr1cs::SynthesisError;
12use ark_std::{
13    any::TypeId,
14    mem::transmute_copy,
15    ops::{Add, Mul},
16};
17
18use crate::{
19    algebra::{Val, field::emulated::EmulatedFieldVar},
20    circuits::WitnessToPublic,
21    traits::{Inputize, InputizeEmulated},
22    transcripts::{Absorbable, AbsorbableVar},
23};
24
25pub mod emulated;
26
27/// [`SonobeField`] trait is a wrapper around [`PrimeField`] that also includes
28/// necessary bounds for the field to be used conveniently in folding schemes.
29pub trait SonobeField:
30    PrimeField<BasePrimeField = Self>
31    + Absorbable
32    + Inputize<Self>
33    + Val<
34        Var: FieldVar<Self, Self> + WitnessToPublic,
35        EmulatedVar<Self> = EmulatedFieldVar<Self, Self>,
36    >
37{
38    /// [`SonobeField::BITS_PER_LIMB`] defines the bit length of each limb when
39    /// representing field elements as limbs in an emulated field variable.
40    // TODO: either make it configurable, or compute an optimal value based on
41    // the modulus size.
42    const BITS_PER_LIMB: usize;
43}
44
45impl<P: FpConfig<N>, const N: usize> SonobeField for Fp<P, N> {
46    const BITS_PER_LIMB: usize = 32;
47}
48
49impl<P: FpConfig<N>, const N: usize> Val for Fp<P, N> {
50    type PreferredConstraintField = Self;
51    type Var = FpVar<Self>;
52
53    type EmulatedVar<F: SonobeField> = EmulatedFieldVar<F, Self>;
54}
55
56impl<P: FpConfig<N>, const N: usize> Absorbable for Fp<P, N> {
57    fn absorb_into<F: PrimeField>(&self, dest: &mut Vec<F>) {
58        if TypeId::of::<F>() == TypeId::of::<Self>() {
59            // Safe because `F` and `Self` have the same type
60            // TODO (@winderica): specialization when???
61            dest.push(unsafe { transmute_copy::<Self, F>(self) });
62        } else {
63            let bits_per_limb = F::MODULUS_BIT_SIZE - 1;
64            let num_limbs = Self::MODULUS_BIT_SIZE.div_ceil(bits_per_limb);
65
66            let mut limbs = self
67                .into_bigint()
68                .to_bits_le()
69                .chunks(bits_per_limb as usize)
70                .map(|chunk| F::from(F::BigInt::from_bits_le(chunk)))
71                .collect::<Vec<F>>();
72            limbs.resize(num_limbs as usize, F::zero());
73
74            dest.extend(&limbs)
75        }
76    }
77}
78
79impl<F: PrimeField> AbsorbableVar<F> for FpVar<F> {
80    fn absorb_into(&self, dest: &mut Vec<FpVar<F>>) -> Result<(), SynthesisError> {
81        dest.push(self.clone());
82        Ok(())
83    }
84}
85
86impl<P: FpConfig<N>, const N: usize> Inputize<Self> for Fp<P, N> {
87    fn inputize(&self) -> Vec<Self> {
88        vec![*self]
89    }
90}
91
92impl<F: SonobeField, P: SonobeField> InputizeEmulated<F> for P {
93    fn inputize_emulated(&self) -> Vec<F> {
94        self.into_bigint()
95            .to_bits_le()
96            .chunks(F::BITS_PER_LIMB)
97            .map(|chunk| F::from(F::BigInt::from_bits_le(chunk)))
98            .collect()
99    }
100}
101
102impl<F: PrimeField> WitnessToPublic for FpVar<F> {
103    fn mark_as_public(&self) -> Result<(), SynthesisError> {
104        // This line "converts" `x` from a witness to a public input.
105        // Instead of directly modifying the constraint system, we allocate a
106        // public input variable explicitly and enforce that its value is indeed
107        // `x`.
108        // While seemingly redundant, comparing `x` with itself is necessary
109        // because:
110        // - `.value()` allows an honest prover to extract public inputs without
111        //   computing them outside the circuit.
112        // - `.enforce_equal()` prevents a malicious prover from claiming public
113        //   inputs that are not the honest `x` computed in-circuit.
114        self.enforce_equal(&FpVar::new_input(self.cs(), || self.value())?)
115    }
116}
117
118/// [`TwoStageFieldVar`] abstracts over field variables that support a
119/// two-stage arithmetic model.
120///
121/// In this model, we consider two stages of in-circuit variables for field
122/// elements when performing arithmetic operations:
123/// 1. Before the operations, we have the standard field variable type, i.e.,
124///    the implementor of this trait.
125/// 2. During the operations, we use [`TwoStageFieldVar::Intermediate`] to hold
126///    the intermediate results.
127///    Therefore, the [`Add`] and [`Mul`] operations between two field variables
128///    yield an intermediate variable.
129pub trait TwoStageFieldVar:
130    Clone
131    + Add<Output = Self::Intermediate>
132    + for<'a> Add<&'a Self, Output = Self::Intermediate>
133    + Mul<Output = Self::Intermediate>
134    + for<'a> Mul<&'a Self, Output = Self::Intermediate>
135{
136    /// The intermediate variable type used during arithmetic operations.
137    ///
138    /// We require this type to support conversions from and to the original
139    /// field variable type.
140    ///
141    /// In addition, to allow chaining operations without excessive conversions,
142    /// we require this type to support [`Add`] and [`Mul`] operations with both
143    /// itself and the original field variable type.
144    type Intermediate: Clone
145        + From<Self>
146        + TryInto<Self>
147        + Add<Output = Self::Intermediate>
148        + for<'a> Add<&'a Self::Intermediate, Output = Self::Intermediate>
149        + Mul<Output = Self::Intermediate>
150        + for<'a> Mul<&'a Self::Intermediate, Output = Self::Intermediate>
151        + Add<Self, Output = Self::Intermediate>
152        + for<'a> Add<&'a Self, Output = Self::Intermediate>
153        + Mul<Self, Output = Self::Intermediate>
154        + for<'a> Mul<&'a Self, Output = Self::Intermediate>;
155}
156
157// Operations over the canonical variable `FpVar` always yield another `FpVar`.
158impl<F: PrimeField> TwoStageFieldVar for FpVar<F> {
159    type Intermediate = Self;
160}