Skip to content

Commit fbc76c7

Browse files
committed
eof: Implement and use ext*call
1 parent 2b3c2b0 commit fbc76c7

File tree

11 files changed

+336
-19
lines changed

11 files changed

+336
-19
lines changed

libevmasm/Instruction.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
182182
{ "STATICCALL", Instruction::STATICCALL },
183183
{ "RETURN", Instruction::RETURN },
184184
{ "DELEGATECALL", Instruction::DELEGATECALL },
185+
{ "EXTCALL", Instruction::EXTCALL },
186+
{ "EXTSTATICCALL", Instruction::EXTSTATICCALL },
187+
{ "EXTDELEGATECALL", Instruction::EXTDELEGATECALL },
185188
{ "CREATE2", Instruction::CREATE2 },
186189
{ "REVERT", Instruction::REVERT },
187190
{ "INVALID", Instruction::INVALID },
@@ -344,6 +347,9 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
344347
{Instruction::RETURN, {"RETURN", 0, 2, 0, true, Tier::Zero}},
345348
{Instruction::DELEGATECALL, {"DELEGATECALL", 0, 6, 1, true, Tier::Special}},
346349
{Instruction::STATICCALL, {"STATICCALL", 0, 6, 1, true, Tier::Special}},
350+
{Instruction::EXTCALL, {"EXTCALL", 0, 4, 1, true, Tier::Special}},
351+
{Instruction::EXTDELEGATECALL,{"EXTDELEGATECALL", 0, 3, 1, true, Tier::Special}},
352+
{Instruction::EXTSTATICCALL, {"EXTSTATICCALL", 0, 3, 1, true, Tier::Special}},
347353
{Instruction::CREATE2, {"CREATE2", 0, 4, 1, true, Tier::Special}},
348354
{Instruction::REVERT, {"REVERT", 0, 2, 0, true, Tier::Zero}},
349355
{Instruction::INVALID, {"INVALID", 0, 0, 0, true, Tier::Zero}},

libevmasm/Instruction.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,10 @@ enum class Instruction: uint8_t
198198
RETURN, ///< halt execution returning output data
199199
DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender
200200
CREATE2 = 0xf5, ///< create new account with associated code at address `sha3(0xff + sender + salt + init code) % 2**160`
201+
EXTCALL = 0xf8, ///< EOF message-call into an account
202+
EXTDELEGATECALL = 0xf9, ///< EOF delegate call
201203
STATICCALL = 0xfa, ///< like CALL but disallow state modifications
204+
EXTSTATICCALL = 0xfb, ///< like EXTCALL but disallow state modifications
202205

203206
REVERT = 0xfd, ///< halt execution, revert state and return output data
204207
INVALID = 0xfe, ///< invalid instruction for expressing runtime errors (e.g., division-by-zero)
@@ -214,6 +217,9 @@ constexpr bool isCallInstruction(Instruction _inst) noexcept
214217
case Instruction::CALLCODE:
215218
case Instruction::DELEGATECALL:
216219
case Instruction::STATICCALL:
220+
case Instruction::EXTCALL:
221+
case Instruction::EXTSTATICCALL:
222+
case Instruction::EXTDELEGATECALL:
217223
return true;
218224
default:
219225
return false;

libevmasm/SemanticInformation.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,37 @@ std::vector<SemanticInformation::Operation> SemanticInformation::readWriteOperat
169169
});
170170
return operations;
171171
}
172+
case Instruction::EXTSTATICCALL:
173+
case Instruction::EXTDELEGATECALL:
174+
{
175+
size_t paramCount = static_cast<size_t>(instructionInfo(_instruction, langutil::EVMVersion()).args);
176+
std::vector<Operation> operations{
177+
Operation{Location::Memory, Effect::Read, paramCount - 2, paramCount - 1, {}},
178+
Operation{Location::Storage, Effect::Read, {}, {}, {}},
179+
Operation{Location::TransientStorage, Effect::Read, {}, {}, {}}
180+
};
181+
if (_instruction == Instruction::EXTDELEGATECALL)
182+
{
183+
operations.emplace_back(Operation{Location::Storage, Effect::Write, {}, {}, {}});
184+
operations.emplace_back(Operation{Location::TransientStorage, Effect::Write, {}, {}, {}});
185+
}
186+
187+
return operations;
188+
}
189+
case Instruction::EXTCALL:
190+
{
191+
size_t paramCount = static_cast<size_t>(instructionInfo(_instruction, langutil::EVMVersion()).args);
192+
std::vector<Operation> operations{
193+
Operation{Location::Memory, Effect::Read, paramCount - 3, paramCount - 2, {}},
194+
Operation{Location::Storage, Effect::Read, {}, {}, {}},
195+
Operation{Location::TransientStorage, Effect::Read, {}, {}, {}}
196+
};
197+
198+
operations.emplace_back(Operation{Location::Storage, Effect::Write, {}, {}, {}});
199+
operations.emplace_back(Operation{Location::TransientStorage, Effect::Write, {}, {}, {}});
200+
201+
return operations;
202+
}
172203
case Instruction::CREATE:
173204
case Instruction::CREATE2:
174205
return std::vector<Operation>{
@@ -380,6 +411,9 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
380411
case Instruction::CALLCODE:
381412
case Instruction::DELEGATECALL:
382413
case Instruction::STATICCALL:
414+
case Instruction::EXTCALL:
415+
case Instruction::EXTDELEGATECALL:
416+
case Instruction::EXTSTATICCALL:
383417
case Instruction::CREATE:
384418
case Instruction::CREATE2:
385419
case Instruction::GAS:
@@ -459,6 +493,9 @@ SemanticInformation::Effect SemanticInformation::memory(Instruction _instruction
459493
case Instruction::CALLCODE:
460494
case Instruction::DELEGATECALL:
461495
case Instruction::STATICCALL:
496+
case Instruction::EXTCALL:
497+
case Instruction::EXTDELEGATECALL:
498+
case Instruction::EXTSTATICCALL:
462499
return SemanticInformation::Write;
463500

464501
case Instruction::CREATE:
@@ -514,10 +551,13 @@ SemanticInformation::Effect SemanticInformation::storage(Instruction _instructio
514551
case Instruction::SSTORE:
515552
case Instruction::EOFCREATE:
516553
case Instruction::RETURNCONTRACT:
554+
case Instruction::EXTCALL:
555+
case Instruction::EXTDELEGATECALL:
517556
return SemanticInformation::Write;
518557

519558
case Instruction::SLOAD:
520559
case Instruction::STATICCALL:
560+
case Instruction::EXTSTATICCALL:
521561
return SemanticInformation::Read;
522562

523563
default:
@@ -537,10 +577,13 @@ SemanticInformation::Effect SemanticInformation::transientStorage(Instruction _i
537577
case Instruction::TSTORE:
538578
case Instruction::EOFCREATE:
539579
case Instruction::RETURNCONTRACT:
580+
case Instruction::EXTCALL:
581+
case Instruction::EXTDELEGATECALL:
540582
return SemanticInformation::Write;
541583

542584
case Instruction::TLOAD:
543585
case Instruction::STATICCALL:
586+
case Instruction::EXTSTATICCALL:
544587
return SemanticInformation::Read;
545588

546589
default:
@@ -559,7 +602,10 @@ SemanticInformation::Effect SemanticInformation::otherState(Instruction _instruc
559602
case Instruction::CREATE2:
560603
case Instruction::EOFCREATE:
561604
case Instruction::RETURNCONTRACT:
605+
case Instruction::EXTCALL:
606+
case Instruction::EXTDELEGATECALL:
562607
case Instruction::SELFDESTRUCT:
608+
case Instruction::EXTSTATICCALL:
563609
case Instruction::STATICCALL: // because it can affect returndatasize
564610
// Strictly speaking, log0, .., log4 writes to the state, but the EVM cannot read it, so they
565611
// are just marked as having 'other side effects.'
@@ -606,6 +652,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction)
606652
case Instruction::NUMBER:
607653
case Instruction::PREVRANDAO:
608654
case Instruction::GASLIMIT:
655+
case Instruction::EXTSTATICCALL:
609656
case Instruction::STATICCALL:
610657
case Instruction::SLOAD:
611658
case Instruction::TLOAD:
@@ -635,6 +682,8 @@ bool SemanticInformation::invalidInViewFunctions(Instruction _instruction)
635682
case Instruction::CALL:
636683
case Instruction::CALLCODE:
637684
case Instruction::DELEGATECALL:
685+
case Instruction::EXTCALL:
686+
case Instruction::EXTDELEGATECALL:
638687
// According to EOF spec https://eips.ethereum.org/EIPS/eip-7620#eofcreate
639688
case Instruction::EOFCREATE:
640689
// According to EOF spec https://eips.ethereum.org/EIPS/eip-7620#returncontract

liblangutil/EVMVersion.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ bool EVMVersion::hasOpcode(Instruction _opcode, std::optional<uint8_t> _eofVersi
8585
case Instruction::CALLF:
8686
case Instruction::JUMPF:
8787
case Instruction::RETF:
88+
case Instruction::EXTCALL:
89+
case Instruction::EXTSTATICCALL:
90+
case Instruction::EXTDELEGATECALL:
8891
return _eofVersion.has_value();
8992
default:
9093
return true;

libsolidity/codegen/ir/IRGeneratorForStatements.cpp

Lines changed: 83 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,11 +1622,16 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
16221622
Whiskers templ(R"(
16231623
let <gas> := 0
16241624
if iszero(<value>) { <gas> := <callStipend> }
1625-
let <success> := call(<gas>, <address>, <value>, 0, 0, 0, 0)
1625+
<?eof>
1626+
let <success> := iszero(extcall(<address>, 0, 0, <value>))
1627+
<!eof>
1628+
let <success> := call(<gas>, <address>, <value>, 0, 0, 0, 0)
1629+
</eof>
16261630
<?isTransfer>
16271631
if iszero(<success>) { <forwardingRevert>() }
16281632
</isTransfer>
16291633
)");
1634+
templ("eof", m_context.eofVersion().has_value());
16301635
templ("gas", m_context.newYulVariable());
16311636
templ("callStipend", toString(evmasm::GasCosts::callStipend));
16321637
templ("address", address);
@@ -1669,17 +1674,30 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
16691674
<?isECRecover>
16701675
mstore(0, 0)
16711676
</isECRecover>
1672-
let <success> := <call>(<gas>, <address> <?isCall>, 0</isCall>, <pos>, sub(<end>, <pos>), 0, 32)
1677+
<?eof>
1678+
// EOF always uses extstaticcall
1679+
let <success> := iszero(extstaticcall(<address>, <pos>, sub(<end>, <pos>)))
1680+
<!eof>
1681+
let <success> := <call>(<gas>, <address> <?isCall>, 0</isCall>, <pos>, sub(<end>, <pos>), 0, 32)
1682+
</eof>
16731683
if iszero(<success>) { <forwardingRevert>() }
1684+
<?eof>
1685+
if eq(returndatasize(), 32) { returndatacopy(0, 0, 32) }
1686+
</eof>
16741687
let <retVars> := <shl>(mload(0))
16751688
)");
1676-
templ("call", m_context.evmVersion().hasStaticCall() ? "staticcall" : "call");
1677-
templ("isCall", !m_context.evmVersion().hasStaticCall());
1689+
auto const eof = m_context.eofVersion().has_value();
1690+
if (!eof)
1691+
{
1692+
templ("call", m_context.evmVersion().hasStaticCall() ? "staticcall" : "call");
1693+
templ("isCall", !m_context.evmVersion().hasStaticCall());
1694+
}
16781695
templ("shl", m_utils.shiftLeftFunction(offset * 8));
16791696
templ("allocateUnbounded", m_utils.allocateUnboundedFunction());
16801697
templ("pos", m_context.newYulVariable());
16811698
templ("end", m_context.newYulVariable());
16821699
templ("isECRecover", FunctionType::Kind::ECRecover == functionType->kind());
1700+
templ("eof", eof);
16831701
if (FunctionType::Kind::ECRecover == functionType->kind())
16841702
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes));
16851703
else
@@ -1844,7 +1862,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
18441862
solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable);
18451863
define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression());
18461864
}
1847-
else if (std::set<std::string>{"call", "callcode", "delegatecall", "staticcall"}.count(member))
1865+
else if (std::set<std::string>{"call", "extcall", "callcode", "delegatecall" , "extdelegatecall", "staticcall", "extstaticcall"}.count(member))
18481866
define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression());
18491867
else
18501868
solAssert(false, "Invalid member access to address");
@@ -2642,7 +2660,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
26422660
}
26432661

26442662
// NOTE: When the expected size of returndata is static, we pass that in to the call opcode and it gets copied automatically.
2645-
// When it's dynamic, we get zero from estimatedReturnSize() instead and then we need an explicit returndatacopy().
2663+
// When it's dynamic, we get zero from estimatedReturnSize() instead and then we need an explicit returndatacopy().
26462664
Whiskers templ(R"(
26472665
<?checkExtcodesize>
26482666
if iszero(extcodesize(<address>)) { <revertNoCode>() }
@@ -2652,7 +2670,11 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
26522670
mstore(<pos>, <shl28>(<funSel>))
26532671
let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
26542672
2655-
let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <staticReturndataSize>)
2673+
<?eof>
2674+
let <success> := iszero(<call>(<address>, <pos>, sub(<end>, <pos>) <?hasValue>, <value></hasValue>))
2675+
<!eof>
2676+
let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <staticReturndataSize>)
2677+
</eof>
26562678
<?noTryCall>
26572679
if iszero(<success>) { <forwardingRevert>() }
26582680
</noTryCall>
@@ -2667,6 +2689,9 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
26672689
if gt(<returnDataSizeVar>, returndatasize()) {
26682690
<returnDataSizeVar> := returndatasize()
26692691
}
2692+
<?eof>
2693+
returndatacopy(<pos>, 0, <returnDataSizeVar>)
2694+
</eof>
26702695
</supportsReturnData>
26712696
</isReturndataSizeDynamic>
26722697
@@ -2678,16 +2703,21 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
26782703
}
26792704
)");
26802705
templ("revertNoCode", m_utils.revertReasonIfDebugFunction("Target contract does not contain code"));
2706+
auto const eof = m_context.eofVersion().has_value();
2707+
templ("eof", eof);
26812708

26822709
// We do not need to check extcodesize if we expect return data: If there is no
26832710
// code, the call will return empty data and the ABI decoder will revert.
26842711
size_t encodedHeadSize = 0;
26852712
for (auto const& t: returnInfo.returnTypes)
26862713
encodedHeadSize += t->decodingType()->calldataHeadSize();
26872714
bool const checkExtcodesize =
2688-
encodedHeadSize == 0 ||
2689-
!m_context.evmVersion().supportsReturndata() ||
2690-
m_context.revertStrings() >= RevertStrings::Debug;
2715+
!m_context.eofVersion().has_value() &&
2716+
(
2717+
encodedHeadSize == 0 ||
2718+
!m_context.evmVersion().supportsReturndata() ||
2719+
m_context.revertStrings() >= RevertStrings::Debug
2720+
);
26912721
templ("checkExtcodesize", checkExtcodesize);
26922722

26932723
templ("pos", m_context.newYulVariable());
@@ -2747,12 +2777,25 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
27472777
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
27482778
}
27492779
// Order is important here, STATICCALL might overlap with DELEGATECALL.
2750-
if (isDelegateCall)
2751-
templ("call", "delegatecall");
2752-
else if (useStaticCall)
2753-
templ("call", "staticcall");
2780+
if (!eof)
2781+
{
2782+
if (isDelegateCall)
2783+
templ("call", "delegatecall");
2784+
else if (useStaticCall)
2785+
templ("call", "staticcall");
2786+
else
2787+
templ("call", "call");
2788+
}
27542789
else
2755-
templ("call", "call");
2790+
{
2791+
if (isDelegateCall)
2792+
templ("call", "extdelegatecall");
2793+
else if (useStaticCall)
2794+
templ("call", "extstaticcall");
2795+
else
2796+
templ("call", "extcall");
2797+
}
2798+
27562799

27572800
templ("forwardingRevert", m_utils.forwardingRevertFunction());
27582801

@@ -2791,13 +2834,21 @@ void IRGeneratorForStatements::appendBareCall(
27912834
let <length> := mload(<arg>)
27922835
</needsEncoding>
27932836
2794-
let <success> := <call>(<gas>, <address>, <?+value> <value>, </+value> <pos>, <length>, 0, 0)
2837+
<?eof>
2838+
let <success> := <call>(<address>, <pos>, <length> <?+value>, <value></+value>)
2839+
<success> := iszero(<success>)
2840+
<!eof>
2841+
let <success> := <call>(<gas>, <address>, <?+value> <value>, </+value> <pos>, <length>, 0, 0)
2842+
</eof>
2843+
27952844
let <returndataVar> := <extractReturndataFunction>()
27962845
)");
27972846

27982847
templ("allocateUnbounded", m_utils.allocateUnboundedFunction());
27992848
templ("pos", m_context.newYulVariable());
28002849
templ("length", m_context.newYulVariable());
2850+
auto const eof = m_context.eofVersion().has_value();
2851+
templ("eof", eof);
28012852

28022853
templ("arg", IRVariable(*_arguments.front()).commaSeparatedList());
28032854
Type const& argType = type(*_arguments.front());
@@ -2819,16 +2870,29 @@ void IRGeneratorForStatements::appendBareCall(
28192870
if (funKind == FunctionType::Kind::BareCall)
28202871
{
28212872
templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
2822-
templ("call", "call");
2873+
if (eof)
2874+
templ("call", "extcall");
2875+
else
2876+
templ("call", "call");
28232877
}
28242878
else
28252879
{
28262880
solAssert(!funType.valueSet(), "Value set for delegatecall or staticcall.");
28272881
templ("value", "");
28282882
if (funKind == FunctionType::Kind::BareStaticCall)
2829-
templ("call", "staticcall");
2883+
{
2884+
if (eof)
2885+
templ("call", "extstaticcall");
2886+
else
2887+
templ("call", "staticcall");
2888+
}
28302889
else
2831-
templ("call", "delegatecall");
2890+
{
2891+
if (eof)
2892+
templ("call", "extdelegatecall");
2893+
else
2894+
templ("call", "delegatecall");
2895+
}
28322896
}
28332897

28342898
if (funType.gasSet())

libyul/AsmAnalysis.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,10 +771,27 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
771771
"PC instruction is a low-level EVM feature. "
772772
"Because of that PC is disallowed in strict assembly."
773773
);
774+
else if (!m_eofVersion.has_value() && (
775+
_instr == evmasm::Instruction::EXTCALL ||
776+
_instr == evmasm::Instruction::EXTDELEGATECALL ||
777+
_instr == evmasm::Instruction::EXTSTATICCALL
778+
))
779+
{
780+
m_errorReporter.typeError(
781+
4328_error,
782+
_location,
783+
fmt::format(
784+
"The \"{instruction}\" instruction is {kind} VMs.",
785+
fmt::arg("instruction", boost::to_lower_copy(instructionInfo(_instr, m_evmVersion).name)),
786+
fmt::arg("kind", "not available in legacy bytecode")
787+
)
788+
);
789+
}
774790
else if (m_eofVersion.has_value() && (
775791
_instr == evmasm::Instruction::CALL ||
776792
_instr == evmasm::Instruction::CALLCODE ||
777793
_instr == evmasm::Instruction::DELEGATECALL ||
794+
_instr == evmasm::Instruction::STATICCALL ||
778795
_instr == evmasm::Instruction::SELFDESTRUCT ||
779796
_instr == evmasm::Instruction::JUMP ||
780797
_instr == evmasm::Instruction::JUMPI ||

0 commit comments

Comments
 (0)