owned this note
owned this note
Published
Linked with GitHub
[TOC]
# BloatNet test cases
## Adversarial Scenarios for Execution Client Stress-Testing
| Scenario Name | Objective / Hypothesis | Transaction / Block Composition | Key Opcodes / Actions | Primary Client Target(s) |
| :--- | :--- | :--- | :--- | :--- |
| **State Write & I/O Pressure Scenarios** | | | | |
| **LSM: Compaction** | Maximize Write Amplification (WA) by forcing repeated compactions of "hot" data, saturating disk I/O. | A block filled with transactions that repeatedly `SSTORE` to a small, predefined set of storage slots. | `SSTORE` (update) | Geth, Nethermind, Besu |
| **LSM: Tombstone** | Inflate logical DB size with deletion markers (tombstones), forcing costly compactions of old data to reclaim space. | A block filled with transactions that call `SELFDESTRUCT` on contracts or `SSTORE` a zero value. The contracts and slots should have been written minutes earlier approx. | `SELFDESTRUCT`, `SSTORE` (delete) | Geth, Nethermind, Besu |
| **B+ Tree: Random Write** | Degrade B+ Tree performance by forcing non-sequential page updates, splits, and tree rebalancing, which is the worst-case write pattern for this DB type. | A mix of 7702 delegation + SSTORE to different hashmaps/vectors in a contract/series of contracts. | `SSTORE`, 7702-authorisation (random insert) | Erigon, Reth |
| **B+ Tree: Freelist Fragmentation** | Induce extreme latency on a single large write by first fragmenting the MDBX freelist, then forcing a slow linear scan to find contiguous space. | **Phase 1:** A sequence of blocks with thousands of random `SSTORE` writes and deletes. **Phase 2:** A single block with one transaction deploying as many max-size (24KB) contracts via `CREATE` as possible. | `SSTORE`, `CREATE` | Erigon, Reth |
| **State Read & CPU-Bound Scenarios** | --------------------------- | --------------------------- |-------------------------- | -------------------------|
| **Wide State** | Maximize state root calculation time by forcing the creation of a maximal number of new trie leaf nodes. | Max number of EOA transfers with receivers as geometrically-distant as possible. | `CALL` | All |
| **Deep State** | Maximize state root calculation time by forcing deep trie traversals for a small number of changes. | A block that modifies a single storage slot (`SSTORE`) in a contract whose address creates a very deep path in the Patricia Trie (e.g., many shared leading nibbles). | `SSTORE` | All |
| **Sender Recovery Choke** | Create a bottleneck in the "Recover Senders" pipeline stage by maximizing signature verifications relative to other work. | A block filled to its transaction limit with minimal 21k gas transfers, each from a unique, newly created Externally Owned Account (EOA). | `CALL` (simple transfer) | Erigon |
| **Besu: JVM Pressure** | Induce a long "stop-the-world" JVM garbage collection pause (**UNSURE IF POSSIBLE**) during block processing, causing non-deterministic latency. | A block filled with transactions that `CREATE2` and then immediately `SELFDESTRUCT` thousands of small, temporary contracts to maximize memory allocation churn. | `CREATE2`, `SELFDESTRUCT` | Besu |
| **Complex & Concurrency Scenarios** | | | | |
| **Reorg Under Compaction** | Test for deadlocks or extreme I/O contention when a reorg's read/write operations conflict with a background DB compaction cycle. | **Phase 1:** Induce a node to start compacting. **Phase 2:** While compaction is active, trigger a deep (10+ block) reorg by manipulating fork-choice via EELS. | `SSTORE`, Deep Reorg | Geth, Nethermind, Besu |
| **MDBX Reorg vs. Long Read** | Exploit the MDBX long-read vulnerability during a reorg to cause catastrophic DB file bloat. | **Phase 1:** On Node A, initiate a long-running RPC query (e.g., `eth_getLogs`). **Phase 2:** On a partitioned Node B, build a longer chain with heavy state writes. **Phase 3:** Reconnect nodes to force Node A to reorg while the read transaction is active. | `eth_getLogs`, Deep Reorg | Erigon, Reth |
| **Trielog Torture Test** | Specifically stress Besu's Bonsai trielog replay mechanism with a deep, state-heavy reorg, which is its architectural weak point. | Create a 20+ block fork where each block is a "Wide State" block. Then, force a reorg to that computationally expensive fork. | `CALL`, Deep Reorg | Besu |
| Peer Overload | Test peer management and network stack limits | Have a network with 100+ nodes, each running spamoor a high rates to propagate txs. Test peers with max-peer connections. | Meempool stress, propagation stress, SSTORE + 7702 + EOAtx | All clients |
| Snapshot Generation | Test Erigon's snapshot generation under 2x state groth & high load | Keep a high 100M+ load constantly removing old state and writing over it with SSTORE or 7702 txs while snapshot is being generated | Snapshot-gen, 7702, `SSTORE`. | Erigon |
| Pruning under load | Test pruning efficiency under extreme load. | Spamoor generates as many blocks with as many state-touching txs possible. (the older the state the better) | `SSTORE`, `SELFDESTRUCT`, 7702, EOAtx. | All prunning clients |
## Opcode State Access Baseline Tests
### Single-opcode tests for state-access opcodes.
:::info
All of the metrics below have to be collected under the following scenarios:
- Normal state
- While compacting
- While prunning
- While compacting + prunning
- While reorging
- While reorging + compacting
- With mempool flooded of 21kgas EOA txs
- With mempool being flooded with invalid txs.
Possibly, we also have to try with altered BloatNet images where max tree depth is 1 or 2 levels deeper than `mainnet`.
That will allow us to see not only the average cases but also to reach on deep superpositions of processes that can make the results much worse.
:::
| Opcode | Test Scenario | Purpose | Status |
| --- | --- | --- | --- |
| `SSTORE` | **Fill block with `SSTORE` (0 -> 1):** Maximize new slot creation cost (20,000 gas). | Baseline for max state growth rate. | 🚧 |
| | **Fill block with `SSTORE` (1 -> 2):** Maximize slot update cost (5,000 gas). | Baseline for state churn I/O. | 🚧 |
| `SLOAD` | **Fill block with warm `SLOAD`:** Repeatedly read the same few storage slots. | Test maximum in-memory read speed. | 🚧 |
| | **Fill block with cold `SLOAD`:** Read a different, uncached storage slot in each transaction. | Test maximum random disk read speed. | 🚧 |
| `CREATE` / `CREATE2` | **Fill block with `CREATE/CREATE2`:** Deploy as many contracts as possible. | Test bytecode insertion & account creation throughput and associated I/O. | 🚧 |
| `SELFDESTRUCT` | **Fill block with `SELFDESTRUCT`:** Destroy as many previously created contracts as possible. | Test account deletion throughput and tombstone creation rate. | 🚧 |
| `BALANCE` | **Fill block with warm/cold `BALANCE`:** Similar to `SLOAD`, test account header read performance. | Isolate performance of reading account data vs. storage data. | 🚧 |
| `EXTCODESIZE` | **Fill block with warm/cold `EXTCODESIZE`:** Test contract code read performance from memory vs. disk. | Measure I/O impact of reading large code bodies. | 🚧 |
| `EXTCODECOPY` | **Fill block with warm/cold `EXTCODECOPY`:** Similar to `EXTCODESIZE` but with memory copying. | Stress both I/O and memory bandwidth. | 🚧 |
| `EXTCODEHASH` | **Fill block with warm/cold `EXTCODEHASH`:** Test performance of hashing contract code. | Isolate hashing computation from I/O read time. | 🚧 |
| `BLOCKHASH` | **Fill block with `BLOCKHASH`:** Call `BLOCKHASH` for various recent block numbers. | Test performance of historical header access. | 🚧 |
### Opcode State Access Combination Tests
:::info
All of the metrics below have to be collected under the following scenarios:
- Normal state
- While compacting
- While prunning
- While compacting + prunning
- While reorging
- While reorging + compacting
- With mempool flooded of 21kgas EOA txs
- With mempool being flooded with invalid txs.
Possibly, we also have to try with altered BloatNet images where max tree depth is 1 or 2 levels deeper than `mainnet`.
That will allow us to see not only the average cases but also to reach on deep superpositions of processes that can make the results much worse.
:::
| Opcode | Test Scenario | Purpose | Status |
| --- | --- | --- | --- |
| `Cold BALANCE + WARM EXTCODESIZE` | **Abuse warm cost forcing small BALANCE cold reads in distinct places and then call warm `EXTCODESIZE` to all the 24kb contracts:** This should be the way to make a client read the most amount of data from DB possible | Baseline for max reading possible | 🚧 |
| **Storage Toggle (`SSTORE` 0→x→0)** | In each tx, do `SSTORE(0→X)` then `SSTORE(X→0)` on the next slot. | Apply maximal gas (close to 20k), then revert to zero: stress writes+erases. Tests refund rules and I/O (≈475 g/B). | 🚧 |
| **Create/Destroy Loop** | Repeatedly `CREATE` a new contract (with some code) then `SELFDESTRUCT` it. | Test rapid account insertions and deletions. Generates many tombstones quickly. | 🚧 |
## Other scenarios
| Attack Technique | Test Scenario | Purpose | Status |
| ------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ------ |
| **Max Deposits Block** | Include 8192 validator deposits (each \~32k gas) in one block (requires \~256M gas). | Test consensus message limits: 8192 deposits exceed the CL’s processing capacity, and the resulting block (with all proofs) approaches/exceeds the 10 MB P2P limit. | 🚧 |
| **Block Size Overlimit** | Construct a block that would encode (RLP) to >10 MB (e.g. many TXs with large fields). | Validate client behavior at RLP size limits. Ethereum’s default RLP cap is \~10 MB; this tests that oversized blocks are safely rejected or handled. | 🚧 |
| **Attestation Flood** | Include the maximum possible attestations/slashings/voluntary exits in a slot. | Stress the CL verification workload and attestation gossip. Ensures the CL can validate large sync committees and slashing events within slot deadlines. | 🚧 |
| **Large tx vs multiple small ones** | This will help understand the diff between same storage slots touched by single tx than multiple ones | Send a tx that touches as much state as possible (both contract storage & account one) and then, compare against multiple smaller txs that touch the same amout of storage | :construction: |
:::danger
Add node deletion that triggers branch rearrangement
:::