We’d rather avoid per-wallet reputation. We only have that for paymasters. But this attack does have a cost to the attacker, probably higher than the damage it causes. In order to propagate through the mempool, the op must be valid at the time of propagation. The attacker has to invalidate it after it has been propagated, by deploying a contract. The attacker also has to deploy a large number of wallets in order to fill the mempool, because each wallet can have only one op in the mempool at any given time.
So the attacker has a one-time setup cost of O(concurrent_ops) for setting up the wallets, and then O(iterations) for deploying a new contract on each iteration. And the damage is a single off-chain simulation for each iteration of the attack, since the 2nd simulation never happens due to rule 9.
I agree it would be better to mitigate it entirely, rather than relying on the cost and unprofitability of the attack. But how would you block it without breaking too much functionality? Preventing calls to accounts without code is a good idea and shouldn’t break anything, but doesn’t block the attack due to selfdestruct and recreate.
It’ll be deactivated, but I don’t know about “soon enough”. We did consider having a rule where contracts touched during validation must not have the selfdestruct opcode, but it wasn’t good enough because the contract could delegatecall to an unknown address (specified in the op rather than in the code), and that address could selfdestruct. Preventing delegatecall to unknown addresses seems too harsh.
Is there any room for adding the transaction types in this proposal? For instance, each transaction will be described as a blob of bytes, where the first byte denotes the type of the transaction. This way it will be easier to keep backward compatibility in case some new types will be added. The question is risen mostly because L2s have different fee model: the price for L1 pubdata is dynamic.
I’m trying to understand the motivation of the bundler to do his job.
An I correct in assuming the preVerificationGas is always paid entirely so it can be arbitrarily large and used as a tip for the bundler?
The motivation is equivalent for a block builder to include any transactions: gas fees premiums.
The builder pays the “outer transaction” fee (probably to itself, if it is the build), and receives from each UserOp a fee based on userOp.priorityFee (and userOp.maxFeePerGas)
preVerificationGas can be used to tip the bundler a fixed amount, but its original purpose was to cover the gas cost that can’t be computed on-chain - the calldata cost, the fixed overhead (21000) and some EntryPoint contract overhead.
The whole idea of the protocol is to check as much as possible from the UserOp in the EntryPoint contract, including the actual gas calculation.
Introducing a byte blobs defeats that purpose.
There is one “placeholder” for external gas calculation per network, namely preVerificationGas. It is assumed to be a function on the data in the UserOp and some network-specific parameters (like the 21000 stipend and the zero/nonzero byte costs)
Thanks you for the answer. It turns out that in order to benefit from the execution of UserOp, the value of UserOpGasPrice must exceed the gas price was used when sending the transaction (except of expenses that can’t be computed on-chain), am I understanding correctly?
Is there any calculations of the potential benefits of bundlers?