Skip to content

Undo ULM changes to make CALL* and CREATE* opcodes execute in a single call frame#47

Draft
Robertorosmaninho wants to merge 28 commits intoulmfrom
moving-call-create-to-inner-execution
Draft

Undo ULM changes to make CALL* and CREATE* opcodes execute in a single call frame#47
Robertorosmaninho wants to merge 28 commits intoulmfrom
moving-call-create-to-inner-execution

Conversation

@Robertorosmaninho
Copy link
Copy Markdown

No description provided.

@Robertorosmaninho Robertorosmaninho self-assigned this May 12, 2025
@Robertorosmaninho Robertorosmaninho force-pushed the moving-call-create-to-inner-execution branch from 52b622b to f996f52 Compare May 12, 2025 17:44
@Robertorosmaninho Robertorosmaninho force-pushed the moving-call-create-to-inner-execution branch from 007a9f3 to f77decb Compare May 22, 2025 22:10
Copy link
Copy Markdown

@sskeirik sskeirik left a comment

Choose a reason for hiding this comment

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

I did a first pass reviewing this PR and I have a few comments --- mostly, they are style suggestions --- one or two seem to be a bug.

Comment on lines +1046 to +1056
rule [call.true]:
<k> #call ACCTFROM ACCTTO ACCTCODE VALUE APPVALUE ARGS STATIC
=> #callWithCode ACCTFROM ACCTTO ACCTCODE GetAndResolveCode(ACCTCODE) VALUE APPVALUE ARGS STATIC
...
</k>

rule [call.false]:
<k> #call ACCTFROM ACCTTO ACCTCODE VALUE APPVALUE ARGS STATIC
=> #callWithCode ACCTFROM ACCTTO ACCTCODE .Bytes VALUE APPVALUE ARGS STATIC
...
</k> [owise]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It seems to me that the LHS of the call.true and call.false rules have overlapping instances, since, in either case, it seems like the LHS is totally unconstrained.

It's just that, based on the names of these rules and the values of their RHS, I would expect that they do not overlap.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I've added requires AccountExists(ACCTCODE) to differentiate them

rule [call]:
<k> CALL _GCAP ACCTTO VALUE ARGSTART ARGWIDTH RETSTART RETWIDTH
=> #return RETSTART RETWIDTH Call(GCALL, ACCTTO, VALUE, #range(LM, ARGSTART, ARGWIDTH))
=> AccessAccount(ACCTTO)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The AccessAccount operation in the rules defining the CALL family of opcodes seems redundant because the #mkCall operation also seems to call AccessAccount.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I agree. This was the behavior copied from RV here and here, but I can try without it.

Comment on lines +1150 to +1196
```vlm
syntax InternalOp ::= "#newAccount" Int
// -------------------------------------

rule <k> #create ACCTFROM ACCTTO VALUE INITCODE
=> IncrementNonce(ACCTFROM)
~> #pushCallStack ~> #pushWorldState
~> #newAccount ACCTTO
~> #transferFundsFrom ACCTFROM ACCTTO VALUE
~> #mkCreate ACCTFROM ACCTTO VALUE INITCODE
...
</k>

rule <k> #mkCreate ACCTFROM ACCTTO VALUE INITCODE
=> AccessAccount(ACCTFROM) ~> AccessAccount(ACCTTO) ~> IncrementNonceOnCreate(ACCTTO, $SCHEDULE) ~> #loadProgram INITCODE ~> #initVM ~> #execute
...
</k>
<id> _ => ACCTTO </id>
<gas> _GAVAIL => GCALL </gas>
<callGas> GCALL => 0 </callGas>
<caller> _ => ACCTFROM </caller>
<callDepth> CD => CD +Int 1 </callDepth>
<callData> _ => .Bytes </callData>
<callValue> _ => VALUE </callValue>

rule <k> #newAccount ACCT => #if NewAccount(ACCT) #then .K #else #end EVMC_ACCOUNT_ALREADY_EXISTS #fi ... </k>
```
```reth
syntax InternalOp ::= "#newAccount" Int Int Int
// ---------------------------------------------

rule <k> #create ACCTFROM ACCTTO VALUE INITCODE
=> IncrementNonce(ACCTFROM)
~> #pushCallStack ~> #pushWorldState
~> #newAccount ACCTFROM ACCTTO VALUE
~> #mkCreate ACCTFROM ACCTTO VALUE INITCODE
...
</k>

rule <k> #mkCreate ACCTFROM ACCTTO VALUE INITCODE => #loadProgram INITCODE ~> #initVM ~> #execute ... </k>
<id> _ => ACCTTO </id>
<gas> _GAVAIL => GCALL </gas>
<callGas> GCALL => 0 </callGas>
<caller> _ => ACCTFROM </caller>
<callDepth> CD => CD +Int 1 </callDepth>
<callData> _ => .Bytes </callData>
<callValue> _ => VALUE </callValue>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'm not sure how much this matters since I think the vlm version will not be maintained much longer --- but if we do want to maintain both versions, we can isolate the difference in behaviors between these two versions in a single rule.

That is, we could have #create and #mkCreate have the same behavior in both versions and make the #newAccount operation contain all of the divergent behaviors including:

  1. the account accesses
  2. the nonce increment on ACCTTO
  3. the account existence check (which uses a different internal hook)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I've simplified this a bit here. I didn't merge all the rules to maintain a minimal separation of responsibilities and to avoid clustering one single rule that does three different things. Let me know if you're happy with this version or want me to proceed with merging #NewAccount and #mkCreate

...
</k>
<callGas> GCALL </callGas>
<id> ACCT </id>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'm a bit surprised that the create-invalid and create2-invalid cases return error EVMC_OUT_OF_GAS when the predicate #hasValidInitCode fails --- instead of some more specific error. I checked to see if Geth also follows this pattern but I wasn't able to deduce its behavior after a few minutes of checking.

I definitely don't know the expected behavior in this case, so if someone else could confirm this behavior, that would be helpful.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Well, the EIP that introduced it doesn't mention a specific exception, but this feature was implemented with the gas limit as background: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3860.md

@Robertorosmaninho Robertorosmaninho force-pushed the moving-call-create-to-inner-execution branch from 8347c4c to cd87cd8 Compare May 28, 2025 06:27
Copy link
Copy Markdown

@sskeirik sskeirik left a comment

Choose a reason for hiding this comment

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

This looks good!

I just have one minor comment, but I'm approving now --- feel free to handle it as you like.

Comment on lines +1151 to +1153
=> #transferFundsFrom ACCTFROM ACCTTO VALUE
~> #if NewAccount(ACCTTO) #then .K #else #end EVMC_ACCOUNT_ALREADY_EXISTS #fi
...
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The order of operations seems strange here --- I would expect that we check for account existence before transferring funds --- since, in a CREATE context, the funds are intended to be transferred to a newly created account only (and not to an existing account that happens to have the same address).

The old code also had the order of these two operations switched.

However, in the end, it may not matter, for two reasons:

  1. The #end EVMC_ACCOUNT_ALREADY_EXISTS might induce a state revert which undoes this transfer;
  2. More importantly, this codepath may never be used since we're moving to Reth.

@Robertorosmaninho Robertorosmaninho force-pushed the moving-call-create-to-inner-execution branch 5 times, most recently from 51124be to 12a46fb Compare June 3, 2025 01:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants