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 }