triton_air/table/
program.rs1use constraint_circuit::ConstraintCircuitBuilder;
2use constraint_circuit::ConstraintCircuitMonad;
3use constraint_circuit::DualRowIndicator;
4use constraint_circuit::DualRowIndicator::CurrentAux;
5use constraint_circuit::DualRowIndicator::CurrentMain;
6use constraint_circuit::DualRowIndicator::NextAux;
7use constraint_circuit::DualRowIndicator::NextMain;
8use constraint_circuit::SingleRowIndicator;
9use constraint_circuit::SingleRowIndicator::Aux;
10use constraint_circuit::SingleRowIndicator::Main;
11use twenty_first::prelude::*;
12
13use crate::AIR;
14use crate::challenge_id::ChallengeId;
15use crate::cross_table_argument::CrossTableArg;
16use crate::cross_table_argument::EvalArg;
17use crate::cross_table_argument::LookupArg;
18use crate::table_column::MasterAuxColumn;
19use crate::table_column::MasterMainColumn;
20
21#[derive(Debug, Copy, Clone, Eq, PartialEq)]
22pub struct ProgramTable;
23
24impl crate::private::Seal for ProgramTable {}
25
26impl AIR for ProgramTable {
27 type MainColumn = crate::table_column::ProgramMainColumn;
28 type AuxColumn = crate::table_column::ProgramAuxColumn;
29
30 fn initial_constraints(
31 circuit_builder: &ConstraintCircuitBuilder<SingleRowIndicator>,
32 ) -> Vec<ConstraintCircuitMonad<SingleRowIndicator>> {
33 let challenge = |c| circuit_builder.challenge(c);
34 let x_constant = |xfe| circuit_builder.x_constant(xfe);
35 let main_row = |col: Self::MainColumn| circuit_builder.input(Main(col.master_main_index()));
36 let aux_row = |col: Self::AuxColumn| circuit_builder.input(Aux(col.master_aux_index()));
37
38 let address = main_row(Self::MainColumn::Address);
39 let instruction = main_row(Self::MainColumn::Instruction);
40 let index_in_chunk = main_row(Self::MainColumn::IndexInChunk);
41 let is_hash_input_padding = main_row(Self::MainColumn::IsHashInputPadding);
42 let instruction_lookup_log_derivative =
43 aux_row(Self::AuxColumn::InstructionLookupServerLogDerivative);
44 let prepare_chunk_running_evaluation =
45 aux_row(Self::AuxColumn::PrepareChunkRunningEvaluation);
46 let send_chunk_running_evaluation = aux_row(Self::AuxColumn::SendChunkRunningEvaluation);
47
48 let lookup_arg_initial = x_constant(LookupArg::default_initial());
49 let eval_arg_initial = x_constant(EvalArg::default_initial());
50
51 let program_attestation_prepare_chunk_indeterminate =
52 challenge(ChallengeId::ProgramAttestationPrepareChunkIndeterminate);
53
54 let first_address_is_zero = address;
55 let index_in_chunk_is_zero = index_in_chunk;
56 let hash_input_padding_indicator_is_zero = is_hash_input_padding;
57
58 let instruction_lookup_log_derivative_is_initialized_correctly =
59 instruction_lookup_log_derivative - lookup_arg_initial;
60
61 let prepare_chunk_running_evaluation_has_absorbed_first_instruction =
62 prepare_chunk_running_evaluation
63 - eval_arg_initial.clone() * program_attestation_prepare_chunk_indeterminate
64 - instruction;
65
66 let send_chunk_running_evaluation_is_default_initial =
67 send_chunk_running_evaluation - eval_arg_initial;
68
69 vec![
70 first_address_is_zero,
71 index_in_chunk_is_zero,
72 hash_input_padding_indicator_is_zero,
73 instruction_lookup_log_derivative_is_initialized_correctly,
74 prepare_chunk_running_evaluation_has_absorbed_first_instruction,
75 send_chunk_running_evaluation_is_default_initial,
76 ]
77 }
78
79 fn consistency_constraints(
80 circuit_builder: &ConstraintCircuitBuilder<SingleRowIndicator>,
81 ) -> Vec<ConstraintCircuitMonad<SingleRowIndicator>> {
82 let constant = |c: u32| circuit_builder.b_constant(c);
83 let main_row = |col: Self::MainColumn| circuit_builder.input(Main(col.master_main_index()));
84
85 let one = constant(1);
86 let max_index_in_chunk = constant((Tip5::RATE - 1).try_into().unwrap());
87
88 let index_in_chunk = main_row(Self::MainColumn::IndexInChunk);
89 let max_minus_index_in_chunk_inv = main_row(Self::MainColumn::MaxMinusIndexInChunkInv);
90 let is_hash_input_padding = main_row(Self::MainColumn::IsHashInputPadding);
91 let is_table_padding = main_row(Self::MainColumn::IsTablePadding);
92
93 let max_minus_index_in_chunk = max_index_in_chunk - index_in_chunk;
94 let max_minus_index_in_chunk_inv_is_zero_or_the_inverse_of_max_minus_index_in_chunk =
95 (one.clone() - max_minus_index_in_chunk.clone() * max_minus_index_in_chunk_inv.clone())
96 * max_minus_index_in_chunk_inv.clone();
97 let max_minus_index_in_chunk_is_zero_or_the_inverse_of_max_minus_index_in_chunk_inv =
98 (one.clone() - max_minus_index_in_chunk.clone() * max_minus_index_in_chunk_inv)
99 * max_minus_index_in_chunk;
100
101 let is_hash_input_padding_is_bit =
102 is_hash_input_padding.clone() * (is_hash_input_padding - one.clone());
103 let is_table_padding_is_bit = is_table_padding.clone() * (is_table_padding - one);
104
105 vec![
106 max_minus_index_in_chunk_inv_is_zero_or_the_inverse_of_max_minus_index_in_chunk,
107 max_minus_index_in_chunk_is_zero_or_the_inverse_of_max_minus_index_in_chunk_inv,
108 is_hash_input_padding_is_bit,
109 is_table_padding_is_bit,
110 ]
111 }
112
113 fn transition_constraints(
114 circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
115 ) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
116 let challenge = |c| circuit_builder.challenge(c);
117 let constant = |c: u64| circuit_builder.b_constant(c);
118
119 let current_main_row =
120 |col: Self::MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
121 let next_main_row =
122 |col: Self::MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
123 let current_aux_row =
124 |col: Self::AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
125 let next_aux_row =
126 |col: Self::AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
127
128 let one = constant(1);
129 let rate_minus_one = constant(u64::try_from(Tip5::RATE).unwrap() - 1);
130
131 let prepare_chunk_indeterminate =
132 challenge(ChallengeId::ProgramAttestationPrepareChunkIndeterminate);
133 let send_chunk_indeterminate =
134 challenge(ChallengeId::ProgramAttestationSendChunkIndeterminate);
135
136 let address = current_main_row(Self::MainColumn::Address);
137 let instruction = current_main_row(Self::MainColumn::Instruction);
138 let lookup_multiplicity = current_main_row(Self::MainColumn::LookupMultiplicity);
139 let index_in_chunk = current_main_row(Self::MainColumn::IndexInChunk);
140 let max_minus_index_in_chunk_inv =
141 current_main_row(Self::MainColumn::MaxMinusIndexInChunkInv);
142 let is_hash_input_padding = current_main_row(Self::MainColumn::IsHashInputPadding);
143 let is_table_padding = current_main_row(Self::MainColumn::IsTablePadding);
144 let log_derivative = current_aux_row(Self::AuxColumn::InstructionLookupServerLogDerivative);
145 let prepare_chunk_running_evaluation =
146 current_aux_row(Self::AuxColumn::PrepareChunkRunningEvaluation);
147 let send_chunk_running_evaluation =
148 current_aux_row(Self::AuxColumn::SendChunkRunningEvaluation);
149
150 let address_next = next_main_row(Self::MainColumn::Address);
151 let instruction_next = next_main_row(Self::MainColumn::Instruction);
152 let index_in_chunk_next = next_main_row(Self::MainColumn::IndexInChunk);
153 let max_minus_index_in_chunk_inv_next =
154 next_main_row(Self::MainColumn::MaxMinusIndexInChunkInv);
155 let is_hash_input_padding_next = next_main_row(Self::MainColumn::IsHashInputPadding);
156 let is_table_padding_next = next_main_row(Self::MainColumn::IsTablePadding);
157 let log_derivative_next =
158 next_aux_row(Self::AuxColumn::InstructionLookupServerLogDerivative);
159 let prepare_chunk_running_evaluation_next =
160 next_aux_row(Self::AuxColumn::PrepareChunkRunningEvaluation);
161 let send_chunk_running_evaluation_next =
162 next_aux_row(Self::AuxColumn::SendChunkRunningEvaluation);
163
164 let address_increases_by_one = address_next - (address.clone() + one.clone());
165 let is_table_padding_is_0_or_remains_unchanged =
166 is_table_padding.clone() * (is_table_padding_next.clone() - is_table_padding);
167
168 let index_in_chunk_cycles_correctly = (one.clone()
169 - max_minus_index_in_chunk_inv.clone()
170 * (rate_minus_one.clone() - index_in_chunk.clone()))
171 * index_in_chunk_next.clone()
172 + max_minus_index_in_chunk_inv.clone()
173 * (index_in_chunk_next.clone() - index_in_chunk.clone() - one.clone());
174
175 let hash_input_indicator_is_0_or_remains_unchanged =
176 is_hash_input_padding.clone() * (is_hash_input_padding_next.clone() - one.clone());
177
178 let first_hash_input_padding_is_1 = (is_hash_input_padding.clone() - one.clone())
179 * is_hash_input_padding_next
180 * (instruction_next.clone() - one.clone());
181
182 let hash_input_padding_is_0_after_the_first_1 =
183 is_hash_input_padding.clone() * instruction_next.clone();
184
185 let next_row_is_table_padding_row = is_table_padding_next.clone() - one.clone();
186 let table_padding_starts_when_hash_input_padding_is_active_and_index_in_chunk_is_zero =
187 is_hash_input_padding.clone()
188 * (one.clone()
189 - max_minus_index_in_chunk_inv.clone()
190 * (rate_minus_one.clone() - index_in_chunk.clone()))
191 * next_row_is_table_padding_row.clone();
192
193 let log_derivative_remains = log_derivative_next.clone() - log_derivative.clone();
194 let compressed_row = challenge(ChallengeId::ProgramAddressWeight) * address
195 + challenge(ChallengeId::ProgramInstructionWeight) * instruction
196 + challenge(ChallengeId::ProgramNextInstructionWeight) * instruction_next.clone();
197
198 let indeterminate = challenge(ChallengeId::InstructionLookupIndeterminate);
199 let log_derivative_updates = (log_derivative_next - log_derivative)
200 * (indeterminate - compressed_row)
201 - lookup_multiplicity;
202 let log_derivative_updates_if_and_only_if_not_a_padding_row =
203 (one.clone() - is_hash_input_padding.clone()) * log_derivative_updates
204 + is_hash_input_padding * log_derivative_remains;
205
206 let prepare_chunk_running_evaluation_absorbs_next_instruction =
207 prepare_chunk_running_evaluation_next.clone()
208 - prepare_chunk_indeterminate.clone() * prepare_chunk_running_evaluation
209 - instruction_next.clone();
210 let prepare_chunk_running_evaluation_resets_and_absorbs_next_instruction =
211 prepare_chunk_running_evaluation_next.clone()
212 - prepare_chunk_indeterminate
213 - instruction_next;
214 let index_in_chunk_is_max = rate_minus_one.clone() - index_in_chunk.clone();
215 let index_in_chunk_is_not_max =
216 one.clone() - max_minus_index_in_chunk_inv * (rate_minus_one.clone() - index_in_chunk);
217 let prepare_chunk_running_evaluation_resets_every_rate_rows_and_absorbs_next_instruction =
218 index_in_chunk_is_max * prepare_chunk_running_evaluation_absorbs_next_instruction
219 + index_in_chunk_is_not_max
220 * prepare_chunk_running_evaluation_resets_and_absorbs_next_instruction;
221
222 let send_chunk_running_evaluation_absorbs_next_chunk = send_chunk_running_evaluation_next
223 .clone()
224 - send_chunk_indeterminate * send_chunk_running_evaluation.clone()
225 - prepare_chunk_running_evaluation_next;
226 let send_chunk_running_evaluation_does_not_change =
227 send_chunk_running_evaluation_next - send_chunk_running_evaluation;
228 let index_in_chunk_next_is_max = rate_minus_one - index_in_chunk_next;
229 let index_in_chunk_next_is_not_max =
230 one - max_minus_index_in_chunk_inv_next * index_in_chunk_next_is_max.clone();
231
232 let send_chunk_running_eval_absorbs_chunk_iff_index_in_chunk_next_is_max_and_not_padding =
233 send_chunk_running_evaluation_absorbs_next_chunk
234 * next_row_is_table_padding_row
235 * index_in_chunk_next_is_not_max
236 + send_chunk_running_evaluation_does_not_change.clone() * is_table_padding_next
237 + send_chunk_running_evaluation_does_not_change * index_in_chunk_next_is_max;
238
239 vec![
240 address_increases_by_one,
241 is_table_padding_is_0_or_remains_unchanged,
242 index_in_chunk_cycles_correctly,
243 hash_input_indicator_is_0_or_remains_unchanged,
244 first_hash_input_padding_is_1,
245 hash_input_padding_is_0_after_the_first_1,
246 table_padding_starts_when_hash_input_padding_is_active_and_index_in_chunk_is_zero,
247 log_derivative_updates_if_and_only_if_not_a_padding_row,
248 prepare_chunk_running_evaluation_resets_every_rate_rows_and_absorbs_next_instruction,
249 send_chunk_running_eval_absorbs_chunk_iff_index_in_chunk_next_is_max_and_not_padding,
250 ]
251 }
252
253 fn terminal_constraints(
254 circuit_builder: &ConstraintCircuitBuilder<SingleRowIndicator>,
255 ) -> Vec<ConstraintCircuitMonad<SingleRowIndicator>> {
256 let constant = |c: u64| circuit_builder.b_constant(c);
257 let main_row = |col: Self::MainColumn| circuit_builder.input(Main(col.master_main_index()));
258
259 let index_in_chunk = main_row(Self::MainColumn::IndexInChunk);
260 let is_hash_input_padding = main_row(Self::MainColumn::IsHashInputPadding);
261 let is_table_padding = main_row(Self::MainColumn::IsTablePadding);
262
263 let hash_input_padding_is_one = is_hash_input_padding - constant(1);
264
265 let max_index_in_chunk = Tip5::RATE as u64 - 1;
266 let index_in_chunk_is_max_or_row_is_padding_row =
267 (index_in_chunk - constant(max_index_in_chunk)) * (is_table_padding - constant(1));
268
269 vec![
270 hash_input_padding_is_one,
271 index_in_chunk_is_max_or_row_is_padding_row,
272 ]
273 }
274}