Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catchup: NetworkFetcher service to export fetchBlock #4388

Merged
merged 14 commits into from
Aug 18, 2022
Merged
60 changes: 39 additions & 21 deletions catchup/networkFetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,54 +35,56 @@ type NetworkFetcher struct {
log logging.Logger
net network.GossipNode
cfg config.Local
auth BlockAuthenticator
peerSelector *peerSelector
}

// MakeNetworkFetcher initializes a NetworkFetcher service
func MakeNetworkFetcher(log logging.Logger, net network.GossipNode, cfg config.Local, pipelineFetch bool) *NetworkFetcher {
func MakeNetworkFetcher(log logging.Logger, net network.GossipNode, cfg config.Local, auth BlockAuthenticator, pipelineFetch bool) *NetworkFetcher {
netFetcher := &NetworkFetcher{
log: log,
net: net,
cfg: cfg,
auth: auth,
peerSelector: createPeerSelector(net, cfg, pipelineFetch),
}
return netFetcher
}

func (netFetcher *NetworkFetcher) getHTTPPeer(count int) (network.HTTPPeer, *peerSelectorPeer, int, error) {
for retryCount := count; retryCount < netFetcher.cfg.CatchupBlockDownloadRetryAttempts; retryCount++ {
func (netFetcher *NetworkFetcher) getHTTPPeer() (network.HTTPPeer, *peerSelectorPeer, error) {
for retryCount := 0; retryCount < netFetcher.cfg.CatchupBlockDownloadRetryAttempts; retryCount++ {
psp, err := netFetcher.peerSelector.getNextPeer()
if err != nil {
if err != errPeerSelectorNoPeerPoolsAvailable {
err = fmt.Errorf("FetchBlock: unable to obtain a list of peers to download the block from : %w", err)
return nil, nil, retryCount, err
return nil, nil, err
}
// this is a possible on startup, since the network package might have yet to retrieve the list of peers.
netFetcher.log.Infof("FetchBlock: unable to obtain a list of peers to download the block from; will retry shortly.")
time.Sleep(noPeersAvailableSleepInterval)
} else {
peer := psp.Peer
httpPeer, ok := peer.(network.HTTPPeer)
if ok {
return httpPeer, psp, retryCount, nil
}
netFetcher.log.Warnf("FetchBlock: non-HTTP peer was provided by the peer selector")
netFetcher.peerSelector.rankPeer(psp, peerRankInvalidDownload)
continue
}
peer := psp.Peer
httpPeer, ok := peer.(network.HTTPPeer)
if ok {
return httpPeer, psp, nil
}
netFetcher.log.Warnf("FetchBlock: non-HTTP peer was provided by the peer selector")
algonautshant marked this conversation as resolved.
Show resolved Hide resolved
netFetcher.peerSelector.rankPeer(psp, peerRankInvalidDownload)
}
return nil, nil, netFetcher.cfg.CatchupBlockDownloadRetryAttempts, errors.New("FetchBlock: recurring non-HTTP peer was provided by the peer selector")
return nil, nil, errors.New("FetchBlock: recurring non-HTTP peer was provided by the peer selector")
}

// FetchBlock function given a round number returns a block from a http peer
func (netFetcher *NetworkFetcher) FetchBlock(ctx context.Context, round basics.Round) (*bookkeeping.Block,
*agreement.Certificate, time.Duration, error) {
// internal retry attempt to fetch the block
for retryCount := 0; retryCount < netFetcher.cfg.CatchupBlockDownloadRetryAttempts; retryCount++ {
// keep retrying until a valid http peer is selected by the peerSelector
httpPeer, psp, retryCount, err := netFetcher.getHTTPPeer(retryCount)
httpPeer, psp, err := netFetcher.getHTTPPeer()
if err != nil {
return nil, nil, time.Duration(0), err
}

fetcher := makeUniversalBlockFetcher(netFetcher.log, netFetcher.net, netFetcher.cfg)
blk, cert, downloadDuration, err := fetcher.fetchBlock(ctx, round, httpPeer)
if err != nil {
Expand All @@ -92,20 +94,36 @@ func (netFetcher *NetworkFetcher) FetchBlock(ctx context.Context, round basics.R
}
netFetcher.log.Infof("FetchBlock: failed to download block %d on attempt %d out of %d. %v", round, retryCount, netFetcher.cfg.CatchupBlockDownloadRetryAttempts, err)
netFetcher.peerSelector.rankPeer(psp, peerRankDownloadFailed)
} else if !blk.ContentsMatchHeader() && blk.Round() > 0 {
continue // retry the fetch
}

// Check that the block's contents match the block header
if !blk.ContentsMatchHeader() && blk.Round() > 0 {
netFetcher.peerSelector.rankPeer(psp, peerRankInvalidDownload)
// Check if this mismatch is due to an unsupported protocol version
if _, ok := config.Consensus[blk.BlockHeader.CurrentProtocol]; !ok {
netFetcher.log.Errorf("FetchBlock: downloaded block(%v) unsupported protocol version detected: '%v'", round, blk.BlockHeader.CurrentProtocol)
}
netFetcher.log.Warnf("FetchBlock: downloaded block(%v) contents do not match header", round)
netFetcher.log.Infof("FetchBlock: failed to download block %d on attempt %d out of %d. %v", round, retryCount, netFetcher.cfg.CatchupBlockDownloadRetryAttempts, err)
} else {
// upon successful download rank the peer according to the download speed
peerRank := netFetcher.peerSelector.peerDownloadDurationToRank(psp, downloadDuration)
netFetcher.peerSelector.rankPeer(psp, peerRank)
return blk, cert, downloadDuration, err
continue // retry the fetch
}

// Authenticate the block. for correct execution, caller should call FetchBlock only when the lookback block is available
if netFetcher.cfg.CatchupVerifyCertificate() {
err = netFetcher.auth.Authenticate(blk, cert)
if err != nil {
netFetcher.log.Warnf("FetchBlock: cert did not authenticate block %d on attempt %d out of %d. %v", round, retryCount, netFetcher.cfg.CatchupBlockDownloadRetryAttempts, err)
algonautshant marked this conversation as resolved.
Show resolved Hide resolved
algonautshant marked this conversation as resolved.
Show resolved Hide resolved
netFetcher.peerSelector.rankPeer(psp, peerRankInvalidDownload)
continue // retry the fetch
}
}

// upon successful download rank the peer according to the download speed
peerRank := netFetcher.peerSelector.peerDownloadDurationToRank(psp, downloadDuration)
netFetcher.peerSelector.rankPeer(psp, peerRank)
return blk, cert, downloadDuration, err

}
err := fmt.Errorf("FetchBlock failed after multiple blocks download attempts: %v unsuccessful attempts", netFetcher.cfg.CatchupBlockDownloadRetryAttempts)
return nil, nil, time.Duration(0), err
Expand Down
4 changes: 3 additions & 1 deletion catchup/networkFetcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ func TestFetchBlock(t *testing.T) {

net.addPeer(rootURL)

fetcher := MakeNetworkFetcher(logging.TestingLog(t), net, cfg, false)
// Disable block authentication
cfg.CatchupBlockValidateMode = 1
fetcher := MakeNetworkFetcher(logging.TestingLog(t), net, cfg, nil, false)

var block *bookkeeping.Block
var cert *agreement.Certificate
Expand Down