use std::{iter, sync::Arc};
use openvm_circuit_primitives::Chip;
use openvm_instructions::{
exe::VmExe,
instruction::Instruction,
program::{Program, DEFAULT_PC_STEP},
LocalOpcode, VmOpcode,
};
use openvm_stark_backend::{
any_air_arc_vec,
p3_field::PrimeCharacteristicRing,
p3_matrix::{dense::RowMajorMatrix, Matrix},
prover::{AirProvingContext, CommittedTraceData, TraceCommitter},
test_utils::dummy_airs::interaction::dummy_interaction_air::DummyInteractionAir,
StarkEngine, StarkTestError,
};
use openvm_stark_sdk::p3_baby_bear::BabyBear;
use crate::{
arch::{instructions::SystemOpcode::*, testing::READ_INSTRUCTION_BUS},
system::program::{trace::generate_cached_trace, ProgramAir, ProgramBus, ProgramChip},
utils::test_cpu_engine,
};
pub(crate) const LOADW: VmOpcode = VmOpcode::from_usize(0x210);
pub(crate) const STOREW: VmOpcode = VmOpcode::from_usize(0x213);
pub(crate) const BEQ: VmOpcode = VmOpcode::from_usize(0x220);
pub(crate) const SUB: VmOpcode = VmOpcode::from_usize(0x201);
pub(crate) const JAL: VmOpcode = VmOpcode::from_usize(0x230);
pub(crate) const BNE: VmOpcode = VmOpcode::from_usize(0x221);
fn interaction_test(program: Program<BabyBear>, execution: Vec<u32>) {
let mut execution_frequencies = vec![0; program.len()];
for pc_idx in execution {
execution_frequencies[pc_idx as usize] += 1;
}
let filtered_exec_frequencies: Vec<_> = program
.instructions_and_debug_infos
.iter()
.enumerate()
.filter(|(_, entry)| entry.is_some())
.map(|(i, _)| execution_frequencies[i])
.collect();
let original_height = filtered_exec_frequencies.len();
let bus = ProgramBus::new(READ_INSTRUCTION_BUS);
let program_air = ProgramAir::new(bus);
let engine = test_cpu_engine();
let exe = VmExe::new(program);
let cached_trace = generate_cached_trace(&exe.program);
let (commitment, pcs_data) = TraceCommitter::commit(engine.device(), &[&cached_trace]).unwrap();
let cached = CommittedTraceData {
commitment,
data: Arc::new(pcs_data),
trace: cached_trace,
};
let chip = ProgramChip {
filtered_exec_frequencies,
cached: Some(cached),
_marker: std::marker::PhantomData,
};
let ctx = chip.generate_proving_ctx(());
let counter_air = DummyInteractionAir::new(9, true, bus.inner.index);
let mut program_cells = vec![];
for (index, frequency) in execution_frequencies.into_iter().enumerate() {
let option = exe.program.get_instruction_and_debug_info(index);
if let Some((instruction, _)) = option {
program_cells.extend([
BabyBear::from_u32(frequency),
BabyBear::from_usize(index * (DEFAULT_PC_STEP as usize)),
instruction.opcode.to_field(),
instruction.a,
instruction.b,
instruction.c,
instruction.d,
instruction.e,
instruction.f,
instruction.g,
]);
}
}
let width = 10;
let desired_height = original_height.next_power_of_two();
let cells_to_add = (desired_height - original_height) * width;
program_cells.extend(iter::repeat_n(BabyBear::ZERO, cells_to_add));
let counter_trace = RowMajorMatrix::new(program_cells, 10);
println!("trace height = {original_height}");
println!("counter trace height = {}", Matrix::height(&counter_trace));
engine
.run_test(
any_air_arc_vec!(program_air, counter_air),
vec![ctx, AirProvingContext::simple_no_pis(counter_trace)],
)
.expect("Verification failed");
}
#[test]
fn test_program_1() {
let n = 2;
let instructions = vec![
Instruction::large_from_isize(STOREW, n, 0, 0, 0, 1, 0, 1),
Instruction::large_from_isize(STOREW, 1, 1, 0, 0, 1, 0, 1),
Instruction::from_isize(BEQ, 0, 0, 3 * DEFAULT_PC_STEP as isize, 1, 0),
Instruction::from_isize(SUB, 0, 0, 1, 1, 1),
Instruction::from_isize(JAL, 2, -2 * (DEFAULT_PC_STEP as isize), 0, 1, 0),
Instruction::from_isize(TERMINATE.global_opcode(), 0, 0, 0, 0, 0),
];
let program = Program::from_instructions(&instructions);
interaction_test(program, vec![0, 3, 2, 5]);
}
#[test]
fn test_program_without_field_arithmetic() {
let instructions = vec![
Instruction::large_from_isize(STOREW, 5, 0, 0, 0, 1, 0, 1),
Instruction::from_isize(BNE, 0, 4, 3 * DEFAULT_PC_STEP as isize, 1, 0),
Instruction::from_isize(JAL, 2, -2 * DEFAULT_PC_STEP as isize, 0, 1, 0),
Instruction::from_isize(TERMINATE.global_opcode(), 0, 0, 0, 0, 0),
Instruction::from_isize(BEQ, 0, 5, -(DEFAULT_PC_STEP as isize), 1, 0),
];
let program = Program::from_instructions(&instructions);
interaction_test(program, vec![0, 2, 4, 1]);
}
#[test]
fn test_program_negative() {
let instructions = vec![
Instruction::large_from_isize(STOREW, -1, 0, 0, 0, 1, 0, 1),
Instruction::large_from_isize(LOADW, -1, 0, 0, 1, 1, 0, 1),
Instruction::large_from_isize(TERMINATE.global_opcode(), 0, 0, 0, 0, 0, 0, 0),
];
let bus = ProgramBus::new(READ_INSTRUCTION_BUS);
let program = Program::from_instructions(&instructions);
let program_air = ProgramAir::new(bus);
let execution_frequencies = vec![1; instructions.len()];
let engine = test_cpu_engine();
let exe = VmExe::new(program);
let cached_trace = generate_cached_trace(&exe.program);
let (commitment, pcs_data) = TraceCommitter::commit(engine.device(), &[&cached_trace]).unwrap();
let cached = CommittedTraceData {
commitment,
data: Arc::new(pcs_data),
trace: cached_trace,
};
let chip = ProgramChip {
filtered_exec_frequencies: execution_frequencies.clone(),
cached: Some(cached),
_marker: std::marker::PhantomData,
};
let ctx = chip.generate_proving_ctx(());
let counter_air = DummyInteractionAir::new(7, true, bus.inner.index);
let mut program_rows = vec![];
for (pc_idx, instruction) in instructions.iter().enumerate() {
program_rows.extend(vec![
BabyBear::from_u32(execution_frequencies[pc_idx]),
BabyBear::from_usize(pc_idx * DEFAULT_PC_STEP as usize),
instruction.opcode.to_field(),
instruction.a,
instruction.b,
instruction.c,
instruction.d,
instruction.e,
]);
}
let width = 8;
let mut counter_trace = RowMajorMatrix::new(program_rows, width);
counter_trace.row_mut(1)[1] = BabyBear::ZERO;
let rows_used = Matrix::height(&counter_trace);
let height = rows_used.next_power_of_two();
counter_trace.values.resize(height * width, BabyBear::ZERO);
let result = engine.run_test(
any_air_arc_vec!(program_air, counter_air),
vec![ctx, AirProvingContext::simple_no_pis(counter_trace)],
);
assert!(matches!(result, Err(StarkTestError::Prover(_))));
}
#[test]
fn test_program_with_undefined_instructions() {
let n = 2;
let instructions = vec![
Some(Instruction::large_from_isize(STOREW, n, 0, 0, 0, 1, 0, 1)),
Some(Instruction::large_from_isize(STOREW, 1, 1, 0, 0, 1, 0, 1)),
Some(Instruction::from_isize(
BEQ,
0,
n,
3 * DEFAULT_PC_STEP as isize,
1,
0,
)),
None,
None,
Some(Instruction::from_isize(
TERMINATE.global_opcode(),
0,
0,
0,
0,
0,
)),
];
let program = Program::new_without_debug_infos_with_option(&instructions, 0);
interaction_test(program, vec![0, 2, 5]);
}