# EVMMAX + EOF
https://github.com/ethereum/EIPs/pull/5843
## Setup section
Section contents:
| name | length | value | description |
|---------------|----------|---------------|-------------|
| bit_width_multiplier | 1 bytes | | `bit_width = bit_width_multiplier * 64` |
| max_memory_slot | 1 bytes | | the size of maximum memory expansion in words |
| memory_offset | 2 bytes | | offset of the memory (in bytes) where the EVMMAX memory starts; this memory can be accessed by regular EVM instructions too |
| modulus | n bytes | | the modulus with `n = bit_width / 8` bytes; big endian |
| montgomery_modulus | 8 bytes | | big endian |
| r_squared | n bytes | | big endian |
Could use "custom" sections in EOF for this?
### Validation
At deployment time this setup section is validated:
1. `modulus` must be odd.
2. `(modulus x motgomery_modulus + 1) % modulus == 0`.
3. Check every EVMMAX instructions' immediate memory slots to be `<= max_memory_slot`, otherwise the instruction is invalid.
4. `r_squared == ((1 << bit_width) ** 2) % modulus`
Furthermore we could specify acceptable `modulus` figures at introduction of EVMMAX, keep extending the supported values, and potentially drop this limitation in the future.
Knowing the `modulus` upfront opens up the possibility for chosing optimal algorithms before execution. (A good example is BLS12-381.)
### Execution
1. When an EOF contract with the EVMMAX setup section is instantiated, the contents of the section are loaded into the `evmmax_state`.
2. Memory expansion is performed and charged for `memory_offset + ((max_memory_slot + 1) * bit_width) / 8`.
3. The `r_squared` value is copied to `memory_offset` (i.e. it is stored in slot 0).
4. Since instruction gas costs are based on `bit_width`, they are precalculated here.
## Gas formula
```python
MULMONTX_GAS_A = 0.1
MULMONTX_GAS_B = 0.7
ADDMODX_GAS_A = 0.2
ADDMODX_GAS_B = 0.6
def gas_mulmontx(bit_width):
return ceil(MULMONTX_GAS_A * ((bit_width / 64) ** 2) + MULMONTX_GAS_B)
def gas_addmodx(bit_width):
return ceil(ADDMODX_GAS_A * (bit_width / 64) + ADDMODX_GAS_B)
def gas_submodx(bit_width):
return gas_addmodx(bit_width)
```
## Instructions
We introduce some helpers:
```python
def slot_to_mem_offset(slot):
return evmmax_state.memory_offset + (slot * bit_width) / 8
def slot_value(slot):
return (slot_to_mem_offset(slot), slot_to_mem_offset(slot + 1))
```
We introduce three new instructions:
1. `ADDMODX{out_slot, x_slot, y_slot}` (0x22)
2. `SUBMODX{out_slot, x_slot, y_slot}` (0x23)
3. `MULMODX{out_slot, x_slot, y_slot}` (0x24)
The `{}` notation means that each of the arguments are 8-bit immediate bytes following the instruction, i.e. `ADDMODX{1, 2, 3}` is encoded as `0x22 0x01 0x02 0x03`.
There is identical runtime checking at each instruction: if `slot_value(out_slot)`, `slot_value(x_slot)` or `slot_value(y_slot)` is `>= modulus`, then abort with OOG.
The `TOMONTX` instruction is not needed anymore, because it can be accomplished with `MULMODX(out_slot, x_slot, 0}`.
Notice that due to deploy time validation, there is no need to:
- Perform stack checks for the immediate arguments.
- Perform memory expansion.
- Perform runtime gas calculation based on the `bit_width` (as it can be calculated once during the set up).