-
Notifications
You must be signed in to change notification settings - Fork 31
Description
Bug Description
The rescanner worker consumes failed block events from other chains, causing it to retry block numbers that are invalid for its own chain. This results in errors like Bitcoin testnet trying to fetch Solana-scale block heights (~449M).
Root Cause
In factory.go:743, a single failedChan is created and shared across all chains:
failedChan := make(chan FailedBlockEvent, 100)All chain workers (Solana, Bitcoin, EVM, etc.) write to this same channel via base.go:146:
bw.failedChan <- FailedBlockEvent{
Chain: bw.chain.GetName(), // Chain field is set...
Block: result.Number,
Attempt: 1,
}But the rescanner reads from it without filtering by chain (rescanner.go:77-81):
for evt := range rw.failedChan {
// evt.Chain is ignored — any chain's failed block gets added
rw.addFailedBlock(evt.Block, ...)
}So when a Solana devnet block (~449M) fails, the Bitcoin testnet rescanner can consume it and attempt to fetch block 449,310,635 from Bitcoin testnet RPC, which fails with "Block height out of range".
Observed Symptoms
DBG Provider failed but not switching provider=bitcoin_testnet-1
url=https://bitcoin-testnet-rpc.publicnode.com
error="failed to get block hash for height 449310635: getblockhash failed:
getblockhash RPC error: RPC error -8: Block height out of range"
Bitcoin testnet latest block is ~4.8M. The 449M height is a Solana devnet slot number.
Proposed Fix
Create a per-chain failedChan instead of a single shared one. Move the channel creation inside the per-chain loop in CreateManagerWithWorkers:
for _, chainName := range managerCfg.Chains {
// ...
failedChan := make(chan FailedBlockEvent, 100) // per-chain channel
deps := WorkerDeps{
// ...
FailedChan: failedChan,
}
// ...
}Remove the unused failedChan field from Manager (it stores but never reads from it).
Note: A simple filter (if evt.Chain != rw.chain.GetName() { continue }) would NOT work because the channel has multiple consumers — skipped events would be lost instead of reaching the correct chain's rescanner.
Affected Files
internal/worker/factory.go— shared channel creation (line 743)internal/worker/rescanner.go— unfiltered consumption (lines 77-81)internal/worker/manager.go— unusedfailedChanfieldinternal/worker/base.go— event producer (line 146)