Skip to main content

risc0_zkp/verify/
read_iop.rs

1// Copyright 2026 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
15use alloc::boxed::Box;
16
17use risc0_core::field::{Elem, Field};
18
19use crate::{
20    core::{
21        digest::Digest,
22        hash::{Rng, RngFactory},
23    },
24    verify::VerificationError,
25};
26
27pub struct ReadIOP<'a, F: Field> {
28    proof: &'a [u32],
29    rng: Box<dyn Rng<F>>,
30}
31
32impl<'a, F: Field> ReadIOP<'a, F> {
33    pub fn new(proof: &'a [u32], rng: &dyn RngFactory<F>) -> Self {
34        ReadIOP {
35            proof,
36            rng: rng.new_rng(),
37        }
38    }
39
40    pub fn read_u32s(&mut self, n: usize) -> Result<&'a [u32], VerificationError> {
41        let Some(u32s) = self.proof.split_off(..n) else {
42            tracing::debug!(
43                "Tried to read {n} u32s from seal; seal length remaining {}",
44                self.proof.len()
45            );
46            return Err(VerificationError::ReceiptFormatError);
47        };
48        Ok(u32s)
49    }
50
51    /// Read some field elements from this IOP, and check to make sure
52    /// they're not INVALID.
53    pub fn read_field_elem_slice<T: Elem>(
54        &mut self,
55        n: usize,
56    ) -> Result<&'a [T], VerificationError> {
57        let u32s = self.read_u32s(n * T::WORDS)?;
58        T::try_from_u32_slice(u32s).map_err(|e| {
59            tracing::debug!("casting seal elems to field slice failed: {e}");
60            VerificationError::ReceiptFormatError
61        })
62    }
63
64    /// Read some plain old data from this IOP without doing any
65    /// validation.  Prefer to use read_field_elem_slice if reading
66    /// field elements.
67    pub fn read_pod_slice<T: bytemuck::Pod>(
68        &mut self,
69        n: usize,
70    ) -> Result<&'a [T], VerificationError> {
71        let read_size = n * core::mem::size_of::<T>() / core::mem::size_of::<u32>();
72        let u32s = self.read_u32s(read_size)?;
73        bytemuck::try_cast_slice(u32s).map_err(|e| {
74            tracing::debug!("casting seal elems to slice failed: {e}");
75            VerificationError::ReceiptFormatError
76        })
77    }
78
79    pub fn commit(&mut self, digest: &Digest) {
80        self.rng.mix(digest);
81    }
82
83    /// Checks that the entire data of the IOP has been read.
84    pub fn verify_complete(&self) -> Result<(), VerificationError> {
85        if !self.proof.is_empty() {
86            tracing::debug!(
87                "verify_complete called with {} words remaining in seal",
88                self.proof.len()
89            );
90            return Err(VerificationError::ReceiptFormatError);
91        }
92        Ok(())
93    }
94
95    /// Get a cryptographically uniform u32
96    pub fn random_bits(&mut self, bits: usize) -> u32 {
97        self.rng.random_bits(bits)
98    }
99
100    /// Get a cryptographically uniform field element
101    pub fn random_elem(&mut self) -> F::Elem {
102        self.rng.random_elem()
103    }
104
105    /// Get a cryptographically uniform extension field element
106    pub fn random_ext_elem(&mut self) -> F::ExtElem {
107        self.rng.random_ext_elem()
108    }
109}