use miden_crypto::stark::air::AirBuilder;
use crate::{CoreCols, MidenAirBuilder, constraints::op_flags::OpFlags};
pub fn enforce_main<AB>(
builder: &mut AB,
local: &CoreCols<AB::Var>,
next: &CoreCols<AB::Var>,
op_flags: &OpFlags<AB::Expr>,
) where
AB: MidenAirBuilder,
{
{
let flag_sum = op_flags.no_shift_at(0) + op_flags.left_shift_at(1);
let expected = op_flags.no_shift_at(0) * local.stack.get(0).into()
+ op_flags.left_shift_at(1) * local.stack.get(1).into();
let actual = next.stack.get(0);
builder.when_transition().assert_zero(actual * flag_sum - expected);
}
for i in 1..15 {
let flag_sum = op_flags.no_shift_at(i)
+ op_flags.left_shift_at(i + 1)
+ op_flags.right_shift_at(i - 1);
let expected = op_flags.no_shift_at(i) * local.stack.get(i).into()
+ op_flags.left_shift_at(i + 1) * local.stack.get(i + 1).into()
+ op_flags.right_shift_at(i - 1) * local.stack.get(i - 1).into();
let actual = next.stack.get(i);
builder.when_transition().assert_zero(actual * flag_sum - expected);
}
{
let flag_sum = op_flags.no_shift_at(15) + op_flags.right_shift_at(14);
let expected = op_flags.no_shift_at(15) * local.stack.get(15).into()
+ op_flags.right_shift_at(14) * local.stack.get(14).into();
let actual = next.stack.get(15);
builder.when_transition().assert_zero(actual * flag_sum - expected);
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use miden_core::{
Felt,
field::{PrimeCharacteristicRing, QuadFelt},
operations::opcodes,
};
use super::enforce_main;
use crate::constraints::{
columns::CoreCols,
op_flags::{OpFlags, generate_test_row},
stack::test_utils::ConstraintEvalBuilder,
};
fn eval_stack_general(local: &CoreCols<Felt>, next: &CoreCols<Felt>) -> Vec<QuadFelt> {
let mut builder = ConstraintEvalBuilder::new();
let op_flags = OpFlags::new(&local.decoder, &local.stack, &next.decoder);
enforce_main(&mut builder, local, next, &op_flags);
builder.evaluations
}
#[test]
fn evalcircuit_rejects_forged_stack_transition() {
let local = generate_test_row(opcodes::EVALCIRCUIT.into());
let mut next = generate_test_row(0);
let evaluations = eval_stack_general(&local, &next);
assert!(evaluations.iter().all(|value| *value == QuadFelt::ZERO));
next.stack.top[7] += Felt::ONE;
let evaluations = eval_stack_general(&local, &next);
assert!(
evaluations.iter().any(|value| *value != QuadFelt::ZERO),
"EVALCIRCUIT must preserve the visible stack"
);
}
}