1use alloc::{str::from_utf8, vec::Vec};
18use core::fmt;
19
20use risc0_core::field::{Elem, ExtElem, Field};
21use serde::{Deserialize, Serialize};
22
23use crate::taps::TapSet;
24
25pub const REGISTER_GROUP_ACCUM: usize = 0;
28pub const REGISTER_GROUP_CODE: usize = 1;
29pub const REGISTER_GROUP_DATA: usize = 2;
30
31const ADAPTER_TRACE_ENABLED: bool = false;
33
34macro_rules! trace_if_enabled {
35 ($($args:tt)*) => {
36 if ADAPTER_TRACE_ENABLED {
37 tracing::trace!($($args)*)
38 }
39 }
40}
41
42#[derive(Clone, Copy, Debug)]
43#[non_exhaustive]
44pub struct MixState<EE: ExtElem> {
45 pub tot: EE,
46 pub mul: EE,
47}
48
49pub trait PolyFp<F: Field> {
50 fn poly_fp(
51 &self,
52 cycle: usize,
53 steps: usize,
54 mix: &[F::ExtElem],
55 args: &[&[F::Elem]],
56 ) -> F::ExtElem;
57}
58
59pub trait PolyExt<F: Field> {
60 fn poly_ext(
61 &self,
62 mix: &F::ExtElem,
63 u: &[F::ExtElem],
64 args: &[&[F::Elem]],
65 ) -> MixState<F::ExtElem>;
66}
67
68pub trait TapsProvider {
69 fn get_taps(&self) -> &'static TapSet<'static>;
70
71 fn accum_size(&self) -> usize {
72 self.get_taps().group_size(REGISTER_GROUP_ACCUM)
73 }
74
75 fn code_size(&self) -> usize {
76 self.get_taps().group_size(REGISTER_GROUP_CODE)
77 }
78
79 fn ctrl_size(&self) -> usize {
80 self.get_taps().group_size(REGISTER_GROUP_CODE)
81 }
82
83 fn data_size(&self) -> usize {
84 self.get_taps().group_size(REGISTER_GROUP_DATA)
85 }
86}
87
88#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
92pub struct ProtocolInfo(pub [u8; 16]);
93
94impl ProtocolInfo {
95 pub fn encode<E: Elem>(&self) -> [E; 16] {
99 let mut elems = [E::ZERO; 16];
100 for (i, elem) in elems.iter_mut().enumerate().take(self.0.len()) {
101 *elem = E::from_u64(self.0[i] as u64);
102 }
103 elems
104 }
105}
106
107impl fmt::Display for ProtocolInfo {
108 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109 match from_utf8(&self.0) {
110 Ok(s) => write!(f, "{s}"),
111 Err(_) => write!(f, "0x{}", hex::encode(self.0)),
112 }
113 }
114}
115
116pub const PROOF_SYSTEM_INFO: ProtocolInfo = ProtocolInfo(*b"RISC0_STARK:v1__");
121
122pub trait CircuitInfo {
123 const CIRCUIT_INFO: ProtocolInfo;
124 const OUTPUT_SIZE: usize;
125 const MIX_SIZE: usize;
126}
127
128pub trait CircuitCoreDef<F: Field>: CircuitInfo + PolyExt<F> + TapsProvider {}
130
131pub type Arg = usize;
132pub type Var = usize;
133
134pub struct PolyExtStepDef {
135 pub block: &'static [PolyExtStep],
136 pub ret: Var,
137}
138
139#[derive(Debug)]
140#[non_exhaustive]
141pub enum PolyExtStep {
142 Const(u32),
143 ConstExt(u32, u32, u32, u32),
144 Get(usize),
145 GetGlobal(Arg, usize),
146 Add(Var, Var),
147 Sub(Var, Var),
148 Mul(Var, Var),
149 True,
150 AndEqz(Var, Var),
151 AndCond(Var, Var, Var),
152}
153
154impl PolyExtStepDef {
155 pub fn step<F: Field>(
156 &self,
157 mix: &F::ExtElem,
158 u: &[F::ExtElem],
159 args: &[&[F::Elem]],
160 ) -> MixState<F::ExtElem> {
161 PolyExtExecutor::<F>::new(self).run(mix, u, args)
162 }
163}
164
165struct PolyExtExecutor<'a, F: Field> {
166 def: &'a PolyExtStepDef,
167 fp_expected: usize,
168 mix_expected: usize,
169 fp_vars: Vec<F::ExtElem>,
170 #[cfg(feature = "circuit_debug")]
171 fp_index: Vec<usize>,
172 mix_vars: Vec<MixState<F::ExtElem>>,
173 #[cfg(feature = "circuit_debug")]
174 mix_index: Vec<usize>,
175}
176
177impl<'a, F: Field> PolyExtExecutor<'a, F> {
178 pub fn new(def: &'a PolyExtStepDef) -> Self {
179 let fp_expected = def.block.len() - (def.ret + 1);
180 let mix_expected = def.ret + 1;
181 Self {
182 def,
183 fp_expected,
184 mix_expected,
185 fp_vars: Vec::with_capacity(fp_expected),
186 #[cfg(feature = "circuit_debug")]
187 fp_index: Vec::with_capacity(fp_expected),
188 mix_vars: Vec::with_capacity(mix_expected),
189 #[cfg(feature = "circuit_debug")]
190 mix_index: Vec::with_capacity(mix_expected),
191 }
192 }
193
194 pub fn run(
195 &mut self,
196 mix: &F::ExtElem,
197 u: &[F::ExtElem],
198 args: &[&[F::Elem]],
199 ) -> MixState<F::ExtElem> {
200 for (idx, op) in self.def.block.iter().enumerate() {
201 self.step(idx, op, mix, u, args);
202 }
203 assert_eq!(
204 self.fp_vars.len(),
205 self.fp_expected,
206 "Miscalculated capacity for fp_vars"
207 );
208 assert_eq!(
209 self.mix_vars.len(),
210 self.mix_expected,
211 "Miscalculated capacity for mix_vars"
212 );
213
214 #[cfg(feature = "circuit_debug")]
215 self.debug(self.def.ret);
216
217 self.mix_vars[self.def.ret]
218 }
219
220 #[cfg(feature = "circuit_debug")]
221 fn debug(&mut self, next: Var) {
222 let op_index = self.mix_index[next];
223 let op = &self.def.block[op_index];
224 tracing::debug!("chain: [m:{next}] {op:?}");
225 match op {
226 PolyExtStep::True => {
227 tracing::debug!("PolyExtStep::True");
228 }
229 PolyExtStep::AndEqz(chain, inner) => {
230 let inner_val = self.fp_vars[*inner];
231 if inner_val != F::ExtElem::ZERO {
233 tracing::debug!("expr: {}", self.debug_expr(*inner));
235 let inner_idx = self.fp_index[*inner];
236 let op = &self.def.block[inner_idx];
237 panic!("eqz failure: [f:{}] {op:?}", *inner);
238 }
239 self.debug(*chain);
240 }
241 PolyExtStep::AndCond(chain, cond, inner) => {
242 let cond = self.fp_vars[*cond];
243 if cond != F::ExtElem::ZERO {
244 tracing::debug!("true conditional");
245 let inner_val = self.fp_vars[*inner];
247 if inner_val != F::ExtElem::ZERO {
249 tracing::debug!("inner != 0");
250 self.debug(*inner);
252 } else {
253 self.debug(*chain)
255 }
256 } else {
257 self.debug(*chain)
259 }
260 }
261 _ => unreachable!(),
262 }
263 }
264
265 #[cfg(feature = "circuit_debug")]
266 fn debug_expr(&self, next: Var) -> String {
267 let op_index = self.fp_index[next];
268 let op = &self.def.block[op_index];
269 match op {
270 PolyExtStep::Const(x) => format!("{x:?}"),
271 PolyExtStep::ConstExt(x0, x1, x2, x3) => format!("({x0:?}, {x1:?}, {x2:?}, {x3:?})"),
272 PolyExtStep::Get(x) => format!("Get({x})"),
273 PolyExtStep::GetGlobal(arg, x) => format!("GetGlobal({arg}, {x})"),
274 PolyExtStep::Add(x, y) => {
275 format!("({} + {})", self.debug_expr(*x), self.debug_expr(*y))
276 }
277 PolyExtStep::Sub(x, y) => {
278 format!("({} - {})", self.debug_expr(*x), self.debug_expr(*y))
279 }
280 PolyExtStep::Mul(x, y) => {
281 format!("({} * {})", self.debug_expr(*x), self.debug_expr(*y))
282 }
283 _ => String::new(),
284 }
285 }
286
287 fn fp_index(&self) -> usize {
288 self.fp_vars.len()
289 }
290
291 fn mix_index(&self) -> usize {
292 self.mix_vars.len()
293 }
294
295 fn push_fp(&mut self, _idx: usize, val: F::ExtElem) {
296 #[cfg(feature = "circuit_debug")]
297 self.fp_index.push(_idx);
298 self.fp_vars.push(val);
299 }
300
301 fn push_mix(&mut self, _idx: usize, mix: MixState<F::ExtElem>) {
302 #[cfg(feature = "circuit_debug")]
303 self.mix_index.push(_idx);
304 self.mix_vars.push(mix);
305 }
306
307 fn step(
308 &mut self,
309 idx: usize,
310 op: &PolyExtStep,
311 mix: &F::ExtElem,
312 u: &[F::ExtElem],
313 args: &[&[F::Elem]],
314 ) {
315 match op {
316 PolyExtStep::Const(value) => {
317 let val = F::Elem::from_u64(*value as u64);
318 trace_if_enabled!("[f:{}] {op:?} -> {val:?}", self.fp_index());
319 self.push_fp(idx, val.into());
320 }
321 PolyExtStep::ConstExt(x0, x1, x2, x3) => {
322 let val = F::ExtElem::from_subelems([
323 F::Elem::from_u64(*x0 as u64),
324 F::Elem::from_u64(*x1 as u64),
325 F::Elem::from_u64(*x2 as u64),
326 F::Elem::from_u64(*x3 as u64),
327 ]);
328 trace_if_enabled!("[f:{}] {op:?} -> {val:?}", self.fp_index());
329 self.push_fp(idx, val);
330 }
331 PolyExtStep::Get(tap) => {
332 let val = u[*tap];
333 trace_if_enabled!("[f:{}] {op:?} -> {val:?}", self.fp_index());
334 self.push_fp(idx, val);
335 }
336 PolyExtStep::GetGlobal(base, offset) => {
337 let val = F::ExtElem::from_subfield(&args[*base][*offset]);
338 trace_if_enabled!("[f:{}] {op:?} -> {val:?}", self.fp_index());
339 self.push_fp(idx, val);
340 }
341 PolyExtStep::Add(x1, x2) => {
342 let val = self.fp_vars[*x1] + self.fp_vars[*x2];
343 trace_if_enabled!("[f:{}] {op:?} -> {val:?}", self.fp_index());
344 self.push_fp(idx, val);
345 }
346 PolyExtStep::Sub(x1, x2) => {
347 let val = self.fp_vars[*x1] - self.fp_vars[*x2];
348 trace_if_enabled!("[f:{}] {op:?} -> {val:?}", self.fp_index());
349 self.push_fp(idx, val);
350 }
351 PolyExtStep::Mul(x1, x2) => {
352 let val = self.fp_vars[*x1] * self.fp_vars[*x2];
353 trace_if_enabled!("[f:{}] {op:?} -> {val:?}", self.fp_index());
354 self.push_fp(idx, val);
355 }
356 PolyExtStep::True => {
357 let mix_val = MixState {
358 tot: F::ExtElem::ZERO,
359 mul: F::ExtElem::ONE,
360 };
361 trace_if_enabled!("[m:{}] {op:?}", self.mix_index());
362 self.push_mix(idx, mix_val);
363 }
364 PolyExtStep::AndEqz(chain, inner) => {
365 let chain = self.mix_vars[*chain];
366 let inner = self.fp_vars[*inner];
367 let mix_val = MixState {
368 tot: chain.tot + chain.mul * inner,
369 mul: chain.mul * *mix,
370 };
371 trace_if_enabled!("[m:{}] {op:?}, inner: {inner:?}", self.mix_index());
372 self.push_mix(idx, mix_val);
373 }
374 PolyExtStep::AndCond(chain, cond, inner) => {
375 let chain = self.mix_vars[*chain];
376 let cond = self.fp_vars[*cond];
377 let inner = self.mix_vars[*inner];
378 let mix_val = MixState {
379 tot: chain.tot + cond * inner.tot * chain.mul,
380 mul: chain.mul * inner.mul,
381 };
382 trace_if_enabled!(
383 "[m:{}] {op:?}, cond: {}, inner: {}",
384 self.mix_index(),
385 cond != F::ExtElem::ZERO,
386 inner.tot != F::ExtElem::ZERO,
387 );
388 self.push_mix(idx, mix_val);
389 }
390 }
391 }
392}