github.com/aergoio/aergo@v1.3.1/syncer/hashfetcher.go (about) 1 package syncer 2 3 import ( 4 "sync" 5 "time" 6 7 "github.com/aergoio/aergo/internal/enc" 8 "github.com/aergoio/aergo/message" 9 "github.com/aergoio/aergo/pkg/component" 10 "github.com/aergoio/aergo/types" 11 "github.com/pkg/errors" 12 ) 13 14 type HashFetcher struct { 15 compRequester component.IComponentRequester //for communicate with other service 16 17 ctx *types.SyncContext 18 19 responseCh chan *message.GetHashesRsp //HashSet response channel (<- Syncer) 20 resultCh chan *HashSet //BlockFetcher input channel (-> BlockFetcher) 21 //HashFetcher can wait in resultCh 22 quitCh chan interface{} 23 24 lastBlockInfo *types.BlockInfo 25 reqCount uint64 26 reqTime time.Time 27 isRequesting bool 28 29 maxHashReq uint64 30 name string 31 32 timeout time.Duration 33 debug bool 34 35 isRunning bool 36 37 waitGroup *sync.WaitGroup 38 } 39 40 type HashSet struct { 41 Count int 42 Hashes []message.BlockHash 43 StartNo types.BlockNo 44 } 45 46 type HashRequest struct { 47 prevInfo *types.BlockInfo 48 count uint64 49 } 50 51 var ( 52 dfltTimeout = time.Second * 180 53 DfltHashReqSize = uint64(1000) 54 ) 55 56 var ( 57 ErrQuitHashFetcher = errors.New("Hashfetcher quit") 58 ErrInvalidHashSet = errors.New("Invalid hash set reply") 59 ErrHashFetcherTimeout = errors.New("HashFetcher response timeout") 60 ) 61 62 func newHashFetcher(ctx *types.SyncContext, compRequester component.IComponentRequester, bfCh chan *HashSet, cfg *SyncerConfig) *HashFetcher { 63 hf := &HashFetcher{ctx: ctx, compRequester: compRequester, name: NameHashFetcher} 64 65 hf.quitCh = make(chan interface{}) 66 hf.responseCh = make(chan *message.GetHashesRsp) 67 68 hf.resultCh = bfCh 69 70 hf.lastBlockInfo = &types.BlockInfo{Hash: ctx.CommonAncestor.GetHash(), No: ctx.CommonAncestor.BlockNo()} 71 72 hf.maxHashReq = cfg.maxHashReqSize 73 74 hf.timeout = dfltTimeout 75 76 return hf 77 } 78 79 func (hf *HashFetcher) setTimeout(timeout time.Duration) { 80 hf.timeout = timeout 81 } 82 83 func (hf *HashFetcher) recover() { 84 85 } 86 func (hf *HashFetcher) Start() { 87 hf.waitGroup = &sync.WaitGroup{} 88 hf.waitGroup.Add(1) 89 90 hf.isRunning = true 91 92 run := func() { 93 defer RecoverSyncer(NameHashFetcher, hf.GetSeq(), hf.compRequester, func() { hf.waitGroup.Done() }) 94 95 logger.Debug().Msg("start hash fetcher") 96 97 timer := time.NewTimer(hf.timeout) 98 99 hf.requestHashSet() 100 101 for { 102 select { 103 case msg, ok := <-hf.responseCh: 104 if !ok { 105 logger.Error().Msg("HashFetcher responseCh is closed. Syncer is stopping now") 106 return 107 } 108 logger.Debug().Msg("process GetHashesRsp") 109 110 timer.Stop() 111 res, err := hf.isValidResponse(msg) 112 if res { 113 HashSet := &HashSet{Count: len(msg.Hashes), Hashes: msg.Hashes, StartNo: msg.PrevInfo.No + 1} 114 115 if err := hf.processHashSet(HashSet); err != nil { 116 //TODO send errmsg to syncer & stop sync 117 logger.Error().Err(err).Msg("error! process hash chunk, HashFetcher exited") 118 if err != ErrQuitHashFetcher { 119 stopSyncer(hf.compRequester, hf.GetSeq(), hf.name, err) 120 } 121 return 122 } 123 124 if hf.isFinished(HashSet) { 125 closeFetcher(hf.compRequester, hf.GetSeq(), hf.name) 126 logger.Info().Msg("HashFetcher finished") 127 return 128 } 129 hf.requestHashSet() 130 } else if err != nil { 131 stopSyncer(hf.compRequester, hf.GetSeq(), hf.name, err) 132 } 133 134 //timer restart 135 timer.Reset(hf.timeout) 136 case <-timer.C: 137 if hf.requestTimeout() { 138 logger.Error().Msg("HashFetcher response timeout.") 139 stopSyncer(hf.compRequester, hf.GetSeq(), hf.name, ErrHashFetcherTimeout) 140 } 141 142 case <-hf.quitCh: 143 logger.Info().Msg("HashFetcher exited") 144 return 145 } 146 } 147 } 148 149 go run() 150 } 151 152 func (hf *HashFetcher) GetSeq() uint64 { 153 return hf.ctx.Seq 154 } 155 156 func (hf *HashFetcher) isFinished(HashSet *HashSet) bool { 157 return (hf.lastBlockInfo.No == hf.ctx.TargetNo) 158 } 159 160 func (hf *HashFetcher) requestTimeout() bool { 161 return hf.isRequesting && time.Now().Sub(hf.reqTime) > hf.timeout 162 } 163 164 func (hf *HashFetcher) requestHashSet() { 165 count := hf.maxHashReq 166 if hf.ctx.TargetNo < hf.lastBlockInfo.No+hf.maxHashReq { 167 count = hf.ctx.TargetNo - hf.lastBlockInfo.No 168 } 169 170 hf.reqCount = count 171 hf.reqTime = time.Now() 172 hf.isRequesting = true 173 174 logger.Debug().Uint64("prev", hf.lastBlockInfo.No).Str("prevhash", enc.ToString(hf.lastBlockInfo.Hash)).Uint64("count", count).Msg("request hashset to peer") 175 176 hf.compRequester.TellTo(message.P2PSvc, &message.GetHashes{Seq: hf.GetSeq(), ToWhom: hf.ctx.PeerID, PrevInfo: hf.lastBlockInfo, Count: count}) 177 } 178 179 func (hf *HashFetcher) processHashSet(hashSet *HashSet) error { 180 //get HashSet reply 181 lastHash := hashSet.Hashes[len(hashSet.Hashes)-1] 182 lastHashNo := hashSet.StartNo + uint64(hashSet.Count) - 1 183 184 if lastHashNo > hf.ctx.TargetNo { 185 logger.Error().Uint64("target", hf.ctx.TargetNo).Uint64("last", lastHashNo).Msg("invalid hashset reponse") 186 return ErrInvalidHashSet 187 } 188 189 hf.lastBlockInfo = &types.BlockInfo{Hash: lastHash, No: lastHashNo} 190 191 //total HashSet in memory can be 3 (network + resultCh + blockFetcher) 192 select { 193 case hf.resultCh <- hashSet: 194 case <-hf.quitCh: 195 logger.Info().Msg("hash fetcher quit while pushing result") 196 return ErrQuitHashFetcher 197 } 198 199 hf.isRequesting = false 200 201 logger.Debug().Uint64("target", hf.ctx.TargetNo).Uint64("start", hashSet.StartNo).Uint64("last", lastHashNo).Int("count", len(hashSet.Hashes)).Msg("push hashset to BlockFetcher") 202 203 return nil 204 } 205 206 func (hf *HashFetcher) stop() { 207 if hf == nil { 208 return 209 } 210 211 if hf.isRunning { 212 logger.Info().Msg("HashFetcher stop#1") 213 214 close(hf.quitCh) 215 logger.Info().Msg("HashFetcher close quitCh") 216 217 close(hf.responseCh) 218 219 hf.waitGroup.Wait() 220 hf.isRunning = false 221 } 222 logger.Info().Msg("HashFetcher stopped") 223 } 224 225 func (hf *HashFetcher) isValidResponse(msg *message.GetHashesRsp) (bool, error) { 226 isValid := true 227 var err error 228 229 if msg == nil { 230 panic("nil message error") 231 } 232 233 if msg.Err != nil { 234 logger.Error().Err(msg.Err).Msg("receive GetHashesRsp with error") 235 err = msg.Err 236 isValid = false 237 } 238 239 if !hf.lastBlockInfo.Equal(msg.PrevInfo) || hf.reqCount != msg.Count { 240 isValid = false 241 } 242 243 if !isValid { 244 logger.Error().Str("req prev", enc.ToString(hf.lastBlockInfo.Hash)). 245 Str("msg prev", enc.ToString(msg.PrevInfo.Hash)). 246 Uint64("req count", hf.reqCount). 247 Uint64("msg count", msg.Count). 248 Msg("invalid GetHashesRsp") 249 return false, err 250 } 251 252 return true, nil 253 } 254 255 func (hf *HashFetcher) GetHahsesRsp(msg *message.GetHashesRsp) { 256 if hf == nil { 257 return 258 } 259 260 count := len(msg.Hashes) 261 262 if count == 0 { 263 logger.Error().Int("count", count). 264 Uint64("prev", msg.PrevInfo.No).Msg("receive empty GetHashesRsp") 265 return 266 } 267 268 logger.Debug().Int("count", count). 269 Uint64("prev", msg.PrevInfo.No). 270 Str("start", enc.ToString(msg.Hashes[0])). 271 Str("end", enc.ToString(msg.Hashes[count-1])).Msg("receive GetHashesRsp") 272 273 hf.responseCh <- msg 274 return 275 }