Skip to content

chore(l1): fix contract deployment tests from EIP-7002 #2630

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

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

DiegoCivi
Copy link
Contributor

@DiegoCivi DiegoCivi commented Apr 28, 2025

Motivation

On #2586 new tests were added and some of them failed on LEVM and REVM.

Description

8 new tests are now working and dont need to be skipped in each of the VMs. The tests were failing because we were not checking if the bytecode of the system contracts that the EIPs (7002 and 7251) define were empty or not. And also because the we were not handling the case were the system calls revert, leading to an invalidate block.

Closes #2598

There where 2 tests that had to do with EIP-7002
and its system contract deployments. A check on the
contract code was missing, making the chain to deploy
a contract without code.
Copy link

github-actions bot commented Apr 28, 2025

Lines of code report

Total lines added: 53
Total lines removed: 1
Total lines changed: 54

Detailed view
+---------------------------------------+-------+------+
| File                                  | Lines | Diff |
+---------------------------------------+-------+------+
| ethrex/crates/vm/backends/levm/mod.rs | 639   | -1   |
+---------------------------------------+-------+------+
| ethrex/crates/vm/backends/revm/mod.rs | 646   | +53  |
+---------------------------------------+-------+------+

DiegoCivi and others added 3 commits April 28, 2025 16:05
As with the missing check that is defined on
EIP-7002, the same one was missing for the system
contract defined on EIP-7251. With this bytecode
verification another 2 tests pass.
@DiegoCivi DiegoCivi marked this pull request as ready for review April 28, 2025 21:03
@DiegoCivi DiegoCivi requested a review from a team as a code owner April 28, 2025 21:03
Copy link
Contributor

@JereSalo JereSalo left a comment

Choose a reason for hiding this comment

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

Good job identifying this!
I wanted to suggest a change if possible:

  • I'd actually make read_withdrawal_requests() and dequeue_consolidation_requests() return a Result<Option<ExecutionReport>, EvmError>, so that when calling them you can use ? for propagating the errors. I think all the code will look cleaner.

@@ -322,6 +323,22 @@ impl LEVM {
)
.ok()?;

// According to EIP-7002 we need to check if the WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS
// has any code after being deployed. If not, the whole block becomes invalid.
let acc = db
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this is usually in the cache but I'd use db.get_account() or db.get_account_info() (whichever exists, after #2629 it will be db.get_account()) instead just to be more careful.

.get(&*WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS)
.unwrap();
if code_hash(&acc.info.bytecode) == EMPTY_CODE_HASH {
return Some(ExecutionReport {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a little bit hacky. It's best to just return an EvmError rather than making up an ExecutionReport

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Awesome! Sorry for the .unwrap(), totally forgot about it, my bad.

Comment on lines 362 to 374
let acc = db
.cache
.get(&*CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS)
.unwrap();
if code_hash(&acc.info.bytecode) == EMPTY_CODE_HASH {
return Some(ExecutionReport {
result: TxResult::Revert(VMError::FatalError),
gas_used: 0,
gas_refunded: 0,
output: Bytes::new(),
logs: vec![],
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto for these changes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

The bytecode of an address was being checked only on
the cache, while it should also be checked on the store.
Now it is being checked on both places. Also removed unwraps.
Copy link
Contributor

@JereSalo JereSalo left a comment

Choose a reason for hiding this comment

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

Nice! It's looking better now.

I think there may be a mistake with ExecutionReport handling though.

The case in which read_withdrawals_requests or dequeue_consolidation_requests return an ExecutionReport with a transaction that reverts doesn't exist in this code, because the match report.result in those functions makes sure to return None if Tx reverted.
I would personally remove that match and return a Result<ExecutionReport, EvmError> instead of Result<Option<ExecutionReport>, EvmError>.

Then, we have to investigate what the correct behavior is when those kinds of transactions revert. Do we leave the data empty or do we return an error? Which one is correct? Are there any tests that test that behavior?

@DiegoCivi
Copy link
Contributor Author

DiegoCivi commented Apr 29, 2025

You are right!

Regarding what should we do if the transaction reverts, the EIP-7002 says:

Although the likelihood of a failed system call to WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS is extremely low, the behavior in such cases is well-defined: the block is marked as invalid. However, if the failure results from processing a transaction within the block, the public mempool may still retain the transaction even after the block is invalidated. This can result in the offending transaction being included again, potentially causing one or more subsequent slots to go without valid blocks. To mitigate this, we recommend that the block producer implementation shuffle their transaction set to increase the chances of producing a valid block, without the offending transaction(s). The block producer implementation and/or the mempool should be aware of system call failure scenarios to enable this behavior.

EIP-7251 handles this the same way for the CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS.
So I guess that if the transaction fails, we should return an error. However, should I also implement the transaction shuffle? Or is it out of this PRs scope? (Maybe it is already implemented, but I could not find it)

About the tests, there is one that to my understanding tests a revert when using the system contract (tests/prague/eip7002_el_triggerable_withdrawals/test_modified_withdrawal_contract.py::test_system_contract_errors[fork_Prague-blockchain_test-system_contract_reverts-system_contract_0x00000961ef480eb55e80d19ad83579a64c007002]) and it expects a BlockException which indicates that the block should be invalid.

@DiegoCivi
Copy link
Contributor Author

In the meantime i was debugging some more tests, and what was talked about before solves them. By changing the Result<Option<ExecutionReport>, EvmError> to a Result<ExecutionReport, EvmError> and returning an error when we have a transaction revert the block execution fails and 4 new tests get fixed.

When doing the system calls defined in EIP-7002 and
EIP-7251, execution could fail and return an ExecutionReport
with a TxResult::Revert, this was not being handled. Now
the block becomes invalid if this happens as defined in the
EIPs.
@DiegoCivi DiegoCivi marked this pull request as draft April 29, 2025 18:55
@DiegoCivi DiegoCivi marked this pull request as ready for review April 30, 2025 23:01
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.

LEVM: Fix prague/eip7002_el_triggerable_withdrawals/contract_deployment
2 participants