1use primitive_types::U256;
2use zksync_vm2_interface::{opcodes, HeapId, OpcodeType, Tracer};
3
4use super::{
5 common::{boilerplate, full_boilerplate},
6 monomorphization::{match_boolean, match_reg_imm, monomorphize, parameterize},
7 ret::spontaneous_panic,
8};
9use crate::{
10 addressing_modes::{
11 Addressable, Arguments, Destination, DestinationWriter, Immediate1, Register1, Register2,
12 RegisterOrImmediate, Source,
13 },
14 fat_pointer::FatPointer,
15 instruction::ExecutionStatus,
16 page_ids::static_memory_page,
17 state::State,
18 ExecutionEnd, Instruction, VirtualMachine, World,
19};
20
21const STATIC_MEMORY_HEAP: HeapId = static_memory_page();
23
24pub(crate) trait HeapFromState {
25 type Read: OpcodeType;
26 type Write: OpcodeType;
27
28 fn get_heap<T, W>(state: &State<T, W>) -> HeapId;
29 fn get_heap_size<T, W>(state: &mut State<T, W>) -> &mut u32;
30}
31
32pub(crate) struct Heap;
33
34impl HeapFromState for Heap {
35 type Read = opcodes::HeapRead;
36 type Write = opcodes::HeapWrite;
37
38 fn get_heap<T, W>(state: &State<T, W>) -> HeapId {
39 state.current_frame.heap
40 }
41
42 fn get_heap_size<T, W>(state: &mut State<T, W>) -> &mut u32 {
43 &mut state.current_frame.heap_size
44 }
45}
46
47pub(crate) struct AuxHeap;
48
49impl HeapFromState for AuxHeap {
50 type Read = opcodes::AuxHeapRead;
51 type Write = opcodes::AuxHeapWrite;
52
53 fn get_heap<T, W>(state: &State<T, W>) -> HeapId {
54 state.current_frame.aux_heap
55 }
56
57 fn get_heap_size<T, W>(state: &mut State<T, W>) -> &mut u32 {
58 &mut state.current_frame.aux_heap_size
59 }
60}
61
62const LAST_ADDRESS: u32 = u32::MAX - 32;
64
65#[inline(always)]
67fn bigger_than_last_address(x: U256) -> bool {
68 x.0[0] > LAST_ADDRESS.into() || x.0[1] != 0 || x.0[2] != 0 || x.0[3] != 0
69}
70
71fn set_incremented_read_offset(
72 args: &Arguments,
73 state: &mut impl Addressable,
74 value: U256,
75 is_pointer: bool,
76) {
77 if is_pointer {
82 Register2::set_fat_ptr(args, state, value);
83 } else {
84 Register2::set(args, state, value);
85 }
86}
87
88fn load<T: Tracer, W: World<T>, H: HeapFromState, In: Source, const INCREMENT: bool>(
89 vm: &mut VirtualMachine<T, W>,
90 world: &mut W,
91 tracer: &mut T,
92) -> ExecutionStatus {
93 boilerplate::<H::Read, _, _>(vm, world, tracer, |vm, args| {
94 let (pointer, input_is_pointer) = In::get_with_pointer_flag(args, &mut vm.state);
97
98 if bigger_than_last_address(pointer) {
99 let _ = vm.state.use_gas(u32::MAX);
100 vm.state.current_frame.pc = spontaneous_panic();
101 return;
102 }
103
104 let address = pointer.low_u32();
105 let new_bound = address.wrapping_add(32);
106 if grow_heap::<_, _, H>(&mut vm.state, new_bound).is_err() {
107 vm.state.current_frame.pc = spontaneous_panic();
108 return;
109 }
110
111 let heap = H::get_heap(&vm.state);
112 let value = vm.state.heaps[heap].read_u256(address);
113 Register1::set(args, &mut vm.state, value);
114
115 if INCREMENT {
116 set_incremented_read_offset(args, &mut vm.state, pointer + 32, input_is_pointer);
117 }
118 })
119}
120
121fn store<T, W: World<T>, H, In, const INCREMENT: bool, const HOOKING_ENABLED: bool>(
122 vm: &mut VirtualMachine<T, W>,
123 world: &mut W,
124 tracer: &mut T,
125) -> ExecutionStatus
126where
127 T: Tracer,
128 H: HeapFromState,
129 In: Source,
130{
131 full_boilerplate::<H::Write, _, _>(vm, world, tracer, |vm, args, _, _| {
132 let (pointer, _) = In::get_with_pointer_flag(args, &mut vm.state);
135
136 if bigger_than_last_address(pointer) {
137 let _ = vm.state.use_gas(u32::MAX);
138 vm.state.current_frame.pc = spontaneous_panic();
139 return ExecutionStatus::Running;
140 }
141
142 let address = pointer.low_u32();
143 let value = Register2::get(args, &mut vm.state);
144
145 let new_bound = address.wrapping_add(32);
146 if grow_heap::<_, _, H>(&mut vm.state, new_bound).is_err() {
147 vm.state.current_frame.pc = spontaneous_panic();
148 return ExecutionStatus::Running;
149 }
150
151 let heap = H::get_heap(&vm.state);
152 vm.state.heaps.write_u256(heap, address, value);
153
154 if INCREMENT {
155 Register1::set(args, &mut vm.state, pointer + 32);
156 }
157
158 if HOOKING_ENABLED && address == vm.settings.hook_address {
159 ExecutionStatus::Stopped(ExecutionEnd::SuspendedOnHook(value.as_u32()))
160 } else {
161 ExecutionStatus::Running
162 }
163 })
164}
165
166pub(crate) fn grow_heap<T, W, H: HeapFromState>(
169 state: &mut State<T, W>,
170 new_bound: u32,
171) -> Result<(), ()> {
172 if let Some(to_pay) = new_bound.checked_sub(*H::get_heap_size(state)) {
173 state.use_gas(to_pay)?;
174 *H::get_heap_size(state) = new_bound;
175 }
176
177 Ok(())
178}
179
180fn load_pointer<T: Tracer, W: World<T>, const INCREMENT: bool>(
181 vm: &mut VirtualMachine<T, W>,
182 world: &mut W,
183 tracer: &mut T,
184) -> ExecutionStatus {
185 boilerplate::<opcodes::PointerRead, _, _>(vm, world, tracer, |vm, args| {
186 let (input, input_is_pointer) = Register1::get_with_pointer_flag(args, &mut vm.state);
187 if !input_is_pointer {
188 vm.state.current_frame.pc = spontaneous_panic();
189 return;
190 }
191 let pointer = FatPointer::from(input);
192
193 if pointer.offset > LAST_ADDRESS {
197 vm.state.current_frame.pc = spontaneous_panic();
198 return;
199 }
200
201 let start = pointer.start + pointer.offset.min(pointer.length);
202 let end = start.saturating_add(32).min(pointer.start + pointer.length);
203
204 let value = vm.state.heaps[pointer.memory_page].read_u256_partially(start..end);
205 Register1::set(args, &mut vm.state, value);
206
207 if INCREMENT {
208 Register2::set_fat_ptr(args, &mut vm.state, input + 32);
210 }
211 })
212}
213
214fn load_static<T: Tracer, W: World<T>, In: Source, const INCREMENT: bool>(
215 vm: &mut VirtualMachine<T, W>,
216 world: &mut W,
217 tracer: &mut T,
218) -> ExecutionStatus {
219 boilerplate::<opcodes::StaticMemoryRead, _, _>(vm, world, tracer, |vm, args| {
220 let (pointer, input_is_pointer) = In::get_with_pointer_flag(args, &mut vm.state);
223
224 if bigger_than_last_address(pointer) {
225 vm.state.current_frame.pc = spontaneous_panic();
226 return;
227 }
228
229 let address = pointer.low_u32();
230 let value = vm.state.heaps[STATIC_MEMORY_HEAP].read_u256(address);
231 Register1::set(args, &mut vm.state, value);
232
233 if INCREMENT {
234 set_incremented_read_offset(args, &mut vm.state, pointer + 32, input_is_pointer);
235 }
236 })
237}
238
239fn store_static<T, W: World<T>, In, const INCREMENT: bool>(
240 vm: &mut VirtualMachine<T, W>,
241 world: &mut W,
242 tracer: &mut T,
243) -> ExecutionStatus
244where
245 T: Tracer,
246 In: Source,
247{
248 boilerplate::<opcodes::StaticMemoryWrite, _, _>(vm, world, tracer, |vm, args| {
249 let (pointer, _) = In::get_with_pointer_flag(args, &mut vm.state);
252
253 if bigger_than_last_address(pointer) {
254 vm.state.current_frame.pc = spontaneous_panic();
255 return;
256 }
257
258 let address = pointer.low_u32();
259 let value = Register2::get(args, &mut vm.state);
260 vm.state
261 .heaps
262 .write_u256(STATIC_MEMORY_HEAP, address, value);
263
264 if INCREMENT {
265 Register1::set(args, &mut vm.state, pointer + 32);
266 }
267 })
268}
269
270impl<T: Tracer, W: World<T>> Instruction<T, W> {
271 pub fn from_heap_read(
273 src: RegisterOrImmediate,
274 out: Register1,
275 incremented_out: Option<Register2>,
276 arguments: Arguments,
277 ) -> Self {
278 Self::from_read::<Heap>(src, out, incremented_out, arguments)
279 }
280
281 pub fn from_aux_heap_read(
283 src: RegisterOrImmediate,
284 out: Register1,
285 incremented_out: Option<Register2>,
286 arguments: Arguments,
287 ) -> Self {
288 Self::from_read::<AuxHeap>(src, out, incremented_out, arguments)
289 }
290
291 pub fn from_static_memory_read(
293 src: RegisterOrImmediate,
294 out: Register1,
295 incremented_out: Option<Register2>,
296 arguments: Arguments,
297 ) -> Self {
298 let mut arguments = arguments.write_source(&src).write_destination(&out);
299
300 let increment = incremented_out.is_some();
301 if let Some(out2) = incremented_out {
302 out2.write_destination(&mut arguments);
303 }
304
305 Self {
306 handler: monomorphize!(load_static [T W] match_reg_imm src match_boolean increment),
307 arguments,
308 }
309 }
310
311 fn from_read<H: HeapFromState>(
312 src: RegisterOrImmediate,
313 out: Register1,
314 incremented_out: Option<Register2>,
315 arguments: Arguments,
316 ) -> Self {
317 let mut arguments = arguments.write_source(&src).write_destination(&out);
318
319 let increment = incremented_out.is_some();
320 if let Some(out2) = incremented_out {
321 out2.write_destination(&mut arguments);
322 }
323
324 Self {
325 handler: monomorphize!(load [T W H] match_reg_imm src match_boolean increment),
326 arguments,
327 }
328 }
329
330 pub fn from_heap_write(
332 src1: RegisterOrImmediate,
333 src2: Register2,
334 incremented_out: Option<Register1>,
335 arguments: Arguments,
336 should_hook: bool,
337 ) -> Self {
338 Self::from_write::<Heap>(src1, src2, incremented_out, arguments, should_hook)
339 }
340
341 pub fn from_aux_heap_store(
343 src1: RegisterOrImmediate,
344 src2: Register2,
345 incremented_out: Option<Register1>,
346 arguments: Arguments,
347 ) -> Self {
348 Self::from_write::<AuxHeap>(src1, src2, incremented_out, arguments, false)
349 }
350
351 pub fn from_static_memory_write(
353 src1: RegisterOrImmediate,
354 src2: Register2,
355 incremented_out: Option<Register1>,
356 arguments: Arguments,
357 ) -> Self {
358 let increment = incremented_out.is_some();
359 Self {
360 handler: monomorphize!(store_static [T W] match_reg_imm src1 match_boolean increment),
361 arguments: arguments
362 .write_source(&src1)
363 .write_source(&src2)
364 .write_destination(&incremented_out),
365 }
366 }
367
368 fn from_write<H: HeapFromState>(
369 src1: RegisterOrImmediate,
370 src2: Register2,
371 incremented_out: Option<Register1>,
372 arguments: Arguments,
373 should_hook: bool,
374 ) -> Self {
375 let increment = incremented_out.is_some();
376 Self {
377 handler: monomorphize!(store [T W H] match_reg_imm src1 match_boolean increment match_boolean should_hook),
378 arguments: arguments
379 .write_source(&src1)
380 .write_source(&src2)
381 .write_destination(&incremented_out),
382 }
383 }
384
385 pub fn from_pointer_read(
387 src: Register1,
388 out: Register1,
389 incremented_out: Option<Register2>,
390 arguments: Arguments,
391 ) -> Self {
392 let increment = incremented_out.is_some();
393 Self {
394 handler: monomorphize!(load_pointer [T W] match_boolean increment),
395 arguments: arguments
396 .write_source(&src)
397 .write_destination(&out)
398 .write_destination(&incremented_out),
399 }
400 }
401}