diff --git a/eip/EIPS/eip-4337.md b/eip/EIPS/eip-4337.md index 927df6bb..78afb46e 100644 --- a/eip/EIPS/eip-4337.md +++ b/eip/EIPS/eip-4337.md @@ -8,6 +8,7 @@ status: Draft type: Standards Track category: ERC created: 2021-09-29 +requires: eip-aa-rules --- ## Abstract @@ -381,40 +382,8 @@ The simulateValidation call returns this range. A node MAY drop a UserOperation if it expires too soon (e.g. wouldn't make it to the next block) If the `ValidationResult` includes `sigFail`, the client SHOULD drop the `UserOperation`. -The operations differ in their opcode banning policy. -In order to distinguish between them, there is a call to the NUMBER opcode (`block.number`), used as a delimiter between the 3 functions. -While simulating `userOp` validation, the client should make sure that: - -1. May not invokes any **forbidden opcodes** -2. Must not use GAS opcode (unless followed immediately by one of { `CALL`, `DELEGATECALL`, `CALLCODE`, `STATICCALL` }.) -3. Storage access is limited as follows: - 1. self storage (of factory/paymaster, respectively) is allowed, but only if self entity is staked - 2. account storage access is allowed (see Storage access by Slots, below), - 3. in any case, may not use storage used by another UserOp `sender` in the same bundle (that is, paymaster and factory are not allowed as senders) -4. Limitation on "CALL" opcodes (`CALL`, `DELEGATECALL`, `CALLCODE`, `STATICCALL`): - 1. must not use value (except from account to the entrypoint) - 2. must not revert with out-of-gas - 3. destination address must have code (EXTCODESIZE>0) or be a standard Ethereum precompile defined at addresses from `0x01` to `0x09` - 4. cannot call EntryPoint's methods, except `depositTo` and "fallback" -5. `EXTCODEHASH` of every address accessed (by any opcode) does not change between first and second simulations of the op. -6. `EXTCODEHASH`, `EXTCODESIZE`, `EXTCODECOPY` may not access the EntryPoint contract address or an address with no deployed code. -The exception is accessing the `sender` address from the `factory` contract as it is guaranteed to deploy the `sender`. -7. If `op.initcode.length != 0` , allow only one `CREATE2` opcode call (in the first (deployment) block), otherwise forbid `CREATE2`. - -Transient Storage slots defined in [EIP-1153](./eip-1153.md) and accessed using `TLOAD` (`0x5c`) and `TSTORE` (`0x5d`) opcodes -must follow the exact same validation rules as persistent storage if Transient Storage is enabled. - -#### Storage associated with an address - -We define storage slots as "associated with an address" as all the slots that uniquely related on this address, and cannot be related with any other address. -In solidity, this includes all storage of the contract itself, and any storage of other contracts that use this contract address as a mapping key. - -An address `A` is associated with: - -1. Slots of contract `A` address itself. -2. Slot `A` on any other address. -3. Slots of type `keccak256(A || X) + n` on any other address. (to cover `mapping(address => value)`, which is usually used for balance in ERC-20 tokens). - `n` is an offset value up to 128, to allow accessing fields in the format `mapping(address => struct)` +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) #### Alternative Mempools @@ -423,10 +392,9 @@ However, there might be use-cases where specific paymasters (and signature aggre (through manual auditing) and verified that they cannot cause any problem, while still require relaxing of the opcode rules. A bundler cannot simply "whitelist" request from a specific paymaster: if that paymaster is not accepted by all bundlers, then its support will be sporadic at best. -Instead, we introduce the term "alternate mempool". -UserOperations that use whitelisted paymasters (or signature aggregators) are put into a separate mempool. -Only bundlers that support this whitelist will use UserOperations from this mempool. -These UserOperations can be bundled together with UserOperations from the main mempool +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 ### Bundling @@ -440,7 +408,7 @@ During bundling, the client should: After creating the batch, before including the transaction in a block, the client should: -* Run `debug_traceCall` with maximum possible gas, to enforce the validation opcode and precompile banning and storage access rules, +* Run `debug_traceCall` with maximum possible gas, to enforce the validation rules on opcode and storage access. as well as to verify the entire `handleOps` batch transaction, and use the consumed gas for the actual transaction execution. * If the call reverted, check the `FailedOp` event. @@ -462,24 +430,8 @@ it is critical that the exact same opcode and precompile banning rules as well a for the `handleOps` validation in its entirety as for individual UserOperations. Otherwise, attackers may be able to use the banned opcodes to detect running on-chain and trigger a `FailedOp` revert. -Banning an offending entity for a given bundler is achieved by increasing its `opsSeen` value by `1000000` -and removing all UserOperations for this entity already present in the mempool. -This change will allow the negative reputation value to deteriorate over time consistent with other banning reasons. - -If any of the three conditions is violated, the client should reject the `op`. If both calls succeed (or, if `op.paymaster == ZERO_ADDRESS` and the first call succeeds)without violating the three conditions, the client should accept the op. On a bundler node, the storage keys accessed by both calls must be saved as the `accessList` of the `UserOperation` - 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. -#### Forbidden opcodes - -The forbidden opcodes are to be forbidden when `depth > 2` (i.e. when it is the factory, account, paymaster, or other contracts called by them that are being executed). They are: `GASPRICE`, `GASLIMIT`, `DIFFICULTY`, `TIMESTAMP`, `BASEFEE`, `BLOCKHASH`, `NUMBER`, `SELFBALANCE`, `BALANCE`, `ORIGIN`, `GAS`, `CREATE`, `COINBASE`, `SELFDESTRUCT`. They should only be forbidden during verification, not execution. These opcodes are forbidden because their outputs may differ between simulation and execution, so simulation of calls using these opcodes does not reliably tell what would happen if these calls are later done on-chain. - -Exceptions to the forbidden opcodes: - -1. A single `CREATE2` is allowed if `op.initcode.length != 0` and must result in the deployment of a previously-undeployed `UserOperation.sender`. -2. `GAS` is allowed if followed immediately by one of { `CALL`, `DELEGATECALL`, `CALLCODE`, `STATICCALL` }. - (that is, making calls is allowed, using `gasleft()` or `gas` opcode directly is forbidden) - ### Reputation scoring and throttling/banning for global entities #### Reputation Rationale. @@ -493,76 +445,9 @@ Note that this stake is never slashed, and can be withdrawn any time (after unst Unstaked entities are allowed, under the rules below. -When staked, an entity is also allowed to use its own associated storage, in addition to sender's associated storage. +When staked, an entity is less restricted in its memory usage. The stake value is not enforced on-chain, but specifically by each node while simulating a transaction. -The stake is expected to be above MIN_STAKE_VALUE, and unstake delay above MIN_UNSTAKE_DELAY -The value of MIN_UNSTAKE_DELAY is 84600 (one day) -The value of MIN_STAKE_VALUE is determined per chain, and specified in the "bundler specification test suite" - -#### Un-staked entities - -Under the following special conditions, unstaked entities still can be used: - -* An entity that doesn't use any storage at all, or only the senders's storage (not the entity's storage - that does require a stake) -* An unstaked account can access [storage associated with the sender](#storage-associated-with-an-address) if the UserOp doesn't create a new account (that is initCode is empty), or the UserOp creates a new account using a - staked `factory` contract. -* A paymaster that has a “postOp()” method (that is, validatePaymasterUserOp returns “context”) must be staked - -#### Specification. - -In the following specification, "entity" is either address that is explicitly referenced by the UserOperation: sender, factory, paymaster and aggregator. -Clients maintain two mappings with a value for staked entities: - -* `opsSeen: Map[Address, int]` -* `opsIncluded: Map[Address, int]` - -If an entity doesn't use storage at all, or only reference storage associated with the "sender" (see [Storage associated with an address](#storage-associated-with-an-address)), then it is considered "OK", without using the rules below. - -When the client learns of a new staked entity, it sets `opsSeen[entity] = 0` and `opsIncluded[entity] = 0` . - -The client sets `opsSeen[entity] +=1` each time it adds an op with that `entity` to the `UserOperationPool`, and the client sets `opsIncluded[entity] += 1` each time an op that was in the `UserOperationPool` is included on-chain. - -Every hour, the client sets `opsSeen[entity] -= opsSeen[entity] // 24` and `opsIncluded[entity] -= opsIncluded[entity] // 24` for all entities (so both values are 24-hour exponential moving averages). - -We define the **status** of an entity as follows: - -```python -OK, THROTTLED, BANNED = 0, 1, 2 - -def status(paymaster: Address, - opsSeen: Map[Address, int], - opsIncluded: Map[Address, int]): - if paymaster not in opsSeen: - return OK - min_expected_included = opsSeen[paymaster] // MIN_INCLUSION_RATE_DENOMINATOR - if min_expected_included <= opsIncluded[paymaster] + THROTTLING_SLACK: - return OK - elif min_expected_included <= opsIncluded[paymaster] + BAN_SLACK: - return THROTTLED - else: - return BANNED -``` - -Stated in simpler terms, we expect at least `1 / MIN_INCLUSION_RATE_DENOMINATOR` of all ops seen on the network to get included. If an entity falls too far behind this minimum, it gets **throttled** (meaning, the client does not accept ops from that paymaster if there is already an op with that entity, and an op only stays in the pool for 10 blocks), If the entity falls even further behind, it gets **banned**. Throttling and banning naturally decay over time because of the exponential-moving-average rule. - -**Non-bundling clients and bundlers should use different settings for the above params**: - -| Param | Client setting | Bundler setting | -| - | - | - | -| `MIN_INCLUSION_RATE_DENOMINATOR` | 100 | 10 | -| `THROTTLING_SLACK` | 10 | 10 | -| `BAN_SLACK` | 50 | 50 | - -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. - -## 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. - -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. This method is required to be almost-pure: it is only allowed to access the storage of the account itself, cannot use environment opcodes (eg. `TIMESTAMP`), and can only edit the storage of the account, and can also send out ETH (needed to pay the entry point). The method is gas-limited by the `verificationGasLimit` of the `UserOperation`; nodes can choose to reject operations whose `verificationGasLimit` is too high. These restrictions allow block builders and network nodes to simulate the verification step locally, and be confident that the result will match the result when the operation actually gets included into a block. - -The entry point-based approach allows for a clean separation between verification and execution, and keeps accounts' logic simple. The alternative would be to require accounts to follow a template where they first self-call to verify and then self-call to execute (so that the execution is sandboxed and cannot cause the fee payment to revert); template-based approaches were rejected due to being harder to implement, as existing code compilation and verification tooling is not designed around template verification. ### Paymasters diff --git a/eip/EIPS/eip-aa-rules.md b/eip/EIPS/eip-aa-rules.md new file mode 100644 index 00000000..8d3b4a0c --- /dev/null +++ b/eip/EIPS/eip-aa-rules.md @@ -0,0 +1,341 @@ +--- +eip: 0 +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 +author: Yoav Weiss (@yoavw), Dror Tirosh (@drortirosh), Alex Forshtat (@forshtat), Shahaf Nacson (@shahafn) +discussions-to: https://www.google.com/ +status: Draft +type: Standards Track +category: Core +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 +block builder or a standalone bundler, and the rationale behind each one of them. + +## Motivation + +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 +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. + +## Specification + +### 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. + +There are 3 ways to perform such an attack: + +1. Create a `UserOperation` that passes the initial validation, but later fails 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. + +### 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) | | +| `INCLUSION_RATE_FACTOR` | 10 | | +| `THROTTLING_SLACK` | 10 | | +| `BAN_SLACK` | 50 | | + +### 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. \ + 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. +4. **Staked Entity:** an entity that has a locked stake of at least `MIN_STAKE_VALUE` + and an unstake delay of at least `MIN_UNSTAKE_DELAY`. +5. **Associated storage:** a storage slot of any smart contract is considered to be "associated" with address `A` if: + 1. The slot value is `A` + 2. The slot value was calculated as `keccak(A||x)+n`, where `x` is a `bytes32` value, and `n` is a value in the range 0..128 +6. **Using an address**: accessing the code of a given address in any way. + This can be done by executing `*CALL` or `EXTCODE*` opcodes for a given address. + +### 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. + +2. **opsIncluded**: a per-entity counter of how many times a unique valid `UserOperation` referencing this entity + appeared in an actual included `UserOperation`. \ + Calculation of this value is based on UserOperationEvents and is only counted for `UserOperations` that were + previously counted as `opsSeen` by this bundler. +3. Both values are updated every hour as `value = value * 23 // 24` \ + Effectively, the value is reduced to 1% after 4 days. +4. **inclusionRate**: Relation of `opsIncluded` to `opsSeen` + + +### Reputation calculation + +We define a value `max_seen = opsSeen // MIN_INCLUSION_RATE_DENOMINATOR`. + +The reputation state of each entity is determined as follows: + +1. **BANNED**: `max_seen > opsIncluded + BAN_SLACK` +2. **THROTTLED**: `max_seen > opsIncluded + THROTTLING_SLACK` +3. **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 + +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. +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. +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`. + +### Mempool Validation Rules + +1. A `UserOperation` is broadcast over the P2P protocol with the following information: + 1. The `UserOperation` itself + 2. The blockhash this `UserOperation` was originally verified against. +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. \ + 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. +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. + 3. If it fails, mark the sender as a "spammer" + +### Opcode Rules +* Block access from opcodes that access information outside of storage and code (aka "environment"). + * **[OP-011]** Blocked Opcodes: + * `BALANCE` (`0x31`) + * `ORIGIN` (`0x32`) + * `GASPRICE` (`0x3A`) + * `BLOCKHASH` (`0x40`) + * `COINBASE` (`0x41`) + * `TIMESTAMP` (`0x42`) + * `NUMBER` (`0x43`) + * `PREVRANDAO`/`DIFFICULTY` (`0x44`) + * `GASLIMIT` (`0x45`) + * `SELFBALANCE` (`0x47`) + * `BASEFEE` (`0x48`) + * `GAS` (`0x5A`) + * `CREATE` (`0xF0`) + * `INVALID` (`0xFE`) + * `SELFDESTRUCT` (`0xFF`) + * **[OP-012]** `GAS `opcode is allowed, but only if followed immediately by `*CALL` instructions.\ + 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-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. +* 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. + 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 +* `*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. + +### 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 permanent storage access with `SLOAD` and `SSTORE` instructions within each phase are 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 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]** Access a storage slot in any non-entity contract that is associated with the entity. +* **[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. + +### 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.\ + Also, all existing `UserOperations` referencing this address are removed from the mempool. +* **[SREP-030]** 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. + * 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? +* **[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)) +* **[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 + +* 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)`. + * 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. + +* **[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-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. + + +## 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.