1use alloc::{collections::VecDeque, vec::Vec};
23use core::{fmt, ops::Deref};
24
25use anyhow::{anyhow, ensure};
26use borsh::{BorshDeserialize, BorshSerialize};
27use risc0_binfmt::{
28 read_sha_halfs, tagged_list, tagged_list_cons, tagged_struct, write_sha_halfs, Digestible,
29 ExitCode, InvalidExitCodeError,
30};
31use risc0_zkp::core::digest::Digest;
32use serde::{Deserialize, Serialize};
33
34use crate::{
35 sha::{self, Sha256},
36 SystemState,
37};
38
39#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
50#[cfg_attr(test, derive(PartialEq))]
51pub struct ReceiptClaim {
52 pub pre: MaybePruned<SystemState>,
54
55 pub post: MaybePruned<SystemState>,
57
58 pub exit_code: ExitCode,
60
61 pub input: MaybePruned<Option<Input>>,
63
64 pub output: MaybePruned<Option<Output>>,
66}
67
68impl ReceiptClaim {
69 pub fn ok(
72 image_id: impl Into<Digest>,
73 journal: impl Into<MaybePruned<Vec<u8>>>,
74 ) -> ReceiptClaim {
75 Self {
76 pre: MaybePruned::Pruned(image_id.into()),
77 post: MaybePruned::Value(SystemState {
78 pc: 0,
79 merkle_root: Digest::ZERO,
80 }),
81 exit_code: ExitCode::Halted(0),
82 input: None.into(),
83 output: Some(Output {
84 journal: journal.into(),
85 assumptions: MaybePruned::Pruned(Digest::ZERO),
86 })
87 .into(),
88 }
89 }
90
91 pub fn paused(
94 image_id: impl Into<Digest>,
95 journal: impl Into<MaybePruned<Vec<u8>>>,
96 ) -> ReceiptClaim {
97 Self {
98 pre: MaybePruned::Pruned(image_id.into()),
99 post: MaybePruned::Value(SystemState {
100 pc: 0,
101 merkle_root: Digest::ZERO,
102 }),
103 exit_code: ExitCode::Paused(0),
104 input: None.into(),
105 output: Some(Output {
106 journal: journal.into(),
107 assumptions: MaybePruned::Pruned(Digest::ZERO),
108 })
109 .into(),
110 }
111 }
112
113 pub fn decode(flat: &mut VecDeque<u32>) -> Result<Self, DecodeError> {
115 let input = read_sha_halfs(flat)?;
116 let pre = SystemState::decode(flat)?;
117 let post = SystemState::decode(flat)?;
118 let sys_exit = flat
119 .pop_front()
120 .ok_or(risc0_binfmt::DecodeError::EndOfStream)?;
121 let user_exit = flat
122 .pop_front()
123 .ok_or(risc0_binfmt::DecodeError::EndOfStream)?;
124 let exit_code = ExitCode::from_pair(sys_exit, user_exit)?;
125 let output = read_sha_halfs(flat)?;
126
127 Ok(Self {
128 input: MaybePruned::Pruned(input),
129 pre: pre.into(),
130 post: post.into(),
131 exit_code,
132 output: MaybePruned::Pruned(output),
133 })
134 }
135
136 pub fn encode(&self, flat: &mut Vec<u32>) -> Result<(), PrunedValueError> {
138 write_sha_halfs(flat, &self.input.digest::<sha::Impl>());
139 self.pre.as_value()?.encode(flat);
140 self.post.as_value()?.encode(flat);
141 let (sys_exit, user_exit) = self.exit_code.into_pair();
142 flat.push(sys_exit);
143 flat.push(user_exit);
144 write_sha_halfs(flat, &self.output.digest::<sha::Impl>());
145 Ok(())
146 }
147}
148
149impl Digestible for ReceiptClaim {
150 fn digest<S: Sha256>(&self) -> Digest {
152 let (sys_exit, user_exit) = self.exit_code.into_pair();
153 tagged_struct::<S>(
154 "risc0.ReceiptClaim",
155 &[
156 self.input.digest::<S>(),
157 self.pre.digest::<S>(),
158 self.post.digest::<S>(),
159 self.output.digest::<S>(),
160 ],
161 &[sys_exit, user_exit],
162 )
163 }
164}
165
166#[derive(Debug, Copy, Clone)]
168pub enum DecodeError {
169 InvalidExitCode(InvalidExitCodeError),
171 Decode(risc0_binfmt::DecodeError),
173}
174
175impl fmt::Display for DecodeError {
176 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
177 match self {
178 Self::InvalidExitCode(e) => write!(f, "failed to decode receipt claim: {e}"),
179 Self::Decode(e) => write!(f, "failed to decode receipt claim: {e}"),
180 }
181 }
182}
183
184impl From<risc0_binfmt::DecodeError> for DecodeError {
185 fn from(e: risc0_binfmt::DecodeError) -> Self {
186 Self::Decode(e)
187 }
188}
189
190impl From<InvalidExitCodeError> for DecodeError {
191 fn from(e: InvalidExitCodeError) -> Self {
192 Self::InvalidExitCode(e)
193 }
194}
195
196#[cfg(feature = "std")]
197impl std::error::Error for DecodeError {}
198
199#[derive(Clone, Debug, Serialize, Deserialize)]
210#[cfg_attr(test, derive(PartialEq))]
211pub enum Unknown {}
212
213impl Digestible for Unknown {
214 fn digest<S: Sha256>(&self) -> Digest {
215 match *self { }
216 }
217}
218
219impl BorshSerialize for Unknown {
220 fn serialize<W>(&self, _: &mut W) -> core::result::Result<(), borsh::io::Error> {
221 unreachable!("unreachable")
222 }
223}
224
225impl BorshDeserialize for Unknown {
226 fn deserialize_reader<R>(_: &mut R) -> core::result::Result<Self, borsh::io::Error> {
227 unreachable!("unreachable")
228 }
229}
230
231#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
236#[cfg_attr(test, derive(PartialEq))]
237pub struct Input {
238 pub(crate) x: Unknown,
242}
243
244impl Digestible for Input {
245 fn digest<S: Sha256>(&self) -> Digest {
247 match self.x { }
248 }
249}
250
251#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
253#[cfg_attr(test, derive(PartialEq))]
254pub struct Output {
255 pub journal: MaybePruned<Vec<u8>>,
257
258 pub assumptions: MaybePruned<Assumptions>,
267}
268
269impl Digestible for Output {
270 fn digest<S: Sha256>(&self) -> Digest {
272 tagged_struct::<S>(
273 "risc0.Output",
274 &[self.journal.digest::<S>(), self.assumptions.digest::<S>()],
275 &[],
276 )
277 }
278}
279
280#[derive(
288 Clone, Debug, Serialize, Deserialize, Eq, Hash, PartialEq, BorshSerialize, BorshDeserialize,
289)]
290pub struct Assumption {
291 pub claim: Digest,
294
295 pub control_root: Digest,
308}
309
310impl Digestible for Assumption {
311 fn digest<S: Sha256>(&self) -> Digest {
313 tagged_struct::<S>("risc0.Assumption", &[self.claim, self.control_root], &[])
314 }
315}
316
317#[derive(Clone, Default, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
319#[cfg_attr(test, derive(PartialEq))]
320pub struct Assumptions(pub Vec<MaybePruned<Assumption>>);
321
322impl Assumptions {
323 pub fn add(&mut self, assumption: MaybePruned<Assumption>) {
325 self.0.insert(0, assumption);
326 }
327
328 pub fn resolve(&mut self, resolved: &Digest) -> anyhow::Result<()> {
332 let head = self
333 .0
334 .first()
335 .ok_or_else(|| anyhow!("cannot resolve assumption from empty list"))?;
336
337 ensure!(
338 &head.digest::<sha::Impl>() == resolved,
339 "resolved assumption is not equal to the head of the list: {} != {}",
340 resolved,
341 head.digest::<sha::Impl>()
342 );
343
344 self.0 = self.0.split_off(1);
346 Ok(())
347 }
348}
349
350impl Deref for Assumptions {
351 type Target = [MaybePruned<Assumption>];
352
353 fn deref(&self) -> &Self::Target {
354 &self.0
355 }
356}
357
358impl Digestible for Assumptions {
359 fn digest<S: Sha256>(&self) -> Digest {
361 tagged_list::<S>(
362 "risc0.Assumptions",
363 &self.0.iter().map(|a| a.digest::<S>()).collect::<Vec<_>>(),
364 )
365 }
366}
367
368impl MaybePruned<Assumptions> {
369 pub fn is_empty(&self) -> bool {
371 match self {
372 MaybePruned::Value(list) => list.is_empty(),
373 MaybePruned::Pruned(digest) => digest == &Digest::ZERO,
374 }
375 }
376
377 pub fn add(&mut self, assumption: MaybePruned<Assumption>) {
381 match self {
382 MaybePruned::Value(list) => list.add(assumption),
383 MaybePruned::Pruned(list_digest) => {
384 *list_digest = tagged_list_cons::<sha::Impl>(
385 "risc0.Assumptions",
386 &assumption.digest::<sha::Impl>(),
387 &*list_digest,
388 );
389 }
390 }
391 }
392
393 pub fn resolve(&mut self, resolved: &Digest, tail: &Digest) -> anyhow::Result<()> {
400 match self {
401 MaybePruned::Value(list) => list.resolve(resolved),
402 MaybePruned::Pruned(list_digest) => {
403 let reconstructed =
404 tagged_list_cons::<sha::Impl>("risc0.Assumptions", resolved, tail);
405 ensure!(
406 &reconstructed == list_digest,
407 "reconstructed list digest does not match; expected {}, reconstructed {}",
408 list_digest,
409 reconstructed
410 );
411
412 *list_digest = *tail;
414 Ok(())
415 }
416 }
417 }
418}
419
420impl From<Vec<MaybePruned<Assumption>>> for Assumptions {
421 fn from(value: Vec<MaybePruned<Assumption>>) -> Self {
422 Self(value)
423 }
424}
425
426impl From<Vec<Assumption>> for Assumptions {
427 fn from(value: Vec<Assumption>) -> Self {
428 Self(value.into_iter().map(Into::into).collect())
429 }
430}
431
432impl From<Vec<Assumption>> for MaybePruned<Assumptions> {
433 fn from(value: Vec<Assumption>) -> Self {
434 Self::Value(value.into())
435 }
436}
437
438#[derive(Clone, Deserialize, Serialize, BorshSerialize, BorshDeserialize)]
447pub enum MaybePruned<T>
448where
449 T: Clone + Serialize,
450{
451 Value(T),
453
454 Pruned(Digest),
456}
457
458impl<T> MaybePruned<T>
459where
460 T: Clone + Serialize,
461{
462 pub fn value(self) -> Result<T, PrunedValueError> {
464 match self {
465 MaybePruned::Value(value) => Ok(value),
466 MaybePruned::Pruned(digest) => Err(PrunedValueError(digest)),
467 }
468 }
469
470 pub fn as_value(&self) -> Result<&T, PrunedValueError> {
472 match self {
473 MaybePruned::Value(ref value) => Ok(value),
474 MaybePruned::Pruned(ref digest) => Err(PrunedValueError(*digest)),
475 }
476 }
477
478 pub fn as_value_mut(&mut self) -> Result<&mut T, PrunedValueError> {
480 match self {
481 MaybePruned::Value(ref mut value) => Ok(value),
482 MaybePruned::Pruned(ref digest) => Err(PrunedValueError(*digest)),
483 }
484 }
485}
486
487impl<T> From<T> for MaybePruned<T>
488where
489 T: Clone + Serialize,
490{
491 fn from(value: T) -> Self {
492 Self::Value(value)
493 }
494}
495
496impl<T> Digestible for MaybePruned<T>
497where
498 T: Digestible + Clone + Serialize,
499{
500 fn digest<S: Sha256>(&self) -> Digest {
501 match self {
502 MaybePruned::Value(ref val) => val.digest::<S>(),
503 MaybePruned::Pruned(digest) => *digest,
504 }
505 }
506}
507
508impl<T> Default for MaybePruned<T>
509where
510 T: Digestible + Default + Clone + Serialize,
511{
512 fn default() -> Self {
513 MaybePruned::Value(Default::default())
514 }
515}
516
517impl<T> MaybePruned<Option<T>>
518where
519 T: Clone + Serialize,
520{
521 pub fn is_none(&self) -> bool {
524 match self {
525 MaybePruned::Value(Some(_)) => false,
526 MaybePruned::Value(None) => true,
527 MaybePruned::Pruned(digest) => digest == &Digest::ZERO,
528 }
529 }
530
531 pub fn is_some(&self) -> bool {
534 !self.is_none()
535 }
536}
537
538#[cfg(test)]
539impl<T> PartialEq for MaybePruned<T>
540where
541 T: Clone + Serialize + PartialEq,
542{
543 fn eq(&self, other: &Self) -> bool {
544 match (self, other) {
545 (Self::Value(a), Self::Value(b)) => a == b,
546 (Self::Pruned(a), Self::Pruned(b)) => a == b,
547 _ => false,
548 }
549 }
550}
551
552impl<T> fmt::Debug for MaybePruned<T>
553where
554 T: Clone + Serialize + Digestible + fmt::Debug,
555{
556 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
560 let mut builder = fmt.debug_struct("MaybePruned");
561 if let MaybePruned::Value(value) = self {
562 builder.field("value", value);
563 }
564 builder
565 .field("digest", &self.digest::<sha::Impl>())
566 .finish()
567 }
568}
569
570#[derive(Debug, Clone)]
572pub struct PrunedValueError(pub Digest);
573
574impl fmt::Display for PrunedValueError {
575 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
576 write!(f, "value is pruned: {}", &self.0)
577 }
578}
579
580#[cfg(feature = "std")]
581impl std::error::Error for PrunedValueError {}
582
583#[cfg(feature = "prove")]
589pub(crate) trait Merge: Digestible + Sized {
590 fn merge(&self, other: &Self) -> Result<Self, MergeInequalityError>;
592
593 fn merge_with(&mut self, other: &Self) -> Result<(), MergeInequalityError> {
595 *self = self.merge(other)?;
597 Ok(())
598 }
599}
600
601#[cfg(feature = "prove")]
603#[derive(Debug, Clone)]
604pub(crate) struct MergeInequalityError(pub Digest, pub Digest);
605
606#[cfg(feature = "prove")]
607impl fmt::Display for MergeInequalityError {
608 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
609 write!(
610 f,
611 "cannot merge values; left and right are not digest equal: left {}, right {}",
612 hex::encode(self.0),
613 hex::encode(self.1)
614 )
615 }
616}
617
618#[cfg(all(feature = "std", feature = "prove"))]
619impl std::error::Error for MergeInequalityError {}
620
621#[cfg(feature = "prove")]
623trait MergeLeaf: Digestible + PartialEq + Clone + Sized {}
624
625#[cfg(feature = "prove")]
626impl MergeLeaf for SystemState {}
627#[cfg(feature = "prove")]
628impl MergeLeaf for Assumption {}
629#[cfg(feature = "prove")]
630impl MergeLeaf for Vec<u8> {}
631
632#[cfg(feature = "prove")]
633impl<T: MergeLeaf> Merge for T {
634 fn merge(&self, other: &Self) -> Result<Self, MergeInequalityError> {
635 if self != other {
636 return Err(MergeInequalityError(
637 self.digest::<sha::Impl>(),
638 other.digest::<sha::Impl>(),
639 ));
640 }
641
642 Ok(self.clone())
643 }
644}
645
646#[cfg(feature = "prove")]
647impl<T> Merge for MaybePruned<T>
648where
649 T: Merge + Serialize + Clone,
650{
651 fn merge(&self, other: &Self) -> Result<Self, MergeInequalityError> {
652 let check_eq = || {
653 if self.digest::<sha::Impl>() != other.digest::<sha::Impl>() {
654 Err(MergeInequalityError(
655 self.digest::<sha::Impl>(),
656 other.digest::<sha::Impl>(),
657 ))
658 } else {
659 Ok(())
660 }
661 };
662
663 Ok(match (self, other) {
664 (MaybePruned::Value(left), MaybePruned::Value(right)) => {
665 MaybePruned::Value(left.merge(right)?)
666 }
667 (MaybePruned::Value(_), MaybePruned::Pruned(_)) => {
668 check_eq()?;
669 self.clone()
670 }
671 (MaybePruned::Pruned(_), MaybePruned::Value(_)) => {
672 check_eq()?;
673 other.clone()
674 }
675 (MaybePruned::Pruned(_), MaybePruned::Pruned(_)) => {
676 check_eq()?;
677 self.clone()
678 }
679 })
680 }
681}
682
683#[cfg(feature = "prove")]
684impl<T: Merge> Merge for Option<T> {
685 fn merge(&self, other: &Self) -> Result<Self, MergeInequalityError> {
686 match (self, other) {
687 (Some(left), Some(right)) => Some(left.merge(right)).transpose(),
688 (None, None) => Ok(None),
689 _ => Err(MergeInequalityError(
690 self.digest::<sha::Impl>(),
691 other.digest::<sha::Impl>(),
692 )),
693 }
694 }
695}
696
697#[cfg(feature = "prove")]
698impl Merge for Assumptions {
699 fn merge(&self, other: &Self) -> Result<Self, MergeInequalityError> {
700 if self.0.len() != other.0.len() {
701 return Err(MergeInequalityError(
702 self.digest::<sha::Impl>(),
703 other.digest::<sha::Impl>(),
704 ));
705 }
706 Ok(Assumptions(
707 self.0
708 .iter()
709 .zip(other.0.iter())
710 .map(|(left, right)| left.merge(right))
711 .collect::<Result<Vec<_>, _>>()?,
712 ))
713 }
714}
715
716#[cfg(feature = "prove")]
717impl Merge for Input {
718 fn merge(&self, _other: &Self) -> Result<Self, MergeInequalityError> {
719 match self.x { }
720 }
721}
722
723#[cfg(feature = "prove")]
724impl Merge for Output {
725 fn merge(&self, other: &Self) -> Result<Self, MergeInequalityError> {
726 Ok(Self {
727 journal: self.journal.merge(&other.journal)?,
728 assumptions: self.assumptions.merge(&other.assumptions)?,
729 })
730 }
731}
732
733#[cfg(feature = "prove")]
734impl Merge for ReceiptClaim {
735 fn merge(&self, other: &Self) -> Result<Self, MergeInequalityError> {
736 if self.exit_code != other.exit_code {
737 return Err(MergeInequalityError(
738 self.digest::<sha::Impl>(),
739 other.digest::<sha::Impl>(),
740 ));
741 }
742 Ok(Self {
743 pre: self.pre.merge(&other.pre)?,
744 post: self.post.merge(&other.post)?,
745 exit_code: self.exit_code,
746 input: self.input.merge(&other.input)?,
747 output: self.output.merge(&other.output)?,
748 })
749 }
750}
751
752#[cfg(feature = "prove")]
753#[cfg(test)]
754mod tests {
755 use hex::FromHex;
756
757 use super::{Assumptions, ExitCode, MaybePruned, Merge, Output, ReceiptClaim, SystemState};
758 use crate::sha::{Digest, Digestible};
759
760 trait RandPrune {
762 fn rand_prune(&self) -> Self;
763 }
764
765 impl RandPrune for MaybePruned<ReceiptClaim> {
766 fn rand_prune(&self) -> Self {
767 match (self, rand::random::<bool>()) {
768 (Self::Value(x), true) => Self::Pruned(x.digest()),
769 (Self::Value(x), false) => ReceiptClaim {
770 pre: x.pre.rand_prune(),
771 post: x.post.rand_prune(),
772 exit_code: x.exit_code,
773 input: x.input.clone(),
774 output: x.output.rand_prune(),
775 }
776 .into(),
777 (Self::Pruned(x), _) => Self::Pruned(*x),
778 }
779 }
780 }
781
782 impl RandPrune for MaybePruned<SystemState> {
783 fn rand_prune(&self) -> Self {
784 match (self, rand::random::<bool>()) {
785 (Self::Value(x), true) => Self::Pruned(x.digest()),
786 (Self::Value(x), false) => SystemState {
787 pc: x.pc,
788 merkle_root: x.merkle_root,
789 }
790 .into(),
791 (Self::Pruned(x), _) => Self::Pruned(*x),
792 }
793 }
794 }
795
796 impl RandPrune for MaybePruned<Option<Output>> {
797 fn rand_prune(&self) -> Self {
798 match (self, rand::random::<bool>()) {
799 (Self::Value(x), true) => Self::Pruned(x.digest()),
800 (Self::Value(x), false) => x
801 .as_ref()
802 .map(|o| Output {
803 journal: o.journal.rand_prune(),
804 assumptions: o.assumptions.rand_prune(),
805 })
806 .into(),
807 (Self::Pruned(x), _) => Self::Pruned(*x),
808 }
809 }
810 }
811
812 impl RandPrune for MaybePruned<Vec<u8>> {
813 fn rand_prune(&self) -> Self {
814 match (self, rand::random::<bool>()) {
815 (Self::Value(x), true) => Self::Pruned(x.digest()),
816 (Self::Value(x), false) => x.clone().into(),
817 (Self::Pruned(x), _) => Self::Pruned(*x),
818 }
819 }
820 }
821
822 impl RandPrune for MaybePruned<Assumptions> {
823 fn rand_prune(&self) -> Self {
824 match (self, rand::random::<bool>()) {
825 (Self::Value(x), true) => Self::Pruned(x.digest()),
826 (Self::Value(x), false) => x.clone().into(),
827 (Self::Pruned(x), _) => Self::Pruned(*x),
828 }
829 }
830 }
831
832 #[test]
833 fn merge_receipt_claim() {
834 let claim = MaybePruned::Value(ReceiptClaim {
835 pre: SystemState {
836 pc: 2100484,
837 merkle_root: Digest::from_hex(
838 "9095da07d84ccc170c5113e3dafdf0531700f0b3f0c627acc9f0329440d984fa",
839 )
840 .unwrap(),
841 }
842 .into(),
843 post: SystemState {
844 pc: 2297164,
845 merkle_root: Digest::from_hex(
846 "223651656250c0cf2f1c3f8923ef3d2c8624a361830492ffec6450e1930fb07d",
847 )
848 .unwrap(),
849 }
850 .into(),
851 exit_code: ExitCode::Halted(0),
852 input: None.into(),
853 output: MaybePruned::Value(Some(Output {
854 journal: MaybePruned::Value(b"hello world".to_vec()),
855 assumptions: MaybePruned::Value(Assumptions(vec![
856 MaybePruned::Pruned(Digest::ZERO),
857 MaybePruned::Pruned(Digest::ZERO),
858 ])),
859 })),
860 });
861
862 for _ in 0..10000 {
864 let left = claim.rand_prune();
865 let right = claim.rand_prune();
866
867 assert_eq!(left.merge(&right).unwrap().digest(), claim.digest());
868 }
869 }
870}