zksync_vm2/instruction_handlers/
precompiles.rs1use primitive_types::U256;
2use zksync_vm2_interface::{opcodes, HeapId, Tracer};
3
4use super::common::boilerplate_ext;
5use crate::{
6 addressing_modes::{Arguments, Destination, Register1, Register2, Source},
7 instruction::ExecutionStatus,
8 precompiles::{PrecompileMemoryReader, Precompiles},
9 Instruction, VirtualMachine, World,
10};
11
12#[derive(Debug)]
13struct PrecompileAuxData {
14 extra_ergs_cost: u32,
15 extra_pubdata_cost: u32,
16}
17
18impl PrecompileAuxData {
19 #[allow(clippy::cast_possible_truncation)]
20 fn from_u256(raw_value: U256) -> Self {
21 let raw = raw_value.0;
22 let extra_ergs_cost = raw[0] as u32;
23 let extra_pubdata_cost = (raw[0] >> 32) as u32;
24
25 Self {
26 extra_ergs_cost,
27 extra_pubdata_cost,
28 }
29 }
30}
31
32#[derive(Debug)]
33struct PrecompileCallAbi {
34 input_memory_offset: u32,
35 input_memory_length: u32,
36 output_memory_offset: u32,
37 output_memory_length: u32,
38 memory_page_to_read: HeapId,
39 memory_page_to_write: HeapId,
40 precompile_interpreted_data: u64,
41}
42
43impl PrecompileCallAbi {
44 #[allow(clippy::cast_possible_truncation)]
45 fn from_u256(raw_value: U256) -> Self {
46 let raw = raw_value.0;
47 let input_memory_offset = raw[0] as u32;
48 let input_memory_length = (raw[0] >> 32) as u32;
49 let output_memory_offset = raw[1] as u32;
50 let output_memory_length = (raw[1] >> 32) as u32;
51 let memory_page_to_read = HeapId::from_u32_unchecked(raw[2] as u32);
52 let memory_page_to_write = HeapId::from_u32_unchecked((raw[2] >> 32) as u32);
53 let precompile_interpreted_data = raw[3];
54
55 Self {
56 input_memory_offset,
57 input_memory_length,
58 output_memory_offset,
59 output_memory_length,
60 memory_page_to_read,
61 memory_page_to_write,
62 precompile_interpreted_data,
63 }
64 }
65}
66
67fn precompile_call<T: Tracer, W: World<T>>(
68 vm: &mut VirtualMachine<T, W>,
69 world: &mut W,
70 tracer: &mut T,
71) -> ExecutionStatus {
72 boilerplate_ext::<opcodes::PrecompileCall, _, _>(
73 vm,
74 world,
75 tracer,
76 |vm, args, world, tracer| {
77 let aux_data = PrecompileAuxData::from_u256(Register2::get(args, &mut vm.state));
80 let Ok(()) = vm.state.use_gas(aux_data.extra_ergs_cost) else {
81 Register1::set(args, &mut vm.state, U256::zero());
82 return;
83 };
84
85 #[allow(clippy::cast_possible_wrap)]
86 {
87 vm.world_diff.pubdata.0 += aux_data.extra_pubdata_cost as i32;
88 }
89
90 let mut abi = PrecompileCallAbi::from_u256(Register1::get(args, &mut vm.state));
91 if abi.memory_page_to_read.as_u32() == 0 {
94 abi.memory_page_to_read = vm.state.current_frame.heap;
95 }
96 if abi.memory_page_to_write.as_u32() == 0 {
97 abi.memory_page_to_write = vm.state.current_frame.heap;
98 }
99
100 let address_bytes = vm.state.current_frame.address.0;
101 let address_low = u16::from_le_bytes([address_bytes[19], address_bytes[18]]);
102 let heap_to_read = &vm.state.heaps[abi.memory_page_to_read];
103 let memory = PrecompileMemoryReader::new(
104 heap_to_read,
105 abi.input_memory_offset,
106 abi.input_memory_length,
107 );
108 let output = world.precompiles().call_precompile(
109 address_low,
110 memory,
111 abi.precompile_interpreted_data,
112 );
113
114 if let Some(cycle_stats) = output.cycle_stats {
115 tracer.on_extra_prover_cycles(cycle_stats);
116 }
117
118 let mut write_offset = abi.output_memory_offset * 32;
119 for i in 0..output.len.min(abi.output_memory_length) {
120 vm.state.heaps.write_u256(
121 abi.memory_page_to_write,
122 write_offset,
123 output.buffer[i as usize],
124 );
125 write_offset += 32;
126 }
127 Register1::set(args, &mut vm.state, 1.into());
128 },
129 )
130}
131
132impl<T: Tracer, W: World<T>> Instruction<T, W> {
133 pub fn from_precompile_call(
135 abi: Register1,
136 burn: Register2,
137 out: Register1,
138 arguments: Arguments,
139 ) -> Self {
140 Self {
141 arguments: arguments
142 .write_source(&abi)
143 .write_source(&burn)
144 .write_destination(&out),
145 handler: precompile_call,
146 }
147 }
148}