#![no_std]
#[macro_use]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use alloc::vec::Vec;
use core::borrow::Borrow;
use miden_core::{
WORD_SIZE, Word,
field::ExtensionField,
precompile::PrecompileTranscriptState,
program::{MIN_STACK_DEPTH, ProgramInfo, StackInputs, StackOutputs},
};
use miden_crypto::stark::air::{
ReducedAuxValues, ReductionError, VarLenPublicInputs, WindowAccess,
};
pub mod config;
mod constraints;
pub mod trace;
use trace::{AUX_TRACE_WIDTH, MainTraceRow, TRACE_WIDTH};
mod export {
pub use miden_core::{
Felt,
serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
utils::ToElements,
};
pub use miden_crypto::stark::{
air::{
AirBuilder, AirWitness, AuxBuilder, BaseAir, ExtensionBuilder, LiftedAir,
LiftedAirBuilder, PermutationAirBuilder,
},
debug,
};
}
pub use export::*;
#[derive(Debug, Clone)]
pub struct PublicInputs {
program_info: ProgramInfo,
stack_inputs: StackInputs,
stack_outputs: StackOutputs,
pc_transcript_state: PrecompileTranscriptState,
}
impl PublicInputs {
pub fn new(
program_info: ProgramInfo,
stack_inputs: StackInputs,
stack_outputs: StackOutputs,
pc_transcript_state: PrecompileTranscriptState,
) -> Self {
Self {
program_info,
stack_inputs,
stack_outputs,
pc_transcript_state,
}
}
pub fn stack_inputs(&self) -> StackInputs {
self.stack_inputs
}
pub fn stack_outputs(&self) -> StackOutputs {
self.stack_outputs
}
pub fn program_info(&self) -> ProgramInfo {
self.program_info.clone()
}
pub fn pc_transcript_state(&self) -> PrecompileTranscriptState {
self.pc_transcript_state
}
pub fn to_air_inputs(&self) -> (Vec<Felt>, Vec<Felt>) {
let mut public_values = Vec::with_capacity(NUM_PUBLIC_VALUES);
public_values.extend_from_slice(self.program_info.program_hash().as_elements());
public_values.extend_from_slice(self.stack_inputs.as_ref());
public_values.extend_from_slice(self.stack_outputs.as_ref());
public_values.extend_from_slice(self.pc_transcript_state.as_ref());
let kernel_felts: Vec<Felt> =
Word::words_as_elements(self.program_info.kernel_procedures()).to_vec();
(public_values, kernel_felts)
}
pub fn to_elements(&self) -> Vec<Felt> {
let mut result = self.program_info.to_elements();
result.extend_from_slice(self.stack_inputs.as_ref());
result.extend_from_slice(self.stack_outputs.as_ref());
result.extend_from_slice(self.pc_transcript_state.as_ref());
result
}
}
impl Serializable for PublicInputs {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.program_info.write_into(target);
self.stack_inputs.write_into(target);
self.stack_outputs.write_into(target);
self.pc_transcript_state.write_into(target);
}
}
impl Deserializable for PublicInputs {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let program_info = ProgramInfo::read_from(source)?;
let stack_inputs = StackInputs::read_from(source)?;
let stack_outputs = StackOutputs::read_from(source)?;
let pc_transcript_state = PrecompileTranscriptState::read_from(source)?;
Ok(PublicInputs {
program_info,
stack_inputs,
stack_outputs,
pc_transcript_state,
})
}
}
pub const NUM_PUBLIC_VALUES: usize = WORD_SIZE + MIN_STACK_DEPTH + MIN_STACK_DEPTH + WORD_SIZE;
const PV_PROGRAM_HASH: usize = 0;
const PV_TRANSCRIPT_STATE: usize = NUM_PUBLIC_VALUES - WORD_SIZE;
#[derive(Copy, Clone, Debug, Default)]
pub struct ProcessorAir;
impl BaseAir<Felt> for ProcessorAir {
fn width(&self) -> usize {
TRACE_WIDTH
}
fn num_public_values(&self) -> usize {
NUM_PUBLIC_VALUES
}
}
impl<EF: ExtensionField<Felt>> LiftedAir<Felt, EF> for ProcessorAir {
fn periodic_columns(&self) -> Vec<Vec<Felt>> {
let mut cols = constraints::chiplets::hasher::periodic_columns();
cols.extend(constraints::chiplets::bitwise::periodic_columns());
cols
}
fn num_randomness(&self) -> usize {
trace::AUX_TRACE_RAND_CHALLENGES
}
fn aux_width(&self) -> usize {
AUX_TRACE_WIDTH
}
fn num_aux_values(&self) -> usize {
AUX_TRACE_WIDTH
}
fn num_var_len_public_inputs(&self) -> usize {
1
}
fn reduced_aux_values(
&self,
aux_values: &[EF],
challenges: &[EF],
public_values: &[Felt],
var_len_public_inputs: VarLenPublicInputs<'_, Felt>,
) -> Result<ReducedAuxValues<EF>, ReductionError>
where
EF: ExtensionField<Felt>,
{
let p1 = aux_values[trace::DECODER_AUX_TRACE_OFFSET];
let p2 = aux_values[trace::DECODER_AUX_TRACE_OFFSET + 1];
let p3 = aux_values[trace::DECODER_AUX_TRACE_OFFSET + 2];
let s_aux = aux_values[trace::STACK_AUX_TRACE_OFFSET];
let b_range = aux_values[trace::RANGE_CHECK_AUX_TRACE_OFFSET];
let b_hash_kernel = aux_values[trace::HASH_KERNEL_VTABLE_AUX_TRACE_OFFSET];
let b_chiplets = aux_values[trace::CHIPLETS_BUS_AUX_TRACE_OFFSET];
let v_wiring = aux_values[trace::ACE_CHIPLET_WIRING_BUS_OFFSET];
if public_values.len() != NUM_PUBLIC_VALUES {
return Err(format!(
"expected {} public values, got {}",
NUM_PUBLIC_VALUES,
public_values.len()
)
.into());
}
let program_hash: Word = public_values[PV_PROGRAM_HASH..PV_PROGRAM_HASH + WORD_SIZE]
.try_into()
.map_err(|_| -> ReductionError { "invalid program hash slice".into() })?;
let pc_transcript_state: PrecompileTranscriptState = public_values
[PV_TRANSCRIPT_STATE..PV_TRANSCRIPT_STATE + WORD_SIZE]
.try_into()
.map_err(|_| -> ReductionError { "invalid transcript state slice".into() })?;
let challenges = trace::Challenges::<EF>::new(challenges[0], challenges[1]);
let ph_msg = program_hash_message(&challenges, &program_hash);
let (default_transcript_msg, final_transcript_msg) =
transcript_messages(&challenges, pc_transcript_state);
let kernel_reduced = kernel_reduced_from_var_len(&challenges, var_len_public_inputs)?;
let expected_denom = final_transcript_msg * kernel_reduced;
let expected_denom_inv = expected_denom
.try_inverse()
.ok_or_else(|| -> ReductionError { "zero denominator in reduced_aux_values".into() })?;
let prod = p1
* p2
* p3
* s_aux
* b_hash_kernel
* b_chiplets
* ph_msg
* default_transcript_msg
* expected_denom_inv;
let sum = b_range + v_wiring;
Ok(ReducedAuxValues { prod, sum })
}
fn eval<AB: LiftedAirBuilder<F = Felt>>(&self, builder: &mut AB) {
use crate::constraints;
let main = builder.main();
let local = main.current_slice();
let next = main.next_slice();
let local: &MainTraceRow<AB::Var> = (*local).borrow();
let next: &MainTraceRow<AB::Var> = (*next).borrow();
constraints::enforce_main(builder, local, next);
constraints::enforce_bus(builder, local, next);
constraints::public_inputs::enforce_main(builder, local);
}
}
fn program_hash_message<EF: ExtensionField<Felt>>(
challenges: &trace::Challenges<EF>,
program_hash: &Word,
) -> EF {
challenges.encode([
Felt::ZERO, program_hash[0],
program_hash[1],
program_hash[2],
program_hash[3],
Felt::ZERO, Felt::ZERO, ])
}
fn transcript_messages<EF: ExtensionField<Felt>>(
challenges: &trace::Challenges<EF>,
final_state: PrecompileTranscriptState,
) -> (EF, EF) {
let encode = |state: PrecompileTranscriptState| {
let cap: &[Felt] = state.as_ref();
challenges.encode([
Felt::from_u8(trace::LOG_PRECOMPILE_LABEL),
cap[0],
cap[1],
cap[2],
cap[3],
])
};
(encode(PrecompileTranscriptState::default()), encode(final_state))
}
fn kernel_proc_message<EF: ExtensionField<Felt>>(
challenges: &trace::Challenges<EF>,
digest: &Word,
) -> EF {
challenges.encode([
trace::chiplets::kernel_rom::KERNEL_PROC_INIT_LABEL,
digest[0],
digest[1],
digest[2],
digest[3],
])
}
fn kernel_reduced_from_var_len<EF: ExtensionField<Felt>>(
challenges: &trace::Challenges<EF>,
var_len_public_inputs: VarLenPublicInputs<'_, Felt>,
) -> Result<EF, ReductionError> {
if var_len_public_inputs.len() != 1 {
return Err(format!(
"expected 1 var-len public input slice, got {}",
var_len_public_inputs.len()
)
.into());
}
let kernel_felts = var_len_public_inputs[0];
if !kernel_felts.len().is_multiple_of(WORD_SIZE) {
return Err(format!(
"kernel digest felts length {} is not a multiple of {}",
kernel_felts.len(),
WORD_SIZE
)
.into());
}
let mut acc = EF::ONE;
for digest in kernel_felts.chunks_exact(WORD_SIZE) {
let word: Word = [digest[0], digest[1], digest[2], digest[3]].into();
acc *= kernel_proc_message(challenges, &word);
}
Ok(acc)
}