use std::{format, string::String};
use miden_core::{Felt, Word};
use miden_crypto::stark::QuotientRecompositionInputs;
use crate::AceError;
#[derive(Clone, Debug)]
pub struct MasmConstraintsEvalConfig<'a> {
pub generated_by: &'a str,
pub layout_module: &'a str,
pub num_inputs: usize,
pub num_eval_gates: usize,
pub stream_len: usize,
pub shuffle_prefix_len: usize,
pub max_cycle_len_log: u32,
pub registry_depth: usize,
pub order_tag_count: usize,
pub num_airs: usize,
pub quotient_inputs: QuotientRecompositionInputs<Felt>,
pub common_commitment: Word,
}
pub fn render_masm_constraints_eval(
config: &MasmConstraintsEvalConfig<'_>,
) -> Result<String, AceError> {
if !config.stream_len.is_multiple_of(8) {
return Err(AceError::InvalidInputLayout {
message: "ACE stream must be 8-felt aligned".into(),
});
}
if !config.shuffle_prefix_len.is_multiple_of(8)
|| config.shuffle_prefix_len >= config.stream_len
{
return Err(AceError::InvalidInputLayout {
message: "ACE shuffle prefix must be a proper 8-felt-aligned stream prefix".into(),
});
}
let prefix_rows = config.shuffle_prefix_len / 8;
let common_rows = (config.stream_len - config.shuffle_prefix_len) / 8;
let common_commitment = config.common_commitment;
let quotient = config.quotient_inputs;
Ok(format!(
concat!(
"# GENERATED by `{generated_by}` — do not edit by hand.\n",
"use miden::core::crypto::hashes::poseidon2\n",
"use miden::core::stark::constants\n",
"use miden::core::stark::constraints_eval_inputs\n",
"use {layout_module}\n\n",
"# CONSTANTS\n",
"# =================================================================================================\n\n",
"# Number of READ variables (inputs + constants) for the constraint evaluation circuit.\n",
"const NUM_INPUTS_CIRCUIT = {num_inputs}\n\n",
"# Number of evaluation gates in the constraint evaluation circuit\n",
"const NUM_EVAL_GATES_CIRCUIT = {num_eval_gates}\n\n",
"# Max cycle length for periodic columns\n",
"const MAX_CYCLE_LEN_LOG = {max_cycle_len_log}\n\n",
"# Depth of the ACE circuit registry tree.\n",
"const ACE_REGISTRY_DEPTH = {registry_depth}\n\n",
"# Number of valid proof-order tags (n! for n AIRs); higher slots are registry\n",
"# padding and must never be opened.\n",
"const ORDER_TAG_COUNT = {order_tag_count}\n\n",
"# Number of AIR instances in the relation.\n",
"const NUM_AIRS = {num_airs}\n\n",
"# Number of 8-felt blocks in each authenticated ACE circuit segment.\n",
"const ACE_PREFIX_BLOCKS = {prefix_rows}\n",
"const ACE_COMMON_BLOCKS = {common_rows}\n\n",
"# Quotient recomposition inputs derived from the circuit's quotient arity and the\n",
"# relation's PCS configuration. QUOTIENT_SHIFT_RATIO depends on arity;\n",
"# QUOTIENT_FIRST_SHIFT depends on the canonical LDE shift and blowup; and\n",
"# QUOTIENT_FIRST_WEIGHT depends on both.\n",
"const QUOTIENT_SHIFT_RATIO = {quotient_shift_ratio}\n",
"const QUOTIENT_FIRST_SHIFT = {quotient_first_shift}\n",
"const QUOTIENT_FIRST_WEIGHT = {quotient_first_weight}\n\n",
"# Poseidon2 digest of the order-invariant common section of the ACE circuit stream\n",
"# (common ops + root padding). The registry leaf for each ORDER_TAG is\n",
"# merge(H(constants | shuffle ops), ACE_COMMON_COMMITMENT).\n",
"const ACE_COMMON_COMMITMENT_0 = {common_commitment_0}\n",
"const ACE_COMMON_COMMITMENT_1 = {common_commitment_1}\n",
"const ACE_COMMON_COMMITMENT_2 = {common_commitment_2}\n",
"const ACE_COMMON_COMMITMENT_3 = {common_commitment_3}\n\n",
"# ERRORS\n",
"# =================================================================================================\n\n",
"const ERR_CIRCUIT_COMMITMENT_MISMATCH = \"merged ACE circuit segment digests do not match the registry commitment\"\n\n",
"const ERR_COMMON_SECTION_MISMATCH = \"common ACE circuit section does not match the compiled-in digest\"\n\n",
"# CONSTRAINT EVALUATION CHECKER\n",
"# =================================================================================================\n\n",
"#! Executes the constraints evaluation check for the proof order selected by ORDER_TAG.\n",
"#!\n",
"#! Inputs: []\n",
"#! Outputs: []\n",
"pub proc execute_constraint_evaluation_check()\n",
" push.ORDER_TAG_COUNT exec.constants::assert_valid_order_tag\n\n",
" push.QUOTIENT_SHIFT_RATIO\n",
" push.QUOTIENT_FIRST_SHIFT\n",
" push.QUOTIENT_FIRST_WEIGHT\n",
" exec.layout::auxiliary_ace_inputs_ptr\n",
" exec.constants::air_trace_length_logs_ptr\n",
" push.NUM_AIRS\n",
" push.MAX_CYCLE_LEN_LOG\n",
" exec.constraints_eval_inputs::set_up_auxiliary_inputs_ace\n\n",
" exec.load_and_authenticate_ace_circuit\n\n",
" push.NUM_EVAL_GATES_CIRCUIT\n",
" push.NUM_INPUTS_CIRCUIT\n",
" exec.constants::public_inputs_address_ptr mem_load\n",
" eval_circuit\n",
" drop drop drop\n",
"end\n\n",
"#! Loads and authenticates the ACE circuit selected by ORDER_TAG.\n",
"#!\n",
"#! The circuit stream is factored into two adv_pipe-aligned segments: a per-order\n",
"#! prefix [constants | shuffle ops] and an order-invariant common section\n",
"#! [common ops | root padding]. Both are hashed separately; the common digest is\n",
"#! pinned to the compiled-in ACE_COMMON_COMMITMENT, and the registry leaf\n",
"#! selected by ORDER_TAG must equal\n",
"#! merge(PREFIX_COMMITMENT, ACE_COMMON_COMMITMENT).\n",
"proc load_and_authenticate_ace_circuit\n",
" exec.load_ace_registry_commitment\n",
" # => [LEAF]\n",
" adv.push_mapval\n",
" exec.layout::ace_circuit_stream_ptr\n",
" padw padw padw\n",
" # => [ZERO, ZERO, ZERO, ptr, LEAF]\n",
" repeat.ACE_PREFIX_BLOCKS\n",
" adv_pipe\n",
" exec.poseidon2::permute\n",
" end\n",
" exec.poseidon2::squeeze_digest\n",
" # => [PREFIX_COMMITMENT, ptr, LEAF]\n",
" movup.4\n",
" # => [ptr, PREFIX_COMMITMENT, LEAF]\n",
" padw padw padw\n",
" repeat.ACE_COMMON_BLOCKS\n",
" adv_pipe\n",
" exec.poseidon2::permute\n",
" end\n",
" exec.poseidon2::squeeze_digest\n",
" # => [COMMON_COMMITMENT, ptr, PREFIX_COMMITMENT, LEAF]\n",
" movup.4 drop\n",
" # => [COMMON_COMMITMENT, PREFIX_COMMITMENT, LEAF]\n",
" dupw push.ACE_COMMON_COMMITMENT_3.ACE_COMMON_COMMITMENT_2.ACE_COMMON_COMMITMENT_1.ACE_COMMON_COMMITMENT_0\n",
" assert_eqw.err=ERR_COMMON_SECTION_MISMATCH\n",
" # => [COMMON_COMMITMENT, PREFIX_COMMITMENT, LEAF]\n",
" swapw\n",
" # => [PREFIX_COMMITMENT, COMMON_COMMITMENT, LEAF]\n",
" exec.poseidon2::merge\n",
" # => [CIRCUIT_COMMITMENT, LEAF]\n",
" assert_eqw.err=ERR_CIRCUIT_COMMITMENT_MISMATCH\n",
"end\n\n",
"#! Loads the ACE circuit commitment selected by ORDER_TAG from the registry tree.\n",
"proc load_ace_registry_commitment\n",
" padw exec.constants::ace_registry_root_ptr mem_loadw_le\n",
" exec.constants::get_order_tag\n",
" push.ACE_REGISTRY_DEPTH\n",
" mtree_get\n",
" swapw dropw\n",
"end\n",
),
generated_by = config.generated_by,
layout_module = config.layout_module,
num_inputs = config.num_inputs,
num_eval_gates = config.num_eval_gates,
max_cycle_len_log = config.max_cycle_len_log,
registry_depth = config.registry_depth,
order_tag_count = config.order_tag_count,
num_airs = config.num_airs,
quotient_shift_ratio = quotient.shift_ratio.as_canonical_u64(),
quotient_first_shift = quotient.first_shift.as_canonical_u64(),
quotient_first_weight = quotient.first_weight.as_canonical_u64(),
prefix_rows = prefix_rows,
common_rows = common_rows,
common_commitment_0 = common_commitment[0].as_canonical_u64(),
common_commitment_1 = common_commitment[1].as_canonical_u64(),
common_commitment_2 = common_commitment[2].as_canonical_u64(),
common_commitment_3 = common_commitment[3].as_canonical_u64(),
))
}