1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
use primitive_types::U256;
use zkevm_opcode_defs::{BlobSha256Format, ContractCodeSha256Format, VersionedHashLen32};
use zksync_vm2_interface::{opcodes, Tracer};

use super::common::boilerplate_ext;
use crate::{
    addressing_modes::{Arguments, Destination, Register1, Register2, Source},
    fat_pointer::FatPointer,
    instruction::ExecutionStatus,
    Instruction, VirtualMachine, World,
};

fn decommit<T: Tracer, W: World<T>>(
    vm: &mut VirtualMachine<T, W>,
    world: &mut W,
    tracer: &mut T,
) -> ExecutionStatus {
    boilerplate_ext::<opcodes::Decommit, _, _>(vm, world, tracer, |vm, args, world, tracer| {
        let code_hash = Register1::get(args, &mut vm.state);
        let extra_cost = Register2::get(args, &mut vm.state).low_u32();

        let mut buffer = [0u8; 32];
        code_hash.to_big_endian(&mut buffer);

        let preimage_len_in_bytes =
            zkevm_opcode_defs::system_params::NEW_KERNEL_FRAME_MEMORY_STIPEND;

        if vm.state.use_gas(extra_cost).is_err()
            || (!ContractCodeSha256Format::is_valid(&buffer)
                && !BlobSha256Format::is_valid(&buffer))
        {
            Register1::set(args, &mut vm.state, U256::zero());
            return;
        }

        let (program, is_fresh) = vm.world_diff.decommit_opcode(world, tracer, code_hash);
        if !is_fresh {
            vm.state.current_frame.gas += extra_cost;
        }

        let heap = vm.state.heaps.allocate_with_content(program.as_ref());
        vm.state.current_frame.heaps_i_am_keeping_alive.push(heap);

        let value = FatPointer {
            offset: 0,
            memory_page: heap,
            start: 0,
            length: preimage_len_in_bytes,
        };
        let value = value.into_u256();
        Register1::set_fat_ptr(args, &mut vm.state, value);
    })
}

impl<T: Tracer, W: World<T>> Instruction<T, W> {
    /// Creates a [`Decommit`](opcodes::Decommit) instruction with the provided params.
    pub fn from_decommit(
        abi: Register1,
        burn: Register2,
        out: Register1,
        arguments: Arguments,
    ) -> Self {
        Self {
            arguments: arguments
                .write_source(&abi)
                .write_source(&burn)
                .write_destination(&out),
            handler: decommit,
        }
    }
}