Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
* @title AttackOrchestrator
* @dev Orchestrates attacks on CREATE2-deployed contracts by deriving their addresses
* and calling attack(uint256) on each one within a specified range.
*/
contract AttackOrchestrator {
// Immutable storage for gas efficiency
address public immutable deployerAddress;
bytes32 public immutable initCodeHash;

// Attack function selector for attack(uint256)
bytes4 private constant ATTACK_SELECTOR = 0x64dd891a;

/**
* @dev Constructor sets the deployer address and init code hash
* @param _deployer The address that deployed the target contracts via CREATE2
* @param _initCodeHash The keccak256 hash of the target contract's init code
*/
constructor(address _deployer, bytes32 _initCodeHash) {
deployerAddress = _deployer;
initCodeHash = _initCodeHash;
}

/**
* @dev Executes attacks on a range of CREATE2-deployed contracts
* @param value The value to pass to each attack() call
* @param startIndex The starting index (inclusive)
* @param endIndex The ending index (exclusive)
*/
function attack(uint256 value, uint256 startIndex, uint256 endIndex) external {
// Load immutables into local variables for assembly access
address _deployer = deployerAddress;
bytes32 _codeHash = initCodeHash;

// Use assembly for gas-efficient CREATE2 address derivation and calls
assembly {
// Use local variables instead of immutables
let deployer := _deployer
let codeHash := _codeHash

// Loop through the range
for { let i := startIndex } lt(i, endIndex) { i := add(i, 1) } {
// Derive CREATE2 address
// Formula: keccak256(0xff ++ deployer ++ salt ++ initCodeHash)[12:]

// Store CREATE2 preimage in memory
// The layout should be: 0xff (1 byte) + deployer (20 bytes) + salt (32 bytes) + codeHash (32 bytes)
// Total: 85 bytes
let memPtr := 0x00
mstore8(memPtr, 0xff) // 0xff prefix at byte 0
mstore(add(memPtr, 0x01), shl(96, deployer)) // deployer address at bytes 1-20
mstore(add(memPtr, 0x15), i) // salt (i as uint256) at bytes 21-52
mstore(add(memPtr, 0x35), codeHash) // init code hash at bytes 53-84

// Compute CREATE2 address
let target := and(
keccak256(0x00, 0x55), // Hash 85 bytes
0xffffffffffffffffffffffffffffffffffffffff // Mask to 20 bytes
)

// Prepare memory for the attack call at a different location to avoid overwriting
let callDataPtr := 0x80
// Hardcode the selector directly: 0x64dd891a shifted left by 224 bits
mstore(callDataPtr, 0x64dd891a00000000000000000000000000000000000000000000000000000000)
mstore(add(callDataPtr, 0x04), value) // Value parameter

// Call attack(value) with 3,650 gas (optimal for SSTORE to deep slot)
// Breakdown: 2,900 gas for cold SSTORE + 342 gas for function overhead + 400 gas safety margin
let success := call(
3650, // gas
target, // to
0, // value (0 ETH)
callDataPtr,// argsOffset (where we stored the call data)
0x24, // argsSize (4 + 32 = 36 bytes)
0, // retOffset (don't store return data)
0 // retSize
)

// Continue regardless of success
// No need to check success, just continue to next iteration
}
}
}

/**
* @dev Fallback function to execute attacks when called directly
* This allows the contract to execute in its constructor if needed
*/
fallback() external {
// Could implement constructor-based attack here if needed
}
}
92 changes: 92 additions & 0 deletions tests/benchmark/stateful/bloatnet/depth_benchmarks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Depth Benchmark Tests

This directory contains tests for worst-case depth attacks on Ethereum state and account tries.

## Scenario Description

These benchmarks test the worst-case scenario for Ethereum clients when dealing with extremely deep state and account tries. The attack involves:

1. **Pre-deployed contracts** with deep storage tries that maximize trie traversal costs
2. **CREATE2-based addressing** for deterministic contract addresses across test runs
3. **Optimized batched attacks** using an AttackOrchestrator contract that can execute up to 2,510 attacks per transaction (8.3x improvement over previous implementation)
4. **Account trie depth** increased by funding auxiliary accounts that make the path deeper

The test measures the performance impact of state root recomputation and IO when modifying deep storage slots across thousands of contracts, simulating the maximum theoretical load on the state trie.

For complete deployment setup and instructions, see the gist: https://gist.github.com/CPerezz/44d521c0f9e6adf7d84187a4f2c11978

## Prerequisites

- Python with `uv` package manager
- Anvil (Ethereum node implementation)
- Solc (Solidity compiler)
- Nick's factory deployed at `0x4e59b44847b379578588920ca78fbf26c0b4956c`

## Workflow

### Step 1: Generate Artifacts

Use [worst_case_miner](https://github.com/CPerezz/worst_case_miner) to generate the necessary artifacts:

```bash
# Clone and build worst_case_miner
git clone https://github.com/CPerezz/worst_case_miner
cd worst_case_miner
cargo build --release

# Generate artifacts (example for depth 9, account depth 3)
./target/release/worst_case_miner --storage-depth 9 --account-depth 3 --output s9_acc3.json
```

This generates:
- `depth_9.sol` - Solidity contract with deep storage trie
- `s9_acc3.json` - Pre-computed CREATE2 addresses and auxiliary accounts

### Step 2: Start the Node (Anvil in this example)

```bash
# Start Anvil with high gas limit and auto-mining
anvil --hardfork prague --block-time 6 --steps-tracing --gas-limit 500000000 --balance 99999999999999 --port 8545
```

### Step 3: Deploy Contracts

Deploy contracts using the provided script with batched transactions:

```bash
# Deploy 1,000 contracts (recommended for testing)
uv run python deploy_worst_case_contracts.py \
--rpc-url http://localhost:8546 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--storage-depth 9 \
--account-depth 3 \
--num-contracts 1000 \
--output deployed_contracts.json


The script:
- Funds auxiliary accounts in batches (3 accounts per contract)
- Deploys contracts via CREATE2 for deterministic addresses
- Dynamically calculates batch sizes based on network gas limit

### Step 4: Run Attack Test

Execute the worst-case depth attack test:

```bash
# Update NUM_CONTRACTS in deep_branch_testing.py to match deployed count (1000 or 15000)

# Run the attack test
uv run execute remote \
--rpc-endpoint=http://localhost:8546 \
--rpc-seed-key=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--rpc-chain-id=31337 \
--gas-benchmark-values 60 \
--fork Prague \
-m stateful \
deep_branch_testing.py::test_worst_depth_stateroot_recomp
```

## Configuration

Adjust `NUM_CONTRACTS` in `deep_branch_testing.py` to match your deployment:
Loading
Loading