diff --git a/erc/ERCS/erc-4337.md b/erc/ERCS/erc-4337.md index e765508f1..0f3079bca 100644 --- a/erc/ERCS/erc-4337.md +++ b/erc/ERCS/erc-4337.md @@ -8,7 +8,7 @@ status: Draft type: Standards Track category: ERC created: 2021-09-29 -requires: eip-aa-rules +requires: 7562 --- ## Abstract @@ -29,8 +29,8 @@ This proposal takes a different approach, avoiding any adjustments to the consen * **Do not require any Ethereum consensus changes**: Ethereum consensus layer development is focusing on the merge and later on scalability-oriented features, and there may not be any opportunity for further protocol changes for a long time. Hence, to increase the chance of faster adoption, this proposal avoids Ethereum consensus changes. * **Try to support other use cases** * Privacy-preserving applications - * Atomic multi-operations (similar goal to [EIP-3074](./eip-3074.md)) - * Pay tx fees with [ERC-20](./eip-20.md) tokens, allow developers to pay fees for their users, and [EIP-3074](./eip-3074.md)-like **sponsored transaction** use cases more generally + * Atomic multi-operations (similar goal to [EIP-3074]) + * Pay tx fees with [ERC-20](./erc-20.md) tokens, allow developers to pay fees for their users, and [EIP-3074]-like **sponsored transaction** use cases more generally * Support aggregated signature (e.g. BLS) ## Specification @@ -245,13 +245,13 @@ A node/bundler SHOULD drop (not add to the mempool) a `UserOperation` that fails ### Extension: paymasters -We extend the entry point logic to support **paymasters** that can sponsor transactions for other users. This feature can be used to allow application developers to subsidize fees for their users, allow users to pay fees with [ERC-20](./eip-20.md) tokens and many other use cases. When the paymasterAndData field in the UserOp is not empty, the entry point implements a different flow for that UserOperation: +We extend the entry point logic to support **paymasters** that can sponsor transactions for other users. This feature can be used to allow application developers to subsidize fees for their users, allow users to pay fees with [ERC-20](./erc-20.md) tokens and many other use cases. When the paymasterAndData field in the UserOp is not empty, the entry point implements a different flow for that UserOperation: ![](../assets/erc-4337/bundle-seq-pm.svg) During the verification loop, in addition to calling `validateUserOp`, the `handleOps` execution also must check that the paymaster has enough ETH deposited with the entry point to pay for the operation, and then call `validatePaymasterUserOp` on the paymaster to verify that the paymaster is willing to pay for the operation. Note that in this case, the `validateUserOp` is called with a `missingAccountFunds` of 0 to reflect that the account's deposit is not used for payment for this userOp. -If the paymaster's validatePaymasterUserOp returns a "context", then `handleOps` must call `postOp` on the paymaster after making the main execution call. It must guarantee the execution of `postOp`, by making the main execution inside an inner call context, and if the inner call context reverts attempting to call `postOp` again in an outer call context. +If the paymaster's validatePaymasterUserOp returns a "context", then `handleOps` must call `postOp` on the paymaster after making the main execution call. Maliciously crafted paymasters _can_ DoS the system. To prevent this, we use a reputation system. paymaster must either limit its storage usage, or have a stake. see the [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. @@ -385,7 +385,7 @@ A node MAY drop a UserOperation if it expires too soon (e.g. wouldn't make it to If the `ValidationResult` includes `sigFail`, the client SHOULD drop the `UserOperation`. In order to prevent DoS attack on bundlers, they must make sure the validation methods above pass the validation rules, which constraint their usage of opcodes and storage. -For the complete procedure see [eip-aa-validation-rules](./eip-aa-rules.md) +For the complete procedure see [ERC-7562](./erc-7562.md) #### Alternative Mempools @@ -396,7 +396,7 @@ A bundler cannot simply "whitelist" request from a specific paymaster: if that p bundlers, then its support will be sporadic at best. Instead, we introduce the term "alternate mempool": a modified validation rules, and procedure of propagating them to other bundlers. -The procedure of using alternate mempools is defined in ../eip-aa-rules.md#Alt-mempools-rules +The procedure of using alternate mempools is defined in [ERC-7562](./erc-7562.md#Alt-mempools-rules) ### Bundling @@ -434,6 +434,21 @@ Otherwise, attackers may be able to use the banned opcodes to detect running on- When a bundler includes a bundle in a block it must ensure that earlier transactions in the block don't make any UserOperation fail. It should either use access lists to prevent conflicts, or place the bundle as the first transaction in the block. + +## Rationale + +The main challenge with a purely smart contract wallet based account abstraction system is DoS safety: how can a block builder including an operation make sure that it will actually pay fees, without having to first execute the entire operation? Requiring the block builder to execute the entire operation opens a DoS attack vector, as an attacker could easily send many operations that pretend to pay a fee but then revert at the last moment after a long execution. Similarly, to prevent attackers from cheaply clogging the mempool, nodes in the P2P network need to check if an operation will pay a fee before they are willing to forward it. + +The first step is clean separation between validation (acceptance of UserOperation, and acceptance to pay) and execution. +In this proposal, we expect accounts to have a `validateUserOp` method that takes as input a `UserOperation`, and verify the signature and pay the fee. +Only if this method returns successfully, the execution will happen. + +The entry point-based approach allows for a clean separation between verification and execution, and keeps accounts' logic simple. It enforces the simple rule that only after validation is successful (and the UserOp can pay), the execution is done, and also guarantees the fee payment. + +The last step is protecting the bundlers from denial-of-service attacks by a mass number of UserOperation that appear to be valid (and pay) but that eventually revert, and thus block the bundler from processing valid UserOperations. +For this purpose, the bundler requires a set of [restrictions on the validation function](./erc-7526.md), to prevent such denial-of-service attacks. + + ### Reputation scoring and throttling/banning for global entities #### Reputation Rationale. @@ -453,12 +468,10 @@ The stake value is not enforced on-chain, but specifically by each node while si ### Paymasters -Paymasters facilitate transaction sponsorship, allowing third-party-designed mechanisms to pay for transactions. Many of these mechanisms _could_ be done by having the paymaster wrap a `UserOperation` with their own, but there are some important fundamental limitations to that approach: - -* No possibility for "passive" paymasters (eg. that accept fees in some ERC-20 token at an exchange rate pulled from an on-chain DEX) -* Paymasters run the risk of getting griefed, as users could send ops that appear to pay the paymaster but then change their behavior after a block +Paymaster contracts allow abstraction of gas: having a contract, that is not the sender of the transaction, pay for the transaction fees. -The paymaster scheme allows a contract to passively pay on users' behalf under arbitrary conditions. It even allows ERC-20 token paymasters to secure a guarantee that they would only need to pay if the user pays them: the paymaster contract can check that there is sufficient approved ERC-20 balance in the `validatePaymasterUserOp` method, and then extract it with `transferFrom` in the `postOp` call; if the op itself transfers out or de-approves too much of the ERC-20s, the inner `postOp` will fail and revert the execution and the outer `postOp` can extract payment (note that because of storage access restrictions the ERC-20 would need to be a wrapper defined within the paymaster itself). +Paymaster architecture allows them to follow the model of "pre-charge, and later refund". +E.g. a token-paymaster may pre-charge the user with the max possible price of the transaction, and refund the user with the excess afterwards. ### First-time account creation @@ -939,7 +952,7 @@ Assume the given UserOperations all pass validation (without actually validating ## Backwards Compatibility -This EIP does not change the consensus layer, so there are no backwards compatibility issues for Ethereum as a whole. Unfortunately it is not easily compatible with pre-[ERC-4337](./eip-4337.md) accounts, because those accounts do not have a `validateUserOp` function. If the account has a function for authorizing a trusted op submitter, then this could be fixed by creating an [ERC-4337](./eip-4337.md) compatible account that re-implements the verification logic as a wrapper and setting it to be the original account's trusted op submitter. +This ERC does not change the consensus layer, so there are no backwards compatibility issues for Ethereum as a whole. Unfortunately it is not easily compatible with pre-[ERC-4337](./eip-4337.md) accounts, because those accounts do not have a `validateUserOp` function. If the account has a function for authorizing a trusted op submitter, then this could be fixed by creating an [ERC-4337](./eip-4337.md) compatible account that re-implements the verification logic as a wrapper and setting it to be the original account's trusted op submitter. ## Reference Implementation @@ -947,7 +960,7 @@ See `https://github.com/eth-infinitism/account-abstraction/tree/main/contracts` ## Security Considerations -The entry point contract will need to be very heavily audited and formally verified, because it will serve as a central trust point for _all_ [ERC-4337](./eip-4337.md). In total, this architecture reduces auditing and formal verification load for the ecosystem, because the amount of work that individual _accounts_ have to do becomes much smaller (they need only verify the `validateUserOp` function and its "check signature and pay fees" logic) and check that other functions are `msg.sender == ENTRY_POINT` gated (perhaps also allowing `msg.sender == self`), but it is nevertheless the case that this is done precisely by concentrating security risk in the entry point contract that needs to be verified to be very robust. +The entry point contract will need to be very heavily audited and formally verified, because it will serve as a central trust point for _all_ [ERC-4337]. In total, this architecture reduces auditing and formal verification load for the ecosystem, because the amount of work that individual _accounts_ have to do becomes much smaller (they need only verify the `validateUserOp` function and its "check signature and pay fees" logic) and check that other functions are `msg.sender == ENTRY_POINT` gated (perhaps also allowing `msg.sender == self`), but it is nevertheless the case that this is done precisely by concentrating security risk in the entry point contract that needs to be verified to be very robust. Verification would need to cover two primary claims (not including claims needed to protect paymasters, and claims needed to establish p2p-level DoS resistance): diff --git a/erc/ERCS/erc-7562.md b/erc/ERCS/erc-7562.md index c75a33068..be798ce94 100644 --- a/erc/ERCS/erc-7562.md +++ b/erc/ERCS/erc-7562.md @@ -1,27 +1,50 @@ --- -eip: 0 +eip: 7562 title: Account Abstraction Validation Scope Rules -description: A set of limitations on validation EVM code that protects Account Abstraction nodes from exponentially complex computations without compensation +description: A set of limitations on validation EVM code to protect Account Abstraction nodes from denial-of-service attacks through unpaid computation. author: Yoav Weiss (@yoavw), Dror Tirosh (@drortirosh), Alex Forshtat (@forshtat), Shahaf Nacson (@shahafn) -discussions-to: https://www.google.com/ +discussions-to: https://ethereum-magicians.org/t/erc-7562-account-abstraction-validation-scope-rules/16683 status: Draft type: Standards Track -category: Core +category: ERC created: 2023-09-01 -requires: 6780 --- ## Abstract -This document describes the validation rules we impose on the validation context of Account Abstraction transactions, -such as [ERC-4337](./eip-4337) `UserOperation` or [EIP-9999](./eip-9999), which are enforced off-chain by a +This document describes the rules we impose on the validation context of Account Abstraction transactions, +such as [ERC-4337](./erc-4337.md) `UserOperation` or [RIP-7560](./rip-7560.md) (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](./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 then another transaction by the same EOA can modify the Ethereum +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, @@ -30,22 +53,66 @@ which means that unrelated `UserOperations` or transactions may invalidate each 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` +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. -## Specification +### 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 all bundlers in the network, becomes invalid and not eligible for inclusion in a block. +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. Create a `UserOperation` that passes the initial validation, but later fails the re-validation +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. +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. @@ -59,9 +126,37 @@ A `UserOperation` that fails the initial validation by a receiving node without 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. -### Constants: +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 + +### 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 | |--------------------------------------|-----------------------------|---------------------------------------------------------------------------------| @@ -73,19 +168,20 @@ RPC nodes already do that to prevent being spammed with invalid transactions whi | `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) | | -| `INCLUSION_RATE_FACTOR` | 10 | | | `THROTTLING_SLACK` | 10 | | | `BAN_SLACK` | 50 | | +| `BAN_OPS_SEEN_PENALTY` | 10000 | | +| `MAX_OPS_ALLOWED_UNSTAKED_ENTITY` | 10000 | ### Validation Rules ### **Definitions**: 1. **Validation Phases**: there are up to 3 phases of validation - 1. smart account deployment - 2. smart account validation - 3. paymaster validation. -2. **Entity**: a contract that is explicitly used by the `UserOperation`. - Includes the `factory`, `paymaster`, `aggregator` and staked `account`, as discussed below. \ + 1. smart account deployment + 2. smart account validation + 3. paymaster validation. +2. **Entity**: a contract that is explicitly specified by the `UserOperation`. + Includes the `factory`, `paymaster`, `aggregator`, and staked `account`, as discussed below. \ Each "validation phase" is attributed to a single entity. \ Entity contracts must have non-empty code on-chain. 3. **Canonical Mempool**: The rules defined in this document apply to the main mempool shared by all bundlers on the network. @@ -100,8 +196,7 @@ RPC nodes already do that to prevent being spammed with invalid transactions whi ### Reputation Definitions: 1. **opsSeen**: a per-entity counter of how many times a unique valid `UserOperation` referencing this entity was received by this bundler. - This includes `UserOperation` received via an incoming RPC calls or through a P2P mempool protocol. - * For a `paymaster`, this value is not incremented if factory or account validation fails. + This includes `UserOperation` received via incoming RPC calls or through a P2P mempool protocol. 2. **opsIncluded**: a per-entity counter of how many times a unique valid `UserOperation` referencing this entity appeared in an actual included `UserOperation`. \ @@ -129,10 +224,10 @@ To help make sense of these params, note that a malicious paymaster can at most ### Running the Validation Rules 1. A block builder or a bundler should perform a full validation before accepting a `UserOperation` into its mempool. -2. During validation phase, the bundler should trace the execution and apply all the rules defined in this document. +2. During the validation phase, the bundler should trace the execution and apply all the rules defined in this document. 3. A bundler should also perform a full validation of the entire bundle before submission. 4. The validation rules prevent an unstaked entity from detecting the bundle validation. - However, it is possible for a malicious staked entity to detect the bundle validation and cause a revert. + However, a malicious staked entity can detect that it is running in a bundle validation and cause a revert. 5. The failed `UserOperation` should be dropped from the bundle. 6. The staked entity that caused a revert violated the Account Abstraction rules and should be marked as `THROTTLED`. @@ -144,10 +239,10 @@ To help make sense of these params, note that a malicious paymaster can at most 2. Once a `UserOperation` is received from another bundler it should be verified locally by a receiving bundler. 3. 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 particular `UserOperation` but keep the connection. -4. The bundler should validate the `UserOperation` against the nonces of last-included bundles. \ + In this case, the bundler should drop this particular `UserOperation` but keep the connection. +4. The bundler should check the `UserOperation` against the nonces of last-included bundles. \ Silently drop `UserOperations` with `nonce` that was recently included. - This invalidation is likely attributable to a network racing condition and should not cause a reputation change. + This invalidation is likely attributable to a network race condition and should not cause a reputation change. 5. If a received `UserOperation` fails against the current block: 1. Retry the validation against the block the `UserOperation` was originally verified against. 2. If it succeeds, silently drop the `UserOperation` and keep the connection. @@ -179,166 +274,119 @@ To help make sense of these params, note that a malicious paymaster can at most * Contract creation: * **[OP-031]** `CREATE2` is allowed exactly once in the deployment phase and must deploy code for the "sender" address. * Access to an address without a deployed code is forbidden: - * **[OP-041]** For `EXTCODE*` and `*CALL` opcodes - * **[OP-042]** Exception: access to "sender" address is allowed. + * **[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. * Allowed access to the `EntryPoint` address: * **[OP-051]** May call `EXTCODESIZE ISZERO`\ - This pattern is used to check destination has code before `depositTo` function is called. - * **[OP-052]** May call `depositTo(sender)` with any value from either the `sender` or `factory` - * **[OP-053]** May call the fallback function from the `sender` with any value - * **[OP-054]** Any other access to the `EntryPoint` is forbidden + This pattern is used to check destination has a code before the `depositTo` function is called. + * **[OP-052]** May call `depositTo(sender)` with any value from either the `sender` or `factory`. + * **[OP-053]** May call the fallback function from the `sender` with any value. + * **[OP-054]** Any other access to the `EntryPoint` is forbidden. * `*CALL` opcodes: * **[OP-061]** `CALL` with `value` is forbidden. The only exception is a call to the `EntryPoint` described above. * **[OP-062]** Precompiles: - * Only allowed the core 9 precompiles.\ - Specifically the computation precompiles that do not access anything in the blockchain state or environment. -* **[OP-070] Transient Storage slots defined in [EIP-1153](./eip-1153.md) and accessed using `TLOAD` (`0x5c`) and `TSTORE` (`0x5d`) opcodes - are treated exactly like persistent storage (SLOAD/SSTORE) + * 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](./rip-7212.md) sec256r1 precompile, on networks that accepted it. +* **[OP-070]** Transient Storage slots defined in [EIP-1153](./eip-1153) and accessed using `TLOAD` (`0x5c`) and `TSTORE` (`0x5d`) opcodes + are treated exactly like persistent storage (SLOAD/SSTORE). ### 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. + entity, or referenced library, may not be changed.\ + If the code is modified, the UserOperation is considered invalid. ### Storage rules. -The permanent storage access with `SLOAD` and `SSTORE` instructions within each phase are limited as follows: +The permanent storage access with `SLOAD` and `SSTORE` 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 +* 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 the `factory` 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 is associated with the entity, in any non-entity contract. + * **[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 another `UserOperation` in the mempool. \ -This means that a `Paymaster` and `Factory` contracts cannot practically be an "account" contract as well. -* **[STO-041]** A contract whose Associated Storage slot is accessed during a `UserOperation` may not be an address of a "sender" in another `UserOperation` in the mempool. + This means that `Paymaster` and `Factory` 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. -### Staked Entities Reputation Rules -* **[SREP-010]** The "canonical mempool" defines a staked entity if it has `MIN_STAKE_VALUE` and unstake delay of `MIN_UNSTAKE_DELAY` -* **[SREP-020]** A `BANNED` address is not allowed into 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 existing `UserOperations` referencing this address are removed from the mempool. -* **[SREP-030]** A `THROTTLED` address is limited to: +* **[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`. -* **[SREP-040]** An `OK` staked entity is unlimited by the reputation rule. +* **[GREP-040]** If an entity fails the bundle creation after passing second validation, its `opsSeen` set to `BAN_OPS_SEEN_PENALTY`, and `opsIncluded` to zero, causing it to be `BANNED`. + +### Staked Entities Reputation Rules + +* **[SREP-010]** The "canonical mempool" defines a staked entity if it has `MIN_STAKE_VALUE` and unstake delay of `MIN_UNSTAKE_DELAY` +* **[SREP-020]** 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]** If a staked entity fails the second validation or fails bundle creation, its `opsSeen` is incremented by `10000`, causing it to be `BANNED`. ### Entity-specific rules: * **[EREP-010]** For each `paymaster`, the mempool must maintain the total gas `UserOperations` using this `paymaster` may consume. - * Do not add a `UserOperation` to the mempool if the maximum total gas usage, including the new `UserOperation`, is above the deposit of the `paymaster` at the current gas price. - * TODO: allow "fractional reserve" based on reputation? + * Do not add a `UserOperation` to the mempool if the maximum total gas cost, including the new `UserOperation`, is above the deposit of the `paymaster` at the current gas price. +* **[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 the `validateUserOp()` is rejected for any reason, 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. \ -(see [https://github.com/eth-infinitism/account-abstraction/issues/274](https://github.com/eth-infinitism/account-abstraction/issues/274)) + That is, if the `validateUserOp()` is rejected for any reason in a `UserOperation` that has an `initCode`, 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]** An unstaked `paymaster` may not return a `context`. - * TODO: can be removed once we remove the "2nd postOp". -### Unstaked Entities Reputation Rules +### Unstaked Paymasters Reputation Rules * Definitions: * **`opsSeen`, `opsIncluded`, and reputation calculation** are defined above. - * `UnstakedReputation` of an entity is a maximum number of entries using this entity allowed in the mempool. - * `opsAllowed` is a reputation based calculation for an unstaked entity, representing how many `UserOperations` it is allowed to have in the mempool. -* **[UREP-010]** `UserOperation` with unstaked sender are only allowed up to `SAME_SENDER_MEMPOOL_COUNT` times in the mempool. -* **[UREP-020]** For other entities: \ - `opsAllowed = SAME_UNSTAKED_ENTITY_MEMPOOL_COUNT + (inclusionRate * INCLUSION_RATE_FACTOR) + (min(opsIncluded, 10000)`. + * `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 many `UserOperations` 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. A staked sender is only limited by the SREP 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]** If an unstaked entity causes an invalidation of a bundle, its `opsSeen` is set to `1000`, effectively blocking it from inclusion for 24 hours. ### Alt-mempools rules: -Alternate mempool is an agreed-upon rule that the bundlers may opt in to. +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 the `UserOperations`, and based on the above rules (and subscribed alt-mempools) decides for which alt-mempools to "tag" it. + The receiving bundler validates the `UserOperations`, 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](#reputation-calculation) ## Security Considerations -### Possible Attacks - -Below is a list of possible attacks that were considered and a reference to the above rule that prevents it. - -This list does not attempt to be an exhaustive list of attacks. -These attacks are examples provided to describe and rationalise the reason for the above rules. - -#### Sample Known (expensive) attack: - -* This attack can't be fully mitigated by the above validation rules. - But the rules made sure it is expensive to maintain. -* Deploy a paymaster which has the following line in the validation function: \ -`require((perSenderMap[userOp.sender]&1)==0);` -* Submit a large number of `UserOperations` with different senders into the mempool. -* Front-run the mempool, and modify the `perSenderMap` for each sender. -* By the time the `UserOperations` are validated the second time they are all invalid. -* The cost of first run is 20000 gas per account, but the second run is 5000 gas per account. -* NOTE: Because of the rule **[STO-021]** "forbid associated storage during creation", the accounts have to be pre-deployed. This introduces a one-time cost of ~40000 gas per account. -* Calculate the cost of blocking the network: -* Assuming a bundler can process 1000 `UserOperations`/second -* Polygon - (2-second blocks) need 2000 `UserOperations` - * The initial cost (deploy+initial state change) is 2000*60000 = 120M gas - * Sustained cost (only storage modification) - 2000*5000 = 10M gas -* Mainnet- (12-second blocks) need 12000 UserOps - * The initial cost (deploy+initial state change) is 12000*60000 = 720M gas - * Sustained cost (only storage modification) - 12000*5000 = 70Mgas -* Total attack cost - * Polygon (@150 gwei) - $18 + $1.5 per block ($2700/hour) - * Mainnet: (@40 gwei) - $51840 + $5040 per block ($1.5M/hour) - * Note that the above costs ignore the gas-price increase because the underlying network is also overloaded. -* Mitigations - * A **staked factory:** - * Can attempt such an attack once, since after invalidating a single bundle creation its reputation drops and another staked factory has to be used. - * So a successful attack needs to deploy and stake a new paymaster on every block. The old paymaster's stake is locked and can be re-used only after 24 hours. - * For **unstaked factory**: - * As per **[UREP-020]**, it is initially allowed only 10 `UserOperations` in the mempool. - * That value increases fast, but drops (to negative) on invalidations. - * It needs to successfully deploy 10 `UserOperations` to get a reputation to deploy a batch of 1000 (which attack). - * After a single "attack", its reputation is dropped, and it gets banned for a day, so another factory needs to be deployed. - * So the cost of the attack is 10 actual deployments for 1000 failures. - -#### List of attacks: - -1. **Use volatile data**\ -Send a large number of `UserOperations` with `initCode` to deploy an account with a validation that contains `require((block.number&1)==0)` into the mempool.\ -They all get invalidated at no cost to the sender when tried to be included in the next block. \ -**Blocked by: [OP-011]** "opcode banning" rule. -2. **Use uninitialized code**\ -The validation function includes the code: `tempaddr.call(..);` and execution does `new {salt}()` where the tempaddr is -initialized to the address returned by the `new`.\ -This code passes validation since the generated contract does not exist, but other `UserOperations` with the same code -will revert since the target is already created, and revert on this calldata.\ -**Blocked by: [OP-041]** "reference uninitialized code" rule. -3. **Use inner contact creation**\ -The validation function includes the code: `require(new {salt:0}SomeContract() !=0)`\ -When submitting multiple such `UserOperations`, all pass the validation and enter the mempool, and even pass the second validation.\ -But when creating a bundle, the first one will succeed and the rest will revert. \ -**Blocked by: **[OP-031]** "`CREATE`/`CREATE2` is blocked"** -4. **Censorship attack**\ -`UserOperation` sender can access associated storage in another `UserOperation` sender that is -supposed to be in the bundle (e.g. `anotherSender.balanceOf(this)`), provide a higher gas price than the other sender, -effectively censoring the other sender. - 1. Assumes the attacked account has a method `getData(xx)`, where `xx` can be set to `this`. - Many account do have such a method, e.g. `ERC721Y` have generic `getData` method. - 2. By rule **[STO-041]**, senders cannot access each other's storage, making bundlers choose only one - `UserOperation` between those, probably the more profitable one. - Currently, the bundlers have no incentive to prevent such censorship attacks. - 3. Mitigation: - 1. First, the sender of the censored `UserOperation` needs to detect such attacks, - and the only mitigation at the moment is to bump the gas price of the censored `UserOperation`. - 2. There should be at least one "proper" bundler in the network, that follows rule [STO-041], - and prefers the sender over the other `UserOperation` which attempts to censor it. +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](../LICENSE.md). +