github.com/aergoio/aergo@v1.3.1/p2p/subproto/blockhash.go (about)

     1  /*
     2   * @file
     3   * @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package subproto
     7  
     8  import (
     9  	"bytes"
    10  	"github.com/aergoio/aergo-lib/log"
    11  	"github.com/aergoio/aergo/p2p/p2pcommon"
    12  	"github.com/aergoio/aergo/p2p/p2putil"
    13  	"github.com/aergoio/aergo/types"
    14  )
    15  
    16  type getHashRequestHandler struct {
    17  	BaseMsgHandler
    18  }
    19  
    20  type getHashResponseHandler struct {
    21  	BaseMsgHandler
    22  }
    23  
    24  // newBlockReqHandler creates handler for GetBlockRequest
    25  func NewGetHashesReqHandler(pm p2pcommon.PeerManager, peer p2pcommon.RemotePeer, logger *log.Logger, actor p2pcommon.ActorService) *getHashRequestHandler {
    26  	bh := &getHashRequestHandler{BaseMsgHandler: BaseMsgHandler{protocol: p2pcommon.GetHashesRequest, pm: pm, peer: peer, actor: actor, logger: logger}}
    27  
    28  	return bh
    29  }
    30  
    31  func (bh *getHashRequestHandler) ParsePayload(rawbytes []byte) (p2pcommon.MessageBody, error) {
    32  	return p2putil.UnmarshalAndReturn(rawbytes, &types.GetHashesRequest{})
    33  }
    34  
    35  func (bh *getHashRequestHandler) Handle(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) {
    36  	remotePeer := bh.peer
    37  	data := msgBody.(*types.GetHashesRequest)
    38  	p2putil.DebugLogReceive(bh.logger, bh.protocol, msg.ID().String(), remotePeer, data)
    39  	chainAccessor := bh.actor.GetChainAccessor()
    40  
    41  	// check if requested too many hashes
    42  	if data.Size > p2pcommon.MaxBlockResponseCount {
    43  		resp := &types.GetHashesResponse{Status: types.ResultStatus_INVALID_ARGUMENT}
    44  		remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp))
    45  		return
    46  	}
    47  	// check if remote peer has valid chain,
    48  	// TODO also check if found prevBlock is on main chain or side chain, assume in main chain for now.
    49  	prevHash, err := chainAccessor.GetHashByNo(data.PrevNumber)
    50  	if err != nil || !bytes.Equal(prevHash, data.PrevHash) {
    51  		resp := &types.GetHashesResponse{Status: types.ResultStatus_INVALID_ARGUMENT}
    52  		remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp))
    53  		return
    54  	}
    55  	// decide total fetched size
    56  	bestBlock, err := bh.actor.GetChainAccessor().GetBestBlock()
    57  	if err != nil {
    58  		resp := &types.GetHashesResponse{Status: types.ResultStatus_INTERNAL}
    59  		remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp))
    60  		return
    61  	}
    62  	startNumber, endNumber, fetchSize := determineFetchSize(data.PrevNumber, bestBlock.Header.BlockNo, int(data.Size))
    63  	if fetchSize <= 0 {
    64  		resp := &types.GetHashesResponse{Status: types.ResultStatus_INTERNAL}
    65  		remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp))
    66  		return
    67  	}
    68  	hashes := make([][]byte, fetchSize)
    69  	cursorNo := endNumber
    70  	for i := fetchSize - 1; i >= 0; i-- {
    71  		hash, err := bh.actor.GetChainAccessor().GetHashByNo(cursorNo)
    72  		if err != nil {
    73  			resp := &types.GetHashesResponse{Status: types.ResultStatus_INTERNAL}
    74  			remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp))
    75  			return
    76  		}
    77  		hashes[i] = hash
    78  		cursorNo--
    79  	}
    80  	// check again if data is changed during fetch
    81  	// check if reorg (or such like it) occurred and mainchain is changed during
    82  	endHash, err := chainAccessor.GetHashByNo(endNumber)
    83  	if err != nil || !bytes.Equal(endHash, hashes[fetchSize-1]) {
    84  		resp := &types.GetHashesResponse{Status: types.ResultStatus_INTERNAL}
    85  		remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp))
    86  		return
    87  	}
    88  	startBlock, err := chainAccessor.GetBlock(hashes[0])
    89  	if err != nil || !bytes.Equal(startBlock.Header.PrevBlockHash, prevHash) || startBlock.Header.BlockNo != startNumber {
    90  		resp := &types.GetHashesResponse{Status: types.ResultStatus_INTERNAL}
    91  		remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp))
    92  		return
    93  	}
    94  
    95  	// generate response message
    96  	resp := &types.GetHashesResponse{
    97  		Hashes:  hashes,
    98  		Status:  types.ResultStatus_OK,
    99  		HasNext: false,
   100  	}
   101  	remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashesResponse, resp))
   102  }
   103  
   104  func determineFetchSize(prevNum, currentLast types.BlockNo, maxSize int) (types.BlockNo, types.BlockNo, int) {
   105  	if currentLast <= prevNum {
   106  		return 0, 0, -1
   107  	}
   108  	fetchSize := int(currentLast - prevNum)
   109  	if fetchSize > maxSize {
   110  		fetchSize = maxSize
   111  	}
   112  
   113  	return prevNum + 1, prevNum + types.BlockNo(fetchSize), fetchSize
   114  }
   115  
   116  // newBlockReqHandler creates handler for GetBlockRequest
   117  func NewGetHashesRespHandler(pm p2pcommon.PeerManager, peer p2pcommon.RemotePeer, logger *log.Logger, actor p2pcommon.ActorService) *getHashResponseHandler {
   118  	bh := &getHashResponseHandler{BaseMsgHandler: BaseMsgHandler{protocol: p2pcommon.GetHashesResponse, pm: pm, peer: peer, actor: actor, logger: logger}}
   119  
   120  	return bh
   121  }
   122  
   123  func (bh *getHashResponseHandler) ParsePayload(rawbytes []byte) (p2pcommon.MessageBody, error) {
   124  	return p2putil.UnmarshalAndReturn(rawbytes, &types.GetHashesResponse{})
   125  }
   126  
   127  func (bh *getHashResponseHandler) Handle(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) {
   128  	remotePeer := bh.peer
   129  	data := msgBody.(*types.GetHashesResponse)
   130  	p2putil.DebugLogReceiveResponse(bh.logger, bh.protocol, msg.ID().String(), msg.OriginalID().String(), bh.peer, data)
   131  
   132  	// locate request data and remove it if found
   133  	remotePeer.GetReceiver(msg.OriginalID())(msg, data)
   134  }
   135  
   136  type getHashByNoRequestHandler struct {
   137  	BaseMsgHandler
   138  }
   139  
   140  type getHashByNoResponseHandler struct {
   141  	BaseMsgHandler
   142  }
   143  
   144  // newBlockReqHandler creates handler for GetBlockRequest
   145  func NewGetHashByNoReqHandler(pm p2pcommon.PeerManager, peer p2pcommon.RemotePeer, logger *log.Logger, actor p2pcommon.ActorService) *getHashByNoRequestHandler {
   146  	bh := &getHashByNoRequestHandler{BaseMsgHandler: BaseMsgHandler{protocol: p2pcommon.GetHashByNoRequest, pm: pm, peer: peer, actor: actor, logger: logger}}
   147  
   148  	return bh
   149  }
   150  
   151  func (bh *getHashByNoRequestHandler) ParsePayload(rawbytes []byte) (p2pcommon.MessageBody, error) {
   152  	return p2putil.UnmarshalAndReturn(rawbytes, &types.GetHashByNo{})
   153  }
   154  
   155  func (bh *getHashByNoRequestHandler) Handle(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) {
   156  	remotePeer := bh.peer
   157  	data := msgBody.(*types.GetHashByNo)
   158  	p2putil.DebugLogReceive(bh.logger, bh.protocol, msg.ID().String(), remotePeer, data)
   159  	chainAccessor := bh.actor.GetChainAccessor()
   160  
   161  	// check if remote peer has valid chain,
   162  	// TODO also check if found prevBlock is on main chain or side chain, assume in main chain for now.
   163  	targetHash, err := chainAccessor.GetHashByNo(data.BlockNo)
   164  	if err != nil {
   165  		resp := &types.GetHashByNoResponse{Status: types.ResultStatus_NOT_FOUND}
   166  		remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashByNoResponse, resp))
   167  		return
   168  	}
   169  
   170  	// generate response message
   171  	resp := &types.GetHashByNoResponse{
   172  		Status:    types.ResultStatus_OK,
   173  		BlockHash: targetHash,
   174  	}
   175  	remotePeer.SendMessage(remotePeer.MF().NewMsgResponseOrder(msg.ID(), p2pcommon.GetHashByNoResponse, resp))
   176  }
   177  
   178  // newBlockReqHandler creates handler for GetBlockRequest
   179  func NewGetHashByNoRespHandler(pm p2pcommon.PeerManager, peer p2pcommon.RemotePeer, logger *log.Logger, actor p2pcommon.ActorService) *getHashByNoResponseHandler {
   180  	bh := &getHashByNoResponseHandler{BaseMsgHandler: BaseMsgHandler{protocol: p2pcommon.GetHashByNoResponse, pm: pm, peer: peer, actor: actor, logger: logger}}
   181  
   182  	return bh
   183  }
   184  
   185  func (bh *getHashByNoResponseHandler) ParsePayload(rawbytes []byte) (p2pcommon.MessageBody, error) {
   186  	return p2putil.UnmarshalAndReturn(rawbytes, &types.GetHashByNoResponse{})
   187  }
   188  
   189  func (bh *getHashByNoResponseHandler) Handle(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) {
   190  	data := msgBody.(*types.GetHashByNoResponse)
   191  	p2putil.DebugLogReceiveResponse(bh.logger, bh.protocol, msg.ID().String(), msg.OriginalID().String(), bh.peer, data)
   192  
   193  	// locate request data and remove it if found
   194  	bh.peer.GetReceiver(msg.OriginalID())(msg, data)
   195  }