Skip to content

Commit

Permalink
fix(EVM): Make CREATE flow equivalent to EVM (#1127)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xVolosnikov committed Dec 10, 2024
1 parent b050221 commit 6e6b852
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 44 deletions.
9 changes: 1 addition & 8 deletions system-contracts/contracts/ContractDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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);
}

Expand Down
86 changes: 62 additions & 24 deletions system-contracts/contracts/EvmEmulator.yul
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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)

Expand Down Expand Up @@ -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),
Expand All @@ -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)

Expand Down
43 changes: 31 additions & 12 deletions system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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)

Expand Down

0 comments on commit 6e6b852

Please sign in to comment.