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        tracing::debug!("SegmentReceipt::verify_integrity_with_context");
98        risc0_circuit_rv32im::verify(&self.seal)?;
99        let decoded_claim = ReceiptClaim::decode_from_seal_v2(&self.seal, None)
100            .or(Err(VerificationError::ReceiptFormatError))?;
101
102        // Receipt is consistent with the claim encoded on the seal. Now check against the
103        // claim on the struct.
104        if decoded_claim.digest::<sha::Impl>() != self.claim.digest::<sha::Impl>() {
105            tracing::debug!(
106                "decoded segment receipt claim does not match claim field:\ndecoded: {:#?},\nexpected: {:#?}",
107                decoded_claim,
108                self.claim,
109            );
110            return Err(VerificationError::ClaimDigestMismatch {
111                expected: self.claim.digest::<sha::Impl>(),
112                received: decoded_claim.digest::<sha::Impl>(),
113            });
114        }
115        Ok(())
116    }
117
118    /// Return the seal for this receipt, as a vector of bytes.
119    pub fn get_seal_bytes(&self) -> Vec<u8> {
120        self.seal.iter().flat_map(|x| x.to_le_bytes()).collect()
121    }
122
123    /// Number of bytes used by the seal for this receipt.
124    pub fn seal_size(&self) -> usize {
125        core::mem::size_of_val(self.seal.as_slice())
126    }
127}
128
129/// Verifier parameters used to verify a [SegmentReceipt].
130#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
131pub struct SegmentReceiptVerifierParameters {
132    /// Set of control ID with which the receipt is expected to verify.
133    pub control_ids: BTreeSet<Digest>,
134
135    /// Protocol info string distinguishing the proof system under which the receipt should verify.
136    pub proof_system_info: ProtocolInfo,
137
138    /// Protocol info string distinguishing circuit with which the receipt should verify.
139    pub circuit_info: ProtocolInfo,
140}
141
142impl Digestible for SegmentReceiptVerifierParameters {
143    /// Hash the [SegmentReceiptVerifierParameters] to get a digest of the struct.
144    fn digest<S: Sha256>(&self) -> Digest {
145        tagged_struct::<S>(
146            "risc0.SegmentReceiptVerifierParameters",
147            &[
148                tagged_iter::<S>("risc0.ControlIdSet", self.control_ids.iter()),
149                *S::hash_bytes(&self.proof_system_info.0),
150                *S::hash_bytes(&self.circuit_info.0),
151            ],
152            &[],
153        )
154    }
155}
156
157impl Default for SegmentReceiptVerifierParameters {
158    /// Default set of parameters used to verify a [SegmentReceipt].
159    fn default() -> Self {
160        Self {
161            control_ids: BTreeSet::default(),
162            proof_system_info: PROOF_SYSTEM_INFO,
163            circuit_info: risc0_circuit_rv32im::CircuitImpl::CIRCUIT_INFO,
164        }
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::SegmentReceiptVerifierParameters;
171    use crate::sha::Digestible;
172    use risc0_zkp::core::digest::digest;
173
174    // Check that the verifier parameters has a stable digest (and therefore a stable value). This
175    // struct encodes parameters used in verification, and so this value should be updated if and
176    // only if a change to the verifier parameters is expected. Updating the verifier parameters
177    // will result in incompatibility with previous versions.
178    #[test]
179    fn segment_receipt_verifier_parameters_is_stable() {
180        assert_eq!(
181            SegmentReceiptVerifierParameters::default().digest(),
182            digest!("5a123dc5ac0a4ed69a91f746cca8453a3af36dc0803ccf36bcc5b63eb4f5e621")
183        );
184    }
185}