#![no_std]
#[macro_use]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use alloc::vec::Vec;
use core::borrow::Borrow;
#[cfg(feature = "arbitrary")]
use miden_core::program::Kernel;
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,
};
#[cfg(feature = "arbitrary")]
use proptest::prelude::*;
pub mod ace;
pub mod config;
mod constraints;
pub mod lookup;
pub mod trace;
pub mod logup {
pub use crate::constraints::lookup::{
BusId, MIDEN_MAX_MESSAGE_WIDTH, messages::*, miden_air::NUM_LOGUP_COMMITTED_FINALS,
};
}
use constraints::{
columns::MainCols,
lookup::{
chiplet_air::{ChipletLookupAir, ChipletLookupBuilder},
main_air::{MainLookupAir, MainLookupBuilder},
miden_air::{MIDEN_COLUMN_SHAPE, emit_miden_boundary},
},
};
use logup::{BusId, MIDEN_MAX_MESSAGE_WIDTH, NUM_LOGUP_COMMITTED_FINALS};
use lookup::{
BoundaryBuilder, Challenges, ConstraintLookupBuilder, LookupAir, LookupMessage,
build_logup_aux_trace,
};
use miden_core::utils::RowMajorMatrix;
use trace::TRACE_WIDTH;
mod export {
pub use miden_core::{
Felt,
serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
utils::ToElements,
};
pub use miden_crypto::stark::{
air::{
AirBuilder, AuxBuilder, BaseAir, ExtensionBuilder, LiftedAir, LiftedAirBuilder,
PermutationAirBuilder,
},
debug,
};
pub use miden_lifted_stark::AirWitness;
}
pub use export::*;
pub trait MidenAirBuilder: LiftedAirBuilder<F = Felt> {}
impl<T: LiftedAirBuilder<F = Felt>> MidenAirBuilder for T {}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
all(feature = "arbitrary", test),
miden_test_serde_macros::serde_test(binary_serde(true), serde_test(false))
)]
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
}
}
#[cfg(feature = "arbitrary")]
impl Arbitrary for PublicInputs {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
fn felt_strategy() -> impl Strategy<Value = Felt> {
any::<u32>().prop_map(Felt::from)
}
fn word_strategy() -> impl Strategy<Value = Word> {
any::<[u32; WORD_SIZE]>().prop_map(|values| Word::new(values.map(Felt::from)))
}
let program_info = word_strategy()
.prop_map(|program_hash| ProgramInfo::new(program_hash, Kernel::default()));
let stack_inputs = proptest::collection::vec(felt_strategy(), 0..=MIN_STACK_DEPTH)
.prop_map(|values| StackInputs::new(&values).expect("generated stack inputs fit"));
let stack_outputs = proptest::collection::vec(felt_strategy(), 0..=MIN_STACK_DEPTH)
.prop_map(|values| StackOutputs::new(&values).expect("generated stack outputs fit"));
(program_info, stack_inputs, stack_outputs, word_strategy())
.prop_map(|(program_info, stack_inputs, stack_outputs, pc_transcript_state)| {
Self::new(program_info, stack_inputs, stack_outputs, pc_transcript_state)
})
.boxed()
}
}
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;
pub const LOGUP_AUX_TRACE_WIDTH: usize = 7;
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>> {
constraints::chiplets::columns::PeriodicCols::periodic_columns()
}
fn num_randomness(&self) -> usize {
trace::AUX_TRACE_RAND_CHALLENGES
}
fn aux_width(&self) -> usize {
LOGUP_AUX_TRACE_WIDTH
}
fn num_aux_values(&self) -> usize {
NUM_LOGUP_COMMITTED_FINALS
}
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>,
{
if public_values.len() != NUM_PUBLIC_VALUES {
return Err(format!(
"expected {} public values, got {}",
NUM_PUBLIC_VALUES,
public_values.len()
)
.into());
}
if var_len_public_inputs.len() != 1 {
return Err(format!(
"expected 1 var-len public input slice, got {}",
var_len_public_inputs.len()
)
.into());
}
if !var_len_public_inputs[0].len().is_multiple_of(WORD_SIZE) {
return Err(format!(
"kernel digest felts length {} is not a multiple of {}",
var_len_public_inputs[0].len(),
WORD_SIZE
)
.into());
}
let challenges = Challenges::<EF>::new(
challenges[0],
challenges[1],
MIDEN_MAX_MESSAGE_WIDTH,
BusId::COUNT,
);
let mut reducer = ReduceBoundaryBuilder {
challenges: &challenges,
public_values,
var_len_public_inputs,
sum: EF::ZERO,
error: None,
};
emit_miden_boundary(&mut reducer);
let total_correction = reducer.finalize()?;
for unused_aux in aux_values.iter().skip(1) {
if !unused_aux.is_zero() {
return Err("padding aux value is non-zero".into());
}
}
let aux_sum: EF = aux_values.iter().copied().sum();
Ok(ReducedAuxValues {
prod: EF::ONE,
sum: aux_sum + total_correction,
})
}
fn eval<AB: MidenAirBuilder>(&self, builder: &mut AB) {
let main = builder.main();
let local = main.current_slice();
let next = main.next_slice();
let local: &MainCols<AB::Var> = (*local).borrow();
let next: &MainCols<AB::Var> = (*next).borrow();
let selectors =
constraints::chiplets::selectors::build_chiplet_selectors(builder, local, next);
let op_flags =
constraints::op_flags::OpFlags::new(&local.decoder, &local.stack, &next.decoder);
constraints::enforce_main(builder, local, next, &selectors, &op_flags);
{
let mut lb = ConstraintLookupBuilder::new(builder, self);
<Self as LookupAir<_>>::eval(self, &mut lb);
}
constraints::public_inputs::enforce_main(builder, local);
}
fn log_quotient_degree(&self) -> usize
where
Self: Sized,
{
3
}
}
impl<LB> LookupAir<LB> for ProcessorAir
where
LB: MainLookupBuilder + ChipletLookupBuilder,
{
fn num_columns(&self) -> usize {
MIDEN_COLUMN_SHAPE.len()
}
fn column_shape(&self) -> &[usize] {
&MIDEN_COLUMN_SHAPE
}
fn max_message_width(&self) -> usize {
MIDEN_MAX_MESSAGE_WIDTH
}
fn num_bus_ids(&self) -> usize {
BusId::COUNT
}
fn eval(&self, builder: &mut LB) {
MainLookupAir.eval(builder);
ChipletLookupAir.eval(builder);
}
fn eval_boundary<B>(&self, boundary: &mut B)
where
B: BoundaryBuilder<F = LB::F, EF = LB::EF>,
{
emit_miden_boundary(boundary);
}
}
impl<EF> AuxBuilder<Felt, EF> for ProcessorAir
where
EF: ExtensionField<Felt>,
{
fn build_aux_trace(
&self,
main: &RowMajorMatrix<Felt>,
challenges: &[EF],
) -> (RowMajorMatrix<EF>, Vec<EF>) {
let (aux_trace, mut committed) = build_logup_aux_trace(self, main, challenges);
debug_assert_eq!(
committed.len(),
1,
"build_logup_aux_trace should return exactly one real committed final"
);
committed.push(EF::ZERO);
(aux_trace, committed)
}
}
struct ReduceBoundaryBuilder<'a, EF: ExtensionField<Felt>> {
challenges: &'a Challenges<EF>,
public_values: &'a [Felt],
var_len_public_inputs: VarLenPublicInputs<'a, Felt>,
sum: EF,
error: Option<ReductionError>,
}
impl<'a, EF: ExtensionField<Felt>> ReduceBoundaryBuilder<'a, EF> {
fn finalize(self) -> Result<EF, ReductionError> {
match self.error {
Some(err) => Err(err),
None => Ok(self.sum),
}
}
}
impl<'a, EF: ExtensionField<Felt>> BoundaryBuilder for ReduceBoundaryBuilder<'a, EF> {
type F = Felt;
type EF = EF;
fn public_values(&self) -> &[Felt] {
self.public_values
}
fn var_len_public_inputs(&self) -> &[&[Felt]] {
self.var_len_public_inputs
}
fn insert<M>(&mut self, _name: &'static str, multiplicity: Felt, msg: M)
where
M: LookupMessage<Felt, EF>,
{
if self.error.is_some() {
return;
}
match msg.encode(self.challenges).try_inverse() {
Some(inv) => self.sum += inv * multiplicity,
None => {
self.error = Some("LogUp boundary denominator was zero".into());
},
}
}
}