github.com/aergoio/aergo@v1.3.1/p2p/blkreceiver.go (about) 1 /* 2 * @file 3 * @copyright defined in aergo/LICENSE.txt 4 */ 5 6 package p2p 7 8 import ( 9 "bytes" 10 "time" 11 12 "github.com/aergoio/aergo/chain" 13 14 "github.com/aergoio/aergo/message" 15 "github.com/aergoio/aergo/p2p/p2pcommon" 16 "github.com/aergoio/aergo/types" 17 ) 18 19 // BlocksChunkReceiver is send p2p getBlocksRequest to target peer and receive p2p responses till all requests blocks are received 20 // It will send response actor message if all blocks are received or failed to receive, but not send response if timeout expired, since 21 // syncer actor already dropped wait before. 22 type BlocksChunkReceiver struct { 23 syncerSeq uint64 24 requestID p2pcommon.MsgID 25 26 peer p2pcommon.RemotePeer 27 actor p2pcommon.ActorService 28 29 blockHashes []message.BlockHash 30 timeout time.Time 31 finished bool 32 status receiverStatus 33 34 got []*types.Block 35 offset int 36 senderFinished chan interface{} 37 } 38 39 type receiverStatus int32 40 41 const ( 42 receiverStatusWaiting receiverStatus = iota 43 receiverStatusCanceled 44 receiverStatusFinished 45 ) 46 47 func NewBlockReceiver(actor p2pcommon.ActorService, peer p2pcommon.RemotePeer, seq uint64, blockHashes []message.BlockHash, ttl time.Duration) *BlocksChunkReceiver { 48 timeout := time.Now().Add(ttl) 49 return &BlocksChunkReceiver{syncerSeq: seq, actor: actor, peer: peer, blockHashes: blockHashes, timeout: timeout, got: make([]*types.Block, len(blockHashes))} 50 } 51 52 func (br *BlocksChunkReceiver) StartGet() { 53 hashes := make([][]byte, len(br.blockHashes)) 54 for i, hash := range br.blockHashes { 55 hashes[i] = ([]byte)(hash) 56 } 57 // create message data 58 req := &types.GetBlockRequest{Hashes: hashes} 59 mo := br.peer.MF().NewMsgBlockRequestOrder(br.ReceiveResp, p2pcommon.GetBlocksRequest, req) 60 br.peer.SendMessage(mo) 61 br.requestID = mo.GetMsgID() 62 } 63 64 // ReceiveResp must be called just in read go routine 65 func (br *BlocksChunkReceiver) ReceiveResp(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) (ret bool) { 66 // cases in waiting 67 // normal not status => wait 68 // normal status (last response) => finish 69 // abnormal resp (no following resp expected): hasNext is true => cancel 70 // abnormal resp (following resp expected): hasNext is false, or invalid resp data type (maybe remote peer is totally broken) => cancel finish 71 // case in status or status 72 ret = true 73 switch br.status { 74 case receiverStatusWaiting: 75 br.handleInWaiting(msg, msgBody) 76 case receiverStatusCanceled: 77 br.ignoreMsg(msg, msgBody) 78 return 79 case receiverStatusFinished: 80 fallthrough 81 default: 82 return 83 } 84 return 85 } 86 87 func (br *BlocksChunkReceiver) handleInWaiting(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) { 88 // consuming request id when timeout, no more resp expected (i.e. hasNext == false ) or malformed body. 89 // timeout 90 if br.timeout.Before(time.Now()) { 91 // silently ignore already status job 92 br.finishReceiver() 93 return 94 } 95 // malformed responses means that later responses will be also malformed.. 96 respBody, ok := msgBody.(types.ResponseMessage) 97 if !ok || respBody.GetStatus() != types.ResultStatus_OK { 98 br.cancelReceiving(message.RemotePeerFailError, false) 99 return 100 } 101 // remote peer response malformed data. 102 body, ok := msgBody.(*types.GetBlockResponse) 103 if !ok || len(body.Blocks) == 0 { 104 br.cancelReceiving(message.MissingHashError, false) 105 return 106 } 107 108 // add to Got 109 for _, block := range body.Blocks { 110 // It also error that response has more blocks than expected(=requested). 111 if br.offset >= len(br.got) { 112 br.cancelReceiving(message.TooManyBlocksError, body.HasNext) 113 return 114 } 115 // unexpected block 116 if !bytes.Equal(br.blockHashes[br.offset], block.Hash) { 117 br.cancelReceiving(message.UnexpectedBlockError, body.HasNext) 118 return 119 } 120 if block.Size() > int(chain.MaxBlockSize()) { 121 br.cancelReceiving(message.TooBigBlockError, body.HasNext) 122 return 123 } 124 br.got[br.offset] = block 125 br.offset++ 126 } 127 // remote peer hopefully sent last chunk 128 if !body.HasNext { 129 if br.offset < len(br.got) { 130 // not all blocks were filled. this is error 131 br.cancelReceiving(message.TooFewBlocksError, body.HasNext) 132 } else { 133 br.actor.TellRequest(message.SyncerSvc, &message.GetBlockChunksRsp{Seq: br.syncerSeq, ToWhom: br.peer.ID(), Blocks: br.got, Err: nil}) 134 br.finishReceiver() 135 } 136 } 137 return 138 } 139 140 // cancelReceiving is cancel wait for receiving and send syncer the failure result. 141 // not all part of response is received, it wait remaining (and useless) response. It is assumed cancelling is not frequently occur 142 func (br *BlocksChunkReceiver) cancelReceiving(err error, hasNext bool) { 143 br.status = receiverStatusCanceled 144 br.actor.TellRequest(message.SyncerSvc, 145 &message.GetBlockChunksRsp{Seq: br.syncerSeq, ToWhom: br.peer.ID(), Err: err}) 146 147 // check time again. since negative duration of timer will not fire channel. 148 interval := br.timeout.Sub(time.Now()) 149 if !hasNext || interval <= 0 { 150 // if remote peer will not send partial response anymore. it it actually same as finish. 151 br.finishReceiver() 152 } else { 153 // canceling in the middle of responses 154 br.senderFinished = make(chan interface{}) 155 go func() { 156 timer := time.NewTimer(interval) 157 select { 158 case <-timer.C: 159 break 160 case <-br.senderFinished: 161 break 162 } 163 br.peer.ConsumeRequest(br.requestID) 164 }() 165 } 166 } 167 168 // finishReceiver is to cancel works, assuming cancellations are not frequently occur 169 func (br *BlocksChunkReceiver) finishReceiver() { 170 br.status = receiverStatusFinished 171 br.peer.ConsumeRequest(br.requestID) 172 } 173 174 // ignoreMsg is silently ignore following responses, which is not useless anymore. 175 func (br *BlocksChunkReceiver) ignoreMsg(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) { 176 body, ok := msgBody.(*types.GetBlockResponse) 177 if !ok { 178 return 179 } 180 if !body.HasNext { 181 // really status from remote peer 182 select { 183 case br.senderFinished <- struct{}{}: 184 default: 185 } 186 } 187 }