diff --git a/nimbus/utils.nim b/nimbus/utils.nim index 9adc756b0..45c864305 100644 --- a/nimbus/utils.nim +++ b/nimbus/utils.nim @@ -20,5 +20,21 @@ func keccakHash*(value: openarray[byte]): Hash256 {.inline.} = func generateAddress*(address: EthAddress, nonce: AccountNonce): EthAddress = result[0..19] = keccakHash(rlp.encodeList(address, nonce)).data.toOpenArray(12, 31) +func generateSafeAddress*(address: EthAddress, salt: Uint256, data: openArray[byte]): EthAddress = + const prefix = [0xff.byte] + let dataHash = keccakHash(data) + var hashResult: Hash256 + + var ctx: keccak256 + ctx.init() + ctx.update(prefix) + ctx.update(address) + ctx.update(salt.toByteArrayBE()) + ctx.update(dataHash.data) + ctx.finish hashResult.data + ctx.clear() + + result[0..19] = hashResult.data.toOpenArray(12, 31) + func hash*(b: BlockHeader): Hash256 {.inline.} = rlpHash(b) diff --git a/nimbus/vm/computation.nim b/nimbus/vm/computation.nim index 71df6e2d8..8938ee889 100644 --- a/nimbus/vm/computation.nim +++ b/nimbus/vm/computation.nim @@ -196,7 +196,7 @@ proc applyMessage*(computation: BaseComputation, opCode: static[Op]) = computation.dispose() # EIP161 nonce incrementation - when opCode == Create: + when opCode in {Create, Create2}: if computation.getFork >= FkSpurious: computation.vmState.mutateStateDb: db.incNonce(computation.msg.storageAddress) diff --git a/nimbus/vm/interpreter/gas_costs.nim b/nimbus/vm/interpreter/gas_costs.nim index 0b5497caa..c1b3b9511 100644 --- a/nimbus/vm/interpreter/gas_costs.nim +++ b/nimbus/vm/interpreter/gas_costs.nim @@ -353,6 +353,10 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) = if gasParams.sd_condition: result.gasCost += static(FeeSchedule[GasNewAccount]) + func `prefix gasCreate2`(currentMemSize, memOffset, memLength: Natural): GasInt {.nimcall.} = + result = static(FeeSchedule[GasCreate]) + + static(FeeSchedule[GasSha3Word]) * (memLength).wordCount + # ################################################################################################### # TODO - change this `let` into `const` - pending: https://github.com/nim-lang/Nim/issues/8015 @@ -532,7 +536,7 @@ template gasCosts(fork: Fork, prefix, ResultGasCostsName: untyped) = CallCode: complex `prefix gasCall`, Return: memExpansion `prefix gasHalt`, DelegateCall: complex `prefix gasCall`, - Create2: complex `prefix gasCall`, + Create2: memExpansion `prefix gasCreate2`, StaticCall: complex `prefix gasCall`, Revert: memExpansion `prefix gasHalt`, Invalid: fixed GasZero, diff --git a/nimbus/vm/interpreter/opcodes_impl.nim b/nimbus/vm/interpreter/opcodes_impl.nim index aad15fd49..33396b96a 100644 --- a/nimbus/vm/interpreter/opcodes_impl.nim +++ b/nimbus/vm/interpreter/opcodes_impl.nim @@ -503,14 +503,18 @@ genLog() # ########################################## # f0s: System operations. -proc canTransfer(computation: BaseComputation, memPos, memLen: int, value: Uint256): bool = - let gasParams = GasParams(kind: Create, - cr_currentMemSize: computation.memory.len, - cr_memOffset: memPos, - cr_memLength: memLen - ) - let gasCost = computation.gasCosts[Create].c_handler(1.u256, gasParams).gasCost - let reason = &"CREATE: GasCreate + {memLen} * memory expansion" +proc canTransfer(computation: BaseComputation, memPos, memLen: int, value: Uint256, opCode: static[Op]): bool = + when opCode == Create: + let gasParams = GasParams(kind: Create, + cr_currentMemSize: computation.memory.len, + cr_memOffset: memPos, + cr_memLength: memLen + ) + let gasCost = computation.gasCosts[Create].c_handler(1.u256, gasParams).gasCost + let reason = &"CREATE: GasCreate + {memLen} * memory expansion" + else: + let gasCost = computation.gasCosts[Create2].m_handler(0, 0, memLen) + let reason = &"CREATE2: GasCreate + {memLen} * GasSha3Word" computation.gasMeter.consumeGas(gasCost, reason = reason) computation.memory.extend(memPos, memLen) @@ -535,7 +539,7 @@ proc canTransfer(computation: BaseComputation, memPos, memLen: int, value: Uint2 result = true -proc setupCreate(computation: BaseComputation, memPos, len: int, value: Uint256): BaseComputation = +proc setupCreate(computation: BaseComputation, memPos, len: int, value: Uint256, opCode: static[Op]): BaseComputation = let callData = computation.memory.read(memPos, len) @@ -553,14 +557,20 @@ proc setupCreate(computation: BaseComputation, memPos, len: int, value: Uint256) contractAddress: EthAddress isCollision: bool - computation.vmState.mutateStateDB: - # Regarding collisions, see: https://github.com/status-im/nimbus/issues/133 - # See: https://github.com/ethereum/EIPs/issues/684 - let creationNonce = db.getNonce(computation.msg.storageAddress) - db.setNonce(computation.msg.storageAddress, creationNonce + 1) + when opCode == Create: + computation.vmState.mutateStateDB: + # Regarding collisions, see: https://github.com/status-im/nimbus/issues/133 + # See: https://github.com/ethereum/EIPs/issues/684 + let creationNonce = db.getNonce(computation.msg.storageAddress) + db.setNonce(computation.msg.storageAddress, creationNonce + 1) - contractAddress = generateAddress(computation.msg.storageAddress, creationNonce) - isCollision = db.hasCodeOrNonce(contractAddress) + contractAddress = generateAddress(computation.msg.storageAddress, creationNonce) + isCollision = db.hasCodeOrNonce(contractAddress) + else: + computation.vmState.mutateStateDB: + let salt = computation.stack.popInt() + contractAddress = generateSafeAddress(computation.msg.storageAddress, salt, callData) + isCollision = db.hasCodeOrNonce(contractAddress) if isCollision: debug "Address collision while creating contract", address = contractAddress.toHex @@ -585,27 +595,31 @@ proc setupCreate(computation: BaseComputation, memPos, len: int, value: Uint256) childMsg, some(computation.getFork)) -op create, inline = false, value, startPosition, size: - ## 0xf0, Create a new account with associated code. - let (memPos, len) = (startPosition.cleanMemRef, size.cleanMemRef) - if not computation.canTransfer(memPos, len, value): - push: 0 - return - - var childComp = setupCreate(computation, memPos, len, value) - if childComp.isNil: return - - computation.child = childComp - continuation(childComp): - computation.addChildComputation(childComp) - - if childComp.isError: +template genCreate(callName: untyped, opCode: Op): untyped = + op callName, inline = false, value, startPosition, size: + ## 0xf0, Create a new account with associated code. + let (memPos, len) = (startPosition.cleanMemRef, size.cleanMemRef) + if not computation.canTransfer(memPos, len, value, opCode): push: 0 - else: - push: childComp.msg.storageAddress + return - checkInStaticContext(computation) - childComp.applyMessage(Create) + var childComp = setupCreate(computation, memPos, len, value, opCode) + if childComp.isNil: return + + computation.child = childComp + continuation(childComp): + addChildComputation(computation, childComp) + + if childComp.isError: + push: 0 + else: + push: childComp.msg.storageAddress + + checkInStaticContext(computation) + childComp.applyMessage(Create) + +genCreate(create, Create) +genCreate(create2, Create2) proc callParams(computation: BaseComputation): (UInt256, UInt256, EthAddress, EthAddress, EthAddress, UInt256, UInt256, UInt256, UInt256, MsgFlags) = let gas = computation.stack.popInt() @@ -909,7 +923,3 @@ op extCodeHash, inline = true: push: 0 else: push: computation.vmState.readOnlyStateDB.getCodeHash(address) - -op create2, inline = false: - # TODO: implementation - discard