From 508c5fcf2fb07b7de166edbb945b73605a1bf424 Mon Sep 17 00:00:00 2001 From: Nishant Das Date: Wed, 16 Dec 2020 00:27:05 +0800 Subject: [PATCH] More Efficient Validation of Proposer Index (#8107) * metric * make it better * make it better * gaz Co-authored-by: Raul Jordan --- beacon-chain/sync/metrics.go | 1 + beacon-chain/sync/validate_beacon_blocks.go | 27 ++++++-- .../sync/validate_beacon_blocks_test.go | 63 +++++++++++++++++++ 3 files changed, 86 insertions(+), 5 deletions(-) diff --git a/beacon-chain/sync/metrics.go b/beacon-chain/sync/metrics.go index bf362adf7f..b7af471c71 100644 --- a/beacon-chain/sync/metrics.go +++ b/beacon-chain/sync/metrics.go @@ -47,6 +47,7 @@ var ( Help: "Count the number of times a node resyncs.", }, ) + arrivalBlockPropagationHistogram = promauto.NewHistogram( prometheus.HistogramOpts{ Name: "block_arrival_latency_milliseconds", diff --git a/beacon-chain/sync/validate_beacon_blocks.go b/beacon-chain/sync/validate_beacon_blocks.go index ecf06e5924..c68f8e32b6 100644 --- a/beacon-chain/sync/validate_beacon_blocks.go +++ b/beacon-chain/sync/validate_beacon_blocks.go @@ -136,7 +136,8 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms log.WithError(err).WithField("blockSlot", blk.Block.Slot).Warn("Rejected block") return pubsub.ValidationReject } - + // Record attribute of valid block. + span.AddAttributes(trace.Int64Attribute("slotInEpoch", int64(blk.Block.Slot%params.BeaconConfig().SlotsPerEpoch))) msg.ValidatorData = blk // Used in downstream subscriber return pubsub.ValidationAccept } @@ -167,10 +168,26 @@ func (s *Service) validateBeaconBlock(ctx context.Context, blk *ethpb.SignedBeac s.setBadBlock(ctx, blockRoot) return err } - - parentState, err = state.ProcessSlots(ctx, parentState, blk.Block.Slot) - if err != nil { - return err + // There is an epoch lookahead for validator proposals + // for the next epoch from the start of our current epoch. We + // use the randao mix at the end of the previous epoch as the seed + // to determine proposals. + // Seed for Next Epoch => Derived From Randao Mix at the end of the Previous Epoch. + // Which is why we simply set the slot over here. + nextEpoch := helpers.NextEpoch(parentState) + expectedEpoch := helpers.SlotToEpoch(blk.Block.Slot) + if expectedEpoch <= nextEpoch { + err = parentState.SetSlot(blk.Block.Slot) + if err != nil { + return err + } + } else { + // In the event the block is more than an epoch ahead from its + // parent state, we have to advance the state forward. + parentState, err = state.ProcessSlots(ctx, parentState, blk.Block.Slot) + if err != nil { + return err + } } idx, err := helpers.BeaconProposerIndex(parentState) if err != nil { diff --git a/beacon-chain/sync/validate_beacon_blocks_test.go b/beacon-chain/sync/validate_beacon_blocks_test.go index fcb69af162..79af5a1eac 100644 --- a/beacon-chain/sync/validate_beacon_blocks_test.go +++ b/beacon-chain/sync/validate_beacon_blocks_test.go @@ -244,6 +244,69 @@ func TestValidateBeaconBlockPubSub_ValidProposerSignature(t *testing.T) { assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data") } +func TestValidateBeaconBlockPubSub_WithLookahead(t *testing.T) { + db, stateSummaryCache := dbtest.SetupDB(t) + p := p2ptest.NewTestP2P(t) + ctx := context.Background() + beaconState, privKeys := testutil.DeterministicGenesisState(t, 100) + parentBlock := testutil.NewBeaconBlock() + require.NoError(t, db.SaveBlock(ctx, parentBlock)) + bRoot, err := parentBlock.Block.HashTreeRoot() + require.NoError(t, err) + require.NoError(t, db.SaveState(ctx, beaconState, bRoot)) + require.NoError(t, db.SaveStateSummary(ctx, &pb.StateSummary{Root: bRoot[:]})) + copied := beaconState.Copy() + // The next block is only 1 epoch ahead so as to not induce a new seed. + blkSlot := params.BeaconConfig().SlotsPerEpoch * helpers.NextEpoch(copied) + copied, err = state.ProcessSlots(context.Background(), copied, blkSlot) + require.NoError(t, err) + proposerIdx, err := helpers.BeaconProposerIndex(copied) + require.NoError(t, err) + msg := testutil.NewBeaconBlock() + msg.Block.ProposerIndex = proposerIdx + msg.Block.Slot = blkSlot + msg.Block.ParentRoot = bRoot[:] + msg.Signature, err = helpers.ComputeDomainAndSign(beaconState, 0, msg.Block, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx]) + require.NoError(t, err) + + c, err := lru.New(10) + require.NoError(t, err) + c2, err := lru.New(10) + require.NoError(t, err) + stateGen := stategen.New(db, stateSummaryCache) + chainService := &mock.ChainService{Genesis: time.Unix(time.Now().Unix()-int64(blkSlot*params.BeaconConfig().SecondsPerSlot), 0), + State: beaconState, + FinalizedCheckPoint: ðpb.Checkpoint{ + Epoch: 0, + }} + r := &Service{ + db: db, + p2p: p, + initialSync: &mockSync.Sync{IsSyncing: false}, + chain: chainService, + blockNotifier: chainService.BlockNotifier(), + seenBlockCache: c, + badBlockCache: c2, + slotToPendingBlocks: gcache.New(time.Second, 2*time.Second), + seenPendingBlocks: make(map[[32]byte]bool), + stateSummaryCache: stateSummaryCache, + stateGen: stateGen, + } + buf := new(bytes.Buffer) + _, err = p.Encoding().EncodeGossip(buf, msg) + require.NoError(t, err) + topic := p2p.GossipTypeMapping[reflect.TypeOf(msg)] + m := &pubsub.Message{ + Message: &pubsubpb.Message{ + Data: buf.Bytes(), + Topic: &topic, + }, + } + result := r.validateBeaconBlockPubSub(ctx, "", m) == pubsub.ValidationAccept + assert.Equal(t, true, result) + assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data") +} + func TestValidateBeaconBlockPubSub_AdvanceEpochsForState(t *testing.T) { db, stateSummaryCache := dbtest.SetupDB(t) p := p2ptest.NewTestP2P(t)