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

     1  package syncer
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/aergoio/aergo/p2p/p2putil"
    12  
    13  	"github.com/aergoio/aergo-actor/actor"
    14  	"github.com/aergoio/aergo/chain"
    15  	"github.com/aergoio/aergo/message"
    16  	"github.com/aergoio/aergo/pkg/component"
    17  	"github.com/aergoio/aergo/types"
    18  	"github.com/stretchr/testify/assert"
    19  )
    20  
    21  type StubSyncer struct {
    22  	realSyncer    *Syncer
    23  	stubRequester *StubRequester
    24  
    25  	localChain  *chain.StubBlockChain
    26  	remoteChain *chain.StubBlockChain
    27  
    28  	stubPeers []*StubPeer
    29  
    30  	t *testing.T
    31  
    32  	waitGroup *sync.WaitGroup
    33  
    34  	cfg *SyncerConfig
    35  
    36  	checkResultFn         TestResultFn
    37  	getAnchorsHookFn      GetAnchorsHookFn
    38  	getSyncAncestorHookFn GetSyncAncestorHookFn
    39  }
    40  
    41  type TestResultFn func(stubSyncer *StubSyncer)
    42  type GetAnchorsHookFn func(stubSyncer *StubSyncer)
    43  type GetSyncAncestorHookFn func(stubSyncer *StubSyncer, msg *message.GetSyncAncestor)
    44  
    45  var (
    46  	targetPeerID = types.PeerID([]byte(fmt.Sprintf("peer-%d", 0)))
    47  )
    48  
    49  func makeStubPeerSet(remoteChains []*chain.StubBlockChain) []*StubPeer {
    50  	stubPeers := make([]*StubPeer, len(remoteChains))
    51  
    52  	for i, chain := range remoteChains {
    53  		stubPeers[i] = NewStubPeer(i, uint64(chain.Best), chain)
    54  	}
    55  
    56  	return stubPeers
    57  }
    58  
    59  func NewTestSyncer(t *testing.T, localChain *chain.StubBlockChain, remoteChain *chain.StubBlockChain, peers []*StubPeer, cfg *SyncerConfig) *StubSyncer {
    60  	syncer := NewSyncer(nil, localChain, cfg)
    61  	testsyncer := &StubSyncer{realSyncer: syncer, localChain: localChain, remoteChain: remoteChain, stubPeers: peers, cfg: cfg, t: t}
    62  
    63  	testsyncer.stubRequester = NewStubRequester()
    64  
    65  	syncer.SetRequester(testsyncer.stubRequester)
    66  
    67  	return testsyncer
    68  }
    69  
    70  func (stubSyncer *StubSyncer) start() {
    71  	stubSyncer.waitGroup = &sync.WaitGroup{}
    72  	stubSyncer.waitGroup.Add(1)
    73  
    74  	go func() {
    75  		defer stubSyncer.waitGroup.Done()
    76  
    77  		for {
    78  			msg := stubSyncer.stubRequester.recvMessage()
    79  			isStop := stubSyncer.handleMessage(msg)
    80  			if isStop {
    81  				return
    82  			}
    83  		}
    84  	}()
    85  }
    86  
    87  func (stubSyncer *StubSyncer) waitStop() {
    88  	logger.Info().Msg("test syncer wait to stop")
    89  	stubSyncer.waitGroup.Wait()
    90  	logger.Info().Msg("test syncer stopped")
    91  }
    92  
    93  func isOtherActorRequest(msg interface{}) bool {
    94  	switch msg.(type) {
    95  	case *message.GetSyncAncestor:
    96  		return true
    97  	case *message.GetAnchors:
    98  		return true
    99  	case *message.GetAncestor:
   100  		return true
   101  	case *message.GetHashByNo:
   102  		return true
   103  	case *message.GetHashes:
   104  		return true
   105  	case *message.GetPeers:
   106  		return true
   107  	case *message.GetBlockChunks:
   108  		return true
   109  	case *message.AddBlock:
   110  		return true
   111  	}
   112  
   113  	return false
   114  }
   115  
   116  func (stubSyncer *StubSyncer) handleMessage(msg interface{}) bool {
   117  	//prefix handle
   118  	switch resmsg := msg.(type) {
   119  	case *message.FinderResult:
   120  		if resmsg.Ancestor != nil && resmsg.Err == nil && resmsg.Ancestor.No >= 0 {
   121  			stubSyncer.localChain.Rollback(resmsg.Ancestor)
   122  		}
   123  	case *message.CloseFetcher:
   124  		if resmsg.FromWho == NameHashFetcher {
   125  			if stubSyncer.cfg.debugContext.debugHashFetcher {
   126  				assert.Equal(stubSyncer.t, stubSyncer.realSyncer.hashFetcher.lastBlockInfo.No, stubSyncer.cfg.debugContext.targetNo, "invalid hash target")
   127  			}
   128  		} else {
   129  			assert.Fail(stubSyncer.t, "invalid closefetcher")
   130  		}
   131  	case *message.SyncStop:
   132  		if stubSyncer.cfg.debugContext.expErrResult != nil {
   133  			assert.Equal(stubSyncer.t, stubSyncer.cfg.debugContext.expErrResult, resmsg.Err, "expected syncer stop error")
   134  		}
   135  		//check final result
   136  		if stubSyncer.checkResultFn != nil {
   137  			stubSyncer.checkResultFn(stubSyncer)
   138  		}
   139  	default:
   140  	}
   141  
   142  	if isOtherActorRequest(msg) {
   143  		logger.Debug().Msg("msg is for testsyncer")
   144  
   145  		stubSyncer.handleActorMsg(msg)
   146  	} else {
   147  
   148  		logger.Debug().Msg("msg is for syncer")
   149  		stubSyncer.realSyncer.handleMessage(msg)
   150  	}
   151  
   152  	//check stop
   153  	switch resmsg := msg.(type) {
   154  	case *message.SyncStop:
   155  		return true
   156  	case *message.FinderResult:
   157  		if stubSyncer.cfg.debugContext.expAncestor >= 0 {
   158  			assert.Equal(stubSyncer.t, uint64(stubSyncer.cfg.debugContext.expAncestor), resmsg.Ancestor.No, "ancestor mismatch")
   159  		} else if !stubSyncer.realSyncer.isRunning {
   160  			assert.Equal(stubSyncer.t, stubSyncer.cfg.debugContext.expAncestor, -1, "ancestor mismatch")
   161  			return true
   162  		}
   163  
   164  		if stubSyncer.cfg.debugContext.debugFinder {
   165  			return true
   166  		}
   167  	case *message.CloseFetcher:
   168  		if stubSyncer.cfg.debugContext.debugHashFetcher {
   169  			return true
   170  		}
   171  	default:
   172  		return false
   173  	}
   174  
   175  	return false
   176  }
   177  
   178  func (stubSyncer *StubSyncer) handleActorMsg(inmsg interface{}) {
   179  	switch msg := inmsg.(type) {
   180  	case *message.GetAnchors:
   181  		stubSyncer.GetAnchors(msg)
   182  	case *message.GetSyncAncestor:
   183  		stubSyncer.GetSyncAncestor(msg)
   184  	case *message.GetHashByNo:
   185  		stubSyncer.GetHashByNo(msg)
   186  
   187  	case *message.GetHashes:
   188  		stubSyncer.GetHashes(msg, nil)
   189  
   190  	case *message.GetPeers:
   191  		stubSyncer.GetPeers(msg)
   192  
   193  	case *message.GetBlockChunks:
   194  		stubSyncer.GetBlockChunks(msg)
   195  
   196  	case *message.AddBlock:
   197  		stubSyncer.AddBlock(msg, nil)
   198  
   199  	case *actor.Started, *actor.Stopping, *actor.Stopped, *component.CompStatReq: // donothing
   200  
   201  	default:
   202  		str := fmt.Sprintf("Missed message. (%v) %s", reflect.TypeOf(msg), msg)
   203  		stubSyncer.t.Fatal(str)
   204  	}
   205  }
   206  
   207  //reply to requestFuture()
   208  func (syncer *StubSyncer) GetAnchors(msg *message.GetAnchors) {
   209  	if syncer.getAnchorsHookFn != nil {
   210  		syncer.getAnchorsHookFn(syncer)
   211  	}
   212  
   213  	go func() {
   214  		if syncer.cfg.debugContext.debugFinder {
   215  			if syncer.stubPeers[0].timeDelaySec > 0 {
   216  				logger.Debug().Str("peer", p2putil.ShortForm(types.PeerID(syncer.stubPeers[0].addr.PeerID))).Msg("slow target peer sleep")
   217  				time.Sleep(syncer.stubPeers[0].timeDelaySec)
   218  				logger.Debug().Str("peer", p2putil.ShortForm(types.PeerID(syncer.stubPeers[0].addr.PeerID))).Msg("slow target peer wakeup")
   219  			}
   220  		}
   221  
   222  		hashes, lastno, err := syncer.localChain.GetAnchors()
   223  
   224  		rspMsg := message.GetAnchorsRsp{Seq: msg.Seq, Hashes: hashes, LastNo: lastno, Err: err}
   225  		syncer.stubRequester.sendReply(StubRequestResult{rspMsg, nil})
   226  	}()
   227  }
   228  
   229  func (syncer *StubSyncer) GetPeers(msg *message.GetPeers) {
   230  	rspMsg := makePeerReply(syncer.stubPeers)
   231  	syncer.stubRequester.sendReply(StubRequestResult{rspMsg, nil})
   232  }
   233  
   234  func (syncer *StubSyncer) SendGetSyncAncestorRsp(msg *message.GetSyncAncestor) {
   235  	//find peer
   236  	stubPeer := syncer.findStubPeer(msg.ToWhom)
   237  	ancestor := stubPeer.blockChain.GetAncestorWithHashes(msg.Hashes)
   238  
   239  	rspMsg := &message.GetSyncAncestorRsp{Seq: msg.Seq, Ancestor: ancestor}
   240  	syncer.stubRequester.TellTo(message.SyncerSvc, rspMsg) //TODO refactoring: stubcompRequesterresult
   241  }
   242  
   243  func (syncer *StubSyncer) GetSyncAncestor(msg *message.GetSyncAncestor) {
   244  	if syncer.getSyncAncestorHookFn != nil {
   245  		syncer.getSyncAncestorHookFn(syncer, msg)
   246  	}
   247  
   248  	syncer.SendGetSyncAncestorRsp(msg)
   249  }
   250  
   251  func (syncer *StubSyncer) GetHashByNo(msg *message.GetHashByNo) {
   252  	//targetPeer = 0
   253  	hash, err := syncer.stubPeers[0].blockChain.GetHashByNo(msg.BlockNo)
   254  	rsp := &message.GetHashByNoRsp{Seq: msg.Seq, BlockHash: hash, Err: err}
   255  	syncer.stubRequester.TellTo(message.SyncerSvc, rsp)
   256  }
   257  func (syncer *StubSyncer) GetHashes(msg *message.GetHashes, responseErr error) {
   258  	blkHashes, _ := syncer.remoteChain.GetHashes(msg.PrevInfo, msg.Count)
   259  
   260  	assert.Equal(syncer.t, len(blkHashes), int(msg.Count))
   261  	rsp := &message.GetHashesRsp{Seq: msg.Seq, PrevInfo: msg.PrevInfo, Hashes: blkHashes, Count: uint64(len(blkHashes)), Err: responseErr}
   262  
   263  	syncer.stubRequester.TellTo(message.SyncerSvc, rsp)
   264  }
   265  
   266  func (syncer *StubSyncer) GetBlockChunks(msg *message.GetBlockChunks) {
   267  	stubPeer := syncer.findStubPeer(msg.ToWhom)
   268  	stubPeer.blockFetched = true
   269  
   270  	assert.True(syncer.t, stubPeer != nil, "peer exist")
   271  
   272  	if stubPeer.HookGetBlockChunkRsp != nil {
   273  		stubPeer.HookGetBlockChunkRsp(msg)
   274  		return
   275  	}
   276  	go func() {
   277  		if stubPeer.timeDelaySec > 0 {
   278  			logger.Debug().Str("peer", p2putil.ShortForm(types.PeerID(stubPeer.addr.PeerID))).Msg("slow peer sleep")
   279  			time.Sleep(stubPeer.timeDelaySec)
   280  			logger.Debug().Str("peer", p2putil.ShortForm(types.PeerID(stubPeer.addr.PeerID))).Msg("slow peer wakeup")
   281  		}
   282  
   283  		//send reply
   284  		blocks, err := stubPeer.blockChain.GetBlocks(msg.Hashes)
   285  
   286  		rsp := &message.GetBlockChunksRsp{Seq: msg.Seq, ToWhom: msg.ToWhom, Blocks: blocks, Err: err}
   287  		syncer.stubRequester.TellTo(message.SyncerSvc, rsp)
   288  	}()
   289  }
   290  
   291  //ChainService
   292  func (syncer *StubSyncer) AddBlock(msg *message.AddBlock, responseErr error) {
   293  	err := syncer.localChain.AddBlock(msg.Block)
   294  
   295  	rsp := &message.AddBlockRsp{BlockNo: msg.Block.GetHeader().BlockNo, BlockHash: msg.Block.GetHash(), Err: err}
   296  	logger.Debug().Uint64("no", msg.Block.GetHeader().BlockNo).Msg("add block succeed")
   297  	syncer.stubRequester.TellTo(message.SyncerSvc, rsp)
   298  }
   299  
   300  func (syncer *StubSyncer) findStubPeer(peerID types.PeerID) *StubPeer {
   301  	for _, tmpPeer := range syncer.stubPeers {
   302  		peerIDStr := string(tmpPeer.addr.PeerID)
   303  		logger.Info().Str("tmp", peerIDStr).Msg("peer is")
   304  		if strings.Compare(peerIDStr, string(peerID)) == 0 {
   305  			return tmpPeer
   306  		}
   307  	}
   308  
   309  	logger.Error().Str("peer", p2putil.ShortForm(peerID)).Msg("can't find peer")
   310  	panic("peer find fail")
   311  	return nil
   312  }
   313  
   314  func makePeerReply(stubPeers []*StubPeer) *message.GetPeersRsp {
   315  	count := len(stubPeers)
   316  	now := time.Now()
   317  	peers := make([]*message.PeerInfo, count)
   318  	for i, p := range stubPeers {
   319  		peerInfo := &message.PeerInfo{
   320  			Addr: p.addr, CheckTime: now, State: p.state, LastBlockHash: p.lastBlk.BlockHash, LastBlockNumber: p.lastBlk.BlockNo,
   321  		}
   322  		peers[i] = peerInfo
   323  	}
   324  
   325  	return &message.GetPeersRsp{Peers: peers}
   326  }
   327  
   328  //test block fetcher only
   329  func (stubSyncer *StubSyncer) runTestBlockFetcher(ctx *types.SyncContext) {
   330  	stubSyncer.realSyncer.blockFetcher = newBlockFetcher(ctx, stubSyncer.realSyncer.getCompRequester(), stubSyncer.cfg)
   331  	stubSyncer.realSyncer.blockFetcher.Start()
   332  }
   333  
   334  func (stubSyncer *StubSyncer) sendHashSetToBlockFetcher(hashSet *HashSet) {
   335  	logger.Debug().Uint64("no", hashSet.StartNo).Msg("test syncer pushed hashset to blockfetcher")
   336  
   337  	stubSyncer.realSyncer.blockFetcher.hfCh <- hashSet
   338  }