From 6f513399a446503ab9f464fc80499b4007f62c43 Mon Sep 17 00:00:00 2001 From: kant Date: Sat, 21 Jun 2025 08:33:51 -0700 Subject: [PATCH 1/7] geth state dump script --- tools/state-dump/README.md | 66 ++++++++++++++ tools/state-dump/initial_genesis.json | 82 ++++++++++++++++++ tools/state-dump/requirements.txt | 40 +++++++++ tools/state-dump/world_state.py | 120 ++++++++++++++++++++++++++ 4 files changed, 308 insertions(+) create mode 100644 tools/state-dump/README.md create mode 100644 tools/state-dump/initial_genesis.json create mode 100644 tools/state-dump/requirements.txt create mode 100644 tools/state-dump/world_state.py diff --git a/tools/state-dump/README.md b/tools/state-dump/README.md new file mode 100644 index 000000000..8ee5e449d --- /dev/null +++ b/tools/state-dump/README.md @@ -0,0 +1,66 @@ +# Geth World State Snapshot + +This repository provides a utility to snapshot the Ethereum world state at a given block and merge it into a new genesis file. The main script, `world_state.py`, connects to an archive node via JSON-RPC, retrieves account and storage data, and produces an updated genesis alloc. + +--- + +## Prerequisites + +- Python 3.8+ +- An Ethereum archive node exposing the JSON-RPC interface (e.g. Geth with `--gcmode=archive`). + +--- + +## Repository Structure + +```bash +/Users/kant/mev-commit/tools/state-dump +├── genesis_test.json +├── initial_genesis.json +├── requirements.txt +└── world_state.py +``` + +--- + +## Setup & Usage + +```bash +# 1. Create and activate a virtual environment +python3 -m venv geth-world-state +source geth-world-state/bin/activate + +# 2. Upgrade pip and install dependencies +pip install --upgrade pip +pip install -r requirements.txt + +# 3. Run the snapshot script and merge into out_genesis.json +python3 world_state.py \ + --rpc http://:8545 \ + --input-genesis initial_genesis.json \ + --output out_genesis.json +``` + +**Example:** + +```bash +python3 world_state.py \ + --rpc http://34.75.194.46:8545 \ + --input-genesis initial_genesis.json \ + --output out_genesis.json +``` + +- `--rpc` + JSON-RPC endpoint of your archive node (e.g. `http://127.0.0.1:8545`). + +- `--input-genesis` + Path to your existing genesis template (e.g. `initial_genesis.json`). + +- `--output` + Path where the merged genesis file will be written (e.g. `out_genesis.json`). + +--- + +## License + +This project is released under the MIT License. See [LICENSE](LICENSE) for details. diff --git a/tools/state-dump/initial_genesis.json b/tools/state-dump/initial_genesis.json new file mode 100644 index 000000000..cc57634bf --- /dev/null +++ b/tools/state-dump/initial_genesis.json @@ -0,0 +1,82 @@ +{ + "config": { + "chainId": 141414, + "homesteadBlock": 0, + "daoForkSupport": true, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "shanghaiTime": 0, + "cancunTime": 0, + "pragueTime": 0, + "depositContractAddress": "0x00000000219ab540356cbb839cbe05303d7705fa", + "blobSchedule": { + "cancun": { + "target": 3, + "max": 6, + "baseFeeUpdateFraction": 3338477 + }, + "prague": { + "target": 6, + "max": 9, + "baseFeeUpdateFraction": 5007716 + } + }, + "terminalTotalDifficulty": 0 + }, + "nonce": "0x0", + "timestamp": "0x0", + "gasLimit": "0x1c9c380", + "difficulty": "0x1", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "E666c8Fc7be06a4A88145Cd0C626FAe4D40fCDA7": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "3Ba6a3318a7D55C73F743529E2ca69cCF112D538": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "BADc9848e1e87E5017E7790Be7c4b5d35C304FC1": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "c286beF43cEa547545d5b7179AEf6747F63Ac8Aa": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "072F6D4d7A1F7Af547d47D927bEaf38E01Fcb33b": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "00000961Ef480Eb55e80D19ad83579A64c007002": { + "balance": "0x0", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460cb5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f457600182026001905f5b5f82111560685781019083028483029004916001019190604d565b909390049250505036603814608857366101f457346101f4575f5260205ff35b34106101f457600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160df575060105b5f5b8181146101835782810160030260040181604c02815460601b8152601401816001015481526020019060020154807fffffffffffffffffffffffffffffffff00000000000000000000000000000000168252906010019060401c908160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160e1565b910180921461019557906002556101a0565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101cd57505f5b6001546002828201116101e25750505f6101e8565b01600290035b5f555f600155604c025ff35b5f5ffd" + }, + "0000bbddc7ce488642fb579f8b00f3a590007251": { + "balance": "0x0", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460d35760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f82111560685781019083028483029004916001019190604d565b9093900492505050366060146088573661019a573461019a575f5260205ff35b341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060021160e7575060025b5f5b8181146101295782810160040260040181607402815460601b815260140181600101548152602001816002015481526020019060030154905260010160e9565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd" + }, + "0x0000F90827F1C53a10cb7A02335B175320002935": { + "balance": "0x0", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500" + }, + "0x00000000219ab540356cbb839cbe05303d7705fa": { + "balance": "0x0", + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a2646970667358221220dceca8706b29e917dacf25fceef95acac8d90d765ac926663ce4096195952b6164736f6c634300060b0033" + } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "withdrawalsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas": "0x7", + "excessBlobGas": "0x0", + "blobGasUsed": "0x0" +} diff --git a/tools/state-dump/requirements.txt b/tools/state-dump/requirements.txt new file mode 100644 index 000000000..c3aef21c9 --- /dev/null +++ b/tools/state-dump/requirements.txt @@ -0,0 +1,40 @@ +aiohappyeyeballs==2.6.1 +aiohttp==3.12.13 +aiosignal==1.3.2 +annotated-types==0.7.0 +attrs==25.3.0 +bitarray==3.4.2 +certifi==2025.6.15 +charset-normalizer==3.4.2 +ckzg==2.1.1 +cytoolz==1.0.1 +eth-account==0.13.7 +eth-hash==0.7.1 +eth-keyfile==0.8.1 +eth-keys==0.7.0 +eth-rlp==2.2.0 +eth-typing==5.2.1 +eth-utils==5.3.0 +eth_abi==5.2.0 +frozenlist==1.7.0 +hexbytes==1.3.1 +idna==3.10 +multidict==6.5.0 +parsimonious==0.10.0 +propcache==0.3.2 +pycryptodome==3.23.0 +pydantic==2.11.7 +pydantic_core==2.33.2 +pyunormalize==16.0.0 +regex==2024.11.6 +requests==2.32.4 +rlp==4.1.0 +toolz==1.0.0 +tqdm==4.67.1 +types-requests==2.32.4.20250611 +typing-inspection==0.4.1 +typing_extensions==4.14.0 +urllib3==2.5.0 +web3==7.12.0 +websockets==15.0.1 +yarl==1.20.1 diff --git a/tools/state-dump/world_state.py b/tools/state-dump/world_state.py new file mode 100644 index 000000000..5a6037406 --- /dev/null +++ b/tools/state-dump/world_state.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +import json +import argparse +import requests +from pathlib import Path + +# Default filename for the migrated genesis output +DEFAULT_OUT_NAME = "out_genesis.json" + +def rpc_call(rpc_url: str, method: str, params: list) -> dict: + payload = { + "jsonrpc": "2.0", + "id": 1, + "method": method, + "params": params + } + resp = requests.post(rpc_url, json=payload) + resp.raise_for_status() + data = resp.json() + if "error" in data: + raise RuntimeError(f"RPC error ({method}): {data['error']}") + return data["result"] + +def rpc_get_block_number(rpc_url: str) -> int: + """Fetch the latest block number (as an int).""" + hex_bn = rpc_call(rpc_url, "eth_blockNumber", []) + return int(hex_bn, 16) + +def rpc_debug_dump_block(rpc_url: str, block_param: str) -> dict: + """Call debug_dumpBlock at the given block tag or hex number.""" + return rpc_call(rpc_url, "debug_dumpBlock", [block_param]) + +def to_hex(x: str) -> str: + """Convert a decimal string to a hex string (0x-prefixed).""" + return hex(int(x)) + +def build_alloc(accounts: dict) -> dict: + """ + Given the 'accounts' map from debug_dumpBlock, return an 'alloc' + dictionary suitable for a genesis file: address → { balance, code?, storage? }. + """ + alloc = {} + for addr, acct in accounts.items(): + entry = {"balance": to_hex(acct["balance"])} + if acct.get("code"): + entry["code"] = acct["code"] + if acct.get("storage"): + entry["storage"] = acct["storage"] + alloc[addr] = entry + return alloc + +def main(): + p = argparse.ArgumentParser( + description="Snapshot world-state via debug_dumpBlock and merge into a genesis template" + ) + p.add_argument( + "--rpc", + default="http://127.0.0.1:8545", + help="Geth RPC endpoint" + ) + p.add_argument( + "--input-genesis", + required=True, + help="Path to your source chain genesis.json" + ) + p.add_argument( + "--block", "-b", + type=int, + help="Block number to snapshot (defaults to latest)" + ) + p.add_argument( + "--output", "-o", + help=( + "Path or directory for output genesis JSON " + f"(default: ./{DEFAULT_OUT_NAME})" + ) + ) + args = p.parse_args() + + # load template + tmpl_path = Path(args.input_genesis) + if not tmpl_path.is_file(): + raise SystemExit(f"❌ Input genesis not found: {tmpl_path}") + genesis_tpl = json.loads(tmpl_path.read_text()) + + # decide which block to dump + if args.block is None: + # fetch actual latest block number + block_no = rpc_get_block_number(args.rpc) + block_param = "latest" + print(f"⛓ Dumping world-state at latest (block {block_no})…") + else: + block_no = args.block + block_param = hex(block_no) + print(f"⛓ Dumping world-state at block {block_no}…") + + # fetch the dump + dump = rpc_debug_dump_block(args.rpc, block_param) + print(f" ↳ {len(dump['accounts'])} accounts loaded") + + # build alloc → merge → write out + latest_alloc = build_alloc(dump["accounts"]) + new_gen = genesis_tpl.copy() + orig_alloc = new_gen.get("alloc", {}) + new_gen["alloc"] = {**orig_alloc, **latest_alloc} + + # determine output path + if args.output: + out_path = Path(args.output) + if out_path.is_dir(): + out_path = out_path / DEFAULT_OUT_NAME + else: + out_path = Path.cwd() / DEFAULT_OUT_NAME + + out_path.parent.mkdir(parents=True, exist_ok=True) + out_path.write_text(json.dumps(new_gen, indent=2)) + print(f"✅ Wrote merged genesis → {out_path}") + +if __name__ == "__main__": + main() From de5e7138fe9897ca8dfbce8cb73cfaa3c6019958 Mon Sep 17 00:00:00 2001 From: kant777 <61204489+kant777@users.noreply.github.com> Date: Sat, 21 Jun 2025 08:35:45 -0700 Subject: [PATCH 2/7] Update README.md --- tools/state-dump/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/state-dump/README.md b/tools/state-dump/README.md index 8ee5e449d..9c1196884 100644 --- a/tools/state-dump/README.md +++ b/tools/state-dump/README.md @@ -14,7 +14,7 @@ This repository provides a utility to snapshot the Ethereum world state at a giv ## Repository Structure ```bash -/Users/kant/mev-commit/tools/state-dump +mev-commit/tools/state-dump ├── genesis_test.json ├── initial_genesis.json ├── requirements.txt From 5cfa61f6e689ff30e836df08d46ef3ce1f748c53 Mon Sep 17 00:00:00 2001 From: kant777 <61204489+kant777@users.noreply.github.com> Date: Sat, 21 Jun 2025 08:36:16 -0700 Subject: [PATCH 3/7] Update README.md --- tools/state-dump/README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tools/state-dump/README.md b/tools/state-dump/README.md index 9c1196884..fa63041d4 100644 --- a/tools/state-dump/README.md +++ b/tools/state-dump/README.md @@ -58,9 +58,3 @@ python3 world_state.py \ - `--output` Path where the merged genesis file will be written (e.g. `out_genesis.json`). - ---- - -## License - -This project is released under the MIT License. See [LICENSE](LICENSE) for details. From 82149c985f5afd111777d47c955e499bc15f689c Mon Sep 17 00:00:00 2001 From: kant Date: Sat, 21 Jun 2025 11:31:03 -0700 Subject: [PATCH 4/7] optionally include account nonces --- tools/state-dump/README.md | 3 +++ tools/state-dump/world_state.py | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/tools/state-dump/README.md b/tools/state-dump/README.md index fa63041d4..509bd6c77 100644 --- a/tools/state-dump/README.md +++ b/tools/state-dump/README.md @@ -58,3 +58,6 @@ python3 world_state.py \ - `--output` Path where the merged genesis file will be written (e.g. `out_genesis.json`). + +- `--include-nonces` + Enabling this flag adds each account’s nonce to the genesis alloc entries diff --git a/tools/state-dump/world_state.py b/tools/state-dump/world_state.py index 5a6037406..5c5d5a728 100644 --- a/tools/state-dump/world_state.py +++ b/tools/state-dump/world_state.py @@ -21,27 +21,33 @@ def rpc_call(rpc_url: str, method: str, params: list) -> dict: raise RuntimeError(f"RPC error ({method}): {data['error']}") return data["result"] + def rpc_get_block_number(rpc_url: str) -> int: """Fetch the latest block number (as an int).""" hex_bn = rpc_call(rpc_url, "eth_blockNumber", []) return int(hex_bn, 16) + def rpc_debug_dump_block(rpc_url: str, block_param: str) -> dict: """Call debug_dumpBlock at the given block tag or hex number.""" return rpc_call(rpc_url, "debug_dumpBlock", [block_param]) + def to_hex(x: str) -> str: """Convert a decimal string to a hex string (0x-prefixed).""" return hex(int(x)) -def build_alloc(accounts: dict) -> dict: + +def build_alloc(accounts: dict, include_nonces: bool) -> dict: """ Given the 'accounts' map from debug_dumpBlock, return an 'alloc' - dictionary suitable for a genesis file: address → { balance, code?, storage? }. + dictionary suitable for a genesis file: address → { balance, code?, storage?, nonce? }. """ alloc = {} for addr, acct in accounts.items(): entry = {"balance": to_hex(acct["balance"])} + if include_nonces and acct.get("nonce") is not None: + entry["nonce"] = to_hex(acct["nonce"]) if acct.get("code"): entry["code"] = acct["code"] if acct.get("storage"): @@ -49,6 +55,7 @@ def build_alloc(accounts: dict) -> dict: alloc[addr] = entry return alloc + def main(): p = argparse.ArgumentParser( description="Snapshot world-state via debug_dumpBlock and merge into a genesis template" @@ -75,6 +82,11 @@ def main(): f"(default: ./{DEFAULT_OUT_NAME})" ) ) + p.add_argument( + "--include-nonces", + action="store_true", + help="Include account nonces in the alloc entries" + ) args = p.parse_args() # load template @@ -85,7 +97,6 @@ def main(): # decide which block to dump if args.block is None: - # fetch actual latest block number block_no = rpc_get_block_number(args.rpc) block_param = "latest" print(f"⛓ Dumping world-state at latest (block {block_no})…") @@ -99,7 +110,7 @@ def main(): print(f" ↳ {len(dump['accounts'])} accounts loaded") # build alloc → merge → write out - latest_alloc = build_alloc(dump["accounts"]) + latest_alloc = build_alloc(dump["accounts"], args.include_nonces) new_gen = genesis_tpl.copy() orig_alloc = new_gen.get("alloc", {}) new_gen["alloc"] = {**orig_alloc, **latest_alloc} From 214a5f0533c16ab73af7f3739e07043c569b7125 Mon Sep 17 00:00:00 2001 From: kant777 <61204489+kant777@users.noreply.github.com> Date: Sat, 21 Jun 2025 11:32:33 -0700 Subject: [PATCH 5/7] Update README.md From 3321dec374190db28267a0fd3fb037d622a2db85 Mon Sep 17 00:00:00 2001 From: kant Date: Sat, 21 Jun 2025 21:38:09 -0700 Subject: [PATCH 6/7] Adding state verifier script --- tools/state-dump/verify_state_migration.py | 199 +++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 tools/state-dump/verify_state_migration.py diff --git a/tools/state-dump/verify_state_migration.py b/tools/state-dump/verify_state_migration.py new file mode 100644 index 000000000..9bfa38d2a --- /dev/null +++ b/tools/state-dump/verify_state_migration.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +import argparse +import json +import sys +from web3 import Web3 +from web3.middleware import ExtraDataToPOAMiddleware +from web3.exceptions import Web3RPCError + + +def parse_quantity(q: str) -> int: + """ + Convert a hex or decimal string to an integer. + """ + return int(q, 16) if isinstance(q, str) and q.startswith("0x") else int(q) + + +def load_alloc(path: str) -> dict: + """ + Load the `alloc` section from a genesis JSON file. + """ + with open(path, 'r') as f: + data = json.load(f) + return data.get("alloc") or data.get("Alloc") or {} + + +# Addresses to exclude (lowercase, no '0x' prefix) +EXCLUDE_ADDRS = { + '00000000219ab540356cbb839cbe05303d7705fa', + '00000961ef480eb55e80d19ad83579a64c007002', + '0000bbddc7ce488642fb579f8b00f3a590007251', + '0000f90827f1c53a10cb7a02335b175320002935', + 'e666c8fc7be06a4a88145cd0c626fae4d40fcda7', + '3ba6a3318a7d55c73f743529e2ca69ccf112d538', + 'badc9848e1e87e5017e7790be7c4b5d35c304fc1', + 'c286bef43cea547545d5b7179aef6747f63ac8aa', + '072f6d4d7a1f7af547d47d927beaf38e01fcb33b' +} + + +def normalize(addr: str) -> str: + """ + Normalize address to lowercase without '0x' prefix for comparison. + """ + a = addr.lower() + return a[2:] if a.startswith('0x') else a + + +def check_nonces(w3_source: Web3, w3_target: Web3, src_id, tgt_id, alloc: dict) -> None: + print("🔎 Checking nonces for all addresses") + for addr, entry in alloc.items(): + if normalize(addr) in EXCLUDE_ADDRS: + continue + cs = Web3.to_checksum_address(addr) + gen_nonce = parse_quantity(entry.get("nonce", "0x0")) + try: + n_src = w3_source.eth.get_transaction_count(cs, block_identifier=src_id) + except Web3RPCError: + n_src = gen_nonce + try: + n_tgt = w3_target.eth.get_transaction_count(cs, block_identifier=tgt_id) + except Web3RPCError: + n_tgt = gen_nonce + status = "✅" if (n_src == n_tgt == gen_nonce) else "⛔" + print(f"Address {addr}: {status} nonce src={n_src} tgt={n_tgt} genesis={gen_nonce}") + print() + + +def check_balances(w3_source: Web3, w3_target: Web3, src_id, tgt_id, alloc: dict) -> None: + print("🔎 Checking balances for all addresses") + for addr, entry in alloc.items(): + if normalize(addr) in EXCLUDE_ADDRS: + continue + cs = Web3.to_checksum_address(addr) + gen_bal = parse_quantity(entry.get("balance", hex(0))) + try: + b_src = w3_source.eth.get_balance(cs, block_identifier=src_id) + except Web3RPCError: + b_src = gen_bal + try: + b_tgt = w3_target.eth.get_balance(cs, block_identifier=tgt_id) + except Web3RPCError: + b_tgt = gen_bal + status = "✅" if (b_src == b_tgt == gen_bal) else "⛔" + print(f"Address {addr}: {status} balance src={b_src} tgt={b_tgt} genesis={gen_bal}") + print() + + +def check_code(w3_source: Web3, w3_target: Web3, src_id, tgt_id, alloc: dict) -> None: + print("🔎 Checking code for all contracts (genesis vs source vs target)") + for addr, entry in alloc.items(): + if normalize(addr) in EXCLUDE_ADDRS: + continue + gen_code = entry.get("code", "").lower() + if gen_code in ("", "0x"): + continue + cs = Web3.to_checksum_address(addr) + try: + code_src = "0x" + w3_source.eth.get_code(cs, block_identifier=src_id).hex() + except Web3RPCError: + code_src = None + try: + code_tgt = "0x" + w3_target.eth.get_code(cs, block_identifier=tgt_id).hex() + except Web3RPCError: + code_tgt = None + if code_src == code_tgt == gen_code: + print(f"Address {addr}: ✅ code matches (genesis=source=target)") + else: + print(f"Address {addr}: ⛔ code mismatch") + print(f" genesis: {gen_code}") + print(f" source: {code_src}") + print(f" target: {code_tgt}") + print() + + +def check_storage_roots(w3_source: Web3, w3_target: Web3, src_id, tgt_id, alloc: dict) -> None: + print("🔎 Checking storage‐roots for all contracts") + for addr, entry in alloc.items(): + if normalize(addr) in EXCLUDE_ADDRS: + continue + gen_code = entry.get("code", "").lower() + if gen_code in ("", "0x"): + continue + + cs = Web3.to_checksum_address(addr) + + try: + proof_src = w3_source.eth.get_proof(cs, [], block_identifier=src_id) + hex_src = Web3.to_hex(proof_src["storageHash"]) + except Web3RPCError: + hex_src = None + + try: + proof_tgt = w3_target.eth.get_proof(cs, [], block_identifier=tgt_id) + hex_tgt = Web3.to_hex(proof_tgt["storageHash"]) + except Web3RPCError: + hex_tgt = None + + if hex_src is not None and hex_src == hex_tgt: + print(f"Address {addr}: ✅ storage‐root matches (value={hex_src})") + else: + print(f"Address {addr}: ⛔ storage‐root src={hex_src} tgt={hex_tgt}") + print() + + +def main() -> None: + parser = argparse.ArgumentParser(description="Verify EVM state migration using a genesis alloc file") + parser.add_argument("--genesis", required=True) + parser.add_argument("--source-rpc-url", required=True) + parser.add_argument( + "--source-block-number", + type=str, + default="latest", + help="source block identifier (hex, dec) or 'latest' (default)" + ) + parser.add_argument("--target-rpc-url", required=True) + parser.add_argument( + "--target-block-number", + type=str, + default="latest", + help="target block identifier (hex, dec) or 'latest' (default)" + ) + args = parser.parse_args() + + alloc = load_alloc(args.genesis) + if not alloc: + print("No alloc found.") + sys.exit(1) + + # Determine source identifier + src_id = args.source_block_number.lower() + if src_id != 'latest': + try: + src_id = parse_quantity(src_id) + except ValueError: + print(f"Invalid source block: {args.source_block_number}") + sys.exit(1) + + # Determine target identifier + tgt_id = args.target_block_number.lower() + if tgt_id != 'latest': + try: + tgt_id = parse_quantity(tgt_id) + except ValueError: + print(f"Invalid target block: {args.target_block_number}") + sys.exit(1) + + w3_src = Web3(Web3.HTTPProvider(args.source_rpc_url)) + w3_src.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) + w3_tgt = Web3(Web3.HTTPProvider(args.target_rpc_url)) + w3_tgt.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) + + check_nonces(w3_src, w3_tgt, src_id, tgt_id, alloc) + check_balances(w3_src, w3_tgt, src_id, tgt_id, alloc) + check_code(w3_src, w3_tgt, src_id, tgt_id, alloc) + check_storage_roots(w3_src, w3_tgt, src_id, tgt_id, alloc) + + +if __name__ == "__main__": + main() From b989a12872fc6ae16c177c316c92e0ac35a0d438 Mon Sep 17 00:00:00 2001 From: kant Date: Sun, 22 Jun 2025 15:32:11 -0700 Subject: [PATCH 7/7] handling singleton storage slots --- tools/state-dump/verify_state_migration.py | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tools/state-dump/verify_state_migration.py b/tools/state-dump/verify_state_migration.py index 9bfa38d2a..32435ac36 100644 --- a/tools/state-dump/verify_state_migration.py +++ b/tools/state-dump/verify_state_migration.py @@ -117,12 +117,52 @@ def check_storage_roots(w3_source: Web3, w3_target: Web3, src_id, tgt_id, alloc: for addr, entry in alloc.items(): if normalize(addr) in EXCLUDE_ADDRS: continue + + # Only contracts gen_code = entry.get("code", "").lower() if gen_code in ("", "0x"): continue cs = Web3.to_checksum_address(addr) + storage_slots = entry.get("storage", {}) + + # Single-slot case: compare that slot’s value + if len(storage_slots) == 1: + # extract the one slot key/value + slot_hex, raw_expected = next(iter(storage_slots.items())) + # normalize both + slot_key = slot_hex if slot_hex.startswith("0x") else "0x" + slot_hex + expected_int = int(raw_expected, 16) + + # fetch src and tgt storage + try: + src_hex = Web3.to_hex( + w3_source.eth.get_storage_at(cs, int(slot_key, 16), block_identifier=src_id) + ) + src_int = int(src_hex, 16) + except Web3RPCError: + src_int = None + + try: + tgt_hex = Web3.to_hex( + w3_target.eth.get_storage_at(cs, int(slot_key, 16), block_identifier=tgt_id) + ) + tgt_int = int(tgt_hex, 16) + except Web3RPCError: + tgt_int = None + + # padded‐hex helper + pad = lambda x: format(x, "#066x") if isinstance(x, int) else None + + if src_int == tgt_int == expected_int: + print(f"Address {addr}: ✅ single slot matches " + f"(slot={slot_key}, value={pad(expected_int)})") + else: + print(f"Address {addr}: ⛔ single slot mismatch " + f"src={pad(src_int)} tgt={pad(tgt_int)} genesis={pad(expected_int)}") + continue + # Multi-slot or zero-slot: fall back to storageHash proof try: proof_src = w3_source.eth.get_proof(cs, [], block_identifier=src_id) hex_src = Web3.to_hex(proof_src["storageHash"])