1use arbitrary::Arbitrary;
2use get_size2::GetSize;
3use isa::program::Program;
4use serde::Deserialize;
5use serde::Serialize;
6use twenty_first::prelude::*;
7
8use crate::error::ProofStreamError;
9use crate::proof_stream::ProofStream;
10
11pub const CURRENT_VERSION: u32 = 0;
34
35#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, GetSize, BFieldCodec, Arbitrary)]
38pub struct Proof(pub Vec<BFieldElement>);
39
40impl Proof {
41 pub fn padded_height(&self) -> Result<usize, ProofStreamError> {
45 let mut log_2_padded_heights = ProofStream::try_from(self)?
46 .items
47 .into_iter()
48 .filter_map(|item| item.try_into_log2_padded_height().ok());
49
50 let log_2_padded_height = log_2_padded_heights
51 .next()
52 .ok_or(ProofStreamError::NoLog2PaddedHeight)?;
53 if log_2_padded_heights.next().is_some() {
54 return Err(ProofStreamError::TooManyLog2PaddedHeights);
55 }
56
57 Ok(1 << log_2_padded_height)
58 }
59}
60
61#[derive(
67 Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, GetSize, BFieldCodec, Arbitrary,
68)]
69pub struct Claim {
70 pub program_digest: Digest,
72
73 pub version: u32,
79
80 pub input: Vec<BFieldElement>,
82
83 pub output: Vec<BFieldElement>,
85}
86
87impl Claim {
88 pub fn new(program_digest: Digest) -> Self {
93 Self {
94 program_digest,
95 version: CURRENT_VERSION,
96 input: vec![],
97 output: vec![],
98 }
99 }
100
101 #[must_use]
102 pub fn about_program(program: &Program) -> Self {
103 Self::new(program.hash())
104 }
105
106 #[must_use]
107 pub fn with_input(mut self, input: impl Into<Vec<BFieldElement>>) -> Self {
108 self.input = input.into();
109 self
110 }
111
112 #[must_use]
113 pub fn with_output(mut self, output: Vec<BFieldElement>) -> Self {
114 self.output = output;
115 self
116 }
117
118 #[must_use]
119 pub fn about_version(mut self, version: u32) -> Self {
120 self.version = version;
121 self
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use assert2::assert;
128 use proptest::collection::vec;
129 use proptest::prelude::*;
130 use proptest_arbitrary_interop::arb;
131 use rand::prelude::*;
132 use test_strategy::proptest;
133
134 use crate::prelude::*;
135 use crate::proof_item::ProofItem;
136
137 use super::*;
138
139 impl Default for Claim {
140 fn default() -> Self {
142 Self::new(Digest::default())
143 }
144 }
145
146 #[test]
147 fn claim_accepts_various_types_for_public_input() {
148 let _claim = Claim::default()
149 .with_input(bfe_vec![42])
150 .with_input(bfe_array![42])
151 .with_input(PublicInput::new(bfe_vec![42]));
152 }
153
154 #[proptest]
155 fn decode_proof(#[strategy(arb())] proof: Proof) {
156 let encoded = proof.encode();
157 let decoded = *Proof::decode(&encoded).unwrap();
158 prop_assert_eq!(proof, decoded);
159 }
160
161 #[proptest]
162 fn decode_claim(#[strategy(arb())] claim: Claim) {
163 let encoded = claim.encode();
164 let decoded = *Claim::decode(&encoded).unwrap();
165 prop_assert_eq!(claim, decoded);
166 }
167
168 #[proptest(cases = 10)]
169 fn proof_with_no_padded_height_gives_err(#[strategy(arb())] root: Digest) {
170 let mut proof_stream = ProofStream::new();
171 proof_stream.enqueue(ProofItem::MerkleRoot(root));
172 let proof: Proof = proof_stream.into();
173 let maybe_padded_height = proof.padded_height();
174 assert!(maybe_padded_height.is_err());
175 }
176
177 #[proptest(cases = 10)]
178 fn proof_with_multiple_padded_height_gives_err(#[strategy(arb())] root: Digest) {
179 let mut proof_stream = ProofStream::new();
180 proof_stream.enqueue(ProofItem::Log2PaddedHeight(8));
181 proof_stream.enqueue(ProofItem::MerkleRoot(root));
182 proof_stream.enqueue(ProofItem::Log2PaddedHeight(7));
183 let proof: Proof = proof_stream.into();
184 let maybe_padded_height = proof.padded_height();
185 assert!(maybe_padded_height.is_err());
186 }
187
188 #[proptest]
189 fn decoding_arbitrary_proof_data_does_not_panic(
190 #[strategy(vec(arb(), 0..1_000))] proof_data: Vec<BFieldElement>,
191 ) {
192 let _proof = Proof::decode(&proof_data);
193 }
194
195 #[test]
196 fn current_proof_version_is_still_current() {
197 let program = triton_program! {
198 pick 11 pick 12 pick 13 pick 14 pick 15
199 read_io 5 assert_vector halt
200 };
201 let claim = Claim::about_program(&program).with_input(program.hash());
202
203 let input = claim.input.clone().into();
204 let non_determinism = NonDeterminism::default();
205 let (aet, _) = VM::trace_execution(program, input, non_determinism).unwrap();
206
207 let mut rng = StdRng::seed_from_u64(4742841043836029231);
208 let proof = Prover::default()
209 .set_randomness_seed_which_may_break_zero_knowledge(rng.random())
210 .prove(&claim, &aet)
211 .unwrap();
212
213 insta::assert_snapshot!(
214 Tip5::hash(&proof),
215 @"09201244033942307448,\
216 02199220141408935358,\
217 14798078607418975656,\
218 16178332457365390929,\
219 00369058580658580912",
220 );
221 }
222}