agents/skills/injectable/l1/mempool-asymmetric-dos/SKILL.md
L1 trigger - audits mempool / transaction pool for eviction asymmetries, replacement policy abuse, blob-pool exhaustion, and DETER-class denial of service.
npx skillsauth add plamentsv/plamen mempool-asymmetric-dosInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
L1 trigger:
L1_PATTERN=trueAND (txpool/ORmempool/ORtx_poolORblob_poolORreth-transaction-pooldetected in recon subsystem map) Inject Into:depth-network-surfaceordepth-state-traceLanguage: Go and Rust Finding prefix:[MP-N]Status: v0.1 draft, Round 4 exemplars pending
Recon identifies a mempool module. Mempools are uniquely exposed: every RPC client and every peer can insert transactions. The DETER paper (USENIX CCS '21) and MemPurge follow-ups showed that most production mempools had asymmetric cost models exploitable at near-zero attacker cost.
The DETER insight: if an attacker can insert transactions with lower cost than it takes to evict them, they can fill the pool faster than honest transactions can reclaim space.
For each mempool:
insert_cost / eviction_cost. If this is much less than 1, the pool is asymmetric.Specific patterns:
Transactions that are initially accepted into the pool but will later fail validation (nonce gap, insufficient balance, invalid signature for a new state root). These cost the attacker 0 but occupy space until the validator reprocesses them.
Check: Look for a "pending" vs "valid" split. When is validity re-checked? Can an attacker keep an invalid tx in "pending" indefinitely?
A transaction with a nonce 100 above the current nonce is "futureNonce" — it occupies space waiting for the lower-nonce gap to fill. Attacker sends 99 futureNonce placeholders + never sends the gap-filler.
Check: Is there a cap on futureNonce slots per account? What's the eviction policy when the cap is hit? Is the cap per-account or shared?
If an attacker can replace a low-fee tx with a slightly-higher-fee tx indefinitely, they churn pool state and cost honest reorganization work. The Bitcoin min_relay_fee_increment and Ethereum's 10% bump rule address this — verify the implementation.
Check: What is the minimum fee bump for replacement? Is it enforced cumulatively (across multiple replacements) or only per-replacement?
Blob transactions have a separate gas market (blob_gas_price). If the blob pool eviction uses only blob-gas-price but insertion is cheap (or the pool is not strictly separated from legacy), asymmetric exploitation is possible.
Check: Is the blob pool size-capped independently of the main pool? Can an attacker spam blobs to evict legacy txs or vice versa?
Tag: [MP-ASYMMETRIC:{insert-cost}:{eviction-cost}:{ratio}]
When the pool is full, which transaction gets evicted?
Tag: [MP-EVICT:{policy}:{gap}]
A mempool bug is worse if the bad transaction propagates:
Tag: [MP-PROPAGATE:{behavior}]
For transactions that do make it into blocks, the ordering algorithm affects MEV and fairness:
Tag: [BLOB:{concern}:{bound}]
| State | Test | Expected | Observed | |---|---|---|---| | Empty pool | first tx inserted | accepted | | | Full pool, same-fee tx | new tx with equal fee | rejected (no displacement) | | | Full pool, 10% higher fee | new tx with RBF-threshold fee | old tx evicted, new accepted | | | Same-sender N txs | sender sends pool_cap+1 txs | oldest or lowest-fee evicted; sender not DoSed | | | futureNonce only | attacker fills with gap transactions | cap enforced; pool does not grow unbounded | | | Invalid sig batch | 1000 txs with invalid sigs | all rejected quickly; peer scored | | | Blob size cap | blobs sum to > cap | excess rejected | |
Tag: [BOUNDARY:mempool:{state}:{result}]
[FUZZ-PASS] (parameterized pool harness) > [LSP-TRACE] > [CODE-TRACE]DETER — Geth and 4 other Ethereum clients (USENIX CCS '21 + USENIX Security '24) — asymmetric eviction. Attackers send "future" nonce or "latent overdraft" transactions that pass cheap admission checks but are invalid at execution time. They fill the mempool and evict valid victim transactions at much lower cost to attacker than damage caused. Fixed in all major Ethereum clients as of Fall 2023. Mempool Symbolic Fuzzing USENIX '24; DETER 2.0. Skill catch point: Section 1 — the core asymmetry invariant. Insertion cost ≥ eviction damage.
MemPurge — Geth pending pool (USENIX '24) — chain-of-txs eviction. Attacker sends chain of ≤65 transactions that appear valid but become invalid after first executes. Attacker pays for only 1 tx worth of fees but evicts 65 honest txs. Combined with ConditionalExhaust, total attack cost ≈ $376 for full pool replacement. Speculative DoS paper; Yaish summary. Skill catch point: Section 1b — model multi-tx transitive validity; admission of tx_1 must account for worst-case state after tx_1 executes, not current state.
Geth GetHeadersFrom integer underflow (CVE-2024-32972) — GetHeadersFrom(number, count uint64) received count-1 where count was 0, producing UINT64_MAX and bypassing maxHeadersServe. Single p2p request forced node to stream all headers from latest back to genesis. GHSA-4xc9-8hmq-j652; fix in PR #29534, shipped v1.13.15. Skill catch point: boundary substitution — every p2p request handler with a count/range parameter must have underflow guard (subtraction below 0), overflow guard (u64 wraparound), and upper bound enforcement after arithmetic, not before. (Shared with p2p-dos-and-eclipse.)
Insert as new Section 1e (MANDATORY invariant): For every mempool, quantify:
insert_cost = minimum resource expenditure to place one tx in the pool
(accounts for: fee, stake, gas, bandwidth, all admission gates)
eviction_damage = maximum resource removal this tx can cause
(accounts for: bytes freed, slot evictions, state churn, propagation)
Invariant: insert_cost ≥ eviction_damage for every admission path.
Check:
Tag: [MP-COST-RATIO:{insert_cost}:{eviction_damage}:{ratio}]
Ratios < 1 are CRITICAL; ratios just above 1 are High (a small-margin attacker can still cause damage under peak load).
When a block can contain MULTIPLE transaction types (regular data tx, commitment tx, system tx, deposit, slashing, governance, oracle update, etc.), each type needs its OWN count cap. A single max_txs_per_block applied uniformly is insufficient — one attacker-controlled free tx type can flood blocks while "expensive" types are correctly bounded.
Methodology:
data_ledgers, commitment_txs, system_txs, slashings, deposits, attestations, seal_operations.len(txs) <= MaxTxPerBlock but another type has no per-type cap (only the total byte cap), the unbounded type is a DoS vector — especially if the unbounded type is cheap to produce (no signature fee, no stake requirement, free-to-spam).Required artifact: {SCRATCHPAD}/tx_type_caps.md:
| Tx Type | Field name | Per-type cap? | Cap value | Cost per tx | Spam risk |
|---|---|---|---|---|---|
| Regular data tx | `data_ledgers[].txs` | YES | MaxTxPerBlock=5000 | signature + state write | OK |
| Commitment tx | `commitment_txs` | **NO** | — | signature only (cheap) | **HIGH — no cap** |
| System tx | `system_txs` | YES | MaxSystemPerBlock=16 | none (privileged) | OK if validated |
| Slashing evidence | `slashings` | YES | MaxSlashPerBlock=32 | free to submit | OK |
Every "NO" in the "Per-type cap?" column is a finding — severity is at least High when the type is cheap to produce and propagates through p2p (commitment txs, attestations, gossip messages). A cap enforced only at production or only at validation is also a finding.
Tag: [MP-NO-PER-TYPE-CAP:{tx_type}]
Add / insert / add_transaction functionpop / evict / remove functionp2p-dos-and-eclipse (peer scoring interaction), rpc-surface-audit (RPC eth_sendRawTransaction is an insertion path), execution-client-hardeningdepth-network-surface, depth-state-tracedocs/l1-mode/severity-matrix.mddevelopment
Prepare Solidity projects for a security audit — test coverage, test quality, NatSpec docs, code hygiene, dependency health, best-practice enforcement, deployment readiness, and project documentation checks. Generates a scored Audit Readiness Report and optionally runs static analysis. Trigger on: "prepare for audit", "audit readiness", "pre-audit check", "audit prep", "NatSpec check", or any request to review a Solidity codebase before a security review.
development
Launch the Plamen deterministic Web3 security audit pipeline
development
Run the Plamen smart-contract audit wizard in Codex
testing
Launch the Plamen deterministic L1 infrastructure audit pipeline