1pub(crate) mod composite;
18pub(crate) mod groth16;
19pub(crate) mod merkle;
20pub(crate) mod segment;
21pub(crate) mod succinct;
22
23use alloc::{collections::BTreeMap, string::String, vec, vec::Vec};
24
25use anyhow::Result;
26use borsh::{BorshDeserialize, BorshSerialize};
27use derive_more::Debug;
28use risc0_core::field::baby_bear::BabyBear;
29use risc0_zkp::{
30 core::{
31 digest::Digest,
32 hash::{
33 blake2b::Blake2bCpuHashSuite, poseidon2::Poseidon2HashSuite, sha::Sha256HashSuite,
34 HashSuite,
35 },
36 },
37 verify::VerificationError,
38};
39use serde::{de::DeserializeOwned, Deserialize, Serialize};
40
41use crate::{
43 claim::Unknown,
44 serde::{from_slice, Error},
45 sha::{Digestible, Sha256},
46 Assumption, Assumptions, MaybePruned, Output, PrunedValueError, ReceiptClaim,
47};
48
49pub use self::groth16::{Groth16Receipt, Groth16ReceiptVerifierParameters};
50
51pub use self::{
52 composite::{CompositeReceipt, CompositeReceiptVerifierParameters},
53 segment::{SegmentReceipt, SegmentReceiptVerifierParameters},
54 succinct::{SuccinctReceipt, SuccinctReceiptVerifierParameters},
55};
56
57#[derive(Clone, Debug, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
115#[cfg_attr(test, derive(PartialEq))]
116pub struct Receipt {
117 pub inner: InnerReceipt,
119
120 pub journal: Journal,
124
125 pub metadata: ReceiptMetadata,
130}
131
132impl Receipt {
133 pub fn new(inner: InnerReceipt, journal: Vec<u8>) -> Self {
135 let metadata = ReceiptMetadata {
136 verifier_parameters: inner.verifier_parameters(),
137 };
138 Self {
139 inner,
140 journal: Journal::new(journal),
141 metadata,
142 }
143 }
144
145 pub fn verify(&self, image_id: impl Into<Digest>) -> Result<(), VerificationError> {
153 self.verify_with_context(&VerifierContext::default(), image_id)
154 }
155
156 pub fn verify_with_context(
164 &self,
165 ctx: &VerifierContext,
166 image_id: impl Into<Digest>,
167 ) -> Result<(), VerificationError> {
168 if self.inner.verifier_parameters() != self.metadata.verifier_parameters {
169 return Err(VerificationError::ReceiptFormatError);
172 }
173
174 tracing::debug!("Receipt::verify_with_context");
175 self.inner.verify_integrity_with_context(ctx)?;
176
177 let expected_claim = ReceiptClaim::ok(image_id, MaybePruned::Pruned(self.journal.digest()));
181 if expected_claim.digest() != self.inner.claim()?.digest() {
182 tracing::debug!(
183 "receipt claim does not match expected claim:\nreceipt: {:#?}\nexpected: {:#?}",
184 self.inner.claim()?,
185 expected_claim
186 );
187 return Err(VerificationError::ClaimDigestMismatch {
188 expected: expected_claim.digest(),
189 received: self.claim()?.digest(),
190 });
191 }
192
193 Ok(())
194 }
195
196 pub fn verify_integrity_with_context(
208 &self,
209 ctx: &VerifierContext,
210 ) -> Result<(), VerificationError> {
211 if self.inner.verifier_parameters() != self.metadata.verifier_parameters {
212 return Err(VerificationError::ReceiptFormatError);
215 }
216
217 tracing::debug!("Receipt::verify_integrity_with_context");
218 self.inner.verify_integrity_with_context(ctx)?;
219
220 let maybe_pruned_claim = self.inner.claim()?;
223 let claim = maybe_pruned_claim
224 .as_value()
225 .map_err(|_| VerificationError::ReceiptFormatError)?;
226
227 let expected_output = claim.exit_code.expects_output().then(|| Output {
228 journal: MaybePruned::Pruned(self.journal.digest()),
229 assumptions: Assumptions(vec![]).into(),
234 });
235
236 if claim.output.digest() != expected_output.digest() {
237 let empty_output = claim.output.is_none() && self.journal.bytes.is_empty();
238 if !empty_output {
239 tracing::debug!(
240 "journal: 0x{}, expected output digest: 0x{}, decoded output digest: 0x{}",
241 hex::encode(&self.journal.bytes),
242 hex::encode(expected_output.digest()),
243 hex::encode(claim.output.digest()),
244 );
245 return Err(VerificationError::JournalDigestMismatch);
246 }
247 tracing::debug!("accepting zero digest for output of receipt with empty journal");
248 }
249
250 Ok(())
251 }
252
253 pub fn claim(&self) -> Result<MaybePruned<ReceiptClaim>, VerificationError> {
255 self.inner.claim()
256 }
257
258 pub fn seal_size(&self) -> usize {
260 self.inner.seal_size()
261 }
262}
263
264#[derive(
270 Clone, Debug, Default, Deserialize, Serialize, PartialEq, BorshSerialize, BorshDeserialize,
271)]
272pub struct Journal {
273 #[debug("{} bytes", bytes.len())]
275 pub bytes: Vec<u8>,
276}
277
278impl Journal {
279 pub fn new(bytes: Vec<u8>) -> Self {
281 Self { bytes }
282 }
283
284 pub fn decode<T: DeserializeOwned>(&self) -> Result<T, Error> {
286 from_slice(&self.bytes)
287 }
288}
289
290impl risc0_binfmt::Digestible for Journal {
291 fn digest<S: Sha256>(&self) -> Digest {
292 *S::hash_bytes(&self.bytes)
293 }
294}
295
296impl AsRef<[u8]> for Journal {
297 fn as_ref(&self) -> &[u8] {
298 &self.bytes
299 }
300}
301
302#[derive(Clone, Debug, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
306#[cfg_attr(test, derive(PartialEq))]
307#[non_exhaustive]
308pub enum InnerReceipt {
309 Composite(CompositeReceipt),
311
312 Succinct(SuccinctReceipt<ReceiptClaim>),
314
315 Groth16(Groth16Receipt<ReceiptClaim>),
317
318 Fake(FakeReceipt<ReceiptClaim>),
320}
321
322impl InnerReceipt {
323 pub fn verify_integrity(&self) -> Result<(), VerificationError> {
326 self.verify_integrity_with_context(&VerifierContext::default())
327 }
328
329 pub fn verify_integrity_with_context(
331 &self,
332 ctx: &VerifierContext,
333 ) -> Result<(), VerificationError> {
334 tracing::debug!("InnerReceipt::verify_integrity_with_context");
335 match self {
336 Self::Composite(inner) => inner.verify_integrity_with_context(ctx),
337 Self::Groth16(inner) => inner.verify_integrity_with_context(ctx),
338 Self::Succinct(inner) => inner.verify_integrity_with_context(ctx),
339 Self::Fake(inner) => inner.verify_integrity_with_context(ctx),
340 }
341 }
342
343 pub fn composite(&self) -> Result<&CompositeReceipt, VerificationError> {
345 if let Self::Composite(x) = self {
346 Ok(x)
347 } else {
348 Err(VerificationError::ReceiptFormatError)
349 }
350 }
351
352 pub fn groth16(&self) -> Result<&Groth16Receipt<ReceiptClaim>, VerificationError> {
354 if let Self::Groth16(x) = self {
355 Ok(x)
356 } else {
357 Err(VerificationError::ReceiptFormatError)
358 }
359 }
360
361 pub fn succinct(&self) -> Result<&SuccinctReceipt<ReceiptClaim>, VerificationError> {
363 if let Self::Succinct(x) = self {
364 Ok(x)
365 } else {
366 Err(VerificationError::ReceiptFormatError)
367 }
368 }
369
370 pub fn claim(&self) -> Result<MaybePruned<ReceiptClaim>, VerificationError> {
372 match self {
373 Self::Composite(ref inner) => Ok(inner.claim()?.into()),
374 Self::Groth16(ref inner) => Ok(inner.claim.clone()),
375 Self::Succinct(ref inner) => Ok(inner.claim.clone()),
376 Self::Fake(ref inner) => Ok(inner.claim.clone()),
377 }
378 }
379
380 pub fn verifier_parameters(&self) -> Digest {
382 match self {
383 Self::Composite(ref inner) => inner.verifier_parameters,
384 Self::Groth16(ref inner) => inner.verifier_parameters,
385 Self::Succinct(ref inner) => inner.verifier_parameters,
386 Self::Fake(_) => Digest::ZERO,
387 }
388 }
389
390 pub fn seal_size(&self) -> usize {
392 match self {
393 Self::Composite(receipt) => receipt.seal_size(),
394 Self::Succinct(receipt) => receipt.seal_size(),
395 Self::Groth16(receipt) => receipt.seal_size(),
396 Self::Fake(_) => 0,
397 }
398 }
399}
400
401impl From<CompositeReceipt> for InnerReceipt {
402 fn from(receipt: CompositeReceipt) -> Self {
403 Self::Composite(receipt)
404 }
405}
406
407impl From<SuccinctReceipt<ReceiptClaim>> for InnerReceipt {
408 fn from(receipt: SuccinctReceipt<ReceiptClaim>) -> Self {
409 Self::Succinct(receipt)
410 }
411}
412
413impl From<Groth16Receipt<ReceiptClaim>> for InnerReceipt {
414 fn from(receipt: Groth16Receipt<ReceiptClaim>) -> Self {
415 Self::Groth16(receipt)
416 }
417}
418
419impl From<FakeReceipt<ReceiptClaim>> for InnerReceipt {
420 fn from(receipt: FakeReceipt<ReceiptClaim>) -> Self {
421 Self::Fake(receipt)
422 }
423}
424
425#[derive(Clone, Debug, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
427#[cfg_attr(test, derive(PartialEq))]
428#[non_exhaustive]
429pub enum GenericReceipt<Claim> {
430 Succinct(SuccinctReceipt<Claim>),
432
433 Groth16(Groth16Receipt<Claim>),
435
436 Fake(FakeReceipt<Claim>),
438}
439
440impl<Claim> GenericReceipt<Claim> {
441 pub fn verify_integrity(&self) -> Result<(), VerificationError>
444 where
445 Claim: risc0_binfmt::Digestible + core::fmt::Debug,
446 {
447 self.verify_integrity_with_context(&VerifierContext::default())
448 }
449
450 pub fn verify_integrity_with_context(
452 &self,
453 ctx: &VerifierContext,
454 ) -> Result<(), VerificationError>
455 where
456 Claim: risc0_binfmt::Digestible + core::fmt::Debug,
457 {
458 tracing::debug!("InnerReceipt::verify_integrity_with_context");
459 match self {
460 Self::Groth16(inner) => inner.verify_integrity_with_context(ctx),
461 Self::Succinct(inner) => inner.verify_integrity_with_context(ctx),
462 Self::Fake(inner) => inner.verify_integrity_with_context(ctx),
463 }
464 }
465
466 pub fn groth16(&self) -> Result<&Groth16Receipt<Claim>, VerificationError> {
468 if let Self::Groth16(x) = self {
469 Ok(x)
470 } else {
471 Err(VerificationError::ReceiptFormatError)
472 }
473 }
474
475 pub fn succinct(&self) -> Result<&SuccinctReceipt<Claim>, VerificationError> {
477 if let Self::Succinct(x) = self {
478 Ok(x)
479 } else {
480 Err(VerificationError::ReceiptFormatError)
481 }
482 }
483
484 pub fn claim(&self) -> MaybePruned<Claim>
486 where
487 Claim: Clone,
488 {
489 match self {
490 Self::Groth16(ref inner) => inner.claim.clone(),
491 Self::Succinct(ref inner) => inner.claim.clone(),
492 Self::Fake(ref inner) => inner.claim.clone(),
493 }
494 }
495
496 pub fn verifier_parameters(&self) -> Digest {
498 match self {
499 Self::Groth16(ref inner) => inner.verifier_parameters,
500 Self::Succinct(ref inner) => inner.verifier_parameters,
501 Self::Fake(_) => Digest::ZERO,
502 }
503 }
504
505 pub fn seal_size(&self) -> usize {
507 match self {
508 Self::Succinct(receipt) => receipt.seal_size(),
509 Self::Groth16(receipt) => receipt.seal_size(),
510 Self::Fake(_) => 0,
511 }
512 }
513
514 pub fn into_unknown(self) -> GenericReceipt<Unknown>
517 where
518 Claim: risc0_binfmt::Digestible,
519 {
520 match self {
521 Self::Succinct(receipt) => receipt.into_unknown().into(),
522 Self::Groth16(receipt) => receipt.into_unknown().into(),
523 Self::Fake(receipt) => receipt.into_unknown().into(),
524 }
525 }
526}
527
528impl<Claim> From<SuccinctReceipt<Claim>> for GenericReceipt<Claim> {
529 fn from(receipt: SuccinctReceipt<Claim>) -> Self {
530 Self::Succinct(receipt)
531 }
532}
533
534impl<Claim> From<Groth16Receipt<Claim>> for GenericReceipt<Claim> {
535 fn from(receipt: Groth16Receipt<Claim>) -> Self {
536 Self::Groth16(receipt)
537 }
538}
539
540impl<Claim> From<FakeReceipt<Claim>> for GenericReceipt<Claim> {
541 fn from(receipt: FakeReceipt<Claim>) -> Self {
542 Self::Fake(receipt)
543 }
544}
545
546#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
557#[cfg_attr(test, derive(PartialEq))]
558#[non_exhaustive]
559pub struct FakeReceipt<Claim> {
560 pub claim: MaybePruned<Claim>,
564}
565
566impl<Claim> FakeReceipt<Claim> {
567 pub fn new(claim: impl Into<MaybePruned<Claim>>) -> Self {
569 Self {
570 claim: claim.into(),
571 }
572 }
573
574 #[deprecated(note = "Use verify_integrity_with_context instead")]
576 pub fn verify_integrity(&self) -> Result<(), VerificationError> {
577 Err(VerificationError::InvalidProof)
578 }
579
580 pub fn verify_integrity_with_context(
584 &self,
585 ctx: &VerifierContext,
586 ) -> Result<(), VerificationError> {
587 if ctx.dev_mode() {
588 assert!(cfg!(not(feature = "disable-dev-mode")));
589 Ok(())
590 } else {
591 Err(VerificationError::InvalidProof)
592 }
593 }
594
595 pub fn into_unknown(self) -> FakeReceipt<Unknown>
598 where
599 Claim: risc0_binfmt::Digestible,
600 {
601 FakeReceipt {
602 claim: MaybePruned::Pruned(self.claim.digest()),
603 }
604 }
605}
606
607impl TryFrom<FakeReceipt<ReceiptClaim>> for Receipt {
608 type Error = PrunedValueError;
609
610 fn try_from(fake_receipt: FakeReceipt<ReceiptClaim>) -> Result<Self, Self::Error> {
613 let journal = fake_receipt
615 .claim
616 .as_value()?
617 .output
618 .as_value()?
619 .as_ref()
620 .map(|output| Ok(output.journal.as_value()?.clone()))
621 .transpose()?
622 .unwrap_or_default();
623 Ok(Receipt::new(InnerReceipt::Fake(fake_receipt), journal))
624 }
625}
626
627#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
634#[non_exhaustive]
635pub struct ReceiptMetadata {
636 pub verifier_parameters: Digest,
643}
644
645#[derive(Clone, Debug, Serialize, Deserialize)]
648pub enum AssumptionReceipt {
649 Proven(InnerAssumptionReceipt),
655
656 Unresolved(Assumption),
663}
664
665impl AssumptionReceipt {
666 pub fn claim_digest(&self) -> Result<Digest, VerificationError> {
668 match self {
669 Self::Proven(receipt) => Ok(receipt.claim_digest()?),
670 Self::Unresolved(assumption) => Ok(assumption.claim),
671 }
672 }
673}
674
675impl From<Receipt> for AssumptionReceipt {
676 fn from(receipt: Receipt) -> Self {
678 Self::Proven(receipt.inner.into())
679 }
680}
681
682impl<Claim> From<GenericReceipt<Claim>> for AssumptionReceipt
683where
684 Claim: risc0_binfmt::Digestible,
685{
686 fn from(receipt: GenericReceipt<Claim>) -> Self {
688 Self::Proven(receipt.into())
689 }
690}
691
692impl From<InnerReceipt> for AssumptionReceipt {
693 fn from(receipt: InnerReceipt) -> Self {
695 Self::Proven(receipt.into())
696 }
697}
698
699impl From<InnerAssumptionReceipt> for AssumptionReceipt {
700 fn from(receipt: InnerAssumptionReceipt) -> Self {
702 Self::Proven(receipt)
703 }
704}
705
706impl<Claim> From<SuccinctReceipt<Claim>> for AssumptionReceipt
707where
708 Claim: risc0_binfmt::Digestible,
709{
710 fn from(receipt: SuccinctReceipt<Claim>) -> Self {
712 Self::Proven(InnerAssumptionReceipt::Succinct(receipt.into_unknown()))
713 }
714}
715
716impl<Claim> From<FakeReceipt<Claim>> for AssumptionReceipt
717where
718 Claim: risc0_binfmt::Digestible,
719{
720 fn from(receipt: FakeReceipt<Claim>) -> Self {
722 Self::Proven(InnerAssumptionReceipt::Fake(receipt.into_unknown()))
723 }
724}
725
726impl From<Assumption> for AssumptionReceipt {
727 fn from(assumption: Assumption) -> Self {
729 Self::Unresolved(assumption)
730 }
731}
732
733impl From<MaybePruned<ReceiptClaim>> for AssumptionReceipt {
734 fn from(claim: MaybePruned<ReceiptClaim>) -> Self {
740 Self::Unresolved(Assumption {
741 claim: claim.digest(),
742 control_root: Digest::ZERO,
743 })
744 }
745}
746
747impl From<ReceiptClaim> for AssumptionReceipt {
748 fn from(claim: ReceiptClaim) -> Self {
754 Self::Unresolved(Assumption {
755 claim: claim.digest(),
756 control_root: Digest::ZERO,
757 })
758 }
759}
760
761#[derive(Clone, Debug, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
765#[cfg_attr(test, derive(PartialEq))]
766#[non_exhaustive]
767pub enum InnerAssumptionReceipt {
768 Composite(CompositeReceipt),
770
771 Succinct(SuccinctReceipt<Unknown>),
773
774 Groth16(Groth16Receipt<Unknown>),
776
777 Fake(FakeReceipt<Unknown>),
779}
780
781impl InnerAssumptionReceipt {
782 pub fn verify_integrity_with_context(
784 &self,
785 ctx: &VerifierContext,
786 ) -> Result<(), VerificationError> {
787 tracing::debug!("InnerAssumptionReceipt::verify_integrity_with_context");
788 match self {
789 Self::Composite(inner) => inner.verify_integrity_with_context(ctx),
790 Self::Groth16(inner) => inner.verify_integrity_with_context(ctx),
791 Self::Succinct(inner) => inner.verify_integrity_with_context(ctx),
792 Self::Fake(inner) => inner.verify_integrity_with_context(ctx),
793 }
794 }
795
796 pub fn composite(&self) -> Result<&CompositeReceipt, VerificationError> {
798 if let Self::Composite(x) = self {
799 Ok(x)
800 } else {
801 Err(VerificationError::ReceiptFormatError)
802 }
803 }
804
805 pub fn groth16(&self) -> Result<&Groth16Receipt<Unknown>, VerificationError> {
807 if let Self::Groth16(x) = self {
808 Ok(x)
809 } else {
810 Err(VerificationError::ReceiptFormatError)
811 }
812 }
813
814 pub fn succinct(&self) -> Result<&SuccinctReceipt<Unknown>, VerificationError> {
816 if let Self::Succinct(x) = self {
817 Ok(x)
818 } else {
819 Err(VerificationError::ReceiptFormatError)
820 }
821 }
822
823 pub fn claim_digest(&self) -> Result<Digest, VerificationError> {
827 match self {
828 Self::Composite(ref inner) => Ok(inner.claim()?.digest()),
829 Self::Groth16(ref inner) => Ok(inner.claim.digest()),
830 Self::Succinct(ref inner) => Ok(inner.claim.digest()),
831 Self::Fake(ref inner) => Ok(inner.claim.digest()),
832 }
833 }
834
835 pub fn verifier_parameters(&self) -> Digest {
837 match self {
838 Self::Composite(ref inner) => inner.verifier_parameters,
839 Self::Groth16(ref inner) => inner.verifier_parameters,
840 Self::Succinct(ref inner) => inner.verifier_parameters,
841 Self::Fake(_) => Digest::ZERO,
842 }
843 }
844
845 pub fn seal_size(&self) -> usize {
847 match self {
848 Self::Composite(receipt) => receipt.seal_size(),
849 Self::Succinct(receipt) => receipt.seal_size(),
850 Self::Groth16(receipt) => receipt.seal_size(),
851 Self::Fake(_) => 0,
852 }
853 }
854}
855
856impl From<InnerReceipt> for InnerAssumptionReceipt {
857 fn from(value: InnerReceipt) -> Self {
858 match value {
859 InnerReceipt::Composite(x) => InnerAssumptionReceipt::Composite(x),
860 InnerReceipt::Succinct(x) => InnerAssumptionReceipt::Succinct(x.into_unknown()),
861 InnerReceipt::Groth16(x) => InnerAssumptionReceipt::Groth16(x.into_unknown()),
862 InnerReceipt::Fake(x) => InnerAssumptionReceipt::Fake(x.into_unknown()),
863 }
864 }
865}
866
867impl<Claim> From<GenericReceipt<Claim>> for InnerAssumptionReceipt
868where
869 Claim: risc0_binfmt::Digestible,
870{
871 fn from(value: GenericReceipt<Claim>) -> Self {
872 match value {
873 GenericReceipt::Succinct(x) => InnerAssumptionReceipt::Succinct(x.into_unknown()),
874 GenericReceipt::Groth16(x) => InnerAssumptionReceipt::Groth16(x.into_unknown()),
875 GenericReceipt::Fake(x) => InnerAssumptionReceipt::Fake(x.into_unknown()),
876 }
877 }
878}
879
880pub const DEFAULT_MAX_PO2: usize = 22;
885
886#[non_exhaustive]
888pub struct VerifierContext {
889 pub suites: BTreeMap<String, HashSuite<BabyBear>>,
891
892 pub segment_verifier_parameters: Option<SegmentReceiptVerifierParameters>,
894
895 pub succinct_verifier_parameters: Option<SuccinctReceiptVerifierParameters>,
897
898 pub groth16_verifier_parameters: Option<Groth16ReceiptVerifierParameters>,
900
901 pub(crate) dev_mode: bool,
903}
904
905impl VerifierContext {
906 pub fn empty() -> Self {
908 Self {
909 suites: BTreeMap::default(),
910 segment_verifier_parameters: None,
911 succinct_verifier_parameters: None,
912 groth16_verifier_parameters: None,
913 dev_mode: crate::is_dev_mode_enabled_via_environment(),
914 }
915 }
916
917 pub fn default_hash_suites() -> BTreeMap<String, HashSuite<BabyBear>> {
919 BTreeMap::from([
920 ("blake2b".into(), Blake2bCpuHashSuite::new_suite()),
921 ("poseidon2".into(), Poseidon2HashSuite::new_suite()),
922 ("sha-256".into(), Sha256HashSuite::new_suite()),
923 ])
924 }
925
926 #[stability::unstable]
930 pub fn from_max_po2(po2_max: usize) -> Self {
931 Self {
932 suites: Self::default_hash_suites(),
933 segment_verifier_parameters: Some(SegmentReceiptVerifierParameters::default()),
934 succinct_verifier_parameters: Some(SuccinctReceiptVerifierParameters::from_max_po2(
935 po2_max,
936 )),
937 groth16_verifier_parameters: Some(Groth16ReceiptVerifierParameters::from_max_po2(
938 po2_max,
939 )),
940 dev_mode: crate::is_dev_mode_enabled_via_environment(),
941 }
942 }
943
944 #[stability::unstable]
947 pub fn all_po2s() -> Self {
948 Self::from_max_po2(risc0_zkp::MAX_CYCLES_PO2)
949 }
950
951 pub fn with_suites(mut self, suites: BTreeMap<String, HashSuite<BabyBear>>) -> Self {
953 self.suites = suites;
954 self
955 }
956
957 pub fn with_segment_verifier_parameters(
959 mut self,
960 params: SegmentReceiptVerifierParameters,
961 ) -> Self {
962 self.segment_verifier_parameters = Some(params);
963 self
964 }
965
966 pub fn with_succinct_verifier_parameters(
968 mut self,
969 params: SuccinctReceiptVerifierParameters,
970 ) -> Self {
971 self.succinct_verifier_parameters = Some(params);
972 self
973 }
974
975 pub fn with_groth16_verifier_parameters(
977 mut self,
978 params: Groth16ReceiptVerifierParameters,
979 ) -> Self {
980 self.groth16_verifier_parameters = Some(params);
981 self
982 }
983
984 pub fn with_dev_mode(mut self, dev_mode: bool) -> Self {
986 if cfg!(feature = "disable-dev-mode") && dev_mode {
987 panic!("zkVM: Inconsistent settings -- please resolve. \
988 The RISC0_DEV_MODE environment variable is set but dev mode has been disabled by feature flag.");
989 }
990 self.dev_mode = dev_mode;
991 self
992 }
993
994 pub fn dev_mode(&self) -> bool {
996 self.dev_mode
997 }
998
999 pub fn composite_verifier_parameters(&self) -> Option<CompositeReceiptVerifierParameters> {
1004 Some(CompositeReceiptVerifierParameters {
1005 segment: self.segment_verifier_parameters.as_ref()?.clone().into(),
1006 succinct: self.succinct_verifier_parameters.as_ref()?.clone().into(),
1007 groth16: self.groth16_verifier_parameters.as_ref()?.clone().into(),
1008 })
1009 }
1010}
1011
1012impl Default for VerifierContext {
1013 fn default() -> Self {
1014 Self {
1015 suites: Self::default_hash_suites(),
1016 segment_verifier_parameters: Some(Default::default()),
1017 succinct_verifier_parameters: Some(Default::default()),
1018 groth16_verifier_parameters: Some(Default::default()),
1019 dev_mode: crate::is_dev_mode_enabled_via_environment(),
1020 }
1021 }
1022}
1023
1024#[cfg(test)]
1025mod tests {
1026 use super::{FakeReceipt, InnerReceipt, Receipt};
1027 use crate::{
1028 sha::{Digest, DIGEST_BYTES},
1029 MaybePruned,
1030 };
1031 use risc0_zkp::verify::VerificationError;
1032
1033 #[test]
1034 fn mangled_version_info_should_error() {
1035 let mut mangled_receipt = Receipt::new(
1036 InnerReceipt::Fake(FakeReceipt {
1037 claim: MaybePruned::Pruned(Digest::ZERO),
1038 }),
1039 vec![],
1040 );
1041 let ones_digest = Digest::from([1u8; DIGEST_BYTES]);
1042 mangled_receipt.metadata.verifier_parameters = ones_digest;
1043
1044 assert_eq!(
1045 mangled_receipt.verify(Digest::ZERO).err().unwrap(),
1046 VerificationError::ReceiptFormatError
1047 );
1048 assert_eq!(
1049 mangled_receipt
1050 .verify_with_context(&Default::default(), Digest::ZERO)
1051 .err()
1052 .unwrap(),
1053 VerificationError::ReceiptFormatError
1054 );
1055 assert_eq!(
1056 mangled_receipt
1057 .verify_integrity_with_context(&Default::default())
1058 .err()
1059 .unwrap(),
1060 VerificationError::ReceiptFormatError
1061 );
1062 }
1063
1064 #[test]
1065 fn borsh_serde() {
1066 use crate::ReceiptClaim;
1067 use risc0_zkvm_methods::MULTI_TEST_ID;
1068
1069 let claim = ReceiptClaim::ok(MULTI_TEST_ID, vec![]);
1070 let receipt = Receipt::new(
1071 InnerReceipt::Fake(FakeReceipt {
1072 claim: MaybePruned::Value(claim),
1073 }),
1074 vec![],
1075 );
1076 let encoded = borsh::to_vec(&receipt).unwrap();
1077 let decoded: Receipt = borsh::from_slice(&encoded).unwrap();
1078 assert_eq!(receipt, decoded);
1079 }
1080}