From fb888645d8f2c07b64554fe7d84c5d169c578856 Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Fri, 17 May 2024 15:15:15 -0300 Subject: [PATCH 01/15] Optimize sload --- .../contracts/EvmInterpreterLoop.template.yul | 6 ++++-- .../contracts/EvmInterpreterPreprocessed.yul | 12 ++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/system-contracts/contracts/EvmInterpreterLoop.template.yul b/system-contracts/contracts/EvmInterpreterLoop.template.yul index 2bd8d7b7a..a6b067811 100644 --- a/system-contracts/contracts/EvmInterpreterLoop.template.yul +++ b/system-contracts/contracts/EvmInterpreterLoop.template.yul @@ -578,13 +578,15 @@ for { } true { } { key, sp := popStackItem(sp) - if iszero(isSlotWarm(key)) { + let wasWarm := isSlotWarm(key) + + if iszero(wasWarm) { evmGasLeft := chargeGas(evmGasLeft, 2000) } value := sload(key) - if iszero(isSlotWarm(key)) { + if iszero(wasWarm) { let _wasW, _orgV := warmSlot(key, value) } diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index e037e09ac..1b3701804 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -1870,13 +1870,15 @@ object "EVMInterpreter" { key, sp := popStackItem(sp) - if iszero(isSlotWarm(key)) { + let wasWarm := isSlotWarm(key) + + if iszero(wasWarm) { evmGasLeft := chargeGas(evmGasLeft, 2000) } value := sload(key) - if iszero(isSlotWarm(key)) { + if iszero(wasWarm) { let _wasW, _orgV := warmSlot(key, value) } @@ -4457,13 +4459,15 @@ object "EVMInterpreter" { key, sp := popStackItem(sp) - if iszero(isSlotWarm(key)) { + let wasWarm := isSlotWarm(key) + + if iszero(wasWarm) { evmGasLeft := chargeGas(evmGasLeft, 2000) } value := sload(key) - if iszero(isSlotWarm(key)) { + if iszero(wasWarm) { let _wasW, _orgV := warmSlot(key, value) } From beab2071b4fed5139e07c65c70177bfbdd8d2ac4 Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Fri, 17 May 2024 15:50:02 -0300 Subject: [PATCH 02/15] Add push optimization --- .../EvmInterpreterFunctions.template.yul | 10 ++++++---- .../contracts/EvmInterpreterPreprocessed.yul | 20 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/system-contracts/contracts/EvmInterpreterFunctions.template.yul b/system-contracts/contracts/EvmInterpreterFunctions.template.yul index 4a1b2307d..e90b4f0f4 100644 --- a/system-contracts/contracts/EvmInterpreterFunctions.template.yul +++ b/system-contracts/contracts/EvmInterpreterFunctions.template.yul @@ -88,12 +88,14 @@ function readIP(ip) -> opcode { } function readBytes(start, length) -> value { - let max := add(start, length) - for {} lt(start, max) { start := add(start, 1) } { - let next_byte := readIP(start) + // TODO: Why not do this at the beginning once instead of every time? + let bytecodeLen := mload(BYTECODE_OFFSET()) - value := or(shl(8, value), next_byte) + let maxAcceptablePos := add(add(BYTECODE_OFFSET(), bytecodeLen), 31) + if gt(add(start,sub(length,1)), maxAcceptablePos) { + revert(0, 0) } + value := shr(mul(8,sub(32,length)),mload(start)) } function dupStackItem(sp, evmGas, position) -> newSp, evmGasLeft { diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index 1b3701804..ddee8ab4e 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -162,12 +162,14 @@ object "EVMInterpreter" { } function readBytes(start, length) -> value { - let max := add(start, length) - for {} lt(start, max) { start := add(start, 1) } { - let next_byte := readIP(start) + // TODO: Why not do this at the beginning once instead of every time? + let bytecodeLen := mload(BYTECODE_OFFSET()) - value := or(shl(8, value), next_byte) + let maxAcceptablePos := add(add(BYTECODE_OFFSET(), bytecodeLen), 31) + if gt(add(start,sub(length,1)), maxAcceptablePos) { + revert(0, 0) } + value := shr(mul(8,sub(32,length)),mload(start)) } function dupStackItem(sp, evmGas, position) -> newSp, evmGasLeft { @@ -2740,12 +2742,14 @@ object "EVMInterpreter" { } function readBytes(start, length) -> value { - let max := add(start, length) - for {} lt(start, max) { start := add(start, 1) } { - let next_byte := readIP(start) + // TODO: Why not do this at the beginning once instead of every time? + let bytecodeLen := mload(BYTECODE_OFFSET()) - value := or(shl(8, value), next_byte) + let maxAcceptablePos := add(add(BYTECODE_OFFSET(), bytecodeLen), 31) + if gt(add(start,sub(length,1)), maxAcceptablePos) { + revert(0, 0) } + value := shr(mul(8,sub(32,length)),mload(start)) } function dupStackItem(sp, evmGas, position) -> newSp, evmGasLeft { From ffe55e3018276100e22927766f8bf04a28fdc2b1 Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Mon, 20 May 2024 10:18:55 -0300 Subject: [PATCH 03/15] Add extcodecpoy optimization --- .../contracts/EvmInterpreterLoop.template.yul | 12 ++++++---- .../contracts/EvmInterpreterPreprocessed.yul | 24 ++++++++++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/system-contracts/contracts/EvmInterpreterLoop.template.yul b/system-contracts/contracts/EvmInterpreterLoop.template.yul index a6b067811..adc4b49e6 100644 --- a/system-contracts/contracts/EvmInterpreterLoop.template.yul +++ b/system-contracts/contracts/EvmInterpreterLoop.template.yul @@ -435,10 +435,14 @@ for { } true { } { } evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - // TODO: Check if Zeroing out the memory is necessary - let _lastByte := add(dest, len) - for {let i := dest} lt(i, _lastByte) { i := add(i, 1) } { - mstore8(i, 0) + let len_32 := shr(5, len) + for {let i := 0} lt(i, len_32) { i := add(i, 1) } { + mstore(shl(5,i),0) + } + let size_32 := shl(5,len_32) + let rest_32 := sub(len, size_32) + for {let i := 0} lt(i, rest_32) { i := add(i, 1) } { + mstore8(add(size_32,i),0) } // Gets the code from the addr pop(_fetchDeployedCode(addr, add(offset, MEM_OFFSET_INNER()), len)) diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index ddee8ab4e..ec5a2b0ac 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -1729,10 +1729,14 @@ object "EVMInterpreter" { } evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - // TODO: Check if Zeroing out the memory is necessary - let _lastByte := add(dest, len) - for {let i := dest} lt(i, _lastByte) { i := add(i, 1) } { - mstore8(i, 0) + let len_32 := shr(5, len) + for {let i := 0} lt(i, len_32) { i := add(i, 1) } { + mstore(shl(5,i),0) + } + let size_32 := shl(5,len_32) + let rest_32 := sub(len, size_32) + for {let i := 0} lt(i, rest_32) { i := add(i, 1) } { + mstore8(add(size_32,i),0) } // Gets the code from the addr pop(_fetchDeployedCode(addr, add(offset, MEM_OFFSET_INNER()), len)) @@ -4320,10 +4324,14 @@ object "EVMInterpreter" { } evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - // TODO: Check if Zeroing out the memory is necessary - let _lastByte := add(dest, len) - for {let i := dest} lt(i, _lastByte) { i := add(i, 1) } { - mstore8(i, 0) + let len_32 := shr(5, len) + for {let i := 0} lt(i, len_32) { i := add(i, 1) } { + mstore(shl(5,i),0) + } + let size_32 := shl(5,len_32) + let rest_32 := sub(len, size_32) + for {let i := 0} lt(i, rest_32) { i := add(i, 1) } { + mstore8(add(size_32,i),0) } // Gets the code from the addr pop(_fetchDeployedCode(addr, add(offset, MEM_OFFSET_INNER()), len)) From 727f9d197670a7b7f107bb3f7d25d885fb186ef9 Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Mon, 20 May 2024 10:32:24 -0300 Subject: [PATCH 04/15] Add memoryExpansion optimization --- .../EvmInterpreterFunctions.template.yul | 11 ++++------- .../contracts/EvmInterpreterPreprocessed.yul | 16 ++++++++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/system-contracts/contracts/EvmInterpreterFunctions.template.yul b/system-contracts/contracts/EvmInterpreterFunctions.template.yul index e90b4f0f4..b5c06aa0a 100644 --- a/system-contracts/contracts/EvmInterpreterFunctions.template.yul +++ b/system-contracts/contracts/EvmInterpreterFunctions.template.yul @@ -472,10 +472,10 @@ function checkMemOverflow(location) { } // Note, that this function can overflow. It's up to the caller to ensure that it does not. -function memCost(memSizeWords) -> gasCost { +/*function memCost(memSizeWords) -> gasCost { // The first term of the sum is the quadratic cost, the second one the linear one. gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords)) -} +}*/ // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. @@ -490,12 +490,9 @@ function expandMemory(newSize) -> gasCost { let newSizeInWords := div(add(newSize, 31), 32) if gt(newSizeInWords, oldSizeInWords) { - // TODO: Check this, it feels like there might be a more optimized way - // of doing this cost calculation. - let oldCost := memCost(oldSizeInWords) - let newCost := memCost(newSizeInWords) + let new_minus_old := sub(newSizeInWords, oldSizeInWords) + gasCost := add(mul(3,new_minus_old), div(mul(new_minus_old,add(newSizeInWords,oldSizeInWords)),512)) - gasCost := sub(newCost, oldCost) mstore(MEM_OFFSET(), newSizeInWords) } } diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index ec5a2b0ac..b6498c775 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -566,10 +566,12 @@ object "EVMInterpreter" { if gt(newSizeInWords, oldSizeInWords) { // TODO: Check this, it feels like there might be a more optimized way // of doing this cost calculation. - let oldCost := memCost(oldSizeInWords) - let newCost := memCost(newSizeInWords) + //let oldCost := memCost(oldSizeInWords) + //let newCost := memCost(newSizeInWords) + let new_minus_old := sub(newSizeInWords, oldSizeInWords) + gasCost := add(mul(3,new_minus_old), div(mul(new_minus_old,add(newSizeInWords,oldSizeInWords)),512)) - gasCost := sub(newCost, oldCost) + //gasCost := sub(newCost, oldCost) mstore(MEM_OFFSET(), newSizeInWords) } } @@ -3150,10 +3152,12 @@ object "EVMInterpreter" { if gt(newSizeInWords, oldSizeInWords) { // TODO: Check this, it feels like there might be a more optimized way // of doing this cost calculation. - let oldCost := memCost(oldSizeInWords) - let newCost := memCost(newSizeInWords) + //let oldCost := memCost(oldSizeInWords) + //let newCost := memCost(newSizeInWords) + let new_minus_old := sub(newSizeInWords, oldSizeInWords) + gasCost := add(mul(3,new_minus_old), div(mul(new_minus_old,add(newSizeInWords,oldSizeInWords)),512)) - gasCost := sub(newCost, oldCost) + //gasCost := sub(newCost, oldCost) mstore(MEM_OFFSET(), newSizeInWords) } } From 84b6a6a8791323aaa6dcc960da76729ee976fcd7 Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Mon, 20 May 2024 12:39:56 -0300 Subject: [PATCH 05/15] Add readIp optimization --- .../EvmInterpreterFunctions.template.yul | 143 +++- .../contracts/EvmInterpreterLoop.template.yul | 237 ++---- .../contracts/EvmInterpreterPreprocessed.yul | 778 +++++++++--------- 3 files changed, 601 insertions(+), 557 deletions(-) diff --git a/system-contracts/contracts/EvmInterpreterFunctions.template.yul b/system-contracts/contracts/EvmInterpreterFunctions.template.yul index b5c06aa0a..e89a1678f 100644 --- a/system-contracts/contracts/EvmInterpreterFunctions.template.yul +++ b/system-contracts/contracts/EvmInterpreterFunctions.template.yul @@ -75,11 +75,8 @@ function MAX_UINT() -> max_uint { } // It is the responsibility of the caller to ensure that ip >= BYTECODE_OFFSET + 32 -function readIP(ip) -> opcode { +function readIP(ip,maxAcceptablePos) -> opcode { // TODO: Why not do this at the beginning once instead of every time? - let bytecodeLen := mload(BYTECODE_OFFSET()) - - let maxAcceptablePos := add(add(BYTECODE_OFFSET(), bytecodeLen), 31) if gt(ip, maxAcceptablePos) { revert(0, 0) } @@ -87,11 +84,7 @@ function readIP(ip) -> opcode { opcode := and(mload(sub(ip, 31)), 0xff) } -function readBytes(start, length) -> value { - // TODO: Why not do this at the beginning once instead of every time? - let bytecodeLen := mload(BYTECODE_OFFSET()) - - let maxAcceptablePos := add(add(BYTECODE_OFFSET(), bytecodeLen), 31) +function readBytes(start, maxAcceptablePos,length) -> value { if gt(add(start,sub(length,1)), maxAcceptablePos) { revert(0, 0) } @@ -1204,3 +1197,135 @@ function genericCreate(addr, offset, size, sp, value, evmGasLeftOld) -> result, back, sp := popStackItem(sp) mstore(sub(offset, 0x80), back) } + +function performExtCodeCopy(evmGas,oldSp) -> evmGasLeft, sp { + evmGasLeft := chargeGas(evmGas, 100) + + let addr, dest, offset, len + addr, sp := popStackItem(oldSp) + dest, sp := popStackItem(sp) + offset, sp := popStackItem(sp) + len, sp := popStackItem(sp) + + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost + // minimum_word_size = (size + 31) / 32 + + let dynamicGas := add( + mul(3, shr(5, add(len, 31))), + expandMemory(add(dest, len)) + ) + if iszero(warmAddress(addr)) { + dynamicGas := add(dynamicGas, 2500) + } + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + let len_32 := shr(5, len) + for {let i := 0} lt(i, len_32) { i := add(i, 1) } { + mstore(shl(5,i),0) + } + let size_32 := shl(5,len_32) + let rest_32 := sub(len, size_32) + for {let i := 0} lt(i, rest_32) { i := add(i, 1) } { + mstore8(add(size_32,i),0) + } + // Gets the code from the addr + pop(_fetchDeployedCode(addr, add(offset, MEM_OFFSET_INNER()), len)) +} + +function performCreate(evmGas,oldSp,isStatic) -> evmGasLeft, sp { + evmGasLeft := chargeGas(evmGas, 32000) + + if isStatic { + revert(0, 0) + } + + let value, offset, size + + value, sp := popStackItem(oldSp) + offset, sp := popStackItem(sp) + size, sp := popStackItem(sp) + + checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size))) + + if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { + revert(0, 0) + } + + if gt(value, balance(address())) { + revert(0, 0) + } + + // 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 addr := getNewAddress(address()) + + let result + result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) + + switch result + case 0 { sp := pushStackItem(sp, 0) } + default { sp := pushStackItem(sp, addr) } +} + +function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp{ + evmGasLeft := chargeGas(evmGas, 32000) + + if isStatic { + revert(0, 0) + } + + let value, offset, size, salt + + value, sp := popStackItem(oldSp) + offset, sp := popStackItem(sp) + size, sp := popStackItem(sp) + salt, sp := popStackItem(sp) + + checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size))) + + if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { + revert(0, 0) + } + + if gt(value, balance(address())) { + revert(0, 0) + } + + // 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)) + )) + + { + let hashedBytecode := keccak256(add(MEM_OFFSET_INNER(), offset), size) + mstore8(0, 0xFF) + mstore(0x01, shl(0x60, address())) + mstore(0x15, salt) + mstore(0x35, hashedBytecode) + } + + let addr := and( + keccak256(0, 0x55), + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + ) + + let result + result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) + + switch result + case 0 { sp := pushStackItem(sp, 0) } + default { sp := pushStackItem(sp, addr) } +} diff --git a/system-contracts/contracts/EvmInterpreterLoop.template.yul b/system-contracts/contracts/EvmInterpreterLoop.template.yul index adc4b49e6..0d3b2e0a9 100644 --- a/system-contracts/contracts/EvmInterpreterLoop.template.yul +++ b/system-contracts/contracts/EvmInterpreterLoop.template.yul @@ -5,8 +5,10 @@ let sp := sub(STACK_OFFSET(), 32) let ip := add(BYTECODE_OFFSET(), 32) let opcode +let maxAcceptablePos := add(add(BYTECODE_OFFSET(), mload(BYTECODE_OFFSET())), 31) + for { } true { } { - opcode := readIP(ip) + opcode := readIP(ip,maxAcceptablePos) ip := add(ip, 1) @@ -270,6 +272,7 @@ for { } true { } { sp := pushStackItem(sp, sar(shift, value)) } case 0x20 { // OP_KECCAK256 + evmGasLeft := chargeGas(evmGasLeft, 30) let offset, size @@ -283,11 +286,11 @@ for { } true { } { // an expansion, which costs gas. // dynamicGas = 6 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let minWordSize := shr(5, add(size, 31)) - let dynamicGas := add(mul(6, minWordSize), expandMemory(add(offset, size))) + let dynamicGas := add(mul(6, shr(5, add(size, 31))), expandMemory(add(offset, size))) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) sp := pushStackItem(sp, keccak) + } case 0x30 { // OP_ADDRESS evmGasLeft := chargeGas(evmGasLeft, 2) @@ -337,6 +340,7 @@ for { } true { } { sp := pushStackItem(sp, calldatasize()) } case 0x37 { // OP_CALLDATACOPY + evmGasLeft := chargeGas(evmGasLeft, 3) let destOffset, offset, size @@ -350,11 +354,11 @@ for { } true { } { // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let minWordSize := shr(5, add(size, 31)) - let dynamicGas := add(mul(3, minWordSize), expandMemory(add(destOffset, size))) + let dynamicGas := add(mul(3, shr(5, add(size, 31))), expandMemory(add(destOffset, size))) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) calldatacopy(add(destOffset, MEM_OFFSET_INNER()), offset, size) + } case 0x38 { // OP_CODESIZE evmGasLeft := chargeGas(evmGasLeft, 2) @@ -363,6 +367,7 @@ for { } true { } { sp := pushStackItem(sp, bytecodeLen) } case 0x39 { // OP_CODECOPY + evmGasLeft := chargeGas(evmGasLeft, 3) let dst, offset, len @@ -373,8 +378,7 @@ for { } true { } { // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let minWordSize := shr(5, add(len, 31)) - let dynamicGas := add(mul(3, minWordSize), expandMemory(add(dst, len))) + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(add(dst, len))) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) dst := add(dst, MEM_OFFSET_INNER()) @@ -392,6 +396,7 @@ for { } true { } { shr(248, mload(add(offset, i))) ) } + } case 0x3A { // OP_GASPRICE evmGasLeft := chargeGas(evmGasLeft, 2) @@ -416,36 +421,7 @@ for { } true { } { default { sp := pushStackItem(sp, _fetchDeployedCodeLen(addr)) } } case 0x3C { // OP_EXTCODECOPY - evmGasLeft := chargeGas(evmGasLeft, 100) - - let addr, dest, offset, len - addr, sp := popStackItem(sp) - dest, sp := popStackItem(sp) - offset, sp := popStackItem(sp) - len, sp := popStackItem(sp) - - // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost - // minimum_word_size = (size + 31) / 32 - let dynamicGas := add( - mul(3, shr(5, add(len, 31))), - expandMemory(add(dest, len)) - ) - if iszero(warmAddress(addr)) { - dynamicGas := add(dynamicGas, 2500) - } - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - - let len_32 := shr(5, len) - for {let i := 0} lt(i, len_32) { i := add(i, 1) } { - mstore(shl(5,i),0) - } - let size_32 := shl(5,len_32) - let rest_32 := sub(len, size_32) - for {let i := 0} lt(i, rest_32) { i := add(i, 1) } { - mstore8(add(size_32,i),0) - } - // Gets the code from the addr - pop(_fetchDeployedCode(addr, add(offset, MEM_OFFSET_INNER()), len)) + evmGasLeft, sp := performExtCodeCopy(evmGasLeft, sp) } case 0x3D { // OP_RETURNDATASIZE evmGasLeft := chargeGas(evmGasLeft, 2) @@ -454,6 +430,7 @@ for { } true { } { sp := pushStackItem(sp, rdz) } case 0x3E { // OP_RETURNDATACOPY + evmGasLeft := chargeGas(evmGasLeft, 3) let dest, offset, len @@ -471,11 +448,11 @@ for { } true { } { // minimum_word_size = (size + 31) / 32 // dynamicGas = 3 * minimum_word_size + memory_expansion_cost - let minWordSize := shr(5, add(len, 31)) - let dynamicGas := add(mul(3, minWordSize), expandMemory(add(dest, len))) + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(add(dest, len))) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) copyActivePtrData(add(MEM_OFFSET_INNER(), dest), offset, len) + } case 0x3F { // OP_EXTCODEHASH evmGasLeft := chargeGas(evmGasLeft, 100) @@ -576,6 +553,7 @@ for { } true { } { mstore8(add(MEM_OFFSET_INNER(), offset), value) } case 0x54 { // OP_SLOAD + evmGasLeft := chargeGas(evmGasLeft, 100) let key, value, isWarm @@ -595,8 +573,10 @@ for { } true { } { } sp := pushStackItem(sp,value) + } case 0x55 { // OP_SSTORE + evmGasLeft := chargeGas(evmGasLeft, 100) if isStatic { @@ -633,6 +613,7 @@ for { } true { } { evmGasLeft := chargeGas(evmGasLeft, gasSpent) sstore(key, value) + } // NOTE: We don't currently do full jumpdest validation // (i.e. validating a jumpdest isn't in PUSH data) @@ -646,7 +627,7 @@ for { } true { } { ip := add(add(BYTECODE_OFFSET(), 32), counter) // Check next opcode is JUMPDEST - let nextOpcode := readIP(ip) + let nextOpcode := readIP(ip,maxAcceptablePos) if iszero(eq(nextOpcode, 0x5B)) { revert(0, 0) } @@ -666,7 +647,7 @@ for { } true { } { ip := add(add(BYTECODE_OFFSET(), 32), counter) // Check next opcode is JUMPDEST - let nextOpcode := readIP(ip) + let nextOpcode := readIP(ip,maxAcceptablePos) if iszero(eq(nextOpcode, 0x5B)) { revert(0, 0) } @@ -705,7 +686,7 @@ for { } true { } { case 0x60 { // OP_PUSH1 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,1) + let value := readBytes(ip,maxAcceptablePos,1) sp := pushStackItem(sp, value) ip := add(ip, 1) @@ -713,7 +694,7 @@ for { } true { } { case 0x61 { // OP_PUSH2 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,2) + let value := readBytes(ip,maxAcceptablePos,2) sp := pushStackItem(sp, value) ip := add(ip, 2) @@ -721,7 +702,7 @@ for { } true { } { case 0x62 { // OP_PUSH3 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,3) + let value := readBytes(ip,maxAcceptablePos,3) sp := pushStackItem(sp, value) ip := add(ip, 3) @@ -729,7 +710,7 @@ for { } true { } { case 0x63 { // OP_PUSH4 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,4) + let value := readBytes(ip,maxAcceptablePos,4) sp := pushStackItem(sp, value) ip := add(ip, 4) @@ -737,7 +718,7 @@ for { } true { } { case 0x64 { // OP_PUSH5 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,5) + let value := readBytes(ip,maxAcceptablePos,5) sp := pushStackItem(sp, value) ip := add(ip, 5) @@ -745,7 +726,7 @@ for { } true { } { case 0x65 { // OP_PUSH6 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,6) + let value := readBytes(ip,maxAcceptablePos,6) sp := pushStackItem(sp, value) ip := add(ip, 6) @@ -753,7 +734,7 @@ for { } true { } { case 0x66 { // OP_PUSH7 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,7) + let value := readBytes(ip,maxAcceptablePos,7) sp := pushStackItem(sp, value) ip := add(ip, 7) @@ -761,7 +742,7 @@ for { } true { } { case 0x67 { // OP_PUSH8 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,8) + let value := readBytes(ip,maxAcceptablePos,8) sp := pushStackItem(sp, value) ip := add(ip, 8) @@ -769,7 +750,7 @@ for { } true { } { case 0x68 { // OP_PUSH9 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,9) + let value := readBytes(ip,maxAcceptablePos,9) sp := pushStackItem(sp, value) ip := add(ip, 9) @@ -777,7 +758,7 @@ for { } true { } { case 0x69 { // OP_PUSH10 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,10) + let value := readBytes(ip,maxAcceptablePos,10) sp := pushStackItem(sp, value) ip := add(ip, 10) @@ -785,7 +766,7 @@ for { } true { } { case 0x6A { // OP_PUSH11 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,11) + let value := readBytes(ip,maxAcceptablePos,11) sp := pushStackItem(sp, value) ip := add(ip, 11) @@ -793,7 +774,7 @@ for { } true { } { case 0x6B { // OP_PUSH12 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,12) + let value := readBytes(ip,maxAcceptablePos,12) sp := pushStackItem(sp, value) ip := add(ip, 12) @@ -801,7 +782,7 @@ for { } true { } { case 0x6C { // OP_PUSH13 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,13) + let value := readBytes(ip,maxAcceptablePos,13) sp := pushStackItem(sp, value) ip := add(ip, 13) @@ -809,7 +790,7 @@ for { } true { } { case 0x6D { // OP_PUSH14 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,14) + let value := readBytes(ip,maxAcceptablePos,14) sp := pushStackItem(sp, value) ip := add(ip, 14) @@ -817,7 +798,7 @@ for { } true { } { case 0x6E { // OP_PUSH15 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,15) + let value := readBytes(ip,maxAcceptablePos,15) sp := pushStackItem(sp, value) ip := add(ip, 15) @@ -825,7 +806,7 @@ for { } true { } { case 0x6F { // OP_PUSH16 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,16) + let value := readBytes(ip,maxAcceptablePos,16) sp := pushStackItem(sp, value) ip := add(ip, 16) @@ -833,7 +814,7 @@ for { } true { } { case 0x70 { // OP_PUSH17 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,17) + let value := readBytes(ip,maxAcceptablePos,17) sp := pushStackItem(sp, value) ip := add(ip, 17) @@ -841,7 +822,7 @@ for { } true { } { case 0x71 { // OP_PUSH18 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,18) + let value := readBytes(ip,maxAcceptablePos,18) sp := pushStackItem(sp, value) ip := add(ip, 18) @@ -849,7 +830,7 @@ for { } true { } { case 0x72 { // OP_PUSH19 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,19) + let value := readBytes(ip,maxAcceptablePos,19) sp := pushStackItem(sp, value) ip := add(ip, 19) @@ -857,7 +838,7 @@ for { } true { } { case 0x73 { // OP_PUSH20 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,20) + let value := readBytes(ip,maxAcceptablePos,20) sp := pushStackItem(sp, value) ip := add(ip, 20) @@ -865,7 +846,7 @@ for { } true { } { case 0x74 { // OP_PUSH21 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,21) + let value := readBytes(ip,maxAcceptablePos,21) sp := pushStackItem(sp, value) ip := add(ip, 21) @@ -873,7 +854,7 @@ for { } true { } { case 0x75 { // OP_PUSH22 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,22) + let value := readBytes(ip,maxAcceptablePos,22) sp := pushStackItem(sp, value) ip := add(ip, 22) @@ -881,7 +862,7 @@ for { } true { } { case 0x76 { // OP_PUSH23 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,23) + let value := readBytes(ip,maxAcceptablePos,23) sp := pushStackItem(sp, value) ip := add(ip, 23) @@ -889,7 +870,7 @@ for { } true { } { case 0x77 { // OP_PUSH24 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,24) + let value := readBytes(ip,maxAcceptablePos,24) sp := pushStackItem(sp, value) ip := add(ip, 24) @@ -897,7 +878,7 @@ for { } true { } { case 0x78 { // OP_PUSH25 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,25) + let value := readBytes(ip,maxAcceptablePos,25) sp := pushStackItem(sp, value) ip := add(ip, 25) @@ -905,7 +886,7 @@ for { } true { } { case 0x79 { // OP_PUSH26 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,26) + let value := readBytes(ip,maxAcceptablePos,26) sp := pushStackItem(sp, value) ip := add(ip, 26) @@ -913,7 +894,7 @@ for { } true { } { case 0x7A { // OP_PUSH27 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,27) + let value := readBytes(ip,maxAcceptablePos,27) sp := pushStackItem(sp, value) ip := add(ip, 27) @@ -921,7 +902,7 @@ for { } true { } { case 0x7B { // OP_PUSH28 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,28) + let value := readBytes(ip,maxAcceptablePos,28) sp := pushStackItem(sp, value) ip := add(ip, 28) @@ -929,7 +910,7 @@ for { } true { } { case 0x7C { // OP_PUSH29 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,29) + let value := readBytes(ip,maxAcceptablePos,29) sp := pushStackItem(sp, value) ip := add(ip, 29) @@ -937,7 +918,7 @@ for { } true { } { case 0x7D { // OP_PUSH30 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,30) + let value := readBytes(ip,maxAcceptablePos,30) sp := pushStackItem(sp, value) ip := add(ip, 30) @@ -945,7 +926,7 @@ for { } true { } { case 0x7E { // OP_PUSH31 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,31) + let value := readBytes(ip,maxAcceptablePos,31) sp := pushStackItem(sp, value) ip := add(ip, 31) @@ -953,7 +934,7 @@ for { } true { } { case 0x7F { // OP_PUSH32 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,32) + let value := readBytes(ip,maxAcceptablePos,32) sp := pushStackItem(sp, value) ip := add(ip, 32) @@ -1100,11 +1081,9 @@ for { } true { } { revert(0, 0) } - let offset, size, topic1, topic2 + let offset, size offset, sp := popStackItem(sp) size, sp := popStackItem(sp) - topic1, sp := popStackItem(sp) - topic2, sp := popStackItem(sp) checkMemOverflow(add(add(offset, MEM_OFFSET_INNER()), size)) @@ -1113,7 +1092,12 @@ for { } true { } { dynamicGas := add(dynamicGas, 750) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - log2(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2) + { + let topic1, topic2 + topic1, sp := popStackItem(sp) + topic2, sp := popStackItem(sp) + log2(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2) + } } case 0xA3 { // OP_LOG3 evmGasLeft := chargeGas(evmGasLeft, 375) @@ -1139,7 +1123,7 @@ for { } true { } { topic2, sp := popStackItem(sp) topic3, sp := popStackItem(sp) log3(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2, topic3) - } + } } case 0xA4 { // OP_LOG4 evmGasLeft := chargeGas(evmGasLeft, 375) @@ -1166,50 +1150,10 @@ for { } true { } { topic3, sp := popStackItem(sp) topic4, sp := popStackItem(sp) log4(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2, topic3, topic4) - } - + } } case 0xF0 { // OP_CREATE - evmGasLeft := chargeGas(evmGasLeft, 32000) - - if isStatic { - revert(0, 0) - } - - let value, offset, size - - value, sp := popStackItem(sp) - offset, sp := popStackItem(sp) - size, sp := popStackItem(sp) - - checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size))) - - if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { - revert(0, 0) - } - - if gt(value, balance(address())) { - revert(0, 0) - } - - // 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 addr := getNewAddress(address()) - - let result - result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) //code_deposit_cost missing - - switch result - case 0 { sp := pushStackItem(sp, 0) } - default { sp := pushStackItem(sp, addr) } + evmGasLeft, sp := performCreate(evmGasLeft, sp, isStatic) } case 0xF1 { // OP_CALL evmGasLeft := chargeGas(evmGasLeft, 100) @@ -1245,58 +1189,7 @@ for { } true { } { evmGasLeft := chargeGas(evmGasLeft, gasUsed) } case 0xF5 { // OP_CREATE2 - evmGasLeft := chargeGas(evmGasLeft, 32000) - - if isStatic { - revert(0, 0) - } - - let value, offset, size, salt - - value, sp := popStackItem(sp) - offset, sp := popStackItem(sp) - size, sp := popStackItem(sp) - salt, sp := popStackItem(sp) - - checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size))) - - if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { - revert(0, 0) - } - - if gt(value, balance(address())) { - revert(0, 0) - } - - // 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)) - )) - - { - let hashedBytecode := keccak256(add(MEM_OFFSET_INNER(), offset), size) - mstore8(0, 0xFF) - mstore(0x01, shl(0x60, address())) - mstore(0x15, salt) - mstore(0x35, hashedBytecode) - } - - let addr := and( - keccak256(0, 0x55), - 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - ) - - let result - result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) //code_deposit_cost missing - - switch result - case 0 { sp := pushStackItem(sp, 0) } - default { sp := pushStackItem(sp, addr) } + evmGasLeft, sp := performCreate2(evmGasLeft, sp, isStatic) } case 0xFA { // OP_STATICCALL evmGasLeft := chargeGas(evmGasLeft, 100) diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index b6498c775..3ae61e83a 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -149,11 +149,8 @@ object "EVMInterpreter" { } // It is the responsibility of the caller to ensure that ip >= BYTECODE_OFFSET + 32 - function readIP(ip) -> opcode { + function readIP(ip,maxAcceptablePos) -> opcode { // TODO: Why not do this at the beginning once instead of every time? - let bytecodeLen := mload(BYTECODE_OFFSET()) - - let maxAcceptablePos := add(add(BYTECODE_OFFSET(), bytecodeLen), 31) if gt(ip, maxAcceptablePos) { revert(0, 0) } @@ -161,11 +158,7 @@ object "EVMInterpreter" { opcode := and(mload(sub(ip, 31)), 0xff) } - function readBytes(start, length) -> value { - // TODO: Why not do this at the beginning once instead of every time? - let bytecodeLen := mload(BYTECODE_OFFSET()) - - let maxAcceptablePos := add(add(BYTECODE_OFFSET(), bytecodeLen), 31) + function readBytes(start, maxAcceptablePos,length) -> value { if gt(add(start,sub(length,1)), maxAcceptablePos) { revert(0, 0) } @@ -546,10 +539,10 @@ object "EVMInterpreter" { } // Note, that this function can overflow. It's up to the caller to ensure that it does not. - function memCost(memSizeWords) -> gasCost { + /*function memCost(memSizeWords) -> gasCost { // The first term of the sum is the quadratic cost, the second one the linear one. gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords)) - } + }*/ // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. @@ -564,14 +557,9 @@ object "EVMInterpreter" { let newSizeInWords := div(add(newSize, 31), 32) if gt(newSizeInWords, oldSizeInWords) { - // TODO: Check this, it feels like there might be a more optimized way - // of doing this cost calculation. - //let oldCost := memCost(oldSizeInWords) - //let newCost := memCost(newSizeInWords) let new_minus_old := sub(newSizeInWords, oldSizeInWords) gasCost := add(mul(3,new_minus_old), div(mul(new_minus_old,add(newSizeInWords,oldSizeInWords)),512)) - //gasCost := sub(newCost, oldCost) mstore(MEM_OFFSET(), newSizeInWords) } } @@ -1284,6 +1272,138 @@ object "EVMInterpreter" { mstore(sub(offset, 0x80), back) } + function performExtCodeCopy(evmGas,oldSp) -> evmGasLeft, sp { + evmGasLeft := chargeGas(evmGas, 100) + + let addr, dest, offset, len + addr, sp := popStackItem(oldSp) + dest, sp := popStackItem(sp) + offset, sp := popStackItem(sp) + len, sp := popStackItem(sp) + + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost + // minimum_word_size = (size + 31) / 32 + + let dynamicGas := add( + mul(3, shr(5, add(len, 31))), + expandMemory(add(dest, len)) + ) + if iszero(warmAddress(addr)) { + dynamicGas := add(dynamicGas, 2500) + } + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + let len_32 := shr(5, len) + for {let i := 0} lt(i, len_32) { i := add(i, 1) } { + mstore(shl(5,i),0) + } + let size_32 := shl(5,len_32) + let rest_32 := sub(len, size_32) + for {let i := 0} lt(i, rest_32) { i := add(i, 1) } { + mstore8(add(size_32,i),0) + } + // Gets the code from the addr + pop(_fetchDeployedCode(addr, add(offset, MEM_OFFSET_INNER()), len)) + } + + function performCreate(evmGas,oldSp,isStatic) -> evmGasLeft, sp { + evmGasLeft := chargeGas(evmGas, 32000) + + if isStatic { + revert(0, 0) + } + + let value, offset, size + + value, sp := popStackItem(oldSp) + offset, sp := popStackItem(sp) + size, sp := popStackItem(sp) + + checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size))) + + if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { + revert(0, 0) + } + + if gt(value, balance(address())) { + revert(0, 0) + } + + // 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 addr := getNewAddress(address()) + + let result + result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) + + switch result + case 0 { sp := pushStackItem(sp, 0) } + default { sp := pushStackItem(sp, addr) } + } + + function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp{ + evmGasLeft := chargeGas(evmGas, 32000) + + if isStatic { + revert(0, 0) + } + + let value, offset, size, salt + + value, sp := popStackItem(oldSp) + offset, sp := popStackItem(sp) + size, sp := popStackItem(sp) + salt, sp := popStackItem(sp) + + checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size))) + + if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { + revert(0, 0) + } + + if gt(value, balance(address())) { + revert(0, 0) + } + + // 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)) + )) + + { + let hashedBytecode := keccak256(add(MEM_OFFSET_INNER(), offset), size) + mstore8(0, 0xFF) + mstore(0x01, shl(0x60, address())) + mstore(0x15, salt) + mstore(0x35, hashedBytecode) + } + + let addr := and( + keccak256(0, 0x55), + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + ) + + let result + result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) + + switch result + case 0 { sp := pushStackItem(sp, 0) } + default { sp := pushStackItem(sp, addr) } + } + function simulate( isCallerEVM, @@ -1301,8 +1421,10 @@ object "EVMInterpreter" { let ip := add(BYTECODE_OFFSET(), 32) let opcode + let maxAcceptablePos := add(add(BYTECODE_OFFSET(), mload(BYTECODE_OFFSET())), 31) + for { } true { } { - opcode := readIP(ip) + opcode := readIP(ip,maxAcceptablePos) ip := add(ip, 1) @@ -1566,6 +1688,7 @@ object "EVMInterpreter" { sp := pushStackItem(sp, sar(shift, value)) } case 0x20 { // OP_KECCAK256 + evmGasLeft := chargeGas(evmGasLeft, 30) let offset, size @@ -1579,11 +1702,11 @@ object "EVMInterpreter" { // an expansion, which costs gas. // dynamicGas = 6 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let minWordSize := shr(5, add(size, 31)) - let dynamicGas := add(mul(6, minWordSize), expandMemory(add(offset, size))) + let dynamicGas := add(mul(6, shr(5, add(size, 31))), expandMemory(add(offset, size))) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) sp := pushStackItem(sp, keccak) + } case 0x30 { // OP_ADDRESS evmGasLeft := chargeGas(evmGasLeft, 2) @@ -1633,6 +1756,7 @@ object "EVMInterpreter" { sp := pushStackItem(sp, calldatasize()) } case 0x37 { // OP_CALLDATACOPY + evmGasLeft := chargeGas(evmGasLeft, 3) let destOffset, offset, size @@ -1646,11 +1770,11 @@ object "EVMInterpreter" { // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let minWordSize := shr(5, add(size, 31)) - let dynamicGas := add(mul(3, minWordSize), expandMemory(add(destOffset, size))) + let dynamicGas := add(mul(3, shr(5, add(size, 31))), expandMemory(add(destOffset, size))) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) calldatacopy(add(destOffset, MEM_OFFSET_INNER()), offset, size) + } case 0x38 { // OP_CODESIZE evmGasLeft := chargeGas(evmGasLeft, 2) @@ -1659,6 +1783,7 @@ object "EVMInterpreter" { sp := pushStackItem(sp, bytecodeLen) } case 0x39 { // OP_CODECOPY + evmGasLeft := chargeGas(evmGasLeft, 3) let dst, offset, len @@ -1669,8 +1794,7 @@ object "EVMInterpreter" { // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let minWordSize := shr(5, add(len, 31)) - let dynamicGas := add(mul(3, minWordSize), expandMemory(add(dst, len))) + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(add(dst, len))) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) dst := add(dst, MEM_OFFSET_INNER()) @@ -1688,6 +1812,7 @@ object "EVMInterpreter" { shr(248, mload(add(offset, i))) ) } + } case 0x3A { // OP_GASPRICE evmGasLeft := chargeGas(evmGasLeft, 2) @@ -1712,36 +1837,7 @@ object "EVMInterpreter" { default { sp := pushStackItem(sp, _fetchDeployedCodeLen(addr)) } } case 0x3C { // OP_EXTCODECOPY - evmGasLeft := chargeGas(evmGasLeft, 100) - - let addr, dest, offset, len - addr, sp := popStackItem(sp) - dest, sp := popStackItem(sp) - offset, sp := popStackItem(sp) - len, sp := popStackItem(sp) - - // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost - // minimum_word_size = (size + 31) / 32 - let dynamicGas := add( - mul(3, shr(5, add(len, 31))), - expandMemory(add(dest, len)) - ) - if iszero(warmAddress(addr)) { - dynamicGas := add(dynamicGas, 2500) - } - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - - let len_32 := shr(5, len) - for {let i := 0} lt(i, len_32) { i := add(i, 1) } { - mstore(shl(5,i),0) - } - let size_32 := shl(5,len_32) - let rest_32 := sub(len, size_32) - for {let i := 0} lt(i, rest_32) { i := add(i, 1) } { - mstore8(add(size_32,i),0) - } - // Gets the code from the addr - pop(_fetchDeployedCode(addr, add(offset, MEM_OFFSET_INNER()), len)) + evmGasLeft, sp := performExtCodeCopy(evmGasLeft, sp) } case 0x3D { // OP_RETURNDATASIZE evmGasLeft := chargeGas(evmGasLeft, 2) @@ -1750,6 +1846,7 @@ object "EVMInterpreter" { sp := pushStackItem(sp, rdz) } case 0x3E { // OP_RETURNDATACOPY + evmGasLeft := chargeGas(evmGasLeft, 3) let dest, offset, len @@ -1767,11 +1864,11 @@ object "EVMInterpreter" { // minimum_word_size = (size + 31) / 32 // dynamicGas = 3 * minimum_word_size + memory_expansion_cost - let minWordSize := shr(5, add(len, 31)) - let dynamicGas := add(mul(3, minWordSize), expandMemory(add(dest, len))) + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(add(dest, len))) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) copyActivePtrData(add(MEM_OFFSET_INNER(), dest), offset, len) + } case 0x3F { // OP_EXTCODEHASH evmGasLeft := chargeGas(evmGasLeft, 100) @@ -1872,6 +1969,7 @@ object "EVMInterpreter" { mstore8(add(MEM_OFFSET_INNER(), offset), value) } case 0x54 { // OP_SLOAD + evmGasLeft := chargeGas(evmGasLeft, 100) let key, value, isWarm @@ -1891,8 +1989,10 @@ object "EVMInterpreter" { } sp := pushStackItem(sp,value) + } case 0x55 { // OP_SSTORE + evmGasLeft := chargeGas(evmGasLeft, 100) if isStatic { @@ -1929,6 +2029,7 @@ object "EVMInterpreter" { evmGasLeft := chargeGas(evmGasLeft, gasSpent) sstore(key, value) + } // NOTE: We don't currently do full jumpdest validation // (i.e. validating a jumpdest isn't in PUSH data) @@ -1942,7 +2043,7 @@ object "EVMInterpreter" { ip := add(add(BYTECODE_OFFSET(), 32), counter) // Check next opcode is JUMPDEST - let nextOpcode := readIP(ip) + let nextOpcode := readIP(ip,maxAcceptablePos) if iszero(eq(nextOpcode, 0x5B)) { revert(0, 0) } @@ -1962,7 +2063,7 @@ object "EVMInterpreter" { ip := add(add(BYTECODE_OFFSET(), 32), counter) // Check next opcode is JUMPDEST - let nextOpcode := readIP(ip) + let nextOpcode := readIP(ip,maxAcceptablePos) if iszero(eq(nextOpcode, 0x5B)) { revert(0, 0) } @@ -2001,7 +2102,7 @@ object "EVMInterpreter" { case 0x60 { // OP_PUSH1 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,1) + let value := readBytes(ip,maxAcceptablePos,1) sp := pushStackItem(sp, value) ip := add(ip, 1) @@ -2009,7 +2110,7 @@ object "EVMInterpreter" { case 0x61 { // OP_PUSH2 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,2) + let value := readBytes(ip,maxAcceptablePos,2) sp := pushStackItem(sp, value) ip := add(ip, 2) @@ -2017,7 +2118,7 @@ object "EVMInterpreter" { case 0x62 { // OP_PUSH3 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,3) + let value := readBytes(ip,maxAcceptablePos,3) sp := pushStackItem(sp, value) ip := add(ip, 3) @@ -2025,7 +2126,7 @@ object "EVMInterpreter" { case 0x63 { // OP_PUSH4 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,4) + let value := readBytes(ip,maxAcceptablePos,4) sp := pushStackItem(sp, value) ip := add(ip, 4) @@ -2033,7 +2134,7 @@ object "EVMInterpreter" { case 0x64 { // OP_PUSH5 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,5) + let value := readBytes(ip,maxAcceptablePos,5) sp := pushStackItem(sp, value) ip := add(ip, 5) @@ -2041,7 +2142,7 @@ object "EVMInterpreter" { case 0x65 { // OP_PUSH6 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,6) + let value := readBytes(ip,maxAcceptablePos,6) sp := pushStackItem(sp, value) ip := add(ip, 6) @@ -2049,7 +2150,7 @@ object "EVMInterpreter" { case 0x66 { // OP_PUSH7 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,7) + let value := readBytes(ip,maxAcceptablePos,7) sp := pushStackItem(sp, value) ip := add(ip, 7) @@ -2057,7 +2158,7 @@ object "EVMInterpreter" { case 0x67 { // OP_PUSH8 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,8) + let value := readBytes(ip,maxAcceptablePos,8) sp := pushStackItem(sp, value) ip := add(ip, 8) @@ -2065,7 +2166,7 @@ object "EVMInterpreter" { case 0x68 { // OP_PUSH9 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,9) + let value := readBytes(ip,maxAcceptablePos,9) sp := pushStackItem(sp, value) ip := add(ip, 9) @@ -2073,7 +2174,7 @@ object "EVMInterpreter" { case 0x69 { // OP_PUSH10 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,10) + let value := readBytes(ip,maxAcceptablePos,10) sp := pushStackItem(sp, value) ip := add(ip, 10) @@ -2081,7 +2182,7 @@ object "EVMInterpreter" { case 0x6A { // OP_PUSH11 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,11) + let value := readBytes(ip,maxAcceptablePos,11) sp := pushStackItem(sp, value) ip := add(ip, 11) @@ -2089,7 +2190,7 @@ object "EVMInterpreter" { case 0x6B { // OP_PUSH12 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,12) + let value := readBytes(ip,maxAcceptablePos,12) sp := pushStackItem(sp, value) ip := add(ip, 12) @@ -2097,7 +2198,7 @@ object "EVMInterpreter" { case 0x6C { // OP_PUSH13 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,13) + let value := readBytes(ip,maxAcceptablePos,13) sp := pushStackItem(sp, value) ip := add(ip, 13) @@ -2105,7 +2206,7 @@ object "EVMInterpreter" { case 0x6D { // OP_PUSH14 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,14) + let value := readBytes(ip,maxAcceptablePos,14) sp := pushStackItem(sp, value) ip := add(ip, 14) @@ -2113,7 +2214,7 @@ object "EVMInterpreter" { case 0x6E { // OP_PUSH15 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,15) + let value := readBytes(ip,maxAcceptablePos,15) sp := pushStackItem(sp, value) ip := add(ip, 15) @@ -2121,7 +2222,7 @@ object "EVMInterpreter" { case 0x6F { // OP_PUSH16 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,16) + let value := readBytes(ip,maxAcceptablePos,16) sp := pushStackItem(sp, value) ip := add(ip, 16) @@ -2129,7 +2230,7 @@ object "EVMInterpreter" { case 0x70 { // OP_PUSH17 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,17) + let value := readBytes(ip,maxAcceptablePos,17) sp := pushStackItem(sp, value) ip := add(ip, 17) @@ -2137,7 +2238,7 @@ object "EVMInterpreter" { case 0x71 { // OP_PUSH18 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,18) + let value := readBytes(ip,maxAcceptablePos,18) sp := pushStackItem(sp, value) ip := add(ip, 18) @@ -2145,7 +2246,7 @@ object "EVMInterpreter" { case 0x72 { // OP_PUSH19 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,19) + let value := readBytes(ip,maxAcceptablePos,19) sp := pushStackItem(sp, value) ip := add(ip, 19) @@ -2153,7 +2254,7 @@ object "EVMInterpreter" { case 0x73 { // OP_PUSH20 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,20) + let value := readBytes(ip,maxAcceptablePos,20) sp := pushStackItem(sp, value) ip := add(ip, 20) @@ -2161,7 +2262,7 @@ object "EVMInterpreter" { case 0x74 { // OP_PUSH21 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,21) + let value := readBytes(ip,maxAcceptablePos,21) sp := pushStackItem(sp, value) ip := add(ip, 21) @@ -2169,7 +2270,7 @@ object "EVMInterpreter" { case 0x75 { // OP_PUSH22 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,22) + let value := readBytes(ip,maxAcceptablePos,22) sp := pushStackItem(sp, value) ip := add(ip, 22) @@ -2177,7 +2278,7 @@ object "EVMInterpreter" { case 0x76 { // OP_PUSH23 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,23) + let value := readBytes(ip,maxAcceptablePos,23) sp := pushStackItem(sp, value) ip := add(ip, 23) @@ -2185,7 +2286,7 @@ object "EVMInterpreter" { case 0x77 { // OP_PUSH24 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,24) + let value := readBytes(ip,maxAcceptablePos,24) sp := pushStackItem(sp, value) ip := add(ip, 24) @@ -2193,7 +2294,7 @@ object "EVMInterpreter" { case 0x78 { // OP_PUSH25 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,25) + let value := readBytes(ip,maxAcceptablePos,25) sp := pushStackItem(sp, value) ip := add(ip, 25) @@ -2201,7 +2302,7 @@ object "EVMInterpreter" { case 0x79 { // OP_PUSH26 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,26) + let value := readBytes(ip,maxAcceptablePos,26) sp := pushStackItem(sp, value) ip := add(ip, 26) @@ -2209,7 +2310,7 @@ object "EVMInterpreter" { case 0x7A { // OP_PUSH27 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,27) + let value := readBytes(ip,maxAcceptablePos,27) sp := pushStackItem(sp, value) ip := add(ip, 27) @@ -2217,7 +2318,7 @@ object "EVMInterpreter" { case 0x7B { // OP_PUSH28 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,28) + let value := readBytes(ip,maxAcceptablePos,28) sp := pushStackItem(sp, value) ip := add(ip, 28) @@ -2225,7 +2326,7 @@ object "EVMInterpreter" { case 0x7C { // OP_PUSH29 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,29) + let value := readBytes(ip,maxAcceptablePos,29) sp := pushStackItem(sp, value) ip := add(ip, 29) @@ -2233,7 +2334,7 @@ object "EVMInterpreter" { case 0x7D { // OP_PUSH30 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,30) + let value := readBytes(ip,maxAcceptablePos,30) sp := pushStackItem(sp, value) ip := add(ip, 30) @@ -2241,7 +2342,7 @@ object "EVMInterpreter" { case 0x7E { // OP_PUSH31 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,31) + let value := readBytes(ip,maxAcceptablePos,31) sp := pushStackItem(sp, value) ip := add(ip, 31) @@ -2249,7 +2350,7 @@ object "EVMInterpreter" { case 0x7F { // OP_PUSH32 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,32) + let value := readBytes(ip,maxAcceptablePos,32) sp := pushStackItem(sp, value) ip := add(ip, 32) @@ -2396,11 +2497,9 @@ object "EVMInterpreter" { revert(0, 0) } - let offset, size, topic1, topic2 + let offset, size offset, sp := popStackItem(sp) size, sp := popStackItem(sp) - topic1, sp := popStackItem(sp) - topic2, sp := popStackItem(sp) checkMemOverflow(add(add(offset, MEM_OFFSET_INNER()), size)) @@ -2409,7 +2508,12 @@ object "EVMInterpreter" { dynamicGas := add(dynamicGas, 750) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - log2(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2) + { + let topic1, topic2 + topic1, sp := popStackItem(sp) + topic2, sp := popStackItem(sp) + log2(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2) + } } case 0xA3 { // OP_LOG3 evmGasLeft := chargeGas(evmGasLeft, 375) @@ -2435,7 +2539,7 @@ object "EVMInterpreter" { topic2, sp := popStackItem(sp) topic3, sp := popStackItem(sp) log3(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2, topic3) - } + } } case 0xA4 { // OP_LOG4 evmGasLeft := chargeGas(evmGasLeft, 375) @@ -2462,50 +2566,10 @@ object "EVMInterpreter" { topic3, sp := popStackItem(sp) topic4, sp := popStackItem(sp) log4(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2, topic3, topic4) - } - + } } case 0xF0 { // OP_CREATE - evmGasLeft := chargeGas(evmGasLeft, 32000) - - if isStatic { - revert(0, 0) - } - - let value, offset, size - - value, sp := popStackItem(sp) - offset, sp := popStackItem(sp) - size, sp := popStackItem(sp) - - checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size))) - - if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { - revert(0, 0) - } - - if gt(value, balance(address())) { - revert(0, 0) - } - - // 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 addr := getNewAddress(address()) - - let result - result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) //code_deposit_cost missing - - switch result - case 0 { sp := pushStackItem(sp, 0) } - default { sp := pushStackItem(sp, addr) } + evmGasLeft, sp := performCreate(evmGasLeft, sp, isStatic) } case 0xF1 { // OP_CALL evmGasLeft := chargeGas(evmGasLeft, 100) @@ -2541,58 +2605,7 @@ object "EVMInterpreter" { evmGasLeft := chargeGas(evmGasLeft, gasUsed) } case 0xF5 { // OP_CREATE2 - evmGasLeft := chargeGas(evmGasLeft, 32000) - - if isStatic { - revert(0, 0) - } - - let value, offset, size, salt - - value, sp := popStackItem(sp) - offset, sp := popStackItem(sp) - size, sp := popStackItem(sp) - salt, sp := popStackItem(sp) - - checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size))) - - if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { - revert(0, 0) - } - - if gt(value, balance(address())) { - revert(0, 0) - } - - // 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)) - )) - - { - let hashedBytecode := keccak256(add(MEM_OFFSET_INNER(), offset), size) - mstore8(0, 0xFF) - mstore(0x01, shl(0x60, address())) - mstore(0x15, salt) - mstore(0x35, hashedBytecode) - } - - let addr := and( - keccak256(0, 0x55), - 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - ) - - let result - result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) //code_deposit_cost missing - - switch result - case 0 { sp := pushStackItem(sp, 0) } - default { sp := pushStackItem(sp, addr) } + evmGasLeft, sp := performCreate2(evmGasLeft, sp, isStatic) } case 0xFA { // OP_STATICCALL evmGasLeft := chargeGas(evmGasLeft, 100) @@ -2735,11 +2748,8 @@ object "EVMInterpreter" { } // It is the responsibility of the caller to ensure that ip >= BYTECODE_OFFSET + 32 - function readIP(ip) -> opcode { + function readIP(ip,maxAcceptablePos) -> opcode { // TODO: Why not do this at the beginning once instead of every time? - let bytecodeLen := mload(BYTECODE_OFFSET()) - - let maxAcceptablePos := add(add(BYTECODE_OFFSET(), bytecodeLen), 31) if gt(ip, maxAcceptablePos) { revert(0, 0) } @@ -2747,11 +2757,7 @@ object "EVMInterpreter" { opcode := and(mload(sub(ip, 31)), 0xff) } - function readBytes(start, length) -> value { - // TODO: Why not do this at the beginning once instead of every time? - let bytecodeLen := mload(BYTECODE_OFFSET()) - - let maxAcceptablePos := add(add(BYTECODE_OFFSET(), bytecodeLen), 31) + function readBytes(start, maxAcceptablePos,length) -> value { if gt(add(start,sub(length,1)), maxAcceptablePos) { revert(0, 0) } @@ -3132,10 +3138,10 @@ object "EVMInterpreter" { } // Note, that this function can overflow. It's up to the caller to ensure that it does not. - function memCost(memSizeWords) -> gasCost { + /*function memCost(memSizeWords) -> gasCost { // The first term of the sum is the quadratic cost, the second one the linear one. gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords)) - } + }*/ // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. @@ -3150,14 +3156,9 @@ object "EVMInterpreter" { let newSizeInWords := div(add(newSize, 31), 32) if gt(newSizeInWords, oldSizeInWords) { - // TODO: Check this, it feels like there might be a more optimized way - // of doing this cost calculation. - //let oldCost := memCost(oldSizeInWords) - //let newCost := memCost(newSizeInWords) let new_minus_old := sub(newSizeInWords, oldSizeInWords) gasCost := add(mul(3,new_minus_old), div(mul(new_minus_old,add(newSizeInWords,oldSizeInWords)),512)) - //gasCost := sub(newCost, oldCost) mstore(MEM_OFFSET(), newSizeInWords) } } @@ -3870,6 +3871,138 @@ object "EVMInterpreter" { mstore(sub(offset, 0x80), back) } + function performExtCodeCopy(evmGas,oldSp) -> evmGasLeft, sp { + evmGasLeft := chargeGas(evmGas, 100) + + let addr, dest, offset, len + addr, sp := popStackItem(oldSp) + dest, sp := popStackItem(sp) + offset, sp := popStackItem(sp) + len, sp := popStackItem(sp) + + // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost + // minimum_word_size = (size + 31) / 32 + + let dynamicGas := add( + mul(3, shr(5, add(len, 31))), + expandMemory(add(dest, len)) + ) + if iszero(warmAddress(addr)) { + dynamicGas := add(dynamicGas, 2500) + } + evmGasLeft := chargeGas(evmGasLeft, dynamicGas) + + let len_32 := shr(5, len) + for {let i := 0} lt(i, len_32) { i := add(i, 1) } { + mstore(shl(5,i),0) + } + let size_32 := shl(5,len_32) + let rest_32 := sub(len, size_32) + for {let i := 0} lt(i, rest_32) { i := add(i, 1) } { + mstore8(add(size_32,i),0) + } + // Gets the code from the addr + pop(_fetchDeployedCode(addr, add(offset, MEM_OFFSET_INNER()), len)) + } + + function performCreate(evmGas,oldSp,isStatic) -> evmGasLeft, sp { + evmGasLeft := chargeGas(evmGas, 32000) + + if isStatic { + revert(0, 0) + } + + let value, offset, size + + value, sp := popStackItem(oldSp) + offset, sp := popStackItem(sp) + size, sp := popStackItem(sp) + + checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size))) + + if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { + revert(0, 0) + } + + if gt(value, balance(address())) { + revert(0, 0) + } + + // 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 addr := getNewAddress(address()) + + let result + result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) + + switch result + case 0 { sp := pushStackItem(sp, 0) } + default { sp := pushStackItem(sp, addr) } + } + + function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp{ + evmGasLeft := chargeGas(evmGas, 32000) + + if isStatic { + revert(0, 0) + } + + let value, offset, size, salt + + value, sp := popStackItem(oldSp) + offset, sp := popStackItem(sp) + size, sp := popStackItem(sp) + salt, sp := popStackItem(sp) + + checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size))) + + if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { + revert(0, 0) + } + + if gt(value, balance(address())) { + revert(0, 0) + } + + // 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)) + )) + + { + let hashedBytecode := keccak256(add(MEM_OFFSET_INNER(), offset), size) + mstore8(0, 0xFF) + mstore(0x01, shl(0x60, address())) + mstore(0x15, salt) + mstore(0x35, hashedBytecode) + } + + let addr := and( + keccak256(0, 0x55), + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + ) + + let result + result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) + + switch result + case 0 { sp := pushStackItem(sp, 0) } + default { sp := pushStackItem(sp, addr) } + } + //////////////////////////////////////////////////////////////// // FALLBACK @@ -3898,8 +4031,10 @@ object "EVMInterpreter" { let ip := add(BYTECODE_OFFSET(), 32) let opcode + let maxAcceptablePos := add(add(BYTECODE_OFFSET(), mload(BYTECODE_OFFSET())), 31) + for { } true { } { - opcode := readIP(ip) + opcode := readIP(ip,maxAcceptablePos) ip := add(ip, 1) @@ -4163,6 +4298,7 @@ object "EVMInterpreter" { sp := pushStackItem(sp, sar(shift, value)) } case 0x20 { // OP_KECCAK256 + evmGasLeft := chargeGas(evmGasLeft, 30) let offset, size @@ -4176,11 +4312,11 @@ object "EVMInterpreter" { // an expansion, which costs gas. // dynamicGas = 6 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let minWordSize := shr(5, add(size, 31)) - let dynamicGas := add(mul(6, minWordSize), expandMemory(add(offset, size))) + let dynamicGas := add(mul(6, shr(5, add(size, 31))), expandMemory(add(offset, size))) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) sp := pushStackItem(sp, keccak) + } case 0x30 { // OP_ADDRESS evmGasLeft := chargeGas(evmGasLeft, 2) @@ -4230,6 +4366,7 @@ object "EVMInterpreter" { sp := pushStackItem(sp, calldatasize()) } case 0x37 { // OP_CALLDATACOPY + evmGasLeft := chargeGas(evmGasLeft, 3) let destOffset, offset, size @@ -4243,11 +4380,11 @@ object "EVMInterpreter" { // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let minWordSize := shr(5, add(size, 31)) - let dynamicGas := add(mul(3, minWordSize), expandMemory(add(destOffset, size))) + let dynamicGas := add(mul(3, shr(5, add(size, 31))), expandMemory(add(destOffset, size))) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) calldatacopy(add(destOffset, MEM_OFFSET_INNER()), offset, size) + } case 0x38 { // OP_CODESIZE evmGasLeft := chargeGas(evmGasLeft, 2) @@ -4256,6 +4393,7 @@ object "EVMInterpreter" { sp := pushStackItem(sp, bytecodeLen) } case 0x39 { // OP_CODECOPY + evmGasLeft := chargeGas(evmGasLeft, 3) let dst, offset, len @@ -4266,8 +4404,7 @@ object "EVMInterpreter" { // dynamicGas = 3 * minimum_word_size + memory_expansion_cost // minimum_word_size = (size + 31) / 32 - let minWordSize := shr(5, add(len, 31)) - let dynamicGas := add(mul(3, minWordSize), expandMemory(add(dst, len))) + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(add(dst, len))) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) dst := add(dst, MEM_OFFSET_INNER()) @@ -4285,6 +4422,7 @@ object "EVMInterpreter" { shr(248, mload(add(offset, i))) ) } + } case 0x3A { // OP_GASPRICE evmGasLeft := chargeGas(evmGasLeft, 2) @@ -4309,36 +4447,7 @@ object "EVMInterpreter" { default { sp := pushStackItem(sp, _fetchDeployedCodeLen(addr)) } } case 0x3C { // OP_EXTCODECOPY - evmGasLeft := chargeGas(evmGasLeft, 100) - - let addr, dest, offset, len - addr, sp := popStackItem(sp) - dest, sp := popStackItem(sp) - offset, sp := popStackItem(sp) - len, sp := popStackItem(sp) - - // dynamicGas = 3 * minimum_word_size + memory_expansion_cost + address_access_cost - // minimum_word_size = (size + 31) / 32 - let dynamicGas := add( - mul(3, shr(5, add(len, 31))), - expandMemory(add(dest, len)) - ) - if iszero(warmAddress(addr)) { - dynamicGas := add(dynamicGas, 2500) - } - evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - - let len_32 := shr(5, len) - for {let i := 0} lt(i, len_32) { i := add(i, 1) } { - mstore(shl(5,i),0) - } - let size_32 := shl(5,len_32) - let rest_32 := sub(len, size_32) - for {let i := 0} lt(i, rest_32) { i := add(i, 1) } { - mstore8(add(size_32,i),0) - } - // Gets the code from the addr - pop(_fetchDeployedCode(addr, add(offset, MEM_OFFSET_INNER()), len)) + evmGasLeft, sp := performExtCodeCopy(evmGasLeft, sp) } case 0x3D { // OP_RETURNDATASIZE evmGasLeft := chargeGas(evmGasLeft, 2) @@ -4347,6 +4456,7 @@ object "EVMInterpreter" { sp := pushStackItem(sp, rdz) } case 0x3E { // OP_RETURNDATACOPY + evmGasLeft := chargeGas(evmGasLeft, 3) let dest, offset, len @@ -4364,11 +4474,11 @@ object "EVMInterpreter" { // minimum_word_size = (size + 31) / 32 // dynamicGas = 3 * minimum_word_size + memory_expansion_cost - let minWordSize := shr(5, add(len, 31)) - let dynamicGas := add(mul(3, minWordSize), expandMemory(add(dest, len))) + let dynamicGas := add(mul(3, shr(5, add(len, 31))), expandMemory(add(dest, len))) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) copyActivePtrData(add(MEM_OFFSET_INNER(), dest), offset, len) + } case 0x3F { // OP_EXTCODEHASH evmGasLeft := chargeGas(evmGasLeft, 100) @@ -4469,6 +4579,7 @@ object "EVMInterpreter" { mstore8(add(MEM_OFFSET_INNER(), offset), value) } case 0x54 { // OP_SLOAD + evmGasLeft := chargeGas(evmGasLeft, 100) let key, value, isWarm @@ -4488,8 +4599,10 @@ object "EVMInterpreter" { } sp := pushStackItem(sp,value) + } case 0x55 { // OP_SSTORE + evmGasLeft := chargeGas(evmGasLeft, 100) if isStatic { @@ -4526,6 +4639,7 @@ object "EVMInterpreter" { evmGasLeft := chargeGas(evmGasLeft, gasSpent) sstore(key, value) + } // NOTE: We don't currently do full jumpdest validation // (i.e. validating a jumpdest isn't in PUSH data) @@ -4539,7 +4653,7 @@ object "EVMInterpreter" { ip := add(add(BYTECODE_OFFSET(), 32), counter) // Check next opcode is JUMPDEST - let nextOpcode := readIP(ip) + let nextOpcode := readIP(ip,maxAcceptablePos) if iszero(eq(nextOpcode, 0x5B)) { revert(0, 0) } @@ -4559,7 +4673,7 @@ object "EVMInterpreter" { ip := add(add(BYTECODE_OFFSET(), 32), counter) // Check next opcode is JUMPDEST - let nextOpcode := readIP(ip) + let nextOpcode := readIP(ip,maxAcceptablePos) if iszero(eq(nextOpcode, 0x5B)) { revert(0, 0) } @@ -4598,7 +4712,7 @@ object "EVMInterpreter" { case 0x60 { // OP_PUSH1 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,1) + let value := readBytes(ip,maxAcceptablePos,1) sp := pushStackItem(sp, value) ip := add(ip, 1) @@ -4606,7 +4720,7 @@ object "EVMInterpreter" { case 0x61 { // OP_PUSH2 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,2) + let value := readBytes(ip,maxAcceptablePos,2) sp := pushStackItem(sp, value) ip := add(ip, 2) @@ -4614,7 +4728,7 @@ object "EVMInterpreter" { case 0x62 { // OP_PUSH3 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,3) + let value := readBytes(ip,maxAcceptablePos,3) sp := pushStackItem(sp, value) ip := add(ip, 3) @@ -4622,7 +4736,7 @@ object "EVMInterpreter" { case 0x63 { // OP_PUSH4 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,4) + let value := readBytes(ip,maxAcceptablePos,4) sp := pushStackItem(sp, value) ip := add(ip, 4) @@ -4630,7 +4744,7 @@ object "EVMInterpreter" { case 0x64 { // OP_PUSH5 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,5) + let value := readBytes(ip,maxAcceptablePos,5) sp := pushStackItem(sp, value) ip := add(ip, 5) @@ -4638,7 +4752,7 @@ object "EVMInterpreter" { case 0x65 { // OP_PUSH6 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,6) + let value := readBytes(ip,maxAcceptablePos,6) sp := pushStackItem(sp, value) ip := add(ip, 6) @@ -4646,7 +4760,7 @@ object "EVMInterpreter" { case 0x66 { // OP_PUSH7 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,7) + let value := readBytes(ip,maxAcceptablePos,7) sp := pushStackItem(sp, value) ip := add(ip, 7) @@ -4654,7 +4768,7 @@ object "EVMInterpreter" { case 0x67 { // OP_PUSH8 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,8) + let value := readBytes(ip,maxAcceptablePos,8) sp := pushStackItem(sp, value) ip := add(ip, 8) @@ -4662,7 +4776,7 @@ object "EVMInterpreter" { case 0x68 { // OP_PUSH9 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,9) + let value := readBytes(ip,maxAcceptablePos,9) sp := pushStackItem(sp, value) ip := add(ip, 9) @@ -4670,7 +4784,7 @@ object "EVMInterpreter" { case 0x69 { // OP_PUSH10 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,10) + let value := readBytes(ip,maxAcceptablePos,10) sp := pushStackItem(sp, value) ip := add(ip, 10) @@ -4678,7 +4792,7 @@ object "EVMInterpreter" { case 0x6A { // OP_PUSH11 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,11) + let value := readBytes(ip,maxAcceptablePos,11) sp := pushStackItem(sp, value) ip := add(ip, 11) @@ -4686,7 +4800,7 @@ object "EVMInterpreter" { case 0x6B { // OP_PUSH12 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,12) + let value := readBytes(ip,maxAcceptablePos,12) sp := pushStackItem(sp, value) ip := add(ip, 12) @@ -4694,7 +4808,7 @@ object "EVMInterpreter" { case 0x6C { // OP_PUSH13 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,13) + let value := readBytes(ip,maxAcceptablePos,13) sp := pushStackItem(sp, value) ip := add(ip, 13) @@ -4702,7 +4816,7 @@ object "EVMInterpreter" { case 0x6D { // OP_PUSH14 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,14) + let value := readBytes(ip,maxAcceptablePos,14) sp := pushStackItem(sp, value) ip := add(ip, 14) @@ -4710,7 +4824,7 @@ object "EVMInterpreter" { case 0x6E { // OP_PUSH15 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,15) + let value := readBytes(ip,maxAcceptablePos,15) sp := pushStackItem(sp, value) ip := add(ip, 15) @@ -4718,7 +4832,7 @@ object "EVMInterpreter" { case 0x6F { // OP_PUSH16 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,16) + let value := readBytes(ip,maxAcceptablePos,16) sp := pushStackItem(sp, value) ip := add(ip, 16) @@ -4726,7 +4840,7 @@ object "EVMInterpreter" { case 0x70 { // OP_PUSH17 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,17) + let value := readBytes(ip,maxAcceptablePos,17) sp := pushStackItem(sp, value) ip := add(ip, 17) @@ -4734,7 +4848,7 @@ object "EVMInterpreter" { case 0x71 { // OP_PUSH18 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,18) + let value := readBytes(ip,maxAcceptablePos,18) sp := pushStackItem(sp, value) ip := add(ip, 18) @@ -4742,7 +4856,7 @@ object "EVMInterpreter" { case 0x72 { // OP_PUSH19 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,19) + let value := readBytes(ip,maxAcceptablePos,19) sp := pushStackItem(sp, value) ip := add(ip, 19) @@ -4750,7 +4864,7 @@ object "EVMInterpreter" { case 0x73 { // OP_PUSH20 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,20) + let value := readBytes(ip,maxAcceptablePos,20) sp := pushStackItem(sp, value) ip := add(ip, 20) @@ -4758,7 +4872,7 @@ object "EVMInterpreter" { case 0x74 { // OP_PUSH21 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,21) + let value := readBytes(ip,maxAcceptablePos,21) sp := pushStackItem(sp, value) ip := add(ip, 21) @@ -4766,7 +4880,7 @@ object "EVMInterpreter" { case 0x75 { // OP_PUSH22 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,22) + let value := readBytes(ip,maxAcceptablePos,22) sp := pushStackItem(sp, value) ip := add(ip, 22) @@ -4774,7 +4888,7 @@ object "EVMInterpreter" { case 0x76 { // OP_PUSH23 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,23) + let value := readBytes(ip,maxAcceptablePos,23) sp := pushStackItem(sp, value) ip := add(ip, 23) @@ -4782,7 +4896,7 @@ object "EVMInterpreter" { case 0x77 { // OP_PUSH24 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,24) + let value := readBytes(ip,maxAcceptablePos,24) sp := pushStackItem(sp, value) ip := add(ip, 24) @@ -4790,7 +4904,7 @@ object "EVMInterpreter" { case 0x78 { // OP_PUSH25 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,25) + let value := readBytes(ip,maxAcceptablePos,25) sp := pushStackItem(sp, value) ip := add(ip, 25) @@ -4798,7 +4912,7 @@ object "EVMInterpreter" { case 0x79 { // OP_PUSH26 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,26) + let value := readBytes(ip,maxAcceptablePos,26) sp := pushStackItem(sp, value) ip := add(ip, 26) @@ -4806,7 +4920,7 @@ object "EVMInterpreter" { case 0x7A { // OP_PUSH27 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,27) + let value := readBytes(ip,maxAcceptablePos,27) sp := pushStackItem(sp, value) ip := add(ip, 27) @@ -4814,7 +4928,7 @@ object "EVMInterpreter" { case 0x7B { // OP_PUSH28 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,28) + let value := readBytes(ip,maxAcceptablePos,28) sp := pushStackItem(sp, value) ip := add(ip, 28) @@ -4822,7 +4936,7 @@ object "EVMInterpreter" { case 0x7C { // OP_PUSH29 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,29) + let value := readBytes(ip,maxAcceptablePos,29) sp := pushStackItem(sp, value) ip := add(ip, 29) @@ -4830,7 +4944,7 @@ object "EVMInterpreter" { case 0x7D { // OP_PUSH30 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,30) + let value := readBytes(ip,maxAcceptablePos,30) sp := pushStackItem(sp, value) ip := add(ip, 30) @@ -4838,7 +4952,7 @@ object "EVMInterpreter" { case 0x7E { // OP_PUSH31 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,31) + let value := readBytes(ip,maxAcceptablePos,31) sp := pushStackItem(sp, value) ip := add(ip, 31) @@ -4846,7 +4960,7 @@ object "EVMInterpreter" { case 0x7F { // OP_PUSH32 evmGasLeft := chargeGas(evmGasLeft, 3) - let value := readBytes(ip,32) + let value := readBytes(ip,maxAcceptablePos,32) sp := pushStackItem(sp, value) ip := add(ip, 32) @@ -4993,11 +5107,9 @@ object "EVMInterpreter" { revert(0, 0) } - let offset, size, topic1, topic2 + let offset, size offset, sp := popStackItem(sp) size, sp := popStackItem(sp) - topic1, sp := popStackItem(sp) - topic2, sp := popStackItem(sp) checkMemOverflow(add(add(offset, MEM_OFFSET_INNER()), size)) @@ -5006,7 +5118,12 @@ object "EVMInterpreter" { dynamicGas := add(dynamicGas, 750) evmGasLeft := chargeGas(evmGasLeft, dynamicGas) - log2(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2) + { + let topic1, topic2 + topic1, sp := popStackItem(sp) + topic2, sp := popStackItem(sp) + log2(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2) + } } case 0xA3 { // OP_LOG3 evmGasLeft := chargeGas(evmGasLeft, 375) @@ -5032,7 +5149,7 @@ object "EVMInterpreter" { topic2, sp := popStackItem(sp) topic3, sp := popStackItem(sp) log3(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2, topic3) - } + } } case 0xA4 { // OP_LOG4 evmGasLeft := chargeGas(evmGasLeft, 375) @@ -5059,50 +5176,10 @@ object "EVMInterpreter" { topic3, sp := popStackItem(sp) topic4, sp := popStackItem(sp) log4(add(offset, MEM_OFFSET_INNER()), size, topic1, topic2, topic3, topic4) - } - + } } case 0xF0 { // OP_CREATE - evmGasLeft := chargeGas(evmGasLeft, 32000) - - if isStatic { - revert(0, 0) - } - - let value, offset, size - - value, sp := popStackItem(sp) - offset, sp := popStackItem(sp) - size, sp := popStackItem(sp) - - checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size))) - - if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { - revert(0, 0) - } - - if gt(value, balance(address())) { - revert(0, 0) - } - - // 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 addr := getNewAddress(address()) - - let result - result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) //code_deposit_cost missing - - switch result - case 0 { sp := pushStackItem(sp, 0) } - default { sp := pushStackItem(sp, addr) } + evmGasLeft, sp := performCreate(evmGasLeft, sp, isStatic) } case 0xF1 { // OP_CALL evmGasLeft := chargeGas(evmGasLeft, 100) @@ -5138,58 +5215,7 @@ object "EVMInterpreter" { evmGasLeft := chargeGas(evmGasLeft, gasUsed) } case 0xF5 { // OP_CREATE2 - evmGasLeft := chargeGas(evmGasLeft, 32000) - - if isStatic { - revert(0, 0) - } - - let value, offset, size, salt - - value, sp := popStackItem(sp) - offset, sp := popStackItem(sp) - size, sp := popStackItem(sp) - salt, sp := popStackItem(sp) - - checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size))) - - if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) { - revert(0, 0) - } - - if gt(value, balance(address())) { - revert(0, 0) - } - - // 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)) - )) - - { - let hashedBytecode := keccak256(add(MEM_OFFSET_INNER(), offset), size) - mstore8(0, 0xFF) - mstore(0x01, shl(0x60, address())) - mstore(0x15, salt) - mstore(0x35, hashedBytecode) - } - - let addr := and( - keccak256(0, 0x55), - 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - ) - - let result - result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) //code_deposit_cost missing - - switch result - case 0 { sp := pushStackItem(sp, 0) } - default { sp := pushStackItem(sp, addr) } + evmGasLeft, sp := performCreate2(evmGasLeft, sp, isStatic) } case 0xFA { // OP_STATICCALL evmGasLeft := chargeGas(evmGasLeft, 100) From f592284ee4018440252df74577c3842f6fcbf598 Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Tue, 21 May 2024 14:53:15 -0300 Subject: [PATCH 06/15] Add call optimizations --- .../EvmInterpreterFunctions.template.yul | 117 +++++++++++------- 1 file changed, 73 insertions(+), 44 deletions(-) diff --git a/system-contracts/contracts/EvmInterpreterFunctions.template.yul b/system-contracts/contracts/EvmInterpreterFunctions.template.yul index e89a1678f..dcac39b80 100644 --- a/system-contracts/contracts/EvmInterpreterFunctions.template.yul +++ b/system-contracts/contracts/EvmInterpreterFunctions.template.yul @@ -680,6 +680,22 @@ function getNonce(addr) -> nonce { nonce := mload(0) } +function getRawNonce(addr) -> nonce { + mstore8(0, 0x5a) + mstore8(1, 0xa9) + mstore8(2, 0xb6) + mstore8(3, 0xb5) + mstore(4, addr) + + let result := staticcall(gas(), NONCE_HOLDER_SYSTEM_CONTRACT(), 0, 36, 0, 32) + + if iszero(result) { + revert(0, 0) + } + + nonce := mload(0) +} + function _isEVM(_addr) -> isEVM { // bytes4 selector = ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.isAccountEVM.selector; (0x8c040477) // function isAccountEVM(address _addr) external view returns (bool); @@ -889,6 +905,46 @@ function getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) -> maxExpa } } +function _performCall(addr,gasToPass,value,argsOffset,argsSize,retOffset,retSize,isStatic) -> success, frameGasLeft, gasToPassNew{ + gasToPassNew := gasToPass + let is_evm := _isEVM(addr) + if isStatic { + if value { + revert(0, 0) + } + success, frameGasLeft:= _performStaticCall( + is_evm, + gasToPassNew, + addr, + argsOffset, + argsSize, + retOffset, + retSize + ) + } + + if and(is_evm, iszero(isStatic)) { + _pushEVMFrame(gasToPassNew, isStatic) + success := call(gasToPassNew, addr, value, argsOffset, argsSize, 0, 0) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + _popEVMFrame() + } + + // zkEVM native + if and(iszero(is_evm), iszero(isStatic)) { + gasToPassNew := _getZkEVMGas(gasToPassNew) + let zkevmGasBefore := gas() + success := call(gasToPassNew, addr, value, argsOffset, argsSize, retOffset, retSize) + _saveReturndataAfterZkEVMCall() + let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) + + frameGasLeft := 0 + if gt(gasToPassNew, gasUsed) { + frameGasLeft := sub(gasToPassNew, gasUsed) + } + } +} + function performCall(oldSp, evmGasLeft, isStatic) -> extraCost, sp { let gasToPass,addr,value,argsOffset,argsSize,retOffset,retSize @@ -932,44 +988,17 @@ function performCall(oldSp, evmGasLeft, isStatic) -> extraCost, sp { checkMemOverflow(add(argsOffset, argsSize)) checkMemOverflow(add(retOffset, retSize)) - let frameGasLeft - let success - - if isStatic { - if value { - revert(0, 0) - } - success, frameGasLeft:= _performStaticCall( - _isEVM(addr), - gasToPass, - addr, - argsOffset, - argsSize, - retOffset, - retSize - ) - } - - if and(_isEVM(addr), iszero(isStatic)) { - _pushEVMFrame(gasToPass, isStatic) - success := call(gasToPass, addr, value, argsOffset, argsSize, 0, 0) - frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) - _popEVMFrame() - } - - // zkEVM native - if and(iszero(_isEVM(addr)), iszero(isStatic)) { - gasToPass := _getZkEVMGas(gasToPass) - let zkevmGasBefore := gas() - success := call(gasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) - _saveReturndataAfterZkEVMCall() - let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) - - frameGasLeft := 0 - if gt(gasToPass, gasUsed) { - frameGasLeft := sub(gasToPass, gasUsed) - } - } + let success, frameGasLeft + success, frameGasLeft, gasToPass:= _performCall( + addr, + gasToPass, + value, + argsOffset, + argsSize, + retOffset, + retSize, + isStatic + ) extraCost := add(extraCost,sub(gasToPass,frameGasLeft)) extraCost := add(extraCost, getGasForPrecompiles(addr, argsOffset, argsSize)) @@ -1104,12 +1133,12 @@ function _performStaticCall( function isAddrEmpty(addr) -> isEmpty { isEmpty := 0 - if and( and( - iszero(balance(addr)), - iszero(extcodesize(addr)) ), - iszero(getNonce(addr)) - ) { - isEmpty := 1 + if iszero(extcodesize(addr)) { // YUL doesn't have short-circuit evaluation + if iszero(balance(addr)) { + if iszero(getRawNonce(addr)) { + isEmpty := 1 + } + } } } From a306e7773982ab3e84d2853fb7efbe819277aaa4 Mon Sep 17 00:00:00 2001 From: Manuel Bilbao Date: Thu, 2 May 2024 14:56:23 -0300 Subject: [PATCH 07/15] Return keccak hash on evm contracts extcodehash --- system-contracts/contracts/AccountCodeStorage.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/system-contracts/contracts/AccountCodeStorage.sol b/system-contracts/contracts/AccountCodeStorage.sol index 5e6cd9271..605f4d8cb 100644 --- a/system-contracts/contracts/AccountCodeStorage.sol +++ b/system-contracts/contracts/AccountCodeStorage.sol @@ -108,6 +108,10 @@ contract AccountCodeStorage is IAccountCodeStorage { codeHash = EMPTY_STRING_KECCAK; } + if (Utils.isCodeHashEVM(codeHash)) { + codeHash = DEPLOYER_SYSTEM_CONTRACT.evmCodeHash(account); + } + return codeHash; } From e5d9274725ce844bf5282040e04086817620e584 Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Mon, 6 May 2024 18:34:43 -0300 Subject: [PATCH 08/15] Change max evm bytecode length --- system-contracts/contracts/libraries/Utils.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-contracts/contracts/libraries/Utils.sol b/system-contracts/contracts/libraries/Utils.sol index 5f13b2256..c1747a53c 100644 --- a/system-contracts/contracts/libraries/Utils.sol +++ b/system-contracts/contracts/libraries/Utils.sol @@ -111,7 +111,7 @@ library Utils { } // the real max supported number is 2^16, but we'll stick to evm convention - uint256 constant MAX_EVM_BYTECODE_LENGTH = 65000; + uint256 constant MAX_EVM_BYTECODE_LENGTH = (2**16) - 1; function hashEVMBytecode(bytes memory _bytecode) internal view returns (bytes32 hashedEVMBytecode) { require(_bytecode.length <= MAX_EVM_BYTECODE_LENGTH, "po"); From 9124b422472a6749239d2fd2d2e5d20dbad3a9ff Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Tue, 7 May 2024 14:18:44 -0300 Subject: [PATCH 09/15] Change max evm bytecode length --- system-contracts/contracts/libraries/Utils.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-contracts/contracts/libraries/Utils.sol b/system-contracts/contracts/libraries/Utils.sol index c1747a53c..c6967ff06 100644 --- a/system-contracts/contracts/libraries/Utils.sol +++ b/system-contracts/contracts/libraries/Utils.sol @@ -111,7 +111,7 @@ library Utils { } // the real max supported number is 2^16, but we'll stick to evm convention - uint256 constant MAX_EVM_BYTECODE_LENGTH = (2**16) - 1; + uint256 constant MAX_EVM_BYTECODE_LENGTH = (2 ** 16) - 1; function hashEVMBytecode(bytes memory _bytecode) internal view returns (bytes32 hashedEVMBytecode) { require(_bytecode.length <= MAX_EVM_BYTECODE_LENGTH, "po"); From 7047533b33af749d45f00fdc9a1f16bde6e811ae Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Tue, 7 May 2024 16:24:10 -0300 Subject: [PATCH 10/15] Fix exp gas --- .../contracts/EvmInterpreterLoop.template.yul | 10 ++++--- .../contracts/EvmInterpreterPreprocessed.yul | 28 ++++++++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/system-contracts/contracts/EvmInterpreterLoop.template.yul b/system-contracts/contracts/EvmInterpreterLoop.template.yul index 2bd8d7b7a..9ee0b6207 100644 --- a/system-contracts/contracts/EvmInterpreterLoop.template.yul +++ b/system-contracts/contracts/EvmInterpreterLoop.template.yul @@ -116,10 +116,12 @@ for { } true { } { sp := pushStackItem(sp, exp(a, exponent)) - if exponent { - let expSizeByte := div(add(exponent, 256), 256) // TODO: Replace with shr(8, add(exponent, 256)) - evmGasLeft := chargeGas(evmGasLeft, mul(50, expSizeByte)) - } + let to_charge := 0 + for {} gt(exponent,0) {} { // while exponent > 0 + to_charge := add(to_charge, 50) + exponent := shr(8, exponent) + } + evmGasLeft := chargeGas(evmGasLeft, to_charge) } case 0x0B { // OP_SIGNEXTEND evmGasLeft := chargeGas(evmGasLeft, 5) diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index e037e09ac..3b3a9e08b 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -1406,12 +1406,16 @@ object "EVMInterpreter" { a, sp := popStackItem(sp) exponent, sp := popStackItem(sp) - sp := pushStackItem(sp, exp(a, exponent)) + let result := exp(a, exponent) - if exponent { - let expSizeByte := div(add(exponent, 256), 256) // TODO: Replace with shr(8, add(exponent, 256)) - evmGasLeft := chargeGas(evmGasLeft, mul(50, expSizeByte)) - } + sp := pushStackItem(sp, result) + + let to_charge := 0 + for {} gt(exponent,0) {} { // while exponent > 0 + to_charge := add(to_charge, 50) + exponent := shr(8, exponent) + } + evmGasLeft := chargeGas(evmGasLeft, to_charge) } case 0x0B { // OP_SIGNEXTEND evmGasLeft := chargeGas(evmGasLeft, 5) @@ -3993,12 +3997,16 @@ object "EVMInterpreter" { a, sp := popStackItem(sp) exponent, sp := popStackItem(sp) - sp := pushStackItem(sp, exp(a, exponent)) + let result := exp(a, exponent) - if exponent { - let expSizeByte := div(add(exponent, 256), 256) // TODO: Replace with shr(8, add(exponent, 256)) - evmGasLeft := chargeGas(evmGasLeft, mul(50, expSizeByte)) - } + sp := pushStackItem(sp, result) + + let to_charge := 0 + for {} gt(exponent,0) {} { // while exponent > 0 + to_charge := add(to_charge, 50) + exponent := shr(8, exponent) + } + evmGasLeft := chargeGas(evmGasLeft, to_charge) } case 0x0B { // OP_SIGNEXTEND evmGasLeft := chargeGas(evmGasLeft, 5) From 83f8847f2ee23842073050dd1dbe13de2041d19b Mon Sep 17 00:00:00 2001 From: Javier Chatruc Date: Wed, 22 May 2024 11:06:54 -0300 Subject: [PATCH 11/15] Run preprocessor --- system-contracts/contracts/EvmInterpreterPreprocessed.yul | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index 3b3a9e08b..7f22f1eef 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -1406,9 +1406,7 @@ object "EVMInterpreter" { a, sp := popStackItem(sp) exponent, sp := popStackItem(sp) - let result := exp(a, exponent) - - sp := pushStackItem(sp, result) + sp := pushStackItem(sp, exp(a, exponent)) let to_charge := 0 for {} gt(exponent,0) {} { // while exponent > 0 @@ -3997,9 +3995,7 @@ object "EVMInterpreter" { a, sp := popStackItem(sp) exponent, sp := popStackItem(sp) - let result := exp(a, exponent) - - sp := pushStackItem(sp, result) + sp := pushStackItem(sp, exp(a, exponent)) let to_charge := 0 for {} gt(exponent,0) {} { // while exponent > 0 From 6079e4fb55fab7d73a9a202177052ab81767083a Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Wed, 22 May 2024 11:12:10 -0300 Subject: [PATCH 12/15] Fix integration tests error --- .../EvmInterpreterFunctions.template.yul | 9 +- .../contracts/EvmInterpreterLoop.template.yul | 6 +- .../contracts/EvmInterpreterPreprocessed.yul | 264 +++++++++++------- 3 files changed, 167 insertions(+), 112 deletions(-) diff --git a/system-contracts/contracts/EvmInterpreterFunctions.template.yul b/system-contracts/contracts/EvmInterpreterFunctions.template.yul index dcac39b80..8bacdfee8 100644 --- a/system-contracts/contracts/EvmInterpreterFunctions.template.yul +++ b/system-contracts/contracts/EvmInterpreterFunctions.template.yul @@ -1304,7 +1304,7 @@ function performCreate(evmGas,oldSp,isStatic) -> evmGasLeft, sp { default { sp := pushStackItem(sp, addr) } } -function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp{ +function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp, result, addr{ evmGasLeft := chargeGas(evmGas, 32000) if isStatic { @@ -1346,15 +1346,10 @@ function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp{ mstore(0x35, hashedBytecode) } - let addr := and( + addr := and( keccak256(0, 0x55), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ) - let result result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) - - switch result - case 0 { sp := pushStackItem(sp, 0) } - default { sp := pushStackItem(sp, addr) } } diff --git a/system-contracts/contracts/EvmInterpreterLoop.template.yul b/system-contracts/contracts/EvmInterpreterLoop.template.yul index 0d3b2e0a9..649235d1d 100644 --- a/system-contracts/contracts/EvmInterpreterLoop.template.yul +++ b/system-contracts/contracts/EvmInterpreterLoop.template.yul @@ -1189,7 +1189,11 @@ for { } true { } { evmGasLeft := chargeGas(evmGasLeft, gasUsed) } case 0xF5 { // OP_CREATE2 - evmGasLeft, sp := performCreate2(evmGasLeft, sp, isStatic) + let result, addr + evmGasLeft, sp, result, addr := performCreate2(evmGasLeft, sp, isStatic) + switch result + case 0 { sp := pushStackItem(sp, 0) } + default { sp := pushStackItem(sp, addr) } } case 0xFA { // OP_STATICCALL evmGasLeft := chargeGas(evmGasLeft, 100) diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index 3ae61e83a..047f5437e 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -754,6 +754,22 @@ object "EVMInterpreter" { nonce := mload(0) } + function getRawNonce(addr) -> nonce { + mstore8(0, 0x5a) + mstore8(1, 0xa9) + mstore8(2, 0xb6) + mstore8(3, 0xb5) + mstore(4, addr) + + let result := staticcall(gas(), NONCE_HOLDER_SYSTEM_CONTRACT(), 0, 36, 0, 32) + + if iszero(result) { + revert(0, 0) + } + + nonce := mload(0) + } + function _isEVM(_addr) -> isEVM { // bytes4 selector = ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.isAccountEVM.selector; (0x8c040477) // function isAccountEVM(address _addr) external view returns (bool); @@ -963,6 +979,46 @@ object "EVMInterpreter" { } } + function _performCall(addr,gasToPass,value,argsOffset,argsSize,retOffset,retSize,isStatic) -> success, frameGasLeft, gasToPassNew{ + gasToPassNew := gasToPass + let is_evm := _isEVM(addr) + if isStatic { + if value { + revert(0, 0) + } + success, frameGasLeft:= _performStaticCall( + is_evm, + gasToPassNew, + addr, + argsOffset, + argsSize, + retOffset, + retSize + ) + } + + if and(is_evm, iszero(isStatic)) { + _pushEVMFrame(gasToPassNew, isStatic) + success := call(gasToPassNew, addr, value, argsOffset, argsSize, 0, 0) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + _popEVMFrame() + } + + // zkEVM native + if and(iszero(is_evm), iszero(isStatic)) { + gasToPassNew := _getZkEVMGas(gasToPassNew) + let zkevmGasBefore := gas() + success := call(gasToPassNew, addr, value, argsOffset, argsSize, retOffset, retSize) + _saveReturndataAfterZkEVMCall() + let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) + + frameGasLeft := 0 + if gt(gasToPassNew, gasUsed) { + frameGasLeft := sub(gasToPassNew, gasUsed) + } + } + } + function performCall(oldSp, evmGasLeft, isStatic) -> extraCost, sp { let gasToPass,addr,value,argsOffset,argsSize,retOffset,retSize @@ -1006,44 +1062,17 @@ object "EVMInterpreter" { checkMemOverflow(add(argsOffset, argsSize)) checkMemOverflow(add(retOffset, retSize)) - let frameGasLeft - let success - - if isStatic { - if value { - revert(0, 0) - } - success, frameGasLeft:= _performStaticCall( - _isEVM(addr), - gasToPass, - addr, - argsOffset, - argsSize, - retOffset, - retSize - ) - } - - if and(_isEVM(addr), iszero(isStatic)) { - _pushEVMFrame(gasToPass, isStatic) - success := call(gasToPass, addr, value, argsOffset, argsSize, 0, 0) - frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) - _popEVMFrame() - } - - // zkEVM native - if and(iszero(_isEVM(addr)), iszero(isStatic)) { - gasToPass := _getZkEVMGas(gasToPass) - let zkevmGasBefore := gas() - success := call(gasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) - _saveReturndataAfterZkEVMCall() - let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) - - frameGasLeft := 0 - if gt(gasToPass, gasUsed) { - frameGasLeft := sub(gasToPass, gasUsed) - } - } + let success, frameGasLeft + success, frameGasLeft, gasToPass:= _performCall( + addr, + gasToPass, + value, + argsOffset, + argsSize, + retOffset, + retSize, + isStatic + ) extraCost := add(extraCost,sub(gasToPass,frameGasLeft)) extraCost := add(extraCost, getGasForPrecompiles(addr, argsOffset, argsSize)) @@ -1178,12 +1207,12 @@ object "EVMInterpreter" { function isAddrEmpty(addr) -> isEmpty { isEmpty := 0 - if and( and( - iszero(balance(addr)), - iszero(extcodesize(addr)) ), - iszero(getNonce(addr)) - ) { - isEmpty := 1 + if iszero(extcodesize(addr)) { // YUL doesn't have short-circuit evaluation + if iszero(balance(addr)) { + if iszero(getRawNonce(addr)) { + isEmpty := 1 + } + } } } @@ -1349,7 +1378,7 @@ object "EVMInterpreter" { default { sp := pushStackItem(sp, addr) } } - function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp{ + function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp, result, addr{ evmGasLeft := chargeGas(evmGas, 32000) if isStatic { @@ -1391,17 +1420,12 @@ object "EVMInterpreter" { mstore(0x35, hashedBytecode) } - let addr := and( + addr := and( keccak256(0, 0x55), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ) - let result result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) - - switch result - case 0 { sp := pushStackItem(sp, 0) } - default { sp := pushStackItem(sp, addr) } } @@ -2605,7 +2629,11 @@ object "EVMInterpreter" { evmGasLeft := chargeGas(evmGasLeft, gasUsed) } case 0xF5 { // OP_CREATE2 - evmGasLeft, sp := performCreate2(evmGasLeft, sp, isStatic) + let result, addr + evmGasLeft, sp, result, addr := performCreate2(evmGasLeft, sp, isStatic) + switch result + case 0 { sp := pushStackItem(sp, 0) } + default { sp := pushStackItem(sp, addr) } } case 0xFA { // OP_STATICCALL evmGasLeft := chargeGas(evmGasLeft, 100) @@ -3353,6 +3381,22 @@ object "EVMInterpreter" { nonce := mload(0) } + function getRawNonce(addr) -> nonce { + mstore8(0, 0x5a) + mstore8(1, 0xa9) + mstore8(2, 0xb6) + mstore8(3, 0xb5) + mstore(4, addr) + + let result := staticcall(gas(), NONCE_HOLDER_SYSTEM_CONTRACT(), 0, 36, 0, 32) + + if iszero(result) { + revert(0, 0) + } + + nonce := mload(0) + } + function _isEVM(_addr) -> isEVM { // bytes4 selector = ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT.isAccountEVM.selector; (0x8c040477) // function isAccountEVM(address _addr) external view returns (bool); @@ -3562,6 +3606,46 @@ object "EVMInterpreter" { } } + function _performCall(addr,gasToPass,value,argsOffset,argsSize,retOffset,retSize,isStatic) -> success, frameGasLeft, gasToPassNew{ + gasToPassNew := gasToPass + let is_evm := _isEVM(addr) + if isStatic { + if value { + revert(0, 0) + } + success, frameGasLeft:= _performStaticCall( + is_evm, + gasToPassNew, + addr, + argsOffset, + argsSize, + retOffset, + retSize + ) + } + + if and(is_evm, iszero(isStatic)) { + _pushEVMFrame(gasToPassNew, isStatic) + success := call(gasToPassNew, addr, value, argsOffset, argsSize, 0, 0) + frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) + _popEVMFrame() + } + + // zkEVM native + if and(iszero(is_evm), iszero(isStatic)) { + gasToPassNew := _getZkEVMGas(gasToPassNew) + let zkevmGasBefore := gas() + success := call(gasToPassNew, addr, value, argsOffset, argsSize, retOffset, retSize) + _saveReturndataAfterZkEVMCall() + let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) + + frameGasLeft := 0 + if gt(gasToPassNew, gasUsed) { + frameGasLeft := sub(gasToPassNew, gasUsed) + } + } + } + function performCall(oldSp, evmGasLeft, isStatic) -> extraCost, sp { let gasToPass,addr,value,argsOffset,argsSize,retOffset,retSize @@ -3605,44 +3689,17 @@ object "EVMInterpreter" { checkMemOverflow(add(argsOffset, argsSize)) checkMemOverflow(add(retOffset, retSize)) - let frameGasLeft - let success - - if isStatic { - if value { - revert(0, 0) - } - success, frameGasLeft:= _performStaticCall( - _isEVM(addr), - gasToPass, - addr, - argsOffset, - argsSize, - retOffset, - retSize - ) - } - - if and(_isEVM(addr), iszero(isStatic)) { - _pushEVMFrame(gasToPass, isStatic) - success := call(gasToPass, addr, value, argsOffset, argsSize, 0, 0) - frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize) - _popEVMFrame() - } - - // zkEVM native - if and(iszero(_isEVM(addr)), iszero(isStatic)) { - gasToPass := _getZkEVMGas(gasToPass) - let zkevmGasBefore := gas() - success := call(gasToPass, addr, value, argsOffset, argsSize, retOffset, retSize) - _saveReturndataAfterZkEVMCall() - let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas())) - - frameGasLeft := 0 - if gt(gasToPass, gasUsed) { - frameGasLeft := sub(gasToPass, gasUsed) - } - } + let success, frameGasLeft + success, frameGasLeft, gasToPass:= _performCall( + addr, + gasToPass, + value, + argsOffset, + argsSize, + retOffset, + retSize, + isStatic + ) extraCost := add(extraCost,sub(gasToPass,frameGasLeft)) extraCost := add(extraCost, getGasForPrecompiles(addr, argsOffset, argsSize)) @@ -3777,12 +3834,12 @@ object "EVMInterpreter" { function isAddrEmpty(addr) -> isEmpty { isEmpty := 0 - if and( and( - iszero(balance(addr)), - iszero(extcodesize(addr)) ), - iszero(getNonce(addr)) - ) { - isEmpty := 1 + if iszero(extcodesize(addr)) { // YUL doesn't have short-circuit evaluation + if iszero(balance(addr)) { + if iszero(getRawNonce(addr)) { + isEmpty := 1 + } + } } } @@ -3948,7 +4005,7 @@ object "EVMInterpreter" { default { sp := pushStackItem(sp, addr) } } - function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp{ + function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp, result, addr{ evmGasLeft := chargeGas(evmGas, 32000) if isStatic { @@ -3990,17 +4047,12 @@ object "EVMInterpreter" { mstore(0x35, hashedBytecode) } - let addr := and( + addr := and( keccak256(0, 0x55), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ) - let result result, evmGasLeft := genericCreate(addr, offset, size, sp, value, evmGasLeft) - - switch result - case 0 { sp := pushStackItem(sp, 0) } - default { sp := pushStackItem(sp, addr) } } @@ -5215,7 +5267,11 @@ object "EVMInterpreter" { evmGasLeft := chargeGas(evmGasLeft, gasUsed) } case 0xF5 { // OP_CREATE2 - evmGasLeft, sp := performCreate2(evmGasLeft, sp, isStatic) + let result, addr + evmGasLeft, sp, result, addr := performCreate2(evmGasLeft, sp, isStatic) + switch result + case 0 { sp := pushStackItem(sp, 0) } + default { sp := pushStackItem(sp, addr) } } case 0xFA { // OP_STATICCALL evmGasLeft := chargeGas(evmGasLeft, 100) From f1817ef35152bd98822983908aa23f3dda6a45c8 Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Mon, 27 May 2024 11:15:18 -0300 Subject: [PATCH 13/15] Remove memCost function --- l1-contracts/lib/forge-std | 2 +- l1-contracts/lib/murky | 2 +- .../contracts/EvmInterpreterFunctions.template.yul | 6 ------ .../contracts/EvmInterpreterPreprocessed.yul | 12 ------------ 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/l1-contracts/lib/forge-std b/l1-contracts/lib/forge-std index 705263c95..52715a217 160000 --- a/l1-contracts/lib/forge-std +++ b/l1-contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 705263c95892a906d7af65f0f73ce8a4a0c80b80 +Subproject commit 52715a217dc51d0de15877878ab8213f6cbbbab5 diff --git a/l1-contracts/lib/murky b/l1-contracts/lib/murky index 40de6e801..5feccd125 160000 --- a/l1-contracts/lib/murky +++ b/l1-contracts/lib/murky @@ -1 +1 @@ -Subproject commit 40de6e80117f39cda69d71b07b7c824adac91b29 +Subproject commit 5feccd1253d7da820f7cccccdedf64471025455d diff --git a/system-contracts/contracts/EvmInterpreterFunctions.template.yul b/system-contracts/contracts/EvmInterpreterFunctions.template.yul index 8bacdfee8..1a1395895 100644 --- a/system-contracts/contracts/EvmInterpreterFunctions.template.yul +++ b/system-contracts/contracts/EvmInterpreterFunctions.template.yul @@ -464,12 +464,6 @@ function checkMemOverflow(location) { } } -// Note, that this function can overflow. It's up to the caller to ensure that it does not. -/*function memCost(memSizeWords) -> gasCost { - // The first term of the sum is the quadratic cost, the second one the linear one. - gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords)) -}*/ - // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. function expandMemory(newSize) -> gasCost { diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index c3802d78f..28b920add 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -538,12 +538,6 @@ object "EVMInterpreter" { } } - // Note, that this function can overflow. It's up to the caller to ensure that it does not. - /*function memCost(memSizeWords) -> gasCost { - // The first term of the sum is the quadratic cost, the second one the linear one. - gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords)) - }*/ - // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. function expandMemory(newSize) -> gasCost { @@ -3167,12 +3161,6 @@ object "EVMInterpreter" { } } - // Note, that this function can overflow. It's up to the caller to ensure that it does not. - /*function memCost(memSizeWords) -> gasCost { - // The first term of the sum is the quadratic cost, the second one the linear one. - gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords)) - }*/ - // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. function expandMemory(newSize) -> gasCost { From 63d1f4309bee8e4b68a0fa0edde24ca096fe6ad4 Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Mon, 27 May 2024 15:40:09 -0300 Subject: [PATCH 14/15] Revert "Remove memCost function" This reverts commit f1817ef35152bd98822983908aa23f3dda6a45c8. --- l1-contracts/lib/forge-std | 2 +- l1-contracts/lib/murky | 2 +- .../contracts/EvmInterpreterFunctions.template.yul | 6 ++++++ .../contracts/EvmInterpreterPreprocessed.yul | 12 ++++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/l1-contracts/lib/forge-std b/l1-contracts/lib/forge-std index 52715a217..705263c95 160000 --- a/l1-contracts/lib/forge-std +++ b/l1-contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 52715a217dc51d0de15877878ab8213f6cbbbab5 +Subproject commit 705263c95892a906d7af65f0f73ce8a4a0c80b80 diff --git a/l1-contracts/lib/murky b/l1-contracts/lib/murky index 5feccd125..40de6e801 160000 --- a/l1-contracts/lib/murky +++ b/l1-contracts/lib/murky @@ -1 +1 @@ -Subproject commit 5feccd1253d7da820f7cccccdedf64471025455d +Subproject commit 40de6e80117f39cda69d71b07b7c824adac91b29 diff --git a/system-contracts/contracts/EvmInterpreterFunctions.template.yul b/system-contracts/contracts/EvmInterpreterFunctions.template.yul index 1a1395895..8bacdfee8 100644 --- a/system-contracts/contracts/EvmInterpreterFunctions.template.yul +++ b/system-contracts/contracts/EvmInterpreterFunctions.template.yul @@ -464,6 +464,12 @@ function checkMemOverflow(location) { } } +// Note, that this function can overflow. It's up to the caller to ensure that it does not. +/*function memCost(memSizeWords) -> gasCost { + // The first term of the sum is the quadratic cost, the second one the linear one. + gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords)) +}*/ + // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. function expandMemory(newSize) -> gasCost { diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index 28b920add..c3802d78f 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -538,6 +538,12 @@ object "EVMInterpreter" { } } + // Note, that this function can overflow. It's up to the caller to ensure that it does not. + /*function memCost(memSizeWords) -> gasCost { + // The first term of the sum is the quadratic cost, the second one the linear one. + gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords)) + }*/ + // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. function expandMemory(newSize) -> gasCost { @@ -3161,6 +3167,12 @@ object "EVMInterpreter" { } } + // Note, that this function can overflow. It's up to the caller to ensure that it does not. + /*function memCost(memSizeWords) -> gasCost { + // The first term of the sum is the quadratic cost, the second one the linear one. + gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords)) + }*/ + // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. function expandMemory(newSize) -> gasCost { From a7028492e9d6dc1073ed86a9dfe8df3dfefd77f0 Mon Sep 17 00:00:00 2001 From: Gianbelinche Date: Mon, 27 May 2024 15:44:21 -0300 Subject: [PATCH 15/15] Remove memcost --- .../contracts/EvmInterpreterFunctions.template.yul | 6 ------ .../contracts/EvmInterpreterPreprocessed.yul | 12 ------------ 2 files changed, 18 deletions(-) diff --git a/system-contracts/contracts/EvmInterpreterFunctions.template.yul b/system-contracts/contracts/EvmInterpreterFunctions.template.yul index 8bacdfee8..1a1395895 100644 --- a/system-contracts/contracts/EvmInterpreterFunctions.template.yul +++ b/system-contracts/contracts/EvmInterpreterFunctions.template.yul @@ -464,12 +464,6 @@ function checkMemOverflow(location) { } } -// Note, that this function can overflow. It's up to the caller to ensure that it does not. -/*function memCost(memSizeWords) -> gasCost { - // The first term of the sum is the quadratic cost, the second one the linear one. - gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords)) -}*/ - // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. function expandMemory(newSize) -> gasCost { diff --git a/system-contracts/contracts/EvmInterpreterPreprocessed.yul b/system-contracts/contracts/EvmInterpreterPreprocessed.yul index c3802d78f..28b920add 100644 --- a/system-contracts/contracts/EvmInterpreterPreprocessed.yul +++ b/system-contracts/contracts/EvmInterpreterPreprocessed.yul @@ -538,12 +538,6 @@ object "EVMInterpreter" { } } - // Note, that this function can overflow. It's up to the caller to ensure that it does not. - /*function memCost(memSizeWords) -> gasCost { - // The first term of the sum is the quadratic cost, the second one the linear one. - gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords)) - }*/ - // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. function expandMemory(newSize) -> gasCost { @@ -3167,12 +3161,6 @@ object "EVMInterpreter" { } } - // Note, that this function can overflow. It's up to the caller to ensure that it does not. - /*function memCost(memSizeWords) -> gasCost { - // The first term of the sum is the quadratic cost, the second one the linear one. - gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords)) - }*/ - // This function can overflow, it is the job of the caller to ensure that it does not. // The argument to this function is the offset into the memory region IN BYTES. function expandMemory(newSize) -> gasCost {