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

     1  /*
     2   * @file
     3   * @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package p2p
     7  
     8  import (
     9  	"bytes"
    10  	"fmt"
    11  	"github.com/aergoio/aergo-lib/log"
    12  	"github.com/aergoio/aergo/chain"
    13  	"github.com/aergoio/aergo/internal/enc"
    14  	"github.com/aergoio/aergo/message"
    15  	"github.com/aergoio/aergo/p2p/p2pcommon"
    16  	"github.com/aergoio/aergo/p2p/p2putil"
    17  	"github.com/aergoio/aergo/types"
    18  	lru "github.com/hashicorp/golang-lru"
    19  )
    20  
    21  type syncManager struct {
    22  	logger *log.Logger
    23  	actor  p2pcommon.ActorService
    24  	pm     p2pcommon.PeerManager
    25  
    26  	blkCache *lru.Cache
    27  	txCache  *lru.Cache
    28  }
    29  
    30  type syncTask struct {
    31  	peer   p2pcommon.RemotePeer
    32  	hashes []types.TxID
    33  	data   *types.NewTransactionsNotice
    34  }
    35  
    36  func newSyncManager(actor p2pcommon.ActorService, pm p2pcommon.PeerManager, logger *log.Logger) p2pcommon.SyncManager {
    37  	var err error
    38  	sm := &syncManager{actor: actor, pm: pm, logger: logger}
    39  
    40  	sm.blkCache, err = lru.New(DefaultGlobalBlockCacheSize)
    41  	if err != nil {
    42  		panic("Failed to create p2p block cache" + err.Error())
    43  	}
    44  	sm.txCache, err = lru.New(DefaultGlobalTxCacheSize)
    45  	if err != nil {
    46  		panic("Failed to create p2p tx cache " + err.Error())
    47  	}
    48  
    49  	return sm
    50  }
    51  
    52  func (sm *syncManager) HandleBlockProducedNotice(peer p2pcommon.RemotePeer, block *types.Block) {
    53  	hash := types.MustParseBlockID(block.GetHash())
    54  	ok, _ := sm.blkCache.ContainsOrAdd(hash, syncManagerChanSize)
    55  	if ok {
    56  		sm.logger.Warn().Str(p2putil.LogBlkHash, hash.String()).Str(p2putil.LogPeerName, peer.Name()).Msg("Duplicated blockProduced notice")
    57  		return
    58  	}
    59  	// check if block size is over the limit
    60  	if block.Size() > int(chain.MaxBlockSize()) {
    61  		sm.logger.Info().Str(p2putil.LogPeerName, peer.Name()).Str(p2putil.LogBlkHash, block.BlockID().String()).Int("size", block.Size()).Msg("invalid blockProduced notice. block size exceed limit")
    62  		return
    63  	}
    64  
    65  	sm.actor.SendRequest(message.ChainSvc, &message.AddBlock{PeerID: peer.ID(), Block: block, Bstate: nil})
    66  }
    67  
    68  func (sm *syncManager) HandleNewBlockNotice(peer p2pcommon.RemotePeer, data *types.NewBlockNotice) {
    69  	hash := types.MustParseBlockID(data.BlockHash)
    70  	peerID := peer.ID()
    71  	//if !sm.checkWorkToken() {
    72  	//	// just ignore it
    73  	//	//sm.logger.Debug().Str(LogBlkHash, enc.ToString(data.BlockHash)).Str(LogPeerID, peerID.Pretty()).Msg("Ignoring newBlock notice sync syncManager is busy now.")
    74  	//	return
    75  	//}
    76  
    77  	// TODO check if evicted return value is needed.
    78  	ok, _ := sm.blkCache.ContainsOrAdd(hash, cachePlaceHolder)
    79  	if ok {
    80  		// Kick out duplicated notice log.
    81  		// if sm.logger.IsDebugEnabled() {
    82  		// 	sm.logger.Debug().Str(LogBlkHash, enc.ToString(data.BlkHash)).Str(LogPeerID, peerID.Pretty()).Msg("Got NewBlock notice, but sent already from other peer")
    83  		// }
    84  		// this notice is already sent to chainservice
    85  		return
    86  	}
    87  
    88  	// request block info if selfnode does not have block already
    89  	foundBlock, _ := sm.actor.GetChainAccessor().GetBlock(data.BlockHash)
    90  	if foundBlock == nil {
    91  		sm.logger.Debug().Str(p2putil.LogBlkHash, enc.ToString(data.BlockHash)).Str(p2putil.LogPeerName, peer.Name()).Msg("new block notice of unknown hash. request back to notifier")
    92  		sm.actor.SendRequest(message.P2PSvc, &message.GetBlockInfos{ToWhom: peerID,
    93  			Hashes: []message.BlockHash{message.BlockHash(data.BlockHash)}})
    94  	}
    95  }
    96  
    97  // HandleGetBlockResponse handle when remote peer send a block information.
    98  // TODO this method will be removed after newer syncer is developed
    99  func (sm *syncManager) HandleGetBlockResponse(peer p2pcommon.RemotePeer, msg p2pcommon.Message, resp *types.GetBlockResponse) {
   100  	blocks := resp.Blocks
   101  	peerID := peer.ID()
   102  
   103  	// The response should have only one block here, since this peer had requested only one block.
   104  	// getBlockResponse with bulky blocks is only called in newsyncer since aergosvr 0.9.9 , which is handled by other receiver and not come to this code.
   105  	// if bulky hashes on this condition block, it is probably sync timeout or bug.
   106  	if len(blocks) != 1 {
   107  		return
   108  	}
   109  	block := blocks[0]
   110  	// check if block size is over the limit
   111  	if block.Size() > int(chain.MaxBlockSize()) {
   112  		sm.logger.Info().Str(p2putil.LogPeerName, peer.Name()).Str(p2putil.LogBlkHash, block.BlockID().String()).Int("size", block.Size()).Msg("cancel to add block. block size exceed limit")
   113  		return
   114  	}
   115  
   116  	sm.actor.SendRequest(message.ChainSvc, &message.AddBlock{PeerID: peerID, Block: block, Bstate: nil})
   117  }
   118  
   119  func (sm *syncManager) HandleNewTxNotice(peer p2pcommon.RemotePeer, hashes []types.TxID, data *types.NewTransactionsNotice) {
   120  	peerID := peer.ID()
   121  
   122  	// TODO it will cause problem if getTransaction failed. (i.e. remote peer was sent notice, but not response getTransaction)
   123  	toGet := make([]message.TXHash, 0, len(data.TxHashes))
   124  	for _, hashArr := range hashes {
   125  		ok, _ := sm.txCache.ContainsOrAdd(hashArr, cachePlaceHolder)
   126  		if ok {
   127  			// Kickout duplicated notice log.
   128  			// if sm.logger.IsDebugEnabled() {
   129  			// 	sm.logger.Debug().Str(LogTxHash, enc.ToString(hashArr[:])).Str(LogPeerID, peerID.Pretty()).Msg("Got NewTx notice, but sent already from other peer")
   130  			// }
   131  			// this notice is already sent to chainservice
   132  			continue
   133  		}
   134  		hash := types.HashID(hashArr).Bytes()
   135  		toGet = append(toGet, hash)
   136  	}
   137  	if len(toGet) == 0 {
   138  		// sm.logger.Debug().Str(LogPeerID, peerID.Pretty()).Msg("No new tx found in tx notice")
   139  		return
   140  	}
   141  	sm.logger.Debug().Str("hashes", txHashArrToString(toGet)).Msg("syncManager request back unknown tx hashes")
   142  	// create message data
   143  	sm.actor.SendRequest(message.P2PSvc, &message.GetTransactions{ToWhom: peerID, Hashes: toGet})
   144  }
   145  
   146  // bytesArrToString converts array of byte array to json array of b58 encoded string.
   147  func txHashArrToString(bbarray []message.TXHash) string {
   148  	return txHashArrToStringWithLimit(bbarray, 10)
   149  }
   150  
   151  func txHashArrToStringWithLimit(bbarray []message.TXHash, limit int) string {
   152  	var buf bytes.Buffer
   153  	buf.WriteByte('[')
   154  	var arrSize = len(bbarray)
   155  	if limit > arrSize {
   156  		limit = arrSize
   157  	}
   158  	for i := 0; i < limit; i++ {
   159  		hash := bbarray[i]
   160  		buf.WriteByte('"')
   161  		buf.WriteString(enc.ToString([]byte(hash)))
   162  		buf.WriteByte('"')
   163  		buf.WriteByte(',')
   164  	}
   165  	if arrSize > limit {
   166  		buf.WriteString(fmt.Sprintf(" (and %d more), ", arrSize-limit))
   167  	}
   168  	buf.WriteByte(']')
   169  	return buf.String()
   170  }