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 30, 2024
1 parent ec95423 commit 815e939
Show file tree
Hide file tree
Showing 33 changed files with 664 additions and 107 deletions.
30 changes: 25 additions & 5 deletions libevmasm/Assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1335,10 +1335,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 @@ -1405,7 +1409,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 @@ -1419,6 +1427,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 @@ -99,6 +99,17 @@ class Assembly
append(AssemblyItem(std::move(_data), _arguments, _returnVariables));
}

AssemblyItem appendEOFCreate(ContainerID _containerId)
{
solAssert(_containerId < m_subs.size(), "EOF Create of undefined container.");
return append(AssemblyItem::eofCreate(_containerId));
}
AssemblyItem appendReturnContract(ContainerID _containerId)
{
solAssert(_containerId < m_subs.size(), "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
23 changes: 22 additions & 1 deletion libevmasm/AssemblyItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ std::pair<std::string, std::string> AssemblyItem::nameAndData(langutil::EVMVersi
switch (type())
{
case Operation:
case EOFCreate:
case ReturnContract:
return {instructionInfo(instruction(), _evmVersion).name, m_data != nullptr ? toStringInHex(*m_data) : ""};
case Push:
return {"PUSH", toStringInHex(data())};
Expand Down Expand Up @@ -167,6 +169,10 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _
return std::get<2>(*m_verbatimBytecode).size();
case AuxDataLoadN:
return 1 + 2;
case EOFCreate:
return 2;
case ReturnContract:
return 2;
case UndefinedItem:
solAssert(false);
}
Expand All @@ -176,7 +182,10 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _

size_t AssemblyItem::arguments() const
{
if (type() == Operation)
if (type() == Operation ||
type() == EOFCreate ||
type() == ReturnContract
)
// The latest EVMVersion is used here, since the InstructionInfo is assumed to be
// the same across all EVM versions except for the instruction name.
return static_cast<size_t>(instructionInfo(instruction(), EVMVersion()).args);
Expand All @@ -193,6 +202,8 @@ size_t AssemblyItem::returnValues() const
switch (m_type)
{
case Operation:
case EOFCreate:
case ReturnContract:
// The latest EVMVersion is used here, since the InstructionInfo is assumed to be
// the same across all EVM versions except for the instruction name.
return static_cast<size_t>(instructionInfo(instruction(), EVMVersion()).ret);
Expand Down Expand Up @@ -226,6 +237,8 @@ bool AssemblyItem::canBeFunctional() const
switch (m_type)
{
case Operation:
case EOFCreate:
case ReturnContract:
return !isDupInstruction(instruction()) && !isSwapInstruction(instruction());
case Push:
case PushTag:
Expand Down Expand Up @@ -344,6 +357,12 @@ std::string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
assertThrow(data() <= std::numeric_limits<size_t>::max(), AssemblyException, "Invalid auxdataloadn argument.");
text = "auxdataloadn{" + std::to_string(static_cast<size_t>(data())) + "}";
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 All @@ -362,6 +381,8 @@ std::ostream& solidity::evmasm::operator<<(std::ostream& _out, AssemblyItem cons
switch (_item.type())
{
case Operation:
case EOFCreate:
case ReturnContract:
_out << " " << instructionInfo(_item.instruction(), EVMVersion()).name;
if (_item.instruction() == Instruction::JUMP || _item.instruction() == Instruction::JUMPI)
_out << "\t" << _item.getJumpTypeAsString();
Expand Down
33 changes: 31 additions & 2 deletions libevmasm/AssemblyItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ enum AssemblyItemType
/// Loads 32 bytes from static auxiliary data of EOF data section. The offset does *not* have to be always from the beginning
/// of the data EOF section. More details here: https://github.com/ipsilon/eof/blob/main/spec/eof.md#data-section-lifecycle
AuxDataLoadN,
EOFCreate, ///< Creates new contract using subcontainer as initcode
ReturnContract, ///< Returns new container (with auxiliary data filled in) to be deployed
VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification.
};

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

class AssemblyItem
{
Expand All @@ -85,13 +88,30 @@ class AssemblyItem
else
m_data = std::make_shared<u256>(std::move(_data));
}

explicit AssemblyItem(AssemblyItemType _type, Instruction _instruction, u256 _data = 0, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create()):
m_type(_type),
m_instruction(_instruction),
m_data(std::make_shared<u256>(std::move(_data))),
m_debugData(std::move(_debugData))
{}

explicit AssemblyItem(bytes _verbatimData, size_t _arguments, size_t _returnVariables):
m_type(VerbatimBytecode),
m_instruction{},
m_verbatimBytecode{{_arguments, _returnVariables, std::move(_verbatimData)}},
m_debugData{langutil::DebugData::create()}
{}

static AssemblyItem eofCreate(ContainerID _containerID, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
{
return AssemblyItem(EOFCreate, Instruction::EOFCREATE, _containerID, std::move(_debugData));
}
static AssemblyItem returnContract(ContainerID _containerID, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
{
return AssemblyItem(ReturnContract, Instruction::RETURNCONTRACT, _containerID, std::move(_debugData));
}

AssemblyItem(AssemblyItem const&) = default;
AssemblyItem(AssemblyItem&&) = default;
AssemblyItem& operator=(AssemblyItem const&) = default;
Expand Down Expand Up @@ -122,8 +142,17 @@ class AssemblyItem

bytes const& verbatimData() const { assertThrow(m_type == VerbatimBytecode, util::Exception, ""); return std::get<2>(*m_verbatimBytecode); }

/// @returns the instruction of this item (only valid if type() == Operation)
Instruction instruction() const { assertThrow(m_type == Operation, util::Exception, ""); return m_instruction; }
/// @returns true if the item has m_instruction properly set.
bool hasInstruction() const
{
return m_type == Operation || m_type == EOFCreate || m_type == ReturnContract;
}
/// @returns the instruction of this item (only valid if type() == Operation || EOFCreate || ReturnContract)
Instruction instruction() const
{
solAssert(hasInstruction());
return m_instruction;
}

/// @returns true if the type and data of the items are equal.
bool operator==(AssemblyItem const& _other) const
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 to be deployed with axiliary data filled in.
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
50 changes: 47 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,9 @@ bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)

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

switch (_item.instruction())
{
// note that CALL, CALLCODE and CREATE do not really alter the control flow, because we
Expand All @@ -293,6 +322,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 +331,7 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)

bool SemanticInformation::terminatesControlFlow(AssemblyItem const& _item)
{
if (_item.type() != evmasm::Operation)
if (!_item.hasInstruction())
return false;
return terminatesControlFlow(_item.instruction());
}
Expand All @@ -315,6 +345,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 +368,7 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
{
assertThrow(_item.type() != VerbatimBytecode, AssemblyException, "");

if (_item.type() != evmasm::Operation)
if (!_item.hasInstruction())
return true;

switch (_item.instruction())
Expand All @@ -357,6 +388,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 +468,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 +507,8 @@ SemanticInformation::Effect SemanticInformation::storage(Instruction _instructio
case Instruction::CREATE:
case Instruction::CREATE2:
case Instruction::SSTORE:
case Instruction::EOFCREATE:
case Instruction::RETURNCONTRACT:
return SemanticInformation::Write;

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

case Instruction::TLOAD:
Expand All @@ -514,6 +552,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 +628,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
Loading

0 comments on commit 815e939

Please sign in to comment.