use p3_air::AirBuilder;
use p3_field::AbstractField;
use crate::air::{BaseAirBuilder, PublicValues, WordAirBuilder};
use crate::cpu::air::{Word, POSEIDON_NUM_WORDS, PV_DIGEST_NUM_WORDS};
use crate::cpu::columns::{CpuCols, OpcodeSelectorCols};
use crate::memory::MemoryCols;
use crate::operations::IsZeroOperation;
use crate::runtime::SyscallCode;
use crate::stark::{CpuChip, SP1AirBuilder};
impl CpuChip {
pub(crate) fn is_ecall_instruction<AB: SP1AirBuilder>(
&self,
opcode_selectors: &OpcodeSelectorCols<AB::Var>,
) -> AB::Expr {
opcode_selectors.is_ecall.into()
}
pub(crate) fn eval_ecall<AB: SP1AirBuilder>(&self, builder: &mut AB, local: &CpuCols<AB::Var>) {
let ecall_cols = local.opcode_specific_columns.ecall();
let is_ecall_instruction = self.is_ecall_instruction::<AB>(&local.selectors);
let syscall_code = local.op_a_access.prev_value();
let syscall_id = syscall_code[0];
let send_to_table = syscall_code[1];
builder.assert_eq(
local.ecall_mul_send_to_table,
send_to_table * is_ecall_instruction.clone(),
);
builder.send_syscall(
local.shard,
local.channel,
local.clk,
ecall_cols.syscall_nonce,
syscall_id,
local.op_b_val().reduce::<AB>(),
local.op_c_val().reduce::<AB>(),
local.ecall_mul_send_to_table,
);
let is_enter_unconstrained = {
IsZeroOperation::<AB::F>::eval(
builder,
syscall_id
- AB::Expr::from_canonical_u32(SyscallCode::ENTER_UNCONSTRAINED.syscall_id()),
ecall_cols.is_enter_unconstrained,
is_ecall_instruction.clone(),
);
ecall_cols.is_enter_unconstrained.result
};
let is_hint_len = {
IsZeroOperation::<AB::F>::eval(
builder,
syscall_id - AB::Expr::from_canonical_u32(SyscallCode::HINT_LEN.syscall_id()),
ecall_cols.is_hint_len,
is_ecall_instruction.clone(),
);
ecall_cols.is_hint_len.result
};
let zero_word = Word::<AB::F>::from(0);
builder
.when(is_ecall_instruction.clone() * is_enter_unconstrained)
.assert_word_eq(local.op_a_val(), zero_word);
builder
.when(is_ecall_instruction.clone())
.when_not(is_enter_unconstrained + is_hint_len)
.assert_word_eq(local.op_a_val(), local.op_a_access.prev_value);
}
pub(crate) fn eval_commit<AB: SP1AirBuilder>(
&self,
builder: &mut AB,
local: &CpuCols<AB::Var>,
commit_digest: [Word<AB::Expr>; PV_DIGEST_NUM_WORDS],
deferred_proofs_digest: [AB::Expr; POSEIDON_NUM_WORDS],
) {
let (is_commit, is_commit_deferred_proofs) =
self.get_is_commit_related_syscall(builder, local);
let ecall_columns = local.opcode_specific_columns.ecall();
let mut bitmap_sum = AB::Expr::zero();
for bit in ecall_columns.index_bitmap.iter() {
builder.when(local.selectors.is_ecall).assert_bool(*bit);
bitmap_sum += (*bit).into();
}
builder
.when(
local.selectors.is_ecall * (is_commit.clone() + is_commit_deferred_proofs.clone()),
)
.assert_one(bitmap_sum.clone());
builder
.when(
local.selectors.is_ecall
* (AB::Expr::one() - (is_commit.clone() + is_commit_deferred_proofs.clone())),
)
.assert_zero(bitmap_sum);
for (i, bit) in ecall_columns.index_bitmap.iter().enumerate() {
builder.when(*bit * local.selectors.is_ecall).assert_eq(
local.op_b_access.prev_value()[0],
AB::Expr::from_canonical_u32(i as u32),
);
}
for i in 0..3 {
builder
.when(
local.selectors.is_ecall
* (is_commit.clone() + is_commit_deferred_proofs.clone()),
)
.assert_eq(
local.op_b_access.prev_value()[i + 1],
AB::Expr::from_canonical_u32(0),
);
}
let expected_pv_digest_word =
builder.index_word_array(&commit_digest, &ecall_columns.index_bitmap);
let digest_word = local.op_c_access.prev_value();
builder
.when(local.selectors.is_ecall * is_commit)
.assert_word_eq(expected_pv_digest_word, *digest_word);
let expected_deferred_proofs_digest_word =
builder.index_array(&deferred_proofs_digest, &ecall_columns.index_bitmap);
builder
.when(local.selectors.is_ecall * is_commit_deferred_proofs)
.assert_eq(
expected_deferred_proofs_digest_word,
digest_word.reduce::<AB>(),
);
}
pub(crate) fn eval_halt_unimpl<AB: SP1AirBuilder>(
&self,
builder: &mut AB,
local: &CpuCols<AB::Var>,
next: &CpuCols<AB::Var>,
public_values: &PublicValues<Word<AB::Expr>, AB::Expr>,
) {
let is_halt = self.get_is_halt_syscall(builder, local);
builder
.when_transition()
.when(is_halt.clone() + local.selectors.is_unimpl)
.assert_zero(next.is_real);
builder.when(is_halt.clone()).assert_zero(local.next_pc);
builder.when(is_halt.clone()).assert_eq(
local.op_b_access.value().reduce::<AB>(),
public_values.exit_code.clone(),
);
}
pub(crate) fn get_is_halt_syscall<AB: SP1AirBuilder>(
&self,
builder: &mut AB,
local: &CpuCols<AB::Var>,
) -> AB::Expr {
let ecall_cols = local.opcode_specific_columns.ecall();
let is_ecall_instruction = self.is_ecall_instruction::<AB>(&local.selectors);
let syscall_code = local.op_a_access.prev_value();
let syscall_id = syscall_code[0];
let is_halt = {
IsZeroOperation::<AB::F>::eval(
builder,
syscall_id - AB::Expr::from_canonical_u32(SyscallCode::HALT.syscall_id()),
ecall_cols.is_halt,
is_ecall_instruction.clone(),
);
ecall_cols.is_halt.result
};
is_halt * is_ecall_instruction
}
pub(crate) fn get_is_commit_related_syscall<AB: SP1AirBuilder>(
&self,
builder: &mut AB,
local: &CpuCols<AB::Var>,
) -> (AB::Expr, AB::Expr) {
let ecall_cols = local.opcode_specific_columns.ecall();
let is_ecall_instruction = self.is_ecall_instruction::<AB>(&local.selectors);
let syscall_code = local.op_a_access.prev_value();
let syscall_id = syscall_code[0];
let is_commit = {
IsZeroOperation::<AB::F>::eval(
builder,
syscall_id - AB::Expr::from_canonical_u32(SyscallCode::COMMIT.syscall_id()),
ecall_cols.is_commit,
is_ecall_instruction.clone(),
);
ecall_cols.is_commit.result
};
let is_commit_deferred_proofs = {
IsZeroOperation::<AB::F>::eval(
builder,
syscall_id
- AB::Expr::from_canonical_u32(
SyscallCode::COMMIT_DEFERRED_PROOFS.syscall_id(),
),
ecall_cols.is_commit_deferred_proofs,
is_ecall_instruction.clone(),
);
ecall_cols.is_commit_deferred_proofs.result
};
(is_commit.into(), is_commit_deferred_proofs.into())
}
pub(crate) fn get_num_extra_ecall_cycles<AB: SP1AirBuilder>(
&self,
local: &CpuCols<AB::Var>,
) -> AB::Expr {
let is_ecall_instruction = self.is_ecall_instruction::<AB>(&local.selectors);
let syscall_code = local.op_a_access.prev_value();
let num_extra_cycles = syscall_code[2];
num_extra_cycles * is_ecall_instruction.clone()
}
}