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 14, 2024
1 parent 1c90f4e commit 947c5dd
Show file tree
Hide file tree
Showing 20 changed files with 365 additions and 54 deletions.
25 changes: 21 additions & 4 deletions libevmasm/Assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1335,10 +1335,15 @@ 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() <= std::numeric_limits<uint16_t>::max(),
"Invalid EofCreate/ReturnContract index value.");
auto const containerId = static_cast<uint16_t>(item.data());
referencedSubcontainersIds.insert(containerId);
}

std::map<uint16_t, uint16_t> replacements;
uint8_t nUnreferenced = 0;
Expand Down Expand Up @@ -1419,6 +1424,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(uint16_t _containerId)
{
assertThrow(_containerId < m_subs.size(), AssemblyException, "EOF Create of undefined container");
return append(AssemblyItem::eofCreate(_containerId));
}
AssemblyItem appendReturnContract(uint16_t _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 @@ -104,6 +104,10 @@ std::pair<std::string, std::string> AssemblyItem::nameAndData(langutil::EVMVersi
return {"VERBATIM", util::toHex(verbatimData())};
case AuxDataLoadN:
return {"AUXDATALOADN", util::toString(data())};
case EofCreate:
return {"EOFCREATE", util::toString(data())};
case ReturnContract:
return {"RETURNCONTRACT", util::toString(data())};
case UndefinedItem:
solAssert(false);
}
Expand Down Expand Up @@ -167,6 +171,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 @@ -184,6 +192,10 @@ size_t AssemblyItem::arguments() const
return std::get<0>(*m_verbatimBytecode);
else if (type() == AssignImmutable)
return 2;
else if (type() == EofCreate)
return 4;
else if (type() == ReturnContract)
return 2;
else
return 0;
}
Expand All @@ -210,7 +222,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 UndefinedItem:
Expand All @@ -237,8 +252,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 @@ -344,6 +361,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 Down Expand Up @@ -414,6 +437,12 @@ std::ostream& solidity::evmasm::operator<<(std::ostream& _out, AssemblyItem cons
case AuxDataLoadN:
_out << "AuxDataLoadN " << util::toString(_item.data());
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
11 changes: 11 additions & 0 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 subcointainer 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 Down Expand Up @@ -92,6 +94,15 @@ class AssemblyItem
m_debugData{langutil::DebugData::create()}
{}

static AssemblyItem eofCreate(uint16_t _containerID, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
{
return AssemblyItem(EofCreate, _containerID, _debugData);
}
static AssemblyItem returnContract(uint16_t _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
6 changes: 6 additions & 0 deletions libevmasm/SemanticInformation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,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 @@ -315,6 +316,7 @@ bool SemanticInformation::terminatesControlFlow(Instruction _instruction)
case Instruction::STOP:
case Instruction::INVALID:
case Instruction::REVERT:
case Instruction::RETURNCONTRACT:
return true;
default:
return false;
Expand Down Expand Up @@ -473,6 +475,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 +497,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 +518,7 @@ SemanticInformation::Effect SemanticInformation::otherState(Instruction _instruc
case Instruction::DELEGATECALL:
case Instruction::CREATE:
case Instruction::CREATE2:
case Instruction::EOFCREATE:
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 +593,7 @@ bool SemanticInformation::invalidInViewFunctions(Instruction _instruction)
case Instruction::CALL:
case Instruction::CALLCODE:
case Instruction::DELEGATECALL:
case Instruction::EOFCREATE:
case Instruction::CREATE2:
case Instruction::SELFDESTRUCT:
return true;
Expand Down
12 changes: 12 additions & 0 deletions libsolidity/codegen/ir/IRGenerationContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ void IRGenerationContext::registerImmutableVariable(VariableDeclaration const& _
solAssert(m_reservedMemory.has_value(), "Reserved memory has already been reset.");
m_immutableVariables[&_variable] = CompilerUtils::generalPurposeMemoryStart + *m_reservedMemory;
solAssert(_variable.annotation().type->memoryHeadSize() == 32, "Memory writes might overlap.");
m_immutableVariablesOffsetsInDataSection[&_variable] = *m_reservedMemory;
*m_reservedMemory += _variable.annotation().type->memoryHeadSize();
}

Expand All @@ -115,6 +116,17 @@ size_t IRGenerationContext::reservedMemory()
return reservedMemory;
}

size_t IRGenerationContext::immutablesMemorySize() const
{
solAssert(m_reservedMemory.has_value(), "Reserved memory was used before.");
return *m_reservedMemory;
}

size_t IRGenerationContext::immutablesMemoryOffset() const
{
return CompilerUtils::generalPurposeMemoryStart;
}

void IRGenerationContext::addStateVariable(
VariableDeclaration const& _declaration,
u256 _storageOffset,
Expand Down
24 changes: 23 additions & 1 deletion libsolidity/codegen/ir/IRGenerationContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,20 @@ 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
langutil::CharStreamProvider const* _soliditySourceProvider,
std::map<VariableDeclaration const*, size_t> _immutableVariablesOffsetsInDataSection
):
m_evmVersion(_evmVersion),
m_eofVersion(_eofVersion),
m_executionContext(_executionContext),
m_revertStrings(_revertStrings),
m_sourceIndices(std::move(_sourceIndices)),
m_immutableVariablesOffsetsInDataSection(_immutableVariablesOffsetsInDataSection),
m_debugInfoSelection(_debugInfoSelection),
m_soliditySourceProvider(_soliditySourceProvider)
{}
Expand Down Expand Up @@ -106,6 +110,8 @@ class IRGenerationContext
/// Intended to be used only once for initializing the free memory pointer
/// to after the area used for immutables.
size_t reservedMemory();
size_t immutablesMemorySize() const;
size_t immutablesMemoryOffset() const;

void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset);
bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); }
Expand Down Expand Up @@ -134,7 +140,16 @@ 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; }
size_t immutableVariableOffsetInDataSection(VariableDeclaration const& _variable) const
{
solAssert(
m_immutableVariablesOffsetsInDataSection.count(&_variable),
"Unknown immutable variable: " + _variable.name()
);
return m_immutableVariablesOffsetsInDataSection.at(&_variable);
}

void setArithmetic(Arithmetic _value) { m_arithmetic = _value; }
Arithmetic arithmetic() const { return m_arithmetic; }
Expand All @@ -157,9 +172,14 @@ class IRGenerationContext

langutil::DebugInfoSelection debugInfoSelection() const { return m_debugInfoSelection; }
langutil::CharStreamProvider const* soliditySourceProvider() const { return m_soliditySourceProvider; }
std::map<VariableDeclaration const*, size_t> const& immutableVariablesOffsetsInDataSection() const
{
return m_immutableVariablesOffsetsInDataSection;
}

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 All @@ -169,6 +189,8 @@ class IRGenerationContext
/// Memory offsets reserved for the values of immutable variables during contract creation.
/// This map is empty in the runtime context.
std::map<VariableDeclaration const*, size_t> m_immutableVariables;
/// EOF auxiliary data section offsets of values of immutable variables
std::map<VariableDeclaration const*, size_t> m_immutableVariablesOffsetsInDataSection;
/// Total amount of reserved memory. Reserved memory is used to store
/// immutable variables during contract creation.
std::optional<size_t> m_reservedMemory = {0};
Expand Down
Loading

0 comments on commit 947c5dd

Please sign in to comment.