risc0_zkp/
layout.rs

1// Copyright 2025 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//! Utilities for accessing buffers of circuits symbolically.
16
17use alloc::{collections::BTreeMap, string::String, vec::Vec};
18#[cfg(not(feature = "std"))]
19use alloc::{format, string::ToString};
20use core::fmt::{Debug, Formatter, Write};
21
22use anyhow::Result;
23use risc0_core::field::Elem;
24
25use crate::core::digest::Digest;
26
27/// A reference to a register in a layout
28pub struct Reg {
29    pub offset: usize,
30}
31
32impl Component for Reg {
33    fn walk<V: Visitor>(&self, v: &mut V) -> core::fmt::Result {
34        v.visit_reg(self.offset)
35    }
36    fn ty_name(&self) -> &'static str {
37        "reg"
38    }
39}
40
41/// A component within a circuit execution trace buffer.  Users should
42/// not use this directly; it is only "pub" so it can be used by the
43/// generated layout code.
44#[doc(hidden)]
45pub trait Component {
46    fn walk<V: Visitor>(&self, v: &mut V) -> core::fmt::Result;
47    fn ty_name(&self) -> &'static str;
48}
49
50impl<C: Component, const N: usize> Component for [&C; N] {
51    fn walk<V: Visitor>(&self, v: &mut V) -> core::fmt::Result {
52        for (n, elem) in self.iter().enumerate() {
53            let name = alloc::format!("[{n}]");
54            v.visit_component(&name, *elem)?;
55        }
56        Ok(())
57    }
58    fn ty_name(&self) -> &'static str {
59        "array"
60    }
61}
62
63/// A visitor that visits components in a component tree. Users should
64/// not use this directly; it is only "pub" so it can be used by the
65/// generated layout code.
66#[doc(hidden)]
67pub trait Visitor {
68    fn visit_component(&mut self, name: &str, component: &impl Component) -> core::fmt::Result;
69    fn visit_reg(&mut self, offset: usize) -> core::fmt::Result;
70}
71
72/// Represents a section of a component tree that's present in a buffer.
73#[derive(Clone, Copy)]
74pub struct Tree<'a, E: Elem + Into<u32>, C: Component> {
75    pub buf: &'a [E],
76    pub component: &'a C,
77}
78
79impl<'a, E: Elem + Into<u32>, C: Component> Tree<'a, E, C> {
80    /// Returns the component tree rooted at the given component.
81    pub fn new(buf: &'a [E], component: &'a C) -> Self {
82        Tree { buf, component }
83    }
84
85    /// Descends into a subtree.  The given function should return the
86    /// requested layout based when passed this tree's layout.
87    pub fn map<SUBC: Component>(&self, f: impl FnOnce(&'a C) -> &'a SUBC) -> Tree<'a, E, SUBC> {
88        Tree {
89            buf: self.buf,
90            component: f(self.component),
91        }
92    }
93
94    /// Interprets the contents of this tree as a list of u32s.
95    pub fn get_u32s(&self) -> Result<Vec<u32>> {
96        let mut gather = TreeGather::new(self.buf);
97        self.component
98            .walk(&mut gather)
99            .map_err(anyhow::Error::msg)?;
100        Ok(gather.vals)
101    }
102
103    /// Interprets the contents of this tree as a list of bytes.
104    pub fn get_bytes(&self) -> Result<Vec<u8>> {
105        self.get_u32s()?
106            .into_iter()
107            .map(|val| u8::try_from(val).map_err(anyhow::Error::msg))
108            .collect()
109    }
110
111    /// Interprets the contents of this tree as a list of shorts.
112    pub fn get_shorts(&self) -> Result<Vec<u16>> {
113        self.get_u32s()?
114            .into_iter()
115            .map(|val| u16::try_from(val).map_err(anyhow::Error::msg))
116            .collect()
117    }
118
119    /// Interprets the contents of this tree as a digest based on a list of shorts.
120    pub fn get_digest_from_shorts(&self) -> Result<Digest> {
121        let bytes: Vec<_> = self
122            .get_shorts()?
123            .into_iter()
124            .flat_map(|short| short.to_le_bytes())
125            .collect();
126        Digest::try_from(bytes.as_slice()).map_err(anyhow::Error::msg)
127    }
128
129    /// Returns the contents of this tree as a u32; elements are expected to be
130    /// 4 bytes.
131    pub fn get_u32_from_bytes(&self) -> Result<u32> {
132        Ok(u32::from_le_bytes(
133            self.get_bytes()?
134                .as_slice()
135                .try_into()
136                .map_err(anyhow::Error::msg)?,
137        ))
138    }
139
140    /// Returns the contents of this tree, which should have a single element, as a u32.
141    pub fn get_u32_from_elem(&self) -> Result<u32> {
142        let u32s = self.get_u32s()?;
143        assert_eq!(u32s.len(), 1, "Expecting only a single u32 in {self:?}");
144        Ok(u32s[0])
145    }
146}
147
148/// Retrieves the given register from a buffer and returns it as a field element
149pub fn get_elem<'a, E>(buf: &'a [E], reg: &Reg) -> &'a E {
150    buf.get(reg.offset).expect("Invalid offset in layout")
151}
152
153/// Retrieves the given register from a buffer and interprets it as a u32
154pub fn get_u32<E: Copy + Into<u32>>(buf: &[E], reg: &Reg) -> u32 {
155    (*get_elem(buf, reg)).into()
156}
157
158impl<E: Elem + Into<u32>, C: Component> Debug for Tree<'_, E, C> {
159    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
160        let mut p = TreePrinter::new(self.buf, Vec::new(), "top");
161        self.component.walk(&mut p)?;
162        for line in p.lines {
163            writeln!(f, "{line}")?;
164        }
165        Ok(())
166    }
167}
168
169// Collects a tree dump as a set of lines.  Lists identical sections as a
170// reference to the first one seen.
171struct TreePrinter<'a, E: Elem> {
172    buf: &'a [E],
173    lines: Vec<String>,
174    item_count: usize,
175    seen: BTreeMap<Vec<String>, String>,
176    path: Vec<&'a str>,
177}
178
179impl<'a, E: Elem> TreePrinter<'a, E> {
180    fn new(buf: &'a [E], mut path: Vec<&'a str>, name: &'a str) -> Self {
181        path.push(name);
182        TreePrinter {
183            buf,
184            lines: Vec::new(),
185            item_count: 0,
186            seen: BTreeMap::new(),
187            path,
188        }
189    }
190}
191
192impl<E: Elem + Into<u32>> Visitor for TreePrinter<'_, E> {
193    fn visit_component(&mut self, name: &str, component: &impl Component) -> core::fmt::Result {
194        match component.ty_name() {
195            "U32Reg" => {
196                // U32regs print as hexadecimal.
197                let mut subtree = TreeGather::new(self.buf);
198                component.walk(&mut subtree)?;
199                if subtree.vals.iter().any(|&x| x != 0) {
200                    let mut msg = format!("{name}: ");
201                    write!(msg, " 0x")?;
202                    for val in subtree.vals {
203                        if val > 255 {
204                            // Unexpected non-byte in u32reg
205                            write!(msg, "[{val:#x}?]")?;
206                        } else {
207                            write!(msg, "{val:02x}")?;
208                        }
209                    }
210                    self.lines.push(msg);
211                    self.item_count += 1;
212                }
213            }
214            _ => {
215                // Generic subtree
216                let mut subtree = TreePrinter::new(self.buf, self.path.clone(), name);
217                component.walk(&mut subtree)?;
218                match subtree.item_count {
219                    0 => {}
220                    1 => {
221                        // Single item inside; build up path on a
222                        // single line to save vertical space for
223                        // structures like a{b{c{x: y}}}.
224                        self.item_count += 1;
225                        let mut lines = subtree.lines;
226                        lines[0] = format!("{name}: {{{}", lines[0]);
227                        lines.last_mut().as_mut().unwrap().push_str(" }");
228                        self.lines.extend(lines);
229                    }
230                    _ => {
231                        // Multi-line structure; if we've seen this before, just emit a reference.
232                        // Otherwise, save it.
233                        self.item_count += 1;
234                        match self.seen.get(&subtree.lines) {
235                            Some(path) => {
236                                self.lines.push(format!("{name}: duplicate of {path}"));
237                            }
238                            None => {
239                                self.seen.extend(subtree.seen);
240                                self.lines.push(format!("{name}: {{"));
241                                self.lines
242                                    .extend(subtree.lines.iter().map(|s| format!("  {s}")));
243                                self.lines.push("}".to_string());
244                                self.seen.insert(subtree.lines, subtree.path.join("."));
245                            }
246                        }
247                    }
248                }
249            }
250        }
251        Ok(())
252    }
253
254    fn visit_reg(&mut self, offset: usize) -> core::fmt::Result {
255        if let Some(val) = self.buf.get(offset) {
256            self.lines.push(format!("{val:?}"));
257            self.item_count += 1;
258        }
259        Ok(())
260    }
261}
262
263// Gathers all the registers in a tree as u32s.
264struct TreeGather<'a, E: Elem> {
265    buf: &'a [E],
266    vals: Vec<u32>,
267}
268
269impl<'a, E: Elem> TreeGather<'a, E> {
270    fn new(buf: &'a [E]) -> Self {
271        Self {
272            buf,
273            vals: Vec::new(),
274        }
275    }
276}
277
278impl<E: Elem + Into<u32>> Visitor for TreeGather<'_, E> {
279    fn visit_component(&mut self, _name: &str, component: &impl Component) -> core::fmt::Result {
280        component.walk(self)
281    }
282
283    fn visit_reg(&mut self, offset: usize) -> core::fmt::Result {
284        if let Some(val) = self.buf.get(offset) {
285            self.vals.push((*val).into())
286        }
287        Ok(())
288    }
289}