Skip to content

Commit

Permalink
reorder "rationale" after "specification"
Browse files Browse the repository at this point in the history
  • Loading branch information
drortirosh committed Feb 25, 2024
1 parent 28a0190 commit 47df0e0
Showing 1 changed file with 94 additions and 93 deletions.
187 changes: 94 additions & 93 deletions ERCS/erc-7562.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,99 +40,6 @@ For the actual interfaces of those contract-based accounts see the definitions i
This documentation uses the terminology "UserOperation" for a transaction created by a smart contract account, and closely follows [ERC-4337](./erc-4337.md) terminology.
However, the rules apply to any account-abstraction framework that uses EVM code to perform transaction validation and makes a distinction between validation and execution.

## Rationale

All transactions initiated by EOAs have an implicit validation phase where balance, nonce, and signature are
checked to be valid for the current state of the Ethereum blockchain.
Once the transaction is checked to be valid by a node, only another transaction by the same EOA can modify the Ethereum
state in a way that makes the first transaction invalid.

With Account Abstraction, however, the validation can also include an arbitrary EVM code and rely on storage as well,
which means that unrelated `UserOperations` or transactions may invalidate each other.

If not addressed, this would make the job of maintaining a mempool of valid `UserOperations` and producing valid
bundles computationally infeasible and susceptible to DoS attacks.

This document describes a set of validation rules that if applied by a bundler before accepting a `UserOperation`
into the mempool can prevent such attacks.

### The high-level goal

The purpose of this specification is to define a consensus between nodes (bundlers or block-builders) when processing incoming UserOperations from an external source.
This external source for UserOperations is either an end-user node (via RPC) or another node in the p2p network.

The protocol tries to detect "spam" - which are large bursts of UserOperations that cannot be included on-chain (and thus can't pay).
The network is protected by throttling down requests from such spammer nodes.

All nodes in the network must have the same definition of "spam": otherwise, if some nodes accept some type of UserOperations and propagate them while others consider them spam, those "forgiving" nodes will be considered "spammers" by the rest of the nodes, and the network effectively gets split.

### The processing flow of a UserOperation

- First, a UserOperation is received - either via RPC (submitted on behalf of a single application) or via the p2p protocol, from another node in the mempool.
- The node performs validation on the UserOperation, and then adds it to its in-memory mempool, and submits it to its peers.
- Lastly, when building a block, a node collects UserOperations from the mempool, performs a 2nd validation to make sure they are all still valid as a bundle and submits them into the next block.

### The need for 2nd validation before submitting a block

A normal Ethereum transaction in the mempool can be invalidated if another transaction was received with the same nonce. That other transaction had to increase the gas price in order to replace the first one, so it satisfied the rule of "must pay to get included into the mempool"
With contract-based accounts, since the UserOperation validity may depend on mutable state, other transactions may invalidate a previously valid UserOperation, so we must check it before inclusion

### Rationale of limiting opcodes:

- the validation is performed off-chain, before creating a block. Some opcodes access information that is known only when creating the block.
- using those opcodes while validating a transaction can easily create a validation rule that will succeed off-chain, but always revert on-chain, and thus cause a DoS attack.
- a simple example is `require block.number==12345`. It can be valid when validating the UserOperation and adding it to the mempool
but will be invalid when attempting to include it on-chain at a later block.

### Rationale for limiting storage access

- We need UserOperation validations not to overlap so that a single storage change can't easily invalidate a large number of UserOperations in the mempool. By limiting UserOperations to access storage associated with the account itself, we know that we can for sure include a single UserOperation for each account in a bundle
- (A bundler MAY include more than one UserOperation of an account in a bundle, MUST first validate them together)

### Rationale of requiring a stake

We want to be able to allow globally-used contracts (paymasters, factories) to use storage not associated with the account, but still prevent them from
spamming the mempool.
If a contract causes too many UserOperations to fail in their second validation after succeeding in their first, we can throttle its use in the mempool.
By requiring such a contract to have a stake, we prevent a "Sybil attack", by making it expensive to create a large number of such paymasters to continue the spam attack.

By following the validation rules, we can detect contracts that cause spam UserOperations, and throttle them.
The stake comes to prevent the fast re-creation of malicious entities.
The stake is never slashed (since it is only used for off-chain detection) but is locked for a period of time, which makes such an attack much more expensive.


### Definition of the `mass invalidation attack`

A possible set of actions is considered to be a `mass invalidation attack` on the network if a large number of
`UserOperations` that did pass the initial validation and were accepted by nodes and propagated further into the
mempool to other bundlers in the network becomes invalid and not eligible for inclusion in a block.

There are 3 ways to perform such an attack:

1. Submit `UserOperation`s that pass the initial validation, but later fail the re-validation
that is performed during the bundle creation.
2. Submit `UserOperation`s that are valid in isolation during validation, but when bundled
together become invalid.
3. Submit valid `UserOperation`s but "front-run" them by executing a state change on the
network that causes them to become invalid. The "front-run" in question must be economically viable.

To prevent such attacks, we attempt to "sandbox" the validation code.
We isolate the validation code from other `UserOperations`, from external changes to the storage, and
from information about the environment such as a current block timestamp.

### What is not considered a `mass invalidation attack`

A `UserOperation` that fails the initial validation by a receiving node without entering its mempool is not
considered an attack. The node is expected to apply web2 security measures and throttle requests based on API key,
source IP address, etc.
RPC nodes already do that to prevent being spammed with invalid transactions which also have a validation cost.
P2P nodes already have (and should apply) a scoring mechanism to determine spammer nodes.

Also, if the invalidation of `N` UserOperations from the mempool costs `N*X` with a sufficiently large `X`, it is not considered an economically viable attack.

- The minimum change to cause an invalidation is a storage change (5k gas)
- Assuming a Node can sustain processing 2000 invalid UserOps per block, the cost of a DoS attack is 10M gas per block.
- The above value is high, but we take further measures to make such an attack more expensive.


## Specification
Expand Down Expand Up @@ -381,6 +288,100 @@ Alt-mempools are served by the same bundlers participating in the canonical memp
The "opsIncluded" is incremented after this UserOperation is included on-chain (either by this bundler, or another)
* **[AREP-020]** the alt-mempool becomes THROTTLED based on the [Reputation Calculation](#reputation-calculation)

## Rationale

All transactions initiated by EOAs have an implicit validation phase where balance, nonce, and signature are
checked to be valid for the current state of the Ethereum blockchain.
Once the transaction is checked to be valid by a node, only another transaction by the same EOA can modify the Ethereum
state in a way that makes the first transaction invalid.

With Account Abstraction, however, the validation can also include an arbitrary EVM code and rely on storage as well,
which means that unrelated `UserOperations` or transactions may invalidate each other.

If not addressed, this would make the job of maintaining a mempool of valid `UserOperations` and producing valid
bundles computationally infeasible and susceptible to DoS attacks.

This document describes a set of validation rules that if applied by a bundler before accepting a `UserOperation`
into the mempool can prevent such attacks.

### The high-level goal

The purpose of this specification is to define a consensus between nodes (bundlers or block-builders) when processing incoming UserOperations from an external source.
This external source for UserOperations is either an end-user node (via RPC) or another node in the p2p network.

The protocol tries to detect "spam" - which are large bursts of UserOperations that cannot be included on-chain (and thus can't pay).
The network is protected by throttling down requests from such spammer nodes.

All nodes in the network must have the same definition of "spam": otherwise, if some nodes accept some type of UserOperations and propagate them while others consider them spam, those "forgiving" nodes will be considered "spammers" by the rest of the nodes, and the network effectively gets split.

### The processing flow of a UserOperation

- First, a UserOperation is received - either via RPC (submitted on behalf of a single application) or via the p2p protocol, from another node in the mempool.
- The node performs validation on the UserOperation, and then adds it to its in-memory mempool, and submits it to its peers.
- Lastly, when building a block, a node collects UserOperations from the mempool, performs a 2nd validation to make sure they are all still valid as a bundle and submits them into the next block.

### The need for 2nd validation before submitting a block

A normal Ethereum transaction in the mempool can be invalidated if another transaction was received with the same nonce. That other transaction had to increase the gas price in order to replace the first one, so it satisfied the rule of "must pay to get included into the mempool"
With contract-based accounts, since the UserOperation validity may depend on mutable state, other transactions may invalidate a previously valid UserOperation, so we must check it before inclusion

### Rationale of limiting opcodes:

- the validation is performed off-chain, before creating a block. Some opcodes access information that is known only when creating the block.
- using those opcodes while validating a transaction can easily create a validation rule that will succeed off-chain, but always revert on-chain, and thus cause a DoS attack.
- a simple example is `require block.number==12345`. It can be valid when validating the UserOperation and adding it to the mempool
but will be invalid when attempting to include it on-chain at a later block.

### Rationale for limiting storage access

- We need UserOperation validations not to overlap so that a single storage change can't easily invalidate a large number of UserOperations in the mempool. By limiting UserOperations to access storage associated with the account itself, we know that we can for sure include a single UserOperation for each account in a bundle
- (A bundler MAY include more than one UserOperation of an account in a bundle, MUST first validate them together)

### Rationale of requiring a stake

We want to be able to allow globally-used contracts (paymasters, factories) to use storage not associated with the account, but still prevent them from
spamming the mempool.
If a contract causes too many UserOperations to fail in their second validation after succeeding in their first, we can throttle its use in the mempool.
By requiring such a contract to have a stake, we prevent a "Sybil attack", by making it expensive to create a large number of such paymasters to continue the spam attack.

By following the validation rules, we can detect contracts that cause spam UserOperations, and throttle them.
The stake comes to prevent the fast re-creation of malicious entities.
The stake is never slashed (since it is only used for off-chain detection) but is locked for a period of time, which makes such an attack much more expensive.


### Definition of the `mass invalidation attack`

A possible set of actions is considered to be a `mass invalidation attack` on the network if a large number of
`UserOperations` that did pass the initial validation and were accepted by nodes and propagated further into the
mempool to other bundlers in the network becomes invalid and not eligible for inclusion in a block.

There are 3 ways to perform such an attack:

1. Submit `UserOperation`s that pass the initial validation, but later fail the re-validation
that is performed during the bundle creation.
2. Submit `UserOperation`s that are valid in isolation during validation, but when bundled
together become invalid.
3. Submit valid `UserOperation`s but "front-run" them by executing a state change on the
network that causes them to become invalid. The "front-run" in question must be economically viable.

To prevent such attacks, we attempt to "sandbox" the validation code.
We isolate the validation code from other `UserOperations`, from external changes to the storage, and
from information about the environment such as a current block timestamp.

### What is not considered a `mass invalidation attack`

A `UserOperation` that fails the initial validation by a receiving node without entering its mempool is not
considered an attack. The node is expected to apply web2 security measures and throttle requests based on API key,
source IP address, etc.
RPC nodes already do that to prevent being spammed with invalid transactions which also have a validation cost.
P2P nodes already have (and should apply) a scoring mechanism to determine spammer nodes.

Also, if the invalidation of `N` UserOperations from the mempool costs `N*X` with a sufficiently large `X`, it is not considered an economically viable attack.

- The minimum change to cause an invalidation is a storage change (5k gas)
- Assuming a Node can sustain processing 2000 invalid UserOps per block, the cost of a DoS attack is 10M gas per block.
- The above value is high, but we take further measures to make such an attack more expensive.

## Security Considerations

This document describes the security considerations bundlers must take to protect themselves (and the entire mempool network)
Expand Down

0 comments on commit 47df0e0

Please sign in to comment.