mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-08 16:01:36 +00:00
Fix the post-sync deadlock where blocks validated via BAL in newPayload were never written to the database, causing ForkchoiceUpdated to fail finding them and triggering infinite sync cycles. Changes: - Export WriteBlockWithoutState and call it after ProcessBlockWithBAL in newPayload, so FCU can find blocks via GetBlockByHash - Guard SetCanonical against recoverAncestors for partial state nodes (they can't re-execute blocks, only apply BAL diffs) - Auto-disable log indexing when partial state is enabled (no receipts) - Fix BAL type field accesses to match upstream bal-devnet-2 types (StorageChanges, CodeChanges, BalanceChanges, Validate signature) - Update newPayload signature (BAL now comes from ExecutableData params) - Add partial sync scripts and documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.2 KiB
5.2 KiB
Partial State Devnet Testing Guide
This document describes how to test partial statefulness with a local devnet using 2 geth instances.
Overview
Partial state nodes:
- Sync all account data (balances, nonces, code hashes)
- Only store storage for tracked contracts
- Process blocks using BAL (Block Access Lists) instead of re-executing transactions
Prerequisites
- Go 1.22+ installed
- Two terminal windows
- Build geth with partial state support:
go build ./cmd/geth
Setup
Terminal 1: Full Node (creates blocks in dev mode)
# Create fresh data directory
rm -rf /tmp/full-node
# Start full node in dev mode
./geth --datadir /tmp/full-node \
--dev \
--dev.period 5 \
--port 30303 \
--http --http.port 8545 \
--http.api eth,net,web3,debug,admin \
--verbosity 3
# Get the enode URL (run in another terminal or use geth attach)
# geth attach /tmp/full-node/geth.ipc --exec admin.nodeInfo.enode
Terminal 2: Partial State Node (receives blocks via P2P)
First, get the enode from the full node:
ENODE=$(geth attach /tmp/full-node/geth.ipc --exec admin.nodeInfo.enode | tr -d '"')
echo "Full node enode: $ENODE"
Then start the partial state node:
# Create fresh data directory
rm -rf /tmp/partial-node
# Start partial state node
./geth --datadir /tmp/partial-node \
--port 30304 \
--http --http.port 8546 \
--http.api eth,net,web3,debug \
--partial-state \
--partial-state.contracts 0xContractAddr1,0xContractAddr2 \
--bootnodes "$ENODE" \
--networkid 1337 \
--verbosity 3
Note: Replace 0xContractAddr1,0xContractAddr2 with actual contract addresses you want to track.
Test Scenarios
1. Block Sync Test
Send a transaction on the full node and verify the partial node receives it:
# On full node (Terminal 1 or new terminal)
geth attach /tmp/full-node/geth.ipc
# In geth console, send a transaction
> eth.sendTransaction({from: eth.coinbase, to: "0x1234567890123456789012345678901234567890", value: web3.toWei(1, "ether")})
# Check block number
> eth.blockNumber
Verify on partial node:
curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
-H "Content-Type: application/json" localhost:8546 | jq
2. Balance Query Test
Both nodes should return the same balance for any account:
# Full node
curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x1234567890123456789012345678901234567890","latest"],"id":1}' \
-H "Content-Type: application/json" localhost:8545 | jq
# Partial node
curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x1234567890123456789012345678901234567890","latest"],"id":1}' \
-H "Content-Type: application/json" localhost:8546 | jq
3. Storage Query Test
Deploy a contract and test storage access:
# Query tracked contract storage (should work)
curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["0xTrackedContractAddr","0x0","latest"],"id":1}' \
-H "Content-Type: application/json" localhost:8546 | jq
# Query untracked contract storage (should fail or return empty)
curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["0xUntrackedContractAddr","0x0","latest"],"id":1}' \
-H "Content-Type: application/json" localhost:8546 | jq
4. State Root Verification
Verify both nodes have the same state root:
# Get latest block from both nodes
FULL_ROOT=$(curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest",false],"id":1}' \
-H "Content-Type: application/json" localhost:8545 | jq -r '.result.stateRoot')
PARTIAL_ROOT=$(curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest",false],"id":1}' \
-H "Content-Type: application/json" localhost:8546 | jq -r '.result.stateRoot')
echo "Full node state root: $FULL_ROOT"
echo "Partial node state root: $PARTIAL_ROOT"
if [ "$FULL_ROOT" = "$PARTIAL_ROOT" ]; then
echo "State roots match!"
else
echo "State roots DO NOT match!"
fi
Database Size Comparison
After syncing, compare database sizes:
echo "Full node database size:"
du -sh /tmp/full-node/geth/chaindata
echo "Partial node database size:"
du -sh /tmp/partial-node/geth/chaindata
The partial node should have a significantly smaller database size due to skipped storage.
Cleanup
# Stop both geth instances (Ctrl+C in each terminal)
# Remove test data
rm -rf /tmp/full-node /tmp/partial-node
Troubleshooting
Nodes not connecting
- Verify bootnodes enode URL is correct
- Check that network IDs match (dev mode uses 1337)
- Ensure ports are not blocked
State root mismatch
- This indicates a bug in BAL processing
- Check geth logs for errors during block processing
- Verify the partial node received the BAL with the block
Storage queries failing
- Verify the contract address is in the tracked contracts list
- Check that the contract was deployed after the partial node started syncing