diff --git a/system-contracts/SystemContractsHashes.json b/system-contracts/SystemContractsHashes.json index 6d2a9c8ee..420013feb 100644 --- a/system-contracts/SystemContractsHashes.json +++ b/system-contracts/SystemContractsHashes.json @@ -31,8 +31,8 @@ "contractName": "ContractDeployer", "bytecodePath": "artifacts-zk/contracts-preprocessed/ContractDeployer.sol/ContractDeployer.json", "sourceCodePath": "contracts-preprocessed/ContractDeployer.sol", - "bytecodeHash": "0x010006893b07703146f0b1c9b4cd9d481e206eef99147d13fea699be2fdc8ad9", - "sourceCodeHash": "0xa4c2c1f55f5ef1281e18fd016865d9b83b71ad7facb2fd5130940b3b0b7de621" + "bytecodeHash": "0x01000697c616ad2f70268eedf874c28c7396a868e5a13f3b3401ed7693a9ec4d", + "sourceCodeHash": "0xa6ce082f01fa320322b961928086128e4f3dbd35928b8754f74e49c07c1db456" }, { "contractName": "Create2Factory", @@ -122,8 +122,8 @@ "contractName": "EvmEmulator", "bytecodePath": "contracts-preprocessed/artifacts/EvmEmulator.yul/EvmEmulator.yul.zbin", "sourceCodePath": "contracts-preprocessed/EvmEmulator.yul", - "bytecodeHash": "0x01000cf74eb8015735f39ee5568f6025c9e789d2d73c9b5510614e6296c87341", - "sourceCodeHash": "0xf5d31991d176ac5beca9190ae39fae9e17f32bfe78d4359d6c564959b5478868" + "bytecodeHash": "0x01000cd3259c135498bea6fabbd58c67187ff2effbbc8c3aa2e9a9358e79defc", + "sourceCodeHash": "0xe607a7ec66e349868419f82ffeb32063b46cb9bde7e85612c44ffd79a318ec08" }, { "contractName": "EvmGasManager", diff --git a/system-contracts/contracts/ContractDeployer.sol b/system-contracts/contracts/ContractDeployer.sol index 2e1278d33..5c8d6e129 100644 --- a/system-contracts/contracts/ContractDeployer.sol +++ b/system-contracts/contracts/ContractDeployer.sol @@ -200,25 +200,30 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { return newAddress; } - function prepareForEvmCreateFromEmulator() public onlySystemCallFromEvmEmulator returns (address) { + function precreateEvmAccountFromEmulator( + bytes32 _salt, + bytes32 evmBytecodeHash + ) public onlySystemCallFromEvmEmulator returns (address newAddress) { if (_getAllowedBytecodeTypesMode() != AllowedBytecodeTypes.EraVmAndEVM) { revert EVMEmulationNotSupported(); } - return address(0); // TODO solidity semantic tests are invalid, remove it later - - // TODO uncomment - /* uint256 senderNonce = NONCE_HOLDER_SYSTEM_CONTRACT.incrementDeploymentNonce(msg.sender); - address newAddress = Utils.getNewAddressCreateEVM(msg.sender, senderNonce); + if (evmBytecodeHash != bytes32(0)) { + // Create2 case + newAddress = Utils.getNewAddressCreate2EVM(msg.sender, _salt, evmBytecodeHash); + } else { + // Create case + 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 require(NONCE_HOLDER_SYSTEM_CONTRACT.getRawNonce(newAddress) == 0x0); require(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getCodeHash(uint256(uint160(newAddress))) == 0x0); return newAddress; - */ } /// Note: only possible revert case should be due to revert in the called constructor @@ -226,34 +231,20 @@ contract ContractDeployer is IContractDeployer, SystemContractBase { address newAddress, bytes calldata _initCode ) external payable onlySystemCallFromEvmEmulator returns (address) { - // ##### TODO solidity semantic tests are invalid, remove it later - { - uint256 senderNonce = NONCE_HOLDER_SYSTEM_CONTRACT.incrementDeploymentNonce(msg.sender); - - newAddress = Utils.getNewAddressCreateEVM(msg.sender, senderNonce); - - // Unfortunately we can not provide revert reason as it would break EVM compatibility - require(NONCE_HOLDER_SYSTEM_CONTRACT.getRawNonce(newAddress) == 0x0); - require(ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.getCodeHash(uint256(uint160(newAddress))) == 0x0); - } - // ##### END TODO - _evmDeployOnAddress(msg.sender, newAddress, _initCode); - return newAddress; } /// @notice Deploys an EVM contract using address derivation of EVM's `CREATE2` opcode /// @param _salt The CREATE2 salt /// @param _initCode The init code for the contract - /// Note: this method may be callable only in system mode, - /// that is checked in the `createAccount` by `onlySystemCall` modifier. + /// Note: this method may be callable only in system mode function create2EVM( bytes32 _salt, bytes calldata _initCode ) external payable override onlySystemCall returns (address) { - // No collision is possible with the zksync's non-EVM CREATE2, since - // the prefixes are different + NONCE_HOLDER_SYSTEM_CONTRACT.incrementDeploymentNonce(msg.sender); + // No collision is possible with the zksync's non-EVM CREATE2, since the prefixes are different bytes32 bytecodeHash = EfficientCall.keccak(_initCode); address newAddress = Utils.getNewAddressCreate2EVM(msg.sender, _salt, bytecodeHash); diff --git a/system-contracts/contracts/EvmEmulator.yul b/system-contracts/contracts/EvmEmulator.yul index c7c07049d..8579ad7cb 100644 --- a/system-contracts/contracts/EvmEmulator.yul +++ b/system-contracts/contracts/EvmEmulator.yul @@ -11,7 +11,7 @@ object "EvmEmulator" { let size := getActivePtrDataSize() - if gt(size, MAX_POSSIBLE_BYTECODE()) { + if gt(size, MAX_POSSIBLE_INIT_BYTECODE()) { panic() } @@ -40,15 +40,21 @@ object "EvmEmulator" { } } - function validateCorrectBytecode(offset, len, gasToReturn) -> returnGas { - if len { + function validateBytecodeAndChargeGas(offset, deployedCodeLen, gasToReturn) -> returnGas { + if deployedCodeLen { + // EIP-3860 + if gt(deployedCodeLen, MAX_POSSIBLE_DEPLOYED_BYTECODE()) { + panic() + } + + // EIP-3541 let firstByte := shr(248, mload(offset)) if eq(firstByte, 0xEF) { - revert(0, 0) + panic() } } - let gasForCode := mul(len, 200) + let gasForCode := mul(deployedCodeLen, 200) returnGas := chargeGas(gasToReturn, gasForCode) } @@ -92,12 +98,16 @@ object "EvmEmulator" { offset := add(STACK_OFFSET(), mul(1024, 32)) } - function MAX_POSSIBLE_BYTECODE() -> max { - max := 32000 + function MAX_POSSIBLE_DEPLOYED_BYTECODE() -> max { + max := 24576 + } + + function MAX_POSSIBLE_INIT_BYTECODE() -> max { + max := mul(2, MAX_POSSIBLE_DEPLOYED_BYTECODE()) // EIP-3860 } function MEM_OFFSET() -> offset { - offset := add(BYTECODE_OFFSET(), MAX_POSSIBLE_BYTECODE()) + offset := add(BYTECODE_OFFSET(), MAX_POSSIBLE_INIT_BYTECODE()) } function MEM_OFFSET_INNER() -> offset { @@ -125,9 +135,9 @@ object "EvmEmulator" { function EVM_GAS_STIPEND() -> gas_stipend { gas_stipend := shl(30, 1) } // 1 << 30 - // We need to pass some gas for MsgValueSimulator internal logic + // We need to pass some gas for MsgValueSimulator internal logic to decommit emulator etc function MSG_VALUE_SIMULATOR_STIPEND_GAS() -> gas_stipend { - gas_stipend := 30000 // 27000 + a little bit more + gas_stipend := 35000 // 27000 + a little bit more } function OVERHEAD() -> overhead { overhead := 2000 } @@ -328,7 +338,7 @@ object "EvmEmulator" { let codeLen := _fetchDeployedCode( getCodeAddress(), add(BYTECODE_OFFSET(), 32), - MAX_POSSIBLE_BYTECODE() + MAX_POSSIBLE_DEPLOYED_BYTECODE() ) mstore(BYTECODE_OFFSET(), codeLen) @@ -379,7 +389,19 @@ object "EvmEmulator" { function performSystemCall( to, dataLength, - ) -> ret { + ) { + let success := performSystemCallRevertable(to, dataLength) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + } + + function performSystemCallRevertable( + to, + dataLength, + ) -> success { let farCallAbi := shl(248, 1) // system call // dataOffset is 0 // dataStart is 0 @@ -389,12 +411,7 @@ object "EvmEmulator" { // forwardingMode is 0 // not constructor call - let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) - - if iszero(success) { - // This error should never happen - revert(0, 0) - } + success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) } function _isEVM(_addr) -> isEVM { @@ -1091,161 +1108,127 @@ object "EvmEmulator" { gasLeft := mload(0) } - function $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeftOld, isCreate2, salt, oldStackHead) -> result, evmGasLeft, addr, stackHead { - _eraseReturndataPointer() + 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()) { + panic() + } - let gasForTheCall := capGasForCall(evmGasLeftOld, INF_PASS_GAS()) + // dynamicGas = init_code_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost + // + hash_cost, if isCreate2 + // 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 dynamicGas := add( + mul(2, minimum_word_size), + expandMemory(add(offset, size)) + ) + if isCreate2 { + // hash_cost = 6 * minimum_word_size + dynamicGas := add(dynamicGas, mul(6, minimum_word_size)) + } + evmGasLeft := chargeGas(evmGasLeftOld, dynamicGas) - offset := add(MEM_OFFSET_INNER(), offset) // TODO gas check + _eraseReturndataPointer() - pushStackCheck(sp, 4) - sp, stackHead := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x80)), oldStackHead) - sp, stackHead := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x60)), stackHead) - sp, stackHead := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x40)), stackHead) - sp, stackHead := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x20)), stackHead) + offset := add(MEM_OFFSET_INNER(), offset) // caller must ensure that it doesn't overflow - _pushEVMFrame(gasForTheCall, false) + let gasForTheCall := capGasForCall(evmGasLeft, INF_PASS_GAS()) - if isCreate2 { - // selector: create2EVM(bytes32 _salt, bytes calldata _initCode) - mstore(sub(offset, 0x80), 0x4e96f4c0) - // salt - mstore(sub(offset, 0x60), salt) - // Where the arg starts (third word) - mstore(sub(offset, 0x40), 0x40) - // Length of the init code - mstore(sub(offset, 0x20), size) + if gt(value, selfbalance()) { // it should be checked before actual deploy call + revertWithGas(evmGasLeft) // gasForTheCall not consumed + } - result := performSystemCallForCreate(value, sub(offset, 0x64), add(size, 0x64)) + let bytecodeHash := 0 + if isCreate2 { + bytecodeHash := keccak256(offset, size) } + // we want to calculate the address of new contract, and if it is deployable (no collision), + // we need to increment deploy nonce. Otherwise - panic. + // We should revert with gas if nonce overflowed, but this should not happen in reality anyway - if iszero(isCreate2) { - if gt(value, selfbalance()) { // it should be checked before actual deploy call - panic() - } + // selector: function precreateEvmAccountFromEmulator(bytes32 salt, bytes32 evmBytecodeHash) + mstore(0, 0xf81dae8600000000000000000000000000000000000000000000000000000000) + mstore(4, salt) + mstore(36, bytecodeHash) + let precreateResult := performSystemCallRevertable(DEPLOYER_SYSTEM_CONTRACT(), 68) - // we want to calculate the address of new contract - // and if it is deployable, we need to increment deploy nonce + if iszero(precreateResult) { + // collision, nonce overflow or EVM not allowed + // this is *internal* panic, consuming all passed gas + evmGasLeft := chargeGas(evmGasLeft, gasForTheCall) + } - // selector: function prepareForEvmCreateFromEmulator() - mstore(0, 0x3ec89a4e00000000000000000000000000000000000000000000000000000000) - performSystemCall(DEPLOYER_SYSTEM_CONTRACT(), 4) + if precreateResult { 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 + + // 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) - // so even if constructor reverts, nonce stays incremented - + // 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)) + // selector: function createEvmFromEmulator(address newAddress, bytes calldata _initCode) mstore(sub(offset, 0x80), 0xe43cec64) - // address mstore(sub(offset, 0x60), addr) - // Where the arg starts (third word) - mstore(sub(offset, 0x40), 0x40) - // Length of the init code - mstore(sub(offset, 0x20), size) - - result := performSystemCallForCreate(value, sub(offset, 0x64), add(size, 0x64)) - } - - let gasLeft - switch result - case 0 { - addr := 0 - gasLeft := _saveReturndataAfterEVMCall(0, 0) - } - default { - returndatacopy(0, 0, 32) - addr := mload(0) - gasLeft := _fetchConstructorReturnGas() - } - - let gasUsed := sub(gasForTheCall, gasLeft) - evmGasLeft := chargeGas(evmGasLeftOld, gasUsed) - - // moving memory slots back - let back + mstore(sub(offset, 0x40), 0x40) // Where the arg starts (third word) + mstore(sub(offset, 0x20), size) // Length of the init code - // skipping check since we pushed exactly 4 items earlier - back, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - mstore(sub(offset, 0x20), back) - back, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - mstore(sub(offset, 0x40), back) - back, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - mstore(sub(offset, 0x60), back) - back, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - mstore(sub(offset, 0x80), back) - } - - function performCreate(evmGas,oldSp,isStatic, oldStackHead) -> evmGasLeft, sp, stackHead { - evmGasLeft := chargeGas(evmGas, 32000) + let result := performSystemCallForCreate(value, sub(offset, 0x64), add(size, 0x64)) - if isStatic { - panic() + // 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)) + + let gasLeft + switch result + case 0 { + addr := 0 + gasLeft := _saveReturndataAfterEVMCall(0, 0) + } + default { + returndatacopy(0, 0, 32) + addr := mload(0) + gasLeft := _fetchConstructorReturnGas() + } + + let gasUsed := sub(gasForTheCall, gasLeft) + evmGasLeft := chargeGas(evmGasLeft, gasUsed) } + } + function performCreate(oldEvmGasLeft, oldSp, oldStackHead) -> evmGasLeft, sp, stackHead { let value, offset, size popStackCheck(oldSp, 3) value, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) offset, sp, size := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) - - if gt(size, MAX_POSSIBLE_BYTECODE()) { - panic() - } - - // dynamicGas = init_code_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost - // minimum_word_size = (size + 31) / 32 - // init_code_cost = 2 * minimum_word_size - // code_deposit_cost = 200 * deployed_code_size - let dynamicGas := add( - shr(4, add(size, 31)), - expandMemory(add(offset, size)) - ) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - - let result, addr - result, evmGasLeft, addr, stackHead := $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeft, false, 0, stackHead) - - switch result - case 0 { stackHead := 0 } - default { stackHead := addr } + evmGasLeft, stackHead := $llvm_NoInline_llvm$_genericCreate(offset, size, value, oldEvmGasLeft, false, 0) } - function performCreate2(evmGas, oldSp, isStatic, oldStackHead) -> evmGasLeft, sp, result, addr, stackHead { - evmGasLeft := chargeGas(evmGas, 32000) - - if isStatic { - panic() - } - + function performCreate2(oldEvmGasLeft, oldSp, oldStackHead) -> evmGasLeft, sp, stackHead { let value, offset, size, salt popStackCheck(oldSp, 4) value, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - salt, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) + size, sp, salt := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) - - if gt(size, MAX_POSSIBLE_BYTECODE()) { - panic() - } - - // dynamicGas = init_code_cost + hash_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost - // minimum_word_size = (size + 31) / 32 - // init_code_cost = 2 * minimum_word_size - // hash_cost = 6 * minimum_word_size - // code_deposit_cost = 200 * deployed_code_size - evmGasLeft := chargeGas(evmGasLeft, add( - expandMemory(add(offset, size)), - shr(2, add(size, 31)) - )) - - result, evmGasLeft, addr, stackHead := $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeft, true, salt, stackHead) + evmGasLeft, stackHead := $llvm_NoInline_llvm$_genericCreate(offset, size, value, oldEvmGasLeft, true, salt) } //////////////////////////////////////////////////////////////// @@ -2645,7 +2628,13 @@ object "EvmEmulator" { ip := add(ip, 1) } case 0xF0 { // OP_CREATE - evmGasLeft, sp, stackHead := performCreate(evmGasLeft, sp, isStatic, stackHead) + evmGasLeft := chargeGas(evmGasLeft, 32000) + + if isStatic { + panic() + } + + evmGasLeft, sp, stackHead := performCreate(evmGasLeft, sp, stackHead) ip := add(ip, 1) } case 0xF1 { // OP_CALL @@ -2681,11 +2670,13 @@ object "EvmEmulator" { ip := add(ip, 1) } case 0xF5 { // OP_CREATE2 - let result, addr - evmGasLeft, sp, result, addr, stackHead := performCreate2(evmGasLeft, sp, isStatic, stackHead) - switch result - case 0 { sp, stackHead := pushStackItem(sp, 0, stackHead) } - default { sp, stackHead := pushStackItem(sp, addr, stackHead) } + evmGasLeft := chargeGas(evmGasLeft, 32000) + + if isStatic { + panic() + } + + evmGasLeft, sp, stackHead := performCreate2(evmGasLeft, sp, stackHead) ip := add(ip, 1) } case 0xFA { // OP_STATICCALL @@ -3082,7 +3073,7 @@ object "EvmEmulator" { let offset, len, gasToReturn := simulate(isCallerEVM, evmGasLeft, false) - gasToReturn := validateCorrectBytecode(offset, len, gasToReturn) + gasToReturn := validateBytecodeAndChargeGas(offset, len, gasToReturn) offset, len := padBytecode(offset, len) @@ -3132,12 +3123,16 @@ object "EvmEmulator" { offset := add(STACK_OFFSET(), mul(1024, 32)) } - function MAX_POSSIBLE_BYTECODE() -> max { - max := 32000 + function MAX_POSSIBLE_DEPLOYED_BYTECODE() -> max { + max := 24576 + } + + function MAX_POSSIBLE_INIT_BYTECODE() -> max { + max := mul(2, MAX_POSSIBLE_DEPLOYED_BYTECODE()) // EIP-3860 } function MEM_OFFSET() -> offset { - offset := add(BYTECODE_OFFSET(), MAX_POSSIBLE_BYTECODE()) + offset := add(BYTECODE_OFFSET(), MAX_POSSIBLE_INIT_BYTECODE()) } function MEM_OFFSET_INNER() -> offset { @@ -3165,9 +3160,9 @@ object "EvmEmulator" { function EVM_GAS_STIPEND() -> gas_stipend { gas_stipend := shl(30, 1) } // 1 << 30 - // We need to pass some gas for MsgValueSimulator internal logic + // We need to pass some gas for MsgValueSimulator internal logic to decommit emulator etc function MSG_VALUE_SIMULATOR_STIPEND_GAS() -> gas_stipend { - gas_stipend := 30000 // 27000 + a little bit more + gas_stipend := 35000 // 27000 + a little bit more } function OVERHEAD() -> overhead { overhead := 2000 } @@ -3368,7 +3363,7 @@ object "EvmEmulator" { let codeLen := _fetchDeployedCode( getCodeAddress(), add(BYTECODE_OFFSET(), 32), - MAX_POSSIBLE_BYTECODE() + MAX_POSSIBLE_DEPLOYED_BYTECODE() ) mstore(BYTECODE_OFFSET(), codeLen) @@ -3419,7 +3414,19 @@ object "EvmEmulator" { function performSystemCall( to, dataLength, - ) -> ret { + ) { + let success := performSystemCallRevertable(to, dataLength) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } + } + + function performSystemCallRevertable( + to, + dataLength, + ) -> success { let farCallAbi := shl(248, 1) // system call // dataOffset is 0 // dataStart is 0 @@ -3429,12 +3436,7 @@ object "EvmEmulator" { // forwardingMode is 0 // not constructor call - let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) - - if iszero(success) { - // This error should never happen - revert(0, 0) - } + success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) } function _isEVM(_addr) -> isEVM { @@ -4131,161 +4133,127 @@ object "EvmEmulator" { gasLeft := mload(0) } - function $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeftOld, isCreate2, salt, oldStackHead) -> result, evmGasLeft, addr, stackHead { - _eraseReturndataPointer() + 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()) { + panic() + } + + // dynamicGas = init_code_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost + // + hash_cost, if isCreate2 + // 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 dynamicGas := add( + mul(2, minimum_word_size), + expandMemory(add(offset, size)) + ) + if isCreate2 { + // hash_cost = 6 * minimum_word_size + dynamicGas := add(dynamicGas, mul(6, minimum_word_size)) + } + evmGasLeft := chargeGas(evmGasLeftOld, dynamicGas) - let gasForTheCall := capGasForCall(evmGasLeftOld, INF_PASS_GAS()) + _eraseReturndataPointer() - offset := add(MEM_OFFSET_INNER(), offset) // TODO gas check + offset := add(MEM_OFFSET_INNER(), offset) // caller must ensure that it doesn't overflow - pushStackCheck(sp, 4) - sp, stackHead := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x80)), oldStackHead) - sp, stackHead := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x60)), stackHead) - sp, stackHead := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x40)), stackHead) - sp, stackHead := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x20)), stackHead) + let gasForTheCall := capGasForCall(evmGasLeft, INF_PASS_GAS()) - _pushEVMFrame(gasForTheCall, false) + if gt(value, selfbalance()) { // it should be checked before actual deploy call + revertWithGas(evmGasLeft) // gasForTheCall not consumed + } + let bytecodeHash := 0 if isCreate2 { - // selector: create2EVM(bytes32 _salt, bytes calldata _initCode) - mstore(sub(offset, 0x80), 0x4e96f4c0) - // salt - mstore(sub(offset, 0x60), salt) - // Where the arg starts (third word) - mstore(sub(offset, 0x40), 0x40) - // Length of the init code - mstore(sub(offset, 0x20), size) - - result := performSystemCallForCreate(value, sub(offset, 0x64), add(size, 0x64)) + bytecodeHash := keccak256(offset, size) } + // we want to calculate the address of new contract, and if it is deployable (no collision), + // we need to increment deploy nonce. Otherwise - panic. + // We should revert with gas if nonce overflowed, but this should not happen in reality anyway - if iszero(isCreate2) { - if gt(value, selfbalance()) { // it should be checked before actual deploy call - panic() - } + // selector: function precreateEvmAccountFromEmulator(bytes32 salt, bytes32 evmBytecodeHash) + mstore(0, 0xf81dae8600000000000000000000000000000000000000000000000000000000) + mstore(4, salt) + mstore(36, bytecodeHash) + let precreateResult := performSystemCallRevertable(DEPLOYER_SYSTEM_CONTRACT(), 68) - // we want to calculate the address of new contract - // and if it is deployable, we need to increment deploy nonce + if iszero(precreateResult) { + // collision, nonce overflow or EVM not allowed + // this is *internal* panic, consuming all passed gas + evmGasLeft := chargeGas(evmGasLeft, gasForTheCall) + } - // selector: function prepareForEvmCreateFromEmulator() - mstore(0, 0x3ec89a4e00000000000000000000000000000000000000000000000000000000) - performSystemCall(DEPLOYER_SYSTEM_CONTRACT(), 4) + if precreateResult { 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 + + // 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) - // so even if constructor reverts, nonce stays incremented - + // 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)) + // selector: function createEvmFromEmulator(address newAddress, bytes calldata _initCode) mstore(sub(offset, 0x80), 0xe43cec64) - // address mstore(sub(offset, 0x60), addr) - // Where the arg starts (third word) - mstore(sub(offset, 0x40), 0x40) - // Length of the init code - mstore(sub(offset, 0x20), size) - - result := performSystemCallForCreate(value, sub(offset, 0x64), add(size, 0x64)) - } - - let gasLeft - switch result - case 0 { - addr := 0 - gasLeft := _saveReturndataAfterEVMCall(0, 0) - } - default { - returndatacopy(0, 0, 32) - addr := mload(0) - gasLeft := _fetchConstructorReturnGas() - } - - let gasUsed := sub(gasForTheCall, gasLeft) - evmGasLeft := chargeGas(evmGasLeftOld, gasUsed) - - // moving memory slots back - let back + mstore(sub(offset, 0x40), 0x40) // Where the arg starts (third word) + mstore(sub(offset, 0x20), size) // Length of the init code - // skipping check since we pushed exactly 4 items earlier - back, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - mstore(sub(offset, 0x20), back) - back, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - mstore(sub(offset, 0x40), back) - back, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - mstore(sub(offset, 0x60), back) - back, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - mstore(sub(offset, 0x80), back) - } + let result := performSystemCallForCreate(value, sub(offset, 0x64), add(size, 0x64)) - function performCreate(evmGas,oldSp,isStatic, oldStackHead) -> evmGasLeft, sp, stackHead { - evmGasLeft := chargeGas(evmGas, 32000) - - if isStatic { - panic() + // 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)) + + let gasLeft + switch result + case 0 { + addr := 0 + gasLeft := _saveReturndataAfterEVMCall(0, 0) + } + default { + returndatacopy(0, 0, 32) + addr := mload(0) + gasLeft := _fetchConstructorReturnGas() + } + + let gasUsed := sub(gasForTheCall, gasLeft) + evmGasLeft := chargeGas(evmGasLeft, gasUsed) } + } + function performCreate(oldEvmGasLeft, oldSp, oldStackHead) -> evmGasLeft, sp, stackHead { let value, offset, size popStackCheck(oldSp, 3) value, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) offset, sp, size := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) - - if gt(size, MAX_POSSIBLE_BYTECODE()) { - panic() - } - - // dynamicGas = init_code_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost - // minimum_word_size = (size + 31) / 32 - // init_code_cost = 2 * minimum_word_size - // code_deposit_cost = 200 * deployed_code_size - let dynamicGas := add( - shr(4, add(size, 31)), - expandMemory(add(offset, size)) - ) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - - let result, addr - result, evmGasLeft, addr, stackHead := $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeft, false, 0, stackHead) - - switch result - case 0 { stackHead := 0 } - default { stackHead := addr } + evmGasLeft, stackHead := $llvm_NoInline_llvm$_genericCreate(offset, size, value, oldEvmGasLeft, false, 0) } - function performCreate2(evmGas, oldSp, isStatic, oldStackHead) -> evmGasLeft, sp, result, addr, stackHead { - evmGasLeft := chargeGas(evmGas, 32000) - - if isStatic { - panic() - } - + function performCreate2(oldEvmGasLeft, oldSp, oldStackHead) -> evmGasLeft, sp, stackHead { let value, offset, size, salt popStackCheck(oldSp, 4) value, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - salt, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) - - if gt(size, MAX_POSSIBLE_BYTECODE()) { - panic() - } + size, sp, salt := popStackItemWithoutCheck(sp, stackHead) - // dynamicGas = init_code_cost + hash_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost - // minimum_word_size = (size + 31) / 32 - // init_code_cost = 2 * minimum_word_size - // hash_cost = 6 * minimum_word_size - // code_deposit_cost = 200 * deployed_code_size - evmGasLeft := chargeGas(evmGasLeft, add( - expandMemory(add(offset, size)), - shr(2, add(size, 31)) - )) - - result, evmGasLeft, addr, stackHead := $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeft, true, salt, stackHead) + evmGasLeft, stackHead := $llvm_NoInline_llvm$_genericCreate(offset, size, value, oldEvmGasLeft, true, salt) } //////////////////////////////////////////////////////////////// @@ -5685,7 +5653,13 @@ object "EvmEmulator" { ip := add(ip, 1) } case 0xF0 { // OP_CREATE - evmGasLeft, sp, stackHead := performCreate(evmGasLeft, sp, isStatic, stackHead) + evmGasLeft := chargeGas(evmGasLeft, 32000) + + if isStatic { + panic() + } + + evmGasLeft, sp, stackHead := performCreate(evmGasLeft, sp, stackHead) ip := add(ip, 1) } case 0xF1 { // OP_CALL @@ -5721,11 +5695,13 @@ object "EvmEmulator" { ip := add(ip, 1) } case 0xF5 { // OP_CREATE2 - let result, addr - evmGasLeft, sp, result, addr, stackHead := performCreate2(evmGasLeft, sp, isStatic, stackHead) - switch result - case 0 { sp, stackHead := pushStackItem(sp, 0, stackHead) } - default { sp, stackHead := pushStackItem(sp, addr, stackHead) } + evmGasLeft := chargeGas(evmGasLeft, 32000) + + if isStatic { + panic() + } + + evmGasLeft, sp, stackHead := performCreate2(evmGasLeft, sp, stackHead) ip := add(ip, 1) } case 0xFA { // OP_STATICCALL diff --git a/system-contracts/evm-emulator/EvmEmulator.template.yul b/system-contracts/evm-emulator/EvmEmulator.template.yul index e6eee5b35..1730a0b8e 100644 --- a/system-contracts/evm-emulator/EvmEmulator.template.yul +++ b/system-contracts/evm-emulator/EvmEmulator.template.yul @@ -11,7 +11,7 @@ object "EvmEmulator" { let size := getActivePtrDataSize() - if gt(size, MAX_POSSIBLE_BYTECODE()) { + if gt(size, MAX_POSSIBLE_INIT_BYTECODE()) { panic() } @@ -40,15 +40,21 @@ object "EvmEmulator" { } } - function validateCorrectBytecode(offset, len, gasToReturn) -> returnGas { - if len { + function validateBytecodeAndChargeGas(offset, deployedCodeLen, gasToReturn) -> returnGas { + if deployedCodeLen { + // EIP-3860 + if gt(deployedCodeLen, MAX_POSSIBLE_DEPLOYED_BYTECODE()) { + panic() + } + + // EIP-3541 let firstByte := shr(248, mload(offset)) if eq(firstByte, 0xEF) { - revert(0, 0) + panic() } } - let gasForCode := mul(len, 200) + let gasForCode := mul(deployedCodeLen, 200) returnGas := chargeGas(gasToReturn, gasForCode) } @@ -88,7 +94,7 @@ object "EvmEmulator" { let offset, len, gasToReturn := simulate(isCallerEVM, evmGasLeft, false) - gasToReturn := validateCorrectBytecode(offset, len, gasToReturn) + gasToReturn := validateBytecodeAndChargeGas(offset, len, gasToReturn) offset, len := padBytecode(offset, len) diff --git a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul index de18a50f4..b81356bfb 100644 --- a/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorFunctions.template.yul @@ -38,12 +38,16 @@ function BYTECODE_OFFSET() -> offset { offset := add(STACK_OFFSET(), mul(1024, 32)) } -function MAX_POSSIBLE_BYTECODE() -> max { - max := 32000 +function MAX_POSSIBLE_DEPLOYED_BYTECODE() -> max { + max := 24576 +} + +function MAX_POSSIBLE_INIT_BYTECODE() -> max { + max := mul(2, MAX_POSSIBLE_DEPLOYED_BYTECODE()) // EIP-3860 } function MEM_OFFSET() -> offset { - offset := add(BYTECODE_OFFSET(), MAX_POSSIBLE_BYTECODE()) + offset := add(BYTECODE_OFFSET(), MAX_POSSIBLE_INIT_BYTECODE()) } function MEM_OFFSET_INNER() -> offset { @@ -71,9 +75,9 @@ function GAS_DIVISOR() -> gas_div { gas_div := 5 } function EVM_GAS_STIPEND() -> gas_stipend { gas_stipend := shl(30, 1) } // 1 << 30 -// We need to pass some gas for MsgValueSimulator internal logic +// We need to pass some gas for MsgValueSimulator internal logic to decommit emulator etc function MSG_VALUE_SIMULATOR_STIPEND_GAS() -> gas_stipend { - gas_stipend := 30000 // 27000 + a little bit more + gas_stipend := 35000 // 27000 + a little bit more } function OVERHEAD() -> overhead { overhead := 2000 } @@ -274,7 +278,7 @@ function getDeployedBytecode() { let codeLen := _fetchDeployedCode( getCodeAddress(), add(BYTECODE_OFFSET(), 32), - MAX_POSSIBLE_BYTECODE() + MAX_POSSIBLE_DEPLOYED_BYTECODE() ) mstore(BYTECODE_OFFSET(), codeLen) @@ -325,7 +329,19 @@ function expandMemory(newSize) -> gasCost { function performSystemCall( to, dataLength, -) -> ret { +) { + let success := performSystemCallRevertable(to, dataLength) + + if iszero(success) { + // This error should never happen + revert(0, 0) + } +} + +function performSystemCallRevertable( + to, + dataLength, +) -> success { let farCallAbi := shl(248, 1) // system call // dataOffset is 0 // dataStart is 0 @@ -335,12 +351,7 @@ function performSystemCall( // forwardingMode is 0 // not constructor call - let success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) - - if iszero(success) { - // This error should never happen - revert(0, 0) - } + success := verbatim_6i_1o("system_call", to, farCallAbi, 0, 0, 0, 0) } function _isEVM(_addr) -> isEVM { @@ -1037,161 +1048,127 @@ function _fetchConstructorReturnGas() -> gasLeft { gasLeft := mload(0) } -function $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeftOld, isCreate2, salt, oldStackHead) -> result, evmGasLeft, addr, stackHead { - _eraseReturndataPointer() +function $llvm_NoInline_llvm$_genericCreate(offset, size, value, evmGasLeftOld, isCreate2, salt) -> evmGasLeft, addr { + checkMemIsAccessible(offset, size) - let gasForTheCall := capGasForCall(evmGasLeftOld, INF_PASS_GAS()) + // EIP-3860 + if gt(size, MAX_POSSIBLE_INIT_BYTECODE()) { + panic() + } - offset := add(MEM_OFFSET_INNER(), offset) // TODO gas check + // dynamicGas = init_code_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost + // + hash_cost, if isCreate2 + // 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 dynamicGas := add( + mul(2, minimum_word_size), + expandMemory(add(offset, size)) + ) + if isCreate2 { + // hash_cost = 6 * minimum_word_size + dynamicGas := add(dynamicGas, mul(6, minimum_word_size)) + } + evmGasLeft := chargeGas(evmGasLeftOld, dynamicGas) - pushStackCheck(sp, 4) - sp, stackHead := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x80)), oldStackHead) - sp, stackHead := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x60)), stackHead) - sp, stackHead := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x40)), stackHead) - sp, stackHead := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x20)), stackHead) + _eraseReturndataPointer() - _pushEVMFrame(gasForTheCall, false) + offset := add(MEM_OFFSET_INNER(), offset) // caller must ensure that it doesn't overflow - if isCreate2 { - // selector: create2EVM(bytes32 _salt, bytes calldata _initCode) - mstore(sub(offset, 0x80), 0x4e96f4c0) - // salt - mstore(sub(offset, 0x60), salt) - // Where the arg starts (third word) - mstore(sub(offset, 0x40), 0x40) - // Length of the init code - mstore(sub(offset, 0x20), size) + let gasForTheCall := capGasForCall(evmGasLeft, INF_PASS_GAS()) - result := performSystemCallForCreate(value, sub(offset, 0x64), add(size, 0x64)) + if gt(value, selfbalance()) { // it should be checked before actual deploy call + revertWithGas(evmGasLeft) // gasForTheCall not consumed } + let bytecodeHash := 0 + if isCreate2 { + bytecodeHash := keccak256(offset, size) + } - if iszero(isCreate2) { - if gt(value, selfbalance()) { // it should be checked before actual deploy call - panic() - } + // we want to calculate the address of new contract, and if it is deployable (no collision), + // we need to increment deploy nonce. Otherwise - panic. + // We should revert with gas if nonce overflowed, but this should not happen in reality anyway - // we want to calculate the address of new contract - // and if it is deployable, we need to increment deploy nonce + // selector: function precreateEvmAccountFromEmulator(bytes32 salt, bytes32 evmBytecodeHash) + mstore(0, 0xf81dae8600000000000000000000000000000000000000000000000000000000) + mstore(4, salt) + mstore(36, bytecodeHash) + let precreateResult := performSystemCallRevertable(DEPLOYER_SYSTEM_CONTRACT(), 68) - // selector: function prepareForEvmCreateFromEmulator() - mstore(0, 0x3ec89a4e00000000000000000000000000000000000000000000000000000000) - performSystemCall(DEPLOYER_SYSTEM_CONTRACT(), 4) + if iszero(precreateResult) { + // collision, nonce overflow or EVM not allowed + // this is *internal* panic, consuming all passed gas + evmGasLeft := chargeGas(evmGasLeft, gasForTheCall) + } + + if precreateResult { returndatacopy(0, 0, 32) addr := mload(0) - - // so even if constructor reverts, nonce stays incremented - + + 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 + + // 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) + + // 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)) + // selector: function createEvmFromEmulator(address newAddress, bytes calldata _initCode) mstore(sub(offset, 0x80), 0xe43cec64) - // address mstore(sub(offset, 0x60), addr) - // Where the arg starts (third word) - mstore(sub(offset, 0x40), 0x40) - // Length of the init code - mstore(sub(offset, 0x20), size) - - result := performSystemCallForCreate(value, sub(offset, 0x64), add(size, 0x64)) - } - - let gasLeft - switch result - case 0 { - addr := 0 - gasLeft := _saveReturndataAfterEVMCall(0, 0) - } - default { - returndatacopy(0, 0, 32) - addr := mload(0) - gasLeft := _fetchConstructorReturnGas() - } - - let gasUsed := sub(gasForTheCall, gasLeft) - evmGasLeft := chargeGas(evmGasLeftOld, gasUsed) - - // moving memory slots back - let back + mstore(sub(offset, 0x40), 0x40) // Where the arg starts (third word) + mstore(sub(offset, 0x20), size) // Length of the init code - // skipping check since we pushed exactly 4 items earlier - back, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - mstore(sub(offset, 0x20), back) - back, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - mstore(sub(offset, 0x40), back) - back, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - mstore(sub(offset, 0x60), back) - back, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - mstore(sub(offset, 0x80), back) -} - -function performCreate(evmGas,oldSp,isStatic, oldStackHead) -> evmGasLeft, sp, stackHead { - evmGasLeft := chargeGas(evmGas, 32000) - - if isStatic { - panic() + 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)) + + let gasLeft + switch result + case 0 { + addr := 0 + gasLeft := _saveReturndataAfterEVMCall(0, 0) + } + default { + returndatacopy(0, 0, 32) + addr := mload(0) + gasLeft := _fetchConstructorReturnGas() + } + + let gasUsed := sub(gasForTheCall, gasLeft) + evmGasLeft := chargeGas(evmGasLeft, gasUsed) } +} +function performCreate(oldEvmGasLeft, oldSp, oldStackHead) -> evmGasLeft, sp, stackHead { let value, offset, size popStackCheck(oldSp, 3) value, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) offset, sp, size := popStackItemWithoutCheck(sp, stackHead) - checkMemIsAccessible(offset, size) - - if gt(size, MAX_POSSIBLE_BYTECODE()) { - panic() - } - - // dynamicGas = init_code_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost - // minimum_word_size = (size + 31) / 32 - // init_code_cost = 2 * minimum_word_size - // code_deposit_cost = 200 * deployed_code_size - let dynamicGas := add( - shr(4, add(size, 31)), - expandMemory(add(offset, size)) - ) - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - - let result, addr - result, evmGasLeft, addr, stackHead := $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeft, false, 0, stackHead) - - switch result - case 0 { stackHead := 0 } - default { stackHead := addr } + evmGasLeft, stackHead := $llvm_NoInline_llvm$_genericCreate(offset, size, value, oldEvmGasLeft, false, 0) } -function performCreate2(evmGas, oldSp, isStatic, oldStackHead) -> evmGasLeft, sp, result, addr, stackHead { - evmGasLeft := chargeGas(evmGas, 32000) - - if isStatic { - panic() - } - +function performCreate2(oldEvmGasLeft, oldSp, oldStackHead) -> evmGasLeft, sp, stackHead { let value, offset, size, salt popStackCheck(oldSp, 4) value, sp, stackHead := popStackItemWithoutCheck(oldSp, oldStackHead) offset, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - size, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - salt, sp, stackHead := popStackItemWithoutCheck(sp, stackHead) - - checkMemIsAccessible(offset, size) + size, sp, salt := popStackItemWithoutCheck(sp, stackHead) - if gt(size, MAX_POSSIBLE_BYTECODE()) { - panic() - } - - // dynamicGas = init_code_cost + hash_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost - // minimum_word_size = (size + 31) / 32 - // init_code_cost = 2 * minimum_word_size - // hash_cost = 6 * minimum_word_size - // code_deposit_cost = 200 * deployed_code_size - evmGasLeft := chargeGas(evmGasLeft, add( - expandMemory(add(offset, size)), - shr(2, add(size, 31)) - )) - - result, evmGasLeft, addr, stackHead := $llvm_NoInline_llvm$_genericCreate(offset, size, sp, value, evmGasLeft, true, salt, stackHead) + evmGasLeft, stackHead := $llvm_NoInline_llvm$_genericCreate(offset, size, value, oldEvmGasLeft, true, salt) } //////////////////////////////////////////////////////////////// diff --git a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul index 113dcab08..d9f388c31 100644 --- a/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul +++ b/system-contracts/evm-emulator/EvmEmulatorLoop.template.yul @@ -1343,7 +1343,13 @@ for { } true { } { ip := add(ip, 1) } case 0xF0 { // OP_CREATE - evmGasLeft, sp, stackHead := performCreate(evmGasLeft, sp, isStatic, stackHead) + evmGasLeft := chargeGas(evmGasLeft, 32000) + + if isStatic { + panic() + } + + evmGasLeft, sp, stackHead := performCreate(evmGasLeft, sp, stackHead) ip := add(ip, 1) } case 0xF1 { // OP_CALL @@ -1379,11 +1385,13 @@ for { } true { } { ip := add(ip, 1) } case 0xF5 { // OP_CREATE2 - let result, addr - evmGasLeft, sp, result, addr, stackHead := performCreate2(evmGasLeft, sp, isStatic, stackHead) - switch result - case 0 { sp, stackHead := pushStackItem(sp, 0, stackHead) } - default { sp, stackHead := pushStackItem(sp, addr, stackHead) } + evmGasLeft := chargeGas(evmGasLeft, 32000) + + if isStatic { + panic() + } + + evmGasLeft, sp, stackHead := performCreate2(evmGasLeft, sp, stackHead) ip := add(ip, 1) } case 0xFA { // OP_STATICCALL