github.com/aergoio/aergo@v1.3.1/syncer/blockprocessor.go (about)

     1  package syncer
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"github.com/aergoio/aergo/chain"
     7  	"github.com/aergoio/aergo/p2p/p2putil"
     8  	"sort"
     9  
    10  	"github.com/aergoio/aergo/internal/enc"
    11  	"github.com/aergoio/aergo/message"
    12  	"github.com/aergoio/aergo/pkg/component"
    13  	"github.com/aergoio/aergo/types"
    14  )
    15  
    16  type BlockProcessor struct {
    17  	compRequester component.IComponentRequester //for communicate with other service
    18  
    19  	blockFetcher *BlockFetcher
    20  
    21  	curConnRequest *ConnectTask
    22  
    23  	connQueue []*ConnectTask
    24  
    25  	prevBlock *types.Block
    26  	curBlock  *types.Block
    27  
    28  	targetBlockNo types.BlockNo
    29  	name          string
    30  }
    31  
    32  type ConnectTask struct {
    33  	FromPeer types.PeerID
    34  	Blocks   []*types.Block
    35  	firstNo  types.BlockNo
    36  	cur      int
    37  }
    38  
    39  func NewBlockProcessor(compRequester component.IComponentRequester, blockFetcher *BlockFetcher, ancestor *types.Block,
    40  	targetNo types.BlockNo) *BlockProcessor {
    41  	return &BlockProcessor{
    42  		compRequester: compRequester,
    43  		blockFetcher:  blockFetcher,
    44  		prevBlock:     ancestor,
    45  		targetBlockNo: targetNo,
    46  		name:          NameBlockProcessor,
    47  	}
    48  }
    49  
    50  func (bproc *BlockProcessor) run(msg interface{}) error {
    51  	//TODO in test mode, if syncer receives invalid messages, syncer stop with panic()
    52  	switch msg.(type) {
    53  	case *message.GetBlockChunksRsp:
    54  		if err := bproc.GetBlockChunkRsp(msg.(*message.GetBlockChunksRsp)); err != nil {
    55  			return err
    56  		}
    57  	case *message.AddBlockRsp:
    58  		if err := bproc.AddBlockResponse(msg.(*message.AddBlockRsp)); err != nil {
    59  			return err
    60  		}
    61  
    62  		chain.TestDebugger.Check(chain.DEBUG_SYNCER_CRASH, 2, nil)
    63  	default:
    64  		return fmt.Errorf("invalid msg type:%T", msg)
    65  	}
    66  
    67  	return nil
    68  }
    69  
    70  func (bproc *BlockProcessor) isValidResponse(msg interface{}) error {
    71  	validateBlockChunksRsp := func(msg *message.GetBlockChunksRsp) error {
    72  		var prev []byte
    73  		blocks := msg.Blocks
    74  
    75  		if msg.Err != nil {
    76  			logger.Error().Err(msg.Err).Msg("GetBlockChunksRsp has error")
    77  			return msg.Err
    78  		}
    79  
    80  		if blocks == nil || len(blocks) == 0 {
    81  			logger.Error().Err(msg.Err).Str("peer", p2putil.ShortForm(msg.ToWhom)).Msg("GetBlockChunksRsp is empty")
    82  			return &ErrSyncMsg{msg: msg, str: "blocks is empty"}
    83  		}
    84  
    85  		for _, block := range blocks {
    86  			if prev != nil && !bytes.Equal(prev, block.GetHeader().GetPrevBlockHash()) {
    87  				logger.Error().Str("peer", p2putil.ShortForm(msg.ToWhom)).Msg("GetBlockChunksRsp hashes inconsistent")
    88  				return &ErrSyncMsg{msg: msg, str: "blocks hash not matched"}
    89  			}
    90  
    91  			prev = block.GetHash()
    92  		}
    93  		return nil
    94  	}
    95  
    96  	validateAddBlockRsp := func(msg *message.AddBlockRsp) error {
    97  		if msg.Err != nil {
    98  			return msg.Err
    99  		}
   100  
   101  		if msg.BlockHash == nil {
   102  			return &ErrSyncMsg{msg: msg, str: "invalid add block resonse"}
   103  		}
   104  
   105  		return nil
   106  	}
   107  
   108  	switch msg.(type) {
   109  	case *message.GetBlockChunksRsp:
   110  		if err := validateBlockChunksRsp(msg.(*message.GetBlockChunksRsp)); err != nil {
   111  			return err
   112  		}
   113  
   114  	case *message.AddBlockRsp:
   115  		if err := validateAddBlockRsp(msg.(*message.AddBlockRsp)); err != nil {
   116  			return err
   117  		}
   118  
   119  	default:
   120  		return fmt.Errorf("invalid msg type:%T", msg)
   121  	}
   122  
   123  	return nil
   124  }
   125  
   126  func (bproc *BlockProcessor) GetBlockChunkRsp(msg *message.GetBlockChunksRsp) error {
   127  	if err := bproc.isValidResponse(msg); err != nil {
   128  		return bproc.GetBlockChunkRspError(msg, err)
   129  	}
   130  
   131  	bf := bproc.blockFetcher
   132  
   133  	logger.Debug().Str("peer", p2putil.ShortForm(msg.ToWhom)).Uint64("startNo", msg.Blocks[0].GetHeader().BlockNo).Int("count", len(msg.Blocks)).Msg("received GetBlockChunkRsp")
   134  
   135  	task, err := bf.findFinished(msg, false)
   136  	if err != nil {
   137  		//TODO invalid peer
   138  		logger.Error().Str("peer", p2putil.ShortForm(msg.ToWhom)).
   139  			Int("count", len(msg.Blocks)).
   140  			Str("from", enc.ToString(msg.Blocks[0].GetHash())).
   141  			Str("to", enc.ToString(msg.Blocks[len(msg.Blocks)-1].GetHash())).
   142  			Msg("dropped unknown block response message")
   143  		return nil
   144  	}
   145  
   146  	bf.pushFreePeer(task.syncPeer)
   147  
   148  	bf.stat.setMaxChunkRsp(msg.Blocks[len(msg.Blocks)-1])
   149  
   150  	bproc.addConnectTask(msg)
   151  
   152  	return nil
   153  }
   154  
   155  func (bproc *BlockProcessor) GetBlockChunkRspError(msg *message.GetBlockChunksRsp, err error) error {
   156  	bf := bproc.blockFetcher
   157  
   158  	logger.Error().Err(err).Str("peer", p2putil.ShortForm(msg.ToWhom)).Msg("receive GetBlockChunksRsp with error message")
   159  
   160  	task, err := bf.findFinished(msg, true)
   161  	if err != nil {
   162  		//TODO invalid peer
   163  		logger.Error().Err(err).Str("peer", p2putil.ShortForm(msg.ToWhom)).Msg("dropped unknown block error message")
   164  		return nil
   165  	}
   166  
   167  	if err := bf.processFailedTask(task, false); err != nil {
   168  		return err
   169  	}
   170  
   171  	return nil
   172  }
   173  
   174  func (bproc *BlockProcessor) AddBlockResponse(msg *message.AddBlockRsp) error {
   175  	if err := bproc.isValidResponse(msg); err != nil {
   176  		logger.Info().Err(err).Uint64("no", msg.BlockNo).Str("hash", enc.ToString(msg.BlockHash)).Msg("block connect failed")
   177  		return err
   178  	}
   179  
   180  	curBlock := bproc.curBlock
   181  	curNo := curBlock.GetHeader().BlockNo
   182  	curHash := curBlock.GetHash()
   183  
   184  	if curNo != msg.BlockNo || !bytes.Equal(curHash, msg.BlockHash) {
   185  		logger.Error().Uint64("curNo", curNo).Uint64("msgNo", msg.BlockNo).
   186  			Str("curHash", enc.ToString(curHash)).Str("msgHash", enc.ToString(msg.BlockHash)).
   187  			Msg("invalid add block response")
   188  		return &ErrSyncMsg{msg: msg, str: "drop unknown add response"}
   189  	}
   190  
   191  	logger.Info().Uint64("no", msg.BlockNo).Str("hash", enc.ToString(msg.BlockHash)).Msg("block connect succeed")
   192  
   193  	bproc.blockFetcher.stat.setLastAddBlock(curBlock)
   194  
   195  	if curBlock.BlockNo() == bproc.targetBlockNo {
   196  		logger.Info().Msg("succeed to add last block, request stopping syncer")
   197  		stopSyncer(bproc.compRequester, bproc.blockFetcher.GetSeq(), bproc.name, nil)
   198  	}
   199  
   200  	bproc.prevBlock = curBlock
   201  	bproc.curBlock = nil
   202  
   203  	block := bproc.getNextBlockToConnect()
   204  
   205  	if block != nil {
   206  		bproc.connectBlock(block)
   207  	}
   208  
   209  	return nil
   210  }
   211  
   212  func (bproc *BlockProcessor) addConnectTask(msg *message.GetBlockChunksRsp) {
   213  	req := &ConnectTask{FromPeer: msg.ToWhom, Blocks: msg.Blocks, firstNo: msg.Blocks[0].GetHeader().BlockNo, cur: 0}
   214  
   215  	logger.Debug().Uint64("firstno", req.firstNo).Int("count", len(req.Blocks)).Msg("add connect task to queue")
   216  
   217  	bproc.pushToConnQueue(req)
   218  
   219  	block := bproc.getNextBlockToConnect()
   220  
   221  	if block != nil {
   222  		bproc.connectBlock(block)
   223  	}
   224  }
   225  
   226  func (bproc *BlockProcessor) getNextBlockToConnect() *types.Block {
   227  	//already prev request is running, don't request any more
   228  	if bproc.curBlock != nil {
   229  		return nil
   230  	}
   231  
   232  	//request next block of current Request
   233  	if bproc.curConnRequest != nil {
   234  		req := bproc.curConnRequest
   235  		req.cur++
   236  
   237  		if req.cur >= len(req.Blocks) {
   238  			logger.Debug().Msg("current connect task is finished")
   239  			bproc.curConnRequest = nil
   240  		}
   241  	}
   242  
   243  	//pop from pending request
   244  	if bproc.curConnRequest == nil {
   245  		nextReq := bproc.popFromConnQueue()
   246  		if nextReq == nil {
   247  			return nil
   248  		}
   249  
   250  		bproc.curConnRequest = nextReq
   251  	}
   252  
   253  	next := bproc.curConnRequest.cur
   254  	nextBlock := bproc.curConnRequest.Blocks[next]
   255  
   256  	logger.Debug().Uint64("no", nextBlock.GetHeader().BlockNo).Str("hash", nextBlock.ID()).
   257  		Int("idx in req", next).Msg("next block to connect")
   258  
   259  	bproc.curBlock = nextBlock
   260  
   261  	return nextBlock
   262  }
   263  
   264  func (bproc *BlockProcessor) connectBlock(block *types.Block) {
   265  	if block == nil {
   266  		return
   267  	}
   268  
   269  	logger.Info().Uint64("no", block.GetHeader().BlockNo).
   270  		Str("hash", enc.ToString(block.GetHash())).
   271  		Msg("request connecting block to chainsvc")
   272  
   273  	bproc.compRequester.RequestTo(message.ChainSvc, &message.AddBlock{PeerID: "", Block: block, Bstate: nil, IsSync: true})
   274  }
   275  
   276  func (bproc *BlockProcessor) pushToConnQueue(newReq *ConnectTask) {
   277  	sortedList := bproc.connQueue
   278  
   279  	index := sort.Search(len(sortedList), func(i int) bool { return sortedList[i].firstNo > newReq.firstNo })
   280  	sortedList = append(sortedList, &ConnectTask{})
   281  	copy(sortedList[index+1:], sortedList[index:])
   282  	sortedList[index] = newReq
   283  
   284  	bproc.connQueue = sortedList
   285  
   286  	logger.Info().Int("len", len(bproc.connQueue)).Uint64("firstno", newReq.firstNo).
   287  		Str("firstHash", enc.ToString(newReq.Blocks[0].GetHash())).
   288  		Msg("add new task to connect queue")
   289  }
   290  
   291  func (bproc *BlockProcessor) popFromConnQueue() *ConnectTask {
   292  	sortedList := bproc.connQueue
   293  	if len(sortedList) == 0 {
   294  		logger.Debug().Msg("connect queue is empty. so wait new connect task")
   295  		return nil
   296  	}
   297  
   298  	//check if first task is next block
   299  	firstReq := sortedList[0]
   300  	if bproc.prevBlock != nil &&
   301  		firstReq.firstNo != (bproc.prevBlock.BlockNo()+1) {
   302  		logger.Debug().Uint64("first", firstReq.firstNo).Uint64("prev", bproc.prevBlock.BlockNo()).Msg("next block is not fetched yet")
   303  		return nil
   304  	}
   305  
   306  	newReq := sortedList[0]
   307  	sortedList = sortedList[1:]
   308  	bproc.connQueue = sortedList
   309  
   310  	logger.Info().Int("len", len(sortedList)).Uint64("firstno", newReq.firstNo).
   311  		Str("firstHash", enc.ToString(newReq.Blocks[0].GetHash())).
   312  		Msg("pop task from connect queue")
   313  
   314  	return newReq
   315  }