From 44bd426a0b5915da962519119185ded8c0277eee Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 27 Nov 2024 17:17:17 +0100 Subject: [PATCH 01/62] fix(EVM): Add static MCOPY cost (#1081) --- system-contracts/contracts/EvmEmulator.yul | 4 ++++ system-contracts/evm-emulator/EvmEmulatorLoop.template.yul | 2 ++ 2 files changed, 6 insertions(+) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index f9a9c0d75..40d167ac5 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -2061,6 +2061,8 @@ object "EvmEmulator" { ip := add(ip, 1) } case 0x5E { // OP_MCOPY + evmGasLeft := chargeGas(evmGasLeft, 3) + let destOffset, offset, size popStackCheck(sp, 3) destOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) @@ -5109,6 +5111,8 @@ object "EvmEmulator" { ip := add(ip, 1) } case 0x5E { // OP_MCOPY + evmGasLeft := chargeGas(evmGasLeft, 3) + let destOffset, offset, size popStackCheck(sp, 3) destOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index 78e1a5315..87dbf7e8c 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -836,6 +836,8 @@ for { } true { } { ip := add(ip, 1) } case 0x5E { // OP_MCOPY + evmGasLeft := chargeGas(evmGasLeft, 3) + let destOffset, offset, size popStackCheck(sp, 3) destOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) From 8e8323de73e15d1c88a0b90d008a7c13528c4a29 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 27 Nov 2024 17:18:16 +0100 Subject: [PATCH 02/62] fix(EVM): Cleanup in checkMemIsAccessible (#1082) --- system-contracts/contracts/EvmEmulator.yul | 20 ++++++------------- .../EvmEmulatorFunctions.template.yul | 10 +++------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 40d167ac5..da253ce42 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -167,10 +167,6 @@ object "EvmEmulator" { max := 0x400000 // 4MB } - function MAX_MEMORY_SLOT() -> max { - max := add(MEM_OFFSET(), MAX_POSSIBLE_MEM_LEN()) - } - function MAX_UINT() -> max_uint { max_uint := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff } @@ -282,10 +278,10 @@ object "EvmEmulator" { } } - function checkMemIsAccessible(index, offset) { - checkOverflow(index, offset) + function checkMemIsAccessible(relativeOffset, size) { + checkOverflow(relativeOffset, size) - if gt(add(index, offset), MAX_MEMORY_SLOT()) { + if gt(add(relativeOffset, size), MAX_POSSIBLE_MEM_LEN()) { panic() } } @@ -3217,10 +3213,6 @@ object "EvmEmulator" { max := 0x400000 // 4MB } - function MAX_MEMORY_SLOT() -> max { - max := add(MEM_OFFSET(), MAX_POSSIBLE_MEM_LEN()) - } - function MAX_UINT() -> max_uint { max_uint := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff } @@ -3332,10 +3324,10 @@ object "EvmEmulator" { } } - function checkMemIsAccessible(index, offset) { - checkOverflow(index, offset) + function checkMemIsAccessible(relativeOffset, size) { + checkOverflow(relativeOffset, size) - if gt(add(index, offset), MAX_MEMORY_SLOT()) { + if gt(add(relativeOffset, size), MAX_POSSIBLE_MEM_LEN()) { panic() } } diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 58736a58d..eb7e554af 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -105,10 +105,6 @@ function MAX_POSSIBLE_MEM_LEN() -> max { max := 0x400000 // 4MB } -function MAX_MEMORY_SLOT() -> max { - max := add(MEM_OFFSET(), MAX_POSSIBLE_MEM_LEN()) -} - function MAX_UINT() -> max_uint { max_uint := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff } @@ -220,10 +216,10 @@ function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> maxExpand { } } -function checkMemIsAccessible(index, offset) { - checkOverflow(index, offset) +function checkMemIsAccessible(relativeOffset, size) { + checkOverflow(relativeOffset, size) - if gt(add(index, offset), MAX_MEMORY_SLOT()) { + if gt(add(relativeOffset, size), MAX_POSSIBLE_MEM_LEN()) { panic() } } From ef91e5233e31550fca911d59300c717b7525f565 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 27 Nov 2024 17:19:56 +0100 Subject: [PATCH 03/62] fix(EVM): Simplify OP_PC (#1084) --- system-contracts/contracts/EvmEmulator.yul | 12 ++++++------ .../evm-emulator/EvmEmulatorLoop.template.yul | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index da253ce42..303b7ffd5 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -2010,10 +2010,10 @@ object "EvmEmulator" { } case 0x58 { // OP_PC evmGasLeft := chargeGas(evmGasLeft, 2) - ip := add(ip, 1) - // PC = ip - 32 (bytecode size) - 1 (current instruction) - sp, stackHead := pushStackItem(sp, sub(sub(ip, BYTECODE_LEN_OFFSET()), 33), stackHead) + sp, stackHead := pushStackItem(sp, sub(ip, BYTECODE_OFFSET()), stackHead) + + ip := add(ip, 1) } case 0x59 { // OP_MSIZE evmGasLeft := chargeGas(evmGasLeft, 2) @@ -5056,10 +5056,10 @@ object "EvmEmulator" { } case 0x58 { // OP_PC evmGasLeft := chargeGas(evmGasLeft, 2) - ip := add(ip, 1) - // PC = ip - 32 (bytecode size) - 1 (current instruction) - sp, stackHead := pushStackItem(sp, sub(sub(ip, BYTECODE_LEN_OFFSET()), 33), stackHead) + sp, stackHead := pushStackItem(sp, sub(ip, BYTECODE_OFFSET()), stackHead) + + ip := add(ip, 1) } case 0x59 { // OP_MSIZE evmGasLeft := chargeGas(evmGasLeft, 2) diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index 87dbf7e8c..20897c518 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -789,10 +789,10 @@ for { } true { } { } case 0x58 { // OP_PC evmGasLeft := chargeGas(evmGasLeft, 2) - ip := add(ip, 1) - // PC = ip - 32 (bytecode size) - 1 (current instruction) - sp, stackHead := pushStackItem(sp, sub(sub(ip, BYTECODE_LEN_OFFSET()), 33), stackHead) + sp, stackHead := pushStackItem(sp, sub(ip, BYTECODE_OFFSET()), stackHead) + + ip := add(ip, 1) } case 0x59 { // OP_MSIZE evmGasLeft := chargeGas(evmGasLeft, 2) From b649f5f5cc0cba54479688d31e98d585542391d0 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 27 Nov 2024 17:25:48 +0100 Subject: [PATCH 04/62] fix(EVM): Do not charge additionally for EraVM decommit (#1086) --- system-contracts/contracts/EvmEmulator.yul | 46 ------------------- .../EvmEmulatorFunctions.template.yul | 23 ---------- 2 files changed, 69 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 303b7ffd5..e4d7e98b5 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -185,9 +185,6 @@ object "EvmEmulator" { function OVERHEAD() -> overhead { overhead := 2000 } - // From precompiles/CodeOracle - function DECOMMIT_COST_PER_WORD() -> cost { cost := 4 } - function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 //////////////////////////////////////////////////////////////// @@ -848,17 +845,6 @@ object "EvmEmulator" { // Call native ZkVm contract from EVM context function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas - let decommitZkVmGasCost := decommitmentCost(addr) - - // we are going to charge decommit cost even if address is already warm - // decommit cost is subtracted from the callee frame - switch gt(decommitZkVmGasCost, zkEvmGasToPass) - case 0 { - zkEvmGasToPass := sub(zkEvmGasToPass, decommitZkVmGasCost) - } - default { - zkEvmGasToPass := 0 - } if gt(zkEvmGasToPass, UINT32_MAX()) { // just in case zkEvmGasToPass := UINT32_MAX() @@ -886,15 +872,6 @@ object "EvmEmulator" { } } - function decommitmentCost(addr) -> cost { - // charge for contract decommitment - let byteSize := extcodesize(addr) - cost := mul( - div(add(byteSize, 31), 32), // rounding up - DECOMMIT_COST_PER_WORD() - ) - } - function capGasForCall(evmGasLeft, oldGasToPass) -> gasToPass { let maxGasToPass := sub(evmGasLeft, shr(6, evmGasLeft)) // evmGasLeft >> 6 == evmGasLeft/64 gasToPass := oldGasToPass @@ -3231,9 +3208,6 @@ object "EvmEmulator" { function OVERHEAD() -> overhead { overhead := 2000 } - // From precompiles/CodeOracle - function DECOMMIT_COST_PER_WORD() -> cost { cost := 4 } - function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 //////////////////////////////////////////////////////////////// @@ -3894,17 +3868,6 @@ object "EvmEmulator" { // Call native ZkVm contract from EVM context function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas - let decommitZkVmGasCost := decommitmentCost(addr) - - // we are going to charge decommit cost even if address is already warm - // decommit cost is subtracted from the callee frame - switch gt(decommitZkVmGasCost, zkEvmGasToPass) - case 0 { - zkEvmGasToPass := sub(zkEvmGasToPass, decommitZkVmGasCost) - } - default { - zkEvmGasToPass := 0 - } if gt(zkEvmGasToPass, UINT32_MAX()) { // just in case zkEvmGasToPass := UINT32_MAX() @@ -3932,15 +3895,6 @@ object "EvmEmulator" { } } - function decommitmentCost(addr) -> cost { - // charge for contract decommitment - let byteSize := extcodesize(addr) - cost := mul( - div(add(byteSize, 31), 32), // rounding up - DECOMMIT_COST_PER_WORD() - ) - } - function capGasForCall(evmGasLeft, oldGasToPass) -> gasToPass { let maxGasToPass := sub(evmGasLeft, shr(6, evmGasLeft)) // evmGasLeft >> 6 == evmGasLeft/64 gasToPass := oldGasToPass diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index eb7e554af..96d0df147 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -123,9 +123,6 @@ function MSG_VALUE_SIMULATOR_STIPEND_GAS() -> gas_stipend { function OVERHEAD() -> overhead { overhead := 2000 } -// From precompiles/CodeOracle -function DECOMMIT_COST_PER_WORD() -> cost { cost := 4 } - function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 //////////////////////////////////////////////////////////////// @@ -786,17 +783,6 @@ function callPrecompile(addr, precompileCost, gasToPass, value, argsOffset, args // Call native ZkVm contract from EVM context function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas - let decommitZkVmGasCost := decommitmentCost(addr) - - // we are going to charge decommit cost even if address is already warm - // decommit cost is subtracted from the callee frame - switch gt(decommitZkVmGasCost, zkEvmGasToPass) - case 0 { - zkEvmGasToPass := sub(zkEvmGasToPass, decommitZkVmGasCost) - } - default { - zkEvmGasToPass := 0 - } if gt(zkEvmGasToPass, UINT32_MAX()) { // just in case zkEvmGasToPass := UINT32_MAX() @@ -824,15 +810,6 @@ function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffs } } -function decommitmentCost(addr) -> cost { - // charge for contract decommitment - let byteSize := extcodesize(addr) - cost := mul( - div(add(byteSize, 31), 32), // rounding up - DECOMMIT_COST_PER_WORD() - ) -} - function capGasForCall(evmGasLeft, oldGasToPass) -> gasToPass { let maxGasToPass := sub(evmGasLeft, shr(6, evmGasLeft)) // evmGasLeft >> 6 == evmGasLeft/64 gasToPass := oldGasToPass From 5db2a306c2e8fbbb104fe09881e585ce5439d61b Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 27 Nov 2024 17:31:58 +0100 Subject: [PATCH 05/62] fix(EVM): Fix stack overflow check (#1085) --- system-contracts/contracts/EvmEmulator.yul | 28 ++++++++----------- .../EvmEmulatorFunctions.template.yul | 14 ++++------ 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index e4d7e98b5..cf7cfd1a9 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -132,8 +132,12 @@ object "EvmEmulator" { offset := add(LAST_RETURNDATA_SIZE_OFFSET(), 64) } + function MAX_STACK_SLOT_OFFSET() -> offset { + offset := add(STACK_OFFSET(), mul(1023, 32)) + } + function BYTECODE_LEN_OFFSET() -> offset { - offset := add(STACK_OFFSET(), mul(1024, 32)) + offset := add(MAX_STACK_SLOT_OFFSET(), 32) } function BYTECODE_OFFSET() -> offset { @@ -507,7 +511,7 @@ object "EvmEmulator" { } function pushStackItem(sp, item, oldStackHead) -> newSp, stackHead { - if iszero(lt(sp, BYTECODE_LEN_OFFSET())) { + if iszero(lt(sp, MAX_STACK_SLOT_OFFSET())) { panic() } @@ -534,12 +538,6 @@ object "EvmEmulator" { } } - function pushStackCheck(sp, numInputs) { - if iszero(lt(add(sp, mul(0x20, sub(numInputs, 1))), BYTECODE_LEN_OFFSET())) { - panic() - } - } - function accessStackHead(sp, stackHead) -> value { if lt(sp, STACK_OFFSET()) { panic() @@ -3155,8 +3153,12 @@ object "EvmEmulator" { offset := add(LAST_RETURNDATA_SIZE_OFFSET(), 64) } + function MAX_STACK_SLOT_OFFSET() -> offset { + offset := add(STACK_OFFSET(), mul(1023, 32)) + } + function BYTECODE_LEN_OFFSET() -> offset { - offset := add(STACK_OFFSET(), mul(1024, 32)) + offset := add(MAX_STACK_SLOT_OFFSET(), 32) } function BYTECODE_OFFSET() -> offset { @@ -3530,7 +3532,7 @@ object "EvmEmulator" { } function pushStackItem(sp, item, oldStackHead) -> newSp, stackHead { - if iszero(lt(sp, BYTECODE_LEN_OFFSET())) { + if iszero(lt(sp, MAX_STACK_SLOT_OFFSET())) { panic() } @@ -3557,12 +3559,6 @@ object "EvmEmulator" { } } - function pushStackCheck(sp, numInputs) { - if iszero(lt(add(sp, mul(0x20, sub(numInputs, 1))), BYTECODE_LEN_OFFSET())) { - panic() - } - } - function accessStackHead(sp, stackHead) -> value { if lt(sp, STACK_OFFSET()) { panic() diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 96d0df147..e58879762 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -70,8 +70,12 @@ function STACK_OFFSET() -> offset { offset := add(LAST_RETURNDATA_SIZE_OFFSET(), 64) } +function MAX_STACK_SLOT_OFFSET() -> offset { + offset := add(STACK_OFFSET(), mul(1023, 32)) +} + function BYTECODE_LEN_OFFSET() -> offset { - offset := add(STACK_OFFSET(), mul(1024, 32)) + offset := add(MAX_STACK_SLOT_OFFSET(), 32) } function BYTECODE_OFFSET() -> offset { @@ -445,7 +449,7 @@ function popStackItem(sp, oldStackHead) -> a, newSp, stackHead { } function pushStackItem(sp, item, oldStackHead) -> newSp, stackHead { - if iszero(lt(sp, BYTECODE_LEN_OFFSET())) { + if iszero(lt(sp, MAX_STACK_SLOT_OFFSET())) { panic() } @@ -472,12 +476,6 @@ function popStackCheck(sp, numInputs) { } } -function pushStackCheck(sp, numInputs) { - if iszero(lt(add(sp, mul(0x20, sub(numInputs, 1))), BYTECODE_LEN_OFFSET())) { - panic() - } -} - function accessStackHead(sp, stackHead) -> value { if lt(sp, STACK_OFFSET()) { panic() From ba50e1931cdafb29c0f9d817eb9445399cd5d12a Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 27 Nov 2024 17:33:14 +0100 Subject: [PATCH 06/62] fix(EVM): Add stack overflow check in dup (#1087) --- system-contracts/contracts/EvmEmulator.yul | 10 ++++++++++ .../evm-emulator/EvmEmulatorFunctions.template.yul | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index cf7cfd1a9..86169cfbb 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -476,6 +476,11 @@ object "EvmEmulator" { function dupStackItem(sp, evmGas, position, oldStackHead) -> newSp, evmGasLeft, stackHead { evmGasLeft := chargeGas(evmGas, 3) + + if iszero(lt(sp, MAX_STACK_SLOT_OFFSET())) { + panic() + } + let tempSp := sub(sp, mul(0x20, sub(position, 1))) if lt(tempSp, STACK_OFFSET()) { @@ -3497,6 +3502,11 @@ object "EvmEmulator" { function dupStackItem(sp, evmGas, position, oldStackHead) -> newSp, evmGasLeft, stackHead { evmGasLeft := chargeGas(evmGas, 3) + + if iszero(lt(sp, MAX_STACK_SLOT_OFFSET())) { + panic() + } + let tempSp := sub(sp, mul(0x20, sub(position, 1))) if lt(tempSp, STACK_OFFSET()) { diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index e58879762..bf71a04e7 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -414,6 +414,11 @@ function performSystemCallRevertable(to, dataLength) -> success { function dupStackItem(sp, evmGas, position, oldStackHead) -> newSp, evmGasLeft, stackHead { evmGasLeft := chargeGas(evmGas, 3) + + if iszero(lt(sp, MAX_STACK_SLOT_OFFSET())) { + panic() + } + let tempSp := sub(sp, mul(0x20, sub(position, 1))) if lt(tempSp, STACK_OFFSET()) { From 30d56adc805e9844e5987eea905b75da2804fa3a Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 27 Nov 2024 17:37:22 +0100 Subject: [PATCH 07/62] fix(EVM): Fix SSTORE warmth check (#1089) --- system-contracts/contracts/EvmEmulator.yul | 2 ++ system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul | 1 + 2 files changed, 3 insertions(+) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 86169cfbb..6de4661d6 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -593,6 +593,7 @@ object "EvmEmulator" { performSystemCall(EVM_GAS_MANAGER_CONTRACT(), 65) + originalValue := currentValue if returndatasize() { isWarm := true returndatacopy(0, 0, 32) @@ -3619,6 +3620,7 @@ object "EvmEmulator" { performSystemCall(EVM_GAS_MANAGER_CONTRACT(), 65) + originalValue := currentValue if returndatasize() { isWarm := true returndatacopy(0, 0, 32) diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index bf71a04e7..2fffe58c1 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -531,6 +531,7 @@ function warmSlot(key, currentValue) -> isWarm, originalValue { performSystemCall(EVM_GAS_MANAGER_CONTRACT(), 65) + originalValue := currentValue if returndatasize() { isWarm := true returndatacopy(0, 0, 32) From 6fe12575d598cc8e0f0b701a9319890759c6ef9d Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 27 Nov 2024 17:46:19 +0100 Subject: [PATCH 08/62] fix(EVM): Fix CALL in static context (#1091) --- system-contracts/contracts/EvmEmulator.yul | 32 ++++++++----------- .../EvmEmulatorFunctions.template.yul | 8 +++-- .../evm-emulator/EvmEmulatorLoop.template.yul | 8 +---- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 6de4661d6..fea544e05 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -641,7 +641,7 @@ object "EvmEmulator" { // CALLS FUNCTIONALITY //////////////////////////////////////////////////////////////// - function performCall(oldSp, evmGasLeft, oldStackHead) -> newGasLeft, sp, stackHead { + function performCall(oldSp, evmGasLeft, oldStackHead, isStatic) -> newGasLeft, sp, stackHead { let gasToPass, addr, value, argsOffset, argsSize, retOffset, retSize popStackCheck(oldSp, 7) @@ -673,6 +673,10 @@ object "EvmEmulator" { gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) if gt(value, 0) { + if isStatic { + panic() + } + gasUsed := add(gasUsed, 9000) // positive_value_cost if isAddrEmpty(addr) { @@ -696,7 +700,7 @@ object "EvmEmulator" { argsSize, add(retOffset, MEM_OFFSET()), retSize, - false + isStatic ) newGasLeft := add(evmGasLeft, frameGasLeft) @@ -2627,13 +2631,7 @@ object "EvmEmulator" { } case 0xF1 { // OP_CALL // A function was implemented in order to avoid stack depth errors. - switch isStatic - case 0 { - evmGasLeft, sp, stackHead := performCall(sp, evmGasLeft, stackHead) - } - default { - evmGasLeft, sp, stackHead := performStaticCall(sp, evmGasLeft, stackHead) - } + evmGasLeft, sp, stackHead := performCall(sp, evmGasLeft, stackHead, isStatic) ip := add(ip, 1) } case 0xF3 { // OP_RETURN @@ -3668,7 +3666,7 @@ object "EvmEmulator" { // CALLS FUNCTIONALITY //////////////////////////////////////////////////////////////// - function performCall(oldSp, evmGasLeft, oldStackHead) -> newGasLeft, sp, stackHead { + function performCall(oldSp, evmGasLeft, oldStackHead, isStatic) -> newGasLeft, sp, stackHead { let gasToPass, addr, value, argsOffset, argsSize, retOffset, retSize popStackCheck(oldSp, 7) @@ -3700,6 +3698,10 @@ object "EvmEmulator" { gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) if gt(value, 0) { + if isStatic { + panic() + } + gasUsed := add(gasUsed, 9000) // positive_value_cost if isAddrEmpty(addr) { @@ -3723,7 +3725,7 @@ object "EvmEmulator" { argsSize, add(retOffset, MEM_OFFSET()), retSize, - false + isStatic ) newGasLeft := add(evmGasLeft, frameGasLeft) @@ -5654,13 +5656,7 @@ object "EvmEmulator" { } case 0xF1 { // OP_CALL // A function was implemented in order to avoid stack depth errors. - switch isStatic - case 0 { - evmGasLeft, sp, stackHead := performCall(sp, evmGasLeft, stackHead) - } - default { - evmGasLeft, sp, stackHead := performStaticCall(sp, evmGasLeft, stackHead) - } + evmGasLeft, sp, stackHead := performCall(sp, evmGasLeft, stackHead, isStatic) ip := add(ip, 1) } case 0xF3 { // OP_RETURN diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 2fffe58c1..81b2d84ac 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -579,7 +579,7 @@ function resetEvmFrame() { // CALLS FUNCTIONALITY //////////////////////////////////////////////////////////////// -function performCall(oldSp, evmGasLeft, oldStackHead) -> newGasLeft, sp, stackHead { +function performCall(oldSp, evmGasLeft, oldStackHead, isStatic) -> newGasLeft, sp, stackHead { let gasToPass, addr, value, argsOffset, argsSize, retOffset, retSize popStackCheck(oldSp, 7) @@ -611,6 +611,10 @@ function performCall(oldSp, evmGasLeft, oldStackHead) -> newGasLeft, sp, stackHe gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) if gt(value, 0) { + if isStatic { + panic() + } + gasUsed := add(gasUsed, 9000) // positive_value_cost if isAddrEmpty(addr) { @@ -634,7 +638,7 @@ function performCall(oldSp, evmGasLeft, oldStackHead) -> newGasLeft, sp, stackHe argsSize, add(retOffset, MEM_OFFSET()), retSize, - false + isStatic ) newGasLeft := add(evmGasLeft, frameGasLeft) diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index 20897c518..d0d4bbfaa 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -1425,13 +1425,7 @@ for { } true { } { } case 0xF1 { // OP_CALL // A function was implemented in order to avoid stack depth errors. - switch isStatic - case 0 { - evmGasLeft, sp, stackHead := performCall(sp, evmGasLeft, stackHead) - } - default { - evmGasLeft, sp, stackHead := performStaticCall(sp, evmGasLeft, stackHead) - } + evmGasLeft, sp, stackHead := performCall(sp, evmGasLeft, stackHead, isStatic) ip := add(ip, 1) } case 0xF3 { // OP_RETURN From 18186267dcf3f551951253a150350f4867ac1f2c Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 27 Nov 2024 17:49:17 +0100 Subject: [PATCH 09/62] fix(EVM): Fix calls to contract being created (#1093) --- system-contracts/contracts/EvmEmulator.yul | 18 ++++++++++++++++-- .../EvmEmulatorFunctions.template.yul | 9 ++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index fea544e05..3d754f38f 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -390,6 +390,13 @@ object "EvmEmulator" { isEVM := fetchFromSystemContract(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 36) } + function isConstructedEvmContract(addr) -> isConstructedEVM { + let rawCodeHash := getRawCodeHash(addr) + let version := shr(248, rawCodeHash) + let isConstructedFlag := xor(shr(240, rawCodeHash), 1) + isConstructedEVM := and(eq(version, 2), isConstructedFlag) + } + // Basically performs an extcodecopy, while returning the length of the copied bytecode. function fetchDeployedCode(addr, dstOffset, srcOffset, len) -> copiedLen { let codeHash := getRawCodeHash(addr) @@ -803,7 +810,7 @@ object "EvmEmulator" { } function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { - switch isEvmContract(addr) + switch isConstructedEvmContract(addr) case 0 { // zkEVM native call let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) @@ -3415,6 +3422,13 @@ object "EvmEmulator" { isEVM := fetchFromSystemContract(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 36) } + function isConstructedEvmContract(addr) -> isConstructedEVM { + let rawCodeHash := getRawCodeHash(addr) + let version := shr(248, rawCodeHash) + let isConstructedFlag := xor(shr(240, rawCodeHash), 1) + isConstructedEVM := and(eq(version, 2), isConstructedFlag) + } + // Basically performs an extcodecopy, while returning the length of the copied bytecode. function fetchDeployedCode(addr, dstOffset, srcOffset, len) -> copiedLen { let codeHash := getRawCodeHash(addr) @@ -3828,7 +3842,7 @@ object "EvmEmulator" { } function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { - switch isEvmContract(addr) + switch isConstructedEvmContract(addr) case 0 { // zkEVM native call let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 81b2d84ac..d8131e1a8 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -328,6 +328,13 @@ function isEvmContract(addr) -> isEVM { isEVM := fetchFromSystemContract(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 36) } +function isConstructedEvmContract(addr) -> isConstructedEVM { + let rawCodeHash := getRawCodeHash(addr) + let version := shr(248, rawCodeHash) + let isConstructedFlag := xor(shr(240, rawCodeHash), 1) + isConstructedEVM := and(eq(version, 2), isConstructedFlag) +} + // Basically performs an extcodecopy, while returning the length of the copied bytecode. function fetchDeployedCode(addr, dstOffset, srcOffset, len) -> copiedLen { let codeHash := getRawCodeHash(addr) @@ -741,7 +748,7 @@ function performDelegateCall(oldSp, evmGasLeft, isStatic, oldStackHead) -> newGa } function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { - switch isEvmContract(addr) + switch isConstructedEvmContract(addr) case 0 { // zkEVM native call let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) From 12efa1c00fbfbd4de7b919438010f5af75750902 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 28 Nov 2024 13:49:35 +0100 Subject: [PATCH 10/62] fix(EVM): Wrap access to calldata to prevent out-of-bounds EraVM panic (#1097) --- system-contracts/contracts/EvmEmulator.yul | 86 +++++++++++++------ .../evm-emulator/EvmEmulatorLoop.template.yul | 43 +++++++--- 2 files changed, 93 insertions(+), 36 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 3d754f38f..ad827c5bb 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -1552,7 +1552,13 @@ object "EvmEmulator" { case 0x35 { // OP_CALLDATALOAD evmGasLeft := chargeGas(evmGasLeft, 3) - stackHead := calldataload(accessStackHead(sp, stackHead)) + let calldataOffset := accessStackHead(sp, stackHead) + + stackHead := 0 + // EraVM will revert if offset + length overflows uint32 + if lt(calldataOffset, UINT32_MAX()) { + stackHead := calldataload(calldataOffset) + } ip := add(ip, 1) } @@ -1565,25 +1571,38 @@ object "EvmEmulator" { case 0x37 { // OP_CALLDATACOPY evmGasLeft := chargeGas(evmGasLeft, 3) - let destOffset, offset, size + let dstOffset, sourceOffset, len popStackCheck(sp, 3) - destOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - offset, sp, stackHead:= popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(destOffset, size) + dstOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + sourceOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - if gt(offset, MAX_UINT64()) { - offset := MAX_UINT64() - } + checkMemIsAccessible(dstOffset, len) // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let dynamicGas := add(mul(3, shr(5, add(size, 31))), expandMemory(destOffset, size)) + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(dstOffset, len)) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - calldatacopy(add(destOffset, MEM_OFFSET()), offset, size) + dstOffset := add(dstOffset, MEM_OFFSET()) + + // EraVM will revert if offset + length overflows uint32 + if gt(sourceOffset, UINT32_MAX()) { + sourceOffset := UINT32_MAX() + } + + // Check bytecode out-of-bounds access + let truncatedLen := len + if gt(add(sourceOffset, len), UINT32_MAX()) { + truncatedLen := sub(UINT32_MAX(), sourceOffset) // truncate + $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, truncatedLen), sub(len, truncatedLen)) // pad with zeroes any out-of-bounds + } + + if truncatedLen { + calldatacopy(dstOffset, sourceOffset, truncatedLen) + } + ip := add(ip, 1) } @@ -4584,7 +4603,13 @@ object "EvmEmulator" { case 0x35 { // OP_CALLDATALOAD evmGasLeft := chargeGas(evmGasLeft, 3) - stackHead := calldataload(accessStackHead(sp, stackHead)) + let calldataOffset := accessStackHead(sp, stackHead) + + stackHead := 0 + // EraVM will revert if offset + length overflows uint32 + if lt(calldataOffset, UINT32_MAX()) { + stackHead := calldataload(calldataOffset) + } ip := add(ip, 1) } @@ -4597,25 +4622,38 @@ object "EvmEmulator" { case 0x37 { // OP_CALLDATACOPY evmGasLeft := chargeGas(evmGasLeft, 3) - let destOffset, offset, size + let dstOffset, sourceOffset, len popStackCheck(sp, 3) - destOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - offset, sp, stackHead:= popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(destOffset, size) + dstOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + sourceOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - if gt(offset, MAX_UINT64()) { - offset := MAX_UINT64() - } + checkMemIsAccessible(dstOffset, len) // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let dynamicGas := add(mul(3, shr(5, add(size, 31))), expandMemory(destOffset, size)) + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(dstOffset, len)) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - calldatacopy(add(destOffset, MEM_OFFSET()), offset, size) + dstOffset := add(dstOffset, MEM_OFFSET()) + + // EraVM will revert if offset + length overflows uint32 + if gt(sourceOffset, UINT32_MAX()) { + sourceOffset := UINT32_MAX() + } + + // Check bytecode out-of-bounds access + let truncatedLen := len + if gt(add(sourceOffset, len), UINT32_MAX()) { + truncatedLen := sub(UINT32_MAX(), sourceOffset) // truncate + $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, truncatedLen), sub(len, truncatedLen)) // pad with zeroes any out-of-bounds + } + + if truncatedLen { + calldatacopy(dstOffset, sourceOffset, truncatedLen) + } + ip := add(ip, 1) } diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index d0d4bbfaa..0c3196a9e 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -339,7 +339,13 @@ for { } true { } { case 0x35 { // OP_CALLDATALOAD evmGasLeft := chargeGas(evmGasLeft, 3) - stackHead := calldataload(accessStackHead(sp, stackHead)) + let calldataOffset := accessStackHead(sp, stackHead) + + stackHead := 0 + // EraVM will revert if offset + length overflows uint32 + if lt(calldataOffset, UINT32_MAX()) { + stackHead := calldataload(calldataOffset) + } ip := add(ip, 1) } @@ -352,25 +358,38 @@ for { } true { } { case 0x37 { // OP_CALLDATACOPY evmGasLeft := chargeGas(evmGasLeft, 3) - let destOffset, offset, size + let dstOffset, sourceOffset, len popStackCheck(sp, 3) - destOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - offset, sp, stackHead:= popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(destOffset, size) + dstOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + sourceOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - if gt(offset, MAX_UINT64()) { - offset := MAX_UINT64() - } + checkMemIsAccessible(dstOffset, len) // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let dynamicGas := add(mul(3, shr(5, add(size, 31))), expandMemory(destOffset, size)) + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(dstOffset, len)) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - calldatacopy(add(destOffset, MEM_OFFSET()), offset, size) + dstOffset := add(dstOffset, MEM_OFFSET()) + + // EraVM will revert if offset + length overflows uint32 + if gt(sourceOffset, UINT32_MAX()) { + sourceOffset := UINT32_MAX() + } + + // Check bytecode out-of-bounds access + let truncatedLen := len + if gt(add(sourceOffset, len), UINT32_MAX()) { + truncatedLen := sub(UINT32_MAX(), sourceOffset) // truncate + $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, truncatedLen), sub(len, truncatedLen)) // pad with zeroes any out-of-bounds + } + + if truncatedLen { + calldatacopy(dstOffset, sourceOffset, truncatedLen) + } + ip := add(ip, 1) } From 07f43d04ae5a76c08a264c201953341269eb45e6 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 28 Nov 2024 19:08:50 +0100 Subject: [PATCH 11/62] fix(EVM): Zero out frame gas left for underpaid precompiles (#1101) --- system-contracts/contracts/EvmEmulator.yul | 2 ++ system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul | 1 + 2 files changed, 3 insertions(+) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index ad827c5bb..e3803ce7f 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -839,6 +839,7 @@ object "EvmEmulator" { let zkVmGasToPass := gas() // pass all remaining gas, precompiles should not call any contracts if lt(gasToPass, precompileCost) { zkVmGasToPass := 0 // in EVM precompile should revert consuming all gas in that case + precompileCost := gasToPass // just in case } switch isStatic @@ -3890,6 +3891,7 @@ object "EvmEmulator" { let zkVmGasToPass := gas() // pass all remaining gas, precompiles should not call any contracts if lt(gasToPass, precompileCost) { zkVmGasToPass := 0 // in EVM precompile should revert consuming all gas in that case + precompileCost := gasToPass // just in case } switch isStatic diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index d8131e1a8..b8b4a8448 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -777,6 +777,7 @@ function callPrecompile(addr, precompileCost, gasToPass, value, argsOffset, args let zkVmGasToPass := gas() // pass all remaining gas, precompiles should not call any contracts if lt(gasToPass, precompileCost) { zkVmGasToPass := 0 // in EVM precompile should revert consuming all gas in that case + precompileCost := gasToPass // just in case } switch isStatic From c74c6ecc2db289fb33f7c88f0fa353b92cce94d9 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 2 Dec 2024 15:22:31 +0100 Subject: [PATCH 12/62] fix(EVM): Fix panics on insufficient balance in calls (#1108) --- system-contracts/contracts/EvmEmulator.yul | 64 ++++++++++++------- .../EvmEmulatorFunctions.template.yul | 32 ++++++---- 2 files changed, 60 insertions(+), 36 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index e3803ce7f..0f0d6b15d 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -293,6 +293,12 @@ object "EvmEmulator" { } } + function insufficientBalance(value) -> res { + if value { + res := gt(value, selfbalance()) + } + } + // It is the responsibility of the caller to ensure that ip is correct function readIP(ip, bytecodeEndOffset) -> opcode { if lt(ip, bytecodeEndOffset) { @@ -825,12 +831,19 @@ object "EvmEmulator" { } } default { - pushEvmFrame(gasToPass, isStatic) - // pass all remaining native gas - success := call(gas(), addr, value, argsOffset, argsSize, 0, 0) - frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) - if iszero(success) { - resetEvmFrame() + switch insufficientBalance(value) + case 0 { + pushEvmFrame(gasToPass, isStatic) + // pass all remaining native gas + success := call(gas(), addr, value, argsOffset, argsSize, 0, 0) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + if iszero(success) { + resetEvmFrame() + } + } + default { + frameGasLeft := gasToPass + _eraseReturndataPointer() } } } @@ -1042,12 +1055,7 @@ object "EvmEmulator" { _eraseReturndataPointer() - let err := 0 - if value { - if gt(value, selfbalance()) { - err := 1 - } - } + let err := insufficientBalance(value) if iszero(err) { offset := add(MEM_OFFSET(), offset) // caller must ensure that it doesn't overflow @@ -3345,6 +3353,12 @@ object "EvmEmulator" { } } + function insufficientBalance(value) -> res { + if value { + res := gt(value, selfbalance()) + } + } + // It is the responsibility of the caller to ensure that ip is correct function readIP(ip, bytecodeEndOffset) -> opcode { if lt(ip, bytecodeEndOffset) { @@ -3877,12 +3891,19 @@ object "EvmEmulator" { } } default { - pushEvmFrame(gasToPass, isStatic) - // pass all remaining native gas - success := call(gas(), addr, value, argsOffset, argsSize, 0, 0) - frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) - if iszero(success) { - resetEvmFrame() + switch insufficientBalance(value) + case 0 { + pushEvmFrame(gasToPass, isStatic) + // pass all remaining native gas + success := call(gas(), addr, value, argsOffset, argsSize, 0, 0) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + if iszero(success) { + resetEvmFrame() + } + } + default { + frameGasLeft := gasToPass + _eraseReturndataPointer() } } } @@ -4094,12 +4115,7 @@ object "EvmEmulator" { _eraseReturndataPointer() - let err := 0 - if value { - if gt(value, selfbalance()) { - err := 1 - } - } + let err := insufficientBalance(value) if iszero(err) { offset := add(MEM_OFFSET(), offset) // caller must ensure that it doesn't overflow diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index b8b4a8448..bd62c2600 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -231,6 +231,12 @@ function checkOverflow(data1, data2) { } } +function insufficientBalance(value) -> res { + if value { + res := gt(value, selfbalance()) + } +} + // It is the responsibility of the caller to ensure that ip is correct function readIP(ip, bytecodeEndOffset) -> opcode { if lt(ip, bytecodeEndOffset) { @@ -763,12 +769,19 @@ function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, r } } default { - pushEvmFrame(gasToPass, isStatic) - // pass all remaining native gas - success := call(gas(), addr, value, argsOffset, argsSize, 0, 0) - frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) - if iszero(success) { - resetEvmFrame() + switch insufficientBalance(value) + case 0 { + pushEvmFrame(gasToPass, isStatic) + // pass all remaining native gas + success := call(gas(), addr, value, argsOffset, argsSize, 0, 0) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + if iszero(success) { + resetEvmFrame() + } + } + default { + frameGasLeft := gasToPass + _eraseReturndataPointer() } } } @@ -980,12 +993,7 @@ function $llvm_NoInline_llvm$_genericCreate(offset, size, value, evmGasLeftOld, _eraseReturndataPointer() - let err := 0 - if value { - if gt(value, selfbalance()) { - err := 1 - } - } + let err := insufficientBalance(value) if iszero(err) { offset := add(MEM_OFFSET(), offset) // caller must ensure that it doesn't overflow From 5869331d0c742a771316af212faa1e5a27cf8c03 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 2 Dec 2024 16:55:09 +0100 Subject: [PATCH 13/62] fix(EVM): Fix DUP1 underflow check (#1110) --- system-contracts/contracts/EvmEmulator.yul | 4 ++-- system-contracts/evm-emulator/EvmEmulatorLoop.template.yul | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 0f0d6b15d..a966924bb 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -2396,7 +2396,7 @@ object "EvmEmulator" { } case 0x80 { // OP_DUP1 evmGasLeft := chargeGas(evmGasLeft, 3) - sp, stackHead := pushStackItem(sp, stackHead, stackHead) + sp, stackHead := pushStackItem(sp, accessStackHead(sp, stackHead), stackHead) ip := add(ip, 1) } case 0x81 { // OP_DUP2 @@ -5456,7 +5456,7 @@ object "EvmEmulator" { } case 0x80 { // OP_DUP1 evmGasLeft := chargeGas(evmGasLeft, 3) - sp, stackHead := pushStackItem(sp, stackHead, stackHead) + sp, stackHead := pushStackItem(sp, accessStackHead(sp, stackHead), stackHead) ip := add(ip, 1) } case 0x81 { // OP_DUP2 diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index 0c3196a9e..c1ba325c1 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -1174,7 +1174,7 @@ for { } true { } { } case 0x80 { // OP_DUP1 evmGasLeft := chargeGas(evmGasLeft, 3) - sp, stackHead := pushStackItem(sp, stackHead, stackHead) + sp, stackHead := pushStackItem(sp, accessStackHead(sp, stackHead), stackHead) ip := add(ip, 1) } case 0x81 { // OP_DUP2 From 55367f570cb699d36fe140527b208443479063dc Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Tue, 3 Dec 2024 10:49:37 +0100 Subject: [PATCH 14/62] fix(EVM): Update LLVM options after fixes (#1112) Signed-off-by: Vladimir Radosavljevic Co-authored-by: Vladimir Radosavljevic --- system-contracts/contracts/EvmEmulator.yul.llvm.options | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-contracts/contracts/EvmEmulator.yul.llvm.options b/system-contracts/contracts/EvmEmulator.yul.llvm.options index ea418b985..704e0a70e 100644 --- a/system-contracts/contracts/EvmEmulator.yul.llvm.options +++ b/system-contracts/contracts/EvmEmulator.yul.llvm.options @@ -1 +1 @@ -'-eravm-jump-table-density-threshold=10 -tail-dup-size=6 -eravm-enable-split-loop-phi-live-ranges -tail-merge-only-bbs-without-succ -join-globalcopies -disable-early-taildup' \ No newline at end of file +'-eravm-jump-table-density-threshold=10 -tail-dup-size=6 -eravm-enable-split-loop-phi-live-ranges -tail-merge-only-bbs-without-succ -tail-dup-fallthrough-bbs' \ No newline at end of file From 70d1de865e264550a62e0862bdb1f57908938a7d Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 4 Dec 2024 13:54:22 +0100 Subject: [PATCH 15/62] fix(EVM): Do not check memory boundaries if size is zero (#1114) --- system-contracts/contracts/EvmEmulator.yul | 84 ++++++++++++------- .../EvmEmulatorFunctions.template.yul | 8 +- .../evm-emulator/EvmEmulatorLoop.template.yul | 34 +++++--- 3 files changed, 81 insertions(+), 45 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index a966924bb..968ab3b38 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -280,10 +280,12 @@ object "EvmEmulator" { } function checkMemIsAccessible(relativeOffset, size) { - checkOverflow(relativeOffset, size) + if size { + checkOverflow(relativeOffset, size) - if gt(add(relativeOffset, size), MAX_POSSIBLE_MEM_LEN()) { - panic() + if gt(add(relativeOffset, size), MAX_POSSIBLE_MEM_LEN()) { + panic() + } } } @@ -2676,14 +2678,17 @@ object "EvmEmulator" { offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) + if size { + checkMemIsAccessible(offset, size) - evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) + + returnLen := size + + // Don't check overflow here since previous checks are enough to ensure this is safe + returnOffset := add(MEM_OFFSET(), offset) + } - returnLen := size - - // Don't check overflow here since previous checks are enough to ensure this is safe - returnOffset := add(MEM_OFFSET(), offset) break } case 0xF4 { // OP_DELEGATECALL @@ -2710,12 +2715,19 @@ object "EvmEmulator" { popStackCheck(sp, 2) offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) - - // Don't check overflow here since previous checks are enough to ensure this is safe - offset := add(offset, MEM_OFFSET()) + + switch iszero(size) + case 0 { + checkMemIsAccessible(offset, size) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) + + // Don't check overflow here since previous checks are enough to ensure this is safe + offset := add(offset, MEM_OFFSET()) + } + default { + offset := MEM_OFFSET() + } + if eq(isCallerEVM, 1) { offset := sub(offset, 32) @@ -3340,10 +3352,12 @@ object "EvmEmulator" { } function checkMemIsAccessible(relativeOffset, size) { - checkOverflow(relativeOffset, size) + if size { + checkOverflow(relativeOffset, size) - if gt(add(relativeOffset, size), MAX_POSSIBLE_MEM_LEN()) { - panic() + if gt(add(relativeOffset, size), MAX_POSSIBLE_MEM_LEN()) { + panic() + } } } @@ -5736,14 +5750,17 @@ object "EvmEmulator" { offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) + if size { + checkMemIsAccessible(offset, size) - evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) + + returnLen := size + + // Don't check overflow here since previous checks are enough to ensure this is safe + returnOffset := add(MEM_OFFSET(), offset) + } - returnLen := size - - // Don't check overflow here since previous checks are enough to ensure this is safe - returnOffset := add(MEM_OFFSET(), offset) break } case 0xF4 { // OP_DELEGATECALL @@ -5770,12 +5787,19 @@ object "EvmEmulator" { popStackCheck(sp, 2) offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) - - // Don't check overflow here since previous checks are enough to ensure this is safe - offset := add(offset, MEM_OFFSET()) + + switch iszero(size) + case 0 { + checkMemIsAccessible(offset, size) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) + + // Don't check overflow here since previous checks are enough to ensure this is safe + offset := add(offset, MEM_OFFSET()) + } + default { + offset := MEM_OFFSET() + } + if eq(isCallerEVM, 1) { offset := sub(offset, 32) diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index bd62c2600..345e13b82 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -218,10 +218,12 @@ function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> maxExpand { } function checkMemIsAccessible(relativeOffset, size) { - checkOverflow(relativeOffset, size) + if size { + checkOverflow(relativeOffset, size) - if gt(add(relativeOffset, size), MAX_POSSIBLE_MEM_LEN()) { - panic() + if gt(add(relativeOffset, size), MAX_POSSIBLE_MEM_LEN()) { + panic() + } } } diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index c1ba325c1..4b817b149 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -1454,14 +1454,17 @@ for { } true { } { offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) + if size { + checkMemIsAccessible(offset, size) - evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) + + returnLen := size + + // Don't check overflow here since previous checks are enough to ensure this is safe + returnOffset := add(MEM_OFFSET(), offset) + } - returnLen := size - - // Don't check overflow here since previous checks are enough to ensure this is safe - returnOffset := add(MEM_OFFSET(), offset) break } case 0xF4 { // OP_DELEGATECALL @@ -1488,12 +1491,19 @@ for { } true { } { popStackCheck(sp, 2) offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) - - // Don't check overflow here since previous checks are enough to ensure this is safe - offset := add(offset, MEM_OFFSET()) + + switch iszero(size) + case 0 { + checkMemIsAccessible(offset, size) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) + + // Don't check overflow here since previous checks are enough to ensure this is safe + offset := add(offset, MEM_OFFSET()) + } + default { + offset := MEM_OFFSET() + } + if eq(isCallerEVM, 1) { offset := sub(offset, 32) From 37874c76783aaab54dc28136c7090b09fc98a878 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 4 Dec 2024 14:46:51 +0100 Subject: [PATCH 16/62] fi(EVM): Fix EXTCODECOPY zeroing offset (#1115) --- system-contracts/contracts/EvmEmulator.yul | 8 ++++++-- .../evm-emulator/EvmEmulatorLoop.template.yul | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 968ab3b38..8f48f4eaf 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -1717,6 +1717,8 @@ object "EvmEmulator" { evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + dstOffset := add(dstOffset, MEM_OFFSET()) + if gt(srcOffset, MAX_UINT64()) { srcOffset := MAX_UINT64() } @@ -1725,7 +1727,7 @@ object "EvmEmulator" { let copiedLen if getRawCodeHash(addr) { // Gets the code from the addr - copiedLen := fetchDeployedCode(addr, add(dstOffset, MEM_OFFSET()), srcOffset, len) + copiedLen := fetchDeployedCode(addr, dstOffset, srcOffset, len) } if lt(copiedLen, len) { @@ -4789,6 +4791,8 @@ object "EvmEmulator" { evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + dstOffset := add(dstOffset, MEM_OFFSET()) + if gt(srcOffset, MAX_UINT64()) { srcOffset := MAX_UINT64() } @@ -4797,7 +4801,7 @@ object "EvmEmulator" { let copiedLen if getRawCodeHash(addr) { // Gets the code from the addr - copiedLen := fetchDeployedCode(addr, add(dstOffset, MEM_OFFSET()), srcOffset, len) + copiedLen := fetchDeployedCode(addr, dstOffset, srcOffset, len) } if lt(copiedLen, len) { diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index 4b817b149..fc8d28827 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -493,6 +493,8 @@ for { } true { } { evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + dstOffset := add(dstOffset, MEM_OFFSET()) + if gt(srcOffset, MAX_UINT64()) { srcOffset := MAX_UINT64() } @@ -501,7 +503,7 @@ for { } true { } { let copiedLen if getRawCodeHash(addr) { // Gets the code from the addr - copiedLen := fetchDeployedCode(addr, add(dstOffset, MEM_OFFSET()), srcOffset, len) + copiedLen := fetchDeployedCode(addr, dstOffset, srcOffset, len) } if lt(copiedLen, len) { From 6a76d761f07174fd5f3c62a34a6c72e34ac09480 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 4 Dec 2024 18:26:19 +0100 Subject: [PATCH 17/62] fix(EVM): Cleanup address bytes in EXTCODECOPY (#1116) --- system-contracts/contracts/EvmEmulator.yul | 4 ++++ system-contracts/evm-emulator/EvmEmulatorLoop.template.yul | 2 ++ 2 files changed, 6 insertions(+) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 8f48f4eaf..9e76b5358 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -1701,6 +1701,8 @@ object "EvmEmulator" { dstOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) srcOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) checkMemIsAccessible(dstOffset, len) @@ -4775,6 +4777,8 @@ object "EvmEmulator" { dstOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) srcOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) checkMemIsAccessible(dstOffset, len) diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index fc8d28827..20cdc0a2f 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -477,6 +477,8 @@ for { } true { } { dstOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) srcOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + + addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) checkMemIsAccessible(dstOffset, len) From 2bdc565526efb347384e95f1ba2a477b06f88d58 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 5 Dec 2024 17:35:42 +0100 Subject: [PATCH 18/62] fix(EVM): Use raw calls to precompiles (#1118) --- system-contracts/contracts/EvmEmulator.yul | 126 ++++++++++++------ .../EvmEmulatorFunctions.template.yul | 63 ++++++--- 2 files changed, 132 insertions(+), 57 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 9e76b5358..2fb6ad8cc 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -463,6 +463,16 @@ object "EvmEmulator" { } } + function build_farcall_abi(isSystemCall, gas, dataStart, dataLength) -> farCallAbi { + farCallAbi := shl(248, isSystemCall) + // dataOffset is 0 + farCallAbi := or(farCallAbi, shl(64, dataStart)) + farCallAbi := or(farCallAbi, shl(96, dataLength)) + farCallAbi := or(farCallAbi, shl(192, gas)) + // shardId is 0 + // forwardingMode is 0 + } + function performSystemCall(to, dataLength) { let success := performSystemCallRevertable(to, dataLength) @@ -473,18 +483,39 @@ object "EvmEmulator" { } function performSystemCallRevertable(to, dataLength) -> success { - let farCallAbi := shl(248, 1) // system call - // dataOffset is 0 - // dataStart is 0 - farCallAbi := or(farCallAbi, shl(96, dataLength)) - farCallAbi := or(farCallAbi, shl(192, gas())) - // shardId is 0 - // forwardingMode is 0 - // not constructor call - + // system call, dataStart is 0 + let farCallAbi := build_farcall_abi(1, gas(), 0, dataLength) success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) } + function rawCall(gas, to, value, dataStart, dataLength, outputOffset, outputLen) -> success { + switch iszero(value) + case 0 { + // system call to MsgValueSimulator, but call to "to" will be non-system + let farCallAbi := build_farcall_abi(1, gas, dataStart, dataLength) + success := verbatim_6i_1o("system_call", MSG_VALUE_SYSTEM_CONTRACT(), farCallAbi, value, to, 0, 0) + if outputLen { + if success { + let rtdz := returndatasize() + switch lt(rtdz, outputLen) + case 0 { returndatacopy(outputOffset, 0, outputLen) } + default { returndatacopy(outputOffset, 0, rtdz) } + } + } + } + default { + // not a system call + let farCallAbi := build_farcall_abi(0, gas, dataStart, dataLength) + success := verbatim_4i_1o("raw_call", to, farCallAbi, outputOffset, outputLen) + } + } + + function rawStaticcall(gas, to, dataStart, dataLength, outputOffset, outputLen) -> success { + // not a system call + let farCallAbi := build_farcall_abi(0, gas, dataStart, dataLength) + success := verbatim_4i_1o("raw_static_call", to, farCallAbi, outputOffset, outputLen) + } + //////////////////////////////////////////////////////////////// // STACK OPERATIONS //////////////////////////////////////////////////////////////// @@ -859,10 +890,10 @@ object "EvmEmulator" { switch isStatic case 0 { - success := call(zkVmGasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) + success := rawCall(zkVmGasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) } default { - success := staticcall(zkVmGasToPass, addr, argsOffset, argsSize, retOffset, retSize) + success := rawStaticcall(zkVmGasToPass, addr, argsOffset, argsSize, retOffset, retSize) } _saveReturndataAfterZkEVMCall() @@ -1136,14 +1167,8 @@ object "EvmEmulator" { } function performSystemCallForCreate(value, bytecodeStart, bytecodeLen) -> success { - let farCallAbi := shl(248, 1) // system call - // dataOffset is 0 - farCallAbi := or(farCallAbi, shl(64, bytecodeStart)) - farCallAbi := or(farCallAbi, shl(96, bytecodeLen)) - farCallAbi := or(farCallAbi, shl(192, gas())) - // shardId is 0 - // forwardingMode is 0 - // not constructor call (ContractDeployer will call constructor) + // system call, not constructor call (ContractDeployer will call constructor) + let farCallAbi := build_farcall_abi(1, gas(), bytecodeStart, bytecodeLen) switch iszero(value) case 0 { @@ -3539,6 +3564,16 @@ object "EvmEmulator" { } } + function build_farcall_abi(isSystemCall, gas, dataStart, dataLength) -> farCallAbi { + farCallAbi := shl(248, isSystemCall) + // dataOffset is 0 + farCallAbi := or(farCallAbi, shl(64, dataStart)) + farCallAbi := or(farCallAbi, shl(96, dataLength)) + farCallAbi := or(farCallAbi, shl(192, gas)) + // shardId is 0 + // forwardingMode is 0 + } + function performSystemCall(to, dataLength) { let success := performSystemCallRevertable(to, dataLength) @@ -3549,18 +3584,39 @@ object "EvmEmulator" { } function performSystemCallRevertable(to, dataLength) -> success { - let farCallAbi := shl(248, 1) // system call - // dataOffset is 0 - // dataStart is 0 - farCallAbi := or(farCallAbi, shl(96, dataLength)) - farCallAbi := or(farCallAbi, shl(192, gas())) - // shardId is 0 - // forwardingMode is 0 - // not constructor call - + // system call, dataStart is 0 + let farCallAbi := build_farcall_abi(1, gas(), 0, dataLength) success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) } + function rawCall(gas, to, value, dataStart, dataLength, outputOffset, outputLen) -> success { + switch iszero(value) + case 0 { + // system call to MsgValueSimulator, but call to "to" will be non-system + let farCallAbi := build_farcall_abi(1, gas, dataStart, dataLength) + success := verbatim_6i_1o("system_call", MSG_VALUE_SYSTEM_CONTRACT(), farCallAbi, value, to, 0, 0) + if outputLen { + if success { + let rtdz := returndatasize() + switch lt(rtdz, outputLen) + case 0 { returndatacopy(outputOffset, 0, outputLen) } + default { returndatacopy(outputOffset, 0, rtdz) } + } + } + } + default { + // not a system call + let farCallAbi := build_farcall_abi(0, gas, dataStart, dataLength) + success := verbatim_4i_1o("raw_call", to, farCallAbi, outputOffset, outputLen) + } + } + + function rawStaticcall(gas, to, dataStart, dataLength, outputOffset, outputLen) -> success { + // not a system call + let farCallAbi := build_farcall_abi(0, gas, dataStart, dataLength) + success := verbatim_4i_1o("raw_static_call", to, farCallAbi, outputOffset, outputLen) + } + //////////////////////////////////////////////////////////////// // STACK OPERATIONS //////////////////////////////////////////////////////////////// @@ -3935,10 +3991,10 @@ object "EvmEmulator" { switch isStatic case 0 { - success := call(zkVmGasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) + success := rawCall(zkVmGasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) } default { - success := staticcall(zkVmGasToPass, addr, argsOffset, argsSize, retOffset, retSize) + success := rawStaticcall(zkVmGasToPass, addr, argsOffset, argsSize, retOffset, retSize) } _saveReturndataAfterZkEVMCall() @@ -4212,14 +4268,8 @@ object "EvmEmulator" { } function performSystemCallForCreate(value, bytecodeStart, bytecodeLen) -> success { - let farCallAbi := shl(248, 1) // system call - // dataOffset is 0 - farCallAbi := or(farCallAbi, shl(64, bytecodeStart)) - farCallAbi := or(farCallAbi, shl(96, bytecodeLen)) - farCallAbi := or(farCallAbi, shl(192, gas())) - // shardId is 0 - // forwardingMode is 0 - // not constructor call (ContractDeployer will call constructor) + // system call, not constructor call (ContractDeployer will call constructor) + let farCallAbi := build_farcall_abi(1, gas(), bytecodeStart, bytecodeLen) switch iszero(value) case 0 { diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 345e13b82..bb0c602db 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -401,6 +401,16 @@ function getMax(a, b) -> max { } } +function build_farcall_abi(isSystemCall, gas, dataStart, dataLength) -> farCallAbi { + farCallAbi := shl(248, isSystemCall) + // dataOffset is 0 + farCallAbi := or(farCallAbi, shl(64, dataStart)) + farCallAbi := or(farCallAbi, shl(96, dataLength)) + farCallAbi := or(farCallAbi, shl(192, gas)) + // shardId is 0 + // forwardingMode is 0 +} + function performSystemCall(to, dataLength) { let success := performSystemCallRevertable(to, dataLength) @@ -411,18 +421,39 @@ function performSystemCall(to, dataLength) { } function performSystemCallRevertable(to, dataLength) -> success { - let farCallAbi := shl(248, 1) // system call - // dataOffset is 0 - // dataStart is 0 - farCallAbi := or(farCallAbi, shl(96, dataLength)) - farCallAbi := or(farCallAbi, shl(192, gas())) - // shardId is 0 - // forwardingMode is 0 - // not constructor call - + // system call, dataStart is 0 + let farCallAbi := build_farcall_abi(1, gas(), 0, dataLength) success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) } +function rawCall(gas, to, value, dataStart, dataLength, outputOffset, outputLen) -> success { + switch iszero(value) + case 0 { + // system call to MsgValueSimulator, but call to "to" will be non-system + let farCallAbi := build_farcall_abi(1, gas, dataStart, dataLength) + success := verbatim_6i_1o("system_call", MSG_VALUE_SYSTEM_CONTRACT(), farCallAbi, value, to, 0, 0) + if outputLen { + if success { + let rtdz := returndatasize() + switch lt(rtdz, outputLen) + case 0 { returndatacopy(outputOffset, 0, outputLen) } + default { returndatacopy(outputOffset, 0, rtdz) } + } + } + } + default { + // not a system call + let farCallAbi := build_farcall_abi(0, gas, dataStart, dataLength) + success := verbatim_4i_1o("raw_call", to, farCallAbi, outputOffset, outputLen) + } +} + +function rawStaticcall(gas, to, dataStart, dataLength, outputOffset, outputLen) -> success { + // not a system call + let farCallAbi := build_farcall_abi(0, gas, dataStart, dataLength) + success := verbatim_4i_1o("raw_static_call", to, farCallAbi, outputOffset, outputLen) +} + //////////////////////////////////////////////////////////////// // STACK OPERATIONS //////////////////////////////////////////////////////////////// @@ -797,10 +828,10 @@ function callPrecompile(addr, precompileCost, gasToPass, value, argsOffset, args switch isStatic case 0 { - success := call(zkVmGasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) + success := rawCall(zkVmGasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) } default { - success := staticcall(zkVmGasToPass, addr, argsOffset, argsSize, retOffset, retSize) + success := rawStaticcall(zkVmGasToPass, addr, argsOffset, argsSize, retOffset, retSize) } _saveReturndataAfterZkEVMCall() @@ -1074,14 +1105,8 @@ function _executeCreate(offset, size, value, evmGasLeftOld, isCreate2, salt) -> } function performSystemCallForCreate(value, bytecodeStart, bytecodeLen) -> success { - let farCallAbi := shl(248, 1) // system call - // dataOffset is 0 - farCallAbi := or(farCallAbi, shl(64, bytecodeStart)) - farCallAbi := or(farCallAbi, shl(96, bytecodeLen)) - farCallAbi := or(farCallAbi, shl(192, gas())) - // shardId is 0 - // forwardingMode is 0 - // not constructor call (ContractDeployer will call constructor) + // system call, not constructor call (ContractDeployer will call constructor) + let farCallAbi := build_farcall_abi(1, gas(), bytecodeStart, bytecodeLen) switch iszero(value) case 0 { From 3299faa649f389b6f3b9855b573e2654206f6c10 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 5 Dec 2024 20:09:47 +0100 Subject: [PATCH 19/62] fix(EVM): Make DELEGATECALL behavior closer to EVM (3) (#1120) --- system-contracts/contracts/EvmEmulator.yul | 272 +++++++++--------- .../EvmEmulatorFunctions.template.yul | 136 ++++----- 2 files changed, 207 insertions(+), 201 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 2fb6ad8cc..c62b333da 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -398,8 +398,7 @@ object "EvmEmulator" { isEVM := fetchFromSystemContract(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 36) } - function isConstructedEvmContract(addr) -> isConstructedEVM { - let rawCodeHash := getRawCodeHash(addr) + function isHashOfConstructedEvmContract(rawCodeHash) -> isConstructedEVM { let version := shr(248, rawCodeHash) let isConstructedFlag := xor(shr(240, rawCodeHash), 1) isConstructedEVM := and(eq(version, 2), isConstructedFlag) @@ -688,21 +687,16 @@ object "EvmEmulator" { //////////////////////////////////////////////////////////////// function performCall(oldSp, evmGasLeft, oldStackHead, isStatic) -> newGasLeft, sp, stackHead { - let gasToPass, addr, value, argsOffset, argsSize, retOffset, retSize + let gasToPass, rawAddr, value, argsOffset, argsSize, retOffset, retSize popStackCheck(oldSp, 7) gasToPass, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) - addr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + rawAddr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) value, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsSize, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) retOffset, sp, retSize := popStackItemWithoutCheck(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) - - checkMemIsAccessible(argsOffset, argsSize) - checkMemIsAccessible(retOffset, retSize) - // static_gas = 0 // dynamic_gas = memory_expansion_cost + code_execution_cost + address_access_cost + positive_value_cost + value_to_empty_account_cost // code_execution_cost is the cost of the called code execution (limited by the gas parameter). @@ -710,13 +704,7 @@ object "EvmEmulator" { // If value is not 0, then positive_value_cost is 9000. In this case there is also a call stipend that is given to make sure that a basic fallback function can be called. // If value is not 0 and the address given points to an empty account, then value_to_empty_account_cost is 25000. An account is empty if its balance is 0, its nonce is 0 and it has no code. - let gasUsed := 100 // warm address access cost - if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { - gasUsed := 2600 // cold address access cost - } - - // memory_expansion_cost - gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) + let addr, gasUsed := _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) if gt(value, 0) { if isStatic { @@ -754,27 +742,16 @@ object "EvmEmulator" { } function performStaticCall(oldSp, evmGasLeft, oldStackHead) -> newGasLeft, sp, stackHead { - let gasToPass,addr, argsOffset, argsSize, retOffset, retSize + let gasToPass, rawAddr, argsOffset, argsSize, retOffset, retSize popStackCheck(oldSp, 6) gasToPass, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) - addr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + rawAddr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsSize, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) retOffset, sp, retSize := popStackItemWithoutCheck(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) - - checkMemIsAccessible(argsOffset, argsSize) - checkMemIsAccessible(retOffset, retSize) - - let gasUsed := 100 // warm address access cost - if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { - gasUsed := 2600 // cold address access cost - } - - // memory_expansion_cost - gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) + let addr, gasUsed := _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) evmGasLeft := chargeGas(evmGasLeft, gasUsed) gasToPass := capGasForCall(evmGasLeft, gasToPass) @@ -797,62 +774,88 @@ object "EvmEmulator" { function performDelegateCall(oldSp, evmGasLeft, isStatic, oldStackHead) -> newGasLeft, sp, stackHead { - let addr, gasToPass, argsOffset, argsSize, retOffset, retSize + let gasToPass, rawAddr, rawArgsOffset, argsSize, rawRetOffset, retSize popStackCheck(oldSp, 6) gasToPass, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) - addr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - argsOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + rawAddr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + rawArgsOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsSize, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - retOffset, sp, retSize := popStackItemWithoutCheck(sp, stackHead) + rawRetOffset, sp, retSize := popStackItemWithoutCheck(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + let addr, gasUsed := _genericPrecallLogic(rawAddr, rawArgsOffset, argsSize, rawRetOffset, retSize) - checkMemIsAccessible(argsOffset, argsSize) - checkMemIsAccessible(retOffset, retSize) + newGasLeft := chargeGas(evmGasLeft, gasUsed) + gasToPass := capGasForCall(newGasLeft, gasToPass) - let gasUsed := 100 // warm address access cost - if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { - gasUsed := 2600 // cold address access cost - } + newGasLeft := sub(newGasLeft, gasToPass) - // memory_expansion_cost - gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) + let success + let frameGasLeft := gasToPass - evmGasLeft := chargeGas(evmGasLeft, gasUsed) + let retOffset := add(MEM_OFFSET(), rawRetOffset) + let argsOffset := add(MEM_OFFSET(), rawArgsOffset) - // it is also not possible to delegatecall precompiles - if iszero(isEvmContract(addr)) { - revertWithGas(evmGasLeft) + let rawCodeHash := getRawCodeHash(addr) + switch isHashOfConstructedEvmContract(rawCodeHash) + case 0 { + // Not a constructed EVM contract + let precompileCost := getGasForPrecompiles(addr, argsSize) + switch precompileCost + case 0 { + // Not a precompile + switch eq(1, shr(248, rawCodeHash)) + case 0 { + // Empty contract or EVM contract being constructed + success := delegatecall(gas(), addr, argsOffset, argsSize, retOffset, retSize) + _saveReturndataAfterZkEVMCall() + } + default { + // We forbid delegatecalls to EraVM native contracts + _eraseReturndataPointer() + } + } + default { + // Precompile. Simlate using staticcall, since EraVM behavior differs here + success, frameGasLeft := callPrecompile(addr, precompileCost, gasToPass, 0, argsOffset, argsSize, retOffset, retSize, true) + } } + default { + // Constructed EVM contract + pushEvmFrame(gasToPass, isStatic) + // pass all remaining native gas + success := delegatecall(gas(), addr, argsOffset, argsSize, 0, 0) - gasToPass := capGasForCall(evmGasLeft, gasToPass) - evmGasLeft := sub(evmGasLeft, gasToPass) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + if iszero(success) { + resetEvmFrame() + } + } - pushEvmFrame(gasToPass, isStatic) - let success := delegatecall( - gas(), // pass all remaining native gas - addr, - add(MEM_OFFSET(), argsOffset), - argsSize, - 0, - 0 - ) + newGasLeft := add(newGasLeft, frameGasLeft) + stackHead := success + } - let frameGasLeft := _saveReturndataAfterEVMCall(add(MEM_OFFSET(), retOffset), retSize) - if iszero(success) { - resetEvmFrame() + function _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) -> addr, gasUsed { + addr := and(rawAddr, 0xffffffffffffffffffffffffffffffffffffffff) + + checkMemIsAccessible(argsOffset, argsSize) + checkMemIsAccessible(retOffset, retSize) + + gasUsed := 100 // warm address access cost + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + gasUsed := 2600 // cold address access cost } - newGasLeft := add(evmGasLeft, frameGasLeft) - stackHead := success + // memory_expansion_cost + gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) } function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { - switch isConstructedEvmContract(addr) + switch isHashOfConstructedEvmContract(getRawCodeHash(addr)) case 0 { // zkEVM native call - let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) + let precompileCost := getGasForPrecompiles(addr, argsSize) switch precompileCost case 0 { // just smart contract @@ -945,8 +948,7 @@ object "EvmEmulator" { // The gas cost mentioned here is purely the cost of the contract, // and does not consider the cost of the call itself nor the instructions // to put the parameters in memory. - // Take into account MEM_OFFSET() when passing the argsOffset - function getGasForPrecompiles(addr, argsOffset, argsSize) -> gasToCharge { + function getGasForPrecompiles(addr, argsSize) -> gasToCharge { switch addr case 0x01 { // ecRecover gasToCharge := 3000 @@ -3499,8 +3501,7 @@ object "EvmEmulator" { isEVM := fetchFromSystemContract(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 36) } - function isConstructedEvmContract(addr) -> isConstructedEVM { - let rawCodeHash := getRawCodeHash(addr) + function isHashOfConstructedEvmContract(rawCodeHash) -> isConstructedEVM { let version := shr(248, rawCodeHash) let isConstructedFlag := xor(shr(240, rawCodeHash), 1) isConstructedEVM := and(eq(version, 2), isConstructedFlag) @@ -3789,21 +3790,16 @@ object "EvmEmulator" { //////////////////////////////////////////////////////////////// function performCall(oldSp, evmGasLeft, oldStackHead, isStatic) -> newGasLeft, sp, stackHead { - let gasToPass, addr, value, argsOffset, argsSize, retOffset, retSize + let gasToPass, rawAddr, value, argsOffset, argsSize, retOffset, retSize popStackCheck(oldSp, 7) gasToPass, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) - addr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + rawAddr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) value, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsSize, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) retOffset, sp, retSize := popStackItemWithoutCheck(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) - - checkMemIsAccessible(argsOffset, argsSize) - checkMemIsAccessible(retOffset, retSize) - // static_gas = 0 // dynamic_gas = memory_expansion_cost + code_execution_cost + address_access_cost + positive_value_cost + value_to_empty_account_cost // code_execution_cost is the cost of the called code execution (limited by the gas parameter). @@ -3811,13 +3807,7 @@ object "EvmEmulator" { // If value is not 0, then positive_value_cost is 9000. In this case there is also a call stipend that is given to make sure that a basic fallback function can be called. // If value is not 0 and the address given points to an empty account, then value_to_empty_account_cost is 25000. An account is empty if its balance is 0, its nonce is 0 and it has no code. - let gasUsed := 100 // warm address access cost - if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { - gasUsed := 2600 // cold address access cost - } - - // memory_expansion_cost - gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) + let addr, gasUsed := _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) if gt(value, 0) { if isStatic { @@ -3855,27 +3845,16 @@ object "EvmEmulator" { } function performStaticCall(oldSp, evmGasLeft, oldStackHead) -> newGasLeft, sp, stackHead { - let gasToPass,addr, argsOffset, argsSize, retOffset, retSize + let gasToPass, rawAddr, argsOffset, argsSize, retOffset, retSize popStackCheck(oldSp, 6) gasToPass, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) - addr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + rawAddr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsSize, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) retOffset, sp, retSize := popStackItemWithoutCheck(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) - - checkMemIsAccessible(argsOffset, argsSize) - checkMemIsAccessible(retOffset, retSize) - - let gasUsed := 100 // warm address access cost - if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { - gasUsed := 2600 // cold address access cost - } - - // memory_expansion_cost - gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) + let addr, gasUsed := _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) evmGasLeft := chargeGas(evmGasLeft, gasUsed) gasToPass := capGasForCall(evmGasLeft, gasToPass) @@ -3898,62 +3877,88 @@ object "EvmEmulator" { function performDelegateCall(oldSp, evmGasLeft, isStatic, oldStackHead) -> newGasLeft, sp, stackHead { - let addr, gasToPass, argsOffset, argsSize, retOffset, retSize + let gasToPass, rawAddr, rawArgsOffset, argsSize, rawRetOffset, retSize popStackCheck(oldSp, 6) gasToPass, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) - addr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - argsOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + rawAddr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + rawArgsOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsSize, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - retOffset, sp, retSize := popStackItemWithoutCheck(sp, stackHead) + rawRetOffset, sp, retSize := popStackItemWithoutCheck(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + let addr, gasUsed := _genericPrecallLogic(rawAddr, rawArgsOffset, argsSize, rawRetOffset, retSize) - checkMemIsAccessible(argsOffset, argsSize) - checkMemIsAccessible(retOffset, retSize) + newGasLeft := chargeGas(evmGasLeft, gasUsed) + gasToPass := capGasForCall(newGasLeft, gasToPass) - let gasUsed := 100 // warm address access cost - if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { - gasUsed := 2600 // cold address access cost - } + newGasLeft := sub(newGasLeft, gasToPass) - // memory_expansion_cost - gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) + let success + let frameGasLeft := gasToPass - evmGasLeft := chargeGas(evmGasLeft, gasUsed) + let retOffset := add(MEM_OFFSET(), rawRetOffset) + let argsOffset := add(MEM_OFFSET(), rawArgsOffset) - // it is also not possible to delegatecall precompiles - if iszero(isEvmContract(addr)) { - revertWithGas(evmGasLeft) + let rawCodeHash := getRawCodeHash(addr) + switch isHashOfConstructedEvmContract(rawCodeHash) + case 0 { + // Not a constructed EVM contract + let precompileCost := getGasForPrecompiles(addr, argsSize) + switch precompileCost + case 0 { + // Not a precompile + switch eq(1, shr(248, rawCodeHash)) + case 0 { + // Empty contract or EVM contract being constructed + success := delegatecall(gas(), addr, argsOffset, argsSize, retOffset, retSize) + _saveReturndataAfterZkEVMCall() + } + default { + // We forbid delegatecalls to EraVM native contracts + _eraseReturndataPointer() + } + } + default { + // Precompile. Simlate using staticcall, since EraVM behavior differs here + success, frameGasLeft := callPrecompile(addr, precompileCost, gasToPass, 0, argsOffset, argsSize, retOffset, retSize, true) + } } + default { + // Constructed EVM contract + pushEvmFrame(gasToPass, isStatic) + // pass all remaining native gas + success := delegatecall(gas(), addr, argsOffset, argsSize, 0, 0) - gasToPass := capGasForCall(evmGasLeft, gasToPass) - evmGasLeft := sub(evmGasLeft, gasToPass) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + if iszero(success) { + resetEvmFrame() + } + } - pushEvmFrame(gasToPass, isStatic) - let success := delegatecall( - gas(), // pass all remaining native gas - addr, - add(MEM_OFFSET(), argsOffset), - argsSize, - 0, - 0 - ) + newGasLeft := add(newGasLeft, frameGasLeft) + stackHead := success + } - let frameGasLeft := _saveReturndataAfterEVMCall(add(MEM_OFFSET(), retOffset), retSize) - if iszero(success) { - resetEvmFrame() + function _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) -> addr, gasUsed { + addr := and(rawAddr, 0xffffffffffffffffffffffffffffffffffffffff) + + checkMemIsAccessible(argsOffset, argsSize) + checkMemIsAccessible(retOffset, retSize) + + gasUsed := 100 // warm address access cost + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + gasUsed := 2600 // cold address access cost } - newGasLeft := add(evmGasLeft, frameGasLeft) - stackHead := success + // memory_expansion_cost + gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) } function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { - switch isConstructedEvmContract(addr) + switch isHashOfConstructedEvmContract(getRawCodeHash(addr)) case 0 { // zkEVM native call - let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) + let precompileCost := getGasForPrecompiles(addr, argsSize) switch precompileCost case 0 { // just smart contract @@ -4046,8 +4051,7 @@ object "EvmEmulator" { // The gas cost mentioned here is purely the cost of the contract, // and does not consider the cost of the call itself nor the instructions // to put the parameters in memory. - // Take into account MEM_OFFSET() when passing the argsOffset - function getGasForPrecompiles(addr, argsOffset, argsSize) -> gasToCharge { + function getGasForPrecompiles(addr, argsSize) -> gasToCharge { switch addr case 0x01 { // ecRecover gasToCharge := 3000 diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index bb0c602db..adc931e45 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -336,8 +336,7 @@ function isEvmContract(addr) -> isEVM { isEVM := fetchFromSystemContract(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 36) } -function isConstructedEvmContract(addr) -> isConstructedEVM { - let rawCodeHash := getRawCodeHash(addr) +function isHashOfConstructedEvmContract(rawCodeHash) -> isConstructedEVM { let version := shr(248, rawCodeHash) let isConstructedFlag := xor(shr(240, rawCodeHash), 1) isConstructedEVM := and(eq(version, 2), isConstructedFlag) @@ -626,21 +625,16 @@ function resetEvmFrame() { //////////////////////////////////////////////////////////////// function performCall(oldSp, evmGasLeft, oldStackHead, isStatic) -> newGasLeft, sp, stackHead { - let gasToPass, addr, value, argsOffset, argsSize, retOffset, retSize + let gasToPass, rawAddr, value, argsOffset, argsSize, retOffset, retSize popStackCheck(oldSp, 7) gasToPass, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) - addr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + rawAddr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) value, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsSize, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) retOffset, sp, retSize := popStackItemWithoutCheck(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) - - checkMemIsAccessible(argsOffset, argsSize) - checkMemIsAccessible(retOffset, retSize) - // static_gas = 0 // dynamic_gas = memory_expansion_cost + code_execution_cost + address_access_cost + positive_value_cost + value_to_empty_account_cost // code_execution_cost is the cost of the called code execution (limited by the gas parameter). @@ -648,13 +642,7 @@ function performCall(oldSp, evmGasLeft, oldStackHead, isStatic) -> newGasLeft, s // If value is not 0, then positive_value_cost is 9000. In this case there is also a call stipend that is given to make sure that a basic fallback function can be called. // If value is not 0 and the address given points to an empty account, then value_to_empty_account_cost is 25000. An account is empty if its balance is 0, its nonce is 0 and it has no code. - let gasUsed := 100 // warm address access cost - if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { - gasUsed := 2600 // cold address access cost - } - - // memory_expansion_cost - gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) + let addr, gasUsed := _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) if gt(value, 0) { if isStatic { @@ -692,27 +680,16 @@ function performCall(oldSp, evmGasLeft, oldStackHead, isStatic) -> newGasLeft, s } function performStaticCall(oldSp, evmGasLeft, oldStackHead) -> newGasLeft, sp, stackHead { - let gasToPass,addr, argsOffset, argsSize, retOffset, retSize + let gasToPass, rawAddr, argsOffset, argsSize, retOffset, retSize popStackCheck(oldSp, 6) gasToPass, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) - addr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + rawAddr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsSize, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) retOffset, sp, retSize := popStackItemWithoutCheck(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) - - checkMemIsAccessible(argsOffset, argsSize) - checkMemIsAccessible(retOffset, retSize) - - let gasUsed := 100 // warm address access cost - if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { - gasUsed := 2600 // cold address access cost - } - - // memory_expansion_cost - gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) + let addr, gasUsed := _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) evmGasLeft := chargeGas(evmGasLeft, gasUsed) gasToPass := capGasForCall(evmGasLeft, gasToPass) @@ -735,62 +712,88 @@ function performStaticCall(oldSp, evmGasLeft, oldStackHead) -> newGasLeft, sp, s function performDelegateCall(oldSp, evmGasLeft, isStatic, oldStackHead) -> newGasLeft, sp, stackHead { - let addr, gasToPass, argsOffset, argsSize, retOffset, retSize + let gasToPass, rawAddr, rawArgsOffset, argsSize, rawRetOffset, retSize popStackCheck(oldSp, 6) gasToPass, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) - addr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - argsOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + rawAddr, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + rawArgsOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) argsSize, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - retOffset, sp, retSize := popStackItemWithoutCheck(sp, stackHead) + rawRetOffset, sp, retSize := popStackItemWithoutCheck(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + let addr, gasUsed := _genericPrecallLogic(rawAddr, rawArgsOffset, argsSize, rawRetOffset, retSize) - checkMemIsAccessible(argsOffset, argsSize) - checkMemIsAccessible(retOffset, retSize) + newGasLeft := chargeGas(evmGasLeft, gasUsed) + gasToPass := capGasForCall(newGasLeft, gasToPass) - let gasUsed := 100 // warm address access cost - if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { - gasUsed := 2600 // cold address access cost - } + newGasLeft := sub(newGasLeft, gasToPass) - // memory_expansion_cost - gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) + let success + let frameGasLeft := gasToPass - evmGasLeft := chargeGas(evmGasLeft, gasUsed) + let retOffset := add(MEM_OFFSET(), rawRetOffset) + let argsOffset := add(MEM_OFFSET(), rawArgsOffset) - // it is also not possible to delegatecall precompiles - if iszero(isEvmContract(addr)) { - revertWithGas(evmGasLeft) + let rawCodeHash := getRawCodeHash(addr) + switch isHashOfConstructedEvmContract(rawCodeHash) + case 0 { + // Not a constructed EVM contract + let precompileCost := getGasForPrecompiles(addr, argsSize) + switch precompileCost + case 0 { + // Not a precompile + switch eq(1, shr(248, rawCodeHash)) + case 0 { + // Empty contract or EVM contract being constructed + success := delegatecall(gas(), addr, argsOffset, argsSize, retOffset, retSize) + _saveReturndataAfterZkEVMCall() + } + default { + // We forbid delegatecalls to EraVM native contracts + _eraseReturndataPointer() + } + } + default { + // Precompile. Simlate using staticcall, since EraVM behavior differs here + success, frameGasLeft := callPrecompile(addr, precompileCost, gasToPass, 0, argsOffset, argsSize, retOffset, retSize, true) + } + } + default { + // Constructed EVM contract + pushEvmFrame(gasToPass, isStatic) + // pass all remaining native gas + success := delegatecall(gas(), addr, argsOffset, argsSize, 0, 0) + + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + if iszero(success) { + resetEvmFrame() + } } - gasToPass := capGasForCall(evmGasLeft, gasToPass) - evmGasLeft := sub(evmGasLeft, gasToPass) + newGasLeft := add(newGasLeft, frameGasLeft) + stackHead := success +} - pushEvmFrame(gasToPass, isStatic) - let success := delegatecall( - gas(), // pass all remaining native gas - addr, - add(MEM_OFFSET(), argsOffset), - argsSize, - 0, - 0 - ) +function _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) -> addr, gasUsed { + addr := and(rawAddr, 0xffffffffffffffffffffffffffffffffffffffff) - let frameGasLeft := _saveReturndataAfterEVMCall(add(MEM_OFFSET(), retOffset), retSize) - if iszero(success) { - resetEvmFrame() + checkMemIsAccessible(argsOffset, argsSize) + checkMemIsAccessible(retOffset, retSize) + + gasUsed := 100 // warm address access cost + if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { + gasUsed := 2600 // cold address access cost } - newGasLeft := add(evmGasLeft, frameGasLeft) - stackHead := success + // memory_expansion_cost + gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) } function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { - switch isConstructedEvmContract(addr) + switch isHashOfConstructedEvmContract(getRawCodeHash(addr)) case 0 { // zkEVM native call - let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize) + let precompileCost := getGasForPrecompiles(addr, argsSize) switch precompileCost case 0 { // just smart contract @@ -883,8 +886,7 @@ function capGasForCall(evmGasLeft, oldGasToPass) -> gasToPass { // The gas cost mentioned here is purely the cost of the contract, // and does not consider the cost of the call itself nor the instructions // to put the parameters in memory. -// Take into account MEM_OFFSET() when passing the argsOffset -function getGasForPrecompiles(addr, argsOffset, argsSize) -> gasToCharge { +function getGasForPrecompiles(addr, argsSize) -> gasToCharge { switch addr case 0x01 { // ecRecover gasToCharge := 3000 From 483b099be582127df0b1291ab8b75d192b1db824 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 5 Dec 2024 21:06:13 +0100 Subject: [PATCH 20/62] fix(EVM): Handle corner case of KECCAK with 0 size (#1121) --- system-contracts/contracts/EvmEmulator.yul | 32 +++++++++++++------ .../evm-emulator/EvmEmulatorLoop.template.yul | 16 +++++++--- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index c62b333da..dabf78485 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -1529,21 +1529,27 @@ object "EvmEmulator" { case 0x20 { // OP_KECCAK256 evmGasLeft := chargeGas(evmGasLeft, 30) - let offset, size + let rawOffset, size popStackCheck(sp, 2) - offset, sp, size := popStackItemWithoutCheck(sp, stackHead) + rawOffset, sp, size := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) + checkMemIsAccessible(rawOffset, size) // When an offset is first accessed (either read or write), memory may trigger // an expansion, which costs gas. // dynamicGas = 6 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let dynamicGas := add(mul(6, shr(5, add(size, 31))), expandMemory(offset, size)) + let dynamicGas := add(mul(6, shr(5, add(size, 31))), expandMemory(rawOffset, size)) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - stackHead := keccak256(add(MEM_OFFSET(), offset), size) + let offset + if size { + // use 0 as offset if size is 0 + offset := add(MEM_OFFSET(), rawOffset) + } + + stackHead := keccak256(offset, size) ip := add(ip, 1) } @@ -4632,21 +4638,27 @@ object "EvmEmulator" { case 0x20 { // OP_KECCAK256 evmGasLeft := chargeGas(evmGasLeft, 30) - let offset, size + let rawOffset, size popStackCheck(sp, 2) - offset, sp, size := popStackItemWithoutCheck(sp, stackHead) + rawOffset, sp, size := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) + checkMemIsAccessible(rawOffset, size) // When an offset is first accessed (either read or write), memory may trigger // an expansion, which costs gas. // dynamicGas = 6 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let dynamicGas := add(mul(6, shr(5, add(size, 31))), expandMemory(offset, size)) + let dynamicGas := add(mul(6, shr(5, add(size, 31))), expandMemory(rawOffset, size)) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - stackHead := keccak256(add(MEM_OFFSET(), offset), size) + let offset + if size { + // use 0 as offset if size is 0 + offset := add(MEM_OFFSET(), rawOffset) + } + + stackHead := keccak256(offset, size) ip := add(ip, 1) } diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index 20cdc0a2f..fde5817f5 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -278,21 +278,27 @@ for { } true { } { case 0x20 { // OP_KECCAK256 evmGasLeft := chargeGas(evmGasLeft, 30) - let offset, size + let rawOffset, size popStackCheck(sp, 2) - offset, sp, size := popStackItemWithoutCheck(sp, stackHead) + rawOffset, sp, size := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) + checkMemIsAccessible(rawOffset, size) // When an offset is first accessed (either read or write), memory may trigger // an expansion, which costs gas. // dynamicGas = 6 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let dynamicGas := add(mul(6, shr(5, add(size, 31))), expandMemory(offset, size)) + let dynamicGas := add(mul(6, shr(5, add(size, 31))), expandMemory(rawOffset, size)) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - stackHead := keccak256(add(MEM_OFFSET(), offset), size) + let offset + if size { + // use 0 as offset if size is 0 + offset := add(MEM_OFFSET(), rawOffset) + } + + stackHead := keccak256(offset, size) ip := add(ip, 1) } From d61e38dd6d7be655b7f43f08985158391d6d0d17 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 6 Dec 2024 11:59:23 +0100 Subject: [PATCH 21/62] fix(EVM): Make fetchDeployedCode more robust (2) (#1123) --- system-contracts/contracts/EvmEmulator.yul | 114 +++++++++--------- .../EvmEmulatorFunctions.template.yul | 50 ++++---- .../evm-emulator/EvmEmulatorLoop.template.yul | 7 +- 3 files changed, 90 insertions(+), 81 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index dabf78485..4da1e794f 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -408,29 +408,35 @@ object "EvmEmulator" { function fetchDeployedCode(addr, dstOffset, srcOffset, len) -> copiedLen { let codeHash := getRawCodeHash(addr) mstore(0, codeHash) - // The first word of returndata is the true length of the bytecode - let codeLen := fetchFromSystemContract(CODE_ORACLE_SYSTEM_CONTRACT(), 32) - - if gt(len, codeLen) { - len := codeLen - } - - let shiftedSrcOffset := add(32, srcOffset) // first 32 bits is length - - let _returndatasize := returndatasize() - if gt(shiftedSrcOffset, _returndatasize) { - shiftedSrcOffset := _returndatasize - } - - if gt(add(len, shiftedSrcOffset), _returndatasize) { - len := sub(_returndatasize, shiftedSrcOffset) - } - - if len { - returndatacopy(dstOffset, shiftedSrcOffset, len) - } + + let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) + // it fails if we don't have any code deployed at this address + if success { + returndatacopy(0, 0, 32) + // The first word of returndata is the true length of the bytecode + let codeLen := mload(0) - copiedLen := len + if gt(len, codeLen) { + len := codeLen + } + + let shiftedSrcOffset := add(32, srcOffset) // first 32 bytes is length + + let _returndatasize := returndatasize() + if gt(shiftedSrcOffset, _returndatasize) { + shiftedSrcOffset := _returndatasize + } + + if gt(add(len, shiftedSrcOffset), _returndatasize) { + len := sub(_returndatasize, shiftedSrcOffset) + } + + if len { + returndatacopy(dstOffset, shiftedSrcOffset, len) + } + + copiedLen := len + } } // Returns the length of the EVM bytecode. @@ -1759,11 +1765,8 @@ object "EvmEmulator" { } if gt(len, 0) { - let copiedLen - if getRawCodeHash(addr) { - // Gets the code from the addr - copiedLen := fetchDeployedCode(addr, dstOffset, srcOffset, len) - } + // Gets the code from the addr + let copiedLen := fetchDeployedCode(addr, dstOffset, srcOffset, len) if lt(copiedLen, len) { $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, copiedLen), sub(len, copiedLen)) @@ -3517,29 +3520,35 @@ object "EvmEmulator" { function fetchDeployedCode(addr, dstOffset, srcOffset, len) -> copiedLen { let codeHash := getRawCodeHash(addr) mstore(0, codeHash) - // The first word of returndata is the true length of the bytecode - let codeLen := fetchFromSystemContract(CODE_ORACLE_SYSTEM_CONTRACT(), 32) - - if gt(len, codeLen) { - len := codeLen - } - - let shiftedSrcOffset := add(32, srcOffset) // first 32 bits is length - - let _returndatasize := returndatasize() - if gt(shiftedSrcOffset, _returndatasize) { - shiftedSrcOffset := _returndatasize - } - - if gt(add(len, shiftedSrcOffset), _returndatasize) { - len := sub(_returndatasize, shiftedSrcOffset) - } - - if len { - returndatacopy(dstOffset, shiftedSrcOffset, len) - } + + let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) + // it fails if we don't have any code deployed at this address + if success { + returndatacopy(0, 0, 32) + // The first word of returndata is the true length of the bytecode + let codeLen := mload(0) - copiedLen := len + if gt(len, codeLen) { + len := codeLen + } + + let shiftedSrcOffset := add(32, srcOffset) // first 32 bytes is length + + let _returndatasize := returndatasize() + if gt(shiftedSrcOffset, _returndatasize) { + shiftedSrcOffset := _returndatasize + } + + if gt(add(len, shiftedSrcOffset), _returndatasize) { + len := sub(_returndatasize, shiftedSrcOffset) + } + + if len { + returndatacopy(dstOffset, shiftedSrcOffset, len) + } + + copiedLen := len + } } // Returns the length of the EVM bytecode. @@ -4868,11 +4877,8 @@ object "EvmEmulator" { } if gt(len, 0) { - let copiedLen - if getRawCodeHash(addr) { - // Gets the code from the addr - copiedLen := fetchDeployedCode(addr, dstOffset, srcOffset, len) - } + // Gets the code from the addr + let copiedLen := fetchDeployedCode(addr, dstOffset, srcOffset, len) if lt(copiedLen, len) { $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, copiedLen), sub(len, copiedLen)) diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index adc931e45..011f7b13a 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -346,29 +346,35 @@ function isHashOfConstructedEvmContract(rawCodeHash) -> isConstructedEVM { function fetchDeployedCode(addr, dstOffset, srcOffset, len) -> copiedLen { let codeHash := getRawCodeHash(addr) mstore(0, codeHash) - // The first word of returndata is the true length of the bytecode - let codeLen := fetchFromSystemContract(CODE_ORACLE_SYSTEM_CONTRACT(), 32) - - if gt(len, codeLen) { - len := codeLen - } - - let shiftedSrcOffset := add(32, srcOffset) // first 32 bits is length - - let _returndatasize := returndatasize() - if gt(shiftedSrcOffset, _returndatasize) { - shiftedSrcOffset := _returndatasize - } - - if gt(add(len, shiftedSrcOffset), _returndatasize) { - len := sub(_returndatasize, shiftedSrcOffset) - } - - if len { - returndatacopy(dstOffset, shiftedSrcOffset, len) - } + + let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) + // it fails if we don't have any code deployed at this address + if success { + returndatacopy(0, 0, 32) + // The first word of returndata is the true length of the bytecode + let codeLen := mload(0) - copiedLen := len + if gt(len, codeLen) { + len := codeLen + } + + let shiftedSrcOffset := add(32, srcOffset) // first 32 bytes is length + + let _returndatasize := returndatasize() + if gt(shiftedSrcOffset, _returndatasize) { + shiftedSrcOffset := _returndatasize + } + + if gt(add(len, shiftedSrcOffset), _returndatasize) { + len := sub(_returndatasize, shiftedSrcOffset) + } + + if len { + returndatacopy(dstOffset, shiftedSrcOffset, len) + } + + copiedLen := len + } } // Returns the length of the EVM bytecode. diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index fde5817f5..c76854588 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -508,11 +508,8 @@ for { } true { } { } if gt(len, 0) { - let copiedLen - if getRawCodeHash(addr) { - // Gets the code from the addr - copiedLen := fetchDeployedCode(addr, dstOffset, srcOffset, len) - } + // Gets the code from the addr + let copiedLen := fetchDeployedCode(addr, dstOffset, srcOffset, len) if lt(copiedLen, len) { $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, copiedLen), sub(len, copiedLen)) From 47b2ddd983e7b398a784a1ed542c2426d0d3e297 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 6 Dec 2024 12:39:15 +0100 Subject: [PATCH 22/62] fix(EVM): Increase max allowed memory (2) (#1124) --- system-contracts/contracts/EvmEmulator.yul | 8 ++++---- .../evm-emulator/EvmEmulatorFunctions.template.yul | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 4da1e794f..625231950 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -166,9 +166,9 @@ object "EvmEmulator" { } // Used to simplify gas calculations for memory expansion. - // The cost to increase the memory to 4 MB is close to 30M gas + // The cost to increase the memory to 12 MB is close to 277M EVM gas function MAX_POSSIBLE_MEM_LEN() -> max { - max := 0x400000 // 4MB + max := 0xC00000 // 12MB } function MAX_UINT() -> max_uint { @@ -3278,9 +3278,9 @@ object "EvmEmulator" { } // Used to simplify gas calculations for memory expansion. - // The cost to increase the memory to 4 MB is close to 30M gas + // The cost to increase the memory to 12 MB is close to 277M EVM gas function MAX_POSSIBLE_MEM_LEN() -> max { - max := 0x400000 // 4MB + max := 0xC00000 // 12MB } function MAX_UINT() -> max_uint { diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 011f7b13a..04ce45ea8 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -104,9 +104,9 @@ function MEM_OFFSET() -> offset { } // Used to simplify gas calculations for memory expansion. -// The cost to increase the memory to 4 MB is close to 30M gas +// The cost to increase the memory to 12 MB is close to 277M EVM gas function MAX_POSSIBLE_MEM_LEN() -> max { - max := 0x400000 // 4MB + max := 0xC00000 // 12MB } function MAX_UINT() -> max_uint { From c4c964b5704ac667f099da7361abf70cbb654689 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 6 Dec 2024 15:07:56 +0100 Subject: [PATCH 23/62] fix(EVM): Implement EXTCODEHASH in emulator (#1125) --- system-contracts/contracts/EvmEmulator.yul | 80 +++++++++++++++---- .../EvmEmulatorFunctions.template.yul | 5 ++ .../evm-emulator/EvmEmulatorLoop.template.yul | 35 ++++++-- 3 files changed, 96 insertions(+), 24 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 625231950..927c5fdd4 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -191,6 +191,11 @@ object "EvmEmulator" { function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 + function EMPTY_KECCAK() -> value { // keccak("") + value := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 + } + + //////////////////////////////////////////////////////////////// // GENERAL FUNCTIONS //////////////////////////////////////////////////////////////// @@ -1818,20 +1823,39 @@ object "EvmEmulator" { evmGasLeft := chargeGas(evmGasLeft, 2500) } - ip := add(ip, 1) - if iszero(addr) { - stackHead := 0 - continue - } - - switch isEvmContract(addr) + let rawCodeHash := getRawCodeHash(addr) + switch isHashOfConstructedEvmContract(rawCodeHash) case 0 { - stackHead := extcodehash(addr) + let codeLen := and(shr(224, rawCodeHash), 0xffff) + + if codeLen { + if lt(addr, 0x100) { + // precompiles and 0x00 + codeLen := 0 + } + } + + switch codeLen + case 0 { + stackHead := EMPTY_KECCAK() + + if iszero(getRawNonce(addr)) { + if iszero(balance(addr)) { + stackHead := 0 + } + } + } + default { + // zkVM contract + stackHead := rawCodeHash + } } default { + // Get precalculated keccak of EVM code stackHead := getEvmExtcodehash(addr) } + ip := add(ip, 1) } case 0x40 { // OP_BLOCKHASH evmGasLeft := chargeGas(evmGasLeft, 20) @@ -3303,6 +3327,11 @@ object "EvmEmulator" { function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 + function EMPTY_KECCAK() -> value { // keccak("") + value := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 + } + + //////////////////////////////////////////////////////////////// // GENERAL FUNCTIONS //////////////////////////////////////////////////////////////// @@ -4930,20 +4959,39 @@ object "EvmEmulator" { evmGasLeft := chargeGas(evmGasLeft, 2500) } - ip := add(ip, 1) - if iszero(addr) { - stackHead := 0 - continue - } - - switch isEvmContract(addr) + let rawCodeHash := getRawCodeHash(addr) + switch isHashOfConstructedEvmContract(rawCodeHash) case 0 { - stackHead := extcodehash(addr) + let codeLen := and(shr(224, rawCodeHash), 0xffff) + + if codeLen { + if lt(addr, 0x100) { + // precompiles and 0x00 + codeLen := 0 + } + } + + switch codeLen + case 0 { + stackHead := EMPTY_KECCAK() + + if iszero(getRawNonce(addr)) { + if iszero(balance(addr)) { + stackHead := 0 + } + } + } + default { + // zkVM contract + stackHead := rawCodeHash + } } default { + // Get precalculated keccak of EVM code stackHead := getEvmExtcodehash(addr) } + ip := add(ip, 1) } case 0x40 { // OP_BLOCKHASH evmGasLeft := chargeGas(evmGasLeft, 20) diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 04ce45ea8..627a9162f 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -129,6 +129,11 @@ function OVERHEAD() -> overhead { overhead := 2000 } function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 +function EMPTY_KECCAK() -> value { // keccak("") + value := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 +} + + //////////////////////////////////////////////////////////////// // GENERAL FUNCTIONS //////////////////////////////////////////////////////////////// diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index c76854588..e1eeae682 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -561,20 +561,39 @@ for { } true { } { evmGasLeft := chargeGas(evmGasLeft, 2500) } - ip := add(ip, 1) - if iszero(addr) { - stackHead := 0 - continue - } - - switch isEvmContract(addr) + let rawCodeHash := getRawCodeHash(addr) + switch isHashOfConstructedEvmContract(rawCodeHash) case 0 { - stackHead := extcodehash(addr) + let codeLen := and(shr(224, rawCodeHash), 0xffff) + + if codeLen { + if lt(addr, 0x100) { + // precompiles and 0x00 + codeLen := 0 + } + } + + switch codeLen + case 0 { + stackHead := EMPTY_KECCAK() + + if iszero(getRawNonce(addr)) { + if iszero(balance(addr)) { + stackHead := 0 + } + } + } + default { + // zkVM contract + stackHead := rawCodeHash + } } default { + // Get precalculated keccak of EVM code stackHead := getEvmExtcodehash(addr) } + ip := add(ip, 1) } case 0x40 { // OP_BLOCKHASH evmGasLeft := chargeGas(evmGasLeft, 20) From 921b3e37de45beacabc517f2f4e2fec5416888fb Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 6 Dec 2024 18:39:35 +0100 Subject: [PATCH 24/62] fix(EVM): Add stipends for calls to zkVM contracts (#1126) --- system-contracts/contracts/EvmEmulator.yul | 64 ++++++++++++++++--- .../EvmEmulatorFunctions.template.yul | 32 ++++++++-- 2 files changed, 81 insertions(+), 15 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 927c5fdd4..f1a37ca8d 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -863,14 +863,15 @@ object "EvmEmulator" { } function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { - switch isHashOfConstructedEvmContract(getRawCodeHash(addr)) + let rawCodeHash := getRawCodeHash(addr) + switch isHashOfConstructedEvmContract(rawCodeHash) case 0 { // zkEVM native call let precompileCost := getGasForPrecompiles(addr, argsSize) switch precompileCost case 0 { // just smart contract - success, frameGasLeft := callZkVmNative(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) + success, frameGasLeft := callZkVmNative(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic, rawCodeHash) } default { // precompile @@ -919,9 +920,20 @@ object "EvmEmulator" { } // Call native ZkVm contract from EVM context - function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { + function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic, rawCodeHash) -> success, frameGasLeft { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas + let additionalStipend + if iszero(and(shr(224, rawCodeHash), 0xffff)) { // if codelen is zero + additionalStipend := 6000 // should cover first access to empty account + } + + if value { + additionalStipend := 27000 // Stipend for MsgValueSimulator. Covered by positive_value_cost + } + + zkEvmGasToPass := add(zkEvmGasToPass, additionalStipend) + if gt(zkEvmGasToPass, UINT32_MAX()) { // just in case zkEvmGasToPass := UINT32_MAX() } @@ -937,11 +949,21 @@ object "EvmEmulator" { let zkEvmGasUsed := sub(zkEvmGasBefore, gas()) _saveReturndataAfterZkEVMCall() - + if gt(zkEvmGasUsed, zkEvmGasBefore) { // overflow case - zkEvmGasUsed := zkEvmGasToPass // should never happen + zkEvmGasUsed := 0 // should never happen + } + + switch gt(zkEvmGasUsed, additionalStipend) + case 0 { + zkEvmGasUsed := 0 + } + default { + zkEvmGasUsed := sub(zkEvmGasUsed, additionalStipend) } + zkEvmGasToPass := sub(zkEvmGasToPass, additionalStipend) + // refund gas if gt(zkEvmGasToPass, zkEvmGasUsed) { frameGasLeft := div(sub(zkEvmGasToPass, zkEvmGasUsed), GAS_DIVISOR()) @@ -3999,14 +4021,15 @@ object "EvmEmulator" { } function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { - switch isHashOfConstructedEvmContract(getRawCodeHash(addr)) + let rawCodeHash := getRawCodeHash(addr) + switch isHashOfConstructedEvmContract(rawCodeHash) case 0 { // zkEVM native call let precompileCost := getGasForPrecompiles(addr, argsSize) switch precompileCost case 0 { // just smart contract - success, frameGasLeft := callZkVmNative(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) + success, frameGasLeft := callZkVmNative(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic, rawCodeHash) } default { // precompile @@ -4055,9 +4078,20 @@ object "EvmEmulator" { } // Call native ZkVm contract from EVM context - function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { + function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic, rawCodeHash) -> success, frameGasLeft { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas + let additionalStipend + if iszero(and(shr(224, rawCodeHash), 0xffff)) { // if codelen is zero + additionalStipend := 6000 // should cover first access to empty account + } + + if value { + additionalStipend := 27000 // Stipend for MsgValueSimulator. Covered by positive_value_cost + } + + zkEvmGasToPass := add(zkEvmGasToPass, additionalStipend) + if gt(zkEvmGasToPass, UINT32_MAX()) { // just in case zkEvmGasToPass := UINT32_MAX() } @@ -4073,11 +4107,21 @@ object "EvmEmulator" { let zkEvmGasUsed := sub(zkEvmGasBefore, gas()) _saveReturndataAfterZkEVMCall() - + if gt(zkEvmGasUsed, zkEvmGasBefore) { // overflow case - zkEvmGasUsed := zkEvmGasToPass // should never happen + zkEvmGasUsed := 0 // should never happen + } + + switch gt(zkEvmGasUsed, additionalStipend) + case 0 { + zkEvmGasUsed := 0 + } + default { + zkEvmGasUsed := sub(zkEvmGasUsed, additionalStipend) } + zkEvmGasToPass := sub(zkEvmGasToPass, additionalStipend) + // refund gas if gt(zkEvmGasToPass, zkEvmGasUsed) { frameGasLeft := div(sub(zkEvmGasToPass, zkEvmGasUsed), GAS_DIVISOR()) diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 627a9162f..eb24341b3 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -801,14 +801,15 @@ function _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) } function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { - switch isHashOfConstructedEvmContract(getRawCodeHash(addr)) + let rawCodeHash := getRawCodeHash(addr) + switch isHashOfConstructedEvmContract(rawCodeHash) case 0 { // zkEVM native call let precompileCost := getGasForPrecompiles(addr, argsSize) switch precompileCost case 0 { // just smart contract - success, frameGasLeft := callZkVmNative(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) + success, frameGasLeft := callZkVmNative(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic, rawCodeHash) } default { // precompile @@ -857,9 +858,20 @@ function callPrecompile(addr, precompileCost, gasToPass, value, argsOffset, args } // Call native ZkVm contract from EVM context -function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { +function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic, rawCodeHash) -> success, frameGasLeft { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas + let additionalStipend + if iszero(and(shr(224, rawCodeHash), 0xffff)) { // if codelen is zero + additionalStipend := 6000 // should cover first access to empty account + } + + if value { + additionalStipend := 27000 // Stipend for MsgValueSimulator. Covered by positive_value_cost + } + + zkEvmGasToPass := add(zkEvmGasToPass, additionalStipend) + if gt(zkEvmGasToPass, UINT32_MAX()) { // just in case zkEvmGasToPass := UINT32_MAX() } @@ -875,11 +887,21 @@ function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffs let zkEvmGasUsed := sub(zkEvmGasBefore, gas()) _saveReturndataAfterZkEVMCall() - + if gt(zkEvmGasUsed, zkEvmGasBefore) { // overflow case - zkEvmGasUsed := zkEvmGasToPass // should never happen + zkEvmGasUsed := 0 // should never happen + } + + switch gt(zkEvmGasUsed, additionalStipend) + case 0 { + zkEvmGasUsed := 0 + } + default { + zkEvmGasUsed := sub(zkEvmGasUsed, additionalStipend) } + zkEvmGasToPass := sub(zkEvmGasToPass, additionalStipend) + // refund gas if gt(zkEvmGasToPass, zkEvmGasUsed) { frameGasLeft := div(sub(zkEvmGasToPass, zkEvmGasUsed), GAS_DIVISOR()) From ef8012290f0b04665edd002116e5443d33c0f247 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 6 Dec 2024 19:22:18 +0100 Subject: [PATCH 25/62] fix(EVM): Make CREATE flow equivalent to EVM (#1127) --- .../contracts/ContractDeployer.sol | 9 +- system-contracts/contracts/EvmEmulator.yul | 86 +++++++++++++------ .../EvmEmulatorFunctions.template.yul | 43 +++++++--- 3 files changed, 94 insertions(+), 44 deletions(-) diff --git a/system-contracts/contracts/ContractDeployer.sol b/system-contracts/contracts/ContractDeployer.sol index 7032bacad..3b36ed732 100644 --- a/system-contracts/contracts/ContractDeployer.sol +++ b/system-contracts/contracts/ContractDeployer.sol @@ -238,13 +238,6 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { newAddress = Utils.getNewAddressCreateEVM(msg.sender, senderNonce); } - // Unfortunately we can not provide revert reason as it would break EVM compatibility - // we should not increase nonce in case of collision - // solhint-disable-next-line reason-string, gas-custom-errors - require(NONCE_HOLDER_SYSTEM_CONTRACT.getRawNonce(newAddress) == 0x0); - // solhint-disable-next-line reason-string, gas-custom-errors - require(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getCodeHash(uint256(uint160(newAddress))) == 0x0); - return newAddress; } @@ -257,7 +250,7 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { address _newAddress, bytes calldata _initCode ) external payable onlySystemCallFromEvmEmulator returns (uint256, address) { - uint256 constructorReturnEvmGas = _evmDeployOnAddress(msg.sender, _newAddress, _initCode); + uint256 constructorReturnEvmGas = _performDeployOnAddressEVM(msg.sender, _newAddress, AccountAbstractionVersion.None, _initCode); return (constructorReturnEvmGas, _newAddress); } diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index f1a37ca8d..c913a49ad 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -1134,9 +1134,15 @@ object "EvmEmulator" { function _executeCreate(offset, size, value, evmGasLeftOld, isCreate2, salt) -> evmGasLeft, addr { let gasForTheCall := capGasForCall(evmGasLeftOld, evmGasLeftOld) // pass 63/64 of remaining gas - let bytecodeHash := 0 + let bytecodeHash if isCreate2 { - bytecodeHash := keccak256(offset, size) + switch size + case 0 { + bytecodeHash := EMPTY_KECCAK() + } + default { + bytecodeHash := keccak256(offset, size) + } } // we want to calculate the address of new contract, and if it is deployable (no collision), @@ -1146,22 +1152,35 @@ object "EvmEmulator" { mstore(0, 0xf81dae8600000000000000000000000000000000000000000000000000000000) mstore(4, salt) mstore(36, bytecodeHash) - let precreateResult := performSystemCallRevertable(DEPLOYER_SYSTEM_CONTRACT(), 68) - - if iszero(precreateResult) { - // Collision, nonce overflow or EVM not allowed. - // This is *internal* panic, consuming all passed gas. - // Note: we should not consume all gas if nonce overflowed, but this should not happen in reality anyway - evmGasLeft := chargeGas(evmGasLeftOld, gasForTheCall) - } + let canBeDeployed := performSystemCallRevertable(DEPLOYER_SYSTEM_CONTRACT(), 68) - if precreateResult { + if canBeDeployed { returndatacopy(0, 0, 32) addr := mload(0) pop($llvm_AlwaysInline_llvm$_warmAddress(addr)) // will stay warm even if constructor reverts // so even if constructor reverts, nonce stays incremented and addr stays warm - + + // check for code collision + canBeDeployed := 0 + if iszero(getRawCodeHash(addr)) { + // check for nonce collision + if iszero(getRawNonce(addr)) { + canBeDeployed := 1 + } + } + } + + if iszero(canBeDeployed) { + // Nonce overflow, EVM not allowed or collision. + // This is *internal* panic, consuming all passed gas. + // Note: we should not consume all gas if nonce overflowed, but this should not happen in reality anyway + evmGasLeft := chargeGas(evmGasLeftOld, gasForTheCall) + addr := 0 + } + + + if canBeDeployed { // verification of the correctness of the deployed bytecode and payment of gas for its storage will occur in the frame of the new contract pushEvmFrame(gasForTheCall, false) @@ -4292,9 +4311,15 @@ object "EvmEmulator" { function _executeCreate(offset, size, value, evmGasLeftOld, isCreate2, salt) -> evmGasLeft, addr { let gasForTheCall := capGasForCall(evmGasLeftOld, evmGasLeftOld) // pass 63/64 of remaining gas - let bytecodeHash := 0 + let bytecodeHash if isCreate2 { - bytecodeHash := keccak256(offset, size) + switch size + case 0 { + bytecodeHash := EMPTY_KECCAK() + } + default { + bytecodeHash := keccak256(offset, size) + } } // we want to calculate the address of new contract, and if it is deployable (no collision), @@ -4304,22 +4329,35 @@ object "EvmEmulator" { mstore(0, 0xf81dae8600000000000000000000000000000000000000000000000000000000) mstore(4, salt) mstore(36, bytecodeHash) - let precreateResult := performSystemCallRevertable(DEPLOYER_SYSTEM_CONTRACT(), 68) - - if iszero(precreateResult) { - // Collision, nonce overflow or EVM not allowed. - // This is *internal* panic, consuming all passed gas. - // Note: we should not consume all gas if nonce overflowed, but this should not happen in reality anyway - evmGasLeft := chargeGas(evmGasLeftOld, gasForTheCall) - } + let canBeDeployed := performSystemCallRevertable(DEPLOYER_SYSTEM_CONTRACT(), 68) - if precreateResult { + if canBeDeployed { returndatacopy(0, 0, 32) addr := mload(0) pop($llvm_AlwaysInline_llvm$_warmAddress(addr)) // will stay warm even if constructor reverts // so even if constructor reverts, nonce stays incremented and addr stays warm - + + // check for code collision + canBeDeployed := 0 + if iszero(getRawCodeHash(addr)) { + // check for nonce collision + if iszero(getRawNonce(addr)) { + canBeDeployed := 1 + } + } + } + + if iszero(canBeDeployed) { + // Nonce overflow, EVM not allowed or collision. + // This is *internal* panic, consuming all passed gas. + // Note: we should not consume all gas if nonce overflowed, but this should not happen in reality anyway + evmGasLeft := chargeGas(evmGasLeftOld, gasForTheCall) + addr := 0 + } + + + if canBeDeployed { // verification of the correctness of the deployed bytecode and payment of gas for its storage will occur in the frame of the new contract pushEvmFrame(gasForTheCall, false) diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index eb24341b3..28edace55 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -1072,9 +1072,15 @@ function $llvm_NoInline_llvm$_genericCreate(offset, size, value, evmGasLeftOld, function _executeCreate(offset, size, value, evmGasLeftOld, isCreate2, salt) -> evmGasLeft, addr { let gasForTheCall := capGasForCall(evmGasLeftOld, evmGasLeftOld) // pass 63/64 of remaining gas - let bytecodeHash := 0 + let bytecodeHash if isCreate2 { - bytecodeHash := keccak256(offset, size) + switch size + case 0 { + bytecodeHash := EMPTY_KECCAK() + } + default { + bytecodeHash := keccak256(offset, size) + } } // we want to calculate the address of new contract, and if it is deployable (no collision), @@ -1084,22 +1090,35 @@ function _executeCreate(offset, size, value, evmGasLeftOld, isCreate2, salt) -> mstore(0, 0xf81dae8600000000000000000000000000000000000000000000000000000000) mstore(4, salt) mstore(36, bytecodeHash) - let precreateResult := performSystemCallRevertable(DEPLOYER_SYSTEM_CONTRACT(), 68) + let canBeDeployed := performSystemCallRevertable(DEPLOYER_SYSTEM_CONTRACT(), 68) - if iszero(precreateResult) { - // Collision, nonce overflow or EVM not allowed. - // This is *internal* panic, consuming all passed gas. - // Note: we should not consume all gas if nonce overflowed, but this should not happen in reality anyway - evmGasLeft := chargeGas(evmGasLeftOld, gasForTheCall) - } - - if precreateResult { + if canBeDeployed { returndatacopy(0, 0, 32) addr := mload(0) pop($llvm_AlwaysInline_llvm$_warmAddress(addr)) // will stay warm even if constructor reverts // so even if constructor reverts, nonce stays incremented and addr stays warm - + + // check for code collision + canBeDeployed := 0 + if iszero(getRawCodeHash(addr)) { + // check for nonce collision + if iszero(getRawNonce(addr)) { + canBeDeployed := 1 + } + } + } + + if iszero(canBeDeployed) { + // Nonce overflow, EVM not allowed or collision. + // This is *internal* panic, consuming all passed gas. + // Note: we should not consume all gas if nonce overflowed, but this should not happen in reality anyway + evmGasLeft := chargeGas(evmGasLeftOld, gasForTheCall) + addr := 0 + } + + + if canBeDeployed { // verification of the correctness of the deployed bytecode and payment of gas for its storage will occur in the frame of the new contract pushEvmFrame(gasForTheCall, false) From b61406b4f9925ddaa363dfab27fb7a0f69d8d8f5 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Sun, 8 Dec 2024 14:46:19 +0100 Subject: [PATCH 26/62] Handle Empty.sol as empty account --- system-contracts/contracts/EvmEmulator.yul | 66 +++++++++++-------- .../EvmEmulatorFunctions.template.yul | 33 ++++++---- 2 files changed, 60 insertions(+), 39 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index c913a49ad..cd45a53dd 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -815,16 +815,19 @@ object "EvmEmulator" { switch precompileCost case 0 { // Not a precompile - switch eq(1, shr(248, rawCodeHash)) - case 0 { - // Empty contract or EVM contract being constructed - success := delegatecall(gas(), addr, argsOffset, argsSize, retOffset, retSize) - _saveReturndataAfterZkEVMCall() + _eraseReturndataPointer() + + let isCallToEmptyContract := iszero(addr) // 0x00 is always "empty" + if iszero(isCallToEmptyContract) { + isCallToEmptyContract := iszero(and(shr(224, rawCodeHash), 0xffff)) // is codelen zero? } - default { - // We forbid delegatecalls to EraVM native contracts - _eraseReturndataPointer() + + if isCallToEmptyContract { + success := delegatecall(gas(), addr, argsOffset, argsSize, retOffset, retSize) + _saveReturndataAfterZkEVMCall() } + + // We forbid delegatecalls to EraVM native contracts } default { // Precompile. Simlate using staticcall, since EraVM behavior differs here @@ -923,12 +926,16 @@ object "EvmEmulator" { function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic, rawCodeHash) -> success, frameGasLeft { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas - let additionalStipend - if iszero(and(shr(224, rawCodeHash), 0xffff)) { // if codelen is zero - additionalStipend := 6000 // should cover first access to empty account + let additionalStipend := 6000 // should cover first access to empty account + switch value + case 0 { + if gt(addr, 0) { // zero address is always "empty" + if and(shr(224, rawCodeHash), 0xffff) { // if codelen is not zero + additionalStipend := 0 + } + } } - - if value { + default { additionalStipend := 27000 // Stipend for MsgValueSimulator. Covered by positive_value_cost } @@ -3992,16 +3999,19 @@ object "EvmEmulator" { switch precompileCost case 0 { // Not a precompile - switch eq(1, shr(248, rawCodeHash)) - case 0 { - // Empty contract or EVM contract being constructed - success := delegatecall(gas(), addr, argsOffset, argsSize, retOffset, retSize) - _saveReturndataAfterZkEVMCall() + _eraseReturndataPointer() + + let isCallToEmptyContract := iszero(addr) // 0x00 is always "empty" + if iszero(isCallToEmptyContract) { + isCallToEmptyContract := iszero(and(shr(224, rawCodeHash), 0xffff)) // is codelen zero? } - default { - // We forbid delegatecalls to EraVM native contracts - _eraseReturndataPointer() + + if isCallToEmptyContract { + success := delegatecall(gas(), addr, argsOffset, argsSize, retOffset, retSize) + _saveReturndataAfterZkEVMCall() } + + // We forbid delegatecalls to EraVM native contracts } default { // Precompile. Simlate using staticcall, since EraVM behavior differs here @@ -4100,12 +4110,16 @@ object "EvmEmulator" { function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic, rawCodeHash) -> success, frameGasLeft { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas - let additionalStipend - if iszero(and(shr(224, rawCodeHash), 0xffff)) { // if codelen is zero - additionalStipend := 6000 // should cover first access to empty account + let additionalStipend := 6000 // should cover first access to empty account + switch value + case 0 { + if gt(addr, 0) { // zero address is always "empty" + if and(shr(224, rawCodeHash), 0xffff) { // if codelen is not zero + additionalStipend := 0 + } + } } - - if value { + default { additionalStipend := 27000 // Stipend for MsgValueSimulator. Covered by positive_value_cost } diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 28edace55..843e2ead3 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -753,16 +753,19 @@ function performDelegateCall(oldSp, evmGasLeft, isStatic, oldStackHead) -> newGa switch precompileCost case 0 { // Not a precompile - switch eq(1, shr(248, rawCodeHash)) - case 0 { - // Empty contract or EVM contract being constructed - success := delegatecall(gas(), addr, argsOffset, argsSize, retOffset, retSize) - _saveReturndataAfterZkEVMCall() + _eraseReturndataPointer() + + let isCallToEmptyContract := iszero(addr) // 0x00 is always "empty" + if iszero(isCallToEmptyContract) { + isCallToEmptyContract := iszero(and(shr(224, rawCodeHash), 0xffff)) // is codelen zero? } - default { - // We forbid delegatecalls to EraVM native contracts - _eraseReturndataPointer() + + if isCallToEmptyContract { + success := delegatecall(gas(), addr, argsOffset, argsSize, retOffset, retSize) + _saveReturndataAfterZkEVMCall() } + + // We forbid delegatecalls to EraVM native contracts } default { // Precompile. Simlate using staticcall, since EraVM behavior differs here @@ -861,12 +864,16 @@ function callPrecompile(addr, precompileCost, gasToPass, value, argsOffset, args function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic, rawCodeHash) -> success, frameGasLeft { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas - let additionalStipend - if iszero(and(shr(224, rawCodeHash), 0xffff)) { // if codelen is zero - additionalStipend := 6000 // should cover first access to empty account + let additionalStipend := 6000 // should cover first access to empty account + switch value + case 0 { + if gt(addr, 0) { // zero address is always "empty" + if and(shr(224, rawCodeHash), 0xffff) { // if codelen is not zero + additionalStipend := 0 + } + } } - - if value { + default { additionalStipend := 27000 // Stipend for MsgValueSimulator. Covered by positive_value_cost } From 72bbed03990d812e0c856266048b5d9f24769e43 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Sun, 8 Dec 2024 15:16:42 +0100 Subject: [PATCH 27/62] Do not call precompiles if not enough gas provided --- system-contracts/contracts/EvmEmulator.yul | 70 ++++++++++--------- .../EvmEmulatorFunctions.template.yul | 35 +++++----- 2 files changed, 57 insertions(+), 48 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index cd45a53dd..367aa87c9 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -900,26 +900,29 @@ object "EvmEmulator" { } function callPrecompile(addr, precompileCost, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { - let zkVmGasToPass := gas() // pass all remaining gas, precompiles should not call any contracts - if lt(gasToPass, precompileCost) { - zkVmGasToPass := 0 // in EVM precompile should revert consuming all gas in that case - precompileCost := gasToPass // just in case - } - - switch isStatic + switch lt(gasToPass, precompileCost) case 0 { - success := rawCall(zkVmGasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) + let zkVmGasToPass := gas() // pass all remaining gas, precompiles should not call any contracts + + switch isStatic + case 0 { + success := rawCall(zkVmGasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) + } + default { + success := rawStaticcall(zkVmGasToPass, addr, argsOffset, argsSize, retOffset, retSize) + } + + _saveReturndataAfterZkEVMCall() + + if success { + frameGasLeft := sub(gasToPass, precompileCost) + } + // else consume all provided gas } default { - success := rawStaticcall(zkVmGasToPass, addr, argsOffset, argsSize, retOffset, retSize) - } - - _saveReturndataAfterZkEVMCall() - - if success { - frameGasLeft := sub(gasToPass, precompileCost) + // consume all provided gas + _eraseReturndataPointer() } - // else consume all provided gas } // Call native ZkVm contract from EVM context @@ -4084,26 +4087,29 @@ object "EvmEmulator" { } function callPrecompile(addr, precompileCost, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { - let zkVmGasToPass := gas() // pass all remaining gas, precompiles should not call any contracts - if lt(gasToPass, precompileCost) { - zkVmGasToPass := 0 // in EVM precompile should revert consuming all gas in that case - precompileCost := gasToPass // just in case - } - - switch isStatic + switch lt(gasToPass, precompileCost) case 0 { - success := rawCall(zkVmGasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) + let zkVmGasToPass := gas() // pass all remaining gas, precompiles should not call any contracts + + switch isStatic + case 0 { + success := rawCall(zkVmGasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) + } + default { + success := rawStaticcall(zkVmGasToPass, addr, argsOffset, argsSize, retOffset, retSize) + } + + _saveReturndataAfterZkEVMCall() + + if success { + frameGasLeft := sub(gasToPass, precompileCost) + } + // else consume all provided gas } default { - success := rawStaticcall(zkVmGasToPass, addr, argsOffset, argsSize, retOffset, retSize) - } - - _saveReturndataAfterZkEVMCall() - - if success { - frameGasLeft := sub(gasToPass, precompileCost) + // consume all provided gas + _eraseReturndataPointer() } - // else consume all provided gas } // Call native ZkVm contract from EVM context diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 843e2ead3..00d579de5 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -838,26 +838,29 @@ function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, r } function callPrecompile(addr, precompileCost, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { - let zkVmGasToPass := gas() // pass all remaining gas, precompiles should not call any contracts - if lt(gasToPass, precompileCost) { - zkVmGasToPass := 0 // in EVM precompile should revert consuming all gas in that case - precompileCost := gasToPass // just in case - } - - switch isStatic + switch lt(gasToPass, precompileCost) case 0 { - success := rawCall(zkVmGasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) + let zkVmGasToPass := gas() // pass all remaining gas, precompiles should not call any contracts + + switch isStatic + case 0 { + success := rawCall(zkVmGasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) + } + default { + success := rawStaticcall(zkVmGasToPass, addr, argsOffset, argsSize, retOffset, retSize) + } + + _saveReturndataAfterZkEVMCall() + + if success { + frameGasLeft := sub(gasToPass, precompileCost) + } + // else consume all provided gas } default { - success := rawStaticcall(zkVmGasToPass, addr, argsOffset, argsSize, retOffset, retSize) - } - - _saveReturndataAfterZkEVMCall() - - if success { - frameGasLeft := sub(gasToPass, precompileCost) + // consume all provided gas + _eraseReturndataPointer() } - // else consume all provided gas } // Call native ZkVm contract from EVM context From daf70229288555d4d1ade61b7e97bf01564502c7 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 9 Dec 2024 12:41:38 +0100 Subject: [PATCH 28/62] fix(EVM): Fix logs implementation (2) (#1137) --- system-contracts/contracts/EvmEmulator.yul | 250 ++++++------------ .../EvmEmulatorFunctions.template.yul | 29 ++ .../evm-emulator/EvmEmulatorLoop.template.yul | 96 +------ 3 files changed, 117 insertions(+), 258 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 367aa87c9..ea8b65d34 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -1301,6 +1301,35 @@ object "EvmEmulator" { $llvm_AlwaysInline_llvm$_copyRest(dest_end, 0, rest_len) } } + + //////////////////////////////////////////////////////////////// + // LOGS FUNCTIONALITY + //////////////////////////////////////////////////////////////// + + function _genericLog(sp, stackHead, evmGasLeft, topicCount, isStatic) -> newEvmGasLeft, offset, size, newSp, newStackHead { + newEvmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + panic() + } + + let rawOffset + popStackCheck(sp, add(2, topicCount)) + rawOffset, newSp, newStackHead := popStackItemWithoutCheck(sp, stackHead) + size, newSp, newStackHead := popStackItemWithoutCheck(newSp, newStackHead) + + checkMemIsAccessible(rawOffset, size) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(rawOffset, size)) + dynamicGas := add(dynamicGas, mul(375, topicCount)) + + newEvmGasLeft := chargeGas(newEvmGasLeft, dynamicGas) + + if size { + offset := add(rawOffset, MEM_OFFSET()) + } + } function simulate( isCallerEVM, @@ -2641,125 +2670,49 @@ object "EvmEmulator" { ip := add(ip, 1) } case 0xA0 { // OP_LOG0 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 2) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - - log0(add(offset, MEM_OFFSET()), size) + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 0, isStatic) + log0(offset, size) ip := add(ip, 1) } case 0xA1 { // OP_LOG1 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 3) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - dynamicGas := add(dynamicGas, 375) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 1, isStatic) { let topic1 topic1, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - log1(add(offset, MEM_OFFSET()), size, topic1) + log1(offset, size, topic1) } ip := add(ip, 1) } case 0xA2 { // OP_LOG2 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 4) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - dynamicGas := add(dynamicGas, 750) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 2, isStatic) { let topic1, topic2 topic1, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic2, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - log2(add(offset, MEM_OFFSET()), size, topic1, topic2) + log2(offset, size, topic1, topic2) } ip := add(ip, 1) } case 0xA3 { // OP_LOG3 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 5) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - dynamicGas := add(dynamicGas, 1125) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 3, isStatic) { let topic1, topic2, topic3 topic1, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic2, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic3, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - log3(add(offset, MEM_OFFSET()), size, topic1, topic2, topic3) + log3(offset, size, topic1, topic2, topic3) } ip := add(ip, 1) } case 0xA4 { // OP_LOG4 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 6) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - dynamicGas := add(dynamicGas, 1500) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 4, isStatic) { let topic1, topic2, topic3, topic4 @@ -2767,7 +2720,7 @@ object "EvmEmulator" { topic2, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic3, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic4, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - log4(add(offset, MEM_OFFSET()), size, topic1, topic2, topic3, topic4) + log4(offset, size, topic1, topic2, topic3, topic4) } ip := add(ip, 1) } @@ -4488,6 +4441,35 @@ object "EvmEmulator" { $llvm_AlwaysInline_llvm$_copyRest(dest_end, 0, rest_len) } } + + //////////////////////////////////////////////////////////////// + // LOGS FUNCTIONALITY + //////////////////////////////////////////////////////////////// + + function _genericLog(sp, stackHead, evmGasLeft, topicCount, isStatic) -> newEvmGasLeft, offset, size, newSp, newStackHead { + newEvmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + panic() + } + + let rawOffset + popStackCheck(sp, add(2, topicCount)) + rawOffset, newSp, newStackHead := popStackItemWithoutCheck(sp, stackHead) + size, newSp, newStackHead := popStackItemWithoutCheck(newSp, newStackHead) + + checkMemIsAccessible(rawOffset, size) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(rawOffset, size)) + dynamicGas := add(dynamicGas, mul(375, topicCount)) + + newEvmGasLeft := chargeGas(newEvmGasLeft, dynamicGas) + + if size { + offset := add(rawOffset, MEM_OFFSET()) + } + } function $llvm_NoInline_llvm$_simulate( isCallerEVM, @@ -5828,125 +5810,49 @@ object "EvmEmulator" { ip := add(ip, 1) } case 0xA0 { // OP_LOG0 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 2) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - - log0(add(offset, MEM_OFFSET()), size) + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 0, isStatic) + log0(offset, size) ip := add(ip, 1) } case 0xA1 { // OP_LOG1 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 3) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - dynamicGas := add(dynamicGas, 375) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 1, isStatic) { let topic1 topic1, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - log1(add(offset, MEM_OFFSET()), size, topic1) + log1(offset, size, topic1) } ip := add(ip, 1) } case 0xA2 { // OP_LOG2 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 4) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - dynamicGas := add(dynamicGas, 750) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 2, isStatic) { let topic1, topic2 topic1, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic2, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - log2(add(offset, MEM_OFFSET()), size, topic1, topic2) + log2(offset, size, topic1, topic2) } ip := add(ip, 1) } case 0xA3 { // OP_LOG3 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 5) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - dynamicGas := add(dynamicGas, 1125) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 3, isStatic) { let topic1, topic2, topic3 topic1, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic2, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic3, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - log3(add(offset, MEM_OFFSET()), size, topic1, topic2, topic3) + log3(offset, size, topic1, topic2, topic3) } ip := add(ip, 1) } case 0xA4 { // OP_LOG4 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 6) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - dynamicGas := add(dynamicGas, 1500) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 4, isStatic) { let topic1, topic2, topic3, topic4 @@ -5954,7 +5860,7 @@ object "EvmEmulator" { topic2, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic3, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic4, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - log4(add(offset, MEM_OFFSET()), size, topic1, topic2, topic3, topic4) + log4(offset, size, topic1, topic2, topic3, topic4) } ip := add(ip, 1) } diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 00d579de5..50089c79d 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -1238,4 +1238,33 @@ function $llvm_AlwaysInline_llvm$_memsetToZero(dest,len) { if rest_len { $llvm_AlwaysInline_llvm$_copyRest(dest_end, 0, rest_len) } +} + +//////////////////////////////////////////////////////////////// +// LOGS FUNCTIONALITY +//////////////////////////////////////////////////////////////// + +function _genericLog(sp, stackHead, evmGasLeft, topicCount, isStatic) -> newEvmGasLeft, offset, size, newSp, newStackHead { + newEvmGasLeft := chargeGas(evmGasLeft, 375) + + if isStatic { + panic() + } + + let rawOffset + popStackCheck(sp, add(2, topicCount)) + rawOffset, newSp, newStackHead := popStackItemWithoutCheck(sp, stackHead) + size, newSp, newStackHead := popStackItemWithoutCheck(newSp, newStackHead) + + checkMemIsAccessible(rawOffset, size) + + // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost + let dynamicGas := add(shl(3, size), expandMemory(rawOffset, size)) + dynamicGas := add(dynamicGas, mul(375, topicCount)) + + newEvmGasLeft := chargeGas(newEvmGasLeft, dynamicGas) + + if size { + offset := add(rawOffset, MEM_OFFSET()) + } } \ No newline at end of file diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index e1eeae682..b5d956b44 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -1328,125 +1328,49 @@ for { } true { } { ip := add(ip, 1) } case 0xA0 { // OP_LOG0 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 2) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - - log0(add(offset, MEM_OFFSET()), size) + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 0, isStatic) + log0(offset, size) ip := add(ip, 1) } case 0xA1 { // OP_LOG1 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 3) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - dynamicGas := add(dynamicGas, 375) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 1, isStatic) { let topic1 topic1, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - log1(add(offset, MEM_OFFSET()), size, topic1) + log1(offset, size, topic1) } ip := add(ip, 1) } case 0xA2 { // OP_LOG2 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 4) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - dynamicGas := add(dynamicGas, 750) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 2, isStatic) { let topic1, topic2 topic1, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic2, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - log2(add(offset, MEM_OFFSET()), size, topic1, topic2) + log2(offset, size, topic1, topic2) } ip := add(ip, 1) } case 0xA3 { // OP_LOG3 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 5) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - dynamicGas := add(dynamicGas, 1125) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 3, isStatic) { let topic1, topic2, topic3 topic1, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic2, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic3, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - log3(add(offset, MEM_OFFSET()), size, topic1, topic2, topic3) + log3(offset, size, topic1, topic2, topic3) } ip := add(ip, 1) } case 0xA4 { // OP_LOG4 - evmGasLeft := chargeGas(evmGasLeft, 375) - - if isStatic { - panic() - } - let offset, size - popStackCheck(sp, 6) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost - let dynamicGas := add(shl(3, size), expandMemory(offset, size)) - dynamicGas := add(dynamicGas, 1500) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + evmGasLeft, offset, size, sp, stackHead := _genericLog(sp, stackHead, evmGasLeft, 4, isStatic) { let topic1, topic2, topic3, topic4 @@ -1454,7 +1378,7 @@ for { } true { } { topic2, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic3, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) topic4, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - log4(add(offset, MEM_OFFSET()), size, topic1, topic2, topic3, topic4) + log4(offset, size, topic1, topic2, topic3, topic4) } ip := add(ip, 1) } From fe838fcbd6479cb98262bbaa4a8752236bc6fdf4 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 16 Dec 2024 18:35:43 +0100 Subject: [PATCH 29/62] feat(EVM): Enable EVM emulator using service call (#1141) --- l1-contracts/contracts/common/Config.sol | 3 ++ .../common/interfaces/IL2ContractDeployer.sol | 12 ++++++ .../contracts/governance/ChainAdmin.sol | 7 ++++ .../contracts/governance/IChainAdmin.sol | 5 +++ .../StateTransitionManager.sol | 34 +++++------------ .../chain-deps/facets/Admin.sol | 12 ++++++ .../chain-deps/facets/Mailbox.sol | 37 +++++++++++++++++- .../facets/ZkSyncHyperchainBase.sol | 7 ++++ .../chain-interfaces/IAdmin.sol | 6 +++ .../chain-interfaces/IMailbox.sol | 9 +++++ .../l2-deps/AllowedBytecodeTypes.sol | 11 ------ .../l2-deps/ISystemContext.sol | 5 +-- .../deploy-scripts/EnableEvmEmulator.s.sol | 18 +++++++++ .../PrepareZKChainRegistrationCalldata.s.sol | 10 +---- .../deploy-scripts/RegisterHyperchain.s.sol | 10 +---- l1-contracts/scripts/register-hyperchain.ts | 13 ++++--- l1-contracts/src.ts/deploy-process.ts | 6 +-- l1-contracts/src.ts/deploy.ts | 22 ++++++----- .../Bridgehub/experimental_bridge.t.sol | 15 ++------ .../CreateNewChain.t.sol | 3 +- .../_StateTransitionManager_Shared.t.sol | 15 +------- system-contracts/SystemContractsHashes.json | 38 +++++++++---------- system-contracts/contracts/Constants.sol | 4 +- .../contracts/ContractDeployer.sol | 17 +++------ system-contracts/contracts/SystemContext.sol | 13 +------ .../contracts/SystemContractErrors.sol | 2 - .../interfaces/IContractDeployer.sol | 4 +- 27 files changed, 185 insertions(+), 153 deletions(-) delete mode 100644 l1-contracts/contracts/state-transition/l2-deps/AllowedBytecodeTypes.sol create mode 100644 l1-contracts/deploy-scripts/EnableEvmEmulator.s.sol diff --git a/l1-contracts/contracts/common/Config.sol b/l1-contracts/contracts/common/Config.sol index c0e05f1fc..e6e3118bb 100644 --- a/l1-contracts/contracts/common/Config.sol +++ b/l1-contracts/contracts/common/Config.sol @@ -108,3 +108,6 @@ bytes32 constant TWO_BRIDGES_MAGIC_VALUE = bytes32(uint256(keccak256("TWO_BRIDGE /// @dev https://eips.ethereum.org/EIPS/eip-1352 address constant BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS = address(uint160(type(uint16).max)); + +/// @dev Used as the `msg.sender` for system service transactions. +address constant SERVICE_TRANSACTION_SENDER = address(uint160(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF)); diff --git a/l1-contracts/contracts/common/interfaces/IL2ContractDeployer.sol b/l1-contracts/contracts/common/interfaces/IL2ContractDeployer.sol index 3d5b597df..7aa5c1cc0 100644 --- a/l1-contracts/contracts/common/interfaces/IL2ContractDeployer.sol +++ b/l1-contracts/contracts/common/interfaces/IL2ContractDeployer.sol @@ -2,6 +2,14 @@ // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version. pragma solidity ^0.8.21; +/// @notice Defines what types of bytecode are allowed to be deployed on this chain +/// - `EraVm` means that only native contracts can be deployed +/// - `EraVmAndEVM` means that native contracts and EVM contracts can be deployed +enum AllowedBytecodeTypes { + EraVm, + EraVmAndEVM +} + /** * @author Matter Labs * @notice System smart contract that is responsible for deploying other smart contracts on a ZKsync hyperchain. @@ -29,4 +37,8 @@ interface IL2ContractDeployer { /// @param _bytecodeHash The correctly formatted hash of the bytecode. /// @param _input The constructor calldata. function create2(bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input) external; + + /// @notice Changes what types of bytecodes are allowed to be deployed on the chain. + /// @param newAllowedBytecodeTypes The new allowed bytecode types mode. + function setAllowedBytecodeTypesToDeploy(AllowedBytecodeTypes newAllowedBytecodeTypes) external; } diff --git a/l1-contracts/contracts/governance/ChainAdmin.sol b/l1-contracts/contracts/governance/ChainAdmin.sol index 4d9ff858f..c322477a7 100644 --- a/l1-contracts/contracts/governance/ChainAdmin.sol +++ b/l1-contracts/contracts/governance/ChainAdmin.sol @@ -48,6 +48,13 @@ contract ChainAdmin is IChainAdmin, Ownable2Step { emit UpdateUpgradeTimestamp(_protocolVersion, _upgradeTimestamp); } + /// @notice Enable EVM emulation on chain. + /// @param _chainContract The chain contract address where the EVM emulator will be enabled. + function enableEvmEmulator(IAdmin _chainContract) external onlyOwner returns (bytes32 canonicalTxHash) { + canonicalTxHash = _chainContract.allowEvmEmulation(); + emit EnableEvmEmulator(); + } + /// @notice Execute multiple calls as part of contract administration. /// @param _calls Array of Call structures defining target, value, and data for each call. /// @param _requireSuccess If true, reverts transaction on any call failure. diff --git a/l1-contracts/contracts/governance/IChainAdmin.sol b/l1-contracts/contracts/governance/IChainAdmin.sol index d5d8f117c..5bdd15ac9 100644 --- a/l1-contracts/contracts/governance/IChainAdmin.sol +++ b/l1-contracts/contracts/governance/IChainAdmin.sol @@ -27,6 +27,9 @@ interface IChainAdmin { /// @notice Emitted when the new token multiplier address is set. event NewTokenMultiplierSetter(address _oldTokenMultiplierSetter, address _newTokenMultiplierSetter); + /// @notice The EVM emulator has been enabled + event EnableEvmEmulator(); + function setTokenMultiplierSetter(address _tokenMultiplierSetter) external; function setUpgradeTimestamp(uint256 _protocolVersion, uint256 _upgradeTimestamp) external; @@ -34,4 +37,6 @@ interface IChainAdmin { function multicall(Call[] calldata _calls, bool _requireSuccess) external payable; function setTokenMultiplier(IAdmin _chainContract, uint128 _nominator, uint128 _denominator) external; + + function enableEvmEmulator(IAdmin _chainContract) external returns (bytes32 canonicalTxHash); } diff --git a/l1-contracts/contracts/state-transition/StateTransitionManager.sol b/l1-contracts/contracts/state-transition/StateTransitionManager.sol index f610bad78..a14298f2a 100644 --- a/l1-contracts/contracts/state-transition/StateTransitionManager.sol +++ b/l1-contracts/contracts/state-transition/StateTransitionManager.sol @@ -13,7 +13,6 @@ import {IDiamondInit} from "./chain-interfaces/IDiamondInit.sol"; import {IExecutor} from "./chain-interfaces/IExecutor.sol"; import {IStateTransitionManager, StateTransitionManagerInitializeData, ChainCreationParams} from "./IStateTransitionManager.sol"; import {ISystemContext} from "./l2-deps/ISystemContext.sol"; -import {AllowedBytecodeTypes} from "./l2-deps/AllowedBytecodeTypes.sol"; import {IZkSyncHyperchain} from "./chain-interfaces/IZkSyncHyperchain.sol"; import {FeeParams} from "./chain-deps/ZkSyncHyperchainStorage.sol"; import {L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR, L2_FORCE_DEPLOYER_ADDR} from "../common/L2ContractAddresses.sol"; @@ -323,15 +322,8 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own /// registration /// @dev we have to set the chainId at genesis, as blockhashzero is the same for all chains with the same chainId - function _setChainConfigurationUpgrade( - uint256 _chainId, - AllowedBytecodeTypes _allowedBytecodeTypesMode, - address _chainContract - ) internal { - bytes memory systemContextCalldata = abi.encodeCall( - ISystemContext.setChainConfiguration, - (_chainId, uint256(_allowedBytecodeTypesMode)) - ); + function _setChainIdUpgrade(uint256 _chainId, address _chainContract) internal { + bytes memory systemContextCalldata = abi.encodeCall(ISystemContext.setChainId, (_chainId)); uint256[] memory uintEmptyArray; bytes[] memory bytesEmptyArray; @@ -404,13 +396,13 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own /// @param _baseToken the base token address used to pay for gas fees /// @param _sharedBridge the shared bridge address, used as base token bridge /// @param _admin the chain's admin address - /// @param _inputData the input data for chain creation + /// @param _diamondCut the diamond cut data that initializes the chains Diamond Proxy function createNewChain( uint256 _chainId, address _baseToken, address _sharedBridge, address _admin, - bytes calldata _inputData + bytes calldata _diamondCut ) external onlyBridgehub { if (getHyperchain(_chainId) != address(0)) { // Hyperchain already registered @@ -418,18 +410,12 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own } // check not registered - (bytes memory _diamondCut, AllowedBytecodeTypes allowedBytecodeTypesMode) = abi.decode( - _inputData, - (bytes, AllowedBytecodeTypes) - ); Diamond.DiamondCutData memory diamondCut = abi.decode(_diamondCut, (Diamond.DiamondCutData)); - { - // check input - bytes32 cutHashInput = keccak256(_diamondCut); - if (cutHashInput != initialCutHash) { - revert HashMismatch(initialCutHash, cutHashInput); - } + // check input + bytes32 cutHashInput = keccak256(_diamondCut); + if (cutHashInput != initialCutHash) { + revert HashMismatch(initialCutHash, cutHashInput); } // construct init data @@ -459,8 +445,8 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own _registerNewHyperchain(_chainId, hyperchainAddress); - // set chain configuration: chainId in VM and allowed bytecode types - _setChainConfigurationUpgrade(_chainId, allowedBytecodeTypesMode, hyperchainAddress); + // set chainId in VM + _setChainIdUpgrade(_chainId, hyperchainAddress); } /// @dev This internal function is used to register a new hyperchain in the system. diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol index 9f98c00ec..479f4bbdb 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol @@ -3,12 +3,15 @@ pragma solidity 0.8.24; import {IAdmin} from "../../chain-interfaces/IAdmin.sol"; +import {IMailbox} from "../../chain-interfaces/IMailbox.sol"; import {Diamond} from "../../libraries/Diamond.sol"; import {MAX_GAS_PER_TRANSACTION} from "../../../common/Config.sol"; import {FeeParams, PubdataPricingMode} from "../ZkSyncHyperchainStorage.sol"; import {ZkSyncHyperchainBase} from "./ZkSyncHyperchainBase.sol"; import {IStateTransitionManager} from "../../IStateTransitionManager.sol"; import {Unauthorized, TooMuchGas, PriorityTxPubdataExceedsMaxPubDataPerBatch, InvalidPubdataPricingMode, ProtocolIdMismatch, ChainAlreadyLive, HashMismatch, ProtocolIdNotGreater, DenominatorIsZero, DiamondAlreadyFrozen, DiamondNotFrozen} from "../../../common/L1ContractErrors.sol"; +import {L2_DEPLOYER_SYSTEM_CONTRACT_ADDR} from "../../../common/L2ContractAddresses.sol"; +import {IL2ContractDeployer, AllowedBytecodeTypes} from "../../../common/interfaces/IL2ContractDeployer.sol"; // While formally the following import is not used, it is needed to inherit documentation from it import {IZkSyncHyperchainBase} from "../../chain-interfaces/IZkSyncHyperchainBase.sol"; @@ -119,6 +122,15 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { emit NewTransactionFilterer(oldTransactionFilterer, _transactionFilterer); } + /// @inheritdoc IAdmin + function allowEvmEmulation() external onlyAdmin returns (bytes32 canonicalTxHash) { + canonicalTxHash = IMailbox(address(this)).requestL2ServiceTransaction( + L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, + abi.encodeCall(IL2ContractDeployer.setAllowedBytecodeTypesToDeploy, AllowedBytecodeTypes.EraVmAndEVM) + ); + emit EnableEvmEmulator(); + } + /*////////////////////////////////////////////////////////////// UPGRADE EXECUTION //////////////////////////////////////////////////////////////*/ diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol index 43f6b04e7..771c66176 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol @@ -15,7 +15,7 @@ import {UncheckedMath} from "../../../common/libraries/UncheckedMath.sol"; import {L2ContractHelper} from "../../../common/libraries/L2ContractHelper.sol"; import {AddressAliasHelper} from "../../../vendor/AddressAliasHelper.sol"; import {ZkSyncHyperchainBase} from "./ZkSyncHyperchainBase.sol"; -import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA, ETH_TOKEN_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH, PRIORITY_OPERATION_L2_TX_TYPE, PRIORITY_EXPIRATION, MAX_NEW_FACTORY_DEPS} from "../../../common/Config.sol"; +import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA, ETH_TOKEN_ADDRESS, L1_GAS_PER_PUBDATA_BYTE, L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH, PRIORITY_OPERATION_L2_TX_TYPE, PRIORITY_EXPIRATION, MAX_NEW_FACTORY_DEPS, SERVICE_TRANSACTION_SENDER} from "../../../common/Config.sol"; import {L2_BOOTLOADER_ADDRESS, L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR} from "../../../common/L2ContractAddresses.sol"; import {IL1SharedBridge} from "../../../bridge/interfaces/IL1SharedBridge.sol"; @@ -248,6 +248,28 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { ); } + /// @inheritdoc IMailbox + function requestL2ServiceTransaction( + address _contractL2, + bytes calldata _l2Calldata + ) external onlySelf returns (bytes32 canonicalTxHash) { + canonicalTxHash = _requestL2TransactionFree( + BridgehubL2TransactionRequest({ + sender: SERVICE_TRANSACTION_SENDER, + contractL2: _contractL2, + mintValue: 0, + l2Value: 0, + // Very large amount + l2GasLimit: 72_000_000, + l2Calldata: _l2Calldata, + l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA, + factoryDeps: new bytes[](0), + // Tx is free, so no refund recipient needed + refundRecipient: address(0) + }) + ); + } + function _requestL2TransactionSender( BridgehubL2TransactionRequest memory _request ) internal nonReentrant returns (bytes32 canonicalTxHash) { @@ -313,6 +335,19 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { canonicalTxHash = _writePriorityOp(_params); } + function _requestL2TransactionFree( + BridgehubL2TransactionRequest memory _request + ) internal nonReentrant returns (bytes32 canonicalTxHash) { + WritePriorityOpParams memory params = WritePriorityOpParams({ + request: _request, + txId: s.priorityQueue.getTotalPriorityTxs(), + l2GasPrice: 0, + expirationTimestamp: uint64(block.timestamp + PRIORITY_EXPIRATION) + }); + + canonicalTxHash = _writePriorityOp(params); + } + function _serializeL2Transaction( WritePriorityOpParams memory _priorityOpParams ) internal pure returns (L2CanonicalTransaction memory transaction) { diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/ZkSyncHyperchainBase.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/ZkSyncHyperchainBase.sol index 0910fcab3..8d6bbb4ce 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/ZkSyncHyperchainBase.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/ZkSyncHyperchainBase.sol @@ -64,4 +64,11 @@ contract ZkSyncHyperchainBase is ReentrancyGuard { } _; } + + modifier onlySelf() { + if (msg.sender != address(this)) { + revert Unauthorized(msg.sender); + } + _; + } } diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol index 643c6114d..6771c349f 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol @@ -46,6 +46,9 @@ interface IAdmin is IZkSyncHyperchainBase { /// @notice Set the transaction filterer function setTransactionFilterer(address _transactionFilterer) external; + /// @notice Allow EVM emulation on chain + function allowEvmEmulation() external returns (bytes32 canonicalTxHash); + /// @notice Perform the upgrade from the current protocol version with the corresponding upgrade data /// @param _protocolVersion The current protocol version from which upgrade is executed /// @param _cutData The diamond cut parameters that is executed in the upgrade @@ -105,4 +108,7 @@ interface IAdmin is IZkSyncHyperchainBase { /// @notice Emitted when the contract is unfrozen. event Unfreeze(); + + /// @notice The EVM emulator has been enabled + event EnableEvmEmulator(); } diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IMailbox.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IMailbox.sol index 9daffebcf..563fcb12e 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IMailbox.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IMailbox.sol @@ -95,6 +95,15 @@ interface IMailbox is IZkSyncHyperchainBase { address _refundRecipient ) external payable returns (bytes32 canonicalTxHash); + /// @notice Request execution of service L2 transaction from L1. + /// @dev Used for chain configuration. Can be called only by DiamondProxy itself. + /// @param _contractL2 The L2 receiver address + /// @param _l2Calldata The input of the L2 transaction + function requestL2ServiceTransaction( + address _contractL2, + bytes calldata _l2Calldata + ) external returns (bytes32 canonicalTxHash); + function bridgehubRequestL2Transaction( BridgehubL2TransactionRequest calldata _request ) external returns (bytes32 canonicalTxHash); diff --git a/l1-contracts/contracts/state-transition/l2-deps/AllowedBytecodeTypes.sol b/l1-contracts/contracts/state-transition/l2-deps/AllowedBytecodeTypes.sol deleted file mode 100644 index 2d6a3a136..000000000 --- a/l1-contracts/contracts/state-transition/l2-deps/AllowedBytecodeTypes.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -// We use a floating point pragma here so it can be used within other projects that interact with the zkSync ecosystem without using our exact pragma version. -pragma solidity ^0.8.21; - -/// @notice Defines what types of bytecode are allowed to be deployed on this chain -/// - `EraVm` means that only native contracts can be deployed -/// - `EraVmAndEVM` means that native contracts and EVM contracts can be deployed -enum AllowedBytecodeTypes { - EraVm, - EraVmAndEVM -} diff --git a/l1-contracts/contracts/state-transition/l2-deps/ISystemContext.sol b/l1-contracts/contracts/state-transition/l2-deps/ISystemContext.sol index cdc1140e7..d3244b74b 100644 --- a/l1-contracts/contracts/state-transition/l2-deps/ISystemContext.sol +++ b/l1-contracts/contracts/state-transition/l2-deps/ISystemContext.sol @@ -3,8 +3,5 @@ pragma solidity ^0.8.21; interface ISystemContext { - /// @notice Set the chain configuration. - /// @param _newChainId The chainId - /// @param _newAllowedBytecodeTypes The new allowed bytecode types mode. - function setChainConfiguration(uint256 _newChainId, uint256 _newAllowedBytecodeTypes) external; + function setChainId(uint256 _newChainId) external; } diff --git a/l1-contracts/deploy-scripts/EnableEvmEmulator.s.sol b/l1-contracts/deploy-scripts/EnableEvmEmulator.s.sol new file mode 100644 index 000000000..5e9d466ce --- /dev/null +++ b/l1-contracts/deploy-scripts/EnableEvmEmulator.s.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import {Script} from "forge-std/Script.sol"; + +import {IZkSyncHyperchain} from "contracts/state-transition/chain-interfaces/IZkSyncHyperchain.sol"; +import {IChainAdmin} from "contracts/governance/IChainAdmin.sol"; + +contract EnableEvmEmulator is Script { + // This function should be called by the owner to update token multiplier setter role + function chainAllowEvmEmulation(address chainAdmin, address target) public { + IChainAdmin admin = IChainAdmin(chainAdmin); + + vm.startBroadcast(); + admin.enableEvmEmulator(IZkSyncHyperchain(target)); + vm.stopBroadcast(); + } +} diff --git a/l1-contracts/deploy-scripts/PrepareZKChainRegistrationCalldata.s.sol b/l1-contracts/deploy-scripts/PrepareZKChainRegistrationCalldata.s.sol index a965704b6..c2d48f322 100644 --- a/l1-contracts/deploy-scripts/PrepareZKChainRegistrationCalldata.s.sol +++ b/l1-contracts/deploy-scripts/PrepareZKChainRegistrationCalldata.s.sol @@ -12,9 +12,7 @@ import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol" import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {L1SharedBridge} from "contracts/bridge/L1SharedBridge.sol"; import {IStateTransitionManager} from "contracts/state-transition/IStateTransitionManager.sol"; -import {AllowedBytecodeTypes} from "contracts/state-transition/l2-deps/AllowedBytecodeTypes.sol"; import {IGovernance} from "contracts/governance/IGovernance.sol"; -import {IChainAdmin} from "contracts/governance/IChainAdmin.sol"; import {Call} from "contracts/governance/Common.sol"; import {Utils} from "./Utils.sol"; @@ -276,12 +274,6 @@ contract PrepareZKChainRegistrationCalldataScript is Script { function prepareRegisterHyperchainCall() internal view returns (Call memory) { Bridgehub bridgehub = Bridgehub(ecosystem.bridgehub); - AllowedBytecodeTypes allowedBytecodeTypesMode = config.allowEvmEmulator - ? AllowedBytecodeTypes.EraVmAndEVM - : AllowedBytecodeTypes.EraVm; - - bytes memory diamondCutEncoded = abi.encode(config.diamondCutData); - bytes memory data = abi.encodeCall( bridgehub.createNewChain, ( @@ -290,7 +282,7 @@ contract PrepareZKChainRegistrationCalldataScript is Script { config.baseToken, config.bridgehubCreateNewChainSalt, config.chainAdmin, - abi.encode(diamondCutEncoded, allowedBytecodeTypesMode) + config.diamondCutData ) ); diff --git a/l1-contracts/deploy-scripts/RegisterHyperchain.s.sol b/l1-contracts/deploy-scripts/RegisterHyperchain.s.sol index 7dfa127d2..9a8c5f781 100644 --- a/l1-contracts/deploy-scripts/RegisterHyperchain.s.sol +++ b/l1-contracts/deploy-scripts/RegisterHyperchain.s.sol @@ -9,7 +9,6 @@ import {stdToml} from "forge-std/StdToml.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; import {IZkSyncHyperchain} from "contracts/state-transition/chain-interfaces/IZkSyncHyperchain.sol"; -import {AllowedBytecodeTypes} from "contracts/state-transition/l2-deps/AllowedBytecodeTypes.sol"; import {ValidatorTimelock} from "contracts/state-transition/ValidatorTimelock.sol"; import {Governance} from "contracts/governance/Governance.sol"; import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; @@ -157,14 +156,7 @@ contract RegisterHyperchainScript is Script { function registerHyperchain() internal { Bridgehub bridgehub = Bridgehub(config.bridgehub); - AllowedBytecodeTypes allowedBytecodeTypesMode = config.allowEvmEmulator - ? AllowedBytecodeTypes.EraVmAndEVM - : AllowedBytecodeTypes.EraVm; - - bytes memory initData = abi.encode(config.diamondCutData, allowedBytecodeTypesMode); - vm.recordLogs(); - bytes memory data = abi.encodeCall( bridgehub.createNewChain, ( @@ -173,7 +165,7 @@ contract RegisterHyperchainScript is Script { config.baseToken, config.bridgehubCreateNewChainSalt, msg.sender, - initData + config.diamondCutData ) ); diff --git a/l1-contracts/scripts/register-hyperchain.ts b/l1-contracts/scripts/register-hyperchain.ts index 587edec2e..485eb3579 100644 --- a/l1-contracts/scripts/register-hyperchain.ts +++ b/l1-contracts/scripts/register-hyperchain.ts @@ -107,10 +107,6 @@ async function main() { const tokenMultiplierSetterAddress = cmd.tokenMultiplierSetterAddress || ""; - const isEvmEmulatorSupported = !!cmd.allowEvmEmulation; - - console.log(`EVM emulator: ${isEvmEmulatorSupported ? "SUPPORTED" : " NOT SUPPORTED"}`); - await deployer.registerHyperchain( baseTokenAddress, cmd.validiumMode, @@ -119,13 +115,18 @@ async function main() { null, null, null, - useGovernance, - isEvmEmulatorSupported + useGovernance ); if (tokenMultiplierSetterAddress != "") { console.log(`Using token multiplier setter address: ${tokenMultiplierSetterAddress}`); await deployer.setTokenMultiplierSetterAddress(tokenMultiplierSetterAddress); } + + if (cmd.allowEvmEmulation) { + console.log("Allowing EVM emulation"); + await deployer.enableEvmEmulation(); + } + await deployer.transferAdminFromDeployerToChainAdmin(); }); diff --git a/l1-contracts/src.ts/deploy-process.ts b/l1-contracts/src.ts/deploy-process.ts index 1e1a00be2..e9622bfbe 100644 --- a/l1-contracts/src.ts/deploy-process.ts +++ b/l1-contracts/src.ts/deploy-process.ts @@ -80,8 +80,7 @@ export async function registerHyperchain( gasPrice: BigNumberish, baseTokenName?: string, chainId?: string, - useGovernance: boolean = false, - isEvmEmulatorSupported: boolean = false + useGovernance: boolean = false ) { const testnetTokens = getTokens(); @@ -100,7 +99,6 @@ export async function registerHyperchain( false, null, chainId, - useGovernance, - isEvmEmulatorSupported + useGovernance ); } diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index 7b96f6358..dfc16a810 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -734,8 +734,7 @@ export class Deployer { compareDiamondCutHash: boolean = false, nonce?, predefinedChainId?: string, - useGovernance: boolean = false, - isEvmEmulatorSupported: boolean = false + useGovernance: boolean = false ) { const gasLimit = 10_000_000; @@ -749,12 +748,6 @@ export class Deployer { const diamondCutData = await this.initialZkSyncHyperchainDiamondCut(extraFacets, compareDiamondCutHash); const diamondCutDataEncoded = new ethers.utils.AbiCoder().encode([DIAMOND_CUT_DATA_ABI_STRING], [diamondCutData]); - const allowedBytecodeTypesMode = isEvmEmulatorSupported ? 1 : 0; - const initData = new ethers.utils.AbiCoder().encode( - ["bytes", "uint256"], - [diamondCutDataEncoded, allowedBytecodeTypesMode] - ); - const receipt = await this.executeDirectOrGovernance( useGovernance, bridgehub, @@ -765,7 +758,7 @@ export class Deployer { baseTokenAddress, Date.now(), admin, - initData, + diamondCutDataEncoded, ], 0, { @@ -871,6 +864,17 @@ export class Deployer { } } + public async enableEvmEmulation() { + const stm = this.stateTransitionManagerContract(this.deployWallet); + const diamondProxyAddress = await stm.getHyperchain(this.chainId); + const hyperchain = IZkSyncHyperchainFactory.connect(diamondProxyAddress, this.deployWallet); + + const receipt = await (await hyperchain.allowEvmEmulation()).wait(); + if (this.verbose) { + console.log(`EVM emulation allowed, gas used: ${receipt.gasUsed.toString()}`); + } + } + public async transferAdminFromDeployerToChainAdmin() { const stm = this.stateTransitionManagerContract(this.deployWallet); const diamondProxyAddress = await stm.getHyperchain(this.chainId); diff --git a/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol index 32fd846db..43826e6ac 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol @@ -8,7 +8,6 @@ import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; import {ChainCreationParams} from "contracts/state-transition/IStateTransitionManager.sol"; -import {AllowedBytecodeTypes} from "contracts/state-transition/l2-deps/AllowedBytecodeTypes.sol"; import {L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter} from "contracts/bridgehub/IBridgehub.sol"; import {DummyStateTransitionManagerWBH} from "contracts/dev-contracts/test/DummyStateTransitionManagerWithBridgeHubAddress.sol"; import {DummyHyperchain} from "contracts/dev-contracts/test/DummyHyperchain.sol"; @@ -662,8 +661,7 @@ contract ExperimentalBridgeTest is Test { isFreezable, mockSelectors, mockInitAddress, - mockInitCalldata, - false + mockInitCalldata ); // bridgeHub.createNewChain => stateTransitionManager.createNewChain => this function sets the stateTransition mapping @@ -1433,8 +1431,7 @@ contract ExperimentalBridgeTest is Test { bool isFreezable, bytes4[] memory mockSelectors, address, //mockInitAddress, - bytes memory, //mockInitCalldata - bool allowEvmEmulation + bytes memory //mockInitCalldata ) internal returns (bytes memory) { bytes4[] memory singleSelector = new bytes4[](1); singleSelector[0] = bytes4(0xabcdef12); @@ -1468,13 +1465,7 @@ contract ExperimentalBridgeTest is Test { mockSTM.setChainCreationParams(params); - bytes memory diamondCutEncoded = abi.encode(diamondCutData); - - AllowedBytecodeTypes allowedBytecodeTypesMode = allowEvmEmulation - ? AllowedBytecodeTypes.EraVmAndEVM - : AllowedBytecodeTypes.EraVm; - - return abi.encode(diamondCutEncoded, allowedBytecodeTypesMode); + return abi.encode(diamondCutData); } function _setUpHyperchainForChainId(uint256 mockChainId) internal returns (uint256 mockChainIdInRange) { diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/CreateNewChain.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/CreateNewChain.t.sol index 48cbb95b2..bce302a9b 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/CreateNewChain.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/CreateNewChain.t.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.24; import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; -import {AllowedBytecodeTypes} from "contracts/state-transition/l2-deps/AllowedBytecodeTypes.sol"; import {Unauthorized, HashMismatch} from "contracts/common/L1ContractErrors.sol"; contract createNewChainTest is StateTransitionManagerTest { @@ -34,7 +33,7 @@ contract createNewChainTest is StateTransitionManagerTest { _baseToken: baseToken, _sharedBridge: sharedBridge, _admin: admin, - _inputData: getCreateInputData(initialDiamondCutData, false) + _diamondCut: abi.encode(initialDiamondCutData) }); } diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol index b13b1f920..80195e850 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol @@ -17,7 +17,6 @@ import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol import {GenesisUpgrade} from "contracts/upgrades/GenesisUpgrade.sol"; import {InitializeDataNewChain} from "contracts/state-transition/chain-interfaces/IDiamondInit.sol"; import {StateTransitionManager} from "contracts/state-transition/StateTransitionManager.sol"; -import {AllowedBytecodeTypes} from "contracts/state-transition/l2-deps/AllowedBytecodeTypes.sol"; import {StateTransitionManagerInitializeData, ChainCreationParams} from "contracts/state-transition/IStateTransitionManager.sol"; import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; import {ZeroAddress} from "contracts/common/L1ContractErrors.sol"; @@ -129,18 +128,6 @@ contract StateTransitionManagerTest is Test { return Diamond.DiamondCutData({facetCuts: facetCuts, initAddress: _diamondInit, initCalldata: initCalldata}); } - function getCreateInputData( - Diamond.DiamondCutData memory _diamondCut, - bool allowEvmEmulator - ) internal view returns (bytes memory) { - bytes memory diamondCutEncoded = abi.encode(_diamondCut); - AllowedBytecodeTypes allowedBytecodeTypesMode = allowEvmEmulator - ? AllowedBytecodeTypes.EraVmAndEVM - : AllowedBytecodeTypes.EraVm; - - return abi.encode(diamondCutEncoded, allowedBytecodeTypesMode); - } - function createNewChain(Diamond.DiamondCutData memory _diamondCut) internal { _createNewChain(_diamondCut, false); } @@ -158,7 +145,7 @@ contract StateTransitionManagerTest is Test { _baseToken: baseToken, _sharedBridge: sharedBridge, _admin: newChainAdmin, - _inputData: getCreateInputData(_diamondCut, allowEvmEmulator) + _diamondCut: abi.encode(_diamondCut) }); } diff --git a/system-contracts/SystemContractsHashes.json b/system-contracts/SystemContractsHashes.json index f8c17571a..1bc5f5115 100644 --- a/system-contracts/SystemContractsHashes.json +++ b/system-contracts/SystemContractsHashes.json @@ -3,49 +3,49 @@ "contractName": "AccountCodeStorage", "bytecodePath": "zkout/AccountCodeStorage.sol/AccountCodeStorage.json", "sourceCodePath": "contracts-preprocessed/AccountCodeStorage.sol", - "bytecodeHash": "0x0100007317994a1fc65998cd916c99f52951e58cefedce3c330aacf7fd20dd94", + "bytecodeHash": "0x0100007360eb189d11c57c8b45bdd29ae2a996786b55e21847fbbe8e7b285ce2", "sourceCodeHash": "0xfdac12f45b5cfd4abd12923206f2d6f253d11a6624783e079b55e975d573ceb6" }, { "contractName": "BootloaderUtilities", "bytecodePath": "zkout/BootloaderUtilities.sol/BootloaderUtilities.json", "sourceCodePath": "contracts-preprocessed/BootloaderUtilities.sol", - "bytecodeHash": "0x010006f3eaa43c2aeece380d750965b772ae2d8874f942fe598445977118fd23", + "bytecodeHash": "0x010006f3bacfc75db6a91f2f2c05485c8f6d7ed0dd259b5d1a7de5bb5f0170a2", "sourceCodeHash": "0x10f30ac1a7098c7fddec2659ac43422783e8d3fdde02a3ba4d3ff45d451d7001" }, { "contractName": "ComplexUpgrader", "bytecodePath": "zkout/ComplexUpgrader.sol/ComplexUpgrader.json", "sourceCodePath": "contracts-preprocessed/ComplexUpgrader.sol", - "bytecodeHash": "0x010000470769b6a1ab43e898daf381c7df9753c51e8001e06b6daa39357f42a8", + "bytecodeHash": "0x01000047cfddb4597ec1fcff6ce1b13c404b128f8c7349943f00c8e702fcecd5", "sourceCodeHash": "0x796046a914fb676ba2bbd337b2924311ee2177ce54571c18a2c3945755c83614" }, { "contractName": "Compressor", "bytecodePath": "zkout/Compressor.sol/Compressor.json", "sourceCodePath": "contracts-preprocessed/Compressor.sol", - "bytecodeHash": "0x0100013f2c94736cbc8e992f7e84df5e01a64d15bf2443c2dda5bee7476c9575", + "bytecodeHash": "0x0100013f5b38355d5720b282db0cf3869b78e2c8d631550ef7a0f33e5757f6be", "sourceCodeHash": "0xc6f7cd8b21aae52ed3dd5083c09b438a7af142a4ecda6067c586770e8be745a5" }, { "contractName": "ContractDeployer", "bytecodePath": "zkout/ContractDeployer.sol/ContractDeployer.json", "sourceCodePath": "contracts-preprocessed/ContractDeployer.sol", - "bytecodeHash": "0x01000673f8b088c2c27ab36f025b222a908368fd22b461bf97b12784753b2fea", - "sourceCodeHash": "0xbb9a2717e6fd1da15df4ef1862d2e55e661547f973478bb1fc3cab1ea3cca575" + "bytecodeHash": "0x01000655cfaee8879d6d5f020e8cac9b00e5830a8aef6d01f9406b4583fa4bd8", + "sourceCodeHash": "0x22f96404a0527177f635c81315349ee274806e7462c5e5ae087a005f991bc157" }, { "contractName": "Create2Factory", "bytecodePath": "zkout/Create2Factory.sol/Create2Factory.json", "sourceCodePath": "contracts-preprocessed/Create2Factory.sol", - "bytecodeHash": "0x0100003ff00c4656ed039bac6da4b2f56c01acf662faff586f7032d33de48634", + "bytecodeHash": "0x0100003f53ff30da1f9c344e63c5a031affbf8af76baf0a92c977fefc8665a0c", "sourceCodeHash": "0x114d9322a9ca654989f3e0b3b21f1311dbc4db84f443d054cd414f6414d84de3" }, { "contractName": "DefaultAccount", "bytecodePath": "zkout/DefaultAccount.sol/DefaultAccount.json", "sourceCodePath": "contracts-preprocessed/DefaultAccount.sol", - "bytecodeHash": "0x0100050bcb0eb0f55ec0821967b509b2ae60fa52ff94f0f934b4de464e2f3a5d", + "bytecodeHash": "0x0100050b06623d83e54b5752821bc70b493f31989359002205a2f9264dba83f4", "sourceCodeHash": "0x300c864fcb3bc6a562875c7b1d83df15d466515da0a878a426b1aaeac26f3656" }, { @@ -59,57 +59,57 @@ "contractName": "ImmutableSimulator", "bytecodePath": "zkout/ImmutableSimulator.sol/ImmutableSimulator.json", "sourceCodePath": "contracts-preprocessed/ImmutableSimulator.sol", - "bytecodeHash": "0x010000338218e48deb166b7e6486e558038239530c8f30bdbc0b15dffc671aa6", + "bytecodeHash": "0x0100003345f92c227817a46a1cb0ee12cac25cb8516251a028f24d6cb1feb77b", "sourceCodeHash": "0x9659e69f7db09e8f60a8bb95314b1ed26afcc689851665cf27f5408122f60c98" }, { "contractName": "KnownCodesStorage", "bytecodePath": "zkout/KnownCodesStorage.sol/KnownCodesStorage.json", "sourceCodePath": "contracts-preprocessed/KnownCodesStorage.sol", - "bytecodeHash": "0x010000cd7ef93ae3e09683ca91c705700694f3f9bd25f5d0d5fecd38d667eccf", + "bytecodeHash": "0x010000cde320e4772a0ef635bff69f07fd908a6140480cf2c17f2cf0214d86a3", "sourceCodeHash": "0x851fb5e170dfde39f1f9bc74654ec0b8f8f1d4c2fb20c06c77844c1e3ee0659a" }, { "contractName": "L1Messenger", "bytecodePath": "zkout/L1Messenger.sol/L1Messenger.json", "sourceCodePath": "contracts-preprocessed/L1Messenger.sol", - "bytecodeHash": "0x01000263345d82f95b6533501ee84340770418287866931d64899423815527f8", + "bytecodeHash": "0x0100026320ad1a050ca08117bb57a06684839b3ab3f3095e17deb1ebeb53d4ac", "sourceCodeHash": "0xa8768fdaac6d8804782f14e2a51bbe2b6be31dee9103b6d02d149ea8dc46eb6a" }, { "contractName": "L2BaseToken", "bytecodePath": "zkout/L2BaseToken.sol/L2BaseToken.json", "sourceCodePath": "contracts-preprocessed/L2BaseToken.sol", - "bytecodeHash": "0x010000dbc44fbd6fd01e930a4cbbb59a17b285bd86fc5dbe11f59d1f375f6286", + "bytecodeHash": "0x010000db58a5798e8cf03299e84c32288bd535f03140112d8461b84ec164887a", "sourceCodeHash": "0xdea518b1ea16718b0f0ec6155b227a8bc8f51374a9eebf7bc17cfe84433df740" }, { "contractName": "MsgValueSimulator", "bytecodePath": "zkout/MsgValueSimulator.sol/MsgValueSimulator.json", "sourceCodePath": "contracts-preprocessed/MsgValueSimulator.sol", - "bytecodeHash": "0x010000594d78e2e2a0f0fa224c8ba6b3fd8ab8b283bff7420c1b565fe8a61ee7", + "bytecodeHash": "0x0100005964d673fb20cd2c77df6f9aa4c178bd8ffa54eb582505df704b1f3b5b", "sourceCodeHash": "0x082f3dcbc2fe4d93706c86aae85faa683387097d1b676e7ebd00f71ee0f13b71" }, { "contractName": "NonceHolder", "bytecodePath": "zkout/NonceHolder.sol/NonceHolder.json", "sourceCodePath": "contracts-preprocessed/NonceHolder.sol", - "bytecodeHash": "0x010000cf17e28b461edeaf764b45bd824cf6b1a653478624bfa361c813a61b5d", + "bytecodeHash": "0x010000cfa9aff9356b0ef66e9ca32df21ae4bcbb2a8d03d3acf74e2d060a3028", "sourceCodeHash": "0xcd0c0366effebf2c98c58cf96322cc242a2d1c675620ef5514b7ed1f0a869edc" }, { "contractName": "PubdataChunkPublisher", "bytecodePath": "zkout/PubdataChunkPublisher.sol/PubdataChunkPublisher.json", "sourceCodePath": "contracts-preprocessed/PubdataChunkPublisher.sol", - "bytecodeHash": "0x01000041dab57304eb3d41b96948d580eb388f303bdf058c15b71afcef0ffac0", + "bytecodeHash": "0x010000419856be032b7143f70782d129e260d6e99e6bbe7c4787884ccd949ce9", "sourceCodeHash": "0xd7161e2c8092cf57b43c6220bc605c0e7e540bddcde1af24e2d90f75633b098e" }, { "contractName": "SystemContext", "bytecodePath": "zkout/SystemContext.sol/SystemContext.json", "sourceCodePath": "contracts-preprocessed/SystemContext.sol", - "bytecodeHash": "0x010001c54c88a8fb2427458c7b4caddc0c7927e396a60a5e690408d36a25eceb", - "sourceCodeHash": "0xe2f6eb015d260aafe9405b28ef3ec27921add4de7f329b7ef61e0aa6c9365e29" + "bytecodeHash": "0x010001a5305c969f525ba5afc3c7496c520d77ca71bdebc9c3b8e71c7a0d3364", + "sourceCodeHash": "0xaf04a52c660f48de04c29b6c88632b4e002e94f58e0f8de034fd73959671c984" }, { "contractName": "EventWriter", @@ -122,8 +122,8 @@ "contractName": "EvmEmulator", "bytecodePath": "zkout/EvmEmulator.yul/contracts-preprocessed/EvmEmulator.yul.json", "sourceCodePath": "contracts-preprocessed/EvmEmulator.yul", - "bytecodeHash": "0x01000c0750cd078b79c3bcf0ac3074e75a6824fe05e0e563e6df442feb879847", - "sourceCodeHash": "0x86296ecb81fc9edf5aac32613dfd645b538535e264e02a12ec7cdc751018de5d" + "bytecodeHash": "0x01000bef3afffa6dcca82a514f5f388101dd3498e9117be2073975d8ae791970", + "sourceCodeHash": "0x7cad60f966f85be729468ca5f3500ffd4a4b2d0ef6df5ca0656ac7f46c0d2402" }, { "contractName": "EvmGasManager", diff --git a/system-contracts/contracts/Constants.sol b/system-contracts/contracts/Constants.sol index 36309b111..fbaabc091 100644 --- a/system-contracts/contracts/Constants.sol +++ b/system-contracts/contracts/Constants.sol @@ -167,4 +167,6 @@ uint256 constant MAX_NUMBER_OF_BLOBS = 6; /// @dev Marker of EraVM bytecode uint8 constant ERA_VM_BYTECODE_FLAG = 1; /// @dev Marker of EVM bytecode -uint8 constant EVM_BYTECODE_FLAG = 2; \ No newline at end of file +uint8 constant EVM_BYTECODE_FLAG = 2; + +address constant SERVICE_CALL_PSEUDO_CALLER = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; \ No newline at end of file diff --git a/system-contracts/contracts/ContractDeployer.sol b/system-contracts/contracts/ContractDeployer.sol index 3b36ed732..d00b50515 100644 --- a/system-contracts/contracts/ContractDeployer.sol +++ b/system-contracts/contracts/ContractDeployer.sol @@ -4,13 +4,13 @@ pragma solidity 0.8.24; import {ImmutableData} from "./interfaces/IImmutableSimulator.sol"; import {IContractDeployer} from "./interfaces/IContractDeployer.sol"; -import {CREATE2_PREFIX, CREATE_PREFIX, NONCE_HOLDER_SYSTEM_CONTRACT, ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT, FORCE_DEPLOYER, MAX_SYSTEM_CONTRACT_ADDRESS, KNOWN_CODE_STORAGE_CONTRACT, BASE_TOKEN_SYSTEM_CONTRACT, IMMUTABLE_SIMULATOR_SYSTEM_CONTRACT, COMPLEX_UPGRADER_CONTRACT, SYSTEM_CONTEXT_CONTRACT} from "./Constants.sol"; +import {CREATE2_PREFIX, CREATE_PREFIX, NONCE_HOLDER_SYSTEM_CONTRACT, ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT, FORCE_DEPLOYER, MAX_SYSTEM_CONTRACT_ADDRESS, KNOWN_CODE_STORAGE_CONTRACT, BASE_TOKEN_SYSTEM_CONTRACT, IMMUTABLE_SIMULATOR_SYSTEM_CONTRACT, COMPLEX_UPGRADER_CONTRACT, SERVICE_CALL_PSEUDO_CALLER} from "./Constants.sol"; import {Utils} from "./libraries/Utils.sol"; import {EfficientCall} from "./libraries/EfficientCall.sol"; import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; import {SystemContractBase} from "./abstract/SystemContractBase.sol"; -import {Unauthorized, InvalidAllowedBytecodeTypesMode, InvalidNonceOrderingChange, ValueMismatch, EmptyBytes32, EVMEmulationNotSupported, NotAllowedToDeployInKernelSpace, HashIsNonZero, NonEmptyAccount, UnknownCodeHash, NonEmptyMsgValue} from "./SystemContractErrors.sol"; +import {Unauthorized, InvalidNonceOrderingChange, ValueMismatch, EmptyBytes32, EVMEmulationNotSupported, NotAllowedToDeployInKernelSpace, HashIsNonZero, NonEmptyAccount, UnknownCodeHash, NonEmptyMsgValue} from "./SystemContractErrors.sol"; /** * @author Matter Labs @@ -360,23 +360,16 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { /// @notice Changes what types of bytecodes are allowed to be deployed on the chain. Can be used only during upgrades. /// @param newAllowedBytecodeTypes The new allowed bytecode types mode. - function setAllowedBytecodeTypesToDeploy(uint256 newAllowedBytecodeTypes) external { + function setAllowedBytecodeTypesToDeploy(AllowedBytecodeTypes newAllowedBytecodeTypes) external { if ( msg.sender != FORCE_DEPLOYER && msg.sender != address(COMPLEX_UPGRADER_CONTRACT) && - msg.sender != address(SYSTEM_CONTEXT_CONTRACT) + msg.sender != SERVICE_CALL_PSEUDO_CALLER ) { revert Unauthorized(msg.sender); } - if ( - newAllowedBytecodeTypes != uint256(AllowedBytecodeTypes.EraVm) && - newAllowedBytecodeTypes != uint256(AllowedBytecodeTypes.EraVmAndEVM) - ) { - revert InvalidAllowedBytecodeTypesMode(); - } - - if (uint256(_getAllowedBytecodeTypesMode()) != newAllowedBytecodeTypes) { + if (_getAllowedBytecodeTypesMode() != newAllowedBytecodeTypes) { assembly { sstore(ALLOWED_BYTECODE_TYPES_MODE_SLOT, newAllowedBytecodeTypes) } diff --git a/system-contracts/contracts/SystemContext.sol b/system-contracts/contracts/SystemContext.sol index 10eef6901..2ce75419d 100644 --- a/system-contracts/contracts/SystemContext.sol +++ b/system-contracts/contracts/SystemContext.sol @@ -8,7 +8,7 @@ import {ISystemContext} from "./interfaces/ISystemContext.sol"; import {SystemContractBase} from "./abstract/SystemContractBase.sol"; import {ISystemContextDeprecated} from "./interfaces/ISystemContextDeprecated.sol"; import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; -import {BOOTLOADER_FORMAL_ADDRESS, DEPLOYER_SYSTEM_CONTRACT, SystemLogKey} from "./Constants.sol"; +import {BOOTLOADER_FORMAL_ADDRESS, SystemLogKey} from "./Constants.sol"; /** * @author Matter Labs @@ -90,17 +90,6 @@ contract SystemContext is ISystemContext, ISystemContextDeprecated, SystemContra chainId = _newChainId; } - /// @notice Set the chain configuration. - /// @param _newChainId The chainId - /// @param _newAllowedBytecodeTypes The new allowed bytecode types mode. - function setChainConfiguration( - uint256 _newChainId, - uint256 _newAllowedBytecodeTypes - ) external onlyCallFromForceDeployer { - chainId = _newChainId; - DEPLOYER_SYSTEM_CONTRACT.setAllowedBytecodeTypesToDeploy(_newAllowedBytecodeTypes); - } - /// @notice Number of current transaction in block. uint16 public txNumberInBlock; diff --git a/system-contracts/contracts/SystemContractErrors.sol b/system-contracts/contracts/SystemContractErrors.sol index 3f0f8c257..2255fafc1 100644 --- a/system-contracts/contracts/SystemContractErrors.sol +++ b/system-contracts/contracts/SystemContractErrors.sol @@ -54,8 +54,6 @@ error IndexSizeError(); error InsufficientFunds(uint256 required, uint256 actual); // 0x1c26714c error InsufficientGas(); -// 0x8322ca79 -error InvalidAllowedBytecodeTypesMode(); // 0xae962d4e error InvalidCall(); // 0x6a84bc39 diff --git a/system-contracts/contracts/interfaces/IContractDeployer.sol b/system-contracts/contracts/interfaces/IContractDeployer.sol index 83bf2392e..fd0d8dec1 100644 --- a/system-contracts/contracts/interfaces/IContractDeployer.sol +++ b/system-contracts/contracts/interfaces/IContractDeployer.sol @@ -112,7 +112,7 @@ interface IContractDeployer { /// @notice Returns keccak of EVM bytecode at address if it is an EVM contract. Returns bytes32(0) if it isn't a EVM contract. function evmCodeHash(address) external view returns (bytes32); - /// @notice Changes what types of bytecodes are allowed to be deployed on the chain. Can be used only during upgrades. + /// @notice Changes what types of bytecodes are allowed to be deployed on the chain. /// @param newAllowedBytecodeTypes The new allowed bytecode types mode. - function setAllowedBytecodeTypesToDeploy(uint256 newAllowedBytecodeTypes) external; + function setAllowedBytecodeTypesToDeploy(AllowedBytecodeTypes newAllowedBytecodeTypes) external; } From e7aa3a96a0129d71ac58ffd9fd7ad876b69dc209 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 18 Dec 2024 12:39:12 +0100 Subject: [PATCH 30/62] fix(EVM): Simplify and optimize out-of-bounds truncation in `CODECOPY` (#1156) --- system-contracts/contracts/EvmEmulator.yul | 18 ++++++++---------- .../evm-emulator/EvmEmulatorLoop.template.yul | 9 ++++----- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index ea8b65d34..feefd03f9 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -1749,7 +1749,6 @@ object "EvmEmulator" { ip := add(ip, 1) } case 0x39 { // OP_CODECOPY - evmGasLeft := chargeGas(evmGasLeft, 3) let dstOffset, sourceOffset, len @@ -1774,14 +1773,14 @@ object "EvmEmulator" { sourceOffset := add(sourceOffset, BYTECODE_OFFSET()) - if gt(sourceOffset, MEM_LEN_OFFSET()) { - sourceOffset := MEM_LEN_OFFSET() + if gt(sourceOffset, bytecodeEndOffset) { + sourceOffset := bytecodeEndOffset } // Check bytecode out-of-bounds access let truncatedLen := len - if gt(add(sourceOffset, len), MEM_LEN_OFFSET()) { - truncatedLen := sub(MEM_LEN_OFFSET(), sourceOffset) // truncate + if gt(add(sourceOffset, len), bytecodeEndOffset) { + truncatedLen := sub(bytecodeEndOffset, sourceOffset) // truncate $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, truncatedLen), sub(len, truncatedLen)) // pad with zeroes any out-of-bounds } @@ -4889,7 +4888,6 @@ object "EvmEmulator" { ip := add(ip, 1) } case 0x39 { // OP_CODECOPY - evmGasLeft := chargeGas(evmGasLeft, 3) let dstOffset, sourceOffset, len @@ -4914,14 +4912,14 @@ object "EvmEmulator" { sourceOffset := add(sourceOffset, BYTECODE_OFFSET()) - if gt(sourceOffset, MEM_LEN_OFFSET()) { - sourceOffset := MEM_LEN_OFFSET() + if gt(sourceOffset, bytecodeEndOffset) { + sourceOffset := bytecodeEndOffset } // Check bytecode out-of-bounds access let truncatedLen := len - if gt(add(sourceOffset, len), MEM_LEN_OFFSET()) { - truncatedLen := sub(MEM_LEN_OFFSET(), sourceOffset) // truncate + if gt(add(sourceOffset, len), bytecodeEndOffset) { + truncatedLen := sub(bytecodeEndOffset, sourceOffset) // truncate $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, truncatedLen), sub(len, truncatedLen)) // pad with zeroes any out-of-bounds } diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index b5d956b44..023b6a747 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -407,7 +407,6 @@ for { } true { } { ip := add(ip, 1) } case 0x39 { // OP_CODECOPY - evmGasLeft := chargeGas(evmGasLeft, 3) let dstOffset, sourceOffset, len @@ -432,14 +431,14 @@ for { } true { } { sourceOffset := add(sourceOffset, BYTECODE_OFFSET()) - if gt(sourceOffset, MEM_LEN_OFFSET()) { - sourceOffset := MEM_LEN_OFFSET() + if gt(sourceOffset, bytecodeEndOffset) { + sourceOffset := bytecodeEndOffset } // Check bytecode out-of-bounds access let truncatedLen := len - if gt(add(sourceOffset, len), MEM_LEN_OFFSET()) { - truncatedLen := sub(MEM_LEN_OFFSET(), sourceOffset) // truncate + if gt(add(sourceOffset, len), bytecodeEndOffset) { + truncatedLen := sub(bytecodeEndOffset, sourceOffset) // truncate $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, truncatedLen), sub(len, truncatedLen)) // pad with zeroes any out-of-bounds } From c396c03ec85ef18bffce73776789e6812a7059b4 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 19 Dec 2024 10:54:12 +0100 Subject: [PATCH 31/62] fix(EVM): Store unpadded EVM bytecode length in versioned bytecode hash (updated) (#1157) --- .../contracts/ContractDeployer.sol | 22 ++- system-contracts/contracts/EvmEmulator.yul | 137 +++++++----------- .../contracts/KnownCodesStorage.sol | 4 +- .../contracts/SystemContractErrors.sol | 3 +- .../interfaces/IKnownCodesStorage.sol | 2 +- .../contracts/libraries/Utils.sol | 22 ++- .../contracts/precompiles/CodeOracle.yul | 19 ++- .../evm-emulator/EvmEmulator.template.yul | 13 +- .../EvmEmulatorFunctions.template.yul | 48 ++---- .../evm-emulator/EvmEmulatorLoop.template.yul | 14 +- .../test/KnownCodesStorage.spec.ts | 11 +- 11 files changed, 142 insertions(+), 153 deletions(-) diff --git a/system-contracts/contracts/ContractDeployer.sol b/system-contracts/contracts/ContractDeployer.sol index d00b50515..f909e3fb0 100644 --- a/system-contracts/contracts/ContractDeployer.sol +++ b/system-contracts/contracts/ContractDeployer.sol @@ -250,7 +250,12 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { address _newAddress, bytes calldata _initCode ) external payable onlySystemCallFromEvmEmulator returns (uint256, address) { - uint256 constructorReturnEvmGas = _performDeployOnAddressEVM(msg.sender, _newAddress, AccountAbstractionVersion.None, _initCode); + uint256 constructorReturnEvmGas = _performDeployOnAddressEVM( + msg.sender, + _newAddress, + AccountAbstractionVersion.None, + _initCode + ); return (constructorReturnEvmGas, _newAddress); } @@ -584,25 +589,26 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { _isSystem: false }); - // Returned data bytes have structure: bytecode.constructorReturnEvmGas + uint256 evmBytecodeLen; + // Returned data bytes have structure: paddedBytecode.evmBytecodeLen.constructorReturnEvmGas assembly { let dataLen := mload(paddedBytecode) + evmBytecodeLen := mload(add(paddedBytecode, sub(dataLen, 0x20))) constructorReturnEvmGas := mload(add(paddedBytecode, dataLen)) - mstore(paddedBytecode, sub(dataLen, 0x20)) // shrink paddedBytecode + mstore(paddedBytecode, sub(dataLen, 0x40)) // shrink paddedBytecode } - bytes32 versionedCodeHash = KNOWN_CODE_STORAGE_CONTRACT.publishEVMBytecode(paddedBytecode); - ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.storeAccountConstructedCodeHash(_newAddress, versionedCodeHash); + bytes32 versionedBytecodeHash = KNOWN_CODE_STORAGE_CONTRACT.publishEVMBytecode(evmBytecodeLen, paddedBytecode); + ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.storeAccountConstructedCodeHash(_newAddress, versionedBytecodeHash); bytes32 evmBytecodeHash; assembly { - let bytecodeLen := mload(add(paddedBytecode, 0x20)) - evmBytecodeHash := keccak256(add(paddedBytecode, 0x40), bytecodeLen) + evmBytecodeHash := keccak256(add(paddedBytecode, 0x20), evmBytecodeLen) } _setEvmCodeHash(_newAddress, evmBytecodeHash); - emit ContractDeployed(_sender, versionedCodeHash, _newAddress); + emit ContractDeployed(_sender, versionedBytecodeHash, _newAddress); } function _setEvmCodeHash(address _address, bytes32 _hash) internal { diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index feefd03f9..9c6e62fef 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -21,16 +21,14 @@ object "EvmEmulator" { copyActivePtrData(BYTECODE_OFFSET(), 0, size) } - function padBytecode(offset, len) -> blobOffset, blobLen { - blobOffset := sub(offset, 32) + function padBytecode(offset, len) -> blobLen { let trueLastByte := add(offset, len) - mstore(blobOffset, len) // clearing out additional bytes mstore(trueLastByte, 0) mstore(add(trueLastByte, 32), 0) - blobLen := add(len, 32) + blobLen := len if iszero(eq(mod(blobLen, 32), 0)) { blobLen := add(blobLen, sub(32, mod(blobLen, 32))) @@ -411,61 +409,41 @@ object "EvmEmulator" { // Basically performs an extcodecopy, while returning the length of the copied bytecode. function fetchDeployedCode(addr, dstOffset, srcOffset, len) -> copiedLen { - let codeHash := getRawCodeHash(addr) - mstore(0, codeHash) + let rawCodeHash := getRawCodeHash(addr) + mstore(0, rawCodeHash) let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) // it fails if we don't have any code deployed at this address if success { - returndatacopy(0, 0, 32) - // The first word of returndata is the true length of the bytecode - let codeLen := mload(0) + // The length of the bytecode is encoded in versioned bytecode hash + let codeLen := and(shr(224, rawCodeHash), 0xffff) + + if eq(shr(248, rawCodeHash), 1) { + // For native zkVM contracts length encoded in words, not bytes + codeLen := shl(5, codeLen) // * 32 + } if gt(len, codeLen) { len := codeLen } - let shiftedSrcOffset := add(32, srcOffset) // first 32 bytes is length - let _returndatasize := returndatasize() - if gt(shiftedSrcOffset, _returndatasize) { - shiftedSrcOffset := _returndatasize + if gt(srcOffset, _returndatasize) { + srcOffset := _returndatasize } - if gt(add(len, shiftedSrcOffset), _returndatasize) { - len := sub(_returndatasize, shiftedSrcOffset) + if gt(add(len, srcOffset), _returndatasize) { + len := sub(_returndatasize, srcOffset) } if len { - returndatacopy(dstOffset, shiftedSrcOffset, len) + returndatacopy(dstOffset, srcOffset, len) } copiedLen := len } } - // Returns the length of the EVM bytecode. - function fetchDeployedEvmCodeLen(addr) -> codeLen { - let codeHash := getRawCodeHash(addr) - mstore(0, codeHash) - - let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) - - switch iszero(success) - case 1 { - // The code oracle call can only fail in the case where the contract - // we are querying is the current one executing and it has not yet been - // deployed, i.e., if someone calls codesize (or extcodesize(address())) - // inside the constructor. In that case, code length is zero. - codeLen := 0 - } - default { - // The first word is the true length of the bytecode - returndatacopy(0, 0, 32) - codeLen := mload(0) - } - } - function getMax(a, b) -> max { max := b if gt(a, b) { @@ -1809,9 +1787,17 @@ object "EvmEmulator" { evmGasLeft := chargeGas(evmGasLeft, 2500) } - switch isEvmContract(addr) - case 0 { stackHead := extcodesize(addr) } - default { stackHead := fetchDeployedEvmCodeLen(addr) } + let rawCodeHash := getRawCodeHash(addr) + switch shr(248, rawCodeHash) + case 1 { + stackHead := extcodesize(addr) + } + case 2 { + stackHead := and(shr(224, rawCodeHash), 0xffff) + } + default { + stackHead := 0 + } ip := add(ip, 1) } @@ -3175,11 +3161,12 @@ object "EvmEmulator" { gasToReturn := validateBytecodeAndChargeGas(offset, len, gasToReturn) - offset, len := padBytecode(offset, len) + let blobLen := padBytecode(offset, len) - mstore(add(offset, len), gasToReturn) + mstore(add(offset, blobLen), len) + mstore(add(offset, add(32, blobLen)), gasToReturn) - verbatim_2i_0o("return_deployed", offset, add(len, 32)) + verbatim_2i_0o("return_deployed", offset, add(blobLen, 64)) } object "EvmEmulator_deployed" { code { @@ -3550,61 +3537,41 @@ object "EvmEmulator" { // Basically performs an extcodecopy, while returning the length of the copied bytecode. function fetchDeployedCode(addr, dstOffset, srcOffset, len) -> copiedLen { - let codeHash := getRawCodeHash(addr) - mstore(0, codeHash) + let rawCodeHash := getRawCodeHash(addr) + mstore(0, rawCodeHash) let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) // it fails if we don't have any code deployed at this address if success { - returndatacopy(0, 0, 32) - // The first word of returndata is the true length of the bytecode - let codeLen := mload(0) + // The length of the bytecode is encoded in versioned bytecode hash + let codeLen := and(shr(224, rawCodeHash), 0xffff) + + if eq(shr(248, rawCodeHash), 1) { + // For native zkVM contracts length encoded in words, not bytes + codeLen := shl(5, codeLen) // * 32 + } if gt(len, codeLen) { len := codeLen } - let shiftedSrcOffset := add(32, srcOffset) // first 32 bytes is length - let _returndatasize := returndatasize() - if gt(shiftedSrcOffset, _returndatasize) { - shiftedSrcOffset := _returndatasize + if gt(srcOffset, _returndatasize) { + srcOffset := _returndatasize } - if gt(add(len, shiftedSrcOffset), _returndatasize) { - len := sub(_returndatasize, shiftedSrcOffset) + if gt(add(len, srcOffset), _returndatasize) { + len := sub(_returndatasize, srcOffset) } if len { - returndatacopy(dstOffset, shiftedSrcOffset, len) + returndatacopy(dstOffset, srcOffset, len) } copiedLen := len } } - // Returns the length of the EVM bytecode. - function fetchDeployedEvmCodeLen(addr) -> codeLen { - let codeHash := getRawCodeHash(addr) - mstore(0, codeHash) - - let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) - - switch iszero(success) - case 1 { - // The code oracle call can only fail in the case where the contract - // we are querying is the current one executing and it has not yet been - // deployed, i.e., if someone calls codesize (or extcodesize(address())) - // inside the constructor. In that case, code length is zero. - codeLen := 0 - } - default { - // The first word is the true length of the bytecode - returndatacopy(0, 0, 32) - codeLen := mload(0) - } - } - function getMax(a, b) -> max { max := b if gt(a, b) { @@ -4948,9 +4915,17 @@ object "EvmEmulator" { evmGasLeft := chargeGas(evmGasLeft, 2500) } - switch isEvmContract(addr) - case 0 { stackHead := extcodesize(addr) } - default { stackHead := fetchDeployedEvmCodeLen(addr) } + let rawCodeHash := getRawCodeHash(addr) + switch shr(248, rawCodeHash) + case 1 { + stackHead := extcodesize(addr) + } + case 2 { + stackHead := and(shr(224, rawCodeHash), 0xffff) + } + default { + stackHead := 0 + } ip := add(ip, 1) } diff --git a/system-contracts/contracts/KnownCodesStorage.sol b/system-contracts/contracts/KnownCodesStorage.sol index 1f3755e4e..463105a64 100644 --- a/system-contracts/contracts/KnownCodesStorage.sol +++ b/system-contracts/contracts/KnownCodesStorage.sol @@ -87,11 +87,13 @@ contract KnownCodesStorage is IKnownCodesStorage, SystemContractBase { /// @notice The method used by ContractDeployer to publish EVM bytecode /// @dev Bytecode should be padded by EraVM rules + /// @param paddedBytecode The length of EVM bytecode in bytes /// @param paddedBytecode The bytecode to be published function publishEVMBytecode( + uint256 evmBytecodeLen, bytes calldata paddedBytecode ) external payable onlyCallFrom(address(DEPLOYER_SYSTEM_CONTRACT)) returns (bytes32) { - bytes32 vesionedBytecodeHash = Utils.hashEVMBytecode(paddedBytecode); + bytes32 vesionedBytecodeHash = Utils.hashEVMBytecode(evmBytecodeLen, paddedBytecode); if (getMarker(vesionedBytecodeHash) == 0) { L1_MESSENGER_CONTRACT.sendToL1(paddedBytecode); diff --git a/system-contracts/contracts/SystemContractErrors.sol b/system-contracts/contracts/SystemContractErrors.sol index 2255fafc1..2ba8eed26 100644 --- a/system-contracts/contracts/SystemContractErrors.sol +++ b/system-contracts/contracts/SystemContractErrors.sol @@ -143,5 +143,6 @@ enum BytecodeError { Length, WordsMustBeOdd, DictionaryLength, - EvmBytecodeLength + EvmBytecodeLength, + EvmBytecodeLengthTooBig } diff --git a/system-contracts/contracts/interfaces/IKnownCodesStorage.sol b/system-contracts/contracts/interfaces/IKnownCodesStorage.sol index 984f203aa..fc019be93 100644 --- a/system-contracts/contracts/interfaces/IKnownCodesStorage.sol +++ b/system-contracts/contracts/interfaces/IKnownCodesStorage.sol @@ -17,5 +17,5 @@ interface IKnownCodesStorage { function getMarker(bytes32 _hash) external view returns (uint256); - function publishEVMBytecode(bytes calldata bytecode) external payable returns (bytes32); + function publishEVMBytecode(uint256 evmBytecodeLen, bytes calldata bytecode) external payable returns (bytes32); } diff --git a/system-contracts/contracts/libraries/Utils.sol b/system-contracts/contracts/libraries/Utils.sol index bc423c885..94a615da0 100644 --- a/system-contracts/contracts/libraries/Utils.sol +++ b/system-contracts/contracts/libraries/Utils.sol @@ -138,35 +138,43 @@ library Utils { uint256 internal constant MAX_EVM_BYTECODE_LENGTH = (2 ** 16) - 1; /// @notice Validate the bytecode format and calculate its hash. - /// @param _bytecode The EVM bytecode to hash. + /// @param _evmBytecodeLen The length of original EVM bytecode in bytes + /// @param _paddedBytecode The padded EVM bytecode to hash. /// @return hashedEVMBytecode The 32-byte hash of the EVM bytecode. /// Note: The function reverts the execution if the bytecode has non expected format: /// - Bytecode bytes length is not a multiple of 32 /// - Bytecode bytes length is greater than 2^16 - 1 bytes /// - Bytecode words length is not odd - function hashEVMBytecode(bytes calldata _bytecode) internal view returns (bytes32 hashedEVMBytecode) { + function hashEVMBytecode( + uint256 _evmBytecodeLen, + bytes calldata _paddedBytecode + ) internal view returns (bytes32 hashedEVMBytecode) { // Note that the length of the bytecode must be provided in 32-byte words. - if (_bytecode.length % 32 != 0) { + if (_paddedBytecode.length % 32 != 0) { revert MalformedBytecode(BytecodeError.Length); } - if (_bytecode.length > MAX_EVM_BYTECODE_LENGTH) { + if (_evmBytecodeLen > _paddedBytecode.length) { revert MalformedBytecode(BytecodeError.EvmBytecodeLength); } - uint256 lengthInWords = _bytecode.length / 32; + if (_evmBytecodeLen > MAX_EVM_BYTECODE_LENGTH) { + revert MalformedBytecode(BytecodeError.EvmBytecodeLengthTooBig); + } + + uint256 lengthInWords = _paddedBytecode.length / 32; // bytecode length in words must be odd if (lengthInWords % 2 == 0) { revert MalformedBytecode(BytecodeError.WordsMustBeOdd); } hashedEVMBytecode = - EfficientCall.sha(_bytecode) & + EfficientCall.sha(_paddedBytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // Setting the version of the hash hashedEVMBytecode = (hashedEVMBytecode | bytes32(uint256(EVM_BYTECODE_FLAG) << 248)); - hashedEVMBytecode = hashedEVMBytecode | bytes32(_bytecode.length << 224); + hashedEVMBytecode = hashedEVMBytecode | bytes32(_evmBytecodeLen << 224); } /// @notice Calculates the address of a deployed contract via create2 on the EVM diff --git a/system-contracts/contracts/precompiles/CodeOracle.yul b/system-contracts/contracts/precompiles/CodeOracle.yul index cb12af19f..87145d3be 100644 --- a/system-contracts/contracts/precompiles/CodeOracle.yul +++ b/system-contracts/contracts/precompiles/CodeOracle.yul @@ -111,6 +111,19 @@ object "CodeOracle" { return(0, lenInBytes) } + function paddedBytecodeLen(len) -> blobLen { + blobLen := len + + if iszero(eq(mod(blobLen, 32), 0)) { + blobLen := add(blobLen, sub(32, mod(blobLen, 32))) + } + + // Now it is divisible by 32, but we must make sure that the number of 32 byte words is odd + if iszero(eq(mod(blobLen, 64), 32)) { + blobLen := add(blobLen, 32) + } + } + //////////////////////////////////////////////////////////////// // FALLBACK //////////////////////////////////////////////////////////////// @@ -147,12 +160,10 @@ object "CodeOracle" { decommit(versionedCodeHash, lengthInWords) } case 2 { - // We do not double check whether the length is 32 mod 64, since it assumed that only valid bytecodes - // can pass the `isCodeHashKnown` check. let lengthInBytes := and(shr(224, versionedCodeHash), 0xffff) + let paddedLengthInBytes := paddedBytecodeLen(lengthInBytes) - // It is assumed that the `lengthInBytes` is divisible by 32. - decommit(versionedCodeHash, div(lengthInBytes, 32)) + decommit(versionedCodeHash, div(paddedLengthInBytes, 32)) } default { // Unsupported diff --git a/system-contracts/evm-emulator/EvmEmulator.template.yul b/system-contracts/evm-emulator/EvmEmulator.template.yul index 247cfd786..8e1a4b813 100644 --- a/system-contracts/evm-emulator/EvmEmulator.template.yul +++ b/system-contracts/evm-emulator/EvmEmulator.template.yul @@ -21,16 +21,14 @@ object "EvmEmulator" { copyActivePtrData(BYTECODE_OFFSET(), 0, size) } - function padBytecode(offset, len) -> blobOffset, blobLen { - blobOffset := sub(offset, 32) + function padBytecode(offset, len) -> blobLen { let trueLastByte := add(offset, len) - mstore(blobOffset, len) // clearing out additional bytes mstore(trueLastByte, 0) mstore(add(trueLastByte, 32), 0) - blobLen := add(len, 32) + blobLen := len if iszero(eq(mod(blobLen, 32), 0)) { blobLen := add(blobLen, sub(32, mod(blobLen, 32))) @@ -98,11 +96,12 @@ object "EvmEmulator" { gasToReturn := validateBytecodeAndChargeGas(offset, len, gasToReturn) - offset, len := padBytecode(offset, len) + let blobLen := padBytecode(offset, len) - mstore(add(offset, len), gasToReturn) + mstore(add(offset, blobLen), len) + mstore(add(offset, add(32, blobLen)), gasToReturn) - verbatim_2i_0o("return_deployed", offset, add(len, 32)) + verbatim_2i_0o("return_deployed", offset, add(blobLen, 64)) } object "EvmEmulator_deployed" { code { diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 50089c79d..61feb9235 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -349,61 +349,41 @@ function isHashOfConstructedEvmContract(rawCodeHash) -> isConstructedEVM { // Basically performs an extcodecopy, while returning the length of the copied bytecode. function fetchDeployedCode(addr, dstOffset, srcOffset, len) -> copiedLen { - let codeHash := getRawCodeHash(addr) - mstore(0, codeHash) + let rawCodeHash := getRawCodeHash(addr) + mstore(0, rawCodeHash) let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) // it fails if we don't have any code deployed at this address if success { - returndatacopy(0, 0, 32) - // The first word of returndata is the true length of the bytecode - let codeLen := mload(0) + // The length of the bytecode is encoded in versioned bytecode hash + let codeLen := and(shr(224, rawCodeHash), 0xffff) + + if eq(shr(248, rawCodeHash), 1) { + // For native zkVM contracts length encoded in words, not bytes + codeLen := shl(5, codeLen) // * 32 + } if gt(len, codeLen) { len := codeLen } - let shiftedSrcOffset := add(32, srcOffset) // first 32 bytes is length - let _returndatasize := returndatasize() - if gt(shiftedSrcOffset, _returndatasize) { - shiftedSrcOffset := _returndatasize + if gt(srcOffset, _returndatasize) { + srcOffset := _returndatasize } - if gt(add(len, shiftedSrcOffset), _returndatasize) { - len := sub(_returndatasize, shiftedSrcOffset) + if gt(add(len, srcOffset), _returndatasize) { + len := sub(_returndatasize, srcOffset) } if len { - returndatacopy(dstOffset, shiftedSrcOffset, len) + returndatacopy(dstOffset, srcOffset, len) } copiedLen := len } } -// Returns the length of the EVM bytecode. -function fetchDeployedEvmCodeLen(addr) -> codeLen { - let codeHash := getRawCodeHash(addr) - mstore(0, codeHash) - - let success := staticcall(gas(), CODE_ORACLE_SYSTEM_CONTRACT(), 0, 32, 0, 0) - - switch iszero(success) - case 1 { - // The code oracle call can only fail in the case where the contract - // we are querying is the current one executing and it has not yet been - // deployed, i.e., if someone calls codesize (or extcodesize(address())) - // inside the constructor. In that case, code length is zero. - codeLen := 0 - } - default { - // The first word is the true length of the bytecode - returndatacopy(0, 0, 32) - codeLen := mload(0) - } -} - function getMax(a, b) -> max { max := b if gt(a, b) { diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index 023b6a747..883941a17 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -467,9 +467,17 @@ for { } true { } { evmGasLeft := chargeGas(evmGasLeft, 2500) } - switch isEvmContract(addr) - case 0 { stackHead := extcodesize(addr) } - default { stackHead := fetchDeployedEvmCodeLen(addr) } + let rawCodeHash := getRawCodeHash(addr) + switch shr(248, rawCodeHash) + case 1 { + stackHead := extcodesize(addr) + } + case 2 { + stackHead := and(shr(224, rawCodeHash), 0xffff) + } + default { + stackHead := 0 + } ip := add(ip, 1) } diff --git a/system-contracts/test/KnownCodesStorage.spec.ts b/system-contracts/test/KnownCodesStorage.spec.ts index 395249eec..5444c519d 100644 --- a/system-contracts/test/KnownCodesStorage.spec.ts +++ b/system-contracts/test/KnownCodesStorage.spec.ts @@ -96,7 +96,7 @@ describe("KnownCodesStorage tests", function () { }); it("non-deployer failed to call", async () => { - await expect(knownCodesStorage.publishEVMBytecode("0x00")).to.be.revertedWithCustomError( + await expect(knownCodesStorage.publishEVMBytecode(1, "0x00")).to.be.revertedWithCustomError( knownCodesStorage, "Unauthorized" ); @@ -104,15 +104,14 @@ describe("KnownCodesStorage tests", function () { it("bytecode with even length failed to publish", async () => { await expect( - knownCodesStorage.connect(deployerAccount).publishEVMBytecode(InvalidBytecode) + knownCodesStorage.connect(deployerAccount).publishEVMBytecode(64, InvalidBytecode) ).to.be.revertedWithCustomError(knownCodesStorage, "MalformedBytecode"); }); it("invalid length bytecode failed to call", async () => { - await expect(knownCodesStorage.connect(deployerAccount).publishEVMBytecode("0x00")).to.be.revertedWithCustomError( - knownCodesStorage, - "MalformedBytecode" - ); + await expect( + knownCodesStorage.connect(deployerAccount).publishEVMBytecode(1, "0x00") + ).to.be.revertedWithCustomError(knownCodesStorage, "MalformedBytecode"); }); }); From 0419d3e1ab3ba2715ce87eb86fa60145cd7a2de8 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 20 Dec 2024 13:39:44 +0100 Subject: [PATCH 32/62] fix(EVM): Add overflow checks in `JUMP` and `JUMPI` opcodes (#1159) --- system-contracts/contracts/EvmEmulator.yul | 20 +++++++++++++++++++ .../evm-emulator/EvmEmulatorLoop.template.yul | 10 ++++++++++ 2 files changed, 30 insertions(+) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 9c6e62fef..6653c623c 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -2122,6 +2122,11 @@ object "EvmEmulator" { let counter counter, sp, stackHead := popStackItem(sp, stackHead) + // Counter certainly can't be bigger than uint64. + if gt(counter, MAX_UINT64()) { + panic() + } + ip := add(BYTECODE_OFFSET(), counter) // Check next opcode is JUMPDEST @@ -2147,6 +2152,11 @@ object "EvmEmulator" { continue } + // Counter certainly can't be bigger than uint64. + if gt(counter, MAX_UINT64()) { + panic() + } + ip := add(BYTECODE_OFFSET(), counter) // Check next opcode is JUMPDEST @@ -5250,6 +5260,11 @@ object "EvmEmulator" { let counter counter, sp, stackHead := popStackItem(sp, stackHead) + // Counter certainly can't be bigger than uint64. + if gt(counter, MAX_UINT64()) { + panic() + } + ip := add(BYTECODE_OFFSET(), counter) // Check next opcode is JUMPDEST @@ -5275,6 +5290,11 @@ object "EvmEmulator" { continue } + // Counter certainly can't be bigger than uint64. + if gt(counter, MAX_UINT64()) { + panic() + } + ip := add(BYTECODE_OFFSET(), counter) // Check next opcode is JUMPDEST diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index 883941a17..cd9d5a2c5 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -802,6 +802,11 @@ for { } true { } { let counter counter, sp, stackHead := popStackItem(sp, stackHead) + // Counter certainly can't be bigger than uint64. + if gt(counter, MAX_UINT64()) { + panic() + } + ip := add(BYTECODE_OFFSET(), counter) // Check next opcode is JUMPDEST @@ -827,6 +832,11 @@ for { } true { } { continue } + // Counter certainly can't be bigger than uint64. + if gt(counter, MAX_UINT64()) { + panic() + } + ip := add(BYTECODE_OFFSET(), counter) // Check next opcode is JUMPDEST From 684c072d0caea28a1f970d4d7ba98effa374f8c4 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 20 Dec 2024 14:27:01 +0100 Subject: [PATCH 33/62] fix(EVM): Handle calldata-related opcodes in constructor (#1160) --- system-contracts/contracts/EvmEmulator.yul | 47 +++++++++++++------ .../evm-emulator/EvmEmulator.template.yul | 4 ++ .../evm-emulator/EvmEmulatorLoop.template.yul | 10 ++-- .../ConstructorScope.template.yul | 11 +++++ .../RuntimeScope.template.yul | 14 ++++++ 5 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 system-contracts/evm-emulator/calldata-opcodes/ConstructorScope.template.yul create mode 100644 system-contracts/evm-emulator/calldata-opcodes/RuntimeScope.template.yul diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 6653c623c..5b41e6643 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -1309,6 +1309,18 @@ object "EvmEmulator" { } } + function $llvm_AlwaysInline_llvm$_calldatasize() -> size { + size := 0 + } + + function $llvm_AlwaysInline_llvm$_calldatacopy(dstOffset, sourceOffset, truncatedLen) { + $llvm_AlwaysInline_llvm$_memsetToZero(dstOffset, truncatedLen) + } + + function $llvm_AlwaysInline_llvm$_calldataload(calldataOffset) -> res { + res := 0 + } + function simulate( isCallerEVM, evmGasLeft, @@ -1667,18 +1679,14 @@ object "EvmEmulator" { let calldataOffset := accessStackHead(sp, stackHead) - stackHead := 0 - // EraVM will revert if offset + length overflows uint32 - if lt(calldataOffset, UINT32_MAX()) { - stackHead := calldataload(calldataOffset) - } + stackHead := $llvm_AlwaysInline_llvm$_calldataload(calldataOffset) ip := add(ip, 1) } case 0x36 { // OP_CALLDATASIZE evmGasLeft := chargeGas(evmGasLeft, 2) - sp, stackHead := pushStackItem(sp, calldatasize(), stackHead) + sp, stackHead := pushStackItem(sp, $llvm_AlwaysInline_llvm$_calldatasize(), stackHead) ip := add(ip, 1) } case 0x37 { // OP_CALLDATACOPY @@ -1713,7 +1721,7 @@ object "EvmEmulator" { } if truncatedLen { - calldatacopy(dstOffset, sourceOffset, truncatedLen) + $llvm_AlwaysInline_llvm$_calldatacopy(dstOffset, sourceOffset, truncatedLen) } ip := add(ip, 1) @@ -4805,18 +4813,14 @@ object "EvmEmulator" { let calldataOffset := accessStackHead(sp, stackHead) - stackHead := 0 - // EraVM will revert if offset + length overflows uint32 - if lt(calldataOffset, UINT32_MAX()) { - stackHead := calldataload(calldataOffset) - } + stackHead := $llvm_AlwaysInline_llvm$_calldataload(calldataOffset) ip := add(ip, 1) } case 0x36 { // OP_CALLDATASIZE evmGasLeft := chargeGas(evmGasLeft, 2) - sp, stackHead := pushStackItem(sp, calldatasize(), stackHead) + sp, stackHead := pushStackItem(sp, $llvm_AlwaysInline_llvm$_calldatasize(), stackHead) ip := add(ip, 1) } case 0x37 { // OP_CALLDATACOPY @@ -4851,7 +4855,7 @@ object "EvmEmulator" { } if truncatedLen { - calldatacopy(dstOffset, sourceOffset, truncatedLen) + $llvm_AlwaysInline_llvm$_calldatacopy(dstOffset, sourceOffset, truncatedLen) } ip := add(ip, 1) @@ -6284,6 +6288,21 @@ object "EvmEmulator" { } + function $llvm_AlwaysInline_llvm$_calldatasize() -> size { + size := calldatasize() + } + + function $llvm_AlwaysInline_llvm$_calldatacopy(dstOffset, sourceOffset, truncatedLen) { + calldatacopy(dstOffset, sourceOffset, truncatedLen) + } + + function $llvm_AlwaysInline_llvm$_calldataload(calldataOffset) -> res { + // EraVM will revert if offset + length overflows uint32 + if lt(calldataOffset, UINT32_MAX()) { + res := calldataload(calldataOffset) + } + } + if eq(isCallerEVM, 1) { // Includes gas returnOffset := sub(returnOffset, 32) diff --git a/system-contracts/evm-emulator/EvmEmulator.template.yul b/system-contracts/evm-emulator/EvmEmulator.template.yul index 8e1a4b813..22d188075 100644 --- a/system-contracts/evm-emulator/EvmEmulator.template.yul +++ b/system-contracts/evm-emulator/EvmEmulator.template.yul @@ -60,6 +60,8 @@ object "EvmEmulator" { + + function simulate( isCallerEVM, evmGasLeft, @@ -134,6 +136,8 @@ object "EvmEmulator" { + + if eq(isCallerEVM, 1) { // Includes gas returnOffset := sub(returnOffset, 32) diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index cd9d5a2c5..d5056dd19 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -347,18 +347,14 @@ for { } true { } { let calldataOffset := accessStackHead(sp, stackHead) - stackHead := 0 - // EraVM will revert if offset + length overflows uint32 - if lt(calldataOffset, UINT32_MAX()) { - stackHead := calldataload(calldataOffset) - } + stackHead := $llvm_AlwaysInline_llvm$_calldataload(calldataOffset) ip := add(ip, 1) } case 0x36 { // OP_CALLDATASIZE evmGasLeft := chargeGas(evmGasLeft, 2) - sp, stackHead := pushStackItem(sp, calldatasize(), stackHead) + sp, stackHead := pushStackItem(sp, $llvm_AlwaysInline_llvm$_calldatasize(), stackHead) ip := add(ip, 1) } case 0x37 { // OP_CALLDATACOPY @@ -393,7 +389,7 @@ for { } true { } { } if truncatedLen { - calldatacopy(dstOffset, sourceOffset, truncatedLen) + $llvm_AlwaysInline_llvm$_calldatacopy(dstOffset, sourceOffset, truncatedLen) } ip := add(ip, 1) diff --git a/system-contracts/evm-emulator/calldata-opcodes/ConstructorScope.template.yul b/system-contracts/evm-emulator/calldata-opcodes/ConstructorScope.template.yul new file mode 100644 index 000000000..075f4dfaa --- /dev/null +++ b/system-contracts/evm-emulator/calldata-opcodes/ConstructorScope.template.yul @@ -0,0 +1,11 @@ +function $llvm_AlwaysInline_llvm$_calldatasize() -> size { + size := 0 +} + +function $llvm_AlwaysInline_llvm$_calldatacopy(dstOffset, sourceOffset, truncatedLen) { + $llvm_AlwaysInline_llvm$_memsetToZero(dstOffset, truncatedLen) +} + +function $llvm_AlwaysInline_llvm$_calldataload(calldataOffset) -> res { + res := 0 +} \ No newline at end of file diff --git a/system-contracts/evm-emulator/calldata-opcodes/RuntimeScope.template.yul b/system-contracts/evm-emulator/calldata-opcodes/RuntimeScope.template.yul new file mode 100644 index 000000000..51c88e7b5 --- /dev/null +++ b/system-contracts/evm-emulator/calldata-opcodes/RuntimeScope.template.yul @@ -0,0 +1,14 @@ +function $llvm_AlwaysInline_llvm$_calldatasize() -> size { + size := calldatasize() +} + +function $llvm_AlwaysInline_llvm$_calldatacopy(dstOffset, sourceOffset, truncatedLen) { + calldatacopy(dstOffset, sourceOffset, truncatedLen) +} + +function $llvm_AlwaysInline_llvm$_calldataload(calldataOffset) -> res { + // EraVM will revert if offset + length overflows uint32 + if lt(calldataOffset, UINT32_MAX()) { + res := calldataload(calldataOffset) + } +} \ No newline at end of file From f9824ea389f1fd13e2163e6645c5ffa450d6b012 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 30 Dec 2024 16:44:01 +0100 Subject: [PATCH 34/62] fix(EVM): Fix and simplify memory expansion checks (#1165) --- system-contracts/contracts/EvmEmulator.yul | 268 ++++++++---------- .../EvmEmulatorFunctions.template.yul | 100 ++++--- .../evm-emulator/EvmEmulatorLoop.template.yul | 33 +-- 3 files changed, 175 insertions(+), 226 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 5b41e6643..ba2ad472f 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -239,56 +239,67 @@ object "EvmEmulator" { } } - // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. function expandMemory(offset, size) -> gasCost { // memory expansion costs 0 if size is 0 if size { - let oldSizeInWords := mload(MEM_LEN_OFFSET()) + checkOverflow(offset, size) + gasCost := _expandMemoryInternal(add(offset, size)) + } + } - // div rounding up - let newSizeInWords := div(add(add(offset, size), 31), 32) - - // memory_size_word = (memory_byte_size + 31) / 32 - // memory_cost = (memory_size_word ** 2) / 512 + (3 * memory_size_word) - // memory_expansion_cost = new_memory_cost - last_memory_cost - if gt(newSizeInWords, oldSizeInWords) { - let linearPart := mul(3, sub(newSizeInWords, oldSizeInWords)) - let quadraticPart := sub( - div( - mul(newSizeInWords, newSizeInWords), - 512 - ), - div( - mul(oldSizeInWords, oldSizeInWords), - 512 - ) + // This function can overflow, it is the job of the caller to ensure that it does not. + // The argument to this function is the new size of memory IN BYTES. + function _expandMemoryInternal(newMemsize) -> gasCost { + if gt(newMemsize, MAX_POSSIBLE_MEM_LEN()) { + panic() + } + + let oldSizeInWords := mload(MEM_LEN_OFFSET()) + + // div rounding up + let newSizeInWords := div(add(newMemsize, 31), 32) + + // memory_size_word = (memory_byte_size + 31) / 32 + // memory_cost = (memory_size_word ** 2) / 512 + (3 * memory_size_word) + // memory_expansion_cost = new_memory_cost - last_memory_cost + if gt(newSizeInWords, oldSizeInWords) { + let linearPart := mul(3, sub(newSizeInWords, oldSizeInWords)) + let quadraticPart := sub( + div( + mul(newSizeInWords, newSizeInWords), + 512 + ), + div( + mul(oldSizeInWords, oldSizeInWords), + 512 ) - - gasCost := add(linearPart, quadraticPart) - - mstore(MEM_LEN_OFFSET(), newSizeInWords) - } + ) + + gasCost := add(linearPart, quadraticPart) + + mstore(MEM_LEN_OFFSET(), newSizeInWords) } } - function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> maxExpand { - switch lt(add(retOffset, retSize), add(argsOffset, argsSize)) - case 0 { - maxExpand := expandMemory(retOffset, retSize) - } - default { - maxExpand := expandMemory(argsOffset, argsSize) + // Returns 0 if size is 0 + function _memsizeRequired(offset, size) -> memorySize { + if size { + checkOverflow(offset, size) + memorySize := add(offset, size) } } - function checkMemIsAccessible(relativeOffset, size) { - if size { - checkOverflow(relativeOffset, size) + function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> maxExpand { + let maxNewMemsize := _memsizeRequired(retOffset, retSize) + let argsMemsize := _memsizeRequired(argsOffset, argsSize) - if gt(add(relativeOffset, size), MAX_POSSIBLE_MEM_LEN()) { - panic() - } + if lt(maxNewMemsize, argsMemsize) { + maxNewMemsize := argsMemsize + } + + if maxNewMemsize { // Memory expansion costs 0 if size is 0 + maxExpand := _expandMemoryInternal(maxNewMemsize) } } @@ -831,16 +842,15 @@ object "EvmEmulator" { function _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) -> addr, gasUsed { addr := and(rawAddr, 0xffffffffffffffffffffffffffffffffffffffff) - checkMemIsAccessible(argsOffset, argsSize) - checkMemIsAccessible(retOffset, retSize) + // memory_expansion_cost + gasUsed := expandMemory2(retOffset, retSize, argsOffset, argsSize) - gasUsed := 100 // warm address access cost + let addressAccessCost := 100 // warm address access cost if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { - gasUsed := 2600 // cold address access cost + addressAccessCost := 2600 // cold address access cost } - // memory_expansion_cost - gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) + gasUsed := add(gasUsed, addressAccessCost) } function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { @@ -1086,8 +1096,6 @@ object "EvmEmulator" { } function $llvm_NoInline_llvm$_genericCreate(offset, size, value, evmGasLeftOld, isCreate2, salt) -> evmGasLeft, addr { - checkMemIsAccessible(offset, size) - // EIP-3860 if gt(size, MAX_POSSIBLE_INIT_BYTECODE_LEN()) { panic() @@ -1296,8 +1304,6 @@ object "EvmEmulator" { rawOffset, newSp, newStackHead := popStackItemWithoutCheck(sp, stackHead) size, newSp, newStackHead := popStackItemWithoutCheck(newSp, newStackHead) - checkMemIsAccessible(rawOffset, size) - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost let dynamicGas := add(shl(3, size), expandMemory(rawOffset, size)) dynamicGas := add(dynamicGas, mul(375, topicCount)) @@ -1615,8 +1621,6 @@ object "EvmEmulator" { popStackCheck(sp, 2) rawOffset, sp, size := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(rawOffset, size) - // When an offset is first accessed (either read or write), memory may trigger // an expansion, which costs gas. // dynamicGas = 6 * minimum_word_size + memory_expansion_cost @@ -1699,8 +1703,6 @@ object "EvmEmulator" { sourceOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(dstOffset, len) - // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(dstOffset, len)) @@ -1744,8 +1746,6 @@ object "EvmEmulator" { sourceOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(dstOffset, len) - // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(dstOffset, len)) @@ -1820,9 +1820,7 @@ object "EvmEmulator" { len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) - - checkMemIsAccessible(dstOffset, len) - + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost // minimum_word_size = (size + 31) / 32 let dynamicGas := add( @@ -1869,8 +1867,6 @@ object "EvmEmulator" { sourceOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(dstOffset, len) - // minimum_word_size = (size + 31) / 32 // dynamicGas = 3 * minimum_word_size + memory_expansion_cost let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(dstOffset, len)) @@ -2017,10 +2013,7 @@ object "EvmEmulator" { evmGasLeft := chargeGas(evmGasLeft, 3) let offset := accessStackHead(sp, stackHead) - - checkMemIsAccessible(offset, 32) - let expansionGas := expandMemory(offset, 32) - evmGasLeft := chargeGas(evmGasLeft, expansionGas) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, 32)) stackHead := mload(add(MEM_OFFSET(), offset)) @@ -2035,9 +2028,7 @@ object "EvmEmulator" { offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) value, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, 32) - let expansionGas := expandMemory(offset, 32) - evmGasLeft := chargeGas(evmGasLeft, expansionGas) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, 32)) mstore(add(MEM_OFFSET(), offset), value) ip := add(ip, 1) @@ -2051,9 +2042,7 @@ object "EvmEmulator" { offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) value, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, 1) - let expansionGas := expandMemory(offset, 1) - evmGasLeft := chargeGas(evmGasLeft, expansionGas) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, 1)) mstore8(add(MEM_OFFSET(), offset), value) ip := add(ip, 1) @@ -2234,9 +2223,6 @@ object "EvmEmulator" { offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) - checkMemIsAccessible(destOffset, size) - // dynamic_gas = 3 * words_copied + memory_expansion_cost let dynamicGas := expandMemory2(offset, size, destOffset, size) let wordsCopied := div(add(size, 31), 32) // div rounding up @@ -2750,8 +2736,6 @@ object "EvmEmulator" { size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) if size { - checkMemIsAccessible(offset, size) - evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) returnLen := size @@ -2789,10 +2773,9 @@ object "EvmEmulator" { switch iszero(size) case 0 { - checkMemIsAccessible(offset, size) evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) - // Don't check overflow here since previous checks are enough to ensure this is safe + // Don't check overflow here since check in expandMemory is enough to ensure this is safe offset := add(offset, MEM_OFFSET()) } default { @@ -3385,56 +3368,67 @@ object "EvmEmulator" { } } - // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. function expandMemory(offset, size) -> gasCost { // memory expansion costs 0 if size is 0 if size { - let oldSizeInWords := mload(MEM_LEN_OFFSET()) - - // div rounding up - let newSizeInWords := div(add(add(offset, size), 31), 32) - - // memory_size_word = (memory_byte_size + 31) / 32 - // memory_cost = (memory_size_word ** 2) / 512 + (3 * memory_size_word) - // memory_expansion_cost = new_memory_cost - last_memory_cost - if gt(newSizeInWords, oldSizeInWords) { - let linearPart := mul(3, sub(newSizeInWords, oldSizeInWords)) - let quadraticPart := sub( - div( - mul(newSizeInWords, newSizeInWords), - 512 - ), - div( - mul(oldSizeInWords, oldSizeInWords), - 512 - ) - ) - - gasCost := add(linearPart, quadraticPart) - - mstore(MEM_LEN_OFFSET(), newSizeInWords) - } + checkOverflow(offset, size) + gasCost := _expandMemoryInternal(add(offset, size)) } } - function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> maxExpand { - switch lt(add(retOffset, retSize), add(argsOffset, argsSize)) - case 0 { - maxExpand := expandMemory(retOffset, retSize) - } - default { - maxExpand := expandMemory(argsOffset, argsSize) + // This function can overflow, it is the job of the caller to ensure that it does not. + // The argument to this function is the new size of memory IN BYTES. + function _expandMemoryInternal(newMemsize) -> gasCost { + if gt(newMemsize, MAX_POSSIBLE_MEM_LEN()) { + panic() + } + + let oldSizeInWords := mload(MEM_LEN_OFFSET()) + + // div rounding up + let newSizeInWords := div(add(newMemsize, 31), 32) + + // memory_size_word = (memory_byte_size + 31) / 32 + // memory_cost = (memory_size_word ** 2) / 512 + (3 * memory_size_word) + // memory_expansion_cost = new_memory_cost - last_memory_cost + if gt(newSizeInWords, oldSizeInWords) { + let linearPart := mul(3, sub(newSizeInWords, oldSizeInWords)) + let quadraticPart := sub( + div( + mul(newSizeInWords, newSizeInWords), + 512 + ), + div( + mul(oldSizeInWords, oldSizeInWords), + 512 + ) + ) + + gasCost := add(linearPart, quadraticPart) + + mstore(MEM_LEN_OFFSET(), newSizeInWords) } } - function checkMemIsAccessible(relativeOffset, size) { + // Returns 0 if size is 0 + function _memsizeRequired(offset, size) -> memorySize { if size { - checkOverflow(relativeOffset, size) + checkOverflow(offset, size) + memorySize := add(offset, size) + } + } - if gt(add(relativeOffset, size), MAX_POSSIBLE_MEM_LEN()) { - panic() - } + function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> maxExpand { + let maxNewMemsize := _memsizeRequired(retOffset, retSize) + let argsMemsize := _memsizeRequired(argsOffset, argsSize) + + if lt(maxNewMemsize, argsMemsize) { + maxNewMemsize := argsMemsize + } + + if maxNewMemsize { // Memory expansion costs 0 if size is 0 + maxExpand := _expandMemoryInternal(maxNewMemsize) } } @@ -3977,16 +3971,15 @@ object "EvmEmulator" { function _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) -> addr, gasUsed { addr := and(rawAddr, 0xffffffffffffffffffffffffffffffffffffffff) - checkMemIsAccessible(argsOffset, argsSize) - checkMemIsAccessible(retOffset, retSize) + // memory_expansion_cost + gasUsed := expandMemory2(retOffset, retSize, argsOffset, argsSize) - gasUsed := 100 // warm address access cost + let addressAccessCost := 100 // warm address access cost if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { - gasUsed := 2600 // cold address access cost + addressAccessCost := 2600 // cold address access cost } - // memory_expansion_cost - gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) + gasUsed := add(gasUsed, addressAccessCost) } function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { @@ -4232,8 +4225,6 @@ object "EvmEmulator" { } function $llvm_NoInline_llvm$_genericCreate(offset, size, value, evmGasLeftOld, isCreate2, salt) -> evmGasLeft, addr { - checkMemIsAccessible(offset, size) - // EIP-3860 if gt(size, MAX_POSSIBLE_INIT_BYTECODE_LEN()) { panic() @@ -4442,8 +4433,6 @@ object "EvmEmulator" { rawOffset, newSp, newStackHead := popStackItemWithoutCheck(sp, stackHead) size, newSp, newStackHead := popStackItemWithoutCheck(newSp, newStackHead) - checkMemIsAccessible(rawOffset, size) - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost let dynamicGas := add(shl(3, size), expandMemory(rawOffset, size)) dynamicGas := add(dynamicGas, mul(375, topicCount)) @@ -4749,8 +4738,6 @@ object "EvmEmulator" { popStackCheck(sp, 2) rawOffset, sp, size := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(rawOffset, size) - // When an offset is first accessed (either read or write), memory may trigger // an expansion, which costs gas. // dynamicGas = 6 * minimum_word_size + memory_expansion_cost @@ -4833,8 +4820,6 @@ object "EvmEmulator" { sourceOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(dstOffset, len) - // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(dstOffset, len)) @@ -4878,8 +4863,6 @@ object "EvmEmulator" { sourceOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(dstOffset, len) - // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(dstOffset, len)) @@ -4954,9 +4937,7 @@ object "EvmEmulator" { len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) - - checkMemIsAccessible(dstOffset, len) - + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost // minimum_word_size = (size + 31) / 32 let dynamicGas := add( @@ -5003,8 +4984,6 @@ object "EvmEmulator" { sourceOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(dstOffset, len) - // minimum_word_size = (size + 31) / 32 // dynamicGas = 3 * minimum_word_size + memory_expansion_cost let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(dstOffset, len)) @@ -5151,10 +5130,7 @@ object "EvmEmulator" { evmGasLeft := chargeGas(evmGasLeft, 3) let offset := accessStackHead(sp, stackHead) - - checkMemIsAccessible(offset, 32) - let expansionGas := expandMemory(offset, 32) - evmGasLeft := chargeGas(evmGasLeft, expansionGas) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, 32)) stackHead := mload(add(MEM_OFFSET(), offset)) @@ -5169,9 +5145,7 @@ object "EvmEmulator" { offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) value, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, 32) - let expansionGas := expandMemory(offset, 32) - evmGasLeft := chargeGas(evmGasLeft, expansionGas) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, 32)) mstore(add(MEM_OFFSET(), offset), value) ip := add(ip, 1) @@ -5185,9 +5159,7 @@ object "EvmEmulator" { offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) value, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, 1) - let expansionGas := expandMemory(offset, 1) - evmGasLeft := chargeGas(evmGasLeft, expansionGas) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, 1)) mstore8(add(MEM_OFFSET(), offset), value) ip := add(ip, 1) @@ -5368,9 +5340,6 @@ object "EvmEmulator" { offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) - checkMemIsAccessible(destOffset, size) - // dynamic_gas = 3 * words_copied + memory_expansion_cost let dynamicGas := expandMemory2(offset, size, destOffset, size) let wordsCopied := div(add(size, 31), 32) // div rounding up @@ -5884,8 +5853,6 @@ object "EvmEmulator" { size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) if size { - checkMemIsAccessible(offset, size) - evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) returnLen := size @@ -5923,10 +5890,9 @@ object "EvmEmulator" { switch iszero(size) case 0 { - checkMemIsAccessible(offset, size) evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) - // Don't check overflow here since previous checks are enough to ensure this is safe + // Don't check overflow here since check in expandMemory is enough to ensure this is safe offset := add(offset, MEM_OFFSET()) } default { diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 61feb9235..a9253a8cd 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -179,56 +179,67 @@ function getEvmGasFromContext() -> evmGas { } } -// This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. function expandMemory(offset, size) -> gasCost { // memory expansion costs 0 if size is 0 if size { - let oldSizeInWords := mload(MEM_LEN_OFFSET()) + checkOverflow(offset, size) + gasCost := _expandMemoryInternal(add(offset, size)) + } +} - // div rounding up - let newSizeInWords := div(add(add(offset, size), 31), 32) - - // memory_size_word = (memory_byte_size + 31) / 32 - // memory_cost = (memory_size_word ** 2) / 512 + (3 * memory_size_word) - // memory_expansion_cost = new_memory_cost - last_memory_cost - if gt(newSizeInWords, oldSizeInWords) { - let linearPart := mul(3, sub(newSizeInWords, oldSizeInWords)) - let quadraticPart := sub( - div( - mul(newSizeInWords, newSizeInWords), - 512 - ), - div( - mul(oldSizeInWords, oldSizeInWords), - 512 - ) +// This function can overflow, it is the job of the caller to ensure that it does not. +// The argument to this function is the new size of memory IN BYTES. +function _expandMemoryInternal(newMemsize) -> gasCost { + if gt(newMemsize, MAX_POSSIBLE_MEM_LEN()) { + panic() + } + + let oldSizeInWords := mload(MEM_LEN_OFFSET()) + + // div rounding up + let newSizeInWords := div(add(newMemsize, 31), 32) + + // memory_size_word = (memory_byte_size + 31) / 32 + // memory_cost = (memory_size_word ** 2) / 512 + (3 * memory_size_word) + // memory_expansion_cost = new_memory_cost - last_memory_cost + if gt(newSizeInWords, oldSizeInWords) { + let linearPart := mul(3, sub(newSizeInWords, oldSizeInWords)) + let quadraticPart := sub( + div( + mul(newSizeInWords, newSizeInWords), + 512 + ), + div( + mul(oldSizeInWords, oldSizeInWords), + 512 ) - - gasCost := add(linearPart, quadraticPart) - - mstore(MEM_LEN_OFFSET(), newSizeInWords) - } + ) + + gasCost := add(linearPart, quadraticPart) + + mstore(MEM_LEN_OFFSET(), newSizeInWords) } } -function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> maxExpand { - switch lt(add(retOffset, retSize), add(argsOffset, argsSize)) - case 0 { - maxExpand := expandMemory(retOffset, retSize) - } - default { - maxExpand := expandMemory(argsOffset, argsSize) +// Returns 0 if size is 0 +function _memsizeRequired(offset, size) -> memorySize { + if size { + checkOverflow(offset, size) + memorySize := add(offset, size) } } -function checkMemIsAccessible(relativeOffset, size) { - if size { - checkOverflow(relativeOffset, size) +function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> maxExpand { + let maxNewMemsize := _memsizeRequired(retOffset, retSize) + let argsMemsize := _memsizeRequired(argsOffset, argsSize) - if gt(add(relativeOffset, size), MAX_POSSIBLE_MEM_LEN()) { - panic() - } + if lt(maxNewMemsize, argsMemsize) { + maxNewMemsize := argsMemsize + } + + if maxNewMemsize { // Memory expansion costs 0 if size is 0 + maxExpand := _expandMemoryInternal(maxNewMemsize) } } @@ -771,16 +782,15 @@ function performDelegateCall(oldSp, evmGasLeft, isStatic, oldStackHead) -> newGa function _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) -> addr, gasUsed { addr := and(rawAddr, 0xffffffffffffffffffffffffffffffffffffffff) - checkMemIsAccessible(argsOffset, argsSize) - checkMemIsAccessible(retOffset, retSize) + // memory_expansion_cost + gasUsed := expandMemory2(retOffset, retSize, argsOffset, argsSize) - gasUsed := 100 // warm address access cost + let addressAccessCost := 100 // warm address access cost if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { - gasUsed := 2600 // cold address access cost + addressAccessCost := 2600 // cold address access cost } - // memory_expansion_cost - gasUsed := add(gasUsed, expandMemory2(retOffset, retSize, argsOffset, argsSize)) + gasUsed := add(gasUsed, addressAccessCost) } function _genericCall(addr, gasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic) -> success, frameGasLeft { @@ -1026,8 +1036,6 @@ function performCreate2(oldEvmGasLeft, oldSp, oldStackHead) -> evmGasLeft, sp, s } function $llvm_NoInline_llvm$_genericCreate(offset, size, value, evmGasLeftOld, isCreate2, salt) -> evmGasLeft, addr { - checkMemIsAccessible(offset, size) - // EIP-3860 if gt(size, MAX_POSSIBLE_INIT_BYTECODE_LEN()) { panic() @@ -1236,8 +1244,6 @@ function _genericLog(sp, stackHead, evmGasLeft, topicCount, isStatic) -> newEvmG rawOffset, newSp, newStackHead := popStackItemWithoutCheck(sp, stackHead) size, newSp, newStackHead := popStackItemWithoutCheck(newSp, newStackHead) - checkMemIsAccessible(rawOffset, size) - // dynamicGas = 375 * topic_count + 8 * size + memory_expansion_cost let dynamicGas := add(shl(3, size), expandMemory(rawOffset, size)) dynamicGas := add(dynamicGas, mul(375, topicCount)) diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index d5056dd19..58e959514 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -283,8 +283,6 @@ for { } true { } { popStackCheck(sp, 2) rawOffset, sp, size := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(rawOffset, size) - // When an offset is first accessed (either read or write), memory may trigger // an expansion, which costs gas. // dynamicGas = 6 * minimum_word_size + memory_expansion_cost @@ -367,8 +365,6 @@ for { } true { } { sourceOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(dstOffset, len) - // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(dstOffset, len)) @@ -412,8 +408,6 @@ for { } true { } { sourceOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(dstOffset, len) - // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(dstOffset, len)) @@ -488,9 +482,7 @@ for { } true { } { len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) - - checkMemIsAccessible(dstOffset, len) - + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost // minimum_word_size = (size + 31) / 32 let dynamicGas := add( @@ -537,8 +529,6 @@ for { } true { } { sourceOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(dstOffset, len) - // minimum_word_size = (size + 31) / 32 // dynamicGas = 3 * minimum_word_size + memory_expansion_cost let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(dstOffset, len)) @@ -685,10 +675,7 @@ for { } true { } { evmGasLeft := chargeGas(evmGasLeft, 3) let offset := accessStackHead(sp, stackHead) - - checkMemIsAccessible(offset, 32) - let expansionGas := expandMemory(offset, 32) - evmGasLeft := chargeGas(evmGasLeft, expansionGas) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, 32)) stackHead := mload(add(MEM_OFFSET(), offset)) @@ -703,9 +690,7 @@ for { } true { } { offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) value, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, 32) - let expansionGas := expandMemory(offset, 32) - evmGasLeft := chargeGas(evmGasLeft, expansionGas) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, 32)) mstore(add(MEM_OFFSET(), offset), value) ip := add(ip, 1) @@ -719,9 +704,7 @@ for { } true { } { offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) value, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, 1) - let expansionGas := expandMemory(offset, 1) - evmGasLeft := chargeGas(evmGasLeft, expansionGas) + evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, 1)) mstore8(add(MEM_OFFSET(), offset), value) ip := add(ip, 1) @@ -902,9 +885,6 @@ for { } true { } { offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) - checkMemIsAccessible(destOffset, size) - // dynamic_gas = 3 * words_copied + memory_expansion_cost let dynamicGas := expandMemory2(offset, size, destOffset, size) let wordsCopied := div(add(size, 31), 32) // div rounding up @@ -1418,8 +1398,6 @@ for { } true { } { size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) if size { - checkMemIsAccessible(offset, size) - evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) returnLen := size @@ -1457,10 +1435,9 @@ for { } true { } { switch iszero(size) case 0 { - checkMemIsAccessible(offset, size) evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) - // Don't check overflow here since previous checks are enough to ensure this is safe + // Don't check overflow here since check in expandMemory is enough to ensure this is safe offset := add(offset, MEM_OFFSET()) } default { From 8349c91f2ccf106900bf7ca5e8b7a2d36ca79db7 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 30 Dec 2024 17:01:21 +0100 Subject: [PATCH 35/62] fix(EVM): Fix access errors in EvmGasManager (L-02, L-06) (#1166) --- system-contracts/contracts/EvmGasManager.yul | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/system-contracts/contracts/EvmGasManager.yul b/system-contracts/contracts/EvmGasManager.yul index bf274eae0..00d236250 100644 --- a/system-contracts/contracts/EvmGasManager.yul +++ b/system-contracts/contracts/EvmGasManager.yul @@ -71,9 +71,9 @@ object "EvmGasManager" { let notSystemCall := iszero(and(callFlags, 2)) if notSystemCall { - // error CallerMustBeEvmContract() - mstore(0, 0xBE4BF9E400000000000000000000000000000000000000000000000000000000) - revert(0, 32) + // error SystemCallFlagRequired() + mstore(0, 0x71C3DA0100000000000000000000000000000000000000000000000000000000) + revert(0, 4) } // SELFDESTRUCT is not supported, so it is ok to cache here @@ -86,7 +86,7 @@ object "EvmGasManager" { if iszero(isEVM) { // error CallerMustBeEvmContract() mstore(0, 0xBE4BF9E400000000000000000000000000000000000000000000000000000000) - revert(0, 32) + revert(0, 4) } // we will not cache contract if it is being constructed From f2677bf98a4e93d1ec5638c51294e08a7d19a73e Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 30 Dec 2024 17:10:42 +0100 Subject: [PATCH 36/62] fix(EVM): Use static call for isSlotWarm (L-07) (#1167) --- system-contracts/contracts/EvmEmulator.yul | 6 ++---- .../evm-emulator/EvmEmulatorFunctions.template.yul | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index ba2ad472f..1b531a3c1 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -617,8 +617,7 @@ object "EvmEmulator" { // non-standard selector 0x01 mstore(0, 0x0100000000000000000000000000000000000000000000000000000000000000) mstore(1, key) - // should be call since we use TSTORE in gas manager - let success := call(gas(), EVM_GAS_MANAGER_CONTRACT(), 0, 0, 33, 0, 0) + let success := staticcall(gas(), EVM_GAS_MANAGER_CONTRACT(), 0, 33, 0, 0) if iszero(success) { // This error should never happen @@ -3746,8 +3745,7 @@ object "EvmEmulator" { // non-standard selector 0x01 mstore(0, 0x0100000000000000000000000000000000000000000000000000000000000000) mstore(1, key) - // should be call since we use TSTORE in gas manager - let success := call(gas(), EVM_GAS_MANAGER_CONTRACT(), 0, 0, 33, 0, 0) + let success := staticcall(gas(), EVM_GAS_MANAGER_CONTRACT(), 0, 33, 0, 0) if iszero(success) { // This error should never happen diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index a9253a8cd..f7544f4ce 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -557,8 +557,7 @@ function isSlotWarm(key) -> isWarm { // non-standard selector 0x01 mstore(0, 0x0100000000000000000000000000000000000000000000000000000000000000) mstore(1, key) - // should be call since we use TSTORE in gas manager - let success := call(gas(), EVM_GAS_MANAGER_CONTRACT(), 0, 0, 33, 0, 0) + let success := staticcall(gas(), EVM_GAS_MANAGER_CONTRACT(), 0, 33, 0, 0) if iszero(success) { // This error should never happen From 34918c3163306b3f927be500622d25ccbc839e1e Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 30 Dec 2024 17:29:49 +0100 Subject: [PATCH 37/62] fix(EVM): Replace hardcoded memory offsets (N-01) (#1168) --- system-contracts/contracts/EvmEmulator.yul | 32 +++++++++---------- .../EvmEmulatorFunctions.template.yul | 16 +++++----- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 1b531a3c1..c69e8407a 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -91,35 +91,35 @@ object "EvmEmulator" { } function GASPRICE_CACHE_OFFSET() -> offset { - offset := mul(24, 32) + offset := add(ORIGIN_CACHE_OFFSET(), 32) } function COINBASE_CACHE_OFFSET() -> offset { - offset := mul(25, 32) + offset := add(GASPRICE_CACHE_OFFSET(), 32) } function BLOCKTIMESTAMP_CACHE_OFFSET() -> offset { - offset := mul(26, 32) + offset := add(COINBASE_CACHE_OFFSET(), 32) } function BLOCKNUMBER_CACHE_OFFSET() -> offset { - offset := mul(27, 32) + offset := add(BLOCKTIMESTAMP_CACHE_OFFSET(), 32) } function PREVRANDAO_CACHE_OFFSET() -> offset { - offset := mul(28, 32) + offset := add(BLOCKNUMBER_CACHE_OFFSET(), 32) } function GASLIMIT_CACHE_OFFSET() -> offset { - offset := mul(29, 32) + offset := add(PREVRANDAO_CACHE_OFFSET(), 32) } function CHAINID_CACHE_OFFSET() -> offset { - offset := mul(30, 32) + offset := add(GASLIMIT_CACHE_OFFSET(), 32) } function BASEFEE_CACHE_OFFSET() -> offset { - offset := mul(31, 32) + offset := add(CHAINID_CACHE_OFFSET(), 32) } function LAST_RETURNDATA_SIZE_OFFSET() -> offset { @@ -3219,35 +3219,35 @@ object "EvmEmulator" { } function GASPRICE_CACHE_OFFSET() -> offset { - offset := mul(24, 32) + offset := add(ORIGIN_CACHE_OFFSET(), 32) } function COINBASE_CACHE_OFFSET() -> offset { - offset := mul(25, 32) + offset := add(GASPRICE_CACHE_OFFSET(), 32) } function BLOCKTIMESTAMP_CACHE_OFFSET() -> offset { - offset := mul(26, 32) + offset := add(COINBASE_CACHE_OFFSET(), 32) } function BLOCKNUMBER_CACHE_OFFSET() -> offset { - offset := mul(27, 32) + offset := add(BLOCKTIMESTAMP_CACHE_OFFSET(), 32) } function PREVRANDAO_CACHE_OFFSET() -> offset { - offset := mul(28, 32) + offset := add(BLOCKNUMBER_CACHE_OFFSET(), 32) } function GASLIMIT_CACHE_OFFSET() -> offset { - offset := mul(29, 32) + offset := add(PREVRANDAO_CACHE_OFFSET(), 32) } function CHAINID_CACHE_OFFSET() -> offset { - offset := mul(30, 32) + offset := add(GASLIMIT_CACHE_OFFSET(), 32) } function BASEFEE_CACHE_OFFSET() -> offset { - offset := mul(31, 32) + offset := add(CHAINID_CACHE_OFFSET(), 32) } function LAST_RETURNDATA_SIZE_OFFSET() -> offset { diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index f7544f4ce..7057e510b 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -31,35 +31,35 @@ function ORIGIN_CACHE_OFFSET() -> offset { } function GASPRICE_CACHE_OFFSET() -> offset { - offset := mul(24, 32) + offset := add(ORIGIN_CACHE_OFFSET(), 32) } function COINBASE_CACHE_OFFSET() -> offset { - offset := mul(25, 32) + offset := add(GASPRICE_CACHE_OFFSET(), 32) } function BLOCKTIMESTAMP_CACHE_OFFSET() -> offset { - offset := mul(26, 32) + offset := add(COINBASE_CACHE_OFFSET(), 32) } function BLOCKNUMBER_CACHE_OFFSET() -> offset { - offset := mul(27, 32) + offset := add(BLOCKTIMESTAMP_CACHE_OFFSET(), 32) } function PREVRANDAO_CACHE_OFFSET() -> offset { - offset := mul(28, 32) + offset := add(BLOCKNUMBER_CACHE_OFFSET(), 32) } function GASLIMIT_CACHE_OFFSET() -> offset { - offset := mul(29, 32) + offset := add(PREVRANDAO_CACHE_OFFSET(), 32) } function CHAINID_CACHE_OFFSET() -> offset { - offset := mul(30, 32) + offset := add(GASLIMIT_CACHE_OFFSET(), 32) } function BASEFEE_CACHE_OFFSET() -> offset { - offset := mul(31, 32) + offset := add(CHAINID_CACHE_OFFSET(), 32) } function LAST_RETURNDATA_SIZE_OFFSET() -> offset { From 7328b8cfb5843ff364d68b22c5fd787f5f2538b6 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 30 Dec 2024 17:34:15 +0100 Subject: [PATCH 38/62] fix(EVM): Fix namings (N-02) (#1169) --- system-contracts/contracts/EvmEmulator.yul | 38 +++++++++---------- .../contracts/KnownCodesStorage.sol | 10 ++--- .../EvmEmulatorFunctions.template.yul | 10 ++--- .../evm-emulator/EvmEmulatorLoop.template.yul | 8 ++-- .../RuntimeScope.template.yul | 2 +- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index c69e8407a..86f799a27 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -187,7 +187,7 @@ object "EvmEmulator" { function OVERHEAD() -> overhead { overhead := 2000 } - function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 + function MAX_UINT32() -> ret { ret := 4294967295 } // 2^32 - 1 function EMPTY_KECCAK() -> value { // keccak("") value := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 @@ -290,7 +290,7 @@ object "EvmEmulator" { } } - function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> maxExpand { + function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> gasCost { let maxNewMemsize := _memsizeRequired(retOffset, retSize) let argsMemsize := _memsizeRequired(argsOffset, argsSize) @@ -299,7 +299,7 @@ object "EvmEmulator" { } if maxNewMemsize { // Memory expansion costs 0 if size is 0 - maxExpand := _expandMemoryInternal(maxNewMemsize) + gasCost := _expandMemoryInternal(maxNewMemsize) } } @@ -931,8 +931,8 @@ object "EvmEmulator" { zkEvmGasToPass := add(zkEvmGasToPass, additionalStipend) - if gt(zkEvmGasToPass, UINT32_MAX()) { // just in case - zkEvmGasToPass := UINT32_MAX() + if gt(zkEvmGasToPass, MAX_UINT32()) { // just in case + zkEvmGasToPass := MAX_UINT32() } let zkEvmGasBefore := gas() @@ -1710,14 +1710,14 @@ object "EvmEmulator" { dstOffset := add(dstOffset, MEM_OFFSET()) // EraVM will revert if offset + length overflows uint32 - if gt(sourceOffset, UINT32_MAX()) { - sourceOffset := UINT32_MAX() + if gt(sourceOffset, MAX_UINT32()) { + sourceOffset := MAX_UINT32() } // Check bytecode out-of-bounds access let truncatedLen := len - if gt(add(sourceOffset, len), UINT32_MAX()) { - truncatedLen := sub(UINT32_MAX(), sourceOffset) // truncate + if gt(add(sourceOffset, len), MAX_UINT32()) { + truncatedLen := sub(MAX_UINT32(), sourceOffset) // truncate $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, truncatedLen), sub(len, truncatedLen)) // pad with zeroes any out-of-bounds } @@ -3315,7 +3315,7 @@ object "EvmEmulator" { function OVERHEAD() -> overhead { overhead := 2000 } - function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 + function MAX_UINT32() -> ret { ret := 4294967295 } // 2^32 - 1 function EMPTY_KECCAK() -> value { // keccak("") value := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 @@ -3418,7 +3418,7 @@ object "EvmEmulator" { } } - function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> maxExpand { + function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> gasCost { let maxNewMemsize := _memsizeRequired(retOffset, retSize) let argsMemsize := _memsizeRequired(argsOffset, argsSize) @@ -3427,7 +3427,7 @@ object "EvmEmulator" { } if maxNewMemsize { // Memory expansion costs 0 if size is 0 - maxExpand := _expandMemoryInternal(maxNewMemsize) + gasCost := _expandMemoryInternal(maxNewMemsize) } } @@ -4059,8 +4059,8 @@ object "EvmEmulator" { zkEvmGasToPass := add(zkEvmGasToPass, additionalStipend) - if gt(zkEvmGasToPass, UINT32_MAX()) { // just in case - zkEvmGasToPass := UINT32_MAX() + if gt(zkEvmGasToPass, MAX_UINT32()) { // just in case + zkEvmGasToPass := MAX_UINT32() } let zkEvmGasBefore := gas() @@ -4826,14 +4826,14 @@ object "EvmEmulator" { dstOffset := add(dstOffset, MEM_OFFSET()) // EraVM will revert if offset + length overflows uint32 - if gt(sourceOffset, UINT32_MAX()) { - sourceOffset := UINT32_MAX() + if gt(sourceOffset, MAX_UINT32()) { + sourceOffset := MAX_UINT32() } // Check bytecode out-of-bounds access let truncatedLen := len - if gt(add(sourceOffset, len), UINT32_MAX()) { - truncatedLen := sub(UINT32_MAX(), sourceOffset) // truncate + if gt(add(sourceOffset, len), MAX_UINT32()) { + truncatedLen := sub(MAX_UINT32(), sourceOffset) // truncate $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, truncatedLen), sub(len, truncatedLen)) // pad with zeroes any out-of-bounds } @@ -6262,7 +6262,7 @@ object "EvmEmulator" { function $llvm_AlwaysInline_llvm$_calldataload(calldataOffset) -> res { // EraVM will revert if offset + length overflows uint32 - if lt(calldataOffset, UINT32_MAX()) { + if lt(calldataOffset, MAX_UINT32()) { res := calldataload(calldataOffset) } } diff --git a/system-contracts/contracts/KnownCodesStorage.sol b/system-contracts/contracts/KnownCodesStorage.sol index 463105a64..3f0ced702 100644 --- a/system-contracts/contracts/KnownCodesStorage.sol +++ b/system-contracts/contracts/KnownCodesStorage.sol @@ -93,20 +93,20 @@ contract KnownCodesStorage is IKnownCodesStorage, SystemContractBase { uint256 evmBytecodeLen, bytes calldata paddedBytecode ) external payable onlyCallFrom(address(DEPLOYER_SYSTEM_CONTRACT)) returns (bytes32) { - bytes32 vesionedBytecodeHash = Utils.hashEVMBytecode(evmBytecodeLen, paddedBytecode); + bytes32 versionedBytecodeHash = Utils.hashEVMBytecode(evmBytecodeLen, paddedBytecode); - if (getMarker(vesionedBytecodeHash) == 0) { + if (getMarker(versionedBytecodeHash) == 0) { L1_MESSENGER_CONTRACT.sendToL1(paddedBytecode); assembly { - sstore(vesionedBytecodeHash, 1) + sstore(versionedBytecodeHash, 1) } - emit MarkedAsKnown(vesionedBytecodeHash, false); + emit MarkedAsKnown(versionedBytecodeHash, false); } assembly { - mstore(0x0, vesionedBytecodeHash) + mstore(0x0, versionedBytecodeHash) return(0x0, 0x20) } } diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 7057e510b..5aa90eecc 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -127,7 +127,7 @@ function MSG_VALUE_SIMULATOR_STIPEND_GAS() -> gas_stipend { function OVERHEAD() -> overhead { overhead := 2000 } -function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 +function MAX_UINT32() -> ret { ret := 4294967295 } // 2^32 - 1 function EMPTY_KECCAK() -> value { // keccak("") value := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 @@ -230,7 +230,7 @@ function _memsizeRequired(offset, size) -> memorySize { } } -function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> maxExpand { +function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> gasCost { let maxNewMemsize := _memsizeRequired(retOffset, retSize) let argsMemsize := _memsizeRequired(argsOffset, argsSize) @@ -239,7 +239,7 @@ function expandMemory2(retOffset, retSize, argsOffset, argsSize) -> maxExpand { } if maxNewMemsize { // Memory expansion costs 0 if size is 0 - maxExpand := _expandMemoryInternal(maxNewMemsize) + gasCost := _expandMemoryInternal(maxNewMemsize) } } @@ -871,8 +871,8 @@ function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffs zkEvmGasToPass := add(zkEvmGasToPass, additionalStipend) - if gt(zkEvmGasToPass, UINT32_MAX()) { // just in case - zkEvmGasToPass := UINT32_MAX() + if gt(zkEvmGasToPass, MAX_UINT32()) { // just in case + zkEvmGasToPass := MAX_UINT32() } let zkEvmGasBefore := gas() diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index 58e959514..c9700c5d9 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -373,14 +373,14 @@ for { } true { } { dstOffset := add(dstOffset, MEM_OFFSET()) // EraVM will revert if offset + length overflows uint32 - if gt(sourceOffset, UINT32_MAX()) { - sourceOffset := UINT32_MAX() + if gt(sourceOffset, MAX_UINT32()) { + sourceOffset := MAX_UINT32() } // Check bytecode out-of-bounds access let truncatedLen := len - if gt(add(sourceOffset, len), UINT32_MAX()) { - truncatedLen := sub(UINT32_MAX(), sourceOffset) // truncate + if gt(add(sourceOffset, len), MAX_UINT32()) { + truncatedLen := sub(MAX_UINT32(), sourceOffset) // truncate $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, truncatedLen), sub(len, truncatedLen)) // pad with zeroes any out-of-bounds } diff --git a/system-contracts/evm-emulator/calldata-opcodes/RuntimeScope.template.yul b/system-contracts/evm-emulator/calldata-opcodes/RuntimeScope.template.yul index 51c88e7b5..7f02e012f 100644 --- a/system-contracts/evm-emulator/calldata-opcodes/RuntimeScope.template.yul +++ b/system-contracts/evm-emulator/calldata-opcodes/RuntimeScope.template.yul @@ -8,7 +8,7 @@ function $llvm_AlwaysInline_llvm$_calldatacopy(dstOffset, sourceOffset, truncate function $llvm_AlwaysInline_llvm$_calldataload(calldataOffset) -> res { // EraVM will revert if offset + length overflows uint32 - if lt(calldataOffset, UINT32_MAX()) { + if lt(calldataOffset, MAX_UINT32()) { res := calldataload(calldataOffset) } } \ No newline at end of file From ba441accca5538799437a62dea0ca5b0161a8cc5 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 30 Dec 2024 17:36:14 +0100 Subject: [PATCH 39/62] fix(EVM): Fix misleading comment (N-03) (#1170) --- system-contracts/contracts/EvmEmulator.yul | 4 ++-- .../evm-emulator/EvmEmulatorFunctions.template.yul | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 86f799a27..336eccf05 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -1058,7 +1058,7 @@ object "EvmEmulator" { mstore(LAST_RETURNDATA_SIZE_OFFSET(), sub(rtsz, 32)) - // Skip the returnData + // Skip first 32 bytes of the returnData ptrAddIntoActive(32) } } @@ -4186,7 +4186,7 @@ object "EvmEmulator" { mstore(LAST_RETURNDATA_SIZE_OFFSET(), sub(rtsz, 32)) - // Skip the returnData + // Skip first 32 bytes of the returnData ptrAddIntoActive(32) } } diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 5aa90eecc..ec695f76b 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -998,7 +998,7 @@ function _saveReturndataAfterEVMCall(_outputOffset, _outputLen) -> _gasLeft { mstore(LAST_RETURNDATA_SIZE_OFFSET(), sub(rtsz, 32)) - // Skip the returnData + // Skip first 32 bytes of the returnData ptrAddIntoActive(32) } } From 2947cabe1ede07b3c3a1c68ef4c7f2a677622b21 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 30 Dec 2024 18:50:38 +0100 Subject: [PATCH 40/62] fix(EVM): Gas optimizations (N-04) (#1171) --- system-contracts/contracts/EvmEmulator.yul | 44 +++++++++---------- .../contracts/precompiles/CodeOracle.yul | 2 +- .../EvmEmulatorFunctions.template.yul | 12 ++--- .../evm-emulator/EvmEmulatorLoop.template.yul | 10 ++--- 4 files changed, 31 insertions(+), 37 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 336eccf05..f54309ed7 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -258,7 +258,7 @@ object "EvmEmulator" { let oldSizeInWords := mload(MEM_LEN_OFFSET()) // div rounding up - let newSizeInWords := div(add(newMemsize, 31), 32) + let newSizeInWords := shr(5, add(newMemsize, 31)) // memory_size_word = (memory_byte_size + 31) / 32 // memory_cost = (memory_size_word ** 2) / 512 + (3 * memory_size_word) @@ -266,13 +266,13 @@ object "EvmEmulator" { if gt(newSizeInWords, oldSizeInWords) { let linearPart := mul(3, sub(newSizeInWords, oldSizeInWords)) let quadraticPart := sub( - div( + shr( + 9, mul(newSizeInWords, newSizeInWords), - 512 ), - div( + shr( + 9, mul(oldSizeInWords, oldSizeInWords), - 512 ) ) @@ -1105,7 +1105,7 @@ object "EvmEmulator" { // minimum_word_size = (size + 31) / 32 // init_code_cost = 2 * minimum_word_size, EIP-3860 // code_deposit_cost = 200 * deployed_code_size, (charged inside call) - let minimum_word_size := div(add(size, 31), 32) // rounding up + let minimum_word_size := shr(5, add(size, 31)) // rounding up let dynamicGas := add( mul(2, minimum_word_size), expandMemory(offset, size) @@ -2224,7 +2224,7 @@ object "EvmEmulator" { // dynamic_gas = 3 * words_copied + memory_expansion_cost let dynamicGas := expandMemory2(offset, size, destOffset, size) - let wordsCopied := div(add(size, 31), 32) // div rounding up + let wordsCopied := shr(5, add(size, 31)) // div rounding up dynamicGas := add(dynamicGas, mul(3, wordsCopied)) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) @@ -2731,8 +2731,7 @@ object "EvmEmulator" { let offset, size popStackCheck(sp, 2) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + offset, sp, size := popStackItemWithoutCheck(sp, stackHead) if size { evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) @@ -2764,11 +2763,10 @@ object "EvmEmulator" { ip := add(ip, 1) } case 0xFD { // OP_REVERT - let offset,size + let offset, size popStackCheck(sp, 2) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + offset, sp, size := popStackItemWithoutCheck(sp, stackHead) switch iszero(size) case 0 { @@ -3386,7 +3384,7 @@ object "EvmEmulator" { let oldSizeInWords := mload(MEM_LEN_OFFSET()) // div rounding up - let newSizeInWords := div(add(newMemsize, 31), 32) + let newSizeInWords := shr(5, add(newMemsize, 31)) // memory_size_word = (memory_byte_size + 31) / 32 // memory_cost = (memory_size_word ** 2) / 512 + (3 * memory_size_word) @@ -3394,13 +3392,13 @@ object "EvmEmulator" { if gt(newSizeInWords, oldSizeInWords) { let linearPart := mul(3, sub(newSizeInWords, oldSizeInWords)) let quadraticPart := sub( - div( + shr( + 9, mul(newSizeInWords, newSizeInWords), - 512 ), - div( + shr( + 9, mul(oldSizeInWords, oldSizeInWords), - 512 ) ) @@ -4233,7 +4231,7 @@ object "EvmEmulator" { // minimum_word_size = (size + 31) / 32 // init_code_cost = 2 * minimum_word_size, EIP-3860 // code_deposit_cost = 200 * deployed_code_size, (charged inside call) - let minimum_word_size := div(add(size, 31), 32) // rounding up + let minimum_word_size := shr(5, add(size, 31)) // rounding up let dynamicGas := add( mul(2, minimum_word_size), expandMemory(offset, size) @@ -5340,7 +5338,7 @@ object "EvmEmulator" { // dynamic_gas = 3 * words_copied + memory_expansion_cost let dynamicGas := expandMemory2(offset, size, destOffset, size) - let wordsCopied := div(add(size, 31), 32) // div rounding up + let wordsCopied := shr(5, add(size, 31)) // div rounding up dynamicGas := add(dynamicGas, mul(3, wordsCopied)) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) @@ -5847,8 +5845,7 @@ object "EvmEmulator" { let offset, size popStackCheck(sp, 2) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + offset, sp, size := popStackItemWithoutCheck(sp, stackHead) if size { evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) @@ -5880,11 +5877,10 @@ object "EvmEmulator" { ip := add(ip, 1) } case 0xFD { // OP_REVERT - let offset,size + let offset, size popStackCheck(sp, 2) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + offset, sp, size := popStackItemWithoutCheck(sp, stackHead) switch iszero(size) case 0 { diff --git a/system-contracts/contracts/precompiles/CodeOracle.yul b/system-contracts/contracts/precompiles/CodeOracle.yul index 87145d3be..aa6d17563 100644 --- a/system-contracts/contracts/precompiles/CodeOracle.yul +++ b/system-contracts/contracts/precompiles/CodeOracle.yul @@ -163,7 +163,7 @@ object "CodeOracle" { let lengthInBytes := and(shr(224, versionedCodeHash), 0xffff) let paddedLengthInBytes := paddedBytecodeLen(lengthInBytes) - decommit(versionedCodeHash, div(paddedLengthInBytes, 32)) + decommit(versionedCodeHash, shr(5, paddedLengthInBytes)) } default { // Unsupported diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index ec695f76b..17f2ea906 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -198,7 +198,7 @@ function _expandMemoryInternal(newMemsize) -> gasCost { let oldSizeInWords := mload(MEM_LEN_OFFSET()) // div rounding up - let newSizeInWords := div(add(newMemsize, 31), 32) + let newSizeInWords := shr(5, add(newMemsize, 31)) // memory_size_word = (memory_byte_size + 31) / 32 // memory_cost = (memory_size_word ** 2) / 512 + (3 * memory_size_word) @@ -206,13 +206,13 @@ function _expandMemoryInternal(newMemsize) -> gasCost { if gt(newSizeInWords, oldSizeInWords) { let linearPart := mul(3, sub(newSizeInWords, oldSizeInWords)) let quadraticPart := sub( - div( + shr( + 9, mul(newSizeInWords, newSizeInWords), - 512 ), - div( + shr( + 9, mul(oldSizeInWords, oldSizeInWords), - 512 ) ) @@ -1045,7 +1045,7 @@ function $llvm_NoInline_llvm$_genericCreate(offset, size, value, evmGasLeftOld, // minimum_word_size = (size + 31) / 32 // init_code_cost = 2 * minimum_word_size, EIP-3860 // code_deposit_cost = 200 * deployed_code_size, (charged inside call) - let minimum_word_size := div(add(size, 31), 32) // rounding up + let minimum_word_size := shr(5, add(size, 31)) // rounding up let dynamicGas := add( mul(2, minimum_word_size), expandMemory(offset, size) diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index c9700c5d9..a8fabaf7b 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -887,7 +887,7 @@ for { } true { } { // dynamic_gas = 3 * words_copied + memory_expansion_cost let dynamicGas := expandMemory2(offset, size, destOffset, size) - let wordsCopied := div(add(size, 31), 32) // div rounding up + let wordsCopied := shr(5, add(size, 31)) // div rounding up dynamicGas := add(dynamicGas, mul(3, wordsCopied)) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) @@ -1394,8 +1394,7 @@ for { } true { } { let offset, size popStackCheck(sp, 2) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + offset, sp, size := popStackItemWithoutCheck(sp, stackHead) if size { evmGasLeft := chargeGas(evmGasLeft, expandMemory(offset, size)) @@ -1427,11 +1426,10 @@ for { } true { } { ip := add(ip, 1) } case 0xFD { // OP_REVERT - let offset,size + let offset, size popStackCheck(sp, 2) - offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + offset, sp, size := popStackItemWithoutCheck(sp, stackHead) switch iszero(size) case 0 { From 20bcb74662b113b53f296964deb0eba5deeedffb Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 30 Dec 2024 19:09:40 +0100 Subject: [PATCH 41/62] fix(EVM): Remove deadcode (N-06) (#1172) --- system-contracts/contracts/EvmEmulator.yul | 50 ------------------- .../EvmEmulatorFunctions.template.yul | 25 ---------- 2 files changed, 75 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index f54309ed7..4eb5dd54d 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -180,11 +180,6 @@ object "EvmEmulator" { // Each evm gas is 5 zkEVM one function GAS_DIVISOR() -> gas_div { gas_div := 5 } - // We need to pass some gas for MsgValueSimulator internal logic to decommit emulator etc - function MSG_VALUE_SIMULATOR_STIPEND_GAS() -> gas_stipend { - gas_stipend := 35000 // 27000 + a little bit more - } - function OVERHEAD() -> overhead { overhead := 2000 } function MAX_UINT32() -> ret { ret := 4294967295 } // 2^32 - 1 @@ -405,13 +400,6 @@ object "EvmEmulator" { evmCodeHash := fetchFromSystemContract(DEPLOYER_SYSTEM_CONTRACT(), 36) } - function isEvmContract(addr) -> isEVM { - // function isAccountEVM(address addr) external view returns (bool); - mstore(0, 0x8C04047700000000000000000000000000000000000000000000000000000000) - mstore(4, addr) - isEVM := fetchFromSystemContract(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 36) - } - function isHashOfConstructedEvmContract(rawCodeHash) -> isConstructedEVM { let version := shr(248, rawCodeHash) let isConstructedFlag := xor(shr(240, rawCodeHash), 1) @@ -455,13 +443,6 @@ object "EvmEmulator" { } } - function getMax(a, b) -> max { - max := b - if gt(a, b) { - max := a - } - } - function build_farcall_abi(isSystemCall, gas, dataStart, dataLength) -> farCallAbi { farCallAbi := shl(248, isSystemCall) // dataOffset is 0 @@ -576,12 +557,6 @@ object "EvmEmulator" { stackHead := mload(newSp) } - function pushStackItemWithoutCheck(sp, item, oldStackHead) -> newSp, stackHead { - mstore(sp, oldStackHead) - stackHead := item - newSp := add(sp, 0x20) - } - function popStackCheck(sp, numInputs) { if lt(sub(sp, mul(0x20, sub(numInputs, 1))), STACK_OFFSET()) { panic() @@ -3306,11 +3281,6 @@ object "EvmEmulator" { // Each evm gas is 5 zkEVM one function GAS_DIVISOR() -> gas_div { gas_div := 5 } - // We need to pass some gas for MsgValueSimulator internal logic to decommit emulator etc - function MSG_VALUE_SIMULATOR_STIPEND_GAS() -> gas_stipend { - gas_stipend := 35000 // 27000 + a little bit more - } - function OVERHEAD() -> overhead { overhead := 2000 } function MAX_UINT32() -> ret { ret := 4294967295 } // 2^32 - 1 @@ -3531,13 +3501,6 @@ object "EvmEmulator" { evmCodeHash := fetchFromSystemContract(DEPLOYER_SYSTEM_CONTRACT(), 36) } - function isEvmContract(addr) -> isEVM { - // function isAccountEVM(address addr) external view returns (bool); - mstore(0, 0x8C04047700000000000000000000000000000000000000000000000000000000) - mstore(4, addr) - isEVM := fetchFromSystemContract(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 36) - } - function isHashOfConstructedEvmContract(rawCodeHash) -> isConstructedEVM { let version := shr(248, rawCodeHash) let isConstructedFlag := xor(shr(240, rawCodeHash), 1) @@ -3581,13 +3544,6 @@ object "EvmEmulator" { } } - function getMax(a, b) -> max { - max := b - if gt(a, b) { - max := a - } - } - function build_farcall_abi(isSystemCall, gas, dataStart, dataLength) -> farCallAbi { farCallAbi := shl(248, isSystemCall) // dataOffset is 0 @@ -3702,12 +3658,6 @@ object "EvmEmulator" { stackHead := mload(newSp) } - function pushStackItemWithoutCheck(sp, item, oldStackHead) -> newSp, stackHead { - mstore(sp, oldStackHead) - stackHead := item - newSp := add(sp, 0x20) - } - function popStackCheck(sp, numInputs) { if lt(sub(sp, mul(0x20, sub(numInputs, 1))), STACK_OFFSET()) { panic() diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 17f2ea906..ae43ddaab 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -120,11 +120,6 @@ function MAX_UINT64() -> max { // Each evm gas is 5 zkEVM one function GAS_DIVISOR() -> gas_div { gas_div := 5 } -// We need to pass some gas for MsgValueSimulator internal logic to decommit emulator etc -function MSG_VALUE_SIMULATOR_STIPEND_GAS() -> gas_stipend { - gas_stipend := 35000 // 27000 + a little bit more -} - function OVERHEAD() -> overhead { overhead := 2000 } function MAX_UINT32() -> ret { ret := 4294967295 } // 2^32 - 1 @@ -345,13 +340,6 @@ function getEvmExtcodehash(addr) -> evmCodeHash { evmCodeHash := fetchFromSystemContract(DEPLOYER_SYSTEM_CONTRACT(), 36) } -function isEvmContract(addr) -> isEVM { - // function isAccountEVM(address addr) external view returns (bool); - mstore(0, 0x8C04047700000000000000000000000000000000000000000000000000000000) - mstore(4, addr) - isEVM := fetchFromSystemContract(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 36) -} - function isHashOfConstructedEvmContract(rawCodeHash) -> isConstructedEVM { let version := shr(248, rawCodeHash) let isConstructedFlag := xor(shr(240, rawCodeHash), 1) @@ -395,13 +383,6 @@ function fetchDeployedCode(addr, dstOffset, srcOffset, len) -> copiedLen { } } -function getMax(a, b) -> max { - max := b - if gt(a, b) { - max := a - } -} - function build_farcall_abi(isSystemCall, gas, dataStart, dataLength) -> farCallAbi { farCallAbi := shl(248, isSystemCall) // dataOffset is 0 @@ -516,12 +497,6 @@ function popStackItemWithoutCheck(sp, oldStackHead) -> a, newSp, stackHead { stackHead := mload(newSp) } -function pushStackItemWithoutCheck(sp, item, oldStackHead) -> newSp, stackHead { - mstore(sp, oldStackHead) - stackHead := item - newSp := add(sp, 0x20) -} - function popStackCheck(sp, numInputs) { if lt(sub(sp, mul(0x20, sub(numInputs, 1))), STACK_OFFSET()) { panic() From ad62263bd4a3ed50ce62ebef4648976dd98175db Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 30 Dec 2024 19:11:22 +0100 Subject: [PATCH 42/62] fix(EVM): Simplify set constructing bytecode hash (N-07) (#1173) --- system-contracts/contracts/ContractDeployer.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-contracts/contracts/ContractDeployer.sol b/system-contracts/contracts/ContractDeployer.sol index f909e3fb0..6a41d91cd 100644 --- a/system-contracts/contracts/ContractDeployer.sol +++ b/system-contracts/contracts/ContractDeployer.sol @@ -567,7 +567,7 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { } // 2. Set the constructed code hash on the account - _storeConstructingByteCodeHashOnAddress( + ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.storeAccountConstructingCodeHash( _newAddress, // Dummy EVM bytecode hash just to call emulator. // The second byte is `0x01` to indicate that it is being constructed. From ddea0fc480a01d9907759860bf3aca3a00c1936a Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 2 Jan 2025 12:48:10 +0100 Subject: [PATCH 43/62] fix(EVM): Code simplification (N-05) (#1174) --- .../chain-deps/facets/Executor.sol | 6 +- system-contracts/contracts/Constants.sol | 17 +- system-contracts/contracts/EvmEmulator.yul | 542 +++--------------- .../EvmEmulatorFunctions.template.yul | 10 + .../evm-emulator/EvmEmulatorLoop.template.yul | 261 ++------- 5 files changed, 140 insertions(+), 696 deletions(-) diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol index bcef798f4..739cc6477 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol @@ -599,14 +599,12 @@ contract ExecutorFacet is ZkSyncHyperchainBase, IExecutor { } function _batchMetaParameters() internal view returns (bytes memory) { - bytes32 l2DefaultAccountBytecodeHash = s.l2DefaultAccountBytecodeHash; - bytes32 l2EvmEmulatorBytecodeHash = s.l2EvmEmulatorBytecodeHash; return abi.encodePacked( s.zkPorterIsAvailable, s.l2BootloaderBytecodeHash, - l2DefaultAccountBytecodeHash, - l2EvmEmulatorBytecodeHash + s.l2DefaultAccountBytecodeHash, + s.l2EvmEmulatorBytecodeHash ); } diff --git a/system-contracts/contracts/Constants.sol b/system-contracts/contracts/Constants.sol index fbaabc091..eb713a518 100644 --- a/system-contracts/contracts/Constants.sol +++ b/system-contracts/contracts/Constants.sol @@ -35,8 +35,6 @@ address constant ECADD_SYSTEM_CONTRACT = address(0x06); address constant ECMUL_SYSTEM_CONTRACT = address(0x07); address constant ECPAIRING_SYSTEM_CONTRACT = address(0x08); -address constant CODE_ORACLE_SYSTEM_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x12); - /// @dev The number of gas that need to be spent for a single byte of pubdata regardless of the pubdata price. /// This variable is used to ensure the following: /// - That the long-term storage of the operator is compensated properly. @@ -69,11 +67,6 @@ address constant MSG_VALUE_SYSTEM_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0 IBaseToken constant BASE_TOKEN_SYSTEM_CONTRACT = IBaseToken(address(SYSTEM_CONTRACTS_OFFSET + 0x0a)); IBaseToken constant REAL_BASE_TOKEN_SYSTEM_CONTRACT = IBaseToken(address(REAL_SYSTEM_CONTRACTS_OFFSET + 0x0a)); -// Hardcoded because even for tests we should keep the address. (Instead `SYSTEM_CONTRACTS_OFFSET + 0x10`) -// Precompile call depends on it. -// And we don't want to mock this contract. -address constant KECCAK256_SYSTEM_CONTRACT = address(0x8010); - ISystemContext constant SYSTEM_CONTEXT_CONTRACT = ISystemContext(payable(address(SYSTEM_CONTRACTS_OFFSET + 0x0b))); ISystemContext constant REAL_SYSTEM_CONTEXT_CONTRACT = ISystemContext(payable(address(REAL_SYSTEM_CONTRACTS_OFFSET + 0x0b))); @@ -85,12 +78,20 @@ address constant EVENT_WRITER_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x0d) ICompressor constant COMPRESSOR_CONTRACT = ICompressor(address(SYSTEM_CONTRACTS_OFFSET + 0x0e)); IComplexUpgrader constant COMPLEX_UPGRADER_CONTRACT = IComplexUpgrader(address(SYSTEM_CONTRACTS_OFFSET + 0x0f)); -address constant EVM_GAS_MANAGER = address(SYSTEM_CONTRACTS_OFFSET + 0x13); + +// Hardcoded because even for tests we should keep the address. (Instead `SYSTEM_CONTRACTS_OFFSET + 0x10`) +// Precompile call depends on it. +// And we don't want to mock this contract. +address constant KECCAK256_SYSTEM_CONTRACT = address(0x8010); IPubdataChunkPublisher constant PUBDATA_CHUNK_PUBLISHER = IPubdataChunkPublisher( address(SYSTEM_CONTRACTS_OFFSET + 0x11) ); +address constant CODE_ORACLE_SYSTEM_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x12); + +address constant EVM_GAS_MANAGER = address(SYSTEM_CONTRACTS_OFFSET + 0x13); + /// @dev If the bitwise AND of the extraAbi[2] param when calling the MSG_VALUE_SIMULATOR /// is non-zero, the call will be assumed to be a system one. uint256 constant MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT = 1; diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 4eb5dd54d..1cf6c3bf0 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -500,6 +500,16 @@ object "EvmEmulator" { // STACK OPERATIONS //////////////////////////////////////////////////////////////// + function pushOpcodeInner(size, ip, sp, evmGas, oldStackHead) -> newIp, newSp, evmGasLeft, stackHead { + evmGasLeft := chargeGas(evmGas, 3) + + newIp := add(ip, 1) + let value := readBytes(newIp, size) + + newSp, stackHead := pushStackItem(sp, value, oldStackHead) + newIp := add(newIp, size) + } + function dupStackItem(sp, evmGas, position, oldStackHead) -> newSp, evmGasLeft, stackHead { evmGasLeft := chargeGas(evmGas, 3) @@ -2209,299 +2219,104 @@ object "EvmEmulator" { } case 0x5F { // OP_PUSH0 evmGasLeft := chargeGas(evmGasLeft, 2) - - let value := 0 - - sp, stackHead := pushStackItem(sp, value, stackHead) + sp, stackHead := pushStackItem(sp, 0, stackHead) ip := add(ip, 1) } case 0x60 { // OP_PUSH1 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 1) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 1) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(1, ip, sp, evmGasLeft, stackHead) } case 0x61 { // OP_PUSH2 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 2) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 2) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(2, ip, sp, evmGasLeft, stackHead) } case 0x62 { // OP_PUSH3 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 3) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 3) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(3, ip, sp, evmGasLeft, stackHead) } case 0x63 { // OP_PUSH4 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 4) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 4) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(4, ip, sp, evmGasLeft, stackHead) } case 0x64 { // OP_PUSH5 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 5) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 5) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(5, ip, sp, evmGasLeft, stackHead) } case 0x65 { // OP_PUSH6 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 6) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 6) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(6, ip, sp, evmGasLeft, stackHead) } case 0x66 { // OP_PUSH7 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 7) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 7) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(7, ip, sp, evmGasLeft, stackHead) } case 0x67 { // OP_PUSH8 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 8) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 8) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(8, ip, sp, evmGasLeft, stackHead) } case 0x68 { // OP_PUSH9 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 9) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 9) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(9, ip, sp, evmGasLeft, stackHead) } case 0x69 { // OP_PUSH10 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 10) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 10) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(10, ip, sp, evmGasLeft, stackHead) } case 0x6A { // OP_PUSH11 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 11) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 11) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(11, ip, sp, evmGasLeft, stackHead) } case 0x6B { // OP_PUSH12 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 12) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 12) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(12, ip, sp, evmGasLeft, stackHead) } case 0x6C { // OP_PUSH13 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 13) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 13) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(13, ip, sp, evmGasLeft, stackHead) } case 0x6D { // OP_PUSH14 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 14) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 14) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(14, ip, sp, evmGasLeft, stackHead) } case 0x6E { // OP_PUSH15 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 15) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 15) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(15, ip, sp, evmGasLeft, stackHead) } case 0x6F { // OP_PUSH16 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 16) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 16) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(16, ip, sp, evmGasLeft, stackHead) } case 0x70 { // OP_PUSH17 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 17) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 17) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(17, ip, sp, evmGasLeft, stackHead) } case 0x71 { // OP_PUSH18 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 18) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 18) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(18, ip, sp, evmGasLeft, stackHead) } case 0x72 { // OP_PUSH19 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 19) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 19) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(19, ip, sp, evmGasLeft, stackHead) } case 0x73 { // OP_PUSH20 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 20) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 20) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(20, ip, sp, evmGasLeft, stackHead) } case 0x74 { // OP_PUSH21 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 21) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 21) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(21, ip, sp, evmGasLeft, stackHead) } case 0x75 { // OP_PUSH22 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 22) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 22) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(22, ip, sp, evmGasLeft, stackHead) } case 0x76 { // OP_PUSH23 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 23) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 23) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(23, ip, sp, evmGasLeft, stackHead) } case 0x77 { // OP_PUSH24 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 24) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 24) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(24, ip, sp, evmGasLeft, stackHead) } case 0x78 { // OP_PUSH25 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 25) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 25) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(25, ip, sp, evmGasLeft, stackHead) } case 0x79 { // OP_PUSH26 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 26) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 26) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(26, ip, sp, evmGasLeft, stackHead) } case 0x7A { // OP_PUSH27 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 27) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 27) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(27, ip, sp, evmGasLeft, stackHead) } case 0x7B { // OP_PUSH28 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 28) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 28) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(28, ip, sp, evmGasLeft, stackHead) } case 0x7C { // OP_PUSH29 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 29) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 29) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(29, ip, sp, evmGasLeft, stackHead) } case 0x7D { // OP_PUSH30 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 30) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 30) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(30, ip, sp, evmGasLeft, stackHead) } case 0x7E { // OP_PUSH31 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 31) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 31) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(31, ip, sp, evmGasLeft, stackHead) } case 0x7F { // OP_PUSH32 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 32) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 32) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(32, ip, sp, evmGasLeft, stackHead) } case 0x80 { // OP_DUP1 evmGasLeft := chargeGas(evmGasLeft, 3) @@ -3601,6 +3416,16 @@ object "EvmEmulator" { // STACK OPERATIONS //////////////////////////////////////////////////////////////// + function pushOpcodeInner(size, ip, sp, evmGas, oldStackHead) -> newIp, newSp, evmGasLeft, stackHead { + evmGasLeft := chargeGas(evmGas, 3) + + newIp := add(ip, 1) + let value := readBytes(newIp, size) + + newSp, stackHead := pushStackItem(sp, value, oldStackHead) + newIp := add(newIp, size) + } + function dupStackItem(sp, evmGas, position, oldStackHead) -> newSp, evmGasLeft, stackHead { evmGasLeft := chargeGas(evmGas, 3) @@ -5298,299 +5123,104 @@ object "EvmEmulator" { } case 0x5F { // OP_PUSH0 evmGasLeft := chargeGas(evmGasLeft, 2) - - let value := 0 - - sp, stackHead := pushStackItem(sp, value, stackHead) + sp, stackHead := pushStackItem(sp, 0, stackHead) ip := add(ip, 1) } case 0x60 { // OP_PUSH1 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 1) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 1) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(1, ip, sp, evmGasLeft, stackHead) } case 0x61 { // OP_PUSH2 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 2) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 2) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(2, ip, sp, evmGasLeft, stackHead) } case 0x62 { // OP_PUSH3 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 3) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 3) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(3, ip, sp, evmGasLeft, stackHead) } case 0x63 { // OP_PUSH4 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 4) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 4) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(4, ip, sp, evmGasLeft, stackHead) } case 0x64 { // OP_PUSH5 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 5) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 5) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(5, ip, sp, evmGasLeft, stackHead) } case 0x65 { // OP_PUSH6 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 6) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 6) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(6, ip, sp, evmGasLeft, stackHead) } case 0x66 { // OP_PUSH7 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 7) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 7) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(7, ip, sp, evmGasLeft, stackHead) } case 0x67 { // OP_PUSH8 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 8) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 8) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(8, ip, sp, evmGasLeft, stackHead) } case 0x68 { // OP_PUSH9 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 9) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 9) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(9, ip, sp, evmGasLeft, stackHead) } case 0x69 { // OP_PUSH10 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 10) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 10) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(10, ip, sp, evmGasLeft, stackHead) } case 0x6A { // OP_PUSH11 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 11) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 11) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(11, ip, sp, evmGasLeft, stackHead) } case 0x6B { // OP_PUSH12 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 12) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 12) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(12, ip, sp, evmGasLeft, stackHead) } case 0x6C { // OP_PUSH13 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 13) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 13) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(13, ip, sp, evmGasLeft, stackHead) } case 0x6D { // OP_PUSH14 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 14) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 14) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(14, ip, sp, evmGasLeft, stackHead) } case 0x6E { // OP_PUSH15 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 15) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 15) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(15, ip, sp, evmGasLeft, stackHead) } case 0x6F { // OP_PUSH16 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 16) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 16) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(16, ip, sp, evmGasLeft, stackHead) } case 0x70 { // OP_PUSH17 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 17) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 17) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(17, ip, sp, evmGasLeft, stackHead) } case 0x71 { // OP_PUSH18 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 18) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 18) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(18, ip, sp, evmGasLeft, stackHead) } case 0x72 { // OP_PUSH19 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 19) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 19) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(19, ip, sp, evmGasLeft, stackHead) } case 0x73 { // OP_PUSH20 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 20) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 20) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(20, ip, sp, evmGasLeft, stackHead) } case 0x74 { // OP_PUSH21 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 21) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 21) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(21, ip, sp, evmGasLeft, stackHead) } case 0x75 { // OP_PUSH22 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 22) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 22) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(22, ip, sp, evmGasLeft, stackHead) } case 0x76 { // OP_PUSH23 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 23) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 23) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(23, ip, sp, evmGasLeft, stackHead) } case 0x77 { // OP_PUSH24 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 24) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 24) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(24, ip, sp, evmGasLeft, stackHead) } case 0x78 { // OP_PUSH25 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 25) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 25) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(25, ip, sp, evmGasLeft, stackHead) } case 0x79 { // OP_PUSH26 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 26) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 26) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(26, ip, sp, evmGasLeft, stackHead) } case 0x7A { // OP_PUSH27 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 27) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 27) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(27, ip, sp, evmGasLeft, stackHead) } case 0x7B { // OP_PUSH28 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 28) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 28) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(28, ip, sp, evmGasLeft, stackHead) } case 0x7C { // OP_PUSH29 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 29) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 29) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(29, ip, sp, evmGasLeft, stackHead) } case 0x7D { // OP_PUSH30 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 30) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 30) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(30, ip, sp, evmGasLeft, stackHead) } case 0x7E { // OP_PUSH31 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 31) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 31) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(31, ip, sp, evmGasLeft, stackHead) } case 0x7F { // OP_PUSH32 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 32) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 32) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(32, ip, sp, evmGasLeft, stackHead) } case 0x80 { // OP_DUP1 evmGasLeft := chargeGas(evmGasLeft, 3) diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index ae43ddaab..dc8e64295 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -440,6 +440,16 @@ function rawStaticcall(gas, to, dataStart, dataLength, outputOffset, outputLen) // STACK OPERATIONS //////////////////////////////////////////////////////////////// +function pushOpcodeInner(size, ip, sp, evmGas, oldStackHead) -> newIp, newSp, evmGasLeft, stackHead { + evmGasLeft := chargeGas(evmGas, 3) + + newIp := add(ip, 1) + let value := readBytes(newIp, size) + + newSp, stackHead := pushStackItem(sp, value, oldStackHead) + newIp := add(newIp, size) +} + function dupStackItem(sp, evmGas, position, oldStackHead) -> newSp, evmGasLeft, stackHead { evmGasLeft := chargeGas(evmGas, 3) diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index a8fabaf7b..762312df0 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -897,299 +897,104 @@ for { } true { } { } case 0x5F { // OP_PUSH0 evmGasLeft := chargeGas(evmGasLeft, 2) - - let value := 0 - - sp, stackHead := pushStackItem(sp, value, stackHead) + sp, stackHead := pushStackItem(sp, 0, stackHead) ip := add(ip, 1) } case 0x60 { // OP_PUSH1 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 1) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 1) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(1, ip, sp, evmGasLeft, stackHead) } case 0x61 { // OP_PUSH2 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 2) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 2) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(2, ip, sp, evmGasLeft, stackHead) } case 0x62 { // OP_PUSH3 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 3) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 3) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(3, ip, sp, evmGasLeft, stackHead) } case 0x63 { // OP_PUSH4 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 4) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 4) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(4, ip, sp, evmGasLeft, stackHead) } case 0x64 { // OP_PUSH5 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 5) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 5) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(5, ip, sp, evmGasLeft, stackHead) } case 0x65 { // OP_PUSH6 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 6) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 6) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(6, ip, sp, evmGasLeft, stackHead) } case 0x66 { // OP_PUSH7 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 7) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 7) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(7, ip, sp, evmGasLeft, stackHead) } case 0x67 { // OP_PUSH8 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 8) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 8) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(8, ip, sp, evmGasLeft, stackHead) } case 0x68 { // OP_PUSH9 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 9) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 9) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(9, ip, sp, evmGasLeft, stackHead) } case 0x69 { // OP_PUSH10 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 10) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 10) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(10, ip, sp, evmGasLeft, stackHead) } case 0x6A { // OP_PUSH11 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 11) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 11) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(11, ip, sp, evmGasLeft, stackHead) } case 0x6B { // OP_PUSH12 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 12) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 12) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(12, ip, sp, evmGasLeft, stackHead) } case 0x6C { // OP_PUSH13 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 13) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 13) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(13, ip, sp, evmGasLeft, stackHead) } case 0x6D { // OP_PUSH14 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 14) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 14) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(14, ip, sp, evmGasLeft, stackHead) } case 0x6E { // OP_PUSH15 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 15) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 15) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(15, ip, sp, evmGasLeft, stackHead) } case 0x6F { // OP_PUSH16 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 16) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 16) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(16, ip, sp, evmGasLeft, stackHead) } case 0x70 { // OP_PUSH17 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 17) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 17) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(17, ip, sp, evmGasLeft, stackHead) } case 0x71 { // OP_PUSH18 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 18) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 18) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(18, ip, sp, evmGasLeft, stackHead) } case 0x72 { // OP_PUSH19 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 19) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 19) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(19, ip, sp, evmGasLeft, stackHead) } case 0x73 { // OP_PUSH20 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 20) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 20) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(20, ip, sp, evmGasLeft, stackHead) } case 0x74 { // OP_PUSH21 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 21) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 21) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(21, ip, sp, evmGasLeft, stackHead) } case 0x75 { // OP_PUSH22 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 22) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 22) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(22, ip, sp, evmGasLeft, stackHead) } case 0x76 { // OP_PUSH23 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 23) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 23) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(23, ip, sp, evmGasLeft, stackHead) } case 0x77 { // OP_PUSH24 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 24) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 24) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(24, ip, sp, evmGasLeft, stackHead) } case 0x78 { // OP_PUSH25 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 25) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 25) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(25, ip, sp, evmGasLeft, stackHead) } case 0x79 { // OP_PUSH26 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 26) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 26) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(26, ip, sp, evmGasLeft, stackHead) } case 0x7A { // OP_PUSH27 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 27) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 27) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(27, ip, sp, evmGasLeft, stackHead) } case 0x7B { // OP_PUSH28 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 28) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 28) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(28, ip, sp, evmGasLeft, stackHead) } case 0x7C { // OP_PUSH29 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 29) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 29) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(29, ip, sp, evmGasLeft, stackHead) } case 0x7D { // OP_PUSH30 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 30) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 30) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(30, ip, sp, evmGasLeft, stackHead) } case 0x7E { // OP_PUSH31 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 31) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 31) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(31, ip, sp, evmGasLeft, stackHead) } case 0x7F { // OP_PUSH32 - evmGasLeft := chargeGas(evmGasLeft, 3) - - ip := add(ip, 1) - let value := readBytes(ip, 32) - - sp, stackHead := pushStackItem(sp, value, stackHead) - ip := add(ip, 32) + ip, sp, evmGasLeft, stackHead := pushOpcodeInner(32, ip, sp, evmGasLeft, stackHead) } case 0x80 { // OP_DUP1 evmGasLeft := chargeGas(evmGasLeft, 3) From 816c3581312163b0cabb032a9fe9f54cb0ff3574 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 2 Jan 2025 13:00:36 +0100 Subject: [PATCH 44/62] fix(EVM): Remove magic numbers (N-09) (#1175) --- system-contracts/contracts/EvmEmulator.yul | 44 ++++++++++--------- .../contracts/libraries/Utils.sol | 14 +++--- .../EvmEmulatorFunctions.template.yul | 13 +++--- .../evm-emulator/EvmEmulatorLoop.template.yul | 9 ++-- 4 files changed, 43 insertions(+), 37 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 1cf6c3bf0..c05d40dec 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -188,6 +188,9 @@ object "EvmEmulator" { value := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 } + function ADDRESS_MASK() -> value { // mask for lower 160 bits + value := 0xffffffffffffffffffffffffffffffffffffffff + } //////////////////////////////////////////////////////////////// // GENERAL FUNCTIONS @@ -585,11 +588,12 @@ object "EvmEmulator" { // EVM GAS MANAGER FUNCTIONALITY //////////////////////////////////////////////////////////////// + // Address higher bytes must be cleaned before function $llvm_AlwaysInline_llvm$_warmAddress(addr) -> isWarm { // function warmAccount(address account) // non-standard selector 0x00 // addr is packed in the same word with selector - mstore(0, and(addr, 0xffffffffffffffffffffffffffffffffffffffff)) + mstore(0, addr) performSystemCall(EVM_GAS_MANAGER_CONTRACT(), 32) @@ -824,11 +828,11 @@ object "EvmEmulator" { } function _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) -> addr, gasUsed { - addr := and(rawAddr, 0xffffffffffffffffffffffffffffffffffffffff) - // memory_expansion_cost gasUsed := expandMemory2(retOffset, retSize, argsOffset, argsSize) + addr := and(rawAddr, ADDRESS_MASK()) + let addressAccessCost := 100 // warm address access cost if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { addressAccessCost := 2600 // cold address access cost @@ -1136,8 +1140,7 @@ object "EvmEmulator" { if canBeDeployed { returndatacopy(0, 0, 32) - addr := mload(0) - + addr := and(mload(0), ADDRESS_MASK()) pop($llvm_AlwaysInline_llvm$_warmAddress(addr)) // will stay warm even if constructor reverts // so even if constructor reverts, nonce stays incremented and addr stays warm @@ -1631,7 +1634,7 @@ object "EvmEmulator" { evmGasLeft := chargeGas(evmGasLeft, 100) let addr := accessStackHead(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + addr := and(addr, ADDRESS_MASK()) if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { evmGasLeft := chargeGas(evmGasLeft, 2500) @@ -1774,7 +1777,7 @@ object "EvmEmulator" { let addr := accessStackHead(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + addr := and(addr, ADDRESS_MASK()) if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { evmGasLeft := chargeGas(evmGasLeft, 2500) } @@ -1803,8 +1806,6 @@ object "EvmEmulator" { srcOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) - // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost // minimum_word_size = (size + 31) / 32 let dynamicGas := add( @@ -1812,6 +1813,7 @@ object "EvmEmulator" { expandMemory(dstOffset, len) ) + addr := and(addr, ADDRESS_MASK()) if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { dynamicGas := add(dynamicGas, 2500) } @@ -1870,7 +1872,7 @@ object "EvmEmulator" { evmGasLeft := chargeGas(evmGasLeft, 100) let addr := accessStackHead(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + addr := and(addr, ADDRESS_MASK()) if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { evmGasLeft := chargeGas(evmGasLeft, 2500) @@ -3104,6 +3106,9 @@ object "EvmEmulator" { value := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 } + function ADDRESS_MASK() -> value { // mask for lower 160 bits + value := 0xffffffffffffffffffffffffffffffffffffffff + } //////////////////////////////////////////////////////////////// // GENERAL FUNCTIONS @@ -3501,11 +3506,12 @@ object "EvmEmulator" { // EVM GAS MANAGER FUNCTIONALITY //////////////////////////////////////////////////////////////// + // Address higher bytes must be cleaned before function $llvm_AlwaysInline_llvm$_warmAddress(addr) -> isWarm { // function warmAccount(address account) // non-standard selector 0x00 // addr is packed in the same word with selector - mstore(0, and(addr, 0xffffffffffffffffffffffffffffffffffffffff)) + mstore(0, addr) performSystemCall(EVM_GAS_MANAGER_CONTRACT(), 32) @@ -3740,11 +3746,11 @@ object "EvmEmulator" { } function _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) -> addr, gasUsed { - addr := and(rawAddr, 0xffffffffffffffffffffffffffffffffffffffff) - // memory_expansion_cost gasUsed := expandMemory2(retOffset, retSize, argsOffset, argsSize) + addr := and(rawAddr, ADDRESS_MASK()) + let addressAccessCost := 100 // warm address access cost if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { addressAccessCost := 2600 // cold address access cost @@ -4052,8 +4058,7 @@ object "EvmEmulator" { if canBeDeployed { returndatacopy(0, 0, 32) - addr := mload(0) - + addr := and(mload(0), ADDRESS_MASK()) pop($llvm_AlwaysInline_llvm$_warmAddress(addr)) // will stay warm even if constructor reverts // so even if constructor reverts, nonce stays incremented and addr stays warm @@ -4535,7 +4540,7 @@ object "EvmEmulator" { evmGasLeft := chargeGas(evmGasLeft, 100) let addr := accessStackHead(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + addr := and(addr, ADDRESS_MASK()) if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { evmGasLeft := chargeGas(evmGasLeft, 2500) @@ -4678,7 +4683,7 @@ object "EvmEmulator" { let addr := accessStackHead(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + addr := and(addr, ADDRESS_MASK()) if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { evmGasLeft := chargeGas(evmGasLeft, 2500) } @@ -4707,8 +4712,6 @@ object "EvmEmulator" { srcOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) - // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost // minimum_word_size = (size + 31) / 32 let dynamicGas := add( @@ -4716,6 +4719,7 @@ object "EvmEmulator" { expandMemory(dstOffset, len) ) + addr := and(addr, ADDRESS_MASK()) if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { dynamicGas := add(dynamicGas, 2500) } @@ -4774,7 +4778,7 @@ object "EvmEmulator" { evmGasLeft := chargeGas(evmGasLeft, 100) let addr := accessStackHead(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + addr := and(addr, ADDRESS_MASK()) if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { evmGasLeft := chargeGas(evmGasLeft, 2500) diff --git a/system-contracts/contracts/libraries/Utils.sol b/system-contracts/contracts/libraries/Utils.sol index 94a615da0..d1ea42c88 100644 --- a/system-contracts/contracts/libraries/Utils.sol +++ b/system-contracts/contracts/libraries/Utils.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.20; import {EfficientCall} from "./EfficientCall.sol"; import {RLPEncoder} from "./RLPEncoder.sol"; import {MalformedBytecode, BytecodeError, Overflow} from "../SystemContractErrors.sol"; -import {ERA_VM_BYTECODE_FLAG, EVM_BYTECODE_FLAG} from "../Constants.sol"; +import {ERA_VM_BYTECODE_FLAG, EVM_BYTECODE_FLAG, CREATE2_EVM_PREFIX} from "../Constants.sol"; /** * @author Matter Labs @@ -103,6 +103,8 @@ library Utils { return _bytecodeHash & ~IS_CONSTRUCTOR_BYTECODE_HASH_BIT_MASK; } + uint256 internal constant MAX_BYTECODE_LENGTH = (2 ** 16) - 1; + /// @notice Validate the bytecode format and calculate its hash. /// @param _bytecode The bytecode to hash. /// @return hashedBytecode The 32-byte hash of the bytecode. @@ -118,7 +120,7 @@ library Utils { uint256 lengthInWords = _bytecode.length / 32; // bytecode length must be less than 2^16 words - if (lengthInWords >= 2 ** 16) { + if (lengthInWords > MAX_BYTECODE_LENGTH) { revert MalformedBytecode(BytecodeError.NumberOfWords); } // bytecode length in words must be odd @@ -134,9 +136,6 @@ library Utils { hashedBytecode = hashedBytecode | bytes32(lengthInWords << 224); } - // the real max supported number is 2^16, but we'll stick to evm convention - uint256 internal constant MAX_EVM_BYTECODE_LENGTH = (2 ** 16) - 1; - /// @notice Validate the bytecode format and calculate its hash. /// @param _evmBytecodeLen The length of original EVM bytecode in bytes /// @param _paddedBytecode The padded EVM bytecode to hash. @@ -158,7 +157,8 @@ library Utils { revert MalformedBytecode(BytecodeError.EvmBytecodeLength); } - if (_evmBytecodeLen > MAX_EVM_BYTECODE_LENGTH) { + // bytecode length must be less than 2^16 bytes + if (_evmBytecodeLen > MAX_BYTECODE_LENGTH) { revert MalformedBytecode(BytecodeError.EvmBytecodeLengthTooBig); } @@ -187,7 +187,7 @@ library Utils { bytes32 _salt, bytes32 _bytecodeHash ) internal pure returns (address newAddress) { - bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), _sender, _salt, _bytecodeHash)); + bytes32 hash = keccak256(abi.encodePacked(bytes1(CREATE2_EVM_PREFIX), _sender, _salt, _bytecodeHash)); newAddress = address(uint160(uint256(hash))); } diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index dc8e64295..b7bab8371 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -128,6 +128,9 @@ function EMPTY_KECCAK() -> value { // keccak("") value := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 } +function ADDRESS_MASK() -> value { // mask for lower 160 bits + value := 0xffffffffffffffffffffffffffffffffffffffff +} //////////////////////////////////////////////////////////////// // GENERAL FUNCTIONS @@ -525,11 +528,12 @@ function accessStackHead(sp, stackHead) -> value { // EVM GAS MANAGER FUNCTIONALITY //////////////////////////////////////////////////////////////// +// Address higher bytes must be cleaned before function $llvm_AlwaysInline_llvm$_warmAddress(addr) -> isWarm { // function warmAccount(address account) // non-standard selector 0x00 // addr is packed in the same word with selector - mstore(0, and(addr, 0xffffffffffffffffffffffffffffffffffffffff)) + mstore(0, addr) performSystemCall(EVM_GAS_MANAGER_CONTRACT(), 32) @@ -764,11 +768,11 @@ function performDelegateCall(oldSp, evmGasLeft, isStatic, oldStackHead) -> newGa } function _genericPrecallLogic(rawAddr, argsOffset, argsSize, retOffset, retSize) -> addr, gasUsed { - addr := and(rawAddr, 0xffffffffffffffffffffffffffffffffffffffff) - // memory_expansion_cost gasUsed := expandMemory2(retOffset, retSize, argsOffset, argsSize) + addr := and(rawAddr, ADDRESS_MASK()) + let addressAccessCost := 100 // warm address access cost if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { addressAccessCost := 2600 // cold address access cost @@ -1076,8 +1080,7 @@ function _executeCreate(offset, size, value, evmGasLeftOld, isCreate2, salt) -> if canBeDeployed { returndatacopy(0, 0, 32) - addr := mload(0) - + addr := and(mload(0), ADDRESS_MASK()) pop($llvm_AlwaysInline_llvm$_warmAddress(addr)) // will stay warm even if constructor reverts // so even if constructor reverts, nonce stays incremented and addr stays warm diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index 762312df0..1d6027b7b 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -309,7 +309,7 @@ for { } true { } { evmGasLeft := chargeGas(evmGasLeft, 100) let addr := accessStackHead(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + addr := and(addr, ADDRESS_MASK()) if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { evmGasLeft := chargeGas(evmGasLeft, 2500) @@ -452,7 +452,7 @@ for { } true { } { let addr := accessStackHead(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + addr := and(addr, ADDRESS_MASK()) if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { evmGasLeft := chargeGas(evmGasLeft, 2500) } @@ -481,8 +481,6 @@ for { } true { } { srcOffset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) len, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) - // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost // minimum_word_size = (size + 31) / 32 let dynamicGas := add( @@ -490,6 +488,7 @@ for { } true { } { expandMemory(dstOffset, len) ) + addr := and(addr, ADDRESS_MASK()) if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { dynamicGas := add(dynamicGas, 2500) } @@ -548,7 +547,7 @@ for { } true { } { evmGasLeft := chargeGas(evmGasLeft, 100) let addr := accessStackHead(sp, stackHead) - addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff) + addr := and(addr, ADDRESS_MASK()) if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) { evmGasLeft := chargeGas(evmGasLeft, 2500) From bfc691f34dc436590317000aa2bb77009bf69215 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 2 Jan 2025 13:48:11 +0100 Subject: [PATCH 45/62] fix(EVM): Add missing documentation (N-10) (#1176) --- system-contracts/contracts/EvmEmulator.yul | 134 ++++++++++-------- system-contracts/contracts/EvmGasManager.yul | 1 + .../EvmEmulatorFunctions.template.yul | 67 +++++---- 3 files changed, 118 insertions(+), 84 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index c05d40dec..f662db3d2 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -126,6 +126,8 @@ object "EvmEmulator" { offset := add(BASEFEE_CACHE_OFFSET(), 32) } + // Note: we have an empty memory slot after LAST_RETURNDATA_SIZE_OFFSET(), it is used to simplify stack logic + function STACK_OFFSET() -> offset { offset := add(LAST_RETURNDATA_SIZE_OFFSET(), 64) } @@ -392,12 +394,14 @@ object "EvmEmulator" { } function getRawCodeHash(addr) -> hash { + // function getRawCodeHash(address _address) mstore(0, 0x4DE2E46800000000000000000000000000000000000000000000000000000000) mstore(4, addr) hash := fetchFromSystemContract(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 36) } function getEvmExtcodehash(addr) -> evmCodeHash { + // function evmCodeHash(address _address) mstore(0, 0x54A3314700000000000000000000000000000000000000000000000000000000) mstore(4, addr) evmCodeHash := fetchFromSystemContract(DEPLOYER_SYSTEM_CONTRACT(), 36) @@ -1233,45 +1237,52 @@ object "EvmEmulator" { } //////////////////////////////////////////////////////////////// - // EXTCODECOPY FUNCTIONALITY + // MEMORY REGIONS FUNCTIONALITY //////////////////////////////////////////////////////////////// - function $llvm_AlwaysInline_llvm$_copyRest(dest, val, len) { - let rest_bits := shl(3, len) - let upper_bits := sub(256, rest_bits) - let val_mask := shl(upper_bits, MAX_UINT()) - let val_masked := and(val, val_mask) - let dst_val := mload(dest) - let dst_mask := shr(rest_bits, MAX_UINT()) - let dst_masked := and(dst_val, dst_mask) - mstore(dest, or(val_masked, dst_masked)) - } - + // Copy the region of memory function $llvm_AlwaysInline_llvm$_memcpy(dest, src, len) { - let dest_addr := dest - let src_addr := src - let dest_end := add(dest, and(len, sub(0, 32))) - for { } lt(dest_addr, dest_end) {} { - mstore(dest_addr, mload(src_addr)) - dest_addr := add(dest_addr, 32) - src_addr := add(src_addr, 32) + // Copy all the whole memory words in a cycle + let destIndex := dest + let srcIndex := src + let destEndIndex := add(dest, and(len, sub(0, 32))) // len / 32 words + for { } lt(destIndex, destEndIndex) {} { + mstore(destIndex, mload(srcIndex)) + destIndex := add(destIndex, 32) + srcIndex := add(srcIndex, 32) } - let rest_len := and(len, 31) - if rest_len { - $llvm_AlwaysInline_llvm$_copyRest(dest_addr, mload(src_addr), rest_len) + // Copy the remainder (if any) + let remainderLen := and(len, 31) + if remainderLen { + $llvm_AlwaysInline_llvm$_memWriteRemainder(destIndex, mload(srcIndex), remainderLen) } } - function $llvm_AlwaysInline_llvm$_memsetToZero(dest,len) { - let dest_end := add(dest, and(len, sub(0, 32))) - for {let i := dest} lt(i, dest_end) { i := add(i, 32) } { + // Write the last part of the copied/cleaned memory region (smaller than the memory word) + function $llvm_AlwaysInline_llvm$_memWriteRemainder(dest, remainder, len) { + let remainderBitLength := shl(3, len) // bytes to bits + + let existingValue := mload(dest) + let existingValueMask := shr(remainderBitLength, MAX_UINT()) + let existingValueMasked := and(existingValue, existingValueMask) // clean up place for remainder + + let remainderMasked := and(remainder, not(existingValueMask)) // using only `len` higher bytes of remainder word + mstore(dest, or(remainderMasked, existingValueMasked)) + } + + // Clean the region of memory + function $llvm_AlwaysInline_llvm$_memsetToZero(dest, len) { + // Clean all the whole memory words in a cycle + let destEndIndex := add(dest, and(len, sub(0, 32))) // len / 32 words + for {let i := dest} lt(i, destEndIndex) { i := add(i, 32) } { mstore(i, 0) } - let rest_len := and(len, 31) - if rest_len { - $llvm_AlwaysInline_llvm$_copyRest(dest_end, 0, rest_len) + // Clean the remainder (if any) + let remainderLen := and(len, 31) + if remainderLen { + $llvm_AlwaysInline_llvm$_memWriteRemainder(destEndIndex, 0, remainderLen) } } @@ -3044,6 +3055,8 @@ object "EvmEmulator" { offset := add(BASEFEE_CACHE_OFFSET(), 32) } + // Note: we have an empty memory slot after LAST_RETURNDATA_SIZE_OFFSET(), it is used to simplify stack logic + function STACK_OFFSET() -> offset { offset := add(LAST_RETURNDATA_SIZE_OFFSET(), 64) } @@ -3310,12 +3323,14 @@ object "EvmEmulator" { } function getRawCodeHash(addr) -> hash { + // function getRawCodeHash(address _address) mstore(0, 0x4DE2E46800000000000000000000000000000000000000000000000000000000) mstore(4, addr) hash := fetchFromSystemContract(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 36) } function getEvmExtcodehash(addr) -> evmCodeHash { + // function evmCodeHash(address _address) mstore(0, 0x54A3314700000000000000000000000000000000000000000000000000000000) mstore(4, addr) evmCodeHash := fetchFromSystemContract(DEPLOYER_SYSTEM_CONTRACT(), 36) @@ -4151,45 +4166,52 @@ object "EvmEmulator" { } //////////////////////////////////////////////////////////////// - // EXTCODECOPY FUNCTIONALITY + // MEMORY REGIONS FUNCTIONALITY //////////////////////////////////////////////////////////////// - function $llvm_AlwaysInline_llvm$_copyRest(dest, val, len) { - let rest_bits := shl(3, len) - let upper_bits := sub(256, rest_bits) - let val_mask := shl(upper_bits, MAX_UINT()) - let val_masked := and(val, val_mask) - let dst_val := mload(dest) - let dst_mask := shr(rest_bits, MAX_UINT()) - let dst_masked := and(dst_val, dst_mask) - mstore(dest, or(val_masked, dst_masked)) - } - + // Copy the region of memory function $llvm_AlwaysInline_llvm$_memcpy(dest, src, len) { - let dest_addr := dest - let src_addr := src - let dest_end := add(dest, and(len, sub(0, 32))) - for { } lt(dest_addr, dest_end) {} { - mstore(dest_addr, mload(src_addr)) - dest_addr := add(dest_addr, 32) - src_addr := add(src_addr, 32) + // Copy all the whole memory words in a cycle + let destIndex := dest + let srcIndex := src + let destEndIndex := add(dest, and(len, sub(0, 32))) // len / 32 words + for { } lt(destIndex, destEndIndex) {} { + mstore(destIndex, mload(srcIndex)) + destIndex := add(destIndex, 32) + srcIndex := add(srcIndex, 32) } - let rest_len := and(len, 31) - if rest_len { - $llvm_AlwaysInline_llvm$_copyRest(dest_addr, mload(src_addr), rest_len) + // Copy the remainder (if any) + let remainderLen := and(len, 31) + if remainderLen { + $llvm_AlwaysInline_llvm$_memWriteRemainder(destIndex, mload(srcIndex), remainderLen) } } - function $llvm_AlwaysInline_llvm$_memsetToZero(dest,len) { - let dest_end := add(dest, and(len, sub(0, 32))) - for {let i := dest} lt(i, dest_end) { i := add(i, 32) } { + // Write the last part of the copied/cleaned memory region (smaller than the memory word) + function $llvm_AlwaysInline_llvm$_memWriteRemainder(dest, remainder, len) { + let remainderBitLength := shl(3, len) // bytes to bits + + let existingValue := mload(dest) + let existingValueMask := shr(remainderBitLength, MAX_UINT()) + let existingValueMasked := and(existingValue, existingValueMask) // clean up place for remainder + + let remainderMasked := and(remainder, not(existingValueMask)) // using only `len` higher bytes of remainder word + mstore(dest, or(remainderMasked, existingValueMasked)) + } + + // Clean the region of memory + function $llvm_AlwaysInline_llvm$_memsetToZero(dest, len) { + // Clean all the whole memory words in a cycle + let destEndIndex := add(dest, and(len, sub(0, 32))) // len / 32 words + for {let i := dest} lt(i, destEndIndex) { i := add(i, 32) } { mstore(i, 0) } - let rest_len := and(len, 31) - if rest_len { - $llvm_AlwaysInline_llvm$_copyRest(dest_end, 0, rest_len) + // Clean the remainder (if any) + let remainderLen := and(len, 31) + if remainderLen { + $llvm_AlwaysInline_llvm$_memWriteRemainder(destEndIndex, 0, remainderLen) } } diff --git a/system-contracts/contracts/EvmGasManager.yul b/system-contracts/contracts/EvmGasManager.yul index 00d236250..56fb18b69 100644 --- a/system-contracts/contracts/EvmGasManager.yul +++ b/system-contracts/contracts/EvmGasManager.yul @@ -51,6 +51,7 @@ object "EvmGasManager" { } function $llvm_AlwaysInline_llvm$__getRawSenderCodeHash() -> hash { + // function getRawCodeHash(address _address) mstore(0, 0x4DE2E46800000000000000000000000000000000000000000000000000000000) mstore(4, caller()) diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index b7bab8371..0aecdccbc 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -66,6 +66,8 @@ function LAST_RETURNDATA_SIZE_OFFSET() -> offset { offset := add(BASEFEE_CACHE_OFFSET(), 32) } +// Note: we have an empty memory slot after LAST_RETURNDATA_SIZE_OFFSET(), it is used to simplify stack logic + function STACK_OFFSET() -> offset { offset := add(LAST_RETURNDATA_SIZE_OFFSET(), 64) } @@ -332,12 +334,14 @@ function getRawNonce(addr) -> nonce { } function getRawCodeHash(addr) -> hash { + // function getRawCodeHash(address _address) mstore(0, 0x4DE2E46800000000000000000000000000000000000000000000000000000000) mstore(4, addr) hash := fetchFromSystemContract(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT(), 36) } function getEvmExtcodehash(addr) -> evmCodeHash { + // function evmCodeHash(address _address) mstore(0, 0x54A3314700000000000000000000000000000000000000000000000000000000) mstore(4, addr) evmCodeHash := fetchFromSystemContract(DEPLOYER_SYSTEM_CONTRACT(), 36) @@ -1173,45 +1177,52 @@ function _saveConstructorReturnGas() -> gasLeft, addr { } //////////////////////////////////////////////////////////////// -// EXTCODECOPY FUNCTIONALITY +// MEMORY REGIONS FUNCTIONALITY //////////////////////////////////////////////////////////////// -function $llvm_AlwaysInline_llvm$_copyRest(dest, val, len) { - let rest_bits := shl(3, len) - let upper_bits := sub(256, rest_bits) - let val_mask := shl(upper_bits, MAX_UINT()) - let val_masked := and(val, val_mask) - let dst_val := mload(dest) - let dst_mask := shr(rest_bits, MAX_UINT()) - let dst_masked := and(dst_val, dst_mask) - mstore(dest, or(val_masked, dst_masked)) -} - +// Copy the region of memory function $llvm_AlwaysInline_llvm$_memcpy(dest, src, len) { - let dest_addr := dest - let src_addr := src - let dest_end := add(dest, and(len, sub(0, 32))) - for { } lt(dest_addr, dest_end) {} { - mstore(dest_addr, mload(src_addr)) - dest_addr := add(dest_addr, 32) - src_addr := add(src_addr, 32) + // Copy all the whole memory words in a cycle + let destIndex := dest + let srcIndex := src + let destEndIndex := add(dest, and(len, sub(0, 32))) // len / 32 words + for { } lt(destIndex, destEndIndex) {} { + mstore(destIndex, mload(srcIndex)) + destIndex := add(destIndex, 32) + srcIndex := add(srcIndex, 32) } - let rest_len := and(len, 31) - if rest_len { - $llvm_AlwaysInline_llvm$_copyRest(dest_addr, mload(src_addr), rest_len) + // Copy the remainder (if any) + let remainderLen := and(len, 31) + if remainderLen { + $llvm_AlwaysInline_llvm$_memWriteRemainder(destIndex, mload(srcIndex), remainderLen) } } -function $llvm_AlwaysInline_llvm$_memsetToZero(dest,len) { - let dest_end := add(dest, and(len, sub(0, 32))) - for {let i := dest} lt(i, dest_end) { i := add(i, 32) } { +// Write the last part of the copied/cleaned memory region (smaller than the memory word) +function $llvm_AlwaysInline_llvm$_memWriteRemainder(dest, remainder, len) { + let remainderBitLength := shl(3, len) // bytes to bits + + let existingValue := mload(dest) + let existingValueMask := shr(remainderBitLength, MAX_UINT()) + let existingValueMasked := and(existingValue, existingValueMask) // clean up place for remainder + + let remainderMasked := and(remainder, not(existingValueMask)) // using only `len` higher bytes of remainder word + mstore(dest, or(remainderMasked, existingValueMasked)) +} + +// Clean the region of memory +function $llvm_AlwaysInline_llvm$_memsetToZero(dest, len) { + // Clean all the whole memory words in a cycle + let destEndIndex := add(dest, and(len, sub(0, 32))) // len / 32 words + for {let i := dest} lt(i, destEndIndex) { i := add(i, 32) } { mstore(i, 0) } - let rest_len := and(len, 31) - if rest_len { - $llvm_AlwaysInline_llvm$_copyRest(dest_end, 0, rest_len) + // Clean the remainder (if any) + let remainderLen := and(len, 31) + if remainderLen { + $llvm_AlwaysInline_llvm$_memWriteRemainder(destEndIndex, 0, remainderLen) } } From 26df944dc464f229ee5ecc66b5a8d6aa1cb9346a Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 2 Jan 2025 14:09:02 +0100 Subject: [PATCH 46/62] fix(EVM): Remove redundant warming of EVM account during construction (N-12) (#1177) --- system-contracts/contracts/EvmEmulator.yul | 2 -- system-contracts/evm-emulator/EvmEmulator.template.yul | 2 -- 2 files changed, 4 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index f662db3d2..fa36d4921 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -2943,8 +2943,6 @@ object "EvmEmulator" { //////////////////////////////////////////////////////////////// // FALLBACK //////////////////////////////////////////////////////////////// - - pop($llvm_AlwaysInline_llvm$_warmAddress(address())) let evmGasLeft, isStatic, isCallerEVM := consumeEvmFrame() diff --git a/system-contracts/evm-emulator/EvmEmulator.template.yul b/system-contracts/evm-emulator/EvmEmulator.template.yul index 22d188075..ac3988999 100644 --- a/system-contracts/evm-emulator/EvmEmulator.template.yul +++ b/system-contracts/evm-emulator/EvmEmulator.template.yul @@ -79,8 +79,6 @@ object "EvmEmulator" { //////////////////////////////////////////////////////////////// // FALLBACK //////////////////////////////////////////////////////////////// - - pop($llvm_AlwaysInline_llvm$_warmAddress(address())) let evmGasLeft, isStatic, isCallerEVM := consumeEvmFrame() From b494799666aa64778c6c3b53182963c3e642ec5b Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 2 Jan 2025 14:30:39 +0100 Subject: [PATCH 47/62] fix(EVM): Use constant PREVRANDAO value (N-11) (#1178) --- system-contracts/contracts/EvmEmulator.yul | 36 ++++++++----------- system-contracts/contracts/SystemContext.sol | 2 +- .../EvmEmulatorFunctions.template.yul | 12 +++---- .../evm-emulator/EvmEmulatorLoop.template.yul | 6 +--- 4 files changed, 22 insertions(+), 34 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index fa36d4921..3c784aa42 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -87,7 +87,7 @@ object "EvmEmulator" { } function ORIGIN_CACHE_OFFSET() -> offset { - offset := mul(23, 32) + offset := mul(24, 32) } function GASPRICE_CACHE_OFFSET() -> offset { @@ -106,12 +106,8 @@ object "EvmEmulator" { offset := add(BLOCKTIMESTAMP_CACHE_OFFSET(), 32) } - function PREVRANDAO_CACHE_OFFSET() -> offset { - offset := add(BLOCKNUMBER_CACHE_OFFSET(), 32) - } - function GASLIMIT_CACHE_OFFSET() -> offset { - offset := add(PREVRANDAO_CACHE_OFFSET(), 32) + offset := add(BLOCKNUMBER_CACHE_OFFSET(), 32) } function CHAINID_CACHE_OFFSET() -> offset { @@ -194,6 +190,10 @@ object "EvmEmulator" { value := 0xffffffffffffffffffffffffffffffffffffffff } + function PREVRANDAO_VALUE() -> value { + value := 2500000000000000 // This value is fixed in EraVM + } + //////////////////////////////////////////////////////////////// // GENERAL FUNCTIONS //////////////////////////////////////////////////////////////// @@ -1959,11 +1959,7 @@ object "EvmEmulator" { } case 0x44 { // OP_PREVRANDAO evmGasLeft := chargeGas(evmGasLeft, 2) - let _prevrandao := mload(PREVRANDAO_CACHE_OFFSET()) - if iszero(_prevrandao) { - _prevrandao := cached(PREVRANDAO_CACHE_OFFSET(), prevrandao()) - } - sp, stackHead := pushStackItem(sp, _prevrandao, stackHead) + sp, stackHead := pushStackItem(sp, PREVRANDAO_VALUE(), stackHead) ip := add(ip, 1) } case 0x45 { // OP_GASLIMIT @@ -3014,7 +3010,7 @@ object "EvmEmulator" { } function ORIGIN_CACHE_OFFSET() -> offset { - offset := mul(23, 32) + offset := mul(24, 32) } function GASPRICE_CACHE_OFFSET() -> offset { @@ -3033,12 +3029,8 @@ object "EvmEmulator" { offset := add(BLOCKTIMESTAMP_CACHE_OFFSET(), 32) } - function PREVRANDAO_CACHE_OFFSET() -> offset { - offset := add(BLOCKNUMBER_CACHE_OFFSET(), 32) - } - function GASLIMIT_CACHE_OFFSET() -> offset { - offset := add(PREVRANDAO_CACHE_OFFSET(), 32) + offset := add(BLOCKNUMBER_CACHE_OFFSET(), 32) } function CHAINID_CACHE_OFFSET() -> offset { @@ -3121,6 +3113,10 @@ object "EvmEmulator" { value := 0xffffffffffffffffffffffffffffffffffffffff } + function PREVRANDAO_VALUE() -> value { + value := 2500000000000000 // This value is fixed in EraVM + } + //////////////////////////////////////////////////////////////// // GENERAL FUNCTIONS //////////////////////////////////////////////////////////////// @@ -4874,11 +4870,7 @@ object "EvmEmulator" { } case 0x44 { // OP_PREVRANDAO evmGasLeft := chargeGas(evmGasLeft, 2) - let _prevrandao := mload(PREVRANDAO_CACHE_OFFSET()) - if iszero(_prevrandao) { - _prevrandao := cached(PREVRANDAO_CACHE_OFFSET(), prevrandao()) - } - sp, stackHead := pushStackItem(sp, _prevrandao, stackHead) + sp, stackHead := pushStackItem(sp, PREVRANDAO_VALUE(), stackHead) ip := add(ip, 1) } case 0x45 { // OP_GASLIMIT diff --git a/system-contracts/contracts/SystemContext.sol b/system-contracts/contracts/SystemContext.sol index 2ce75419d..ee9f2aaf4 100644 --- a/system-contracts/contracts/SystemContext.sol +++ b/system-contracts/contracts/SystemContext.sol @@ -42,10 +42,10 @@ contract SystemContext is ISystemContext, ISystemContextDeprecated, SystemContra /// @notice The `block.coinbase` in the current transaction. /// @dev For the support of coinbase, we will use the bootloader formal address for now - /// @dev (!) EVM emulator doesn't expect this value to change address public coinbase = BOOTLOADER_FORMAL_ADDRESS; /// @notice Formal `block.difficulty` parameter. + /// @dev (!) EVM emulator doesn't expect this value to change uint256 public difficulty = 2.5e15; /// @notice The `block.basefee`. diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 0aecdccbc..f388086be 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -27,7 +27,7 @@ function MSG_VALUE_SYSTEM_CONTRACT() -> addr { } function ORIGIN_CACHE_OFFSET() -> offset { - offset := mul(23, 32) + offset := mul(24, 32) } function GASPRICE_CACHE_OFFSET() -> offset { @@ -46,12 +46,8 @@ function BLOCKNUMBER_CACHE_OFFSET() -> offset { offset := add(BLOCKTIMESTAMP_CACHE_OFFSET(), 32) } -function PREVRANDAO_CACHE_OFFSET() -> offset { - offset := add(BLOCKNUMBER_CACHE_OFFSET(), 32) -} - function GASLIMIT_CACHE_OFFSET() -> offset { - offset := add(PREVRANDAO_CACHE_OFFSET(), 32) + offset := add(BLOCKNUMBER_CACHE_OFFSET(), 32) } function CHAINID_CACHE_OFFSET() -> offset { @@ -134,6 +130,10 @@ function ADDRESS_MASK() -> value { // mask for lower 160 bits value := 0xffffffffffffffffffffffffffffffffffffffff } +function PREVRANDAO_VALUE() -> value { + value := 2500000000000000 // This value is fixed in EraVM +} + //////////////////////////////////////////////////////////////// // GENERAL FUNCTIONS //////////////////////////////////////////////////////////////// diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index 1d6027b7b..be56ed95b 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -623,11 +623,7 @@ for { } true { } { } case 0x44 { // OP_PREVRANDAO evmGasLeft := chargeGas(evmGasLeft, 2) - let _prevrandao := mload(PREVRANDAO_CACHE_OFFSET()) - if iszero(_prevrandao) { - _prevrandao := cached(PREVRANDAO_CACHE_OFFSET(), prevrandao()) - } - sp, stackHead := pushStackItem(sp, _prevrandao, stackHead) + sp, stackHead := pushStackItem(sp, PREVRANDAO_VALUE(), stackHead) ip := add(ip, 1) } case 0x45 { // OP_GASLIMIT From e3307c3389fcafccb4f64645cd06916f075267d7 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Thu, 2 Jan 2025 16:51:02 +0100 Subject: [PATCH 48/62] fix(EVM): Add functionality for force EVM deploys (L-04) (#1179) --- .../contracts/ContractDeployer.sol | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/system-contracts/contracts/ContractDeployer.sol b/system-contracts/contracts/ContractDeployer.sol index 6a41d91cd..7a360b926 100644 --- a/system-contracts/contracts/ContractDeployer.sol +++ b/system-contracts/contracts/ContractDeployer.sol @@ -302,11 +302,11 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { /// @notice A struct that describes a forced deployment on an address struct ForceDeployment { - // The bytecode hash to put on an address + // The bytecode hash to put on an address. Hash and length parts are ignored in case of EVM bytecode. bytes32 bytecodeHash; // The address on which to deploy the bytecodehash to address newAddress; - // Whether to run the constructor on the force deployment + // Whether to run the constructor on the force deployment. Ignored in case of EVM deployment. bool callConstructor; // The value with which to initialize a contract uint256 value; @@ -318,25 +318,31 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { /// @param _deployment Information about the forced deployment. /// @param _sender The `msg.sender` inside the constructor call. function forceDeployOnAddress(ForceDeployment calldata _deployment, address _sender) external payable onlySelf { - _ensureBytecodeIsKnown(_deployment.bytecodeHash); - // Since the `forceDeployOnAddress` function is called only during upgrades, the Governance is trusted to correctly select // the addresses to deploy the new bytecodes to and to assess whether overriding the AccountInfo for the "force-deployed" // contract is acceptable. - AccountInfo memory newAccountInfo; - newAccountInfo.supportedAAVersion = AccountAbstractionVersion.None; - // Accounts have sequential nonces by default. - newAccountInfo.nonceOrdering = AccountNonceOrdering.Sequential; - _storeAccountInfo(_deployment.newAddress, newAccountInfo); - _constructContract({ - _sender: _sender, - _newAddress: _deployment.newAddress, - _bytecodeHash: _deployment.bytecodeHash, - _input: _deployment.input, - _isSystem: false, - _callConstructor: _deployment.callConstructor - }); + if (Utils.isCodeHashEVM(_deployment.bytecodeHash)) { + // It is not possible to change the AccountInfo for EVM contracts. + _constructEVMContract(_sender, _deployment.newAddress, _deployment.input); + } else { + _ensureBytecodeIsKnown(_deployment.bytecodeHash); + + AccountInfo memory newAccountInfo; + newAccountInfo.supportedAAVersion = AccountAbstractionVersion.None; + // Accounts have sequential nonces by default. + newAccountInfo.nonceOrdering = AccountNonceOrdering.Sequential; + _storeAccountInfo(_deployment.newAddress, newAccountInfo); + + _constructContract({ + _sender: _sender, + _newAddress: _deployment.newAddress, + _bytecodeHash: _deployment.bytecodeHash, + _input: _deployment.input, + _isSystem: false, + _callConstructor: _deployment.callConstructor + }); + } } /// @notice This method is to be used only during an upgrade to set bytecodes on specific addresses. From 66c8e51bd8bfc961ae528a6b484e80aaac369beb Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 3 Jan 2025 11:31:50 +0100 Subject: [PATCH 49/62] fix(EVM): Fix cross vm panic returndata logic (L-03) (#1180) --- system-contracts/contracts/EvmEmulator.yul | 520 +++++++++--------- system-contracts/contracts/EvmGasManager.yul | 2 +- .../evm-emulator/EvmEmulator.template.yul | 6 +- .../EvmEmulatorFunctions.template.yul | 28 +- .../evm-emulator/EvmEmulatorLoop.template.yul | 7 +- .../EvmEmulatorLoopUnusedOpcodes.template.yul | 222 ++++---- 6 files changed, 391 insertions(+), 394 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 3c784aa42..fa1ecab2b 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -86,8 +86,12 @@ object "EvmEmulator" { addr := 0x0000000000000000000000000000000000008009 } + function PANIC_RETURNDATASIZE_OFFSET() -> offset { + offset := mul(23, 32) + } + function ORIGIN_CACHE_OFFSET() -> offset { - offset := mul(24, 32) + offset := add(PANIC_RETURNDATASIZE_OFFSET(), 32) } function GASPRICE_CACHE_OFFSET() -> offset { @@ -203,19 +207,15 @@ object "EvmEmulator" { revert(0, 0) } - function $llvm_NoInline_llvm$_panic() { // revert consuming all EVM gas - mstore(0, 0) - revert(0, 32) - } - - function revertWithGas(evmGasLeft) { - mstore(0, evmGasLeft) - revert(0, 32) + function $llvm_NoInline_llvm$_invalid() { // revert consuming all EVM gas + panic() } function panic() { // revert consuming all EVM gas + // we return empty 32 bytes encoding 0 gas left if caller is EVM, and 0 bytes if caller isn't EVM + // it is done without if-else block so this function will be inlined mstore(0, 0) - revert(0, 32) + revert(0, mload(PANIC_RETURNDATASIZE_OFFSET())) } function cached(cacheIndex, value) -> _value { @@ -648,16 +648,16 @@ object "EvmEmulator" { } function consumeEvmFrame() -> passGas, isStatic, callerEVM { - // function consumeEvmFrame() external returns (uint256 passGas, uint256 auxDataRes) + // function consumeEvmFrame(_caller) external returns (uint256 passGas, uint256 auxDataRes) // non-standard selector 0x04 - mstore(0, 0x0400000000000000000000000000000000000000000000000000000000000000) - mstore(1, caller()) + mstore(0, or(0x0400000000000000000000000000000000000000000000000000000000000000, caller())) - performSystemCall(EVM_GAS_MANAGER_CONTRACT(), 33) + performSystemCall(EVM_GAS_MANAGER_CONTRACT(), 32) let _returndatasize := returndatasize() if _returndatasize { callerEVM := true + mstore(PANIC_RETURNDATASIZE_OFFSET(), 32) // we should return 0 gas after panics returndatacopy(0, 0, 32) passGas := mload(0) @@ -2579,7 +2579,7 @@ object "EvmEmulator" { } - if eq(isCallerEVM, 1) { + if isCallerEVM { offset := sub(offset, 32) size := add(size, 32) @@ -2590,345 +2590,344 @@ object "EvmEmulator" { revert(offset, size) } case 0xFE { // OP_INVALID - evmGasLeft := 0 - revertWithGas(evmGasLeft) + $llvm_NoInline_llvm$_invalid() } // We explicitly add unused opcodes to optimize the jump table by compiler. case 0x0C { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x0D { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x0E { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x0F { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x1E { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x1F { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x21 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x22 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x23 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x24 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x25 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x26 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x27 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x28 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x29 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2A { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2B { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2C { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2D { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2E { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2F { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x49 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4A { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4B { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4C { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4D { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4E { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4F { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAD { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB0 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB1 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB3 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB4 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBD { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC0 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC1 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC3 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC4 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCD { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD0 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD1 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD3 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD4 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDD { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE0 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE1 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE3 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE4 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xED { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xFB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xFC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xFF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } default { - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } } @@ -3009,8 +3008,12 @@ object "EvmEmulator" { addr := 0x0000000000000000000000000000000000008009 } + function PANIC_RETURNDATASIZE_OFFSET() -> offset { + offset := mul(23, 32) + } + function ORIGIN_CACHE_OFFSET() -> offset { - offset := mul(24, 32) + offset := add(PANIC_RETURNDATASIZE_OFFSET(), 32) } function GASPRICE_CACHE_OFFSET() -> offset { @@ -3126,19 +3129,15 @@ object "EvmEmulator" { revert(0, 0) } - function $llvm_NoInline_llvm$_panic() { // revert consuming all EVM gas - mstore(0, 0) - revert(0, 32) - } - - function revertWithGas(evmGasLeft) { - mstore(0, evmGasLeft) - revert(0, 32) + function $llvm_NoInline_llvm$_invalid() { // revert consuming all EVM gas + panic() } function panic() { // revert consuming all EVM gas + // we return empty 32 bytes encoding 0 gas left if caller is EVM, and 0 bytes if caller isn't EVM + // it is done without if-else block so this function will be inlined mstore(0, 0) - revert(0, 32) + revert(0, mload(PANIC_RETURNDATASIZE_OFFSET())) } function cached(cacheIndex, value) -> _value { @@ -3571,16 +3570,16 @@ object "EvmEmulator" { } function consumeEvmFrame() -> passGas, isStatic, callerEVM { - // function consumeEvmFrame() external returns (uint256 passGas, uint256 auxDataRes) + // function consumeEvmFrame(_caller) external returns (uint256 passGas, uint256 auxDataRes) // non-standard selector 0x04 - mstore(0, 0x0400000000000000000000000000000000000000000000000000000000000000) - mstore(1, caller()) + mstore(0, or(0x0400000000000000000000000000000000000000000000000000000000000000, caller())) - performSystemCall(EVM_GAS_MANAGER_CONTRACT(), 33) + performSystemCall(EVM_GAS_MANAGER_CONTRACT(), 32) let _returndatasize := returndatasize() if _returndatasize { callerEVM := true + mstore(PANIC_RETURNDATASIZE_OFFSET(), 32) // we should return 0 gas after panics returndatacopy(0, 0, 32) passGas := mload(0) @@ -4236,7 +4235,7 @@ object "EvmEmulator" { } } - function $llvm_NoInline_llvm$_simulate( + function simulate( isCallerEVM, evmGasLeft, isStatic, @@ -5490,7 +5489,7 @@ object "EvmEmulator" { } - if eq(isCallerEVM, 1) { + if isCallerEVM { offset := sub(offset, 32) size := add(size, 32) @@ -5501,345 +5500,344 @@ object "EvmEmulator" { revert(offset, size) } case 0xFE { // OP_INVALID - evmGasLeft := 0 - revertWithGas(evmGasLeft) + $llvm_NoInline_llvm$_invalid() } // We explicitly add unused opcodes to optimize the jump table by compiler. case 0x0C { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x0D { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x0E { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x0F { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x1E { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x1F { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x21 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x22 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x23 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x24 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x25 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x26 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x27 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x28 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x29 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2A { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2B { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2C { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2D { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2E { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2F { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x49 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4A { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4B { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4C { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4D { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4E { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4F { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAD { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB0 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB1 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB3 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB4 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBD { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC0 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC1 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC3 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC4 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCD { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD0 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD1 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD3 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD4 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDD { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE0 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE1 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE3 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE4 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xED { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xFB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xFC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xFF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } default { - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } } @@ -5859,7 +5857,7 @@ object "EvmEmulator" { } } - if eq(isCallerEVM, 1) { + if isCallerEVM { // Includes gas returnOffset := sub(returnOffset, 32) checkOverflow(returnLen, 32) @@ -5884,7 +5882,7 @@ object "EvmEmulator" { // segment of memory. getDeployedBytecode() - let returnOffset, returnLen := $llvm_NoInline_llvm$_simulate(isCallerEVM, evmGasLeft, isStatic) + let returnOffset, returnLen := simulate(isCallerEVM, evmGasLeft, isStatic) return(returnOffset, returnLen) } } diff --git a/system-contracts/contracts/EvmGasManager.yul b/system-contracts/contracts/EvmGasManager.yul index 56fb18b69..4e99eee56 100644 --- a/system-contracts/contracts/EvmGasManager.yul +++ b/system-contracts/contracts/EvmGasManager.yul @@ -221,7 +221,7 @@ object "EvmGasManager" { // We do not have active frame. This means that the EVM contract was called from the EraVM contract. // mark caller and txorigin as warm - let _msgsender := calldataload(1) + let _msgsender := and(ADDRESS_MASK(), _calldata0Slot) let _origin := origin() warmAccount(_msgsender) if iszero(eq(_msgsender, _origin)) { diff --git a/system-contracts/evm-emulator/EvmEmulator.template.yul b/system-contracts/evm-emulator/EvmEmulator.template.yul index ac3988999..c7083e839 100644 --- a/system-contracts/evm-emulator/EvmEmulator.template.yul +++ b/system-contracts/evm-emulator/EvmEmulator.template.yul @@ -123,7 +123,7 @@ object "EvmEmulator" { - function $llvm_NoInline_llvm$_simulate( + function simulate( isCallerEVM, evmGasLeft, isStatic, @@ -136,7 +136,7 @@ object "EvmEmulator" { - if eq(isCallerEVM, 1) { + if isCallerEVM { // Includes gas returnOffset := sub(returnOffset, 32) checkOverflow(returnLen, 32) @@ -161,7 +161,7 @@ object "EvmEmulator" { // segment of memory. getDeployedBytecode() - let returnOffset, returnLen := $llvm_NoInline_llvm$_simulate(isCallerEVM, evmGasLeft, isStatic) + let returnOffset, returnLen := simulate(isCallerEVM, evmGasLeft, isStatic) return(returnOffset, returnLen) } } diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index f388086be..8cbe2096c 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -26,8 +26,12 @@ function MSG_VALUE_SYSTEM_CONTRACT() -> addr { addr := 0x0000000000000000000000000000000000008009 } +function PANIC_RETURNDATASIZE_OFFSET() -> offset { + offset := mul(23, 32) +} + function ORIGIN_CACHE_OFFSET() -> offset { - offset := mul(24, 32) + offset := add(PANIC_RETURNDATASIZE_OFFSET(), 32) } function GASPRICE_CACHE_OFFSET() -> offset { @@ -143,19 +147,15 @@ function abortEvmEnvironment() { revert(0, 0) } -function $llvm_NoInline_llvm$_panic() { // revert consuming all EVM gas - mstore(0, 0) - revert(0, 32) -} - -function revertWithGas(evmGasLeft) { - mstore(0, evmGasLeft) - revert(0, 32) +function $llvm_NoInline_llvm$_invalid() { // revert consuming all EVM gas + panic() } function panic() { // revert consuming all EVM gas + // we return empty 32 bytes encoding 0 gas left if caller is EVM, and 0 bytes if caller isn't EVM + // it is done without if-else block so this function will be inlined mstore(0, 0) - revert(0, 32) + revert(0, mload(PANIC_RETURNDATASIZE_OFFSET())) } function cached(cacheIndex, value) -> _value { @@ -588,16 +588,16 @@ function pushEvmFrame(passGas, isStatic) { } function consumeEvmFrame() -> passGas, isStatic, callerEVM { - // function consumeEvmFrame() external returns (uint256 passGas, uint256 auxDataRes) + // function consumeEvmFrame(_caller) external returns (uint256 passGas, uint256 auxDataRes) // non-standard selector 0x04 - mstore(0, 0x0400000000000000000000000000000000000000000000000000000000000000) - mstore(1, caller()) + mstore(0, or(0x0400000000000000000000000000000000000000000000000000000000000000, caller())) - performSystemCall(EVM_GAS_MANAGER_CONTRACT(), 33) + performSystemCall(EVM_GAS_MANAGER_CONTRACT(), 32) let _returndatasize := returndatasize() if _returndatasize { callerEVM := true + mstore(PANIC_RETURNDATASIZE_OFFSET(), 32) // we should return 0 gas after panics returndatacopy(0, 0, 32) passGas := mload(0) diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index be56ed95b..aaf26e57b 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -1243,7 +1243,7 @@ for { } true { } { } - if eq(isCallerEVM, 1) { + if isCallerEVM { offset := sub(offset, 32) size := add(size, 32) @@ -1254,12 +1254,11 @@ for { } true { } { revert(offset, size) } case 0xFE { // OP_INVALID - evmGasLeft := 0 - revertWithGas(evmGasLeft) + $llvm_NoInline_llvm$_invalid() } // We explicitly add unused opcodes to optimize the jump table by compiler. default { - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } } diff --git a/system-contracts/evm-emulator/EvmEmulatorLoopUnusedOpcodes.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoopUnusedOpcodes.template.yul index a59e4eb58..a9f121f82 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoopUnusedOpcodes.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoopUnusedOpcodes.template.yul @@ -1,333 +1,333 @@ case 0x0C { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x0D { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x0E { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x0F { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x1E { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x1F { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x21 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x22 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x23 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x24 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x25 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x26 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x27 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x28 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x29 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2A { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2B { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2C { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2D { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2E { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x2F { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x49 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4A { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4B { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4C { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4D { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4E { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0x4F { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xA9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAD { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xAF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB0 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB1 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB3 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB4 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xB9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBD { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xBF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC0 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC1 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC3 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC4 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xC9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCD { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xCF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD0 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD1 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD3 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD4 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xD9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDD { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xDF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE0 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE1 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE3 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE4 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE5 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xE9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEA { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xED { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEE { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xEF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF2 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF6 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF7 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF8 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xF9 { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xFB { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xFC { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } case 0xFF { // Unused opcode - $llvm_NoInline_llvm$_panic() + $llvm_NoInline_llvm$_invalid() } \ No newline at end of file From 969886c3764514c0342b7458341975493dbb8c4e Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 3 Jan 2025 12:23:45 +0100 Subject: [PATCH 50/62] fix(EVM): Check the bytecode length on call (L-05) (#1181) --- system-contracts/contracts/EvmEmulator.yul | 6 +++++- system-contracts/evm-emulator/EvmEmulator.template.yul | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index fa1ecab2b..38cf0a9be 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -2973,8 +2973,12 @@ object "EvmEmulator" { getCodeAddress(), BYTECODE_OFFSET(), // destination offset 0, // source offset - MAX_POSSIBLE_DEPLOYED_BYTECODE_LEN() + add(MAX_POSSIBLE_DEPLOYED_BYTECODE_LEN(), 1) // so we can check that bytecode isn't too big ) + + if gt(codeLen, MAX_POSSIBLE_DEPLOYED_BYTECODE_LEN()) { + panic() + } mstore(EMPTY_CODE_OFFSET(), 0) mstore(BYTECODE_LEN_OFFSET(), codeLen) diff --git a/system-contracts/evm-emulator/EvmEmulator.template.yul b/system-contracts/evm-emulator/EvmEmulator.template.yul index c7083e839..b9bc96a2b 100644 --- a/system-contracts/evm-emulator/EvmEmulator.template.yul +++ b/system-contracts/evm-emulator/EvmEmulator.template.yul @@ -114,8 +114,12 @@ object "EvmEmulator" { getCodeAddress(), BYTECODE_OFFSET(), // destination offset 0, // source offset - MAX_POSSIBLE_DEPLOYED_BYTECODE_LEN() + add(MAX_POSSIBLE_DEPLOYED_BYTECODE_LEN(), 1) // so we can check that bytecode isn't too big ) + + if gt(codeLen, MAX_POSSIBLE_DEPLOYED_BYTECODE_LEN()) { + panic() + } mstore(EMPTY_CODE_OFFSET(), 0) mstore(BYTECODE_LEN_OFFSET(), codeLen) From b194c862bf7132fdf10b5c4a6a8cfbb6cc562f92 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 3 Jan 2025 12:40:17 +0100 Subject: [PATCH 51/62] fix(EVM): Check bytecodehash version in create functions of ContractDeployer (#1182) --- system-contracts/contracts/ContractDeployer.sol | 5 ++++- system-contracts/contracts/SystemContractErrors.sol | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/system-contracts/contracts/ContractDeployer.sol b/system-contracts/contracts/ContractDeployer.sol index 7a360b926..96691da99 100644 --- a/system-contracts/contracts/ContractDeployer.sol +++ b/system-contracts/contracts/ContractDeployer.sol @@ -10,7 +10,7 @@ import {Utils} from "./libraries/Utils.sol"; import {EfficientCall} from "./libraries/EfficientCall.sol"; import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; import {SystemContractBase} from "./abstract/SystemContractBase.sol"; -import {Unauthorized, InvalidNonceOrderingChange, ValueMismatch, EmptyBytes32, EVMEmulationNotSupported, NotAllowedToDeployInKernelSpace, HashIsNonZero, NonEmptyAccount, UnknownCodeHash, NonEmptyMsgValue} from "./SystemContractErrors.sol"; +import {Unauthorized, InvalidNonceOrderingChange, ValueMismatch, EmptyBytes32, EVMBytecodeHash, EVMEmulationNotSupported, NotAllowedToDeployInKernelSpace, HashIsNonZero, NonEmptyAccount, UnknownCodeHash, NonEmptyMsgValue} from "./SystemContractErrors.sol"; /** * @author Matter Labs @@ -398,6 +398,9 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { if (_bytecodeHash == bytes32(0x0)) { revert EmptyBytes32(); } + if (Utils.isCodeHashEVM(_bytecodeHash)) { + revert EVMBytecodeHash(); + } if (uint160(_newAddress) <= MAX_SYSTEM_CONTRACT_ADDRESS) { revert NotAllowedToDeployInKernelSpace(); } diff --git a/system-contracts/contracts/SystemContractErrors.sol b/system-contracts/contracts/SystemContractErrors.sol index 2ba8eed26..e6bdb473e 100644 --- a/system-contracts/contracts/SystemContractErrors.sol +++ b/system-contracts/contracts/SystemContractErrors.sol @@ -34,6 +34,8 @@ error EmptyVirtualBlocks(); error EncodedAndRealBytecodeChunkNotEqual(uint64 expected, uint64 provided); // 0x2bfbfc11 error EncodedLengthNotFourTimesSmallerThanOriginal(); +// 0x39bae0e6 +error EVMBytecodeHash(); // 0xb9e6e31f error EVMEmulationNotSupported(); // 0xe95a1fbe From a539040301bfdac86f8d5d87e4a213b9f76ea422 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 3 Jan 2025 12:47:50 +0100 Subject: [PATCH 52/62] fix(EVM): Simplify ContractDeployer storage layout (#1143) --- .../contracts/ContractDeployer.sol | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/system-contracts/contracts/ContractDeployer.sol b/system-contracts/contracts/ContractDeployer.sol index 96691da99..57bd23487 100644 --- a/system-contracts/contracts/ContractDeployer.sol +++ b/system-contracts/contracts/ContractDeployer.sol @@ -24,14 +24,14 @@ import {Unauthorized, InvalidNonceOrderingChange, ValueMismatch, EmptyBytes32, E contract ContractDeployer is IContractDeployer, SystemContractBase { /// @dev Prefix for EVM contracts hashes storage slots. uint256 private constant EVM_HASHES_PREFIX = 1 << 254; - /// @dev keccak256("ALLOWED_BYTECODE_TYPES_MODE_SLOT"). - bytes32 private constant ALLOWED_BYTECODE_TYPES_MODE_SLOT = - 0xd70708d0b933e26eab552567ce3a8ad69e6fbec9a2a68f16d51bd417a47d9d3b; /// @notice Information about an account contract. /// @dev For EOA and simple contracts (i.e. not accounts) this value is 0. mapping(address => AccountInfo) internal accountInfo; + /// @notice What types of bytecode are allowed to be deployed on this chain. + AllowedBytecodeTypes public allowedBytecodeTypesToDeploy; + modifier onlySelf() { if (msg.sender != address(this)) { revert Unauthorized(msg.sender); @@ -39,11 +39,6 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { _; } - /// @notice Returns what types of bytecode are allowed to be deployed on this chain. - function allowedBytecodeTypesToDeploy() external view returns (AllowedBytecodeTypes mode) { - mode = _getAllowedBytecodeTypesMode(); - } - /// @notice Returns keccak of EVM bytecode at address if it is an EVM contract. Returns bytes32(0) if it isn't a EVM contract. function evmCodeHash(address _address) external view returns (bytes32 _hash) { _hash = _getEvmCodeHash(_address); @@ -224,7 +219,7 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { bytes32 _salt, bytes32 _evmBytecodeHash ) public onlySystemCallFromEvmEmulator returns (address newAddress) { - if (_getAllowedBytecodeTypesMode() != AllowedBytecodeTypes.EraVmAndEVM) { + if (allowedBytecodeTypesToDeploy != AllowedBytecodeTypes.EraVmAndEVM) { revert EVMEmulationNotSupported(); } @@ -380,12 +375,10 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { revert Unauthorized(msg.sender); } - if (_getAllowedBytecodeTypesMode() != newAllowedBytecodeTypes) { - assembly { - sstore(ALLOWED_BYTECODE_TYPES_MODE_SLOT, newAllowedBytecodeTypes) - } + if (allowedBytecodeTypesToDeploy != newAllowedBytecodeTypes) { + allowedBytecodeTypesToDeploy = newAllowedBytecodeTypes; - emit AllowedBytecodeTypesModeUpdated(AllowedBytecodeTypes(newAllowedBytecodeTypes)); + emit AllowedBytecodeTypesModeUpdated(newAllowedBytecodeTypes); } } @@ -424,7 +417,7 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { address _newAddress, bytes calldata _initCode ) internal returns (uint256 constructorReturnEvmGas) { - if (_getAllowedBytecodeTypesMode() != AllowedBytecodeTypes.EraVmAndEVM) { + if (allowedBytecodeTypesToDeploy != AllowedBytecodeTypes.EraVmAndEVM) { revert EVMEmulationNotSupported(); } @@ -631,10 +624,4 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { _hash := sload(or(EVM_HASHES_PREFIX, _address)) } } - - function _getAllowedBytecodeTypesMode() internal view returns (AllowedBytecodeTypes mode) { - assembly { - mode := sload(ALLOWED_BYTECODE_TYPES_MODE_SLOT) - } - } } From 0ac215c56535fdadeebdc7ac188c85b5d9d02b67 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Fri, 3 Jan 2025 19:30:54 +0100 Subject: [PATCH 53/62] fix(EVM): Small misc fixes (#1184) --- .../contracts/ContractDeployer.sol | 2 +- system-contracts/contracts/EvmEmulator.yul | 32 +++++++++---------- .../EvmEmulatorFunctions.template.yul | 16 +++++----- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/system-contracts/contracts/ContractDeployer.sol b/system-contracts/contracts/ContractDeployer.sol index 57bd23487..e9ec97837 100644 --- a/system-contracts/contracts/ContractDeployer.sol +++ b/system-contracts/contracts/ContractDeployer.sol @@ -425,7 +425,7 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { // solhint-disable-next-line reason-string, gas-custom-errors require(NONCE_HOLDER_SYSTEM_CONTRACT.getRawNonce(_newAddress) == 0x0); // solhint-disable-next-line reason-string, gas-custom-errors - require(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getCodeHash(uint256(uint160(_newAddress))) == 0x0); + require(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getRawCodeHash(_newAddress) == 0x0); return _performDeployOnAddressEVM(_sender, _newAddress, AccountAbstractionVersion.None, _initCode); } diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 38cf0a9be..736815d26 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -1172,10 +1172,10 @@ object "EvmEmulator" { pushEvmFrame(gasForTheCall, false) // move needed memory slots to the scratch space - mstore(mul(10, 32), mload(sub(offset, 0x80)) - mstore(mul(11, 32), mload(sub(offset, 0x60)) - mstore(mul(12, 32), mload(sub(offset, 0x40)) - mstore(mul(13, 32), mload(sub(offset, 0x20)) + mstore(mul(10, 32), mload(sub(offset, 0x80))) + mstore(mul(11, 32), mload(sub(offset, 0x60))) + mstore(mul(12, 32), mload(sub(offset, 0x40))) + mstore(mul(13, 32), mload(sub(offset, 0x20))) // selector: function createEvmFromEmulator(address newAddress, bytes calldata _initCode) mstore(sub(offset, 0x80), 0xe43cec64) @@ -1186,10 +1186,10 @@ object "EvmEmulator" { let result := performSystemCallForCreate(value, sub(offset, 0x64), add(size, 0x64)) // move memory slots back - mstore(sub(offset, 0x80), mload(mul(10, 32)) - mstore(sub(offset, 0x60), mload(mul(11, 32)) - mstore(sub(offset, 0x40), mload(mul(12, 32)) - mstore(sub(offset, 0x20), mload(mul(13, 32)) + mstore(sub(offset, 0x80), mload(mul(10, 32))) + mstore(sub(offset, 0x60), mload(mul(11, 32))) + mstore(sub(offset, 0x40), mload(mul(12, 32))) + mstore(sub(offset, 0x20), mload(mul(13, 32))) let gasLeft switch result @@ -4098,10 +4098,10 @@ object "EvmEmulator" { pushEvmFrame(gasForTheCall, false) // move needed memory slots to the scratch space - mstore(mul(10, 32), mload(sub(offset, 0x80)) - mstore(mul(11, 32), mload(sub(offset, 0x60)) - mstore(mul(12, 32), mload(sub(offset, 0x40)) - mstore(mul(13, 32), mload(sub(offset, 0x20)) + mstore(mul(10, 32), mload(sub(offset, 0x80))) + mstore(mul(11, 32), mload(sub(offset, 0x60))) + mstore(mul(12, 32), mload(sub(offset, 0x40))) + mstore(mul(13, 32), mload(sub(offset, 0x20))) // selector: function createEvmFromEmulator(address newAddress, bytes calldata _initCode) mstore(sub(offset, 0x80), 0xe43cec64) @@ -4112,10 +4112,10 @@ object "EvmEmulator" { let result := performSystemCallForCreate(value, sub(offset, 0x64), add(size, 0x64)) // move memory slots back - mstore(sub(offset, 0x80), mload(mul(10, 32)) - mstore(sub(offset, 0x60), mload(mul(11, 32)) - mstore(sub(offset, 0x40), mload(mul(12, 32)) - mstore(sub(offset, 0x20), mload(mul(13, 32)) + mstore(sub(offset, 0x80), mload(mul(10, 32))) + mstore(sub(offset, 0x60), mload(mul(11, 32))) + mstore(sub(offset, 0x40), mload(mul(12, 32))) + mstore(sub(offset, 0x20), mload(mul(13, 32))) let gasLeft switch result diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 8cbe2096c..659423eb4 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -1112,10 +1112,10 @@ function _executeCreate(offset, size, value, evmGasLeftOld, isCreate2, salt) -> pushEvmFrame(gasForTheCall, false) // move needed memory slots to the scratch space - mstore(mul(10, 32), mload(sub(offset, 0x80)) - mstore(mul(11, 32), mload(sub(offset, 0x60)) - mstore(mul(12, 32), mload(sub(offset, 0x40)) - mstore(mul(13, 32), mload(sub(offset, 0x20)) + mstore(mul(10, 32), mload(sub(offset, 0x80))) + mstore(mul(11, 32), mload(sub(offset, 0x60))) + mstore(mul(12, 32), mload(sub(offset, 0x40))) + mstore(mul(13, 32), mload(sub(offset, 0x20))) // selector: function createEvmFromEmulator(address newAddress, bytes calldata _initCode) mstore(sub(offset, 0x80), 0xe43cec64) @@ -1126,10 +1126,10 @@ function _executeCreate(offset, size, value, evmGasLeftOld, isCreate2, salt) -> let result := performSystemCallForCreate(value, sub(offset, 0x64), add(size, 0x64)) // move memory slots back - mstore(sub(offset, 0x80), mload(mul(10, 32)) - mstore(sub(offset, 0x60), mload(mul(11, 32)) - mstore(sub(offset, 0x40), mload(mul(12, 32)) - mstore(sub(offset, 0x20), mload(mul(13, 32)) + mstore(sub(offset, 0x80), mload(mul(10, 32))) + mstore(sub(offset, 0x60), mload(mul(11, 32))) + mstore(sub(offset, 0x40), mload(mul(12, 32))) + mstore(sub(offset, 0x20), mload(mul(13, 32))) let gasLeft switch result From 600d20a6c29cf3a30ed8e57e885346b30d8a7940 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Mon, 6 Jan 2025 21:28:16 +0100 Subject: [PATCH 54/62] chore(EVM): Update hashes (#1185) --- system-contracts/SystemContractsHashes.json | 48 ++++++++++----------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/system-contracts/SystemContractsHashes.json b/system-contracts/SystemContractsHashes.json index 1bc5f5115..e4676e3cd 100644 --- a/system-contracts/SystemContractsHashes.json +++ b/system-contracts/SystemContractsHashes.json @@ -3,49 +3,49 @@ "contractName": "AccountCodeStorage", "bytecodePath": "zkout/AccountCodeStorage.sol/AccountCodeStorage.json", "sourceCodePath": "contracts-preprocessed/AccountCodeStorage.sol", - "bytecodeHash": "0x0100007360eb189d11c57c8b45bdd29ae2a996786b55e21847fbbe8e7b285ce2", + "bytecodeHash": "0x010000735d2420c3cd2c89979bb25e9d0a2043af1a544191cf69159441b0c9b5", "sourceCodeHash": "0xfdac12f45b5cfd4abd12923206f2d6f253d11a6624783e079b55e975d573ceb6" }, { "contractName": "BootloaderUtilities", "bytecodePath": "zkout/BootloaderUtilities.sol/BootloaderUtilities.json", "sourceCodePath": "contracts-preprocessed/BootloaderUtilities.sol", - "bytecodeHash": "0x010006f3bacfc75db6a91f2f2c05485c8f6d7ed0dd259b5d1a7de5bb5f0170a2", + "bytecodeHash": "0x010006f384b45bc23926b9b818ca1ba76b831c754643fcb3f81370667d8699ab", "sourceCodeHash": "0x10f30ac1a7098c7fddec2659ac43422783e8d3fdde02a3ba4d3ff45d451d7001" }, { "contractName": "ComplexUpgrader", "bytecodePath": "zkout/ComplexUpgrader.sol/ComplexUpgrader.json", "sourceCodePath": "contracts-preprocessed/ComplexUpgrader.sol", - "bytecodeHash": "0x01000047cfddb4597ec1fcff6ce1b13c404b128f8c7349943f00c8e702fcecd5", + "bytecodeHash": "0x0100004708162d81999c5d3d86bb012beed3d3bd1379c5f0a658edca34ad3c06", "sourceCodeHash": "0x796046a914fb676ba2bbd337b2924311ee2177ce54571c18a2c3945755c83614" }, { "contractName": "Compressor", "bytecodePath": "zkout/Compressor.sol/Compressor.json", "sourceCodePath": "contracts-preprocessed/Compressor.sol", - "bytecodeHash": "0x0100013f5b38355d5720b282db0cf3869b78e2c8d631550ef7a0f33e5757f6be", + "bytecodeHash": "0x0100013fe3b72ec3e360e03a10c1193951e6ed35c84922a3756fd9bb044bd877", "sourceCodeHash": "0xc6f7cd8b21aae52ed3dd5083c09b438a7af142a4ecda6067c586770e8be745a5" }, { "contractName": "ContractDeployer", "bytecodePath": "zkout/ContractDeployer.sol/ContractDeployer.json", "sourceCodePath": "contracts-preprocessed/ContractDeployer.sol", - "bytecodeHash": "0x01000655cfaee8879d6d5f020e8cac9b00e5830a8aef6d01f9406b4583fa4bd8", - "sourceCodeHash": "0x22f96404a0527177f635c81315349ee274806e7462c5e5ae087a005f991bc157" + "bytecodeHash": "0x010006d1863074c74b08fa628699b4a38599f234ada0322c430bebf62a9b28d1", + "sourceCodeHash": "0x857490d38ca4a071602c65a2463eaedd7b6a9727fe147ca0f5345a911876dac1" }, { "contractName": "Create2Factory", "bytecodePath": "zkout/Create2Factory.sol/Create2Factory.json", "sourceCodePath": "contracts-preprocessed/Create2Factory.sol", - "bytecodeHash": "0x0100003f53ff30da1f9c344e63c5a031affbf8af76baf0a92c977fefc8665a0c", + "bytecodeHash": "0x0100003fa2756165fad5789d24668ae72a1f86933ddc5b86a163f6555543d439", "sourceCodeHash": "0x114d9322a9ca654989f3e0b3b21f1311dbc4db84f443d054cd414f6414d84de3" }, { "contractName": "DefaultAccount", "bytecodePath": "zkout/DefaultAccount.sol/DefaultAccount.json", "sourceCodePath": "contracts-preprocessed/DefaultAccount.sol", - "bytecodeHash": "0x0100050b06623d83e54b5752821bc70b493f31989359002205a2f9264dba83f4", + "bytecodeHash": "0x0100050ba0d37b396c4ab89c75c0c343a12b6e4410b59830cb86e58e9c47fe4b", "sourceCodeHash": "0x300c864fcb3bc6a562875c7b1d83df15d466515da0a878a426b1aaeac26f3656" }, { @@ -59,57 +59,57 @@ "contractName": "ImmutableSimulator", "bytecodePath": "zkout/ImmutableSimulator.sol/ImmutableSimulator.json", "sourceCodePath": "contracts-preprocessed/ImmutableSimulator.sol", - "bytecodeHash": "0x0100003345f92c227817a46a1cb0ee12cac25cb8516251a028f24d6cb1feb77b", + "bytecodeHash": "0x01000033ab104bf96563676f04e933fe69cc4fe30b821a94b9ee533820ff0110", "sourceCodeHash": "0x9659e69f7db09e8f60a8bb95314b1ed26afcc689851665cf27f5408122f60c98" }, { "contractName": "KnownCodesStorage", "bytecodePath": "zkout/KnownCodesStorage.sol/KnownCodesStorage.json", "sourceCodePath": "contracts-preprocessed/KnownCodesStorage.sol", - "bytecodeHash": "0x010000cde320e4772a0ef635bff69f07fd908a6140480cf2c17f2cf0214d86a3", - "sourceCodeHash": "0x851fb5e170dfde39f1f9bc74654ec0b8f8f1d4c2fb20c06c77844c1e3ee0659a" + "bytecodeHash": "0x010000cf2a5d068008e625d480e8cbb6754f21ea448b98148e8dd35029385dc4", + "sourceCodeHash": "0x89f3adaabfcbdd6c4eedbaa16ed7f7fd81157de4a7c33dabddafd24f6fdc608e" }, { "contractName": "L1Messenger", "bytecodePath": "zkout/L1Messenger.sol/L1Messenger.json", "sourceCodePath": "contracts-preprocessed/L1Messenger.sol", - "bytecodeHash": "0x0100026320ad1a050ca08117bb57a06684839b3ab3f3095e17deb1ebeb53d4ac", + "bytecodeHash": "0x01000263e98414ad563dcdec402a9e88ca752097aef67d9f245f83cbc6ff1af6", "sourceCodeHash": "0xa8768fdaac6d8804782f14e2a51bbe2b6be31dee9103b6d02d149ea8dc46eb6a" }, { "contractName": "L2BaseToken", "bytecodePath": "zkout/L2BaseToken.sol/L2BaseToken.json", "sourceCodePath": "contracts-preprocessed/L2BaseToken.sol", - "bytecodeHash": "0x010000db58a5798e8cf03299e84c32288bd535f03140112d8461b84ec164887a", + "bytecodeHash": "0x010000db0a147bb0589d9b148984a6d084e882194b4a1d47ec4cc986751a4981", "sourceCodeHash": "0xdea518b1ea16718b0f0ec6155b227a8bc8f51374a9eebf7bc17cfe84433df740" }, { "contractName": "MsgValueSimulator", "bytecodePath": "zkout/MsgValueSimulator.sol/MsgValueSimulator.json", "sourceCodePath": "contracts-preprocessed/MsgValueSimulator.sol", - "bytecodeHash": "0x0100005964d673fb20cd2c77df6f9aa4c178bd8ffa54eb582505df704b1f3b5b", + "bytecodeHash": "0x01000059515d3792fb4e1984f63780c69fdcc114cddac8807926cbacf31b9ea3", "sourceCodeHash": "0x082f3dcbc2fe4d93706c86aae85faa683387097d1b676e7ebd00f71ee0f13b71" }, { "contractName": "NonceHolder", "bytecodePath": "zkout/NonceHolder.sol/NonceHolder.json", "sourceCodePath": "contracts-preprocessed/NonceHolder.sol", - "bytecodeHash": "0x010000cfa9aff9356b0ef66e9ca32df21ae4bcbb2a8d03d3acf74e2d060a3028", + "bytecodeHash": "0x010000cfa108b30afba73277d38d1436eaa76e42f80051fe1b4139230f0bf3bf", "sourceCodeHash": "0xcd0c0366effebf2c98c58cf96322cc242a2d1c675620ef5514b7ed1f0a869edc" }, { "contractName": "PubdataChunkPublisher", "bytecodePath": "zkout/PubdataChunkPublisher.sol/PubdataChunkPublisher.json", "sourceCodePath": "contracts-preprocessed/PubdataChunkPublisher.sol", - "bytecodeHash": "0x010000419856be032b7143f70782d129e260d6e99e6bbe7c4787884ccd949ce9", + "bytecodeHash": "0x010000412a6645d262f1bda8fe9b3c46bd913c51a0b947f8899a523124787fc0", "sourceCodeHash": "0xd7161e2c8092cf57b43c6220bc605c0e7e540bddcde1af24e2d90f75633b098e" }, { "contractName": "SystemContext", "bytecodePath": "zkout/SystemContext.sol/SystemContext.json", "sourceCodePath": "contracts-preprocessed/SystemContext.sol", - "bytecodeHash": "0x010001a5305c969f525ba5afc3c7496c520d77ca71bdebc9c3b8e71c7a0d3364", - "sourceCodeHash": "0xaf04a52c660f48de04c29b6c88632b4e002e94f58e0f8de034fd73959671c984" + "bytecodeHash": "0x010001a5df8b55454fb3a8a2ba9c369a37a6bc0e51ee101add2e0f08f29297df", + "sourceCodeHash": "0x71cf3e2eac1b74f9955b1479970bc159096600a652bcff5b06e1daaa37fd04a7" }, { "contractName": "EventWriter", @@ -122,22 +122,22 @@ "contractName": "EvmEmulator", "bytecodePath": "zkout/EvmEmulator.yul/contracts-preprocessed/EvmEmulator.yul.json", "sourceCodePath": "contracts-preprocessed/EvmEmulator.yul", - "bytecodeHash": "0x01000bef3afffa6dcca82a514f5f388101dd3498e9117be2073975d8ae791970", - "sourceCodeHash": "0x7cad60f966f85be729468ca5f3500ffd4a4b2d0ef6df5ca0656ac7f46c0d2402" + "bytecodeHash": "0x01000bc1e9dd8a2f03462607f313ea4eef563b7625c643b512ddd0b8ab338e7e", + "sourceCodeHash": "0x197c5e14c8f9cdfd1a170fb97bbc5297aaa549818734928df23fa438da5c886f" }, { "contractName": "EvmGasManager", "bytecodePath": "zkout/EvmGasManager.yul/contracts-preprocessed/EvmGasManager.yul.json", "sourceCodePath": "contracts-preprocessed/EvmGasManager.yul", - "bytecodeHash": "0x01000073ec5f4ae42b87e9ef80c92506f25710cce88d32a510f79093633260bf", - "sourceCodeHash": "0x825dc1d9bec5128457a8b96eaf7ebf7c89dbb95a9bd7aab0f1601bc06455abfb" + "bytecodeHash": "0x01000075efcb155e204fa38a44ba36c113001fefde8c813c772624ef72637c42", + "sourceCodeHash": "0xdc594f0c78a29eae7a1afa0138edbc024e7b5c8df17991c9f06017c1b31b0fa6" }, { "contractName": "CodeOracle", "bytecodePath": "zkout/CodeOracle.yul/contracts-preprocessed/precompiles/CodeOracle.yul.json", "sourceCodePath": "contracts-preprocessed/precompiles/CodeOracle.yul", - "bytecodeHash": "0x01000023fa893b49736f6009966b73613fe2b1ffd6d57ffb7a03dd4cd48940a9", - "sourceCodeHash": "0xf4b21b6712a6e2a001a1b8214ac15959e670bd0ff125984486a28e128cb8846d" + "bytecodeHash": "0x01000025b1cdbcd1552a815e8d5049404dec6709f86cc057e4326484e6a1a706", + "sourceCodeHash": "0x420719c88c00478214d5babde85eb093c0bce1048c391ad67e2510ed3a61272b" }, { "contractName": "EcAdd", From 3c77d4a015db6dd4e4aab6baa25a72f56ffa11ef Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Tue, 14 Jan 2025 14:44:51 +0100 Subject: [PATCH 55/62] fix(EVM): Change additional stipends for calls to empty contracts (M-03) (#1194) --- system-contracts/contracts/EvmEmulator.yul | 68 ++++++++----------- .../EvmEmulatorFunctions.template.yul | 34 ++++------ 2 files changed, 42 insertions(+), 60 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 736815d26..a3c9137b3 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -909,25 +909,21 @@ object "EvmEmulator" { function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic, rawCodeHash) -> success, frameGasLeft { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas - let additionalStipend := 6000 // should cover first access to empty account - switch value - case 0 { - if gt(addr, 0) { // zero address is always "empty" - if and(shr(224, rawCodeHash), 0xffff) { // if codelen is not zero - additionalStipend := 0 - } + let emptyContractExecutionCost := 500 // enough to call "empty" contract + let isEmptyContract := or(eq(addr, 0), iszero(and(shr(224, rawCodeHash), 0xffff))) + if isEmptyContract { + // we should add some gas to cover overhead of calling EmptyContract or DefaultAccount + // if value isn't zero, MsgValueSimulator will take required gas directly from our frame (as 2300 stipend) + if iszero(value) { + zkEvmGasToPass := add(zkEvmGasToPass, emptyContractExecutionCost) } } - default { - additionalStipend := 27000 // Stipend for MsgValueSimulator. Covered by positive_value_cost - } - - zkEvmGasToPass := add(zkEvmGasToPass, additionalStipend) if gt(zkEvmGasToPass, MAX_UINT32()) { // just in case zkEvmGasToPass := MAX_UINT32() } + // Please note, that decommitment cost and MsgValueSimulator additional overhead will be charged directly from this frame let zkEvmGasBefore := gas() switch isStatic case 0 { @@ -944,16 +940,14 @@ object "EvmEmulator" { zkEvmGasUsed := 0 // should never happen } - switch gt(zkEvmGasUsed, additionalStipend) - case 0 { - zkEvmGasUsed := 0 - } - default { - zkEvmGasUsed := sub(zkEvmGasUsed, additionalStipend) + if isEmptyContract { + if iszero(value) { + zkEvmGasToPass := sub(zkEvmGasToPass, emptyContractExecutionCost) + } + + zkEvmGasUsed := 0 // Calling empty contracts is free from the EVM point of view } - zkEvmGasToPass := sub(zkEvmGasToPass, additionalStipend) - // refund gas if gt(zkEvmGasToPass, zkEvmGasUsed) { frameGasLeft := div(sub(zkEvmGasToPass, zkEvmGasUsed), GAS_DIVISOR()) @@ -3835,25 +3829,21 @@ object "EvmEmulator" { function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic, rawCodeHash) -> success, frameGasLeft { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas - let additionalStipend := 6000 // should cover first access to empty account - switch value - case 0 { - if gt(addr, 0) { // zero address is always "empty" - if and(shr(224, rawCodeHash), 0xffff) { // if codelen is not zero - additionalStipend := 0 - } + let emptyContractExecutionCost := 500 // enough to call "empty" contract + let isEmptyContract := or(eq(addr, 0), iszero(and(shr(224, rawCodeHash), 0xffff))) + if isEmptyContract { + // we should add some gas to cover overhead of calling EmptyContract or DefaultAccount + // if value isn't zero, MsgValueSimulator will take required gas directly from our frame (as 2300 stipend) + if iszero(value) { + zkEvmGasToPass := add(zkEvmGasToPass, emptyContractExecutionCost) } } - default { - additionalStipend := 27000 // Stipend for MsgValueSimulator. Covered by positive_value_cost - } - - zkEvmGasToPass := add(zkEvmGasToPass, additionalStipend) if gt(zkEvmGasToPass, MAX_UINT32()) { // just in case zkEvmGasToPass := MAX_UINT32() } + // Please note, that decommitment cost and MsgValueSimulator additional overhead will be charged directly from this frame let zkEvmGasBefore := gas() switch isStatic case 0 { @@ -3870,16 +3860,14 @@ object "EvmEmulator" { zkEvmGasUsed := 0 // should never happen } - switch gt(zkEvmGasUsed, additionalStipend) - case 0 { - zkEvmGasUsed := 0 - } - default { - zkEvmGasUsed := sub(zkEvmGasUsed, additionalStipend) + if isEmptyContract { + if iszero(value) { + zkEvmGasToPass := sub(zkEvmGasToPass, emptyContractExecutionCost) + } + + zkEvmGasUsed := 0 // Calling empty contracts is free from the EVM point of view } - zkEvmGasToPass := sub(zkEvmGasToPass, additionalStipend) - // refund gas if gt(zkEvmGasToPass, zkEvmGasUsed) { frameGasLeft := div(sub(zkEvmGasToPass, zkEvmGasUsed), GAS_DIVISOR()) diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 659423eb4..ec39ab1d1 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -849,25 +849,21 @@ function callPrecompile(addr, precompileCost, gasToPass, value, argsOffset, args function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffset, retSize, isStatic, rawCodeHash) -> success, frameGasLeft { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas - let additionalStipend := 6000 // should cover first access to empty account - switch value - case 0 { - if gt(addr, 0) { // zero address is always "empty" - if and(shr(224, rawCodeHash), 0xffff) { // if codelen is not zero - additionalStipend := 0 - } + let emptyContractExecutionCost := 500 // enough to call "empty" contract + let isEmptyContract := or(eq(addr, 0), iszero(and(shr(224, rawCodeHash), 0xffff))) + if isEmptyContract { + // we should add some gas to cover overhead of calling EmptyContract or DefaultAccount + // if value isn't zero, MsgValueSimulator will take required gas directly from our frame (as 2300 stipend) + if iszero(value) { + zkEvmGasToPass := add(zkEvmGasToPass, emptyContractExecutionCost) } } - default { - additionalStipend := 27000 // Stipend for MsgValueSimulator. Covered by positive_value_cost - } - - zkEvmGasToPass := add(zkEvmGasToPass, additionalStipend) if gt(zkEvmGasToPass, MAX_UINT32()) { // just in case zkEvmGasToPass := MAX_UINT32() } + // Please note, that decommitment cost and MsgValueSimulator additional overhead will be charged directly from this frame let zkEvmGasBefore := gas() switch isStatic case 0 { @@ -884,16 +880,14 @@ function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffs zkEvmGasUsed := 0 // should never happen } - switch gt(zkEvmGasUsed, additionalStipend) - case 0 { - zkEvmGasUsed := 0 - } - default { - zkEvmGasUsed := sub(zkEvmGasUsed, additionalStipend) + if isEmptyContract { + if iszero(value) { + zkEvmGasToPass := sub(zkEvmGasToPass, emptyContractExecutionCost) + } + + zkEvmGasUsed := 0 // Calling empty contracts is free from the EVM point of view } - zkEvmGasToPass := sub(zkEvmGasToPass, additionalStipend) - // refund gas if gt(zkEvmGasToPass, zkEvmGasUsed) { frameGasLeft := div(sub(zkEvmGasToPass, zkEvmGasUsed), GAS_DIVISOR()) From 875d0a53447d261763204ce4b45e33583e78113d Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Tue, 14 Jan 2025 14:47:05 +0100 Subject: [PATCH 56/62] fix(EVM): Simplify padding (H-05) (#1195) --- system-contracts/contracts/EvmEmulator.yul | 4 ++-- system-contracts/contracts/precompiles/CodeOracle.yul | 4 ++-- system-contracts/evm-emulator/EvmEmulator.template.yul | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index a3c9137b3..293b53f55 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -30,12 +30,12 @@ object "EvmEmulator" { blobLen := len - if iszero(eq(mod(blobLen, 32), 0)) { + if mod(blobLen, 32) { blobLen := add(blobLen, sub(32, mod(blobLen, 32))) } // Now it is divisible by 32, but we must make sure that the number of 32 byte words is odd - if iszero(eq(mod(blobLen, 64), 32)) { + if iszero(mod(blobLen, 64)) { blobLen := add(blobLen, 32) } } diff --git a/system-contracts/contracts/precompiles/CodeOracle.yul b/system-contracts/contracts/precompiles/CodeOracle.yul index aa6d17563..2cbd084ca 100644 --- a/system-contracts/contracts/precompiles/CodeOracle.yul +++ b/system-contracts/contracts/precompiles/CodeOracle.yul @@ -114,12 +114,12 @@ object "CodeOracle" { function paddedBytecodeLen(len) -> blobLen { blobLen := len - if iszero(eq(mod(blobLen, 32), 0)) { + if mod(blobLen, 32) { blobLen := add(blobLen, sub(32, mod(blobLen, 32))) } // Now it is divisible by 32, but we must make sure that the number of 32 byte words is odd - if iszero(eq(mod(blobLen, 64), 32)) { + if iszero(mod(blobLen, 64)) { blobLen := add(blobLen, 32) } } diff --git a/system-contracts/evm-emulator/EvmEmulator.template.yul b/system-contracts/evm-emulator/EvmEmulator.template.yul index b9bc96a2b..a0d0a039e 100644 --- a/system-contracts/evm-emulator/EvmEmulator.template.yul +++ b/system-contracts/evm-emulator/EvmEmulator.template.yul @@ -30,12 +30,12 @@ object "EvmEmulator" { blobLen := len - if iszero(eq(mod(blobLen, 32), 0)) { + if mod(blobLen, 32) { blobLen := add(blobLen, sub(32, mod(blobLen, 32))) } // Now it is divisible by 32, but we must make sure that the number of 32 byte words is odd - if iszero(eq(mod(blobLen, 64), 32)) { + if iszero(mod(blobLen, 64)) { blobLen := add(blobLen, 32) } } From 0d6fec79bbcd084a1ae70655fcf41535fdc2273f Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Tue, 14 Jan 2025 14:57:35 +0100 Subject: [PATCH 57/62] fix(EVM): Increment nonce after EVM force deploy (L-04) (#1197) --- system-contracts/contracts/ContractDeployer.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/system-contracts/contracts/ContractDeployer.sol b/system-contracts/contracts/ContractDeployer.sol index e9ec97837..575ccfe47 100644 --- a/system-contracts/contracts/ContractDeployer.sol +++ b/system-contracts/contracts/ContractDeployer.sol @@ -318,6 +318,12 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { // contract is acceptable. if (Utils.isCodeHashEVM(_deployment.bytecodeHash)) { + // Note, that for contracts the "nonce" is set as deployment nonce. + uint256 deploymentNonce = NONCE_HOLDER_SYSTEM_CONTRACT.getDeploymentNonce(_deployment.newAddress); + if (deploymentNonce == 0) { + NONCE_HOLDER_SYSTEM_CONTRACT.incrementDeploymentNonce(_deployment.newAddress); + } + // It is not possible to change the AccountInfo for EVM contracts. _constructEVMContract(_sender, _deployment.newAddress, _deployment.input); } else { From 16b76a2073b4398e7ce08738c436c3be03fb1c43 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Tue, 14 Jan 2025 19:49:41 +0100 Subject: [PATCH 58/62] fix(EVM): Use stricter checks for calldata (M-06, M-07) (#1200) --- system-contracts/contracts/EvmEmulator.yul | 22 +++++++++++-------- .../EvmEmulatorFunctions.template.yul | 2 ++ .../evm-emulator/EvmEmulatorLoop.template.yul | 8 +++---- .../RuntimeScope.template.yul | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 293b53f55..f80bf75fa 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -186,6 +186,8 @@ object "EvmEmulator" { function MAX_UINT32() -> ret { ret := 4294967295 } // 2^32 - 1 + function MAX_CALLDATA_OFFSET() -> ret { ret := sub(MAX_UINT32(), 32) } // EraVM will panic if offset + length overflows u32 + function EMPTY_KECCAK() -> value { // keccak("") value := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 } @@ -1703,14 +1705,14 @@ object "EvmEmulator" { dstOffset := add(dstOffset, MEM_OFFSET()) // EraVM will revert if offset + length overflows uint32 - if gt(sourceOffset, MAX_UINT32()) { - sourceOffset := MAX_UINT32() + if gt(sourceOffset, MAX_CALLDATA_OFFSET()) { + sourceOffset := MAX_CALLDATA_OFFSET() } // Check bytecode out-of-bounds access let truncatedLen := len - if gt(add(sourceOffset, len), MAX_UINT32()) { - truncatedLen := sub(MAX_UINT32(), sourceOffset) // truncate + if gt(add(sourceOffset, len), MAX_CALLDATA_OFFSET()) { // in theory we could also copy MAX_CALLDATA_OFFSET slot, but it is unreachable + truncatedLen := sub(MAX_CALLDATA_OFFSET(), sourceOffset) // truncate $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, truncatedLen), sub(len, truncatedLen)) // pad with zeroes any out-of-bounds } @@ -3106,6 +3108,8 @@ object "EvmEmulator" { function MAX_UINT32() -> ret { ret := 4294967295 } // 2^32 - 1 + function MAX_CALLDATA_OFFSET() -> ret { ret := sub(MAX_UINT32(), 32) } // EraVM will panic if offset + length overflows u32 + function EMPTY_KECCAK() -> value { // keccak("") value := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 } @@ -4611,14 +4615,14 @@ object "EvmEmulator" { dstOffset := add(dstOffset, MEM_OFFSET()) // EraVM will revert if offset + length overflows uint32 - if gt(sourceOffset, MAX_UINT32()) { - sourceOffset := MAX_UINT32() + if gt(sourceOffset, MAX_CALLDATA_OFFSET()) { + sourceOffset := MAX_CALLDATA_OFFSET() } // Check bytecode out-of-bounds access let truncatedLen := len - if gt(add(sourceOffset, len), MAX_UINT32()) { - truncatedLen := sub(MAX_UINT32(), sourceOffset) // truncate + if gt(add(sourceOffset, len), MAX_CALLDATA_OFFSET()) { // in theory we could also copy MAX_CALLDATA_OFFSET slot, but it is unreachable + truncatedLen := sub(MAX_CALLDATA_OFFSET(), sourceOffset) // truncate $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, truncatedLen), sub(len, truncatedLen)) // pad with zeroes any out-of-bounds } @@ -5844,7 +5848,7 @@ object "EvmEmulator" { function $llvm_AlwaysInline_llvm$_calldataload(calldataOffset) -> res { // EraVM will revert if offset + length overflows uint32 - if lt(calldataOffset, MAX_UINT32()) { + if lt(calldataOffset, MAX_CALLDATA_OFFSET()) { // in theory we could also copy MAX_CALLDATA_OFFSET slot, but it is unreachable res := calldataload(calldataOffset) } } diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index ec39ab1d1..e0568c250 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -126,6 +126,8 @@ function OVERHEAD() -> overhead { overhead := 2000 } function MAX_UINT32() -> ret { ret := 4294967295 } // 2^32 - 1 +function MAX_CALLDATA_OFFSET() -> ret { ret := sub(MAX_UINT32(), 32) } // EraVM will panic if offset + length overflows u32 + function EMPTY_KECCAK() -> value { // keccak("") value := 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 } diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index aaf26e57b..ab88e92e1 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -373,14 +373,14 @@ for { } true { } { dstOffset := add(dstOffset, MEM_OFFSET()) // EraVM will revert if offset + length overflows uint32 - if gt(sourceOffset, MAX_UINT32()) { - sourceOffset := MAX_UINT32() + if gt(sourceOffset, MAX_CALLDATA_OFFSET()) { + sourceOffset := MAX_CALLDATA_OFFSET() } // Check bytecode out-of-bounds access let truncatedLen := len - if gt(add(sourceOffset, len), MAX_UINT32()) { - truncatedLen := sub(MAX_UINT32(), sourceOffset) // truncate + if gt(add(sourceOffset, len), MAX_CALLDATA_OFFSET()) { // in theory we could also copy MAX_CALLDATA_OFFSET slot, but it is unreachable + truncatedLen := sub(MAX_CALLDATA_OFFSET(), sourceOffset) // truncate $llvm_AlwaysInline_llvm$_memsetToZero(add(dstOffset, truncatedLen), sub(len, truncatedLen)) // pad with zeroes any out-of-bounds } diff --git a/system-contracts/evm-emulator/calldata-opcodes/RuntimeScope.template.yul b/system-contracts/evm-emulator/calldata-opcodes/RuntimeScope.template.yul index 7f02e012f..0d73af003 100644 --- a/system-contracts/evm-emulator/calldata-opcodes/RuntimeScope.template.yul +++ b/system-contracts/evm-emulator/calldata-opcodes/RuntimeScope.template.yul @@ -8,7 +8,7 @@ function $llvm_AlwaysInline_llvm$_calldatacopy(dstOffset, sourceOffset, truncate function $llvm_AlwaysInline_llvm$_calldataload(calldataOffset) -> res { // EraVM will revert if offset + length overflows uint32 - if lt(calldataOffset, MAX_UINT32()) { + if lt(calldataOffset, MAX_CALLDATA_OFFSET()) { // in theory we could also copy MAX_CALLDATA_OFFSET slot, but it is unreachable res := calldataload(calldataOffset) } } \ No newline at end of file From 0e0a7ac244cafc4e76b9fccafe94efb1ced1c0cc Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Tue, 14 Jan 2025 19:58:03 +0100 Subject: [PATCH 59/62] Add comments for delegate call implementation (L-10) --- system-contracts/contracts/EvmEmulator.yul | 8 ++++++-- .../evm-emulator/EvmEmulatorFunctions.template.yul | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index f80bf75fa..2fdfc58d2 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -806,6 +806,8 @@ object "EvmEmulator" { } if isCallToEmptyContract { + // In case of a call to the EVM contract that is currently being constructed, + // the DefaultAccount bytecode will be used instead. This is implemented at the virtual machine level. success := delegatecall(gas(), addr, argsOffset, argsSize, retOffset, retSize) _saveReturndataAfterZkEVMCall() } @@ -813,7 +815,7 @@ object "EvmEmulator" { // We forbid delegatecalls to EraVM native contracts } default { - // Precompile. Simlate using staticcall, since EraVM behavior differs here + // Precompile. Simulate using staticcall, since EraVM behavior differs here success, frameGasLeft := callPrecompile(addr, precompileCost, gasToPass, 0, argsOffset, argsSize, retOffset, retSize, true) } } @@ -3728,6 +3730,8 @@ object "EvmEmulator" { } if isCallToEmptyContract { + // In case of a call to the EVM contract that is currently being constructed, + // the DefaultAccount bytecode will be used instead. This is implemented at the virtual machine level. success := delegatecall(gas(), addr, argsOffset, argsSize, retOffset, retSize) _saveReturndataAfterZkEVMCall() } @@ -3735,7 +3739,7 @@ object "EvmEmulator" { // We forbid delegatecalls to EraVM native contracts } default { - // Precompile. Simlate using staticcall, since EraVM behavior differs here + // Precompile. Simulate using staticcall, since EraVM behavior differs here success, frameGasLeft := callPrecompile(addr, precompileCost, gasToPass, 0, argsOffset, argsSize, retOffset, retSize, true) } } diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index e0568c250..3a0b08bcd 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -746,6 +746,8 @@ function performDelegateCall(oldSp, evmGasLeft, isStatic, oldStackHead) -> newGa } if isCallToEmptyContract { + // In case of a call to the EVM contract that is currently being constructed, + // the DefaultAccount bytecode will be used instead. This is implemented at the virtual machine level. success := delegatecall(gas(), addr, argsOffset, argsSize, retOffset, retSize) _saveReturndataAfterZkEVMCall() } @@ -753,7 +755,7 @@ function performDelegateCall(oldSp, evmGasLeft, isStatic, oldStackHead) -> newGa // We forbid delegatecalls to EraVM native contracts } default { - // Precompile. Simlate using staticcall, since EraVM behavior differs here + // Precompile. Simulate using staticcall, since EraVM behavior differs here success, frameGasLeft := callPrecompile(addr, precompileCost, gasToPass, 0, argsOffset, argsSize, retOffset, retSize, true) } } From a9eb2735e7f5760eb6da9d8fc116c91bf06b473c Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Tue, 14 Jan 2025 20:01:03 +0100 Subject: [PATCH 60/62] Apply suggested change --- system-contracts/contracts/EvmEmulator.yul | 4 ++-- .../evm-emulator/EvmEmulatorFunctions.template.yul | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 2fdfc58d2..587352fc6 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -914,7 +914,7 @@ object "EvmEmulator" { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas let emptyContractExecutionCost := 500 // enough to call "empty" contract - let isEmptyContract := or(eq(addr, 0), iszero(and(shr(224, rawCodeHash), 0xffff))) + let isEmptyContract := or(iszero(addr), iszero(and(shr(224, rawCodeHash), 0xffff))) if isEmptyContract { // we should add some gas to cover overhead of calling EmptyContract or DefaultAccount // if value isn't zero, MsgValueSimulator will take required gas directly from our frame (as 2300 stipend) @@ -3838,7 +3838,7 @@ object "EvmEmulator" { let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas let emptyContractExecutionCost := 500 // enough to call "empty" contract - let isEmptyContract := or(eq(addr, 0), iszero(and(shr(224, rawCodeHash), 0xffff))) + let isEmptyContract := or(iszero(addr), iszero(and(shr(224, rawCodeHash), 0xffff))) if isEmptyContract { // we should add some gas to cover overhead of calling EmptyContract or DefaultAccount // if value isn't zero, MsgValueSimulator will take required gas directly from our frame (as 2300 stipend) diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index 3a0b08bcd..0b45dc423 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -854,7 +854,7 @@ function callZkVmNative(addr, evmGasToPass, value, argsOffset, argsSize, retOffs let zkEvmGasToPass := mul(evmGasToPass, GAS_DIVISOR()) // convert EVM gas -> ZkVM gas let emptyContractExecutionCost := 500 // enough to call "empty" contract - let isEmptyContract := or(eq(addr, 0), iszero(and(shr(224, rawCodeHash), 0xffff))) + let isEmptyContract := or(iszero(addr), iszero(and(shr(224, rawCodeHash), 0xffff))) if isEmptyContract { // we should add some gas to cover overhead of calling EmptyContract or DefaultAccount // if value isn't zero, MsgValueSimulator will take required gas directly from our frame (as 2300 stipend) From bbfd8e79ad52be67f9f08b8c312bdc3ab9d5eac4 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Tue, 14 Jan 2025 20:16:38 +0100 Subject: [PATCH 61/62] Charge additional 32k gas in contract creation from EOA or EraVM contract (N-13) --- system-contracts/contracts/EvmEmulator.yul | 1 + system-contracts/evm-emulator/EvmEmulator.template.yul | 2 ++ 2 files changed, 3 insertions(+) diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index 587352fc6..bc5c762fe 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -2947,6 +2947,7 @@ object "EvmEmulator" { if iszero(isCallerEVM) { evmGasLeft := getEvmGasFromContext() + evmGasLeft := chargeGas(evmGasLeft, 32000) } let offset, len, gasToReturn := simulate(isCallerEVM, evmGasLeft, false) diff --git a/system-contracts/evm-emulator/EvmEmulator.template.yul b/system-contracts/evm-emulator/EvmEmulator.template.yul index a0d0a039e..bd2f7dd85 100644 --- a/system-contracts/evm-emulator/EvmEmulator.template.yul +++ b/system-contracts/evm-emulator/EvmEmulator.template.yul @@ -90,6 +90,8 @@ object "EvmEmulator" { if iszero(isCallerEVM) { evmGasLeft := getEvmGasFromContext() + // Charge additional creation cost + evmGasLeft := chargeGas(evmGasLeft, 32000) } let offset, len, gasToReturn := simulate(isCallerEVM, evmGasLeft, false) From 281e460979bb5b6b985e0a4c91e649a5185d70f5 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Wed, 15 Jan 2025 14:51:01 +0100 Subject: [PATCH 62/62] Update hashes --- system-contracts/SystemContractsHashes.json | 12 ++++++------ system-contracts/contracts/EvmEmulator.yul | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/system-contracts/SystemContractsHashes.json b/system-contracts/SystemContractsHashes.json index e4676e3cd..e603b196e 100644 --- a/system-contracts/SystemContractsHashes.json +++ b/system-contracts/SystemContractsHashes.json @@ -31,8 +31,8 @@ "contractName": "ContractDeployer", "bytecodePath": "zkout/ContractDeployer.sol/ContractDeployer.json", "sourceCodePath": "contracts-preprocessed/ContractDeployer.sol", - "bytecodeHash": "0x010006d1863074c74b08fa628699b4a38599f234ada0322c430bebf62a9b28d1", - "sourceCodeHash": "0x857490d38ca4a071602c65a2463eaedd7b6a9727fe147ca0f5345a911876dac1" + "bytecodeHash": "0x010006f5ebddced229f5e5df868c4ad8c11c2875f09c9d3654a8262c2993f353", + "sourceCodeHash": "0x478a9ddf9309c7c047b0913360c7b54a6662cc78021c9881fbb40696eed52184" }, { "contractName": "Create2Factory", @@ -122,8 +122,8 @@ "contractName": "EvmEmulator", "bytecodePath": "zkout/EvmEmulator.yul/contracts-preprocessed/EvmEmulator.yul.json", "sourceCodePath": "contracts-preprocessed/EvmEmulator.yul", - "bytecodeHash": "0x01000bc1e9dd8a2f03462607f313ea4eef563b7625c643b512ddd0b8ab338e7e", - "sourceCodeHash": "0x197c5e14c8f9cdfd1a170fb97bbc5297aaa549818734928df23fa438da5c886f" + "bytecodeHash": "0x01000bbbd68833b14b8dc6b4b9331185c1e02e9c90fcdd0874c6b97c9f9ede6a", + "sourceCodeHash": "0x6ca0844e27e2e75b7c5e59f67a2effcd1b9cd1ce305efcda260a6848ea7e7b8a" }, { "contractName": "EvmGasManager", @@ -136,8 +136,8 @@ "contractName": "CodeOracle", "bytecodePath": "zkout/CodeOracle.yul/contracts-preprocessed/precompiles/CodeOracle.yul.json", "sourceCodePath": "contracts-preprocessed/precompiles/CodeOracle.yul", - "bytecodeHash": "0x01000025b1cdbcd1552a815e8d5049404dec6709f86cc057e4326484e6a1a706", - "sourceCodeHash": "0x420719c88c00478214d5babde85eb093c0bce1048c391ad67e2510ed3a61272b" + "bytecodeHash": "0x01000025c0dedc05a88665dd99d6e9a6e48800f250b4945d507268b9c872c3b6", + "sourceCodeHash": "0x0f26872aa0baae2a0c46eb8b436536c02d53d7fa97b2ae688d58bf2a20a2b73d" }, { "contractName": "EcAdd", diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index bc5c762fe..952686c01 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -2947,7 +2947,8 @@ object "EvmEmulator" { if iszero(isCallerEVM) { evmGasLeft := getEvmGasFromContext() - evmGasLeft := chargeGas(evmGasLeft, 32000) + // Charge additional creation cost + evmGasLeft := chargeGas(evmGasLeft, 32000) } let offset, len, gasToReturn := simulate(isCallerEVM, evmGasLeft, false)