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