1use std::fmt::Debug;
2use std::marker::PhantomData;
3
4use triton_vm::prelude::*;
5
6use crate::prelude::*;
7
8#[derive(Clone, Debug)]
13pub struct VerifyNdSiIntegrity<PreloadedData: TasmObject + Clone + Debug> {
14 _phantom_data: PhantomData<PreloadedData>,
15}
16
17impl<T: TasmObject + Clone + Debug> Default for VerifyNdSiIntegrity<T> {
18 fn default() -> Self {
19 Self {
20 _phantom_data: PhantomData,
21 }
22 }
23}
24
25impl<T: TasmObject + Clone + Debug> BasicSnippet for VerifyNdSiIntegrity<T> {
26 fn parameters(&self) -> Vec<(DataType, String)> {
27 vec![(DataType::VoidPointer, "*struct".to_owned())]
28 }
29
30 fn return_values(&self) -> Vec<(DataType, String)> {
31 vec![(DataType::U32, "struct size".to_owned())]
32 }
33
34 fn entrypoint(&self) -> String {
35 let name = T::label_friendly_name();
36 format!("tasmlib_structure_verify_nd_si_integrity___{name}")
37 }
38
39 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
40 let entrypoint = self.entrypoint();
41 let si_integrity_check_code = T::compute_size_and_assert_valid_size_indicator(library);
42
43 triton_asm!(
44 {entrypoint}:
45 dup 0
48 {&si_integrity_check_code}
49 dup 1
53 pop_count pop 1
57 swap 1
60 dup 1
61 add
62 pop_count pop 1
66 return
69 )
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use std::fmt::Debug;
76
77 use arbitrary::Arbitrary;
78 use arbitrary::Unstructured;
79 use num_traits::ConstZero;
80 use twenty_first::util_types::mmr::mmr_successor_proof::MmrSuccessorProof;
81
82 use super::*;
83 use crate::memory::encode_to_memory;
84 use crate::neptune::neptune_like_types_for_tests::*;
85 use crate::test_prelude::*;
86
87 impl<T> VerifyNdSiIntegrity<T>
88 where
89 T: TasmObject + BFieldCodec + for<'a> Arbitrary<'a> + Debug + Clone,
90 {
91 fn initial_state(&self, address: BFieldElement, t: T) -> AccessorInitialState {
92 let mut memory = HashMap::default();
93 encode_to_memory(&mut memory, address, &t);
94
95 AccessorInitialState {
96 stack: [self.init_stack_for_isolated_run(), vec![address]].concat(),
97 memory,
98 }
99 }
100
101 fn prepare_random_object(&self, randomness: &[u8]) -> T {
102 let unstructured = Unstructured::new(randomness);
103 T::arbitrary_take_rest(unstructured).unwrap()
104 }
105 }
106
107 impl<T> Accessor for VerifyNdSiIntegrity<T>
108 where
109 T: TasmObject + for<'a> Arbitrary<'a> + Debug + Clone,
110 {
111 fn rust_shadow(
112 &self,
113 stack: &mut Vec<BFieldElement>,
114 memory: &HashMap<BFieldElement, BFieldElement>,
115 ) -> Result<(), RustShadowError> {
116 let pointer = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
118 let obj = T::decode_from_memory(memory, pointer)
119 .map_err(|_| RustShadowError::DecodingError)?;
120 let encoding_len = obj.encode().len();
121 let encoding_len: u32 = encoding_len
122 .try_into()
123 .map_err(|_| RustShadowError::UsizeToU32Error)?;
124
125 let start_address: u32 = pointer
127 .value()
128 .try_into()
129 .map_err(|_| RustShadowError::U64ToU32Error)?;
130 let _end_address = start_address
131 .checked_add(encoding_len)
132 .ok_or(RustShadowError::ArithmeticOverflow)?;
133
134 stack.push(bfe!(u64::from(encoding_len)));
135
136 Ok(())
137 }
138
139 fn pseudorandom_initial_state(
140 &self,
141 seed: [u8; 32],
142 _bench_case: Option<BenchmarkCase>,
143 ) -> AccessorInitialState {
144 let mut rng = StdRng::from_seed(seed);
145
146 let t: T = {
147 let mut randomness = [0u8; 100000];
148 rng.fill(&mut randomness);
149 self.prepare_random_object(&randomness)
150 };
151
152 let address: u32 = rng.random_range(0..(1 << 30));
153 let address = bfe!(address);
154 self.initial_state(address, t)
155 }
156
157 fn corner_case_initial_states(&self) -> Vec<AccessorInitialState> {
158 let empty_struct: T = {
161 let unstructured = Unstructured::new(&[]);
162 T::arbitrary_take_rest(unstructured).unwrap()
163 };
164
165 println!("empty_struct:\n{empty_struct:?}");
166 let empty_struct_at_zero = self.initial_state(BFieldElement::ZERO, empty_struct);
167
168 vec![empty_struct_at_zero]
169 }
170 }
171
172 macro_rules! test_case {
173 (fn $test_name:ident for $t:ty) => {
174 #[macro_rules_attr::apply(test)]
175 fn $test_name() {
176 ShadowedAccessor::new(VerifyNdSiIntegrity::<$t>::default()).test();
177 }
178 };
179 (fn $test_name:ident for new type $t:ty: $($type_declaration:tt)*) => {
180 #[macro_rules_attr::apply(test)]
181 fn $test_name() {
182 #[derive(Debug, Clone, TasmObject, BFieldCodec, Arbitrary)]
183 $($type_declaration)*
184 ShadowedAccessor::new(VerifyNdSiIntegrity::<$t>::default()).test();
185 }
186 };
187 }
188
189 mod simple_struct {
190 use super::*;
191 use crate::test_helpers::negative_test;
192 use crate::test_helpers::test_assertion_failure;
193
194 #[derive(Debug, Clone, TasmObject, BFieldCodec, Arbitrary)]
195 struct TestStruct {
196 a: Vec<u128>,
197 b: Digest,
198 c: Vec<Digest>,
199 }
200
201 test_case! { fn test_pbt_simple_struct for TestStruct }
202
203 #[macro_rules_attr::apply(test)]
204 fn struct_not_contained_in_nd_region() {
205 let snippet = VerifyNdSiIntegrity::<TestStruct>::default();
206
207 let t = snippet.prepare_random_object(&[]);
208 let begin_address = bfe!((1u64 << 32) - 4);
209 let init_state = snippet.initial_state(begin_address, t.clone());
210
211 let actual_size = t.encode().len();
212 let end_address = begin_address + bfe!(actual_size as u64);
213 let expected_err =
214 InstructionError::OpStackError(OpStackError::FailedU32Conversion(end_address));
215 negative_test(
216 &ShadowedAccessor::new(snippet),
217 init_state.into(),
218 &[expected_err],
219 )
220 }
221
222 #[macro_rules_attr::apply(test)]
223 fn struct_does_not_start_in_nd_region() {
224 let snippet = VerifyNdSiIntegrity::<TestStruct>::default();
225
226 let begin_address = bfe!(-4);
227 let init_state =
228 snippet.initial_state(begin_address, snippet.prepare_random_object(&[]));
229 let expected_err =
230 InstructionError::OpStackError(OpStackError::FailedU32Conversion(begin_address));
231 negative_test(
232 &ShadowedAccessor::new(snippet),
233 init_state.into(),
234 &[expected_err],
235 )
236 }
237
238 #[macro_rules_attr::apply(test)]
239 fn lie_about_digest_vec_size() {
240 let snippet = VerifyNdSiIntegrity::<TestStruct>::default();
241
242 let begin_address = bfe!(4);
243 let mut init_state =
244 snippet.initial_state(begin_address, snippet.prepare_random_object(&[]));
245 let true_value = init_state.memory[&begin_address];
246 init_state
247 .memory
248 .insert(begin_address, true_value + bfe!(1));
249
250 test_assertion_failure(&ShadowedAccessor::new(snippet), init_state.into(), &[181])
251 }
252
253 #[macro_rules_attr::apply(test)]
254 fn lie_about_digest_vec_len() {
255 let snippet = VerifyNdSiIntegrity::<TestStruct>::default();
256
257 let begin_address = bfe!(4);
258 let mut init_state =
259 snippet.initial_state(begin_address, snippet.prepare_random_object(&[42u8; 20000]));
260 let vec_digest_len_indicator = begin_address + bfe!(1);
261 let true_value = init_state.memory[&vec_digest_len_indicator];
262 init_state
263 .memory
264 .insert(vec_digest_len_indicator, true_value + bfe!(1));
265
266 test_assertion_failure(&ShadowedAccessor::new(snippet), init_state.into(), &[181])
267 }
268 }
269
270 mod option_types {
271 use super::*;
272 use crate::test_helpers::test_assertion_failure;
273
274 #[derive(Debug, Clone, TasmObject, BFieldCodec, Arbitrary)]
275 struct StatSizedPayload {
276 a: Option<Digest>,
277 }
278
279 #[derive(Debug, Clone, TasmObject, BFieldCodec, Arbitrary, Default)]
280 struct DynSizedPayload {
281 a: Option<Vec<u128>>,
282 b: Digest,
283 c: Vec<Vec<BFieldElement>>,
284 d: Option<Vec<Option<BFieldElement>>>,
285 }
286
287 test_case! {fn test_option_stat_sized_elem for StatSizedPayload }
288 test_case! {fn test_option_dyn_sized_elem for DynSizedPayload }
289
290 #[macro_rules_attr::apply(test)]
291 fn lie_about_option_payload_field_size() {
292 let snippet = VerifyNdSiIntegrity::<DynSizedPayload>::default();
293
294 let begin_address = bfe!(4);
295 let randomness = rand::random::<[u8; 100_000]>();
296 let obj = snippet.prepare_random_object(&randomness);
297 let true_init_state = snippet.initial_state(begin_address, obj.clone());
298
299 let mut manipulated_si_outer = true_init_state.clone();
301 let outer_option_payload_si_ptr = begin_address; let true_value = true_init_state.memory[&outer_option_payload_si_ptr];
303 manipulated_si_outer
304 .memory
305 .insert(outer_option_payload_si_ptr, true_value + bfe!(1));
306
307 test_assertion_failure(
308 &ShadowedAccessor::new(snippet),
309 manipulated_si_outer.into(),
310 &[181],
311 );
312 }
313
314 #[macro_rules_attr::apply(test)]
315 fn illegal_discriminant_value_for_option() {
316 let snippet = VerifyNdSiIntegrity::<DynSizedPayload>::default();
317
318 let obj = DynSizedPayload::default();
319 let begin_address = bfe!(4);
320 let mut manipulated_init_state = snippet.initial_state(begin_address, obj.clone());
321 let option_discriminant_ptr = begin_address + bfe!(1);
322 manipulated_init_state
323 .memory
324 .insert(option_discriminant_ptr, bfe!(2));
325
326 test_assertion_failure(
327 &ShadowedAccessor::new(snippet.clone()),
328 manipulated_init_state.into(),
329 &[200],
330 );
331 }
332
333 #[macro_rules_attr::apply(test)]
334 fn lie_about_option_payload_size() {
335 let snippet = VerifyNdSiIntegrity::<DynSizedPayload>::default();
336
337 let obj = DynSizedPayload {
338 d: Some(vec![Some(bfe!(14)), None, Some(bfe!(15))]),
339 ..Default::default()
340 };
341 let begin_address = bfe!(4);
342 let true_init_state = snippet.initial_state(begin_address, obj.clone());
343
344 let mut add_one = true_init_state.clone();
346 let len_of_dyn_sized_list_elem_0 = begin_address + bfe!(3);
347 let true_value = true_init_state.memory[&len_of_dyn_sized_list_elem_0];
348 add_one
349 .memory
350 .insert(len_of_dyn_sized_list_elem_0, true_value + bfe!(1));
351
352 test_assertion_failure(
353 &ShadowedAccessor::new(snippet.clone()),
354 add_one.into(),
355 &[211],
356 );
357
358 let mut sub_one = true_init_state.clone();
359 sub_one
360 .memory
361 .insert(len_of_dyn_sized_list_elem_0, true_value - bfe!(1));
362
363 test_assertion_failure(&ShadowedAccessor::new(snippet), sub_one.into(), &[211]);
364 }
365 }
366
367 test_case! { fn test_stat_sized_tuple for new type TestStruct:
368 struct TestStruct { field: (Digest, XFieldElement) }
369 }
370
371 test_case! { fn test_dyn_state_sized_tuple_right_dyn for new type RightIsDyn:
372 struct RightIsDyn { field: (Digest, Vec<XFieldElement>) }
373 }
374
375 test_case! { fn test_dyn_state_sized_tuple_left_dyn for new type LeftIsDyn:
376 struct LeftIsDyn { field: (Vec<XFieldElement>, Digest) }
377 }
378
379 test_case! { fn test_dyn_state_sized_tuple_both_dyn for new type BothDyn:
380 struct BothDyn { field: (Vec<XFieldElement>, Vec<XFieldElement>) }
381 }
382
383 test_case! { fn proof for Proof }
384 test_case! { fn coin for CoinLookalike }
385 test_case! { fn utxo for UtxoLookalike }
386 test_case! { fn salted_utxos for SaltedUtxosLookalike }
387 test_case! { fn collect_lock_scripts_witness for CollectLockScriptsWitnessLookalike }
388 test_case! { fn collect_type_scripts_witness for CollectTypeScriptsWitnessLookalike }
389 test_case! { fn claim for Claim }
390 test_case! { fn neptune_coins for NeptuneCoinsLookalike }
391 test_case! { fn option_neptune_coins for Option<NeptuneCoinsLookalike> }
392 test_case! { fn chunk for ChunkLookalike }
393 test_case! { fn chunk_dictionary for ChunkDictionaryLookalike }
394 test_case! { fn proof_collection for ProofCollectionLookalike }
395 test_case! { fn absolute_index_set for AbsoluteIndexSetLookalike }
396 test_case! { fn removal_record for RemovalRecordLookalike }
397 test_case! { fn addition_record for AdditionRecordLookalike }
398 test_case! { fn public_announcement for PublicAnnouncementLookalike }
399 test_case! { fn timestamp for TimestampLookalike }
400 test_case! { fn transaction_kernel for TransactionKernelLookalike }
401 test_case! { fn kernel_to_outputs_witness for KernelToOutputsWitnessLookalike }
402 test_case! { fn merge_witness for MergeWitnessLookalike }
403 test_case! { fn ms_membership_proof for MsMembershipProofLookalike }
404 test_case! { fn removal_records_witness for RemovalRecordsIntegrityWitnessLookalike }
405 test_case! { fn update_witness for UpdateWitnessLookalike }
406 test_case! { fn lock_script_and_witness for LockScriptAndWitnessLookalike }
407 test_case! { fn mutator_set_accumulator for MutatorSetAccumulatorLookalike }
408 test_case! { fn primitive_witness for PrimitiveWitnessLookalike }
409 test_case! { fn mmr_successor_proof for MmrSuccessorProof }
410}
411
412#[cfg(test)]
413mod benches {
414 use super::*;
415 use crate::neptune::neptune_like_types_for_tests::ProofCollectionLookalike;
416 use crate::neptune::neptune_like_types_for_tests::TransactionKernelLookalike;
417 use crate::test_prelude::*;
418
419 #[macro_rules_attr::apply(test)]
420 fn bench_proof_collection_lookalike() {
421 ShadowedAccessor::new(VerifyNdSiIntegrity::<ProofCollectionLookalike>::default()).bench();
422 }
423
424 #[macro_rules_attr::apply(test)]
425 fn bench_transaction_kernel_lookalike() {
426 ShadowedAccessor::new(VerifyNdSiIntegrity::<TransactionKernelLookalike>::default()).bench();
427 }
428}