diff --git a/eth/api_backend.go b/eth/api_backend.go index 182c081d2b..4885ca1540 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" "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/tracers" "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) { return b.eth.stateAtTransaction(ctx, block, txIndex, reexec) } + +func (b *EthAPIBackend) Downloader() *downloader.Downloader { + return b.eth.Downloader() +} diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index e6f29c970b..6bd43f26d8 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -337,8 +337,14 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl // that should be fixed, not papered over. header := api.remoteBlocks.get(update.HeadBlockHash) if header == nil { - log.Warn("Forkchoice requested unknown head", "hash", update.HeadBlockHash) - return engine.STATUS_SYNCING, nil + log.Warn("Forkchoice requested unknown head, trying to retrive it from a random peer", "hash", update.HeadBlockHash) + 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 // potentially more data to the freezer from the get go. diff --git a/eth/downloader/beacondevsync.go b/eth/downloader/beacondevsync.go index 9a38fedd46..7571228369 100644 --- a/eth/downloader/beacondevsync.go +++ b/eth/downloader/beacondevsync.go @@ -21,6 +21,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "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") default: } - // 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 + header, err := d.GetHeader(hash) + if err != nil { + time.Sleep(time.Second) + continue } - d.peers.lock.RUnlock() - if peer == nil { - 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]) + return d.BeaconSync(mode, header, header) } } + +// 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 +} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index b49c22eed8..e202aea298 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -38,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "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/gasestimator" "github.com/ethereum/go-ethereum/eth/tracers/logger" @@ -1700,21 +1701,11 @@ func (api *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, // namespace. type DebugAPI struct { b Backend - // contains filtered or unexported fields } // NewDebugAPI creates a new DebugAPI instance. func NewDebugAPI(b Backend) *DebugAPI { - // Fill defaults based on the chain config - 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, - } + return &DebugAPI{b: b} } // 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 { // 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. - go func() { - err := api.b.Downloader().BeaconDevSync(ethconfig.FullSync, target, nil) + type Downloader interface { + 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 { - log.Warn("debug_setSyncTarget: BeaconDevSync failed", "target", target, "err", err) - } else { - log.Info("debug_setSyncTarget: BeaconDevSync initiated", "target", target) + continue } - }() - return nil // API call returns immediately + if err := backend.Downloader().BeaconSync(ethconfig.FullSync, header, header); err != nil { + return err + } else { + // Sync target set successfully, return + return nil + } + } + return errors.New("could not set sync target") } // ChainConfig returns the active chain configuration.