From ef6ff2f05550e4386f2fec7753a44fe39a124488 Mon Sep 17 00:00:00 2001 From: "Rodrigo Q. Saramago" Date: Wed, 23 Nov 2022 11:51:34 +0100 Subject: [PATCH] Adds support for the EVM version "Paris". MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deprecates `block.difficulty` and disallow `difficulty()` in inline assembly for EVM versions >= paris. The change is due to the renaming introduced by EIP-4399 (see: https://eips.ethereum.org/EIPS/eip-4399). Introduces `block.prevrandao` in Solidity and `prevrandao()` in inline assembly for EVM versions >= paris. Co-authored-by: Alex Beregszaszi Co-authored-by: Daniel Co-authored-by: matheusaaguiar <95899911+matheusaaguiar@users.noreply.github.com> Co-authored-by: Nikola Matić --- Changelog.md | 4 +- docs/cheatsheet.rst | 5 +- docs/grammar/SolidityLexer.g4 | 4 +- docs/units-and-global-variables.rst | 7 +- docs/yul.rst | 12 ++- libevmasm/Assembly.cpp | 2 +- libevmasm/Assembly.h | 5 +- libevmasm/AssemblyItem.cpp | 17 +++-- libevmasm/AssemblyItem.h | 3 +- libevmasm/CommonSubexpressionEliminator.cpp | 4 +- libevmasm/ConstantOptimiser.cpp | 12 +-- libevmasm/ConstantOptimiser.h | 2 +- libevmasm/Disassemble.cpp | 9 ++- libevmasm/Disassemble.h | 4 +- libevmasm/GasMeter.cpp | 24 +++--- libevmasm/GasMeter.h | 3 +- libevmasm/Instruction.cpp | 10 ++- libevmasm/Instruction.h | 5 +- libevmasm/KnownState.cpp | 4 +- libevmasm/PeepholeOptimiser.cpp | 73 +++++++++++++------ libevmasm/SemanticInformation.cpp | 10 +-- libevmasm/SimplificationRule.h | 2 +- libevmasm/SimplificationRules.cpp | 3 +- liblangutil/EVMVersion.h | 1 + libsolidity/analysis/TypeChecker.cpp | 39 +++++++--- libsolidity/ast/Types.cpp | 1 + libsolidity/codegen/CompilerContext.h | 2 +- libsolidity/codegen/ExpressionCompiler.cpp | 4 +- .../codegen/ir/IRGeneratorForStatements.cpp | 9 ++- libsolidity/interface/StandardCompiler.cpp | 6 +- libyul/AsmAnalysis.cpp | 4 +- libyul/CompilabilityChecker.cpp | 2 +- libyul/YulStack.cpp | 2 +- libyul/backends/evm/AbstractAssembly.h | 1 + libyul/backends/evm/EVMDialect.cpp | 25 ++++++- libyul/backends/evm/EVMMetrics.cpp | 6 +- libyul/backends/evm/EthAssemblyAdapter.cpp | 2 +- libyul/backends/evm/NoOutputAssembly.cpp | 2 +- libyul/backends/evm/NoOutputAssembly.h | 3 +- libyul/backends/evm/StackLayoutGenerator.cpp | 8 +- libyul/optimiser/ExpressionSimplifier.cpp | 2 +- libyul/optimiser/KnowledgeBase.cpp | 2 +- libyul/optimiser/Metrics.cpp | 3 +- libyul/optimiser/OptimizerUtilities.cpp | 7 ++ libyul/optimiser/OptimizerUtilities.h | 5 ++ libyul/optimiser/SimplificationRules.cpp | 6 +- libyul/optimiser/SimplificationRules.h | 2 +- scripts/check_style.sh | 3 +- scripts/test_antlr_grammar.sh | 3 + solc/CommandLineInterface.cpp | 6 +- .../evmasm_difficulty_post_paris/args | 1 + .../evmasm_difficulty_post_paris/err | 5 ++ .../evmasm_difficulty_post_paris/input.sol | 18 +++++ .../evmasm_difficulty_post_paris/output | 13 ++++ .../evmasm_difficulty_pre_paris/args | 1 + .../evmasm_difficulty_pre_paris/input.sol | 18 +++++ .../evmasm_difficulty_pre_paris/output | 13 ++++ .../evmasm_prevrandao_post_paris/args | 1 + .../evmasm_prevrandao_post_paris/input.sol | 18 +++++ .../evmasm_prevrandao_post_paris/output | 13 ++++ .../evmasm_prevrandao_pre_paris/args | 1 + .../evmasm_prevrandao_pre_paris/err | 5 ++ .../evmasm_prevrandao_pre_paris/input.sol | 18 +++++ .../evmasm_prevrandao_pre_paris/output | 13 ++++ test/libevmasm/Assembler.cpp | 27 ++++--- test/libevmasm/Optimiser.cpp | 24 +++--- test/libsolidity/SolidityOptimizer.cpp | 6 +- .../analysis/FunctionCallGraph.cpp | 1 + .../inlineAssembly/difficulty.sol | 12 +++ .../inlineAssembly/prevrandao.sol | 12 +++ .../semanticTests/state/block_prevrandao.sol | 10 +++ .../state/block_prevrandao_pre_paris.sol | 10 +++ .../difficulty_builtin_pre_paris.sol | 14 ++++ ...fficulty_disallowed_function_pre_paris.sol | 21 ++++++ ...difficulty_magic_block_warn_post_paris.sol | 9 +++ .../difficulty_nobuiltin_post_paris.sol | 12 +++ .../difficulty_reserved_post_paris.sol | 22 ++++++ .../prevrandao_allowed_function_pre_paris.sol | 20 +++++ .../prevrandao_builtin_post_paris.sol | 14 ++++ ...vrandao_disallowed_function_post_paris.sol | 21 ++++++ .../prevrandao_magic_block_warn_pre_paris.sol | 9 +++ .../prevrandao_nobuitin_pre_paris.sol | 12 +++ ...-byzantium.sol => low_level_homestead.sol} | 2 +- .../tryCatch/low_level_spuriousDragon.sol | 13 ++++ .../tryCatch/low_level_tangerineWhistle.sol | 13 ++++ ...byzantium.sol => structured_homestead.sol} | 2 +- .../tryCatch/structured_spuriousDragon.sol | 13 ++++ .../tryCatch/structured_tangerineWhistle.sol | 13 ++++ .../types/magic_block_istanbul.sol | 7 +- .../syntaxTests/types/magic_block_paris.sol | 13 ++++ .../inline_assembly_instructions_allowed.sol | 4 +- ...e_assembly_instructions_allowed_london.sol | 11 +++ ...ine_assembly_instructions_allowed_pure.sol | 1 - ...ine_assembly_instructions_allowed_view.sol | 4 +- ...embly_instructions_allowed_view_london.sol | 10 +++ ..._assembly_instructions_disallowed_pure.sol | 60 ++++++--------- ...instructions_disallowed_pure_byzantium.sol | 11 +++ ...uctions_disallowed_pure_constantinople.sol | 13 ++++ ..._instructions_disallowed_pure_istanbul.sol | 13 ++++ ...ly_instructions_disallowed_pure_london.sol | 11 +++ ...bly_instructions_disallowed_pure_paris.sol | 11 +++ ...instructions_disallowed_pure_pre_paris.sol | 11 +++ test/libyul/EVMCodeTransformTest.cpp | 4 +- test/libyul/ObjectCompilerTest.cpp | 4 +- test/libyul/YulInterpreterTest.cpp | 2 +- .../ewasmTranslationTests/difficulty.yul | 2 + .../libyul/yulInterpreterTests/difficulty.yul | 10 +++ .../libyul/yulInterpreterTests/prevrandao.yul | 10 +++ test/tools/fuzzer_common.cpp | 2 +- .../EVMInstructionInterpreter.cpp | 8 +- .../EVMInstructionInterpreter.h | 6 +- .../EwasmBuiltinInterpreter.cpp | 2 +- test/tools/yulInterpreter/Interpreter.cpp | 2 +- test/tools/yulInterpreter/Interpreter.h | 1 + 114 files changed, 842 insertions(+), 221 deletions(-) create mode 100644 test/cmdlineTests/evmasm_difficulty_post_paris/args create mode 100644 test/cmdlineTests/evmasm_difficulty_post_paris/err create mode 100644 test/cmdlineTests/evmasm_difficulty_post_paris/input.sol create mode 100644 test/cmdlineTests/evmasm_difficulty_post_paris/output create mode 100644 test/cmdlineTests/evmasm_difficulty_pre_paris/args create mode 100644 test/cmdlineTests/evmasm_difficulty_pre_paris/input.sol create mode 100644 test/cmdlineTests/evmasm_difficulty_pre_paris/output create mode 100644 test/cmdlineTests/evmasm_prevrandao_post_paris/args create mode 100644 test/cmdlineTests/evmasm_prevrandao_post_paris/input.sol create mode 100644 test/cmdlineTests/evmasm_prevrandao_post_paris/output create mode 100644 test/cmdlineTests/evmasm_prevrandao_pre_paris/args create mode 100644 test/cmdlineTests/evmasm_prevrandao_pre_paris/err create mode 100644 test/cmdlineTests/evmasm_prevrandao_pre_paris/input.sol create mode 100644 test/cmdlineTests/evmasm_prevrandao_pre_paris/output create mode 100644 test/libsolidity/semanticTests/inlineAssembly/difficulty.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/prevrandao.sol create mode 100644 test/libsolidity/semanticTests/state/block_prevrandao.sol create mode 100644 test/libsolidity/semanticTests/state/block_prevrandao_pre_paris.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/difficulty_builtin_pre_paris.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/difficulty_disallowed_function_pre_paris.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/difficulty_magic_block_warn_post_paris.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/difficulty_nobuiltin_post_paris.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/difficulty_reserved_post_paris.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/prevrandao_allowed_function_pre_paris.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/prevrandao_builtin_post_paris.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/prevrandao_disallowed_function_post_paris.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/prevrandao_magic_block_warn_pre_paris.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/prevrandao_nobuitin_pre_paris.sol rename test/libsolidity/syntaxTests/tryCatch/{low_level_pre-byzantium.sol => low_level_homestead.sol} (92%) create mode 100644 test/libsolidity/syntaxTests/tryCatch/low_level_spuriousDragon.sol create mode 100644 test/libsolidity/syntaxTests/tryCatch/low_level_tangerineWhistle.sol rename test/libsolidity/syntaxTests/tryCatch/{structured_pre_byzantium.sol => structured_homestead.sol} (92%) create mode 100644 test/libsolidity/syntaxTests/tryCatch/structured_spuriousDragon.sol create mode 100644 test/libsolidity/syntaxTests/tryCatch/structured_tangerineWhistle.sol create mode 100644 test/libsolidity/syntaxTests/types/magic_block_paris.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_london.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_london.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_byzantium.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_constantinople.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_istanbul.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_london.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_paris.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_pre_paris.sol create mode 100644 test/libyul/yulInterpreterTests/difficulty.yul create mode 100644 test/libyul/yulInterpreterTests/prevrandao.yul diff --git a/Changelog.md b/Changelog.md index 2877bbf8c..251144b37 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,7 +7,9 @@ Language Features: Compiler Features: * Commandline Interface: Return exit code ``2`` on uncaught exceptions. * Commandline Interface: Add `--no-cbor-metadata` that skips CBOR metadata from getting appended at the end of the bytecode. - * EVM: Basic support for the EVM version "Paris". + * EVM: Deprecate ``block.difficulty`` and disallow ``difficulty()`` in inline assembly for EVM versions >= paris. The change is due to the renaming introduced by [EIP-4399](https://eips.ethereum.org/EIPS/eip-4399). + * EVM: Introduce ``block.prevrandao`` in Solidity and ``prevrandao()`` in inline assembly for EVM versions >= paris. + * EVM: Support for the EVM version "Paris". * Natspec: Add event Natspec inheritance for devdoc. * Standard JSON: Add a boolean field `settings.metadata.appendCBOR` that skips CBOR metadata from getting appended at the end of the bytecode. * Yul EVM Code Transform: Generate more optimal code for user-defined functions that always terminate a transaction. No return labels will be pushed for calls to functions that always terminate. diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 412678953..5d8e6ede7 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -8,7 +8,7 @@ Order of Precedence of Operators ================================ .. include:: types/operator-precedence-table.rst -.. index:: assert, block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, codehash, send +.. index:: assert, block, coinbase, difficulty, prevrandao, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, codehash, send Global Variables ================ @@ -32,9 +32,10 @@ Global Variables - ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 `_ and `EIP-1559 `_) - ``block.chainid`` (``uint``): current chain id - ``block.coinbase`` (``address payable``): current block miner's address -- ``block.difficulty`` (``uint``): current block difficulty +- ``block.difficulty`` (``uint``): current block difficulty (``EVM < Paris``). For other EVM versions it behaves as a deprecated alias for ``block.prevrandao`` that will be removed in the next breaking release - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number +- ``block.prevrandao`` (``uint``): random number provided by the beacon chain (``EVM >= Paris``) (see `EIP-4399 `_ ) - ``block.timestamp`` (``uint``): current block timestamp in seconds since Unix epoch - ``gasleft() returns (uint256)``: remaining gas - ``msg.data`` (``bytes``): complete calldata diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 index c47dc411c..b0dfca0c4 100644 --- a/docs/grammar/SolidityLexer.g4 +++ b/docs/grammar/SolidityLexer.g4 @@ -291,8 +291,8 @@ YulEVMBuiltin: | 'returndatacopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' | 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid' | 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice' - | 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'gaslimit' - | 'basefee'; + | 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'prevrandao' + | 'gaslimit' | 'basefee'; YulLBrace: '{' -> pushMode(YulMode); YulRBrace: '}' -> popMode; diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index cf4fb2bd7..6e4966590 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -65,7 +65,7 @@ There are special variables and functions which always exist in the global namespace and are mainly used to provide information about the blockchain or are general-use utility functions. -.. index:: abi, block, coinbase, difficulty, encode, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin +.. index:: abi, block, coinbase, difficulty, prevrandao, encode, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin Block and Transaction Properties @@ -75,9 +75,10 @@ Block and Transaction Properties - ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 `_ and `EIP-1559 `_) - ``block.chainid`` (``uint``): current chain id - ``block.coinbase`` (``address payable``): current block miner's address -- ``block.difficulty`` (``uint``): current block difficulty +- ``block.difficulty`` (``uint``): current block difficulty (``EVM < Paris``). For other EVM versions it behaves as a deprecated alias for ``block.prevrandao`` (`EIP-4399 `_ ) - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number +- ``block.prevrandao`` (``uint``): random number provided by the beacon chain (``EVM >= Paris``) - ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch - ``gasleft() returns (uint256)``: remaining gas - ``msg.data`` (``bytes calldata``): complete calldata @@ -394,4 +395,4 @@ These keywords are reserved in Solidity. They might become part of the syntax in ``define``, ``final``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``, ``mutable``, ``null``, ``of``, ``partial``, ``promise``, ``reference``, ``relocatable``, ``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``typedef``, ``typeof``, -``var``. \ No newline at end of file +``var``. diff --git a/docs/yul.rst b/docs/yul.rst index 8ae454b2e..206f80cac 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -751,8 +751,8 @@ This document does not want to be a full description of the Ethereum virtual mac Please refer to a different document if you are interested in the precise semantics. Opcodes marked with ``-`` do not return a result and all others return exactly one value. -Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I`` and ``L`` are present since Frontier, Homestead, -Byzantium, Constantinople, Istanbul or London respectively. +Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I``, ``L`` and ``P`` are present since Frontier, +Homestead, Byzantium, Constantinople, Istanbul, London or Paris respectively. In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to but not including position ``b`` and ``storage[p]`` signifies the storage contents at slot ``p``. @@ -931,6 +931,8 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a +-------------------------+-----+---+-----------------------------------------------------------------+ | difficulty() | | F | difficulty of the current block (see note below) | +-------------------------+-----+---+-----------------------------------------------------------------+ +| prevrandao() | | P | randomness provided by the beacon chain (see note below) | ++-------------------------+-----+---+-----------------------------------------------------------------+ | gaslimit() | | F | block gas limit of the current block | +-------------------------+-----+---+-----------------------------------------------------------------+ @@ -945,8 +947,10 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a The remaining bytes will retain their values as of before the call. .. note:: - With the Paris network upgrade the semantics of ``difficulty`` have been changed. - It returns the value of ``prevrandao``, which is a 256-bit value, whereas the highest recorded + The `difficulty()` instruction is disallowed in EVM version >= Paris. + With the Paris network upgrade the semantics of the instruction that was previously called + ``difficulty`` have been changed and the instruction was renamed to ``prevrandao``. + It can now return arbitrary values in the full 256-bit range, whereas the highest recorded difficulty value within Ethash was ~54 bits. This change is described in `EIP-4399 `_. Please note that irrelevant to which EVM version is selected in the compiler, the semantics of diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 5b6e2a86f..f596f6303 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -237,7 +237,7 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices, sourceIndex = static_cast(iter->second); } - auto [name, data] = item.nameAndData(); + auto [name, data] = item.nameAndData(m_evmVersion); Json::Value jsonItem; jsonItem["name"] = name; jsonItem["begin"] = item.location().start; diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 7b8b4509d..bd37b3a7f 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -49,7 +49,7 @@ using AssemblyPointer = std::shared_ptr; class Assembly { public: - Assembly(bool _creation, std::string _name): m_creation(_creation), m_name(std::move(_name)) { } + Assembly(langutil::EVMVersion _evmVersion, bool _creation, std::string _name): m_evmVersion(_evmVersion), m_creation(_creation), m_name(std::move(_name)) { } AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); } @@ -112,6 +112,7 @@ public: /// Changes the source location used for each appended item. void setSourceLocation(langutil::SourceLocation const& _location) { m_currentSourceLocation = _location; } langutil::SourceLocation const& currentSourceLocation() const { return m_currentSourceLocation; } + langutil::EVMVersion const& evmVersion() const { return m_evmVersion; } /// Assembles the assembly into bytecode. The assembly should not be modified after this call, since the assembled version is cached. LinkerObject const& assemble() const; @@ -208,6 +209,8 @@ protected: mutable LinkerObject m_assembledObject; mutable std::vector m_tagPositionsInBytecode; + langutil::EVMVersion m_evmVersion; + int m_deposit = 0; /// True, if the assembly contains contract creation code. bool const m_creation = false; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index a36e0ddb1..ce60c2516 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -69,12 +69,12 @@ pair AssemblyItem::splitForeignPushTag() const return make_pair(subId, tag); } -pair AssemblyItem::nameAndData() const +pair AssemblyItem::nameAndData(langutil::EVMVersion _evmVersion) const { switch (type()) { case Operation: - return {instructionInfo(instruction()).name, m_data != nullptr ? toStringInHex(*m_data) : ""}; + return {instructionInfo(instruction(), _evmVersion).name, m_data != nullptr ? toStringInHex(*m_data) : ""}; case Push: return {"PUSH", toStringInHex(data())}; case PushTag: @@ -168,7 +168,9 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision) size_t AssemblyItem::arguments() const { if (type() == Operation) - return static_cast(instructionInfo(instruction()).args); + // The latest EVMVersion is used here, since the InstructionInfo is assumed to be + // the same across all EVM versions except for the instruction name. + return static_cast(instructionInfo(instruction(), EVMVersion()).args); else if (type() == VerbatimBytecode) return get<0>(*m_verbatimBytecode); else if (type() == AssignImmutable) @@ -182,7 +184,9 @@ size_t AssemblyItem::returnValues() const switch (m_type) { case Operation: - return static_cast(instructionInfo(instruction()).ret); + // The latest EVMVersion is used here, since the InstructionInfo is assumed to be + // the same across all EVM versions except for the instruction name. + return static_cast(instructionInfo(instruction(), EVMVersion()).ret); case Push: case PushTag: case PushData: @@ -251,7 +255,7 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const case Operation: { assertThrow(isValidInstruction(instruction()), AssemblyException, "Invalid instruction."); - text = util::toLower(instructionInfo(instruction()).name); + text = util::toLower(instructionInfo(instruction(), _assembly.evmVersion()).name); break; } case Push: @@ -323,12 +327,13 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const return text; } +// Note: This method is exclusively used for debugging. ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item) { switch (_item.type()) { case Operation: - _out << " " << instructionInfo(_item.instruction()).name; + _out << " " << instructionInfo(_item.instruction(), EVMVersion()).name; if (_item.instruction() == Instruction::JUMP || _item.instruction() == Instruction::JUMPI) _out << "\t" << _item.getJumpTypeAsString(); break; diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 4aef82d8b..077c1912d 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -108,10 +108,11 @@ public: /// This function is used in `Assembly::assemblyJSON`. /// It returns the name & data of the current assembly item. + /// @param _evmVersion the EVM version. /// @returns a pair, where the first element is the json-assembly /// item name, where second element is the string representation /// of it's data. - std::pair nameAndData() const; + std::pair nameAndData(langutil::EVMVersion _evmVersion) const; bytes const& verbatimData() const { assertThrow(m_type == VerbatimBytecode, util::Exception, ""); return std::get<2>(*m_verbatimBytecode); } diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 19b275cb6..d097f3c72 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -406,7 +406,7 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced) m_stack.erase(m_stackHeight - static_cast(i)); } appendItem(*expr.item); - if (expr.item->type() != Operation || instructionInfo(expr.item->instruction()).ret == 1) + if (expr.item->type() != Operation || instructionInfo(expr.item->instruction(), EVMVersion()).ret == 1) { m_stack[m_stackHeight] = _c; m_classPositions[_c].insert(m_stackHeight); @@ -414,7 +414,7 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced) else { assertThrow( - instructionInfo(expr.item->instruction()).ret == 0, + instructionInfo(expr.item->instruction(), EVMVersion()).ret == 0, OptimizerException, "Invalid number of return values." ); diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index 37ac4c18b..5bce21cf1 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -79,18 +79,18 @@ unsigned ConstantOptimisationMethod::optimiseConstants( return optimisations; } -bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items) +bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items, langutil::EVMVersion _evmVersion) { bigint gas = 0; for (AssemblyItem const& item: _items) if (item.type() == Push) - gas += GasMeter::runGas(Instruction::PUSH1); + gas += GasMeter::runGas(Instruction::PUSH1, _evmVersion); else if (item.type() == Operation) { if (item.instruction() == Instruction::EXP) gas += GasCosts::expGas; else - gas += GasMeter::runGas(item.instruction()); + gas += GasMeter::runGas(item.instruction(), _evmVersion); } return gas; } @@ -131,7 +131,7 @@ void ConstantOptimisationMethod::replaceConstants( bigint LiteralMethod::gasNeeded() const { return combineGas( - simpleRunGas({Instruction::PUSH1}), + simpleRunGas({Instruction::PUSH1}, m_params.evmVersion), // PUSHX plus data (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)), 0 @@ -142,7 +142,7 @@ bigint CodeCopyMethod::gasNeeded() const { return combineGas( // Run gas: we ignore memory increase costs - simpleRunGas(copyRoutine()) + GasCosts::copyGas, + simpleRunGas(copyRoutine(), m_params.evmVersion) + GasCosts::copyGas, // Data gas for copy routines: Some bytes are zero, but we ignore them. bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas), // Data gas for data itself @@ -322,7 +322,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const { auto numExps = static_cast(count(_routine.begin(), _routine.end(), Instruction::EXP)); return combineGas( - simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)), + simpleRunGas(_routine, m_params.evmVersion) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)), // Data gas for routine: Some bytes are zero, but we ignore them. bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas), 0 diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index ece28d249..b491fb004 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -76,7 +76,7 @@ protected: protected: /// @returns the run gas for the given items ignoring special gas costs - static bigint simpleRunGas(AssemblyItems const& _items); + static bigint simpleRunGas(AssemblyItems const& _items, langutil::EVMVersion _evmVersion); /// @returns the gas needed to store the given data literally bigint dataGas(bytes const& _data) const; static size_t bytesRequired(AssemblyItems const& _items); diff --git a/libevmasm/Disassemble.cpp b/libevmasm/Disassemble.cpp index aa5b2820c..f1c904b69 100644 --- a/libevmasm/Disassemble.cpp +++ b/libevmasm/Disassemble.cpp @@ -30,6 +30,7 @@ using namespace solidity::evmasm; void solidity::evmasm::eachInstruction( bytes const& _mem, + langutil::EVMVersion _evmVersion, function const& _onInstruction ) { @@ -38,7 +39,7 @@ void solidity::evmasm::eachInstruction( Instruction const instr{*it}; int additional = 0; if (isValidInstruction(instr)) - additional = instructionInfo(instr).additional; + additional = instructionInfo(instr, _evmVersion).additional; u256 data{}; @@ -57,15 +58,15 @@ void solidity::evmasm::eachInstruction( } } -string solidity::evmasm::disassemble(bytes const& _mem, string const& _delimiter) +string solidity::evmasm::disassemble(bytes const& _mem, langutil::EVMVersion _evmVersion, string const& _delimiter) { stringstream ret; - eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) { + eachInstruction(_mem, _evmVersion, [&](Instruction _instr, u256 const& _data) { if (!isValidInstruction(_instr)) ret << "0x" << std::uppercase << std::hex << static_cast(_instr) << _delimiter; else { - InstructionInfo info = instructionInfo(_instr); + InstructionInfo info = instructionInfo(_instr, _evmVersion); ret << info.name; if (info.additional) ret << " 0x" << std::uppercase << std::hex << _data; diff --git a/libevmasm/Disassemble.h b/libevmasm/Disassemble.h index cf194e24b..2e03a5d35 100644 --- a/libevmasm/Disassemble.h +++ b/libevmasm/Disassemble.h @@ -30,9 +30,9 @@ namespace solidity::evmasm { /// Iterate through EVM code and call a function on each instruction. -void eachInstruction(bytes const& _mem, std::function const& _onInstruction); +void eachInstruction(bytes const& _mem, langutil::EVMVersion _evmVersion, std::function const& _onInstruction); /// Convert from EVM code to simple EVM assembly language. -std::string disassemble(bytes const& _mem, std::string const& _delimiter = " "); +std::string disassemble(bytes const& _mem, langutil::EVMVersion _evmVersion, std::string const& _delimiter = " "); } diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index d49ee9891..258f68b31 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -52,10 +52,10 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case PushProgramSize: case PushLibraryAddress: case PushDeployTimeAddress: - gas = runGas(Instruction::PUSH1); + gas = runGas(Instruction::PUSH1, m_evmVersion); break; case Tag: - gas = runGas(Instruction::JUMPDEST); + gas = runGas(Instruction::JUMPDEST, m_evmVersion); break; case Operation: { @@ -80,19 +80,19 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ break; case Instruction::RETURN: case Instruction::REVERT: - gas = runGas(_item.instruction()); + gas = runGas(_item.instruction(), m_evmVersion); gas += memoryGas(0, -1); break; case Instruction::MLOAD: case Instruction::MSTORE: - gas = runGas(_item.instruction()); + gas = runGas(_item.instruction(), m_evmVersion); gas += memoryGas(classes.find(Instruction::ADD, { m_state->relativeStackElement(0), classes.find(AssemblyItem(32)) })); break; case Instruction::MSTORE8: - gas = runGas(_item.instruction()); + gas = runGas(_item.instruction(), m_evmVersion); gas += memoryGas(classes.find(Instruction::ADD, { m_state->relativeStackElement(0), classes.find(AssemblyItem(1)) @@ -106,7 +106,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Instruction::CALLDATACOPY: case Instruction::CODECOPY: case Instruction::RETURNDATACOPY: - gas = runGas(_item.instruction()); + gas = runGas(_item.instruction(), m_evmVersion); gas += memoryGas(0, -2); gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2)); break; @@ -195,13 +195,13 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ gas = GasCosts::balanceGas(m_evmVersion); break; case Instruction::CHAINID: - gas = runGas(Instruction::CHAINID); + gas = runGas(Instruction::CHAINID, m_evmVersion); break; case Instruction::SELFBALANCE: - gas = runGas(Instruction::SELFBALANCE); + gas = runGas(Instruction::SELFBALANCE, m_evmVersion); break; default: - gas = runGas(_item.instruction()); + gas = runGas(_item.instruction(), m_evmVersion); break; } break; @@ -252,12 +252,12 @@ GasMeter::GasConsumption GasMeter::memoryGas(int _stackPosOffset, int _stackPosS })); } -unsigned GasMeter::runGas(Instruction _instruction) +unsigned GasMeter::runGas(Instruction _instruction, langutil::EVMVersion _evmVersion) { if (_instruction == Instruction::JUMPDEST) return 1; - switch (instructionInfo(_instruction).gasPriceTier) + switch (instructionInfo(_instruction, _evmVersion).gasPriceTier) { case Tier::Zero: return GasCosts::tier0Gas; case Tier::Base: return GasCosts::tier1Gas; @@ -268,7 +268,7 @@ unsigned GasMeter::runGas(Instruction _instruction) case Tier::Ext: return GasCosts::tier6Gas; default: break; } - assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction).name); + assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction, _evmVersion).name); return 0; } diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index 306fc0208..ec1946644 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -221,7 +221,7 @@ public: /// @returns gas costs for simple instructions with constant gas costs (that do not /// change with EVM versions) - static unsigned runGas(Instruction _instruction); + static unsigned runGas(Instruction _instruction, langutil::EVMVersion _evmVersion); /// @returns the gas cost of the supplied data, depending whether it is in creation code, or not. /// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas @@ -257,5 +257,4 @@ inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption con return _str << std::dec << _consumption.value; } - } diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index e2ce48b7f..418a6e185 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -76,7 +76,8 @@ std::map const solidity::evmasm::c_instructions = { "COINBASE", Instruction::COINBASE }, { "TIMESTAMP", Instruction::TIMESTAMP }, { "NUMBER", Instruction::NUMBER }, - { "DIFFICULTY", Instruction::DIFFICULTY }, + { "DIFFICULTY", Instruction::PREVRANDAO }, + { "PREVRANDAO", Instruction::PREVRANDAO }, { "GASLIMIT", Instruction::GASLIMIT }, { "CHAINID", Instruction::CHAINID }, { "SELFBALANCE", Instruction::SELFBALANCE }, @@ -174,6 +175,7 @@ std::map const solidity::evmasm::c_instructions = { "SELFDESTRUCT", Instruction::SELFDESTRUCT } }; +/// @note InstructionInfo is assumed to be the same across all EVM versions except for the instruction name. static std::map const c_instructionInfo = { // Add, Args, Ret, SideEffects, GasPriceTier { Instruction::STOP, { "STOP", 0, 0, 0, true, Tier::Zero } }, @@ -223,7 +225,7 @@ static std::map const c_instructionInfo = { Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } }, { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } }, { Instruction::NUMBER, { "NUMBER", 0, 0, 1, false, Tier::Base } }, - { Instruction::DIFFICULTY, { "DIFFICULTY", 0, 0, 1, false, Tier::Base } }, + { Instruction::PREVRANDAO, { "PREVRANDAO", 0, 0, 1, false, Tier::Base } }, { Instruction::GASLIMIT, { "GASLIMIT", 0, 0, 1, false, Tier::Base } }, { Instruction::CHAINID, { "CHAINID", 0, 0, 1, false, Tier::Base } }, { Instruction::SELFBALANCE, { "SELFBALANCE", 0, 0, 1, false, Tier::Low } }, @@ -321,10 +323,12 @@ static std::map const c_instructionInfo = { Instruction::SELFDESTRUCT, { "SELFDESTRUCT", 0, 1, 0, true, Tier::Special } } }; -InstructionInfo solidity::evmasm::instructionInfo(Instruction _inst) +InstructionInfo solidity::evmasm::instructionInfo(Instruction _inst, langutil::EVMVersion _evmVersion) { try { + if (_inst == Instruction::PREVRANDAO && _evmVersion < langutil::EVMVersion::paris()) + return InstructionInfo({ "DIFFICULTY", 0, 0, 1, false, Tier::Base }); return c_instructionInfo.at(_inst); } catch (...) diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 5d4432ca5..48ee37b5c 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace solidity::evmasm { @@ -83,7 +84,7 @@ enum class Instruction: uint8_t COINBASE, ///< get the block's coinbase address TIMESTAMP, ///< get the block's timestamp NUMBER, ///< get the block's number - DIFFICULTY, ///< get the block's difficulty + PREVRANDAO, ///< get randomness provided by the beacon chain GASLIMIT, ///< get the block's gas limit CHAINID, ///< get the config's chainid param SELFBALANCE, ///< get balance of the current account @@ -306,7 +307,7 @@ struct InstructionInfo }; /// Information on all the instructions. -InstructionInfo instructionInfo(Instruction _inst); +InstructionInfo instructionInfo(Instruction _inst, langutil::EVMVersion _evmVersion); /// check whether instructions exists. bool isValidInstruction(Instruction _inst); diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index 1e886f7ab..a2bda5af7 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -130,7 +130,9 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool else { Instruction instruction = _item.instruction(); - InstructionInfo info = instructionInfo(instruction); + // The latest EVMVersion is used here, since the InstructionInfo is assumed to be + // the same across all EVM versions except for the instruction name. + InstructionInfo info = instructionInfo(instruction, EVMVersion()); if (SemanticInformation::isDupInstruction(_item)) setStackElement( m_stackHeight + 1, diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 334903c92..22d40c8ea 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -38,7 +38,7 @@ struct OptimiserState { AssemblyItems const& items; size_t i; - std::back_insert_iterator out; + back_insert_iterator out; }; template @@ -53,7 +53,11 @@ template struct SimplePeepholeOptimizerMethod { template - static bool applyRule(AssemblyItems::const_iterator _in, back_insert_iterator _out, index_sequence) + static bool applyRule( + AssemblyItems::const_iterator _in, + back_insert_iterator _out, + index_sequence + ) { return Method::applySimple(_in[Indices]..., _out); } @@ -75,7 +79,10 @@ struct SimplePeepholeOptimizerMethod struct Identity: SimplePeepholeOptimizerMethod { - static bool applySimple(AssemblyItem const& _item, std::back_insert_iterator _out) + static bool applySimple( + AssemblyItem const& _item, + back_insert_iterator _out + ) { *_out = _item; return true; @@ -84,7 +91,11 @@ struct Identity: SimplePeepholeOptimizerMethod struct PushPop: SimplePeepholeOptimizerMethod { - static bool applySimple(AssemblyItem const& _push, AssemblyItem const& _pop, std::back_insert_iterator) + static bool applySimple( + AssemblyItem const& _push, + AssemblyItem const& _pop, + back_insert_iterator + ) { auto t = _push.type(); return _pop == Instruction::POP && ( @@ -100,15 +111,15 @@ struct OpPop: SimplePeepholeOptimizerMethod static bool applySimple( AssemblyItem const& _op, AssemblyItem const& _pop, - std::back_insert_iterator _out + back_insert_iterator _out ) { if (_pop == Instruction::POP && _op.type() == Operation) { Instruction instr = _op.instruction(); - if (instructionInfo(instr).ret == 1 && !instructionInfo(instr).sideEffects) + if (instructionInfo(instr, langutil::EVMVersion()).ret == 1 && !instructionInfo(instr, langutil::EVMVersion()).sideEffects) { - for (int j = 0; j < instructionInfo(instr).args; j++) + for (int j = 0; j < instructionInfo(instr, langutil::EVMVersion()).args; j++) *_out = {Instruction::POP, _op.location()}; return true; } @@ -122,7 +133,7 @@ struct OpStop: SimplePeepholeOptimizerMethod static bool applySimple( AssemblyItem const& _op, AssemblyItem const& _stop, - std::back_insert_iterator _out + back_insert_iterator _out ) { if (_stop == Instruction::STOP) @@ -130,7 +141,7 @@ struct OpStop: SimplePeepholeOptimizerMethod if (_op.type() == Operation) { Instruction instr = _op.instruction(); - if (!instructionInfo(instr).sideEffects) + if (!instructionInfo(instr, langutil::EVMVersion()).sideEffects) { *_out = {Instruction::STOP, _op.location()}; return true; @@ -153,7 +164,7 @@ struct OpReturnRevert: SimplePeepholeOptimizerMethod AssemblyItem const& _push, AssemblyItem const& _pushOrDup, AssemblyItem const& _returnRevert, - std::back_insert_iterator _out + back_insert_iterator _out ) { if ( @@ -162,7 +173,7 @@ struct OpReturnRevert: SimplePeepholeOptimizerMethod (_pushOrDup.type() == Push || _pushOrDup == dupInstruction(1)) ) if ( - (_op.type() == Operation && !instructionInfo(_op.instruction()).sideEffects) || + (_op.type() == Operation && !instructionInfo(_op.instruction(), langutil::EVMVersion()).sideEffects) || _op.type() == Push ) { @@ -177,7 +188,11 @@ struct OpReturnRevert: SimplePeepholeOptimizerMethod struct DoubleSwap: SimplePeepholeOptimizerMethod { - static size_t applySimple(AssemblyItem const& _s1, AssemblyItem const& _s2, std::back_insert_iterator) + static size_t applySimple( + AssemblyItem const& _s1, + AssemblyItem const& _s2, + back_insert_iterator + ) { return _s1 == _s2 && SemanticInformation::isSwapInstruction(_s1); } @@ -185,7 +200,11 @@ struct DoubleSwap: SimplePeepholeOptimizerMethod struct DoublePush: SimplePeepholeOptimizerMethod { - static bool applySimple(AssemblyItem const& _push1, AssemblyItem const& _push2, std::back_insert_iterator _out) + static bool applySimple( + AssemblyItem const& _push1, + AssemblyItem const& _push2, + back_insert_iterator _out + ) { if (_push1.type() == Push && _push2.type() == Push && _push1.data() == _push2.data()) { @@ -200,7 +219,11 @@ struct DoublePush: SimplePeepholeOptimizerMethod struct CommutativeSwap: SimplePeepholeOptimizerMethod { - static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator _out) + static bool applySimple( + AssemblyItem const& _swap, + AssemblyItem const& _op, + back_insert_iterator _out + ) { // Remove SWAP1 if following instruction is commutative if ( @@ -218,7 +241,11 @@ struct CommutativeSwap: SimplePeepholeOptimizerMethod struct SwapComparison: SimplePeepholeOptimizerMethod { - static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator _out) + static bool applySimple( + AssemblyItem const& _swap, + AssemblyItem const& _op, + back_insert_iterator _out + ) { static map const swappableOps{ { Instruction::LT, Instruction::GT }, @@ -247,7 +274,7 @@ struct DupSwap: SimplePeepholeOptimizerMethod static size_t applySimple( AssemblyItem const& _dupN, AssemblyItem const& _swapN, - std::back_insert_iterator _out + back_insert_iterator _out ) { if ( @@ -272,7 +299,7 @@ struct IsZeroIsZeroJumpI: SimplePeepholeOptimizerMethod AssemblyItem const& _iszero2, AssemblyItem const& _pushTag, AssemblyItem const& _jumpi, - std::back_insert_iterator _out + back_insert_iterator _out ) { if ( @@ -298,7 +325,7 @@ struct EqIsZeroJumpI: SimplePeepholeOptimizerMethod AssemblyItem const& _iszero, AssemblyItem const& _pushTag, AssemblyItem const& _jumpi, - std::back_insert_iterator _out + back_insert_iterator _out ) { if ( @@ -327,7 +354,7 @@ struct DoubleJump: SimplePeepholeOptimizerMethod AssemblyItem const& _pushTag2, AssemblyItem const& _jump, AssemblyItem const& _tag1, - std::back_insert_iterator _out + back_insert_iterator _out ) { if ( @@ -356,7 +383,7 @@ struct JumpToNext: SimplePeepholeOptimizerMethod AssemblyItem const& _pushTag, AssemblyItem const& _jump, AssemblyItem const& _tag, - std::back_insert_iterator _out + back_insert_iterator _out ) { if ( @@ -382,7 +409,7 @@ struct TagConjunctions: SimplePeepholeOptimizerMethod AssemblyItem const& _pushTag, AssemblyItem const& _pushConstant, AssemblyItem const& _and, - std::back_insert_iterator _out + back_insert_iterator _out ) { if (_and != Instruction::AND) @@ -417,7 +444,7 @@ struct TruthyAnd: SimplePeepholeOptimizerMethod AssemblyItem const& _push, AssemblyItem const& _not, AssemblyItem const& _and, - std::back_insert_iterator + back_insert_iterator ) { return ( @@ -484,7 +511,7 @@ bool PeepholeOptimiser::optimise() { // Avoid referencing immutables too early by using approx. counting in bytesRequired() auto const approx = evmasm::Precision::Approximate; - OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)}; + OptimiserState state {m_items, 0, back_inserter(m_optimisedItems)}; while (state.i < m_items.size()) applyMethods( state, diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index acbcaa5c0..b6bcc8280 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -110,7 +110,7 @@ vector SemanticInformation::readWriteOperations( case Instruction::CALLCODE: case Instruction::DELEGATECALL: { - size_t paramCount = static_cast(instructionInfo(_instruction).args); + size_t paramCount = static_cast(instructionInfo(_instruction, langutil::EVMVersion()).args); vector operations{ Operation{Location::Memory, Effect::Read, paramCount - 4, paramCount - 3, {}}, Operation{Location::Storage, Effect::Read, {}, {}, {}} @@ -178,7 +178,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool return true; // GAS and PC assume a specific order of opcodes if (_item.instruction() == Instruction::MSIZE) return true; // msize is modified already by memory access, avoid that for now - InstructionInfo info = instructionInfo(_item.instruction()); + InstructionInfo info = instructionInfo(_item.instruction(), langutil::EVMVersion()); if (_item.instruction() == Instruction::SSTORE) return false; if (_item.instruction() == Instruction::MSTORE) @@ -318,7 +318,7 @@ bool SemanticInformation::movable(Instruction _instruction) // These are not really functional. if (isDupInstruction(_instruction) || isSwapInstruction(_instruction)) return false; - InstructionInfo info = instructionInfo(_instruction); + InstructionInfo info = instructionInfo(_instruction, langutil::EVMVersion()); if (info.sideEffects) return false; switch (_instruction) @@ -345,7 +345,7 @@ bool SemanticInformation::canBeRemoved(Instruction _instruction) // These are not really functional. assertThrow(!isDupInstruction(_instruction) && !isSwapInstruction(_instruction), AssemblyException, ""); - return !instructionInfo(_instruction).sideEffects; + return !instructionInfo(_instruction, langutil::EVMVersion()).sideEffects; } bool SemanticInformation::canBeRemovedIfNoMSize(Instruction _instruction) @@ -483,7 +483,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) case Instruction::COINBASE: case Instruction::TIMESTAMP: case Instruction::NUMBER: - case Instruction::DIFFICULTY: + case Instruction::PREVRANDAO: case Instruction::GASLIMIT: case Instruction::STATICCALL: case Instruction::SLOAD: diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h index d548a00df..e970068fb 100644 --- a/libevmasm/SimplificationRule.h +++ b/libevmasm/SimplificationRule.h @@ -122,7 +122,7 @@ struct EVMBuiltins static auto constexpr COINBASE = PatternGenerator{}; static auto constexpr TIMESTAMP = PatternGenerator{}; static auto constexpr NUMBER = PatternGenerator{}; - static auto constexpr DIFFICULTY = PatternGenerator{}; + static auto constexpr PREVRANDAO = PatternGenerator{}; static auto constexpr GASLIMIT = PatternGenerator{}; static auto constexpr CHAINID = PatternGenerator{}; static auto constexpr SELFBALANCE = PatternGenerator{}; diff --git a/libevmasm/SimplificationRules.cpp b/libevmasm/SimplificationRules.cpp index a21ed4195..bcb8da388 100644 --- a/libevmasm/SimplificationRules.cpp +++ b/libevmasm/SimplificationRules.cpp @@ -141,7 +141,8 @@ string Pattern::toString() const switch (m_type) { case Operation: - s << instructionInfo(m_instruction).name; + // Note: This function is exclusively used for debugging. + s << instructionInfo(m_instruction, EVMVersion()).name; break; case Push: if (m_data) diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index d36633845..f985b4aff 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -96,6 +96,7 @@ public: bool hasChainID() const { return *this >= istanbul(); } bool hasSelfBalance() const { return *this >= istanbul(); } bool hasBaseFee() const { return *this >= london(); } + bool hasPrevRandao() const { return *this >= paris(); } bool hasOpcode(evmasm::Instruction _opcode) const; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 00c763a90..ac00ab7fe 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3296,18 +3296,33 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) (memberName == "min" || memberName == "max") ) annotation.isPure = true; - else if (magicType->kind() == MagicType::Kind::Block && memberName == "chainid" && !m_evmVersion.hasChainID()) - m_errorReporter.typeError( - 3081_error, - _memberAccess.location(), - "\"chainid\" is not supported by the VM version." - ); - else if (magicType->kind() == MagicType::Kind::Block && memberName == "basefee" && !m_evmVersion.hasBaseFee()) - m_errorReporter.typeError( - 5921_error, - _memberAccess.location(), - "\"basefee\" is not supported by the VM version." - ); + else if (magicType->kind() == MagicType::Kind::Block) + { + if (memberName == "chainid" && !m_evmVersion.hasChainID()) + m_errorReporter.typeError( + 3081_error, + _memberAccess.location(), + "\"chainid\" is not supported by the VM version." + ); + else if (memberName == "basefee" && !m_evmVersion.hasBaseFee()) + m_errorReporter.typeError( + 5921_error, + _memberAccess.location(), + "\"basefee\" is not supported by the VM version." + ); + else if (memberName == "prevrandao" && !m_evmVersion.hasPrevRandao()) + m_errorReporter.warning( + 9432_error, + _memberAccess.location(), + "\"prevrandao\" is not supported by the VM version and will be treated as \"difficulty\"." + ); + else if (memberName == "difficulty" && m_evmVersion.hasPrevRandao()) + m_errorReporter.warning( + 8417_error, + _memberAccess.location(), + "Since the VM version paris, \"difficulty\" was replaced by \"prevrandao\", which now returns a random number based on the beacon chain." + ); + } } if ( diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 9bbd70a67..0a148f2fb 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -4067,6 +4067,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const {"timestamp", TypeProvider::uint256()}, {"blockhash", TypeProvider::function(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, StateMutability::View)}, {"difficulty", TypeProvider::uint256()}, + {"prevrandao", TypeProvider::uint256()}, {"number", TypeProvider::uint256()}, {"gaslimit", TypeProvider::uint256()}, {"chainid", TypeProvider::uint256()}, diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 4f7fd3abd..8eba084eb 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -65,7 +65,7 @@ public: RevertStrings _revertStrings, CompilerContext* _runtimeContext = nullptr ): - m_asm(std::make_shared(_runtimeContext != nullptr, std::string{})), + m_asm(std::make_shared(_evmVersion, _runtimeContext != nullptr, std::string{})), m_evmVersion(_evmVersion), m_revertStrings(_revertStrings), m_reservedMemory{0}, diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index b26825c77..2aa2315f9 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1808,8 +1808,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) m_context << Instruction::COINBASE; else if (member == "timestamp") m_context << Instruction::TIMESTAMP; - else if (member == "difficulty") - m_context << Instruction::DIFFICULTY; + else if (member == "difficulty" || member == "prevrandao") + m_context << Instruction::PREVRANDAO; else if (member == "number") m_context << Instruction::NUMBER; else if (member == "gaslimit") diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 1ec64b548..3e3041bc1 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1822,8 +1822,13 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) define(_memberAccess) << "coinbase()\n"; else if (member == "timestamp") define(_memberAccess) << "timestamp()\n"; - else if (member == "difficulty") - define(_memberAccess) << "difficulty()\n"; + else if (member == "difficulty" || member == "prevrandao") + { + if (m_context.evmVersion().hasPrevRandao()) + define(_memberAccess) << "prevrandao()\n"; + else + define(_memberAccess) << "difficulty()\n"; + } else if (member == "number") define(_memberAccess) << "number()\n"; else if (member == "gaslimit") diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 823c7c2f2..27fa1c989 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -381,6 +381,7 @@ Json::Value formatImmutableReferences(map>> co } Json::Value collectEVMObject( + langutil::EVMVersion _evmVersion, evmasm::LinkerObject const& _object, string const* _sourceMap, Json::Value _generatedSources, @@ -392,7 +393,7 @@ Json::Value collectEVMObject( if (_artifactRequested("object")) output["object"] = _object.toHex(); if (_artifactRequested("opcodes")) - output["opcodes"] = evmasm::disassemble(_object.bytecode); + output["opcodes"] = evmasm::disassemble(_object.bytecode, _evmVersion); if (_artifactRequested("sourceMap")) output["sourceMap"] = _sourceMap ? *_sourceMap : ""; if (_artifactRequested("functionDebugData")) @@ -1328,6 +1329,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting wildcardMatchesExperimental )) evmData["bytecode"] = collectEVMObject( + _inputsAndSettings.evmVersion, compilerStack.object(contractName), compilerStack.sourceMapping(contractName), compilerStack.generatedSources(contractName), @@ -1349,6 +1351,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting wildcardMatchesExperimental )) evmData["deployedBytecode"] = collectEVMObject( + _inputsAndSettings.evmVersion, compilerStack.runtimeObject(contractName), compilerStack.runtimeSourceMapping(contractName), compilerStack.generatedSources(contractName, true), @@ -1491,6 +1494,7 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) if (o.bytecode) output["contracts"][sourceName][contractName]["evm"][kind] = collectEVMObject( + _inputsAndSettings.evmVersion, *o.bytecode, o.sourceMappings.get(), Json::arrayValue, diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 2e9f8f1d3..4c9abdadd 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -119,7 +119,6 @@ vector AsmAnalyzer::operator()(Literal const& _literal) "Invalid type \"" + _literal.type.str() + "\" for literal \"" + _literal.value.str() + "\"." ); - return {_literal.type}; } @@ -619,7 +618,6 @@ Scope& AsmAnalyzer::scope(Block const* _block) void AsmAnalyzer::expectValidIdentifier(YulString _identifier, SourceLocation const& _location) { // NOTE: the leading dot case is handled by the parser not allowing it. - if (boost::ends_with(_identifier.str(), ".")) m_errorReporter.syntaxError( 3384_error, @@ -692,7 +690,7 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio _location, fmt::format( "The \"{instruction}\" instruction is {kind} VMs (you are currently compiling for \"{version}\").", - fmt::arg("instruction", boost::to_lower_copy(instructionInfo(_instr).name)), + fmt::arg("instruction", boost::to_lower_copy(instructionInfo(_instr, m_evmVersion).name)), fmt::arg("kind", vmKindMessage), fmt::arg("version", m_evmVersion.name()) ) diff --git a/libyul/CompilabilityChecker.cpp b/libyul/CompilabilityChecker.cpp index 270773cbc..acb48cfa5 100644 --- a/libyul/CompilabilityChecker.cpp +++ b/libyul/CompilabilityChecker.cpp @@ -50,7 +50,7 @@ CompilabilityChecker::CompilabilityChecker( builtinContext.subIDs[_object.name] = 1; for (auto const& subNode: _object.subObjects) builtinContext.subIDs[subNode->name] = 1; - NoOutputAssembly assembly; + NoOutputAssembly assembly{evmDialect->evmVersion()}; CodeTransform transform( assembly, analysisInfo, diff --git a/libyul/YulStack.cpp b/libyul/YulStack.cpp index b33026949..f258de6a8 100644 --- a/libyul/YulStack.cpp +++ b/libyul/YulStack.cpp @@ -264,7 +264,7 @@ YulStack::assembleEVMWithDeployed(optional _deployName) const yulAssert(m_parserResult->code, ""); yulAssert(m_parserResult->analysisInfo, ""); - evmasm::Assembly assembly(true, {}); + evmasm::Assembly assembly(m_evmVersion, true, {}); EthAssemblyAdapter adapter(assembly); compileEVM(adapter, m_optimiserSettings.optimizeStackAllocation); diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index 2d7be9001..c64669abe 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 71b9dba4c..b66738b9d 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -47,11 +47,12 @@ namespace { pair createEVMFunction( + langutil::EVMVersion _evmVersion, string const& _name, evmasm::Instruction _instruction ) { - evmasm::InstructionInfo info = evmasm::instructionInfo(_instruction); + evmasm::InstructionInfo info = evmasm::instructionInfo(_instruction, _evmVersion); BuiltinFunctionForEVM f; f.name = YulString{_name}; f.parameters.resize(static_cast(info.args)); @@ -119,11 +120,19 @@ set createReservedIdentifiers(langutil::EVMVersion _evmVersion) return _instr == evmasm::Instruction::BASEFEE && _evmVersion < langutil::EVMVersion::london(); }; + // TODO remove this in 0.9.0. We allow creating functions or identifiers in Yul with the name + // prevrandao for VMs before paris. + auto prevRandaoException = [&](string const& _instrName) -> bool + { + // Using string comparison as the opcode is the same as for "difficulty" + return _instrName == "prevrandao" && _evmVersion < langutil::EVMVersion::paris(); + }; + set reserved; for (auto const& instr: evmasm::c_instructions) { string name = toLower(instr.first); - if (!baseFeeException(instr.second)) + if (!baseFeeException(instr.second) && !prevRandaoException(name)) reserved.emplace(name); } reserved += vector{ @@ -139,6 +148,13 @@ set createReservedIdentifiers(langutil::EVMVersion _evmVersion) map createBuiltins(langutil::EVMVersion _evmVersion, bool _objectAccess) { + + // Exclude prevrandao as builtin for VMs before paris and difficulty for VMs after paris. + auto prevRandaoException = [&](string const& _instrName) -> bool + { + return (_instrName == "prevrandao" && _evmVersion < langutil::EVMVersion::paris()) || (_instrName == "difficulty" && _evmVersion >= langutil::EVMVersion::paris()); + }; + map builtins; for (auto const& instr: evmasm::c_instructions) { @@ -152,9 +168,10 @@ map createBuiltins(langutil::EVMVersion _evmVe opcode != evmasm::Instruction::JUMP && opcode != evmasm::Instruction::JUMPI && opcode != evmasm::Instruction::JUMPDEST && - _evmVersion.hasOpcode(opcode) + _evmVersion.hasOpcode(opcode) && + !prevRandaoException(name) ) - builtins.emplace(createEVMFunction(name, opcode)); + builtins.emplace(createEVMFunction(_evmVersion, name, opcode)); } if (_objectAccess) diff --git a/libyul/backends/evm/EVMMetrics.cpp b/libyul/backends/evm/EVMMetrics.cpp index bd39f966f..095969838 100644 --- a/libyul/backends/evm/EVMMetrics.cpp +++ b/libyul/backends/evm/EVMMetrics.cpp @@ -88,7 +88,7 @@ void GasMeterVisitor::operator()(FunctionCall const& _funCall) void GasMeterVisitor::operator()(Literal const& _lit) { - m_runGas += evmasm::GasMeter::runGas(evmasm::Instruction::PUSH1); + m_runGas += evmasm::GasMeter::runGas(evmasm::Instruction::PUSH1, m_dialect.evmVersion()); m_dataGas += singleByteDataGas() + evmasm::GasMeter::dataGas( @@ -100,7 +100,7 @@ void GasMeterVisitor::operator()(Literal const& _lit) void GasMeterVisitor::operator()(Identifier const&) { - m_runGas += evmasm::GasMeter::runGas(evmasm::Instruction::DUP1); + m_runGas += evmasm::GasMeter::runGas(evmasm::Instruction::DUP1, m_dialect.evmVersion()); m_dataGas += singleByteDataGas(); } @@ -120,6 +120,6 @@ void GasMeterVisitor::instructionCostsInternal(evmasm::Instruction _instruction) // Assumes that Keccak-256 is computed on a single word (rounded up). m_runGas += evmasm::GasCosts::keccak256Gas + evmasm::GasCosts::keccak256WordGas; else - m_runGas += evmasm::GasMeter::runGas(_instruction); + m_runGas += evmasm::GasMeter::runGas(_instruction, m_dialect.evmVersion()); m_dataGas += singleByteDataGas(); } diff --git a/libyul/backends/evm/EthAssemblyAdapter.cpp b/libyul/backends/evm/EthAssemblyAdapter.cpp index 685f56cbc..28a4e4ae2 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.cpp +++ b/libyul/backends/evm/EthAssemblyAdapter.cpp @@ -124,7 +124,7 @@ void EthAssemblyAdapter::appendAssemblySize() pair, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly(bool _creation, string _name) { - shared_ptr assembly{make_shared(_creation, std::move(_name))}; + shared_ptr assembly{make_shared(m_assembly.evmVersion(), _creation, std::move(_name))}; auto sub = m_assembly.newSub(assembly); return {make_shared(*assembly), static_cast(sub.data())}; } diff --git a/libyul/backends/evm/NoOutputAssembly.cpp b/libyul/backends/evm/NoOutputAssembly.cpp index e26204201..ac64418ad 100644 --- a/libyul/backends/evm/NoOutputAssembly.cpp +++ b/libyul/backends/evm/NoOutputAssembly.cpp @@ -37,7 +37,7 @@ using namespace solidity::langutil; void NoOutputAssembly::appendInstruction(evmasm::Instruction _instr) { - m_stackHeight += instructionInfo(_instr).ret - instructionInfo(_instr).args; + m_stackHeight += instructionInfo(_instr, m_evmVersion).ret - instructionInfo(_instr, m_evmVersion).args; } void NoOutputAssembly::appendConstant(u256 const&) diff --git a/libyul/backends/evm/NoOutputAssembly.h b/libyul/backends/evm/NoOutputAssembly.h index 1103392ef..eeb00039c 100644 --- a/libyul/backends/evm/NoOutputAssembly.h +++ b/libyul/backends/evm/NoOutputAssembly.h @@ -45,7 +45,7 @@ namespace solidity::yul class NoOutputAssembly: public AbstractAssembly { public: - explicit NoOutputAssembly() { } + explicit NoOutputAssembly(langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) { } ~NoOutputAssembly() override = default; void setSourceLocation(langutil::SourceLocation const&) override {} @@ -79,6 +79,7 @@ public: private: int m_stackHeight = 0; + langutil::EVMVersion m_evmVersion; }; diff --git a/libyul/backends/evm/StackLayoutGenerator.cpp b/libyul/backends/evm/StackLayoutGenerator.cpp index 95c840733..ddb1b6379 100644 --- a/libyul/backends/evm/StackLayoutGenerator.cpp +++ b/libyul/backends/evm/StackLayoutGenerator.cpp @@ -747,23 +747,23 @@ void StackLayoutGenerator::fillInJunk(CFG::BasicBlock const& _block, CFG::Functi if (_swapDepth > 16) opGas += 1000; else - opGas += evmasm::GasMeter::runGas(evmasm::swapInstruction(_swapDepth)); + opGas += evmasm::GasMeter::runGas(evmasm::swapInstruction(_swapDepth), langutil::EVMVersion()); }; auto dupOrPush = [&](StackSlot const& _slot) { if (canBeFreelyGenerated(_slot)) - opGas += evmasm::GasMeter::runGas(evmasm::pushInstruction(32)); + opGas += evmasm::GasMeter::runGas(evmasm::pushInstruction(32), langutil::EVMVersion()); else { auto depth = util::findOffset(_source | ranges::views::reverse, _slot); yulAssert(depth); if (*depth < 16) - opGas += evmasm::GasMeter::runGas(evmasm::dupInstruction(static_cast(*depth + 1))); + opGas += evmasm::GasMeter::runGas(evmasm::dupInstruction(static_cast(*depth + 1)), langutil::EVMVersion()); else opGas += 1000; } }; - auto pop = [&]() { opGas += evmasm::GasMeter::runGas(evmasm::Instruction::POP); }; + auto pop = [&]() { opGas += evmasm::GasMeter::runGas(evmasm::Instruction::POP,langutil::EVMVersion()); }; createStackLayout(_source, _target, swap, dupOrPush, pop); return opGas; }; diff --git a/libyul/optimiser/ExpressionSimplifier.cpp b/libyul/optimiser/ExpressionSimplifier.cpp index caf74cfb7..4a423fe46 100644 --- a/libyul/optimiser/ExpressionSimplifier.cpp +++ b/libyul/optimiser/ExpressionSimplifier.cpp @@ -47,7 +47,7 @@ void ExpressionSimplifier::visit(Expression& _expression) m_dialect, [this](YulString _var) { return variableValue(_var); } )) - _expression = match->action().toExpression(debugDataOf(_expression)); + _expression = match->action().toExpression(debugDataOf(_expression), evmVersionFromDialect(m_dialect)); if (auto* functionCall = get_if(&_expression)) if (optional instruction = toEVMInstruction(m_dialect, functionCall->functionName.name)) diff --git a/libyul/optimiser/KnowledgeBase.cpp b/libyul/optimiser/KnowledgeBase.cpp index 77848cce0..c706d529b 100644 --- a/libyul/optimiser/KnowledgeBase.cpp +++ b/libyul/optimiser/KnowledgeBase.cpp @@ -102,7 +102,7 @@ Expression KnowledgeBase::simplifyRecursively(Expression _expression) arg = simplifyRecursively(arg); if (auto match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_variableValues)) - return simplifyRecursively(match->action().toExpression(debugDataOf(_expression))); + return simplifyRecursively(match->action().toExpression(debugDataOf(_expression), langutil::EVMVersion())); return _expression; } diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp index 298dfe9c1..2eac51595 100644 --- a/libyul/optimiser/Metrics.cpp +++ b/libyul/optimiser/Metrics.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -180,7 +181,7 @@ void CodeCost::visit(Expression const& _expression) void CodeCost::addInstructionCost(evmasm::Instruction _instruction) { - evmasm::Tier gasPriceTier = evmasm::instructionInfo(_instruction).gasPriceTier; + evmasm::Tier gasPriceTier = evmasm::instructionInfo(_instruction, evmVersionFromDialect(m_dialect)).gasPriceTier; if (gasPriceTier < evmasm::Tier::VeryLow) m_cost -= 1; else if (gasPriceTier < evmasm::Tier::High) diff --git a/libyul/optimiser/OptimizerUtilities.cpp b/libyul/optimiser/OptimizerUtilities.cpp index ba06b2180..bd474f748 100644 --- a/libyul/optimiser/OptimizerUtilities.cpp +++ b/libyul/optimiser/OptimizerUtilities.cpp @@ -58,6 +58,13 @@ optional yul::toEVMInstruction(Dialect const& _dialect, Yul return nullopt; } +langutil::EVMVersion const yul::evmVersionFromDialect(Dialect const& _dialect) +{ + if (auto const* dialect = dynamic_cast(&_dialect)) + return dialect->evmVersion(); + return langutil::EVMVersion(); +} + void StatementRemover::operator()(Block& _block) { util::iterateReplacing( diff --git a/libyul/optimiser/OptimizerUtilities.h b/libyul/optimiser/OptimizerUtilities.h index b491e57e1..cb07f7a53 100644 --- a/libyul/optimiser/OptimizerUtilities.h +++ b/libyul/optimiser/OptimizerUtilities.h @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -49,6 +50,10 @@ bool isRestrictedIdentifier(Dialect const& _dialect, YulString const& _identifie /// Helper function that returns the instruction, if the `_name` is a BuiltinFunction std::optional toEVMInstruction(Dialect const& _dialect, YulString const& _name); +/// Helper function that returns the EVM version from a dialect. +/// It returns the default EVM version if dialect is not an EVMDialect. +langutil::EVMVersion const evmVersionFromDialect(Dialect const& _dialect); + class StatementRemover: public ASTModifier { public: diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 9d0144750..c3c4d5a53 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -235,7 +235,7 @@ evmasm::Instruction Pattern::instruction() const return m_instruction; } -Expression Pattern::toExpression(shared_ptr const& _debugData) const +Expression Pattern::toExpression(shared_ptr const& _debugData, langutil::EVMVersion _evmVersion) const { if (matchGroup()) return ASTCopier().translate(matchGroupValue()); @@ -248,9 +248,9 @@ Expression Pattern::toExpression(shared_ptr const& _debugData) { vector arguments; for (auto const& arg: m_arguments) - arguments.emplace_back(arg.toExpression(_debugData)); + arguments.emplace_back(arg.toExpression(_debugData, _evmVersion)); - string name = util::toLower(instructionInfo(m_instruction).name); + string name = util::toLower(instructionInfo(m_instruction, _evmVersion).name); return FunctionCall{_debugData, Identifier{_debugData, YulString{name}}, diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index 7444c47fb..e36730f05 100644 --- a/libyul/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -131,7 +131,7 @@ public: /// Turns this pattern into an actual expression. Should only be called /// for patterns resulting from an action, i.e. with match groups assigned. - Expression toExpression(std::shared_ptr const& _debugData) const; + Expression toExpression(std::shared_ptr const& _debugData, langutil::EVMVersion _evmVersion) const; private: Expression const& matchGroupValue() const; diff --git a/scripts/check_style.sh b/scripts/check_style.sh index d1ad6bb9e..e3dbe6cb6 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -5,9 +5,10 @@ set -eu ERROR_LOG="$(mktemp -t check_style_XXXXXX.log)" EXCLUDE_FILES=( + # The line below is left unquoted to allow the shell globbing path expansion + test/cmdlineTests/*/{err,output} "libsolutil/picosha2.h" "test/cmdlineTests/strict_asm_only_cr/input.yul" - "test/cmdlineTests/strict_asm_only_cr/err" "test/libsolutil/UTF8.cpp" "test/libsolidity/syntaxTests/license/license_cr_endings.sol" "test/libsolidity/syntaxTests/license/license_crlf_endings.sol" diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 5745f01be..3394534f8 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -121,6 +121,9 @@ done < <( grep -v -E 'revertStatement/non_called.sol' | # Skipping a test with "let basefee := ..." grep -v -E 'inlineAssembly/basefee_berlin_function.sol' | + # Skipping tests with "let prevrandao := ..." + grep -v -E 'inlineAssembly/prevrandao_allowed_function_pre_paris.sol' | + grep -v -E 'inlineAssembly/prevrandao_disallowed_function_post_paris.sol' | # Skipping license error, unrelated to the grammar grep -v -E 'license/license_double5.sol' | grep -v -E 'license/license_hidden_unicode.sol' | diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 1d64fa494..e9688cda3 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -198,11 +198,11 @@ void CommandLineInterface::handleOpcode(string const& _contract) solAssert(CompilerInputModes.count(m_options.input.mode) == 1); if (!m_options.output.dir.empty()) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode)); + createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode, m_options.output.evmVersion)); else { sout() << "Opcodes:" << endl; - sout() << std::uppercase << evmasm::disassemble(m_compiler->object(_contract).bytecode); + sout() << std::uppercase << evmasm::disassemble(m_compiler->object(_contract).bytecode, m_options.output.evmVersion); sout() << endl; } } @@ -825,7 +825,7 @@ void CommandLineInterface::handleCombinedJSON() if (m_options.compiler.combinedJsonRequests->binaryRuntime && m_compiler->compilationSuccessful()) contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex(); if (m_options.compiler.combinedJsonRequests->opcodes && m_compiler->compilationSuccessful()) - contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode); + contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode, m_options.output.evmVersion); if (m_options.compiler.combinedJsonRequests->asm_ && m_compiler->compilationSuccessful()) contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); if (m_options.compiler.combinedJsonRequests->storageLayout && m_compiler->compilationSuccessful()) diff --git a/test/cmdlineTests/evmasm_difficulty_post_paris/args b/test/cmdlineTests/evmasm_difficulty_post_paris/args new file mode 100644 index 000000000..a0588fcfc --- /dev/null +++ b/test/cmdlineTests/evmasm_difficulty_post_paris/args @@ -0,0 +1 @@ +--no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none --evm-version paris diff --git a/test/cmdlineTests/evmasm_difficulty_post_paris/err b/test/cmdlineTests/evmasm_difficulty_post_paris/err new file mode 100644 index 000000000..d8210184e --- /dev/null +++ b/test/cmdlineTests/evmasm_difficulty_post_paris/err @@ -0,0 +1,5 @@ +Warning: Since the VM version paris, "difficulty" was replaced by "prevrandao", which now returns a random number based on the beacon chain. + --> evmasm_difficulty_post_paris/input.sol:6:21: + | +6 | uint256 x = block.difficulty; + | ^^^^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/evmasm_difficulty_post_paris/input.sol b/test/cmdlineTests/evmasm_difficulty_post_paris/input.sol new file mode 100644 index 000000000..e81c034e7 --- /dev/null +++ b/test/cmdlineTests/evmasm_difficulty_post_paris/input.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract C { + constructor() payable { + uint256 x = block.difficulty; + assembly { + sstore(0, x) + stop() + } + } + + fallback() external payable { + assembly { + stop() + } + } +} diff --git a/test/cmdlineTests/evmasm_difficulty_post_paris/output b/test/cmdlineTests/evmasm_difficulty_post_paris/output new file mode 100644 index 000000000..f8c1611e0 --- /dev/null +++ b/test/cmdlineTests/evmasm_difficulty_post_paris/output @@ -0,0 +1,13 @@ + +======= evmasm_difficulty_post_paris/input.sol:C ======= +EVM assembly: + sstore(0x00, prevrandao) + stop +stop + +sub_0: assembly { + stop +} + +Opcodes: +PREVRANDAO PUSH1 0x0 SSTORE STOP INVALID diff --git a/test/cmdlineTests/evmasm_difficulty_pre_paris/args b/test/cmdlineTests/evmasm_difficulty_pre_paris/args new file mode 100644 index 000000000..67d6f9a65 --- /dev/null +++ b/test/cmdlineTests/evmasm_difficulty_pre_paris/args @@ -0,0 +1 @@ +--no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none --evm-version london diff --git a/test/cmdlineTests/evmasm_difficulty_pre_paris/input.sol b/test/cmdlineTests/evmasm_difficulty_pre_paris/input.sol new file mode 100644 index 000000000..e81c034e7 --- /dev/null +++ b/test/cmdlineTests/evmasm_difficulty_pre_paris/input.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract C { + constructor() payable { + uint256 x = block.difficulty; + assembly { + sstore(0, x) + stop() + } + } + + fallback() external payable { + assembly { + stop() + } + } +} diff --git a/test/cmdlineTests/evmasm_difficulty_pre_paris/output b/test/cmdlineTests/evmasm_difficulty_pre_paris/output new file mode 100644 index 000000000..ad75355f5 --- /dev/null +++ b/test/cmdlineTests/evmasm_difficulty_pre_paris/output @@ -0,0 +1,13 @@ + +======= evmasm_difficulty_pre_paris/input.sol:C ======= +EVM assembly: + sstore(0x00, difficulty) + stop +stop + +sub_0: assembly { + stop +} + +Opcodes: +DIFFICULTY PUSH1 0x0 SSTORE STOP INVALID diff --git a/test/cmdlineTests/evmasm_prevrandao_post_paris/args b/test/cmdlineTests/evmasm_prevrandao_post_paris/args new file mode 100644 index 000000000..a0588fcfc --- /dev/null +++ b/test/cmdlineTests/evmasm_prevrandao_post_paris/args @@ -0,0 +1 @@ +--no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none --evm-version paris diff --git a/test/cmdlineTests/evmasm_prevrandao_post_paris/input.sol b/test/cmdlineTests/evmasm_prevrandao_post_paris/input.sol new file mode 100644 index 000000000..707d12d74 --- /dev/null +++ b/test/cmdlineTests/evmasm_prevrandao_post_paris/input.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract C { + constructor() payable { + uint256 x = block.prevrandao; + assembly { + sstore(0, x) + stop() + } + } + + fallback() external payable { + assembly { + stop() + } + } +} diff --git a/test/cmdlineTests/evmasm_prevrandao_post_paris/output b/test/cmdlineTests/evmasm_prevrandao_post_paris/output new file mode 100644 index 000000000..47c85bb50 --- /dev/null +++ b/test/cmdlineTests/evmasm_prevrandao_post_paris/output @@ -0,0 +1,13 @@ + +======= evmasm_prevrandao_post_paris/input.sol:C ======= +EVM assembly: + sstore(0x00, prevrandao) + stop +stop + +sub_0: assembly { + stop +} + +Opcodes: +PREVRANDAO PUSH1 0x0 SSTORE STOP INVALID diff --git a/test/cmdlineTests/evmasm_prevrandao_pre_paris/args b/test/cmdlineTests/evmasm_prevrandao_pre_paris/args new file mode 100644 index 000000000..67d6f9a65 --- /dev/null +++ b/test/cmdlineTests/evmasm_prevrandao_pre_paris/args @@ -0,0 +1 @@ +--no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none --evm-version london diff --git a/test/cmdlineTests/evmasm_prevrandao_pre_paris/err b/test/cmdlineTests/evmasm_prevrandao_pre_paris/err new file mode 100644 index 000000000..5970ec202 --- /dev/null +++ b/test/cmdlineTests/evmasm_prevrandao_pre_paris/err @@ -0,0 +1,5 @@ +Warning: "prevrandao" is not supported by the VM version and will be treated as "difficulty". + --> evmasm_prevrandao_pre_paris/input.sol:6:21: + | +6 | uint256 x = block.prevrandao; + | ^^^^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/evmasm_prevrandao_pre_paris/input.sol b/test/cmdlineTests/evmasm_prevrandao_pre_paris/input.sol new file mode 100644 index 000000000..707d12d74 --- /dev/null +++ b/test/cmdlineTests/evmasm_prevrandao_pre_paris/input.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract C { + constructor() payable { + uint256 x = block.prevrandao; + assembly { + sstore(0, x) + stop() + } + } + + fallback() external payable { + assembly { + stop() + } + } +} diff --git a/test/cmdlineTests/evmasm_prevrandao_pre_paris/output b/test/cmdlineTests/evmasm_prevrandao_pre_paris/output new file mode 100644 index 000000000..e41148a83 --- /dev/null +++ b/test/cmdlineTests/evmasm_prevrandao_pre_paris/output @@ -0,0 +1,13 @@ + +======= evmasm_prevrandao_pre_paris/input.sol:C ======= +EVM assembly: + sstore(0x00, difficulty) + stop +stop + +sub_0: assembly { + stop +} + +Opcodes: +DIFFICULTY PUSH1 0x0 SSTORE STOP INVALID diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index 680531799..12c3be67d 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -20,6 +20,7 @@ * @date 2018 * Tests for the assembler. */ +#include #include #include @@ -59,15 +60,16 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) { "sub.asm", 1 }, { "verbatim.asm", 2 } }; - Assembly _assembly{false, {}}; + EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion(); + Assembly _assembly{evmVersion, false, {}}; auto root_asm = make_shared("root.asm"); _assembly.setSourceLocation({1, 3, root_asm}); - Assembly _subAsm{false, {}}; + Assembly _subAsm{evmVersion, false, {}}; auto sub_asm = make_shared("sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); - Assembly _verbatimAsm(true, ""); + Assembly _verbatimAsm(evmVersion, true, ""); auto verbatim_asm = make_shared("verbatim.asm"); _verbatimAsm.setSourceLocation({8, 18, verbatim_asm}); @@ -215,6 +217,7 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps) { + EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion(); // Tests for 1, 2, 3 number of immutables. for (int numImmutables = 1; numImmutables <= 3; ++numImmutables) { @@ -243,7 +246,7 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps) { *subName, 1 } }; - auto subAsm = make_shared(false, string{}); + auto subAsm = make_shared(evmVersion, false, string{}); for (char i = 0; i < numImmutables; ++i) { for (int r = 0; r < numActualRefs; ++r) @@ -253,7 +256,7 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps) } } - Assembly assembly{true, {}}; + Assembly assembly{evmVersion, true, {}}; for (char i = 1; i <= numImmutables; ++i) { assembly.setSourceLocation({10*i, 10*i + 3+i, assemblyName}); @@ -270,7 +273,7 @@ BOOST_AUTO_TEST_CASE(immutables_and_its_source_maps) auto const numberOfMappings = std::count(sourceMappings.begin(), sourceMappings.end(), ';'); LinkerObject const& obj = assembly.assemble(); - string const disassembly = disassemble(obj.bytecode, "\n"); + string const disassembly = disassemble(obj.bytecode, evmVersion, "\n"); auto const numberOfOpcodes = std::count(disassembly.begin(), disassembly.end(), '\n'); #if 0 // {{{ debug prints @@ -302,11 +305,12 @@ BOOST_AUTO_TEST_CASE(immutable) { "root.asm", 0 }, { "sub.asm", 1 } }; - Assembly _assembly{true, {}}; + EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion(); + Assembly _assembly{evmVersion, true, {}}; auto root_asm = make_shared("root.asm"); _assembly.setSourceLocation({1, 3, root_asm}); - Assembly _subAsm{false, {}}; + Assembly _subAsm{evmVersion, false, {}}; auto sub_asm = make_shared("sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); _subAsm.appendImmutable("someImmutable"); @@ -395,10 +399,11 @@ BOOST_AUTO_TEST_CASE(immutable) BOOST_AUTO_TEST_CASE(subobject_encode_decode) { - Assembly assembly{true, {}}; + EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion(); + Assembly assembly{evmVersion, true, {}}; - shared_ptr subAsmPtr = make_shared(false, string{}); - shared_ptr subSubAsmPtr = make_shared(false, string{}); + shared_ptr subAsmPtr = make_shared(evmVersion, false, string{}); + shared_ptr subSubAsmPtr = make_shared(evmVersion, false, string{}); assembly.appendSubroutine(subAsmPtr); subAsmPtr->appendSubroutine(subSubAsmPtr); diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 6fc3c5a74..6f1a13759 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -1250,8 +1250,18 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies) // tag unifications (due to block deduplication) is also // visible at the super-assembly. - Assembly main{false, {}}; - AssemblyPointer sub = make_shared(true, string{}); + Assembly::OptimiserSettings settings; + settings.runInliner = false; + settings.runJumpdestRemover = true; + settings.runPeephole = true; + settings.runDeduplicate = true; + settings.runCSE = true; + settings.runConstantOptimiser = true; + settings.evmVersion = solidity::test::CommonOptions::get().evmVersion(); + settings.expectedExecutionsPerDeployment = OptimiserSettings{}.expectedExecutionsPerDeployment; + + Assembly main{settings.evmVersion, false, {}}; + AssemblyPointer sub = make_shared(settings.evmVersion, true, string{}); sub->append(u256(1)); auto t1 = sub->newTag(); @@ -1277,16 +1287,6 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies) main.append(t1.toSubAssemblyTag(subId)); main.append(u256(8)); - Assembly::OptimiserSettings settings; - settings.runInliner = false; - settings.runJumpdestRemover = true; - settings.runPeephole = true; - settings.runDeduplicate = true; - settings.runCSE = true; - settings.runConstantOptimiser = true; - settings.evmVersion = solidity::test::CommonOptions::get().evmVersion(); - settings.expectedExecutionsPerDeployment = OptimiserSettings{}.expectedExecutionsPerDeployment; - main.optimise(settings); AssemblyItems expectationMain{ diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index a1580fb53..e775bb6d0 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -109,7 +109,7 @@ public: bytes realCode = bytecodeSansMetadata(_bytecode); BOOST_REQUIRE_MESSAGE(!realCode.empty(), "Invalid or missing metadata in bytecode."); size_t instructions = 0; - evmasm::eachInstruction(realCode, [&](Instruction _instr, u256 const&) { + evmasm::eachInstruction(realCode, m_evmVersion, [&](Instruction _instr, u256 const&) { if (!_which || *_which == _instr) instructions++; }); @@ -287,7 +287,7 @@ BOOST_AUTO_TEST_CASE(retain_information_in_branches) bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "c", true); size_t numSHA3s = 0; - eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) { + eachInstruction(optimizedBytecode, m_evmVersion, [&](Instruction _instr, u256 const&) { if (_instr == Instruction::KECCAK256) numSHA3s++; }); @@ -330,7 +330,7 @@ BOOST_AUTO_TEST_CASE(store_tags_as_unions) bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "test", true); size_t numSHA3s = 0; - eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) { + eachInstruction(optimizedBytecode, m_evmVersion, [&](Instruction _instr, u256 const&) { if (_instr == Instruction::KECCAK256) numSHA3s++; }); diff --git a/test/libsolidity/analysis/FunctionCallGraph.cpp b/test/libsolidity/analysis/FunctionCallGraph.cpp index 8c10ca72a..fcc8d1423 100644 --- a/test/libsolidity/analysis/FunctionCallGraph.cpp +++ b/test/libsolidity/analysis/FunctionCallGraph.cpp @@ -1860,6 +1860,7 @@ BOOST_AUTO_TEST_CASE(builtins) block.chainid; block.coinbase; block.difficulty; + block.prevrandao; block.gaslimit; block.number; block.timestamp; diff --git a/test/libsolidity/semanticTests/inlineAssembly/difficulty.sol b/test/libsolidity/semanticTests/inlineAssembly/difficulty.sol new file mode 100644 index 000000000..7ae286110 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/difficulty.sol @@ -0,0 +1,12 @@ +contract C { + function f() public view returns (uint ret) { + assembly { + ret := difficulty() + } + } +} +// ==== +// compileToEwasm: also +// EVMVersion: 200000000 diff --git a/test/libsolidity/semanticTests/inlineAssembly/prevrandao.sol b/test/libsolidity/semanticTests/inlineAssembly/prevrandao.sol new file mode 100644 index 000000000..5a3b791fe --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/prevrandao.sol @@ -0,0 +1,12 @@ +contract C { + function f() public view returns (uint ret) { + assembly { + ret := prevrandao() + } + } +} +// ==== +// compileToEwasm: also +// EVMVersion: >=paris +// ---- +// f() -> 0xa86c2e601b6c44eb4848f7d23d9df3113fbcac42041c49cbed5000cb4f118777 diff --git a/test/libsolidity/semanticTests/state/block_prevrandao.sol b/test/libsolidity/semanticTests/state/block_prevrandao.sol new file mode 100644 index 000000000..24d5f2af6 --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_prevrandao.sol @@ -0,0 +1,10 @@ +contract C { + function f() public view returns (uint) { + return block.prevrandao; + } +} +// ==== +// compileToEwasm: also +// EVMVersion: >=paris +// ---- +// f() -> 0xa86c2e601b6c44eb4848f7d23d9df3113fbcac42041c49cbed5000cb4f118777 diff --git a/test/libsolidity/semanticTests/state/block_prevrandao_pre_paris.sol b/test/libsolidity/semanticTests/state/block_prevrandao_pre_paris.sol new file mode 100644 index 000000000..efc4d226f --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_prevrandao_pre_paris.sol @@ -0,0 +1,10 @@ +contract C { + function f() public view returns (uint) { + return block.prevrandao; + } +} +// ==== +// compileToEwasm: also +// EVMVersion: 200000000 diff --git a/test/libsolidity/syntaxTests/inlineAssembly/difficulty_builtin_pre_paris.sol b/test/libsolidity/syntaxTests/inlineAssembly/difficulty_builtin_pre_paris.sol new file mode 100644 index 000000000..13ea92c52 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/difficulty_builtin_pre_paris.sol @@ -0,0 +1,14 @@ +contract C { + function f() public view returns (uint256) { + return block.difficulty; + } + + function g() public view returns (uint256 ret) { + assembly { + ret := difficulty() + } + } +} +// ==== +// EVMVersion: r { + r := 1000 + } + ret := difficulty() + } + } +} +// ==== +// EVMVersion: =paris +// ---- +// Warning 8417: (77-93): Since the VM version paris, "difficulty" was replaced by "prevrandao", which now returns a random number based on the beacon chain. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/difficulty_nobuiltin_post_paris.sol b/test/libsolidity/syntaxTests/inlineAssembly/difficulty_nobuiltin_post_paris.sol new file mode 100644 index 000000000..65fe4c15c --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/difficulty_nobuiltin_post_paris.sol @@ -0,0 +1,12 @@ +contract C { + function f() public { + assembly { + pop(difficulty()) + } + } +} +// ==== +// EVMVersion: >=paris +// ---- +// DeclarationError 4619: (74-84): Function "difficulty" not found. +// TypeError 3950: (74-86): Expected expression to evaluate to one value, but got 0 values instead. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/difficulty_reserved_post_paris.sol b/test/libsolidity/syntaxTests/inlineAssembly/difficulty_reserved_post_paris.sol new file mode 100644 index 000000000..5121a110b --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/difficulty_reserved_post_paris.sol @@ -0,0 +1,22 @@ +contract C { + function f() public view returns (uint256 ret) { + assembly { + let difficulty := sload(0) + ret := difficulty + } + } + + function g() public pure returns (uint256 ret) { + assembly { + function difficulty() -> r { + r := 1000 + } + ret := difficulty() + } + } +} +// ==== +// EVMVersion: >=paris +// ---- +// DeclarationError 5017: (101-111): The identifier "difficulty" is reserved and can not be used. +// DeclarationError 5017: (255-323): The identifier "difficulty" is reserved and can not be used. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/prevrandao_allowed_function_pre_paris.sol b/test/libsolidity/syntaxTests/inlineAssembly/prevrandao_allowed_function_pre_paris.sol new file mode 100644 index 000000000..d85e77a45 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/prevrandao_allowed_function_pre_paris.sol @@ -0,0 +1,20 @@ +contract C { + function f() public view returns (uint256 ret) { + assembly { + let prevrandao := sload(0) + ret := prevrandao + } + } + + function g() public pure returns (uint256 ret) { + assembly { + function prevrandao() -> r { + r := 1000 + } + ret := prevrandao() + } + } +} +// ==== +// EVMVersion: =paris +// ---- diff --git a/test/libsolidity/syntaxTests/inlineAssembly/prevrandao_disallowed_function_post_paris.sol b/test/libsolidity/syntaxTests/inlineAssembly/prevrandao_disallowed_function_post_paris.sol new file mode 100644 index 000000000..6c284a005 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/prevrandao_disallowed_function_post_paris.sol @@ -0,0 +1,21 @@ +contract C { + function f() public view returns (uint256 ret) { + assembly { + let prevrandao := sload(0) + ret := prevrandao + } + } + + function g() public pure returns (uint256 ret) { + assembly { + function prevrandao() -> r { + r := 1000 + } + ret := prevrandao() + } + } +} +// ==== +// EVMVersion: >=paris +// ---- +// ParserError 5568: (101-111): Cannot use builtin function name "prevrandao" as identifier name. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/prevrandao_magic_block_warn_pre_paris.sol b/test/libsolidity/syntaxTests/inlineAssembly/prevrandao_magic_block_warn_pre_paris.sol new file mode 100644 index 000000000..2086892b1 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/prevrandao_magic_block_warn_pre_paris.sol @@ -0,0 +1,9 @@ +contract C { + function f() public view returns (uint256) { + return block.prevrandao; + } +} +// ==== +// EVMVersion: =paris +// ---- diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed.sol index 8b634c219..2b9e5ae5f 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed.sol @@ -73,7 +73,7 @@ contract C { pop(coinbase()) pop(timestamp()) pop(number()) - pop(difficulty()) + pop(prevrandao()) pop(gaslimit()) // NOTE: msize() is allowed only with optimizer disabled @@ -83,7 +83,7 @@ contract C { } } // ==== -// EVMVersion: >=london +// EVMVersion: >=paris // ---- // Warning 5740: (89-1716): Unreachable code. // Warning 5740: (1729-1741): Unreachable code. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_london.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_london.sol new file mode 100644 index 000000000..be9f7281f --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_london.sol @@ -0,0 +1,11 @@ +contract C { + function f() public { + assembly { + pop(difficulty()) + } + } +} +// ==== +// EVMVersion: <=london +// ---- +// Warning 2018: (17-103): Function state mutability can be restricted to view diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_pure.sol index 85d7b6300..16c75d6f7 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_pure.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_pure.sol @@ -73,7 +73,6 @@ contract C { //pop(coinbase()) //pop(timestamp()) //pop(number()) - //pop(difficulty()) //pop(gaslimit()) // NOTE: msize() is allowed only with optimizer disabled diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view.sol index af30bc21a..0bb92c1fd 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view.sol @@ -73,7 +73,7 @@ contract C { pop(coinbase()) pop(timestamp()) pop(number()) - pop(difficulty()) + pop(prevrandao()) pop(gaslimit()) // NOTE: msize() is allowed only with optimizer disabled @@ -83,7 +83,7 @@ contract C { } } // ==== -// EVMVersion: >=london +// EVMVersion: >=paris // ---- // Warning 5740: (94-1733): Unreachable code. // Warning 5740: (1746-1758): Unreachable code. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_london.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_london.sol new file mode 100644 index 000000000..ffc757a19 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_london.sol @@ -0,0 +1,10 @@ +contract C { + function f() public view { + assembly { + pop(difficulty()) + } + } +} +// ==== +// EVMVersion: <=london +// ---- diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure.sol index f9a652fdb..9b3806f4d 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure.sol @@ -6,33 +6,26 @@ contract C { pop(gas()) pop(address()) pop(balance(0)) - pop(selfbalance()) pop(caller()) pop(callvalue()) pop(extcodesize(0)) extcodecopy(0, 1, 2, 3) - pop(extcodehash(0)) pop(create(0, 1, 2)) - pop(create2(0, 1, 2, 3)) pop(call(0, 1, 2, 3, 4, 5, 6)) pop(callcode(0, 1, 2, 3, 4, 5, 6)) pop(delegatecall(0, 1, 2, 3, 4, 5)) - pop(staticcall(0, 1, 2, 3, 4, 5)) selfdestruct(0) log0(0, 1) log1(0, 1, 2) log2(0, 1, 2, 3) log3(0, 1, 2, 3, 4) log4(0, 1, 2, 3, 4, 5) - pop(chainid()) - pop(basefee()) pop(origin()) pop(gasprice()) pop(blockhash(0)) pop(coinbase()) pop(timestamp()) pop(number()) - pop(difficulty()) pop(gaslimit()) // These two are disallowed too but the error suppresses other errors. @@ -41,40 +34,31 @@ contract C { } } } -// ==== -// EVMVersion: >=london // ---- -// Warning 5740: (672-1083): Unreachable code. +// Warning 5740: (526-853): Unreachable code. // TypeError 2527: (79-87): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". // TypeError 8961: (101-113): Function cannot be declared as pure because this expression (potentially) modifies the state. // TypeError 2527: (130-135): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". // TypeError 2527: (153-162): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". // TypeError 2527: (180-190): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (208-221): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (239-247): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (265-276): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (294-308): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (322-345): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (362-376): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 8961: (394-409): Function cannot be declared as pure because this expression (potentially) modifies the state. -// TypeError 8961: (427-446): Function cannot be declared as pure because this expression (potentially) modifies the state. -// TypeError 8961: (464-489): Function cannot be declared as pure because this expression (potentially) modifies the state. -// TypeError 8961: (507-536): Function cannot be declared as pure because this expression (potentially) modifies the state. -// TypeError 8961: (554-584): Function cannot be declared as pure because this expression (potentially) modifies the state. -// TypeError 2527: (602-630): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 8961: (644-659): Function cannot be declared as pure because this expression (potentially) modifies the state. -// TypeError 8961: (672-682): Function cannot be declared as pure because this expression (potentially) modifies the state. -// TypeError 8961: (695-708): Function cannot be declared as pure because this expression (potentially) modifies the state. -// TypeError 8961: (721-737): Function cannot be declared as pure because this expression (potentially) modifies the state. -// TypeError 8961: (750-769): Function cannot be declared as pure because this expression (potentially) modifies the state. -// TypeError 8961: (782-804): Function cannot be declared as pure because this expression (potentially) modifies the state. -// TypeError 2527: (821-830): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (848-857): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (875-883): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (901-911): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (929-941): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (959-969): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (987-998): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (1016-1024): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (1042-1054): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". -// TypeError 2527: (1072-1082): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (208-216): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (234-245): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (263-277): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (291-314): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 8961: (331-346): Function cannot be declared as pure because this expression (potentially) modifies the state. +// TypeError 8961: (364-389): Function cannot be declared as pure because this expression (potentially) modifies the state. +// TypeError 8961: (407-436): Function cannot be declared as pure because this expression (potentially) modifies the state. +// TypeError 8961: (454-484): Function cannot be declared as pure because this expression (potentially) modifies the state. +// TypeError 8961: (498-513): Function cannot be declared as pure because this expression (potentially) modifies the state. +// TypeError 8961: (526-536): Function cannot be declared as pure because this expression (potentially) modifies the state. +// TypeError 8961: (549-562): Function cannot be declared as pure because this expression (potentially) modifies the state. +// TypeError 8961: (575-591): Function cannot be declared as pure because this expression (potentially) modifies the state. +// TypeError 8961: (604-623): Function cannot be declared as pure because this expression (potentially) modifies the state. +// TypeError 8961: (636-658): Function cannot be declared as pure because this expression (potentially) modifies the state. +// TypeError 2527: (675-683): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (701-711): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (729-741): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (759-769): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (787-798): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (816-824): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (842-852): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_byzantium.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_byzantium.sol new file mode 100644 index 000000000..f0320d8b0 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_byzantium.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + pop(staticcall(0, 1, 2, 3, 4, 5)) + } + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// TypeError 2527: (79-107): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_constantinople.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_constantinople.sol new file mode 100644 index 000000000..30210efc3 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_constantinople.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + assembly { + pop(extcodehash(0)) + pop(create2(0, 1, 2, 3)) + } + } +} +// ==== +// EVMVersion: >=constantinople +// ---- +// TypeError 2527: (79-93): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 8961: (111-130): Function cannot be declared as pure because this expression (potentially) modifies the state. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_istanbul.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_istanbul.sol new file mode 100644 index 000000000..b4e022483 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_istanbul.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + assembly { + pop(selfbalance()) + pop(chainid()) + } + } +} +// ==== +// EVMVersion: >=istanbul +// ---- +// TypeError 2527: (79-92): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (110-119): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_london.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_london.sol new file mode 100644 index 000000000..ba47d4447 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_london.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + pop(basefee()) + } + } +} +// ==== +// EVMVersion: >=london +// ---- +// TypeError 2527: (79-88): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_paris.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_paris.sol new file mode 100644 index 000000000..e374522a1 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_paris.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + pop(prevrandao()) + } + } +} +// ==== +// EVMVersion: >=paris +// ---- +// TypeError 2527: (79-91): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_pre_paris.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_pre_paris.sol new file mode 100644 index 000000000..e348fd98b --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_pre_paris.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + pop(difficulty()) + } + } +} +// ==== +// EVMVersion: <=london +// ---- +// TypeError 2527: (79-91): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libyul/EVMCodeTransformTest.cpp b/test/libyul/EVMCodeTransformTest.cpp index 744fa8c03..73042b18b 100644 --- a/test/libyul/EVMCodeTransformTest.cpp +++ b/test/libyul/EVMCodeTransformTest.cpp @@ -19,6 +19,8 @@ #include #include +#include + #include #include #include @@ -66,7 +68,7 @@ TestCase::TestResult EVMCodeTransformTest::run(ostream& _stream, string const& _ return TestResult::FatalError; } - evmasm::Assembly assembly{false, {}}; + evmasm::Assembly assembly{solidity::test::CommonOptions::get().evmVersion(), false, {}}; EthAssemblyAdapter adapter(assembly); EVMObjectCompiler::compile( *stack.parserResult(), diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index 190001a57..3a8d96692 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -20,6 +20,8 @@ #include +#include + #include #include @@ -101,7 +103,7 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li "Bytecode: " + util::toHex(obj.bytecode->bytecode) + "\nOpcodes: " + - boost::trim_copy(evmasm::disassemble(obj.bytecode->bytecode)) + + boost::trim_copy(evmasm::disassemble(obj.bytecode->bytecode, solidity::test::CommonOptions::get().evmVersion())) + "\nSourceMappings:" + (obj.sourceMappings->empty() ? "" : " " + *obj.sourceMappings) + "\n"; diff --git a/test/libyul/YulInterpreterTest.cpp b/test/libyul/YulInterpreterTest.cpp index 3d1974e96..a1ada928b 100644 --- a/test/libyul/YulInterpreterTest.cpp +++ b/test/libyul/YulInterpreterTest.cpp @@ -98,7 +98,7 @@ string YulInterpreterTest::interpret() { Interpreter::run( state, - EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}), + EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()), *m_ast, /*disableExternalCalls=*/ !m_simulateExternalCallsToSelf, /*disableMemoryTracing=*/ false diff --git a/test/libyul/ewasmTranslationTests/difficulty.yul b/test/libyul/ewasmTranslationTests/difficulty.yul index c467d51a7..52e4275b4 100644 --- a/test/libyul/ewasmTranslationTests/difficulty.yul +++ b/test/libyul/ewasmTranslationTests/difficulty.yul @@ -1,6 +1,8 @@ { sstore(0, difficulty()) } +// ==== +// EVMVersion: =paris +// ---- +// Trace: +// Memory dump: +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000010000000000000001 diff --git a/test/tools/fuzzer_common.cpp b/test/tools/fuzzer_common.cpp index 25ad6986c..895957f4d 100644 --- a/test/tools/fuzzer_common.cpp +++ b/test/tools/fuzzer_common.cpp @@ -189,7 +189,7 @@ void FuzzerUtil::testConstantOptimizer(string const& _input, bool _quiet) for (bool isCreation: {false, true}) { - Assembly assembly{isCreation, {}}; + Assembly assembly{langutil::EVMVersion{}, isCreation, {}}; for (u256 const& n: numbers) { if (!_quiet) diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index f0691a836..db0e64828 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -98,7 +98,7 @@ u256 EVMInstructionInterpreter::eval( using namespace solidity::evmasm; using evmasm::Instruction; - auto info = instructionInfo(_instruction); + auto info = instructionInfo(_instruction, m_evmVersion); yulAssert(static_cast(info.args) == _arguments.size(), ""); auto const& arg = _arguments; @@ -268,8 +268,8 @@ u256 EVMInstructionInterpreter::eval( return m_state.timestamp; case Instruction::NUMBER: return m_state.blockNumber; - case Instruction::DIFFICULTY: - return m_state.difficulty; + case Instruction::PREVRANDAO: + return (m_evmVersion < langutil::EVMVersion::paris()) ? m_state.difficulty : m_state.prevrandao; case Instruction::GASLIMIT: return m_state.gaslimit; // --------------- memory / storage / logs --------------- @@ -543,7 +543,7 @@ void EVMInstructionInterpreter::logTrace( ) { logTrace( - evmasm::instructionInfo(_instruction).name, + evmasm::instructionInfo(_instruction, m_evmVersion).name, SemanticInformation::memory(_instruction) == SemanticInformation::Effect::Write, _arguments, _data diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.h b/test/tools/yulInterpreter/EVMInstructionInterpreter.h index c60e4b4e7..9f0f473f1 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.h +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.h @@ -26,6 +26,8 @@ #include #include +#include + #include namespace solidity::evmasm @@ -74,7 +76,8 @@ struct InterpreterState; class EVMInstructionInterpreter { public: - explicit EVMInstructionInterpreter(InterpreterState& _state, bool _disableMemWriteTrace): + explicit EVMInstructionInterpreter(langutil::EVMVersion _evmVersion, InterpreterState& _state, bool _disableMemWriteTrace): + m_evmVersion(_evmVersion), m_state(_state), m_disableMemoryWriteInstructions(_disableMemWriteTrace) {} @@ -128,6 +131,7 @@ private: return m_disableMemoryWriteInstructions; } + langutil::EVMVersion m_evmVersion; InterpreterState& m_state; /// Flag to disable trace of instructions that write to memory. bool m_disableMemoryWriteInstructions; diff --git a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp index 36f51b11c..fdba09aa4 100644 --- a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp +++ b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp @@ -597,7 +597,7 @@ u256 EwasmBuiltinInterpreter::readU256(uint64_t _offset, size_t _croppedTo) void EwasmBuiltinInterpreter::logTrace(evmasm::Instruction _instruction, std::vector const& _arguments, bytes const& _data) { - logTrace(evmasm::instructionInfo(_instruction).name, _arguments, _data); + logTrace(evmasm::instructionInfo(_instruction, langutil::EVMVersion()).name, _arguments, _data); } void EwasmBuiltinInterpreter::logTrace(std::string const& _pseudoInstruction, std::vector const& _arguments, bytes const& _data) diff --git a/test/tools/yulInterpreter/Interpreter.cpp b/test/tools/yulInterpreter/Interpreter.cpp index e10f6a9e5..b66967e26 100644 --- a/test/tools/yulInterpreter/Interpreter.cpp +++ b/test/tools/yulInterpreter/Interpreter.cpp @@ -314,7 +314,7 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall) { if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name)) { - EVMInstructionInterpreter interpreter(m_state, m_disableMemoryTrace); + EVMInstructionInterpreter interpreter(dialect->evmVersion(), m_state, m_disableMemoryTrace); u256 const value = interpreter.evalBuiltin(*fun, _funCall.arguments, values()); diff --git a/test/tools/yulInterpreter/Interpreter.h b/test/tools/yulInterpreter/Interpreter.h index 949d0ce4e..26a8b2701 100644 --- a/test/tools/yulInterpreter/Interpreter.h +++ b/test/tools/yulInterpreter/Interpreter.h @@ -94,6 +94,7 @@ struct InterpreterState u256 timestamp = 0x88888888; u256 blockNumber = 1024; u256 difficulty = 0x9999999; + u256 prevrandao = (u256(1) << 64) + 1; u256 gaslimit = 4000000; u256 chainid = 0x01; /// The minimum value of basefee: 7 wei.