Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eof: Fix EOF builtin names unintentionally reserved outside of EOF #15700

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from

Conversation

rodiazet
Copy link
Contributor

@rodiazet rodiazet commented Jan 9, 2025

Resolves: #15672

This comment was marked as resolved.

@rodiazet rodiazet force-pushed the eof-reserved-names-fix branch from 7c98754 to 4e697bf Compare January 9, 2025 11:29
Comment on lines 3 to 8
function dataloadn() {}
function rjump() {}
function rjumpi() {}
function callf() {}
function jumpf() {}
function retf() {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are any of these implemented in EOF, i.e. callable, like ext* calls?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are not callable from yul level, but legacy jump and jumpi are reserved but also not callable from yul. So I assumed they should be reserved also.

libyul/AsmAnalysis.cpp Outdated Show resolved Hide resolved
libyul/AsmAnalysis.cpp Outdated Show resolved Hide resolved
// bytecodeFormat: >=EOFv1
// ----
// ParserError 5568: (41-53): Cannot use builtin function name "auxdataloadn" as identifier name.
// ParserError 8143: (41-53): Expected keyword "data" or "object" or "}".
Copy link
Member

@cameel cameel Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like for some reason a clash with a builtin name is a fatal error. There's no good reason for that. I submitted a PR to fix that: #15712. When it's merged, you'll be able to put all the EOF builtins in a single test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting that when I changed the test to version without object and code section. The test results looks differently. Not sure if it's expected behavior.

yulSyntaxTests/eof/auxdataloadn_reserved_in_eof.yul: FAIL
  Contract:
    {
        function auxdataloadn() {}
    }
    // ====
    // bytecodeFormat: >=EOFv1

  Expected result:
    ParserError 5568: (41-53): Cannot use builtin function name "auxdataloadn" as identifier name.
    ParserError 8143: (41-53): Expected keyword "data" or "object" or "}".
  Obtained result:
    ParserError 5568: (15-27): Cannot use builtin function name "auxdataloadn" as identifier name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it looks fine.

The Yul parser is split into two parts: the main one which parses code blocks (and produces ASTs) and a smaller one that only handles the object structure (and produces Object tree).

What was originally happening here was that the object parser would run the code parser to parse a code block, which in turn would hit a fatal error (resulting in an exception). The object parser would probably catch that error but not stop and instead try to parse another nested object or code block at that point. This would produce the second error.

Now that you dropped the object, the object parser is no longer involved and you only get the first error. #15712 would have the same effect in this particular case but via a different route - by making the first error non-fatal and letting the code parser continue until the end of the code block, leaving the object parser at the right spot to parse the next thing.

@rodiazet rodiazet force-pushed the eof-reserved-names-fix branch from a6c3c05 to 577e2cc Compare January 16, 2025 13:26
@rodiazet rodiazet force-pushed the eof-reserved-names-fix branch from 577e2cc to d597ad7 Compare January 20, 2025 12:41
Comment on lines 161 to 173
(
_instr == evmasm::Instruction::EXTCALL ||
_instr == evmasm::Instruction::EXTSTATICCALL ||
_instr == evmasm::Instruction::EXTDELEGATECALL
_instr == evmasm::Instruction::EXTDELEGATECALL ||
_instr == evmasm::Instruction::DATALOADN ||
_instr == evmasm::Instruction::EOFCREATE ||
_instr == evmasm::Instruction::RETURNCONTRACT ||
_instr == evmasm::Instruction::RJUMP ||
_instr == evmasm::Instruction::RJUMPI ||
_instr == evmasm::Instruction::CALLF ||
_instr == evmasm::Instruction::JUMPF ||
_instr == evmasm::Instruction::RETF
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use hasOpcode() to avoid repeating these:

EVMVersion::prague().hasOpcode(_instr, 1) && !EVMVersion::prague().hasOpcode(_instr, std::nullopt)

With an extra assert that EOF version is 1 and EVM version is <= Prague because this will need to be adjusted once new versions are out (if they and add/remove instructions).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, validateInstructions() should have such an assert too, because it hard-coded EOF version to 1 and we can easily forget to update it once we have EOF v2.

{
auto const functionName = resolveFunctionName(_funCall.functionName, m_dialect);

if (functionName == "auxdataloadn" || functionName == "eofcreate" || functionName == "returncontract")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more general way to check this would be:

if (resolveBuiltinFunction(
	funCall.functionName,
	EVMDialect::strictAssemblyForEVMObjects(EVMVersion::prague(), 1)
))

And that also with an assert that EOF version <= 1 and EVM version <= Prague.

And a comment that strictAssemblyForEVMObjects() is expected to be the dialect that contains all the possible builtins. Or maybe we should just check both it and strictAssemblyForEVM() to be safe.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I think it would fit better in validateInstructions() (the overload that takes a string). Just before return false, when we know we're not dealing with an instruction.

7223_error,
nativeLocationOf(_funCall.functionName),
fmt::format(
"The \"{}\" instruction is only available in EOF.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"The \"{}\" instruction is only available in EOF.",
"Builtin function \"{}\" is only available in EOF.",

@cameel cameel added the EOF label Jan 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Some EOF-specific Yul builtin names are reserved even outside of EOF
3 participants