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}