github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/sync/rpc_status_test.go (about) 1 package sync 2 3 import ( 4 "context" 5 "sync" 6 "testing" 7 "time" 8 9 "github.com/ethereum/go-ethereum/p2p/enr" 10 "github.com/kevinms/leakybucket-go" 11 "github.com/libp2p/go-libp2p-core/network" 12 "github.com/libp2p/go-libp2p-core/protocol" 13 types "github.com/prysmaticlabs/eth2-types" 14 mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" 15 "github.com/prysmaticlabs/prysm/beacon-chain/core/state" 16 "github.com/prysmaticlabs/prysm/beacon-chain/db/kv" 17 testingDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" 18 "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers" 19 p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" 20 p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types" 21 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1" 22 mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing" 23 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 24 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 25 "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper" 26 interfaces2 "github.com/prysmaticlabs/prysm/proto/interfaces" 27 "github.com/prysmaticlabs/prysm/shared/bytesutil" 28 "github.com/prysmaticlabs/prysm/shared/interfaces" 29 "github.com/prysmaticlabs/prysm/shared/params" 30 "github.com/prysmaticlabs/prysm/shared/testutil" 31 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 32 "github.com/prysmaticlabs/prysm/shared/testutil/require" 33 "github.com/prysmaticlabs/prysm/shared/timeutils" 34 "google.golang.org/protobuf/proto" 35 ) 36 37 func TestStatusRPCHandler_Disconnects_OnForkVersionMismatch(t *testing.T) { 38 p1 := p2ptest.NewTestP2P(t) 39 p2 := p2ptest.NewTestP2P(t) 40 p1.Connect(p2) 41 assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected") 42 root := [32]byte{'C'} 43 44 r := &Service{ 45 cfg: &Config{ 46 P2P: p1, 47 Chain: &mock.ChainService{ 48 Fork: &pb.Fork{ 49 PreviousVersion: params.BeaconConfig().GenesisForkVersion, 50 CurrentVersion: params.BeaconConfig().GenesisForkVersion, 51 }, 52 FinalizedCheckPoint: ðpb.Checkpoint{ 53 Epoch: 0, 54 Root: root[:], 55 }, 56 Genesis: time.Now(), 57 ValidatorsRoot: [32]byte{'A'}, 58 Root: make([]byte, 32), 59 }, 60 }, 61 rateLimiter: newRateLimiter(p1), 62 } 63 pcl := protocol.ID("/testing") 64 topic := string(pcl) 65 r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false) 66 67 var wg sync.WaitGroup 68 wg.Add(1) 69 p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) { 70 defer wg.Done() 71 expectSuccess(t, stream) 72 out := &pb.Status{} 73 assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out)) 74 assert.DeepEqual(t, root[:], out.FinalizedRoot) 75 assert.NoError(t, stream.Close()) 76 }) 77 78 pcl2 := protocol.ID("/eth2/beacon_chain/req/goodbye/1/ssz_snappy") 79 topic = string(pcl2) 80 r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false) 81 var wg2 sync.WaitGroup 82 wg2.Add(1) 83 p2.BHost.SetStreamHandler(pcl2, func(stream network.Stream) { 84 defer wg2.Done() 85 msg := new(types.SSZUint64) 86 assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, msg)) 87 assert.Equal(t, p2ptypes.GoodbyeCodeWrongNetwork, *msg) 88 assert.NoError(t, stream.Close()) 89 }) 90 91 stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl) 92 require.NoError(t, err) 93 assert.NoError(t, r.statusRPCHandler(context.Background(), &pb.Status{ForkDigest: bytesutil.PadTo([]byte("f"), 4), HeadRoot: make([]byte, 32), FinalizedRoot: make([]byte, 32)}, stream1)) 94 95 if testutil.WaitTimeout(&wg, 1*time.Second) { 96 t.Fatal("Did not receive stream within 1 sec") 97 } 98 if testutil.WaitTimeout(&wg2, 1*time.Second) { 99 t.Fatal("Did not receive stream within 1 sec") 100 } 101 102 assert.Equal(t, 0, len(p1.BHost.Network().Peers()), "handler did not disconnect peer") 103 } 104 105 func TestStatusRPCHandler_ConnectsOnGenesis(t *testing.T) { 106 p1 := p2ptest.NewTestP2P(t) 107 p2 := p2ptest.NewTestP2P(t) 108 p1.Connect(p2) 109 assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected") 110 root := [32]byte{} 111 112 r := &Service{ 113 cfg: &Config{ 114 P2P: p1, 115 Chain: &mock.ChainService{ 116 Fork: &pb.Fork{ 117 PreviousVersion: params.BeaconConfig().GenesisForkVersion, 118 CurrentVersion: params.BeaconConfig().GenesisForkVersion, 119 }, 120 FinalizedCheckPoint: ðpb.Checkpoint{ 121 Epoch: 0, 122 Root: params.BeaconConfig().ZeroHash[:], 123 }, 124 Genesis: time.Now(), 125 ValidatorsRoot: [32]byte{'A'}, 126 Root: make([]byte, 32), 127 }, 128 }, 129 rateLimiter: newRateLimiter(p1), 130 } 131 pcl := protocol.ID("/testing") 132 topic := string(pcl) 133 r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false) 134 135 var wg sync.WaitGroup 136 wg.Add(1) 137 p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) { 138 defer wg.Done() 139 expectSuccess(t, stream) 140 out := &pb.Status{} 141 assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out)) 142 assert.DeepEqual(t, root[:], out.FinalizedRoot) 143 }) 144 145 stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl) 146 require.NoError(t, err) 147 digest, err := r.forkDigest() 148 require.NoError(t, err) 149 150 err = r.statusRPCHandler(context.Background(), &pb.Status{ForkDigest: digest[:], FinalizedRoot: params.BeaconConfig().ZeroHash[:]}, stream1) 151 assert.NoError(t, err) 152 153 if testutil.WaitTimeout(&wg, 1*time.Second) { 154 t.Fatal("Did not receive stream within 1 sec") 155 } 156 157 assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Handler disconnected with peer") 158 } 159 160 func TestStatusRPCHandler_ReturnsHelloMessage(t *testing.T) { 161 p1 := p2ptest.NewTestP2P(t) 162 p2 := p2ptest.NewTestP2P(t) 163 p1.Connect(p2) 164 assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected") 165 db := testingDB.SetupDB(t) 166 167 // Set up a head state with data we expect. 168 head := testutil.NewBeaconBlock() 169 head.Block.Slot = 111 170 headRoot, err := head.Block.HashTreeRoot() 171 require.NoError(t, err) 172 blkSlot := 3 * params.BeaconConfig().SlotsPerEpoch 173 finalized := testutil.NewBeaconBlock() 174 finalized.Block.Slot = blkSlot 175 finalizedRoot, err := finalized.Block.HashTreeRoot() 176 require.NoError(t, err) 177 genesisState, err := state.GenesisBeaconState(context.Background(), nil, 0, ðpb.Eth1Data{}) 178 require.NoError(t, err) 179 require.NoError(t, genesisState.SetSlot(111)) 180 require.NoError(t, genesisState.UpdateBlockRootAtIndex(111%uint64(params.BeaconConfig().SlotsPerHistoricalRoot), headRoot)) 181 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(finalized))) 182 require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), finalizedRoot)) 183 finalizedCheckpt := ðpb.Checkpoint{ 184 Epoch: 3, 185 Root: finalizedRoot[:], 186 } 187 totalSec := int64(params.BeaconConfig().SlotsPerEpoch.Mul(5 * params.BeaconConfig().SecondsPerSlot)) 188 genTime := time.Now().Unix() - totalSec 189 190 r := &Service{ 191 cfg: &Config{ 192 P2P: p1, 193 Chain: &mock.ChainService{ 194 State: genesisState, 195 FinalizedCheckPoint: finalizedCheckpt, 196 Root: headRoot[:], 197 Fork: &pb.Fork{ 198 PreviousVersion: params.BeaconConfig().GenesisForkVersion, 199 CurrentVersion: params.BeaconConfig().GenesisForkVersion, 200 }, 201 ValidatorsRoot: [32]byte{'A'}, 202 Genesis: time.Unix(genTime, 0), 203 }, 204 DB: db, 205 }, 206 rateLimiter: newRateLimiter(p1), 207 } 208 digest, err := r.forkDigest() 209 require.NoError(t, err) 210 211 // Setup streams 212 pcl := protocol.ID("/testing") 213 topic := string(pcl) 214 r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false) 215 var wg sync.WaitGroup 216 wg.Add(1) 217 p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) { 218 defer wg.Done() 219 expectSuccess(t, stream) 220 out := &pb.Status{} 221 assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out)) 222 expected := &pb.Status{ 223 ForkDigest: digest[:], 224 HeadSlot: genesisState.Slot(), 225 HeadRoot: headRoot[:], 226 FinalizedEpoch: 3, 227 FinalizedRoot: finalizedRoot[:], 228 } 229 if !proto.Equal(out, expected) { 230 t.Errorf("Did not receive expected message. Got %+v wanted %+v", out, expected) 231 } 232 }) 233 stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl) 234 require.NoError(t, err) 235 236 err = r.statusRPCHandler(context.Background(), &pb.Status{ 237 ForkDigest: digest[:], 238 FinalizedRoot: finalizedRoot[:], 239 FinalizedEpoch: 3, 240 }, stream1) 241 assert.NoError(t, err) 242 243 if testutil.WaitTimeout(&wg, 1*time.Second) { 244 t.Fatal("Did not receive stream within 1 sec") 245 } 246 } 247 248 func TestHandshakeHandlers_Roundtrip(t *testing.T) { 249 // Scenario is that p1 and p2 connect, exchange handshakes. 250 // p2 disconnects and p1 should forget the handshake status. 251 p1 := p2ptest.NewTestP2P(t) 252 p2 := p2ptest.NewTestP2P(t) 253 db := testingDB.SetupDB(t) 254 255 p1.LocalMetadata = interfaces.WrappedMetadataV0(&pb.MetaDataV0{ 256 SeqNumber: 2, 257 Attnets: bytesutil.PadTo([]byte{'A', 'B'}, 8), 258 }) 259 260 p2.LocalMetadata = interfaces.WrappedMetadataV0(&pb.MetaDataV0{ 261 SeqNumber: 2, 262 Attnets: bytesutil.PadTo([]byte{'C', 'D'}, 8), 263 }) 264 265 st, err := v1.InitializeFromProto(&pb.BeaconState{ 266 Slot: 5, 267 }) 268 require.NoError(t, err) 269 blk := testutil.NewBeaconBlock() 270 blk.Block.Slot = 0 271 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk))) 272 finalizedRoot, err := blk.Block.HashTreeRoot() 273 require.NoError(t, err) 274 require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), finalizedRoot)) 275 r := &Service{ 276 cfg: &Config{ 277 P2P: p1, 278 Chain: &mock.ChainService{ 279 State: st, 280 FinalizedCheckPoint: ðpb.Checkpoint{Epoch: 0, Root: finalizedRoot[:]}, 281 Fork: &pb.Fork{ 282 PreviousVersion: params.BeaconConfig().GenesisForkVersion, 283 CurrentVersion: params.BeaconConfig().GenesisForkVersion, 284 }, 285 Genesis: time.Now(), 286 ValidatorsRoot: [32]byte{'A'}, 287 Root: make([]byte, 32), 288 }, 289 DB: db, 290 }, 291 ctx: context.Background(), 292 rateLimiter: newRateLimiter(p1), 293 } 294 p1.Digest, err = r.forkDigest() 295 require.NoError(t, err) 296 297 r2 := &Service{ 298 cfg: &Config{ 299 Chain: &mock.ChainService{ 300 FinalizedCheckPoint: ðpb.Checkpoint{Epoch: 0, Root: finalizedRoot[:]}, 301 }, 302 P2P: p2, 303 }, 304 rateLimiter: newRateLimiter(p2), 305 } 306 p2.Digest, err = r.forkDigest() 307 require.NoError(t, err) 308 309 r.Start() 310 311 // Setup streams 312 pcl := protocol.ID("/eth2/beacon_chain/req/status/1/ssz_snappy") 313 topic := string(pcl) 314 r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false) 315 var wg sync.WaitGroup 316 wg.Add(1) 317 p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) { 318 defer wg.Done() 319 out := &pb.Status{} 320 assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out)) 321 log.WithField("status", out).Warn("received status") 322 resp := &pb.Status{HeadSlot: 100, HeadRoot: make([]byte, 32), ForkDigest: p2.Digest[:], 323 FinalizedRoot: finalizedRoot[:], FinalizedEpoch: 0} 324 _, err := stream.Write([]byte{responseCodeSuccess}) 325 assert.NoError(t, err) 326 _, err = r.cfg.P2P.Encoding().EncodeWithMaxLength(stream, resp) 327 assert.NoError(t, err) 328 log.WithField("status", out).Warn("sending status") 329 if err := stream.Close(); err != nil { 330 t.Log(err) 331 } 332 }) 333 334 pcl = "/eth2/beacon_chain/req/ping/1/ssz_snappy" 335 topic = string(pcl) 336 r2.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false) 337 var wg2 sync.WaitGroup 338 wg2.Add(1) 339 p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) { 340 defer wg2.Done() 341 out := new(types.SSZUint64) 342 assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out)) 343 assert.Equal(t, uint64(2), uint64(*out)) 344 assert.NoError(t, r2.pingHandler(context.Background(), out, stream)) 345 assert.NoError(t, stream.Close()) 346 }) 347 348 numInactive1 := len(p1.Peers().Inactive()) 349 numActive1 := len(p1.Peers().Active()) 350 351 p1.Connect(p2) 352 353 p1.Peers().Add(new(enr.Record), p2.BHost.ID(), p2.BHost.Addrs()[0], network.DirUnknown) 354 p1.Peers().SetMetadata(p2.BHost.ID(), p2.LocalMetadata) 355 356 p2.Peers().Add(new(enr.Record), p1.BHost.ID(), p1.BHost.Addrs()[0], network.DirUnknown) 357 p2.Peers().SetMetadata(p1.BHost.ID(), p1.LocalMetadata) 358 359 if testutil.WaitTimeout(&wg, 1*time.Second) { 360 t.Fatal("Did not receive stream within 1 sec") 361 } 362 if testutil.WaitTimeout(&wg2, 1*time.Second) { 363 t.Fatal("Did not receive stream within 1 sec") 364 } 365 366 // Wait for stream buffer to be read. 367 time.Sleep(200 * time.Millisecond) 368 369 numInactive2 := len(p1.Peers().Inactive()) 370 numActive2 := len(p1.Peers().Active()) 371 372 assert.Equal(t, numInactive1, numInactive1, "Number of inactive peers changed unexpectedly") 373 assert.Equal(t, numActive1+1, numActive2, "Number of active peers unexpected") 374 375 require.NoError(t, p2.Disconnect(p1.PeerID())) 376 p1.Peers().SetConnectionState(p2.PeerID(), peers.PeerDisconnected) 377 378 // Wait for disconnect event to trigger. 379 time.Sleep(200 * time.Millisecond) 380 381 numInactive3 := len(p1.Peers().Inactive()) 382 numActive3 := len(p1.Peers().Active()) 383 assert.Equal(t, numInactive2+1, numInactive3, "Number of inactive peers unexpected") 384 assert.Equal(t, numActive2-1, numActive3, "Number of active peers unexpected") 385 } 386 387 func TestStatusRPCRequest_RequestSent(t *testing.T) { 388 p1 := p2ptest.NewTestP2P(t) 389 p2 := p2ptest.NewTestP2P(t) 390 391 // Set up a head state with data we expect. 392 head := testutil.NewBeaconBlock() 393 head.Block.Slot = 111 394 headRoot, err := head.Block.HashTreeRoot() 395 require.NoError(t, err) 396 finalized := testutil.NewBeaconBlock() 397 finalized.Block.Slot = 40 398 finalizedRoot, err := finalized.Block.HashTreeRoot() 399 require.NoError(t, err) 400 genesisState, err := state.GenesisBeaconState(context.Background(), nil, 0, ðpb.Eth1Data{}) 401 require.NoError(t, err) 402 require.NoError(t, genesisState.SetSlot(111)) 403 require.NoError(t, genesisState.UpdateBlockRootAtIndex(111%uint64(params.BeaconConfig().SlotsPerHistoricalRoot), headRoot)) 404 finalizedCheckpt := ðpb.Checkpoint{ 405 Epoch: 5, 406 Root: finalizedRoot[:], 407 } 408 409 r := &Service{ 410 cfg: &Config{ 411 P2P: p1, 412 Chain: &mock.ChainService{ 413 State: genesisState, 414 FinalizedCheckPoint: finalizedCheckpt, 415 Root: headRoot[:], 416 Fork: &pb.Fork{ 417 PreviousVersion: params.BeaconConfig().GenesisForkVersion, 418 CurrentVersion: params.BeaconConfig().GenesisForkVersion, 419 }, 420 Genesis: time.Now(), 421 ValidatorsRoot: [32]byte{'A'}, 422 }, 423 }, 424 ctx: context.Background(), 425 rateLimiter: newRateLimiter(p1), 426 } 427 428 // Setup streams 429 pcl := protocol.ID("/eth2/beacon_chain/req/status/1/ssz_snappy") 430 topic := string(pcl) 431 r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false) 432 var wg sync.WaitGroup 433 wg.Add(1) 434 p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) { 435 defer wg.Done() 436 out := &pb.Status{} 437 assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out)) 438 digest, err := r.forkDigest() 439 assert.NoError(t, err) 440 expected := &pb.Status{ 441 ForkDigest: digest[:], 442 HeadSlot: genesisState.Slot(), 443 HeadRoot: headRoot[:], 444 FinalizedEpoch: 5, 445 FinalizedRoot: finalizedRoot[:], 446 } 447 if !proto.Equal(out, expected) { 448 t.Errorf("Did not receive expected message. Got %+v wanted %+v", out, expected) 449 } 450 }) 451 452 p1.AddConnectionHandler(r.sendRPCStatusRequest, nil) 453 p1.Connect(p2) 454 455 if testutil.WaitTimeout(&wg, 1*time.Second) { 456 t.Fatal("Did not receive stream within 1 sec") 457 } 458 459 assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to continue being connected") 460 } 461 462 func TestStatusRPCRequest_FinalizedBlockExists(t *testing.T) { 463 p1 := p2ptest.NewTestP2P(t) 464 p2 := p2ptest.NewTestP2P(t) 465 db := testingDB.SetupDB(t) 466 467 // Set up a head state with data we expect. 468 head := testutil.NewBeaconBlock() 469 head.Block.Slot = 111 470 headRoot, err := head.Block.HashTreeRoot() 471 require.NoError(t, err) 472 blkSlot := 3 * params.BeaconConfig().SlotsPerEpoch 473 finalized := testutil.NewBeaconBlock() 474 finalized.Block.Slot = blkSlot 475 finalizedRoot, err := finalized.Block.HashTreeRoot() 476 require.NoError(t, err) 477 genesisState, err := state.GenesisBeaconState(context.Background(), nil, 0, ðpb.Eth1Data{DepositRoot: make([]byte, 32), BlockHash: make([]byte, 32)}) 478 require.NoError(t, err) 479 require.NoError(t, genesisState.SetSlot(111)) 480 require.NoError(t, genesisState.UpdateBlockRootAtIndex(111%uint64(params.BeaconConfig().SlotsPerHistoricalRoot), headRoot)) 481 blk := testutil.NewBeaconBlock() 482 blk.Block.Slot = blkSlot 483 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk))) 484 require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), finalizedRoot)) 485 finalizedCheckpt := ðpb.Checkpoint{ 486 Epoch: 3, 487 Root: finalizedRoot[:], 488 } 489 totalSec := int64(params.BeaconConfig().SlotsPerEpoch.Mul(5 * params.BeaconConfig().SecondsPerSlot)) 490 genTime := time.Now().Unix() - totalSec 491 r := &Service{ 492 cfg: &Config{ 493 P2P: p1, 494 Chain: &mock.ChainService{ 495 State: genesisState, 496 FinalizedCheckPoint: finalizedCheckpt, 497 Root: headRoot[:], 498 Fork: &pb.Fork{ 499 PreviousVersion: params.BeaconConfig().GenesisForkVersion, 500 CurrentVersion: params.BeaconConfig().GenesisForkVersion, 501 }, 502 Genesis: time.Unix(genTime, 0), 503 ValidatorsRoot: [32]byte{'A'}, 504 }, 505 }, 506 ctx: context.Background(), 507 rateLimiter: newRateLimiter(p1), 508 } 509 510 r2 := &Service{ 511 cfg: &Config{ 512 P2P: p1, 513 Chain: &mock.ChainService{ 514 State: genesisState, 515 FinalizedCheckPoint: finalizedCheckpt, 516 Root: headRoot[:], 517 Fork: &pb.Fork{ 518 PreviousVersion: params.BeaconConfig().GenesisForkVersion, 519 CurrentVersion: params.BeaconConfig().GenesisForkVersion, 520 }, 521 Genesis: time.Unix(genTime, 0), 522 ValidatorsRoot: [32]byte{'A'}, 523 }, 524 DB: db, 525 }, 526 ctx: context.Background(), 527 rateLimiter: newRateLimiter(p1), 528 } 529 530 // Setup streams 531 pcl := protocol.ID("/eth2/beacon_chain/req/status/1/ssz_snappy") 532 topic := string(pcl) 533 r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false) 534 var wg sync.WaitGroup 535 wg.Add(1) 536 p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) { 537 defer wg.Done() 538 out := &pb.Status{} 539 assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out)) 540 assert.NoError(t, r2.validateStatusMessage(context.Background(), out)) 541 }) 542 543 p1.AddConnectionHandler(r.sendRPCStatusRequest, nil) 544 p1.Connect(p2) 545 546 if testutil.WaitTimeout(&wg, 1*time.Second) { 547 t.Fatal("Did not receive stream within 1 sec") 548 } 549 550 assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to continue being connected") 551 } 552 553 func TestStatusRPCRequest_FinalizedBlockSkippedSlots(t *testing.T) { 554 db, err := kv.NewKVStore(context.Background(), t.TempDir(), &kv.Config{}) 555 require.NoError(t, err) 556 bState, err := state.GenesisBeaconState(context.Background(), nil, 0, ðpb.Eth1Data{DepositRoot: make([]byte, 32), BlockHash: make([]byte, 32)}) 557 require.NoError(t, err) 558 559 blk := testutil.NewBeaconBlock() 560 blk.Block.Slot = 0 561 genRoot, err := blk.Block.HashTreeRoot() 562 require.NoError(t, err) 563 564 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(blk))) 565 require.NoError(t, db.SaveGenesisBlockRoot(context.Background(), genRoot)) 566 blocksTillHead := makeBlocks(t, 1, 1000, genRoot) 567 require.NoError(t, db.SaveBlocks(context.Background(), blocksTillHead)) 568 569 stateSummaries := make([]*pb.StateSummary, len(blocksTillHead)) 570 for i, b := range blocksTillHead { 571 bRoot, err := b.Block().HashTreeRoot() 572 require.NoError(t, err) 573 stateSummaries[i] = &pb.StateSummary{ 574 Slot: b.Block().Slot(), 575 Root: bRoot[:], 576 } 577 } 578 require.NoError(t, db.SaveStateSummaries(context.Background(), stateSummaries)) 579 580 rootFetcher := func(slot types.Slot) [32]byte { 581 rt, err := blocksTillHead[slot-1].Block().HashTreeRoot() 582 require.NoError(t, err) 583 return rt 584 } 585 tests := []struct { 586 name string 587 expectedFinalizedEpoch types.Epoch 588 expectedFinalizedRoot [32]byte 589 headSlot types.Slot 590 remoteFinalizedEpoch types.Epoch 591 remoteFinalizedRoot [32]byte 592 remoteHeadSlot types.Slot 593 expectError bool 594 }{ 595 { 596 name: "valid finalized epoch", 597 expectedFinalizedEpoch: 3, 598 expectedFinalizedRoot: rootFetcher(3 * params.BeaconConfig().SlotsPerEpoch), 599 headSlot: 111, 600 remoteFinalizedEpoch: 3, 601 remoteFinalizedRoot: rootFetcher(3 * params.BeaconConfig().SlotsPerEpoch), 602 remoteHeadSlot: 100, 603 expectError: false, 604 }, 605 { 606 name: "invalid finalized epoch", 607 expectedFinalizedEpoch: 3, 608 expectedFinalizedRoot: rootFetcher(3 * params.BeaconConfig().SlotsPerEpoch), 609 headSlot: 111, 610 remoteFinalizedEpoch: 3, 611 // give an incorrect root relative to the finalized epoch. 612 remoteFinalizedRoot: rootFetcher(2 * params.BeaconConfig().SlotsPerEpoch), 613 remoteHeadSlot: 120, 614 expectError: true, 615 }, 616 { 617 name: "invalid finalized root", 618 expectedFinalizedEpoch: 3, 619 expectedFinalizedRoot: rootFetcher(3 * params.BeaconConfig().SlotsPerEpoch), 620 headSlot: 111, 621 remoteFinalizedEpoch: 3, 622 // give a bad finalized root, and the beacon node verifies that 623 // it is indeed incorrect. 624 remoteFinalizedRoot: [32]byte{'a', 'b', 'c'}, 625 remoteHeadSlot: 120, 626 expectError: true, 627 }, 628 } 629 630 for _, tt := range tests { 631 p1 := p2ptest.NewTestP2P(t) 632 p2 := p2ptest.NewTestP2P(t) 633 634 expectedFinalizedEpoch := tt.expectedFinalizedEpoch 635 headSlot := tt.headSlot 636 637 nState := bState.Copy() 638 // Set up a head state with data we expect. 639 head := blocksTillHead[len(blocksTillHead)-1] 640 headRoot, err := head.Block().HashTreeRoot() 641 require.NoError(t, err) 642 643 rHead := blocksTillHead[tt.remoteHeadSlot-1] 644 rHeadRoot, err := rHead.Block().HashTreeRoot() 645 require.NoError(t, err) 646 647 require.NoError(t, nState.SetSlot(headSlot)) 648 require.NoError(t, nState.UpdateBlockRootAtIndex(uint64(headSlot.ModSlot(params.BeaconConfig().SlotsPerHistoricalRoot)), headRoot)) 649 650 finalizedCheckpt := ðpb.Checkpoint{ 651 Epoch: expectedFinalizedEpoch, 652 Root: tt.expectedFinalizedRoot[:], 653 } 654 655 remoteFinalizedChkpt := ðpb.Checkpoint{ 656 Epoch: tt.remoteFinalizedEpoch, 657 Root: tt.remoteFinalizedRoot[:], 658 } 659 require.NoError(t, db.SaveFinalizedCheckpoint(context.Background(), finalizedCheckpt)) 660 661 epoch := expectedFinalizedEpoch.Add(2) 662 totalSec := uint64(params.BeaconConfig().SlotsPerEpoch.Mul(uint64(epoch) * params.BeaconConfig().SecondsPerSlot)) 663 genTime := time.Now().Unix() - int64(totalSec) 664 r := &Service{ 665 cfg: &Config{ 666 P2P: p1, 667 Chain: &mock.ChainService{ 668 State: nState, 669 FinalizedCheckPoint: remoteFinalizedChkpt, 670 Root: rHeadRoot[:], 671 Fork: &pb.Fork{ 672 PreviousVersion: params.BeaconConfig().GenesisForkVersion, 673 CurrentVersion: params.BeaconConfig().GenesisForkVersion, 674 }, 675 Genesis: time.Unix(genTime, 0), 676 ValidatorsRoot: [32]byte{'A'}, 677 }, 678 }, 679 ctx: context.Background(), 680 rateLimiter: newRateLimiter(p1), 681 } 682 683 r2 := &Service{ 684 cfg: &Config{ 685 P2P: p2, 686 Chain: &mock.ChainService{ 687 State: nState, 688 FinalizedCheckPoint: finalizedCheckpt, 689 Root: headRoot[:], 690 Fork: &pb.Fork{ 691 PreviousVersion: params.BeaconConfig().GenesisForkVersion, 692 CurrentVersion: params.BeaconConfig().GenesisForkVersion, 693 }, 694 Genesis: time.Unix(genTime, 0), 695 ValidatorsRoot: [32]byte{'A'}, 696 }, 697 DB: db, 698 }, 699 700 ctx: context.Background(), 701 rateLimiter: newRateLimiter(p1), 702 } 703 704 // Setup streams 705 pcl := protocol.ID("/eth2/beacon_chain/req/status/1/ssz_snappy") 706 topic := string(pcl) 707 r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false) 708 var wg sync.WaitGroup 709 wg.Add(1) 710 p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) { 711 defer wg.Done() 712 out := &pb.Status{} 713 assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out)) 714 assert.Equal(t, tt.expectError, r2.validateStatusMessage(context.Background(), out) != nil) 715 }) 716 717 p1.AddConnectionHandler(r.sendRPCStatusRequest, nil) 718 p1.Connect(p2) 719 720 if testutil.WaitTimeout(&wg, 1*time.Second) { 721 t.Fatal("Did not receive stream within 1 sec") 722 } 723 724 assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to continue being connected") 725 assert.NoError(t, p1.Disconnect(p2.PeerID())) 726 } 727 assert.NoError(t, db.Close()) 728 } 729 730 func TestStatusRPCRequest_BadPeerHandshake(t *testing.T) { 731 p1 := p2ptest.NewTestP2P(t) 732 p2 := p2ptest.NewTestP2P(t) 733 734 // Set up a head state with data we expect. 735 head := testutil.NewBeaconBlock() 736 head.Block.Slot = 111 737 headRoot, err := head.Block.HashTreeRoot() 738 require.NoError(t, err) 739 finalized := testutil.NewBeaconBlock() 740 finalizedRoot, err := finalized.Block.HashTreeRoot() 741 require.NoError(t, err) 742 genesisState, err := state.GenesisBeaconState(context.Background(), nil, 0, ðpb.Eth1Data{}) 743 require.NoError(t, err) 744 require.NoError(t, genesisState.SetSlot(111)) 745 require.NoError(t, genesisState.UpdateBlockRootAtIndex(111%uint64(params.BeaconConfig().SlotsPerHistoricalRoot), headRoot)) 746 finalizedCheckpt := ðpb.Checkpoint{ 747 Epoch: 5, 748 Root: finalizedRoot[:], 749 } 750 751 r := &Service{ 752 cfg: &Config{ 753 P2P: p1, 754 Chain: &mock.ChainService{ 755 State: genesisState, 756 FinalizedCheckPoint: finalizedCheckpt, 757 Root: headRoot[:], 758 Fork: &pb.Fork{ 759 PreviousVersion: params.BeaconConfig().GenesisForkVersion, 760 CurrentVersion: params.BeaconConfig().GenesisForkVersion, 761 }, 762 Genesis: time.Now(), 763 ValidatorsRoot: [32]byte{'A'}, 764 }, 765 }, 766 767 ctx: context.Background(), 768 rateLimiter: newRateLimiter(p1), 769 } 770 771 r.Start() 772 773 // Setup streams 774 pcl := protocol.ID("/eth2/beacon_chain/req/status/1/ssz_snappy") 775 topic := string(pcl) 776 r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1, 1, false) 777 var wg sync.WaitGroup 778 wg.Add(1) 779 p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) { 780 defer wg.Done() 781 out := &pb.Status{} 782 assert.NoError(t, r.cfg.P2P.Encoding().DecodeWithMaxLength(stream, out)) 783 expected := &pb.Status{ 784 ForkDigest: []byte{1, 1, 1, 1}, 785 HeadSlot: genesisState.Slot(), 786 HeadRoot: headRoot[:], 787 FinalizedEpoch: 5, 788 FinalizedRoot: finalizedRoot[:], 789 } 790 if _, err := stream.Write([]byte{responseCodeSuccess}); err != nil { 791 log.WithError(err).Debug("Could not write to stream") 792 } 793 _, err := r.cfg.P2P.Encoding().EncodeWithMaxLength(stream, expected) 794 assert.NoError(t, err) 795 }) 796 797 assert.Equal(t, false, p1.Peers().Scorers().IsBadPeer(p2.PeerID()), "Peer is marked as bad") 798 p1.Connect(p2) 799 800 if testutil.WaitTimeout(&wg, time.Second) { 801 t.Fatal("Did not receive stream within 1 sec") 802 } 803 time.Sleep(100 * time.Millisecond) 804 805 connectionState, err := p1.Peers().ConnectionState(p2.PeerID()) 806 require.NoError(t, err, "Could not obtain peer connection state") 807 assert.Equal(t, peers.PeerDisconnected, connectionState, "Expected peer to be disconnected") 808 809 assert.Equal(t, true, p1.Peers().Scorers().IsBadPeer(p2.PeerID()), "Peer is not marked as bad") 810 } 811 812 func TestStatusRPC_ValidGenesisMessage(t *testing.T) { 813 // Set up a head state with data we expect. 814 head := testutil.NewBeaconBlock() 815 head.Block.Slot = 111 816 headRoot, err := head.Block.HashTreeRoot() 817 require.NoError(t, err) 818 blkSlot := 3 * params.BeaconConfig().SlotsPerEpoch 819 finalized := testutil.NewBeaconBlock() 820 finalized.Block.Slot = blkSlot 821 finalizedRoot, err := finalized.Block.HashTreeRoot() 822 require.NoError(t, err) 823 genesisState, err := state.GenesisBeaconState(context.Background(), nil, 0, ðpb.Eth1Data{}) 824 require.NoError(t, err) 825 require.NoError(t, genesisState.SetSlot(111)) 826 require.NoError(t, genesisState.UpdateBlockRootAtIndex(111%uint64(params.BeaconConfig().SlotsPerHistoricalRoot), headRoot)) 827 finalizedCheckpt := ðpb.Checkpoint{ 828 Epoch: 5, 829 Root: finalizedRoot[:], 830 } 831 r := &Service{ 832 cfg: &Config{ 833 Chain: &mock.ChainService{ 834 State: genesisState, 835 FinalizedCheckPoint: finalizedCheckpt, 836 Root: headRoot[:], 837 Fork: &pb.Fork{ 838 PreviousVersion: params.BeaconConfig().GenesisForkVersion, 839 CurrentVersion: params.BeaconConfig().GenesisForkVersion, 840 }, 841 Genesis: time.Now(), 842 ValidatorsRoot: [32]byte{'A'}, 843 }, 844 }, 845 ctx: context.Background(), 846 } 847 digest, err := r.forkDigest() 848 require.NoError(t, err) 849 // There should be no error for a status message 850 // with a genesis checkpoint. 851 err = r.validateStatusMessage(r.ctx, &pb.Status{ 852 ForkDigest: digest[:], 853 FinalizedRoot: params.BeaconConfig().ZeroHash[:], 854 FinalizedEpoch: 0, 855 HeadRoot: headRoot[:], 856 HeadSlot: 111, 857 }) 858 require.NoError(t, err) 859 } 860 861 func TestShouldResync(t *testing.T) { 862 type args struct { 863 genesis time.Time 864 syncing bool 865 headSlot types.Slot 866 } 867 tests := []struct { 868 name string 869 args args 870 want bool 871 }{ 872 { 873 name: "genesis epoch should not resync when syncing is true", 874 args: args{ 875 headSlot: 31, 876 genesis: timeutils.Now(), 877 syncing: true, 878 }, 879 want: false, 880 }, 881 { 882 name: "genesis epoch should not resync when syncing is false", 883 args: args{ 884 headSlot: 31, 885 genesis: timeutils.Now(), 886 syncing: false, 887 }, 888 want: false, 889 }, 890 { 891 name: "two epochs behind, resync ok", 892 args: args{ 893 headSlot: 31, 894 genesis: timeutils.Now().Add(-1 * 96 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second), 895 syncing: false, 896 }, 897 want: true, 898 }, 899 { 900 name: "two epochs behind, already syncing", 901 args: args{ 902 headSlot: 31, 903 genesis: timeutils.Now().Add(-1 * 96 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second), 904 syncing: true, 905 }, 906 want: false, 907 }, 908 } 909 for _, tt := range tests { 910 headState, err := state.GenesisBeaconState(context.Background(), nil, 0, ðpb.Eth1Data{}) 911 require.NoError(t, err) 912 require.NoError(t, headState.SetSlot(tt.args.headSlot)) 913 r := &Service{ 914 cfg: &Config{ 915 Chain: &mock.ChainService{ 916 State: headState, 917 Genesis: tt.args.genesis, 918 }, 919 InitialSync: &mockSync.Sync{IsSyncing: tt.args.syncing}, 920 }, 921 ctx: context.Background(), 922 } 923 t.Run(tt.name, func(t *testing.T) { 924 if got := r.shouldReSync(); got != tt.want { 925 t.Errorf("shouldReSync() = %v, want %v", got, tt.want) 926 } 927 }) 928 } 929 } 930 931 func makeBlocks(t *testing.T, i, n uint64, previousRoot [32]byte) []interfaces2.SignedBeaconBlock { 932 blocks := make([]*ethpb.SignedBeaconBlock, n) 933 ifaceBlocks := make([]interfaces2.SignedBeaconBlock, n) 934 for j := i; j < n+i; j++ { 935 parentRoot := make([]byte, 32) 936 copy(parentRoot, previousRoot[:]) 937 blocks[j-i] = testutil.NewBeaconBlock() 938 blocks[j-i].Block.Slot = types.Slot(j + 1) 939 blocks[j-i].Block.ParentRoot = parentRoot 940 var err error 941 previousRoot, err = blocks[j-i].Block.HashTreeRoot() 942 require.NoError(t, err) 943 ifaceBlocks[j-i] = wrapper.WrappedPhase0SignedBeaconBlock(blocks[j-i]) 944 } 945 return ifaceBlocks 946 }