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

     1  /*
     2   * @file
     3   * @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package subproto
     7  
     8  import (
     9  	"bytes"
    10  	"crypto/sha256"
    11  	"github.com/aergoio/aergo-lib/log"
    12  	"github.com/aergoio/aergo/internal/enc"
    13  	"github.com/aergoio/aergo/p2p/p2pcommon"
    14  	"github.com/aergoio/etcd/raft/raftpb"
    15  	"github.com/gofrs/uuid"
    16  	"github.com/golang/mock/gomock"
    17  	"github.com/stretchr/testify/assert"
    18  	"sync"
    19  	"testing"
    20  
    21  	"github.com/aergoio/aergo/chain"
    22  	"github.com/aergoio/aergo/p2p/p2pmock"
    23  	"github.com/aergoio/aergo/types"
    24  )
    25  
    26  func TestGetHashRequestHandler_handle(t *testing.T) {
    27  	ctrl := gomock.NewController(t)
    28  	defer ctrl.Finish()
    29  
    30  	logger := log.NewLogger("test.subproto")
    31  
    32  	var dummyPeerID, _ = types.IDB58Decode("16Uiu2HAmN5YU8V2LnTy9neuuJCLNsxLnd5xVSRZqkjvZUHS3mLoD")
    33  	var sampleMsgID = p2pcommon.NewMsgID()
    34  
    35  	var sampleBlksB58 = []string{
    36  		"v6zbuQ4aVSdbTwQhaiZGp5pcL5uL55X3kt2wfxor5W6",
    37  		"2VEPg4MqJUoaS3EhZ6WWSAUuFSuD4oSJ645kSQsGV7H9",
    38  		"AtzTZ2CZS45F1276RpTdLfYu2DLgRcd9HL3aLqDT1qte",
    39  		"2n9QWNDoUvML756X7xdHWCFLZrM4CQEtnVH2RzG5FYAw",
    40  		"6cy7U7XKYtDTMnF3jNkcJvJN5Rn85771NSKjc5Tfo2DM",
    41  		"3bmB8D37XZr4DNPs64NiGRa2Vw3i8VEgEy6Xc2XBmRXC",
    42  	}
    43  	var sampleBlks [][]byte
    44  	var sampleBlksHashes []types.BlockID
    45  
    46  	sampleBlks = make([][]byte, len(sampleBlksB58))
    47  	sampleBlksHashes = make([]types.BlockID, len(sampleBlksB58))
    48  	for i, hashb58 := range sampleBlksB58 {
    49  		hash, _ := enc.ToBytes(hashb58)
    50  		sampleBlks[i] = hash
    51  		copy(sampleBlksHashes[i][:], hash)
    52  	}
    53  
    54  
    55  	baseHeight := uint64(110000)
    56  	sampleSize := 21
    57  	mainChainHashes := make([][]byte, sampleSize)
    58  	sideChainHashes := make([][]byte, sampleSize)
    59  	digest := sha256.New()
    60  	for i := 0; i < sampleSize; i++ {
    61  		digest.Write(uuid.Must(uuid.NewV4()).Bytes())
    62  		mainChainHashes[i] = digest.Sum(nil)
    63  		digest.Write(uuid.Must(uuid.NewV4()).Bytes())
    64  		sideChainHashes[i] = digest.Sum(nil)
    65  	}
    66  	tests := []struct {
    67  		name   string
    68  		inNum  uint64
    69  		inHash []byte
    70  		inSize uint64
    71  
    72  		firstChain [][]byte
    73  		reorgIdx   int
    74  		lastChain  [][]byte
    75  
    76  		expectedStatus  types.ResultStatus
    77  		expectedHashCnt int
    78  	}{
    79  		// 1. success (exact prev and enough chaining)
    80  		{"Tsucc", baseHeight, mainChainHashes[0], 20,
    81  			mainChainHashes, 99999, mainChainHashes, types.ResultStatus_OK, 20},
    82  		// 2. exact prev but smaller chaining
    83  		{"TShorter", baseHeight, mainChainHashes[0], 20,
    84  			mainChainHashes[:16], 99999, mainChainHashes[:16], types.ResultStatus_OK, 15},
    85  		// 3. wrong prev
    86  		{"TWrongPrev", baseHeight, sampleBlks[0], 20,
    87  			mainChainHashes, 99999, mainChainHashes, types.ResultStatus_INVALID_ARGUMENT, 0},
    88  		// 4. missing prev (smaller best block than prev)
    89  		{"TMissingPrev", baseHeight + 30, mainChainHashes[0], 20,
    90  			mainChainHashes, 99999, mainChainHashes, types.ResultStatus_INVALID_ARGUMENT, 0},
    91  		// 5. exact prev , but reorg from middle before first fetch
    92  		{"TReorgBefore", baseHeight, mainChainHashes[0], 20,
    93  			mainChainHashes, 0, append(append(make([][]byte, 0, sampleSize), mainChainHashes[:5]...), sideChainHashes[5:]...), types.ResultStatus_OK, 20},
    94  		// 6. exact prev , but changed (by such as reorg) during fetch
    95  		{"TReorgMid", baseHeight, mainChainHashes[0], 20,
    96  			mainChainHashes, 10, append(append(make([][]byte, 0, sampleSize), mainChainHashes[:5]...), sideChainHashes[5:]...), types.ResultStatus_INTERNAL, 0},
    97  		// 7. exact prev at first, but changed prev (and decent blocks also) before first fetch
    98  		{"TReorgWhole", baseHeight, mainChainHashes[0], 20,
    99  			mainChainHashes, 0, sideChainHashes, types.ResultStatus_INTERNAL, 0},
   100  		// 7. exact prev at first, but changed prev (and decent blocks also) during fetch
   101  		{"TReorgWhole2", baseHeight, mainChainHashes[0], 20,
   102  			mainChainHashes, 10, sideChainHashes, types.ResultStatus_INTERNAL, 0},
   103  	}
   104  	for _, test := range tests {
   105  		t.Run(test.name, func(t *testing.T) {
   106  			mockPM := p2pmock.NewMockPeerManager(ctrl)
   107  			mockPeer := p2pmock.NewMockRemotePeer(ctrl)
   108  			mockActor := p2pmock.NewMockActorService(ctrl)
   109  			dummyMF := &testDoubleHashesRespFactory{}
   110  			mockPeer.EXPECT().ID().Return(dummyPeerID).AnyTimes()
   111  			mockPeer.EXPECT().Name().Return("16..aadecf@1").AnyTimes()
   112  			mockPeer.EXPECT().MF().Return(dummyMF).MinTimes(1)
   113  			mockPeer.EXPECT().SendMessage(gomock.Any()).Times(1)
   114  
   115  			mockAcc := &testDoubleChainAccessor{firstChain: test.firstChain, lastChain: test.lastChain, baseHeight: baseHeight, reorgTiming: test.reorgIdx}
   116  			mockActor.EXPECT().GetChainAccessor().Return(mockAcc).MinTimes(1)
   117  
   118  			msg := p2pmock.NewMockMessage(ctrl)
   119  			msg.EXPECT().ID().Return(sampleMsgID).AnyTimes()
   120  			msg.EXPECT().Subprotocol().Return(p2pcommon.GetHashesRequest).AnyTimes()
   121  			body := &types.GetHashesRequest{PrevNumber: test.inNum, PrevHash: test.inHash, Size: test.inSize}
   122  
   123  			h := NewGetHashesReqHandler(mockPM, mockPeer, logger, mockActor)
   124  			h.Handle(msg, body)
   125  
   126  			// verify whether handler send response with expected result
   127  			assert.Equal(t, test.expectedStatus.String(), dummyMF.lastStatus.String())
   128  			if test.expectedStatus == types.ResultStatus_OK {
   129  				assert.Equal(t, test.expectedHashCnt, len(dummyMF.lastResp.Hashes))
   130  			}
   131  		})
   132  	}
   133  }
   134  
   135  func TestGetHashByNoRequestHandler_handle(t *testing.T) {
   136  	ctrl := gomock.NewController(t)
   137  	defer ctrl.Finish()
   138  
   139  	logger := log.NewLogger("test.subproto")
   140  
   141  	var dummyPeerID, _ = types.IDB58Decode("16Uiu2HAmN5YU8V2LnTy9neuuJCLNsxLnd5xVSRZqkjvZUHS3mLoD")
   142  	var sampleMsgID = p2pcommon.NewMsgID()
   143  
   144  	var sampleBlksB58 = []string{
   145  		"v6zbuQ4aVSdbTwQhaiZGp5pcL5uL55X3kt2wfxor5W6",
   146  		"2VEPg4MqJUoaS3EhZ6WWSAUuFSuD4oSJ645kSQsGV7H9",
   147  		"AtzTZ2CZS45F1276RpTdLfYu2DLgRcd9HL3aLqDT1qte",
   148  		"2n9QWNDoUvML756X7xdHWCFLZrM4CQEtnVH2RzG5FYAw",
   149  		"6cy7U7XKYtDTMnF3jNkcJvJN5Rn85771NSKjc5Tfo2DM",
   150  		"3bmB8D37XZr4DNPs64NiGRa2Vw3i8VEgEy6Xc2XBmRXC",
   151  	}
   152  	var sampleBlks [][]byte
   153  	var sampleBlksHashes []types.BlockID
   154  
   155  	sampleBlks = make([][]byte, len(sampleBlksB58))
   156  	sampleBlksHashes = make([]types.BlockID, len(sampleBlksB58))
   157  	for i, hashb58 := range sampleBlksB58 {
   158  		hash, _ := enc.ToBytes(hashb58)
   159  		sampleBlks[i] = hash
   160  		copy(sampleBlksHashes[i][:], hash)
   161  	}
   162  
   163  	baseHeight := uint64(110000)
   164  	wrongHeight := uint64(21531535)
   165  	tests := []struct {
   166  		name  string
   167  		inNum uint64
   168  		accRet []byte
   169  		accRetErr error
   170  
   171  		expectedStatus types.ResultStatus
   172  	}{
   173  		// 1. success (exact prev and enough chaining)
   174  		{"Tsucc", baseHeight, sampleBlks[0], nil, types.ResultStatus_OK},
   175  		// 2. exact prev but smaller chaining
   176  		{"TMissing", wrongHeight, nil, &chain.ErrNoBlock{}, types.ResultStatus_NOT_FOUND},
   177  	}
   178  	for _, test := range tests {
   179  		t.Run(test.name, func(t *testing.T) {
   180  			mockPM := p2pmock.NewMockPeerManager(ctrl)
   181  			mockPeer := p2pmock.NewMockRemotePeer(ctrl)
   182  			mockActor := p2pmock.NewMockActorService(ctrl)
   183  			dummyMF := &testDoubleMOFactory{}
   184  			mockPeer.EXPECT().ID().Return(dummyPeerID).AnyTimes()
   185  			mockPeer.EXPECT().Name().Return("16..aadecf@1").AnyTimes()
   186  			mockPeer.EXPECT().MF().Return(dummyMF).MinTimes(1)
   187  			mockPeer.EXPECT().SendMessage(gomock.Any()).Times(1)
   188  
   189  			mockAcc := p2pmock.NewMockChainAccessor(ctrl)
   190  			mockAcc.EXPECT().GetHashByNo(test.inNum).Return(test.accRet, test.accRetErr).Times(1)
   191  			mockActor.EXPECT().GetChainAccessor().Return(mockAcc).AnyTimes()
   192  
   193  			msg :=  p2pmock.NewMockMessage(ctrl)
   194  			msg.EXPECT().ID().Return(sampleMsgID).AnyTimes()
   195  			msg.EXPECT().Subprotocol().Return(p2pcommon.GetHashByNoRequest).AnyTimes()
   196  			body := &types.GetHashByNo{BlockNo: test.inNum}
   197  
   198  			h := NewGetHashByNoReqHandler(mockPM, mockPeer, logger, mockActor)
   199  			h.Handle(msg, body)
   200  
   201  			// verify
   202  			assert.Equal(t, test.expectedStatus.String(), dummyMF.lastStatus.String())
   203  		})
   204  	}
   205  }
   206  
   207  
   208  func (a *testDoubleChainAccessor) GetBestBlock() (*types.Block, error) {
   209  	mychain := a.getChain()
   210  	idx := len(mychain) - 1
   211  	return &types.Block{Hash: mychain[idx], Header: &types.BlockHeader{BlockNo: a.baseHeight + types.BlockNo(idx)}}, nil
   212  }
   213  
   214  func (a *testDoubleChainAccessor) GetConsensusInfo() string {
   215  	return ""
   216  }
   217  
   218  // GetBlock return block of blockHash. It return nil and error if not found block of that hash or there is a problem in db store
   219  func (a *testDoubleChainAccessor) GetBlock(blockHash []byte) (*types.Block, error) {
   220  	mychain := a.getChain()
   221  	for i, hash := range mychain {
   222  		if bytes.Equal(hash, blockHash) {
   223  			prevHash := []byte(nil)
   224  			if i > 0 {
   225  				prevHash = mychain[i-1]
   226  			}
   227  			number := a.baseHeight + types.BlockNo(i)
   228  			return &types.Block{Hash: hash, Header: &types.BlockHeader{BlockNo: number, PrevBlockHash: prevHash}}, nil
   229  		}
   230  	}
   231  	return nil, chain.ErrNoBlock{}
   232  }
   233  
   234  // GetHashByNo returns hash of block. It return nil and error if not found block of that number or there is a problem in db store
   235  func (a *testDoubleChainAccessor) GetHashByNo(blockNo types.BlockNo) ([]byte, error) {
   236  	mychain := a.getChain()
   237  	a.callCount++
   238  	idx := blockNo - a.baseHeight
   239  	if idx < 0 || idx >= types.BlockNo(len(mychain)) {
   240  		return nil, chain.ErrNoBlock{}
   241  	}
   242  	return mychain[int(idx)], nil
   243  }
   244  
   245  
   246  
   247  // emulate db
   248  type testDoubleChainAccessor struct {
   249  	p2pmock.MockChainAccessor
   250  	baseHeight types.BlockNo
   251  
   252  	callCount   int
   253  	reorgTiming int
   254  	firstChain  [][]byte
   255  	lastChain   [][]byte
   256  }
   257  
   258  func (a *testDoubleChainAccessor) getChain() [][]byte {
   259  	if a.callCount <= a.reorgTiming {
   260  		return a.firstChain
   261  	} else {
   262  		return a.lastChain
   263  	}
   264  }
   265  
   266  
   267  type testDoubleHashesRespFactory struct {
   268  	lastResp   *types.GetHashesResponse
   269  	lastStatus types.ResultStatus
   270  }
   271  
   272  
   273  func (f *testDoubleHashesRespFactory) NewMsgRequestOrder(expecteResponse bool, protocolID p2pcommon.SubProtocol, message p2pcommon.MessageBody) p2pcommon.MsgOrder {
   274  	panic("implement me")
   275  }
   276  
   277  func (f *testDoubleHashesRespFactory) NewMsgBlockRequestOrder(respReceiver p2pcommon.ResponseReceiver, protocolID p2pcommon.SubProtocol, message p2pcommon.MessageBody) p2pcommon.MsgOrder {
   278  	panic("implement me")
   279  }
   280  
   281  func (f *testDoubleHashesRespFactory) NewMsgResponseOrder(reqID p2pcommon.MsgID, protocolID p2pcommon.SubProtocol, message p2pcommon.MessageBody) p2pcommon.MsgOrder {
   282  	f.lastResp = message.(*types.GetHashesResponse)
   283  	f.lastStatus = f.lastResp.Status
   284  	return &testMo{message:&testMessage{id:reqID, subProtocol:protocolID}}
   285  }
   286  
   287  func (f *testDoubleHashesRespFactory) NewMsgBlkBroadcastOrder(noticeMsg *types.NewBlockNotice) p2pcommon.MsgOrder {
   288  	panic("implement me")
   289  }
   290  
   291  func (f *testDoubleHashesRespFactory) NewMsgTxBroadcastOrder(noticeMsg *types.NewTransactionsNotice) p2pcommon.MsgOrder {
   292  	panic("implement me")
   293  }
   294  
   295  func (f *testDoubleHashesRespFactory) NewMsgBPBroadcastOrder(noticeMsg *types.BlockProducedNotice) p2pcommon.MsgOrder {
   296  	panic("implement me")
   297  }
   298  
   299  func (f *testDoubleHashesRespFactory) NewRaftMsgOrder(msgType raftpb.MessageType, raftMsg *raftpb.Message) p2pcommon.MsgOrder {
   300  	panic("implement me")
   301  }
   302  
   303  
   304  
   305  // testDoubleMOFactory keep last created message and last result status of response message
   306  type testDoubleMOFactory struct {
   307  	mutex sync.Mutex
   308  	lastResp   p2pcommon.MessageBody
   309  	lastStatus types.ResultStatus
   310  }
   311  
   312  func (f *testDoubleMOFactory) NewMsgBlkBroadcastOrder(noticeMsg *types.NewBlockNotice) p2pcommon.MsgOrder {
   313  	panic("implement me")
   314  }
   315  
   316  func (f *testDoubleMOFactory) NewMsgTxBroadcastOrder(noticeMsg *types.NewTransactionsNotice) p2pcommon.MsgOrder {
   317  	panic("implement me")
   318  }
   319  
   320  func (f *testDoubleMOFactory) NewMsgBPBroadcastOrder(noticeMsg *types.BlockProducedNotice) p2pcommon.MsgOrder {
   321  	panic("implement me")
   322  }
   323  
   324  func (f *testDoubleMOFactory) NewMsgRequestOrder(expecteResponse bool, protocolID p2pcommon.SubProtocol, message p2pcommon.MessageBody) p2pcommon.MsgOrder {
   325  	panic("implement me")
   326  }
   327  
   328  func (f *testDoubleMOFactory) NewMsgBlockRequestOrder(respReceiver p2pcommon.ResponseReceiver, protocolID p2pcommon.SubProtocol, message p2pcommon.MessageBody) p2pcommon.MsgOrder {
   329  	panic("implement me")
   330  }
   331  
   332  func (f *testDoubleMOFactory) NewMsgResponseOrder(reqID p2pcommon.MsgID, protocolID p2pcommon.SubProtocol, message p2pcommon.MessageBody) p2pcommon.MsgOrder {
   333  	f.mutex.Lock()
   334  	defer f.mutex.Unlock()
   335  
   336  	f.lastResp = message
   337  	f.lastStatus = f.lastResp.(types.ResponseMessage).GetStatus()
   338  	return &testMo{message:&testMessage{id:reqID, subProtocol:protocolID}}
   339  }
   340  
   341  func (f *testDoubleMOFactory) NewRaftMsgOrder(msgType raftpb.MessageType, raftMsg *raftpb.Message) p2pcommon.MsgOrder {
   342  	panic("implement me")
   343  }
   344  
   345  
   346  type testMo struct {
   347  	protocolID p2pcommon.SubProtocol // protocolName and msg struct type MUST be matched.
   348  	message p2pcommon.Message
   349  }
   350  
   351  func (mo *testMo) GetMsgID() p2pcommon.MsgID {
   352  	return mo.message.ID()
   353  }
   354  
   355  func (mo *testMo) Timestamp() int64 {
   356  	return mo.message.Timestamp()
   357  }
   358  
   359  func (*testMo) IsRequest() bool {
   360  	return false
   361  }
   362  
   363  func (*testMo) IsNeedSign() bool {
   364  	return true
   365  }
   366  
   367  func (mo *testMo) GetProtocolID() p2pcommon.SubProtocol {
   368  	return mo.protocolID
   369  }
   370  
   371  func (mo *testMo) SendTo(p p2pcommon.RemotePeer) error {
   372  	return nil
   373  }
   374  
   375  func (mo *testMo) CancelSend(p p2pcommon.RemotePeer) {
   376  
   377  }
   378  
   379  type testMessage struct {
   380  	subProtocol p2pcommon.SubProtocol
   381  	// Length is lenght of payload
   382  	length uint32
   383  	// timestamp is unix time (precision of second)
   384  	timestamp int64
   385  	// ID is 16 bytes unique identifier
   386  	id p2pcommon.MsgID
   387  	// OriginalID is message id of request which trigger this message. it will be all zero, if message is request or notice.
   388  	originalID p2pcommon.MsgID
   389  
   390  	// marshaled by google protocol buffer v3. object is determined by Subprotocol
   391  	payload []byte
   392  }
   393  
   394  func (m *testMessage) Subprotocol() p2pcommon.SubProtocol {
   395  	return m.subProtocol
   396  }
   397  
   398  func (m *testMessage) Length() uint32 {
   399  	return m.length
   400  
   401  }
   402  
   403  func (m *testMessage) Timestamp() int64 {
   404  	return m.timestamp
   405  }
   406  
   407  func (m *testMessage) ID() p2pcommon.MsgID {
   408  	return m.id
   409  }
   410  
   411  func (m *testMessage) OriginalID() p2pcommon.MsgID {
   412  	return m.originalID
   413  }
   414  
   415  func (m *testMessage) Payload() []byte {
   416  	return m.payload
   417  }