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  }