diff --git a/beacon-chain/attestation/BUILD.bazel b/beacon-chain/attestation/BUILD.bazel index 75e4df87f..e0e370dd9 100644 --- a/beacon-chain/attestation/BUILD.bazel +++ b/beacon-chain/attestation/BUILD.bazel @@ -38,6 +38,8 @@ go_test( "//shared/bytesutil:go_default_library", "//shared/hashutil:go_default_library", "//shared/params:go_default_library", + "//shared/testutil:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", + "@com_github_sirupsen_logrus//hooks/test:go_default_library", ], ) diff --git a/beacon-chain/attestation/service.go b/beacon-chain/attestation/service.go index 6994b3946..9bff9e51e 100644 --- a/beacon-chain/attestation/service.go +++ b/beacon-chain/attestation/service.go @@ -95,17 +95,12 @@ func (a *Service) IncomingAttestationFeed() *event.Feed { // Attestation` be the attestation with the highest slot number in `store` // from the validator with the given `validator_index` func (a *Service) LatestAttestation(ctx context.Context, index uint64) (*pb.Attestation, error) { - bState, err := a.beaconDB.HeadState(ctx) + validator, err := a.beaconDB.ValidatorFromState(ctx, index) if err != nil { return nil, err } - // return error if it's an invalid validator index. - if index >= uint64(len(bState.ValidatorRegistry)) { - return nil, fmt.Errorf("invalid validator index %d", index) - } - - pubKey := bytesutil.ToBytes48(bState.ValidatorRegistry[index].Pubkey) + pubKey := bytesutil.ToBytes48(validator.Pubkey) a.store.RLock() defer a.store.RUnlock() if _, exists := a.store.m[pubKey]; !exists { @@ -294,6 +289,15 @@ func (a *Service) updateAttestation(ctx context.Context, headRoot [32]byte, beac continue } + if i >= len(committee) { + log.Errorf("Bitfield points to an invalid index in the committee: bitfield %08b", bitfield) + continue + } + + if int(committee[i]) >= len(beaconState.ValidatorRegistry) { + log.Errorf("Index doesn't exist in validator registry: index %d", committee[i]) + } + // If the attestation came from this attester. We use the slot committee to find the // validator's actual index. pubkey := bytesutil.ToBytes48(beaconState.ValidatorRegistry[committee[i]].Pubkey) diff --git a/beacon-chain/attestation/service_test.go b/beacon-chain/attestation/service_test.go index 53c53f6ca..a376e4bed 100644 --- a/beacon-chain/attestation/service_test.go +++ b/beacon-chain/attestation/service_test.go @@ -14,7 +14,9 @@ import ( "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/testutil" "github.com/sirupsen/logrus" + logTest "github.com/sirupsen/logrus/hooks/test" ) func init() { @@ -412,6 +414,49 @@ func TestUpdateLatestAttestation_CacheEnabledAndHit(t *testing.T) { } } +func TestUpdateLatestAttestation_InvalidIndex(t *testing.T) { + beaconDB := internal.SetupDB(t) + hook := logTest.NewGlobal() + defer internal.TeardownDB(t, beaconDB) + ctx := context.Background() + + var validators []*pb.Validator + for i := 0; i < 64; i++ { + validators = append(validators, &pb.Validator{ + Pubkey: []byte{byte(i)}, + ActivationEpoch: params.BeaconConfig().GenesisEpoch, + ExitEpoch: params.BeaconConfig().GenesisEpoch + 10, + }) + } + + beaconState := &pb.BeaconState{ + Slot: params.BeaconConfig().GenesisSlot + 1, + ValidatorRegistry: validators, + } + block := &pb.BeaconBlock{ + Slot: params.BeaconConfig().GenesisSlot + 1, + } + if err := beaconDB.SaveBlock(block); err != nil { + t.Fatal(err) + } + if err := beaconDB.UpdateChainHead(ctx, block, beaconState); err != nil { + t.Fatal(err) + } + service := NewAttestationService(context.Background(), &Config{BeaconDB: beaconDB}) + attestation := &pb.Attestation{ + AggregationBitfield: []byte{0xC0}, + Data: &pb.AttestationData{ + Slot: params.BeaconConfig().GenesisSlot + 1, + Shard: 1, + }, + } + + if err := service.UpdateLatestAttestation(ctx, attestation); err != nil { + t.Fatalf("could not update latest attestation: %v", err) + } + testutil.AssertLogsContain(t, hook, "Bitfield points to an invalid index in the committee") +} + func TestUpdateLatestAttestation_BatchUpdate(t *testing.T) { beaconDB := internal.SetupDB(t) defer internal.TeardownDB(t, beaconDB) diff --git a/beacon-chain/core/state/transition.go b/beacon-chain/core/state/transition.go index 523d170d3..18145bc3a 100644 --- a/beacon-chain/core/state/transition.go +++ b/beacon-chain/core/state/transition.go @@ -376,7 +376,9 @@ func ProcessEpoch(ctx context.Context, state *pb.BeaconState, block *pb.BeaconBl } case epochsSinceFinality > 4: - log.WithField("epochSinceFinality", epochsSinceFinality).Info("Applying quadratic leak penalties") + if config.Logging { + log.WithField("epochSinceFinality", epochsSinceFinality).Info("Applying quadratic leak penalties") + } // Apply penalties for long inactive FFG source participants. state = bal.InactivityFFGSource( state,