1use std::collections::HashMap;
2
3use triton_vm::prelude::*;
4
5use crate::list::get::Get;
6use crate::prelude::*;
7use crate::traits::basic_snippet::Reviewer;
8use crate::traits::basic_snippet::SignOffFingerprint;
9
10#[derive(Debug, Clone, Eq, PartialEq, Hash)]
31pub struct Push {
32 element_type: DataType,
33}
34
35impl Push {
36 pub const MEM_PAGE_ACCESS_VIOLATION_ERROR_ID: i128 = 410;
39
40 pub fn new(element_type: DataType) -> Self {
47 assert!(element_type.stack_size() < 16);
49 Get::assert_element_type_is_supported(&element_type);
50
51 Self { element_type }
52 }
53}
54
55impl BasicSnippet for Push {
56 fn inputs(&self) -> Vec<(DataType, String)> {
57 let list_type = DataType::List(Box::new(self.element_type.clone()));
58 let element_type = self.element_type.clone();
59
60 vec![
61 (list_type, "*list".to_string()),
62 (element_type, "element".to_string()),
63 ]
64 }
65
66 fn outputs(&self) -> Vec<(DataType, String)> {
67 vec![]
68 }
69
70 fn entrypoint(&self) -> String {
71 let element_type = self.element_type.label_friendly_name();
72 format!("tasmlib_list_push___{element_type}")
73 }
74
75 fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
76 let mul_with_element_size = match self.element_type.stack_size() {
77 1 => triton_asm!(), n => triton_asm!(push {n} mul),
79 };
80 let add_element_size_minus_1 = match self.element_type.stack_size() {
81 1 => triton_asm!(), n => triton_asm!(addi {n - 1}),
83 };
84
85 triton_asm!(
86 {self.entrypoint()}:
89 pick {self.element_type.stack_size()}
90 read_mem 1 addi 1
93
94 dup 1 addi 1 pick 1
98 write_mem 1 pick 1
102 {&mul_with_element_size}
103 dup 0
107 {&add_element_size_minus_1}
108 split
110 pop 1
111 push 0
112 eq
113 assert error_id {Self::MEM_PAGE_ACCESS_VIOLATION_ERROR_ID}
114 add {&self.element_type.write_value_to_memory_pop_pointer()}
119 return
121 )
122 }
123
124 fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
125 let mut sign_offs = HashMap::new();
126 match self.element_type.stack_size() {
127 1 => _ = sign_offs.insert(Reviewer("ferdinand"), 0x292abedacc166a44.into()),
128 2 => _ = sign_offs.insert(Reviewer("ferdinand"), 0x713379aa2ec2f141.into()),
129 3 => _ = sign_offs.insert(Reviewer("ferdinand"), 0x187fb6005d7699cc.into()),
130 5 => _ = sign_offs.insert(Reviewer("ferdinand"), 0x456653f3204bda08.into()),
131 _ => (),
132 }
133
134 sign_offs
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use crate::rust_shadowing_helper_functions::list::insert_random_list;
142 use crate::rust_shadowing_helper_functions::list::list_push;
143 use crate::test_prelude::*;
144
145 impl Push {
146 fn set_up_initial_state(
147 &self,
148 list_length: usize,
149 list_pointer: BFieldElement,
150 element: Vec<BFieldElement>,
151 ) -> FunctionInitialState {
152 let mut memory = HashMap::default();
153 insert_random_list(&self.element_type, list_pointer, list_length, &mut memory);
154
155 let mut stack = self.init_stack_for_isolated_run();
156 stack.push(list_pointer);
157 stack.extend(element.into_iter().rev());
158
159 FunctionInitialState { stack, memory }
160 }
161 }
162
163 impl Function for Push {
164 fn rust_shadow(
165 &self,
166 stack: &mut Vec<BFieldElement>,
167 memory: &mut HashMap<BFieldElement, BFieldElement>,
168 ) {
169 let element = (0..self.element_type.stack_size())
170 .map(|_| stack.pop().unwrap())
171 .collect();
172 let list_pointer = stack.pop().unwrap();
173
174 list_push(list_pointer, element, memory);
175 }
176
177 fn pseudorandom_initial_state(
178 &self,
179 seed: [u8; 32],
180 bench_case: Option<BenchmarkCase>,
181 ) -> FunctionInitialState {
182 let mut rng = StdRng::from_seed(seed);
183 let (list_length, _, list_pointer) = Get::random_len_idx_ptr(bench_case, &mut rng);
184 let element = self.element_type.seeded_random_element(&mut rng);
185
186 self.set_up_initial_state(list_length, list_pointer, element)
187 }
188 }
189
190 #[test]
191 fn rust_shadow() {
192 for ty in [
193 DataType::Bool,
194 DataType::Bfe,
195 DataType::U32,
196 DataType::U64,
197 DataType::Xfe,
198 DataType::Digest,
199 ] {
200 ShadowedFunction::new(Push::new(ty)).test()
201 }
202 }
203
204 #[proptest(cases = 100)]
206 fn too_large_lists_crash_vm(
207 #[strategy(1_u64 << 29..1 << 32)] list_length: u64,
208 #[strategy(arb())] list_pointer: BFieldElement,
209 ) {
210 let mut memory = HashMap::default();
212 memory.insert(list_pointer, bfe!(list_length));
213
214 let tuple_ty = DataType::Tuple(vec![DataType::Bfe; 15]);
216 let push = Push::new(tuple_ty);
217
218 let mut stack = push.init_stack_for_isolated_run();
219 stack.push(list_pointer);
220 stack.extend(bfe_array![0; 15]);
221 let initial_state = AccessorInitialState { stack, memory };
222
223 test_assertion_failure(
224 &ShadowedFunction::new(push),
225 initial_state.into(),
226 &[Push::MEM_PAGE_ACCESS_VIOLATION_ERROR_ID],
227 );
228 }
229}
230
231#[cfg(test)]
232mod benches {
233 use super::*;
234 use crate::test_prelude::*;
235
236 #[test]
237 fn benchmark() {
238 ShadowedFunction::new(Push::new(DataType::Digest));
239 }
240}