github.com/aergoio/aergo@v1.3.1/syncer/blockprocessor.go (about) 1 package syncer 2 3 import ( 4 "bytes" 5 "fmt" 6 "github.com/aergoio/aergo/chain" 7 "github.com/aergoio/aergo/p2p/p2putil" 8 "sort" 9 10 "github.com/aergoio/aergo/internal/enc" 11 "github.com/aergoio/aergo/message" 12 "github.com/aergoio/aergo/pkg/component" 13 "github.com/aergoio/aergo/types" 14 ) 15 16 type BlockProcessor struct { 17 compRequester component.IComponentRequester //for communicate with other service 18 19 blockFetcher *BlockFetcher 20 21 curConnRequest *ConnectTask 22 23 connQueue []*ConnectTask 24 25 prevBlock *types.Block 26 curBlock *types.Block 27 28 targetBlockNo types.BlockNo 29 name string 30 } 31 32 type ConnectTask struct { 33 FromPeer types.PeerID 34 Blocks []*types.Block 35 firstNo types.BlockNo 36 cur int 37 } 38 39 func NewBlockProcessor(compRequester component.IComponentRequester, blockFetcher *BlockFetcher, ancestor *types.Block, 40 targetNo types.BlockNo) *BlockProcessor { 41 return &BlockProcessor{ 42 compRequester: compRequester, 43 blockFetcher: blockFetcher, 44 prevBlock: ancestor, 45 targetBlockNo: targetNo, 46 name: NameBlockProcessor, 47 } 48 } 49 50 func (bproc *BlockProcessor) run(msg interface{}) error { 51 //TODO in test mode, if syncer receives invalid messages, syncer stop with panic() 52 switch msg.(type) { 53 case *message.GetBlockChunksRsp: 54 if err := bproc.GetBlockChunkRsp(msg.(*message.GetBlockChunksRsp)); err != nil { 55 return err 56 } 57 case *message.AddBlockRsp: 58 if err := bproc.AddBlockResponse(msg.(*message.AddBlockRsp)); err != nil { 59 return err 60 } 61 62 chain.TestDebugger.Check(chain.DEBUG_SYNCER_CRASH, 2, nil) 63 default: 64 return fmt.Errorf("invalid msg type:%T", msg) 65 } 66 67 return nil 68 } 69 70 func (bproc *BlockProcessor) isValidResponse(msg interface{}) error { 71 validateBlockChunksRsp := func(msg *message.GetBlockChunksRsp) error { 72 var prev []byte 73 blocks := msg.Blocks 74 75 if msg.Err != nil { 76 logger.Error().Err(msg.Err).Msg("GetBlockChunksRsp has error") 77 return msg.Err 78 } 79 80 if blocks == nil || len(blocks) == 0 { 81 logger.Error().Err(msg.Err).Str("peer", p2putil.ShortForm(msg.ToWhom)).Msg("GetBlockChunksRsp is empty") 82 return &ErrSyncMsg{msg: msg, str: "blocks is empty"} 83 } 84 85 for _, block := range blocks { 86 if prev != nil && !bytes.Equal(prev, block.GetHeader().GetPrevBlockHash()) { 87 logger.Error().Str("peer", p2putil.ShortForm(msg.ToWhom)).Msg("GetBlockChunksRsp hashes inconsistent") 88 return &ErrSyncMsg{msg: msg, str: "blocks hash not matched"} 89 } 90 91 prev = block.GetHash() 92 } 93 return nil 94 } 95 96 validateAddBlockRsp := func(msg *message.AddBlockRsp) error { 97 if msg.Err != nil { 98 return msg.Err 99 } 100 101 if msg.BlockHash == nil { 102 return &ErrSyncMsg{msg: msg, str: "invalid add block resonse"} 103 } 104 105 return nil 106 } 107 108 switch msg.(type) { 109 case *message.GetBlockChunksRsp: 110 if err := validateBlockChunksRsp(msg.(*message.GetBlockChunksRsp)); err != nil { 111 return err 112 } 113 114 case *message.AddBlockRsp: 115 if err := validateAddBlockRsp(msg.(*message.AddBlockRsp)); err != nil { 116 return err 117 } 118 119 default: 120 return fmt.Errorf("invalid msg type:%T", msg) 121 } 122 123 return nil 124 } 125 126 func (bproc *BlockProcessor) GetBlockChunkRsp(msg *message.GetBlockChunksRsp) error { 127 if err := bproc.isValidResponse(msg); err != nil { 128 return bproc.GetBlockChunkRspError(msg, err) 129 } 130 131 bf := bproc.blockFetcher 132 133 logger.Debug().Str("peer", p2putil.ShortForm(msg.ToWhom)).Uint64("startNo", msg.Blocks[0].GetHeader().BlockNo).Int("count", len(msg.Blocks)).Msg("received GetBlockChunkRsp") 134 135 task, err := bf.findFinished(msg, false) 136 if err != nil { 137 //TODO invalid peer 138 logger.Error().Str("peer", p2putil.ShortForm(msg.ToWhom)). 139 Int("count", len(msg.Blocks)). 140 Str("from", enc.ToString(msg.Blocks[0].GetHash())). 141 Str("to", enc.ToString(msg.Blocks[len(msg.Blocks)-1].GetHash())). 142 Msg("dropped unknown block response message") 143 return nil 144 } 145 146 bf.pushFreePeer(task.syncPeer) 147 148 bf.stat.setMaxChunkRsp(msg.Blocks[len(msg.Blocks)-1]) 149 150 bproc.addConnectTask(msg) 151 152 return nil 153 } 154 155 func (bproc *BlockProcessor) GetBlockChunkRspError(msg *message.GetBlockChunksRsp, err error) error { 156 bf := bproc.blockFetcher 157 158 logger.Error().Err(err).Str("peer", p2putil.ShortForm(msg.ToWhom)).Msg("receive GetBlockChunksRsp with error message") 159 160 task, err := bf.findFinished(msg, true) 161 if err != nil { 162 //TODO invalid peer 163 logger.Error().Err(err).Str("peer", p2putil.ShortForm(msg.ToWhom)).Msg("dropped unknown block error message") 164 return nil 165 } 166 167 if err := bf.processFailedTask(task, false); err != nil { 168 return err 169 } 170 171 return nil 172 } 173 174 func (bproc *BlockProcessor) AddBlockResponse(msg *message.AddBlockRsp) error { 175 if err := bproc.isValidResponse(msg); err != nil { 176 logger.Info().Err(err).Uint64("no", msg.BlockNo).Str("hash", enc.ToString(msg.BlockHash)).Msg("block connect failed") 177 return err 178 } 179 180 curBlock := bproc.curBlock 181 curNo := curBlock.GetHeader().BlockNo 182 curHash := curBlock.GetHash() 183 184 if curNo != msg.BlockNo || !bytes.Equal(curHash, msg.BlockHash) { 185 logger.Error().Uint64("curNo", curNo).Uint64("msgNo", msg.BlockNo). 186 Str("curHash", enc.ToString(curHash)).Str("msgHash", enc.ToString(msg.BlockHash)). 187 Msg("invalid add block response") 188 return &ErrSyncMsg{msg: msg, str: "drop unknown add response"} 189 } 190 191 logger.Info().Uint64("no", msg.BlockNo).Str("hash", enc.ToString(msg.BlockHash)).Msg("block connect succeed") 192 193 bproc.blockFetcher.stat.setLastAddBlock(curBlock) 194 195 if curBlock.BlockNo() == bproc.targetBlockNo { 196 logger.Info().Msg("succeed to add last block, request stopping syncer") 197 stopSyncer(bproc.compRequester, bproc.blockFetcher.GetSeq(), bproc.name, nil) 198 } 199 200 bproc.prevBlock = curBlock 201 bproc.curBlock = nil 202 203 block := bproc.getNextBlockToConnect() 204 205 if block != nil { 206 bproc.connectBlock(block) 207 } 208 209 return nil 210 } 211 212 func (bproc *BlockProcessor) addConnectTask(msg *message.GetBlockChunksRsp) { 213 req := &ConnectTask{FromPeer: msg.ToWhom, Blocks: msg.Blocks, firstNo: msg.Blocks[0].GetHeader().BlockNo, cur: 0} 214 215 logger.Debug().Uint64("firstno", req.firstNo).Int("count", len(req.Blocks)).Msg("add connect task to queue") 216 217 bproc.pushToConnQueue(req) 218 219 block := bproc.getNextBlockToConnect() 220 221 if block != nil { 222 bproc.connectBlock(block) 223 } 224 } 225 226 func (bproc *BlockProcessor) getNextBlockToConnect() *types.Block { 227 //already prev request is running, don't request any more 228 if bproc.curBlock != nil { 229 return nil 230 } 231 232 //request next block of current Request 233 if bproc.curConnRequest != nil { 234 req := bproc.curConnRequest 235 req.cur++ 236 237 if req.cur >= len(req.Blocks) { 238 logger.Debug().Msg("current connect task is finished") 239 bproc.curConnRequest = nil 240 } 241 } 242 243 //pop from pending request 244 if bproc.curConnRequest == nil { 245 nextReq := bproc.popFromConnQueue() 246 if nextReq == nil { 247 return nil 248 } 249 250 bproc.curConnRequest = nextReq 251 } 252 253 next := bproc.curConnRequest.cur 254 nextBlock := bproc.curConnRequest.Blocks[next] 255 256 logger.Debug().Uint64("no", nextBlock.GetHeader().BlockNo).Str("hash", nextBlock.ID()). 257 Int("idx in req", next).Msg("next block to connect") 258 259 bproc.curBlock = nextBlock 260 261 return nextBlock 262 } 263 264 func (bproc *BlockProcessor) connectBlock(block *types.Block) { 265 if block == nil { 266 return 267 } 268 269 logger.Info().Uint64("no", block.GetHeader().BlockNo). 270 Str("hash", enc.ToString(block.GetHash())). 271 Msg("request connecting block to chainsvc") 272 273 bproc.compRequester.RequestTo(message.ChainSvc, &message.AddBlock{PeerID: "", Block: block, Bstate: nil, IsSync: true}) 274 } 275 276 func (bproc *BlockProcessor) pushToConnQueue(newReq *ConnectTask) { 277 sortedList := bproc.connQueue 278 279 index := sort.Search(len(sortedList), func(i int) bool { return sortedList[i].firstNo > newReq.firstNo }) 280 sortedList = append(sortedList, &ConnectTask{}) 281 copy(sortedList[index+1:], sortedList[index:]) 282 sortedList[index] = newReq 283 284 bproc.connQueue = sortedList 285 286 logger.Info().Int("len", len(bproc.connQueue)).Uint64("firstno", newReq.firstNo). 287 Str("firstHash", enc.ToString(newReq.Blocks[0].GetHash())). 288 Msg("add new task to connect queue") 289 } 290 291 func (bproc *BlockProcessor) popFromConnQueue() *ConnectTask { 292 sortedList := bproc.connQueue 293 if len(sortedList) == 0 { 294 logger.Debug().Msg("connect queue is empty. so wait new connect task") 295 return nil 296 } 297 298 //check if first task is next block 299 firstReq := sortedList[0] 300 if bproc.prevBlock != nil && 301 firstReq.firstNo != (bproc.prevBlock.BlockNo()+1) { 302 logger.Debug().Uint64("first", firstReq.firstNo).Uint64("prev", bproc.prevBlock.BlockNo()).Msg("next block is not fetched yet") 303 return nil 304 } 305 306 newReq := sortedList[0] 307 sortedList = sortedList[1:] 308 bproc.connQueue = sortedList 309 310 logger.Info().Int("len", len(sortedList)).Uint64("firstno", newReq.firstNo). 311 Str("firstHash", enc.ToString(newReq.Blocks[0].GetHash())). 312 Msg("pop task from connect queue") 313 314 return newReq 315 }