Skip to content

Commit

Permalink
eof: Support EOF contract creation.
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet committed Oct 21, 2024
1 parent 35e0796 commit 46af949
Show file tree
Hide file tree
Showing 29 changed files with 532 additions and 69 deletions.
30 changes: 25 additions & 5 deletions libevmasm/Assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1340,10 +1340,14 @@ std::map<uint16_t, uint16_t> Assembly::findReferencedContainers() const
std::set<uint16_t> referencedSubcontainersIds;
solAssert(m_subs.size() <= 0x100); // According to EOF spec

// TODO: Implement properly when opcodes referring sub containers added.
for (uint16_t i = 0; i < m_subs.size(); ++i)
referencedSubcontainersIds.insert(static_cast<uint16_t>(i));
// END TODO
for (auto&& codeSection: m_codeSections)
for (AssemblyItem const& item: codeSection.items)
if (item.type() == EofCreate || item.type() == ReturnContract)
{
solAssert(item.data() <= m_subs.size(), "Invalid subcontainer index.");
auto const containerId = static_cast<ContainerID>(item.data());
referencedSubcontainersIds.insert(containerId);
}

std::map<uint16_t, uint16_t> replacements;
uint8_t nUnreferenced = 0;
Expand Down Expand Up @@ -1428,7 +1432,11 @@ LinkerObject const& Assembly::assembleEOF() const
switch (item.type())
{
case Operation:
solAssert(item.instruction() != Instruction::DATALOADN);
solAssert(
item.instruction() != Instruction::DATALOADN &&
item.instruction() != Instruction::RETURNCONTRACT &&
item.instruction() != Instruction::EOFCREATE
);
solAssert(!(item.instruction() >= Instruction::PUSH0 && item.instruction() <= Instruction::PUSH32));
ret.bytecode += assembleOperation(item);
break;
Expand All @@ -1442,6 +1450,18 @@ LinkerObject const& Assembly::assembleEOF() const
ret.linkReferences.insert(linkRef);
break;
}
case EofCreate:
{
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::EOFCREATE));
ret.bytecode.push_back(static_cast<uint8_t>(item.data()));
break;
}
case ReturnContract:
{
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::RETURNCONTRACT));
ret.bytecode.push_back(static_cast<uint8_t>(item.data()));
break;
}
case VerbatimBytecode:
ret.bytecode += assembleVerbatimBytecode(item);
break;
Expand Down
11 changes: 11 additions & 0 deletions libevmasm/Assembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ class Assembly
append(AssemblyItem(std::move(_data), _arguments, _returnVariables));
}

AssemblyItem appendEOFCreate(ContainerID _containerId)
{
assertThrow(_containerId < m_subs.size(), AssemblyException, "EOF Create of undefined container");
return append(AssemblyItem::eofCreate(_containerId));
}
AssemblyItem appendReturnContract(ContainerID _containerId)
{
assertThrow(_containerId < m_subs.size(), AssemblyException, "Return undefined container id");
return append(AssemblyItem::returnContract(_containerId));
}

AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }
Expand Down
29 changes: 29 additions & 0 deletions libevmasm/AssemblyItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ std::pair<std::string, std::string> AssemblyItem::nameAndData(langutil::EVMVersi
return {"AUXDATALOADN", util::toString(data())};
case AuxDataStore:
return {"AUXDATASTORE", ""};
case EofCreate:
return {"EOFCREATE", util::toString(data())};
case ReturnContract:
return {"RETURNCONTRACT", util::toString(data())};
case UndefinedItem:
solAssert(false);
}
Expand Down Expand Up @@ -171,6 +175,10 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _
return 1 + 2;
case AuxDataStore:
return 1;
case EofCreate:
return 2;
case ReturnContract:
return 2;
case UndefinedItem:
solAssert(false);
}
Expand All @@ -190,6 +198,10 @@ size_t AssemblyItem::arguments() const
return 2;
else if (type() == AuxDataStore)
return 3;
else if (type() == EofCreate)
return 4;
else if (type() == ReturnContract)
return 2;
else
return 0;
}
Expand All @@ -216,7 +228,10 @@ size_t AssemblyItem::returnValues() const
return 0;
case VerbatimBytecode:
return std::get<1>(*m_verbatimBytecode);
case ReturnContract:
return 0;
case AuxDataLoadN:
case EofCreate:
return 1;
case AssignImmutable:
case AuxDataStore:
Expand Down Expand Up @@ -244,8 +259,10 @@ bool AssemblyItem::canBeFunctional() const
case PushDeployTimeAddress:
case PushImmutable:
case AuxDataLoadN:
case EofCreate:
return true;
case Tag:
case ReturnContract:
return false;
case AssignImmutable:
case VerbatimBytecode:
Expand Down Expand Up @@ -355,6 +372,12 @@ std::string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
case AuxDataStore:
text = "auxdatastore()";
break;
case EofCreate:
text = "eofcreate(" + std::to_string(static_cast<size_t>(data())) + ")";
break;
case ReturnContract:
text = "returcontract(" + std::to_string(static_cast<size_t>(data())) + ")";
break;
}
if (m_jumpType == JumpType::IntoFunction || m_jumpType == JumpType::OutOfFunction)
{
Expand Down Expand Up @@ -428,6 +451,12 @@ std::ostream& solidity::evmasm::operator<<(std::ostream& _out, AssemblyItem cons
case AuxDataStore:
_out << " AuxDataStore";
break;
case EofCreate:
_out << " EofCreate" << util::toString(_item.data());
break;
case ReturnContract:
_out << " ReturnContract" << util::toString(_item.data());
break;
case UndefinedItem:
_out << " ???";
break;
Expand Down
12 changes: 12 additions & 0 deletions libevmasm/AssemblyItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ enum AssemblyItemType
/// of the data EOF section. More details here: https://github.com/ipsilon/eof/blob/main/spec/eof.md#data-section-lifecycle
AuxDataLoadN,
AuxDataStore, ///< Stores value in memory which will be appended to EOF data section as static auxiliary data.
EofCreate, ///< Creates new contract using subcontainer as initcode
ReturnContract, ///< Returns new container along (with auxiliary data section) to be deployed
VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification.
};

Expand All @@ -64,6 +66,7 @@ enum class Precision { Precise , Approximate };
class Assembly;
class AssemblyItem;
using AssemblyItems = std::vector<AssemblyItem>;
using ContainerID = uint8_t;

class AssemblyItem
{
Expand Down Expand Up @@ -93,6 +96,15 @@ class AssemblyItem
m_debugData{langutil::DebugData::create()}
{}

static AssemblyItem eofCreate(ContainerID _containerID, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
{
return AssemblyItem(EofCreate, _containerID, _debugData);
}
static AssemblyItem returnContract(ContainerID _containerID, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
{
return AssemblyItem(ReturnContract, _containerID, _debugData);
}

AssemblyItem(AssemblyItem const&) = default;
AssemblyItem(AssemblyItem&&) = default;
AssemblyItem& operator=(AssemblyItem const&) = default;
Expand Down
4 changes: 4 additions & 0 deletions libevmasm/Instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "LOG3", Instruction::LOG3 },
{ "LOG4", Instruction::LOG4 },
{ "DATALOADN", Instruction::DATALOADN },
{ "EOFCREATE", Instruction::EOFCREATE },
{ "RETURNCONTRACT", Instruction::RETURNCONTRACT },
{ "CREATE", Instruction::CREATE },
{ "CALL", Instruction::CALL },
{ "CALLCODE", Instruction::CALLCODE },
Expand Down Expand Up @@ -324,6 +326,8 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{Instruction::LOG2, {"LOG2", 0, 4, 0, true, Tier::Special}},
{Instruction::LOG3, {"LOG3", 0, 5, 0, true, Tier::Special}},
{Instruction::LOG4, {"LOG4", 0, 6, 0, true, Tier::Special}},
{Instruction::EOFCREATE, {"EOFCREATE", 1, 4, 1, true, Tier::Special}},
{Instruction::RETURNCONTRACT, {"RETURNCONTRACT", 1, 2, 0, true, Tier::Special}},
{Instruction::CREATE, {"CREATE", 0, 3, 1, true, Tier::Special}},
{Instruction::CALL, {"CALL", 0, 7, 1, true, Tier::Special}},
{Instruction::CALLCODE, {"CALLCODE", 0, 7, 1, true, Tier::Special}},
Expand Down
2 changes: 2 additions & 0 deletions libevmasm/Instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ enum class Instruction: uint8_t
LOG4, ///< Makes a log entry; 4 topics.

DATALOADN = 0xd1, ///< load data from EOF data section
EOFCREATE = 0xec, ///< create a new account with associated container code.
RETURNCONTRACT = 0xee, ///< return container id with axiliary data section to be deployed.
CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account
CALLCODE, ///< message-call with another account's code only
Expand Down
54 changes: 51 additions & 3 deletions libevmasm/SemanticInformation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,34 @@ std::vector<SemanticInformation::Operation> SemanticInformation::readWriteOperat
Operation{Location::TransientStorage, Effect::Read, {}, {}, {}},
Operation{Location::TransientStorage, Effect::Write, {}, {}, {}}
};
case Instruction::EOFCREATE:
return std::vector<Operation>{
Operation{
Location::Memory,
Effect::Read,
2,
3,
{}
},
Operation{Location::Storage, Effect::Read, {}, {}, {}},
Operation{Location::Storage, Effect::Write, {}, {}, {}},
Operation{Location::TransientStorage, Effect::Read, {}, {}, {}},
Operation{Location::TransientStorage, Effect::Write, {}, {}, {}}
};
case Instruction::RETURNCONTRACT:
return std::vector<Operation>{
Operation{
Location::Memory,
Effect::Read,
0,
1,
{}
},
Operation{Location::Storage, Effect::Read, {}, {}, {}},
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<Operation>{};
Expand Down Expand Up @@ -280,8 +308,11 @@ bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)

bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
{
if (_item.type() != evmasm::Operation)
if (_item.type() == evmasm::ReturnContract)
return true;
else if (_item.type() != evmasm::Operation)
return false;

switch (_item.instruction())
{
// note that CALL, CALLCODE and CREATE do not really alter the control flow, because we
Expand All @@ -293,6 +324,7 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
case Instruction::STOP:
case Instruction::INVALID:
case Instruction::REVERT:
case Instruction::RETURNCONTRACT:
return true;
default:
return false;
Expand All @@ -301,7 +333,9 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)

bool SemanticInformation::terminatesControlFlow(AssemblyItem const& _item)
{
if (_item.type() != evmasm::Operation)
if (_item.type() == evmasm::ReturnContract)
return true;
else if (_item.type() != evmasm::Operation)
return false;
return terminatesControlFlow(_item.instruction());
}
Expand All @@ -315,6 +349,7 @@ bool SemanticInformation::terminatesControlFlow(Instruction _instruction)
case Instruction::STOP:
case Instruction::INVALID:
case Instruction::REVERT:
case Instruction::RETURNCONTRACT:
return true;
default:
return false;
Expand All @@ -337,7 +372,9 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
{
assertThrow(_item.type() != VerbatimBytecode, AssemblyException, "");

if (_item.type() != evmasm::Operation)
if (_item.type() == evmasm::EofCreate)
return false;
else if (_item.type() != evmasm::Operation)
return true;

switch (_item.instruction())
Expand All @@ -357,6 +394,7 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
case Instruction::EXTCODEHASH:
case Instruction::RETURNDATACOPY: // depends on previous calls
case Instruction::RETURNDATASIZE:
case Instruction::EOFCREATE:
return false;
default:
return true;
Expand Down Expand Up @@ -436,6 +474,8 @@ SemanticInformation::Effect SemanticInformation::memory(Instruction _instruction
case Instruction::LOG2:
case Instruction::LOG3:
case Instruction::LOG4:
case Instruction::EOFCREATE:
case Instruction::RETURNCONTRACT:
return SemanticInformation::Read;

default:
Expand Down Expand Up @@ -473,6 +513,7 @@ SemanticInformation::Effect SemanticInformation::storage(Instruction _instructio
case Instruction::CREATE:
case Instruction::CREATE2:
case Instruction::SSTORE:
case Instruction::EOFCREATE:
return SemanticInformation::Write;

case Instruction::SLOAD:
Expand All @@ -494,6 +535,7 @@ SemanticInformation::Effect SemanticInformation::transientStorage(Instruction _i
case Instruction::CREATE:
case Instruction::CREATE2:
case Instruction::TSTORE:
case Instruction::EOFCREATE:
return SemanticInformation::Write;

case Instruction::TLOAD:
Expand All @@ -514,6 +556,8 @@ SemanticInformation::Effect SemanticInformation::otherState(Instruction _instruc
case Instruction::DELEGATECALL:
case Instruction::CREATE:
case Instruction::CREATE2:
case Instruction::EOFCREATE:
case Instruction::RETURNCONTRACT:
case Instruction::SELFDESTRUCT:
case Instruction::STATICCALL: // because it can affect returndatasize
// Strictly speaking, log0, .., log4 writes to the state, but the EVM cannot read it, so they
Expand Down Expand Up @@ -588,6 +632,10 @@ bool SemanticInformation::invalidInViewFunctions(Instruction _instruction)
case Instruction::CALL:
case Instruction::CALLCODE:
case Instruction::DELEGATECALL:
// According to EOF spec https://eips.ethereum.org/EIPS/eip-7620#eofcreate
case Instruction::EOFCREATE:
// According to EOF spec https://eips.ethereum.org/EIPS/eip-7620#returncontract
case Instruction::RETURNCONTRACT:
case Instruction::CREATE2:
case Instruction::SELFDESTRUCT:
return true;
Expand Down
2 changes: 2 additions & 0 deletions liblangutil/EVMVersion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ bool EVMVersion::hasOpcode(Instruction _opcode, std::optional<uint8_t> _eofVersi
case Instruction::GAS:
return !_eofVersion.has_value();
// Instructions below available only in EOF
case Instruction::EOFCREATE:
case Instruction::RETURNCONTRACT:
case Instruction::DATALOADN:
return _eofVersion.has_value();
default:
Expand Down
4 changes: 4 additions & 0 deletions libsolidity/codegen/ir/IRGenerationContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ class IRGenerationContext

IRGenerationContext(
langutil::EVMVersion _evmVersion,
std::optional<uint8_t> _eofVersion,
ExecutionContext _executionContext,
RevertStrings _revertStrings,
std::map<std::string, unsigned> _sourceIndices,
langutil::DebugInfoSelection const& _debugInfoSelection,
langutil::CharStreamProvider const* _soliditySourceProvider
):
m_evmVersion(_evmVersion),
m_eofVersion(_eofVersion),
m_executionContext(_executionContext),
m_revertStrings(_revertStrings),
m_sourceIndices(std::move(_sourceIndices)),
Expand Down Expand Up @@ -134,6 +136,7 @@ class IRGenerationContext
YulUtilFunctions utils();

langutil::EVMVersion evmVersion() const { return m_evmVersion; }
std::optional<uint8_t> eofVersion() const { return m_eofVersion; }
ExecutionContext executionContext() const { return m_executionContext; }

void setArithmetic(Arithmetic _value) { m_arithmetic = _value; }
Expand All @@ -160,6 +163,7 @@ class IRGenerationContext

private:
langutil::EVMVersion m_evmVersion;
std::optional<uint8_t> m_eofVersion;
ExecutionContext m_executionContext;
RevertStrings m_revertStrings;
std::map<std::string, unsigned> m_sourceIndices;
Expand Down
Loading

0 comments on commit 46af949

Please sign in to comment.