Skip to content

Commit

Permalink
Merge pull request #1135 from matter-labs/evm-emulator/treat-zero-add…
Browse files Browse the repository at this point in the history
…ress-as-empty-account

fix(EVM): Treat 0x00 as empty account
  • Loading branch information
0xVolosnikov authored Dec 8, 2024
2 parents 1fa8728 + ee95851 commit de2cb3c
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 39 deletions.
66 changes: 40 additions & 26 deletions system-contracts/contracts/EvmEmulator.yul
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand Down
33 changes: 20 additions & 13 deletions system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand Down

0 comments on commit de2cb3c

Please sign in to comment.