risc0_zkp/
adapter.rs

1// Copyright 2024 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Interface between the circuit and prover/verifier
16
17use 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
26// TODO: Remove references to these constants so we don't depend on a
27// fixed set of register groups.
28pub const REGISTER_GROUP_ACCUM: usize = 0;
29pub const REGISTER_GROUP_CODE: usize = 1;
30pub const REGISTER_GROUP_DATA: usize = 2;
31
32// If true, enable tracing of adapter internals.
33const 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)]
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/// A protocol info string for the proof system and circuits.
144/// Used to seed the Fiat-Shamir transcript and provide domain separation between different
145/// protocol and circuit versions.
146#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
147pub struct ProtocolInfo(pub [u8; 16]);
148
149impl ProtocolInfo {
150    /// Encode a fixed context byte-string to elements, with one element per byte.
151    // NOTE: This function is intended to be compatible with const, but is not const currently because
152    // E::from_u64 is not const, as const functions on traits is not stable.
153    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
171/// Versioned info string for the proof system.
172///
173/// NOTE: This string should be bumped with every change to the proof system, as defined by a
174/// change to checks applied by the verifier.
175pub 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
183/// traits implemented by generated rust code used in both prover and verifier
184pub trait CircuitCoreDef<F: Field>: CircuitInfo + PolyExt<F> + TapsProvider {}
185
186/// traits implemented by generated rust code used in only the prover
187pub 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    Shift,
213}
214
215impl PolyExtStep {
216    pub fn step<F: Field>(
217        &self,
218        fp_vars: &mut Vec<F::ExtElem>,
219        mix_vars: &mut Vec<MixState<F::ExtElem>>,
220        mix: &F::ExtElem,
221        u: &[F::ExtElem],
222        args: &[&[F::Elem]],
223    ) {
224        match self {
225            PolyExtStep::Const(value) => {
226                let elem = F::Elem::from_u64(*value as u64);
227                trace_if_enabled!("[{}] {self:?} -> {elem:?}", fp_vars.len());
228                fp_vars.push(F::ExtElem::from_subfield(&elem));
229            }
230            PolyExtStep::ConstExt(x0, x1, x2, x3) => {
231                let elem = F::ExtElem::from_subelems([
232                    F::Elem::from_u64(*x0 as u64),
233                    F::Elem::from_u64(*x1 as u64),
234                    F::Elem::from_u64(*x2 as u64),
235                    F::Elem::from_u64(*x3 as u64),
236                ]);
237                trace_if_enabled!("[{}] {self:?} -> {elem:?}", fp_vars.len());
238                fp_vars.push(elem);
239            }
240            PolyExtStep::Get(tap) => {
241                let val = u[*tap];
242                trace_if_enabled!("[{}] {self:?} -> {val:?}", fp_vars.len());
243                fp_vars.push(val);
244            }
245            PolyExtStep::GetGlobal(base, offset) => {
246                let val = F::ExtElem::from_subfield(&args[*base][*offset]);
247                trace_if_enabled!("[{}] {self:?} -> {val:?}", fp_vars.len());
248                fp_vars.push(val);
249            }
250            PolyExtStep::Add(x1, x2) => {
251                let val = fp_vars[*x1] + fp_vars[*x2];
252                trace_if_enabled!("[{}] {self:?} -> {val:?}", fp_vars.len());
253                fp_vars.push(val);
254            }
255            PolyExtStep::Sub(x1, x2) => {
256                let val = fp_vars[*x1] - fp_vars[*x2];
257                trace_if_enabled!("[{}] {self:?} -> {val:?}", fp_vars.len());
258                fp_vars.push(val);
259            }
260            PolyExtStep::Mul(x1, x2) => {
261                let val = fp_vars[*x1] * fp_vars[*x2];
262                trace_if_enabled!("[{}] {self:?} -> {val:?}", fp_vars.len());
263                fp_vars.push(val);
264            }
265            PolyExtStep::Shift => {
266                // Return [0, 1, ...] to allow construction of
267                // values in the extension field
268                let val = F::ExtElem::from_subelems(
269                    [F::Elem::ZERO, F::Elem::ONE]
270                        .into_iter()
271                        .chain(core::iter::repeat(F::Elem::ZERO))
272                        .take(F::ExtElem::EXT_SIZE),
273                );
274                trace_if_enabled!("[{}] {self:?} -> {val:?}", mix_vars.len());
275                fp_vars.push(val);
276            }
277            PolyExtStep::True => {
278                let mix_val = MixState {
279                    tot: F::ExtElem::ZERO,
280                    mul: F::ExtElem::ONE,
281                };
282                trace_if_enabled!("[{}] {self:?} -> {mix_val:?}", mix_vars.len());
283                mix_vars.push(mix_val);
284            }
285            PolyExtStep::AndEqz(x, val) => {
286                let x = mix_vars[*x];
287                let val = fp_vars[*val];
288                let mix_val = MixState {
289                    tot: x.tot + x.mul * val,
290                    mul: x.mul * *mix,
291                };
292                trace_if_enabled!("[{}] {self:?} -> {mix_val:?}", mix_vars.len());
293                mix_vars.push(mix_val);
294            }
295            PolyExtStep::AndCond(x, cond, inner) => {
296                let x = mix_vars[*x];
297                let cond = fp_vars[*cond];
298                let inner = mix_vars[*inner];
299                let mix_val = MixState {
300                    tot: x.tot + cond * inner.tot * x.mul,
301                    mul: x.mul * inner.mul,
302                };
303                trace_if_enabled!("[{}] {self:?} -> {mix_val:?}", mix_vars.len());
304                mix_vars.push(mix_val);
305            }
306        }
307    }
308}
309
310impl PolyExtStepDef {
311    pub fn step<F: Field>(
312        &self,
313        mix: &F::ExtElem,
314        u: &[F::ExtElem],
315        args: &[&[F::Elem]],
316    ) -> MixState<F::ExtElem> {
317        let mut fp_vars = Vec::with_capacity(self.block.len() - (self.ret + 1));
318        let mut mix_vars = Vec::with_capacity(self.ret + 1);
319        for op in self.block.iter() {
320            op.step::<F>(&mut fp_vars, &mut mix_vars, mix, u, args);
321        }
322        assert_eq!(
323            fp_vars.len(),
324            self.block.len() - (self.ret + 1),
325            "Miscalculated capacity for fp_vars"
326        );
327        assert_eq!(
328            mix_vars.len(),
329            self.ret + 1,
330            "Miscalculated capacity for mix_vars"
331        );
332        mix_vars[self.ret]
333    }
334}