|
1 | 1 | use std::collections::HashMap;
|
2 | 2 |
|
3 |
| -use num::One; |
4 |
| -use rand::prelude::*; |
5 | 3 | use triton_vm::prelude::*;
|
6 |
| -use twenty_first::prelude::U32s; |
7 | 4 |
|
8 |
| -use crate::empty_stack; |
9 | 5 | use crate::prelude::*;
|
10 |
| -use crate::push_encodable; |
11 |
| -use crate::traits::deprecated_snippet::DeprecatedSnippet; |
12 |
| -use crate::InitVmState; |
13 |
| - |
14 |
| -#[derive(Clone, Debug)] |
| 6 | +use crate::traits::basic_snippet::Reviewer; |
| 7 | +use crate::traits::basic_snippet::SignOffFingerprint; |
| 8 | + |
| 9 | +/// Increment a `u64` by 1. |
| 10 | +/// |
| 11 | +/// Crashes the VM if the input is [`u64::MAX`]. |
| 12 | +/// |
| 13 | +/// ### Behavior |
| 14 | +/// |
| 15 | +/// ```text |
| 16 | +/// BEFORE: _ v |
| 17 | +/// AFTER: _ (v+1) |
| 18 | +/// ``` |
| 19 | +/// |
| 20 | +/// ### Preconditions |
| 21 | +/// |
| 22 | +/// - all input arguments are properly [`BFieldCodec`] encoded |
| 23 | +/// |
| 24 | +/// ### Postconditions |
| 25 | +/// |
| 26 | +/// - the output is properly [`BFieldCodec`] encoded |
| 27 | +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)] |
15 | 28 | pub struct Incr;
|
16 | 29 |
|
17 |
| -impl DeprecatedSnippet for Incr { |
18 |
| - fn entrypoint_name(&self) -> String { |
19 |
| - "tasmlib_arithmetic_u64_incr".to_string() |
20 |
| - } |
21 |
| - |
22 |
| - fn input_field_names(&self) -> Vec<String> { |
23 |
| - vec!["value_hi".to_string(), "value_lo".to_string()] |
24 |
| - } |
| 30 | +impl Incr { |
| 31 | + pub const OVERFLOW_ERROR_ID: i128 = 440; |
| 32 | +} |
25 | 33 |
|
26 |
| - fn input_types(&self) -> Vec<DataType> { |
27 |
| - vec![DataType::U64] |
| 34 | +impl BasicSnippet for Incr { |
| 35 | + fn inputs(&self) -> Vec<(DataType, String)> { |
| 36 | + vec![(DataType::U64, "value".to_string())] |
28 | 37 | }
|
29 | 38 |
|
30 |
| - fn output_field_names(&self) -> Vec<String> { |
31 |
| - vec!["(value + 1)_hi".to_string(), "(value + 1)_lo".to_string()] |
| 39 | + fn outputs(&self) -> Vec<(DataType, String)> { |
| 40 | + vec![(DataType::U64, "value + 1".to_string())] |
32 | 41 | }
|
33 | 42 |
|
34 |
| - fn output_types(&self) -> Vec<DataType> { |
35 |
| - vec![DataType::U64] |
| 43 | + fn entrypoint(&self) -> String { |
| 44 | + "tasmlib_arithmetic_u64_incr".to_string() |
36 | 45 | }
|
37 | 46 |
|
38 |
| - fn stack_diff(&self) -> isize { |
39 |
| - 0 |
40 |
| - } |
| 47 | + fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> { |
| 48 | + let entrypoint = self.entrypoint(); |
| 49 | + let carry = format!("{entrypoint}_carry"); |
| 50 | + triton_asm!( |
| 51 | + // BEFORE: _ [value: u64] |
| 52 | + // AFTER: _ [value + 1: u64] |
| 53 | + {entrypoint}: |
| 54 | + addi 1 |
| 55 | + dup 0 |
| 56 | + push {1_u64 << 32} |
| 57 | + eq |
| 58 | + skiz |
| 59 | + call {carry} |
| 60 | + return |
41 | 61 |
|
42 |
| - fn function_code(&self, _library: &mut Library) -> String { |
43 |
| - let entrypoint = self.entrypoint_name(); |
44 |
| - const TWO_POW_32: u64 = 1 << 32; |
45 |
| - format!( |
46 |
| - " |
47 |
| - // Before: _ value_hi value_lo |
48 |
| - // After: _ (value + 1)_hi (value + 1)_lo |
49 |
| - {entrypoint}_carry: |
| 62 | + {carry}: |
50 | 63 | pop 1
|
51 |
| - push 1 |
52 |
| - add |
| 64 | + addi 1 |
53 | 65 | dup 0
|
54 |
| - push {TWO_POW_32} |
| 66 | + push {1_u64 << 32} |
55 | 67 | eq
|
56 | 68 | push 0
|
57 | 69 | eq
|
58 |
| - assert |
| 70 | + assert error_id {Self::OVERFLOW_ERROR_ID} |
59 | 71 | push 0
|
60 | 72 | return
|
61 |
| -
|
62 |
| - {entrypoint}: |
63 |
| - push 1 |
64 |
| - add |
65 |
| - dup 0 |
66 |
| - push {TWO_POW_32} |
67 |
| - eq |
68 |
| - skiz |
69 |
| - call {entrypoint}_carry |
70 |
| - return |
71 |
| - ", |
72 | 73 | )
|
73 | 74 | }
|
74 | 75 |
|
75 |
| - fn crash_conditions(&self) -> Vec<String> { |
76 |
| - vec!["value == u64::MAX".to_string()] |
77 |
| - } |
78 |
| - |
79 |
| - fn gen_input_states(&self) -> Vec<InitVmState> { |
80 |
| - let mut rng = rand::thread_rng(); |
81 |
| - let values = vec![ |
82 |
| - U32s::new([u32::MAX, 0]), |
83 |
| - U32s::new([0, u32::MAX]), |
84 |
| - U32s::new([u32::MAX, u32::MAX - 1]), |
85 |
| - // U32s::new([u32::MAX, u32::MAX]) |
86 |
| - rng.next_u32().into(), |
87 |
| - U32s::<2>::try_from(rng.next_u64()).unwrap(), |
88 |
| - ]; |
89 |
| - values |
90 |
| - .into_iter() |
91 |
| - .map(|value| { |
92 |
| - let mut stack = empty_stack(); |
93 |
| - push_encodable(&mut stack, &value); |
94 |
| - InitVmState::with_stack(stack) |
95 |
| - }) |
96 |
| - .collect() |
97 |
| - } |
98 |
| - |
99 |
| - fn common_case_input_state(&self) -> InitVmState { |
100 |
| - // no carry |
101 |
| - InitVmState::with_stack( |
102 |
| - [ |
103 |
| - empty_stack(), |
104 |
| - vec![BFieldElement::new(1000), BFieldElement::new(7)], |
105 |
| - ] |
106 |
| - .concat(), |
107 |
| - ) |
108 |
| - } |
109 |
| - |
110 |
| - fn worst_case_input_state(&self) -> InitVmState { |
111 |
| - // with carry |
112 |
| - InitVmState::with_stack( |
113 |
| - [ |
114 |
| - empty_stack(), |
115 |
| - vec![BFieldElement::new(1000), BFieldElement::new((1 << 32) - 1)], |
116 |
| - ] |
117 |
| - .concat(), |
118 |
| - ) |
119 |
| - } |
120 |
| - |
121 |
| - fn rust_shadowing( |
122 |
| - &self, |
123 |
| - stack: &mut Vec<BFieldElement>, |
124 |
| - _std_in: Vec<BFieldElement>, |
125 |
| - _secret_in: Vec<BFieldElement>, |
126 |
| - _memory: &mut HashMap<BFieldElement, BFieldElement>, |
127 |
| - ) { |
128 |
| - let a: u32 = stack.pop().unwrap().try_into().unwrap(); |
129 |
| - let b: u32 = stack.pop().unwrap().try_into().unwrap(); |
130 |
| - let ab = U32s::<2>::new([a, b]); |
131 |
| - let ab_incr = ab + U32s::one(); |
132 |
| - let mut res = ab_incr.encode(); |
133 |
| - for _ in 0..res.len() { |
134 |
| - stack.push(res.pop().unwrap()); |
135 |
| - } |
| 76 | + fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> { |
| 77 | + let mut sign_offs = HashMap::new(); |
| 78 | + sign_offs.insert(Reviewer("ferdinand"), 0x786629a8064b2786.into()); |
| 79 | + sign_offs |
136 | 80 | }
|
137 | 81 | }
|
138 | 82 |
|
139 | 83 | #[cfg(test)]
|
140 | 84 | mod tests {
|
141 | 85 | use super::*;
|
142 |
| - use crate::empty_stack; |
143 |
| - use crate::test_helpers::test_rust_equivalence_multiple_deprecated; |
| 86 | + use crate::test_prelude::*; |
144 | 87 |
|
145 |
| - #[test] |
146 |
| - fn incr_u64_test() { |
147 |
| - test_rust_equivalence_multiple_deprecated(&Incr, true); |
| 88 | + impl Closure for Incr { |
| 89 | + type Args = u64; |
| 90 | + |
| 91 | + fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) { |
| 92 | + let v = pop_encodable::<Self::Args>(stack); |
| 93 | + let incr = v.checked_add(1).unwrap(); |
| 94 | + push_encodable(stack, &incr); |
| 95 | + } |
| 96 | + |
| 97 | + fn pseudorandom_args( |
| 98 | + &self, |
| 99 | + seed: [u8; 32], |
| 100 | + bench_case: Option<BenchmarkCase>, |
| 101 | + ) -> Self::Args { |
| 102 | + match bench_case { |
| 103 | + Some(BenchmarkCase::CommonCase) => (1000 << 32) + 7, // no carry |
| 104 | + Some(BenchmarkCase::WorstCase) => (1000 << 32) + u64::from(u32::MAX), // carry |
| 105 | + None => StdRng::from_seed(seed).gen(), |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + fn corner_case_args(&self) -> Vec<Self::Args> { |
| 110 | + vec![0, u32::MAX.into(), u64::MAX - 1] |
| 111 | + } |
148 | 112 | }
|
149 | 113 |
|
150 | 114 | #[test]
|
151 |
| - fn incr_u64_negative_tasm_test() { |
152 |
| - let mut stack = empty_stack(); |
153 |
| - let u64_max = U32s::<2>::try_from(u64::MAX).unwrap(); |
154 |
| - push_encodable(&mut stack, &u64_max); |
155 |
| - assert!(Incr |
156 |
| - .link_and_run_tasm_for_test(&mut stack, vec![], NonDeterminism::default()) |
157 |
| - .is_err()); |
| 115 | + fn rust_shadow() { |
| 116 | + ShadowedClosure::new(Incr).test(); |
158 | 117 | }
|
159 | 118 |
|
160 | 119 | #[test]
|
161 |
| - #[should_panic] |
162 |
| - fn incr_u64_negative_rust_test() { |
163 |
| - let mut stack = empty_stack(); |
164 |
| - let u64_max = U32s::<2>::try_from(u64::MAX).unwrap(); |
165 |
| - push_encodable(&mut stack, &u64_max); |
166 |
| - Incr::rust_shadowing(&Incr, &mut stack, vec![], vec![], &mut HashMap::default()); |
| 120 | + fn u64_max_crashes_vm() { |
| 121 | + test_assertion_failure( |
| 122 | + &ShadowedClosure::new(Incr), |
| 123 | + InitVmState::with_stack(Incr.set_up_test_stack(u64::MAX)), |
| 124 | + &[Incr::OVERFLOW_ERROR_ID], |
| 125 | + ); |
167 | 126 | }
|
168 | 127 | }
|
169 | 128 |
|
170 | 129 | #[cfg(test)]
|
171 | 130 | mod benches {
|
172 | 131 | use super::*;
|
173 |
| - use crate::snippet_bencher::bench_and_write; |
| 132 | + use crate::test_prelude::*; |
174 | 133 |
|
175 | 134 | #[test]
|
176 |
| - fn incr_u64_benchmark() { |
177 |
| - bench_and_write(Incr); |
| 135 | + fn benchmark() { |
| 136 | + ShadowedClosure::new(Incr).bench(); |
178 | 137 | }
|
179 | 138 | }
|
0 commit comments