Adds support for the EVM version "Paris".

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 <alex@rtfs.hu>
Co-authored-by: Daniel <daniel@ekpyron.org>
Co-authored-by: matheusaaguiar <95899911+matheusaaguiar@users.noreply.github.com>
Co-authored-by: Nikola Matić <nikola.matic@ethereum.org>
This commit is contained in:
Rodrigo Q. Saramago
2023-01-23 18:50:36 +00:00
co-authored by Alex Beregszaszi Daniel matheusaaguiar Nikola Matić
parent d70d79af4a
commit ef6ff2f055
114 changed files with 842 additions and 221 deletions
+1 -3
View File
@@ -119,7 +119,6 @@ vector<YulString> 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())
)
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -264,7 +264,7 @@ YulStack::assembleEVMWithDeployed(optional<string_view> _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);
+1
View File
@@ -28,6 +28,7 @@
#include <libsolutil/Common.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/Numeric.h>
#include <liblangutil/EVMVersion.h>
#include <functional>
#include <memory>
+21 -4
View File
@@ -47,11 +47,12 @@ namespace
{
pair<YulString, BuiltinFunctionForEVM> 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<size_t>(info.args));
@@ -119,11 +120,19 @@ set<YulString> 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<YulString> 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<YulString>{
@@ -139,6 +148,13 @@ set<YulString> createReservedIdentifiers(langutil::EVMVersion _evmVersion)
map<YulString, BuiltinFunctionForEVM> 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<YulString, BuiltinFunctionForEVM> builtins;
for (auto const& instr: evmasm::c_instructions)
{
@@ -152,9 +168,10 @@ map<YulString, BuiltinFunctionForEVM> 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)
+3 -3
View File
@@ -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();
}
+1 -1
View File
@@ -124,7 +124,7 @@ void EthAssemblyAdapter::appendAssemblySize()
pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly(bool _creation, string _name)
{
shared_ptr<evmasm::Assembly> assembly{make_shared<evmasm::Assembly>(_creation, std::move(_name))};
shared_ptr<evmasm::Assembly> assembly{make_shared<evmasm::Assembly>(m_assembly.evmVersion(), _creation, std::move(_name))};
auto sub = m_assembly.newSub(assembly);
return {make_shared<EthAssemblyAdapter>(*assembly), static_cast<size_t>(sub.data())};
}
+1 -1
View File
@@ -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&)
+2 -1
View File
@@ -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;
};
+4 -4
View File
@@ -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<unsigned>(*depth + 1)));
opGas += evmasm::GasMeter::runGas(evmasm::dupInstruction(static_cast<unsigned>(*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;
};
+1 -1
View File
@@ -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<FunctionCall>(&_expression))
if (optional<evmasm::Instruction> instruction = toEVMInstruction(m_dialect, functionCall->functionName.name))
+1 -1
View File
@@ -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;
}
+2 -1
View File
@@ -20,6 +20,7 @@
#include <libyul/optimiser/Metrics.h>
#include <libyul/optimiser/OptimizerUtilities.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/AST.h>
#include <libyul/Exceptions.h>
@@ -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)
+7
View File
@@ -58,6 +58,13 @@ optional<evmasm::Instruction> yul::toEVMInstruction(Dialect const& _dialect, Yul
return nullopt;
}
langutil::EVMVersion const yul::evmVersionFromDialect(Dialect const& _dialect)
{
if (auto const* dialect = dynamic_cast<EVMDialect const*>(&_dialect))
return dialect->evmVersion();
return langutil::EVMVersion();
}
void StatementRemover::operator()(Block& _block)
{
util::iterateReplacing(
+5
View File
@@ -26,6 +26,7 @@
#include <libyul/Dialect.h>
#include <libyul/YulString.h>
#include <libyul/optimiser/ASTWalker.h>
#include <liblangutil/EVMVersion.h>
#include <optional>
@@ -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<evmasm::Instruction> 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:
+3 -3
View File
@@ -235,7 +235,7 @@ evmasm::Instruction Pattern::instruction() const
return m_instruction;
}
Expression Pattern::toExpression(shared_ptr<DebugData const> const& _debugData) const
Expression Pattern::toExpression(shared_ptr<DebugData const> const& _debugData, langutil::EVMVersion _evmVersion) const
{
if (matchGroup())
return ASTCopier().translate(matchGroupValue());
@@ -248,9 +248,9 @@ Expression Pattern::toExpression(shared_ptr<DebugData const> const& _debugData)
{
vector<Expression> 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}},
+1 -1
View File
@@ -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<DebugData const> const& _debugData) const;
Expression toExpression(std::shared_ptr<DebugData const> const& _debugData, langutil::EVMVersion _evmVersion) const;
private:
Expression const& matchGroupValue() const;