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