risc0_zkvm/receipt/
segment.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
15use alloc::{collections::BTreeSet, string::String, vec::Vec};
16
17use anyhow::Result;
18use borsh::{BorshDeserialize, BorshSerialize};
19use derive_more::Debug;
20use risc0_binfmt::{tagged_iter, tagged_struct, Digestible};
21use risc0_zkp::{
22    adapter::{CircuitInfo as _, ProtocolInfo, PROOF_SYSTEM_INFO},
23    core::{digest::Digest, hash::sha::Sha256},
24    verify::VerificationError,
25};
26use serde::{Deserialize, Serialize};
27
28use super::VerifierContext;
29use crate::{sha, ReceiptClaim};
30
31/// A receipt attesting to the execution of a Segment.
32///
33/// A SegmentReceipt attests that a Segment was executed in a manner
34/// consistent with the [ReceiptClaim] included in the receipt.
35#[derive(Clone, Debug, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
36#[cfg_attr(test, derive(PartialEq))]
37#[non_exhaustive]
38pub struct SegmentReceipt {
39    /// The cryptographic data attesting to the validity of the code execution.
40    ///
41    /// This data is used by the ZKP Verifier (as called by
42    /// [SegmentReceipt::verify_integrity_with_context]) to cryptographically prove that this
43    /// Segment was faithfully executed. It is largely opaque cryptographic data, but contains a
44    /// non-opaque claim component, which can be conveniently accessed with
45    /// [SegmentReceipt::claim].
46    #[debug("{} bytes", self.get_seal_bytes().len())]
47    pub seal: Vec<u32>,
48
49    /// Segment index within the [Receipt](crate::Receipt)
50    pub index: u32,
51
52    /// Name of the hash function used to create this receipt.
53    pub hashfn: String,
54
55    /// A digest of the verifier parameters that can be used to verify this receipt.
56    ///
57    /// Acts as a fingerprint to identity differing proof system or circuit versions between a
58    /// prover and a verifier. Is not intended to contain the full verifier parameters, which must
59    /// be provided by a trusted source (e.g. packaged with the verifier code).
60    pub verifier_parameters: Digest,
61
62    /// [ReceiptClaim] containing information about the execution that this receipt proves.
63    pub claim: ReceiptClaim,
64}
65
66impl SegmentReceipt {
67    /// Verify the integrity of this receipt, ensuring the claim is attested
68    /// to by the seal.
69    pub fn verify_integrity_with_context(
70        &self,
71        ctx: &VerifierContext,
72    ) -> Result<(), VerificationError> {
73        let params = ctx
74            .segment_verifier_parameters
75            .as_ref()
76            .ok_or(VerificationError::VerifierParametersMissing)?;
77
78        // Check that the proof system and circuit info strings match what is implemented by this
79        // function. Info strings are used a version identifiers, and this verify implementation
80        // supports exactly one proof systema and circuit version at a time.
81        if params.proof_system_info != PROOF_SYSTEM_INFO {
82            return Err(VerificationError::ProofSystemInfoMismatch {
83                expected: PROOF_SYSTEM_INFO,
84                received: params.proof_system_info,
85            });
86        }
87
88        let expected = risc0_circuit_rv32im::CircuitImpl::CIRCUIT_INFO;
89
90        if params.circuit_info != expected {
91            return Err(VerificationError::CircuitInfoMismatch {
92                expected,
93                received: params.circuit_info,
94            });
95        }
96
97        if self.hashfn != "poseidon2" {
98            return Err(VerificationError::InvalidHashSuite);
99        }
100
101        tracing::debug!("SegmentReceipt::verify_integrity_with_context");
102        risc0_circuit_rv32im::verify(&self.seal)?;
103        let decoded_claim = ReceiptClaim::decode_from_seal_v2(&self.seal, None)
104            .or(Err(VerificationError::ReceiptFormatError))?;
105
106        // Receipt is consistent with the claim encoded on the seal. Now check against the
107        // claim on the struct.
108        if decoded_claim.digest::<sha::Impl>() != self.claim.digest::<sha::Impl>() {
109            tracing::debug!(
110                "decoded segment receipt claim does not match claim field:\ndecoded: {:#?},\nexpected: {:#?}",
111                decoded_claim,
112                self.claim,
113            );
114            return Err(VerificationError::ClaimDigestMismatch {
115                expected: self.claim.digest::<sha::Impl>(),
116                received: decoded_claim.digest::<sha::Impl>(),
117            });
118        }
119        Ok(())
120    }
121
122    /// Return the seal for this receipt, as a vector of bytes.
123    pub fn get_seal_bytes(&self) -> Vec<u8> {
124        self.seal.iter().flat_map(|x| x.to_le_bytes()).collect()
125    }
126
127    /// Number of bytes used by the seal for this receipt.
128    pub fn seal_size(&self) -> usize {
129        core::mem::size_of_val(self.seal.as_slice())
130    }
131}
132
133/// Verifier parameters used to verify a [SegmentReceipt].
134#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
135pub struct SegmentReceiptVerifierParameters {
136    /// Set of control ID with which the receipt is expected to verify.
137    pub control_ids: BTreeSet<Digest>,
138
139    /// Protocol info string distinguishing the proof system under which the receipt should verify.
140    pub proof_system_info: ProtocolInfo,
141
142    /// Protocol info string distinguishing circuit with which the receipt should verify.
143    pub circuit_info: ProtocolInfo,
144}
145
146impl Digestible for SegmentReceiptVerifierParameters {
147    /// Hash the [SegmentReceiptVerifierParameters] to get a digest of the struct.
148    fn digest<S: Sha256>(&self) -> Digest {
149        tagged_struct::<S>(
150            "risc0.SegmentReceiptVerifierParameters",
151            &[
152                tagged_iter::<S>("risc0.ControlIdSet", self.control_ids.iter()),
153                *S::hash_bytes(&self.proof_system_info.0),
154                *S::hash_bytes(&self.circuit_info.0),
155            ],
156            &[],
157        )
158    }
159}
160
161impl Default for SegmentReceiptVerifierParameters {
162    /// Default set of parameters used to verify a [SegmentReceipt].
163    fn default() -> Self {
164        Self {
165            control_ids: BTreeSet::default(),
166            proof_system_info: PROOF_SYSTEM_INFO,
167            circuit_info: risc0_circuit_rv32im::CircuitImpl::CIRCUIT_INFO,
168        }
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use super::SegmentReceiptVerifierParameters;
175    use crate::sha::Digestible;
176    use risc0_zkp::core::digest::digest;
177
178    // Check that the verifier parameters has a stable digest (and therefore a stable value). This
179    // struct encodes parameters used in verification, and so this value should be updated if and
180    // only if a change to the verifier parameters is expected. Updating the verifier parameters
181    // will result in incompatibility with previous versions.
182    #[test]
183    fn segment_receipt_verifier_parameters_is_stable() {
184        assert_eq!(
185            SegmentReceiptVerifierParameters::default().digest(),
186            digest!("5a123dc5ac0a4ed69a91f746cca8453a3af36dc0803ccf36bcc5b63eb4f5e621")
187        );
188    }
189}