Account Abstraction Validation Scope Rules
Abstract
This document describes the rules we impose on the validation context of Account Abstraction transactions,
such as ERC-4337 UserOperation
or RIP-7560 (Native Account Abstraction), which are enforced off-chain by a
block builder or a standalone bundler, and the rationale behind each one of them.
Motivation
With Account-Abstraction, instead of hard-coded logic for processing a transaction (validation, gas-payment, and execution), this logic is executed by EVM code. The benefits for the account are countless -
- abstracting the validation allows the contract to use different signature schemes, multisig configuration, custom recovery, and more.
- abstracting gas payments allows easy onboarding by 3rd party payments, paying with tokens, cross-chain gas payments
- abstracting execution allows batch transactions
All of the above are missing from the EOA account model.
However, there is one rule a transaction must follow to preserve the decentralized network: once submitted into the network (the mempool), the transaction is guaranteed to pay. This comes to prevent denial of service attacks on the network.
The EOA model implicitly follows the rule: a valid transaction can’t become invalid without payment by the account: e.g account balance can’t be reduced (except with a higher paying transaction)
This simple rule makes the network sustainable and DoS-protected: the network can’t be cheaply attacked by a mass of transactions. An attack (sending a mass of transactions) is expensive, and gets more expensive as the network clogs. Legitimate users pay more, and can delay operations to avoid the cost, but the attacker pays a huge (and increasing) amount to keep the network clogged.
For Account-Abstraction system we want to keep the same rule, so that attempting a DoS attack on the network should be as expensive. In order to do so, we add the following validation rules.
For the actual interfaces of those contract-based accounts see the definitions in ERC-4337 and RIP-7560.
This documentation uses the terminology “UserOperation” for a transaction created by a smart contract account, and closely follows ERC-4337 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.
Specification
Rule Types
There are two types of rules:
- Network-wide rules rules that MUST be applied to each UserOperation before accepting it into the local mempool and propagating it.
These rules include the opcode and storage rules.
- Failing these validation rules SHOULD drop the UserOperation
- Failing these validations during 2nd validation phase (before submitting a bundle) SHOULD degrade the reputation of the offending entity
- Bundler MUST NOT propagate UserOperations that fail the validation rules, otherwise it will be considered a “spammer” by other bundlers in the mempool, and get disconnected.
- Local rules
These are “soft” rules, based on the reputation of entities.
These rules come to protect the bundler itself from spamming attacks.
- Bundlers SHOULD drop such UserOperations without performing validation.
- Bundlers SHOULD NOT propagate such UserOperations to other bundlers.
- Bundlers SHOULD NOT consider another bundler a “spammer” if it does.
Constants
Title | Value | Comment |
---|---|---|
MIN_UNSTAKE_DELAY |
86400 | 1 day |
MIN_STAKE_VALUE |
Adjustable per chain value | Equivalent to ~$1000 in native tokens |
SAME_SENDER_MEMPOOL_COUNT |
4 | |
SAME_UNSTAKED_ENTITY_MEMPOOL_COUNT |
10 | |
THROTTLED_ENTITY_MEMPOOL_COUNT |
4 | Number of UserOperations with a throttled entity that can stay in the mempool |
THROTTLED_ENTITY_LIVE_BLOCKS |
10 | Number of blocks a UserOperations with a throttled entity can stay in mempool |
THROTTLED_ENTITY_BUNDLE_COUNT |
4 | |
MIN_INCLUSION_RATE_DENOMINATOR |
100 (client) \ 10 (bundler) | |
THROTTLING_SLACK |
10 | |
BAN_SLACK |
50 | |
BAN_OPS_SEEN_PENALTY |
10000 | |
MAX_OPS_ALLOWED_UNSTAKED_ENTITY |
10000 |
Validation Rules
Definitions:
- Validation Phases: there are up to 3 phases of validation
- smart account deployment
- smart account validation
- paymaster validation.
- Entity: a contract that is explicitly specified by the
UserOperation
. Includes thefactory
,paymaster
,aggregator
, and stakedaccount
, as discussed below.
Each “validation phase” is attributed to a single entity.
Entity contracts must have non-empty code on-chain. - Canonical Mempool: The rules defined in this document apply to the main mempool shared by all bundlers on the network.
- Staked Entity: an entity that has a locked stake of at least
MIN_STAKE_VALUE
and an unstake delay of at leastMIN_UNSTAKE_DELAY
. - Associated storage: a storage slot of any smart contract is considered to be “associated” with address
A
if:- The slot value is
A
- The slot value was calculated as
keccak(A||x)+n
, wherex
is abytes32
value, andn
is a value in the range 0..128
- The slot value is
- Using an address: accessing the code of a given address in any way.
This can be done by executing
*CALL
orEXTCODE*
opcodes for a given address.
Reputation Definitions
-
opsSeen: a per-entity counter of how many times a unique valid
UserOperation
referencing this entity was received by this bundler. This includesUserOperation
received via incoming RPC calls or through a P2P mempool protocol. - opsIncluded: a per-entity counter of how many times a unique valid
UserOperation
referencing this entity appeared in an actual includedUserOperation
.
Calculation of this value is based on UserOperationEvents and is only counted forUserOperations
that were previously counted asopsSeen
by this bundler. - Both values are updated every hour as
value = value * 23 // 24
Effectively, the value is reduced to 1% after 4 days. - inclusionRate: Relation of
opsIncluded
toopsSeen
Reputation Calculation
We define a value max_seen = opsSeen // MIN_INCLUSION_RATE_DENOMINATOR
.
The reputation state of each entity is determined as follows:
- BANNED:
max_seen > opsIncluded + BAN_SLACK
- THROTTLED:
max_seen > opsIncluded + THROTTLING_SLACK
- OK: otherwise
Note that new entities start with an OK
reputation.
To help make sense of these params, note that a malicious paymaster can at most cause the network (only the p2p network, not the blockchain) to process BAN_SLACK * MIN_INCLUSION_RATE_DENOMINATOR / 24
non-paying ops per hour.
Running the Validation Rules
- A block builder or a bundler should perform a full validation before accepting a
UserOperation
into its mempool. - During the validation phase, the bundler should trace the execution and apply all the rules defined in this document.
- A bundler should also perform a full validation of the entire bundle before submission.
- The validation rules prevent an unstaked entity from detecting the bundle validation. However, a malicious staked entity can detect that it is running in a bundle validation and cause a revert.
- The failed
UserOperation
should be dropped from the bundle. - The staked entity that caused a revert violated the Account Abstraction rules and should be marked as
THROTTLED
.
Mempool Validation Rules
- A
UserOperation
is broadcast over the P2P protocol with the following information:- The
UserOperation
itself - The blockhash this
UserOperation
was originally verified against.
- The
- Once a
UserOperation
is received from another bundler it should be verified locally by a receiving bundler. - A received
UserOperation
may fail any of the reasonable static checks, such as:
invalid format, values below minimum, submitted with a blockhash that isn’t recent, etc.
In this case, the bundler should drop this particularUserOperation
but keep the connection. - The bundler should check the
UserOperation
against the nonces of last-included bundles.
Silently dropUserOperations
withnonce
that was recently included. This invalidation is likely attributable to a network race condition and should not cause a reputation change. - If a received
UserOperation
fails against the current block:- Retry the validation against the block the
UserOperation
was originally verified against. - If it succeeds, silently drop the
UserOperation
and keep the connection. - If it fails, mark the sender as a “spammer”
- Retry the validation against the block the
Opcode Rules
- Block access from opcodes that access information outside of storage and code (aka “environment”).
- [OP-011] Blocked opcodes:
ORIGIN
(0x32
)GASPRICE
(0x3A
)BLOCKHASH
(0x40
)COINBASE
(0x41
)TIMESTAMP
(0x42
)NUMBER
(0x43
)PREVRANDAO
/DIFFICULTY
(0x44
)GASLIMIT
(0x45
)BASEFEE
(0x48
)CREATE
(0xF0
)INVALID
(0xFE
)SELFDESTRUCT
(0xFF
)
- [OP-012]
GAS
(0x5A
) opcode is allowed, but only if followed immediately by*CALL
instructions, else it is blocked.
This is a common way to pass all remaining gas to an external call, and it means that the actual value is consumed from the stack immediately and cannot be accessed by any other opcode. - [OP-13] any “unassigned” opcode.
- [OP-011] Blocked opcodes:
- [OP-020] Revert on “out of gas” is forbidden as it can “leak” the gas limit or the current call stack depth.
- Contract creation:
- [OP-031]
CREATE2
is allowed exactly once in the deployment phase and must deploy code for the “sender” address.
- [OP-031]
- Access to an address without a deployed code is forbidden:
- [OP-041] For
EXTCODE*
and*CALL
opcodes. - [OP-042] Exception: access to the “sender” address is allowed.
This is only possible in
factory
code during the deployment phase.
- [OP-041] For
- Allowed access to the
EntryPoint
address:- [OP-051] May call
EXTCODESIZE ISZERO
This pattern is used to check destination has a code before thedepositTo
function is called. - [OP-052] May call
depositTo(sender)
with any value from either thesender
orfactory
. - [OP-053] May call the fallback function from the
sender
with any value. - [OP-054] Any other access to the
EntryPoint
is forbidden.
- [OP-051] May call
*CALL
opcodes:- [OP-061]
CALL
withvalue
is forbidden. The only exception is a call to theEntryPoint
described above. - [OP-062] Precompiles:
- Only allow known accepted precompiles on the network, that do not access anything in the blockchain state or environment.
- The core precompiles 0x1 .. 0x9
- The RIP-7212 secp256r1 precompile, on networks that accepted it.
- [OP-061]
- [OP-070] Transient Storage slots defined in EIP-1153 and accessed using
TLOAD
(0x5c
) andTSTORE
(0x5d
) opcodes are treated exactly like persistent storage (SLOAD/SSTORE). - [OP-080]
BALANCE
(0x31
) andSELFBALANCE
(0x47
) are allowed only from a staked entity, else they are blocked.
Code Rules
- [COD-010] Between the first and the second validations, the
EXTCODEHASH
value of any visited address, entity, or referenced library, may not be changed.
If the code is modified, the UserOperation is considered invalid.
Storage Rules
The storage access with SLOAD
and SSTORE
(and TLOAD
, TSTORE
) instructions within each phase is limited as follows:
- [STO-010] Access to the “account” storage is always allowed.
- Access to associated storage of the account in an external (non-entity) contract is allowed if either:
- [STO-021] The account already exists.
- [STO-022] There is an
initCode
and thefactory
contract is staked.
- If the entity (
paymaster
,factory
) is staked, then it is also allowed:- [STO-031] Access the entity’s own storage.
- [STO-032] Read/Write Access to storage slots that are associated with the entity, in any non-entity contract.
- [STO-033] Read-only access to any storage in non-entity contract.
Local Rules
Local storage rules protect the bundler against denial of service at the time of bundling. They do not affect mempool propagation and cannot cause a bundler to be marked as a “spammer”.
- [STO-040]
UserOperation
may not use an entity address (factory
/paymaster
/aggregator
) that is used as an “account” in anotherUserOperation
in the mempool.
This means thatPaymaster
andFactory
contracts cannot practically be an “account” contract as well. - [STO-041]
UserOperation
may not use associated storage (of either its account or from staked entity) in a contract that is a “sender” of another UserOperation in the mempool.
General Reputation Rules
The following reputation rules apply for all staked entities, and for unstaked paymasters. All rules apply to all of these entities unless specified otherwise.
- [GREP-010] A
BANNED
address is not allowed into the mempool.
Also, all existingUserOperations
referencing this address are removed from the mempool. - [GREP-020] A
THROTTLED
address is limited to:THROTTLED_ENTITY_MEMPOOL_COUNT
entries in the mempool.THROTTLED_ENTITY_BUNDLE_COUNT
UserOperations
in a bundle.- Can remain in the mempool only for
THROTTLED_ENTITY_LIVE_BLOCKS
.
- [GREP-040] If an entity fails the bundle creation after passing second validation, its
opsSeen
set toBAN_OPS_SEEN_PENALTY
, andopsIncluded
to zero, causing it to beBANNED
.
Staked Entities Reputation Rules
- [SREP-010] The “canonical mempool” defines a staked entity if it has
MIN_STAKE_VALUE
and unstake delay ofMIN_UNSTAKE_DELAY
- [SREP-020] MOVED TO GREP-010
- [SREP-030] MOVED TO GREP-020
- [SREP-040] An
OK
staked entity is unlimited by the reputation rule.- Allowed in unlimited numbers in the mempool.
- Allowed in unlimited numbers in a bundle.
- [SREP-050] MOVED TO GREP-040
Entity-specific Rules
- [EREP-010] For each
paymaster
, the mempool must maintain the total gasUserOperations
using thispaymaster
may consume.- Do not add a
UserOperation
to the mempool if the maximum total gas cost, including the newUserOperation
, is above the deposit of thepaymaster
at the current gas price.
- Do not add a
- [EREP-011] REMOVED
- [EREP-015] A
paymaster
should not have its opsSeen incremented on failure of factory or account- When running 2nd validation (before inclusion in a bundle), if a UserOperation fails because of factory or account error (either a FailOp revert or validation rule), then the paymaster’s opsSeen valid is decremented by 1.
- [EREP-020] A staked factory is “accountable” for account breaking the rules.
That is, if thevalidateUserOp()
is rejected for any reason in aUserOperation
that has aninitCode
, it is treated as if the factory caused this failure, and thus this affects its reputation. - [EREP-030] A Staked Account is accountable for failures in other entities (
paymaster
,aggregator
) even if they are staked. - [EREP-040] An
aggregator
must be staked, regardless of storage usage. - [EREP-050] REMOVED
Unstaked Paymasters Reputation Rules
- Definitions:
opsSeen
,opsIncluded
, and reputation calculation are defined above.UnstakedReputation
of an entity determines the maximum number of entries using this entity allowed in the mempool.opsAllowed
is a reputation-based calculation for an unstaked entity, representing how manyUserOperations
it is allowed to have in the mempool.- Rules:
- [UREP-010] An unstaked sender is only allowed to have
SAME_SENDER_MEMPOOL_COUNT
UserOperation
s in the mempool. - [UREP-011] A staked sender is only limited by the Staked Entities Reputation Rules.
- [UREP-020] For an unstaked paymaster only that is not throttled/banned:
opsAllowed = SAME_UNSTAKED_ENTITY_MEMPOOL_COUNT + inclusionRate * min(opsIncluded, MAX_OPS_ALLOWED_UNSTAKED_ENTITY)
. - This is a default of
SAME_UNSTAKED_ENTITY_MEMPOOL_COUNT
for new entity - [UREP-030] REMOVED
- [UREP-010] An unstaked sender is only allowed to have
Alt-mempools Rules
Alternate mempool is an agreed-upon rule that the bundlers may opt into, in addition to the canonical mempool The alt-mempool “topic” is a unique identifier. By convention, this is the IPFS hash of the document describing (in clear test and YAML file) the specifics of this alt mempool
- [ALT-010] The bundler listens to the alt-mempool “topic” over the P2P protocol
- [ALT-020] The alt mempool rules MUST be checked only when a canonical rule is violated
- That is, if validation follows the canonical rules above, it is not considered part of an alt-mempool.
- [ALT-021] Such a
UserOperation
(that violates the canonical rules) is checked against all the “alternate mempools”, and is considered part of all those alt-mempools - [ALT-030] Bundlers SHOULD forward
UserOperations
to other bundlers only once, regardless of how many (shared) alt-mempools they have.
The receiving bundler validates theUserOperations
, and based on the above rules (and subscribed alt-mempools) decides which alt-mempools to propagate it to. - [ALT-040] opsInclude and opsSeen of entities are kept per alt-mempool. That is, an entity can be considered throttled (or banned) in one mempool, while still active on another.
Alt-mempool Reputation
Alt-mempools are served by the same bundlers participating in the canonical mempool, but change the rules and may introduce denial-of-service attack vectors. To prevent them from taking the canonical mempool or other alt mempools down with them, a reputation is managed for each. An alt mempool that causes too many invalidations gets throttled. This limits the scope of the attack and lets the bundler continue doing its work for other mempools.
- [AREP-010] each alt-mempool manages “opsSeen” and “opsIncluded”, much like entities. The opsSeen is incremented after
UserOperation
initial validation, where it is considered part of this mempool. 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
- [AREP-030] REMOVED
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 multiple UserOperations of the same account in a bundle, but 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:
- Submit
UserOperation
s that pass the initial validation, but later fail the re-validation that is performed during the bundle creation. - Submit
UserOperation
s that are valid in isolation during validation, but when bundled together become invalid. - 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) from denial-of-service attacks.
Copyright
Copyright and related rights waived via CC0.