ZKsync Era Extension Simulation (verbatim)

NOTES:
  • changed META - it can be used for MSIZE simulation
  • setting ergs per pubdata is done by separate opcode now (not part of near_call)
  • incrementing TX counter is done by separate opcode now (not part of far_call)

Our VM has some opcodes that are not expressible in Solidity, but we can simulate them on compiler level by abusing “CALL” instruction. We use 2nd parameter of “CALL” (address) as a marker, and remaining 6 parameters as input parameters (we use “address”-like field since it’s kind of shorter type, if assembly block cares about types in Solidity). Unfortunately “CALL” returns only 1 stack parameter, but it looks sufficient for our purposes.

Please note, that some of the methods don’t modify state, so STATICCALL instead of CALL should be used for them. The type of the needed method is indicated in the rightmost column.

Call types are not validated and do not affect the simulation behavior, unless specified otherwise, like in raw_far_call and system_call simulations, where the call type is passed through.

For some simulations below we assume that there exist a hidden global pseudo-variable called ACTIVE_PTR for manipulations, since one can not easily load pointer value into Solidity’s variable.

Simulated opcodeCALL param 0 (gas)CALL param 1 (address)CALL param 2 (value)CALL param 3 (input offset)CALL param 4 (input length)CALL param 5 (output offset)CALL param 6 (output length)Return valuecall typeLLVM implementationMotivation
to_l1(is_first, in1, in2)if_first (bool)0xFFFFin1 (u256)in2 (u256)0xFFFF to prevent optimizing out by Yul00_call@llvm.syncvm.tol1(i256 %in1, i256 %in2, i256 %is_first)Send messages to L1
code_source00xFFFE-00xFFFF to prevent optimizing out by Yul00addressstaticcall@llvm.syncvm.context(i256 %param) ; param == 2 (see SyncVM.h)Largely to be able to catch “delegatecalls” in system contracts (by comparing this == code_source)
precompile(in1, ergs_to_burn, out0)in1 (u256)0xFFFD-ergs_to_burn (u32)0xFFFF to prevent optimizing out by Yul00out0staticcall@llvm.syncvm.precompile(i256 %in1, i256 %ergs)way to trigger call to precompile in VM
decommit(versioned_hash, ergs_to_burn, out0)versioned_hash (u256)0xFFDD-ergs_to_burn (u32)0xFFFF to prevent optimizing out by Yul00out0staticcallsaves the result pointer to @ptr_decommitway to trigger decommit in VM
meta00xFFFC-00xFFFF to prevent optimizing out by Yul00u256 tight packingstaticcall@llvm.syncvm.context(i256 %param) ; param == 3 (see SyncVM.h)way to trigger call to meta information about some small pieces of the state in VM
mimic_call(to, abi_data, implicit r5 = who to mimic)who_to_call0xFFFB0abi_datawho_to_mimic00WILL mess up the registers and WILL use r1-r4 for our standard ABI convention and r5 for the extra who_to_mimic argumentany in the code; mimic call in the bytecodeRuntime {i256, i1} __mimiccall(i256, i256, i256, {i256, i1})
system_mimic_call(to, abi_data, implicit r3, r4, r5 = who to mimic)who_to_call0xFFFA0abi_datawho_to_mimicvalue_to_put_into_r3value_to_put_into_r4WILL mess up the registers and WILL use r1-r4 for our standard ABI convention and r5 for the extra who_to_mimic argumentany in the code; mimic call in the bytecodeRuntime {i256, i1} __mimiccall(i256, i256, i256, {i256, i1})
mimic_call_byref(to, ACTIVE_PTR, implicit r5 = who to mimic)who_to_call0xFFF900who_to_mimic00WILL mess up the registers and WILL use r1-r4 for our standard ABI convention and r5 for the extra who_to_mimic argumentany in the code; mimic call in the bytecodeRuntime {i256, i1} __mimiccall(i8 addrspace(3), i256, i256, {i256, i1})Same as one above, but takes ABI data from ACTIVE_PTR
system_mimic_call_byref(to, ACTIVE_PTR, implicit r3, r4, r5 = who to mimic)who_to_call0xFFF800who_to_mimicvalue_to_put_into_r3value_to_put_into_r4WILL mess up the registers and WILL use r1-r4 for our standard ABI convention and r5 for the extra who_to_mimic argumentany in the code; mimic call in the bytecodeRuntime {i256, i1} __mimiccall(i8 addrspace(3), i256, i256, {i256, i1})Same as one above, but takes ABI data from ACTIVE_PTR
raw_far_callwho_to_call0xFFF700abi_data (CAN be with “to system = true”)output_offsetoutput_lengthSame as for EVM callcallstaticdelegate (the call type is preserved)
It’s very similar to “system_call” described below, but for the cases when we only need to have to_system = true set in ABI (responsibility of the user, NOT the compiler), but we do not actually need to pass anything through r3 and r4 (so we can save on setting them or zeroing them, whatever)
raw_far_call_byrefwho_to_call0xFFF6000xFFFF to prevent optimizing out by Yuloutput_offsetoutput_lengthSame as for EVM callcallstaticdelegate (the call type is preserved)
Same as one above, but takes ABI data from ACTIVE_PTR
system_callwho_to_call0xFFF5value_to_put_into_r3 (only for call with 7 arguments)value_to_put_into_r4abi_data (MUST have “to system” set)value_to_put_into_r5value_to_put_into_r6Same as for EVM callcallstaticdelegate (the call type is preserved)
to call system contracts, like MSG_VALUE_SIMULATOR. We may need 4 different formal definitions for cases when we would want to have integer/ptr in r3 and r4
system_call_byrefwho_to_call0xFFF4value_to_put_into_r3 (only for call with 7 arguments)value_to_put_into_r40xFFFF to prevent optimizing out by Yulvalue_to_put_into_r5value_to_put_into_r6Same as for EVM callcallstaticdelegate (the call type is preserved)
to call system contracts, like MSG_VALUE_SIMULATOR. We may need 4 different formal definitions for cases when we would want to have integer/ptr in r3 and r4
Same as one above, but takes ABI data from ACTIVE_PTR
set_context_u12800xFFF3value00xFFFF to prevent optimizing out by Yul00-call
set_pubdata_pricein10xFFF2000xFFFF to prevent optimizing out by Yul00-callcontext.set_ergs_per_pubdata in1 in assembly
increment_tx_counter00xFFF1000xFFFF to prevent optimizing out by Yul00-callcontext.inc_tx_num in assembly
ptr_calldata00xFFF0-00xFFFF to prevent optimizing out by Yul00staticcallone passed in r1 on far_call to the callee (save in very first instructions on entry)Loads as INTEGER!
call_flags00xFFEF-00xFFFF to prevent optimizing out by Yul00staticcallone passed in r2 on far_call to the callee (save in very first instructions on entry)
ptr_return_data00xFFEE-00xFFFF to prevent optimizing out by Yul00staticcallone passed in r1 on return from far_call back to the caller (save in very first instruction in the corresponding branch!)Loads as INTEGER!
event_initializein10xFFED-in20xFFFF to prevent optimizing out by Yul00call
event_writein10xFFEC-in20xFFFF to prevent optimizing out by Yul00call
load_calldata_into_active_ptr00xFFEB-00xFFFF to prevent optimizing out by Yul00staticcallloads value of @calldataptr (from r1 at the entry point of the contract into virtual ACTIVE_PTR)ACTIVE_PTR
load_returndata_into_active_ptr00xFFEA-00xFFFF to prevent optimizing out by Yul00staticcallloads value of the latest @returndataptr (from the r1 at the point of return from the child into virtual ACTIVE_PTR)
load_decommit_into_active_ptr00xFFDC-00xFFFF to prevent optimizing out by Yul00staticcallloads value of the @ptr_decommit into virtual ACTIVE_PTR
ptr_add_into_activein10xFFE9-00xFFFF to prevent optimizing out by Yul00staticcallperforms ptr.add ACTIVE_PTR, in1, ACTIVE_PTR
ptr_shrink_into_activein10xFFE8-00xFFFF to prevent optimizing out by Yul00staticcallperforms ptr.shrink ACTIVE_PTR, in1, ACTIVE_PTR
ptr_pack_into_activein10xFFE7-00xFFFF to prevent optimizing out by Yul00staticcallperforms ptr.pack ACTIVE_PTR, in1, ACTIVE_PTR
multiplication_highin10xFFE6-in20xFFFF to prevent optimizing out by Yul00Returns the higher register (the overflown part)staticcall
extra_abi_datain10xFFE5-00xFFFF to prevent optimizing out by Yul00staticcallones passed in r3-r12 on far_call to the callee (saved in the very first instructions in the entry)
ptr_data_loadoffset0xFFE4-00xFFFF to prevent optimizing out by Yul00staticcall
ptr_data_copydestination0xFFE3-sourcesize00staticcall
ptr_data_size00xFFE2-00xFFFF to prevent optimizing out by Yul00staticcall
active_ptr_swapindex_10xFFD9-index_20xFFFF to prevent optimizing out by Yul00staticcallswaps active pointers
const_array_declareindex(constant)0xFFE1-size(constant)0xFFFF to prevent optimizing out by Yul00staticcall
const_array_setindex(constant)0xFFE0-offset(constant)0xFFFF to prevent optimizing out by Yulvalue(constant)0staticcall
const_array_finalizeindex(constant)0xFFDF-00xFFFF to prevent optimizing out by Yul00staticcall
const_array_getindex(constant)0xFFDE-offset0xFFFF to prevent optimizing out by Yul00staticcall
return_forward00xFFDB-00xFFFF to prevent optimizing out by Yul00staticcallgenerates a return forwarding the active pointer
revert_forward00xFFDA-00xFFFF to prevent optimizing out by Yul00staticcallgenerates a revert forwarding the active pointer

Requirements for calling system contracts

By default, all system contracts up to the address 0xFFFF require that the call was done via system call (i.e. call_flags&2 != 0 .

Exceptions:

  • BOOTLOADER_FORMAL address as the users need to be able to send money there.

Meaning of ABI params:

  • MSG_VALUE_SIMULATOR: extra_abi_data_1 = value || whether_the_call_is_system, where || denotes the concatenation, value should occupy first 128 bits, while whether_the_call_is_system is a 1-bit flag that denotes whether the call should be a system call. extra_abi_data_2 is the address of the callee.
  • No meaning for the rest.

Made with ❤️ by the ZKsync Community