zkevm_opcode_defs/
lib.rs

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
pub const REGISTERS_COUNT: usize = 15;

pub mod decoding;
pub mod definitions;
pub mod imm_mem_modifiers;
pub mod opcode;
pub mod system_params;
pub mod utils;

pub mod circuit_prices;

use std::collections::HashMap;

use circuit_prices::CODE_DECOMMITMENT_COST_PER_WORD_IN_ERGS;
use circuit_prices::CODE_DECOMMITTER_SORTER_COST_IN_ERGS;
use circuit_prices::RAM_PERMUTATION_COST_IN_ERGS;
use circuit_prices::VM_CYCLE_COST_IN_ERGS;

pub use bitflags;
pub use blake2;
pub use ethereum_types;
pub use k256;
pub use p256;
pub use sha2;
pub use sha3;

pub use self::definitions::*;
pub use self::imm_mem_modifiers::*;
pub use self::opcode::*;
pub use self::system_params::*;
pub use self::utils::*;

use lazy_static::lazy_static;

use crate::decoding::VariantMonotonicNumber;

pub const OPCODES_TABLE_WIDTH: usize = 11;
pub const CONDITIONAL_BITS_SHIFT: usize = 13;
pub const MEMORY_GROWTH_ERGS_PER_BYTE: u32 = 1;

const _: () = if MEMORY_GROWTH_ERGS_PER_BYTE != 1 {
    panic!()
};

pub const VARIANT_AND_CONDITION_ENCODING_BITS: usize = 16;

pub const REGISTER_INDEX_ENCODING_BITS: usize = 4;

pub const SRC_REGS_SHIFT: u32 = 16;
pub const DST_REGS_SHIFT: u32 = 24;

// flattened bits the exclusively (mostly) encode all the opcode properties
pub const OPCODE_TYPE_BITS: usize = NUM_OPCODES;

pub const OPCODE_INPUT_VARIANT_FLAGS: usize = 6;
pub const OPCODE_OUTPUT_VARIANT_FLAGS: usize = 4;

// aux flags for resolution of exceptions
pub const KERNEL_MODE_FLAG_BITS: usize = 1;
pub const CAN_BE_USED_IN_STATIC_CONTEXT_FLAG_BITS: usize = 1;
pub const EXPLICIT_PANIC_FLAG_BITS: usize = 1;

pub const KERNER_MODE_FLAG_IDX: usize = 0;
pub const CAN_BE_USED_IN_STATIC_CONTEXT_FLAG_IDX: usize = 1;
pub const EXPLICIT_PANIC_FLAG_IDX: usize = 2;

const WIDTH_MULTIPLE: usize = 16;

pub const INITIAL_SP_ON_FAR_CALL: u64 = 0;
pub const UNMAPPED_PAGE: u32 = 0;
pub const STATIC_MEMORY_PAGE: u32 = 1;
pub const BOOTLOADER_BASE_PAGE: u32 = 8;
pub const BOOTLOADER_CODE_PAGE: u32 = BOOTLOADER_BASE_PAGE;
pub const BOOTLOADER_CALLDATA_PAGE: u32 = BOOTLOADER_BASE_PAGE - 1; // some convention
pub const BOOTLOADER_STACK_PAGE: u32 = BOOTLOADER_BASE_PAGE + 1;
pub const BOOTLOADER_HEAP_PAGE: u32 = BOOTLOADER_BASE_PAGE + 2;
pub const BOOTLOADER_AUX_HEAP_PAGE: u32 = BOOTLOADER_BASE_PAGE + 3;

pub const NEW_MEMORY_PAGES_PER_FAR_CALL: u32 = 8;
pub const STARTING_TIMESTAMP: u32 = 1024;
pub const STARTING_BASE_PAGE: u32 = 2048;
pub const TIME_DELTA_PER_CYCLE: u32 = 4;
pub const MAX_PENDING_CYCLES: usize = 1;

pub const LOG2_NUM_ADDRESSABLE_HEAP_BYTES: u32 = 24;

pub(crate) const NUM_NON_EXCLUSIVE_FLAGS: usize = 2;

pub fn total_description_bits_for_version(version: ISAVersion) -> usize {
    let total = OPCODE_TYPE_BITS
        + max_num_variants_for_version(version)
        + max_num_flags_for_version(version)
        + OPCODE_INPUT_VARIANT_FLAGS
        + OPCODE_OUTPUT_VARIANT_FLAGS;

    total
}

pub fn total_description_bits_rounded_for_version(version: ISAVersion) -> usize {
    let mut total = OPCODE_TYPE_BITS
        + max_num_variants_for_version(version)
        + max_num_flags_for_version(version)
        + OPCODE_INPUT_VARIANT_FLAGS
        + OPCODE_OUTPUT_VARIANT_FLAGS;
    if total % WIDTH_MULTIPLE != 0 {
        total += WIDTH_MULTIPLE - total % WIDTH_MULTIPLE;
    }

    total
}

pub const TOTAL_AUX_BITS: usize =
    KERNEL_MODE_FLAG_BITS + CAN_BE_USED_IN_STATIC_CONTEXT_FLAG_BITS + EXPLICIT_PANIC_FLAG_BITS;

pub fn total_opcode_description_and_aux_bits_for_version(version: ISAVersion) -> usize {
    total_description_bits_rounded_for_version(version) + TOTAL_AUX_BITS
}

pub const DEFAULT_ISA_VERSION: ISAVersion = ISAVersion(2);

pub const NUM_SYSTEM_CONTRACTS: usize = 1 << 16;

lazy_static! {
    pub static ref OPCODES_TABLE: [OpcodeVariant; 1 << OPCODES_TABLE_WIDTH] = {
        synthesize_opcode_decoding_tables(OPCODES_TABLE_WIDTH, DEFAULT_ISA_VERSION).try_into().unwrap()
    };

    pub static ref OPCODES_PROPS_INTEGER_BITMASKS: [u64; 1 << OPCODES_TABLE_WIDTH] = {
        synthesize_bit_decomposition_table(&*OPCODES_TABLE, DEFAULT_ISA_VERSION).try_into().unwrap()
    };

    pub static ref OPCODE_TO_MONOTONIC_INDEX_NUMBER_MAP: HashMap<OpcodeVariant, VariantMonotonicNumber> = {
        let mut result = HashMap::<OpcodeVariant, VariantMonotonicNumber>::new();
        for (idx, el) in OPCODES_TABLE.iter().enumerate() {
            if let Some(existing) = result.get(el) {
                let usize_index = (*existing).into_usize();
                assert_eq!(OPCODES_TABLE[usize_index], INVALID_OPCODE_VARIANT);
            } else {
                let _ = result.insert(*el, VariantMonotonicNumber::from_usize(idx));
            }
        }

        result
    };

    pub static ref OPCODE_TO_CANONICAL_INDEX_LOOKUP_MAP: HashMap<OpcodeVariant, usize> = {
        let mut result = HashMap::new();
        for (idx, el) in OPCODES_TABLE.iter().enumerate() {
            if let Some(existing) = result.get(el) {
                assert_eq!(OPCODES_TABLE[*existing], INVALID_OPCODE_VARIANT);
            } else {
                let _ = result.insert(*el, idx);
            }
        }

        result
    };

    pub static ref NOP_OPCODE_VARIANT: OpcodeVariant = {
        let variant = OpcodeVariant{
            opcode: Opcode::Nop(NopOpcode),
            src0_operand_type: Operand::Full(ImmMemHandlerFlags::UseRegOnly),
            dst0_operand_type: Operand::Full(ImmMemHandlerFlags::UseRegOnly),
            flags: [false; NUM_NON_EXCLUSIVE_FLAGS],
        };
        assert!(OPCODE_TO_CANONICAL_INDEX_LOOKUP_MAP.contains_key(&variant));

        variant
    };

    pub static ref NOP_BITSPREAD_U64: u64 = {
        let index = OPCODE_TO_CANONICAL_INDEX_LOOKUP_MAP[&NOP_OPCODE_VARIANT];
        let bitspread = OPCODES_PROPS_INTEGER_BITMASKS[index];

        bitspread
    };

    pub static ref PANIC_OPCODE_VARIANT: OpcodeVariant = {
        let variant = OpcodeVariant{
            opcode: Opcode::Ret(RetOpcode::Panic),
            src0_operand_type: Operand::RegOnly,
            dst0_operand_type: Operand::RegOnly,
            flags: [false; NUM_NON_EXCLUSIVE_FLAGS],
        };

        assert!(OPCODE_TO_CANONICAL_INDEX_LOOKUP_MAP.contains_key(&variant));

        variant
    };

    pub static ref PANIC_BITSPREAD_U64: u64 = {
        let index = OPCODE_TO_CANONICAL_INDEX_LOOKUP_MAP[&PANIC_OPCODE_VARIANT];
        let bitspread = OPCODES_PROPS_INTEGER_BITMASKS[index];

        bitspread
    };

    pub static ref OPCODE_PROTOTYPES: Vec<Box<dyn OpcodeProps>> = {
        all_opcodes()
    };

    pub static ref NUM_LOGICAL_OPCODES: usize = {
        OPCODE_PROTOTYPES.len()
    };

    pub static ref NUM_INPUT_VARIANTS: usize = {
        ImmMemHandlerFlags::num_src_variants()
    };

    pub static ref NUM_OUTPUT_VARIANTS: usize = {
        ImmMemHandlerFlags::num_dst_variants()
    };

    // Preliminary pricing
    pub static ref OPCODES_PRICES: [u32; 1 << OPCODES_TABLE_WIDTH] = {
        let mut result = Vec::with_capacity(1 << OPCODES_TABLE_WIDTH);
        for opcode in OPCODES_TABLE.iter() {
            let price = opcode.ergs_price();
            result.push(price)
        }

        result.try_into().unwrap()
    };

    pub static ref STIPENDS_AND_EXTRA_COSTS_TABLE: Box<[(u32, u32); NUM_SYSTEM_CONTRACTS]> = {
        let mut default_table = Box::new([(0u32, 0u32); NUM_SYSTEM_CONTRACTS]);
        use crate::system_params::MSG_VALUE_SIMULATOR_ADDITIVE_COST;
        default_table[ADDRESS_MSG_VALUE as usize] = (0u32, MSG_VALUE_SIMULATOR_ADDITIVE_COST);

        default_table
    };
}

pub const INVALID_OPCODE_ERGS: u32 = u32::MAX; // will burn everything at once

// `RICH_ADDRESSING_OPCODE_ERGS` is for opcodes that can write
// their return value/read the input onto the stack and so take 1-2 RAM permutations more than
// an average opcode. Note, that while, in the worst case, a rich addressing may take 3 ram permutations
// (1 for reading the opcode, 1 for writing input value, 1 for writing output value), the
// 1 "reading of opcode" reads 4 sequential opcodes at the same time, so if we priced users by the worst case
// (VM_CYCLE_COST_IN_ERGS + 3 * RAM_PERMUTATION_COST_IN_ERGS), they would overpay too much, while in case of a DDoS
// attack, we would only overpay only 1.2x.
pub const RICH_ADDRESSING_OPCODE_ERGS: u32 =
    VM_CYCLE_COST_IN_ERGS + 2 * RAM_PERMUTATION_COST_IN_ERGS;
pub const AVERAGE_OPCODE_ERGS: u32 = VM_CYCLE_COST_IN_ERGS + RAM_PERMUTATION_COST_IN_ERGS;

/// The following prices are meant to take into account the I/O overhead for
/// these operations (i.e. state bloat that becomes with them)
pub const STORAGE_READ_IO_PRICE: u32 = 150;
pub const STORAGE_WRITE_IO_PRICE: u32 = 250;
pub const EVENT_IO_PRICE: u32 = 25;
pub const L1_MESSAGE_IO_PRICE: u32 = 100; // Extra for merklization

/// This variable is meant to represent the cost for creating a new item on callstack
pub const CALL_LIKE_ERGS_COST: u32 = 20;

pub const ERGS_PER_CODE_WORD_DECOMMITTMENT: u32 = CODE_DECOMMITMENT_COST_PER_WORD_IN_ERGS;

const _: () = if CODE_DECOMMITTER_SORTER_COST_IN_ERGS > u16::MAX as u32 {
    panic!()
};