From 3eb96919dd0645ef5ef12c707e96f7579b617749 Mon Sep 17 00:00:00 2001 From: Javier Chatruc Date: Mon, 20 May 2024 12:10:13 -0300 Subject: [PATCH 1/2] Charge for zkEVM Contract Decommits --- .../EvmInterpreterFunctions.template.yul | 24 +++++----- .../contracts/EvmInterpreterPreprocessed.yul | 48 +++++++++---------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/system-contracts/contracts/EvmInterpreterFunctions.template.yul b/system-contracts/contracts/EvmInterpreterFunctions.template.yul index 4a1b2307d..e3ee35b57 100644 --- a/system-contracts/contracts/EvmInterpreterFunctions.template.yul +++ b/system-contracts/contracts/EvmInterpreterFunctions.template.yul @@ -752,11 +752,9 @@ function GAS_DIVISOR() -> gas_div { gas_div := 5 } function EVM_GAS_STIPEND() -> gas_stipend { gas_stipend := shl(30, 1) } // 1 << 30 function OVERHEAD() -> overhead { overhead := 2000 } -function GAS_CONSTANTS() -> divisor, stipend, overhead { - divisor := GAS_DIVISOR() - stipend := EVM_GAS_STIPEND() - overhead := OVERHEAD() -} +// From precompiles/CodeOracle +function DECOMMIT_COST_PER_WORD() -> cost { cost := 4 } +function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 function _calcEVMGas(_zkevmGas) -> calczkevmGas { calczkevmGas := div(_zkevmGas, GAS_DIVISOR()) @@ -774,11 +772,13 @@ function getEVMGas() -> evmGas { evmGas := div(sub(_gas, requiredGas), GAS_DIVISOR()) } -function _getZkEVMGas(_evmGas) -> zkevmGas { - /* - TODO: refine the formula, especially with regard to decommitment costs - */ +function _getZkEVMGas(_evmGas, addr) -> zkevmGas { zkevmGas := mul(_evmGas, GAS_DIVISOR()) + let byteSize := extcodesize(addr) + zkevmGas := add(zkevmGas, mul(byteSize, DECOMMIT_COST_PER_WORD())) + if gt(zkevmGas, UINT32_MAX()) { + zkevmGas := UINT32_MAX() + } } function _saveReturndataAfterEVMCall(_outputOffset, _outputLen) -> _gasLeft{ @@ -861,7 +861,7 @@ function performStaticCall(oldSp,evmGasLeft) -> extraCost, sp { // zkEVM native if iszero(_isEVM(addr)) { - gasToPass := _getZkEVMGas(gasToPass) + gasToPass := _getZkEVMGas(gasToPass, addr) let zkevmGasBefore := gas() success := staticcall(gasToPass, addr, add(MEM_OFFSET_INNER(), argsOffset), argsSize, add(MEM_OFFSET_INNER(), retOffset), retSize) _saveReturndataAfterZkEVMCall() @@ -967,7 +967,7 @@ function performCall(oldSp, evmGasLeft, isStatic) -> extraCost, sp { // zkEVM native if and(iszero(_isEVM(addr)), iszero(isStatic)) { - gasToPass := _getZkEVMGas(gasToPass) + gasToPass := _getZkEVMGas(gasToPass, addr) let zkevmGasBefore := gas() success := call(gasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) _saveReturndataAfterZkEVMCall() @@ -1095,7 +1095,7 @@ function _performStaticCall( // zkEVM native if iszero(_calleeIsEVM) { - _calleeGas := _getZkEVMGas(_calleeGas) + _calleeGas := _getZkEVMGas(_calleeGas, _callee) let zkevmGasBefore := gas() success := staticcall(_calleeGas, _callee, _inputOffset, _inputLen, _outputOffset, _outputLen) diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index e037e09ac..37e261d67 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -826,11 +826,9 @@ object "EVMInterpreter" { function EVM_GAS_STIPEND() -> gas_stipend { gas_stipend := shl(30, 1) } // 1 << 30 function OVERHEAD() -> overhead { overhead := 2000 } - function GAS_CONSTANTS() -> divisor, stipend, overhead { - divisor := GAS_DIVISOR() - stipend := EVM_GAS_STIPEND() - overhead := OVERHEAD() - } + // From precompiles/CodeOracle + function DECOMMIT_COST_PER_WORD() -> cost { cost := 4 } + function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 function _calcEVMGas(_zkevmGas) -> calczkevmGas { calczkevmGas := div(_zkevmGas, GAS_DIVISOR()) @@ -848,11 +846,13 @@ object "EVMInterpreter" { evmGas := div(sub(_gas, requiredGas), GAS_DIVISOR()) } - function _getZkEVMGas(_evmGas) -> zkevmGas { - /* - TODO: refine the formula, especially with regard to decommitment costs - */ + function _getZkEVMGas(_evmGas, addr) -> zkevmGas { zkevmGas := mul(_evmGas, GAS_DIVISOR()) + let byteSize := extcodesize(addr) + zkevmGas := add(zkevmGas, mul(byteSize, DECOMMIT_COST_PER_WORD())) + if gt(zkevmGas, UINT32_MAX()) { + zkevmGas := UINT32_MAX() + } } function _saveReturndataAfterEVMCall(_outputOffset, _outputLen) -> _gasLeft{ @@ -935,7 +935,7 @@ object "EVMInterpreter" { // zkEVM native if iszero(_isEVM(addr)) { - gasToPass := _getZkEVMGas(gasToPass) + gasToPass := _getZkEVMGas(gasToPass, addr) let zkevmGasBefore := gas() success := staticcall(gasToPass, addr, add(MEM_OFFSET_INNER(), argsOffset), argsSize, add(MEM_OFFSET_INNER(), retOffset), retSize) _saveReturndataAfterZkEVMCall() @@ -1041,7 +1041,7 @@ object "EVMInterpreter" { // zkEVM native if and(iszero(_isEVM(addr)), iszero(isStatic)) { - gasToPass := _getZkEVMGas(gasToPass) + gasToPass := _getZkEVMGas(gasToPass, addr) let zkevmGasBefore := gas() success := call(gasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) _saveReturndataAfterZkEVMCall() @@ -1169,7 +1169,7 @@ object "EVMInterpreter" { // zkEVM native if iszero(_calleeIsEVM) { - _calleeGas := _getZkEVMGas(_calleeGas) + _calleeGas := _getZkEVMGas(_calleeGas, _callee) let zkevmGasBefore := gas() success := staticcall(_calleeGas, _callee, _inputOffset, _inputLen, _outputOffset, _outputLen) @@ -3402,11 +3402,9 @@ object "EVMInterpreter" { function EVM_GAS_STIPEND() -> gas_stipend { gas_stipend := shl(30, 1) } // 1 << 30 function OVERHEAD() -> overhead { overhead := 2000 } - function GAS_CONSTANTS() -> divisor, stipend, overhead { - divisor := GAS_DIVISOR() - stipend := EVM_GAS_STIPEND() - overhead := OVERHEAD() - } + // From precompiles/CodeOracle + function DECOMMIT_COST_PER_WORD() -> cost { cost := 4 } + function UINT32_MAX() -> ret { ret := 4294967295 } // 2^32 - 1 function _calcEVMGas(_zkevmGas) -> calczkevmGas { calczkevmGas := div(_zkevmGas, GAS_DIVISOR()) @@ -3424,11 +3422,13 @@ object "EVMInterpreter" { evmGas := div(sub(_gas, requiredGas), GAS_DIVISOR()) } - function _getZkEVMGas(_evmGas) -> zkevmGas { - /* - TODO: refine the formula, especially with regard to decommitment costs - */ + function _getZkEVMGas(_evmGas, addr) -> zkevmGas { zkevmGas := mul(_evmGas, GAS_DIVISOR()) + let byteSize := extcodesize(addr) + zkevmGas := add(zkevmGas, mul(byteSize, DECOMMIT_COST_PER_WORD())) + if gt(zkevmGas, UINT32_MAX()) { + zkevmGas := UINT32_MAX() + } } function _saveReturndataAfterEVMCall(_outputOffset, _outputLen) -> _gasLeft{ @@ -3511,7 +3511,7 @@ object "EVMInterpreter" { // zkEVM native if iszero(_isEVM(addr)) { - gasToPass := _getZkEVMGas(gasToPass) + gasToPass := _getZkEVMGas(gasToPass, addr) let zkevmGasBefore := gas() success := staticcall(gasToPass, addr, add(MEM_OFFSET_INNER(), argsOffset), argsSize, add(MEM_OFFSET_INNER(), retOffset), retSize) _saveReturndataAfterZkEVMCall() @@ -3617,7 +3617,7 @@ object "EVMInterpreter" { // zkEVM native if and(iszero(_isEVM(addr)), iszero(isStatic)) { - gasToPass := _getZkEVMGas(gasToPass) + gasToPass := _getZkEVMGas(gasToPass, addr) let zkevmGasBefore := gas() success := call(gasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) _saveReturndataAfterZkEVMCall() @@ -3745,7 +3745,7 @@ object "EVMInterpreter" { // zkEVM native if iszero(_calleeIsEVM) { - _calleeGas := _getZkEVMGas(_calleeGas) + _calleeGas := _getZkEVMGas(_calleeGas, _callee) let zkevmGasBefore := gas() success := staticcall(_calleeGas, _callee, _inputOffset, _inputLen, _outputOffset, _outputLen) From 1a88e645c6c8709c4dfdb1489da0ea6026ec1364 Mon Sep 17 00:00:00 2001 From: Javier Chatruc Date: Wed, 3 Jul 2024 16:06:05 -0300 Subject: [PATCH 2/2] Properly charge cost per word and substract it instead of adding --- .../contracts/EvmInterpreterFunctions.template.yul | 7 ++++++- .../contracts/EvmInterpreterPreprocessed.yul | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/system-contracts/contracts/EvmInterpreterFunctions.template.yul b/system-contracts/contracts/EvmInterpreterFunctions.template.yul index d054a8180..239d1171f 100644 --- a/system-contracts/contracts/EvmInterpreterFunctions.template.yul +++ b/system-contracts/contracts/EvmInterpreterFunctions.template.yul @@ -777,7 +777,12 @@ function getEVMGas() -> evmGas { function _getZkEVMGas(_evmGas, addr) -> zkevmGas { zkevmGas := mul(_evmGas, GAS_DIVISOR()) let byteSize := extcodesize(addr) - zkevmGas := add(zkevmGas, mul(byteSize, DECOMMIT_COST_PER_WORD())) + let should_ceil := mod(byteSize, 32) + if gt(should_ceil, 0) { + byteSize := add(byteSize, sub(32, should_ceil)) + } + let decommitGasCost := mul(div(byteSize,32), DECOMMIT_COST_PER_WORD()) + zkevmGas := sub(zkevmGas, decommitGasCost) if gt(zkevmGas, UINT32_MAX()) { zkevmGas := UINT32_MAX() } diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index 4f997b4bf..dddc81c28 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -851,7 +851,12 @@ object "EVMInterpreter" { function _getZkEVMGas(_evmGas, addr) -> zkevmGas { zkevmGas := mul(_evmGas, GAS_DIVISOR()) let byteSize := extcodesize(addr) - zkevmGas := add(zkevmGas, mul(byteSize, DECOMMIT_COST_PER_WORD())) + let should_ceil := mod(byteSize, 32) + if gt(should_ceil, 0) { + byteSize := add(byteSize, sub(32, should_ceil)) + } + let decommitGasCost := mul(div(byteSize,32), DECOMMIT_COST_PER_WORD()) + zkevmGas := sub(zkevmGas, decommitGasCost) if gt(zkevmGas, UINT32_MAX()) { zkevmGas := UINT32_MAX() } @@ -3474,7 +3479,12 @@ object "EVMInterpreter" { function _getZkEVMGas(_evmGas, addr) -> zkevmGas { zkevmGas := mul(_evmGas, GAS_DIVISOR()) let byteSize := extcodesize(addr) - zkevmGas := add(zkevmGas, mul(byteSize, DECOMMIT_COST_PER_WORD())) + let should_ceil := mod(byteSize, 32) + if gt(should_ceil, 0) { + byteSize := add(byteSize, sub(32, should_ceil)) + } + let decommitGasCost := mul(div(byteSize,32), DECOMMIT_COST_PER_WORD()) + zkevmGas := sub(zkevmGas, decommitGasCost) if gt(zkevmGas, UINT32_MAX()) { zkevmGas := UINT32_MAX() }