Bug Description
When syncing Polygon bor-mainnet from genesis with --no-downloader, the execution stage (4/6) enters an infinite loop at block ~856,625. The node repeatedly executes the same 5,000 block batch without advancing.
Logs
[4/6 Execution] starting from=851625 to=878729 fromTxNum=1703301 initialCycle=true
[4/6 Execution] Done blk=856625 blks=5001 gas/s=0 buf=4B/1.0GB
[4/6 Execution] starting from=851625 to=878729 fromTxNum=1703301 initialCycle=true
[4/6 Execution] Done blk=856625 blks=5001 gas/s=0 buf=4B/1.0GB
... (repeats infinitely)
Key indicators:
gas/s=0 — all blocks in the batch have zero gas usage
buf=4B — no state changes buffered
from=851625 never advances
blks=5001 = exactly LoopBlockLimit + 1
Root Cause
Early Polygon blocks (~0 to ~1.2M) have zero transactions and zero gas. During initial sync:
ExecV3 processes blocks from the last commitment position
- After processing
LoopBlockLimit (5000) blocks, the block limit fires at line 815 of exec3.go and breaks the loop
- The function returns
ErrLoopExhausted to the caller
- The caller commits the transaction and re-invokes
ExecV3
NewSharedDomains → SeekCommitment reads the same old commitment position because:
- No state changes occurred (0 gas blocks)
- The commitment system can only write at step boundaries (
DefaultStepSize = 1,562,500)
- The current
txNum (1,703,301) is between step boundaries, so no new commitment was written
- Execution starts from the same block → infinite loop
Proposed Fix
Add totalGasUsed > 0 to the block limit condition in execution/stagedsync/exec3.go line 815:
- if blockLimit > 0 && blockNum-startBlockNum+1 >= blockLimit {
+ if blockLimit > 0 && blockNum-startBlockNum+1 >= blockLimit && totalGasUsed > 0 {
This allows execution to continue through empty-block regions until blocks with actual state changes are reached, where commitment CAN be saved. Empty blocks are essentially free to process (~80K blocks/sec).
Why This Is Safe
totalGasUsed is already tracked in the execution loop (used for logging at line 727)
- Empty blocks (0 gas) do not create state changes, so processing more is essentially free
- Once blocks with actual gas are encountered, the block limit fires normally
- The commitment system can then save state because real state changes create writable data
- Tested: node successfully advanced from block 856,625 to 1,100,000+ after applying this fix
Affected Versions
v3.5.0 (0xPolygon fork) — loops at block ~856,625
v3.1.0 (erigontech upstream) — loops at block ~806,238
- Any version syncing a chain with extended zero-gas block regions from genesis
Reproduction
erigon --chain=bor-mainnet --no-downloader --prune.mode=minimal
# Wait for execution stage to reach ~856K
# Observe infinite loop in logs
Environment
- OS: macOS ARM64 (also reproducible on Linux)
- Chain: Polygon bor-mainnet
- Sync mode: from genesis, no snapshots (
--no-downloader)
Bug Description
When syncing Polygon
bor-mainnetfrom genesis with--no-downloader, the execution stage (4/6) enters an infinite loop at block ~856,625. The node repeatedly executes the same 5,000 block batch without advancing.Logs
Key indicators:
gas/s=0— all blocks in the batch have zero gas usagebuf=4B— no state changes bufferedfrom=851625never advancesblks=5001= exactlyLoopBlockLimit + 1Root Cause
Early Polygon blocks (~0 to ~1.2M) have zero transactions and zero gas. During initial sync:
ExecV3processes blocks from the last commitment positionLoopBlockLimit(5000) blocks, the block limit fires at line 815 ofexec3.goand breaks the loopErrLoopExhaustedto the callerExecV3NewSharedDomains → SeekCommitmentreads the same old commitment position because:DefaultStepSize = 1,562,500)txNum(1,703,301) is between step boundaries, so no new commitment was writtenProposed Fix
Add
totalGasUsed > 0to the block limit condition inexecution/stagedsync/exec3.goline 815:This allows execution to continue through empty-block regions until blocks with actual state changes are reached, where commitment CAN be saved. Empty blocks are essentially free to process (~80K blocks/sec).
Why This Is Safe
totalGasUsedis already tracked in the execution loop (used for logging at line 727)Affected Versions
v3.5.0(0xPolygon fork) — loops at block ~856,625v3.1.0(erigontech upstream) — loops at block ~806,238Reproduction
Environment
--no-downloader)