Skip to content

Commit b5b8084

Browse files
authored
Merge pull request #15512 from ipsilon/eof-contract-creation
eof: new contract creation
2 parents fd96fcf + 974c112 commit b5b8084

33 files changed

+661
-107
lines changed

libevmasm/Assembly.cpp

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,10 +1335,14 @@ std::map<uint16_t, uint16_t> Assembly::findReferencedContainers() const
13351335
std::set<uint16_t> referencedSubcontainersIds;
13361336
solAssert(m_subs.size() <= 0x100); // According to EOF spec
13371337

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

13431347
std::map<uint16_t, uint16_t> replacements;
13441348
uint8_t nUnreferenced = 0;
@@ -1405,7 +1409,11 @@ LinkerObject const& Assembly::assembleEOF() const
14051409
switch (item.type())
14061410
{
14071411
case Operation:
1408-
solAssert(item.instruction() != Instruction::DATALOADN);
1412+
solAssert(
1413+
item.instruction() != Instruction::DATALOADN &&
1414+
item.instruction() != Instruction::RETURNCONTRACT &&
1415+
item.instruction() != Instruction::EOFCREATE
1416+
);
14091417
solAssert(!(item.instruction() >= Instruction::PUSH0 && item.instruction() <= Instruction::PUSH32));
14101418
ret.bytecode += assembleOperation(item);
14111419
break;
@@ -1419,6 +1427,18 @@ LinkerObject const& Assembly::assembleEOF() const
14191427
ret.linkReferences.insert(linkRef);
14201428
break;
14211429
}
1430+
case EOFCreate:
1431+
{
1432+
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::EOFCREATE));
1433+
ret.bytecode.push_back(static_cast<uint8_t>(item.data()));
1434+
break;
1435+
}
1436+
case ReturnContract:
1437+
{
1438+
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::RETURNCONTRACT));
1439+
ret.bytecode.push_back(static_cast<uint8_t>(item.data()));
1440+
break;
1441+
}
14221442
case VerbatimBytecode:
14231443
ret.bytecode += assembleVerbatimBytecode(item);
14241444
break;

libevmasm/Assembly.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,17 @@ class Assembly
9999
append(AssemblyItem(std::move(_data), _arguments, _returnVariables));
100100
}
101101

102+
AssemblyItem appendEOFCreate(ContainerID _containerId)
103+
{
104+
solAssert(_containerId < m_subs.size(), "EOF Create of undefined container.");
105+
return append(AssemblyItem::eofCreate(_containerId));
106+
}
107+
AssemblyItem appendReturnContract(ContainerID _containerId)
108+
{
109+
solAssert(_containerId < m_subs.size(), "Return undefined container ID.");
110+
return append(AssemblyItem::returnContract(_containerId));
111+
}
112+
102113
AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
103114
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
104115
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }

libevmasm/AssemblyItem.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ std::pair<std::string, std::string> AssemblyItem::nameAndData(langutil::EVMVersi
7474
switch (type())
7575
{
7676
case Operation:
77+
case EOFCreate:
78+
case ReturnContract:
7779
return {instructionInfo(instruction(), _evmVersion).name, m_data != nullptr ? toStringInHex(*m_data) : ""};
7880
case Push:
7981
return {"PUSH", toStringInHex(data())};
@@ -167,6 +169,10 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _
167169
return std::get<2>(*m_verbatimBytecode).size();
168170
case AuxDataLoadN:
169171
return 1 + 2;
172+
case EOFCreate:
173+
return 2;
174+
case ReturnContract:
175+
return 2;
170176
case UndefinedItem:
171177
solAssert(false);
172178
}
@@ -176,7 +182,7 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _
176182

177183
size_t AssemblyItem::arguments() const
178184
{
179-
if (type() == Operation)
185+
if (hasInstruction())
180186
// The latest EVMVersion is used here, since the InstructionInfo is assumed to be
181187
// the same across all EVM versions except for the instruction name.
182188
return static_cast<size_t>(instructionInfo(instruction(), EVMVersion()).args);
@@ -193,6 +199,8 @@ size_t AssemblyItem::returnValues() const
193199
switch (m_type)
194200
{
195201
case Operation:
202+
case EOFCreate:
203+
case ReturnContract:
196204
// The latest EVMVersion is used here, since the InstructionInfo is assumed to be
197205
// the same across all EVM versions except for the instruction name.
198206
return static_cast<size_t>(instructionInfo(instruction(), EVMVersion()).ret);
@@ -226,6 +234,8 @@ bool AssemblyItem::canBeFunctional() const
226234
switch (m_type)
227235
{
228236
case Operation:
237+
case EOFCreate:
238+
case ReturnContract:
229239
return !isDupInstruction(instruction()) && !isSwapInstruction(instruction());
230240
case Push:
231241
case PushTag:
@@ -344,6 +354,12 @@ std::string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
344354
assertThrow(data() <= std::numeric_limits<size_t>::max(), AssemblyException, "Invalid auxdataloadn argument.");
345355
text = "auxdataloadn{" + std::to_string(static_cast<size_t>(data())) + "}";
346356
break;
357+
case EOFCreate:
358+
text = "eofcreate{" + std::to_string(static_cast<size_t>(data())) + "}";
359+
break;
360+
case ReturnContract:
361+
text = "returcontract{" + std::to_string(static_cast<size_t>(data())) + "}";
362+
break;
347363
}
348364
if (m_jumpType == JumpType::IntoFunction || m_jumpType == JumpType::OutOfFunction)
349365
{
@@ -362,6 +378,8 @@ std::ostream& solidity::evmasm::operator<<(std::ostream& _out, AssemblyItem cons
362378
switch (_item.type())
363379
{
364380
case Operation:
381+
case EOFCreate:
382+
case ReturnContract:
365383
_out << " " << instructionInfo(_item.instruction(), EVMVersion()).name;
366384
if (_item.instruction() == Instruction::JUMP || _item.instruction() == Instruction::JUMPI)
367385
_out << "\t" << _item.getJumpTypeAsString();

libevmasm/AssemblyItem.h

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ enum AssemblyItemType
5555
/// Loads 32 bytes from static auxiliary data of EOF data section. The offset does *not* have to be always from the beginning
5656
/// of the data EOF section. More details here: https://github.com/ipsilon/eof/blob/main/spec/eof.md#data-section-lifecycle
5757
AuxDataLoadN,
58+
EOFCreate, ///< Creates new contract using subcontainer as initcode
59+
ReturnContract, ///< Returns new container (with auxiliary data filled in) to be deployed
5860
VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification.
5961
};
6062

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

6770
class AssemblyItem
6871
{
@@ -85,13 +88,30 @@ class AssemblyItem
8588
else
8689
m_data = std::make_shared<u256>(std::move(_data));
8790
}
91+
92+
explicit AssemblyItem(AssemblyItemType _type, Instruction _instruction, u256 _data = 0, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create()):
93+
m_type(_type),
94+
m_instruction(_instruction),
95+
m_data(std::make_shared<u256>(std::move(_data))),
96+
m_debugData(std::move(_debugData))
97+
{}
98+
8899
explicit AssemblyItem(bytes _verbatimData, size_t _arguments, size_t _returnVariables):
89100
m_type(VerbatimBytecode),
90101
m_instruction{},
91102
m_verbatimBytecode{{_arguments, _returnVariables, std::move(_verbatimData)}},
92103
m_debugData{langutil::DebugData::create()}
93104
{}
94105

106+
static AssemblyItem eofCreate(ContainerID _containerID, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
107+
{
108+
return AssemblyItem(EOFCreate, Instruction::EOFCREATE, _containerID, std::move(_debugData));
109+
}
110+
static AssemblyItem returnContract(ContainerID _containerID, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
111+
{
112+
return AssemblyItem(ReturnContract, Instruction::RETURNCONTRACT, _containerID, std::move(_debugData));
113+
}
114+
95115
AssemblyItem(AssemblyItem const&) = default;
96116
AssemblyItem(AssemblyItem&&) = default;
97117
AssemblyItem& operator=(AssemblyItem const&) = default;
@@ -122,8 +142,17 @@ class AssemblyItem
122142

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

125-
/// @returns the instruction of this item (only valid if type() == Operation)
126-
Instruction instruction() const { assertThrow(m_type == Operation, util::Exception, ""); return m_instruction; }
145+
/// @returns true if the item has m_instruction properly set.
146+
bool hasInstruction() const
147+
{
148+
return m_type == Operation || m_type == EOFCreate || m_type == ReturnContract;
149+
}
150+
/// @returns the instruction of this item (only valid if type() == Operation || EOFCreate || ReturnContract)
151+
Instruction instruction() const
152+
{
153+
solAssert(hasInstruction());
154+
return m_instruction;
155+
}
127156

128157
/// @returns true if the type and data of the items are equal.
129158
bool operator==(AssemblyItem const& _other) const

libevmasm/Instruction.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
169169
{ "LOG3", Instruction::LOG3 },
170170
{ "LOG4", Instruction::LOG4 },
171171
{ "DATALOADN", Instruction::DATALOADN },
172+
{ "EOFCREATE", Instruction::EOFCREATE },
173+
{ "RETURNCONTRACT", Instruction::RETURNCONTRACT },
172174
{ "CREATE", Instruction::CREATE },
173175
{ "CALL", Instruction::CALL },
174176
{ "CALLCODE", Instruction::CALLCODE },
@@ -324,6 +326,8 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
324326
{Instruction::LOG2, {"LOG2", 0, 4, 0, true, Tier::Special}},
325327
{Instruction::LOG3, {"LOG3", 0, 5, 0, true, Tier::Special}},
326328
{Instruction::LOG4, {"LOG4", 0, 6, 0, true, Tier::Special}},
329+
{Instruction::EOFCREATE, {"EOFCREATE", 1, 4, 1, true, Tier::Special}},
330+
{Instruction::RETURNCONTRACT, {"RETURNCONTRACT", 1, 2, 0, true, Tier::Special}},
327331
{Instruction::CREATE, {"CREATE", 0, 3, 1, true, Tier::Special}},
328332
{Instruction::CALL, {"CALL", 0, 7, 1, true, Tier::Special}},
329333
{Instruction::CALLCODE, {"CALLCODE", 0, 7, 1, true, Tier::Special}},

libevmasm/Instruction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ enum class Instruction: uint8_t
183183
LOG4, ///< Makes a log entry; 4 topics.
184184

185185
DATALOADN = 0xd1, ///< load data from EOF data section
186+
EOFCREATE = 0xec, ///< create a new account with associated container code.
187+
RETURNCONTRACT = 0xee, ///< return container to be deployed with axiliary data filled in.
186188
CREATE = 0xf0, ///< create a new account with associated code
187189
CALL, ///< message-call into an account
188190
CALLCODE, ///< message-call with another account's code only

libevmasm/SemanticInformation.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,34 @@ std::vector<SemanticInformation::Operation> SemanticInformation::readWriteOperat
184184
Operation{Location::TransientStorage, Effect::Read, {}, {}, {}},
185185
Operation{Location::TransientStorage, Effect::Write, {}, {}, {}}
186186
};
187+
case Instruction::EOFCREATE:
188+
return std::vector<Operation>{
189+
Operation{
190+
Location::Memory,
191+
Effect::Read,
192+
2,
193+
3,
194+
{}
195+
},
196+
Operation{Location::Storage, Effect::Read, {}, {}, {}},
197+
Operation{Location::Storage, Effect::Write, {}, {}, {}},
198+
Operation{Location::TransientStorage, Effect::Read, {}, {}, {}},
199+
Operation{Location::TransientStorage, Effect::Write, {}, {}, {}}
200+
};
201+
case Instruction::RETURNCONTRACT:
202+
return std::vector<Operation>{
203+
Operation{
204+
Location::Memory,
205+
Effect::Read,
206+
0,
207+
1,
208+
{}
209+
},
210+
Operation{Location::Storage, Effect::Read, {}, {}, {}},
211+
Operation{Location::Storage, Effect::Write, {}, {}, {}},
212+
Operation{Location::TransientStorage, Effect::Read, {}, {}, {}},
213+
Operation{Location::TransientStorage, Effect::Write, {}, {}, {}}
214+
};
187215
case Instruction::MSIZE:
188216
// This is just to satisfy the assert below.
189217
return std::vector<Operation>{};
@@ -280,8 +308,9 @@ bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
280308

281309
bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
282310
{
283-
if (_item.type() != evmasm::Operation)
311+
if (!_item.hasInstruction())
284312
return false;
313+
285314
switch (_item.instruction())
286315
{
287316
// note that CALL, CALLCODE and CREATE do not really alter the control flow, because we
@@ -293,6 +322,7 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
293322
case Instruction::STOP:
294323
case Instruction::INVALID:
295324
case Instruction::REVERT:
325+
case Instruction::RETURNCONTRACT:
296326
return true;
297327
default:
298328
return false;
@@ -301,7 +331,7 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
301331

302332
bool SemanticInformation::terminatesControlFlow(AssemblyItem const& _item)
303333
{
304-
if (_item.type() != evmasm::Operation)
334+
if (!_item.hasInstruction())
305335
return false;
306336
return terminatesControlFlow(_item.instruction());
307337
}
@@ -315,6 +345,7 @@ bool SemanticInformation::terminatesControlFlow(Instruction _instruction)
315345
case Instruction::STOP:
316346
case Instruction::INVALID:
317347
case Instruction::REVERT:
348+
case Instruction::RETURNCONTRACT:
318349
return true;
319350
default:
320351
return false;
@@ -337,7 +368,7 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
337368
{
338369
assertThrow(_item.type() != VerbatimBytecode, AssemblyException, "");
339370

340-
if (_item.type() != evmasm::Operation)
371+
if (!_item.hasInstruction())
341372
return true;
342373

343374
switch (_item.instruction())
@@ -357,6 +388,7 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
357388
case Instruction::EXTCODEHASH:
358389
case Instruction::RETURNDATACOPY: // depends on previous calls
359390
case Instruction::RETURNDATASIZE:
391+
case Instruction::EOFCREATE:
360392
return false;
361393
default:
362394
return true;
@@ -436,6 +468,8 @@ SemanticInformation::Effect SemanticInformation::memory(Instruction _instruction
436468
case Instruction::LOG2:
437469
case Instruction::LOG3:
438470
case Instruction::LOG4:
471+
case Instruction::EOFCREATE:
472+
case Instruction::RETURNCONTRACT:
439473
return SemanticInformation::Read;
440474

441475
default:
@@ -473,6 +507,8 @@ SemanticInformation::Effect SemanticInformation::storage(Instruction _instructio
473507
case Instruction::CREATE:
474508
case Instruction::CREATE2:
475509
case Instruction::SSTORE:
510+
case Instruction::EOFCREATE:
511+
case Instruction::RETURNCONTRACT:
476512
return SemanticInformation::Write;
477513

478514
case Instruction::SLOAD:
@@ -494,6 +530,8 @@ SemanticInformation::Effect SemanticInformation::transientStorage(Instruction _i
494530
case Instruction::CREATE:
495531
case Instruction::CREATE2:
496532
case Instruction::TSTORE:
533+
case Instruction::EOFCREATE:
534+
case Instruction::RETURNCONTRACT:
497535
return SemanticInformation::Write;
498536

499537
case Instruction::TLOAD:
@@ -514,6 +552,8 @@ SemanticInformation::Effect SemanticInformation::otherState(Instruction _instruc
514552
case Instruction::DELEGATECALL:
515553
case Instruction::CREATE:
516554
case Instruction::CREATE2:
555+
case Instruction::EOFCREATE:
556+
case Instruction::RETURNCONTRACT:
517557
case Instruction::SELFDESTRUCT:
518558
case Instruction::STATICCALL: // because it can affect returndatasize
519559
// Strictly speaking, log0, .., log4 writes to the state, but the EVM cannot read it, so they
@@ -588,6 +628,10 @@ bool SemanticInformation::invalidInViewFunctions(Instruction _instruction)
588628
case Instruction::CALL:
589629
case Instruction::CALLCODE:
590630
case Instruction::DELEGATECALL:
631+
// According to EOF spec https://eips.ethereum.org/EIPS/eip-7620#eofcreate
632+
case Instruction::EOFCREATE:
633+
// According to EOF spec https://eips.ethereum.org/EIPS/eip-7620#returncontract
634+
case Instruction::RETURNCONTRACT:
591635
case Instruction::CREATE2:
592636
case Instruction::SELFDESTRUCT:
593637
return true;

liblangutil/EVMVersion.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ bool EVMVersion::hasOpcode(Instruction _opcode, std::optional<uint8_t> _eofVersi
7777
case Instruction::GAS:
7878
return !_eofVersion.has_value();
7979
// Instructions below available only in EOF
80+
case Instruction::EOFCREATE:
81+
case Instruction::RETURNCONTRACT:
8082
case Instruction::DATALOADN:
8183
return _eofVersion.has_value();
8284
default:

0 commit comments

Comments
 (0)