eth/catalyst: try to fetch header from network

This commit is contained in:
MariusVanDerWijden 2025-04-23 14:43:48 +02:00
parent d6a91b9da8
commit fc09e939ef
4 changed files with 70 additions and 47 deletions

View file

@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -456,3 +457,7 @@ func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, re
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) { func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec) return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
} }
func (b *EthAPIBackend) Downloader() *downloader.Downloader {
return b.eth.Downloader()
}

View file

@ -337,8 +337,14 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
// that should be fixed, not papered over. // that should be fixed, not papered over.
header := api.remoteBlocks.get(update.HeadBlockHash) header := api.remoteBlocks.get(update.HeadBlockHash)
if header == nil { if header == nil {
log.Warn("Forkchoice requested unknown head", "hash", update.HeadBlockHash) log.Warn("Forkchoice requested unknown head, trying to retrive it from a random peer", "hash", update.HeadBlockHash)
return engine.STATUS_SYNCING, nil retrievedHead, err := api.eth.Downloader().GetHeader(update.HeadBlockHash)
if err != nil {
log.Warn("Could not retrieve unknown head from peers")
return engine.STATUS_SYNCING, nil
}
api.remoteBlocks.put(retrievedHead.Hash(), retrievedHead)
header = retrievedHead
} }
// If the finalized hash is known, we can direct the downloader to move // If the finalized hash is known, we can direct the downloader to move
// potentially more data to the freezer from the get go. // potentially more data to the freezer from the get go.

View file

@ -21,6 +21,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
@ -48,34 +49,42 @@ func (d *Downloader) BeaconDevSync(mode SyncMode, hash common.Hash, stop chan st
return errors.New("stop requested") return errors.New("stop requested")
default: default:
} }
// Pick a random peer to sync from and keep retrying if none are yet header, err := d.GetHeader(hash)
// available due to fresh startup if err != nil {
d.peers.lock.RLock() time.Sleep(time.Second)
var peer *peerConnection continue
for _, peer = range d.peers.peers {
break
} }
d.peers.lock.RUnlock()
if peer == nil { return d.BeaconSync(mode, header, header)
time.Sleep(time.Second)
continue
}
// Found a peer, attempt to retrieve the header whilst blocking and
// retry if it fails for whatever reason
log.Info("Attempting to retrieve sync target", "peer", peer.id)
headers, metas, err := d.fetchHeadersByHash(peer, hash, 1, 0, false)
if err != nil || len(headers) != 1 {
log.Warn("Failed to fetch sync target", "headers", len(headers), "err", err)
time.Sleep(time.Second)
continue
}
// Head header retrieved, if the hash matches, start the actual sync
if metas[0] != hash {
log.Error("Received invalid sync target", "want", hash, "have", metas[0])
time.Sleep(time.Second)
continue
}
return d.BeaconSync(mode, headers[0], headers[0])
} }
} }
// GetHeader tries to retrieve the header with a given hash from a random peer.
func (d *Downloader) GetHeader(hash common.Hash) (*types.Header, error) {
// Pick a random peer to sync from and keep retrying if none are yet
// available due to fresh startup
d.peers.lock.RLock()
var peer *peerConnection
for _, peer = range d.peers.peers {
break
}
d.peers.lock.RUnlock()
if peer == nil {
return nil, errors.New("could not find peer")
}
// Found a peer, attempt to retrieve the header whilst blocking and
// retry if it fails for whatever reason
log.Info("Attempting to retrieve sync target", "peer", peer.id)
headers, metas, err := d.fetchHeadersByHash(peer, hash, 1, 0, false)
if err != nil || len(headers) != 1 {
log.Warn("Failed to fetch sync target", "headers", len(headers), "err", err)
return nil, errors.New("failed to fetch sync target")
}
// Head header retrieved, if the hash matches, start the actual sync
if metas[0] != hash {
log.Error("Received invalid sync target", "want", hash, "have", metas[0])
return nil, errors.New("received invalid sync target")
}
return headers[0], nil
}

View file

@ -38,6 +38,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/gasestimator" "github.com/ethereum/go-ethereum/eth/gasestimator"
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
@ -1700,21 +1701,11 @@ func (api *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs,
// namespace. // namespace.
type DebugAPI struct { type DebugAPI struct {
b Backend b Backend
// contains filtered or unexported fields
} }
// NewDebugAPI creates a new DebugAPI instance. // NewDebugAPI creates a new DebugAPI instance.
func NewDebugAPI(b Backend) *DebugAPI { func NewDebugAPI(b Backend) *DebugAPI {
// Fill defaults based on the chain config return &DebugAPI{b: b}
var gasLimit uint64
if head := b.CurrentBlock(); head != nil {
gasLimit = head.GasLimit
}
// The geth console doesn't allow flags, so we depend on the configured backend.
return &DebugAPI{
b: b,
defaultCallGas: gasLimit,
}
} }
// SetSyncTarget manually sets the target hash for full sync. // SetSyncTarget manually sets the target hash for full sync.
@ -1723,15 +1714,27 @@ func NewDebugAPI(b Backend) *DebugAPI {
func (api *DebugAPI) SetSyncTarget(ctx context.Context, target common.Hash) error { func (api *DebugAPI) SetSyncTarget(ctx context.Context, target common.Hash) error {
// Note: BeaconDevSync might block; running in a goroutine to avoid blocking RPC/console. // Note: BeaconDevSync might block; running in a goroutine to avoid blocking RPC/console.
// Also, passing nil for the cancel channel as we don't provide a way to cancel via API. // Also, passing nil for the cancel channel as we don't provide a way to cancel via API.
go func() { type Downloader interface {
err := api.b.Downloader().BeaconDevSync(ethconfig.FullSync, target, nil) Downloader() *downloader.Downloader
}
backend, ok := api.b.(Downloader)
if !ok {
log.Error("Could not get downloader")
}
// retry 20 times to retrieve the header from random peers
for range 20 {
header, err := backend.Downloader().GetHeader(target)
if err != nil { if err != nil {
log.Warn("debug_setSyncTarget: BeaconDevSync failed", "target", target, "err", err) continue
} else {
log.Info("debug_setSyncTarget: BeaconDevSync initiated", "target", target)
} }
}() if err := backend.Downloader().BeaconSync(ethconfig.FullSync, header, header); err != nil {
return nil // API call returns immediately return err
} else {
// Sync target set successfully, return
return nil
}
}
return errors.New("could not set sync target")
} }
// ChainConfig returns the active chain configuration. // ChainConfig returns the active chain configuration.