From c6bd2979b1265cb81d35124a2785e3828658f7cc Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Tue, 31 Jul 2018 00:54:43 +0000 Subject: Add assembly support for EXTCODEHASH (EIP-1052) --- Changelog.md | 1 + libevmasm/GasMeter.cpp | 1 + libevmasm/Instruction.cpp | 2 ++ libevmasm/Instruction.h | 1 + libevmasm/SemanticInformation.cpp | 3 +++ libsolidity/inlineasm/AsmAnalysis.cpp | 12 +++++++++++- 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 15edb6d5..866cb860 100644 --- a/Changelog.md +++ b/Changelog.md @@ -90,6 +90,7 @@ Language Features: Compiler Features: * C API (``libsolc``): Export the ``solidity_license``, ``solidity_version`` and ``solidity_compile`` methods. * Code Generator: ``CREATE2`` instruction has been updated to match EIP1014 (aka "Skinny CREATE2"). It also is accepted as part of Constantinople. + * Code Generator: ``EXTCODEHASH`` instruction has been added based on EIP1052. * Type Checker: Nicer error message when trying to reference overloaded identifiers in inline assembly. * Type Checker: Show named argument in case of error. * Type System: IntegerType is split into IntegerType and AddressType internally. diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index b40617c1..59afc4da 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -112,6 +112,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2)); break; case Instruction::EXTCODESIZE: + case Instruction::EXTCODEHASH: gas = GasCosts::extCodeGas(m_evmVersion); break; case Instruction::EXTCODECOPY: diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index d5b82e75..46f6c628 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -73,6 +73,7 @@ const std::map dev::solidity::c_instructions = { "EXTCODECOPY", Instruction::EXTCODECOPY }, { "RETURNDATASIZE", Instruction::RETURNDATASIZE }, { "RETURNDATACOPY", Instruction::RETURNDATACOPY }, + { "EXTCODEHASH", Instruction::EXTCODEHASH }, { "BLOCKHASH", Instruction::BLOCKHASH }, { "COINBASE", Instruction::COINBASE }, { "TIMESTAMP", Instruction::TIMESTAMP }, @@ -216,6 +217,7 @@ static const std::map c_instructionInfo = { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::ExtCode } }, { Instruction::RETURNDATASIZE, {"RETURNDATASIZE", 0, 0, 1, false, Tier::Base } }, { Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } }, + { Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::ExtCode } }, { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } }, { Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } }, { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 50c1f47d..63424eeb 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -82,6 +82,7 @@ enum class Instruction: uint8_t EXTCODECOPY, ///< copy external code (from another contract) RETURNDATASIZE = 0x3d, ///< get size of return data buffer RETURNDATACOPY = 0x3e, ///< copy return data in current environment to memory + EXTCODEHASH = 0x3f, ///< get external code hash (from another contract) BLOCKHASH = 0x40, ///< get hash of most recent complete block COINBASE, ///< get the block's coinbase address diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 71267ee8..78f3c9c7 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -151,6 +151,7 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item) case Instruction::MSIZE: // depends on previous writes and reads, not only on content case Instruction::BALANCE: // depends on previous calls case Instruction::EXTCODESIZE: + case Instruction::EXTCODEHASH: case Instruction::RETURNDATACOPY: // depends on previous calls case Instruction::RETURNDATASIZE: return false; @@ -172,6 +173,7 @@ bool SemanticInformation::movable(Instruction _instruction) case Instruction::KECCAK256: case Instruction::BALANCE: case Instruction::EXTCODESIZE: + case Instruction::EXTCODEHASH: case Instruction::RETURNDATASIZE: case Instruction::SLOAD: case Instruction::PC: @@ -233,6 +235,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) case Instruction::GASPRICE: case Instruction::EXTCODESIZE: case Instruction::EXTCODECOPY: + case Instruction::EXTCODEHASH: case Instruction::BLOCKHASH: case Instruction::COINBASE: case Instruction::TIMESTAMP: diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 0f2c0f56..947b6d05 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -568,7 +568,17 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio // Similarly we assume bitwise shifting and create2 go together. solAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); - if (( + if (_instr == solidity::Instruction::EXTCODEHASH) + m_errorReporter.warning( + _location, + "The \"" + + boost::to_lower_copy(instructionInfo(_instr).name) + + "\" instruction is not supported by the VM version \"" + + "" + m_evmVersion.name() + + "\" you are currently compiling for. " + + "It will be interpreted as an invalid instruction on this VM." + ); + else if (( _instr == solidity::Instruction::RETURNDATACOPY || _instr == solidity::Instruction::RETURNDATASIZE || _instr == solidity::Instruction::STATICCALL -- cgit From 5d985abcab8d54fcef142f01039ab6d5628f3237 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 25 Sep 2018 22:37:46 +0100 Subject: Add test for extcodehash --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index b2e2b63b..640bf4d0 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -415,6 +415,19 @@ BOOST_AUTO_TEST_CASE(create2_as_variable) CHECK_ALLOW_MULTI(text, expectations); } +BOOST_AUTO_TEST_CASE(extcodehash_as_variable) +{ + char const* text = R"( + contract c { function f() public view { uint extcodehash; extcodehash; assembly { pop(extcodehash(0)) } }} + )"; + // This needs special treatment, because the message mentions the EVM version, + // so cannot be run via isoltest. + CHECK_ALLOW_MULTI(text, (std::vector>{ + {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, + {Error::Type::Warning, "The \"extcodehash\" instruction is not supported by the VM version"}, + })); +} + BOOST_AUTO_TEST_CASE(getter_is_memory_type) { char const* text = R"( -- cgit From 6a1e79a8e3fa8e8135de32b2b16eca93de5fb14c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 25 Sep 2018 22:49:26 +0100 Subject: Set the price of EXTCODEHASH properly --- libevmasm/GasMeter.cpp | 4 +++- libevmasm/Instruction.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 59afc4da..d98b3efa 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -112,9 +112,11 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2)); break; case Instruction::EXTCODESIZE: - case Instruction::EXTCODEHASH: gas = GasCosts::extCodeGas(m_evmVersion); break; + case Instruction::EXTCODEHASH: + gas = GasCosts::balanceGas(m_evmVersion); + break; case Instruction::EXTCODECOPY: gas = GasCosts::extCodeGas(m_evmVersion); gas += memoryGas(-1, -3); diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 46f6c628..cf98c938 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -217,7 +217,7 @@ static const std::map c_instructionInfo = { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::ExtCode } }, { Instruction::RETURNDATASIZE, {"RETURNDATASIZE", 0, 0, 1, false, Tier::Base } }, { Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } }, - { Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::ExtCode } }, + { Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::Balance } }, { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } }, { Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } }, { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } }, -- cgit From f2fa5133204b6c98c67b5336e8714647147221c0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 25 Sep 2018 22:56:34 +0100 Subject: Add LLL tests for EXTCODEHASH --- test/liblll/Compiler.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp index be798dc8..a4394f54 100644 --- a/test/liblll/Compiler.cpp +++ b/test/liblll/Compiler.cpp @@ -186,6 +186,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_functional) "60006000600060003c", "3d", "6000600060003e", + "60003f", "600040", "41", "42", @@ -291,6 +292,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_functional) "{ (EXTCODECOPY 0 0 0 0) }", "{ (RETURNDATASIZE) }", "{ (RETURNDATACOPY 0 0 0) }", + "{ (EXTCODEHASH 0) }", "{ (BLOCKHASH 0) }", "{ (COINBASE) }", "{ (TIMESTAMP) }", @@ -409,6 +411,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_asm) "3c", "3d", "3e", + "3f", "40", "41", "42", @@ -547,6 +550,7 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_asm) "{ (asm EXTCODECOPY) }", "{ (asm RETURNDATASIZE) }", "{ (asm RETURNDATACOPY) }", + "{ (asm EXTCODEHASH) }", "{ (asm BLOCKHASH) }", "{ (asm COINBASE) }", "{ (asm TIMESTAMP) }", -- cgit From 8d8c855810ed51d17cab679cd89f7a75410a4258 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 26 Sep 2018 16:12:54 +0100 Subject: Add extcodehash to the assembly documentation --- docs/assembly.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 5d723645..02a802a8 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -271,12 +271,14 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+-----+---+-----------------------------------------------------------------+ | returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t | +-------------------------+-----+---+-----------------------------------------------------------------+ +| extcodehash(a) | | C | code hash of address a | ++-------------------------+-----+---+-----------------------------------------------------------------+ | create(v, p, s) | | F | create new contract with code mem[p...(p+s)) and send v wei | | | | | and return the new address | +-------------------------+-----+---+-----------------------------------------------------------------+ | create2(v, n, p, s) | | C | create new contract with code mem[p...(p+s)) at address | -| | | | keccak256(
. n . keccak256(mem[p...(p+s))) and send v | -| | | | wei and return the new address | +| | | | keccak256(0xff .
. n . keccak256(mem[p...(p+s))) | +| | | | and send v wei and return the new address | +-------------------------+-----+---+-----------------------------------------------------------------+ | call(g, a, v, in, | | F | call contract at address a with input mem[in...(in+insize)) | | insize, out, outsize) | | | providing g gas and v wei and output area | -- cgit