-
Notifications
You must be signed in to change notification settings - Fork 2
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
WIP: Add support for uninterrupted delegatecall chains #10
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,9 +49,11 @@ Relying on the concept of "code sections" introduced with EIP-3540 appears to be | |
|
||
### Constants | ||
|
||
| Name | Value | | ||
|------------|-------| | ||
| AA_TX_TYPE | TBD | | ||
| Name | Value | | ||
|------------------|--------------------------------------------| | ||
| AA_TX_TYPE | TBD | | ||
| AA_BASE_GAS_COST | 15000 | | ||
| AA_ORIGIN | 0x7701000000000000000000000000000000007701 | | ||
|
||
### New Transaction Type | ||
|
||
|
@@ -61,6 +63,9 @@ Transactions of this type are referred to as | |
|
||
The contents of such transactions are fully described in RIP-7560. | ||
|
||
The base gas cost of this transaction is set to `AA_BASE_GAS_COST` instead of 21000 to reflect the lack of "intrinsic" | ||
ECDSA signature verification. | ||
|
||
### System-level code entry points | ||
|
||
Modify the EOF container format to consist of the following sections: | ||
|
@@ -81,6 +86,7 @@ entry_points_section := (entry_point_role, target_section_index, target_section_ | |
``` | ||
|
||
For regular calls to the contract, the execution always starts at the first byte of code section 0, and `pc` is set to | ||
|
||
0. | ||
|
||
Here the `entry_points_section` defines alternative indexes of code sections to start the execution for system calls. | ||
|
@@ -94,11 +100,12 @@ The contract that has a role in an Account Abstraction transaction, either as a | |
has to contain all necessary sections marked with one of the following `entry_point_role` markers: | ||
|
||
``` | ||
role_sender_execution = 0x00000000 | ||
role_sender_deployment = 0x00000001 | ||
role_sender_validation = 0x00000002 | ||
role_paymaster_validation = 0x00000003 | ||
role_paymaster_post_tx = 0x00000004 | ||
role_none = 0x0000 | ||
role_sender_execution = 0x0001 | ||
role_sender_deployment = 0x0002 | ||
role_sender_validation = 0x00003 | ||
role_paymaster_validation = 0x0004 | ||
role_paymaster_post_tx = 0x0005 | ||
``` | ||
|
||
This section is equivalent to a code section. | ||
|
@@ -109,6 +116,18 @@ If it is the first code section of a contract, it can act as an entry point duri | |
Only a single section per role is allowed in a contract. | ||
This rule is validated during contract creation. | ||
|
||
### Context variable for the `entry_point_role` value | ||
|
||
During the execution of the `Sender`, `Paymaster` or a `Deployer` code as defined by the `AA_TX_TYPE` transaction, | ||
the global `entry_point_role` variable is set to the corresponding role. | ||
The `entry_point_role` remains set through an uninterrupted chain of `DELEGATECALL`/`EXTDELEGATECALL` calls. | ||
|
||
The default value for `entry_point_role` is `role_none`. Call frames initiated with any opcodes other than | ||
`DELEGATECALL`/`EXTDELEGATECALL` run with the default role. | ||
|
||
Frames that originate as part of the EIP-7701 transaction have their `ORIGIN` and `SENDER` opcodes | ||
return the `AA_ORIGIN` address. | ||
|
||
### Execution entry point for Account Abstraction transaction type participant entity (Sender, Paymaster and Deployer) | ||
|
||
During a regular contract code execution, its behaviour is defined as follows by EIP-3540: | ||
|
@@ -118,15 +137,27 @@ Execution starts at the first byte of code section 0, and pc is set to 0 | |
``` | ||
|
||
However, if a contract is referenced in an `AA_TX_TYPE` transaction as a `Sender`, `Paymaster` or a `Deployer`, | ||
execution starts at the first byte of code section with the `entry_point_role` marker corresponding to the current step, | ||
execution starts at the first byte of code section with the current `entry_point_role` variable value, | ||
and `pc` is set to `0`. | ||
|
||
If the specified contract does not contain such a section, or is not an EOF contract, the transaction is not valid. | ||
If the specified contract does not contain such a section, or is not an EOF contract, | ||
execution starts at code section 0, and pc is set to 0. | ||
|
||
The transaction is considered invalid if it never reached a code section corresponding to the current `entry_point_role` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know the goal is to support legacy proxies but it seems risky:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re 1: The the Re 2: As the implementation in this scenario is an EOF contract, which means it has no There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re 2: Its storage can't be accessed, but environment opcodes can still be used, no? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it. That makes sense. I previously misunderstood the uninterrupted chain of DELEGATECALLs part. Looks like it does solve the proxy case, neat.
Since it's a chain, a non-EOF link in the chain could break the chain and never reach an EOF contract. But your assumption still holds since there's no SELFDESTRUCT for an existing non-EOF contract either and it can't access its own storage. Therefore invalidation can only be done in the account itself, making it an O(n) attack. Seems fine.
Since it can't use storage to divert the flow to a different implementation, the opcodes during offchain simulation and block inclusion are the same. Therefore the ERC-7562 rules can ensure that no banned opcodes are used. |
||
in any contract called through an uninterrupted chain of `DELEGATECALL`/`EXTDELEGATECALL` calls. | ||
|
||
The transaction is considered invalid if it reached such a code section but did not | ||
perform [explicit acceptance](#accepting-the-transaction-by-sender-or-paymaster). | ||
|
||
The `target_section_flags` parameter is added to provide signaling into the EVM so that EOF can perform some additional | ||
validations as part of EOF code validation. | ||
The description of specific flags and their impact on EOF validation shall be added as a separate EIP. | ||
|
||
### Accepting the transaction by `Sender` or `Paymaster` | ||
|
||
In order for the `Sender` or `Paymaster` contracts to accept the transaction, | ||
they must perform a `CALL` to the `AA_ORIGIN` address with the appropriate acceptance data as call data. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the call have to be performed by the actual There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think supporting existing proxy contracts is essential, so the call must be made in the context of the |
||
|
||
### Encoding inputs for different execution frames | ||
|
||
#### Sender Deployment | ||
|
@@ -159,7 +190,7 @@ abi.encode( | |
This step is performed with the `role_sender_deployment` code section. | ||
|
||
In order for the transaction to be considered valid the | ||
sender validation frame MUST return two 64-bit values: | ||
sender validation frame MUST provide the following acceptance data values: | ||
|
||
``` | ||
abi.encode(bool success, uint64 validUntil, uint64 validAfter) | ||
|
@@ -172,7 +203,7 @@ Inputs to the `Paymaster` validation section are same as the ones in the [Sender | |
This step is performed with the `role_paymaster_validation` code section. | ||
|
||
In order for the transaction to be considered valid the | ||
paymaster validation frame MUST return the following values: | ||
paymaster validation frame MUST provide the following acceptance data values: | ||
|
||
``` | ||
abi.encode(bool success, uint64 validUntil, uint64 validAfter, bytes context) | ||
|
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.
How is this variable accessed from EVM? A new opcode?
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.
This variable is not actually visible inside the EVM. It is used by the EVM implementation to decide which code section to enter when using
DELEGATECALL
.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.
We could encode that variable into
ORIGIN
so instead of0x7701000000000000000000000000000000007701
it would return0x7701000000000000000000000000000000000000 | entry_point_role
but I'm not sure if we should. May be useful if a function called by the role's code section needs to know the context. OTOH the code itself could pass it as an arg if it's needed.