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