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}