diff --git a/Changelog.md b/Changelog.md index ea9cd13676c0..c876aa7bc8ff 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,11 +6,14 @@ Language Features: * Yul: Introduce builtin ``blobbasefee()`` for retrieving the blob base fee of the current block. * Yul: Introduce builtin ``blobhash()`` for retrieving versioned hashes of blobs associated with the transaction. * Yul: Introduce builtin ``mcopy()`` for cheaply copying data between memory areas. + * Yul: Introduce builtins ``tload()`` and ``tstore()`` for transient storage access. + Compiler Features: * EVM: Support for the EVM Version "Cancun". * SMTChecker: Support `bytes.concat` except when string literals are passed as arguments. + Bugfixes: * AST import: Fix bug when importing inline assembly with empty ``let`` variable declaration. diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 index 91d6b4177434..43648ceb5d97 100644 --- a/docs/grammar/SolidityLexer.g4 +++ b/docs/grammar/SolidityLexer.g4 @@ -298,7 +298,7 @@ YulEVMBuiltin: 'stop' | 'add' | 'sub' | 'mul' | 'div' | 'sdiv' | 'mod' | 'smod' | 'exp' | 'not' | 'lt' | 'gt' | 'slt' | 'sgt' | 'eq' | 'iszero' | 'and' | 'or' | 'xor' | 'byte' | 'shl' | 'shr' | 'sar' | 'addmod' | 'mulmod' | 'signextend' | 'keccak256' - | 'pop' | 'mload' | 'mstore' | 'mstore8' | 'sload' | 'sstore' | 'msize' | 'gas' + | 'pop' | 'mload' | 'mstore' | 'mstore8' | 'sload' | 'sstore' | 'tload' | 'tstore'| 'msize' | 'gas' | 'address' | 'balance' | 'selfbalance' | 'caller' | 'callvalue' | 'calldataload' | 'calldatasize' | 'calldatacopy' | 'extcodesize' | 'extcodecopy' | 'returndatasize' | 'returndatacopy' | 'mcopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index bb495c8155c5..bbeb116c9eab 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -180,6 +180,7 @@ at each version. Backward compatibility is not guaranteed between each version. - The block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) can be accessed via the global ``block.blobbasefee`` or ``blobbasefee()`` in inline assembly. - Introduces ``blobhash()`` in inline assembly and a corresponding global function to retrieve versioned hashes of blobs associated with the transaction (see `EIP-4844 `_). - Opcode ``mcopy`` is available in assembly (see `EIP-5656 `_). + - Opcodes ``tstore`` and ``tload`` are available in assembly (see `EIP-1153 `_). .. index:: ! standard JSON, ! --standard-json .. _compiler-api: diff --git a/docs/yul.rst b/docs/yul.rst index 482e80ea134c..3cf2785fc444 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -756,7 +756,8 @@ Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I``, ``L``, ``P`` and ``N`` ar Homestead, Byzantium, Constantinople, Istanbul, London, Paris or Cancun 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``. +but not including position ``b``, ``storage[p]`` signifies the storage contents at slot ``p``, and +similarly, ``transientStorage[p]`` signifies the transient storage contents at slot ``p``. Since Yul manages local variables and control-flow, opcodes that interfere with these features are not available. This includes @@ -833,6 +834,10 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a +-------------------------+-----+---+-----------------------------------------------------------------+ | sstore(p, v) | `-` | F | storage[p] := v | +-------------------------+-----+---+-----------------------------------------------------------------+ +| tload(p) | | N | transientStorage[p] | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| tstore(p, v) | `-` | N | transientStorage[p] := v | ++-------------------------+-----+---+-----------------------------------------------------------------+ | msize() | | F | size of memory, i.e. largest accessed memory index | +-------------------------+-----+---+-----------------------------------------------------------------+ | gas() | | F | gas still available to execution | diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 6594d1ff6c2f..5982b8a08092 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -274,13 +274,14 @@ unsigned GasMeter::runGas(Instruction _instruction, langutil::EVMVersion _evmVer switch (instructionInfo(_instruction, _evmVersion).gasPriceTier) { - case Tier::Zero: return GasCosts::tier0Gas; - case Tier::Base: return GasCosts::tier1Gas; - case Tier::VeryLow: return GasCosts::tier2Gas; - case Tier::Low: return GasCosts::tier3Gas; - case Tier::Mid: return GasCosts::tier4Gas; - case Tier::High: return GasCosts::tier5Gas; - case Tier::Ext: return GasCosts::tier6Gas; + case Tier::Zero: return GasCosts::tier0Gas; + case Tier::Base: return GasCosts::tier1Gas; + case Tier::VeryLow: return GasCosts::tier2Gas; + case Tier::Low: return GasCosts::tier3Gas; + case Tier::Mid: return GasCosts::tier4Gas; + case Tier::High: return GasCosts::tier5Gas; + case Tier::Ext: return GasCosts::tier6Gas; + case Tier::WarmAccess: return GasCosts::warmStorageReadCost; default: break; } assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction, _evmVersion).name); diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index c00bc3542151..a21f33e1838f 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -90,6 +90,8 @@ std::map const solidity::evmasm::c_instructions = { "MSTORE8", Instruction::MSTORE8 }, { "SLOAD", Instruction::SLOAD }, { "SSTORE", Instruction::SSTORE }, + { "TLOAD", Instruction::TLOAD }, + { "TSTORE", Instruction::TSTORE }, { "JUMP", Instruction::JUMP }, { "JUMPI", Instruction::JUMPI }, { "PC", Instruction::PC }, @@ -242,6 +244,8 @@ static std::map const c_instructionInfo = { Instruction::MSTORE8, { "MSTORE8", 0, 2, 0, true, Tier::VeryLow } }, { Instruction::SLOAD, { "SLOAD", 0, 1, 1, false, Tier::Special } }, { Instruction::SSTORE, { "SSTORE", 0, 2, 0, true, Tier::Special } }, + { Instruction::TLOAD, { "TLOAD", 0, 1, 1, false, Tier::WarmAccess} }, + { Instruction::TSTORE, { "TSTORE", 0, 2, 0, true, Tier::WarmAccess} }, { Instruction::JUMP, { "JUMP", 0, 1, 0, true, Tier::Mid } }, { Instruction::JUMPI, { "JUMPI", 0, 2, 0, true, Tier::High } }, { Instruction::PC, { "PC", 0, 0, 1, false, Tier::Base } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 4e7ac8b97ff0..1733863aa5a2 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -106,6 +106,9 @@ enum class Instruction: uint8_t JUMPDEST, ///< set a potential jump destination MCOPY = 0x5e, ///< copy between memory areas + TLOAD = 0x5c, ///< load word from transient storage + TSTORE = 0x5d, ///< save word to transient storage + PUSH0 = 0x5f, ///< place the value 0 on stack PUSH1 = 0x60, ///< place 1 byte item on stack PUSH2, ///< place 2 byte item on stack @@ -293,6 +296,7 @@ enum class Tier Mid, // 8, Mid High, // 10, Slow Ext, // 20, Ext + WarmAccess, // 100, Warm Access ExtCode, // 700, Extcode Balance, // 400, Balance Special, // multiparam or otherwise special diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 5c8b986be90d..c5e14d5f4b85 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -37,6 +37,7 @@ std::vector SemanticInformation::readWriteOperat { assertThrow(memory(_instruction) == Effect::None, OptimizerException, ""); assertThrow(storage(_instruction) != Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); Operation op; op.effect = storage(_instruction); op.location = Location::Storage; @@ -51,6 +52,7 @@ std::vector SemanticInformation::readWriteOperat { assertThrow(memory(_instruction) != Effect::None, OptimizerException, ""); assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); Operation op; op.effect = memory(_instruction); op.location = Location::Memory; @@ -62,6 +64,19 @@ std::vector SemanticInformation::readWriteOperat return {op}; } + case Instruction::TSTORE: + case Instruction::TLOAD: + { + assertThrow(memory(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) != Effect::None, OptimizerException, ""); + Operation op; + op.effect = transientStorage(_instruction); + op.location = Location::TransientStorage; + op.startParameter = 0; + op.lengthConstant = 1; + return {op}; + } case Instruction::REVERT: case Instruction::RETURN: case Instruction::KECCAK256: @@ -72,6 +87,7 @@ std::vector SemanticInformation::readWriteOperat case Instruction::LOG4: { assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); assertThrow(memory(_instruction) == Effect::Read, OptimizerException, ""); Operation op; op.effect = memory(_instruction); @@ -84,6 +100,7 @@ std::vector SemanticInformation::readWriteOperat { assertThrow(memory(_instruction) == Effect::Write, OptimizerException, ""); assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); Operation op; op.effect = memory(_instruction); op.location = Location::Memory; @@ -97,6 +114,7 @@ std::vector SemanticInformation::readWriteOperat { assertThrow(memory(_instruction) == Effect::Write, OptimizerException, ""); assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); Operation op; op.effect = memory(_instruction); op.location = Location::Memory; @@ -131,10 +149,14 @@ std::vector SemanticInformation::readWriteOperat size_t paramCount = static_cast(instructionInfo(_instruction, langutil::EVMVersion()).args); std::vector operations{ Operation{Location::Memory, Effect::Read, paramCount - 4, paramCount - 3, {}}, - Operation{Location::Storage, Effect::Read, {}, {}, {}} + Operation{Location::Storage, Effect::Read, {}, {}, {}}, + Operation{Location::TransientStorage, Effect::Read, {}, {}, {}} }; if (_instruction != Instruction::STATICCALL) + { operations.emplace_back(Operation{Location::Storage, Effect::Write, {}, {}, {}}); + operations.emplace_back(Operation{Location::TransientStorage, Effect::Write, {}, {}, {}}); + } operations.emplace_back(Operation{ Location::Memory, Effect::Write, @@ -157,13 +179,15 @@ std::vector SemanticInformation::readWriteOperat {} }, Operation{Location::Storage, Effect::Read, {}, {}, {}}, - Operation{Location::Storage, Effect::Write, {}, {}, {}} + Operation{Location::Storage, Effect::Write, {}, {}, {}}, + Operation{Location::TransientStorage, Effect::Read, {}, {}, {}}, + Operation{Location::TransientStorage, Effect::Write, {}, {}, {}} }; case Instruction::MSIZE: // This is just to satisfy the assert below. return std::vector{}; default: - assertThrow(storage(_instruction) == None && memory(_instruction) == None, AssemblyException, ""); + assertThrow(storage(_instruction) == None && memory(_instruction) == None && transientStorage(_instruction) == None, AssemblyException, ""); } return {}; } @@ -348,6 +372,7 @@ bool SemanticInformation::movable(Instruction _instruction) case Instruction::EXTCODEHASH: case Instruction::RETURNDATASIZE: case Instruction::SLOAD: + case Instruction::TLOAD: case Instruction::PC: case Instruction::MSIZE: case Instruction::GAS: @@ -420,6 +445,7 @@ bool SemanticInformation::movableApartFromEffects(Instruction _instruction) case Instruction::BALANCE: case Instruction::SELFBALANCE: case Instruction::SLOAD: + case Instruction::TLOAD: case Instruction::KECCAK256: case Instruction::MLOAD: return true; @@ -450,6 +476,27 @@ SemanticInformation::Effect SemanticInformation::storage(Instruction _instructio } } +SemanticInformation::Effect SemanticInformation::transientStorage(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::CALL: + case Instruction::CALLCODE: + case Instruction::DELEGATECALL: + case Instruction::CREATE: + case Instruction::CREATE2: + case Instruction::TSTORE: + return SemanticInformation::Write; + + case Instruction::TLOAD: + case Instruction::STATICCALL: + return SemanticInformation::Read; + + default: + return SemanticInformation::None; + } +} + SemanticInformation::Effect SemanticInformation::otherState(Instruction _instruction) { switch (_instruction) @@ -508,6 +555,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) case Instruction::GASLIMIT: case Instruction::STATICCALL: case Instruction::SLOAD: + case Instruction::TLOAD: return true; default: break; @@ -520,6 +568,7 @@ bool SemanticInformation::invalidInViewFunctions(Instruction _instruction) switch (_instruction) { case Instruction::SSTORE: + case Instruction::TSTORE: case Instruction::JUMP: case Instruction::JUMPI: case Instruction::LOG0: diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index 36aabd55f9f6..c3491cda8bd8 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -39,8 +39,8 @@ class AssemblyItem; */ struct SemanticInformation { - /// Corresponds to the effect that a YUL-builtin has on a generic data location (storage, memory - /// and other blockchain state). + /// Corresponds to the effect that a YUL-builtin has on a generic data location (storage, memory, + /// transient storage and other blockchain state). enum Effect { None, @@ -48,7 +48,7 @@ struct SemanticInformation Write }; - enum class Location { Storage, Memory }; + enum class Location { Storage, Memory, TransientStorage }; /** * Represents a read or write operation from or to one of the data locations. @@ -87,10 +87,10 @@ struct SemanticInformation static bool terminatesControlFlow(Instruction _instruction); static bool reverts(Instruction _instruction); /// @returns false if the value put on the stack by _item depends on anything else than - /// the information in the current block header, memory, storage or stack. + /// the information in the current block header, memory, storage, transient storage or stack. static bool isDeterministic(AssemblyItem const& _item); /// @returns true if the instruction can be moved or copied (together with its arguments) - /// without altering the semantics. This means it cannot depend on storage or memory, + /// without altering the semantics. This means it cannot depend on storage, transient storage or memory, /// cannot have any side-effects, but it can depend on a call-constant state of the blockchain. static bool movable(Instruction _instruction); /// If true, the expressions in this code can be moved or copied (together with their arguments) @@ -109,6 +109,7 @@ struct SemanticInformation static bool canBeRemovedIfNoMSize(Instruction _instruction); static Effect memory(Instruction _instruction); static Effect storage(Instruction _instruction); + static Effect transientStorage(Instruction _instruction); static Effect otherState(Instruction _instruction); static bool invalidInPureFunctions(Instruction _instruction); static bool invalidInViewFunctions(Instruction _instruction); diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h index 746dfa4c67d1..6d34a50410fb 100644 --- a/libevmasm/SimplificationRule.h +++ b/libevmasm/SimplificationRule.h @@ -136,6 +136,8 @@ struct EVMBuiltins static auto constexpr MSTORE8 = PatternGenerator{}; static auto constexpr SLOAD = PatternGenerator{}; static auto constexpr SSTORE = PatternGenerator{}; + static auto constexpr TLOAD = PatternGenerator{}; + static auto constexpr TSTORE = PatternGenerator{}; static auto constexpr PC = PatternGenerator{}; static auto constexpr MSIZE = PatternGenerator{}; static auto constexpr GAS = PatternGenerator{}; diff --git a/liblangutil/EVMVersion.cpp b/liblangutil/EVMVersion.cpp index 34996dc6b98c..438932a1cdaa 100644 --- a/liblangutil/EVMVersion.cpp +++ b/liblangutil/EVMVersion.cpp @@ -55,6 +55,9 @@ bool EVMVersion::hasOpcode(Instruction _opcode) const return hasBlobBaseFee(); case Instruction::MCOPY: return hasMcopy(); + case Instruction::TSTORE: + case Instruction::TLOAD: + return supportsTransientStorage(); default: return true; } diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index 5b4e73b15e7d..992933a898bf 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -106,6 +106,7 @@ class EVMVersion: bool hasPush0() const { return *this >= shanghai(); } bool hasBlobHash() const { return *this >= cancun(); } bool hasMcopy() const { return *this >= cancun(); } + bool supportsTransientStorage() const { return *this >= cancun(); } bool hasOpcode(evmasm::Instruction _opcode) const; diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index c4dade689830..97f3d6eeb5c9 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -319,6 +319,20 @@ std::vector AsmAnalyzer::operator()(FunctionCall const& _funCall) "The underlying opcode will eventually undergo breaking changes, " "and its use is not recommended." ); + else if ( + m_evmVersion.supportsTransientStorage() && + _funCall.functionName.name == "tstore"_yulstring + ) + m_errorReporter.warning( + 2394_error, + nativeLocationOf(_funCall.functionName), + "Transient storage as defined by EIP-1153 can break the composability of smart contracts: " + "Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, " + "your contract may unintentionally misbehave when invoked multiple times in a complex transaction. " + "To avoid this, be sure to clear all transient storage at the end of any call to your contract. " + "The use of transient storage for reentrancy guards that are cleared at the end of the call is safe." + ); + parameterTypes = &f->parameters; returnTypes = &f->returns; if (!f->literalArguments.empty()) @@ -739,6 +753,9 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio else if (_instr == evmasm::Instruction::MCOPY && !m_evmVersion.hasMcopy()) // TODO: Change this assertion to an error, similar to the ones above, when Cancun becomes the default EVM version. yulAssert(false); + else if ((_instr == evmasm::Instruction::TSTORE || _instr == evmasm::Instruction::TLOAD) && !m_evmVersion.supportsTransientStorage()) + // TODO: Change this assertion to an error, similar to the ones above, when Cancun becomes the default EVM version. + yulAssert(false); else if (_instr == evmasm::Instruction::PC) m_errorReporter.error( 2450_error, diff --git a/libyul/SideEffects.h b/libyul/SideEffects.h index b6df8df83f89..eb3fe9b55a95 100644 --- a/libyul/SideEffects.h +++ b/libyul/SideEffects.h @@ -50,7 +50,7 @@ struct SideEffects /// At statement level, it means that functions containing this code can be /// called multiple times, their calls can be rearranged and calls can also be /// deleted without changing the semantics. - /// This means it cannot depend on storage or memory, cannot have any side-effects, + /// This means it cannot depend on storage, memory or transient storage, cannot have any side-effects, /// but it can depend on state that is constant across an EVM-call. bool movable = true; /// If true, the expressions in this code can be moved or copied (together with their arguments) @@ -76,11 +76,15 @@ struct SideEffects /// or `None` respectively. Note that, when the value is `Read`, the expression can have an /// effect on `msize()`. Effect memory = None; + /// Can write, read or have no effect on transient storage, when the value of `transientStorage` is `Write`, `Read` + /// or `None` respectively. When the value is `Write`, the expression can invalidate transient storage, + /// potentially indirectly through external calls. + Effect transientStorage = None; /// @returns the worst-case side effects. static SideEffects worst() { - return SideEffects{false, false, false, false, false, Write, Write, Write}; + return SideEffects{false, false, false, false, false, Write, Write, Write, Write}; } /// @returns the combined side effects of two pieces of code. @@ -94,7 +98,8 @@ struct SideEffects cannotLoop && _other.cannotLoop, otherState + _other.otherState, storage + _other.storage, - memory + _other.memory + memory + _other.memory, + transientStorage + _other.transientStorage }; } @@ -115,7 +120,8 @@ struct SideEffects cannotLoop == _other.cannotLoop && otherState == _other.otherState && storage == _other.storage && - memory == _other.memory; + memory == _other.memory && + transientStorage == _other.transientStorage; } }; diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 36e6f1d0078d..f3c529bb1ab5 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -148,6 +148,14 @@ std::set createReservedIdentifiers(langutil::EVMVersion _evmVersion) { return _instr == evmasm::Instruction::BLOBHASH && _evmVersion < langutil::EVMVersion::cancun(); }; + // TODO remove this in 0.9.0. We allow creating functions or identifiers in Yul with the names + // tstore or tload for VMs before cancun. + auto transientStorageException = [&](evmasm::Instruction _instr) -> bool + { + return + _evmVersion < langutil::EVMVersion::cancun() && + (_instr == evmasm::Instruction::TSTORE || _instr == evmasm::Instruction::TLOAD); + }; std::set reserved; for (auto const& instr: evmasm::c_instructions) @@ -158,7 +166,8 @@ std::set createReservedIdentifiers(langutil::EVMVersion _evmVersion) !prevRandaoException(name) && !blobHashException(instr.second) && !blobBaseFeeException(instr.second) && - !mcopyException(instr.second) + !mcopyException(instr.second) && + !transientStorageException(instr.second) ) reserved.emplace(name); } @@ -277,7 +286,17 @@ std::map createBuiltins(langutil::EVMVersion _ "datacopy", 3, 0, - SideEffects{false, true, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write}, + SideEffects{ + false, // movable + true, // movableApartFromEffects + false, // canBeRemoved + false, // canBeRemovedIfNotMSize + true, // cannotLoop + SideEffects::None, // otherState + SideEffects::None, // storage + SideEffects::Write, // memory + SideEffects::None // transientStorage + }, {}, []( FunctionCall const&, @@ -291,7 +310,17 @@ std::map createBuiltins(langutil::EVMVersion _ "setimmutable", 3, 0, - SideEffects{false, false, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write}, + SideEffects{ + false, // movable + false, // movableApartFromEffects + false, // canBeRemoved + false, // canBeRemovedIfNotMSize + true, // cannotLoop + SideEffects::None, // otherState + SideEffects::None, // storage + SideEffects::Write, // memory + SideEffects::None // transientStorage + }, {std::nullopt, LiteralKind::String, std::nullopt}, []( FunctionCall const& _call, @@ -396,6 +425,7 @@ SideEffects EVMDialect::sideEffectsOfInstruction(evmasm::Instruction _instructio translate(evmasm::SemanticInformation::otherState(_instruction)), translate(evmasm::SemanticInformation::storage(_instruction)), translate(evmasm::SemanticInformation::memory(_instruction)), + translate(evmasm::SemanticInformation::transientStorage(_instruction)), }; } diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index 762c80110618..df8ae17a58e1 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -78,7 +78,8 @@ class SideEffectsCollector: public ASTWalker !m_sideEffects.movableApartFromEffects || m_sideEffects.storage == SideEffects::Write || m_sideEffects.otherState == SideEffects::Write || - m_sideEffects.memory == SideEffects::Write + m_sideEffects.memory == SideEffects::Write || + m_sideEffects.transientStorage == SideEffects::Write ) return false; @@ -94,6 +95,10 @@ class SideEffectsCollector: public ASTWalker if (_codeContainsMSize || _other.memory == SideEffects::Write) return false; + if (m_sideEffects.transientStorage == SideEffects::Read) + if (_other.transientStorage == SideEffects::Write) + return false; + return true; } diff --git a/libyul/optimiser/UnusedStoreEliminator.cpp b/libyul/optimiser/UnusedStoreEliminator.cpp index 95296369b77d..ed3394847c35 100644 --- a/libyul/optimiser/UnusedStoreEliminator.cpp +++ b/libyul/optimiser/UnusedStoreEliminator.cpp @@ -214,7 +214,10 @@ void UnusedStoreEliminator::visit(Statement const& _statement) if (operations.front().location == Location::Storage) activeStorageStores().insert(&_statement); else + { + yulAssert(operations.front().location == Location::Memory, ""); activeMemoryStores().insert(&_statement); + } m_storeOperations[&_statement] = std::move(operations.front()); } } diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index d21228c5be3f..5256b8128600 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -131,6 +131,8 @@ done < <( # Skipping a test with "let blobhash := ..." grep -v -E 'inlineAssembly/blobhash_pre_cancun.sol' | grep -v -E 'inlineAssembly/blobhash_pre_cancun_not_reserved.sol' | + # Skipping tests with "let tstore/tload := ..." + grep -v -E 'inlineAssembly/tload_tstore_not_reserved_before_cancun.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/test/EVMHost.cpp b/test/EVMHost.cpp index 84327a5b977f..5b4776273c3d 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -186,11 +186,16 @@ void EVMHost::newTransactionFrame() recorded_account_accesses.clear(); for (auto& [address, account]: accounts) + { for (auto& [slot, value]: account.storage) { value.access_status = EVMC_ACCESS_COLD; // Clear EIP-2929 storage access indicator value.original = value.current; // Clear EIP-2200 dirty slot } + + // Clear transient storage according to EIP 1153 + account.transient_storage.clear(); + } // Process selfdestruct list for (auto& [address, _]: recorded_selfdestructs) accounts.erase(address); diff --git a/test/cmdlineTests/evmasm_transient_storage_opcodes/args b/test/cmdlineTests/evmasm_transient_storage_opcodes/args new file mode 100644 index 000000000000..552cea1ca144 --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_opcodes/args @@ -0,0 +1 @@ +--evm-version cancun --no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none diff --git a/test/cmdlineTests/evmasm_transient_storage_opcodes/err b/test/cmdlineTests/evmasm_transient_storage_opcodes/err new file mode 100644 index 000000000000..a6cd56261e05 --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_opcodes/err @@ -0,0 +1,5 @@ +Warning: Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. + --> evmasm_transient_storage_opcodes/input.sol:7:13: + | +7 | tstore(0, 0) + | ^^^^^^ diff --git a/test/cmdlineTests/evmasm_transient_storage_opcodes/input.sol b/test/cmdlineTests/evmasm_transient_storage_opcodes/input.sol new file mode 100644 index 000000000000..94914ac4eef7 --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_opcodes/input.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract C { + fallback() external { + assembly { + tstore(0, 0) + sstore(0, tload(0)) + } + } +} diff --git a/test/cmdlineTests/evmasm_transient_storage_opcodes/output b/test/cmdlineTests/evmasm_transient_storage_opcodes/output new file mode 100644 index 000000000000..3d1eddc9ad02 --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_opcodes/output @@ -0,0 +1,36 @@ + +======= evmasm_transient_storage_opcodes/input.sol:C ======= +EVM assembly: + 0x80 + dup1 + 0x40 + mstore + jumpi(tag_1, callvalue) + dataSize(sub_0) + swap1 + dup2 + dataOffset(sub_0) + dup3 + codecopy + return +tag_1: + 0x00 + dup1 + revert +stop + +sub_0: assembly { + jumpi(tag_1, callvalue) + 0x00 + dup1 + tstore + sstore(0x00, tload(0x00)) + stop + tag_1: + 0x00 + dup1 + revert +} + +Opcodes: +PUSH1 0x80 DUP1 PUSH1 0x40 MSTORE CALLVALUE PUSH1 0x13 JUMPI PUSH1 0x10 SWAP1 DUP2 PUSH1 0x18 DUP3 CODECOPY RETURN JUMPDEST PUSH0 DUP1 REVERT INVALID CALLVALUE PUSH1 0xC JUMPI PUSH0 DUP1 TSTORE PUSH0 TLOAD PUSH0 SSTORE STOP JUMPDEST PUSH0 DUP1 REVERT diff --git a/test/libsolidity/semanticTests/inlineAssembly/tload_tstore_not_reserved_before_cancun.sol b/test/libsolidity/semanticTests/inlineAssembly/tload_tstore_not_reserved_before_cancun.sol new file mode 100644 index 000000000000..930c531eaacf --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/tload_tstore_not_reserved_before_cancun.sol @@ -0,0 +1,25 @@ +contract C { + function f() public view returns (uint ret) { + assembly { + let tload := sload(0) + let tstore := add(tload, 1) + ret := tstore + } + } + function g() public view returns (uint ret) { + assembly { + function tstore() -> a { + a := 2 + } + function tload() -> b { + b := 3 + } + ret := add(tstore(), tload()) + } + } +} +// ==== +// EVMVersion: 1 +// g() -> 5 diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_creation.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_creation.sol new file mode 100644 index 000000000000..5a97dc84ac0d --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_creation.sol @@ -0,0 +1,14 @@ +contract C { + constructor() { + uint x; + assembly { + tstore(0, 42) + x := tload(0) + } + assert(x == 42); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// constructor() -> diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_low_level_calls.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_low_level_calls.sol new file mode 100644 index 000000000000..f3fe0384472d --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_low_level_calls.sol @@ -0,0 +1,70 @@ +contract D { + function addOne() external { + assembly { + let x := tload(0) + tstore(0, add(x, 1)) + } + } + function get() external returns (uint x) { + assembly { + x := tload(0) + } + } +} + +contract C { + function set(uint x) external { + assembly { + tstore(0, x) + } + } + + function get() external view returns (uint x) { + assembly { + x := tload(0) + } + } + + function testDelegateCall() external returns (bool) { + this.set(5); + D d = new D(); + // Caller contract is the owner of the transient storage + (bool success, ) = address(d).delegatecall(abi.encodeCall(d.addOne, ())); + require(success); + require(this.get() == 6); + return true; + } + + function testCall() external returns (bool) { + this.set(5); + D d = new D(); + // Callee/Target contract is the owner of the transient storage + (bool success, ) = address(d).call(abi.encodeCall(d.addOne, ())); + require(success); + require(d.get() == 1); + return true; + } + + function tloadAllowedStaticCall() external returns (bool) { + this.set(5); + D d = new D(); + (bool success, bytes memory result) = address(d).staticcall(abi.encodeCall(d.get, ())); + require(success); + require(abi.decode(result, (uint)) == 0); + return true; + } + + function tstoreNotAllowedStaticCall() external returns (bool) { + D d = new D(); + (bool success, ) = address(d).staticcall(abi.encodeCall(d.addOne, ())); + require(!success); + return true; + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// testDelegateCall() -> true +// testCall() -> true +// tloadAllowedStaticCall() -> true +// tstoreNotAllowedStaticCall() -> true diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_calls_different_transactions.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_calls_different_transactions.sol new file mode 100644 index 000000000000..c82286550eb0 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_calls_different_transactions.sol @@ -0,0 +1,24 @@ +contract C { + function get(uint256 addr) external view returns (uint256 x) { + assembly { + x := tload(addr) + } + } + function set(uint256 addr, uint256 x) external { + assembly { + tstore(addr, x) + } + } + function test() public { + assert(this.get(0) == 0 && this.get(42) == 0); + this.set(0, 21); + assert(this.get(0) == 21 && this.get(42) == 0); + this.set(42, 131); + assert(this.get(0) == 21 && this.get(42) == 131); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// test() -> +// test() -> diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_transactions.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_transactions.sol new file mode 100644 index 000000000000..931f34ded5dc --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_transactions.sol @@ -0,0 +1,38 @@ +contract C { + function set(uint value) private { + assembly { + tstore(0, value) + } + } + + function get() private view returns (uint value) { + assembly { + value := tload(0) + } + } + + function f() external { + assembly { + tstore(0, 42) + } + } + + function g() external view returns(uint r) { + assembly { + r := tload(0) + } + } + + function h() external returns (uint x) { + set(99); + x = get(); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// g() -> 0 +// f() -> +// g() -> 0 +// h() -> 0x63 +// g() -> 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_reset_between_creation_runtime.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_reset_between_creation_runtime.sol new file mode 100644 index 000000000000..e41fcb600339 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_reset_between_creation_runtime.sol @@ -0,0 +1,24 @@ +contract C { + constructor() { + uint x; + assembly { + tstore(0, 42) + x := tload(0) + } + assert(x == 42); + } + + function f() public returns (uint x) { + assembly { + x := tload(0) + if eq(x, 42) { + revert(0, 0) + } + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// constructor() -> +// f() -> 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_sanity_checks.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_sanity_checks.sol new file mode 100644 index 000000000000..101c3c103d1b --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_sanity_checks.sol @@ -0,0 +1,24 @@ +contract C { + function f() external { + assembly { + tstore(0, 21) + mstore(0, 42) + sstore(0, 42) + if iszero(eq(tload(0), 21)) { + revert(0, 0) + } + } + } + function g() external view returns (uint s, uint m, uint t) { + assembly { + s := sload(0) + m := mload(0) + t := tload(0) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// f() -> +// g() -> 0x2a, 0, 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_selfdestruct.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_selfdestruct.sol new file mode 100644 index 000000000000..935435420e44 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_selfdestruct.sol @@ -0,0 +1,44 @@ +contract C { + function set(uint value) external { + assembly { + tstore(0, value) + } + } + + function get() external view returns (uint value) { + assembly { + value := tload(0) + } + } + + function terminate(address payable a) external { + selfdestruct(a); + } +} + +contract D { + C public c; + + constructor() { + c = new C(); + } + + function destroy() external { + c.set(42); + c.terminate(payable(address(this))); + assert(c.get() == 42); + } + + function createAndDestroy() external { + c = new C(); + c.set(42); + c.terminate(payable(address(this))); + assert(c.get() == 42); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// constructor() -> +// destroy() -> +// createAndDestroy() -> diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_simple_reentrancy_lock.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_simple_reentrancy_lock.sol new file mode 100644 index 000000000000..0d05c926ceef --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_simple_reentrancy_lock.sol @@ -0,0 +1,22 @@ +contract C { + modifier nonreentrant { + assembly { + if tload(0) { revert(0, 0) } + tstore(0, 1) + } + _; + assembly { + tstore(0, 0) + } + } + function f(bool simulateReentrancy) nonreentrant public { + if (simulateReentrancy) { + f(false); + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// f(bool): false -> +// f(bool): true -> FAILURE diff --git a/test/libsolidity/semanticTests/inlineAssembly/tstore_hidden_staticcall.sol b/test/libsolidity/semanticTests/inlineAssembly/tstore_hidden_staticcall.sol new file mode 100644 index 000000000000..bdd9401ee5a6 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/tstore_hidden_staticcall.sol @@ -0,0 +1,20 @@ +contract C { + function f() internal { + assembly { + tstore(0, 0) + } + } + function g() public view { + function() internal ptr = f; + function() internal view ptr2; + assembly { ptr2 := ptr } + ptr2(); // we force calling the non-view function, which should result in a revert during the staticcall + } + function test() public { + this.g(); // an external call to a view function should use static call + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// test() -> FAILURE diff --git a/test/libsolidity/syntaxTests/inlineAssembly/tload_reserved_cancun.sol b/test/libsolidity/syntaxTests/inlineAssembly/tload_reserved_cancun.sol new file mode 100644 index 000000000000..77c988577620 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/tload_reserved_cancun.sol @@ -0,0 +1,12 @@ +contract C { + function f() public view returns (uint ret) { + assembly { + let tload := sload(0) + ret := tload + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (98-103): Cannot use builtin function name "tload" as identifier name. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/transient_storage_invalid_pre_cancun.sol b/test/libsolidity/syntaxTests/inlineAssembly/transient_storage_invalid_pre_cancun.sol new file mode 100644 index 000000000000..25ae9e9d172b --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/transient_storage_invalid_pre_cancun.sol @@ -0,0 +1,13 @@ +contract C { + function f() public { + assembly { + tstore(0, 13) + tload(0) + } + } +} +// ==== +// EVMVersion: =cancun +// ---- +// Warning 2394: (88-94): Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/tstore_reserved_cancun.sol b/test/libsolidity/syntaxTests/inlineAssembly/tstore_reserved_cancun.sol new file mode 100644 index 000000000000..9f08d985aa38 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/tstore_reserved_cancun.sol @@ -0,0 +1,12 @@ +contract C { + function f() public view returns (uint ret) { + assembly { + let tstore := sload(0) + ret := tstore + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (98-104): Cannot use builtin function name "tstore" as identifier name. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol index 8aacecdec7ed..dcf6d6c6fc6e 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol @@ -4,6 +4,7 @@ contract C { pop(blobhash(0)) pop(blobbasefee()) mcopy(1, 2, 3) + pop(tload(0)) } } } diff --git a/test/libsolidity/syntaxTests/viewPureChecker/tload_not_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/tload_not_pure.sol new file mode 100644 index 000000000000..13b44f197f09 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/tload_not_pure.sol @@ -0,0 +1,11 @@ +contract C { + function f() external pure { + assembly { + pop(tload(0)) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// TypeError 2527: (81-89): 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/tload_view.sol b/test/libsolidity/syntaxTests/viewPureChecker/tload_view.sol new file mode 100644 index 000000000000..c94bd867027b --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/tload_view.sol @@ -0,0 +1,10 @@ +contract C { + function f() external view returns (uint a) { + assembly { + a := tload(0) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- diff --git a/test/libsolidity/syntaxTests/viewPureChecker/tstore_not_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/tstore_not_pure.sol new file mode 100644 index 000000000000..3cda8d08e9d8 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/tstore_not_pure.sol @@ -0,0 +1,12 @@ +contract C { + function f() external pure { + assembly { + tstore(0, 0) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 2394: (77-83): Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. +// TypeError 8961: (77-89): Function cannot be declared as pure because this expression (potentially) modifies the state. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/tstore_not_view.sol b/test/libsolidity/syntaxTests/viewPureChecker/tstore_not_view.sol new file mode 100644 index 000000000000..5e02ef910c3c --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/tstore_not_view.sol @@ -0,0 +1,12 @@ +contract C { + function f() external view { + assembly { + tstore(0, 0) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 2394: (77-83): Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. +// TypeError 8961: (77-89): Function cannot be declared as view because this expression (potentially) modifies the state. diff --git a/test/libyul/yulInterpreterTests/access_large_memory_offsets.yul b/test/libyul/yulInterpreterTests/access_large_memory_offsets.yul index 483220bc1317..ce108ddf7dd4 100644 --- a/test/libyul/yulInterpreterTests/access_large_memory_offsets.yul +++ b/test/libyul/yulInterpreterTests/access_large_memory_offsets.yul @@ -12,3 +12,4 @@ // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000007 // 0000000000000000000000000000000000000000000000000000000000000001: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/ambiguous_vars.yul b/test/libyul/yulInterpreterTests/ambiguous_vars.yul index 25e0928ae653..2fca766ea177 100644 --- a/test/libyul/yulInterpreterTests/ambiguous_vars.yul +++ b/test/libyul/yulInterpreterTests/ambiguous_vars.yul @@ -12,3 +12,4 @@ // 0: 0000000000000000000000000000000000000000000000000000000000000003 // 20: 0000000000000000000000000000000000000000000000000000000000000002 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/and_create.yul b/test/libyul/yulInterpreterTests/and_create.yul index cf32c4658edb..da962c3c0b04 100644 --- a/test/libyul/yulInterpreterTests/and_create.yul +++ b/test/libyul/yulInterpreterTests/and_create.yul @@ -11,3 +11,4 @@ // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000000001 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/and_create2.yul b/test/libyul/yulInterpreterTests/and_create2.yul index 5201b0188a76..0b83d818f087 100644 --- a/test/libyul/yulInterpreterTests/and_create2.yul +++ b/test/libyul/yulInterpreterTests/and_create2.yul @@ -13,3 +13,4 @@ // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000000001 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/blobbasefee.yul b/test/libyul/yulInterpreterTests/blobbasefee.yul index d863b1d8265f..5dd9095e7616 100644 --- a/test/libyul/yulInterpreterTests/blobbasefee.yul +++ b/test/libyul/yulInterpreterTests/blobbasefee.yul @@ -8,3 +8,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/blobhash.yul b/test/libyul/yulInterpreterTests/blobhash.yul index 2d5ff80f03c8..48ed47c68590 100644 --- a/test/libyul/yulInterpreterTests/blobhash.yul +++ b/test/libyul/yulInterpreterTests/blobhash.yul @@ -11,3 +11,4 @@ // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 014916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5 // 0000000000000000000000000000000000000000000000000000000000000001: 0167d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/bounded_recursion.yul b/test/libyul/yulInterpreterTests/bounded_recursion.yul index ce22638ca188..48d133409ebe 100644 --- a/test/libyul/yulInterpreterTests/bounded_recursion.yul +++ b/test/libyul/yulInterpreterTests/bounded_recursion.yul @@ -14,3 +14,4 @@ // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000000096 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/create2.yul b/test/libyul/yulInterpreterTests/create2.yul index 928a16304667..b7484f5db8de 100644 --- a/test/libyul/yulInterpreterTests/create2.yul +++ b/test/libyul/yulInterpreterTests/create2.yul @@ -11,3 +11,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000020 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/datacopy.yul b/test/libyul/yulInterpreterTests/datacopy.yul index fb8998c8bc79..ce5b6ce5c53a 100644 --- a/test/libyul/yulInterpreterTests/datacopy.yul +++ b/test/libyul/yulInterpreterTests/datacopy.yul @@ -16,3 +16,4 @@ object "main" // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 6465636f00000000000000000000000000000000000000000000000000000000 // 0000000000000000000000000000000000000000000000000000000000000001: 636f6465636f6465000000000000000000000000000000000000000000000000 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/dataoffset.yul b/test/libyul/yulInterpreterTests/dataoffset.yul index 399a8c95ceb9..f5a9aeb12f34 100644 --- a/test/libyul/yulInterpreterTests/dataoffset.yul +++ b/test/libyul/yulInterpreterTests/dataoffset.yul @@ -12,3 +12,4 @@ object "main" // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 000000000000000000000000000000000000000000000000000000000000006e // 0000000000000000000000000000000000000000000000000000000000000001: 000000000000000000000000000000000000000000000000000000000000070c +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/datasize.yul b/test/libyul/yulInterpreterTests/datasize.yul index 6e38e9321115..ff80edee7c94 100644 --- a/test/libyul/yulInterpreterTests/datasize.yul +++ b/test/libyul/yulInterpreterTests/datasize.yul @@ -12,3 +12,4 @@ object "main" // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000b64 // 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000109 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/difficulty.yul b/test/libyul/yulInterpreterTests/difficulty.yul index 7a59bade2a47..bea70cb44dbb 100644 --- a/test/libyul/yulInterpreterTests/difficulty.yul +++ b/test/libyul/yulInterpreterTests/difficulty.yul @@ -8,3 +8,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000009999999 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/exp.yul b/test/libyul/yulInterpreterTests/exp.yul index 07e7d7f7234c..15825b8a7592 100644 --- a/test/libyul/yulInterpreterTests/exp.yul +++ b/test/libyul/yulInterpreterTests/exp.yul @@ -6,3 +6,4 @@ // Memory dump: // 0: 8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e39 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/expr_nesting_depth_exceeded.yul b/test/libyul/yulInterpreterTests/expr_nesting_depth_exceeded.yul index c1456b00f355..5447dcb91c71 100644 --- a/test/libyul/yulInterpreterTests/expr_nesting_depth_exceeded.yul +++ b/test/libyul/yulInterpreterTests/expr_nesting_depth_exceeded.yul @@ -12,3 +12,4 @@ // Maximum expression nesting level reached. // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/expr_nesting_depth_not_exceeded.yul b/test/libyul/yulInterpreterTests/expr_nesting_depth_not_exceeded.yul index 0a347057129b..19aa3677f0fa 100644 --- a/test/libyul/yulInterpreterTests/expr_nesting_depth_not_exceeded.yul +++ b/test/libyul/yulInterpreterTests/expr_nesting_depth_not_exceeded.yul @@ -12,3 +12,4 @@ // Memory dump: // 0: 000000000000000000000000000000000000000000000000000000000000001f // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/external_call_to_self.yul b/test/libyul/yulInterpreterTests/external_call_to_self.yul index ad2d2bb77f7d..3f5c887b4aeb 100644 --- a/test/libyul/yulInterpreterTests/external_call_to_self.yul +++ b/test/libyul/yulInterpreterTests/external_call_to_self.yul @@ -20,3 +20,4 @@ // 100: 0000000000000000000000000000000000000000000000000000000000000042 // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000100: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/external_call_unexecuted.yul b/test/libyul/yulInterpreterTests/external_call_unexecuted.yul index 5edfe600d25a..0a5458b47e2f 100644 --- a/test/libyul/yulInterpreterTests/external_call_unexecuted.yul +++ b/test/libyul/yulInterpreterTests/external_call_unexecuted.yul @@ -8,3 +8,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000064: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/external_callcode_unexecuted.yul b/test/libyul/yulInterpreterTests/external_callcode_unexecuted.yul index 427d5604b36c..5245d7443a93 100644 --- a/test/libyul/yulInterpreterTests/external_callcode_unexecuted.yul +++ b/test/libyul/yulInterpreterTests/external_callcode_unexecuted.yul @@ -8,3 +8,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000064: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/external_delegatecall_unexecuted.yul b/test/libyul/yulInterpreterTests/external_delegatecall_unexecuted.yul index 661782589434..a827181fca2c 100644 --- a/test/libyul/yulInterpreterTests/external_delegatecall_unexecuted.yul +++ b/test/libyul/yulInterpreterTests/external_delegatecall_unexecuted.yul @@ -8,3 +8,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000064: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/external_staticcall_unexecuted.yul b/test/libyul/yulInterpreterTests/external_staticcall_unexecuted.yul index 32b50270dad6..66ea146a06ab 100644 --- a/test/libyul/yulInterpreterTests/external_staticcall_unexecuted.yul +++ b/test/libyul/yulInterpreterTests/external_staticcall_unexecuted.yul @@ -10,3 +10,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000064: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/function_calls.yul b/test/libyul/yulInterpreterTests/function_calls.yul index 2abc26380aa6..69a351ae3fed 100644 --- a/test/libyul/yulInterpreterTests/function_calls.yul +++ b/test/libyul/yulInterpreterTests/function_calls.yul @@ -11,3 +11,4 @@ // Memory dump: // Storage dump: // 000000000000000000000000000000000000000000000000000000000000000d: 000000000000000000000000000000000000000000000000000000000000002a +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/function_scopes.yul b/test/libyul/yulInterpreterTests/function_scopes.yul index 0150fa990b08..48e29a40e4ac 100644 --- a/test/libyul/yulInterpreterTests/function_scopes.yul +++ b/test/libyul/yulInterpreterTests/function_scopes.yul @@ -18,3 +18,4 @@ // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000007 // 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000008 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/hex_literals.yul b/test/libyul/yulInterpreterTests/hex_literals.yul index 60e59b21932d..a0710de4e3f8 100644 --- a/test/libyul/yulInterpreterTests/hex_literals.yul +++ b/test/libyul/yulInterpreterTests/hex_literals.yul @@ -10,3 +10,4 @@ // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 112233445566778899aabbccddeeff6677889900000000000000000000000000 // 0000000000000000000000000000000000000000000000000000000000000001: 1234abcd00000000000000000000000000000000000000000000000000000000 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/infinite_recursion.yul b/test/libyul/yulInterpreterTests/infinite_recursion.yul index 0e3109ab5e07..91d1773f1788 100644 --- a/test/libyul/yulInterpreterTests/infinite_recursion.yul +++ b/test/libyul/yulInterpreterTests/infinite_recursion.yul @@ -9,3 +9,4 @@ // Interpreter execution step limit reached. // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul b/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul index 45d3cc781c71..207d004e3499 100644 --- a/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul +++ b/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul @@ -42,3 +42,4 @@ // Trace size limit reached. // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/leave.yul b/test/libyul/yulInterpreterTests/leave.yul index 6f082ee33e83..b127f31298ea 100644 --- a/test/libyul/yulInterpreterTests/leave.yul +++ b/test/libyul/yulInterpreterTests/leave.yul @@ -18,3 +18,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000005: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/leave_for_init.yul b/test/libyul/yulInterpreterTests/leave_for_init.yul index cd0557e71ba1..ad4965e4feac 100644 --- a/test/libyul/yulInterpreterTests/leave_for_init.yul +++ b/test/libyul/yulInterpreterTests/leave_for_init.yul @@ -16,3 +16,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000007 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/long_obect_name.yul b/test/libyul/yulInterpreterTests/long_obect_name.yul index 4105c6e42df4..ede304029997 100644 --- a/test/libyul/yulInterpreterTests/long_obect_name.yul +++ b/test/libyul/yulInterpreterTests/long_obect_name.yul @@ -17,3 +17,4 @@ object "t" { // Trace: // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/loop.yul b/test/libyul/yulInterpreterTests/loop.yul index 08ed090c33c3..cccbdc0c94c6 100644 --- a/test/libyul/yulInterpreterTests/loop.yul +++ b/test/libyul/yulInterpreterTests/loop.yul @@ -8,3 +8,4 @@ // Memory dump: // 40: 0000000000000000000000900000000000000000000000000000000000000000 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy.yul b/test/libyul/yulInterpreterTests/mcopy.yul index 7331544b8b1b..6483239cb151 100644 --- a/test/libyul/yulInterpreterTests/mcopy.yul +++ b/test/libyul/yulInterpreterTests/mcopy.yul @@ -32,3 +32,4 @@ // 100: 1111111111111111111111111111111122222222222222222222222222222222 // 120: 0000000000000000000000000000000044000000000000000000000000000000 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_memory_access_out_of_range.yul b/test/libyul/yulInterpreterTests/mcopy_memory_access_out_of_range.yul index 45599109b5a3..d45163b8ff2a 100644 --- a/test/libyul/yulInterpreterTests/mcopy_memory_access_out_of_range.yul +++ b/test/libyul/yulInterpreterTests/mcopy_memory_access_out_of_range.yul @@ -19,3 +19,4 @@ // Memory dump: // FFFFFFFFFFFFFFE0: 0000000000000000000000000000000000000000000000000000000000000001 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_read.yul b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_read.yul index b0b2b561e747..a20dc7b07267 100644 --- a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_read.yul +++ b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_read.yul @@ -10,3 +10,4 @@ // MCOPY(48, 48, 0) // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_write.yul b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_write.yul index 0448b20c0669..69c4fedf8309 100644 --- a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_write.yul +++ b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_write.yul @@ -11,3 +11,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000040 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_zero_size.yul b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_zero_size.yul index 0448b20c0669..69c4fedf8309 100644 --- a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_zero_size.yul +++ b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_zero_size.yul @@ -11,3 +11,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000040 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_overlap.yul b/test/libyul/yulInterpreterTests/mcopy_overlap.yul index 2ac2460d7fe3..aefb84beada2 100644 --- a/test/libyul/yulInterpreterTests/mcopy_overlap.yul +++ b/test/libyul/yulInterpreterTests/mcopy_overlap.yul @@ -19,3 +19,4 @@ // 40: 1111111122222222333333334444444455555555666666661111111122222222 // 60: 3333333344444444555555556666666677777777888888880000000000000000 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul b/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul index 4e431a6b182e..ec9ca2fe6437 100644 --- a/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul +++ b/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul @@ -8,3 +8,4 @@ // CALL(0, 0, 0, 0, 0, 0, 0) // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul b/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul index b520b9181cbd..e9a63dcb5a50 100644 --- a/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul +++ b/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul @@ -9,3 +9,4 @@ // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000001337 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/prevrandao.yul b/test/libyul/yulInterpreterTests/prevrandao.yul index 809506c9fe56..29a5c114eaee 100644 --- a/test/libyul/yulInterpreterTests/prevrandao.yul +++ b/test/libyul/yulInterpreterTests/prevrandao.yul @@ -8,3 +8,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000010000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/recursion.yul b/test/libyul/yulInterpreterTests/recursion.yul index 0d80faf30d62..777e01857d92 100644 --- a/test/libyul/yulInterpreterTests/recursion.yul +++ b/test/libyul/yulInterpreterTests/recursion.yul @@ -12,3 +12,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000015 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/recursive_function_for_loop.yul b/test/libyul/yulInterpreterTests/recursive_function_for_loop.yul index b3cd4b48c0e1..9d2c08ffeb46 100644 --- a/test/libyul/yulInterpreterTests/recursive_function_for_loop.yul +++ b/test/libyul/yulInterpreterTests/recursive_function_for_loop.yul @@ -20,3 +20,4 @@ // 0: 0001000000000000000000000000000000000000000000000000000000000000 // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000002 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/self_balance.yul b/test/libyul/yulInterpreterTests/self_balance.yul index 99b1f960c271..b61f470c8691 100644 --- a/test/libyul/yulInterpreterTests/self_balance.yul +++ b/test/libyul/yulInterpreterTests/self_balance.yul @@ -14,3 +14,4 @@ // 40: 0000000000000000000000000000000000000000000000000000000022223333 // 60: 0000000000000000000000000000000000000000000000000000000022222222 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/shadowed_symbol.yul b/test/libyul/yulInterpreterTests/shadowed_symbol.yul index 335f98a02c90..b6443f51f694 100644 --- a/test/libyul/yulInterpreterTests/shadowed_symbol.yul +++ b/test/libyul/yulInterpreterTests/shadowed_symbol.yul @@ -17,3 +17,4 @@ // Trace: // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/side_effect_free.yul b/test/libyul/yulInterpreterTests/side_effect_free.yul index 1b8107646ffc..adedd299f579 100644 --- a/test/libyul/yulInterpreterTests/side_effect_free.yul +++ b/test/libyul/yulInterpreterTests/side_effect_free.yul @@ -18,3 +18,4 @@ // Trace: // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/simple_mstore.yul b/test/libyul/yulInterpreterTests/simple_mstore.yul index 3beef347dbee..82b088e91f83 100644 --- a/test/libyul/yulInterpreterTests/simple_mstore.yul +++ b/test/libyul/yulInterpreterTests/simple_mstore.yul @@ -6,3 +6,4 @@ // Memory dump: // 20: 0000000000000000000b00000000000000000000000000000000000000000000 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/smoke.yul b/test/libyul/yulInterpreterTests/smoke.yul index f7f1a1aefb8a..f1d3564c06ae 100644 --- a/test/libyul/yulInterpreterTests/smoke.yul +++ b/test/libyul/yulInterpreterTests/smoke.yul @@ -3,3 +3,4 @@ // Trace: // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/switch_statement.yul b/test/libyul/yulInterpreterTests/switch_statement.yul index 1a067ee1bd0d..52b4319df7a7 100644 --- a/test/libyul/yulInterpreterTests/switch_statement.yul +++ b/test/libyul/yulInterpreterTests/switch_statement.yul @@ -9,3 +9,4 @@ // Memory dump: // 20: 0200000000000000000000000000000000000000000000000000000000000000 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/transient_storage.yul b/test/libyul/yulInterpreterTests/transient_storage.yul new file mode 100644 index 000000000000..3e9466f8e405 --- /dev/null +++ b/test/libyul/yulInterpreterTests/transient_storage.yul @@ -0,0 +1,13 @@ +{ + tstore(0, 8) + mstore(0, tload(0)) +} +// ==== +// EVMVersion: >=cancun +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000008 +// Storage dump: +// Transient storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000008 diff --git a/test/libyul/yulInterpreterTests/zero_length_reads.yul b/test/libyul/yulInterpreterTests/zero_length_reads.yul index 6c1f38d1e9ff..661db49ffbb6 100644 --- a/test/libyul/yulInterpreterTests/zero_length_reads.yul +++ b/test/libyul/yulInterpreterTests/zero_length_reads.yul @@ -38,3 +38,4 @@ // RETURN(0, 0) // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/zero_length_reads_and_revert.yul b/test/libyul/yulInterpreterTests/zero_length_reads_and_revert.yul index 4be49eeb0977..0d0f92420e30 100644 --- a/test/libyul/yulInterpreterTests/zero_length_reads_and_revert.yul +++ b/test/libyul/yulInterpreterTests/zero_length_reads_and_revert.yul @@ -38,3 +38,4 @@ // REVERT(0, 0) // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/zero_range.yul b/test/libyul/yulInterpreterTests/zero_range.yul index 1ad25ed37bca..50b604358846 100644 --- a/test/libyul/yulInterpreterTests/zero_range.yul +++ b/test/libyul/yulInterpreterTests/zero_range.yul @@ -8,3 +8,4 @@ // CALLDATACOPY(0, 0, 0) // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/transient_storage.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/transient_storage.yul new file mode 100644 index 000000000000..f50e1ba06ab5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/transient_storage.yul @@ -0,0 +1,25 @@ +{ + tstore(0, 42) + tstore(0, 42) + let x := tload(0) + tstore(0, 42) + x := tload(0) + tstore(0, 24) + x := tload(0) + tstore(0, 24) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// tstore(0, 42) +// tstore(0, 42) +// let x := tload(0) +// tstore(0, 42) +// x := tload(0) +// tstore(0, 24) +// x := tload(0) +// tstore(0, 24) +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/transient_storage.yul b/test/libyul/yulOptimizerTests/fullSuite/transient_storage.yul new file mode 100644 index 000000000000..44cd0a7f553e --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/transient_storage.yul @@ -0,0 +1,21 @@ +{ + tstore(0, 13) + tstore(0, 42) + tstore(0x20, tload(0)) + let x:= tload(0x20) + tstore(0, x) + pop(tload(0x20)) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: fullSuite +// +// { +// { +// tstore(0, 13) +// tstore(0, 42) +// tstore(0x20, tload(0)) +// tstore(0, tload(0x20)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_transient_storage.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_transient_storage.yul new file mode 100644 index 000000000000..4c766c0bc8eb --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_transient_storage.yul @@ -0,0 +1,20 @@ +{ + for { let i := 1 } iszero(eq(i, 10)) { i := add(i, 1) } + { + tstore(0, i) + sstore(i, tload(0)) + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: loopInvariantCodeMotion +// +// { +// let i := 1 +// for { } iszero(eq(i, 10)) { i := add(i, 1) } +// { +// tstore(0, i) +// sstore(i, tload(0)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/transient_storage.yul b/test/libyul/yulOptimizerTests/unusedPruner/transient_storage.yul new file mode 100644 index 000000000000..de0832053b9f --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/transient_storage.yul @@ -0,0 +1,20 @@ +{ + tstore(0, 5) + let x:= tload(0) + tstore(0, 8) + let y := tload(0) + tstore(0, y) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedPruner +// +// { +// { +// tstore(0, 5) +// tstore(0, 8) +// let y := tload(0) +// tstore(0, y) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/tload.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/tload.yul new file mode 100644 index 000000000000..43b96f0e92a3 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/tload.yul @@ -0,0 +1,22 @@ +{ + let zero := 0 + tstore(zero, 5) + let x := tload(zero) + tstore(zero, 8) + let y := tload(zero) + tstore(zero, y) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// let zero := 0 +// tstore(zero, 5) +// let x := tload(zero) +// tstore(zero, 8) +// tstore(zero, tload(zero)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/tstore.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/tstore.yul new file mode 100644 index 000000000000..f3befb79d0ad --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/tstore.yul @@ -0,0 +1,23 @@ +{ + let x := 5 + tstore(x, 10) + sstore(x, 10) + pop(mload(0)) + tstore(x, 10) + sstore(x, 20) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// let x := 5 +// tstore(x, 10) +// let _2 := 10 +// pop(mload(0)) +// tstore(x, 10) +// sstore(x, 20) +// } +// } diff --git a/test/libyul/yulSyntaxTests/tload_as_identifier_post_cancun.yul b/test/libyul/yulSyntaxTests/tload_as_identifier_post_cancun.yul new file mode 100644 index 000000000000..2c1dcb0d92cf --- /dev/null +++ b/test/libyul/yulSyntaxTests/tload_as_identifier_post_cancun.yul @@ -0,0 +1,9 @@ +{ + function f() { + let tload := 0 + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (33-38): Cannot use builtin function name "tload" as identifier name. diff --git a/test/libyul/yulSyntaxTests/tstore_as_identifier_post_cancun.yul b/test/libyul/yulSyntaxTests/tstore_as_identifier_post_cancun.yul new file mode 100644 index 000000000000..d810ed2b38aa --- /dev/null +++ b/test/libyul/yulSyntaxTests/tstore_as_identifier_post_cancun.yul @@ -0,0 +1,7 @@ +{ + function tstore() {} +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (15-21): Cannot use builtin function name "tstore" as identifier name. diff --git a/test/libyul/yulSyntaxTests/tstore_tload.yul b/test/libyul/yulSyntaxTests/tstore_tload.yul new file mode 100644 index 000000000000..e73429351d43 --- /dev/null +++ b/test/libyul/yulSyntaxTests/tstore_tload.yul @@ -0,0 +1,11 @@ +{ + function f() { + let x := tload(0) + let y := add(x, 1) + tstore(0, y) + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 2394: (82-88): Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. diff --git a/test/libyul/yulSyntaxTests/tstore_tload_as_identifiers_pre_cancun.yul b/test/libyul/yulSyntaxTests/tstore_tload_as_identifiers_pre_cancun.yul new file mode 100644 index 000000000000..19e9d2f5280b --- /dev/null +++ b/test/libyul/yulSyntaxTests/tstore_tload_as_identifiers_pre_cancun.yul @@ -0,0 +1,6 @@ +{ + function tstore() {} + function tload() {} +} +// ==== +// EVMVersion: storage; + std::map transientStorage; util::h160 address = util::h160("0x0000000000000000000000000000000011111111"); u256 balance = 0x22222222; u256 selfbalance = 0x22223333; @@ -125,6 +126,8 @@ struct InterpreterState void dumpTraceAndState(std::ostream& _out, bool _disableMemoryTrace) const; /// Prints non-zero storage to @param _out. void dumpStorage(std::ostream& _out) const; + /// Prints non-zero transient storage to @param _out. + void dumpTransientStorage(std::ostream& _out) const; bytes readMemory(u256 const& _offset, u256 const& _size) {