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 inputs(&self) -> Vec<(DataType, String)> {
27 vec![(DataType::VoidPointer, "*struct".to_owned())]
28 }
29
30 fn outputs(&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 ) {
116 let pointer = stack.pop().unwrap();
118 let obj = T::decode_from_memory(memory, pointer).unwrap();
119 let encoding_len = obj.encode().len();
120 let encoding_len: u32 = encoding_len.try_into().unwrap();
121
122 let start_address: u32 = pointer.value().try_into().unwrap();
124 let _end_address = start_address.checked_add(encoding_len).unwrap();
125
126 stack.push(bfe!(obj.encode().len() as u64));
127 }
128
129 fn pseudorandom_initial_state(
130 &self,
131 seed: [u8; 32],
132 _bench_case: Option<BenchmarkCase>,
133 ) -> AccessorInitialState {
134 let mut rng = StdRng::from_seed(seed);
135
136 let t: T = {
137 let mut randomness = [0u8; 100000];
138 rng.fill(&mut randomness);
139 self.prepare_random_object(&randomness)
140 };
141
142 let address: u32 = rng.random_range(0..(1 << 30));
143 let address = bfe!(address);
144 self.initial_state(address, t)
145 }
146
147 fn corner_case_initial_states(&self) -> Vec<AccessorInitialState> {
148 let empty_struct: T = {
151 let unstructured = Unstructured::new(&[]);
152 T::arbitrary_take_rest(unstructured).unwrap()
153 };
154
155 println!("empty_struct:\n{empty_struct:?}");
156 let empty_struct_at_zero = self.initial_state(BFieldElement::ZERO, empty_struct);
157
158 vec![empty_struct_at_zero]
159 }
160 }
161
162 macro_rules! test_case {
163 (fn $test_name:ident for $t:ty) => {
164 #[test]
165 fn $test_name() {
166 ShadowedAccessor::new(VerifyNdSiIntegrity::<$t>::default()).test();
167 }
168 };
169 (fn $test_name:ident for new type $t:ty: $($type_declaration:tt)*) => {
170 #[test]
171 fn $test_name() {
172 #[derive(Debug, Clone, TasmObject, BFieldCodec, Arbitrary)]
173 $($type_declaration)*
174 ShadowedAccessor::new(VerifyNdSiIntegrity::<$t>::default()).test();
175 }
176 };
177 }
178
179 mod simple_struct {
180 use super::*;
181 use crate::test_helpers::negative_test;
182 use crate::test_helpers::test_assertion_failure;
183
184 #[derive(Debug, Clone, TasmObject, BFieldCodec, Arbitrary)]
185 struct TestStruct {
186 a: Vec<u128>,
187 b: Digest,
188 c: Vec<Digest>,
189 }
190
191 test_case! { fn test_pbt_simple_struct for TestStruct }
192
193 #[test]
194 fn struct_not_contained_in_nd_region() {
195 let snippet = VerifyNdSiIntegrity::<TestStruct>::default();
196
197 let t = snippet.prepare_random_object(&[]);
198 let begin_address = bfe!((1u64 << 32) - 4);
199 let init_state = snippet.initial_state(begin_address, t.clone());
200
201 let actual_size = t.encode().len();
202 let end_address = begin_address + bfe!(actual_size as u64);
203 let expected_err =
204 InstructionError::OpStackError(OpStackError::FailedU32Conversion(end_address));
205 negative_test(
206 &ShadowedAccessor::new(snippet),
207 init_state.into(),
208 &[expected_err],
209 )
210 }
211
212 #[test]
213 fn struct_does_not_start_in_nd_region() {
214 let snippet = VerifyNdSiIntegrity::<TestStruct>::default();
215
216 let begin_address = bfe!(-4);
217 let init_state =
218 snippet.initial_state(begin_address, snippet.prepare_random_object(&[]));
219 let expected_err =
220 InstructionError::OpStackError(OpStackError::FailedU32Conversion(begin_address));
221 negative_test(
222 &ShadowedAccessor::new(snippet),
223 init_state.into(),
224 &[expected_err],
225 )
226 }
227
228 #[test]
229 fn lie_about_digest_vec_size() {
230 let snippet = VerifyNdSiIntegrity::<TestStruct>::default();
231
232 let begin_address = bfe!(4);
233 let mut init_state =
234 snippet.initial_state(begin_address, snippet.prepare_random_object(&[]));
235 let true_value = init_state.memory[&begin_address];
236 init_state
237 .memory
238 .insert(begin_address, true_value + bfe!(1));
239
240 test_assertion_failure(&ShadowedAccessor::new(snippet), init_state.into(), &[181])
241 }
242
243 #[test]
244 fn lie_about_digest_vec_len() {
245 let snippet = VerifyNdSiIntegrity::<TestStruct>::default();
246
247 let begin_address = bfe!(4);
248 let mut init_state =
249 snippet.initial_state(begin_address, snippet.prepare_random_object(&[42u8; 20000]));
250 let vec_digest_len_indicator = begin_address + bfe!(1);
251 let true_value = init_state.memory[&vec_digest_len_indicator];
252 init_state
253 .memory
254 .insert(vec_digest_len_indicator, true_value + bfe!(1));
255
256 test_assertion_failure(&ShadowedAccessor::new(snippet), init_state.into(), &[181])
257 }
258 }
259
260 mod option_types {
261 use super::*;
262 use crate::test_helpers::test_assertion_failure;
263
264 #[derive(Debug, Clone, TasmObject, BFieldCodec, Arbitrary)]
265 struct StatSizedPayload {
266 a: Option<Digest>,
267 }
268
269 #[derive(Debug, Clone, TasmObject, BFieldCodec, Arbitrary, Default)]
270 struct DynSizedPayload {
271 a: Option<Vec<u128>>,
272 b: Digest,
273 c: Vec<Vec<BFieldElement>>,
274 d: Option<Vec<Option<BFieldElement>>>,
275 }
276
277 test_case! {fn test_option_stat_sized_elem for StatSizedPayload }
278 test_case! {fn test_option_dyn_sized_elem for DynSizedPayload }
279
280 #[test]
281 fn lie_about_option_payload_field_size() {
282 let snippet = VerifyNdSiIntegrity::<DynSizedPayload>::default();
283
284 let begin_address = bfe!(4);
285 let randomness = rand::random::<[u8; 100_000]>();
286 let obj = snippet.prepare_random_object(&randomness);
287 let true_init_state = snippet.initial_state(begin_address, obj.clone());
288
289 let mut manipulated_si_outer = true_init_state.clone();
291 let outer_option_payload_si_ptr = begin_address; let true_value = true_init_state.memory[&outer_option_payload_si_ptr];
293 manipulated_si_outer
294 .memory
295 .insert(outer_option_payload_si_ptr, true_value + bfe!(1));
296
297 test_assertion_failure(
298 &ShadowedAccessor::new(snippet),
299 manipulated_si_outer.into(),
300 &[181],
301 );
302 }
303
304 #[test]
305 fn illegal_discriminant_value_for_option() {
306 let snippet = VerifyNdSiIntegrity::<DynSizedPayload>::default();
307
308 let obj = DynSizedPayload::default();
309 let begin_address = bfe!(4);
310 let mut manipulated_init_state = snippet.initial_state(begin_address, obj.clone());
311 let option_discriminant_ptr = begin_address + bfe!(1);
312 manipulated_init_state
313 .memory
314 .insert(option_discriminant_ptr, bfe!(2));
315
316 test_assertion_failure(
317 &ShadowedAccessor::new(snippet.clone()),
318 manipulated_init_state.into(),
319 &[200],
320 );
321 }
322
323 #[test]
324 fn lie_about_option_payload_size() {
325 let snippet = VerifyNdSiIntegrity::<DynSizedPayload>::default();
326
327 let obj = DynSizedPayload {
328 d: Some(vec![Some(bfe!(14)), None, Some(bfe!(15))]),
329 ..Default::default()
330 };
331 let begin_address = bfe!(4);
332 let true_init_state = snippet.initial_state(begin_address, obj.clone());
333
334 let mut add_one = true_init_state.clone();
336 let len_of_dyn_sized_list_elem_0 = begin_address + bfe!(3);
337 let true_value = true_init_state.memory[&len_of_dyn_sized_list_elem_0];
338 add_one
339 .memory
340 .insert(len_of_dyn_sized_list_elem_0, true_value + bfe!(1));
341
342 test_assertion_failure(
343 &ShadowedAccessor::new(snippet.clone()),
344 add_one.into(),
345 &[211],
346 );
347
348 let mut sub_one = true_init_state.clone();
349 sub_one
350 .memory
351 .insert(len_of_dyn_sized_list_elem_0, true_value - bfe!(1));
352
353 test_assertion_failure(&ShadowedAccessor::new(snippet), sub_one.into(), &[211]);
354 }
355 }
356
357 test_case! { fn test_stat_sized_tuple for new type TestStruct:
358 struct TestStruct { field: (Digest, XFieldElement) }
359 }
360
361 test_case! { fn test_dyn_state_sized_tuple_right_dyn for new type RightIsDyn:
362 struct RightIsDyn { field: (Digest, Vec<XFieldElement>) }
363 }
364
365 test_case! { fn test_dyn_state_sized_tuple_left_dyn for new type LeftIsDyn:
366 struct LeftIsDyn { field: (Vec<XFieldElement>, Digest) }
367 }
368
369 test_case! { fn test_dyn_state_sized_tuple_both_dyn for new type BothDyn:
370 struct BothDyn { field: (Vec<XFieldElement>, Vec<XFieldElement>) }
371 }
372
373 test_case! { fn proof for Proof }
374 test_case! { fn coin for CoinLookalike }
375 test_case! { fn utxo for UtxoLookalike }
376 test_case! { fn salted_utxos for SaltedUtxosLookalike }
377 test_case! { fn collect_lock_scripts_witness for CollectLockScriptsWitnessLookalike }
378 test_case! { fn collect_type_scripts_witness for CollectTypeScriptsWitnessLookalike }
379 test_case! { fn claim for Claim }
380 test_case! { fn neptune_coins for NeptuneCoinsLookalike }
381 test_case! { fn option_neptune_coins for Option<NeptuneCoinsLookalike> }
382 test_case! { fn chunk for ChunkLookalike }
383 test_case! { fn chunk_dictionary for ChunkDictionaryLookalike }
384 test_case! { fn proof_collection for ProofCollectionLookalike }
385 test_case! { fn absolute_index_set for AbsoluteIndexSetLookalike }
386 test_case! { fn removal_record for RemovalRecordLookalike }
387 test_case! { fn addition_record for AdditionRecordLookalike }
388 test_case! { fn public_announcement for PublicAnnouncementLookalike }
389 test_case! { fn timestamp for TimestampLookalike }
390 test_case! { fn transaction_kernel for TransactionKernelLookalike }
391 test_case! { fn kernel_to_outputs_witness for KernelToOutputsWitnessLookalike }
392 test_case! { fn merge_witness for MergeWitnessLookalike }
393 test_case! { fn ms_membership_proof for MsMembershipProofLookalike }
394 test_case! { fn removal_records_witness for RemovalRecordsIntegrityWitnessLookalike }
395 test_case! { fn update_witness for UpdateWitnessLookalike }
396 test_case! { fn lock_script_and_witness for LockScriptAndWitnessLookalike }
397 test_case! { fn mutator_set_accumulator for MutatorSetAccumulatorLookalike }
398 test_case! { fn primitive_witness for PrimitiveWitnessLookalike }
399 test_case! { fn mmr_successor_proof for MmrSuccessorProof }
400}
401
402#[cfg(test)]
403mod benches {
404 use super::*;
405 use crate::neptune::neptune_like_types_for_tests::ProofCollectionLookalike;
406 use crate::neptune::neptune_like_types_for_tests::TransactionKernelLookalike;
407 use crate::test_prelude::*;
408
409 #[test]
410 fn bench_proof_collection_lookalike() {
411 ShadowedAccessor::new(VerifyNdSiIntegrity::<ProofCollectionLookalike>::default()).bench();
412 }
413
414 #[test]
415 fn bench_transaction_kernel_lookalike() {
416 ShadowedAccessor::new(VerifyNdSiIntegrity::<TransactionKernelLookalike>::default()).bench();
417 }
418}