github.com/aergoio/aergo@v1.3.1/p2p/subproto/getblock.go (about) 1 /* 2 * @file 3 * @copyright defined in aergo/LICENSE.txt 4 */ 5 6 package subproto 7 8 import ( 9 "time" 10 11 "github.com/aergoio/aergo-lib/log" 12 "github.com/aergoio/aergo/internal/enc" 13 "github.com/aergoio/aergo/p2p/p2pcommon" 14 "github.com/aergoio/aergo/p2p/p2putil" 15 "github.com/aergoio/aergo/types" 16 "github.com/golang/protobuf/proto" 17 ) 18 19 type blockRequestHandler struct { 20 BaseMsgHandler 21 asyncHelper 22 } 23 24 var _ p2pcommon.MessageHandler = (*blockRequestHandler)(nil) 25 26 type blockResponseHandler struct { 27 BaseMsgHandler 28 } 29 30 var _ p2pcommon.MessageHandler = (*blockResponseHandler)(nil) 31 32 // newBlockReqHandler creates handler for GetBlockRequest 33 func NewBlockReqHandler(pm p2pcommon.PeerManager, peer p2pcommon.RemotePeer, logger *log.Logger, actor p2pcommon.ActorService) *blockRequestHandler { 34 bh := &blockRequestHandler{BaseMsgHandler: BaseMsgHandler{protocol: p2pcommon.GetBlocksRequest, pm: pm, peer: peer, actor: actor, logger: logger}, asyncHelper: newAsyncHelper()} 35 36 return bh 37 } 38 39 func (bh *blockRequestHandler) ParsePayload(rawbytes []byte) (p2pcommon.MessageBody, error) { 40 return p2putil.UnmarshalAndReturn(rawbytes, &types.GetBlockRequest{}) 41 } 42 43 const ( 44 EmptyGetBlockResponseSize = 12 // roughly estimated maximum size if element is full 45 ) 46 47 func (bh *blockRequestHandler) Handle(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) { 48 remotePeer := bh.peer 49 data := msgBody.(*types.GetBlockRequest) 50 p2putil.DebugLogReceive(bh.logger, bh.protocol, msg.ID().String(), remotePeer, data) 51 if bh.issue() { 52 go bh.handleBlkReq(msg, data) 53 } else { 54 bh.logger.Info().Str(p2putil.LogProtoID,bh.protocol.String()).Str(p2putil.LogMsgID,msg.ID().String()).Str(p2putil.LogPeerName, remotePeer.Name()).Msg("return error for busy") 55 resp := &types.GetBlockResponse{ 56 Status: types.ResultStatus_RESOURCE_EXHAUSTED, 57 Blocks: nil, HasNext: false} 58 59 remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetBlocksResponse, resp)) 60 } 61 } 62 63 func (bh *blockRequestHandler) handleBlkReq(msg p2pcommon.Message, data *types.GetBlockRequest) { 64 defer bh.release() 65 remotePeer := bh.peer 66 67 requestID := msg.ID() 68 sliceCap := p2pcommon.MaxBlockResponseCount 69 if len(data.Hashes) < sliceCap { 70 sliceCap = len(data.Hashes) 71 } 72 73 defaultMsgTimeout := time.Second * 30 74 // find block info from chainservice 75 idx := 0 76 msgSentCount := 0 77 status := types.ResultStatus_OK 78 blockInfos := make([]*types.Block, 0, sliceCap) 79 payloadSize := EmptyGetBlockResponseSize 80 var blockSize, fieldSize int 81 for _, hash := range data.Hashes { 82 foundBlock, err := bh.actor.GetChainAccessor().GetBlock(hash) 83 if err != nil { 84 // the block hash from request must exists. this error is fatal. 85 bh.logger.Warn().Err(err).Str(p2putil.LogBlkHash, enc.ToString(hash)).Str(p2putil.LogOrgReqID, requestID.String()).Msg("failed to get block while processing getBlock") 86 status = types.ResultStatus_INTERNAL 87 break 88 } 89 if foundBlock == nil { 90 // the remote peer request not existing block 91 bh.logger.Debug().Str(p2putil.LogBlkHash, enc.ToString(hash)).Str(p2putil.LogOrgReqID, requestID.String()).Msg("requested block hash is missing") 92 status = types.ResultStatus_NOT_FOUND 93 break 94 95 } 96 blockSize = proto.Size(foundBlock) 97 fieldSize = blockSize + p2putil.CalculateFieldDescSize(blockSize) 98 if len(blockInfos) >= sliceCap || (payloadSize+fieldSize) > p2pcommon.MaxPayloadLength { 99 msgSentCount++ 100 // send partial list 101 resp := &types.GetBlockResponse{ 102 Status: status, 103 Blocks: blockInfos, 104 HasNext: true, 105 //HasNext:msgSentCount<MaxResponseSplitCount, // always have nextItem ( see foundBlock) but msg count limit will affect 106 } 107 bh.logger.Debug().Uint64("first_blk_number", blockInfos[0].Header.GetBlockNo()).Int(p2putil.LogBlkCount, len(blockInfos)).Str(p2putil.LogOrgReqID, requestID.String()).Msg("Sending partial getBlock response") 108 err := remotePeer.SendAndWaitMessage(remotePeer.MF().NewMsgResponseOrder(requestID, p2pcommon.GetBlocksResponse, resp), defaultMsgTimeout) 109 if err != nil { 110 bh.logger.Info().Uint64("first_blk_number", blockInfos[0].Header.GetBlockNo()).Err(err).Int(p2putil.LogBlkCount, len(blockInfos)).Str(p2putil.LogOrgReqID, requestID.String()).Msg("Sending failed") 111 return 112 } 113 blockInfos = make([]*types.Block, 0, sliceCap) 114 payloadSize = EmptyGetBlockResponseSize 115 } 116 blockInfos = append(blockInfos, foundBlock) 117 payloadSize += fieldSize 118 idx++ 119 } 120 121 if 0 == idx { 122 status = types.ResultStatus_NOT_FOUND 123 } 124 // Failed response does not need incomplete blocks information 125 if status != types.ResultStatus_OK { 126 blockInfos = blockInfos[:0] 127 } 128 // generate response message 129 resp := &types.GetBlockResponse{ 130 Status: status, 131 Blocks: blockInfos, HasNext: false} 132 133 // ???: have to check arguments 134 bh.logger.Debug().Int(p2putil.LogBlkCount, len(blockInfos)).Str(p2putil.LogOrgReqID, requestID.String()).Msg("Sending last part of getBlock response") 135 err := remotePeer.SendAndWaitMessage(remotePeer.MF().NewMsgResponseOrder(requestID, p2pcommon.GetBlocksResponse, resp), defaultMsgTimeout) 136 if err != nil { 137 bh.logger.Info().Int(p2putil.LogBlkCount, len(data.Hashes)).Err(err).Str(p2putil.LogOrgReqID, requestID.String()).Msg("Sending failed") 138 return 139 } 140 } 141 142 // newBlockRespHandler creates handler for GetBlockResponse 143 func NewBlockRespHandler(pm p2pcommon.PeerManager, peer p2pcommon.RemotePeer, logger *log.Logger, actor p2pcommon.ActorService, sm p2pcommon.SyncManager) *blockResponseHandler { 144 bh := &blockResponseHandler{BaseMsgHandler{protocol: p2pcommon.GetBlocksResponse, pm: pm, sm: sm, peer: peer, actor: actor, logger: logger}} 145 return bh 146 } 147 148 func (bh *blockResponseHandler) ParsePayload(rawbytes []byte) (p2pcommon.MessageBody, error) { 149 return p2putil.UnmarshalAndReturn(rawbytes, &types.GetBlockResponse{}) 150 } 151 152 func (bh *blockResponseHandler) Handle(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) { 153 remotePeer := bh.peer 154 data := msgBody.(*types.GetBlockResponse) 155 if bh.logger.IsDebugEnabled() { 156 p2putil.DebugLogReceiveResponse(bh.logger, bh.protocol, msg.ID().String(), msg.OriginalID().String(), remotePeer, data) 157 } 158 159 // locate request data and remove it if found 160 if !remotePeer.GetReceiver(msg.OriginalID())(msg, data) { 161 remotePeer.ConsumeRequest(msg.OriginalID()) 162 // TODO temporary code and will be deleted after newer syncer is made. 163 if data.Status != types.ResultStatus_OK || len(data.Blocks) == 0 { 164 return 165 } 166 bh.sm.HandleGetBlockResponse(remotePeer, msg, data) 167 } 168 }