1use std::{format, string::String};
4
5use miden_core::{Felt, Word};
6use miden_crypto::stark::QuotientRecompositionInputs;
7
8use crate::AceError;
9
10#[derive(Clone, Debug)]
12pub struct MasmConstraintsEvalConfig<'a> {
13 pub generated_by: &'a str,
15 pub layout_module: &'a str,
18 pub num_inputs: usize,
20 pub num_eval_gates: usize,
22 pub stream_len: usize,
24 pub shuffle_prefix_len: usize,
26 pub max_cycle_len_log: u32,
28 pub registry_depth: usize,
30 pub order_tag_count: usize,
32 pub num_airs: usize,
34 pub quotient_inputs: QuotientRecompositionInputs<Felt>,
36 pub common_commitment: Word,
38}
39
40pub fn render_masm_constraints_eval(
47 config: &MasmConstraintsEvalConfig<'_>,
48) -> Result<String, AceError> {
49 if !config.stream_len.is_multiple_of(8) {
50 return Err(AceError::InvalidInputLayout {
51 message: "ACE stream must be 8-felt aligned".into(),
52 });
53 }
54 if !config.shuffle_prefix_len.is_multiple_of(8)
55 || config.shuffle_prefix_len >= config.stream_len
56 {
57 return Err(AceError::InvalidInputLayout {
58 message: "ACE shuffle prefix must be a proper 8-felt-aligned stream prefix".into(),
59 });
60 }
61
62 let prefix_rows = config.shuffle_prefix_len / 8;
63 let common_rows = (config.stream_len - config.shuffle_prefix_len) / 8;
64 let common_commitment = config.common_commitment;
65 let quotient = config.quotient_inputs;
66
67 Ok(format!(
68 concat!(
69 "# GENERATED by `{generated_by}` — do not edit by hand.\n",
70 "use miden::core::stark::types\n",
71 "use miden::core::crypto::hashes::poseidon2\n",
72 "use miden::core::stark::constants\n",
73 "use miden::core::stark::constraints_eval_inputs\n",
74 "use {layout_module}\n\n",
75 "# CONSTANTS\n",
76 "# =================================================================================================\n\n",
77 "# Number of READ variables (inputs + constants) for the constraint evaluation circuit.\n",
78 "const NUM_INPUTS_CIRCUIT = {num_inputs}\n\n",
79 "# Number of evaluation gates in the constraint evaluation circuit\n",
80 "const NUM_EVAL_GATES_CIRCUIT = {num_eval_gates}\n\n",
81 "# Max cycle length for periodic columns\n",
82 "const MAX_CYCLE_LEN_LOG = {max_cycle_len_log}\n\n",
83 "# Depth of the ACE circuit registry tree.\n",
84 "const ACE_REGISTRY_DEPTH = {registry_depth}\n\n",
85 "# Number of valid proof-order tags (n! for n AIRs); higher slots are registry\n",
86 "# padding and must never be opened.\n",
87 "const ORDER_TAG_COUNT = {order_tag_count}\n\n",
88 "# Number of AIR instances in the relation.\n",
89 "const NUM_AIRS = {num_airs}\n\n",
90 "# Number of 8-felt blocks in each authenticated ACE circuit segment.\n",
91 "const ACE_PREFIX_BLOCKS = {prefix_rows}\n",
92 "const ACE_COMMON_BLOCKS = {common_rows}\n\n",
93 "# Quotient recomposition inputs derived from the circuit's quotient arity and the\n",
94 "# relation's PCS configuration. QUOTIENT_SHIFT_RATIO depends on arity;\n",
95 "# QUOTIENT_FIRST_SHIFT depends on the canonical LDE shift and blowup; and\n",
96 "# QUOTIENT_FIRST_WEIGHT depends on both.\n",
97 "const QUOTIENT_SHIFT_RATIO = {quotient_shift_ratio}\n",
98 "const QUOTIENT_FIRST_SHIFT = {quotient_first_shift}\n",
99 "const QUOTIENT_FIRST_WEIGHT = {quotient_first_weight}\n\n",
100 "# Poseidon2 digest of the order-invariant common section of the ACE circuit stream\n",
101 "# (common ops + root padding). The registry leaf for each ORDER_TAG is\n",
102 "# merge(H(constants | shuffle ops), ACE_COMMON_COMMITMENT).\n",
103 "const ACE_COMMON_COMMITMENT_0 = {common_commitment_0}\n",
104 "const ACE_COMMON_COMMITMENT_1 = {common_commitment_1}\n",
105 "const ACE_COMMON_COMMITMENT_2 = {common_commitment_2}\n",
106 "const ACE_COMMON_COMMITMENT_3 = {common_commitment_3}\n\n",
107 "# ERRORS\n",
108 "# =================================================================================================\n\n",
109 "const ERR_CIRCUIT_COMMITMENT_MISMATCH = \"merged ACE circuit segment digests do not match the registry commitment\"\n\n",
110 "const ERR_COMMON_SECTION_MISMATCH = \"common ACE circuit section does not match the compiled-in digest\"\n\n",
111 "# CONSTRAINT EVALUATION CHECKER\n",
112 "# =================================================================================================\n\n",
113 "#! Executes the constraints evaluation check for the proof order selected by ORDER_TAG.\n",
114 "#!\n",
115 "#! Inputs: []\n",
116 "#! Outputs: []\n",
117 "pub proc execute_constraint_evaluation_check()\n",
118 " push.ORDER_TAG_COUNT exec.constants::assert_valid_order_tag\n\n",
119 " push.QUOTIENT_SHIFT_RATIO\n",
120 " push.QUOTIENT_FIRST_SHIFT\n",
121 " push.QUOTIENT_FIRST_WEIGHT\n",
122 " exec.layout::auxiliary_ace_inputs_ptr\n",
123 " exec.constants::air_trace_length_logs_ptr\n",
124 " push.NUM_AIRS\n",
125 " push.MAX_CYCLE_LEN_LOG\n",
126 " exec.constraints_eval_inputs::set_up_auxiliary_inputs_ace\n\n",
127 " exec.load_and_authenticate_ace_circuit\n\n",
128 " push.NUM_EVAL_GATES_CIRCUIT\n",
129 " push.NUM_INPUTS_CIRCUIT\n",
130 " exec.constants::public_inputs_address_ptr mem_load\n",
131 " eval_circuit\n",
132 " drop drop drop\n",
133 "end\n\n",
134 "#! Loads and authenticates the ACE circuit selected by ORDER_TAG.\n",
135 "#!\n",
136 "#! The circuit stream is factored into two adv_pipe-aligned segments: a per-order\n",
137 "#! prefix [constants | shuffle ops] and an order-invariant common section\n",
138 "#! [common ops | root padding]. Both are hashed separately; the common digest is\n",
139 "#! pinned to the compiled-in ACE_COMMON_COMMITMENT, and the registry leaf\n",
140 "#! selected by ORDER_TAG must equal\n",
141 "#! merge(PREFIX_COMMITMENT, ACE_COMMON_COMMITMENT).\n",
142 "proc load_and_authenticate_ace_circuit()\n",
143 " exec.load_ace_registry_commitment\n",
144 " # => [LEAF]\n",
145 " adv.push_mapval\n",
146 " exec.layout::ace_circuit_stream_ptr\n",
147 " padw padw padw\n",
148 " # => [ZERO, ZERO, ZERO, ptr, LEAF]\n",
149 " repeat.ACE_PREFIX_BLOCKS\n",
150 " adv_pipe\n",
151 " exec.poseidon2::permute\n",
152 " end\n",
153 " exec.poseidon2::squeeze_digest\n",
154 " # => [PREFIX_COMMITMENT, ptr, LEAF]\n",
155 " movup.4\n",
156 " # => [ptr, PREFIX_COMMITMENT, LEAF]\n",
157 " padw padw padw\n",
158 " repeat.ACE_COMMON_BLOCKS\n",
159 " adv_pipe\n",
160 " exec.poseidon2::permute\n",
161 " end\n",
162 " exec.poseidon2::squeeze_digest\n",
163 " # => [COMMON_COMMITMENT, ptr, PREFIX_COMMITMENT, LEAF]\n",
164 " movup.4 drop\n",
165 " # => [COMMON_COMMITMENT, PREFIX_COMMITMENT, LEAF]\n",
166 " dupw push.ACE_COMMON_COMMITMENT_3.ACE_COMMON_COMMITMENT_2.ACE_COMMON_COMMITMENT_1.ACE_COMMON_COMMITMENT_0\n",
167 " assert_eqw.err=ERR_COMMON_SECTION_MISMATCH\n",
168 " # => [COMMON_COMMITMENT, PREFIX_COMMITMENT, LEAF]\n",
169 " swapw\n",
170 " # => [PREFIX_COMMITMENT, COMMON_COMMITMENT, LEAF]\n",
171 " exec.poseidon2::merge\n",
172 " # => [CIRCUIT_COMMITMENT, LEAF]\n",
173 " assert_eqw.err=ERR_CIRCUIT_COMMITMENT_MISMATCH\n",
174 "end\n\n",
175 "#! Loads the ACE circuit commitment selected by ORDER_TAG from the registry tree.\n",
176 "proc load_ace_registry_commitment() -> types::Digest\n",
177 " padw exec.constants::ace_registry_root_ptr mem_loadw_le\n",
178 " exec.constants::get_order_tag\n",
179 " push.ACE_REGISTRY_DEPTH\n",
180 " mtree_get\n",
181 " swapw dropw\n",
182 "end\n",
183 ),
184 generated_by = config.generated_by,
185 layout_module = config.layout_module,
186 num_inputs = config.num_inputs,
187 num_eval_gates = config.num_eval_gates,
188 max_cycle_len_log = config.max_cycle_len_log,
189 registry_depth = config.registry_depth,
190 order_tag_count = config.order_tag_count,
191 num_airs = config.num_airs,
192 quotient_shift_ratio = quotient.shift_ratio.as_canonical_u64(),
193 quotient_first_shift = quotient.first_shift.as_canonical_u64(),
194 quotient_first_weight = quotient.first_weight.as_canonical_u64(),
195 prefix_rows = prefix_rows,
196 common_rows = common_rows,
197 common_commitment_0 = common_commitment[0].as_canonical_u64(),
198 common_commitment_1 = common_commitment[1].as_canonical_u64(),
199 common_commitment_2 = common_commitment[2].as_canonical_u64(),
200 common_commitment_3 = common_commitment[3].as_canonical_u64(),
201 ))
202}