-
Notifications
You must be signed in to change notification settings - Fork 381
Add support for EIP-7702 #323
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
base: v0.x
Are you sure you want to change the base?
Add support for EIP-7702 #323
Conversation
/// Get code of address, following EIP-7702 delegations if enabled. | ||
fn delegated_code(&self, address: H160) -> Option<Vec<u8>> { | ||
let code = self.code(address); | ||
evm_core::extract_delegation_address(&code) | ||
.map(|delegated_address| self.code(delegated_address)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that your implementation provide no protection against delegation chains/loops.
EIP-7702 specifies that clients must retrieve only the first code and stop following delegation chains to prevent infinite loops.
You might need to implement delegation chain tracking to prevent infinite loops.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The delegated code is resolved once within call_inner
, then executed with no further check.
// EIP-7702: Get execution code, following delegations if enabled
let code = if self.config.has_eip_7702 {
self.delegated_code(code_address)
.unwrap_or_else(|| self.code(code_address))
} else {
self.code(code_address)
};
Under the assumption that call_inner
is not invoked within a loop, this should be enough to prevent chain following in my mind.
I added a test to check the behavior of the current implementation: 2564062
The result:
cargo test test_eip7702_delegation_chain_violation -- --nocapture
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.04s
Running unittests src/lib.rs (target/debug/deps/evm-e7728aa468c7f7c2)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/eip7702_tests.rs (target/debug/deps/eip7702_tests-ff89983bca9ad4d2)
running 1 test
Exit reason: Error(InvalidCode(Opcode(239)))
Return data: []
✓ CORRECT: Delegation chain properly stopped, invalid code executed
test test_eip7702_delegation_chain_violation ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 17 filtered out; finished in 0.00s
Maybe there are other execution branches where this could be violated?
Are there some conservative hardening measures that we could adopt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks sufficient. From my understanding of the specification, we need to prevent loops per call. So each call attempts to fetch delegation exactly once, which seems to be what this PR is doing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe there are other execution branches where this could be violated?
From my look at the code there's only a single use of delegated_code
so it seems fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that there is several special cases that might not be handled correctly in this implementation:
- Delegation Chain Protection: EIP-7702 specifies that clients must retrieve only the first code and stop following delegation chains to prevent infinite loops.
- Delegation to a Precompile: EIP-7702 specifies that when a precompile address is the target of delegation, the retrieved code should be considered empty.
- Opcodes
CODESIZE
andCODECOPY
should follows delegation, can you add tests for that?
See discussions above.
This seems to be correctly implemented. In case of delegating to precompile:
So this seems fine.
Right now from my review it looks correct. CODESIZE and CODECOPY returns in all cases the runtime code ( But nevertheless as @librelois has said, it'll always be better to have some more test cases! |
Addressed in 3134553 |
Added a comprehensive test suite in f5cc851 and currently investigating the failing tests. |
EIP-7702 states: "When multiple tuples from the same authority are present, set the code using the address in the last valid occurrence." It is my understanding that, when multiple authorizations from the same authorities are present in a list, for them to be valid they must have increasing nonces. I updated the corresponding test to reflect this in cc32e59 |
|
||
// Should either succeed (infinite loop prevented) or fail gracefully | ||
assert!( | ||
matches!(call_exit_reason, ExitReason::Succeed(_)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should fail as the EOA can't have valid executable bytecode
You tested EXTCODECOPY and EXTCODESIZE, but I didn’t find tests for the corresponding behavior of CODECOPY and CODESIZE. It’s very important to test all affected opcodes. |
I did some research and it seems that geth increment the nonce for every valid tuple. So, if there is two authorizations for the same authority, the nonce must increase twice. |
Description
Adds EIP-7702 support.