kimchi/circuits/polynomials/foreign_field_add/
gadget.rs

1//! This module obtains the gates of a foreign field addition circuit.
2
3use ark_ff::PrimeField;
4use num_bigint::BigUint;
5
6use crate::circuits::{
7    gate::{CircuitGate, Connect, GateType},
8    polynomials::foreign_field_common::BigUintForeignFieldHelpers,
9    wires::Wire,
10};
11
12use super::witness::FFOps;
13
14impl<F: PrimeField> CircuitGate<F> {
15    /// Create foreign field addition gate chain without range checks (needs to wire the range check for result bound manually)
16    /// - Inputs
17    ///   - starting row
18    ///   - operations to perform
19    ///   - modulus of the foreign field
20    /// - Outputs tuple (next_row, circuit_gates) where
21    ///   - next_row      - next row after this gate
22    ///   - circuit_gates - vector of circuit gates comprising this gate
23    ///
24    /// Note that the final structure of the circuit is as follows:
25    /// circuit_gates = [
26    ///      {
27    ///        (i) ->      -> 1 ForeignFieldAdd row
28    ///      } * num times
29    ///      (n)           -> 1 ForeignFieldAdd row (this is where the final result goes)
30    ///      (n+1)         -> 1 Zero row for bound result
31    /// ]
32    ///
33    /// Warning:
34    /// - Wire the range check for result bound manually
35    /// - Connect to public input containing the 1 value for the overflow in the final bound check
36    /// - If the inputs of the addition come from public input, wire it as well
37    pub fn create_chain_ffadd(
38        start_row: usize,
39        opcodes: &[FFOps],
40        foreign_field_modulus: &BigUint,
41    ) -> (usize, Vec<Self>) {
42        if *foreign_field_modulus > BigUint::max_foreign_field_modulus::<F>() {
43            panic!(
44                "foreign_field_modulus exceeds maximum: {} > {}",
45                *foreign_field_modulus,
46                BigUint::max_foreign_field_modulus::<F>()
47            );
48        }
49
50        let next_row = start_row;
51        let foreign_field_modulus = foreign_field_modulus.to_field_limbs::<F>();
52        let mut circuit_gates = vec![];
53        let num = opcodes.len();
54        // ---------------------------
55        // Foreign field addition gates
56        // ---------------------------
57        // First the single-addition gates
58        for (i, opcode) in opcodes.iter().enumerate() {
59            let mut coeffs = foreign_field_modulus.to_vec();
60            coeffs.push(opcode.sign::<F>());
61            circuit_gates.append(&mut vec![CircuitGate {
62                typ: GateType::ForeignFieldAdd,
63                wires: Wire::for_row(next_row + i),
64                coeffs,
65            }]);
66        }
67        let mut final_coeffs = foreign_field_modulus.to_vec();
68        final_coeffs.push(FFOps::Add.sign::<F>());
69        // Then the final bound gate and the zero gate
70        circuit_gates.append(&mut vec![
71            CircuitGate {
72                typ: GateType::ForeignFieldAdd,
73                wires: Wire::for_row(next_row + num),
74                coeffs: final_coeffs,
75            },
76            CircuitGate {
77                typ: GateType::Zero,
78                wires: Wire::for_row(next_row + num + 1),
79                coeffs: vec![],
80            },
81        ]);
82        (start_row + circuit_gates.len(), circuit_gates)
83    }
84
85    /// Create a single foreign field addition gate. This is used for example in the final bound check.
86    /// - Inputs
87    ///   - starting row
88    ///   - operation to perform
89    ///   - modulus of the foreign field
90    /// - Outputs tuple (next_row, circuit_gates) where
91    ///   - next_row      - next row after this gate
92    ///   - circuit_gates - vector of circuit gates comprising this gate
93    pub fn create_single_ffadd(
94        start_row: usize,
95        operation: FFOps,
96        foreign_field_modulus: &BigUint,
97    ) -> (usize, Vec<Self>) {
98        if *foreign_field_modulus > BigUint::max_foreign_field_modulus::<F>() {
99            panic!(
100                "foreign_field_modulus exceeds maximum: {} > {}",
101                *foreign_field_modulus,
102                BigUint::max_foreign_field_modulus::<F>()
103            );
104        }
105
106        let foreign_field_modulus = foreign_field_modulus.to_field_limbs::<F>();
107        let mut coeffs = foreign_field_modulus.to_vec();
108        coeffs.push(operation.sign::<F>());
109        let circuit_gates = vec![
110            CircuitGate {
111                typ: GateType::ForeignFieldAdd,
112                wires: Wire::for_row(start_row),
113                coeffs,
114            },
115            CircuitGate {
116                typ: GateType::Zero,
117                wires: Wire::for_row(start_row + 1),
118                coeffs: vec![],
119            },
120        ];
121
122        (start_row + circuit_gates.len(), circuit_gates)
123    }
124
125    /// Extend a chain of foreign field addition gates. It already wires 1 value to the overflow cell.
126    /// - Inputs
127    ///   - gates: vector of gates to extend
128    ///   - pub_row: row of the public input
129    ///   - curr_row: mutable reference to the current row
130    ///   - opcodes: operations to perform
131    ///   - foreign_field_modulus: modulus of the foreign field
132    pub fn extend_chain_ffadd(
133        gates: &mut Vec<Self>,
134        pub_row: usize,
135        curr_row: &mut usize,
136        opcodes: &[FFOps],
137        foreign_field_modulus: &BigUint,
138    ) {
139        let (next_row, add_gates) =
140            Self::create_chain_ffadd(*curr_row, opcodes, foreign_field_modulus);
141        gates.extend_from_slice(&add_gates);
142        *curr_row = next_row;
143        // check overflow flag is one
144        gates.connect_cell_pair((pub_row, 0), (*curr_row - 2, 6));
145    }
146
147    /// Extend a single foreign field addition gate followed by a zero row containing the result
148    pub fn extend_single_ffadd(
149        gates: &mut Vec<Self>,
150        curr_row: &mut usize,
151        operation: FFOps,
152        foreign_field_modulus: &BigUint,
153    ) {
154        let (next_row, add_gates) =
155            Self::create_single_ffadd(*curr_row, operation, foreign_field_modulus);
156        *curr_row = next_row;
157        gates.extend_from_slice(&add_gates);
158    }
159}