github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/netsync/chainmgr/msg_fetcher.go (about)

     1  package chainmgr
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	log "github.com/sirupsen/logrus"
     8  
     9  	"github.com/bytom/bytom/errors"
    10  	"github.com/bytom/bytom/netsync/peers"
    11  	"github.com/bytom/bytom/p2p/security"
    12  	"github.com/bytom/bytom/protocol/bc"
    13  	"github.com/bytom/bytom/protocol/bc/types"
    14  )
    15  
    16  const (
    17  	maxNumOfParallelFetchBlocks = 7
    18  	blockProcessChSize          = 1024
    19  	blocksProcessChSize         = 128
    20  	headersProcessChSize        = 1024
    21  	maxNumOfFastSyncPeers       = 128
    22  )
    23  
    24  var (
    25  	requireBlockTimeout      = 20 * time.Second
    26  	requireHeadersTimeout    = 30 * time.Second
    27  	requireBlocksTimeout     = 90 * time.Second
    28  	checkSyncPeerNumInterval = 5 * time.Second
    29  
    30  	errRequestBlocksTimeout = errors.New("request blocks timeout")
    31  	errRequestTimeout       = errors.New("request timeout")
    32  	errPeerDropped          = errors.New("Peer dropped")
    33  	errSendMsg              = errors.New("send message error")
    34  )
    35  
    36  // MsgFetcher is the interface for msg fetch struct
    37  type MsgFetcher interface {
    38  	resetParameter()
    39  	addSyncPeer(peerID string)
    40  	requireBlock(peerID string, height uint64) (*types.Block, error)
    41  	parallelFetchBlocks(work []*fetchBlocksWork, downloadNotifyCh chan struct{}, ProcessStopCh chan struct{}, wg *sync.WaitGroup)
    42  	parallelFetchHeaders(peers []*peers.Peer, locator []*bc.Hash, stopHash *bc.Hash, skip uint64) map[string][]*types.BlockHeader
    43  }
    44  
    45  type fetchBlocksWork struct {
    46  	startHeader, stopHeader *types.BlockHeader
    47  }
    48  
    49  type fetchBlocksResult struct {
    50  	startHeight, stopHeight uint64
    51  	err                     error
    52  }
    53  
    54  type msgFetcher struct {
    55  	storage          *storage
    56  	syncPeers        *fastSyncPeers
    57  	peers            *peers.PeerSet
    58  	blockProcessCh   chan *blockMsg
    59  	blocksProcessCh  chan *blocksMsg
    60  	headersProcessCh chan *headersMsg
    61  	blocksMsgChanMap map[string]chan []*types.Block
    62  	mux              sync.RWMutex
    63  }
    64  
    65  func newMsgFetcher(storage *storage, peers *peers.PeerSet) *msgFetcher {
    66  	return &msgFetcher{
    67  		storage:          storage,
    68  		syncPeers:        newFastSyncPeers(),
    69  		peers:            peers,
    70  		blockProcessCh:   make(chan *blockMsg, blockProcessChSize),
    71  		blocksProcessCh:  make(chan *blocksMsg, blocksProcessChSize),
    72  		headersProcessCh: make(chan *headersMsg, headersProcessChSize),
    73  		blocksMsgChanMap: make(map[string]chan []*types.Block),
    74  	}
    75  }
    76  
    77  func (mf *msgFetcher) addSyncPeer(peerID string) {
    78  	mf.syncPeers.add(peerID)
    79  }
    80  
    81  func (mf *msgFetcher) collectResultLoop(peerCh chan string, quit chan struct{}, resultCh chan *fetchBlocksResult, workerCloseCh chan struct{}, workSize int) {
    82  	defer close(workerCloseCh)
    83  	ticker := time.NewTicker(checkSyncPeerNumInterval)
    84  	defer ticker.Stop()
    85  
    86  	//collect fetch results
    87  	for resultCount := 0; resultCount < workSize && mf.syncPeers.size() > 0; {
    88  		select {
    89  		case result := <-resultCh:
    90  			resultCount++
    91  			if result.err != nil {
    92  				log.WithFields(log.Fields{"module": logModule, "startHeight": result.startHeight, "stopHeight": result.stopHeight, "err": result.err}).Error("failed on fetch blocks")
    93  				return
    94  			}
    95  
    96  			peer, err := mf.syncPeers.selectIdlePeer()
    97  			if err != nil {
    98  				log.WithFields(log.Fields{"module": logModule, "err": result.err}).Warn("failed on find fast sync peer")
    99  				break
   100  			}
   101  			peerCh <- peer
   102  		case <-ticker.C:
   103  			if mf.syncPeers.size() == 0 {
   104  				log.WithFields(log.Fields{"module": logModule}).Warn("num of fast sync peer is 0")
   105  				return
   106  			}
   107  		case _, ok := <-quit:
   108  			if !ok {
   109  				return
   110  			}
   111  		}
   112  	}
   113  }
   114  
   115  func (mf *msgFetcher) fetchBlocks(work *fetchBlocksWork, peerID string) ([]*types.Block, error) {
   116  	defer mf.syncPeers.setIdle(peerID)
   117  	startHash := work.startHeader.Hash()
   118  	stopHash := work.stopHeader.Hash()
   119  	blocks, err := mf.requireBlocks(peerID, []*bc.Hash{&startHash}, &stopHash)
   120  	if err != nil {
   121  		mf.syncPeers.delete(peerID)
   122  		mf.peers.ProcessIllegal(peerID, security.LevelConnException, err.Error())
   123  		return nil, err
   124  	}
   125  
   126  	if err := mf.verifyBlocksMsg(blocks, work.startHeader, work.stopHeader); err != nil {
   127  		mf.syncPeers.delete(peerID)
   128  		mf.peers.ProcessIllegal(peerID, security.LevelConnException, err.Error())
   129  		return nil, err
   130  	}
   131  
   132  	return blocks, nil
   133  }
   134  
   135  func (mf *msgFetcher) fetchBlocksProcess(work *fetchBlocksWork, peerCh chan string, downloadNotifyCh chan struct{}, closeCh chan struct{}) error {
   136  	for {
   137  		select {
   138  		case peerID := <-peerCh:
   139  			for {
   140  				blocks, err := mf.fetchBlocks(work, peerID)
   141  				if err != nil {
   142  					log.WithFields(log.Fields{"module": logModule, "startHeight": work.startHeader.Height, "stopHeight": work.stopHeader.Height, "error": err}).Info("failed on fetch blocks")
   143  					break
   144  				}
   145  
   146  				if err := mf.storage.writeBlocks(peerID, blocks); err != nil {
   147  					log.WithFields(log.Fields{"module": logModule, "error": err}).Info("write block error")
   148  					return err
   149  				}
   150  
   151  				// send to block process pool
   152  				select {
   153  				case downloadNotifyCh <- struct{}{}:
   154  				default:
   155  				}
   156  
   157  				// work completed
   158  				if blocks[len(blocks)-1].Height >= work.stopHeader.Height-1 {
   159  					return nil
   160  				}
   161  
   162  				//unfinished work, continue
   163  				work.startHeader = &blocks[len(blocks)-1].BlockHeader
   164  			}
   165  		case <-closeCh:
   166  			return nil
   167  		}
   168  	}
   169  }
   170  
   171  func (mf *msgFetcher) fetchBlocksWorker(workCh chan *fetchBlocksWork, peerCh chan string, resultCh chan *fetchBlocksResult, closeCh chan struct{}, downloadNotifyCh chan struct{}, wg *sync.WaitGroup) {
   172  	for {
   173  		select {
   174  		case work := <-workCh:
   175  			err := mf.fetchBlocksProcess(work, peerCh, downloadNotifyCh, closeCh)
   176  			resultCh <- &fetchBlocksResult{startHeight: work.startHeader.Height, stopHeight: work.stopHeader.Height, err: err}
   177  		case <-closeCh:
   178  			wg.Done()
   179  			return
   180  		}
   181  	}
   182  }
   183  
   184  func (mf *msgFetcher) parallelFetchBlocks(works []*fetchBlocksWork, downloadNotifyCh chan struct{}, ProcessStopCh chan struct{}, wg *sync.WaitGroup) {
   185  	workSize := len(works)
   186  	workCh := make(chan *fetchBlocksWork, workSize)
   187  	peerCh := make(chan string, maxNumOfFastSyncPeers)
   188  	resultCh := make(chan *fetchBlocksResult, workSize)
   189  	closeCh := make(chan struct{})
   190  
   191  	for _, work := range works {
   192  		workCh <- work
   193  	}
   194  	syncPeers := mf.syncPeers.selectIdlePeers()
   195  	for i := 0; i < len(syncPeers) && i < maxNumOfFastSyncPeers; i++ {
   196  		peerCh <- syncPeers[i]
   197  	}
   198  
   199  	var workWg sync.WaitGroup
   200  	for i := 0; i <= maxNumOfParallelFetchBlocks && i < workSize; i++ {
   201  		workWg.Add(1)
   202  		go mf.fetchBlocksWorker(workCh, peerCh, resultCh, closeCh, downloadNotifyCh, &workWg)
   203  	}
   204  
   205  	go mf.collectResultLoop(peerCh, ProcessStopCh, resultCh, closeCh, workSize)
   206  
   207  	workWg.Wait()
   208  	close(resultCh)
   209  	close(peerCh)
   210  	close(workCh)
   211  	close(downloadNotifyCh)
   212  	wg.Done()
   213  }
   214  
   215  func (mf *msgFetcher) parallelFetchHeaders(peers []*peers.Peer, locator []*bc.Hash, stopHash *bc.Hash, skip uint64) map[string][]*types.BlockHeader {
   216  	result := make(map[string][]*types.BlockHeader)
   217  	response := make(map[string]bool)
   218  	for _, peer := range peers {
   219  		if ok := peer.GetHeaders(locator, stopHash, skip); !ok {
   220  			continue
   221  		}
   222  		result[peer.ID()] = nil
   223  	}
   224  
   225  	timeout := time.NewTimer(requireHeadersTimeout)
   226  	defer timeout.Stop()
   227  	for {
   228  		select {
   229  		case msg := <-mf.headersProcessCh:
   230  			if _, ok := result[msg.peerID]; ok {
   231  				result[msg.peerID] = append(result[msg.peerID], msg.headers[:]...)
   232  				response[msg.peerID] = true
   233  				if len(response) == len(result) {
   234  					return result
   235  				}
   236  			}
   237  		case <-timeout.C:
   238  			log.WithFields(log.Fields{"module": logModule, "err": errRequestTimeout}).Warn("failed on parallel fetch headers")
   239  			return result
   240  		}
   241  	}
   242  }
   243  
   244  func (mf *msgFetcher) processBlock(peerID string, block *types.Block) {
   245  	mf.blockProcessCh <- &blockMsg{block: block, peerID: peerID}
   246  }
   247  
   248  func (mf *msgFetcher) processBlocks(peerID string, blocks []*types.Block) {
   249  	mf.blocksProcessCh <- &blocksMsg{blocks: blocks, peerID: peerID}
   250  	mf.mux.RLock()
   251  	blocksMsgChan, ok := mf.blocksMsgChanMap[peerID]
   252  	mf.mux.RUnlock()
   253  	if !ok {
   254  		mf.peers.ProcessIllegal(peerID, security.LevelMsgIllegal, "msg from unsolicited peer")
   255  		return
   256  	}
   257  
   258  	blocksMsgChan <- blocks
   259  }
   260  
   261  func (mf *msgFetcher) processHeaders(peerID string, headers []*types.BlockHeader) {
   262  	mf.headersProcessCh <- &headersMsg{headers: headers, peerID: peerID}
   263  }
   264  
   265  func (mf *msgFetcher) requireBlock(peerID string, height uint64) (*types.Block, error) {
   266  	peer := mf.peers.GetPeer(peerID)
   267  	if peer == nil {
   268  		return nil, errPeerDropped
   269  	}
   270  
   271  	if ok := peer.GetBlockByHeight(height); !ok {
   272  		return nil, errSendMsg
   273  	}
   274  
   275  	timeout := time.NewTimer(requireBlockTimeout)
   276  	defer timeout.Stop()
   277  
   278  	for {
   279  		select {
   280  		case msg := <-mf.blockProcessCh:
   281  			if msg.peerID != peerID {
   282  				continue
   283  			}
   284  			if msg.block.Height != height {
   285  				continue
   286  			}
   287  			return msg.block, nil
   288  		case <-timeout.C:
   289  			return nil, errors.Wrap(errRequestTimeout, "requireBlock")
   290  		}
   291  	}
   292  }
   293  
   294  func (mf *msgFetcher) requireBlocks(peerID string, locator []*bc.Hash, stopHash *bc.Hash) ([]*types.Block, error) {
   295  	peer := mf.peers.GetPeer(peerID)
   296  	if peer == nil {
   297  		mf.syncPeers.delete(peerID)
   298  		return nil, errPeerDropped
   299  	}
   300  
   301  	receiveCh := make(chan []*types.Block, 1)
   302  	mf.mux.Lock()
   303  	mf.blocksMsgChanMap[peerID] = receiveCh
   304  	mf.mux.Unlock()
   305  
   306  	if ok := peer.GetBlocks(locator, stopHash); !ok {
   307  		return nil, errSendMsg
   308  	}
   309  
   310  	timeout := time.NewTimer(requireBlocksTimeout)
   311  	defer timeout.Stop()
   312  	select {
   313  	case blocks := <-receiveCh:
   314  		return blocks, nil
   315  	case <-timeout.C:
   316  		return nil, errRequestBlocksTimeout
   317  	}
   318  }
   319  
   320  func (mf *msgFetcher) resetParameter() {
   321  	mf.blocksMsgChanMap = make(map[string]chan []*types.Block)
   322  	mf.syncPeers = newFastSyncPeers()
   323  	mf.storage.resetParameter()
   324  	//empty chan
   325  	for {
   326  		select {
   327  		case <-mf.blocksProcessCh:
   328  		case <-mf.headersProcessCh:
   329  		default:
   330  			return
   331  		}
   332  	}
   333  }
   334  
   335  func (mf *msgFetcher) verifyBlocksMsg(blocks []*types.Block, startHeader, stopHeader *types.BlockHeader) error {
   336  	// null blocks
   337  	if len(blocks) == 0 {
   338  		return errors.New("null blocks msg")
   339  	}
   340  
   341  	// blocks more than request
   342  	if uint64(len(blocks)) > stopHeader.Height-startHeader.Height+1 {
   343  		return errors.New("exceed length blocks msg")
   344  	}
   345  
   346  	// verify start block
   347  	if blocks[0].Hash() != startHeader.Hash() {
   348  		return errors.New("get mismatch blocks msg")
   349  	}
   350  
   351  	// verify blocks continuity
   352  	for i := 0; i < len(blocks)-1; i++ {
   353  		if blocks[i].Hash() != blocks[i+1].PreviousBlockHash {
   354  			return errors.New("get discontinuous blocks msg")
   355  		}
   356  	}
   357  
   358  	return nil
   359  }