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}