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

     1  /*
     2   * @file
     3   * @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package p2p
     7  
     8  import (
     9  	"time"
    10  
    11  	"github.com/aergoio/aergo/message"
    12  	"github.com/aergoio/aergo/p2p/p2pcommon"
    13  	"github.com/aergoio/aergo/types"
    14  )
    15  
    16  // BlockHashesReceiver is send p2p GetHashesRequest to target peer and receive p2p responses till all requested hashes are received
    17  // It will send response actor message if all hashes are received or failed to receive, but not send response if timeout expired.
    18  type BlockHashesReceiver struct {
    19  	syncerSeq uint64
    20  	requestID p2pcommon.MsgID
    21  
    22  	peer  p2pcommon.RemotePeer
    23  	actor p2pcommon.ActorService
    24  
    25  	prevBlock *types.BlockInfo
    26  	count     int
    27  	timeout   time.Time
    28  	finished  bool
    29  	status      receiverStatus
    30  
    31  	got    []message.BlockHash
    32  	offset int
    33  	senderFinished chan interface{}
    34  }
    35  
    36  func NewBlockHashesReceiver(actor p2pcommon.ActorService, peer p2pcommon.RemotePeer, seq uint64, req *message.GetHashes, ttl time.Duration) *BlockHashesReceiver {
    37  	timeout := time.Now().Add(ttl)
    38  	return &BlockHashesReceiver{syncerSeq:seq, actor: actor, peer: peer, prevBlock: req.PrevInfo, count: int(req.Count), timeout: timeout, got: make([]message.BlockHash, int(req.Count))}
    39  }
    40  
    41  func (br *BlockHashesReceiver) StartGet() {
    42  	// create message data
    43  	req := &types.GetHashesRequest{PrevHash: br.prevBlock.Hash, PrevNumber: br.prevBlock.No, Size: uint64(br.count)}
    44  	mo := br.peer.MF().NewMsgBlockRequestOrder(br.ReceiveResp, p2pcommon.GetHashesRequest, req)
    45  	br.peer.SendMessage(mo)
    46  	br.requestID = mo.GetMsgID()
    47  }
    48  
    49  // ReceiveResp must be called just in read go routine
    50  func (br *BlockHashesReceiver) ReceiveResp(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) (ret bool) {
    51  	// TODO this code is exact copy of BlocksChunkReceiver, so be lots of other codes in this file. consider refactoring
    52  	ret = true
    53  	switch br.status {
    54  	case receiverStatusWaiting:
    55  		br.handleInWaiting(msg, msgBody)
    56  	case receiverStatusCanceled:
    57  		br.ignoreMsg(msg, msgBody)
    58  		return
    59  	case receiverStatusFinished:
    60  		fallthrough
    61  	default:
    62  		return
    63  	}
    64  	return
    65  }
    66  
    67  func (br *BlockHashesReceiver) handleInWaiting(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) {
    68  	// consuming request id when timeout, no more resp expected (i.e. hasNext == false ) or malformed body.
    69  	// timeout
    70  	if br.timeout.Before(time.Now()) {
    71  		// silently ignore already status job
    72  		br.finishReceiver()
    73  		return
    74  	}
    75  	// malformed responses means that later responses will be also malformed..
    76  	respBody, ok := msgBody.(types.ResponseMessage)
    77  	if !ok || respBody.GetStatus() != types.ResultStatus_OK {
    78  		br.cancelReceiving(message.RemotePeerFailError, false)
    79  		return
    80  	}
    81  
    82  	// remote peer response failure
    83  	body, ok := msgBody.(*types.GetHashesResponse)
    84  	if !ok || len(body.Hashes) == 0 {
    85  		br.cancelReceiving(message.MissingHashError, false)
    86  		return
    87  	}
    88  
    89  	// add to Got
    90  	for _, block := range body.Hashes {
    91  		// It also error that response has more hashes than expected(=requested).
    92  		if br.offset >= len(br.got) {
    93  			br.cancelReceiving(message.TooManyBlocksError, body.HasNext)
    94  			return
    95  		}
    96  		br.got[br.offset] = block
    97  		br.offset++
    98  	}
    99  	// remote peer hopefully sent last part
   100  	if !body.HasNext {
   101  		br.actor.TellRequest(message.SyncerSvc, &message.GetHashesRsp{Seq:br.syncerSeq, Hashes: br.got, PrevInfo: br.prevBlock, Count: uint64(len(br.got))})
   102  		br.finishReceiver()
   103  	}
   104  	return
   105  }
   106  
   107  // cancelReceiving is cancel wait for receiving and send syncer the failure result.
   108  // not all part of response is received, it wait remaining (and useless) response. It is assumed canceling is not frequently occur
   109  func (br *BlockHashesReceiver) cancelReceiving(err error, hasNext bool) {
   110  	br.status = receiverStatusCanceled
   111  	br.actor.TellRequest(message.SyncerSvc,
   112  		&message.GetHashesRsp{Seq: br.syncerSeq, PrevInfo:br.prevBlock, Err: err})
   113  
   114  	// check time again. since negative duration of timer will not fire channel.
   115  	interval := br.timeout.Sub(time.Now())
   116  	if !hasNext || interval <= 0 {
   117  		// if remote peer will not send partial response anymore. it it actually same as finish.
   118  		br.finishReceiver()
   119  	} else {
   120  		// canceling in the middle of responses
   121  		br.senderFinished = make(chan interface{})
   122  		go func() {
   123  			timer := time.NewTimer(interval)
   124  			select {
   125  			case <-timer.C:
   126  				break
   127  			case <-br.senderFinished:
   128  				break
   129  			}
   130  			br.peer.ConsumeRequest(br.requestID)
   131  		}()
   132  	}
   133  }
   134  
   135  // finishReceiver is to cancel works, assuming cancellations are not frequently occur
   136  func (br *BlockHashesReceiver) finishReceiver() {
   137  	br.status = receiverStatusFinished
   138  	br.peer.ConsumeRequest(br.requestID)
   139  }
   140  
   141  // ignoreMsg is silently ignore following responses, which is not useless anymore.
   142  func (br *BlockHashesReceiver) ignoreMsg(msg p2pcommon.Message, msgBody p2pcommon.MessageBody) {
   143  	body, ok := msgBody.(*types.GetBlockResponse)
   144  	if !ok {
   145  		return
   146  	}
   147  	if !body.HasNext {
   148  		// really status from remote peer
   149  		select {
   150  		case br.senderFinished <- struct{}{}:
   151  		default:
   152  		}
   153  	}
   154  }