From ee95851e972cb5eb0442553c6d8f5db24ec42dec Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Sun, 8 Dec 2024 14:46:19 +0100 Subject: [PATCH] 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 26b09ddb2..834fa0221 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 } @@ -3945,16 +3952,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 @@ -4053,12 +4063,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 b1402d863..9ddfa2182 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 }