Fix log indexer noise after debug_setHead operations (#31934)

## Summary
This PR resolves Issue #31929 by reducing log noise generated by the log
indexer after `debug_setHead` operations.

## Problem Description
When `debug_setHead` is called to rewind the blockchain, blocks are
removed from the database. However, the log indexer's `ChainView`
objects may still hold references to these deleted blocks. When
`extendNonCanonical()` attempts to access these missing headers, it
results in:

1. **Repeated ERROR logs**: `Header not found number=X hash=0x...`
2. **Log noise** that can mask other important errors  
3. **User confusion** about whether this indicates a real problem

## Root Cause Analysis
The issue occurs because:
- `debug_setHead` removes blocks from the blockchain database
- Log indexer's `ChainView` may still reference deleted block hashes
- `extendNonCanonical()` in `core/filtermaps/chain_view.go` tries to
fetch these missing headers
- The existing `return false` logic properly handles the error, but logs
at ERROR level

## Solution
This is a **logging improvement only** - no functional logic changes:

### Changes Made
1. **Log level**: Changed from `ERROR` to `DEBUG` 
2. **Log message**: Enhanced with descriptive context about chain view
extension
3. **Comments**: Added explanation for when this situation occurs
4. **Behavior**: Maintains existing error handling (`return false` was
already present)

### Code Changes
```go
// Before
log.Error("Header not found", "number", number, "hash", hash)
return false

// After  
// Header not found - this can happen after debug_setHead operations
// where blocks have been deleted. Return false to indicate the chain view
// is no longer valid rather than logging repeated errors.
log.Debug("Header not found during chain view extension", "number", number, "hash", hash)
return false
```

## Testing

### Automated Tests
-  All existing filtermaps tests pass: `go test ./core/filtermaps -v`
-  No regressions in related functionality

### Manual Verification
1. **Before fix**: Started geth in dev mode, generated blocks, called
`debug_setHead(3)` → **5 repeated ERROR logs**
2. **After fix**: Same scenario → **4 DEBUG logs, no ERROR noise**

### Test Environment
```bash
# Setup test environment
rm -rf ./dev-test-data
./build/bin/geth --dev --datadir ./dev-test-data --http --http.api debug,eth,net,web3 --verbosity 4

# Generate test blocks and trigger issue
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"debug_setHead","params":["0x3"],"id":1}' http://localhost:8545
```


## Related Issues
- Fixes #31929

## Additional Context
This issue was reported as spurious error messages appearing after
`debug_setHead` operations. The investigation revealed that while the
error handling was functionally correct, the ERROR log level was
inappropriate for this expected scenario in development/debugging
workflows.

The fix maintains full compatibility while significantly improving the
debugging experience for developers using `debug_setHead`.

---------

Co-authored-by: Sun Tae, Kim <38067691+humblefirm@users.noreply.github.com>
Co-authored-by: zsfelfoldi <zsfelfoldi@gmail.com>
This commit is contained in:
Forrest Kim 2025-07-01 15:50:02 +09:00 committed by GitHub
parent 1f4ea4d162
commit 8a9f4bbb6d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -55,7 +55,9 @@ func NewChainView(chain blockchain, number uint64, hash common.Hash) *ChainView
headNumber: number,
hashes: []common.Hash{hash},
}
cv.extendNonCanonical()
if !cv.extendNonCanonical() {
return nil
}
return cv
}
@ -129,7 +131,11 @@ func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] {
return common.Range[uint64]{}
}
var sharedLen uint64
for n := min(cv.headNumber+1-uint64(len(cv.hashes)), cv2.headNumber+1-uint64(len(cv2.hashes))); n <= cv.headNumber && n <= cv2.headNumber && cv.blockHash(n) == cv2.blockHash(n); n++ {
for n := min(cv.headNumber+1-uint64(len(cv.hashes)), cv2.headNumber+1-uint64(len(cv2.hashes))); n <= cv.headNumber && n <= cv2.headNumber; n++ {
h1, h2 := cv.blockHash(n), cv2.blockHash(n)
if h1 != h2 || h1 == (common.Hash{}) {
break
}
sharedLen = n + 1
}
return common.NewRange(0, sharedLen)
@ -153,10 +159,13 @@ func matchViews(cv1, cv2 *ChainView, number uint64) bool {
if cv1.headNumber < number || cv2.headNumber < number {
return false
}
var h1, h2 common.Hash
if number == cv1.headNumber || number == cv2.headNumber {
return cv1.BlockId(number) == cv2.BlockId(number)
h1, h2 = cv1.BlockId(number), cv2.BlockId(number)
} else {
h1, h2 = cv1.BlockHash(number), cv2.BlockHash(number)
}
return cv1.BlockHash(number) == cv2.BlockHash(number)
return h1 == h2 && h1 != common.Hash{}
}
// extendNonCanonical checks whether the previously known reverse list of head
@ -175,7 +184,10 @@ func (cv *ChainView) extendNonCanonical() bool {
}
header := cv.chain.GetHeader(hash, number)
if header == nil {
log.Error("Header not found", "number", number, "hash", hash)
// Header not found - this can happen after debug_setHead operations
// where blocks have been deleted. Return false to indicate the chain view
// is no longer valid rather than logging repeated errors.
log.Debug("Header not found during chain view extension", "number", number, "hash", hash)
return false
}
cv.hashes = append(cv.hashes, header.ParentHash)