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