Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: add support for transient storage in the solidity #14957

Draft
wants to merge 19 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions contracts/Test.sol
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity >0.8.25;

contract Test {
uint256 transient tlock;

modifier reentrancyGuard() {
require(tlock == 0, 'Already locked');
tlock = 1;
_;
tlock = 0;
}

function requiresLock() external reentrancyGuard {
require(true, 'Yay');
}
}
17 changes: 17 additions & 0 deletions contracts/TestStorage.sol
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity >0.8.25;

contract Test {
uint256 slock;

modifier reentrancyGuard() {
require(slock == 0, 'Already locked');
slock = 1;
_;
slock = 0;
}

function requiresLock() external reentrancyGuard {
require(true, 'Yay');
}
}
23 changes: 23 additions & 0 deletions contracts/TestYul.sol
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity >0.8.25;

contract Test {
modifier reentrancyGuard {
uint256 tlock;
assembly {
tlock := tload(0)
}
require(tlock == 0, 'Already locked');
assembly {
tstore(0, 1)
}
_;
assembly {
tstore(0, 0)
}
}

function requiresLock() external reentrancyGuard {
require(true, 'Yay');
}
}
67 changes: 67 additions & 0 deletions contracts/example.sol
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: MIT
pragma solidity >0.8.25;

library Lib {
function set(mapping(address => uint256) transient m, uint256 v) internal { m[msg.sender] += v; }
function set(mapping(address => uint256) storage m, uint256 v) internal { m[msg.sender] += v; }
}

contract Test {
using Lib for mapping(address => uint256);

struct MyStruct {
uint256 v;
mapping(address => uint256) m;
}

bytes transient /*public*/ b_t;
bytes /*public*/ b_s;
uint256 transient public v_t;
uint256 public v_s;
uint256[] transient public a_t;
uint256[] public a_s;
MyStruct transient public s_t;
MyStruct public s_s;
mapping(address => uint256) transient public m_t;
mapping(address => uint256) public m_s;
mapping(uint256 => mapping(address => uint256)) transient public m2_t;
mapping(uint256 => mapping(address => uint256)) public m2_s;

function id(mapping(address => uint256) transient m) internal pure returns (mapping(address => uint256) transient) { return m; }
function id(mapping(address => uint256) storage m) internal pure returns (mapping(address => uint256) storage ) { return m; }
function set(mapping(address => uint256) transient m, uint256 v) internal { m[msg.sender] = v; }
function set(mapping(address => uint256) storage m, uint256 v) internal { m[msg.sender] = v; }

function set(uint256 value) public {
b_t[0] = 0xFF;
b_s[0] = 0xFF;
v_t += value;
v_s += value;
a_t.push(value);
a_s.push(value);
a_t[0] += value;
a_s[0] += value;
s_t.v += value;
s_s.v += value;
s_t.m.set(value);
s_s.m.set(value);
// m_t.set(value);
// m_s.set(value);
id(m_t).set(value);
id(m_s).set(value);
m2_t[0].set(value);
m2_s[0].set(value);


MyStruct transient s_t_ref = s_t;
s_t_ref.v += value;

MyStruct transient s;
assembly { s.slot := 0 }
s.v *= value;

assembly {
tstore(0, add(tload(0), value))
}
}
}
18 changes: 9 additions & 9 deletions libevmasm/Instruction.cpp
Expand Up @@ -36,8 +36,10 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "SDIV", Instruction::SDIV },
{ "MOD", Instruction::MOD },
{ "SMOD", Instruction::SMOD },
{ "ADDMOD", Instruction::ADDMOD },
{ "MULMOD", Instruction::MULMOD },
{ "EXP", Instruction::EXP },
{ "NOT", Instruction::NOT },
{ "SIGNEXTEND", Instruction::SIGNEXTEND },
{ "LT", Instruction::LT },
{ "GT", Instruction::GT },
{ "SLT", Instruction::SLT },
Expand All @@ -47,13 +49,11 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "AND", Instruction::AND },
{ "OR", Instruction::OR },
{ "XOR", Instruction::XOR },
{ "NOT", Instruction::NOT },
{ "BYTE", Instruction::BYTE },
{ "SHL", Instruction::SHL },
{ "SHR", Instruction::SHR },
{ "SAR", Instruction::SAR },
{ "ADDMOD", Instruction::ADDMOD },
{ "MULMOD", Instruction::MULMOD },
{ "SIGNEXTEND", Instruction::SIGNEXTEND },
{ "KECCAK256", Instruction::KECCAK256 },
{ "ADDRESS", Instruction::ADDRESS },
{ "BALANCE", Instruction::BALANCE },
Expand All @@ -70,10 +70,8 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "EXTCODECOPY", Instruction::EXTCODECOPY },
{ "RETURNDATASIZE", Instruction::RETURNDATASIZE },
{ "RETURNDATACOPY", Instruction::RETURNDATACOPY },
{ "MCOPY", Instruction::MCOPY },
{ "EXTCODEHASH", Instruction::EXTCODEHASH },
{ "BLOCKHASH", Instruction::BLOCKHASH },
{ "BLOBHASH", Instruction::BLOBHASH },
{ "COINBASE", Instruction::COINBASE },
{ "TIMESTAMP", Instruction::TIMESTAMP },
{ "NUMBER", Instruction::NUMBER },
Expand All @@ -83,21 +81,23 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "CHAINID", Instruction::CHAINID },
{ "SELFBALANCE", Instruction::SELFBALANCE },
{ "BASEFEE", Instruction::BASEFEE },
{ "BLOBHASH", Instruction::BLOBHASH },
{ "BLOBBASEFEE", Instruction::BLOBBASEFEE },
{ "POP", Instruction::POP },
{ "MLOAD", Instruction::MLOAD },
{ "MSTORE", Instruction::MSTORE },
{ "MSTORE8", Instruction::MSTORE8 },
{ "SLOAD", Instruction::SLOAD },
{ "SSTORE", Instruction::SSTORE },
{ "TLOAD", Instruction::TLOAD },
{ "TSTORE", Instruction::TSTORE },
{ "JUMP", Instruction::JUMP },
{ "JUMPI", Instruction::JUMPI },
{ "PC", Instruction::PC },
{ "MSIZE", Instruction::MSIZE },
{ "GAS", Instruction::GAS },
{ "JUMPDEST", Instruction::JUMPDEST },
{ "TLOAD", Instruction::TLOAD },
{ "TSTORE", Instruction::TSTORE },
{ "MCOPY", Instruction::MCOPY },
{ "PUSH0", Instruction::PUSH0 },
{ "PUSH1", Instruction::PUSH1 },
{ "PUSH2", Instruction::PUSH2 },
Expand Down Expand Up @@ -171,10 +171,10 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "CREATE", Instruction::CREATE },
{ "CALL", Instruction::CALL },
{ "CALLCODE", Instruction::CALLCODE },
{ "STATICCALL", Instruction::STATICCALL },
{ "RETURN", Instruction::RETURN },
{ "DELEGATECALL", Instruction::DELEGATECALL },
{ "CREATE2", Instruction::CREATE2 },
{ "STATICCALL", Instruction::STATICCALL },
{ "REVERT", Instruction::REVERT },
{ "INVALID", Instruction::INVALID },
{ "SELFDESTRUCT", Instruction::SELFDESTRUCT }
Expand Down
15 changes: 7 additions & 8 deletions libevmasm/Instruction.h
Expand Up @@ -88,9 +88,9 @@ enum class Instruction: uint8_t
GASLIMIT, ///< get the block's gas limit
CHAINID, ///< get the config's chainid param
SELFBALANCE, ///< get balance of the current account
BASEFEE, ///< get the block's basefee
BLOBHASH = 0x49, ///< get a versioned hash of one of the blobs associated with the transaction
BLOBBASEFEE = 0x4a, ///< get the block's blob basefee
BASEFEE, ///< get the block's basefee
BLOBHASH = 0x49, ///< get a versioned hash of one of the blobs associated with the transaction
BLOBBASEFEE = 0x4a, ///< get the block's blob basefee

POP = 0x50, ///< remove item from stack
MLOAD, ///< load word from memory
Expand All @@ -104,12 +104,11 @@ enum class Instruction: uint8_t
MSIZE, ///< get the size of active memory
GAS, ///< get the amount of available gas
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
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
PUSH0 = 0x5f, ///< place the value 0 on stack
PUSH1 = 0x60, ///< place 1 byte item on stack
PUSH2, ///< place 2 byte item on stack
PUSH3, ///< place 3 byte item on stack
Expand Down
5 changes: 4 additions & 1 deletion libevmasm/KnownState.cpp
Expand Up @@ -259,14 +259,17 @@ void KnownState::reduceToCommonKnowledge(KnownState const& _other, bool _combine
}

intersect(m_storageContent, _other.m_storageContent);
intersect(m_transientStorageContent, _other.m_transientStorageContent);
intersect(m_memoryContent, _other.m_memoryContent);
if (_combineSequenceNumbers)
m_sequenceNumber = std::max(m_sequenceNumber, _other.m_sequenceNumber);
}

bool KnownState::operator==(KnownState const& _other) const
{
if (m_storageContent != _other.m_storageContent || m_memoryContent != _other.m_memoryContent)
if (m_storageContent != _other.m_storageContent ||
m_transientStorageContent != _other.m_transientStorageContent ||
m_memoryContent != _other.m_memoryContent)
return false;
int stackDiff = m_stackHeight - _other.m_stackHeight;
auto thisIt = m_stackElements.cbegin();
Expand Down
5 changes: 5 additions & 0 deletions libevmasm/KnownState.h
Expand Up @@ -109,6 +109,8 @@ class KnownState

/// Resets any knowledge about storage.
void resetStorage() { m_storageContent.clear(); }
/// Reset any knowledge about transient storage.
void resetTransientStorage() { m_transientStorageContent.clear(); }
/// Resets any knowledge about memory.
void resetMemory() { m_memoryContent.clear(); }
/// Resets known Keccak-256 hashes
Expand Down Expand Up @@ -150,6 +152,7 @@ class KnownState
ExpressionClasses& expressionClasses() const { return *m_expressionClasses; }

std::map<Id, Id> const& storageContent() const { return m_storageContent; }
std::map<Id, Id> const& transientStorageContent() const { return m_transientStorageContent; }

private:
/// Assigns a new equivalence class to the next sequence number of the given stack element.
Expand Down Expand Up @@ -183,6 +186,8 @@ class KnownState
unsigned m_sequenceNumber = 1;
/// Knowledge about storage content.
std::map<Id, Id> m_storageContent;
/// Knowledge about transient storage content.
std::map<Id, Id> m_transientStorageContent;
/// Knowledge about memory content. Keys are memory addresses, note that the values overlap
/// and are not contained here if they are not completely known.
std::map<Id, Id> m_memoryContent;
Expand Down
5 changes: 5 additions & 0 deletions libevmasm/SemanticInformation.cpp
Expand Up @@ -226,6 +226,9 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
return false;
if (_item.instruction() == Instruction::MSTORE)
return false;
/// [Amxx] TODO: Transient is not supported by the optimize yet
// if (_item.instruction() == Instruction::TSTORE)
// return false;
if (!_msizeImportant && (
_item.instruction() == Instruction::MLOAD ||
_item.instruction() == Instruction::KECCAK256
Expand Down Expand Up @@ -411,6 +414,7 @@ SemanticInformation::Effect SemanticInformation::memory(Instruction _instruction
case Instruction::MCOPY:
case Instruction::MSTORE:
case Instruction::MSTORE8:
case Instruction::TSTORE:
case Instruction::CALL:
case Instruction::CALLCODE:
case Instruction::DELEGATECALL:
Expand All @@ -421,6 +425,7 @@ SemanticInformation::Effect SemanticInformation::memory(Instruction _instruction
case Instruction::CREATE2:
case Instruction::KECCAK256:
case Instruction::MLOAD:
case Instruction::TLOAD:
case Instruction::MSIZE:
case Instruction::RETURN:
case Instruction::REVERT:
Expand Down
3 changes: 2 additions & 1 deletion liblangutil/Token.h
Expand Up @@ -182,6 +182,7 @@ namespace solidity::langutil
K(Return, "return", 0) \
K(Returns, "returns", 0) \
K(Storage, "storage", 0) \
K(Transient, "transient", 0) \
K(CallData, "calldata", 0) \
K(Struct, "struct", 0) \
K(Throw, "throw", 0) \
Expand Down Expand Up @@ -316,7 +317,7 @@ namespace TokenTraits
constexpr bool isShiftOp(Token op) { return (Token::SHL <= op) && (op <= Token::SHR); }
constexpr bool isVariableVisibilitySpecifier(Token op) { return op == Token::Public || op == Token::Private || op == Token::Internal; }
constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External; }
constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::CallData; }
constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::Transient || op == Token::CallData; }

constexpr bool isStateMutabilitySpecifier(Token op)
{
Expand Down
19 changes: 16 additions & 3 deletions libsolidity/analysis/DeclarationTypeChecker.cpp
Expand Up @@ -276,7 +276,7 @@ void DeclarationTypeChecker::endVisit(Mapping const& _mapping)

// Convert value type to storage reference.
valueType = TypeProvider::withLocationIfReference(DataLocation::Storage, valueType);
_mapping.annotation().type = TypeProvider::mapping(keyType, keyName, valueType, valueName);
_mapping.annotation().type = TypeProvider::mapping(keyType, keyName, valueType, valueName, DataLocation::Storage);

// Check if parameter names are conflicting.
if (!keyName.empty())
Expand Down Expand Up @@ -406,6 +406,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
switch (_location)
{
case Location::Memory: return "\"memory\"";
case Location::Transient: return "\"transient\"";
case Location::Storage: return "\"storage\"";
case Location::CallData: return "\"calldata\"";
case Location::Unspecified: return "none";
Expand Down Expand Up @@ -456,8 +457,17 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
}
else if (_variable.isStateVariable())
{
solAssert(varLoc == Location::Unspecified, "");
typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage;
switch (varLoc)
{
case Location::Unspecified:
typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage;
break;
case Location::Transient:
typeLoc = DataLocation::Transient;
break;
default:
solAssert(false, "");
}
}
else if (
dynamic_cast<StructDefinition const*>(_variable.scope()) ||
Expand All @@ -471,6 +481,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
case Location::Memory:
typeLoc = DataLocation::Memory;
break;
case Location::Transient:
typeLoc = DataLocation::Transient;
break;
case Location::Storage:
typeLoc = DataLocation::Storage;
break;
Expand Down