forked from algorand/go-algorand
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnetworkFetcher.go
134 lines (122 loc) · 5.58 KB
/
networkFetcher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (C) 2019-2022 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
package catchup
import (
"context"
"errors"
"fmt"
"time"
"github.com/algorand/go-algorand/agreement"
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/network"
)
// NetworkFetcher is the struct used to export fetchBlock function from universalFetcher
type NetworkFetcher struct {
log logging.Logger
cfg config.Local
auth BlockAuthenticator
peerSelector *peerSelector
fetcher *universalBlockFetcher
}
// MakeNetworkFetcher initializes a NetworkFetcher service
func MakeNetworkFetcher(log logging.Logger, net network.GossipNode, cfg config.Local, auth BlockAuthenticator, pipelineFetch bool) *NetworkFetcher {
netFetcher := &NetworkFetcher{
log: log,
cfg: cfg,
auth: auth,
peerSelector: createPeerSelector(net, cfg, pipelineFetch),
fetcher: makeUniversalBlockFetcher(log, net, cfg),
}
return netFetcher
}
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, 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)
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")
netFetcher.peerSelector.rankPeer(psp, peerRankInvalidDownload)
}
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++ {
httpPeer, psp, err := netFetcher.getHTTPPeer()
if err != nil {
return nil, nil, time.Duration(0), err
}
blk, cert, downloadDuration, err := netFetcher.fetcher.fetchBlock(ctx, round, httpPeer)
if err != nil {
if ctx.Err() != nil {
// caller of the function decided to cancel the download
return nil, nil, time.Duration(0), err
}
netFetcher.log.Infof("FetchBlock: failed to download block %d on attempt %d out of %d. %v",
round, retryCount+1, netFetcher.cfg.CatchupBlockDownloadRetryAttempts, err)
netFetcher.peerSelector.rankPeer(psp, peerRankDownloadFailed)
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+1, netFetcher.cfg.CatchupBlockDownloadRetryAttempts, 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 authenticatation failed for block %d on attempt %d out of %d. %v",
round, retryCount+1, netFetcher.cfg.CatchupBlockDownloadRetryAttempts, err)
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
}