risc0_ethereum_contracts/
receipt.rs1use alloy_primitives::Bytes;
16use risc0_aggregation::{
17 decode_set_inclusion_seal, SetInclusionDecodingError, SetInclusionEncodingError,
18 SetInclusionReceipt,
19};
20use risc0_zkvm::{sha::Digest, FakeReceipt, InnerReceipt, ReceiptClaim};
21use serde::{Deserialize, Serialize};
22use thiserror::Error;
23
24use crate::{
25 encode_seal,
26 groth16::decode_groth16_seal,
27 selector::{Selector, SelectorError, SelectorType},
28};
29
30#[derive(Clone, Debug, Deserialize, Serialize)]
32pub enum Receipt {
33 Base(Box<risc0_zkvm::Receipt>),
34 SetInclusion(Box<SetInclusionReceipt<ReceiptClaim>>),
35}
36
37impl Receipt {
38 pub fn abi_encode_seal(&self) -> Result<Vec<u8>, SetInclusionEncodingError> {
40 match self {
41 Receipt::Base(receipt) => {
42 encode_seal(receipt).map_err(|_| SetInclusionEncodingError::UnsupportedReceipt)
43 }
44 Receipt::SetInclusion(receipt) => receipt.abi_encode_seal(),
45 }
46 }
47
48 pub fn receipt(&self) -> Option<&risc0_zkvm::Receipt> {
50 match self {
51 Receipt::Base(receipt) => Some(receipt),
52 _ => None,
53 }
54 }
55
56 pub fn set_inclusion_receipt(&self) -> Option<&SetInclusionReceipt<ReceiptClaim>> {
58 match self {
59 Receipt::SetInclusion(receipt) => Some(receipt),
60 _ => None,
61 }
62 }
63}
64
65#[derive(Debug, Error)]
67#[non_exhaustive]
68pub enum DecodingError {
69 #[error("Seal too short")]
70 SealTooShort,
71 #[error("Unsupported selector {0:?}")]
72 UnsupportedSelector([u8; 4]),
73 #[error("Selector error: {0}")]
74 SelectorError(#[from] SelectorError),
75 #[error("Decoding error: {0}")]
76 SetInclusionError(#[from] SetInclusionDecodingError),
77 #[error("Decoding error: {0}")]
78 Anyhow(#[from] anyhow::Error),
79}
80
81pub fn decode_seal(
83 seal: Bytes,
84 image_id: impl Into<Digest>,
85 journal: impl Into<Vec<u8>>,
86) -> Result<Receipt, DecodingError> {
87 let journal = journal.into();
88 let claim = ReceiptClaim::ok(image_id, journal.clone());
89 decode_seal_with_claim(seal, claim, journal)
90}
91
92pub fn decode_seal_with_claim(
94 seal: Bytes,
95 claim: ReceiptClaim,
96 journal: impl Into<Vec<u8>>,
97) -> Result<Receipt, DecodingError> {
98 if seal.len() < 4 {
99 return Err(DecodingError::SealTooShort);
100 }
101 let selector = [seal[0], seal[1], seal[2], seal[3]];
102 let selector = Selector::from_bytes(selector)
103 .ok_or_else(|| DecodingError::UnsupportedSelector(selector))?;
104 match selector.get_type() {
105 SelectorType::FakeReceipt => {
106 let receipt = risc0_zkvm::Receipt::new(
107 InnerReceipt::Fake(FakeReceipt::new(claim)),
108 journal.into(),
109 );
110 Ok(Receipt::Base(Box::new(receipt)))
111 }
112 SelectorType::Groth16 => {
113 let verifier_parameters = selector.verifier_parameters_digest()?;
114 let receipt =
115 decode_groth16_seal(seal, claim, journal.into(), Some(verifier_parameters))?;
116 Ok(Receipt::Base(Box::new(receipt)))
117 }
118 SelectorType::SetVerifier => {
119 let verifier_parameters = selector.verifier_parameters_digest()?;
120 let receipt = decode_set_inclusion_seal(&seal, claim, verifier_parameters)?;
121 Ok(Receipt::SetInclusion(Box::new(receipt)))
122 }
123 }
124}