From 8a6e2a5c6379b5012ec04537e069be721202bdbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kapka?= Date: Tue, 28 Sep 2021 18:54:12 +0200 Subject: [PATCH 01/25] Add `MIN_SYNC_COMMITTEE_PARTICIPANTS` parameter to config (#9689) Co-authored-by: Preston Van Loon --- beacon-chain/rpc/eth/beacon/config_test.go | 5 ++++- config/params/config.go | 3 +++ config/params/mainnet_config.go | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/beacon-chain/rpc/eth/beacon/config_test.go b/beacon-chain/rpc/eth/beacon/config_test.go index 5d2de74222a4..121b79054827 100644 --- a/beacon-chain/rpc/eth/beacon/config_test.go +++ b/beacon-chain/rpc/eth/beacon/config_test.go @@ -95,6 +95,7 @@ func TestGetSpec(t *testing.T) { config.MinSlashingPenaltyQuotientAltair = 68 config.ProportionalSlashingMultiplierAltair = 69 config.InactivityScoreRecoveryRate = 70 + config.MinSyncCommitteeParticipants = 71 var dbp [4]byte copy(dbp[:], []byte{'0', '0', '0', '1'}) @@ -124,7 +125,7 @@ func TestGetSpec(t *testing.T) { resp, err := server.GetSpec(context.Background(), &emptypb.Empty{}) require.NoError(t, err) - assert.Equal(t, 90, len(resp.Data)) + assert.Equal(t, 91, len(resp.Data)) for k, v := range resp.Data { switch k { case "CONFIG_NAME": @@ -291,6 +292,8 @@ func TestGetSpec(t *testing.T) { assert.Equal(t, "69", v) case "INACTIVITY_SCORE_RECOVERY_RATE": assert.Equal(t, "70", v) + case "MIN_SYNC_COMMITTEE_PARTICIPANTS": + assert.Equal(t, "71", v) case "PROPOSER_WEIGHT": assert.Equal(t, "8", v) case "DOMAIN_BEACON_PROPOSER": diff --git a/config/params/config.go b/config/params/config.go index 60a805c495e6..ea0c759901ac 100644 --- a/config/params/config.go +++ b/config/params/config.go @@ -172,6 +172,9 @@ type BeaconChainConfig struct { InactivityPenaltyQuotientAltair uint64 `yaml:"INACTIVITY_PENALTY_QUOTIENT_ALTAIR" spec:"true"` // InactivityPenaltyQuotientAltair for penalties during inactivity post Altair hard fork. MinSlashingPenaltyQuotientAltair uint64 `yaml:"MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR" spec:"true"` // MinSlashingPenaltyQuotientAltair for slashing penalties post Altair hard fork. ProportionalSlashingMultiplierAltair uint64 `yaml:"PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR" spec:"true"` // ProportionalSlashingMultiplierAltair for slashing penalties multiplier post Alair hard fork. + + // Light client + MinSyncCommitteeParticipants uint64 `yaml:"MIN_SYNC_COMMITTEE_PARTICIPANTS" spec:"true"` // MinSyncCommitteeParticipants defines the minimum amount of sync committee participants for which the light client acknowledges the signature. } // InitializeForkSchedule initializes the schedules forks baked into the config. diff --git a/config/params/mainnet_config.go b/config/params/mainnet_config.go index d00ddc28084b..48ae71f8adf8 100644 --- a/config/params/mainnet_config.go +++ b/config/params/mainnet_config.go @@ -230,4 +230,7 @@ var mainnetBeaconConfig = &BeaconChainConfig{ InactivityPenaltyQuotientAltair: 3 * 1 << 24, //50331648 MinSlashingPenaltyQuotientAltair: 64, ProportionalSlashingMultiplierAltair: 2, + + // Light client + MinSyncCommitteeParticipants: 1, } From 57f965df50c0087f075427b162b623b93051805f Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Tue, 28 Sep 2021 13:13:16 -0500 Subject: [PATCH 02/25] Include Slasher Receiving Methods (#9692) * first * add receive details * ensure most builds * add slasherkv changes * db iface additions * build * gaz * proper todo comment * terence comments * sig check * bad sig checks * proper lock issue * fix test * fix up tests --- beacon-chain/db/iface/interface.go | 3 + beacon-chain/db/slasherkv/pruning.go | 4 +- beacon-chain/slasher/BUILD.bazel | 8 + beacon-chain/slasher/helpers.go | 13 ++ beacon-chain/slasher/helpers_test.go | 66 ++++++ beacon-chain/slasher/queue.go | 83 +++++++ beacon-chain/slasher/queue_test.go | 71 ++++++ beacon-chain/slasher/receive.go | 220 ++++++++++++++++++ beacon-chain/slasher/receive_test.go | 333 +++++++++++++++++++++++++++ beacon-chain/slasher/service.go | 7 + 10 files changed, 806 insertions(+), 2 deletions(-) create mode 100644 beacon-chain/slasher/queue.go create mode 100644 beacon-chain/slasher/queue_test.go create mode 100644 beacon-chain/slasher/receive.go create mode 100644 beacon-chain/slasher/receive_test.go diff --git a/beacon-chain/db/iface/interface.go b/beacon-chain/db/iface/interface.go index cc602e21321a..f409b8517d1e 100644 --- a/beacon-chain/db/iface/interface.go +++ b/beacon-chain/db/iface/interface.go @@ -129,6 +129,9 @@ type SlasherDatabase interface { AttestationRecordForValidator( ctx context.Context, validatorIdx types.ValidatorIndex, targetEpoch types.Epoch, ) (*slashertypes.IndexedAttestationWrapper, error) + BlockProposalForValidator( + ctx context.Context, validatorIdx types.ValidatorIndex, slot types.Slot, + ) (*slashertypes.SignedBlockHeaderWrapper, error) CheckAttesterDoubleVotes( ctx context.Context, attestations []*slashertypes.IndexedAttestationWrapper, ) ([]*slashertypes.AttesterDoubleVote, error) diff --git a/beacon-chain/db/slasherkv/pruning.go b/beacon-chain/db/slasherkv/pruning.go index e1b16ad2296c..299193e8fea4 100644 --- a/beacon-chain/db/slasherkv/pruning.go +++ b/beacon-chain/db/slasherkv/pruning.go @@ -14,7 +14,7 @@ import ( // PruneAttestationsAtEpoch deletes all attestations from the slasher DB with target epoch // less than or equal to the specified epoch. func (s *Store) PruneAttestationsAtEpoch( - _ context.Context, maxEpoch types.Epoch, + ctx context.Context, maxEpoch types.Epoch, ) (numPruned uint, err error) { // We can prune everything less than the current epoch - history length. encodedEndPruneEpoch := fssz.MarshalUint64([]byte{}, uint64(maxEpoch)) @@ -85,7 +85,7 @@ func (s *Store) PruneAttestationsAtEpoch( // PruneProposalsAtEpoch deletes all proposals from the slasher DB with epoch // less than or equal to the specified epoch. func (s *Store) PruneProposalsAtEpoch( - _ context.Context, maxEpoch types.Epoch, + ctx context.Context, maxEpoch types.Epoch, ) (numPruned uint, err error) { var endPruneSlot types.Slot endPruneSlot, err = core.EndSlot(maxEpoch) diff --git a/beacon-chain/slasher/BUILD.bazel b/beacon-chain/slasher/BUILD.bazel index 9fad531edee0..e6a3bc773989 100644 --- a/beacon-chain/slasher/BUILD.bazel +++ b/beacon-chain/slasher/BUILD.bazel @@ -9,13 +9,18 @@ go_library( "metrics.go", "params.go", "process_slashings.go", + "queue.go", + "receive.go", "service.go", ], importpath = "github.com/prysmaticlabs/prysm/beacon-chain/slasher", visibility = ["//beacon-chain:__subpackages__"], deps = [ + "//async/event:go_default_library", "//beacon-chain/blockchain:go_default_library", + "//beacon-chain/core:go_default_library", "//beacon-chain/core/blocks:go_default_library", + "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/db:go_default_library", "//beacon-chain/operations/slashings:go_default_library", "//beacon-chain/slasher/types:go_default_library", @@ -41,9 +46,12 @@ go_test( "helpers_test.go", "params_test.go", "process_slashings_test.go", + "queue_test.go", + "receive_test.go", ], embed = [":go_default_library"], deps = [ + "//async/event:go_default_library", "//beacon-chain/blockchain/testing:go_default_library", "//beacon-chain/core/signing:go_default_library", "//beacon-chain/db/testing:go_default_library", diff --git a/beacon-chain/slasher/helpers.go b/beacon-chain/slasher/helpers.go index af3233fb2175..366e16acc1bf 100644 --- a/beacon-chain/slasher/helpers.go +++ b/beacon-chain/slasher/helpers.go @@ -1,6 +1,7 @@ package slasher import ( + "bytes" "strconv" types "github.com/prysmaticlabs/eth2-types" @@ -105,6 +106,18 @@ func validateAttestationIntegrity(att *ethpb.IndexedAttestation) bool { return sourceEpoch < targetEpoch } +// Validates the signed beacon block header integrity, ensuring we have no nil values. +func validateBlockHeaderIntegrity(header *ethpb.SignedBeaconBlockHeader) bool { + // If a signed block header is malformed, we drop it. + if header == nil || + header.Header == nil || + len(header.Signature) != params.BeaconConfig().BLSSignatureLength || + bytes.Equal(header.Signature, make([]byte, params.BeaconConfig().BLSSignatureLength)) { + return false + } + return true +} + func logAttesterSlashing(slashing *ethpb.AttesterSlashing) { indices := slice.IntersectionUint64(slashing.Attestation_1.AttestingIndices, slashing.Attestation_2.AttestingIndices) log.WithFields(logrus.Fields{ diff --git a/beacon-chain/slasher/helpers_test.go b/beacon-chain/slasher/helpers_test.go index 8c87f3740c8d..5e28398c9dee 100644 --- a/beacon-chain/slasher/helpers_test.go +++ b/beacon-chain/slasher/helpers_test.go @@ -406,6 +406,72 @@ func Test_validateAttestationIntegrity(t *testing.T) { } } +func Test_validateBlockHeaderIntegrity(t *testing.T) { + type args struct { + header *ethpb.SignedBeaconBlockHeader + } + fakeSig := make([]byte, 96) + copy(fakeSig, "hi") + tests := []struct { + name string + args args + want bool + }{ + { + name: "nil header", + args: args{ + header: nil, + }, + want: false, + }, + { + name: "nil inner header", + args: args{ + header: ðpb.SignedBeaconBlockHeader{}, + }, + want: false, + }, + { + name: "bad signature 1", + args: args{ + header: ðpb.SignedBeaconBlockHeader{Header: ðpb.BeaconBlockHeader{}, Signature: []byte("hi")}, + }, + want: false, + }, + { + name: "bad signature 2", + args: args{ + header: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{}, + Signature: make([]byte, params.BeaconConfig().BLSSignatureLength+1), + }, + }, + want: false, + }, + { + name: "empty signature", + args: args{ + header: ðpb.SignedBeaconBlockHeader{Header: ðpb.BeaconBlockHeader{}}, + }, + want: false, + }, + { + name: "OK", + args: args{ + header: ðpb.SignedBeaconBlockHeader{Header: ðpb.BeaconBlockHeader{}, Signature: fakeSig}, + }, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := validateBlockHeaderIntegrity(tt.args.header); got != tt.want { + t.Errorf("validateBlockHeaderIntegrity() = %v, want %v", got, tt.want) + } + }) + } +} + func Test_isDoubleProposal(t *testing.T) { type args struct { incomingSigningRoot [32]byte diff --git a/beacon-chain/slasher/queue.go b/beacon-chain/slasher/queue.go new file mode 100644 index 000000000000..1cb03e31c30a --- /dev/null +++ b/beacon-chain/slasher/queue.go @@ -0,0 +1,83 @@ +package slasher + +import ( + "sync" + + slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" +) + +// Struct for handling a thread-safe list of indexed attestation wrappers. +type attestationsQueue struct { + sync.RWMutex + items []*slashertypes.IndexedAttestationWrapper +} + +// Struct for handling a thread-safe list of beacon block header wrappers. +type blocksQueue struct { + lock sync.RWMutex + items []*slashertypes.SignedBlockHeaderWrapper +} + +func newAttestationsQueue() *attestationsQueue { + return &attestationsQueue{ + items: make([]*slashertypes.IndexedAttestationWrapper, 0), + } +} + +func newBlocksQueue() *blocksQueue { + return &blocksQueue{ + items: make([]*slashertypes.SignedBlockHeaderWrapper, 0), + } +} + +func (q *attestationsQueue) push(att *slashertypes.IndexedAttestationWrapper) { + q.Lock() + defer q.Unlock() + q.items = append(q.items, att) +} + +func (q *attestationsQueue) dequeue() []*slashertypes.IndexedAttestationWrapper { + q.Lock() + defer q.Unlock() + items := q.items + q.items = make([]*slashertypes.IndexedAttestationWrapper, 0) + return items +} + +func (q *attestationsQueue) size() int { + q.RLock() + defer q.RUnlock() + return len(q.items) +} + +func (q *attestationsQueue) extend(atts []*slashertypes.IndexedAttestationWrapper) { + q.Lock() + defer q.Unlock() + q.items = append(q.items, atts...) +} + +func (q *blocksQueue) push(blk *slashertypes.SignedBlockHeaderWrapper) { + q.lock.Lock() + defer q.lock.Unlock() + q.items = append(q.items, blk) +} + +func (q *blocksQueue) dequeue() []*slashertypes.SignedBlockHeaderWrapper { + q.lock.Lock() + defer q.lock.Unlock() + items := q.items + q.items = make([]*slashertypes.SignedBlockHeaderWrapper, 0) + return items +} + +func (q *blocksQueue) size() int { + q.lock.RLock() + defer q.lock.RUnlock() + return len(q.items) +} + +func (q *blocksQueue) extend(blks []*slashertypes.SignedBlockHeaderWrapper) { + q.lock.Lock() + defer q.lock.Unlock() + q.items = append(q.items, blks...) +} diff --git a/beacon-chain/slasher/queue_test.go b/beacon-chain/slasher/queue_test.go new file mode 100644 index 000000000000..4b7f211d5e1c --- /dev/null +++ b/beacon-chain/slasher/queue_test.go @@ -0,0 +1,71 @@ +package slasher + +import ( + "testing" + + types "github.com/prysmaticlabs/eth2-types" + slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" + "github.com/prysmaticlabs/prysm/testing/require" +) + +func Test_attestationsQueue(t *testing.T) { + t.Run("push_and_dequeue", func(tt *testing.T) { + attQueue := newAttestationsQueue() + wantedAtts := []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 1, []uint64{1}, make([]byte, 32)), + createAttestationWrapper(t, 1, 2, []uint64{1}, make([]byte, 32)), + } + attQueue.push(wantedAtts[0]) + attQueue.push(wantedAtts[1]) + require.DeepEqual(t, 2, attQueue.size()) + + received := attQueue.dequeue() + require.DeepEqual(t, 0, attQueue.size()) + require.DeepEqual(t, wantedAtts, received) + }) + + t.Run("extend_and_dequeue", func(tt *testing.T) { + attQueue := newAttestationsQueue() + wantedAtts := []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 1, []uint64{1}, make([]byte, 32)), + createAttestationWrapper(t, 1, 2, []uint64{1}, make([]byte, 32)), + } + attQueue.extend(wantedAtts) + require.DeepEqual(t, 2, attQueue.size()) + + received := attQueue.dequeue() + require.DeepEqual(t, 0, attQueue.size()) + require.DeepEqual(t, wantedAtts, received) + }) +} + +func Test_blocksQueue(t *testing.T) { + t.Run("push_and_dequeue", func(tt *testing.T) { + blkQueue := newBlocksQueue() + wantedBlks := []*slashertypes.SignedBlockHeaderWrapper{ + createProposalWrapper(t, 0, types.ValidatorIndex(1), make([]byte, 32)), + createProposalWrapper(t, 1, types.ValidatorIndex(1), make([]byte, 32)), + } + blkQueue.push(wantedBlks[0]) + blkQueue.push(wantedBlks[1]) + require.DeepEqual(t, 2, blkQueue.size()) + + received := blkQueue.dequeue() + require.DeepEqual(t, 0, blkQueue.size()) + require.DeepEqual(t, wantedBlks, received) + }) + + t.Run("extend_and_dequeue", func(tt *testing.T) { + blkQueue := newBlocksQueue() + wantedBlks := []*slashertypes.SignedBlockHeaderWrapper{ + createProposalWrapper(t, 0, types.ValidatorIndex(1), make([]byte, 32)), + createProposalWrapper(t, 1, types.ValidatorIndex(1), make([]byte, 32)), + } + blkQueue.extend(wantedBlks) + require.DeepEqual(t, 2, blkQueue.size()) + + received := blkQueue.dequeue() + require.DeepEqual(t, 0, blkQueue.size()) + require.DeepEqual(t, wantedBlks, received) + }) +} diff --git a/beacon-chain/slasher/receive.go b/beacon-chain/slasher/receive.go new file mode 100644 index 000000000000..b6b1bcb81613 --- /dev/null +++ b/beacon-chain/slasher/receive.go @@ -0,0 +1,220 @@ +package slasher + +import ( + "context" + "time" + + "github.com/pkg/errors" + types "github.com/prysmaticlabs/eth2-types" + "github.com/prysmaticlabs/prysm/beacon-chain/core" + slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/sirupsen/logrus" +) + +// Receive indexed attestations from some source event feed, +// validating their integrity before appending them to an attestation queue +// for batch processing in a separate routine. +func (s *Service) receiveAttestations(ctx context.Context, indexedAttsChan chan *ethpb.IndexedAttestation) { + sub := s.serviceCfg.IndexedAttestationsFeed.Subscribe(indexedAttsChan) + defer sub.Unsubscribe() + for { + select { + case att := <-indexedAttsChan: + if !validateAttestationIntegrity(att) { + continue + } + signingRoot, err := att.Data.HashTreeRoot() + if err != nil { + log.WithError(err).Error("Could not get hash tree root of attestation") + continue + } + attWrapper := &slashertypes.IndexedAttestationWrapper{ + IndexedAttestation: att, + SigningRoot: signingRoot, + } + s.attsQueue.push(attWrapper) + case err := <-sub.Err(): + log.WithError(err).Debug("Subscriber closed with error") + return + case <-ctx.Done(): + return + } + } +} + +// Receive beacon blocks from some source event feed, +func (s *Service) receiveBlocks(ctx context.Context, beaconBlockHeadersChan chan *ethpb.SignedBeaconBlockHeader) { + sub := s.serviceCfg.BeaconBlockHeadersFeed.Subscribe(beaconBlockHeadersChan) + defer sub.Unsubscribe() + for { + select { + case blockHeader := <-beaconBlockHeadersChan: + if !validateBlockHeaderIntegrity(blockHeader) { + continue + } + signingRoot, err := blockHeader.Header.HashTreeRoot() + if err != nil { + log.WithError(err).Error("Could not get hash tree root of signed block header") + continue + } + wrappedProposal := &slashertypes.SignedBlockHeaderWrapper{ + SignedBeaconBlockHeader: blockHeader, + SigningRoot: signingRoot, + } + s.blksQueue.push(wrappedProposal) + case err := <-sub.Err(): + log.WithError(err).Debug("Subscriber closed with error") + return + case <-ctx.Done(): + return + } + } +} + +// Process queued attestations every time an epoch ticker fires. We retrieve +// these attestations from a queue, then group them all by validator chunk index. +// This grouping will allow us to perform detection on batches of attestations +// per validator chunk index which can be done concurrently. +func (s *Service) processQueuedAttestations(ctx context.Context, slotTicker <-chan types.Slot) { + for { + select { + case currentSlot := <-slotTicker: + attestations := s.attsQueue.dequeue() + currentEpoch := core.SlotToEpoch(currentSlot) + // We take all the attestations in the queue and filter out + // those which are valid now and valid in the future. + validAtts, validInFuture, numDropped := s.filterAttestations(attestations, currentEpoch) + + deferredAttestationsTotal.Add(float64(len(validInFuture))) + droppedAttestationsTotal.Add(float64(numDropped)) + + // We add back those attestations that are valid in the future to the queue. + s.attsQueue.extend(validInFuture) + + log.WithFields(logrus.Fields{ + "currentSlot": currentSlot, + "currentEpoch": currentEpoch, + "numValidAtts": len(validAtts), + "numDeferredAtts": len(validInFuture), + "numDroppedAtts": numDropped, + }).Info("New slot, processing queued atts for slashing detection") + + start := time.Now() + // Save the attestation records to our database. + if err := s.serviceCfg.Database.SaveAttestationRecordsForValidators( + ctx, validAtts, + ); err != nil { + log.WithError(err).Error("Could not save attestation records to DB") + continue + } + + // Check for slashings. + // TODO(#8331): Detect slashings. + slashings := make([]*ethpb.AttesterSlashing, 0) + + // Process attester slashings by verifying their signatures, submitting + // to the beacon node's operations pool, and logging them. + if err := s.processAttesterSlashings(ctx, slashings); err != nil { + log.WithError(err).Error("Could not process attester slashings") + continue + } + + log.WithField("elapsed", time.Since(start)).Debug("Done checking slashable attestations") + + processedAttestationsTotal.Add(float64(len(validAtts))) + case <-ctx.Done(): + return + } + } +} + +// Process queued blocks every time an epoch ticker fires. We retrieve +// these blocks from a queue, then perform double proposal detection. +func (s *Service) processQueuedBlocks(ctx context.Context, slotTicker <-chan types.Slot) { + for { + select { + case currentSlot := <-slotTicker: + blocks := s.blksQueue.dequeue() + currentEpoch := core.SlotToEpoch(currentSlot) + + receivedBlocksTotal.Add(float64(len(blocks))) + + log.WithFields(logrus.Fields{ + "currentSlot": currentSlot, + "currentEpoch": currentEpoch, + "numBlocks": len(blocks), + }).Info("New slot, processing queued blocks for slashing detection") + + start := time.Now() + // Check for slashings. + // TODO(#8331): Detect slashings. + slashings := make([]*ethpb.ProposerSlashing, 0) + + // Process proposer slashings by verifying their signatures, submitting + // to the beacon node's operations pool, and logging them. + if err := s.processProposerSlashings(ctx, slashings); err != nil { + log.WithError(err).Error("Could not process proposer slashings") + continue + } + + log.WithField("elapsed", time.Since(start)).Debug("Done checking slashable blocks") + + processedBlocksTotal.Add(float64(len(blocks))) + case <-ctx.Done(): + return + } + } +} + +// Prunes slasher data on each slot tick to prevent unnecessary build-up of disk space usage. +func (s *Service) pruneSlasherData(ctx context.Context, slotTicker <-chan types.Slot) { + for { + select { + case <-slotTicker: + headEpoch := core.SlotToEpoch(s.serviceCfg.HeadStateFetcher.HeadSlot()) + if err := s.pruneSlasherDataWithinSlidingWindow(ctx, headEpoch); err != nil { + log.WithError(err).Error("Could not prune slasher data") + continue + } + case <-ctx.Done(): + return + } + } +} + +// Prunes slasher data by using a sliding window of [current_epoch - HISTORY_LENGTH, current_epoch]. +// All data before that window is unnecessary for slasher, so can be periodically deleted. +// Say HISTORY_LENGTH is 4 and we have data for epochs 0, 1, 2, 3. Once we hit epoch 4, the sliding window +// we care about is 1, 2, 3, 4, so we can delete data for epoch 0. +func (s *Service) pruneSlasherDataWithinSlidingWindow(ctx context.Context, currentEpoch types.Epoch) error { + var maxPruningEpoch types.Epoch + if currentEpoch >= s.params.historyLength { + maxPruningEpoch = currentEpoch - s.params.historyLength + } else { + // If the current epoch is less than the history length, we should not + // attempt to prune at all. + return nil + } + log.WithFields(logrus.Fields{ + "currentEpoch": currentEpoch, + "pruningAllBeforeEpoch": maxPruningEpoch, + }).Info("Pruning old attestations and proposals for slasher") + numPrunedAtts, err := s.serviceCfg.Database.PruneAttestationsAtEpoch( + ctx, maxPruningEpoch, + ) + if err != nil { + return errors.Wrap(err, "Could not prune attestations") + } + numPrunedProposals, err := s.serviceCfg.Database.PruneProposalsAtEpoch( + ctx, maxPruningEpoch, + ) + if err != nil { + return errors.Wrap(err, "Could not prune proposals") + } + log.WithFields(logrus.Fields{ + "prunedAttestationRecords": numPrunedAtts, + "prunedProposalRecords": numPrunedProposals, + }).Info("Successfully pruned slasher data") + return nil +} diff --git a/beacon-chain/slasher/receive_test.go b/beacon-chain/slasher/receive_test.go new file mode 100644 index 000000000000..3d12b5c676f6 --- /dev/null +++ b/beacon-chain/slasher/receive_test.go @@ -0,0 +1,333 @@ +package slasher + +import ( + "context" + "testing" + + types "github.com/prysmaticlabs/eth2-types" + "github.com/prysmaticlabs/prysm/async/event" + mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" + params2 "github.com/prysmaticlabs/prysm/config/params" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/testing/assert" + "github.com/prysmaticlabs/prysm/testing/require" + "github.com/prysmaticlabs/prysm/testing/util" + logTest "github.com/sirupsen/logrus/hooks/test" +) + +func TestSlasher_receiveAttestations_OK(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + s := &Service{ + serviceCfg: &ServiceConfig{ + IndexedAttestationsFeed: new(event.Feed), + StateNotifier: &mock.MockStateNotifier{}, + }, + attsQueue: newAttestationsQueue(), + } + indexedAttsChan := make(chan *ethpb.IndexedAttestation) + defer close(indexedAttsChan) + + exitChan := make(chan struct{}) + go func() { + s.receiveAttestations(ctx, indexedAttsChan) + exitChan <- struct{}{} + }() + firstIndices := []uint64{1, 2, 3} + secondIndices := []uint64{4, 5, 6} + att1 := createAttestationWrapper(t, 1, 2, firstIndices, nil) + att2 := createAttestationWrapper(t, 1, 2, secondIndices, nil) + indexedAttsChan <- att1.IndexedAttestation + indexedAttsChan <- att2.IndexedAttestation + cancel() + <-exitChan + wanted := []*slashertypes.IndexedAttestationWrapper{ + att1, + att2, + } + require.DeepEqual(t, wanted, s.attsQueue.dequeue()) +} + +func TestService_pruneSlasherDataWithinSlidingWindow_AttestationsPruned(t *testing.T) { + ctx := context.Background() + params := DefaultParams() + params.historyLength = 4 // 4 epochs worth of history. + slasherDB := dbtest.SetupSlasherDB(t) + s := &Service{ + serviceCfg: &ServiceConfig{ + Database: slasherDB, + }, + params: params, + } + + // Setup attestations for 2 validators at each epoch for epochs 0, 1, 2, 3. + err := slasherDB.SaveAttestationRecordsForValidators(ctx, []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 0, []uint64{0}, bytesutil.PadTo([]byte("0a"), 32)), + createAttestationWrapper(t, 0, 0, []uint64{1}, bytesutil.PadTo([]byte("0b"), 32)), + createAttestationWrapper(t, 0, 1, []uint64{0}, bytesutil.PadTo([]byte("1a"), 32)), + createAttestationWrapper(t, 0, 1, []uint64{1}, bytesutil.PadTo([]byte("1b"), 32)), + createAttestationWrapper(t, 0, 2, []uint64{0}, bytesutil.PadTo([]byte("2a"), 32)), + createAttestationWrapper(t, 0, 2, []uint64{1}, bytesutil.PadTo([]byte("2b"), 32)), + createAttestationWrapper(t, 0, 3, []uint64{0}, bytesutil.PadTo([]byte("3a"), 32)), + createAttestationWrapper(t, 0, 3, []uint64{1}, bytesutil.PadTo([]byte("3b"), 32)), + }) + require.NoError(t, err) + + // Attempt to prune and discover that all data is still intact. + currentEpoch := types.Epoch(3) + err = s.pruneSlasherDataWithinSlidingWindow(ctx, currentEpoch) + require.NoError(t, err) + + epochs := []types.Epoch{0, 1, 2, 3} + for _, epoch := range epochs { + att, err := slasherDB.AttestationRecordForValidator(ctx, types.ValidatorIndex(0), epoch) + require.NoError(t, err) + require.NotNil(t, att) + att, err = slasherDB.AttestationRecordForValidator(ctx, types.ValidatorIndex(1), epoch) + require.NoError(t, err) + require.NotNil(t, att) + } + + // Setup attestations for 2 validators at epoch 4. + err = slasherDB.SaveAttestationRecordsForValidators(ctx, []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 4, []uint64{0}, bytesutil.PadTo([]byte("4a"), 32)), + createAttestationWrapper(t, 0, 4, []uint64{1}, bytesutil.PadTo([]byte("4b"), 32)), + }) + require.NoError(t, err) + + // Attempt to prune again by setting current epoch to 4. + currentEpoch = types.Epoch(4) + err = s.pruneSlasherDataWithinSlidingWindow(ctx, currentEpoch) + require.NoError(t, err) + + // We should now only have data for epochs 1, 2, 3, 4. We should + // have pruned data from epoch 0. + att, err := slasherDB.AttestationRecordForValidator(ctx, types.ValidatorIndex(0), 0) + require.NoError(t, err) + require.Equal(t, true, att == nil) + att, err = slasherDB.AttestationRecordForValidator(ctx, types.ValidatorIndex(1), 0) + require.NoError(t, err) + require.Equal(t, true, att == nil) + + epochs = []types.Epoch{1, 2, 3, 4} + for _, epoch := range epochs { + att, err := slasherDB.AttestationRecordForValidator(ctx, types.ValidatorIndex(0), epoch) + require.NoError(t, err) + require.NotNil(t, att) + att, err = slasherDB.AttestationRecordForValidator(ctx, types.ValidatorIndex(1), epoch) + require.NoError(t, err) + require.NotNil(t, att) + } +} + +func TestService_pruneSlasherDataWithinSlidingWindow_ProposalsPruned(t *testing.T) { + ctx := context.Background() + + // Override beacon config to 1 slot per epoch for easier testing. + params2.SetupTestConfigCleanup(t) + config := params2.BeaconConfig() + config.SlotsPerEpoch = 1 + params2.OverrideBeaconConfig(config) + + params := DefaultParams() + params.historyLength = 4 // 4 epochs worth of history. + slasherDB := dbtest.SetupSlasherDB(t) + s := &Service{ + serviceCfg: &ServiceConfig{ + Database: slasherDB, + }, + params: params, + } + + // Setup block proposals for 2 validators at each epoch for epochs 0, 1, 2, 3. + err := slasherDB.SaveBlockProposals(ctx, []*slashertypes.SignedBlockHeaderWrapper{ + createProposalWrapper(t, 0, 0, bytesutil.PadTo([]byte("0a"), 32)), + createProposalWrapper(t, 0, 1, bytesutil.PadTo([]byte("0b"), 32)), + createProposalWrapper(t, 1, 0, bytesutil.PadTo([]byte("1a"), 32)), + createProposalWrapper(t, 1, 1, bytesutil.PadTo([]byte("1b"), 32)), + createProposalWrapper(t, 2, 0, bytesutil.PadTo([]byte("2a"), 32)), + createProposalWrapper(t, 2, 1, bytesutil.PadTo([]byte("2b"), 32)), + createProposalWrapper(t, 3, 0, bytesutil.PadTo([]byte("3a"), 32)), + createProposalWrapper(t, 3, 1, bytesutil.PadTo([]byte("3b"), 32)), + }) + require.NoError(t, err) + + // Attempt to prune and discover that all data is still intact. + currentEpoch := types.Epoch(3) + err = s.pruneSlasherDataWithinSlidingWindow(ctx, currentEpoch) + require.NoError(t, err) + + slots := []types.Slot{0, 1, 2, 3} + for _, slot := range slots { + blk, err := slasherDB.BlockProposalForValidator(ctx, types.ValidatorIndex(0), slot) + require.NoError(t, err) + require.NotNil(t, blk) + blk, err = slasherDB.BlockProposalForValidator(ctx, types.ValidatorIndex(1), slot) + require.NoError(t, err) + require.NotNil(t, blk) + } + + // Setup block proposals for 2 validators at epoch 4. + err = slasherDB.SaveBlockProposals(ctx, []*slashertypes.SignedBlockHeaderWrapper{ + createProposalWrapper(t, 4, 0, bytesutil.PadTo([]byte("4a"), 32)), + createProposalWrapper(t, 4, 1, bytesutil.PadTo([]byte("4b"), 32)), + }) + require.NoError(t, err) + + // Attempt to prune again by setting current epoch to 4. + currentEpoch = types.Epoch(4) + err = s.pruneSlasherDataWithinSlidingWindow(ctx, currentEpoch) + require.NoError(t, err) + + // We should now only have data for epochs 1, 2, 3, 4. We should + // have pruned data from epoch 0. + blk, err := slasherDB.BlockProposalForValidator(ctx, types.ValidatorIndex(0), 0) + require.NoError(t, err) + require.Equal(t, true, blk == nil) + blk, err = slasherDB.BlockProposalForValidator(ctx, types.ValidatorIndex(1), 0) + require.NoError(t, err) + require.Equal(t, true, blk == nil) + + slots = []types.Slot{1, 2, 3, 4} + for _, slot := range slots { + blk, err := slasherDB.BlockProposalForValidator(ctx, types.ValidatorIndex(0), slot) + require.NoError(t, err) + require.NotNil(t, blk) + blk, err = slasherDB.BlockProposalForValidator(ctx, types.ValidatorIndex(1), slot) + require.NoError(t, err) + require.NotNil(t, blk) + } +} + +func TestSlasher_receiveAttestations_OnlyValidAttestations(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + s := &Service{ + serviceCfg: &ServiceConfig{ + IndexedAttestationsFeed: new(event.Feed), + StateNotifier: &mock.MockStateNotifier{}, + }, + attsQueue: newAttestationsQueue(), + } + indexedAttsChan := make(chan *ethpb.IndexedAttestation) + defer close(indexedAttsChan) + + exitChan := make(chan struct{}) + defer close(exitChan) + go func() { + s.receiveAttestations(ctx, indexedAttsChan) + exitChan <- struct{}{} + }() + firstIndices := []uint64{1, 2, 3} + secondIndices := []uint64{4, 5, 6} + // Add a valid attestation. + validAtt := createAttestationWrapper(t, 1, 2, firstIndices, nil) + indexedAttsChan <- validAtt.IndexedAttestation + // Send an invalid, bad attestation which will not + // pass integrity checks at it has invalid attestation data. + indexedAttsChan <- ðpb.IndexedAttestation{ + AttestingIndices: secondIndices, + } + cancel() + <-exitChan + // Expect only a single, valid attestation was added to the queue. + require.Equal(t, 1, s.attsQueue.size()) + wanted := []*slashertypes.IndexedAttestationWrapper{ + validAtt, + } + require.DeepEqual(t, wanted, s.attsQueue.dequeue()) +} + +func TestSlasher_receiveBlocks_OK(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + s := &Service{ + serviceCfg: &ServiceConfig{ + BeaconBlockHeadersFeed: new(event.Feed), + StateNotifier: &mock.MockStateNotifier{}, + }, + blksQueue: newBlocksQueue(), + } + beaconBlockHeadersChan := make(chan *ethpb.SignedBeaconBlockHeader) + defer close(beaconBlockHeadersChan) + exitChan := make(chan struct{}) + go func() { + s.receiveBlocks(ctx, beaconBlockHeadersChan) + exitChan <- struct{}{} + }() + + block1 := createProposalWrapper(t, 0, 1, nil).SignedBeaconBlockHeader + block2 := createProposalWrapper(t, 0, 2, nil).SignedBeaconBlockHeader + beaconBlockHeadersChan <- block1 + beaconBlockHeadersChan <- block2 + cancel() + <-exitChan + wanted := []*slashertypes.SignedBlockHeaderWrapper{ + createProposalWrapper(t, 0, block1.Header.ProposerIndex, nil), + createProposalWrapper(t, 0, block2.Header.ProposerIndex, nil), + } + require.DeepEqual(t, wanted, s.blksQueue.dequeue()) +} + +func TestService_processQueuedBlocks(t *testing.T) { + hook := logTest.NewGlobal() + slasherDB := dbtest.SetupSlasherDB(t) + + beaconState, err := util.NewBeaconState() + require.NoError(t, err) + currentSlot := types.Slot(0) + require.NoError(t, beaconState.SetSlot(currentSlot)) + mockChain := &mock.ChainService{ + State: beaconState, + Slot: ¤tSlot, + } + + s := &Service{ + params: DefaultParams(), + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + HeadStateFetcher: mockChain, + }, + blksQueue: newBlocksQueue(), + } + s.blksQueue.extend([]*slashertypes.SignedBlockHeaderWrapper{ + createProposalWrapper(t, 0, 1, nil), + }) + ctx, cancel := context.WithCancel(context.Background()) + tickerChan := make(chan types.Slot) + exitChan := make(chan struct{}) + go func() { + s.processQueuedBlocks(ctx, tickerChan) + exitChan <- struct{}{} + }() + + // Send a value over the ticker. + tickerChan <- 0 + cancel() + <-exitChan + assert.LogsContain(t, hook, "New slot, processing queued") +} + +func createProposalWrapper(t *testing.T, slot types.Slot, proposerIndex types.ValidatorIndex, signingRoot []byte) *slashertypes.SignedBlockHeaderWrapper { + header := ðpb.BeaconBlockHeader{ + Slot: slot, + ProposerIndex: proposerIndex, + ParentRoot: params2.BeaconConfig().ZeroHash[:], + StateRoot: bytesutil.PadTo(signingRoot, 32), + BodyRoot: params2.BeaconConfig().ZeroHash[:], + } + signRoot, err := header.HashTreeRoot() + if err != nil { + t.Fatal(err) + } + someSig := make([]byte, params2.BeaconConfig().BLSSignatureLength) + copy(someSig, "foobar") + return &slashertypes.SignedBlockHeaderWrapper{ + SignedBeaconBlockHeader: ðpb.SignedBeaconBlockHeader{ + Header: header, + Signature: someSig, + }, + SigningRoot: signRoot, + } +} diff --git a/beacon-chain/slasher/service.go b/beacon-chain/slasher/service.go index 1956ddf59a48..066fc34fdf69 100644 --- a/beacon-chain/slasher/service.go +++ b/beacon-chain/slasher/service.go @@ -1,7 +1,9 @@ package slasher import ( + "github.com/prysmaticlabs/prysm/async/event" "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" + statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings" "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" @@ -14,8 +16,11 @@ var log = logrus.WithField("prefix", "slasher") type ServiceConfig struct { Database db.SlasherDatabase AttestationStateFetcher blockchain.AttestationStateFetcher + IndexedAttestationsFeed *event.Feed + BeaconBlockHeadersFeed *event.Feed StateGen stategen.StateManager SlashingPoolInserter slashings.PoolManager + StateNotifier statefeed.Notifier HeadStateFetcher blockchain.HeadFetcher } @@ -23,4 +28,6 @@ type ServiceConfig struct { type Service struct { params *Parameters serviceCfg *ServiceConfig + blksQueue *blocksQueue + attsQueue *attestationsQueue } From c32090aae577b85a72e307aaf0634aa62a60cee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kapka?= Date: Tue, 28 Sep 2021 21:07:32 +0200 Subject: [PATCH 03/25] Allow sending Altair blocks to `/eth/v1/beacon/blocks` (#9685) * Allow sending Altar blocks to `/eth/v1/beacon/blocks` * tests * add documentation * fix ineffectual assignment * change type of sync committee bits * remove unused import * fix Altair epoch calculation * compare slot against slot * do not publicly export E2E constant * tests for setInitialPublishBlockPostRequest Co-authored-by: terence tsao Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- api/gateway/apimiddleware/api_middleware.go | 8 +- beacon-chain/rpc/apimiddleware/BUILD.bazel | 5 + .../rpc/apimiddleware/custom_hooks.go | 93 ++++- .../rpc/apimiddleware/custom_hooks_test.go | 125 ++++-- .../rpc/apimiddleware/endpoint_factory.go | 4 +- beacon-chain/rpc/eth/beacon/blocks.go | 97 +++-- beacon-chain/rpc/eth/beacon/blocks_test.go | 114 ++++-- config/params/testnet_e2e_config.go | 4 +- proto/eth/service/beacon_chain_service.pb.go | 365 +++++++++--------- .../eth/service/beacon_chain_service.pb.gw.go | 4 +- proto/eth/service/beacon_chain_service.proto | 2 +- proto/eth/v2/beacon_block.pb.go | 178 ++++----- proto/eth/v2/beacon_block.proto | 2 +- proto/migration/BUILD.bazel | 1 + proto/migration/v1alpha1_to_v1.go | 17 +- proto/migration/v1alpha1_to_v1_test.go | 30 ++ testing/endtoend/evaluators/api_middleware.go | 8 +- testing/endtoend/evaluators/fork.go | 6 +- testing/endtoend/evaluators/validator.go | 11 +- testing/endtoend/helpers/helpers.go | 1 + testing/util/BUILD.bazel | 2 + testing/util/block.go | 54 +++ testing/util/block_test.go | 25 +- 23 files changed, 741 insertions(+), 415 deletions(-) diff --git a/api/gateway/apimiddleware/api_middleware.go b/api/gateway/apimiddleware/api_middleware.go index e4efc89d6959..fa082c395921 100644 --- a/api/gateway/apimiddleware/api_middleware.go +++ b/api/gateway/apimiddleware/api_middleware.go @@ -59,8 +59,8 @@ type CustomHandler = func(m *ApiProxyMiddleware, endpoint Endpoint, w http.Respo // HookCollection contains hooks that can be used to amend the default request/response cycle with custom logic for a specific endpoint. type HookCollection struct { - OnPreDeserializeRequestBodyIntoContainer func(endpoint Endpoint, w http.ResponseWriter, req *http.Request) (RunDefault, ErrorJson) - OnPostDeserializeRequestBodyIntoContainer func(endpoint Endpoint, w http.ResponseWriter, req *http.Request) ErrorJson + OnPreDeserializeRequestBodyIntoContainer func(endpoint *Endpoint, w http.ResponseWriter, req *http.Request) (RunDefault, ErrorJson) + OnPostDeserializeRequestBodyIntoContainer func(endpoint *Endpoint, w http.ResponseWriter, req *http.Request) ErrorJson OnPreDeserializeGrpcResponseBodyIntoContainer func([]byte, interface{}) (RunDefault, ErrorJson) OnPreSerializeMiddlewareResponseIntoJson func(interface{}) (RunDefault, []byte, ErrorJson) } @@ -170,7 +170,7 @@ func (m *ApiProxyMiddleware) handleApiPath(gatewayRouter *mux.Router, path strin func deserializeRequestBodyIntoContainerWrapped(endpoint *Endpoint, req *http.Request, w http.ResponseWriter) ErrorJson { runDefault := true if endpoint.Hooks.OnPreDeserializeRequestBodyIntoContainer != nil { - run, errJson := endpoint.Hooks.OnPreDeserializeRequestBodyIntoContainer(*endpoint, w, req) + run, errJson := endpoint.Hooks.OnPreDeserializeRequestBodyIntoContainer(endpoint, w, req) if errJson != nil { return errJson } @@ -184,7 +184,7 @@ func deserializeRequestBodyIntoContainerWrapped(endpoint *Endpoint, req *http.Re } } if endpoint.Hooks.OnPostDeserializeRequestBodyIntoContainer != nil { - if errJson := endpoint.Hooks.OnPostDeserializeRequestBodyIntoContainer(*endpoint, w, req); errJson != nil { + if errJson := endpoint.Hooks.OnPostDeserializeRequestBodyIntoContainer(endpoint, w, req); errJson != nil { return errJson } } diff --git a/beacon-chain/rpc/apimiddleware/BUILD.bazel b/beacon-chain/rpc/apimiddleware/BUILD.bazel index 0a947d8366eb..379c6294a5b8 100644 --- a/beacon-chain/rpc/apimiddleware/BUILD.bazel +++ b/beacon-chain/rpc/apimiddleware/BUILD.bazel @@ -14,11 +14,14 @@ go_library( deps = [ "//api/gateway/apimiddleware:go_default_library", "//api/grpc:go_default_library", + "//beacon-chain/core:go_default_library", "//beacon-chain/rpc/eth/events:go_default_library", + "//config/params:go_default_library", "//encoding/bytesutil:go_default_library", "//proto/eth/v2:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_pkg_errors//:go_default_library", + "@com_github_prysmaticlabs_eth2_types//:go_default_library", "@com_github_r3labs_sse//:go_default_library", ], ) @@ -34,7 +37,9 @@ go_test( deps = [ "//api/gateway/apimiddleware:go_default_library", "//api/grpc:go_default_library", + "//beacon-chain/core:go_default_library", "//beacon-chain/rpc/eth/events:go_default_library", + "//config/params:go_default_library", "//encoding/bytesutil:go_default_library", "//proto/eth/v2:go_default_library", "//testing/assert:go_default_library", diff --git a/beacon-chain/rpc/apimiddleware/custom_hooks.go b/beacon-chain/rpc/apimiddleware/custom_hooks.go index df7a09658395..a0a6e0bb030e 100644 --- a/beacon-chain/rpc/apimiddleware/custom_hooks.go +++ b/beacon-chain/rpc/apimiddleware/custom_hooks.go @@ -5,11 +5,15 @@ import ( "encoding/json" "io/ioutil" "net/http" + "strconv" "strings" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" + types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/api/gateway/apimiddleware" + "github.com/prysmaticlabs/prysm/beacon-chain/core" + "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/encoding/bytesutil" ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2" ) @@ -17,7 +21,7 @@ import ( // https://ethereum.github.io/beacon-apis/#/Beacon/submitPoolAttestations expects posting a top-level array. // We make it more proto-friendly by wrapping it in a struct with a 'data' field. func wrapAttestationsArray( - endpoint apimiddleware.Endpoint, + endpoint *apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request, ) (apimiddleware.RunDefault, apimiddleware.ErrorJson) { @@ -39,7 +43,7 @@ func wrapAttestationsArray( // Some endpoints e.g. https://ethereum.github.io/beacon-apis/#/Validator/getAttesterDuties expect posting a top-level array. // We make it more proto-friendly by wrapping it in a struct with an 'Index' field. func wrapValidatorIndicesArray( - endpoint apimiddleware.Endpoint, + endpoint *apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request, ) (apimiddleware.RunDefault, apimiddleware.ErrorJson) { @@ -61,7 +65,7 @@ func wrapValidatorIndicesArray( // https://ethereum.github.io/beacon-apis/#/Validator/publishAggregateAndProofs expects posting a top-level array. // We make it more proto-friendly by wrapping it in a struct with a 'data' field. func wrapSignedAggregateAndProofArray( - endpoint apimiddleware.Endpoint, + endpoint *apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request, ) (apimiddleware.RunDefault, apimiddleware.ErrorJson) { @@ -83,7 +87,7 @@ func wrapSignedAggregateAndProofArray( // https://ethereum.github.io/beacon-apis/#/Validator/prepareBeaconCommitteeSubnet expects posting a top-level array. // We make it more proto-friendly by wrapping it in a struct with a 'data' field. func wrapBeaconCommitteeSubscriptionsArray( - endpoint apimiddleware.Endpoint, + endpoint *apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request, ) (apimiddleware.RunDefault, apimiddleware.ErrorJson) { @@ -105,7 +109,7 @@ func wrapBeaconCommitteeSubscriptionsArray( // https://ethereum.github.io/beacon-APIs/#/Validator/prepareSyncCommitteeSubnets expects posting a top-level array. // We make it more proto-friendly by wrapping it in a struct with a 'data' field. func wrapSyncCommitteeSubscriptionsArray( - endpoint apimiddleware.Endpoint, + endpoint *apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request, ) (apimiddleware.RunDefault, apimiddleware.ErrorJson) { @@ -127,7 +131,7 @@ func wrapSyncCommitteeSubscriptionsArray( // https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolSyncCommitteeSignatures expects posting a top-level array. // We make it more proto-friendly by wrapping it in a struct with a 'data' field. func wrapSyncCommitteeSignaturesArray( - endpoint apimiddleware.Endpoint, + endpoint *apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request, ) (apimiddleware.RunDefault, apimiddleware.ErrorJson) { @@ -149,7 +153,7 @@ func wrapSyncCommitteeSignaturesArray( // https://ethereum.github.io/beacon-APIs/#/Validator/publishContributionAndProofs expects posting a top-level array. // We make it more proto-friendly by wrapping it in a struct with a 'data' field. func wrapSignedContributionAndProofsArray( - endpoint apimiddleware.Endpoint, + endpoint *apimiddleware.Endpoint, _ http.ResponseWriter, req *http.Request, ) (apimiddleware.RunDefault, apimiddleware.ErrorJson) { @@ -168,15 +172,82 @@ func wrapSignedContributionAndProofsArray( return true, nil } -// Posted graffiti needs to have length of 32 bytes, but client is allowed to send data of any length. -func prepareGraffiti(endpoint apimiddleware.Endpoint, _ http.ResponseWriter, _ *http.Request) apimiddleware.ErrorJson { +type phase0PublishBlockRequestJson struct { + Phase0Block *beaconBlockJson `json:"phase0_block"` + Signature string `json:"signature" hex:"true"` +} + +type altairPublishBlockRequestJson struct { + AltairBlock *beaconBlockAltairJson `json:"altair_block"` + Signature string `json:"signature" hex:"true"` +} + +// setInitialPublishBlockPostRequest is triggered before we deserialize the request JSON into a struct. +// We don't know which version of the block got posted, but we can determine it from the slot. +// We know that both Phase 0 and Altair blocks have a Message field with a Slot field, +// so we deserialize the request into a struct s, which has the right fields, to obtain the slot. +// Once we know the slot, we can determine what the PostRequest field of the endpoint should be, and we set it appropriately. +func setInitialPublishBlockPostRequest(endpoint *apimiddleware.Endpoint, + _ http.ResponseWriter, + req *http.Request, +) (apimiddleware.RunDefault, apimiddleware.ErrorJson) { + s := struct { + Message struct { + Slot string + } + }{} + + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + return false, apimiddleware.InternalServerErrorWithMessage(err, "could not read body") + } + if err := json.Unmarshal(buf, &s); err != nil { + return false, apimiddleware.InternalServerErrorWithMessage(err, "could not read slot from body") + } + slot, err := strconv.ParseUint(s.Message.Slot, 10, 64) + if err != nil { + return false, apimiddleware.InternalServerErrorWithMessage(err, "slot is not an unsigned integer") + } + if core.SlotToEpoch(types.Slot(slot)) < params.BeaconConfig().AltairForkEpoch { + endpoint.PostRequest = &signedBeaconBlockContainerJson{} + } else { + endpoint.PostRequest = &signedBeaconBlockAltairContainerJson{} + } + req.Body = ioutil.NopCloser(bytes.NewBuffer(buf)) + return true, nil +} + +// In preparePublishedBlock we transform the PostRequest. +// gRPC expects either a phase0_block or an altair_block field in the JSON object, but we have a message field at this point. +// We do a simple conversion depending on the type of endpoint.PostRequest (which was filled out previously in setInitialPublishBlockPostRequest) +func preparePublishedBlock(endpoint *apimiddleware.Endpoint, _ http.ResponseWriter, _ *http.Request) apimiddleware.ErrorJson { if block, ok := endpoint.PostRequest.(*signedBeaconBlockContainerJson); ok { - b := bytesutil.ToBytes32([]byte(block.Message.Body.Graffiti)) - block.Message.Body.Graffiti = hexutil.Encode(b[:]) + // Prepare post request that can be properly decoded on gRPC side. + actualPostReq := &phase0PublishBlockRequestJson{ + Phase0Block: block.Message, + Signature: block.Signature, + } + endpoint.PostRequest = actualPostReq + block.Message.Body.Graffiti = prepareGraffiti(block.Message.Body.Graffiti) + } + if block, ok := endpoint.PostRequest.(*signedBeaconBlockAltairContainerJson); ok { + // Prepare post request that can be properly decoded on gRPC side. + actualPostReq := &altairPublishBlockRequestJson{ + AltairBlock: block.Message, + Signature: block.Signature, + } + endpoint.PostRequest = actualPostReq + block.Message.Body.Graffiti = prepareGraffiti(block.Message.Body.Graffiti) } return nil } +// Posted graffiti needs to have length of 32 bytes, but client is allowed to send data of any length. +func prepareGraffiti(graffiti string) string { + b := bytesutil.ToBytes32([]byte(graffiti)) + return hexutil.Encode(b[:]) +} + type tempSyncCommitteesResponseJson struct { Data *tempSyncCommitteeValidatorsJson `json:"data"` } diff --git a/beacon-chain/rpc/apimiddleware/custom_hooks_test.go b/beacon-chain/rpc/apimiddleware/custom_hooks_test.go index 335a6111b7f0..8006dbbaf629 100644 --- a/beacon-chain/rpc/apimiddleware/custom_hooks_test.go +++ b/beacon-chain/rpc/apimiddleware/custom_hooks_test.go @@ -5,11 +5,15 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "reflect" + "strconv" "strings" "testing" "github.com/gogo/protobuf/types" "github.com/prysmaticlabs/prysm/api/gateway/apimiddleware" + "github.com/prysmaticlabs/prysm/beacon-chain/core" + "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/encoding/bytesutil" ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2" "github.com/prysmaticlabs/prysm/testing/assert" @@ -18,7 +22,7 @@ import ( func TestWrapAttestationArray(t *testing.T) { t.Run("ok", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &submitAttestationRequestJson{}, } unwrappedAtts := []*attestationJson{{AggregationBits: "1010"}} @@ -40,7 +44,7 @@ func TestWrapAttestationArray(t *testing.T) { }) t.Run("invalid_body", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &submitAttestationRequestJson{}, } var body bytes.Buffer @@ -58,7 +62,7 @@ func TestWrapAttestationArray(t *testing.T) { func TestWrapValidatorIndicesArray(t *testing.T) { t.Run("ok", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &dutiesRequestJson{}, } unwrappedIndices := []string{"1", "2"} @@ -81,7 +85,7 @@ func TestWrapValidatorIndicesArray(t *testing.T) { }) t.Run("invalid_body", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &dutiesRequestJson{}, } var body bytes.Buffer @@ -99,7 +103,7 @@ func TestWrapValidatorIndicesArray(t *testing.T) { func TestWrapSignedAggregateAndProofArray(t *testing.T) { t.Run("ok", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &submitAggregateAndProofsRequestJson{}, } unwrappedAggs := []*signedAggregateAttestationAndProofJson{{Signature: "sig"}} @@ -121,7 +125,7 @@ func TestWrapSignedAggregateAndProofArray(t *testing.T) { }) t.Run("invalid_body", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &submitAggregateAndProofsRequestJson{}, } var body bytes.Buffer @@ -139,7 +143,7 @@ func TestWrapSignedAggregateAndProofArray(t *testing.T) { func TestWrapBeaconCommitteeSubscriptionsArray(t *testing.T) { t.Run("ok", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &submitBeaconCommitteeSubscriptionsRequestJson{}, } unwrappedSubs := []*beaconCommitteeSubscribeJson{{ @@ -171,7 +175,7 @@ func TestWrapBeaconCommitteeSubscriptionsArray(t *testing.T) { }) t.Run("invalid_body", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &submitBeaconCommitteeSubscriptionsRequestJson{}, } var body bytes.Buffer @@ -189,7 +193,7 @@ func TestWrapBeaconCommitteeSubscriptionsArray(t *testing.T) { func TestWrapSyncCommitteeSubscriptionsArray(t *testing.T) { t.Run("ok", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &submitSyncCommitteeSubscriptionRequestJson{}, } unwrappedSubs := []*syncCommitteeSubscriptionJson{ @@ -226,7 +230,7 @@ func TestWrapSyncCommitteeSubscriptionsArray(t *testing.T) { }) t.Run("invalid_body", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &submitSyncCommitteeSubscriptionRequestJson{}, } var body bytes.Buffer @@ -244,7 +248,7 @@ func TestWrapSyncCommitteeSubscriptionsArray(t *testing.T) { func TestWrapSyncCommitteeSignaturesArray(t *testing.T) { t.Run("ok", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &submitSyncCommitteeSignaturesRequestJson{}, } unwrappedSigs := []*syncCommitteeMessageJson{{ @@ -274,7 +278,7 @@ func TestWrapSyncCommitteeSignaturesArray(t *testing.T) { }) t.Run("invalid_body", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &submitSyncCommitteeSignaturesRequestJson{}, } var body bytes.Buffer @@ -292,7 +296,7 @@ func TestWrapSyncCommitteeSignaturesArray(t *testing.T) { func TestWrapSignedContributionAndProofsArray(t *testing.T) { t.Run("ok", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &submitContributionAndProofsRequestJson{}, } unwrapped := []*signedContributionAndProofJson{ @@ -343,7 +347,7 @@ func TestWrapSignedContributionAndProofsArray(t *testing.T) { }) t.Run("invalid_body", func(t *testing.T) { - endpoint := apimiddleware.Endpoint{ + endpoint := &apimiddleware.Endpoint{ PostRequest: &submitContributionAndProofsRequestJson{}, } var body bytes.Buffer @@ -359,45 +363,96 @@ func TestWrapSignedContributionAndProofsArray(t *testing.T) { }) } -func TestPrepareGraffiti(t *testing.T) { - endpoint := apimiddleware.Endpoint{ - PostRequest: &signedBeaconBlockContainerJson{ - Message: &beaconBlockJson{ - Body: &beaconBlockBodyJson{}, +func TestSetInitialPublishBlockPostRequest(t *testing.T) { + endpoint := &apimiddleware.Endpoint{} + s := struct { + Message struct { + Slot string + } + }{} + t.Run("Phase 0", func(t *testing.T) { + s.Message = struct{ Slot string }{Slot: "0"} + j, err := json.Marshal(s) + require.NoError(t, err) + var body bytes.Buffer + _, err = body.Write(j) + require.NoError(t, err) + request := httptest.NewRequest("POST", "http://foo.example", &body) + runDefault, errJson := setInitialPublishBlockPostRequest(endpoint, nil, request) + require.Equal(t, true, errJson == nil) + assert.Equal(t, apimiddleware.RunDefault(true), runDefault) + assert.Equal(t, reflect.TypeOf(signedBeaconBlockContainerJson{}).Name(), reflect.Indirect(reflect.ValueOf(endpoint.PostRequest)).Type().Name()) + }) + t.Run("Altair", func(t *testing.T) { + slot, err := core.StartSlot(params.BeaconConfig().AltairForkEpoch) + require.NoError(t, err) + s.Message = struct{ Slot string }{Slot: strconv.FormatUint(uint64(slot), 10)} + j, err := json.Marshal(s) + require.NoError(t, err) + var body bytes.Buffer + _, err = body.Write(j) + require.NoError(t, err) + request := httptest.NewRequest("POST", "http://foo.example", &body) + runDefault, errJson := setInitialPublishBlockPostRequest(endpoint, nil, request) + require.Equal(t, true, errJson == nil) + assert.Equal(t, apimiddleware.RunDefault(true), runDefault) + assert.Equal(t, reflect.TypeOf(signedBeaconBlockAltairContainerJson{}).Name(), reflect.Indirect(reflect.ValueOf(endpoint.PostRequest)).Type().Name()) + }) +} + +func TestPreparePublishedBlock(t *testing.T) { + t.Run("Phase 0", func(t *testing.T) { + endpoint := &apimiddleware.Endpoint{ + PostRequest: &signedBeaconBlockContainerJson{ + Message: &beaconBlockJson{ + Body: &beaconBlockBodyJson{}, + }, }, - }, - } + } + preparePublishedBlock(endpoint, nil, nil) + _, ok := endpoint.PostRequest.(*phase0PublishBlockRequestJson) + assert.Equal(t, true, ok) + }) - t.Run("32_bytes", func(t *testing.T) { - endpoint.PostRequest.(*signedBeaconBlockContainerJson).Message.Body.Graffiti = string(bytesutil.PadTo([]byte("foo"), 32)) + t.Run("Altair", func(t *testing.T) { + endpoint := &apimiddleware.Endpoint{ + PostRequest: &signedBeaconBlockAltairContainerJson{ + Message: &beaconBlockAltairJson{ + Body: &beaconBlockBodyAltairJson{}, + }, + }, + } + preparePublishedBlock(endpoint, nil, nil) + _, ok := endpoint.PostRequest.(*altairPublishBlockRequestJson) + assert.Equal(t, true, ok) + }) +} - prepareGraffiti(endpoint, nil, nil) +func TestPrepareGraffiti(t *testing.T) { + t.Run("32_bytes", func(t *testing.T) { + result := prepareGraffiti(string(bytesutil.PadTo([]byte("foo"), 32))) assert.Equal( t, "0x666f6f0000000000000000000000000000000000000000000000000000000000", - endpoint.PostRequest.(*signedBeaconBlockContainerJson).Message.Body.Graffiti, + result, ) }) - t.Run("less_than_32_bytes", func(t *testing.T) { - endpoint.PostRequest.(*signedBeaconBlockContainerJson).Message.Body.Graffiti = "foo" - - prepareGraffiti(endpoint, nil, nil) + t.Run("graffiti_less_than_32_bytes", func(t *testing.T) { + result := prepareGraffiti("foo") assert.Equal( t, "0x666f6f0000000000000000000000000000000000000000000000000000000000", - endpoint.PostRequest.(*signedBeaconBlockContainerJson).Message.Body.Graffiti, + result, ) }) - t.Run("more_than_32_bytes", func(t *testing.T) { - endpoint.PostRequest.(*signedBeaconBlockContainerJson).Message.Body.Graffiti = string(bytesutil.PadTo([]byte("foo"), 33)) - - prepareGraffiti(endpoint, nil, nil) + t.Run("graffiti_more_than_32_bytes", func(t *testing.T) { + result := prepareGraffiti(string(bytesutil.PadTo([]byte("foo"), 33))) assert.Equal( t, "0x666f6f0000000000000000000000000000000000000000000000000000000000", - endpoint.PostRequest.(*signedBeaconBlockContainerJson).Message.Body.Graffiti, + result, ) }) } diff --git a/beacon-chain/rpc/apimiddleware/endpoint_factory.go b/beacon-chain/rpc/apimiddleware/endpoint_factory.go index 99e6ecf62ab2..3a4e64d70606 100644 --- a/beacon-chain/rpc/apimiddleware/endpoint_factory.go +++ b/beacon-chain/rpc/apimiddleware/endpoint_factory.go @@ -101,9 +101,9 @@ func (f *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, er case "/eth/v1/beacon/headers/{block_id}": endpoint.GetResponse = &blockHeaderResponseJson{} case "/eth/v1/beacon/blocks": - endpoint.PostRequest = &signedBeaconBlockContainerJson{} endpoint.Hooks = apimiddleware.HookCollection{ - OnPostDeserializeRequestBodyIntoContainer: prepareGraffiti, + OnPreDeserializeRequestBodyIntoContainer: setInitialPublishBlockPostRequest, + OnPostDeserializeRequestBodyIntoContainer: preparePublishedBlock, } case "/eth/v1/beacon/blocks/{block_id}": endpoint.GetResponse = &blockResponseJson{} diff --git a/beacon-chain/rpc/eth/beacon/blocks.go b/beacon-chain/rpc/eth/beacon/blocks.go index decf65a4a14c..1fdbfa37a962 100644 --- a/beacon-chain/rpc/eth/beacon/blocks.go +++ b/beacon-chain/rpc/eth/beacon/blocks.go @@ -144,39 +144,80 @@ func (bs *Server) ListBlockHeaders(ctx context.Context, req *ethpbv1.BlockHeader // response (20X) only indicates that the broadcast has been successful. The beacon node is expected to integrate the // new block into its state, and therefore validate the block internally, however blocks which fail the validation are // still broadcast but a different status code is returned (202). -func (bs *Server) SubmitBlock(ctx context.Context, req *ethpbv1.BeaconBlockContainer) (*emptypb.Empty, error) { +func (bs *Server) SubmitBlock(ctx context.Context, req *ethpbv2.SignedBeaconBlockContainerV2) (*emptypb.Empty, error) { ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlock") defer span.End() - blk := req.Message - rBlock, err := migration.V1ToV1Alpha1SignedBlock(ðpbv1.SignedBeaconBlock{Block: blk, Signature: req.Signature}) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block") - } - v1alpha1Block := wrapper.WrappedPhase0SignedBeaconBlock(rBlock) + phase0BlkContainer, ok := req.Message.(*ethpbv2.SignedBeaconBlockContainerV2_Phase0Block) + if ok { + phase0Blk := phase0BlkContainer.Phase0Block + v1alpha1Blk, err := migration.V1ToV1Alpha1SignedBlock(ðpbv1.SignedBeaconBlock{Block: phase0Blk, Signature: req.Signature}) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block") + } + wrappedPhase0Blk := wrapper.WrappedPhase0SignedBeaconBlock(v1alpha1Blk) - root, err := blk.HashTreeRoot() - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err) - } + root, err := phase0Blk.HashTreeRoot() + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err) + } - // Do not block proposal critical path with debug logging or block feed updates. - defer func() { - log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf( - "Block proposal received via RPC") - bs.BlockNotifier.BlockFeed().Send(&feed.Event{ - Type: blockfeed.ReceivedBlock, - Data: &blockfeed.ReceivedBlockData{SignedBlock: v1alpha1Block}, - }) - }() + // Do not block proposal critical path with debug logging or block feed updates. + defer func() { + log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf( + "Block proposal received via RPC") + bs.BlockNotifier.BlockFeed().Send(&feed.Event{ + Type: blockfeed.ReceivedBlock, + Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedPhase0Blk}, + }) + }() + + // Broadcast the new block to the network. + if err := bs.Broadcaster.Broadcast(ctx, wrappedPhase0Blk.Proto()); err != nil { + return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err) + } - // Broadcast the new block to the network. - if err := bs.Broadcaster.Broadcast(ctx, v1alpha1Block.Proto()); err != nil { - return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err) - } + if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedPhase0Blk, root); err != nil { + return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err) + } + } else { + altairBlkContainer, ok := req.Message.(*ethpbv2.SignedBeaconBlockContainerV2_AltairBlock) + if !ok { + return nil, status.Errorf(codes.InvalidArgument, "Could not get Altair block from request") + } + altairBlk := altairBlkContainer.AltairBlock + v1alpha1Blk, err := migration.AltairToV1Alpha1SignedBlock(ðpbv2.SignedBeaconBlockAltair{Message: altairBlk, Signature: req.Signature}) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block") + } + wrappedAltairBlk, err := wrapper.WrappedAltairSignedBeaconBlock(v1alpha1Blk) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Could not prepare Altair block") + } - if err := bs.BlockReceiver.ReceiveBlock(ctx, v1alpha1Block, root); err != nil { - return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err) + root, err := altairBlk.HashTreeRoot() + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err) + } + + // Do not block proposal critical path with debug logging or block feed updates. + defer func() { + log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf( + "Block proposal received via RPC") + bs.BlockNotifier.BlockFeed().Send(&feed.Event{ + Type: blockfeed.ReceivedBlock, + Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedAltairBlk}, + }) + }() + + // Broadcast the new block to the network. + if err := bs.Broadcaster.Broadcast(ctx, wrappedAltairBlk.Proto()); err != nil { + return nil, status.Errorf(codes.Internal, "Could not broadcast block: %v", err) + } + + if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedAltairBlk, root); err != nil { + return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err) + } } return &emptypb.Empty{}, nil @@ -245,7 +286,7 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) ( return ðpbv2.BlockResponseV2{ Version: ethpbv2.Version_PHASE0, Data: ðpbv2.SignedBeaconBlockContainerV2{ - Block: ðpbv2.SignedBeaconBlockContainerV2_Phase0Block{Phase0Block: v1Blk.Block}, + Message: ðpbv2.SignedBeaconBlockContainerV2_Phase0Block{Phase0Block: v1Blk.Block}, Signature: v1Blk.Signature, }, }, nil @@ -261,7 +302,7 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) ( return ðpbv2.BlockResponseV2{ Version: ethpbv2.Version_ALTAIR, Data: ðpbv2.SignedBeaconBlockContainerV2{ - Block: ðpbv2.SignedBeaconBlockContainerV2_AltairBlock{AltairBlock: v2Blk}, + Message: ðpbv2.SignedBeaconBlockContainerV2_AltairBlock{AltairBlock: v2Blk}, Signature: blk.Signature(), }, }, nil diff --git a/beacon-chain/rpc/eth/beacon/blocks_test.go b/beacon-chain/rpc/eth/beacon/blocks_test.go index c81ceb6403de..fe807ab39df5 100644 --- a/beacon-chain/rpc/eth/beacon/blocks_test.go +++ b/beacon-chain/rpc/eth/beacon/blocks_test.go @@ -10,7 +10,6 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/db" dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" mockp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" - "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/encoding/bytesutil" ethpbv1 "github.com/prysmaticlabs/prysm/proto/eth/v1" ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2" @@ -290,42 +289,83 @@ func TestServer_ListBlockHeaders(t *testing.T) { } func TestServer_ProposeBlock_OK(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - params.SetupTestConfigCleanup(t) - params.OverrideBeaconConfig(params.MainnetConfig()) + t.Run("Phase 0", func(t *testing.T) { + beaconDB := dbTest.SetupDB(t) + ctx := context.Background() - genesis := util.NewBeaconBlock() - require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(genesis)), "Could not save genesis block") + genesis := util.NewBeaconBlock() + require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(genesis)), "Could not save genesis block") - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - } - req := util.NewBeaconBlock() - req.Block.Slot = 5 - req.Block.ParentRoot = bsRoot[:] - v1Block, err := migration.V1Alpha1ToV1SignedBlock(req) - require.NoError(t, err) - require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(req))) - blockReq := ðpbv1.BeaconBlockContainer{ - Message: v1Block.Block, - Signature: v1Block.Signature, - } - _, err = beaconChainServer.SubmitBlock(context.Background(), blockReq) - assert.NoError(t, err, "Could not propose block correctly") + numDeposits := uint64(64) + beaconState, _ := util.DeterministicGenesisState(t, numDeposits) + bsRoot, err := beaconState.HashTreeRoot(ctx) + require.NoError(t, err) + genesisRoot, err := genesis.Block.HashTreeRoot() + require.NoError(t, err) + require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") + + c := &mock.ChainService{Root: bsRoot[:], State: beaconState} + beaconChainServer := &Server{ + BeaconDB: beaconDB, + BlockReceiver: c, + ChainInfoFetcher: c, + BlockNotifier: c.BlockNotifier(), + Broadcaster: mockp2p.NewTestP2P(t), + } + req := util.NewBeaconBlock() + req.Block.Slot = 5 + req.Block.ParentRoot = bsRoot[:] + v1Block, err := migration.V1Alpha1ToV1SignedBlock(req) + require.NoError(t, err) + require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(req))) + blockReq := ðpbv2.SignedBeaconBlockContainerV2{ + Message: ðpbv2.SignedBeaconBlockContainerV2_Phase0Block{Phase0Block: v1Block.Block}, + Signature: v1Block.Signature, + } + _, err = beaconChainServer.SubmitBlock(context.Background(), blockReq) + assert.NoError(t, err, "Could not propose block correctly") + }) + + t.Run("Altair", func(t *testing.T) { + beaconDB := dbTest.SetupDB(t) + ctx := context.Background() + + genesis := util.NewBeaconBlockAltair() + wrapped, err := wrapper.WrappedAltairSignedBeaconBlock(genesis) + require.NoError(t, err) + require.NoError(t, beaconDB.SaveBlock(context.Background(), wrapped), "Could not save genesis block") + + numDeposits := uint64(64) + beaconState, _ := util.DeterministicGenesisState(t, numDeposits) + bsRoot, err := beaconState.HashTreeRoot(ctx) + require.NoError(t, err) + genesisRoot, err := genesis.Block.HashTreeRoot() + require.NoError(t, err) + require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") + + c := &mock.ChainService{Root: bsRoot[:], State: beaconState} + beaconChainServer := &Server{ + BeaconDB: beaconDB, + BlockReceiver: c, + ChainInfoFetcher: c, + BlockNotifier: c.BlockNotifier(), + Broadcaster: mockp2p.NewTestP2P(t), + } + req := util.NewBeaconBlockAltair() + req.Block.Slot = 5 + req.Block.ParentRoot = bsRoot[:] + v2Block, err := migration.V1Alpha1BeaconBlockAltairToV2(req.Block) + require.NoError(t, err) + wrapped, err = wrapper.WrappedAltairSignedBeaconBlock(req) + require.NoError(t, err) + require.NoError(t, beaconDB.SaveBlock(ctx, wrapped)) + blockReq := ðpbv2.SignedBeaconBlockContainerV2{ + Message: ðpbv2.SignedBeaconBlockContainerV2_AltairBlock{AltairBlock: v2Block}, + Signature: req.Signature, + } + _, err = beaconChainServer.SubmitBlock(context.Background(), blockReq) + assert.NoError(t, err, "Could not propose block correctly") + }) } func TestServer_GetBlock(t *testing.T) { @@ -538,7 +578,7 @@ func TestServer_GetBlockV2(t *testing.T) { v1Block, err := migration.V1Alpha1ToV1SignedBlock(tt.want) require.NoError(t, err) - phase0Block, ok := blk.Data.Block.(*ethpbv2.SignedBeaconBlockContainerV2_Phase0Block) + phase0Block, ok := blk.Data.Message.(*ethpbv2.SignedBeaconBlockContainerV2_Phase0Block) require.Equal(t, true, ok) if !reflect.DeepEqual(phase0Block.Phase0Block, v1Block.Block) { t.Error("Expected blocks to equal") @@ -655,7 +695,7 @@ func TestServer_GetBlockV2(t *testing.T) { v2Block, err := migration.V1Alpha1BeaconBlockAltairToV2(tt.want.Block) require.NoError(t, err) - altairBlock, ok := blk.Data.Block.(*ethpbv2.SignedBeaconBlockContainerV2_AltairBlock) + altairBlock, ok := blk.Data.Message.(*ethpbv2.SignedBeaconBlockContainerV2_AltairBlock) require.Equal(t, true, ok) if !reflect.DeepEqual(altairBlock.AltairBlock, v2Block) { t.Error("Expected blocks to equal") diff --git a/config/params/testnet_e2e_config.go b/config/params/testnet_e2e_config.go index 974de13b9a5d..049199cd333d 100644 --- a/config/params/testnet_e2e_config.go +++ b/config/params/testnet_e2e_config.go @@ -1,6 +1,6 @@ package params -const AltairE2EForkEpoch = 6 +const altairE2EForkEpoch = 6 // UseE2EConfig for beacon chain services. func UseE2EConfig() { @@ -35,7 +35,7 @@ func E2ETestConfig() *BeaconChainConfig { e2eConfig.DepositNetworkID = 1337 // Network ID of eth1 dev net. // Altair Fork Parameters. - e2eConfig.AltairForkEpoch = AltairE2EForkEpoch + e2eConfig.AltairForkEpoch = altairE2EForkEpoch // Prysm constants. e2eConfig.ConfigName = ConfigNames[EndToEnd] diff --git a/proto/eth/service/beacon_chain_service.pb.go b/proto/eth/service/beacon_chain_service.pb.go index 532f7567cda4..5a5dd0790baa 100755 --- a/proto/eth/service/beacon_chain_service.pb.go +++ b/proto/eth/service/beacon_chain_service.pb.go @@ -55,7 +55,7 @@ var file_proto_eth_service_beacon_chain_service_proto_rawDesc = []byte{ 0x32, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xe0, 0x22, 0x0a, 0x0b, 0x42, 0x65, 0x61, 0x63, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xe8, 0x22, 0x0a, 0x0b, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x6f, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x47, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x20, 0x2e, @@ -163,187 +163,188 @@ var file_proto_eth_service_beacon_chain_service_proto_rawDesc = []byte{ 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x12, 0x2a, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x77, - 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x25, 0x2e, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, - 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x29, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, - 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x89, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, - 0x6f, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x30, 0x12, 0x2e, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, - 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, - 0x6f, 0x6f, 0x74, 0x12, 0x7c, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, - 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, - 0x7d, 0x12, 0x86, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x53, - 0x5a, 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x53, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x12, 0x2d, 0x2f, 0x69, 0x6e, + 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x7f, + 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2d, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, + 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x56, 0x32, 0x1a, 0x16, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, 0x1e, 0x2f, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x89, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, + 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, 0x12, 0x2e, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x73, 0x7a, 0x12, 0x82, 0x01, 0x0a, 0x0a, 0x47, - 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x32, 0x12, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x32, 0x1a, 0x20, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x32, 0x22, 0x31, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, - 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x12, - 0x8c, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x53, 0x5a, 0x56, + 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x12, 0x7c, 0x0a, 0x08, 0x47, + 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, + 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x86, 0x01, 0x0a, 0x0b, 0x47, 0x65, + 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x53, 0x5a, 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x53, 0x53, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x2f, 0x12, 0x2d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, + 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, + 0x73, 0x7a, 0x12, 0x82, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x32, 0x12, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x56, 0x32, 0x1a, 0x23, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x53, 0x5a, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x32, 0x22, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x12, - 0x2d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, - 0x32, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, - 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x73, 0x7a, 0x12, 0xa2, - 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x3e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x38, 0x12, 0x36, 0x2f, 0x69, 0x6e, + 0x56, 0x32, 0x1a, 0x20, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x56, 0x32, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x2f, 0x62, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x8c, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x53, 0x5a, 0x56, 0x32, 0x12, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x32, 0x1a, 0x23, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x53, 0x53, 0x5a, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x32, 0x22, + 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x12, 0x2d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, + 0x64, 0x7d, 0x2f, 0x73, 0x73, 0x7a, 0x12, 0xa2, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3e, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x38, 0x12, 0x36, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, + 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x61, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x9e, 0x01, 0x0a, 0x14, + 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, + 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x6f, 0x6f, + 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x2b, 0x12, 0x29, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, + 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, + 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x8e, 0x01, 0x0a, + 0x12, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x22, + 0x29, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, + 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x61, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9c, 0x01, + 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x1a, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, + 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x12, 0x2f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, - 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x7b, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x9e, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6f, 0x6c, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, 0x65, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, - 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, - 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, - 0x62, 0x6d, 0x69, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, - 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x22, 0x29, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, - 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9c, 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, - 0x6f, 0x6c, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, - 0x6e, 0x67, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x2e, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, - 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x50, - 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x31, 0x12, 0x2f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, - 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, - 0x6c, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, - 0x69, 0x6e, 0x67, 0x73, 0x12, 0x8f, 0x01, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12, - 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, - 0x6e, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x34, 0x22, 0x2f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, - 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, - 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, - 0x6e, 0x67, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9b, 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x6f, 0x6f, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, - 0x69, 0x6e, 0x67, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x2d, 0x2e, 0x65, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x50, - 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x31, 0x12, 0x2f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, - 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, - 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, - 0x69, 0x6e, 0x67, 0x73, 0x12, 0x8f, 0x01, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x50, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12, - 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, - 0x6e, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x34, 0x22, 0x2f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, - 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, - 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, - 0x6e, 0x67, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x93, 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x6f, 0x6f, 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, - 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x6c, 0x75, - 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x12, 0x2c, - 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, - 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x76, 0x6f, 0x6c, - 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x12, 0x8c, 0x01, 0x0a, - 0x13, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, - 0x45, 0x78, 0x69, 0x74, 0x12, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, - 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x8f, 0x01, 0x0a, + 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, + 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x22, 0x2c, 0x2f, 0x69, 0x6e, 0x74, + 0x74, 0x79, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x22, 0x2f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, - 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xa8, 0x01, 0x0a, 0x21, - 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x73, 0x12, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x53, 0x79, - 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x73, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x37, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x22, 0x2c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, - 0x6f, 0x6f, 0x6c, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, - 0x65, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, - 0x6b, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9b, + 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x16, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, + 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x12, 0x2f, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, + 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x8f, 0x01, 0x0a, + 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, + 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x22, 0x2f, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, + 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x93, + 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6e, + 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x1a, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, - 0x12, 0x25, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, - 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x73, - 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x66, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x53, 0x70, - 0x65, 0x63, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x65, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1e, 0x12, 0x1c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, - 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x73, 0x70, 0x65, 0x63, 0x12, - 0x88, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x43, 0x6f, - 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x28, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, - 0x12, 0x28, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, - 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x42, 0x95, 0x01, 0x0a, 0x18, 0x6f, - 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x17, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, - 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, - 0x73, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0xaa, 0x02, 0x14, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x45, 0x74, 0x68, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0xca, 0x02, 0x14, 0x45, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x79, 0x1a, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, + 0x74, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x12, 0x2c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, + 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, + 0x78, 0x69, 0x74, 0x73, 0x12, 0x8c, 0x01, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x56, + 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x12, 0x24, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, + 0x69, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x31, 0x22, 0x2c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, + 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, + 0x2f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0xa8, 0x01, 0x0a, 0x21, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x50, 0x6f, + 0x6f, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x6d, + 0x69, 0x74, 0x50, 0x6f, 0x6f, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x1a, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x22, 0x2c, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, + 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x6f, 0x6c, 0x2f, 0x73, 0x79, 0x6e, 0x63, + 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, + 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, 0x6b, + 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x12, 0x25, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2f, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, + 0x66, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x2f, 0x73, 0x70, 0x65, 0x63, 0x12, 0x88, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x44, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x12, 0x28, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, + 0x63, 0x74, 0x42, 0x95, 0x01, 0x0a, 0x18, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, + 0x17, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, + 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0xaa, 0x02, 0x14, 0x45, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0xca, 0x02, 0x14, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, + 0x74, 0x68, 0x5c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var file_proto_eth_service_beacon_chain_service_proto_goTypes = []interface{}{ @@ -356,7 +357,7 @@ var file_proto_eth_service_beacon_chain_service_proto_goTypes = []interface{}{ (*v2.StateSyncCommitteesRequest)(nil), // 6: ethereum.eth.v2.StateSyncCommitteesRequest (*v1.BlockHeadersRequest)(nil), // 7: ethereum.eth.v1.BlockHeadersRequest (*v1.BlockRequest)(nil), // 8: ethereum.eth.v1.BlockRequest - (*v1.BeaconBlockContainer)(nil), // 9: ethereum.eth.v1.BeaconBlockContainer + (*v2.SignedBeaconBlockContainerV2)(nil), // 9: ethereum.eth.v2.SignedBeaconBlockContainerV2 (*v2.BlockRequestV2)(nil), // 10: ethereum.eth.v2.BlockRequestV2 (*v1.AttestationsPoolRequest)(nil), // 11: ethereum.eth.v1.AttestationsPoolRequest (*v1.SubmitAttestationsRequest)(nil), // 12: ethereum.eth.v1.SubmitAttestationsRequest @@ -401,7 +402,7 @@ var file_proto_eth_service_beacon_chain_service_proto_depIdxs = []int32{ 6, // 8: ethereum.eth.service.BeaconChain.ListSyncCommittees:input_type -> ethereum.eth.v2.StateSyncCommitteesRequest 7, // 9: ethereum.eth.service.BeaconChain.ListBlockHeaders:input_type -> ethereum.eth.v1.BlockHeadersRequest 8, // 10: ethereum.eth.service.BeaconChain.GetBlockHeader:input_type -> ethereum.eth.v1.BlockRequest - 9, // 11: ethereum.eth.service.BeaconChain.SubmitBlock:input_type -> ethereum.eth.v1.BeaconBlockContainer + 9, // 11: ethereum.eth.service.BeaconChain.SubmitBlock:input_type -> ethereum.eth.v2.SignedBeaconBlockContainerV2 8, // 12: ethereum.eth.service.BeaconChain.GetBlockRoot:input_type -> ethereum.eth.v1.BlockRequest 8, // 13: ethereum.eth.service.BeaconChain.GetBlock:input_type -> ethereum.eth.v1.BlockRequest 8, // 14: ethereum.eth.service.BeaconChain.GetBlockSSZ:input_type -> ethereum.eth.v1.BlockRequest @@ -504,7 +505,7 @@ type BeaconChainClient interface { ListSyncCommittees(ctx context.Context, in *v2.StateSyncCommitteesRequest, opts ...grpc.CallOption) (*v2.StateSyncCommitteesResponse, error) ListBlockHeaders(ctx context.Context, in *v1.BlockHeadersRequest, opts ...grpc.CallOption) (*v1.BlockHeadersResponse, error) GetBlockHeader(ctx context.Context, in *v1.BlockRequest, opts ...grpc.CallOption) (*v1.BlockHeaderResponse, error) - SubmitBlock(ctx context.Context, in *v1.BeaconBlockContainer, opts ...grpc.CallOption) (*empty.Empty, error) + SubmitBlock(ctx context.Context, in *v2.SignedBeaconBlockContainerV2, opts ...grpc.CallOption) (*empty.Empty, error) GetBlockRoot(ctx context.Context, in *v1.BlockRequest, opts ...grpc.CallOption) (*v1.BlockRootResponse, error) GetBlock(ctx context.Context, in *v1.BlockRequest, opts ...grpc.CallOption) (*v1.BlockResponse, error) GetBlockSSZ(ctx context.Context, in *v1.BlockRequest, opts ...grpc.CallOption) (*v1.BlockSSZResponse, error) @@ -632,7 +633,7 @@ func (c *beaconChainClient) GetBlockHeader(ctx context.Context, in *v1.BlockRequ return out, nil } -func (c *beaconChainClient) SubmitBlock(ctx context.Context, in *v1.BeaconBlockContainer, opts ...grpc.CallOption) (*empty.Empty, error) { +func (c *beaconChainClient) SubmitBlock(ctx context.Context, in *v2.SignedBeaconBlockContainerV2, opts ...grpc.CallOption) (*empty.Empty, error) { out := new(empty.Empty) err := c.cc.Invoke(ctx, "/ethereum.eth.service.BeaconChain/SubmitBlock", in, out, opts...) if err != nil { @@ -816,7 +817,7 @@ type BeaconChainServer interface { ListSyncCommittees(context.Context, *v2.StateSyncCommitteesRequest) (*v2.StateSyncCommitteesResponse, error) ListBlockHeaders(context.Context, *v1.BlockHeadersRequest) (*v1.BlockHeadersResponse, error) GetBlockHeader(context.Context, *v1.BlockRequest) (*v1.BlockHeaderResponse, error) - SubmitBlock(context.Context, *v1.BeaconBlockContainer) (*empty.Empty, error) + SubmitBlock(context.Context, *v2.SignedBeaconBlockContainerV2) (*empty.Empty, error) GetBlockRoot(context.Context, *v1.BlockRequest) (*v1.BlockRootResponse, error) GetBlock(context.Context, *v1.BlockRequest) (*v1.BlockResponse, error) GetBlockSSZ(context.Context, *v1.BlockRequest) (*v1.BlockSSZResponse, error) @@ -874,7 +875,7 @@ func (*UnimplementedBeaconChainServer) ListBlockHeaders(context.Context, *v1.Blo func (*UnimplementedBeaconChainServer) GetBlockHeader(context.Context, *v1.BlockRequest) (*v1.BlockHeaderResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetBlockHeader not implemented") } -func (*UnimplementedBeaconChainServer) SubmitBlock(context.Context, *v1.BeaconBlockContainer) (*empty.Empty, error) { +func (*UnimplementedBeaconChainServer) SubmitBlock(context.Context, *v2.SignedBeaconBlockContainerV2) (*empty.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method SubmitBlock not implemented") } func (*UnimplementedBeaconChainServer) GetBlockRoot(context.Context, *v1.BlockRequest) (*v1.BlockRootResponse, error) { @@ -1135,7 +1136,7 @@ func _BeaconChain_GetBlockHeader_Handler(srv interface{}, ctx context.Context, d } func _BeaconChain_SubmitBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(v1.BeaconBlockContainer) + in := new(v2.SignedBeaconBlockContainerV2) if err := dec(in); err != nil { return nil, err } @@ -1147,7 +1148,7 @@ func _BeaconChain_SubmitBlock_Handler(srv interface{}, ctx context.Context, dec FullMethod: "/ethereum.eth.service.BeaconChain/SubmitBlock", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BeaconChainServer).SubmitBlock(ctx, req.(*v1.BeaconBlockContainer)) + return srv.(BeaconChainServer).SubmitBlock(ctx, req.(*v2.SignedBeaconBlockContainerV2)) } return interceptor(ctx, in, info, handler) } diff --git a/proto/eth/service/beacon_chain_service.pb.gw.go b/proto/eth/service/beacon_chain_service.pb.gw.go index aff0f6728985..6e375a540170 100755 --- a/proto/eth/service/beacon_chain_service.pb.gw.go +++ b/proto/eth/service/beacon_chain_service.pb.gw.go @@ -674,7 +674,7 @@ func local_request_BeaconChain_GetBlockHeader_0(ctx context.Context, marshaler r } func request_BeaconChain_SubmitBlock_0(ctx context.Context, marshaler runtime.Marshaler, client BeaconChainClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq v1.BeaconBlockContainer + var protoReq eth.SignedBeaconBlockContainerV2 var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) @@ -691,7 +691,7 @@ func request_BeaconChain_SubmitBlock_0(ctx context.Context, marshaler runtime.Ma } func local_request_BeaconChain_SubmitBlock_0(ctx context.Context, marshaler runtime.Marshaler, server BeaconChainServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq v1.BeaconBlockContainer + var protoReq eth.SignedBeaconBlockContainerV2 var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) diff --git a/proto/eth/service/beacon_chain_service.proto b/proto/eth/service/beacon_chain_service.proto index 1735cca47ce7..486cef6df96b 100644 --- a/proto/eth/service/beacon_chain_service.proto +++ b/proto/eth/service/beacon_chain_service.proto @@ -123,7 +123,7 @@ service BeaconChain { // response (20X) only indicates that the broadcast has been successful. The beacon node is expected to integrate the // new block into its state, and therefore validate the block internally, however blocks which fail the validation are // still broadcast but a different status code is returned (202). - rpc SubmitBlock(v1.BeaconBlockContainer) returns (google.protobuf.Empty) { + rpc SubmitBlock(v2.SignedBeaconBlockContainerV2) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/internal/eth/v1/beacon/blocks" body: "*" diff --git a/proto/eth/v2/beacon_block.pb.go b/proto/eth/v2/beacon_block.pb.go index a024d67193f3..a7f0d39737ce 100755 --- a/proto/eth/v2/beacon_block.pb.go +++ b/proto/eth/v2/beacon_block.pb.go @@ -271,11 +271,11 @@ type SignedBeaconBlockContainerV2 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Types that are assignable to Block: + // Types that are assignable to Message: // *SignedBeaconBlockContainerV2_Phase0Block // *SignedBeaconBlockContainerV2_AltairBlock - Block isSignedBeaconBlockContainerV2_Block `protobuf_oneof:"block"` - Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty" ssz-size:"96"` + Message isSignedBeaconBlockContainerV2_Message `protobuf_oneof:"message"` + Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty" ssz-size:"96"` } func (x *SignedBeaconBlockContainerV2) Reset() { @@ -310,22 +310,22 @@ func (*SignedBeaconBlockContainerV2) Descriptor() ([]byte, []int) { return file_proto_eth_v2_beacon_block_proto_rawDescGZIP(), []int{4} } -func (m *SignedBeaconBlockContainerV2) GetBlock() isSignedBeaconBlockContainerV2_Block { +func (m *SignedBeaconBlockContainerV2) GetMessage() isSignedBeaconBlockContainerV2_Message { if m != nil { - return m.Block + return m.Message } return nil } func (x *SignedBeaconBlockContainerV2) GetPhase0Block() *v1.BeaconBlock { - if x, ok := x.GetBlock().(*SignedBeaconBlockContainerV2_Phase0Block); ok { + if x, ok := x.GetMessage().(*SignedBeaconBlockContainerV2_Phase0Block); ok { return x.Phase0Block } return nil } func (x *SignedBeaconBlockContainerV2) GetAltairBlock() *BeaconBlockAltair { - if x, ok := x.GetBlock().(*SignedBeaconBlockContainerV2_AltairBlock); ok { + if x, ok := x.GetMessage().(*SignedBeaconBlockContainerV2_AltairBlock); ok { return x.AltairBlock } return nil @@ -338,8 +338,8 @@ func (x *SignedBeaconBlockContainerV2) GetSignature() []byte { return nil } -type isSignedBeaconBlockContainerV2_Block interface { - isSignedBeaconBlockContainerV2_Block() +type isSignedBeaconBlockContainerV2_Message interface { + isSignedBeaconBlockContainerV2_Message() } type SignedBeaconBlockContainerV2_Phase0Block struct { @@ -350,9 +350,9 @@ type SignedBeaconBlockContainerV2_AltairBlock struct { AltairBlock *BeaconBlockAltair `protobuf:"bytes,2,opt,name=altair_block,json=altairBlock,proto3,oneof"` } -func (*SignedBeaconBlockContainerV2_Phase0Block) isSignedBeaconBlockContainerV2_Block() {} +func (*SignedBeaconBlockContainerV2_Phase0Block) isSignedBeaconBlockContainerV2_Message() {} -func (*SignedBeaconBlockContainerV2_AltairBlock) isSignedBeaconBlockContainerV2_Block() {} +func (*SignedBeaconBlockContainerV2_AltairBlock) isSignedBeaconBlockContainerV2_Message() {} type SignedBeaconBlockAltair struct { state protoimpl.MessageState @@ -641,7 +641,7 @@ var file_proto_eth_v2_beacon_block_proto_rawDesc = []byte{ 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x07, 0x0a, 0x05, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x22, 0xd9, 0x01, 0x0a, 0x1c, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, + 0x63, 0x6b, 0x22, 0xdb, 0x01, 0x0a, 0x1c, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x56, 0x32, 0x12, 0x41, 0x0a, 0x0c, 0x70, 0x68, 0x61, 0x73, 0x65, 0x30, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x74, 0x68, 0x65, @@ -654,84 +654,84 @@ var file_proto_eth_v2_beacon_block_proto_rawDesc = []byte{ 0x48, 0x00, 0x52, 0x0b, 0x61, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x7d, - 0x0a, 0x17, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x3c, 0x0a, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x52, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, - 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xc0, 0x02, - 0x0a, 0x11, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, - 0x61, 0x69, 0x72, 0x12, 0x40, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x42, 0x2c, 0x82, 0xb5, 0x18, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, - 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, - 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x5d, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, - 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x36, 0x82, - 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, - 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, - 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, - 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, - 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, - 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3a, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x42, 0x6f, 0x64, 0x79, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, - 0x22, 0xfa, 0x04, 0x0a, 0x15, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x42, 0x6f, 0x64, 0x79, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, - 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, - 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x74, 0x68, - 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, - 0x69, 0x74, 0x69, 0x12, 0x58, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, - 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, - 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, - 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x57, 0x0a, - 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, - 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, - 0x18, 0x01, 0x32, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, - 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x49, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03, - 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x3c, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, - 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, - 0x55, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, - 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, - 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, - 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x45, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0x7d, 0x0a, 0x17, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x3c, 0x0a, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, + 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, + 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, + 0xc0, 0x02, 0x0a, 0x11, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, + 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x40, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x42, 0x2c, 0x82, 0xb5, 0x18, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, + 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, + 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x5d, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, + 0x36, 0x82, 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, + 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, + 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, + 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, + 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3a, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x52, 0x04, 0x62, 0x6f, + 0x64, 0x79, 0x22, 0xfa, 0x04, 0x0a, 0x15, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x2b, 0x0a, 0x0d, + 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, + 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x09, 0x65, 0x74, 0x68, + 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x45, + 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, + 0x66, 0x66, 0x69, 0x74, 0x69, 0x12, 0x58, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, + 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, + 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, + 0x57, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, + 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, + 0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, + 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x49, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, - 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x42, 0x80, 0x01, - 0x0a, 0x13, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x32, 0x42, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x74, 0x65, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, - 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x0f, 0x45, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x32, 0xca, 0x02, - 0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x32, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, + 0x18, 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, + 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, + 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x73, 0x12, 0x55, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, + 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, + 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, + 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x45, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, + 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x42, + 0x80, 0x01, 0x0a, 0x13, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x42, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, + 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, + 0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x32, + 0xca, 0x02, 0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, + 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/eth/v2/beacon_block.proto b/proto/eth/v2/beacon_block.proto index 17e0f8c13f9f..60aa421fcd2b 100644 --- a/proto/eth/v2/beacon_block.proto +++ b/proto/eth/v2/beacon_block.proto @@ -51,7 +51,7 @@ message BeaconBlockContainerV2 { } message SignedBeaconBlockContainerV2 { - oneof block { + oneof message { v1.BeaconBlock phase0_block = 1; BeaconBlockAltair altair_block = 2; } diff --git a/proto/migration/BUILD.bazel b/proto/migration/BUILD.bazel index 8a64f116b9af..8dcebacaa6f5 100644 --- a/proto/migration/BUILD.bazel +++ b/proto/migration/BUILD.bazel @@ -31,6 +31,7 @@ go_test( deps = [ "//encoding/bytesutil:go_default_library", "//proto/eth/v1:go_default_library", + "//proto/eth/v2:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1/wrapper:go_default_library", "//testing/assert:go_default_library", diff --git a/proto/migration/v1alpha1_to_v1.go b/proto/migration/v1alpha1_to_v1.go index 7a0bbe4702b3..726ca7c7bc16 100644 --- a/proto/migration/v1alpha1_to_v1.go +++ b/proto/migration/v1alpha1_to_v1.go @@ -44,8 +44,8 @@ func V1Alpha1ToV1SignedBlock(alphaBlk *ethpbalpha.SignedBeaconBlock) (*ethpbv1.S } // V1ToV1Alpha1SignedBlock converts a v1 SignedBeaconBlock proto to a v1alpha1 proto. -func V1ToV1Alpha1SignedBlock(alphaBlk *ethpbv1.SignedBeaconBlock) (*ethpbalpha.SignedBeaconBlock, error) { - marshaledBlk, err := proto.Marshal(alphaBlk) +func V1ToV1Alpha1SignedBlock(v1Blk *ethpbv1.SignedBeaconBlock) (*ethpbalpha.SignedBeaconBlock, error) { + marshaledBlk, err := proto.Marshal(v1Blk) if err != nil { return nil, errors.Wrap(err, "could not marshal block") } @@ -56,6 +56,19 @@ func V1ToV1Alpha1SignedBlock(alphaBlk *ethpbv1.SignedBeaconBlock) (*ethpbalpha.S return v1alpha1Block, nil } +// AltairToV1Alpha1SignedBlock converts a v2 SignedBeaconBlockAltair proto to a v1alpha1 proto. +func AltairToV1Alpha1SignedBlock(altairBlk *ethpbv2.SignedBeaconBlockAltair) (*ethpbalpha.SignedBeaconBlockAltair, error) { + marshaledBlk, err := proto.Marshal(altairBlk) + if err != nil { + return nil, errors.Wrap(err, "could not marshal block") + } + v1alpha1Block := ðpbalpha.SignedBeaconBlockAltair{} + if err := proto.Unmarshal(marshaledBlk, v1alpha1Block); err != nil { + return nil, errors.Wrap(err, "could not unmarshal block") + } + return v1alpha1Block, nil +} + // V1Alpha1ToV1Block converts a v1alpha1 BeaconBlock proto to a v1 proto. func V1Alpha1ToV1Block(alphaBlk *ethpbalpha.BeaconBlock) (*ethpbv1.BeaconBlock, error) { marshaledBlk, err := proto.Marshal(alphaBlk) diff --git a/proto/migration/v1alpha1_to_v1_test.go b/proto/migration/v1alpha1_to_v1_test.go index 6145022e12b2..f98c0376d7d1 100644 --- a/proto/migration/v1alpha1_to_v1_test.go +++ b/proto/migration/v1alpha1_to_v1_test.go @@ -7,6 +7,7 @@ import ( "github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/prysm/encoding/bytesutil" ethpbv1 "github.com/prysmaticlabs/prysm/proto/eth/v1" + ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2" ethpbalpha "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper" "github.com/prysmaticlabs/prysm/testing/assert" @@ -119,6 +120,35 @@ func Test_V1ToV1Alpha1SignedBlock(t *testing.T) { assert.DeepEqual(t, v1Root, alphaRoot) } +func Test_AltairToV1Alpha1SignedBlock(t *testing.T) { + v2Block := util.HydrateV2SignedBeaconBlock(ðpbv2.SignedBeaconBlockAltair{}) + v2Block.Message.Slot = slot + v2Block.Message.ProposerIndex = validatorIndex + v2Block.Message.ParentRoot = parentRoot + v2Block.Message.StateRoot = stateRoot + v2Block.Message.Body.RandaoReveal = randaoReveal + v2Block.Message.Body.Eth1Data = ðpbv1.Eth1Data{ + DepositRoot: depositRoot, + DepositCount: depositCount, + BlockHash: blockHash, + } + syncCommitteeBits := bitfield.NewBitvector512() + syncCommitteeBits.SetBitAt(100, true) + v2Block.Message.Body.SyncAggregate = ðpbv1.SyncAggregate{ + SyncCommitteeBits: syncCommitteeBits, + SyncCommitteeSignature: signature, + } + v2Block.Signature = signature + + alphaBlock, err := AltairToV1Alpha1SignedBlock(v2Block) + require.NoError(t, err) + alphaRoot, err := alphaBlock.HashTreeRoot() + require.NoError(t, err) + v2Root, err := v2Block.HashTreeRoot() + require.NoError(t, err) + assert.DeepEqual(t, v2Root, alphaRoot) +} + func Test_V1ToV1Alpha1Block(t *testing.T) { alphaBlock := util.HydrateBeaconBlock(ðpbalpha.BeaconBlock{}) alphaBlock.Slot = slot diff --git a/testing/endtoend/evaluators/api_middleware.go b/testing/endtoend/evaluators/api_middleware.go index 85f7d0aead66..857e007183c5 100644 --- a/testing/endtoend/evaluators/api_middleware.go +++ b/testing/endtoend/evaluators/api_middleware.go @@ -12,10 +12,10 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" types "github.com/prysmaticlabs/eth2-types" - sharedparams "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/proto/eth/service" ethpbv1 "github.com/prysmaticlabs/prysm/proto/eth/v1" ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2" + "github.com/prysmaticlabs/prysm/testing/endtoend/helpers" "github.com/prysmaticlabs/prysm/testing/endtoend/params" "github.com/prysmaticlabs/prysm/testing/endtoend/policies" e2etypes "github.com/prysmaticlabs/prysm/testing/endtoend/types" @@ -43,7 +43,7 @@ type validatorContainerJson struct { // This ensures our API Middleware returns good data compared to gRPC. var APIMiddlewareVerifyIntegrity = e2etypes.Evaluator{ Name: "api_middleware_verify_integrity_epoch_%d", - Policy: policies.OnEpoch(sharedparams.AltairE2EForkEpoch), + Policy: policies.OnEpoch(helpers.AltairE2EForkEpoch), Evaluation: apiMiddlewareVerify, } @@ -207,7 +207,7 @@ func withCompareAttesterDuties(beaconNodeIdx int, conn *grpc.ClientConn) error { ctx := context.Background() validatorClient := service.NewBeaconValidatorClient(conn) resp, err := validatorClient.GetAttesterDuties(ctx, ðpbv1.AttesterDutiesRequest{ - Epoch: sharedparams.AltairE2EForkEpoch, + Epoch: helpers.AltairE2EForkEpoch, Index: []types.ValidatorIndex{0}, }) if err != nil { @@ -217,7 +217,7 @@ func withCompareAttesterDuties(beaconNodeIdx int, conn *grpc.ClientConn) error { reqJSON := []string{"0"} respJSON := &attesterDutiesResponseJson{} if err := doMiddlewareJSONPostRequestV1( - "/validator/duties/attester/"+strconv.Itoa(sharedparams.AltairE2EForkEpoch), + "/validator/duties/attester/"+strconv.Itoa(helpers.AltairE2EForkEpoch), beaconNodeIdx, reqJSON, respJSON, diff --git a/testing/endtoend/evaluators/fork.go b/testing/endtoend/evaluators/fork.go index 918ffddad963..b44e3eba61e6 100644 --- a/testing/endtoend/evaluators/fork.go +++ b/testing/endtoend/evaluators/fork.go @@ -5,9 +5,9 @@ import ( "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/beacon-chain/core" - "github.com/prysmaticlabs/prysm/config/params" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" wrapperv2 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper" + "github.com/prysmaticlabs/prysm/testing/endtoend/helpers" "github.com/prysmaticlabs/prysm/testing/endtoend/policies" "github.com/prysmaticlabs/prysm/testing/endtoend/types" "google.golang.org/grpc" @@ -16,7 +16,7 @@ import ( // ForkTransition ensures that the hard fork has occurred successfully. var ForkTransition = types.Evaluator{ Name: "fork_transition_%d", - Policy: policies.OnEpoch(params.AltairE2EForkEpoch), + Policy: policies.OnEpoch(helpers.AltairE2EForkEpoch), Evaluation: forkOccurs, } @@ -29,7 +29,7 @@ func forkOccurs(conns ...*grpc.ClientConn) error { if err != nil { return errors.Wrap(err, "failed to get stream") } - fSlot, err := core.StartSlot(params.AltairE2EForkEpoch) + fSlot, err := core.StartSlot(helpers.AltairE2EForkEpoch) if err != nil { return err } diff --git a/testing/endtoend/evaluators/validator.go b/testing/endtoend/evaluators/validator.go index 43278d97af68..a22cb5e43ca4 100644 --- a/testing/endtoend/evaluators/validator.go +++ b/testing/endtoend/evaluators/validator.go @@ -8,6 +8,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core" "github.com/prysmaticlabs/prysm/config/params" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/testing/endtoend/helpers" "github.com/prysmaticlabs/prysm/testing/endtoend/policies" "github.com/prysmaticlabs/prysm/testing/endtoend/types" "google.golang.org/grpc" @@ -36,7 +37,7 @@ var ValidatorsParticipating = types.Evaluator{ // are active. var ValidatorSyncParticipation = types.Evaluator{ Name: "validator_sync_participation_%d", - Policy: policies.AfterNthEpoch(params.AltairE2EForkEpoch - 1), + Policy: policies.AfterNthEpoch(helpers.AltairE2EForkEpoch - 1), Evaluation: validatorsSyncParticipation, } @@ -129,8 +130,8 @@ func validatorsSyncParticipation(conns ...*grpc.ClientConn) error { currEpoch := core.SlotToEpoch(currSlot) lowestBound := currEpoch - 1 - if lowestBound < params.AltairE2EForkEpoch { - lowestBound = params.AltairE2EForkEpoch + if lowestBound < helpers.AltairE2EForkEpoch { + lowestBound = helpers.AltairE2EForkEpoch } blockCtrs, err := altairClient.ListBeaconBlocks(context.Background(), ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Epoch{Epoch: lowestBound}}) if err != nil { @@ -144,7 +145,7 @@ func validatorsSyncParticipation(conns ...*grpc.ClientConn) error { if blk.Block == nil || blk.Block.Body == nil || blk.Block.Body.SyncAggregate == nil { return errors.New("nil block provided") } - forkSlot, err := core.StartSlot(params.AltairE2EForkEpoch) + forkSlot, err := core.StartSlot(helpers.AltairE2EForkEpoch) if err != nil { return err } @@ -173,7 +174,7 @@ func validatorsSyncParticipation(conns ...*grpc.ClientConn) error { if blk.Block == nil || blk.Block.Body == nil || blk.Block.Body.SyncAggregate == nil { return errors.New("nil block provided") } - forkSlot, err := core.StartSlot(params.AltairE2EForkEpoch) + forkSlot, err := core.StartSlot(helpers.AltairE2EForkEpoch) if err != nil { return err } diff --git a/testing/endtoend/helpers/helpers.go b/testing/endtoend/helpers/helpers.go index 5be77f28dda8..840e81acff57 100644 --- a/testing/endtoend/helpers/helpers.go +++ b/testing/endtoend/helpers/helpers.go @@ -33,6 +33,7 @@ const ( cpuProfileFileName = "node_cpu_profile_%d.pb.gz" fileBufferSize = 64 * 1024 maxFileBufferSize = 1024 * 1024 + AltairE2EForkEpoch = 6 ) // Graffiti is a list of sample graffiti strings. diff --git a/testing/util/BUILD.bazel b/testing/util/BUILD.bazel index a38a6f4e766c..a123da4dcccb 100644 --- a/testing/util/BUILD.bazel +++ b/testing/util/BUILD.bazel @@ -35,6 +35,7 @@ go_library( "//crypto/rand:go_default_library", "//encoding/bytesutil:go_default_library", "//proto/eth/v1:go_default_library", + "//proto/eth/v2:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1/wrapper:go_default_library", "//runtime/interop:go_default_library", @@ -67,6 +68,7 @@ go_test( "//config/params:go_default_library", "//encoding/bytesutil:go_default_library", "//proto/eth/v1:go_default_library", + "//proto/eth/v2:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1/wrapper:go_default_library", "//testing/assert:go_default_library", diff --git a/testing/util/block.go b/testing/util/block.go index e468d94f3fa7..47f9611dd16d 100644 --- a/testing/util/block.go +++ b/testing/util/block.go @@ -15,6 +15,7 @@ import ( "github.com/prysmaticlabs/prysm/crypto/rand" "github.com/prysmaticlabs/prysm/encoding/bytesutil" v1 "github.com/prysmaticlabs/prysm/proto/eth/v1" + v2 "github.com/prysmaticlabs/prysm/proto/eth/v2" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" ) @@ -506,6 +507,59 @@ func HydrateV1BeaconBlockBody(b *v1.BeaconBlockBody) *v1.BeaconBlockBody { return b } +// HydrateV2SignedBeaconBlock hydrates a signed beacon block with correct field length sizes +// to comply with fssz marshalling and unmarshalling rules. +func HydrateV2SignedBeaconBlock(b *v2.SignedBeaconBlockAltair) *v2.SignedBeaconBlockAltair { + if b.Signature == nil { + b.Signature = make([]byte, params.BeaconConfig().BLSSignatureLength) + } + b.Message = HydrateV2BeaconBlock(b.Message) + return b +} + +// HydrateV2BeaconBlock hydrates a beacon block with correct field length sizes +// to comply with fssz marshalling and unmarshalling rules. +func HydrateV2BeaconBlock(b *v2.BeaconBlockAltair) *v2.BeaconBlockAltair { + if b == nil { + b = &v2.BeaconBlockAltair{} + } + if b.ParentRoot == nil { + b.ParentRoot = make([]byte, 32) + } + if b.StateRoot == nil { + b.StateRoot = make([]byte, 32) + } + b.Body = HydrateV2BeaconBlockBody(b.Body) + return b +} + +// HydrateV2BeaconBlockBody hydrates a beacon block body with correct field length sizes +// to comply with fssz marshalling and unmarshalling rules. +func HydrateV2BeaconBlockBody(b *v2.BeaconBlockBodyAltair) *v2.BeaconBlockBodyAltair { + if b == nil { + b = &v2.BeaconBlockBodyAltair{} + } + if b.RandaoReveal == nil { + b.RandaoReveal = make([]byte, params.BeaconConfig().BLSSignatureLength) + } + if b.Graffiti == nil { + b.Graffiti = make([]byte, 32) + } + if b.Eth1Data == nil { + b.Eth1Data = &v1.Eth1Data{ + DepositRoot: make([]byte, 32), + BlockHash: make([]byte, 32), + } + } + if b.SyncAggregate == nil { + b.SyncAggregate = &v1.SyncAggregate{ + SyncCommitteeBits: make([]byte, 64), + SyncCommitteeSignature: make([]byte, 96), + } + } + return b +} + // HydrateSignedBeaconBlockAltair hydrates a signed beacon block with correct field length sizes // to comply with fssz marshalling and unmarshalling rules. func HydrateSignedBeaconBlockAltair(b *ethpb.SignedBeaconBlockAltair) *ethpb.SignedBeaconBlockAltair { diff --git a/testing/util/block_test.go b/testing/util/block_test.go index d5f897a59083..5058dd1b0f4f 100644 --- a/testing/util/block_test.go +++ b/testing/util/block_test.go @@ -10,9 +10,9 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core/transition/stateutils" "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/encoding/bytesutil" - v1 "github.com/prysmaticlabs/prysm/proto/eth/v1" - eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" - v2 "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + ethpbv1 "github.com/prysmaticlabs/prysm/proto/eth/v1" + ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2" + ethpbalpha "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper" "github.com/prysmaticlabs/prysm/testing/require" ) @@ -179,7 +179,7 @@ func TestGenerateFullBlock_ValidVoluntaryExits(t *testing.T) { } func TestHydrateSignedBeaconBlock_NoError(t *testing.T) { - b := ð.SignedBeaconBlock{} + b := ðpbalpha.SignedBeaconBlock{} b = HydrateSignedBeaconBlock(b) _, err := b.HashTreeRoot() require.NoError(t, err) @@ -190,7 +190,7 @@ func TestHydrateSignedBeaconBlock_NoError(t *testing.T) { } func TestHydrateV1SignedBeaconBlock_NoError(t *testing.T) { - b := &v1.SignedBeaconBlock{} + b := ðpbv1.SignedBeaconBlock{} b = HydrateV1SignedBeaconBlock(b) _, err := b.HashTreeRoot() require.NoError(t, err) @@ -200,8 +200,19 @@ func TestHydrateV1SignedBeaconBlock_NoError(t *testing.T) { require.NoError(t, err) } -func TestHydrateV2SignedBeaconBlockAltair_NoError(t *testing.T) { - b := &v2.SignedBeaconBlockAltair{} +func TestHydrateV2SignedBeaconBlock_NoError(t *testing.T) { + b := ðpbv2.SignedBeaconBlockAltair{} + b = HydrateV2SignedBeaconBlock(b) + _, err := b.HashTreeRoot() + require.NoError(t, err) + _, err = b.Message.HashTreeRoot() + require.NoError(t, err) + _, err = b.Message.Body.HashTreeRoot() + require.NoError(t, err) +} + +func TestHydrateSignedBeaconBlockAltair_NoError(t *testing.T) { + b := ðpbalpha.SignedBeaconBlockAltair{} b = HydrateSignedBeaconBlockAltair(b) // HTR should not error. It errors with incorrect field length sizes. From 1816906bc7cb001b858b9ed321a4770c0fef58b4 Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Tue, 28 Sep 2021 14:33:45 -0500 Subject: [PATCH 04/25] Detect Slashable Blocks in Optimized Slasher (#9693) * pass * Update beacon-chain/slasher/detect_blocks.go Co-authored-by: terence tsao * Update beacon-chain/slasher/detect_blocks.go Co-authored-by: terence tsao Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- beacon-chain/slasher/BUILD.bazel | 3 + beacon-chain/slasher/detect_blocks.go | 92 +++++++++++++ beacon-chain/slasher/detect_blocks_test.go | 149 +++++++++++++++++++++ beacon-chain/slasher/receive.go | 7 +- 4 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 beacon-chain/slasher/detect_blocks.go create mode 100644 beacon-chain/slasher/detect_blocks_test.go diff --git a/beacon-chain/slasher/BUILD.bazel b/beacon-chain/slasher/BUILD.bazel index e6a3bc773989..7f29bf5f2bc6 100644 --- a/beacon-chain/slasher/BUILD.bazel +++ b/beacon-chain/slasher/BUILD.bazel @@ -4,6 +4,7 @@ go_library( name = "go_default_library", srcs = [ "chunks.go", + "detect_blocks.go", "doc.go", "helpers.go", "metrics.go", @@ -36,6 +37,7 @@ go_library( "@com_github_prometheus_client_golang//prometheus/promauto:go_default_library", "@com_github_prysmaticlabs_eth2_types//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", + "@io_opencensus_go//trace:go_default_library", ], ) @@ -43,6 +45,7 @@ go_test( name = "go_default_test", srcs = [ "chunks_test.go", + "detect_blocks_test.go", "helpers_test.go", "params_test.go", "process_slashings_test.go", diff --git a/beacon-chain/slasher/detect_blocks.go b/beacon-chain/slasher/detect_blocks.go new file mode 100644 index 000000000000..ab1c5044f132 --- /dev/null +++ b/beacon-chain/slasher/detect_blocks.go @@ -0,0 +1,92 @@ +package slasher + +import ( + "context" + + "github.com/pkg/errors" + types "github.com/prysmaticlabs/eth2-types" + slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "go.opencensus.io/trace" +) + +// detectProposerSlashings takes in signed block header wrappers and returns a list of proposer slashings detected. +func (s *Service) detectProposerSlashings( + ctx context.Context, + proposedBlocks []*slashertypes.SignedBlockHeaderWrapper, +) ([]*ethpb.ProposerSlashing, error) { + ctx, span := trace.StartSpan(ctx, "slasher.detectProposerSlashings") + defer span.End() + // We check if there are any slashable double proposals in the input list + // of proposals with respect to each other. + slashings := make([]*ethpb.ProposerSlashing, 0) + existingProposals := make(map[string]*slashertypes.SignedBlockHeaderWrapper) + for i, proposal := range proposedBlocks { + key := proposalKey(proposal) + existingProposal, ok := existingProposals[key] + if !ok { + existingProposals[key] = proposal + continue + } + if isDoubleProposal(proposedBlocks[i].SigningRoot, existingProposal.SigningRoot) { + doubleProposalsTotal.Inc() + slashing := ðpb.ProposerSlashing{ + Header_1: existingProposal.SignedBeaconBlockHeader, + Header_2: proposedBlocks[i].SignedBeaconBlockHeader, + } + slashings = append(slashings, slashing) + } + } + + proposerSlashings, err := s.serviceCfg.Database.CheckDoubleBlockProposals(ctx, proposedBlocks) + if err != nil { + return nil, errors.Wrap(err, "could not check for double proposals on disk") + } + if err := s.saveSafeProposals(ctx, proposedBlocks, proposerSlashings); err != nil { + return nil, errors.Wrap(err, "could not save safe proposals") + } + slashings = append(slashings, proposerSlashings...) + return slashings, nil +} + +// Check for double proposals in our database given a list of incoming block proposals. +// For the proposals that were not slashable, we save them to the database. +func (s *Service) saveSafeProposals( + ctx context.Context, + proposedBlocks []*slashertypes.SignedBlockHeaderWrapper, + proposerSlashings []*ethpb.ProposerSlashing, +) error { + ctx, span := trace.StartSpan(ctx, "slasher.saveSafeProposals") + defer span.End() + return s.serviceCfg.Database.SaveBlockProposals( + ctx, + filterSafeProposals(proposedBlocks, proposerSlashings), + ) +} + +func filterSafeProposals( + proposedBlocks []*slashertypes.SignedBlockHeaderWrapper, + proposerSlashings []*ethpb.ProposerSlashing, +) []*slashertypes.SignedBlockHeaderWrapper { + // We initialize a map of proposers that are safe from slashing. + safeProposers := make(map[types.ValidatorIndex]*slashertypes.SignedBlockHeaderWrapper, len(proposedBlocks)) + for _, proposal := range proposedBlocks { + safeProposers[proposal.SignedBeaconBlockHeader.Header.ProposerIndex] = proposal + } + for _, doubleProposal := range proposerSlashings { + // If a proposer is found to have committed a slashable offense, we delete + // them from the safe proposers map. + delete(safeProposers, doubleProposal.Header_1.Header.ProposerIndex) + } + // We save all the proposals that are determined "safe" and not-slashable to our database. + safeProposals := make([]*slashertypes.SignedBlockHeaderWrapper, 0, len(safeProposers)) + for _, proposal := range safeProposers { + safeProposals = append(safeProposals, proposal) + } + return safeProposals +} + +func proposalKey(proposal *slashertypes.SignedBlockHeaderWrapper) string { + header := proposal.SignedBeaconBlockHeader.Header + return uintToString(uint64(header.Slot)) + ":" + uintToString(uint64(header.ProposerIndex)) +} diff --git a/beacon-chain/slasher/detect_blocks_test.go b/beacon-chain/slasher/detect_blocks_test.go new file mode 100644 index 000000000000..84ba4831c0a8 --- /dev/null +++ b/beacon-chain/slasher/detect_blocks_test.go @@ -0,0 +1,149 @@ +package slasher + +import ( + "context" + "testing" + + types "github.com/prysmaticlabs/eth2-types" + mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/core/signing" + dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings" + slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" + "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" + "github.com/prysmaticlabs/prysm/config/params" + "github.com/prysmaticlabs/prysm/crypto/bls" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/testing/require" + "github.com/prysmaticlabs/prysm/testing/util" + logTest "github.com/sirupsen/logrus/hooks/test" +) + +func Test_processQueuedBlocks_DetectsDoubleProposals(t *testing.T) { + hook := logTest.NewGlobal() + slasherDB := dbtest.SetupSlasherDB(t) + beaconDB := dbtest.SetupDB(t) + ctx, cancel := context.WithCancel(context.Background()) + + beaconState, err := util.NewBeaconState() + require.NoError(t, err) + + // Initialize validators in the state. + numVals := params.BeaconConfig().MinGenesisActiveValidatorCount + validators := make([]*ethpb.Validator, numVals) + privKeys := make([]bls.SecretKey, numVals) + for i := range validators { + privKey, err := bls.RandKey() + require.NoError(t, err) + privKeys[i] = privKey + validators[i] = ðpb.Validator{ + PublicKey: privKey.PublicKey().Marshal(), + WithdrawalCredentials: make([]byte, 32), + } + } + err = beaconState.SetValidators(validators) + require.NoError(t, err) + domain, err := signing.Domain( + beaconState.Fork(), + 0, + params.BeaconConfig().DomainBeaconProposer, + beaconState.GenesisValidatorRoot(), + ) + require.NoError(t, err) + + mockChain := &mock.ChainService{ + State: beaconState, + } + s := &Service{ + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + HeadStateFetcher: mockChain, + StateGen: stategen.New(beaconDB), + SlashingPoolInserter: &slashings.PoolMock{}, + }, + params: DefaultParams(), + blksQueue: newBlocksQueue(), + } + + parentRoot := bytesutil.ToBytes32([]byte("parent")) + err = s.serviceCfg.StateGen.SaveState(ctx, parentRoot, beaconState) + require.NoError(t, err) + + currentSlotChan := make(chan types.Slot) + exitChan := make(chan struct{}) + go func() { + s.processQueuedBlocks(ctx, currentSlotChan) + exitChan <- struct{}{} + }() + + signedBlkHeaders := []*slashertypes.SignedBlockHeaderWrapper{ + createProposalWrapper(t, 4, 1, []byte{1}), + createProposalWrapper(t, 4, 1, []byte{1}), + createProposalWrapper(t, 4, 1, []byte{1}), + createProposalWrapper(t, 4, 1, []byte{2}), + } + + // Add valid signatures to the block headers we are testing. + for _, proposalWrapper := range signedBlkHeaders { + proposalWrapper.SignedBeaconBlockHeader.Header.ParentRoot = parentRoot[:] + headerHtr, err := proposalWrapper.SignedBeaconBlockHeader.Header.HashTreeRoot() + require.NoError(t, err) + container := ðpb.SigningData{ + ObjectRoot: headerHtr[:], + Domain: domain, + } + signingRoot, err := container.HashTreeRoot() + require.NoError(t, err) + privKey := privKeys[proposalWrapper.SignedBeaconBlockHeader.Header.ProposerIndex] + proposalWrapper.SignedBeaconBlockHeader.Signature = privKey.Sign(signingRoot[:]).Marshal() + } + + s.blksQueue.extend(signedBlkHeaders) + + currentSlot := types.Slot(4) + currentSlotChan <- currentSlot + cancel() + <-exitChan + require.LogsContain(t, hook, "Proposer slashing detected") +} + +func Test_processQueuedBlocks_NotSlashable(t *testing.T) { + hook := logTest.NewGlobal() + slasherDB := dbtest.SetupSlasherDB(t) + ctx, cancel := context.WithCancel(context.Background()) + + beaconState, err := util.NewBeaconState() + require.NoError(t, err) + currentSlot := types.Slot(4) + require.NoError(t, beaconState.SetSlot(currentSlot)) + mockChain := &mock.ChainService{ + State: beaconState, + Slot: ¤tSlot, + } + + s := &Service{ + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + HeadStateFetcher: mockChain, + }, + params: DefaultParams(), + blksQueue: newBlocksQueue(), + } + currentSlotChan := make(chan types.Slot) + exitChan := make(chan struct{}) + go func() { + s.processQueuedBlocks(ctx, currentSlotChan) + exitChan <- struct{}{} + }() + s.blksQueue.extend([]*slashertypes.SignedBlockHeaderWrapper{ + createProposalWrapper(t, 4, 1, []byte{1}), + createProposalWrapper(t, 4, 1, []byte{1}), + }) + currentSlotChan <- currentSlot + cancel() + <-exitChan + require.LogsDoNotContain(t, hook, "Proposer slashing detected") +} diff --git a/beacon-chain/slasher/receive.go b/beacon-chain/slasher/receive.go index b6b1bcb81613..efbb23401920 100644 --- a/beacon-chain/slasher/receive.go +++ b/beacon-chain/slasher/receive.go @@ -148,8 +148,11 @@ func (s *Service) processQueuedBlocks(ctx context.Context, slotTicker <-chan typ start := time.Now() // Check for slashings. - // TODO(#8331): Detect slashings. - slashings := make([]*ethpb.ProposerSlashing, 0) + slashings, err := s.detectProposerSlashings(ctx, blocks) + if err != nil { + log.WithError(err).Error("Could not detect proposer slashings") + continue + } // Process proposer slashings by verifying their signatures, submitting // to the beacon node's operations pool, and logging them. From c94ba40db26f033bcabad322bc38d1fcd2a64577 Mon Sep 17 00:00:00 2001 From: Preston Van Loon Date: Tue, 28 Sep 2021 15:02:12 -0500 Subject: [PATCH 05/25] config: ensure config matches altair presets (#9690) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * config: ensure config matches altair presets * gofmt Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> Co-authored-by: RadosÅ‚aw Kapka --- config/params/loader_test.go | 29 +++++++++++++-------- config/params/testnet_prater_config_test.go | 8 +++--- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/config/params/loader_test.go b/config/params/loader_test.go index 16e6df7b69fb..d4340e224c12 100644 --- a/config/params/loader_test.go +++ b/config/params/loader_test.go @@ -110,19 +110,23 @@ func TestLoadConfigFileMainnet(t *testing.T) { } t.Run("mainnet", func(t *testing.T) { - mainnetPresetsFile := presetsFilePath(t, "mainnet") - params.LoadChainConfigFile(mainnetPresetsFile) + mainnetPresetsFiles := presetsFilePath(t, "mainnet") + for _, fp := range mainnetPresetsFiles { + params.LoadChainConfigFile(fp) + } mainnetConfigFile := configFilePath(t, "mainnet") params.LoadChainConfigFile(mainnetConfigFile) - fields := fieldsFromYamls(t, []string{mainnetPresetsFile, mainnetConfigFile}) + fields := fieldsFromYamls(t, append(mainnetPresetsFiles, mainnetConfigFile)) assertVals("mainnet", fields, params.MainnetConfig(), params.BeaconConfig()) }) t.Run("minimal", func(t *testing.T) { - minimalPresetsFile := presetsFilePath(t, "minimal") - params.LoadChainConfigFile(minimalPresetsFile) + minimalPresetsFiles := presetsFilePath(t, "minimal") + for _, fp := range minimalPresetsFiles { + params.LoadChainConfigFile(fp) + } minimalConfigFile := configFilePath(t, "minimal") - fields := fieldsFromYamls(t, []string{minimalPresetsFile, minimalConfigFile}) + fields := fieldsFromYamls(t, append(minimalPresetsFiles, minimalConfigFile)) assertVals("minimal", fields, params.MinimalSpecConfig(), params.BeaconConfig()) }) } @@ -225,13 +229,16 @@ func configFilePath(t *testing.T, config string) string { return configFilePath } -// presetsFilePath sets the proper preset and returns the relevant -// preset file path from eth2-spec-tests directory. -func presetsFilePath(t *testing.T, config string) string { +// presetsFilePath returns the relevant preset file paths from eth2-spec-tests +// directory. This method returns a preset file path for each hard fork or +// major network upgrade, in order. +func presetsFilePath(t *testing.T, config string) []string { filepath, err := bazel.Runfile("external/consensus_spec") require.NoError(t, err) - configFilePath := path.Join(filepath, "presets", config, "phase0.yaml") - return configFilePath + return []string{ + path.Join(filepath, "presets", config, "phase0.yaml"), + path.Join(filepath, "presets", config, "altair.yaml"), + } } func fieldsFromYamls(t *testing.T, fps []string) []string { diff --git a/config/params/testnet_prater_config_test.go b/config/params/testnet_prater_config_test.go index 9548fba0985a..feec6dfaba9b 100644 --- a/config/params/testnet_prater_config_test.go +++ b/config/params/testnet_prater_config_test.go @@ -7,10 +7,12 @@ import ( ) func TestPraterConfigMatchesUpstreamYaml(t *testing.T) { - presetFP := presetsFilePath(t, "mainnet") - params.LoadChainConfigFile(presetFP) + presetFPs := presetsFilePath(t, "mainnet") + for _, fp := range presetFPs { + params.LoadChainConfigFile(fp) + } configFP := testnetConfigFilePath(t, "prater") params.LoadChainConfigFile(configFP) - fields := fieldsFromYamls(t, []string{configFP, presetFP}) + fields := fieldsFromYamls(t, append(presetFPs, configFP)) assertYamlFieldsMatch(t, "prater", fields, params.BeaconConfig(), params.PraterConfig()) } From 2a2239d9371919811768f592af4419c1dd33686d Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Tue, 28 Sep 2021 15:54:17 -0500 Subject: [PATCH 06/25] Detect Slashable Attestations in Optimized Slasher (#9694) * detect slashable attestations * Update beacon-chain/slasher/detect_attestations.go Co-authored-by: Preston Van Loon Co-authored-by: Preston Van Loon --- beacon-chain/slasher/BUILD.bazel | 4 + beacon-chain/slasher/detect_attestations.go | 474 +++++++++ .../slasher/detect_attestations_test.go | 917 ++++++++++++++++++ beacon-chain/slasher/helpers_test.go | 27 - beacon-chain/slasher/receive.go | 7 +- beacon-chain/slasher/service.go | 11 +- 6 files changed, 1407 insertions(+), 33 deletions(-) create mode 100644 beacon-chain/slasher/detect_attestations.go create mode 100644 beacon-chain/slasher/detect_attestations_test.go diff --git a/beacon-chain/slasher/BUILD.bazel b/beacon-chain/slasher/BUILD.bazel index 7f29bf5f2bc6..8a795aac0f79 100644 --- a/beacon-chain/slasher/BUILD.bazel +++ b/beacon-chain/slasher/BUILD.bazel @@ -4,6 +4,7 @@ go_library( name = "go_default_library", srcs = [ "chunks.go", + "detect_attestations.go", "detect_blocks.go", "doc.go", "helpers.go", @@ -31,6 +32,7 @@ go_library( "//container/slice:go_default_library", "//encoding/bytesutil:go_default_library", "//proto/prysm/v1alpha1:go_default_library", + "//time/slots:go_default_library", "@com_github_ferranbt_fastssz//:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_prometheus_client_golang//prometheus:go_default_library", @@ -45,6 +47,7 @@ go_test( name = "go_default_test", srcs = [ "chunks_test.go", + "detect_attestations_test.go", "detect_blocks_test.go", "helpers_test.go", "params_test.go", @@ -56,6 +59,7 @@ go_test( deps = [ "//async/event:go_default_library", "//beacon-chain/blockchain/testing:go_default_library", + "//beacon-chain/core:go_default_library", "//beacon-chain/core/signing:go_default_library", "//beacon-chain/db/testing:go_default_library", "//beacon-chain/operations/slashings:go_default_library", diff --git a/beacon-chain/slasher/detect_attestations.go b/beacon-chain/slasher/detect_attestations.go new file mode 100644 index 000000000000..128affdf9d6e --- /dev/null +++ b/beacon-chain/slasher/detect_attestations.go @@ -0,0 +1,474 @@ +package slasher + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + types "github.com/prysmaticlabs/eth2-types" + slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/time/slots" + "go.opencensus.io/trace" +) + +// Takes in a list of indexed attestation wrappers and returns any +// found attester slashings to the caller. +func (s *Service) checkSlashableAttestations( + ctx context.Context, atts []*slashertypes.IndexedAttestationWrapper, +) ([]*ethpb.AttesterSlashing, error) { + currentEpoch := slots.EpochsSinceGenesis(s.genesisTime) + slashings := make([]*ethpb.AttesterSlashing, 0) + indices := make([]types.ValidatorIndex, 0) + + // TODO(#8331): Consider using goroutines and wait groups here. + groupedAtts := s.groupByValidatorChunkIndex(atts) + for validatorChunkIdx, batch := range groupedAtts { + attSlashings, err := s.detectAllAttesterSlashings(ctx, &chunkUpdateArgs{ + validatorChunkIndex: validatorChunkIdx, + currentEpoch: currentEpoch, + }, batch) + + if err != nil { + return nil, errors.Wrap(err, "Could not detect slashable attestations") + } + slashings = append(slashings, attSlashings...) + indices = append(indices, s.params.validatorIndicesInChunk(validatorChunkIdx)...) + } + if err := s.serviceCfg.Database.SaveLastEpochWrittenForValidators(ctx, indices, currentEpoch); err != nil { + return nil, err + } + return slashings, nil +} + +// Given a list of attestations all corresponding to a validator chunk index as well +// as the current epoch in time, we perform slashing detection. +// The process is as follows given a list of attestations: +// +// 1. Check for attester double votes using the list of attestations. +// 2. Group the attestations by chunk index. +// 3. Update the min and max spans for those grouped attestations, check if any slashings are +// found in the process +// 4. Update the latest written epoch for all validators involved to the current epoch. +// +// This function performs a lot of critical actions and is split into smaller helpers for cleanliness. +func (s *Service) detectAllAttesterSlashings( + ctx context.Context, + args *chunkUpdateArgs, + attestations []*slashertypes.IndexedAttestationWrapper, +) ([]*ethpb.AttesterSlashing, error) { + // Check for double votes. + doubleVoteSlashings, err := s.checkDoubleVotes(ctx, attestations) + if err != nil { + return nil, errors.Wrap(err, "could not check slashable double votes") + } + + // Group attestations by chunk index. + groupedAtts := s.groupByChunkIndex(attestations) + + // Update min and max spans and retrieve any detected slashable offenses. + surroundingSlashings, err := s.updateSpans(ctx, &chunkUpdateArgs{ + kind: slashertypes.MinSpan, + validatorChunkIndex: args.validatorChunkIndex, + currentEpoch: args.currentEpoch, + }, groupedAtts) + if err != nil { + return nil, errors.Wrapf( + err, + "could not update min attestation spans for validator chunk index %d", + args.validatorChunkIndex, + ) + } + + surroundedSlashings, err := s.updateSpans(ctx, &chunkUpdateArgs{ + kind: slashertypes.MaxSpan, + validatorChunkIndex: args.validatorChunkIndex, + currentEpoch: args.currentEpoch, + }, groupedAtts) + if err != nil { + return nil, errors.Wrapf( + err, + "could not update max attestation spans for validator chunk index %d", + args.validatorChunkIndex, + ) + } + + // Consolidate all slashings into a slice. + slashings := make([]*ethpb.AttesterSlashing, 0, len(doubleVoteSlashings)+len(surroundingSlashings)+len(surroundedSlashings)) + slashings = append(slashings, doubleVoteSlashings...) + slashings = append(slashings, surroundingSlashings...) + slashings = append(slashings, surroundedSlashings...) + if len(slashings) > 0 { + log.WithField("numSlashings", len(slashings)).Info("Slashable attestation offenses found") + } + return slashings, nil +} + +// Check for attester slashing double votes by looking at every single validator index +// in each attestation's attesting indices and checking if there already exist records for such +// attestation's target epoch. If so, we append a double vote slashing object to a list of slashings +// we return to the caller. +func (s *Service) checkDoubleVotes( + ctx context.Context, attestations []*slashertypes.IndexedAttestationWrapper, +) ([]*ethpb.AttesterSlashing, error) { + ctx, span := trace.StartSpan(ctx, "Slasher.checkDoubleVotes") + defer span.End() + // We check if there are any slashable double votes in the input list + // of attestations with respect to each other. + slashings := make([]*ethpb.AttesterSlashing, 0) + existingAtts := make(map[string]*slashertypes.IndexedAttestationWrapper) + for _, att := range attestations { + for _, valIdx := range att.IndexedAttestation.AttestingIndices { + key := uintToString(uint64(att.IndexedAttestation.Data.Target.Epoch)) + ":" + uintToString(valIdx) + existingAtt, ok := existingAtts[key] + if !ok { + existingAtts[key] = att + continue + } + if att.SigningRoot != existingAtt.SigningRoot { + doubleVotesTotal.Inc() + slashings = append(slashings, ðpb.AttesterSlashing{ + Attestation_1: existingAtt.IndexedAttestation, + Attestation_2: att.IndexedAttestation, + }) + } + } + } + + // We check if there are any slashable double votes in the input list + // of attestations with respect to our database. + moreSlashings, err := s.checkDoubleVotesOnDisk(ctx, attestations) + if err != nil { + return nil, errors.Wrap(err, "could not check attestation double votes on disk") + } + return append(slashings, moreSlashings...), nil +} + +// Check for double votes in our database given a list of incoming attestations. +func (s *Service) checkDoubleVotesOnDisk( + ctx context.Context, attestations []*slashertypes.IndexedAttestationWrapper, +) ([]*ethpb.AttesterSlashing, error) { + ctx, span := trace.StartSpan(ctx, "Slasher.checkDoubleVotesOnDisk") + defer span.End() + doubleVotes, err := s.serviceCfg.Database.CheckAttesterDoubleVotes( + ctx, attestations, + ) + if err != nil { + return nil, errors.Wrap(err, "could not retrieve potential double votes from disk") + } + doubleVoteSlashings := make([]*ethpb.AttesterSlashing, 0) + for _, doubleVote := range doubleVotes { + doubleVotesTotal.Inc() + doubleVoteSlashings = append(doubleVoteSlashings, ðpb.AttesterSlashing{ + Attestation_1: doubleVote.PrevAttestationWrapper.IndexedAttestation, + Attestation_2: doubleVote.AttestationWrapper.IndexedAttestation, + }) + } + return doubleVoteSlashings, nil +} + +// Updates spans and detects any slashable attester offenses along the way. +// 1. Determine the chunks we need to use for updating for the validator indices +// in a validator chunk index, then retrieve those chunks from the database. +// 2. Using the chunks from step (1): +// for every attestation by chunk index: +// for each validator in the attestation's attesting indices: +// - Check if the attestation is slashable, if so return a slashing object. +// 3. Save the updated chunks to disk. +func (s *Service) updateSpans( + ctx context.Context, + args *chunkUpdateArgs, + attestationsByChunkIdx map[uint64][]*slashertypes.IndexedAttestationWrapper, +) ([]*ethpb.AttesterSlashing, error) { + ctx, span := trace.StartSpan(ctx, "Slasher.updateSpans") + defer span.End() + // Determine the chunk indices we need to use for slashing detection. + validatorIndices := s.params.validatorIndicesInChunk(args.validatorChunkIndex) + chunkIndices, err := s.determineChunksToUpdateForValidators(ctx, args, validatorIndices) + if err != nil { + return nil, errors.Wrapf( + err, + "could not determine chunks to update for validator indices %v", + validatorIndices, + ) + } + // Load the required chunks from disk. + chunksByChunkIdx, err := s.loadChunks(ctx, args, chunkIndices) + if err != nil { + return nil, errors.Wrapf( + err, + "could not load chunks for chunk indices %v", + chunkIndices, + ) + } + + // Apply the attestations to the related chunks and find any + // slashings along the way. + slashings := make([]*ethpb.AttesterSlashing, 0) + for _, attestationBatch := range attestationsByChunkIdx { + for _, att := range attestationBatch { + for _, validatorIdx := range att.IndexedAttestation.AttestingIndices { + validatorIndex := types.ValidatorIndex(validatorIdx) + computedValidatorChunkIdx := s.params.validatorChunkIndex(validatorIndex) + + // Every validator chunk index represents a range of validators. + // If it possible that the validator index in this loop iteration is + // not part of the validator chunk index we are updating chunks for. + // + // For example, if there are 4 validators per validator chunk index, + // then validator chunk index 0 contains validator indices [0, 1, 2, 3] + // If we see an attestation with attesting indices [3, 4, 5] and we are updating + // chunks for validator chunk index 0, only validator index 3 should make + // it past this line. + if args.validatorChunkIndex != computedValidatorChunkIdx { + continue + } + slashing, err := s.applyAttestationForValidator( + ctx, + args, + validatorIndex, + chunksByChunkIdx, + att, + ) + if err != nil { + return nil, errors.Wrapf( + err, + "could not apply attestation for validator index %d", + validatorIndex, + ) + } + if slashing != nil { + slashings = append(slashings, slashing) + } + } + } + } + + // Write the updated chunks to disk. + return slashings, s.saveUpdatedChunks(ctx, args, chunksByChunkIdx) +} + +// For a list of validator indices, we retrieve their latest written epoch. Then, for each +// (validator, latest epoch written) pair, we determine the chunks we need to update and +// perform slashing detection with. +func (s *Service) determineChunksToUpdateForValidators( + ctx context.Context, + args *chunkUpdateArgs, + validatorIndices []types.ValidatorIndex, +) (chunkIndices []uint64, err error) { + ctx, span := trace.StartSpan(ctx, "Slasher.determineChunksToUpdateForValidators") + defer span.End() + lastCurrentEpochs, err := s.serviceCfg.Database.LastEpochWrittenForValidators(ctx, validatorIndices) + if err != nil { + err = errors.Wrap(err, "could not get latest epoch attested for validators") + return + } + + // Initialize the last epoch written for each validator to 0. + lastCurrentEpochByValidator := make(map[types.ValidatorIndex]types.Epoch, len(validatorIndices)) + for _, valIdx := range validatorIndices { + lastCurrentEpochByValidator[valIdx] = 0 + } + for _, lastEpoch := range lastCurrentEpochs { + lastCurrentEpochByValidator[lastEpoch.ValidatorIndex] = lastEpoch.Epoch + } + + // For every single validator and their last written current epoch, we determine + // the chunk indices we need to update based on all the chunks between the last + // epoch written and the current epoch, inclusive. + chunkIndicesToUpdate := make(map[uint64]bool) + + for _, epoch := range lastCurrentEpochByValidator { + latestEpochWritten := epoch + for latestEpochWritten <= args.currentEpoch { + chunkIdx := s.params.chunkIndex(latestEpochWritten) + chunkIndicesToUpdate[chunkIdx] = true + latestEpochWritten++ + } + } + chunkIndices = make([]uint64, 0, len(chunkIndicesToUpdate)) + for chunkIdx := range chunkIndicesToUpdate { + chunkIndices = append(chunkIndices, chunkIdx) + } + return +} + +// Checks if an incoming attestation is slashable based on the validator chunk it +// corresponds to. If a slashable offense is found, we return it to the caller. +// If not, then update every single chunk the attestation covers, starting from its +// source epoch up to its target. +func (s *Service) applyAttestationForValidator( + ctx context.Context, + args *chunkUpdateArgs, + validatorIndex types.ValidatorIndex, + chunksByChunkIdx map[uint64]Chunker, + attestation *slashertypes.IndexedAttestationWrapper, +) (*ethpb.AttesterSlashing, error) { + ctx, span := trace.StartSpan(ctx, "Slasher.applyAttestationForValidator") + defer span.End() + sourceEpoch := attestation.IndexedAttestation.Data.Source.Epoch + targetEpoch := attestation.IndexedAttestation.Data.Target.Epoch + + attestationDistance.Observe(float64(targetEpoch) - float64(sourceEpoch)) + + chunkIdx := s.params.chunkIndex(sourceEpoch) + chunk, err := s.getChunk(ctx, args, chunksByChunkIdx, chunkIdx) + if err != nil { + return nil, errors.Wrapf(err, "could not get chunk at index %d", chunkIdx) + } + + // Check slashable, if so, return the slashing. + slashing, err := chunk.CheckSlashable( + ctx, + s.serviceCfg.Database, + validatorIndex, + attestation, + ) + if err != nil { + return nil, errors.Wrapf( + err, + "could not check if attestation for validator index %d is slashable", + validatorIndex, + ) + } + if slashing != nil { + return slashing, nil + } + + // Get the first start epoch for the chunk. If it does not exist or + // is not possible based on the input arguments, do not continue with the update. + startEpoch, exists := chunk.StartEpoch(sourceEpoch, args.currentEpoch) + if !exists { + return nil, nil + } + + // Given a single attestation could span across multiple chunks + // for a validator min or max span, we attempt to update the current chunk + // for the source epoch of the attestation. If the update function tells + // us we need to proceed to the next chunk, we continue by determining + // the start epoch of the next chunk. We exit once no longer need to + // keep updating chunks. + for { + chunkIdx = s.params.chunkIndex(startEpoch) + chunk, err := s.getChunk(ctx, args, chunksByChunkIdx, chunkIdx) + if err != nil { + return nil, errors.Wrapf(err, "could not get chunk at index %d", chunkIdx) + } + keepGoing, err := chunk.Update( + &chunkUpdateArgs{ + chunkIndex: chunkIdx, + currentEpoch: args.currentEpoch, + }, + validatorIndex, + startEpoch, + targetEpoch, + ) + if err != nil { + return nil, errors.Wrapf( + err, + "could not update chunk at chunk index %d for validator index %d and current epoch %d", + chunkIdx, + validatorIndex, + args.currentEpoch, + ) + } + // We update the chunksByChunkIdx map with the chunk we just updated. + chunksByChunkIdx[chunkIdx] = chunk + if !keepGoing { + break + } + // Move to first epoch of next chunk if needed. + startEpoch = chunk.NextChunkStartEpoch(startEpoch) + } + return nil, nil +} + +// Retrieves a chunk at a chunk index from a map. If such chunk does not exist, which +// should be rare (occurring when we receive an attestation with source and target epochs +// that span multiple chunk indices), then we fallback to fetching from disk. +func (s *Service) getChunk( + ctx context.Context, + args *chunkUpdateArgs, + chunksByChunkIdx map[uint64]Chunker, + chunkIdx uint64, +) (Chunker, error) { + chunk, ok := chunksByChunkIdx[chunkIdx] + if ok { + return chunk, nil + } + // We can ensure we load the appropriate chunk we need by fetching from the DB. + diskChunks, err := s.loadChunks(ctx, args, []uint64{chunkIdx}) + if err != nil { + return nil, errors.Wrapf(err, "could not load chunk at index %d", chunkIdx) + } + if chunk, ok := diskChunks[chunkIdx]; ok { + return chunk, nil + } + return nil, fmt.Errorf("could not retrieve chunk at chunk index %d from disk", chunkIdx) +} + +// Load chunks for a specified list of chunk indices. We attempt to load it from the database. +// If the data exists, then we initialize a chunk of a specified kind. Otherwise, we create +// an empty chunk, add it to our map, and then return it to the caller. +func (s *Service) loadChunks( + ctx context.Context, + args *chunkUpdateArgs, + chunkIndices []uint64, +) (map[uint64]Chunker, error) { + ctx, span := trace.StartSpan(ctx, "Slasher.loadChunks") + defer span.End() + chunkKeys := make([][]byte, 0, len(chunkIndices)) + for _, chunkIdx := range chunkIndices { + chunkKeys = append(chunkKeys, s.params.flatSliceID(args.validatorChunkIndex, chunkIdx)) + } + rawChunks, chunksExist, err := s.serviceCfg.Database.LoadSlasherChunks(ctx, args.kind, chunkKeys) + if err != nil { + return nil, errors.Wrapf( + err, + "could not load slasher chunk index", + ) + } + chunksByChunkIdx := make(map[uint64]Chunker, len(rawChunks)) + for i := 0; i < len(rawChunks); i++ { + // If the chunk exists in the database, we initialize it from the raw bytes data. + // If it does not exist, we initialize an empty chunk. + var chunk Chunker + switch args.kind { + case slashertypes.MinSpan: + if chunksExist[i] { + chunk, err = MinChunkSpansSliceFrom(s.params, rawChunks[i]) + } else { + chunk = EmptyMinSpanChunksSlice(s.params) + } + case slashertypes.MaxSpan: + if chunksExist[i] { + chunk, err = MaxChunkSpansSliceFrom(s.params, rawChunks[i]) + } else { + chunk = EmptyMaxSpanChunksSlice(s.params) + } + } + if err != nil { + return nil, errors.Wrap(err, "could not initialize chunk") + } + chunksByChunkIdx[chunkIndices[i]] = chunk + } + return chunksByChunkIdx, nil +} + +// Saves updated chunks to disk given the required database schema. +func (s *Service) saveUpdatedChunks( + ctx context.Context, + args *chunkUpdateArgs, + updatedChunksByChunkIdx map[uint64]Chunker, +) error { + ctx, span := trace.StartSpan(ctx, "Slasher.saveUpdatedChunks") + defer span.End() + chunkKeys := make([][]byte, 0, len(updatedChunksByChunkIdx)) + chunks := make([][]uint16, 0, len(updatedChunksByChunkIdx)) + for chunkIdx, chunk := range updatedChunksByChunkIdx { + chunkKeys = append(chunkKeys, s.params.flatSliceID(args.validatorChunkIndex, chunkIdx)) + chunks = append(chunks, chunk.Chunk()) + } + chunksSavedTotal.Add(float64(len(chunks))) + return s.serviceCfg.Database.SaveSlasherChunks(ctx, args.kind, chunkKeys, chunks) +} diff --git a/beacon-chain/slasher/detect_attestations_test.go b/beacon-chain/slasher/detect_attestations_test.go new file mode 100644 index 000000000000..01b0f584bbe0 --- /dev/null +++ b/beacon-chain/slasher/detect_attestations_test.go @@ -0,0 +1,917 @@ +package slasher + +import ( + "context" + "fmt" + "sort" + "testing" + "time" + + types "github.com/prysmaticlabs/eth2-types" + mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/core" + "github.com/prysmaticlabs/prysm/beacon-chain/core/signing" + dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings" + slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" + "github.com/prysmaticlabs/prysm/config/params" + "github.com/prysmaticlabs/prysm/crypto/bls" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/testing/assert" + "github.com/prysmaticlabs/prysm/testing/require" + "github.com/prysmaticlabs/prysm/testing/util" + logTest "github.com/sirupsen/logrus/hooks/test" +) + +func Test_processQueuedAttestations(t *testing.T) { + type args struct { + attestationQueue []*slashertypes.IndexedAttestationWrapper + currentEpoch types.Epoch + } + tests := []struct { + name string + args args + shouldNotBeSlashable bool + }{ + { + name: "Detects surrounding vote (source 1, target 2), (source 0, target 3)", + args: args{ + attestationQueue: []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 1, 2, []uint64{0, 1}, nil), + createAttestationWrapper(t, 0, 3, []uint64{0, 1}, nil), + }, + currentEpoch: 4, + }, + }, + { + name: "Detects surrounding vote (source 50, target 51), (source 0, target 1000)", + args: args{ + attestationQueue: []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 50, 51, []uint64{0}, nil), + createAttestationWrapper(t, 0, 1000, []uint64{0}, nil), + }, + currentEpoch: 1000, + }, + }, + { + name: "Detects surrounded vote (source 0, target 3), (source 1, target 2)", + args: args{ + attestationQueue: []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 3, []uint64{0, 1}, nil), + createAttestationWrapper(t, 1, 2, []uint64{0, 1}, nil), + }, + currentEpoch: 4, + }, + }, + { + name: "Detects double vote, (source 1, target 2), (source 0, target 2)", + args: args{ + attestationQueue: []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 1, 2, []uint64{0, 1}, nil), + createAttestationWrapper(t, 0, 2, []uint64{0, 1}, nil), + }, + currentEpoch: 4, + }, + }, + { + name: "Not slashable, surrounding but non-overlapping attesting indices within same validator chunk index", + args: args{ + attestationQueue: []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 1, 2, []uint64{0}, nil), + createAttestationWrapper(t, 0, 3, []uint64{1}, nil), + }, + currentEpoch: 4, + }, + shouldNotBeSlashable: true, + }, + { + name: "Not slashable, surrounded but non-overlapping attesting indices within same validator chunk index", + args: args{ + attestationQueue: []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 3, []uint64{0, 1}, nil), + createAttestationWrapper(t, 1, 2, []uint64{2, 3}, nil), + }, + currentEpoch: 4, + }, + shouldNotBeSlashable: true, + }, + { + name: "Not slashable, surrounding but non-overlapping attesting indices in different validator chunk index", + args: args{ + attestationQueue: []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 3, []uint64{0}, nil), + createAttestationWrapper( + t, + 1, + 2, + []uint64{params.BeaconConfig().MinGenesisActiveValidatorCount - 1}, + nil, + ), + }, + currentEpoch: 4, + }, + shouldNotBeSlashable: true, + }, + { + name: "Not slashable, surrounded but non-overlapping attesting indices in different validator chunk index", + args: args{ + attestationQueue: []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 3, []uint64{0}, nil), + createAttestationWrapper( + t, + 1, + 2, + []uint64{params.BeaconConfig().MinGenesisActiveValidatorCount - 1}, + nil, + ), + }, + currentEpoch: 4, + }, + shouldNotBeSlashable: true, + }, + { + name: "Not slashable, (source 1, target 2), (source 2, target 3)", + args: args{ + attestationQueue: []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 1, 2, []uint64{0, 1}, nil), + createAttestationWrapper(t, 2, 3, []uint64{0, 1}, nil), + }, + currentEpoch: 4, + }, + shouldNotBeSlashable: true, + }, + { + name: "Not slashable, (source 0, target 3), (source 2, target 4)", + args: args{ + attestationQueue: []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 3, []uint64{0, 1}, nil), + createAttestationWrapper(t, 2, 4, []uint64{0, 1}, nil), + }, + currentEpoch: 4, + }, + shouldNotBeSlashable: true, + }, + { + name: "Not slashable, (source 0, target 2), (source 0, target 3)", + args: args{ + attestationQueue: []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 2, []uint64{0, 1}, nil), + createAttestationWrapper(t, 0, 3, []uint64{0, 1}, nil), + }, + currentEpoch: 4, + }, + shouldNotBeSlashable: true, + }, + { + name: "Not slashable, (source 0, target 3), (source 0, target 2)", + args: args{ + attestationQueue: []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 3, []uint64{0, 1}, nil), + createAttestationWrapper(t, 0, 2, []uint64{0, 1}, nil), + }, + currentEpoch: 4, + }, + shouldNotBeSlashable: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + hook := logTest.NewGlobal() + defer hook.Reset() + slasherDB := dbtest.SetupSlasherDB(t) + ctx, cancel := context.WithCancel(context.Background()) + + currentTime := time.Now() + totalSlots := uint64(tt.args.currentEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch) + secondsSinceGenesis := time.Duration(totalSlots * params.BeaconConfig().SecondsPerSlot) + genesisTime := currentTime.Add(-secondsSinceGenesis * time.Second) + + beaconState, err := util.NewBeaconState() + require.NoError(t, err) + slot, err := core.StartSlot(tt.args.currentEpoch) + require.NoError(t, err) + require.NoError(t, beaconState.SetSlot(slot)) + mockChain := &mock.ChainService{ + State: beaconState, + Slot: &slot, + } + + // Initialize validators in the state. + numVals := params.BeaconConfig().MinGenesisActiveValidatorCount + validators := make([]*ethpb.Validator, numVals) + privKeys := make([]bls.SecretKey, numVals) + for i := range validators { + privKey, err := bls.RandKey() + require.NoError(t, err) + privKeys[i] = privKey + validators[i] = ðpb.Validator{ + PublicKey: privKey.PublicKey().Marshal(), + WithdrawalCredentials: make([]byte, 32), + } + } + err = beaconState.SetValidators(validators) + require.NoError(t, err) + domain, err := signing.Domain( + beaconState.Fork(), + 0, + params.BeaconConfig().DomainBeaconAttester, + beaconState.GenesisValidatorRoot(), + ) + require.NoError(t, err) + + // Create valid signatures for all input attestations in the test. + for _, attestationWrapper := range tt.args.attestationQueue { + signingRoot, err := signing.ComputeSigningRoot(attestationWrapper.IndexedAttestation.Data, domain) + require.NoError(t, err) + attestingIndices := attestationWrapper.IndexedAttestation.AttestingIndices + sigs := make([]bls.Signature, len(attestingIndices)) + for i, validatorIndex := range attestingIndices { + privKey := privKeys[validatorIndex] + sigs[i] = privKey.Sign(signingRoot[:]) + } + attestationWrapper.IndexedAttestation.Signature = bls.AggregateSignatures(sigs).Marshal() + } + + s := &Service{ + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + HeadStateFetcher: mockChain, + AttestationStateFetcher: mockChain, + SlashingPoolInserter: &slashings.PoolMock{}, + }, + params: DefaultParams(), + attsQueue: newAttestationsQueue(), + genesisTime: genesisTime, + } + currentSlotChan := make(chan types.Slot) + exitChan := make(chan struct{}) + go func() { + s.processQueuedAttestations(ctx, currentSlotChan) + exitChan <- struct{}{} + }() + s.attsQueue.extend(tt.args.attestationQueue) + currentSlotChan <- slot + time.Sleep(time.Millisecond * 200) + cancel() + <-exitChan + if tt.shouldNotBeSlashable { + require.LogsDoNotContain(t, hook, "Attester slashing detected") + } else { + require.LogsContain(t, hook, "Attester slashing detected") + } + }) + } +} + +func Test_processQueuedAttestations_MultipleChunkIndices(t *testing.T) { + hook := logTest.NewGlobal() + defer hook.Reset() + + slasherDB := dbtest.SetupSlasherDB(t) + ctx, cancel := context.WithCancel(context.Background()) + slasherParams := DefaultParams() + + // We process submit attestations from chunk index 0 to chunk index 1. + // What we want to test here is if we can proceed + // with processing queued attestations once the chunk index changes. + // For example, epochs 0 - 15 are chunk 0, epochs 16 - 31 are chunk 1, etc. + startEpoch := types.Epoch(slasherParams.chunkSize) + endEpoch := types.Epoch(slasherParams.chunkSize + 1) + + currentTime := time.Now() + totalSlots := uint64(startEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch) + secondsSinceGenesis := time.Duration(totalSlots * params.BeaconConfig().SecondsPerSlot) + genesisTime := currentTime.Add(-secondsSinceGenesis * time.Second) + + beaconState, err := util.NewBeaconState() + require.NoError(t, err) + mockChain := &mock.ChainService{ + State: beaconState, + } + + s := &Service{ + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + HeadStateFetcher: mockChain, + AttestationStateFetcher: mockChain, + SlashingPoolInserter: &slashings.PoolMock{}, + }, + params: slasherParams, + attsQueue: newAttestationsQueue(), + genesisTime: genesisTime, + } + currentSlotChan := make(chan types.Slot) + exitChan := make(chan struct{}) + go func() { + s.processQueuedAttestations(ctx, currentSlotChan) + exitChan <- struct{}{} + }() + + for i := startEpoch; i <= endEpoch; i++ { + source := types.Epoch(0) + target := types.Epoch(0) + if i != 0 { + source = i - 1 + target = i + } + var sr [32]byte + copy(sr[:], fmt.Sprintf("%d", i)) + att := createAttestationWrapper(t, source, target, []uint64{0}, sr[:]) + s.attsQueue = newAttestationsQueue() + s.attsQueue.push(att) + slot, err := core.StartSlot(i) + require.NoError(t, err) + require.NoError(t, mockChain.State.SetSlot(slot)) + s.serviceCfg.HeadStateFetcher = mockChain + currentSlotChan <- slot + } + + time.Sleep(time.Millisecond * 200) + cancel() + <-exitChan + require.LogsDoNotContain(t, hook, "Slashable offenses found") + require.LogsDoNotContain(t, hook, "Could not detect") +} + +func Test_processQueuedAttestations_OverlappingChunkIndices(t *testing.T) { + hook := logTest.NewGlobal() + defer hook.Reset() + + slasherDB := dbtest.SetupSlasherDB(t) + ctx, cancel := context.WithCancel(context.Background()) + slasherParams := DefaultParams() + + startEpoch := types.Epoch(slasherParams.chunkSize) + + currentTime := time.Now() + totalSlots := uint64(startEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch) + secondsSinceGenesis := time.Duration(totalSlots * params.BeaconConfig().SecondsPerSlot) + genesisTime := currentTime.Add(-secondsSinceGenesis * time.Second) + + beaconState, err := util.NewBeaconState() + require.NoError(t, err) + mockChain := &mock.ChainService{ + State: beaconState, + } + + s := &Service{ + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + HeadStateFetcher: mockChain, + AttestationStateFetcher: mockChain, + SlashingPoolInserter: &slashings.PoolMock{}, + }, + params: slasherParams, + attsQueue: newAttestationsQueue(), + genesisTime: genesisTime, + } + currentSlotChan := make(chan types.Slot) + exitChan := make(chan struct{}) + go func() { + s.processQueuedAttestations(ctx, currentSlotChan) + exitChan <- struct{}{} + }() + + // We create two attestations fully spanning chunk indices 0 and chunk 1 + att1 := createAttestationWrapper(t, types.Epoch(slasherParams.chunkSize-2), types.Epoch(slasherParams.chunkSize), []uint64{0, 1}, nil) + att2 := createAttestationWrapper(t, types.Epoch(slasherParams.chunkSize-1), types.Epoch(slasherParams.chunkSize+1), []uint64{0, 1}, nil) + + // We attempt to process the batch. + s.attsQueue = newAttestationsQueue() + s.attsQueue.push(att1) + s.attsQueue.push(att2) + slot, err := core.StartSlot(att2.IndexedAttestation.Data.Target.Epoch) + require.NoError(t, err) + mockChain.Slot = &slot + s.serviceCfg.HeadStateFetcher = mockChain + currentSlotChan <- slot + + time.Sleep(time.Millisecond * 200) + cancel() + <-exitChan + require.LogsDoNotContain(t, hook, "Slashable offenses found") + require.LogsDoNotContain(t, hook, "Could not detect") +} + +func Test_determineChunksToUpdateForValidators_FromLatestWrittenEpoch(t *testing.T) { + slasherDB := dbtest.SetupSlasherDB(t) + ctx := context.Background() + + // Check if the chunk at chunk index already exists in-memory. + s := &Service{ + params: &Parameters{ + chunkSize: 2, // 2 epochs in a chunk. + validatorChunkSize: 2, // 2 validators in a chunk. + historyLength: 4, + }, + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + }, + } + validators := []types.ValidatorIndex{ + 1, 2, + } + currentEpoch := types.Epoch(3) + + // Set the latest written epoch for validators to current epoch - 1. + latestWrittenEpoch := currentEpoch - 1 + err := slasherDB.SaveLastEpochWrittenForValidators(ctx, validators, latestWrittenEpoch) + require.NoError(t, err) + + // Because the validators have no recorded latest epoch written in the database, + // Because the latest written epoch for the input validators is == 2, we expect + // that we will update all epochs from 2 up to 3 (the current epoch). This is all + // safe contained in chunk index 1. + chunkIndices, err := s.determineChunksToUpdateForValidators( + ctx, + &chunkUpdateArgs{ + currentEpoch: currentEpoch, + }, + validators, + ) + require.NoError(t, err) + require.DeepEqual(t, []uint64{1}, chunkIndices) +} + +func Test_determineChunksToUpdateForValidators_FromGenesis(t *testing.T) { + slasherDB := dbtest.SetupSlasherDB(t) + ctx := context.Background() + + // Check if the chunk at chunk index already exists in-memory. + s := &Service{ + params: &Parameters{ + chunkSize: 2, // 2 epochs in a chunk. + validatorChunkSize: 2, // 2 validators in a chunk. + historyLength: 4, + }, + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + }, + } + validators := []types.ValidatorIndex{ + 1, 2, + } + // Because the validators have no recorded latest epoch written in the database, + // we expect that we will update all epochs from genesis up to the current epoch. + // Given the chunk size is 2 epochs per chunk, updating with current epoch == 3 + // will mean that we should be updating from epoch 0 to 3, meaning chunk indices 0 and 1. + chunkIndices, err := s.determineChunksToUpdateForValidators( + ctx, + &chunkUpdateArgs{ + currentEpoch: 3, + }, + validators, + ) + require.NoError(t, err) + sort.Slice(chunkIndices, func(i, j int) bool { + return chunkIndices[i] < chunkIndices[j] + }) + require.DeepEqual(t, []uint64{0, 1}, chunkIndices) +} + +func Test_applyAttestationForValidator_MinSpanChunk(t *testing.T) { + ctx := context.Background() + slasherDB := dbtest.SetupSlasherDB(t) + params := DefaultParams() + srv := &Service{ + params: params, + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + }, + } + // We initialize an empty chunks slice. + chunk := EmptyMinSpanChunksSlice(params) + chunkIdx := uint64(0) + currentEpoch := types.Epoch(3) + validatorIdx := types.ValidatorIndex(0) + args := &chunkUpdateArgs{ + chunkIndex: chunkIdx, + currentEpoch: currentEpoch, + } + chunksByChunkIdx := map[uint64]Chunker{ + chunkIdx: chunk, + } + + // We apply attestation with (source 1, target 2) for our validator. + source := types.Epoch(1) + target := types.Epoch(2) + att := createAttestationWrapper(t, source, target, nil, nil) + slashing, err := srv.applyAttestationForValidator( + ctx, + args, + validatorIdx, + chunksByChunkIdx, + att, + ) + require.NoError(t, err) + require.Equal(t, true, slashing == nil) + att.IndexedAttestation.AttestingIndices = []uint64{uint64(validatorIdx)} + err = slasherDB.SaveAttestationRecordsForValidators( + ctx, + []*slashertypes.IndexedAttestationWrapper{att}, + ) + require.NoError(t, err) + + // Next, we apply an attestation with (source 0, target 3) and + // expect a slashable offense to be returned. + source = types.Epoch(0) + target = types.Epoch(3) + slashableAtt := createAttestationWrapper(t, source, target, nil, nil) + slashing, err = srv.applyAttestationForValidator( + ctx, + args, + validatorIdx, + chunksByChunkIdx, + slashableAtt, + ) + require.NoError(t, err) + require.NotNil(t, slashing) +} + +func Test_applyAttestationForValidator_MaxSpanChunk(t *testing.T) { + ctx := context.Background() + slasherDB := dbtest.SetupSlasherDB(t) + params := DefaultParams() + srv := &Service{ + params: params, + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + }, + } + // We initialize an empty chunks slice. + chunk := EmptyMaxSpanChunksSlice(params) + chunkIdx := uint64(0) + currentEpoch := types.Epoch(3) + validatorIdx := types.ValidatorIndex(0) + args := &chunkUpdateArgs{ + chunkIndex: chunkIdx, + currentEpoch: currentEpoch, + } + chunksByChunkIdx := map[uint64]Chunker{ + chunkIdx: chunk, + } + + // We apply attestation with (source 0, target 3) for our validator. + source := types.Epoch(0) + target := types.Epoch(3) + att := createAttestationWrapper(t, source, target, nil, nil) + slashing, err := srv.applyAttestationForValidator( + ctx, + args, + validatorIdx, + chunksByChunkIdx, + att, + ) + require.NoError(t, err) + require.Equal(t, true, slashing == nil) + att.IndexedAttestation.AttestingIndices = []uint64{uint64(validatorIdx)} + err = slasherDB.SaveAttestationRecordsForValidators( + ctx, + []*slashertypes.IndexedAttestationWrapper{att}, + ) + require.NoError(t, err) + + // Next, we apply an attestation with (source 1, target 2) and + // expect a slashable offense to be returned. + source = types.Epoch(1) + target = types.Epoch(2) + slashableAtt := createAttestationWrapper(t, source, target, nil, nil) + slashing, err = srv.applyAttestationForValidator( + ctx, + args, + validatorIdx, + chunksByChunkIdx, + slashableAtt, + ) + require.NoError(t, err) + require.NotNil(t, slashing) +} + +func Test_checkDoubleVotes_SlashableInputAttestations(t *testing.T) { + slasherDB := dbtest.SetupSlasherDB(t) + ctx := context.Background() + // For a list of input attestations, check that we can + // indeed check there could exist a double vote offense + // within the list with respect to other entries in the list. + atts := []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 1, []uint64{1, 2}, []byte{1}), + createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{1}), + createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{2}), // Different signing root. + } + srv := &Service{ + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + }, + params: DefaultParams(), + } + prev1 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{1}) + cur1 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{2}) + prev2 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{1}) + cur2 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{2}) + wanted := []*ethpb.AttesterSlashing{ + { + Attestation_1: prev1.IndexedAttestation, + Attestation_2: cur1.IndexedAttestation, + }, + { + Attestation_1: prev2.IndexedAttestation, + Attestation_2: cur2.IndexedAttestation, + }, + } + slashings, err := srv.checkDoubleVotes(ctx, atts) + require.NoError(t, err) + require.DeepEqual(t, wanted, slashings) +} + +func Test_checkDoubleVotes_SlashableAttestationsOnDisk(t *testing.T) { + slasherDB := dbtest.SetupSlasherDB(t) + ctx := context.Background() + // For a list of input attestations, check that we can + // indeed check there could exist a double vote offense + // within the list with respect to previous entries in the db. + prevAtts := []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 1, []uint64{1, 2}, []byte{1}), + createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{1}), + } + srv := &Service{ + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + }, + params: DefaultParams(), + } + err := slasherDB.SaveAttestationRecordsForValidators(ctx, prevAtts) + require.NoError(t, err) + + prev1 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{1}) + cur1 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{2}) + prev2 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{1}) + cur2 := createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{2}) + wanted := []*ethpb.AttesterSlashing{ + { + Attestation_1: prev1.IndexedAttestation, + Attestation_2: cur1.IndexedAttestation, + }, + { + Attestation_1: prev2.IndexedAttestation, + Attestation_2: cur2.IndexedAttestation, + }, + } + newAtts := []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 2, []uint64{1, 2}, []byte{2}), // Different signing root. + } + slashings, err := srv.checkDoubleVotes(ctx, newAtts) + require.NoError(t, err) + require.DeepEqual(t, wanted, slashings) +} + +func Test_loadChunks_MinSpans(t *testing.T) { + testLoadChunks(t, slashertypes.MinSpan) +} + +func Test_loadChunks_MaxSpans(t *testing.T) { + testLoadChunks(t, slashertypes.MaxSpan) +} + +func testLoadChunks(t *testing.T, kind slashertypes.ChunkKind) { + slasherDB := dbtest.SetupSlasherDB(t) + ctx := context.Background() + + // Check if the chunk at chunk index already exists in-memory. + params := DefaultParams() + s := &Service{ + params: DefaultParams(), + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + }, + } + // If a chunk at a chunk index does not exist, ensure it + // is initialized as an empty chunk. + var emptyChunk Chunker + if kind == slashertypes.MinSpan { + emptyChunk = EmptyMinSpanChunksSlice(params) + } else { + emptyChunk = EmptyMaxSpanChunksSlice(params) + } + chunkIdx := uint64(2) + received, err := s.loadChunks(ctx, &chunkUpdateArgs{ + validatorChunkIndex: 0, + kind: kind, + }, []uint64{chunkIdx}) + require.NoError(t, err) + wanted := map[uint64]Chunker{ + chunkIdx: emptyChunk, + } + require.DeepEqual(t, wanted, received) + + // Save chunks to disk, then load them properly from disk. + var existingChunk Chunker + if kind == slashertypes.MinSpan { + existingChunk = EmptyMinSpanChunksSlice(params) + } else { + existingChunk = EmptyMaxSpanChunksSlice(params) + } + validatorIdx := types.ValidatorIndex(0) + epochInChunk := types.Epoch(0) + targetEpoch := types.Epoch(2) + err = setChunkDataAtEpoch( + params, + existingChunk.Chunk(), + validatorIdx, + epochInChunk, + targetEpoch, + ) + require.NoError(t, err) + require.DeepNotEqual(t, existingChunk, emptyChunk) + + updatedChunks := map[uint64]Chunker{ + 2: existingChunk, + 4: existingChunk, + 6: existingChunk, + } + err = s.saveUpdatedChunks( + ctx, + &chunkUpdateArgs{ + validatorChunkIndex: 0, + kind: kind, + }, + updatedChunks, + ) + require.NoError(t, err) + // Check if the retrieved chunks match what we just saved to disk. + received, err = s.loadChunks(ctx, &chunkUpdateArgs{ + validatorChunkIndex: 0, + kind: kind, + }, []uint64{2, 4, 6}) + require.NoError(t, err) + require.DeepEqual(t, updatedChunks, received) +} + +func TestService_processQueuedAttestations(t *testing.T) { + hook := logTest.NewGlobal() + slasherDB := dbtest.SetupSlasherDB(t) + + beaconState, err := util.NewBeaconState() + require.NoError(t, err) + slot, err := core.StartSlot(1) + require.NoError(t, err) + require.NoError(t, beaconState.SetSlot(slot)) + mockChain := &mock.ChainService{ + State: beaconState, + Slot: &slot, + } + + s := &Service{ + params: DefaultParams(), + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + HeadStateFetcher: mockChain, + }, + attsQueue: newAttestationsQueue(), + } + + s.attsQueue.extend([]*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 1, []uint64{0, 1} /* indices */, nil /* signingRoot */), + }) + ctx, cancel := context.WithCancel(context.Background()) + tickerChan := make(chan types.Slot) + exitChan := make(chan struct{}) + go func() { + s.processQueuedAttestations(ctx, tickerChan) + exitChan <- struct{}{} + }() + + // Send a value over the ticker. + tickerChan <- 1 + cancel() + <-exitChan + assert.LogsContain(t, hook, "New slot, processing queued") +} + +func BenchmarkCheckSlashableAttestations(b *testing.B) { + slasherDB := dbtest.SetupSlasherDB(b) + + beaconState, err := util.NewBeaconState() + require.NoError(b, err) + slot := types.Slot(0) + mockChain := &mock.ChainService{ + State: beaconState, + Slot: &slot, + } + + s := &Service{ + params: DefaultParams(), + serviceCfg: &ServiceConfig{ + Database: slasherDB, + StateNotifier: &mock.MockStateNotifier{}, + HeadStateFetcher: mockChain, + }, + attsQueue: newAttestationsQueue(), + } + + b.Run("1 attestation 1 validator", func(b *testing.B) { + b.ResetTimer() + runAttestationsBenchmark(b, s, 1, 1 /* validator */) + }) + b.Run("1 attestation 100 validators", func(b *testing.B) { + b.ResetTimer() + runAttestationsBenchmark(b, s, 1, 100 /* validator */) + }) + b.Run("1 attestation 1000 validators", func(b *testing.B) { + b.ResetTimer() + runAttestationsBenchmark(b, s, 1, 1000 /* validator */) + }) + + b.Run("100 attestations 1 validator", func(b *testing.B) { + b.ResetTimer() + runAttestationsBenchmark(b, s, 100, 1 /* validator */) + }) + b.Run("100 attestations 100 validators", func(b *testing.B) { + b.ResetTimer() + runAttestationsBenchmark(b, s, 100, 100 /* validator */) + }) + b.Run("100 attestations 1000 validators", func(b *testing.B) { + b.ResetTimer() + runAttestationsBenchmark(b, s, 100, 1000 /* validator */) + }) + + b.Run("1000 attestations 1 validator", func(b *testing.B) { + b.ResetTimer() + runAttestationsBenchmark(b, s, 1000, 1 /* validator */) + }) + b.Run("1000 attestations 100 validators", func(b *testing.B) { + b.ResetTimer() + runAttestationsBenchmark(b, s, 1000, 100 /* validator */) + }) + b.Run("1000 attestations 1000 validators", func(b *testing.B) { + b.ResetTimer() + runAttestationsBenchmark(b, s, 1000, 1000 /* validator */) + }) +} + +func runAttestationsBenchmark(b *testing.B, s *Service, numAtts, numValidators uint64) { + indices := make([]uint64, numValidators) + for i := uint64(0); i < numValidators; i++ { + indices[i] = i + } + atts := make([]*slashertypes.IndexedAttestationWrapper, numAtts) + for i := uint64(0); i < numAtts; i++ { + source := types.Epoch(i) + target := types.Epoch(i + 1) + signingRoot := [32]byte{} + copy(signingRoot[:], fmt.Sprintf("%d", i)) + atts[i] = createAttestationWrapper( + b, + source, + target, /* target */ + indices, /* indices */ + signingRoot[:], /* signingRoot */ + ) + } + for i := 0; i < b.N; i++ { + numEpochs := numAtts + totalSeconds := numEpochs * uint64(params.BeaconConfig().SlotsPerEpoch) * params.BeaconConfig().SecondsPerSlot + genesisTime := time.Now().Add(-time.Second * time.Duration(totalSeconds)) + s.genesisTime = genesisTime + + _, err := s.checkSlashableAttestations(context.Background(), atts) + require.NoError(b, err) + } +} + +func createAttestationWrapper(t testing.TB, source, target types.Epoch, indices []uint64, signingRoot []byte) *slashertypes.IndexedAttestationWrapper { + data := ðpb.AttestationData{ + BeaconBlockRoot: bytesutil.PadTo(signingRoot, 32), + Source: ðpb.Checkpoint{ + Epoch: source, + Root: params.BeaconConfig().ZeroHash[:], + }, + Target: ðpb.Checkpoint{ + Epoch: target, + Root: params.BeaconConfig().ZeroHash[:], + }, + } + signRoot, err := data.HashTreeRoot() + if err != nil { + t.Fatal(err) + } + return &slashertypes.IndexedAttestationWrapper{ + IndexedAttestation: ðpb.IndexedAttestation{ + AttestingIndices: indices, + Data: data, + Signature: params.BeaconConfig().EmptySignature[:], + }, + SigningRoot: signRoot, + } +} diff --git a/beacon-chain/slasher/helpers_test.go b/beacon-chain/slasher/helpers_test.go index 5e28398c9dee..a6b9547aae16 100644 --- a/beacon-chain/slasher/helpers_test.go +++ b/beacon-chain/slasher/helpers_test.go @@ -7,7 +7,6 @@ import ( types "github.com/prysmaticlabs/eth2-types" slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" "github.com/prysmaticlabs/prysm/config/params" - "github.com/prysmaticlabs/prysm/encoding/bytesutil" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/testing/require" logTest "github.com/sirupsen/logrus/hooks/test" @@ -515,29 +514,3 @@ func Test_isDoubleProposal(t *testing.T) { }) } } - -func createAttestationWrapper(t testing.TB, source, target types.Epoch, indices []uint64, signingRoot []byte) *slashertypes.IndexedAttestationWrapper { - data := ðpb.AttestationData{ - BeaconBlockRoot: bytesutil.PadTo(signingRoot, 32), - Source: ðpb.Checkpoint{ - Epoch: source, - Root: params.BeaconConfig().ZeroHash[:], - }, - Target: ðpb.Checkpoint{ - Epoch: target, - Root: params.BeaconConfig().ZeroHash[:], - }, - } - signRoot, err := data.HashTreeRoot() - if err != nil { - t.Fatal(err) - } - return &slashertypes.IndexedAttestationWrapper{ - IndexedAttestation: ðpb.IndexedAttestation{ - AttestingIndices: indices, - Data: data, - Signature: params.BeaconConfig().EmptySignature[:], - }, - SigningRoot: signRoot, - } -} diff --git a/beacon-chain/slasher/receive.go b/beacon-chain/slasher/receive.go index efbb23401920..dd3b0055390e 100644 --- a/beacon-chain/slasher/receive.go +++ b/beacon-chain/slasher/receive.go @@ -110,8 +110,11 @@ func (s *Service) processQueuedAttestations(ctx context.Context, slotTicker <-ch } // Check for slashings. - // TODO(#8331): Detect slashings. - slashings := make([]*ethpb.AttesterSlashing, 0) + slashings, err := s.checkSlashableAttestations(ctx, validAtts) + if err != nil { + log.WithError(err).Error("Could not check slashable attestations") + continue + } // Process attester slashings by verifying their signatures, submitting // to the beacon node's operations pool, and logging them. diff --git a/beacon-chain/slasher/service.go b/beacon-chain/slasher/service.go index 066fc34fdf69..8b7b2953b287 100644 --- a/beacon-chain/slasher/service.go +++ b/beacon-chain/slasher/service.go @@ -1,6 +1,8 @@ package slasher import ( + "time" + "github.com/prysmaticlabs/prysm/async/event" "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" @@ -26,8 +28,9 @@ type ServiceConfig struct { // Service for running slasher mode in a beacon node. type Service struct { - params *Parameters - serviceCfg *ServiceConfig - blksQueue *blocksQueue - attsQueue *attestationsQueue + params *Parameters + serviceCfg *ServiceConfig + blksQueue *blocksQueue + attsQueue *attestationsQueue + genesisTime time.Time } From 806bcf1d29de23262e3f20cde6a5e6b493d521d8 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 28 Sep 2021 14:21:07 -0700 Subject: [PATCH 07/25] Clean up unused types & function comments (#9691) Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- beacon-chain/cache/committees.go | 3 --- beacon-chain/p2p/broadcaster.go | 2 +- beacon-chain/p2p/testing/mock_metadataprovider.go | 2 +- beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go | 5 ----- beacon-chain/state/stategen/replay.go | 2 +- config/features/config.go | 2 +- proto/prysm/v1alpha1/wrapper/metadata.go | 2 +- validator/client/attest_test.go | 2 +- validator/client/propose_test.go | 2 +- validator/client/validator_test.go | 2 +- 10 files changed, 8 insertions(+), 16 deletions(-) diff --git a/beacon-chain/cache/committees.go b/beacon-chain/cache/committees.go index 1afaf21e43f3..ba1bfb4887ac 100644 --- a/beacon-chain/cache/committees.go +++ b/beacon-chain/cache/committees.go @@ -10,9 +10,6 @@ import ( // a Committee struct. var ErrNotCommittee = errors.New("object is not a committee struct") -// ErrNonCommitteeKey will be returned when the committee key does not exist in cache. -var ErrNonCommitteeKey = errors.New("committee key does not exist") - // Committees defines the shuffled committees seed. type Committees struct { CommitteeCount uint64 diff --git a/beacon-chain/p2p/broadcaster.go b/beacon-chain/p2p/broadcaster.go index a832fe3e89f8..62393121023e 100644 --- a/beacon-chain/p2p/broadcaster.go +++ b/beacon-chain/p2p/broadcaster.go @@ -24,7 +24,7 @@ import ( // GossipTypeMapping. var ErrMessageNotMapped = errors.New("message type is not mapped to a PubSub topic") -// Broadcasts a message to the p2p network, the message is assumed to be +// Broadcast a message to the p2p network, the message is assumed to be // broadcasted to the current fork. func (s *Service) Broadcast(ctx context.Context, msg proto.Message) error { ctx, span := trace.StartSpan(ctx, "p2p.Broadcast") diff --git a/beacon-chain/p2p/testing/mock_metadataprovider.go b/beacon-chain/p2p/testing/mock_metadataprovider.go index 7a05dd30c592..d9e6910fa5b5 100644 --- a/beacon-chain/p2p/testing/mock_metadataprovider.go +++ b/beacon-chain/p2p/testing/mock_metadataprovider.go @@ -1,7 +1,7 @@ package testing import ( - metadata "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/metadata" + "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/metadata" ) // MockMetadataProvider is a fake implementation of the MetadataProvider interface. diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go index b99f382cb4d1..cecf38705115 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go @@ -48,11 +48,6 @@ type eth1DataSingleVote struct { blockHeight *big.Int } -type eth1DataAggregatedVote struct { - data eth1DataSingleVote - votes int -} - // blockData required to create a beacon block. type blockData struct { ParentRoot []byte diff --git a/beacon-chain/state/stategen/replay.go b/beacon-chain/state/stategen/replay.go index 4962bbbb7a5d..fc8c50b34c7c 100644 --- a/beacon-chain/state/stategen/replay.go +++ b/beacon-chain/state/stategen/replay.go @@ -8,7 +8,7 @@ import ( types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/beacon-chain/core" "github.com/prysmaticlabs/prysm/beacon-chain/core/altair" - transition "github.com/prysmaticlabs/prysm/beacon-chain/core/transition" + "github.com/prysmaticlabs/prysm/beacon-chain/core/transition" "github.com/prysmaticlabs/prysm/beacon-chain/db/filters" "github.com/prysmaticlabs/prysm/beacon-chain/state" "github.com/prysmaticlabs/prysm/encoding/bytesutil" diff --git a/config/features/config.go b/config/features/config.go index a99c31e1f577..a184f6d04cd2 100644 --- a/config/features/config.go +++ b/config/features/config.go @@ -1,5 +1,5 @@ /* -Package featureconfig defines which features are enabled for runtime +Package features defines which features are enabled for runtime in order to selectively enable certain features to maintain a stable runtime. The process for implementing new features using this package is as follows: diff --git a/proto/prysm/v1alpha1/wrapper/metadata.go b/proto/prysm/v1alpha1/wrapper/metadata.go index fed523654458..549eaa42b2f3 100644 --- a/proto/prysm/v1alpha1/wrapper/metadata.go +++ b/proto/prysm/v1alpha1/wrapper/metadata.go @@ -3,7 +3,7 @@ package wrapper import ( "github.com/prysmaticlabs/go-bitfield" pb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" - metadata "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/metadata" + "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/metadata" "github.com/prysmaticlabs/prysm/runtime/version" "google.golang.org/protobuf/proto" ) diff --git a/validator/client/attest_test.go b/validator/client/attest_test.go index dacccb14b5f4..090b64011f72 100644 --- a/validator/client/attest_test.go +++ b/validator/client/attest_test.go @@ -26,7 +26,7 @@ import ( "github.com/prysmaticlabs/prysm/testing/util" prysmTime "github.com/prysmaticlabs/prysm/time" logTest "github.com/sirupsen/logrus/hooks/test" - grpc "google.golang.org/grpc" + "google.golang.org/grpc" "gopkg.in/d4l3k/messagediff.v1" ) diff --git a/validator/client/propose_test.go b/validator/client/propose_test.go index 5304347b527a..1fb18c5e56f2 100644 --- a/validator/client/propose_test.go +++ b/validator/client/propose_test.go @@ -24,7 +24,7 @@ import ( testing2 "github.com/prysmaticlabs/prysm/validator/db/testing" "github.com/prysmaticlabs/prysm/validator/graffiti" logTest "github.com/sirupsen/logrus/hooks/test" - grpc "google.golang.org/grpc" + "google.golang.org/grpc" "google.golang.org/protobuf/types/known/timestamppb" ) diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index eefcdf5a0bc0..5d4f42fd5cb5 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -27,7 +27,7 @@ import ( dbTest "github.com/prysmaticlabs/prysm/validator/db/testing" "github.com/sirupsen/logrus" logTest "github.com/sirupsen/logrus/hooks/test" - grpc "google.golang.org/grpc" + "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" ) From 8f8ccf11e42c9682e0fe10e57cdf82b33a3db778 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 28 Sep 2021 14:51:11 -0700 Subject: [PATCH 08/25] Update head more timely before (#9651) * Move update head closer to transition * Update process_block.go * Update tests * Move savePostStateInfo back * Update process_block.go * Update process_block_helpers.go * Minor clean up for better diff Co-authored-by: Raul Jordan Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- beacon-chain/blockchain/process_block.go | 35 +++++++++++-------- .../blockchain/process_block_helpers.go | 2 +- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index 53885c41969f..275538113dab 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -106,20 +106,6 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b return err } - // Updating next slot state cache can happen in the background. It shouldn't block rest of the process. - if features.Get().EnableNextSlotStateCache { - go func() { - // Use a custom deadline here, since this method runs asynchronously. - // We ignore the parent method's context and instead create a new one - // with a custom deadline, therefore using the background context instead. - slotCtx, cancel := context.WithTimeout(context.Background(), slotDeadline) - defer cancel() - if err := transition.UpdateNextSlotCache(slotCtx, blockRoot[:], postState); err != nil { - log.WithError(err).Debug("could not update next slot state cache") - } - }() - } - // Update justified check point. if postState.CurrentJustifiedCheckpoint().Epoch > s.justifiedCheckpt.Epoch { if err := s.updateJustified(ctx, postState); err != nil { @@ -155,6 +141,27 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b }, }) + // Updating next slot state cache can happen in the background. It shouldn't block rest of the process. + if features.Get().EnableNextSlotStateCache { + go func() { + // Use a custom deadline here, since this method runs asynchronously. + // We ignore the parent method's context and instead create a new one + // with a custom deadline, therefore using the background context instead. + slotCtx, cancel := context.WithTimeout(context.Background(), slotDeadline) + defer cancel() + if err := transition.UpdateNextSlotCache(slotCtx, blockRoot[:], postState); err != nil { + log.WithError(err).Debug("could not update next slot state cache") + } + }() + } + + // Save justified check point to db. + if postState.CurrentJustifiedCheckpoint().Epoch > s.justifiedCheckpt.Epoch { + if err := s.cfg.BeaconDB.SaveJustifiedCheckpoint(ctx, postState.CurrentJustifiedCheckpoint()); err != nil { + return err + } + } + // Update finalized check point. if newFinalized { if err := s.updateFinalized(ctx, postState.FinalizedCheckpoint()); err != nil { diff --git a/beacon-chain/blockchain/process_block_helpers.go b/beacon-chain/blockchain/process_block_helpers.go index ba89cb4d293f..738c0133e2bc 100644 --- a/beacon-chain/blockchain/process_block_helpers.go +++ b/beacon-chain/blockchain/process_block_helpers.go @@ -212,7 +212,7 @@ func (s *Service) updateJustified(ctx context.Context, state state.ReadOnlyBeaco } } - return s.cfg.BeaconDB.SaveJustifiedCheckpoint(ctx, cpt) + return nil } // This caches input checkpoint as justified for the service struct. It rotates current justified to previous justified, From 393549ad1973b57714c104f1c8550049abffaacb Mon Sep 17 00:00:00 2001 From: Nishant Das Date: Wed, 29 Sep 2021 06:19:10 +0800 Subject: [PATCH 09/25] Add in Balance Safety Check (#9419) Co-authored-by: terence tsao Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- beacon-chain/core/altair/epoch_precompute.go | 26 ++++++++++++++----- beacon-chain/core/altair/epoch_spec_test.go | 14 ++++++++++ beacon-chain/core/epoch/epoch_processing.go | 5 +++- .../core/epoch/epoch_processing_test.go | 14 ++++++++++ .../core/epoch/precompute/reward_penalty.go | 5 +++- .../core/helpers/rewards_penalties.go | 11 +++++--- .../core/helpers/rewards_penalties_test.go | 21 +++++++++++++++ .../altair_transition_no_verify_sig_test.go | 19 ++++++++++++++ 8 files changed, 103 insertions(+), 12 deletions(-) diff --git a/beacon-chain/core/altair/epoch_precompute.go b/beacon-chain/core/altair/epoch_precompute.go index f1c4f24f308b..5fcbfe773e05 100644 --- a/beacon-chain/core/altair/epoch_precompute.go +++ b/beacon-chain/core/altair/epoch_precompute.go @@ -98,7 +98,10 @@ func ProcessInactivityScores( v.InactivityScore -= 1 } } else { - v.InactivityScore += bias + v.InactivityScore, err = math.Add64(v.InactivityScore, bias) + if err != nil { + return nil, nil, err + } } if !helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) { @@ -200,7 +203,10 @@ func ProcessRewardsAndPenaltiesPrecompute( // Compute the post balance of the validator after accounting for the // attester and proposer rewards and penalties. - balances[i] = helpers.IncreaseBalanceWithVal(balances[i], attsRewards[i]) + balances[i], err = helpers.IncreaseBalanceWithVal(balances[i], attsRewards[i]) + if err != nil { + return nil, err + } balances[i] = helpers.DecreaseBalanceWithVal(balances[i], attsPenalties[i]) vals[i].AfterEpochTransitionBalance = balances[i] @@ -230,7 +236,10 @@ func AttestationsDelta(beaconState state.BeaconStateAltair, bal *precompute.Bala inactivityDenominator := cfg.InactivityScoreBias * cfg.InactivityPenaltyQuotientAltair for i, v := range vals { - rewards[i], penalties[i] = attestationDelta(bal, v, baseRewardMultiplier, inactivityDenominator, leak) + rewards[i], penalties[i], err = attestationDelta(bal, v, baseRewardMultiplier, inactivityDenominator, leak) + if err != nil { + return nil, nil, err + } } return rewards, penalties, nil @@ -240,11 +249,11 @@ func attestationDelta( bal *precompute.Balance, val *precompute.Validator, baseRewardMultiplier, inactivityDenominator uint64, - inactivityLeak bool) (reward, penalty uint64) { + inactivityLeak bool) (reward, penalty uint64, err error) { eligible := val.IsActivePrevEpoch || (val.IsSlashed && !val.IsWithdrawableCurrentEpoch) // Per spec `ActiveCurrentEpoch` can't be 0 to process attestation delta. if !eligible || bal.ActiveCurrentEpoch == 0 { - return 0, 0 + return 0, 0, nil } cfg := params.BeaconConfig() @@ -289,9 +298,12 @@ func attestationDelta( // Process finality delay penalty // Apply an additional penalty to validators that did not vote on the correct target or slashed if !val.IsPrevEpochTargetAttester || val.IsSlashed { - n := effectiveBalance * val.InactivityScore + n, err := math.Mul64(effectiveBalance, val.InactivityScore) + if err != nil { + return 0, 0, err + } penalty += n / inactivityDenominator } - return reward, penalty + return reward, penalty, nil } diff --git a/beacon-chain/core/altair/epoch_spec_test.go b/beacon-chain/core/altair/epoch_spec_test.go index 7ab45bfafda7..12526062c619 100644 --- a/beacon-chain/core/altair/epoch_spec_test.go +++ b/beacon-chain/core/altair/epoch_spec_test.go @@ -3,6 +3,7 @@ package altair_test import ( "context" "fmt" + "math" "testing" types "github.com/prysmaticlabs/eth2-types" @@ -181,3 +182,16 @@ func TestProcessSlashings_SlashedLess(t *testing.T) { }) } } + +func TestProcessSlashings_BadValue(t *testing.T) { + base := ðpb.BeaconStateAltair{ + Slot: 0, + Validators: []*ethpb.Validator{{Slashed: true}}, + Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance}, + Slashings: []uint64{math.MaxUint64, 1e9}, + } + s, err := stateAltair.InitializeFromProto(base) + require.NoError(t, err) + _, err = epoch.ProcessSlashings(s, params.BeaconConfig().ProportionalSlashingMultiplierAltair) + require.ErrorContains(t, "addition overflows", err) +} diff --git a/beacon-chain/core/epoch/epoch_processing.go b/beacon-chain/core/epoch/epoch_processing.go index 93a767555f57..e655869e6b9b 100644 --- a/beacon-chain/core/epoch/epoch_processing.go +++ b/beacon-chain/core/epoch/epoch_processing.go @@ -180,7 +180,10 @@ func ProcessSlashings(state state.BeaconState, slashingMultiplier uint64) (state slashings := state.Slashings() totalSlashing := uint64(0) for _, slashing := range slashings { - totalSlashing += slashing + totalSlashing, err = math.Add64(totalSlashing, slashing) + if err != nil { + return nil, err + } } // a callback is used here to apply the following actions to all validators diff --git a/beacon-chain/core/epoch/epoch_processing_test.go b/beacon-chain/core/epoch/epoch_processing_test.go index 51444d205ad3..7063922364f1 100644 --- a/beacon-chain/core/epoch/epoch_processing_test.go +++ b/beacon-chain/core/epoch/epoch_processing_test.go @@ -3,6 +3,7 @@ package epoch_test import ( "context" "fmt" + "math" "testing" types "github.com/prysmaticlabs/eth2-types" @@ -440,3 +441,16 @@ func buildState(t testing.TB, slot types.Slot, validatorCount uint64) state.Beac } return s } + +func TestProcessSlashings_BadValue(t *testing.T) { + base := ðpb.BeaconState{ + Slot: 0, + Validators: []*ethpb.Validator{{Slashed: true}}, + Balances: []uint64{params.BeaconConfig().MaxEffectiveBalance}, + Slashings: []uint64{math.MaxUint64, 1e9}, + } + s, err := v1.InitializeFromProto(base) + require.NoError(t, err) + _, err = epoch.ProcessSlashings(s, params.BeaconConfig().ProportionalSlashingMultiplier) + require.ErrorContains(t, "addition overflows", err) +} diff --git a/beacon-chain/core/epoch/precompute/reward_penalty.go b/beacon-chain/core/epoch/precompute/reward_penalty.go index 942093b9388a..7de5cf7a45b3 100644 --- a/beacon-chain/core/epoch/precompute/reward_penalty.go +++ b/beacon-chain/core/epoch/precompute/reward_penalty.go @@ -47,7 +47,10 @@ func ProcessRewardsAndPenaltiesPrecompute( // Compute the post balance of the validator after accounting for the // attester and proposer rewards and penalties. - validatorBals[i] = helpers.IncreaseBalanceWithVal(validatorBals[i], attsRewards[i]+proposerRewards[i]) + validatorBals[i], err = helpers.IncreaseBalanceWithVal(validatorBals[i], attsRewards[i]+proposerRewards[i]) + if err != nil { + return nil, err + } validatorBals[i] = helpers.DecreaseBalanceWithVal(validatorBals[i], attsPenalties[i]) vp[i].AfterEpochTransitionBalance = validatorBals[i] diff --git a/beacon-chain/core/helpers/rewards_penalties.go b/beacon-chain/core/helpers/rewards_penalties.go index 6264de0fb77b..d3087d7aec58 100644 --- a/beacon-chain/core/helpers/rewards_penalties.go +++ b/beacon-chain/core/helpers/rewards_penalties.go @@ -8,6 +8,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core" "github.com/prysmaticlabs/prysm/beacon-chain/state" "github.com/prysmaticlabs/prysm/config/params" + mathutil "github.com/prysmaticlabs/prysm/math" ) var balanceCache = cache.NewEffectiveBalanceCache() @@ -95,7 +96,11 @@ func IncreaseBalance(state state.BeaconState, idx types.ValidatorIndex, delta ui if err != nil { return err } - return state.UpdateBalancesAtIndex(idx, IncreaseBalanceWithVal(balAtIdx, delta)) + newBal, err := IncreaseBalanceWithVal(balAtIdx, delta) + if err != nil { + return err + } + return state.UpdateBalancesAtIndex(idx, newBal) } // IncreaseBalanceWithVal increases validator with the given 'index' balance by 'delta' in Gwei. @@ -108,8 +113,8 @@ func IncreaseBalance(state state.BeaconState, idx types.ValidatorIndex, delta ui // Increase the validator balance at index ``index`` by ``delta``. // """ // state.balances[index] += delta -func IncreaseBalanceWithVal(currBalance, delta uint64) uint64 { - return currBalance + delta +func IncreaseBalanceWithVal(currBalance, delta uint64) (uint64, error) { + return mathutil.Add64(currBalance, delta) } // DecreaseBalance decreases validator with the given 'index' balance by 'delta' in Gwei. diff --git a/beacon-chain/core/helpers/rewards_penalties_test.go b/beacon-chain/core/helpers/rewards_penalties_test.go index 3c882210e566..87a214d3ff6f 100644 --- a/beacon-chain/core/helpers/rewards_penalties_test.go +++ b/beacon-chain/core/helpers/rewards_penalties_test.go @@ -1,6 +1,7 @@ package helpers import ( + "math" "testing" types "github.com/prysmaticlabs/eth2-types" @@ -238,3 +239,23 @@ func buildState(slot types.Slot, validatorCount uint64) *ethpb.BeaconState { CurrentJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}, } } + +func TestIncreaseBadBalance_NotOK(t *testing.T) { + tests := []struct { + i types.ValidatorIndex + b []uint64 + nb uint64 + }{ + {i: 0, b: []uint64{math.MaxUint64, math.MaxUint64, math.MaxUint64}, nb: 1}, + {i: 2, b: []uint64{math.MaxUint64, math.MaxUint64, math.MaxUint64}, nb: 33 * 1e9}, + } + for _, test := range tests { + state, err := v1.InitializeFromProto(ðpb.BeaconState{ + Validators: []*ethpb.Validator{ + {EffectiveBalance: 4}, {EffectiveBalance: 4}, {EffectiveBalance: 4}}, + Balances: test.b, + }) + require.NoError(t, err) + require.ErrorContains(t, "addition overflows", IncreaseBalance(state, test.i, test.nb)) + } +} diff --git a/beacon-chain/core/transition/altair_transition_no_verify_sig_test.go b/beacon-chain/core/transition/altair_transition_no_verify_sig_test.go index 4991955cede3..9791e63554d1 100644 --- a/beacon-chain/core/transition/altair_transition_no_verify_sig_test.go +++ b/beacon-chain/core/transition/altair_transition_no_verify_sig_test.go @@ -2,6 +2,7 @@ package transition_test import ( "context" + "math" "testing" "github.com/prysmaticlabs/go-bitfield" @@ -207,6 +208,24 @@ func TestExecuteStateTransitionNoVerifyAnySig_PassesProcessingConditions(t *test require.Equal(t, true, verified, "Could not verify signature set") } +func TestProcessEpoch_BadBalanceAltair(t *testing.T) { + s, _ := util.DeterministicGenesisStateAltair(t, 100) + assert.NoError(t, s.SetSlot(63)) + assert.NoError(t, s.UpdateBalancesAtIndex(0, math.MaxUint64)) + participation := byte(0) + participation = altair.AddValidatorFlag(participation, params.BeaconConfig().TimelyHeadFlagIndex) + participation = altair.AddValidatorFlag(participation, params.BeaconConfig().TimelySourceFlagIndex) + participation = altair.AddValidatorFlag(participation, params.BeaconConfig().TimelyTargetFlagIndex) + + epochParticipation, err := s.CurrentEpochParticipation() + assert.NoError(t, err) + epochParticipation[0] = participation + assert.NoError(t, s.SetCurrentParticipationBits(epochParticipation)) + assert.NoError(t, s.SetPreviousParticipationBits(epochParticipation)) + _, err = altair.ProcessEpoch(context.Background(), s) + assert.ErrorContains(t, "addition overflows", err) +} + func createFullAltairBlockWithOperations(t *testing.T) (state.BeaconStateAltair, *ethpb.SignedBeaconBlockAltair) { beaconState, privKeys := util.DeterministicGenesisStateAltair(t, 32) From 6c5bf70021c0b5c37dd783b791ede3c9d66c6b74 Mon Sep 17 00:00:00 2001 From: Preston Van Loon Date: Tue, 28 Sep 2021 19:18:31 -0500 Subject: [PATCH 10/25] CI: fix remote caching (#9696) --- .buildkite-bazelrc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.buildkite-bazelrc b/.buildkite-bazelrc index eca539e9e20a..5cd30b5d632d 100644 --- a/.buildkite-bazelrc +++ b/.buildkite-bazelrc @@ -10,7 +10,7 @@ # Prysm specific remote-cache properties. #build:remote-cache --disk_cache= -build:remote-cache --remote_download_minimal +build:remote-cache --remote_download_toplevel build:remote-cache --remote_cache=grpc://bazel-remote-cache:9092 build:remote-cache --experimental_remote_downloader=grpc://bazel-remote-cache:9092 build:remote-cache --remote_local_fallback @@ -46,4 +46,4 @@ test:fuzz --flaky_test_attempts=1 # Better caching build:nostamp --nostamp -build:nostamp --workspace_status_command=./hack/workspace_status_ci.sh \ No newline at end of file +build:nostamp --workspace_status_command=./hack/workspace_status_ci.sh From 0edb3b9e65386ffe983b62f7405992081978de0c Mon Sep 17 00:00:00 2001 From: Nishant Das Date: Wed, 29 Sep 2021 09:56:55 +0800 Subject: [PATCH 11/25] Flatten Attestation Packing (#9683) * flatten attestation packing * dedup again * terence's review Co-authored-by: Raul Jordan --- .../rpc/prysm/v1alpha1/validator/proposer.go | 81 +++++++++---------- .../prysm/v1alpha1/validator/proposer_test.go | 2 +- 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go index cecf38705115..4ef09e76f3cc 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go @@ -702,23 +702,15 @@ func (vs *Server) validateDepositTrie(trie *trie.SparseMerkleTrie, canonicalEth1 } // This filters the input attestations to return a list of valid attestations to be packaged inside a beacon block. -func (vs *Server) filterAttestationsForBlockInclusion(ctx context.Context, st state.BeaconState, atts []*ethpb.Attestation) ([]*ethpb.Attestation, error) { - ctx, span := trace.StartSpan(ctx, "ProposerServer.filterAttestationsForBlockInclusion") +func (vs *Server) validateAndDeleteAttsInPool(ctx context.Context, st state.BeaconState, atts []*ethpb.Attestation) ([]*ethpb.Attestation, error) { + ctx, span := trace.StartSpan(ctx, "ProposerServer.validateAndDeleteAttsInPool") defer span.End() validAtts, invalidAtts := proposerAtts(atts).filter(ctx, st) if err := vs.deleteAttsInPool(ctx, invalidAtts); err != nil { return nil, err } - deduped, err := validAtts.dedup() - if err != nil { - return nil, err - } - sorted, err := deduped.sortByProfitability() - if err != nil { - return nil, err - } - return sorted.limitToMaxAttestations(), nil + return validAtts, nil } // The input attestations are processed and seen by the node, this deletes them from pool @@ -761,50 +753,53 @@ func (vs *Server) packAttestations(ctx context.Context, latestState state.Beacon defer span.End() atts := vs.AttPool.AggregatedAttestations() - atts, err := vs.filterAttestationsForBlockInclusion(ctx, latestState, atts) + atts, err := vs.validateAndDeleteAttsInPool(ctx, latestState, atts) if err != nil { return nil, errors.Wrap(err, "could not filter attestations") } - // If there is any room left in the block, consider unaggregated attestations as well. - numAtts := uint64(len(atts)) - if numAtts < params.BeaconConfig().MaxAttestations { - uAtts, err := vs.AttPool.UnaggregatedAttestations() - if err != nil { - return nil, errors.Wrap(err, "could not get unaggregated attestations") - } - uAtts, err = vs.filterAttestationsForBlockInclusion(ctx, latestState, uAtts) - if err != nil { - return nil, errors.Wrap(err, "could not filter attestations") - } - atts = append(atts, uAtts...) + uAtts, err := vs.AttPool.UnaggregatedAttestations() + if err != nil { + return nil, errors.Wrap(err, "could not get unaggregated attestations") + } + uAtts, err = vs.validateAndDeleteAttsInPool(ctx, latestState, uAtts) + if err != nil { + return nil, errors.Wrap(err, "could not filter attestations") + } + atts = append(atts, uAtts...) - attsByDataRoot := make(map[[32]byte][]*ethpb.Attestation, len(atts)) - for _, att := range atts { - attDataRoot, err := att.Data.HashTreeRoot() - if err != nil { - return nil, err - } - attsByDataRoot[attDataRoot] = append(attsByDataRoot[attDataRoot], att) - } + // Remove duplicates from both aggregated/unaggregated attestations. This + // prevents inefficient aggregates being created. + atts, err = proposerAtts(atts).dedup() + if err != nil { + return nil, err + } - attsForInclusion := proposerAtts(make([]*ethpb.Attestation, 0)) - for _, as := range attsByDataRoot { - as, err := attaggregation.Aggregate(as) - if err != nil { - return nil, err - } - attsForInclusion = append(attsForInclusion, as...) - } - deduped, err := attsForInclusion.dedup() + attsByDataRoot := make(map[[32]byte][]*ethpb.Attestation, len(atts)) + for _, att := range atts { + attDataRoot, err := att.Data.HashTreeRoot() if err != nil { return nil, err } - sorted, err := deduped.sortByProfitability() + attsByDataRoot[attDataRoot] = append(attsByDataRoot[attDataRoot], att) + } + + attsForInclusion := proposerAtts(make([]*ethpb.Attestation, 0)) + for _, as := range attsByDataRoot { + as, err := attaggregation.Aggregate(as) if err != nil { return nil, err } - atts = sorted.limitToMaxAttestations() + attsForInclusion = append(attsForInclusion, as...) + } + deduped, err := attsForInclusion.dedup() + if err != nil { + return nil, err + } + sorted, err := deduped.sortByProfitability() + if err != nil { + return nil, err } + atts = sorted.limitToMaxAttestations() return atts, nil } diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go index 9bfec5278595..7a83634cff8d 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go @@ -1874,7 +1874,7 @@ func TestProposer_FilterAttestation(t *testing.T) { HeadFetcher: &mock.ChainService{State: state, Root: genesisRoot[:]}, } atts := tt.inputAtts() - received, err := proposerServer.filterAttestationsForBlockInclusion(context.Background(), state, atts) + received, err := proposerServer.validateAndDeleteAttsInPool(context.Background(), state, atts) if tt.wantedErr != "" { assert.ErrorContains(t, tt.wantedErr, err) assert.Equal(t, nil, received) From 9edba29f64ea42021b45040e708cbbc2af3a2118 Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Tue, 28 Sep 2021 21:27:21 -0500 Subject: [PATCH 12/25] Slasher Simulator Code for Testing Optimized Slasher Behavior (#9695) * slashing simulator * add in necessary items for slasher sim * sim item * fix up * fixed build * rev * slasher sim in testing * testonly * gaz * gaz * fix viz Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> Co-authored-by: prestonvanloon --- beacon-chain/blockchain/BUILD.bazel | 1 + beacon-chain/blockchain/testing/BUILD.bazel | 1 + beacon-chain/core/BUILD.bazel | 1 + beacon-chain/core/blocks/proposer_slashing.go | 2 +- beacon-chain/core/feed/BUILD.bazel | 2 +- beacon-chain/core/feed/state/BUILD.bazel | 5 +- beacon-chain/core/helpers/BUILD.bazel | 1 + beacon-chain/core/signing/signing_root.go | 2 +- beacon-chain/db/BUILD.bazel | 1 + beacon-chain/db/testing/BUILD.bazel | 2 +- beacon-chain/operations/slashings/BUILD.bazel | 2 + beacon-chain/operations/slashings/mock.go | 6 +- beacon-chain/operations/slashings/service.go | 2 +- .../operations/slashings/service_test.go | 7 + beacon-chain/operations/slashings/types.go | 17 +- beacon-chain/slasher/BUILD.bazel | 8 +- beacon-chain/slasher/log.go | 5 + beacon-chain/slasher/service.go | 149 +++++++++- beacon-chain/state/BUILD.bazel | 1 + beacon-chain/state/stategen/BUILD.bazel | 1 + testing/slasher/simulator/BUILD.bazel | 59 ++++ .../simulator/attestation_generator.go | 177 +++++++++++ .../simulator/attestation_generator_test.go | 64 ++++ testing/slasher/simulator/block_generator.go | 98 +++++++ .../slasher/simulator/block_generator_test.go | 31 ++ testing/slasher/simulator/simulator.go | 274 ++++++++++++++++++ testing/slasher/simulator/simulator_test.go | 48 +++ 27 files changed, 937 insertions(+), 30 deletions(-) create mode 100644 beacon-chain/slasher/log.go create mode 100644 testing/slasher/simulator/BUILD.bazel create mode 100644 testing/slasher/simulator/attestation_generator.go create mode 100644 testing/slasher/simulator/attestation_generator_test.go create mode 100644 testing/slasher/simulator/block_generator.go create mode 100644 testing/slasher/simulator/block_generator_test.go create mode 100644 testing/slasher/simulator/simulator.go create mode 100644 testing/slasher/simulator/simulator_test.go diff --git a/beacon-chain/blockchain/BUILD.bazel b/beacon-chain/blockchain/BUILD.bazel index 587ef9ad23fa..7f8934dd3bb2 100644 --- a/beacon-chain/blockchain/BUILD.bazel +++ b/beacon-chain/blockchain/BUILD.bazel @@ -23,6 +23,7 @@ go_library( visibility = [ "//beacon-chain:__subpackages__", "//testing/fuzz:__pkg__", + "//testing/slasher/simulator:__pkg__", ], deps = [ "//async:go_default_library", diff --git a/beacon-chain/blockchain/testing/BUILD.bazel b/beacon-chain/blockchain/testing/BUILD.bazel index 6e264716a4a2..d18126c3d9db 100644 --- a/beacon-chain/blockchain/testing/BUILD.bazel +++ b/beacon-chain/blockchain/testing/BUILD.bazel @@ -7,6 +7,7 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing", visibility = [ "//beacon-chain:__subpackages__", + "//testing:__subpackages__", "//testing/fuzz:__pkg__", ], deps = [ diff --git a/beacon-chain/core/BUILD.bazel b/beacon-chain/core/BUILD.bazel index 6e7ea4cf2426..2f880b074fae 100644 --- a/beacon-chain/core/BUILD.bazel +++ b/beacon-chain/core/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "//testing/benchmark/benchmark_files:__subpackages__", "//testing/endtoend/evaluators:__pkg__", "//testing/fuzz:__pkg__", + "//testing/slasher/simulator:__subpackages__", "//testing/spectest:__subpackages__", "//testing/util:__pkg__", "//tools:__subpackages__", diff --git a/beacon-chain/core/blocks/proposer_slashing.go b/beacon-chain/core/blocks/proposer_slashing.go index e33e0e4c2500..5d5bbfca5bd0 100644 --- a/beacon-chain/core/blocks/proposer_slashing.go +++ b/beacon-chain/core/blocks/proposer_slashing.go @@ -72,7 +72,7 @@ func ProcessProposerSlashings( // VerifyProposerSlashing verifies that the data provided from slashing is valid. func VerifyProposerSlashing( - beaconState state.BeaconState, + beaconState state.ReadOnlyBeaconState, slashing *ethpb.ProposerSlashing, ) error { if slashing.Header_1 == nil || slashing.Header_1.Header == nil || slashing.Header_2 == nil || slashing.Header_2.Header == nil { diff --git a/beacon-chain/core/feed/BUILD.bazel b/beacon-chain/core/feed/BUILD.bazel index bf84d634759d..b62508de578c 100644 --- a/beacon-chain/core/feed/BUILD.bazel +++ b/beacon-chain/core/feed/BUILD.bazel @@ -6,6 +6,6 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/feed", visibility = [ "//beacon-chain:__subpackages__", - "//shared:__subpackages__", + "//testing/slasher/simulator:__subpackages__", ], ) diff --git a/beacon-chain/core/feed/state/BUILD.bazel b/beacon-chain/core/feed/state/BUILD.bazel index 520ec1cdef0c..7223dc3db74a 100644 --- a/beacon-chain/core/feed/state/BUILD.bazel +++ b/beacon-chain/core/feed/state/BUILD.bazel @@ -7,7 +7,10 @@ go_library( "notifier.go", ], importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state", - visibility = ["//beacon-chain:__subpackages__"], + visibility = [ + "//beacon-chain:__subpackages__", + "//testing/slasher/simulator:__subpackages__", + ], deps = [ "//async/event:go_default_library", "//proto/prysm/v1alpha1/block:go_default_library", diff --git a/beacon-chain/core/helpers/BUILD.bazel b/beacon-chain/core/helpers/BUILD.bazel index 7467353f4dc7..73c706719a4a 100644 --- a/beacon-chain/core/helpers/BUILD.bazel +++ b/beacon-chain/core/helpers/BUILD.bazel @@ -29,6 +29,7 @@ go_library( "//testing/benchmark/benchmark_files:__subpackages__", "//testing/endtoend/evaluators:__pkg__", "//testing/fuzz:__pkg__", + "//testing/slasher/simulator:__pkg__", "//testing/spectest:__subpackages__", "//testing/util:__pkg__", "//tools:__subpackages__", diff --git a/beacon-chain/core/signing/signing_root.go b/beacon-chain/core/signing/signing_root.go index b2cc8a56e8ba..b6e362a97f07 100644 --- a/beacon-chain/core/signing/signing_root.go +++ b/beacon-chain/core/signing/signing_root.go @@ -64,7 +64,7 @@ func signingData(rootFunc func() ([32]byte, error), domain []byte) ([32]byte, er } // ComputeDomainVerifySigningRoot computes domain and verifies signing root of an object given the beacon state, validator index and signature. -func ComputeDomainVerifySigningRoot(st state.BeaconState, index types.ValidatorIndex, epoch types.Epoch, obj fssz.HashRoot, domain [4]byte, sig []byte) error { +func ComputeDomainVerifySigningRoot(st state.ReadOnlyBeaconState, index types.ValidatorIndex, epoch types.Epoch, obj fssz.HashRoot, domain [4]byte, sig []byte) error { v, err := st.ValidatorAtIndex(index) if err != nil { return err diff --git a/beacon-chain/db/BUILD.bazel b/beacon-chain/db/BUILD.bazel index b697104e4d52..5226616c06fa 100644 --- a/beacon-chain/db/BUILD.bazel +++ b/beacon-chain/db/BUILD.bazel @@ -13,6 +13,7 @@ go_library( "//beacon-chain:__subpackages__", "//cmd/beacon-chain:__subpackages__", "//testing/fuzz:__pkg__", + "//testing/slasher/simulator:__pkg__", "//tools:__subpackages__", ], deps = [ diff --git a/beacon-chain/db/testing/BUILD.bazel b/beacon-chain/db/testing/BUILD.bazel index 90bb0105be69..14f683c8fa69 100644 --- a/beacon-chain/db/testing/BUILD.bazel +++ b/beacon-chain/db/testing/BUILD.bazel @@ -7,7 +7,7 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db/testing", visibility = [ "//beacon-chain:__subpackages__", - "//testing/endtoend:__subpackages__", + "//testing:__subpackages__", ], deps = [ "//beacon-chain/db:go_default_library", diff --git a/beacon-chain/operations/slashings/BUILD.bazel b/beacon-chain/operations/slashings/BUILD.bazel index 7cd8076c951c..7a6987853afb 100644 --- a/beacon-chain/operations/slashings/BUILD.bazel +++ b/beacon-chain/operations/slashings/BUILD.bazel @@ -13,7 +13,9 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings", visibility = [ "//beacon-chain:__subpackages__", + "//testing/endtoend:__subpackages__", "//testing/fuzz:__pkg__", + "//testing/slasher/simulator:__pkg__", ], deps = [ "//beacon-chain/core:go_default_library", diff --git a/beacon-chain/operations/slashings/mock.go b/beacon-chain/operations/slashings/mock.go index 2961df241c2c..47046ce83a40 100644 --- a/beacon-chain/operations/slashings/mock.go +++ b/beacon-chain/operations/slashings/mock.go @@ -30,17 +30,17 @@ func (m *PoolMock) InsertAttesterSlashing(_ context.Context, _ state.ReadOnlyBea } // InsertProposerSlashing -- -func (m *PoolMock) InsertProposerSlashing(_ context.Context, _ state.BeaconState, slashing *ethpb.ProposerSlashing) error { +func (m *PoolMock) InsertProposerSlashing(_ context.Context, _ state.ReadOnlyBeaconState, slashing *ethpb.ProposerSlashing) error { m.PendingPropSlashings = append(m.PendingPropSlashings, slashing) return nil } // MarkIncludedAttesterSlashing -- -func (m *PoolMock) MarkIncludedAttesterSlashing(_ *ethpb.AttesterSlashing) { +func (_ *PoolMock) MarkIncludedAttesterSlashing(_ *ethpb.AttesterSlashing) { panic("implement me") } // MarkIncludedProposerSlashing -- -func (m *PoolMock) MarkIncludedProposerSlashing(_ *ethpb.ProposerSlashing) { +func (_ *PoolMock) MarkIncludedProposerSlashing(_ *ethpb.ProposerSlashing) { panic("implement me") } diff --git a/beacon-chain/operations/slashings/service.go b/beacon-chain/operations/slashings/service.go index 94e9a6531f95..9860ce2cca9a 100644 --- a/beacon-chain/operations/slashings/service.go +++ b/beacon-chain/operations/slashings/service.go @@ -175,7 +175,7 @@ func (p *Pool) InsertAttesterSlashing( // has been included recently, the validator is already exited, or the validator was already slashed. func (p *Pool) InsertProposerSlashing( ctx context.Context, - state state.BeaconState, + state state.ReadOnlyBeaconState, slashing *ethpb.ProposerSlashing, ) error { p.lock.Lock() diff --git a/beacon-chain/operations/slashings/service_test.go b/beacon-chain/operations/slashings/service_test.go index ae0b8852a9e1..888eacc931e7 100644 --- a/beacon-chain/operations/slashings/service_test.go +++ b/beacon-chain/operations/slashings/service_test.go @@ -6,6 +6,13 @@ import ( "github.com/prysmaticlabs/prysm/testing/require" ) +var ( + _ = PoolManager(&Pool{}) + _ = PoolInserter(&Pool{}) + _ = PoolManager(&PoolMock{}) + _ = PoolInserter(&PoolMock{}) +) + func TestPool_validatorSlashingPreconditionCheck_requiresLock(t *testing.T) { p := &Pool{} _, err := p.validatorSlashingPreconditionCheck(nil, 0) diff --git a/beacon-chain/operations/slashings/types.go b/beacon-chain/operations/slashings/types.go index 6e3aabe5a682..23a054952686 100644 --- a/beacon-chain/operations/slashings/types.go +++ b/beacon-chain/operations/slashings/types.go @@ -9,11 +9,8 @@ import ( ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" ) -// PoolManager maintains a pool of pending and recently included attester and proposer slashings. -// This pool is used by proposers to insert data into new blocks. -type PoolManager interface { - PendingAttesterSlashings(ctx context.Context, state state.ReadOnlyBeaconState, noLimit bool) []*ethpb.AttesterSlashing - PendingProposerSlashings(ctx context.Context, state state.ReadOnlyBeaconState, noLimit bool) []*ethpb.ProposerSlashing +// PoolInserter is capable of inserting new slashing objects into the operations pool. +type PoolInserter interface { InsertAttesterSlashing( ctx context.Context, state state.ReadOnlyBeaconState, @@ -21,9 +18,17 @@ type PoolManager interface { ) error InsertProposerSlashing( ctx context.Context, - state state.BeaconState, + state state.ReadOnlyBeaconState, slashing *ethpb.ProposerSlashing, ) error +} + +// PoolManager maintains a pool of pending and recently included attester and proposer slashings. +// This pool is used by proposers to insert data into new blocks. +type PoolManager interface { + PoolInserter + PendingAttesterSlashings(ctx context.Context, state state.ReadOnlyBeaconState, noLimit bool) []*ethpb.AttesterSlashing + PendingProposerSlashings(ctx context.Context, state state.ReadOnlyBeaconState, noLimit bool) []*ethpb.ProposerSlashing MarkIncludedAttesterSlashing(as *ethpb.AttesterSlashing) MarkIncludedProposerSlashing(ps *ethpb.ProposerSlashing) } diff --git a/beacon-chain/slasher/BUILD.bazel b/beacon-chain/slasher/BUILD.bazel index 8a795aac0f79..9f0c680d43a6 100644 --- a/beacon-chain/slasher/BUILD.bazel +++ b/beacon-chain/slasher/BUILD.bazel @@ -8,6 +8,7 @@ go_library( "detect_blocks.go", "doc.go", "helpers.go", + "log.go", "metrics.go", "params.go", "process_slashings.go", @@ -16,18 +17,23 @@ go_library( "service.go", ], importpath = "github.com/prysmaticlabs/prysm/beacon-chain/slasher", - visibility = ["//beacon-chain:__subpackages__"], + visibility = [ + "//beacon-chain:__subpackages__", + "//testing/slasher/simulator:__subpackages__", + ], deps = [ "//async/event:go_default_library", "//beacon-chain/blockchain:go_default_library", "//beacon-chain/core:go_default_library", "//beacon-chain/core/blocks:go_default_library", + "//beacon-chain/core/feed:go_default_library", "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/db:go_default_library", "//beacon-chain/operations/slashings:go_default_library", "//beacon-chain/slasher/types:go_default_library", "//beacon-chain/state:go_default_library", "//beacon-chain/state/stategen:go_default_library", + "//beacon-chain/sync:go_default_library", "//config/params:go_default_library", "//container/slice:go_default_library", "//encoding/bytesutil:go_default_library", diff --git a/beacon-chain/slasher/log.go b/beacon-chain/slasher/log.go new file mode 100644 index 000000000000..d45613e76e50 --- /dev/null +++ b/beacon-chain/slasher/log.go @@ -0,0 +1,5 @@ +package slasher + +import "github.com/sirupsen/logrus" + +var log = logrus.WithField("prefix", "slasher") diff --git a/beacon-chain/slasher/service.go b/beacon-chain/slasher/service.go index 8b7b2953b287..66f252c81dc1 100644 --- a/beacon-chain/slasher/service.go +++ b/beacon-chain/slasher/service.go @@ -1,36 +1,157 @@ +// Package slasher implements slashing detection for eth2, able to catch slashable attestations +// and proposals that it receives via two event feeds, respectively. Any found slashings +// are then submitted to the beacon node's slashing operations pool. See the design document +// here https://hackmd.io/@prysmaticlabs/slasher. package slasher import ( + "context" "time" "github.com/prysmaticlabs/prysm/async/event" "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" + "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings" "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" - "github.com/sirupsen/logrus" + "github.com/prysmaticlabs/prysm/beacon-chain/sync" + "github.com/prysmaticlabs/prysm/config/params" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/time/slots" ) -var log = logrus.WithField("prefix", "slasher") - -// ServiceConfig contains service dependencies for slasher. +// ServiceConfig for the slasher service in the beacon node. +// This struct allows us to specify required dependencies and +// parameters for slasher to function as needed. type ServiceConfig struct { - Database db.SlasherDatabase - AttestationStateFetcher blockchain.AttestationStateFetcher IndexedAttestationsFeed *event.Feed BeaconBlockHeadersFeed *event.Feed - StateGen stategen.StateManager - SlashingPoolInserter slashings.PoolManager + Database db.SlasherDatabase StateNotifier statefeed.Notifier + AttestationStateFetcher blockchain.AttestationStateFetcher + StateGen stategen.StateManager + SlashingPoolInserter slashings.PoolInserter HeadStateFetcher blockchain.HeadFetcher + SyncChecker sync.Checker } -// Service for running slasher mode in a beacon node. +// SlashingChecker is an interface for defining services that the beacon node may interact with to provide slashing data. +type SlashingChecker interface { + IsSlashableBlock(ctx context.Context, proposal *ethpb.SignedBeaconBlockHeader) (*ethpb.ProposerSlashing, error) + IsSlashableAttestation(ctx context.Context, attestation *ethpb.IndexedAttestation) ([]*ethpb.AttesterSlashing, error) +} + +// Service defining a slasher implementation as part of +// the beacon node, able to detect eth2 slashable offenses. type Service struct { - params *Parameters - serviceCfg *ServiceConfig - blksQueue *blocksQueue - attsQueue *attestationsQueue - genesisTime time.Time + params *Parameters + serviceCfg *ServiceConfig + indexedAttsChan chan *ethpb.IndexedAttestation + beaconBlockHeadersChan chan *ethpb.SignedBeaconBlockHeader + attsQueue *attestationsQueue + blksQueue *blocksQueue + ctx context.Context + cancel context.CancelFunc + slotTicker *slots.SlotTicker + genesisTime time.Time +} + +// New instantiates a new slasher from configuration values. +func New(ctx context.Context, srvCfg *ServiceConfig) (*Service, error) { + ctx, cancel := context.WithCancel(ctx) + return &Service{ + params: DefaultParams(), + serviceCfg: srvCfg, + indexedAttsChan: make(chan *ethpb.IndexedAttestation, 1), + beaconBlockHeadersChan: make(chan *ethpb.SignedBeaconBlockHeader, 1), + attsQueue: newAttestationsQueue(), + blksQueue: newBlocksQueue(), + ctx: ctx, + cancel: cancel, + }, nil +} + +// Start listening for received indexed attestations and blocks +// and perform slashing detection on them. +func (s *Service) Start() { + go s.run() +} + +func (s *Service) run() { + stateChannel := make(chan *feed.Event, 1) + stateSub := s.serviceCfg.StateNotifier.StateFeed().Subscribe(stateChannel) + stateEvent := <-stateChannel + + // Wait for us to receive the genesis time via a chain started notification. + if stateEvent.Type == statefeed.ChainStarted { + data, ok := stateEvent.Data.(*statefeed.ChainStartedData) + if !ok { + log.Error("Could not receive chain start notification, want *statefeed.ChainStartedData") + return + } + s.genesisTime = data.StartTime + log.WithField("genesisTime", s.genesisTime).Info("Starting slasher, received chain start event") + } else if stateEvent.Type == statefeed.Initialized { + // Alternatively, if the chain has already started, we then read the genesis + // time value from this data. + data, ok := stateEvent.Data.(*statefeed.InitializedData) + if !ok { + log.Error("Could not receive chain start notification, want *statefeed.ChainStartedData") + return + } + s.genesisTime = data.StartTime + log.WithField("genesisTime", s.genesisTime).Info("Starting slasher, chain already initialized") + } else { + // This should not happen. + log.Error("Could start slasher, could not receive chain start event") + return + } + + stateSub.Unsubscribe() + secondsPerSlot := params.BeaconConfig().SecondsPerSlot + s.slotTicker = slots.NewSlotTicker(s.genesisTime, secondsPerSlot) + + s.waitForSync(s.genesisTime) + + indexedAttsChan := make(chan *ethpb.IndexedAttestation, 1) + beaconBlockHeadersChan := make(chan *ethpb.SignedBeaconBlockHeader, 1) + log.Info("Completed chain sync, starting slashing detection") + go s.processQueuedAttestations(s.ctx, s.slotTicker.C()) + go s.processQueuedBlocks(s.ctx, s.slotTicker.C()) + go s.receiveAttestations(s.ctx, indexedAttsChan) + go s.receiveBlocks(s.ctx, beaconBlockHeadersChan) + go s.pruneSlasherData(s.ctx, s.slotTicker.C()) +} + +// Stop the slasher service. +func (s *Service) Stop() error { + s.cancel() + if s.slotTicker != nil { + s.slotTicker.Done() + } + return nil +} + +// Status of the slasher service. +func (s *Service) Status() error { + return nil +} + +func (s *Service) waitForSync(genesisTime time.Time) { + if slots.SlotsSinceGenesis(genesisTime) == 0 || !s.serviceCfg.SyncChecker.Syncing() { + return + } + for { + select { + case <-s.slotTicker.C(): + // If node is still syncing, do not operate slasher. + if s.serviceCfg.SyncChecker.Syncing() { + continue + } + return + case <-s.ctx.Done(): + return + } + } } diff --git a/beacon-chain/state/BUILD.bazel b/beacon-chain/state/BUILD.bazel index a49754c62426..eb287fa39de8 100644 --- a/beacon-chain/state/BUILD.bazel +++ b/beacon-chain/state/BUILD.bazel @@ -15,6 +15,7 @@ go_library( "//slasher/rpc:__subpackages__", "//testing/benchmark:__pkg__", "//testing/fuzz:__pkg__", + "//testing/slasher/simulator:__pkg__", "//testing/spectest:__subpackages__", "//testing/util:__pkg__", "//tools/benchmark-files-gen:__pkg__", diff --git a/beacon-chain/state/stategen/BUILD.bazel b/beacon-chain/state/stategen/BUILD.bazel index 13e45b08fccf..b7b90435daf0 100644 --- a/beacon-chain/state/stategen/BUILD.bazel +++ b/beacon-chain/state/stategen/BUILD.bazel @@ -19,6 +19,7 @@ go_library( visibility = [ "//beacon-chain:__subpackages__", "//testing/fuzz:__pkg__", + "//testing/slasher/simulator:__pkg__", ], deps = [ "//beacon-chain/core:go_default_library", diff --git a/testing/slasher/simulator/BUILD.bazel b/testing/slasher/simulator/BUILD.bazel new file mode 100644 index 000000000000..6be2b0898d4b --- /dev/null +++ b/testing/slasher/simulator/BUILD.bazel @@ -0,0 +1,59 @@ +load("@prysm//tools/go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + testonly = True, + srcs = [ + "attestation_generator.go", + "block_generator.go", + "simulator.go", + ], + importpath = "github.com/prysmaticlabs/prysm/testing/slasher/simulator", + visibility = [ + "//endtoend:__subpackages__", + ], + deps = [ + "//async/event:go_default_library", + "//beacon-chain/blockchain:go_default_library", + "//beacon-chain/core:go_default_library", + "//beacon-chain/core/feed:go_default_library", + "//beacon-chain/core/feed/state:go_default_library", + "//beacon-chain/core/helpers:go_default_library", + "//beacon-chain/core/signing:go_default_library", + "//beacon-chain/db:go_default_library", + "//beacon-chain/operations/slashings:go_default_library", + "//beacon-chain/slasher:go_default_library", + "//beacon-chain/state:go_default_library", + "//beacon-chain/state/stategen:go_default_library", + "//config/params:go_default_library", + "//crypto/bls:go_default_library", + "//crypto/rand:go_default_library", + "//encoding/bytesutil:go_default_library", + "//proto/prysm/v1alpha1:go_default_library", + "//time/slots:go_default_library", + "@com_github_prysmaticlabs_eth2_types//:go_default_library", + "@com_github_sirupsen_logrus//:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = [ + "attestation_generator_test.go", + "block_generator_test.go", + "simulator_test.go", + ], + embed = [":go_default_library"], + deps = [ + "//beacon-chain/blockchain/testing:go_default_library", + "//beacon-chain/db/testing:go_default_library", + "//beacon-chain/state/stategen:go_default_library", + "//config/params:go_default_library", + "//crypto/bls:go_default_library", + "//proto/prysm/v1alpha1:go_default_library", + "//proto/prysm/v1alpha1/slashings:go_default_library", + "//testing/require:go_default_library", + "//testing/util:go_default_library", + "@com_github_prysmaticlabs_eth2_types//:go_default_library", + ], +) diff --git a/testing/slasher/simulator/attestation_generator.go b/testing/slasher/simulator/attestation_generator.go new file mode 100644 index 000000000000..59082391a954 --- /dev/null +++ b/testing/slasher/simulator/attestation_generator.go @@ -0,0 +1,177 @@ +package simulator + +import ( + "context" + "math" + + types "github.com/prysmaticlabs/eth2-types" + "github.com/prysmaticlabs/prysm/beacon-chain/core" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/beacon-chain/core/signing" + "github.com/prysmaticlabs/prysm/beacon-chain/state" + "github.com/prysmaticlabs/prysm/config/params" + "github.com/prysmaticlabs/prysm/crypto/bls" + "github.com/prysmaticlabs/prysm/crypto/rand" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/sirupsen/logrus" +) + +func (s *Simulator) generateAttestationsForSlot( + ctx context.Context, slot types.Slot, +) ([]*ethpb.IndexedAttestation, []*ethpb.AttesterSlashing, error) { + attestations := make([]*ethpb.IndexedAttestation, 0) + slashings := make([]*ethpb.AttesterSlashing, 0) + currentEpoch := core.SlotToEpoch(slot) + + committeesPerSlot := helpers.SlotCommitteeCount(s.srvConfig.Params.NumValidators) + valsPerCommittee := s.srvConfig.Params.NumValidators / + (committeesPerSlot * uint64(s.srvConfig.Params.SlotsPerEpoch)) + valsPerSlot := committeesPerSlot * valsPerCommittee + + var sourceEpoch types.Epoch = 0 + if currentEpoch != 0 { + sourceEpoch = currentEpoch - 1 + } + + var slashedIndices []uint64 + startIdx := valsPerSlot * uint64(slot%s.srvConfig.Params.SlotsPerEpoch) + endIdx := startIdx + valsPerCommittee + for c := types.CommitteeIndex(0); uint64(c) < committeesPerSlot; c++ { + attData := ðpb.AttestationData{ + Slot: slot, + CommitteeIndex: c, + BeaconBlockRoot: bytesutil.PadTo([]byte("block"), 32), + Source: ðpb.Checkpoint{ + Epoch: sourceEpoch, + Root: bytesutil.PadTo([]byte("source"), 32), + }, + Target: ðpb.Checkpoint{ + Epoch: currentEpoch, + Root: bytesutil.PadTo([]byte("target"), 32), + }, + } + + valsPerAttestation := uint64(math.Floor(s.srvConfig.Params.AggregationPercent * float64(valsPerCommittee))) + for i := startIdx; i < endIdx; i += valsPerAttestation { + attEndIdx := i + valsPerAttestation + if attEndIdx >= endIdx { + attEndIdx = endIdx + } + indices := make([]uint64, 0, valsPerAttestation) + for idx := i; idx < attEndIdx; idx++ { + indices = append(indices, idx) + } + att := ðpb.IndexedAttestation{ + AttestingIndices: indices, + Data: attData, + Signature: params.BeaconConfig().EmptySignature[:], + } + beaconState, err := s.srvConfig.AttestationStateFetcher.AttestationTargetState(ctx, att.Data.Target) + if err != nil { + return nil, nil, err + } + + // Sign the attestation with a valid signature. + aggSig, err := s.aggregateSigForAttestation(beaconState, att) + if err != nil { + return nil, nil, err + } + att.Signature = aggSig.Marshal() + + attestations = append(attestations, att) + if rand.NewGenerator().Float64() < s.srvConfig.Params.AttesterSlashingProbab { + slashableAtt := makeSlashableFromAtt(att, []uint64{indices[0]}) + aggSig, err := s.aggregateSigForAttestation(beaconState, slashableAtt) + if err != nil { + return nil, nil, err + } + slashableAtt.Signature = aggSig.Marshal() + slashedIndices = append(slashedIndices, slashableAtt.AttestingIndices...) + slashings = append(slashings, ðpb.AttesterSlashing{ + Attestation_1: att, + Attestation_2: slashableAtt, + }) + attestations = append(attestations, slashableAtt) + } + } + startIdx += valsPerCommittee + endIdx += valsPerCommittee + } + if len(slashedIndices) > 0 { + log.WithFields(logrus.Fields{ + "amount": len(slashedIndices), + "indices": slashedIndices, + }).Infof("Slashable attestation made") + } + return attestations, slashings, nil +} + +func (s *Simulator) aggregateSigForAttestation( + beaconState state.BeaconState, att *ethpb.IndexedAttestation, +) (bls.Signature, error) { + domain, err := signing.Domain( + beaconState.Fork(), + att.Data.Target.Epoch, + params.BeaconConfig().DomainBeaconAttester, + beaconState.GenesisValidatorRoot(), + ) + if err != nil { + return nil, err + } + signingRoot, err := signing.ComputeSigningRoot(att.Data, domain) + if err != nil { + return nil, err + } + sigs := make([]bls.Signature, len(att.AttestingIndices)) + for i, validatorIndex := range att.AttestingIndices { + privKey := s.srvConfig.PrivateKeysByValidatorIndex[types.ValidatorIndex(validatorIndex)] + sigs[i] = privKey.Sign(signingRoot[:]) + } + return bls.AggregateSignatures(sigs), nil +} + +func makeSlashableFromAtt(att *ethpb.IndexedAttestation, indices []uint64) *ethpb.IndexedAttestation { + if att.Data.Source.Epoch <= 2 { + return makeDoubleVoteFromAtt(att, indices) + } + attData := ðpb.AttestationData{ + Slot: att.Data.Slot, + CommitteeIndex: att.Data.CommitteeIndex, + BeaconBlockRoot: att.Data.BeaconBlockRoot, + Source: ðpb.Checkpoint{ + Epoch: att.Data.Source.Epoch - 3, + Root: att.Data.Source.Root, + }, + Target: ðpb.Checkpoint{ + Epoch: att.Data.Target.Epoch, + Root: att.Data.Target.Root, + }, + } + return ðpb.IndexedAttestation{ + AttestingIndices: indices, + Data: attData, + Signature: params.BeaconConfig().EmptySignature[:], + } +} + +func makeDoubleVoteFromAtt(att *ethpb.IndexedAttestation, indices []uint64) *ethpb.IndexedAttestation { + attData := ðpb.AttestationData{ + Slot: att.Data.Slot, + CommitteeIndex: att.Data.CommitteeIndex, + BeaconBlockRoot: bytesutil.PadTo([]byte("slash me"), 32), + Source: ðpb.Checkpoint{ + Epoch: att.Data.Source.Epoch, + Root: att.Data.Source.Root, + }, + Target: ðpb.Checkpoint{ + Epoch: att.Data.Target.Epoch, + Root: att.Data.Target.Root, + }, + } + return ðpb.IndexedAttestation{ + AttestingIndices: indices, + Data: attData, + Signature: params.BeaconConfig().EmptySignature[:], + } +} diff --git a/testing/slasher/simulator/attestation_generator_test.go b/testing/slasher/simulator/attestation_generator_test.go new file mode 100644 index 000000000000..858af33575b5 --- /dev/null +++ b/testing/slasher/simulator/attestation_generator_test.go @@ -0,0 +1,64 @@ +package simulator + +import ( + "context" + "testing" + + "github.com/prysmaticlabs/prysm/config/params" + "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/slashings" + "github.com/prysmaticlabs/prysm/testing/require" +) + +func TestGenerateAttestationsForSlot_Slashing(t *testing.T) { + ctx := context.Background() + simParams := &Parameters{ + SecondsPerSlot: params.BeaconConfig().SecondsPerSlot, + SlotsPerEpoch: params.BeaconConfig().SlotsPerEpoch, + AggregationPercent: 1, + NumValidators: 64, + AttesterSlashingProbab: 1, + } + srv := setupService(t, simParams) + + epoch3Atts, _, err := srv.generateAttestationsForSlot(ctx, params.BeaconConfig().SlotsPerEpoch*3) + require.NoError(t, err) + epoch4Atts, _, err := srv.generateAttestationsForSlot(ctx, params.BeaconConfig().SlotsPerEpoch*4) + require.NoError(t, err) + for i := 0; i < len(epoch3Atts); i += 2 { + goodAtt := epoch3Atts[i] + surroundAtt := epoch4Atts[i+1] + require.Equal(t, true, slashings.IsSurround(surroundAtt, goodAtt)) + } +} + +func TestGenerateAttestationsForSlot_CorrectIndices(t *testing.T) { + ctx := context.Background() + simParams := &Parameters{ + SecondsPerSlot: params.BeaconConfig().SecondsPerSlot, + SlotsPerEpoch: params.BeaconConfig().SlotsPerEpoch, + AggregationPercent: 1, + NumValidators: 16384, + AttesterSlashingProbab: 0, + } + srv := setupService(t, simParams) + slot0Atts, _, err := srv.generateAttestationsForSlot(ctx, 0) + require.NoError(t, err) + slot1Atts, _, err := srv.generateAttestationsForSlot(ctx, 1) + require.NoError(t, err) + slot2Atts, _, err := srv.generateAttestationsForSlot(ctx, 2) + require.NoError(t, err) + var validatorIndices []uint64 + for _, att := range append(slot0Atts, slot1Atts...) { + validatorIndices = append(validatorIndices, att.AttestingIndices...) + } + for _, att := range slot2Atts { + validatorIndices = append(validatorIndices, att.AttestingIndices...) + } + + // Making sure indices are one after the other for attestations. + var validatorIndex uint64 + for _, ii := range validatorIndices { + require.Equal(t, validatorIndex, ii) + validatorIndex++ + } +} diff --git a/testing/slasher/simulator/block_generator.go b/testing/slasher/simulator/block_generator.go new file mode 100644 index 000000000000..0db6283a818f --- /dev/null +++ b/testing/slasher/simulator/block_generator.go @@ -0,0 +1,98 @@ +package simulator + +import ( + "context" + + types "github.com/prysmaticlabs/eth2-types" + "github.com/prysmaticlabs/prysm/beacon-chain/core/signing" + "github.com/prysmaticlabs/prysm/beacon-chain/state" + "github.com/prysmaticlabs/prysm/config/params" + "github.com/prysmaticlabs/prysm/crypto/bls" + "github.com/prysmaticlabs/prysm/crypto/rand" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" +) + +func (s *Simulator) generateBlockHeadersForSlot( + ctx context.Context, slot types.Slot, +) ([]*ethpb.SignedBeaconBlockHeader, []*ethpb.ProposerSlashing, error) { + blocks := make([]*ethpb.SignedBeaconBlockHeader, 0) + slashings := make([]*ethpb.ProposerSlashing, 0) + proposer := rand.NewGenerator().Uint64() % s.srvConfig.Params.NumValidators + + parentRoot := [32]byte{} + beaconState, err := s.srvConfig.StateGen.StateByRoot(ctx, parentRoot) + if err != nil { + return nil, nil, err + } + block := ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: slot, + ProposerIndex: types.ValidatorIndex(proposer), + ParentRoot: bytesutil.PadTo([]byte{}, 32), + StateRoot: bytesutil.PadTo([]byte{}, 32), + BodyRoot: bytesutil.PadTo([]byte("good block"), 32), + }, + } + sig, err := s.signBlockHeader(beaconState, block) + if err != nil { + return nil, nil, err + } + block.Signature = sig.Marshal() + + blocks = append(blocks, block) + if rand.NewGenerator().Float64() < s.srvConfig.Params.ProposerSlashingProbab { + log.WithField("proposerIndex", proposer).Infof("Slashable block made") + slashableBlock := ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: slot, + ProposerIndex: types.ValidatorIndex(proposer), + ParentRoot: bytesutil.PadTo([]byte{}, 32), + StateRoot: bytesutil.PadTo([]byte{}, 32), + BodyRoot: bytesutil.PadTo([]byte("bad block"), 32), + }, + Signature: sig.Marshal(), + } + sig, err = s.signBlockHeader(beaconState, slashableBlock) + if err != nil { + return nil, nil, err + } + slashableBlock.Signature = sig.Marshal() + + blocks = append(blocks, slashableBlock) + slashings = append(slashings, ðpb.ProposerSlashing{ + Header_1: block, + Header_2: slashableBlock, + }) + } + return blocks, slashings, nil +} + +func (s *Simulator) signBlockHeader( + beaconState state.BeaconState, + header *ethpb.SignedBeaconBlockHeader, +) (bls.Signature, error) { + domain, err := signing.Domain( + beaconState.Fork(), + 0, + params.BeaconConfig().DomainBeaconProposer, + beaconState.GenesisValidatorRoot(), + ) + if err != nil { + return nil, err + } + htr, err := header.Header.HashTreeRoot() + if err != nil { + return nil, err + } + container := ðpb.SigningData{ + ObjectRoot: htr[:], + Domain: domain, + } + signingRoot, err := container.HashTreeRoot() + if err != nil { + return nil, err + } + validatorPrivKey := s.srvConfig.PrivateKeysByValidatorIndex[header.Header.ProposerIndex] + return validatorPrivKey.Sign(signingRoot[:]), nil +} diff --git a/testing/slasher/simulator/block_generator_test.go b/testing/slasher/simulator/block_generator_test.go new file mode 100644 index 000000000000..03be6549a705 --- /dev/null +++ b/testing/slasher/simulator/block_generator_test.go @@ -0,0 +1,31 @@ +package simulator + +import ( + "bytes" + "context" + "testing" + + "github.com/prysmaticlabs/prysm/testing/require" +) + +func TestGenerateBlockHeadersForSlot_Slashing(t *testing.T) { + ctx := context.Background() + simParams := &Parameters{ + AggregationPercent: 1, + NumValidators: 64, + ProposerSlashingProbab: 1, + } + srv := setupService(t, simParams) + + slot1Blocks, _, err := srv.generateBlockHeadersForSlot(ctx, 1) + require.NoError(t, err) + require.Equal(t, 2, len(slot1Blocks)) + + block1Root, err := slot1Blocks[0].HashTreeRoot() + require.NoError(t, err) + block2Root, err := slot1Blocks[1].HashTreeRoot() + require.NoError(t, err) + if slot1Blocks[0].Header.ProposerIndex == slot1Blocks[1].Header.ProposerIndex && bytes.Equal(block1Root[:], block2Root[:]) { + t.Error("Blocks received were not slashable") + } +} diff --git a/testing/slasher/simulator/simulator.go b/testing/slasher/simulator/simulator.go new file mode 100644 index 000000000000..a03a4faeeb3d --- /dev/null +++ b/testing/slasher/simulator/simulator.go @@ -0,0 +1,274 @@ +package simulator + +import ( + "context" + "time" + + types "github.com/prysmaticlabs/eth2-types" + "github.com/prysmaticlabs/prysm/async/event" + "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" + "github.com/prysmaticlabs/prysm/beacon-chain/core" + "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" + statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" + "github.com/prysmaticlabs/prysm/beacon-chain/db" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings" + "github.com/prysmaticlabs/prysm/beacon-chain/slasher" + "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" + "github.com/prysmaticlabs/prysm/config/params" + "github.com/prysmaticlabs/prysm/crypto/bls" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/time/slots" + "github.com/sirupsen/logrus" +) + +var log = logrus.WithField("prefix", "simulator") + +// ServiceConfig for the simulator. +type ServiceConfig struct { + Params *Parameters + Database db.SlasherDatabase + StateNotifier statefeed.Notifier + AttestationStateFetcher blockchain.AttestationStateFetcher + HeadStateFetcher blockchain.HeadFetcher + StateGen stategen.StateManager + SlashingsPool slashings.PoolManager + PrivateKeysByValidatorIndex map[types.ValidatorIndex]bls.SecretKey +} + +// Parameters for a slasher simulator. +type Parameters struct { + SecondsPerSlot uint64 + SlotsPerEpoch types.Slot + AggregationPercent float64 + ProposerSlashingProbab float64 + AttesterSlashingProbab float64 + NumValidators uint64 + NumEpochs uint64 +} + +// Simulator defines a struct which can launch a slasher simulation +// at scale using configuration parameters. +type Simulator struct { + ctx context.Context + slasher *slasher.Service + srvConfig *ServiceConfig + indexedAttsFeed *event.Feed + beaconBlocksFeed *event.Feed + sentAttSlashingFeed *event.Feed + sentBlockSlashingFeed *event.Feed + sentProposerSlashings map[[32]byte]*ethpb.ProposerSlashing + sentAttesterSlashings map[[32]byte]*ethpb.AttesterSlashing + genesisTime time.Time +} + +// DefaultParams for launching a slasher simulator. +func DefaultParams() *Parameters { + return &Parameters{ + SecondsPerSlot: params.BeaconConfig().SecondsPerSlot, + SlotsPerEpoch: 4, + AggregationPercent: 1.0, + ProposerSlashingProbab: 0.3, + AttesterSlashingProbab: 0.3, + NumValidators: params.BeaconConfig().MinGenesisActiveValidatorCount, + NumEpochs: 4, + } +} + +// New initializes a slasher simulator from a beacon database +// and configuration parameters. +func New(ctx context.Context, srvConfig *ServiceConfig) (*Simulator, error) { + indexedAttsFeed := new(event.Feed) + beaconBlocksFeed := new(event.Feed) + sentBlockSlashingFeed := new(event.Feed) + sentAttSlashingFeed := new(event.Feed) + + slasherSrv, err := slasher.New(ctx, &slasher.ServiceConfig{ + IndexedAttestationsFeed: indexedAttsFeed, + BeaconBlockHeadersFeed: beaconBlocksFeed, + Database: srvConfig.Database, + StateNotifier: srvConfig.StateNotifier, + HeadStateFetcher: srvConfig.HeadStateFetcher, + AttestationStateFetcher: srvConfig.AttestationStateFetcher, + StateGen: srvConfig.StateGen, + SlashingPoolInserter: srvConfig.SlashingsPool, + }) + if err != nil { + return nil, err + } + return &Simulator{ + ctx: ctx, + slasher: slasherSrv, + srvConfig: srvConfig, + indexedAttsFeed: indexedAttsFeed, + beaconBlocksFeed: beaconBlocksFeed, + sentAttSlashingFeed: sentAttSlashingFeed, + sentBlockSlashingFeed: sentBlockSlashingFeed, + sentProposerSlashings: make(map[[32]byte]*ethpb.ProposerSlashing), + sentAttesterSlashings: make(map[[32]byte]*ethpb.AttesterSlashing), + }, nil +} + +// Start a simulator. +func (s *Simulator) Start() { + log.WithFields(logrus.Fields{ + "numValidators": s.srvConfig.Params.NumValidators, + "numEpochs": s.srvConfig.Params.NumEpochs, + "secondsPerSlot": s.srvConfig.Params.SecondsPerSlot, + "proposerSlashingProbab": s.srvConfig.Params.ProposerSlashingProbab, + "attesterSlashingProbab": s.srvConfig.Params.AttesterSlashingProbab, + }).Info("Starting slasher simulator") + + // Override global configuration for simulation purposes. + config := params.BeaconConfig().Copy() + config.SecondsPerSlot = s.srvConfig.Params.SecondsPerSlot + config.SlotsPerEpoch = s.srvConfig.Params.SlotsPerEpoch + params.OverrideBeaconConfig(config) + defer params.OverrideBeaconConfig(params.BeaconConfig()) + + // Start slasher in the background. + go s.slasher.Start() + + // Wait some time and then send a "chain started" event over a notifier + // for slasher to pick up a genesis time. + time.Sleep(time.Second) + s.genesisTime = time.Now() + s.srvConfig.StateNotifier.StateFeed().Send(&feed.Event{ + Type: statefeed.ChainStarted, + Data: &statefeed.ChainStartedData{StartTime: s.genesisTime}, + }) + + // We simulate blocks and attestations for N epochs. + s.simulateBlocksAndAttestations(s.ctx) + + // Verify the slashings we detected are the same as those the + // simulator produced, effectively checking slasher caught all slashable offenses. + s.verifySlashingsWereDetected(s.ctx) +} + +// Stop the simulator. +func (s *Simulator) Stop() error { + return s.slasher.Stop() +} + +func (s *Simulator) simulateBlocksAndAttestations(ctx context.Context) { + // Add a small offset to producing blocks and attestations a little bit after a slot starts. + ticker := slots.NewSlotTicker(s.genesisTime.Add(time.Millisecond*500), params.BeaconConfig().SecondsPerSlot) + defer ticker.Done() + for { + select { + case slot := <-ticker.C(): + // We only run the simulator for a specified number of epochs. + totalEpochs := types.Epoch(s.srvConfig.Params.NumEpochs) + if core.SlotToEpoch(slot) >= totalEpochs { + return + } + + // Since processing slashings requires at least one slot, we do nothing + // if we are a few slots from the end of the simulation. + endSlot, err := core.StartSlot(totalEpochs) + if err != nil { + log.WithError(err).Fatal("Could not get epoch start slot") + } + if slot+3 > endSlot { + continue + } + + blockHeaders, propSlashings, err := s.generateBlockHeadersForSlot(ctx, slot) + if err != nil { + log.WithError(err).Fatal("Could not generate block headers for slot") + } + log.WithFields(logrus.Fields{ + "numBlocks": len(blockHeaders), + "numSlashable": len(propSlashings), + }).Infof("Producing blocks for slot %d", slot) + for _, sl := range propSlashings { + slashingRoot, err := sl.HashTreeRoot() + if err != nil { + log.WithError(err).Fatal("Could not hash tree root slashing") + } + s.sentProposerSlashings[slashingRoot] = sl + } + for _, bb := range blockHeaders { + s.beaconBlocksFeed.Send(bb) + } + + atts, attSlashings, err := s.generateAttestationsForSlot(ctx, slot) + if err != nil { + log.WithError(err).Fatal("Could not generate block headers for slot") + } + log.WithFields(logrus.Fields{ + "numAtts": len(atts), + "numSlashable": len(propSlashings), + }).Infof("Producing attestations for slot %d", slot) + for _, sl := range attSlashings { + slashingRoot, err := sl.HashTreeRoot() + if err != nil { + log.WithError(err).Fatal("Could not hash tree root slashing") + } + s.sentAttesterSlashings[slashingRoot] = sl + } + for _, aa := range atts { + s.indexedAttsFeed.Send(aa) + } + case <-ctx.Done(): + return + } + } +} + +func (s *Simulator) verifySlashingsWereDetected(ctx context.Context) { + poolProposerSlashings := s.srvConfig.SlashingsPool.PendingProposerSlashings( + ctx, nil, true, /* no limit */ + ) + poolAttesterSlashings := s.srvConfig.SlashingsPool.PendingAttesterSlashings( + ctx, nil, true, /* no limit */ + ) + detectedProposerSlashings := make(map[[32]byte]*ethpb.ProposerSlashing) + detectedAttesterSlashings := make(map[[32]byte]*ethpb.AttesterSlashing) + for _, slashing := range poolProposerSlashings { + slashingRoot, err := slashing.HashTreeRoot() + if err != nil { + log.WithError(err).Error("Could not determine slashing root") + } + detectedProposerSlashings[slashingRoot] = slashing + } + for _, slashing := range poolAttesterSlashings { + slashingRoot, err := slashing.HashTreeRoot() + if err != nil { + log.WithError(err).Error("Could not determine slashing root") + } + detectedAttesterSlashings[slashingRoot] = slashing + } + + // Check if the sent slashings made it into the slashings pool. + for slashingRoot, slashing := range s.sentProposerSlashings { + if _, ok := detectedProposerSlashings[slashingRoot]; !ok { + log.WithFields(logrus.Fields{ + "slot": slashing.Header_1.Header.Slot, + "proposerIndex": slashing.Header_1.Header.ProposerIndex, + }).Errorf("Did not detect simulated proposer slashing") + continue + } + log.WithFields(logrus.Fields{ + "slot": slashing.Header_1.Header.Slot, + "proposerIndex": slashing.Header_1.Header.ProposerIndex, + }).Info("Correctly detected simulated proposer slashing") + } + for slashingRoot, slashing := range s.sentAttesterSlashings { + if _, ok := detectedAttesterSlashings[slashingRoot]; !ok { + log.WithFields(logrus.Fields{ + "targetEpoch": slashing.Attestation_1.Data.Target.Epoch, + "prevTargetEpoch": slashing.Attestation_2.Data.Target.Epoch, + "sourceEpoch": slashing.Attestation_1.Data.Source.Epoch, + "prevSourceEpoch": slashing.Attestation_2.Data.Source.Epoch, + }).Errorf("Did not detect simulated attester slashing") + continue + } + log.WithFields(logrus.Fields{ + "targetEpoch": slashing.Attestation_1.Data.Target.Epoch, + "prevTargetEpoch": slashing.Attestation_2.Data.Target.Epoch, + "sourceEpoch": slashing.Attestation_1.Data.Source.Epoch, + "prevSourceEpoch": slashing.Attestation_2.Data.Source.Epoch, + }).Info("Correctly detected simulated attester slashing") + } +} diff --git a/testing/slasher/simulator/simulator_test.go b/testing/slasher/simulator/simulator_test.go new file mode 100644 index 000000000000..9aa7c41e3b5e --- /dev/null +++ b/testing/slasher/simulator/simulator_test.go @@ -0,0 +1,48 @@ +package simulator + +import ( + "testing" + + types "github.com/prysmaticlabs/eth2-types" + mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" + "github.com/prysmaticlabs/prysm/crypto/bls" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/testing/require" + "github.com/prysmaticlabs/prysm/testing/util" +) + +func setupService(t *testing.T, params *Parameters) *Simulator { + slasherDB := dbtest.SetupSlasherDB(t) + beaconState, err := util.NewBeaconState() + require.NoError(t, err) + + // We setup validators in the beacon state along with their + // private keys used to generate valid signatures in generated objects. + validators := make([]*ethpb.Validator, params.NumValidators) + privKeys := make(map[types.ValidatorIndex]bls.SecretKey) + for valIdx := range validators { + privKey, err := bls.RandKey() + require.NoError(t, err) + privKeys[types.ValidatorIndex(valIdx)] = privKey + validators[valIdx] = ðpb.Validator{ + PublicKey: privKey.PublicKey().Marshal(), + WithdrawalCredentials: make([]byte, 32), + } + } + err = beaconState.SetValidators(validators) + require.NoError(t, err) + + gen := stategen.NewMockService() + gen.AddStateForRoot(beaconState, [32]byte{}) + return &Simulator{ + srvConfig: &ServiceConfig{ + Params: params, + Database: slasherDB, + AttestationStateFetcher: &mock.ChainService{State: beaconState}, + PrivateKeysByValidatorIndex: privKeys, + StateGen: gen, + }, + } +} From bfc821d03a634c53f3f47aaa4d6a9f318e6ccda0 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 28 Sep 2021 20:00:52 -0700 Subject: [PATCH 13/25] Can save justified checkpoint to DB (#9697) * Can save justified checkpoint to DB * Update beacon-chain/blockchain/process_block_test.go Co-authored-by: Nishant Das * Update beacon-chain/blockchain/process_block.go Co-authored-by: Nishant Das * Update beacon-chain/blockchain/process_block.go Co-authored-by: Nishant Das * Update beacon-chain/blockchain/process_block.go Co-authored-by: Nishant Das Co-authored-by: Nishant Das --- beacon-chain/blockchain/process_block.go | 5 +++-- beacon-chain/blockchain/process_block_test.go | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index 275538113dab..4fe9cbbd9585 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -107,7 +107,8 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b } // Update justified check point. - if postState.CurrentJustifiedCheckpoint().Epoch > s.justifiedCheckpt.Epoch { + currJustifiedEpoch := s.justifiedCheckpt.Epoch + if postState.CurrentJustifiedCheckpoint().Epoch > currJustifiedEpoch { if err := s.updateJustified(ctx, postState); err != nil { return err } @@ -156,7 +157,7 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b } // Save justified check point to db. - if postState.CurrentJustifiedCheckpoint().Epoch > s.justifiedCheckpt.Epoch { + if postState.CurrentJustifiedCheckpoint().Epoch > currJustifiedEpoch { if err := s.cfg.BeaconDB.SaveJustifiedCheckpoint(ctx, postState.CurrentJustifiedCheckpoint()); err != nil { return err } diff --git a/beacon-chain/blockchain/process_block_test.go b/beacon-chain/blockchain/process_block_test.go index 90a4ad0ec9e9..d3058ef5248f 100644 --- a/beacon-chain/blockchain/process_block_test.go +++ b/beacon-chain/blockchain/process_block_test.go @@ -957,6 +957,14 @@ func TestOnBlock_CanFinalize(t *testing.T) { } require.Equal(t, types.Epoch(3), service.CurrentJustifiedCheckpt().Epoch) require.Equal(t, types.Epoch(2), service.FinalizedCheckpt().Epoch) + + // The update should persist in DB. + j, err := service.cfg.BeaconDB.JustifiedCheckpoint(ctx) + require.NoError(t, err) + require.Equal(t, j.Epoch, service.CurrentJustifiedCheckpt().Epoch) + f, err := service.cfg.BeaconDB.FinalizedCheckpoint(ctx) + require.NoError(t, err) + require.Equal(t, f.Epoch, service.FinalizedCheckpt().Epoch) } func TestInsertFinalizedDeposits(t *testing.T) { From 861c2f51205d679f76c8439cf41eff06ffe2379e Mon Sep 17 00:00:00 2001 From: Potuz Date: Wed, 29 Sep 2021 11:17:06 -0300 Subject: [PATCH 14/25] remove unnecessary asserts from tests (#9700) --- beacon-chain/sync/pending_attestations_queue_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/beacon-chain/sync/pending_attestations_queue_test.go b/beacon-chain/sync/pending_attestations_queue_test.go index 8417f788c4fd..545ed9a7df13 100644 --- a/beacon-chain/sync/pending_attestations_queue_test.go +++ b/beacon-chain/sync/pending_attestations_queue_test.go @@ -83,7 +83,6 @@ func TestProcessPendingAtts_HasBlockSaveUnAggregatedAtt(t *testing.T) { assert.NoError(t, err) attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee) require.NoError(t, err) - assert.NoError(t, err) attesterDomain, err := signing.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorRoot()) require.NoError(t, err) hashTreeRoot, err := signing.ComputeSigningRoot(att.Data, attesterDomain) @@ -199,7 +198,6 @@ func TestProcessPendingAtts_NoBroadcastWithBadSignature(t *testing.T) { assert.NoError(t, err) attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee) require.NoError(t, err) - assert.NoError(t, err) attesterDomain, err := signing.Domain(s.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, s.GenesisValidatorRoot()) require.NoError(t, err) hashTreeRoot, err := signing.ComputeSigningRoot(att.Data, attesterDomain) @@ -273,7 +271,6 @@ func TestProcessPendingAtts_HasBlockSaveAggregatedAtt(t *testing.T) { assert.NoError(t, err) attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee) require.NoError(t, err) - assert.NoError(t, err) attesterDomain, err := signing.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorRoot()) require.NoError(t, err) hashTreeRoot, err := signing.ComputeSigningRoot(att.Data, attesterDomain) From 10251c419132395a4bd8e3d6a7754ee0dd753f3c Mon Sep 17 00:00:00 2001 From: Nishant Das Date: Wed, 29 Sep 2021 23:01:10 +0800 Subject: [PATCH 15/25] increase size (#9702) --- beacon-chain/p2p/service.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/beacon-chain/p2p/service.go b/beacon-chain/p2p/service.go index a256ff220ac2..a9eb44538106 100644 --- a/beacon-chain/p2p/service.go +++ b/beacon-chain/p2p/service.go @@ -51,6 +51,10 @@ var refreshRate = slots.DivideSlotBy(2) // maxBadResponses is the maximum number of bad responses from a peer before we stop talking to it. const maxBadResponses = 5 +// pubsubQueueSize is the size that we assign to our validation queue and outbound message queue for +// gossipsub. +const pubsubQueueSize = 600 + // maxDialTimeout is the timeout for a single peer dial. var maxDialTimeout = params.BeaconNetworkConfig().RespTimeout @@ -141,8 +145,8 @@ func NewService(ctx context.Context, cfg *Config) (*Service, error) { return MsgID(s.genesisValidatorsRoot, pmsg) }), pubsub.WithSubscriptionFilter(s), - pubsub.WithPeerOutboundQueueSize(256), - pubsub.WithValidateQueueSize(256), + pubsub.WithPeerOutboundQueueSize(pubsubQueueSize), + pubsub.WithValidateQueueSize(pubsubQueueSize), pubsub.WithPeerScore(peerScoringParams()), pubsub.WithPeerScoreInspect(s.peerInspector, time.Minute), pubsub.WithGossipSubParams(pubsubGossipParam()), From 0678e9f718c50cd0c31f897dac3cab7a5dca2ed0 Mon Sep 17 00:00:00 2001 From: Mohamed Zahoor Date: Wed, 29 Sep 2021 21:13:24 +0530 Subject: [PATCH 16/25] finalise deposits before we initialise the node (#9639) * finalize deposits before we initialize the node * Update beacon-chain/blockchain/service.go Co-authored-by: terence tsao * Update beacon-chain/blockchain/service.go Co-authored-by: terence tsao * moved this when intiializing caches * added test case * satisfy deepsource * fix gazel Co-authored-by: Raul Jordan Co-authored-by: terence tsao Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> Co-authored-by: Nishant Das Co-authored-by: Preston Van Loon --- beacon-chain/powchain/BUILD.bazel | 3 + beacon-chain/powchain/service.go | 10 ++++ beacon-chain/powchain/service_test.go | 80 +++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) diff --git a/beacon-chain/powchain/BUILD.bazel b/beacon-chain/powchain/BUILD.bazel index 6992cba296c2..22767f88886a 100644 --- a/beacon-chain/powchain/BUILD.bazel +++ b/beacon-chain/powchain/BUILD.bazel @@ -76,6 +76,7 @@ go_test( deps = [ "//async/event:go_default_library", "//beacon-chain/cache/depositcache:go_default_library", + "//beacon-chain/core:go_default_library", "//beacon-chain/core/feed:go_default_library", "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/core/helpers:go_default_library", @@ -84,6 +85,7 @@ go_test( "//beacon-chain/db/testing:go_default_library", "//beacon-chain/powchain/testing:go_default_library", "//beacon-chain/powchain/types:go_default_library", + "//beacon-chain/state/stategen:go_default_library", "//config/params:go_default_library", "//container/trie:go_default_library", "//contracts/deposit:go_default_library", @@ -93,6 +95,7 @@ go_test( "//network:go_default_library", "//network/authorization:go_default_library", "//proto/prysm/v1alpha1:go_default_library", + "//proto/prysm/v1alpha1/wrapper:go_default_library", "//testing/assert:go_default_library", "//testing/require:go_default_library", "//testing/util:go_default_library", diff --git a/beacon-chain/powchain/service.go b/beacon-chain/powchain/service.go index 40843217ae83..80c6aba6077d 100644 --- a/beacon-chain/powchain/service.go +++ b/beacon-chain/powchain/service.go @@ -616,6 +616,16 @@ func (s *Service) initDepositCaches(ctx context.Context, ctrs []*protodb.Deposit } // Set deposit index to the one in the current archived state. currIndex = fState.Eth1DepositIndex() + + // when a node pauses for some time and starts again, the deposits to finalize + // accumulates. we finalize them here before we are ready to receive a block. + // Otherwise, the first few blocks will be slower to compute as we will + // hold the lock and be busy finalizing the deposits. + s.cfg.DepositCache.InsertFinalizedDeposits(ctx, int64(currIndex)) + // Deposit proofs are only used during state transition and can be safely removed to save space. + if err = s.cfg.DepositCache.PruneProofs(ctx, int64(currIndex)); err != nil { + return errors.Wrap(err, "could not prune deposit proofs") + } } validDepositsCount.Add(float64(currIndex)) // Only add pending deposits if the container slice length diff --git a/beacon-chain/powchain/service_test.go b/beacon-chain/powchain/service_test.go index f2b942cc23b5..4f0d9df9038d 100644 --- a/beacon-chain/powchain/service_test.go +++ b/beacon-chain/powchain/service_test.go @@ -17,14 +17,18 @@ import ( gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/prysmaticlabs/prysm/async/event" "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" + "github.com/prysmaticlabs/prysm/beacon-chain/core" dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" "github.com/prysmaticlabs/prysm/config/params" contracts "github.com/prysmaticlabs/prysm/contracts/deposit" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/monitoring/clientstats" "github.com/prysmaticlabs/prysm/network" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" protodb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper" "github.com/prysmaticlabs/prysm/testing/assert" "github.com/prysmaticlabs/prysm/testing/require" "github.com/prysmaticlabs/prysm/testing/util" @@ -463,6 +467,82 @@ func TestInitDepositCache_OK(t *testing.T) { require.Equal(t, 3, len(s.cfg.DepositCache.PendingContainers(context.Background(), nil))) } +func TestInitDepositCacheWithFinalization_OK(t *testing.T) { + ctrs := []*protodb.DepositContainer{ + { + Index: 0, + Eth1BlockHeight: 2, + Deposit: ðpb.Deposit{ + Data: ðpb.Deposit_Data{ + PublicKey: bytesutil.PadTo([]byte{0}, 48), + WithdrawalCredentials: make([]byte, 32), + Signature: make([]byte, 96), + }, + }, + }, + { + Index: 1, + Eth1BlockHeight: 4, + Deposit: ðpb.Deposit{ + Data: ðpb.Deposit_Data{ + PublicKey: bytesutil.PadTo([]byte{1}, 48), + WithdrawalCredentials: make([]byte, 32), + Signature: make([]byte, 96), + }, + }, + }, + { + Index: 2, + Eth1BlockHeight: 6, + Deposit: ðpb.Deposit{ + Data: ðpb.Deposit_Data{ + PublicKey: bytesutil.PadTo([]byte{2}, 48), + WithdrawalCredentials: make([]byte, 32), + Signature: make([]byte, 96), + }, + }, + }, + } + gs, _ := util.DeterministicGenesisState(t, 1) + beaconDB := dbutil.SetupDB(t) + s := &Service{ + chainStartData: &protodb.ChainStartData{Chainstarted: false}, + preGenesisState: gs, + cfg: &Web3ServiceConfig{BeaconDB: beaconDB}, + } + var err error + s.cfg.DepositCache, err = depositcache.New() + require.NoError(t, err) + require.NoError(t, s.initDepositCaches(context.Background(), ctrs)) + + require.Equal(t, 0, len(s.cfg.DepositCache.PendingContainers(context.Background(), nil))) + + headBlock := util.NewBeaconBlock() + headRoot, err := headBlock.Block.HashTreeRoot() + require.NoError(t, err) + stateGen := stategen.New(beaconDB) + + emptyState, err := util.NewBeaconState() + require.NoError(t, err) + require.NoError(t, s.cfg.BeaconDB.SaveGenesisBlockRoot(context.Background(), headRoot)) + require.NoError(t, s.cfg.BeaconDB.SaveState(context.Background(), emptyState, headRoot)) + require.NoError(t, stateGen.SaveState(context.Background(), headRoot, emptyState)) + s.cfg.StateGen = stateGen + require.NoError(t, emptyState.SetEth1DepositIndex(2)) + + ctx := context.Background() + require.NoError(t, stateGen.SaveState(ctx, headRoot, emptyState)) + require.NoError(t, beaconDB.SaveState(ctx, emptyState, headRoot)) + require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(headBlock))) + require.NoError(t, beaconDB.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Epoch: core.SlotToEpoch(0), Root: headRoot[:]})) + + s.chainStartData.Chainstarted = true + require.NoError(t, s.initDepositCaches(context.Background(), ctrs)) + + deps := s.cfg.DepositCache.NonFinalizedDeposits(context.Background(), nil) + assert.Equal(t, 0, len(deps)) +} + func TestNewService_EarliestVotingBlock(t *testing.T) { testAcc, err := contracts.Setup() require.NoError(t, err, "Unable to set up simulated backend") From ff625d55df3ab5733215842c2a5b6834393a565a Mon Sep 17 00:00:00 2001 From: Potuz Date: Wed, 29 Sep 2021 14:27:47 -0300 Subject: [PATCH 17/25] Use target root for pending attestations in tests instead of unrelated one (#9699) * Use target root for pending attestations instead of unrelated one * remove unnecessary block saves Co-authored-by: Preston Van Loon --- .../sync/pending_attestations_queue_test.go | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/beacon-chain/sync/pending_attestations_queue_test.go b/beacon-chain/sync/pending_attestations_queue_test.go index 545ed9a7df13..6c31d52c831d 100644 --- a/beacon-chain/sync/pending_attestations_queue_test.go +++ b/beacon-chain/sync/pending_attestations_queue_test.go @@ -122,15 +122,11 @@ func TestProcessPendingAtts_HasBlockSaveUnAggregatedAtt(t *testing.T) { seenUnAggregatedAttestationCache: lruwrpr.New(10), } - sb = util.NewBeaconBlock() - r32, err := sb.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, r.cfg.DB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(sb))) s, err := util.NewBeaconState() require.NoError(t, err) - require.NoError(t, r.cfg.DB.SaveState(context.Background(), s, r32)) + require.NoError(t, r.cfg.DB.SaveState(context.Background(), s, root)) - r.blkRootToPendingAtts[r32] = []*ethpb.SignedAggregateAttestationAndProof{{Message: aggregateAndProof, Signature: aggreSig}} + r.blkRootToPendingAtts[root] = []*ethpb.SignedAggregateAttestationAndProof{{Message: aggregateAndProof, Signature: aggreSig}} require.NoError(t, r.processPendingAtts(context.Background())) atts, err := r.cfg.AttPool.UnaggregatedAttestations() @@ -313,15 +309,11 @@ func TestProcessPendingAtts_HasBlockSaveAggregatedAtt(t *testing.T) { seenAggregatedAttestationCache: lruwrpr.New(10), } - sb = util.NewBeaconBlock() - r32, err := sb.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, r.cfg.DB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(sb))) s, err := util.NewBeaconState() require.NoError(t, err) - require.NoError(t, r.cfg.DB.SaveState(context.Background(), s, r32)) + require.NoError(t, r.cfg.DB.SaveState(context.Background(), s, root)) - r.blkRootToPendingAtts[r32] = []*ethpb.SignedAggregateAttestationAndProof{{Message: aggregateAndProof, Signature: aggreSig}} + r.blkRootToPendingAtts[root] = []*ethpb.SignedAggregateAttestationAndProof{{Message: aggregateAndProof, Signature: aggreSig}} require.NoError(t, r.processPendingAtts(context.Background())) assert.Equal(t, 1, len(r.cfg.AttPool.AggregatedAttestations()), "Did not save aggregated att") From 86efa871016691a7e050b4cea81b1a851c9ea790 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Wed, 29 Sep 2021 10:46:51 -0700 Subject: [PATCH 18/25] Refactor sync and participation field roots (#9703) Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- beacon-chain/state/stateutil/BUILD.bazel | 2 ++ .../participation_bit_root.go} | 6 +++--- .../sync_committee.root.go} | 6 +++--- beacon-chain/state/v2/BUILD.bazel | 2 -- beacon-chain/state/v2/field_roots.go | 8 ++++---- beacon-chain/state/v2/state_trie.go | 8 ++++---- 6 files changed, 16 insertions(+), 16 deletions(-) rename beacon-chain/state/{v2/field_root_participation_bit.go => stateutil/participation_bit_root.go} (92%) rename beacon-chain/state/{v2/field_root_sync_committee.go => stateutil/sync_committee.root.go} (90%) diff --git a/beacon-chain/state/stateutil/BUILD.bazel b/beacon-chain/state/stateutil/BUILD.bazel index 025209d4dfe5..ec81b9c13262 100644 --- a/beacon-chain/state/stateutil/BUILD.bazel +++ b/beacon-chain/state/stateutil/BUILD.bazel @@ -6,8 +6,10 @@ go_library( "array_root.go", "block_header_root.go", "eth1_root.go", + "participation_bit_root.go", "pending_attestation_root.go", "reference.go", + "sync_committee.root.go", "trie_helpers.go", "validator_map_handler.go", "validator_root.go", diff --git a/beacon-chain/state/v2/field_root_participation_bit.go b/beacon-chain/state/stateutil/participation_bit_root.go similarity index 92% rename from beacon-chain/state/v2/field_root_participation_bit.go rename to beacon-chain/state/stateutil/participation_bit_root.go index ab21edb1ca5a..6836f78d1607 100644 --- a/beacon-chain/state/v2/field_root_participation_bit.go +++ b/beacon-chain/state/stateutil/participation_bit_root.go @@ -1,4 +1,4 @@ -package v2 +package stateutil import ( "encoding/binary" @@ -9,9 +9,9 @@ import ( "github.com/prysmaticlabs/prysm/encoding/ssz" ) -// participationBitsRoot computes the HashTreeRoot merkleization of +// ParticipationBitsRoot computes the HashTreeRoot merkleization of // participation roots. -func participationBitsRoot(bits []byte) ([32]byte, error) { +func ParticipationBitsRoot(bits []byte) ([32]byte, error) { hasher := hash.CustomSHA256Hasher() chunkedRoots, err := packParticipationBits(bits) if err != nil { diff --git a/beacon-chain/state/v2/field_root_sync_committee.go b/beacon-chain/state/stateutil/sync_committee.root.go similarity index 90% rename from beacon-chain/state/v2/field_root_sync_committee.go rename to beacon-chain/state/stateutil/sync_committee.root.go index c62d1884b3dc..492818a6ff2c 100644 --- a/beacon-chain/state/v2/field_root_sync_committee.go +++ b/beacon-chain/state/stateutil/sync_committee.root.go @@ -1,4 +1,4 @@ -package v2 +package stateutil import ( "github.com/prysmaticlabs/prysm/crypto/hash" @@ -6,10 +6,10 @@ import ( ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" ) -// syncCommitteeRoot computes the HashTreeRoot Merkleization of a commitee root. +// SyncCommitteeRoot computes the HashTreeRoot Merkleization of a commitee root. // a SyncCommitteeRoot struct according to the eth2 // Simple Serialize specification. -func syncCommitteeRoot(committee *ethpb.SyncCommittee) ([32]byte, error) { +func SyncCommitteeRoot(committee *ethpb.SyncCommittee) ([32]byte, error) { hasher := hash.CustomSHA256Hasher() var fieldRoots [][32]byte if committee == nil { diff --git a/beacon-chain/state/v2/BUILD.bazel b/beacon-chain/state/v2/BUILD.bazel index 99b8f8db4df3..cc8418cd664a 100644 --- a/beacon-chain/state/v2/BUILD.bazel +++ b/beacon-chain/state/v2/BUILD.bazel @@ -6,8 +6,6 @@ go_library( "deprecated_getters.go", "deprecated_setters.go", "field_root_eth1.go", - "field_root_participation_bit.go", - "field_root_sync_committee.go", "field_root_validator.go", "field_root_vector.go", "field_roots.go", diff --git a/beacon-chain/state/v2/field_roots.go b/beacon-chain/state/v2/field_roots.go index da88018c6e07..51217c559418 100644 --- a/beacon-chain/state/v2/field_roots.go +++ b/beacon-chain/state/v2/field_roots.go @@ -163,14 +163,14 @@ func (h *stateRootHasher) computeFieldRootsWithHasher(ctx context.Context, state fieldRoots[14] = slashingsRootsRoot[:] // PreviousEpochParticipation slice root. - prevParticipationRoot, err := participationBitsRoot(state.PreviousEpochParticipation) + prevParticipationRoot, err := stateutil.ParticipationBitsRoot(state.PreviousEpochParticipation) if err != nil { return nil, errors.Wrap(err, "could not compute previous epoch participation merkleization") } fieldRoots[15] = prevParticipationRoot[:] // CurrentEpochParticipation slice root. - currParticipationRoot, err := participationBitsRoot(state.CurrentEpochParticipation) + currParticipationRoot, err := stateutil.ParticipationBitsRoot(state.CurrentEpochParticipation) if err != nil { return nil, errors.Wrap(err, "could not compute current epoch participation merkleization") } @@ -209,14 +209,14 @@ func (h *stateRootHasher) computeFieldRootsWithHasher(ctx context.Context, state fieldRoots[21] = inactivityScoresRoot[:] // Current sync committee root. - currentSyncCommitteeRoot, err := syncCommitteeRoot(state.CurrentSyncCommittee) + currentSyncCommitteeRoot, err := stateutil.SyncCommitteeRoot(state.CurrentSyncCommittee) if err != nil { return nil, errors.Wrap(err, "could not compute sync committee merkleization") } fieldRoots[22] = currentSyncCommitteeRoot[:] // Next sync committee root. - nextSyncCommitteeRoot, err := syncCommitteeRoot(state.NextSyncCommittee) + nextSyncCommitteeRoot, err := stateutil.SyncCommitteeRoot(state.NextSyncCommittee) if err != nil { return nil, errors.Wrap(err, "could not compute sync committee merkleization") } diff --git a/beacon-chain/state/v2/state_trie.go b/beacon-chain/state/v2/state_trie.go index d72b48a7b463..40a6cab31cfb 100644 --- a/beacon-chain/state/v2/state_trie.go +++ b/beacon-chain/state/v2/state_trie.go @@ -338,9 +338,9 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex) case slashings: return ssz.SlashingsRoot(b.state.Slashings) case previousEpochParticipationBits: - return participationBitsRoot(b.state.PreviousEpochParticipation) + return stateutil.ParticipationBitsRoot(b.state.PreviousEpochParticipation) case currentEpochParticipationBits: - return participationBitsRoot(b.state.CurrentEpochParticipation) + return stateutil.ParticipationBitsRoot(b.state.CurrentEpochParticipation) case justificationBits: return bytesutil.ToBytes32(b.state.JustificationBits), nil case previousJustifiedCheckpoint: @@ -352,9 +352,9 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex) case inactivityScores: return stateutil.Uint64ListRootWithRegistryLimit(b.state.InactivityScores) case currentSyncCommittee: - return syncCommitteeRoot(b.state.CurrentSyncCommittee) + return stateutil.SyncCommitteeRoot(b.state.CurrentSyncCommittee) case nextSyncCommittee: - return syncCommitteeRoot(b.state.NextSyncCommittee) + return stateutil.SyncCommitteeRoot(b.state.NextSyncCommittee) } return [32]byte{}, errors.New("invalid field index provided") } From df33ce330918ac0c5404d848ec7b569eec14464d Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Wed, 29 Sep 2021 13:17:37 -0500 Subject: [PATCH 19/25] Remaining Slasher Beacon Node Changes (#9701) * slasher beacon node changes * remaining beacon node items * moar changes * gaz * flag fix * rem slashable * builds * imports * fix up * pruning faster test * deepsource * fix wrong item * node node feature flags * broken test * preston review * more preston comments * comment Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- beacon-chain/blockchain/BUILD.bazel | 1 + beacon-chain/blockchain/process_block.go | 28 +++ beacon-chain/blockchain/service.go | 2 + beacon-chain/core/BUILD.bazel | 3 +- beacon-chain/core/helpers/BUILD.bazel | 4 +- beacon-chain/node/BUILD.bazel | 2 + beacon-chain/node/node.go | 189 ++++++++++++++---- beacon-chain/rpc/BUILD.bazel | 2 + .../rpc/prysm/v1alpha1/slasher/BUILD.bazel | 29 +++ .../prysm/v1alpha1/slasher/attestations.go | 34 ++++ .../rpc/prysm/v1alpha1/slasher/blocks.go | 28 +++ .../rpc/prysm/v1alpha1/slasher/server.go | 12 ++ .../rpc/prysm/v1alpha1/slasher/server_test.go | 54 +++++ beacon-chain/rpc/service.go | 8 + beacon-chain/slasher/BUILD.bazel | 11 + beacon-chain/slasher/detect_blocks_test.go | 21 ++ beacon-chain/slasher/mock_slashing_checker.go | 57 ++++++ beacon-chain/slasher/receive_test.go | 23 --- beacon-chain/slasher/rpc.go | 64 ++++++ beacon-chain/slasher/rpc_test.go | 137 +++++++++++++ beacon-chain/slasher/service_test.go | 99 +++++++++ beacon-chain/state/BUILD.bazel | 2 +- beacon-chain/state/stategen/BUILD.bazel | 1 + beacon-chain/state/stateutil/BUILD.bazel | 2 +- beacon-chain/state/v1/BUILD.bazel | 2 +- beacon-chain/sync/BUILD.bazel | 2 + beacon-chain/sync/service.go | 28 +-- .../subscriber_beacon_aggregate_proof_test.go | 8 +- beacon-chain/sync/validate_aggregate_proof.go | 2 +- .../sync/validate_aggregate_proof_test.go | 22 +- .../sync/validate_beacon_attestation.go | 32 ++- .../sync/validate_beacon_attestation_test.go | 10 +- beacon-chain/sync/validate_beacon_blocks.go | 14 ++ config/features/config.go | 11 +- config/features/flags.go | 9 +- proto/prysm/v1alpha1/slasher.pb.go | 153 +++----------- proto/prysm/v1alpha1/slasher.proto | 16 -- validator/client/attest_protect.go | 4 +- validator/client/attest_protect_test.go | 8 +- validator/client/propose_protect.go | 26 +-- validator/client/propose_protect_test.go | 13 +- validator/client/runner.go | 2 +- validator/client/runner_test.go | 2 +- .../slashing_protection_interchange_test.go | 2 +- validator/client/validator.go | 2 +- .../db/kv/prune_attester_protection_test.go | 2 +- validator/node/node.go | 15 +- validator/slashing-protection/BUILD.bazel | 1 + validator/slashing-protection/external.go | 42 +--- .../slashing-protection/external_test.go | 49 +---- .../slashing-protection/iface/protector.go | 4 +- validator/testing/mock_protector.go | 16 +- validator/testing/mock_slasher.go | 40 +--- 53 files changed, 916 insertions(+), 434 deletions(-) create mode 100644 beacon-chain/rpc/prysm/v1alpha1/slasher/BUILD.bazel create mode 100644 beacon-chain/rpc/prysm/v1alpha1/slasher/attestations.go create mode 100644 beacon-chain/rpc/prysm/v1alpha1/slasher/blocks.go create mode 100644 beacon-chain/rpc/prysm/v1alpha1/slasher/server.go create mode 100644 beacon-chain/rpc/prysm/v1alpha1/slasher/server_test.go create mode 100644 beacon-chain/slasher/mock_slashing_checker.go create mode 100644 beacon-chain/slasher/rpc.go create mode 100644 beacon-chain/slasher/rpc_test.go create mode 100644 beacon-chain/slasher/service_test.go diff --git a/beacon-chain/blockchain/BUILD.bazel b/beacon-chain/blockchain/BUILD.bazel index 7f8934dd3bb2..8cfd5cfc3c75 100644 --- a/beacon-chain/blockchain/BUILD.bazel +++ b/beacon-chain/blockchain/BUILD.bazel @@ -27,6 +27,7 @@ go_library( ], deps = [ "//async:go_default_library", + "//async/event:go_default_library", "//beacon-chain/cache:go_default_library", "//beacon-chain/cache/depositcache:go_default_library", "//beacon-chain/core:go_default_library", diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index 4fe9cbbd9585..393bd8bfa0d9 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -16,6 +16,7 @@ import ( "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/crypto/bls" "github.com/prysmaticlabs/prysm/encoding/bytesutil" + "github.com/prysmaticlabs/prysm/monitoring/tracing" ethpbv1 "github.com/prysmaticlabs/prysm/proto/eth/v1" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/attestation" @@ -106,6 +107,33 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b return err } + // If slasher is configured, forward the attestations in the block via + // an event feed for processing. + if features.Get().EnableSlasher { + // Feed the indexed attestation to slasher if enabled. This action + // is done in the background to avoid adding more load to this critical code path. + go func() { + // Using a different context to prevent timeouts as this operation can be expensive + // and we want to avoid affecting the critical code path. + ctx := context.TODO() + for _, att := range signed.Block().Body().Attestations() { + committee, err := helpers.BeaconCommitteeFromState(ctx, preState, att.Data.Slot, att.Data.CommitteeIndex) + if err != nil { + log.WithError(err).Error("Could not get attestation committee") + tracing.AnnotateError(span, err) + return + } + indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committee) + if err != nil { + log.WithError(err).Error("Could not convert to indexed attestation") + tracing.AnnotateError(span, err) + return + } + s.cfg.SlasherAttestationsFeed.Send(indexedAtt) + } + }() + } + // Update justified check point. currJustifiedEpoch := s.justifiedCheckpt.Epoch if postState.CurrentJustifiedCheckpoint().Epoch > currJustifiedEpoch { diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go index 3d9a575a3d3a..0e96b4491b2d 100644 --- a/beacon-chain/blockchain/service.go +++ b/beacon-chain/blockchain/service.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" types "github.com/prysmaticlabs/eth2-types" + "github.com/prysmaticlabs/prysm/async/event" "github.com/prysmaticlabs/prysm/beacon-chain/cache" "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" "github.com/prysmaticlabs/prysm/beacon-chain/core" @@ -82,6 +83,7 @@ type Config struct { ForkChoiceStore f.ForkChoicer AttService *attestations.Service StateGen *stategen.State + SlasherAttestationsFeed *event.Feed WeakSubjectivityCheckpt *ethpb.Checkpoint } diff --git a/beacon-chain/core/BUILD.bazel b/beacon-chain/core/BUILD.bazel index 2f880b074fae..287454be74fb 100644 --- a/beacon-chain/core/BUILD.bazel +++ b/beacon-chain/core/BUILD.bazel @@ -7,12 +7,11 @@ go_library( visibility = [ "//beacon-chain:__subpackages__", "//contracts/deposit:__pkg__", + "//crypto/keystore:__pkg__", "//fuzz:__pkg__", "//network/forks:__pkg__", "//proto/prysm/v1alpha1/attestation:__pkg__", "//runtime/interop:__pkg__", - "//shared/attestationutil:__pkg__", - "//shared/keystore:__pkg__", "//slasher:__subpackages__", "//testing/altair:__pkg__", "//testing/benchmark/benchmark_files:__subpackages__", diff --git a/beacon-chain/core/helpers/BUILD.bazel b/beacon-chain/core/helpers/BUILD.bazel index 73c706719a4a..74c64999fdc5 100644 --- a/beacon-chain/core/helpers/BUILD.bazel +++ b/beacon-chain/core/helpers/BUILD.bazel @@ -18,12 +18,12 @@ go_library( visibility = [ "//beacon-chain:__subpackages__", "//contracts/deposit:__pkg__", + "//crypto/keystore:__pkg__", "//fuzz:__pkg__", "//network/forks:__pkg__", + "//proto/prysm/v1alpha1:__subpackages__", "//proto/prysm/v1alpha1/attestation:__pkg__", "//runtime/interop:__pkg__", - "//shared/attestationutil:__pkg__", - "//shared/keystore:__pkg__", "//slasher:__subpackages__", "//testing/altair:__pkg__", "//testing/benchmark/benchmark_files:__subpackages__", diff --git a/beacon-chain/node/BUILD.bazel b/beacon-chain/node/BUILD.bazel index 879cd6dfe93e..71b1b5821ef3 100644 --- a/beacon-chain/node/BUILD.bazel +++ b/beacon-chain/node/BUILD.bazel @@ -21,6 +21,7 @@ go_library( "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/db:go_default_library", "//beacon-chain/db/kv:go_default_library", + "//beacon-chain/db/slasherkv:go_default_library", "//beacon-chain/forkchoice:go_default_library", "//beacon-chain/forkchoice/protoarray:go_default_library", "//beacon-chain/gateway:go_default_library", @@ -34,6 +35,7 @@ go_library( "//beacon-chain/powchain:go_default_library", "//beacon-chain/rpc:go_default_library", "//beacon-chain/rpc/apimiddleware:go_default_library", + "//beacon-chain/slasher:go_default_library", "//beacon-chain/state/stategen:go_default_library", "//beacon-chain/sync:go_default_library", "//beacon-chain/sync/initial-sync:go_default_library", diff --git a/beacon-chain/node/node.go b/beacon-chain/node/node.go index ccf5f2257730..b9c937b10683 100644 --- a/beacon-chain/node/node.go +++ b/beacon-chain/node/node.go @@ -24,6 +24,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/db" "github.com/prysmaticlabs/prysm/beacon-chain/db/kv" + "github.com/prysmaticlabs/prysm/beacon-chain/db/slasherkv" "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice" "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray" "github.com/prysmaticlabs/prysm/beacon-chain/gateway" @@ -37,6 +38,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/powchain" "github.com/prysmaticlabs/prysm/beacon-chain/rpc" "github.com/prysmaticlabs/prysm/beacon-chain/rpc/apimiddleware" + "github.com/prysmaticlabs/prysm/beacon-chain/slasher" "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" regularsync "github.com/prysmaticlabs/prysm/beacon-chain/sync" initialsync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync" @@ -64,24 +66,27 @@ const debugGrpcMaxMsgSize = 1 << 27 // full PoS node. It handles the lifecycle of the entire system and registers // services to a service registry. type BeaconNode struct { - cliCtx *cli.Context - ctx context.Context - cancel context.CancelFunc - services *runtime.ServiceRegistry - lock sync.RWMutex - stop chan struct{} // Channel to wait for termination notifications. - db db.Database - attestationPool attestations.Pool - exitPool voluntaryexits.PoolManager - slashingsPool slashings.PoolManager - syncCommitteePool synccommittee.Pool - depositCache *depositcache.DepositCache - stateFeed *event.Feed - blockFeed *event.Feed - opFeed *event.Feed - forkChoiceStore forkchoice.ForkChoicer - stateGen *stategen.State - collector *bcnodeCollector + cliCtx *cli.Context + ctx context.Context + cancel context.CancelFunc + services *runtime.ServiceRegistry + lock sync.RWMutex + stop chan struct{} // Channel to wait for termination notifications. + db db.Database + slasherDB db.SlasherDatabase + attestationPool attestations.Pool + exitPool voluntaryexits.PoolManager + slashingsPool slashings.PoolManager + syncCommitteePool synccommittee.Pool + depositCache *depositcache.DepositCache + stateFeed *event.Feed + blockFeed *event.Feed + opFeed *event.Feed + forkChoiceStore forkchoice.ForkChoicer + stateGen *stategen.State + collector *bcnodeCollector + slasherBlockHeadersFeed *event.Feed + slasherAttestationsFeed *event.Feed } // New creates a new node instance, sets up configuration options, and registers @@ -108,18 +113,20 @@ func New(cliCtx *cli.Context) (*BeaconNode, error) { ctx, cancel := context.WithCancel(cliCtx.Context) beacon := &BeaconNode{ - cliCtx: cliCtx, - ctx: ctx, - cancel: cancel, - services: registry, - stop: make(chan struct{}), - stateFeed: new(event.Feed), - blockFeed: new(event.Feed), - opFeed: new(event.Feed), - attestationPool: attestations.NewPool(), - exitPool: voluntaryexits.NewPool(), - slashingsPool: slashings.NewPool(), - syncCommitteePool: synccommittee.NewPool(), + cliCtx: cliCtx, + ctx: ctx, + cancel: cancel, + services: registry, + stop: make(chan struct{}), + stateFeed: new(event.Feed), + blockFeed: new(event.Feed), + opFeed: new(event.Feed), + attestationPool: attestations.NewPool(), + exitPool: voluntaryexits.NewPool(), + slashingsPool: slashings.NewPool(), + syncCommitteePool: synccommittee.NewPool(), + slasherBlockHeadersFeed: new(event.Feed), + slasherAttestationsFeed: new(event.Feed), } depositAddress, err := registration.DepositContractAddress() @@ -130,6 +137,10 @@ func New(cliCtx *cli.Context) (*BeaconNode, error) { return nil, err } + if err := beacon.startSlasherDB(cliCtx); err != nil { + return nil, err + } + beacon.startStateGen() if err := beacon.registerP2P(cliCtx); err != nil { @@ -162,6 +173,10 @@ func New(cliCtx *cli.Context) (*BeaconNode, error) { return nil, err } + if err := beacon.registerSlasherService(); err != nil { + return nil, err + } + if err := beacon.registerRPCService(); err != nil { return nil, err } @@ -329,11 +344,9 @@ func (b *BeaconNode) startDB(cliCtx *cli.Context, depositAddress string) error { return errors.Wrap(err, "could not load genesis from file") } } - if err := b.db.EnsureEmbeddedGenesis(b.ctx); err != nil { return err } - knownContract, err := b.db.DepositContractAddress(b.ctx) if err != nil { return err @@ -351,7 +364,53 @@ func (b *BeaconNode) startDB(cliCtx *cli.Context, depositAddress string) error { knownContract, addr.Bytes()) } log.Infof("Deposit contract: %#x", addr.Bytes()) + return nil +} +func (b *BeaconNode) startSlasherDB(cliCtx *cli.Context) error { + if !features.Get().EnableSlasher { + return nil + } + baseDir := cliCtx.String(cmd.DataDirFlag.Name) + dbPath := filepath.Join(baseDir, kv.BeaconNodeDbDirName) + clearDB := cliCtx.Bool(cmd.ClearDB.Name) + forceClearDB := cliCtx.Bool(cmd.ForceClearDB.Name) + + log.WithField("database-path", dbPath).Info("Checking DB") + + d, err := slasherkv.NewKVStore(b.ctx, dbPath, &slasherkv.Config{ + InitialMMapSize: cliCtx.Int(cmd.BoltMMapInitialSizeFlag.Name), + }) + if err != nil { + return err + } + clearDBConfirmed := false + if clearDB && !forceClearDB { + actionText := "This will delete your beacon chain database stored in your data directory. " + + "Your database backups will not be removed - do you want to proceed? (Y/N)" + deniedText := "Database will not be deleted. No changes have been made." + clearDBConfirmed, err = cmd.ConfirmAction(actionText, deniedText) + if err != nil { + return err + } + } + if clearDBConfirmed || forceClearDB { + log.Warning("Removing database") + if err := d.Close(); err != nil { + return errors.Wrap(err, "could not close db prior to clearing") + } + if err := d.ClearDB(); err != nil { + return errors.Wrap(err, "could not clear database") + } + d, err = slasherkv.NewKVStore(b.ctx, dbPath, &slasherkv.Config{ + InitialMMapSize: cliCtx.Int(cmd.BoltMMapInitialSizeFlag.Name), + }) + if err != nil { + return errors.Wrap(err, "could not create new database") + } + } + + b.slasherDB = d return nil } @@ -441,6 +500,7 @@ func (b *BeaconNode) registerBlockchainService() error { ForkChoiceStore: b.forkChoiceStore, AttService: attService, StateGen: b.stateGen, + SlasherAttestationsFeed: b.slasherAttestationsFeed, WeakSubjectivityCheckpt: wsCheckpt, }) if err != nil { @@ -500,18 +560,21 @@ func (b *BeaconNode) registerSyncService() error { } rs := regularsync.NewService(b.ctx, ®ularsync.Config{ - DB: b.db, - P2P: b.fetchP2P(), - Chain: chainService, - InitialSync: initSync, - StateNotifier: b, - BlockNotifier: b, - OperationNotifier: b, - AttPool: b.attestationPool, - ExitPool: b.exitPool, - SlashingPool: b.slashingsPool, - SyncCommsPool: b.syncCommitteePool, - StateGen: b.stateGen, + DB: b.db, + P2P: b.fetchP2P(), + Chain: chainService, + InitialSync: initSync, + StateNotifier: b, + BlockNotifier: b, + AttestationNotifier: b, + OperationNotifier: b, + AttPool: b.attestationPool, + ExitPool: b.exitPool, + SlashingPool: b.slashingsPool, + SyncCommsPool: b.syncCommitteePool, + StateGen: b.stateGen, + SlasherAttestationsFeed: b.slasherAttestationsFeed, + SlasherBlockHeadersFeed: b.slasherBlockHeadersFeed, }) return b.services.RegisterService(rs) @@ -533,6 +596,36 @@ func (b *BeaconNode) registerInitialSyncService() error { return b.services.RegisterService(is) } +func (b *BeaconNode) registerSlasherService() error { + if !features.Get().EnableSlasher { + return nil + } + var chainService *blockchain.Service + if err := b.services.FetchService(&chainService); err != nil { + return err + } + var syncService *initialsync.Service + if err := b.services.FetchService(&syncService); err != nil { + return err + } + + slasherSrv, err := slasher.New(b.ctx, &slasher.ServiceConfig{ + IndexedAttestationsFeed: b.slasherAttestationsFeed, + BeaconBlockHeadersFeed: b.slasherBlockHeadersFeed, + Database: b.slasherDB, + StateNotifier: b, + AttestationStateFetcher: chainService, + StateGen: b.stateGen, + SlashingPoolInserter: b.slashingsPool, + SyncChecker: syncService, + HeadStateFetcher: chainService, + }) + if err != nil { + return err + } + return b.services.RegisterService(slasherSrv) +} + func (b *BeaconNode) registerRPCService() error { var chainService *blockchain.Service if err := b.services.FetchService(&chainService); err != nil { @@ -549,6 +642,13 @@ func (b *BeaconNode) registerRPCService() error { return err } + var slasherService *slasher.Service + if features.Get().EnableSlasher { + if err := b.services.FetchService(&slasherService); err != nil { + return err + } + } + genesisValidators := b.cliCtx.Uint64(flags.InteropNumValidatorsFlag.Name) genesisStatePath := b.cliCtx.String(flags.InteropGenesisStateFlag.Name) var depositFetcher depositcache.DepositFetcher @@ -604,6 +704,7 @@ func (b *BeaconNode) registerRPCService() error { AttestationsPool: b.attestationPool, ExitPool: b.exitPool, SlashingsPool: b.slashingsPool, + SlashingChecker: slasherService, SyncCommitteeObjectPool: b.syncCommitteePool, POWChainService: web3Service, ChainStartFetcher: chainStartFetcher, diff --git a/beacon-chain/rpc/BUILD.bazel b/beacon-chain/rpc/BUILD.bazel index 23045d3c30ca..a17d8255f6a5 100644 --- a/beacon-chain/rpc/BUILD.bazel +++ b/beacon-chain/rpc/BUILD.bazel @@ -30,8 +30,10 @@ go_library( "//beacon-chain/rpc/prysm/v1alpha1/beacon:go_default_library", "//beacon-chain/rpc/prysm/v1alpha1/debug:go_default_library", "//beacon-chain/rpc/prysm/v1alpha1/node:go_default_library", + "//beacon-chain/rpc/prysm/v1alpha1/slasher:go_default_library", "//beacon-chain/rpc/prysm/v1alpha1/validator:go_default_library", "//beacon-chain/rpc/statefetcher:go_default_library", + "//beacon-chain/slasher:go_default_library", "//beacon-chain/state/stategen:go_default_library", "//beacon-chain/sync:go_default_library", "//config/features:go_default_library", diff --git a/beacon-chain/rpc/prysm/v1alpha1/slasher/BUILD.bazel b/beacon-chain/rpc/prysm/v1alpha1/slasher/BUILD.bazel new file mode 100644 index 000000000000..38e9907d4e38 --- /dev/null +++ b/beacon-chain/rpc/prysm/v1alpha1/slasher/BUILD.bazel @@ -0,0 +1,29 @@ +load("@prysm//tools/go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "attestations.go", + "blocks.go", + "server.go", + ], + importpath = "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/slasher", + visibility = ["//beacon-chain:__subpackages__"], + deps = [ + "//beacon-chain/slasher:go_default_library", + "//proto/prysm/v1alpha1:go_default_library", + "@org_golang_google_grpc//codes:go_default_library", + "@org_golang_google_grpc//status:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["server_test.go"], + embed = [":go_default_library"], + deps = [ + "//beacon-chain/slasher:go_default_library", + "//proto/prysm/v1alpha1:go_default_library", + "//testing/require:go_default_library", + ], +) diff --git a/beacon-chain/rpc/prysm/v1alpha1/slasher/attestations.go b/beacon-chain/rpc/prysm/v1alpha1/slasher/attestations.go new file mode 100644 index 000000000000..d34e823aa383 --- /dev/null +++ b/beacon-chain/rpc/prysm/v1alpha1/slasher/attestations.go @@ -0,0 +1,34 @@ +package slasher + +import ( + "context" + + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// IsSlashableAttestation returns an attester slashing if an input +// attestation is found to be slashable. +func (s *Server) IsSlashableAttestation( + ctx context.Context, req *ethpb.IndexedAttestation, +) (*ethpb.AttesterSlashingResponse, error) { + attesterSlashings, err := s.SlashingChecker.IsSlashableAttestation(ctx, req) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not determine if attestation is slashable: %v", err) + } + if len(attesterSlashings) > 0 { + return ðpb.AttesterSlashingResponse{ + AttesterSlashings: attesterSlashings, + }, nil + } + return ðpb.AttesterSlashingResponse{}, nil +} + +// HighestAttestations returns the highest source and target epochs attested for +// validator indices that have been observed by slasher. +func (_ *Server) HighestAttestations( + ctx context.Context, req *ethpb.HighestAttestationRequest, +) (*ethpb.HighestAttestationResponse, error) { + return nil, status.Error(codes.Unimplemented, "Unimplemented") +} diff --git a/beacon-chain/rpc/prysm/v1alpha1/slasher/blocks.go b/beacon-chain/rpc/prysm/v1alpha1/slasher/blocks.go new file mode 100644 index 000000000000..7718758ab683 --- /dev/null +++ b/beacon-chain/rpc/prysm/v1alpha1/slasher/blocks.go @@ -0,0 +1,28 @@ +package slasher + +import ( + "context" + + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// IsSlashableBlock returns a proposer slashing if an input +// signed beacon block header is found to be slashable. +func (s *Server) IsSlashableBlock( + ctx context.Context, req *ethpb.SignedBeaconBlockHeader, +) (*ethpb.ProposerSlashingResponse, error) { + proposerSlashing, err := s.SlashingChecker.IsSlashableBlock(ctx, req) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not determine if block is slashable: %v", err) + } + if proposerSlashing == nil { + return ðpb.ProposerSlashingResponse{ + ProposerSlashings: []*ethpb.ProposerSlashing{}, + }, nil + } + return ðpb.ProposerSlashingResponse{ + ProposerSlashings: []*ethpb.ProposerSlashing{proposerSlashing}, + }, nil +} diff --git a/beacon-chain/rpc/prysm/v1alpha1/slasher/server.go b/beacon-chain/rpc/prysm/v1alpha1/slasher/server.go new file mode 100644 index 000000000000..ba41b16e1c9f --- /dev/null +++ b/beacon-chain/rpc/prysm/v1alpha1/slasher/server.go @@ -0,0 +1,12 @@ +// Package slasher defines a gRPC server implementation of a slasher service +// which allows for checking if attestations or blocks are slashable. +package slasher + +import ( + slasherservice "github.com/prysmaticlabs/prysm/beacon-chain/slasher" +) + +// Server defines a server implementation of the gRPC slasher service. +type Server struct { + SlashingChecker slasherservice.SlashingChecker +} diff --git a/beacon-chain/rpc/prysm/v1alpha1/slasher/server_test.go b/beacon-chain/rpc/prysm/v1alpha1/slasher/server_test.go new file mode 100644 index 000000000000..f0ec4ec66a42 --- /dev/null +++ b/beacon-chain/rpc/prysm/v1alpha1/slasher/server_test.go @@ -0,0 +1,54 @@ +package slasher + +import ( + "context" + "testing" + + "github.com/prysmaticlabs/prysm/beacon-chain/slasher" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/testing/require" +) + +func TestServer_IsSlashableAttestation_SlashingFound(t *testing.T) { + mockSlasher := &slasher.MockSlashingChecker{ + AttesterSlashingFound: true, + } + s := Server{SlashingChecker: mockSlasher} + ctx := context.Background() + slashing, err := s.IsSlashableAttestation(ctx, ðpb.IndexedAttestation{}) + require.NoError(t, err) + require.Equal(t, true, len(slashing.AttesterSlashings) > 0) +} + +func TestServer_IsSlashableAttestation_SlashingNotFound(t *testing.T) { + mockSlasher := &slasher.MockSlashingChecker{ + AttesterSlashingFound: false, + } + s := Server{SlashingChecker: mockSlasher} + ctx := context.Background() + slashing, err := s.IsSlashableAttestation(ctx, ðpb.IndexedAttestation{}) + require.NoError(t, err) + require.Equal(t, true, len(slashing.AttesterSlashings) == 0) +} + +func TestServer_IsSlashableBlock_SlashingFound(t *testing.T) { + mockSlasher := &slasher.MockSlashingChecker{ + ProposerSlashingFound: true, + } + s := Server{SlashingChecker: mockSlasher} + ctx := context.Background() + slashing, err := s.IsSlashableBlock(ctx, ðpb.SignedBeaconBlockHeader{}) + require.NoError(t, err) + require.Equal(t, true, len(slashing.ProposerSlashings) > 0) +} + +func TestServer_IsSlashableBlock_SlashingNotFound(t *testing.T) { + mockSlasher := &slasher.MockSlashingChecker{ + ProposerSlashingFound: false, + } + s := Server{SlashingChecker: mockSlasher} + ctx := context.Background() + slashing, err := s.IsSlashableBlock(ctx, ðpb.SignedBeaconBlockHeader{}) + require.NoError(t, err) + require.Equal(t, true, len(slashing.ProposerSlashings) == 0) +} diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index 49a731f39455..6f2cbb25ce0c 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -34,8 +34,10 @@ import ( beaconv1alpha1 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/beacon" debugv1alpha1 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/debug" nodev1alpha1 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/node" + slasherv1alpha1 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/slasher" validatorv1alpha1 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/validator" "github.com/prysmaticlabs/prysm/beacon-chain/rpc/statefetcher" + slasherservice "github.com/prysmaticlabs/prysm/beacon-chain/slasher" "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" chainSync "github.com/prysmaticlabs/prysm/beacon-chain/sync" "github.com/prysmaticlabs/prysm/config/features" @@ -93,6 +95,7 @@ type Config struct { AttestationsPool attestations.Pool ExitPool voluntaryexits.PoolManager SlashingsPool slashings.PoolManager + SlashingChecker slasherservice.SlashingChecker SyncCommitteeObjectPool synccommittee.Pool SyncService chainSync.Checker Broadcaster p2p.Broadcaster @@ -234,6 +237,10 @@ func (s *Service) Start() { HeadFetcher: s.cfg.HeadFetcher, } + slasherServer := &slasherv1alpha1.Server{ + SlashingChecker: s.cfg.SlashingChecker, + } + beaconChainServer := &beaconv1alpha1.Server{ Ctx: s.ctx, BeaconDB: s.cfg.BeaconDB, @@ -280,6 +287,7 @@ func (s *Service) Start() { ethpbv1alpha1.RegisterNodeServer(s.grpcServer, nodeServer) ethpbservice.RegisterBeaconNodeServer(s.grpcServer, nodeServerV1) ethpbv1alpha1.RegisterHealthServer(s.grpcServer, nodeServer) + ethpbv1alpha1.RegisterSlasherServer(s.grpcServer, slasherServer) ethpbv1alpha1.RegisterBeaconChainServer(s.grpcServer, beaconChainServer) ethpbservice.RegisterBeaconChainServer(s.grpcServer, beaconChainServerV1) ethpbservice.RegisterEventsServer(s.grpcServer, &events.Server{ diff --git a/beacon-chain/slasher/BUILD.bazel b/beacon-chain/slasher/BUILD.bazel index 9f0c680d43a6..3c3d30141a43 100644 --- a/beacon-chain/slasher/BUILD.bazel +++ b/beacon-chain/slasher/BUILD.bazel @@ -10,10 +10,12 @@ go_library( "helpers.go", "log.go", "metrics.go", + "mock_slashing_checker.go", "params.go", "process_slashings.go", "queue.go", "receive.go", + "rpc.go", "service.go", ], importpath = "github.com/prysmaticlabs/prysm/beacon-chain/slasher", @@ -46,6 +48,8 @@ go_library( "@com_github_prysmaticlabs_eth2_types//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@io_opencensus_go//trace:go_default_library", + "@org_golang_google_grpc//codes:go_default_library", + "@org_golang_google_grpc//status:go_default_library", ], ) @@ -60,17 +64,22 @@ go_test( "process_slashings_test.go", "queue_test.go", "receive_test.go", + "rpc_test.go", + "service_test.go", ], embed = [":go_default_library"], deps = [ "//async/event:go_default_library", "//beacon-chain/blockchain/testing:go_default_library", "//beacon-chain/core:go_default_library", + "//beacon-chain/core/feed:go_default_library", + "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/core/signing:go_default_library", "//beacon-chain/db/testing:go_default_library", "//beacon-chain/operations/slashings:go_default_library", "//beacon-chain/slasher/types:go_default_library", "//beacon-chain/state/stategen:go_default_library", + "//beacon-chain/sync/initial-sync/testing:go_default_library", "//config/params:go_default_library", "//crypto/bls:go_default_library", "//encoding/bytesutil:go_default_library", @@ -78,8 +87,10 @@ go_test( "//testing/assert:go_default_library", "//testing/require:go_default_library", "//testing/util:go_default_library", + "//time/slots:go_default_library", "@com_github_ferranbt_fastssz//:go_default_library", "@com_github_prysmaticlabs_eth2_types//:go_default_library", + "@com_github_sirupsen_logrus//:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", ], ) diff --git a/beacon-chain/slasher/detect_blocks_test.go b/beacon-chain/slasher/detect_blocks_test.go index 84ba4831c0a8..57c476ee2827 100644 --- a/beacon-chain/slasher/detect_blocks_test.go +++ b/beacon-chain/slasher/detect_blocks_test.go @@ -147,3 +147,24 @@ func Test_processQueuedBlocks_NotSlashable(t *testing.T) { <-exitChan require.LogsDoNotContain(t, hook, "Proposer slashing detected") } + +func createProposalWrapper(t *testing.T, slot types.Slot, proposerIndex types.ValidatorIndex, signingRoot []byte) *slashertypes.SignedBlockHeaderWrapper { + header := ðpb.BeaconBlockHeader{ + Slot: slot, + ProposerIndex: proposerIndex, + ParentRoot: params.BeaconConfig().ZeroHash[:], + StateRoot: bytesutil.PadTo(signingRoot, 32), + BodyRoot: params.BeaconConfig().ZeroHash[:], + } + signRoot, err := header.HashTreeRoot() + require.NoError(t, err) + fakeSig := make([]byte, 96) + copy(fakeSig, "hello") + return &slashertypes.SignedBlockHeaderWrapper{ + SignedBeaconBlockHeader: ðpb.SignedBeaconBlockHeader{ + Header: header, + Signature: fakeSig, + }, + SigningRoot: signRoot, + } +} diff --git a/beacon-chain/slasher/mock_slashing_checker.go b/beacon-chain/slasher/mock_slashing_checker.go new file mode 100644 index 000000000000..a1f260aba624 --- /dev/null +++ b/beacon-chain/slasher/mock_slashing_checker.go @@ -0,0 +1,57 @@ +package slasher + +import ( + "context" + + "github.com/prysmaticlabs/prysm/config/params" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" +) + +type MockSlashingChecker struct { + AttesterSlashingFound bool + ProposerSlashingFound bool +} + +func (s *MockSlashingChecker) IsSlashableBlock(ctx context.Context, proposal *ethpb.SignedBeaconBlockHeader) (*ethpb.ProposerSlashing, error) { + if s.ProposerSlashingFound { + return ðpb.ProposerSlashing{ + Header_1: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + ProposerIndex: 0, + ParentRoot: params.BeaconConfig().ZeroHash[:], + StateRoot: params.BeaconConfig().ZeroHash[:], + BodyRoot: params.BeaconConfig().ZeroHash[:], + }, + Signature: params.BeaconConfig().EmptySignature[:], + }, + Header_2: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + ProposerIndex: 0, + ParentRoot: params.BeaconConfig().ZeroHash[:], + StateRoot: params.BeaconConfig().ZeroHash[:], + BodyRoot: params.BeaconConfig().ZeroHash[:], + }, + Signature: params.BeaconConfig().EmptySignature[:], + }, + }, nil + } + return nil, nil +} + +func (s *MockSlashingChecker) IsSlashableAttestation(ctx context.Context, attestation *ethpb.IndexedAttestation) ([]*ethpb.AttesterSlashing, error) { + if s.AttesterSlashingFound { + return []*ethpb.AttesterSlashing{ + { + Attestation_1: ðpb.IndexedAttestation{ + Data: ðpb.AttestationData{}, + }, + Attestation_2: ðpb.IndexedAttestation{ + Data: ðpb.AttestationData{}, + }, + }, + }, nil + } + return nil, nil +} diff --git a/beacon-chain/slasher/receive_test.go b/beacon-chain/slasher/receive_test.go index 3d12b5c676f6..c871d33d9087 100644 --- a/beacon-chain/slasher/receive_test.go +++ b/beacon-chain/slasher/receive_test.go @@ -308,26 +308,3 @@ func TestService_processQueuedBlocks(t *testing.T) { <-exitChan assert.LogsContain(t, hook, "New slot, processing queued") } - -func createProposalWrapper(t *testing.T, slot types.Slot, proposerIndex types.ValidatorIndex, signingRoot []byte) *slashertypes.SignedBlockHeaderWrapper { - header := ðpb.BeaconBlockHeader{ - Slot: slot, - ProposerIndex: proposerIndex, - ParentRoot: params2.BeaconConfig().ZeroHash[:], - StateRoot: bytesutil.PadTo(signingRoot, 32), - BodyRoot: params2.BeaconConfig().ZeroHash[:], - } - signRoot, err := header.HashTreeRoot() - if err != nil { - t.Fatal(err) - } - someSig := make([]byte, params2.BeaconConfig().BLSSignatureLength) - copy(someSig, "foobar") - return &slashertypes.SignedBlockHeaderWrapper{ - SignedBeaconBlockHeader: ðpb.SignedBeaconBlockHeader{ - Header: header, - Signature: someSig, - }, - SigningRoot: signRoot, - } -} diff --git a/beacon-chain/slasher/rpc.go b/beacon-chain/slasher/rpc.go new file mode 100644 index 000000000000..80ac93a10009 --- /dev/null +++ b/beacon-chain/slasher/rpc.go @@ -0,0 +1,64 @@ +package slasher + +import ( + "context" + + slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// IsSlashableBlock checks if an input block header is slashable +// with respect to historical block proposal data. +func (s *Service) IsSlashableBlock( + ctx context.Context, block *ethpb.SignedBeaconBlockHeader, +) (*ethpb.ProposerSlashing, error) { + dataRoot, err := block.Header.HashTreeRoot() + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get block header hash tree root: %v", err) + } + signedBlockWrapper := &slashertypes.SignedBlockHeaderWrapper{ + SignedBeaconBlockHeader: block, + SigningRoot: dataRoot, + } + proposerSlashings, err := s.detectProposerSlashings(ctx, []*slashertypes.SignedBlockHeaderWrapper{signedBlockWrapper}) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not check if proposal is slashable: %v", err) + } + if len(proposerSlashings) == 0 { + return nil, nil + } + return proposerSlashings[0], nil +} + +// IsSlashableAttestation checks if an input indexed attestation is slashable +// with respect to historical attestation data. +func (s *Service) IsSlashableAttestation( + ctx context.Context, attestation *ethpb.IndexedAttestation, +) ([]*ethpb.AttesterSlashing, error) { + dataRoot, err := attestation.Data.HashTreeRoot() + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get attestation data hash tree root: %v", err) + } + indexedAttWrapper := &slashertypes.IndexedAttestationWrapper{ + IndexedAttestation: attestation, + SigningRoot: dataRoot, + } + + attesterSlashings, err := s.checkSlashableAttestations(ctx, []*slashertypes.IndexedAttestationWrapper{indexedAttWrapper}) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not check if attestation is slashable: %v", err) + } + if len(attesterSlashings) == 0 { + // If the incoming attestations are not slashable, we mark them as saved in + // slasher's DB storage to help us with future detection. + if err := s.serviceCfg.Database.SaveAttestationRecordsForValidators( + ctx, []*slashertypes.IndexedAttestationWrapper{indexedAttWrapper}, + ); err != nil { + return nil, status.Errorf(codes.Internal, "Could not save attestation records to DB: %v", err) + } + return nil, nil + } + return attesterSlashings, nil +} diff --git a/beacon-chain/slasher/rpc_test.go b/beacon-chain/slasher/rpc_test.go new file mode 100644 index 000000000000..872a17baad7b --- /dev/null +++ b/beacon-chain/slasher/rpc_test.go @@ -0,0 +1,137 @@ +package slasher + +import ( + "context" + "testing" + "time" + + types "github.com/prysmaticlabs/eth2-types" + dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" + "github.com/prysmaticlabs/prysm/config/params" + "github.com/prysmaticlabs/prysm/testing/assert" + "github.com/prysmaticlabs/prysm/testing/require" +) + +func TestIsSlashableBlock(t *testing.T) { + ctx := context.Background() + slasherDB := dbtest.SetupSlasherDB(t) + s := &Service{ + serviceCfg: &ServiceConfig{ + Database: slasherDB, + }, + params: DefaultParams(), + blksQueue: newBlocksQueue(), + } + err := slasherDB.SaveBlockProposals(ctx, []*slashertypes.SignedBlockHeaderWrapper{ + createProposalWrapper(t, 2, 3, []byte{1}), + createProposalWrapper(t, 3, 3, []byte{1}), + }) + require.NoError(t, err) + tests := []struct { + name string + blockToCheck *slashertypes.SignedBlockHeaderWrapper + shouldBeSlashable bool + }{ + { + name: "should not detect if same signing root", + blockToCheck: createProposalWrapper(t, 2, 3, []byte{1}), + shouldBeSlashable: false, + }, + { + name: "should not detect if different slot", + blockToCheck: createProposalWrapper(t, 1, 3, []byte{2}), + shouldBeSlashable: false, + }, + { + name: "should not detect if different validator index", + blockToCheck: createProposalWrapper(t, 2, 4, []byte{2}), + shouldBeSlashable: false, + }, + { + name: "detects differing signing root", + blockToCheck: createProposalWrapper(t, 2, 3, []byte{2}), + shouldBeSlashable: true, + }, + { + name: "should detect another slot", + blockToCheck: createProposalWrapper(t, 3, 3, []byte{2}), + shouldBeSlashable: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + proposerSlashing, err := s.IsSlashableBlock(ctx, tt.blockToCheck.SignedBeaconBlockHeader) + require.NoError(t, err) + assert.Equal(t, tt.shouldBeSlashable, proposerSlashing != nil) + }) + } +} + +func TestIsSlashableAttestation(t *testing.T) { + ctx := context.Background() + slasherDB := dbtest.SetupSlasherDB(t) + + currentEpoch := types.Epoch(3) + currentTime := time.Now() + totalSlots := uint64(currentEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch) + secondsSinceGenesis := time.Duration(totalSlots * params.BeaconConfig().SecondsPerSlot) + genesisTime := currentTime.Add(-secondsSinceGenesis * time.Second) + + s := &Service{ + serviceCfg: &ServiceConfig{ + Database: slasherDB, + }, + params: DefaultParams(), + blksQueue: newBlocksQueue(), + genesisTime: genesisTime, + } + prevAtts := []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 2, 3, []uint64{0}, []byte{1}), + createAttestationWrapper(t, 2, 3, []uint64{1}, []byte{1}), + } + err := slasherDB.SaveAttestationRecordsForValidators(ctx, prevAtts) + require.NoError(t, err) + attesterSlashings, err := s.checkSlashableAttestations(ctx, prevAtts) + require.NoError(t, err) + require.Equal(t, 0, len(attesterSlashings)) + + tests := []struct { + name string + attToCheck *slashertypes.IndexedAttestationWrapper + amtSlashable uint64 + }{ + { + name: "should not detect if same attestation data", + attToCheck: createAttestationWrapper(t, 2, 3, []uint64{1}, []byte{1}), + amtSlashable: 0, + }, + { + name: "should not detect if different index", + attToCheck: createAttestationWrapper(t, 0, 3, []uint64{2}, []byte{2}), + amtSlashable: 0, + }, + { + name: "should detect double if same index", + attToCheck: createAttestationWrapper(t, 0, 3, []uint64{0}, []byte{2}), + amtSlashable: 1, + }, + { + name: "should detect multiple double if multiple same indices", + attToCheck: createAttestationWrapper(t, 0, 3, []uint64{0, 1}, []byte{2}), + amtSlashable: 2, + }, + { + name: "should detect multiple surround if multiple same indices", + attToCheck: createAttestationWrapper(t, 1, 4, []uint64{0, 1}, []byte{2}), + amtSlashable: 2, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + attesterSlashings, err = s.IsSlashableAttestation(ctx, tt.attToCheck.IndexedAttestation) + require.NoError(t, err) + assert.Equal(t, tt.amtSlashable, uint64(len(attesterSlashings))) + }) + } +} diff --git a/beacon-chain/slasher/service_test.go b/beacon-chain/slasher/service_test.go new file mode 100644 index 000000000000..ee96e57fa313 --- /dev/null +++ b/beacon-chain/slasher/service_test.go @@ -0,0 +1,99 @@ +package slasher + +import ( + "context" + "io/ioutil" + "testing" + "time" + + types "github.com/prysmaticlabs/eth2-types" + "github.com/prysmaticlabs/prysm/async/event" + mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" + statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" + dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing" + "github.com/prysmaticlabs/prysm/testing/require" + "github.com/prysmaticlabs/prysm/testing/util" + "github.com/prysmaticlabs/prysm/time/slots" + "github.com/sirupsen/logrus" + logTest "github.com/sirupsen/logrus/hooks/test" +) + +var _ = SlashingChecker(&Service{}) +var _ = SlashingChecker(&MockSlashingChecker{}) + +func TestMain(m *testing.M) { + logrus.SetLevel(logrus.DebugLevel) + logrus.SetOutput(ioutil.Discard) + + m.Run() +} + +func TestService_StartStop_ChainStartEvent(t *testing.T) { + slasherDB := dbtest.SetupSlasherDB(t) + hook := logTest.NewGlobal() + + beaconState, err := util.NewBeaconState() + require.NoError(t, err) + currentSlot := types.Slot(4) + require.NoError(t, beaconState.SetSlot(currentSlot)) + mockChain := &mock.ChainService{ + State: beaconState, + Slot: ¤tSlot, + } + + srv, err := New(context.Background(), &ServiceConfig{ + IndexedAttestationsFeed: new(event.Feed), + BeaconBlockHeadersFeed: new(event.Feed), + StateNotifier: &mock.MockStateNotifier{}, + Database: slasherDB, + HeadStateFetcher: mockChain, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + }) + require.NoError(t, err) + go srv.Start() + time.Sleep(time.Millisecond * 100) + srv.serviceCfg.StateNotifier.StateFeed().Send(&feed.Event{ + Type: statefeed.ChainStarted, + Data: &statefeed.ChainStartedData{StartTime: time.Now()}, + }) + time.Sleep(time.Millisecond * 100) + srv.slotTicker = &slots.SlotTicker{} + require.NoError(t, srv.Stop()) + require.NoError(t, srv.Status()) + require.LogsContain(t, hook, "received chain start event") +} + +func TestService_StartStop_ChainAlreadyInitialized(t *testing.T) { + slasherDB := dbtest.SetupSlasherDB(t) + hook := logTest.NewGlobal() + beaconState, err := util.NewBeaconState() + require.NoError(t, err) + currentSlot := types.Slot(4) + require.NoError(t, beaconState.SetSlot(currentSlot)) + mockChain := &mock.ChainService{ + State: beaconState, + Slot: ¤tSlot, + } + srv, err := New(context.Background(), &ServiceConfig{ + IndexedAttestationsFeed: new(event.Feed), + BeaconBlockHeadersFeed: new(event.Feed), + StateNotifier: &mock.MockStateNotifier{}, + Database: slasherDB, + HeadStateFetcher: mockChain, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + }) + require.NoError(t, err) + go srv.Start() + time.Sleep(time.Millisecond * 100) + srv.serviceCfg.StateNotifier.StateFeed().Send(&feed.Event{ + Type: statefeed.Initialized, + Data: &statefeed.InitializedData{StartTime: time.Now()}, + }) + time.Sleep(time.Millisecond * 100) + srv.slotTicker = &slots.SlotTicker{} + require.NoError(t, srv.Stop()) + require.NoError(t, srv.Status()) + require.LogsContain(t, hook, "chain already initialized") +} diff --git a/beacon-chain/state/BUILD.bazel b/beacon-chain/state/BUILD.bazel index eb287fa39de8..87e3d5c49fb2 100644 --- a/beacon-chain/state/BUILD.bazel +++ b/beacon-chain/state/BUILD.bazel @@ -10,8 +10,8 @@ go_library( visibility = [ "//beacon-chain:__subpackages__", "//contracts/deposit:__subpackages__", + "//proto/prysm/v1alpha1:__subpackages__", "//proto/testing:__subpackages__", - "//shared/aggregation:__subpackages__", "//slasher/rpc:__subpackages__", "//testing/benchmark:__pkg__", "//testing/fuzz:__pkg__", diff --git a/beacon-chain/state/stategen/BUILD.bazel b/beacon-chain/state/stategen/BUILD.bazel index b7b90435daf0..90b5a707eb3a 100644 --- a/beacon-chain/state/stategen/BUILD.bazel +++ b/beacon-chain/state/stategen/BUILD.bazel @@ -18,6 +18,7 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen", visibility = [ "//beacon-chain:__subpackages__", + "//testing/endtoend:__subpackages__", "//testing/fuzz:__pkg__", "//testing/slasher/simulator:__pkg__", ], diff --git a/beacon-chain/state/stateutil/BUILD.bazel b/beacon-chain/state/stateutil/BUILD.bazel index ec81b9c13262..27966c685b7b 100644 --- a/beacon-chain/state/stateutil/BUILD.bazel +++ b/beacon-chain/state/stateutil/BUILD.bazel @@ -18,8 +18,8 @@ go_library( visibility = [ "//beacon-chain:__subpackages__", "//proto/migration:__subpackages__", + "//proto/prysm/v1alpha1:__subpackages__", "//proto/testing:__subpackages__", - "//shared/blockutil:__subpackages__", "//slasher:__subpackages__", "//testing:__subpackages__", "//testing/fuzz:__pkg__", diff --git a/beacon-chain/state/v1/BUILD.bazel b/beacon-chain/state/v1/BUILD.bazel index 73f99df81f03..c0360749dfc7 100644 --- a/beacon-chain/state/v1/BUILD.bazel +++ b/beacon-chain/state/v1/BUILD.bazel @@ -32,9 +32,9 @@ go_library( "//beacon-chain:__subpackages__", "//contracts/deposit:__subpackages__", "//proto/migration:__subpackages__", + "//proto/prysm/v1alpha1:__subpackages__", "//proto/testing:__subpackages__", "//runtime/interop:__subpackages__", - "//shared/aggregation:__subpackages__", "//slasher/rpc:__subpackages__", "//testing/benchmark:__pkg__", "//testing/fuzz:__pkg__", diff --git a/beacon-chain/sync/BUILD.bazel b/beacon-chain/sync/BUILD.bazel index 24f1667b5145..8668da504d91 100644 --- a/beacon-chain/sync/BUILD.bazel +++ b/beacon-chain/sync/BUILD.bazel @@ -52,6 +52,7 @@ go_library( deps = [ "//async:go_default_library", "//async/abool:go_default_library", + "//async/event:go_default_library", "//beacon-chain/blockchain:go_default_library", "//beacon-chain/cache:go_default_library", "//beacon-chain/core:go_default_library", @@ -89,6 +90,7 @@ go_library( "//monitoring/tracing:go_default_library", "//network/forks:go_default_library", "//proto/prysm/v1alpha1:go_default_library", + "//proto/prysm/v1alpha1/attestation:go_default_library", "//proto/prysm/v1alpha1/block:go_default_library", "//proto/prysm/v1alpha1/metadata:go_default_library", "//proto/prysm/v1alpha1/wrapper:go_default_library", diff --git a/beacon-chain/sync/service.go b/beacon-chain/sync/service.go index 35467f2c2b3f..a6b782ca0f44 100644 --- a/beacon-chain/sync/service.go +++ b/beacon-chain/sync/service.go @@ -17,6 +17,7 @@ import ( "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/async" "github.com/prysmaticlabs/prysm/async/abool" + "github.com/prysmaticlabs/prysm/async/event" "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/beacon-chain/core" "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" @@ -68,18 +69,21 @@ type validationFn func(ctx context.Context) (pubsub.ValidationResult, error) // Config to set up the regular sync service. type Config struct { - P2P p2p.P2P - DB db.NoHeadAccessDatabase - AttPool attestations.Pool - ExitPool voluntaryexits.PoolManager - SlashingPool slashings.PoolManager - SyncCommsPool synccommittee.Pool - Chain blockchainService - InitialSync Checker - StateNotifier statefeed.Notifier - BlockNotifier blockfeed.Notifier - OperationNotifier operation.Notifier - StateGen *stategen.State + AttestationNotifier operation.Notifier + P2P p2p.P2P + DB db.NoHeadAccessDatabase + AttPool attestations.Pool + ExitPool voluntaryexits.PoolManager + SlashingPool slashings.PoolManager + SyncCommsPool synccommittee.Pool + Chain blockchainService + InitialSync Checker + StateNotifier statefeed.Notifier + BlockNotifier blockfeed.Notifier + OperationNotifier operation.Notifier + StateGen *stategen.State + SlasherAttestationsFeed *event.Feed + SlasherBlockHeadersFeed *event.Feed } // This defines the interface for interacting with block chain service diff --git a/beacon-chain/sync/subscriber_beacon_aggregate_proof_test.go b/beacon-chain/sync/subscriber_beacon_aggregate_proof_test.go index dd61a6b9326b..0c64f9bc69ad 100644 --- a/beacon-chain/sync/subscriber_beacon_aggregate_proof_test.go +++ b/beacon-chain/sync/subscriber_beacon_aggregate_proof_test.go @@ -17,8 +17,8 @@ import ( func TestBeaconAggregateProofSubscriber_CanSaveAggregatedAttestation(t *testing.T) { r := &Service{ cfg: &Config{ - AttPool: attestations.NewPool(), - OperationNotifier: (&mock.ChainService{}).OperationNotifier(), + AttPool: attestations.NewPool(), + AttestationNotifier: (&mock.ChainService{}).OperationNotifier(), }, seenUnAggregatedAttestationCache: lruwrpr.New(10), } @@ -39,8 +39,8 @@ func TestBeaconAggregateProofSubscriber_CanSaveAggregatedAttestation(t *testing. func TestBeaconAggregateProofSubscriber_CanSaveUnaggregatedAttestation(t *testing.T) { r := &Service{ cfg: &Config{ - AttPool: attestations.NewPool(), - OperationNotifier: (&mock.ChainService{}).OperationNotifier(), + AttPool: attestations.NewPool(), + AttestationNotifier: (&mock.ChainService{}).OperationNotifier(), }, seenUnAggregatedAttestationCache: lruwrpr.New(10), } diff --git a/beacon-chain/sync/validate_aggregate_proof.go b/beacon-chain/sync/validate_aggregate_proof.go index 4ae49351d9ae..a2d5a5d22114 100644 --- a/beacon-chain/sync/validate_aggregate_proof.go +++ b/beacon-chain/sync/validate_aggregate_proof.go @@ -59,7 +59,7 @@ func (s *Service) validateAggregateAndProof(ctx context.Context, pid peer.ID, ms // Broadcast the aggregated attestation on a feed to notify other services in the beacon node // of a received aggregated attestation. - s.cfg.OperationNotifier.OperationFeed().Send(&feed.Event{ + s.cfg.AttestationNotifier.OperationFeed().Send(&feed.Event{ Type: operation.AggregatedAttReceived, Data: &operation.AggregatedAttReceivedData{ Attestation: m.Message, diff --git a/beacon-chain/sync/validate_aggregate_proof_test.go b/beacon-chain/sync/validate_aggregate_proof_test.go index a05d1079b6d0..74174c508d83 100644 --- a/beacon-chain/sync/validate_aggregate_proof_test.go +++ b/beacon-chain/sync/validate_aggregate_proof_test.go @@ -189,8 +189,8 @@ func TestValidateAggregateAndProof_NotWithinSlotRange(t *testing.T) { Genesis: time.Now(), State: beaconState, }, - AttPool: attestations.NewPool(), - OperationNotifier: (&mock.ChainService{}).OperationNotifier(), + AttPool: attestations.NewPool(), + AttestationNotifier: (&mock.ChainService{}).OperationNotifier(), }, seenAggregatedAttestationCache: lruwrpr.New(10), } @@ -271,7 +271,7 @@ func TestValidateAggregateAndProof_ExistedInPool(t *testing.T) { InitialSync: &mockSync.Sync{IsSyncing: false}, Chain: &mock.ChainService{Genesis: time.Now(), State: beaconState}, - OperationNotifier: (&mock.ChainService{}).OperationNotifier(), + AttestationNotifier: (&mock.ChainService{}).OperationNotifier(), }, seenAggregatedAttestationCache: lruwrpr.New(10), blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof), @@ -364,8 +364,8 @@ func TestValidateAggregateAndProof_CanValidate(t *testing.T) { Epoch: 0, Root: att.Data.BeaconBlockRoot, }}, - AttPool: attestations.NewPool(), - OperationNotifier: (&mock.ChainService{}).OperationNotifier(), + AttPool: attestations.NewPool(), + AttestationNotifier: (&mock.ChainService{}).OperationNotifier(), }, seenAggregatedAttestationCache: lruwrpr.New(10), } @@ -459,8 +459,8 @@ func TestVerifyIndexInCommittee_SeenAggregatorEpoch(t *testing.T) { Root: signedAggregateAndProof.Message.Aggregate.Data.BeaconBlockRoot, }}, - AttPool: attestations.NewPool(), - OperationNotifier: (&mock.ChainService{}).OperationNotifier(), + AttPool: attestations.NewPool(), + AttestationNotifier: (&mock.ChainService{}).OperationNotifier(), }, seenAggregatedAttestationCache: lruwrpr.New(10), } @@ -570,8 +570,8 @@ func TestValidateAggregateAndProof_BadBlock(t *testing.T) { FinalizedCheckPoint: ðpb.Checkpoint{ Epoch: 0, }}, - AttPool: attestations.NewPool(), - OperationNotifier: (&mock.ChainService{}).OperationNotifier(), + AttPool: attestations.NewPool(), + AttestationNotifier: (&mock.ChainService{}).OperationNotifier(), }, seenAggregatedAttestationCache: lruwrpr.New(10), } @@ -661,8 +661,8 @@ func TestValidateAggregateAndProof_RejectWhenAttEpochDoesntEqualTargetEpoch(t *t Epoch: 0, Root: att.Data.BeaconBlockRoot, }}, - AttPool: attestations.NewPool(), - OperationNotifier: (&mock.ChainService{}).OperationNotifier(), + AttPool: attestations.NewPool(), + AttestationNotifier: (&mock.ChainService{}).OperationNotifier(), }, seenAggregatedAttestationCache: lruwrpr.New(10), } diff --git a/beacon-chain/sync/validate_beacon_attestation.go b/beacon-chain/sync/validate_beacon_attestation.go index 8ae2114749e7..e46cdb667554 100644 --- a/beacon-chain/sync/validate_beacon_attestation.go +++ b/beacon-chain/sync/validate_beacon_attestation.go @@ -21,6 +21,7 @@ import ( "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/monitoring/tracing" eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/attestation" "go.opencensus.io/trace" ) @@ -63,7 +64,7 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p // Broadcast the unaggregated attestation on a feed to notify other services in the beacon node // of a received unaggregated attestation. - s.cfg.OperationNotifier.OperationFeed().Send(&feed.Event{ + s.cfg.AttestationNotifier.OperationFeed().Send(&feed.Event{ Type: operation.UnaggregatedAttReceived, Data: &operation.UnAggregatedAttReceivedData{ Attestation: att, @@ -81,6 +82,35 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p return pubsub.ValidationReject, err } + if features.Get().EnableSlasher { + // Feed the indexed attestation to slasher if enabled. This action + // is done in the background to avoid adding more load to this critical code path. + go func() { + // Using a different context to prevent timeouts as this operation can be expensive + // and we want to avoid affecting the critical code path. + ctx := context.TODO() + preState, err := s.cfg.Chain.AttestationTargetState(ctx, att.Data.Target) + if err != nil { + log.WithError(err).Error("Could not retrieve pre state") + tracing.AnnotateError(span, err) + return + } + committee, err := helpers.BeaconCommitteeFromState(ctx, preState, att.Data.Slot, att.Data.CommitteeIndex) + if err != nil { + log.WithError(err).Error("Could not get attestation committee") + tracing.AnnotateError(span, err) + return + } + indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committee) + if err != nil { + log.WithError(err).Error("Could not convert to indexed attestation") + tracing.AnnotateError(span, err) + return + } + s.cfg.SlasherAttestationsFeed.Send(indexedAtt) + }() + } + // Verify this the first attestation received for the participating validator for the slot. if s.hasSeenCommitteeIndicesSlot(att.Data.Slot, att.Data.CommitteeIndex, att.AggregationBits) { return pubsub.ValidationIgnore, nil diff --git a/beacon-chain/sync/validate_beacon_attestation_test.go b/beacon-chain/sync/validate_beacon_attestation_test.go index c3fc6e51f5bb..5404520fbd42 100644 --- a/beacon-chain/sync/validate_beacon_attestation_test.go +++ b/beacon-chain/sync/validate_beacon_attestation_test.go @@ -38,11 +38,11 @@ func TestService_validateCommitteeIndexBeaconAttestation(t *testing.T) { s := &Service{ cfg: &Config{ - InitialSync: &mockSync.Sync{IsSyncing: false}, - P2P: p, - DB: db, - Chain: chain, - OperationNotifier: (&mockChain.ChainService{}).OperationNotifier(), + InitialSync: &mockSync.Sync{IsSyncing: false}, + P2P: p, + DB: db, + Chain: chain, + AttestationNotifier: (&mockChain.ChainService{}).OperationNotifier(), }, blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof), seenUnAggregatedAttestationCache: lruwrpr.New(10), diff --git a/beacon-chain/sync/validate_beacon_blocks.go b/beacon-chain/sync/validate_beacon_blocks.go index c61b4b43b0c6..ff25db245d19 100644 --- a/beacon-chain/sync/validate_beacon_blocks.go +++ b/beacon-chain/sync/validate_beacon_blocks.go @@ -15,6 +15,7 @@ import ( blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/core/transition" + "github.com/prysmaticlabs/prysm/config/features" "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/monitoring/tracing" @@ -70,6 +71,18 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms }, }) + if features.Get().EnableSlasher { + // Feed the block header to slasher if enabled. This action + // is done in the background to avoid adding more load to this critical code path. + go func() { + blockHeader, err := block.SignedBeaconBlockHeaderFromBlockInterface(blk) + if err != nil { + log.WithError(err).WithField("blockSlot", blk.Block().Slot()).Warn("Could not extract block header") + } + s.cfg.SlasherBlockHeadersFeed.Send(blockHeader) + }() + } + // Verify the block is the first block received for the proposer for the slot. if s.hasSeenBlockIndexSlot(blk.Block().Slot(), blk.Block().ProposerIndex()) { return pubsub.ValidationIgnore, nil @@ -149,6 +162,7 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms if err := s.validateBeaconBlock(ctx, blk, blockRoot); err != nil { return pubsub.ValidationReject, err } + // Record attribute of valid block. span.AddAttributes(trace.Int64Attribute("slotInEpoch", int64(blk.Block().Slot()%params.BeaconConfig().SlotsPerEpoch))) msg.ValidatorData = blk.Proto() // Used in downstream subscriber diff --git a/config/features/config.go b/config/features/config.go index a184f6d04cd2..486d85462342 100644 --- a/config/features/config.go +++ b/config/features/config.go @@ -39,9 +39,9 @@ type Flags struct { PyrmontTestnet bool // PyrmontTestnet defines the flag through which we can enable the node to run on the Pyrmont testnet. // Feature related flags. + RemoteSlasherProtection bool // RemoteSlasherProtection utilizes a beacon node with --slasher mode for validator slashing protection. WriteSSZStateTransitions bool // WriteSSZStateTransitions to tmp directory. SkipBLSVerify bool // Skips BLS verification across the runtime. - SlasherProtection bool // SlasherProtection protects validator fron sending over a slashable offense over the network using external slasher. EnablePeerScorer bool // EnablePeerScorer enables experimental peer scoring in p2p. EnableLargerGossipHistory bool // EnableLargerGossipHistory increases the gossip history we store in our caches. WriteWalletPasswordOnWebOnboarding bool // WriteWalletPasswordOnWebOnboarding writes the password to disk after Prysm web signup. @@ -66,6 +66,7 @@ type Flags struct { // Bug fixes related flags. AttestTimely bool // AttestTimely fixes #8185. It is gated behind a flag to ensure beacon node's fix can safely roll out first. We'll invert this in v1.1.0. + EnableSlasher bool // Enable slasher in the beacon node runtime. // EnableSlashingProtectionPruning for the validator client. EnableSlashingProtectionPruning bool @@ -180,6 +181,10 @@ func ConfigureBeaconChain(ctx *cli.Context) { logDisabled(disableNextSlotStateCache) cfg.EnableNextSlotStateCache = false } + if ctx.Bool(enableSlasherFlag.Name) { + log.WithField(enableSlasherFlag.Name, enableSlasherFlag.Usage).Warn(enabledFeatureFlag) + cfg.EnableSlasher = true + } cfg.ProposerAttsSelectionUsingMaxCover = true if ctx.Bool(disableProposerAttsSelectionUsingMaxCover.Name) { logDisabled(disableProposerAttsSelectionUsingMaxCover) @@ -223,8 +228,8 @@ func ConfigureValidator(ctx *cli.Context) { cfg := &Flags{} configureTestnet(ctx, cfg) if ctx.Bool(enableExternalSlasherProtectionFlag.Name) { - logEnabled(enableExternalSlasherProtectionFlag) - cfg.SlasherProtection = true + log.WithField(enableExternalSlasherProtectionFlag.Name, enableExternalSlasherProtectionFlag.Usage).Warn(enabledFeatureFlag) + cfg.RemoteSlasherProtection = true } if ctx.Bool(writeWalletPasswordOnWebOnboarding.Name) { logEnabled(writeWalletPasswordOnWebOnboarding) diff --git a/config/features/flags.go b/config/features/flags.go index e3b77d9a0457..283f335b831e 100644 --- a/config/features/flags.go +++ b/config/features/flags.go @@ -33,8 +33,8 @@ var ( } enableExternalSlasherProtectionFlag = &cli.BoolFlag{ Name: "enable-external-slasher-protection", - Usage: "Enables the validator to connect to external slasher to prevent it from " + - "transmitting a slashable offence over the network.", + Usage: "Enables the validator to connect to a beacon node using the --slasher flag" + + "for remote slashing protection", } disableLookbackFlag = &cli.BoolFlag{ Name: "disable-lookback", @@ -93,6 +93,10 @@ var ( Name: "disable-next-slot-state-cache", Usage: "Disable next slot cache which improves attesting and proposing efficiency by caching the next slot state at the end of the current slot", } + enableSlasherFlag = &cli.BoolFlag{ + Name: "slasher", + Usage: "Enables a slasher in the beacon node for detecting slashable offenses", + } disableProposerAttsSelectionUsingMaxCover = &cli.BoolFlag{ Name: "disable-proposer-atts-selection-using-max-cover", Usage: "Disable max-cover algorithm when selecting attestations for proposer", @@ -178,6 +182,7 @@ var BeaconChainFlags = append(deprecatedFlags, []cli.Flag{ disableBroadcastSlashingFlag, disableNextSlotStateCache, forceOptMaxCoverAggregationStategy, + enableSlasherFlag, disableProposerAttsSelectionUsingMaxCover, disableOptimizedBalanceUpdate, enableHistoricalSpaceRepresentation, diff --git a/proto/prysm/v1alpha1/slasher.pb.go b/proto/prysm/v1alpha1/slasher.pb.go index 35f0e79f4b76..8df26646901d 100755 --- a/proto/prysm/v1alpha1/slasher.pb.go +++ b/proto/prysm/v1alpha1/slasher.pb.go @@ -541,7 +541,7 @@ var file_proto_prysm_v1alpha1_slasher_proto_rawDesc = []byte{ 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x32, 0xf1, 0x05, 0x0a, 0x07, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x72, 0x12, + 0x02, 0x38, 0x01, 0x32, 0x90, 0x04, 0x0a, 0x07, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x72, 0x12, 0xad, 0x01, 0x0a, 0x16, 0x49, 0x73, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, @@ -563,42 +563,28 @@ var file_proto_prysm_v1alpha1_slasher_proto_rawDesc = []byte{ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x73, 0x6c, 0x61, 0x73, - 0x68, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x72, 0x0a, 0x1e, 0x49, 0x73, 0x53, 0x6c, 0x61, 0x73, 0x68, - 0x61, 0x62, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, - 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x1a, 0x20, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x6c, 0x61, 0x73, 0x68, - 0x61, 0x62, 0x6c, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x6b, 0x0a, 0x18, 0x49, 0x73, 0x53, - 0x6c, 0x61, 0x73, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x6f, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, - 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x1a, - 0x20, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x61, 0x62, 0x6c, - 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0xae, 0x01, 0x0a, 0x13, 0x48, 0x69, 0x67, 0x68, 0x65, - 0x73, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x65, 0x73, 0x74, 0x41, 0x74, - 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x65, 0x73, 0x74, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x12, 0x2a, 0x2f, 0x65, 0x74, - 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68, - 0x65, 0x72, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, - 0x68, 0x69, 0x67, 0x68, 0x65, 0x73, 0x74, 0x42, 0x94, 0x01, 0x0a, 0x19, 0x6f, 0x72, 0x67, 0x2e, + 0x68, 0x61, 0x62, 0x6c, 0x65, 0x12, 0xae, 0x01, 0x0a, 0x13, 0x48, 0x69, 0x67, 0x68, 0x65, 0x73, + 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x72, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, - 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, - 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, - 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, - 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x65, 0x73, 0x74, 0x41, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x48, 0x69, 0x67, 0x68, 0x65, 0x73, 0x74, 0x41, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x12, 0x2a, 0x2f, 0x65, 0x74, 0x68, + 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x65, + 0x72, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x68, + 0x69, 0x67, 0x68, 0x65, 0x73, 0x74, 0x42, 0x94, 0x01, 0x0a, 0x19, 0x6f, 0x72, 0x67, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x42, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x72, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, + 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, + 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x15, + 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -628,7 +614,6 @@ var file_proto_prysm_v1alpha1_slasher_proto_goTypes = []interface{}{ (*ProposerSlashing)(nil), // 10: ethereum.eth.v1alpha1.ProposerSlashing (*IndexedAttestation)(nil), // 11: ethereum.eth.v1alpha1.IndexedAttestation (*SignedBeaconBlockHeader)(nil), // 12: ethereum.eth.v1alpha1.SignedBeaconBlockHeader - (*BeaconBlockHeader)(nil), // 13: ethereum.eth.v1alpha1.BeaconBlockHeader } var file_proto_prysm_v1alpha1_slasher_proto_depIdxs = []int32{ 9, // 0: ethereum.eth.v1alpha1.AttesterSlashingResponse.attester_slashings:type_name -> ethereum.eth.v1alpha1.AttesterSlashing @@ -637,16 +622,12 @@ var file_proto_prysm_v1alpha1_slasher_proto_depIdxs = []int32{ 8, // 3: ethereum.eth.v1alpha1.AttestationHistory.target_to_source:type_name -> ethereum.eth.v1alpha1.AttestationHistory.TargetToSourceEntry 11, // 4: ethereum.eth.v1alpha1.Slasher.IsSlashableAttestation:input_type -> ethereum.eth.v1alpha1.IndexedAttestation 12, // 5: ethereum.eth.v1alpha1.Slasher.IsSlashableBlock:input_type -> ethereum.eth.v1alpha1.SignedBeaconBlockHeader - 11, // 6: ethereum.eth.v1alpha1.Slasher.IsSlashableAttestationNoUpdate:input_type -> ethereum.eth.v1alpha1.IndexedAttestation - 13, // 7: ethereum.eth.v1alpha1.Slasher.IsSlashableBlockNoUpdate:input_type -> ethereum.eth.v1alpha1.BeaconBlockHeader - 2, // 8: ethereum.eth.v1alpha1.Slasher.HighestAttestations:input_type -> ethereum.eth.v1alpha1.HighestAttestationRequest - 0, // 9: ethereum.eth.v1alpha1.Slasher.IsSlashableAttestation:output_type -> ethereum.eth.v1alpha1.AttesterSlashingResponse - 1, // 10: ethereum.eth.v1alpha1.Slasher.IsSlashableBlock:output_type -> ethereum.eth.v1alpha1.ProposerSlashingResponse - 6, // 11: ethereum.eth.v1alpha1.Slasher.IsSlashableAttestationNoUpdate:output_type -> ethereum.eth.v1alpha1.Slashable - 6, // 12: ethereum.eth.v1alpha1.Slasher.IsSlashableBlockNoUpdate:output_type -> ethereum.eth.v1alpha1.Slashable - 3, // 13: ethereum.eth.v1alpha1.Slasher.HighestAttestations:output_type -> ethereum.eth.v1alpha1.HighestAttestationResponse - 9, // [9:14] is the sub-list for method output_type - 4, // [4:9] is the sub-list for method input_type + 2, // 6: ethereum.eth.v1alpha1.Slasher.HighestAttestations:input_type -> ethereum.eth.v1alpha1.HighestAttestationRequest + 0, // 7: ethereum.eth.v1alpha1.Slasher.IsSlashableAttestation:output_type -> ethereum.eth.v1alpha1.AttesterSlashingResponse + 1, // 8: ethereum.eth.v1alpha1.Slasher.IsSlashableBlock:output_type -> ethereum.eth.v1alpha1.ProposerSlashingResponse + 3, // 9: ethereum.eth.v1alpha1.Slasher.HighestAttestations:output_type -> ethereum.eth.v1alpha1.HighestAttestationResponse + 7, // [7:10] is the sub-list for method output_type + 4, // [4:7] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name @@ -790,10 +771,6 @@ const _ = grpc.SupportPackageIsVersion6 type SlasherClient interface { IsSlashableAttestation(ctx context.Context, in *IndexedAttestation, opts ...grpc.CallOption) (*AttesterSlashingResponse, error) IsSlashableBlock(ctx context.Context, in *SignedBeaconBlockHeader, opts ...grpc.CallOption) (*ProposerSlashingResponse, error) - // Deprecated: Do not use. - IsSlashableAttestationNoUpdate(ctx context.Context, in *IndexedAttestation, opts ...grpc.CallOption) (*Slashable, error) - // Deprecated: Do not use. - IsSlashableBlockNoUpdate(ctx context.Context, in *BeaconBlockHeader, opts ...grpc.CallOption) (*Slashable, error) HighestAttestations(ctx context.Context, in *HighestAttestationRequest, opts ...grpc.CallOption) (*HighestAttestationResponse, error) } @@ -823,26 +800,6 @@ func (c *slasherClient) IsSlashableBlock(ctx context.Context, in *SignedBeaconBl return out, nil } -// Deprecated: Do not use. -func (c *slasherClient) IsSlashableAttestationNoUpdate(ctx context.Context, in *IndexedAttestation, opts ...grpc.CallOption) (*Slashable, error) { - out := new(Slashable) - err := c.cc.Invoke(ctx, "/ethereum.eth.v1alpha1.Slasher/IsSlashableAttestationNoUpdate", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Deprecated: Do not use. -func (c *slasherClient) IsSlashableBlockNoUpdate(ctx context.Context, in *BeaconBlockHeader, opts ...grpc.CallOption) (*Slashable, error) { - out := new(Slashable) - err := c.cc.Invoke(ctx, "/ethereum.eth.v1alpha1.Slasher/IsSlashableBlockNoUpdate", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *slasherClient) HighestAttestations(ctx context.Context, in *HighestAttestationRequest, opts ...grpc.CallOption) (*HighestAttestationResponse, error) { out := new(HighestAttestationResponse) err := c.cc.Invoke(ctx, "/ethereum.eth.v1alpha1.Slasher/HighestAttestations", in, out, opts...) @@ -856,10 +813,6 @@ func (c *slasherClient) HighestAttestations(ctx context.Context, in *HighestAtte type SlasherServer interface { IsSlashableAttestation(context.Context, *IndexedAttestation) (*AttesterSlashingResponse, error) IsSlashableBlock(context.Context, *SignedBeaconBlockHeader) (*ProposerSlashingResponse, error) - // Deprecated: Do not use. - IsSlashableAttestationNoUpdate(context.Context, *IndexedAttestation) (*Slashable, error) - // Deprecated: Do not use. - IsSlashableBlockNoUpdate(context.Context, *BeaconBlockHeader) (*Slashable, error) HighestAttestations(context.Context, *HighestAttestationRequest) (*HighestAttestationResponse, error) } @@ -873,12 +826,6 @@ func (*UnimplementedSlasherServer) IsSlashableAttestation(context.Context, *Inde func (*UnimplementedSlasherServer) IsSlashableBlock(context.Context, *SignedBeaconBlockHeader) (*ProposerSlashingResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method IsSlashableBlock not implemented") } -func (*UnimplementedSlasherServer) IsSlashableAttestationNoUpdate(context.Context, *IndexedAttestation) (*Slashable, error) { - return nil, status.Errorf(codes.Unimplemented, "method IsSlashableAttestationNoUpdate not implemented") -} -func (*UnimplementedSlasherServer) IsSlashableBlockNoUpdate(context.Context, *BeaconBlockHeader) (*Slashable, error) { - return nil, status.Errorf(codes.Unimplemented, "method IsSlashableBlockNoUpdate not implemented") -} func (*UnimplementedSlasherServer) HighestAttestations(context.Context, *HighestAttestationRequest) (*HighestAttestationResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method HighestAttestations not implemented") } @@ -923,42 +870,6 @@ func _Slasher_IsSlashableBlock_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } -func _Slasher_IsSlashableAttestationNoUpdate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(IndexedAttestation) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SlasherServer).IsSlashableAttestationNoUpdate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/ethereum.eth.v1alpha1.Slasher/IsSlashableAttestationNoUpdate", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SlasherServer).IsSlashableAttestationNoUpdate(ctx, req.(*IndexedAttestation)) - } - return interceptor(ctx, in, info, handler) -} - -func _Slasher_IsSlashableBlockNoUpdate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(BeaconBlockHeader) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SlasherServer).IsSlashableBlockNoUpdate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/ethereum.eth.v1alpha1.Slasher/IsSlashableBlockNoUpdate", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SlasherServer).IsSlashableBlockNoUpdate(ctx, req.(*BeaconBlockHeader)) - } - return interceptor(ctx, in, info, handler) -} - func _Slasher_HighestAttestations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(HighestAttestationRequest) if err := dec(in); err != nil { @@ -989,14 +900,6 @@ var _Slasher_serviceDesc = grpc.ServiceDesc{ MethodName: "IsSlashableBlock", Handler: _Slasher_IsSlashableBlock_Handler, }, - { - MethodName: "IsSlashableAttestationNoUpdate", - Handler: _Slasher_IsSlashableAttestationNoUpdate_Handler, - }, - { - MethodName: "IsSlashableBlockNoUpdate", - Handler: _Slasher_IsSlashableBlockNoUpdate_Handler, - }, { MethodName: "HighestAttestations", Handler: _Slasher_HighestAttestations_Handler, diff --git a/proto/prysm/v1alpha1/slasher.proto b/proto/prysm/v1alpha1/slasher.proto index 1779c42e2fee..4bc3620bb125 100644 --- a/proto/prysm/v1alpha1/slasher.proto +++ b/proto/prysm/v1alpha1/slasher.proto @@ -49,22 +49,6 @@ service Slasher { }; } - // Returns if a given indexed attestation could be slashable when compared to - // the slashers history for the attesters. This function is read-only, and - // does not need the indexed attestation to be signed. - rpc IsSlashableAttestationNoUpdate(ethereum.eth.v1alpha1.IndexedAttestation) - returns (Slashable) { - option deprecated = true; - }; - - // Returns if a given beacon block header could be slashable when compared to - // the slashers history for the proposer. This function is read-only, and does - // not need the beacon block header to be signed. - rpc IsSlashableBlockNoUpdate(ethereum.eth.v1alpha1.BeaconBlockHeader) - returns (Slashable) { - option deprecated = true; - }; - // Returns the highest source and target attestation for validator indices // that have been observed by slasher. rpc HighestAttestations(HighestAttestationRequest) diff --git a/validator/client/attest_protect.go b/validator/client/attest_protect.go index 2bb148571db8..7763c706dfcc 100644 --- a/validator/client/attest_protect.go +++ b/validator/client/attest_protect.go @@ -81,8 +81,8 @@ func (v *validator) slashableAttestationCheck( return errors.Wrap(err, "could not save attestation history for validator public key") } - if features.Get().SlasherProtection && v.protector != nil { - if !v.protector.CommitAttestation(ctx, indexedAtt) { + if features.Get().RemoteSlasherProtection && v.protector != nil { + if !v.protector.CheckAttestationSafety(ctx, indexedAtt) { if v.emitAccountMetrics { ValidatorAttestFailVecSlasher.WithLabelValues(fmtKey).Inc() } diff --git a/validator/client/attest_protect_test.go b/validator/client/attest_protect_test.go index 9398bf25ccec..d5d6b07b5aa5 100644 --- a/validator/client/attest_protect_test.go +++ b/validator/client/attest_protect_test.go @@ -15,7 +15,7 @@ import ( func Test_slashableAttestationCheck(t *testing.T) { config := &features.Flags{ - SlasherProtection: true, + RemoteSlasherProtection: true, } reset := features.InitWithReset(config) defer reset() @@ -50,7 +50,7 @@ func Test_slashableAttestationCheck(t *testing.T) { func Test_slashableAttestationCheck_UpdatesLowestSignedEpochs(t *testing.T) { config := &features.Flags{ - SlasherProtection: true, + RemoteSlasherProtection: true, } reset := features.InitWithReset(config) defer reset() @@ -102,7 +102,7 @@ func Test_slashableAttestationCheck_UpdatesLowestSignedEpochs(t *testing.T) { func Test_slashableAttestationCheck_OK(t *testing.T) { config := &features.Flags{ - SlasherProtection: false, + RemoteSlasherProtection: false, } reset := features.InitWithReset(config) defer reset() @@ -133,7 +133,7 @@ func Test_slashableAttestationCheck_OK(t *testing.T) { func Test_slashableAttestationCheck_GenesisEpoch(t *testing.T) { config := &features.Flags{ - SlasherProtection: false, + RemoteSlasherProtection: false, } reset := features.InitWithReset(config) defer reset() diff --git a/validator/client/propose_protect.go b/validator/client/propose_protect.go index 0624c36265ae..1c742285fbd1 100644 --- a/validator/client/propose_protect.go +++ b/validator/client/propose_protect.go @@ -56,20 +56,6 @@ func (v *validator) preBlockSignValidations( blk.Slot(), ) } - - if features.Get().SlasherProtection && v.protector != nil { - blockHdr, err := block.BeaconBlockHeaderFromBlockInterface(blk) - if err != nil { - return errors.Wrap(err, "failed to get block header from block") - } - if !v.protector.CheckBlockSafety(ctx, blockHdr) { - if v.emitAccountMetrics { - ValidatorProposeFailVecSlasher.WithLabelValues(fmtKey).Inc() - } - return errors.New(failedPreBlockSignExternalErr) - } - } - return nil } @@ -80,20 +66,16 @@ func (v *validator) postBlockSignUpdate( signingRoot [32]byte, ) error { fmtKey := fmt.Sprintf("%#x", pubKey[:]) - if features.Get().SlasherProtection && v.protector != nil { - sbh, err := block.SignedBeaconBlockHeaderFromBlockInterface(blk) + if features.Get().RemoteSlasherProtection && v.protector != nil { + blockHdr, err := block.SignedBeaconBlockHeaderFromBlockInterface(blk) if err != nil { return errors.Wrap(err, "failed to get block header from block") } - valid, err := v.protector.CommitBlock(ctx, sbh) - if err != nil { - return err - } - if !valid { + if !v.protector.CheckBlockSafety(ctx, blockHdr) { if v.emitAccountMetrics { ValidatorProposeFailVecSlasher.WithLabelValues(fmtKey).Inc() } - return fmt.Errorf(failedPostBlockSignErr) + return errors.New(failedPostBlockSignErr) } } if err := v.db.SaveProposalHistoryForSlot(ctx, pubKey, blk.Block().Slot(), signingRoot[:]); err != nil { diff --git a/validator/client/propose_protect_test.go b/validator/client/propose_protect_test.go index 53464b143012..05bb0aa28a5c 100644 --- a/validator/client/propose_protect_test.go +++ b/validator/client/propose_protect_test.go @@ -66,7 +66,7 @@ func TestPreBlockSignLocalValidation_PreventsLowerThanMinProposal(t *testing.T) func TestPreBlockSignLocalValidation(t *testing.T) { ctx := context.Background() config := &features.Flags{ - SlasherProtection: false, + RemoteSlasherProtection: false, } reset := features.InitWithReset(config) defer reset() @@ -117,11 +117,6 @@ func TestPreBlockSignLocalValidation(t *testing.T) { } func TestPreBlockSignValidation(t *testing.T) { - config := &features.Flags{ - SlasherProtection: true, - } - reset := features.InitWithReset(config) - defer reset() validator, _, validatorKey, finish := setup(t) defer finish() pubKey := [48]byte{} @@ -131,16 +126,14 @@ func TestPreBlockSignValidation(t *testing.T) { block.Block.Slot = 10 mockProtector := &mockSlasher.MockProtector{AllowBlock: false} validator.protector = mockProtector - err := validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block.Block), [32]byte{2}) - require.ErrorContains(t, failedPreBlockSignExternalErr, err) mockProtector.AllowBlock = true - err = validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block.Block), [32]byte{2}) + err := validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block.Block), [32]byte{2}) require.NoError(t, err, "Expected allowed block not to throw error") } func TestPostBlockSignUpdate(t *testing.T) { config := &features.Flags{ - SlasherProtection: true, + RemoteSlasherProtection: true, } reset := features.InitWithReset(config) defer reset() diff --git a/validator/client/runner.go b/validator/client/runner.go index 5d82f6b4365b..6944cbcbd197 100644 --- a/validator/client/runner.go +++ b/validator/client/runner.go @@ -40,7 +40,7 @@ func run(ctx context.Context, v iface.Validator) { cleanup() log.Fatalf("Wallet is not ready: %v", err) } - if features.Get().SlasherProtection { + if features.Get().RemoteSlasherProtection { if err := v.SlasherReady(ctx); err != nil { log.Fatalf("Slasher is not ready: %v", err) } diff --git a/validator/client/runner_test.go b/validator/client/runner_test.go index 682fad7b88a9..6abe12d9860f 100644 --- a/validator/client/runner_test.go +++ b/validator/client/runner_test.go @@ -65,7 +65,7 @@ func TestCancelledContext_WaitsForActivation(t *testing.T) { func TestCancelledContext_ChecksSlasherReady(t *testing.T) { v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} cfg := &features.Flags{ - SlasherProtection: true, + RemoteSlasherProtection: true, } reset := features.InitWithReset(cfg) defer reset() diff --git a/validator/client/slashing_protection_interchange_test.go b/validator/client/slashing_protection_interchange_test.go index 2bde13bab759..a5ae48b78955 100644 --- a/validator/client/slashing_protection_interchange_test.go +++ b/validator/client/slashing_protection_interchange_test.go @@ -77,7 +77,7 @@ func setupEIP3076SpecTests(t *testing.T) []*eip3076TestCase { func TestEIP3076SpecTests(t *testing.T) { config := &features.Flags{ - SlasherProtection: true, + RemoteSlasherProtection: true, } reset := features.InitWithReset(config) defer reset() diff --git a/validator/client/validator.go b/validator/client/validator.go index eca9d3efc9bc..275370ebd3dd 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -229,7 +229,7 @@ func (v *validator) WaitForSync(ctx context.Context) error { func (v *validator) SlasherReady(ctx context.Context) error { ctx, span := trace.StartSpan(ctx, "validator.SlasherReady") defer span.End() - if features.Get().SlasherProtection { + if features.Get().RemoteSlasherProtection { err := v.protector.Status() if err == nil { return nil diff --git a/validator/db/kv/prune_attester_protection_test.go b/validator/db/kv/prune_attester_protection_test.go index fefbcba5d0b6..3d206374e374 100644 --- a/validator/db/kv/prune_attester_protection_test.go +++ b/validator/db/kv/prune_attester_protection_test.go @@ -39,7 +39,7 @@ func TestPruneAttestations_NoPruning(t *testing.T) { } func TestPruneAttestations_OK(t *testing.T) { - numKeys := uint64(2048) + numKeys := uint64(64) pks := make([][48]byte, 0, numKeys) for i := uint64(0); i < numKeys; i++ { pks = append(pks, bytesutil.ToBytes48(bytesutil.ToBytes(i, 48))) diff --git a/validator/node/node.go b/validator/node/node.go index ac83a39c5c82..5377904f0a92 100644 --- a/validator/node/node.go +++ b/validator/node/node.go @@ -251,10 +251,8 @@ func (c *ValidatorClient) initializeFromCLI(cliCtx *cli.Context) error { return err } } - if features.Get().SlasherProtection { - if err := c.registerSlasherService(); err != nil { - return err - } + if err := c.registerSlasherService(); err != nil { + return err } if err := c.registerValidatorService(keyManager); err != nil { return err @@ -340,10 +338,8 @@ func (c *ValidatorClient) initializeForWeb(cliCtx *cli.Context) error { return err } } - if features.Get().SlasherProtection { - if err := c.registerSlasherService(); err != nil { - return err - } + if err := c.registerSlasherService(); err != nil { + return err } if err := c.registerValidatorService(keyManager); err != nil { return err @@ -437,6 +433,9 @@ func (c *ValidatorClient) registerValidatorService( return c.services.RegisterService(v) } func (c *ValidatorClient) registerSlasherService() error { + if !features.Get().RemoteSlasherProtection { + return nil + } endpoint := c.cliCtx.String(flags.SlasherRPCProviderFlag.Name) if endpoint == "" { return errors.New("external slasher feature flag is set but no slasher endpoint is configured") diff --git a/validator/slashing-protection/BUILD.bazel b/validator/slashing-protection/BUILD.bazel index d6a83a4ebfb7..91e3e492d0de 100644 --- a/validator/slashing-protection/BUILD.bazel +++ b/validator/slashing-protection/BUILD.bazel @@ -48,6 +48,7 @@ go_test( deps = [ "//cmd:go_default_library", "//cmd/validator/flags:go_default_library", + "//config/params:go_default_library", "//encoding/bytesutil:go_default_library", "//io/file:go_default_library", "//proto/prysm/v1alpha1:go_default_library", diff --git a/validator/slashing-protection/external.go b/validator/slashing-protection/external.go index 437c7e9fc9b1..053a890cc4a8 100644 --- a/validator/slashing-protection/external.go +++ b/validator/slashing-protection/external.go @@ -6,52 +6,22 @@ import ( ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" ) -// CheckBlockSafety this function is part of slashing protection for block proposals it performs -// validation without db update. To be used before the block is signed. -func (s *Service) CheckBlockSafety(ctx context.Context, blockHeader *ethpb.BeaconBlockHeader) bool { - slashable, err := s.slasherClient.IsSlashableBlockNoUpdate(ctx, blockHeader) - if err != nil { - log.Errorf("External slashing block protection returned an error: %v", err) - return false - } - if slashable != nil && slashable.Slashable { - log.Warn("External slashing proposal protection found the block to be slashable") - } - return !slashable.Slashable -} - -// CommitBlock this function is part of slashing protection for block proposals it performs -// validation and db update. To be used after the block is proposed. -func (s *Service) CommitBlock(ctx context.Context, blockHeader *ethpb.SignedBeaconBlockHeader) (bool, error) { +// CheckBlockSafety for blocks before submitting them to the node. +func (s *Service) CheckBlockSafety(ctx context.Context, blockHeader *ethpb.SignedBeaconBlockHeader) bool { ps, err := s.slasherClient.IsSlashableBlock(ctx, blockHeader) if err != nil { log.Errorf("External slashing block protection returned an error: %v", err) - return false, err + return false } if ps != nil && len(ps.ProposerSlashings) != 0 { log.Warn("External slashing proposal protection found the block to be slashable") - return false, nil - } - return true, nil -} - -// CheckAttestationSafety implements the slashing protection for attestations without db update. -// To be used before signing. -func (s *Service) CheckAttestationSafety(ctx context.Context, attestation *ethpb.IndexedAttestation) bool { - slashable, err := s.slasherClient.IsSlashableAttestationNoUpdate(ctx, attestation) - if err != nil { - log.Errorf("External slashing attestation protection returned an error: %v", err) return false } - if slashable.Slashable { - log.Warn("External slashing attestation protection found the attestation to be slashable") - } - return !slashable.Slashable + return true } -// CommitAttestation implements the slashing protection for attestations it performs -// validation and db update. To be used after the attestation is proposed. -func (s *Service) CommitAttestation(ctx context.Context, attestation *ethpb.IndexedAttestation) bool { +// CheckAttestationSafety for attestations before submitting them to the node. +func (s *Service) CheckAttestationSafety(ctx context.Context, attestation *ethpb.IndexedAttestation) bool { as, err := s.slasherClient.IsSlashableAttestation(ctx, attestation) if err != nil { log.Errorf("External slashing attestation protection returned an error: %v", err) diff --git a/validator/slashing-protection/external_test.go b/validator/slashing-protection/external_test.go index ead8f71cc228..d8ee72d2cdfe 100644 --- a/validator/slashing-protection/external_test.go +++ b/validator/slashing-protection/external_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/encoding/bytesutil" eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/testing/assert" @@ -33,49 +34,6 @@ func TestService_VerifyAttestation(t *testing.T) { assert.Equal(t, true, s.CheckAttestationSafety(context.Background(), att), "Expected verify attestation to pass verification") } -func TestService_CommitAttestation(t *testing.T) { - s := &Service{slasherClient: mockSlasher.MockSlasher{SlashAttestation: true}} - att := ð.IndexedAttestation{ - AttestingIndices: []uint64{1, 2}, - Data: ð.AttestationData{ - Slot: 5, - CommitteeIndex: 2, - BeaconBlockRoot: []byte("great block"), - Source: ð.Checkpoint{ - Epoch: 4, - Root: []byte("good source"), - }, - Target: ð.Checkpoint{ - Epoch: 10, - Root: []byte("good target"), - }, - }, - } - assert.Equal(t, false, s.CommitAttestation(context.Background(), att), "Expected commit attestation to fail verification") - s = &Service{slasherClient: mockSlasher.MockSlasher{SlashAttestation: false}} - assert.Equal(t, true, s.CommitAttestation(context.Background(), att), "Expected commit attestation to pass verification") -} - -func TestService_CommitBlock(t *testing.T) { - s := &Service{slasherClient: mockSlasher.MockSlasher{SlashBlock: true}} - blk := ð.SignedBeaconBlockHeader{ - Header: ð.BeaconBlockHeader{ - Slot: 0, - ProposerIndex: 0, - ParentRoot: bytesutil.PadTo([]byte("parent"), 32), - StateRoot: bytesutil.PadTo([]byte("state"), 32), - BodyRoot: bytesutil.PadTo([]byte("body"), 32), - }, - } - slashable, err := s.CommitBlock(context.Background(), blk) - assert.NoError(t, err) - assert.Equal(t, false, slashable, "Expected commit block to fail verification") - s = &Service{slasherClient: mockSlasher.MockSlasher{SlashBlock: false}} - slashable, err = s.CommitBlock(context.Background(), blk) - assert.NoError(t, err) - assert.Equal(t, true, slashable, "Expected commit block to pass verification") -} - func TestService_VerifyBlock(t *testing.T) { s := &Service{slasherClient: mockSlasher.MockSlasher{SlashBlock: true}} blk := ð.BeaconBlockHeader{ @@ -85,7 +43,8 @@ func TestService_VerifyBlock(t *testing.T) { StateRoot: bytesutil.PadTo([]byte("state"), 32), BodyRoot: bytesutil.PadTo([]byte("body"), 32), } - assert.Equal(t, false, s.CheckBlockSafety(context.Background(), blk), "Expected verify block to fail verification") + sblk := ð.SignedBeaconBlockHeader{Header: blk, Signature: params.BeaconConfig().EmptySignature[:]} + assert.Equal(t, false, s.CheckBlockSafety(context.Background(), sblk), "Expected verify block to fail verification") s = &Service{slasherClient: mockSlasher.MockSlasher{SlashBlock: false}} - assert.Equal(t, true, s.CheckBlockSafety(context.Background(), blk), "Expected verify block to pass verification") + assert.Equal(t, true, s.CheckBlockSafety(context.Background(), sblk), "Expected verify block to pass verification") } diff --git a/validator/slashing-protection/iface/protector.go b/validator/slashing-protection/iface/protector.go index 2719b112136e..9c9844cd4dfc 100644 --- a/validator/slashing-protection/iface/protector.go +++ b/validator/slashing-protection/iface/protector.go @@ -9,8 +9,6 @@ import ( // Protector interface defines the methods of the service that provides slashing protection. type Protector interface { CheckAttestationSafety(ctx context.Context, attestation *eth.IndexedAttestation) bool - CommitAttestation(ctx context.Context, attestation *eth.IndexedAttestation) bool - CheckBlockSafety(ctx context.Context, blockHeader *eth.BeaconBlockHeader) bool - CommitBlock(ctx context.Context, blockHeader *eth.SignedBeaconBlockHeader) (bool, error) + CheckBlockSafety(ctx context.Context, blockHeader *eth.SignedBeaconBlockHeader) bool Status() error } diff --git a/validator/testing/mock_protector.go b/validator/testing/mock_protector.go index bef63bacdea0..a491a8461198 100644 --- a/validator/testing/mock_protector.go +++ b/validator/testing/mock_protector.go @@ -11,9 +11,7 @@ type MockProtector struct { AllowAttestation bool AllowBlock bool VerifyAttestationCalled bool - CommitAttestationCalled bool VerifyBlockCalled bool - CommitBlockCalled bool StatusCalled bool } @@ -23,24 +21,12 @@ func (mp MockProtector) CheckAttestationSafety(_ context.Context, _ *eth.Indexed return mp.AllowAttestation } -// CommitAttestation returns bool with allow attestation value. -func (mp MockProtector) CommitAttestation(_ context.Context, _ *eth.IndexedAttestation) bool { - mp.CommitAttestationCalled = true - return mp.AllowAttestation -} - // CheckBlockSafety returns bool with allow block value. -func (mp MockProtector) CheckBlockSafety(_ context.Context, _ *eth.BeaconBlockHeader) bool { +func (mp MockProtector) CheckBlockSafety(_ context.Context, _ *eth.SignedBeaconBlockHeader) bool { mp.VerifyBlockCalled = true return mp.AllowBlock } -// CommitBlock returns bool with allow block value. -func (mp MockProtector) CommitBlock(_ context.Context, _ *eth.SignedBeaconBlockHeader) (bool, error) { - mp.CommitBlockCalled = true - return mp.AllowBlock, nil -} - // Status returns nil. func (mp MockProtector) Status() error { mp.StatusCalled = true diff --git a/validator/testing/mock_slasher.go b/validator/testing/mock_slasher.go index 85c675ca1cf8..79449765080b 100644 --- a/validator/testing/mock_slasher.go +++ b/validator/testing/mock_slasher.go @@ -5,30 +5,27 @@ import ( "errors" eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" - slashpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "google.golang.org/grpc" "google.golang.org/protobuf/proto" ) // MockSlasher mocks the slasher rpc server. type MockSlasher struct { - SlashAttestation bool - SlashBlock bool - IsSlashableAttestationCalled bool - IsSlashableAttestationNoUpdateCalled bool - IsSlashableBlockCalled bool - IsSlashableBlockNoUpdateCalled bool + SlashAttestation bool + SlashBlock bool + IsSlashableAttestationCalled bool + IsSlashableBlockCalled bool } // HighestAttestations will return an empty array of attestations. -func (ms MockSlasher) HighestAttestations(ctx context.Context, req *slashpb.HighestAttestationRequest, _ ...grpc.CallOption) (*slashpb.HighestAttestationResponse, error) { - return &slashpb.HighestAttestationResponse{ +func (_ MockSlasher) HighestAttestations(ctx context.Context, req *eth.HighestAttestationRequest, _ ...grpc.CallOption) (*eth.HighestAttestationResponse, error) { + return ð.HighestAttestationResponse{ Attestations: nil, }, nil } // IsSlashableAttestation returns slashbale attestation if slash attestation is set to true. -func (ms MockSlasher) IsSlashableAttestation(_ context.Context, in *eth.IndexedAttestation, _ ...grpc.CallOption) (*slashpb.AttesterSlashingResponse, error) { +func (ms MockSlasher) IsSlashableAttestation(_ context.Context, in *eth.IndexedAttestation, _ ...grpc.CallOption) (*eth.AttesterSlashingResponse, error) { ms.IsSlashableAttestationCalled = true if ms.SlashAttestation { @@ -42,24 +39,15 @@ func (ms MockSlasher) IsSlashableAttestation(_ context.Context, in *eth.IndexedA Attestation_2: slashingAtt, }, } - return &slashpb.AttesterSlashingResponse{ + return ð.AttesterSlashingResponse{ AttesterSlashings: slashings, }, nil } return nil, nil } -// IsSlashableAttestationNoUpdate returns slashbale if slash attestation is set to true. -func (ms MockSlasher) IsSlashableAttestationNoUpdate(_ context.Context, _ *eth.IndexedAttestation, _ ...grpc.CallOption) (*slashpb.Slashable, error) { - ms.IsSlashableAttestationNoUpdateCalled = true - return &slashpb.Slashable{ - Slashable: ms.SlashAttestation, - }, nil - -} - // IsSlashableBlock returns proposer slashing if slash block is set to true. -func (ms MockSlasher) IsSlashableBlock(_ context.Context, in *eth.SignedBeaconBlockHeader, _ ...grpc.CallOption) (*slashpb.ProposerSlashingResponse, error) { +func (ms MockSlasher) IsSlashableBlock(_ context.Context, in *eth.SignedBeaconBlockHeader, _ ...grpc.CallOption) (*eth.ProposerSlashingResponse, error) { ms.IsSlashableBlockCalled = true if ms.SlashBlock { slashingBlk, ok := proto.Clone(in).(*eth.SignedBeaconBlockHeader) @@ -72,17 +60,9 @@ func (ms MockSlasher) IsSlashableBlock(_ context.Context, in *eth.SignedBeaconBl Header_2: slashingBlk, }, } - return &slashpb.ProposerSlashingResponse{ + return ð.ProposerSlashingResponse{ ProposerSlashings: slashings, }, nil } return nil, nil } - -// IsSlashableBlockNoUpdate returns slashbale if slash block is set to true. -func (ms MockSlasher) IsSlashableBlockNoUpdate(_ context.Context, _ *eth.BeaconBlockHeader, _ ...grpc.CallOption) (*slashpb.Slashable, error) { - ms.IsSlashableBlockNoUpdateCalled = true - return &slashpb.Slashable{ - Slashable: ms.SlashBlock, - }, nil -} From 520bc9d9554b8690b9813d5fa5079e2fd7c1e4f0 Mon Sep 17 00:00:00 2001 From: Preston Van Loon Date: Wed, 29 Sep 2021 15:49:58 -0500 Subject: [PATCH 20/25] Update validator reporting logs and metrics for Altair (#9589) * Mark fields as deprecated due to Altair * Only print inclusion distance fields before Altair fork * Report phase0 and altair metrics respectively * only set phase0 fields in phase0, only set altair fields in altair * better use of fields * Update go pbs * Update individual votes method * regen go proto files * formatting * Feedback from @potuz * Annotate metrics per @potuz suggestion * Set previous release e2e to end 1 epoch before altair. Add some out of bounds checks for validator metrics reporting and a panic catch * gofmt Co-authored-by: terence tsao Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- .../rpc/prysm/v1alpha1/beacon/validators.go | 18 +- .../prysm/v1alpha1/beacon/validators_test.go | 5 +- proto/prysm/v1alpha1/beacon_chain.pb.go | 576 +++++++++--------- proto/prysm/v1alpha1/beacon_chain.proto | 26 +- testing/endtoend/BUILD.bazel | 1 + testing/endtoend/endtoend_test.go | 44 +- testing/endtoend/minimal_e2e_test.go | 3 +- validator/client/metrics.go | 185 +++++- validator/client/runner.go | 11 +- 9 files changed, 530 insertions(+), 339 deletions(-) diff --git a/beacon-chain/rpc/prysm/v1alpha1/beacon/validators.go b/beacon-chain/rpc/prysm/v1alpha1/beacon/validators.go index b1f3537c5811..ef03f6e69b66 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/beacon/validators.go +++ b/beacon-chain/rpc/prysm/v1alpha1/beacon/validators.go @@ -762,6 +762,7 @@ func (bs *Server) GetValidatorPerformance( correctlyVotedSource := make([]bool, 0, responseCap) correctlyVotedTarget := make([]bool, 0, responseCap) correctlyVotedHead := make([]bool, 0, responseCap) + inactivityScores := make([]uint64, 0, responseCap) // Append performance summaries. // Also track missing validators using public keys. for _, idx := range validatorIndices { @@ -786,24 +787,30 @@ func (bs *Server) GetValidatorPerformance( effectiveBalances = append(effectiveBalances, summary.CurrentEpochEffectiveBalance) beforeTransitionBalances = append(beforeTransitionBalances, summary.BeforeEpochTransitionBalance) afterTransitionBalances = append(afterTransitionBalances, summary.AfterEpochTransitionBalance) - inclusionSlots = append(inclusionSlots, summary.InclusionSlot) - inclusionDistances = append(inclusionDistances, summary.InclusionDistance) correctlyVotedSource = append(correctlyVotedSource, summary.IsPrevEpochAttester) correctlyVotedTarget = append(correctlyVotedTarget, summary.IsPrevEpochTargetAttester) correctlyVotedHead = append(correctlyVotedHead, summary.IsPrevEpochHeadAttester) + + if headState.Version() == version.Phase0 { + inclusionSlots = append(inclusionSlots, summary.InclusionSlot) + inclusionDistances = append(inclusionDistances, summary.InclusionDistance) + } else { + inactivityScores = append(inactivityScores, summary.InactivityScore) + } } return ðpb.ValidatorPerformanceResponse{ PublicKeys: pubKeys, - InclusionSlots: inclusionSlots, - InclusionDistances: inclusionDistances, CorrectlyVotedSource: correctlyVotedSource, - CorrectlyVotedTarget: correctlyVotedTarget, + CorrectlyVotedTarget: correctlyVotedTarget, // In altair, when this is true then the attestation was definitely included. CorrectlyVotedHead: correctlyVotedHead, CurrentEffectiveBalances: effectiveBalances, BalancesBeforeEpochTransition: beforeTransitionBalances, BalancesAfterEpochTransition: afterTransitionBalances, MissingValidators: missingValidators, + InclusionSlots: inclusionSlots, // Only populated in phase0 + InclusionDistances: inclusionDistances, // Only populated in phase 0 + InactivityScores: inactivityScores, // Only populated in Altair }, nil } @@ -889,6 +896,7 @@ func (bs *Server) GetIndividualVotes( CurrentEpochEffectiveBalanceGwei: v[index].CurrentEpochEffectiveBalance, InclusionSlot: v[index].InclusionSlot, InclusionDistance: v[index].InclusionDistance, + InactivityScore: v[index].InactivityScore, }) } diff --git a/beacon-chain/rpc/prysm/v1alpha1/beacon/validators_test.go b/beacon-chain/rpc/prysm/v1alpha1/beacon/validators_test.go index 6cb9d6a75c81..26c3909dce89 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/beacon/validators_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/beacon/validators_test.go @@ -2032,14 +2032,15 @@ func TestGetValidatorPerformanceAltair_OK(t *testing.T) { want := ðpb.ValidatorPerformanceResponse{ PublicKeys: [][]byte{publicKey2[:], publicKey3[:]}, CurrentEffectiveBalances: []uint64{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance}, - InclusionSlots: []types.Slot{0, 0}, - InclusionDistances: []types.Slot{0, 0}, + InclusionSlots: nil, + InclusionDistances: nil, CorrectlyVotedSource: []bool{false, false}, CorrectlyVotedTarget: []bool{false, false}, CorrectlyVotedHead: []bool{false, false}, BalancesBeforeEpochTransition: []uint64{101, 102}, BalancesAfterEpochTransition: []uint64{0, 0}, MissingValidators: [][]byte{publicKey1[:]}, + InactivityScores: []uint64{0, 0}, } res, err := bs.GetValidatorPerformance(ctx, ðpb.ValidatorPerformanceRequest{ diff --git a/proto/prysm/v1alpha1/beacon_chain.pb.go b/proto/prysm/v1alpha1/beacon_chain.pb.go index 6ea8b876e167..d1989e67617a 100755 --- a/proto/prysm/v1alpha1/beacon_chain.pb.go +++ b/proto/prysm/v1alpha1/beacon_chain.pb.go @@ -1789,8 +1789,10 @@ type ValidatorPerformanceResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - CurrentEffectiveBalances []uint64 `protobuf:"varint,1,rep,packed,name=current_effective_balances,json=currentEffectiveBalances,proto3" json:"current_effective_balances,omitempty"` - InclusionSlots []github_com_prysmaticlabs_eth2_types.Slot `protobuf:"varint,2,rep,packed,name=inclusion_slots,json=inclusionSlots,proto3" json:"inclusion_slots,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Slot"` + CurrentEffectiveBalances []uint64 `protobuf:"varint,1,rep,packed,name=current_effective_balances,json=currentEffectiveBalances,proto3" json:"current_effective_balances,omitempty"` + // Deprecated: Do not use. + InclusionSlots []github_com_prysmaticlabs_eth2_types.Slot `protobuf:"varint,2,rep,packed,name=inclusion_slots,json=inclusionSlots,proto3" json:"inclusion_slots,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Slot"` + // Deprecated: Do not use. InclusionDistances []github_com_prysmaticlabs_eth2_types.Slot `protobuf:"varint,3,rep,packed,name=inclusion_distances,json=inclusionDistances,proto3" json:"inclusion_distances,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Slot"` CorrectlyVotedSource []bool `protobuf:"varint,4,rep,packed,name=correctly_voted_source,json=correctlyVotedSource,proto3" json:"correctly_voted_source,omitempty"` CorrectlyVotedTarget []bool `protobuf:"varint,5,rep,packed,name=correctly_voted_target,json=correctlyVotedTarget,proto3" json:"correctly_voted_target,omitempty"` @@ -1800,6 +1802,7 @@ type ValidatorPerformanceResponse struct { MissingValidators [][]byte `protobuf:"bytes,9,rep,name=missing_validators,json=missingValidators,proto3" json:"missing_validators,omitempty"` AverageActiveValidatorBalance float32 `protobuf:"fixed32,10,opt,name=average_active_validator_balance,json=averageActiveValidatorBalance,proto3" json:"average_active_validator_balance,omitempty"` PublicKeys [][]byte `protobuf:"bytes,11,rep,name=public_keys,json=publicKeys,proto3" json:"public_keys,omitempty" ssz-size:"?,48"` + InactivityScores []uint64 `protobuf:"varint,12,rep,packed,name=inactivity_scores,json=inactivityScores,proto3" json:"inactivity_scores,omitempty"` } func (x *ValidatorPerformanceResponse) Reset() { @@ -1841,6 +1844,7 @@ func (x *ValidatorPerformanceResponse) GetCurrentEffectiveBalances() []uint64 { return nil } +// Deprecated: Do not use. func (x *ValidatorPerformanceResponse) GetInclusionSlots() []github_com_prysmaticlabs_eth2_types.Slot { if x != nil { return x.InclusionSlots @@ -1848,6 +1852,7 @@ func (x *ValidatorPerformanceResponse) GetInclusionSlots() []github_com_prysmati return []github_com_prysmaticlabs_eth2_types.Slot(nil) } +// Deprecated: Do not use. func (x *ValidatorPerformanceResponse) GetInclusionDistances() []github_com_prysmaticlabs_eth2_types.Slot { if x != nil { return x.InclusionDistances @@ -1911,6 +1916,13 @@ func (x *ValidatorPerformanceResponse) GetPublicKeys() [][]byte { return nil } +func (x *ValidatorPerformanceResponse) GetInactivityScores() []uint64 { + if x != nil { + return x.InactivityScores + } + return nil +} + type ValidatorQueue struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3032,8 +3044,11 @@ type IndividualVotesRespond_IndividualVote struct { IsPreviousEpochTargetAttester bool `protobuf:"varint,11,opt,name=is_previous_epoch_target_attester,json=isPreviousEpochTargetAttester,proto3" json:"is_previous_epoch_target_attester,omitempty"` IsPreviousEpochHeadAttester bool `protobuf:"varint,12,opt,name=is_previous_epoch_head_attester,json=isPreviousEpochHeadAttester,proto3" json:"is_previous_epoch_head_attester,omitempty"` CurrentEpochEffectiveBalanceGwei uint64 `protobuf:"varint,13,opt,name=current_epoch_effective_balance_gwei,json=currentEpochEffectiveBalanceGwei,proto3" json:"current_epoch_effective_balance_gwei,omitempty"` - InclusionSlot github_com_prysmaticlabs_eth2_types.Slot `protobuf:"varint,14,opt,name=inclusion_slot,json=inclusionSlot,proto3" json:"inclusion_slot,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Slot"` - InclusionDistance github_com_prysmaticlabs_eth2_types.Slot `protobuf:"varint,15,opt,name=inclusion_distance,json=inclusionDistance,proto3" json:"inclusion_distance,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Slot"` + // Deprecated: Do not use. + InclusionSlot github_com_prysmaticlabs_eth2_types.Slot `protobuf:"varint,14,opt,name=inclusion_slot,json=inclusionSlot,proto3" json:"inclusion_slot,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Slot"` + // Deprecated: Do not use. + InclusionDistance github_com_prysmaticlabs_eth2_types.Slot `protobuf:"varint,15,opt,name=inclusion_distance,json=inclusionDistance,proto3" json:"inclusion_distance,omitempty" cast-type:"github.com/prysmaticlabs/eth2-types.Slot"` + InactivityScore uint64 `protobuf:"varint,16,opt,name=inactivity_score,json=inactivityScore,proto3" json:"inactivity_score,omitempty"` } func (x *IndividualVotesRespond_IndividualVote) Reset() { @@ -3159,6 +3174,7 @@ func (x *IndividualVotesRespond_IndividualVote) GetCurrentEpochEffectiveBalanceG return 0 } +// Deprecated: Do not use. func (x *IndividualVotesRespond_IndividualVote) GetInclusionSlot() github_com_prysmaticlabs_eth2_types.Slot { if x != nil { return x.InclusionSlot @@ -3166,6 +3182,7 @@ func (x *IndividualVotesRespond_IndividualVote) GetInclusionSlot() github_com_pr return github_com_prysmaticlabs_eth2_types.Slot(0) } +// Deprecated: Do not use. func (x *IndividualVotesRespond_IndividualVote) GetInclusionDistance() github_com_prysmaticlabs_eth2_types.Slot { if x != nil { return x.InclusionDistance @@ -3173,6 +3190,13 @@ func (x *IndividualVotesRespond_IndividualVote) GetInclusionDistance() github_co return github_com_prysmaticlabs_eth2_types.Slot(0) } +func (x *IndividualVotesRespond_IndividualVote) GetInactivityScore() uint64 { + if x != nil { + return x.InactivityScore + } + return 0 +} + var File_proto_prysm_v1alpha1_beacon_chain_proto protoreflect.FileDescriptor var file_proto_prysm_v1alpha1_beacon_chain_proto_rawDesc = []byte{ @@ -3580,291 +3604,297 @@ var file_proto_prysm_v1alpha1_beacon_chain_proto_rawDesc = []byte{ 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x07, 0x69, 0x6e, 0x64, - 0x69, 0x63, 0x65, 0x73, 0x22, 0xe3, 0x05, 0x0a, 0x1c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x69, 0x63, 0x65, 0x73, 0x22, 0x94, 0x06, 0x0a, 0x1c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x1a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, 0x18, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x73, 0x12, 0x55, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, - 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x42, 0x2c, 0x82, 0xb5, - 0x18, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, - 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x6f, 0x74, 0x73, 0x12, 0x5d, 0x0a, 0x13, 0x69, 0x6e, - 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x42, 0x2c, 0x82, 0xb5, 0x18, 0x28, 0x67, 0x69, 0x74, + 0x63, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x42, 0x2e, 0x18, 0x01, + 0x82, 0xb5, 0x18, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, + 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, + 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x0e, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x6f, 0x74, 0x73, 0x12, 0x5f, 0x0a, 0x13, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x42, 0x2e, 0x18, 0x01, 0x82, 0xb5, 0x18, + 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, + 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x34, 0x0a, + 0x16, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x5f, 0x76, 0x6f, 0x74, 0x65, 0x64, + 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x08, 0x52, 0x14, 0x63, + 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x56, 0x6f, 0x74, 0x65, 0x64, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, + 0x5f, 0x76, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x08, 0x52, 0x14, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x56, 0x6f, + 0x74, 0x65, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x72, + 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x5f, 0x76, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, + 0x64, 0x18, 0x06, 0x20, 0x03, 0x28, 0x08, 0x52, 0x12, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, + 0x6c, 0x79, 0x56, 0x6f, 0x74, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x12, 0x47, 0x0a, 0x20, 0x62, + 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x65, + 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x07, 0x20, 0x03, 0x28, 0x04, 0x52, 0x1d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x42, + 0x65, 0x66, 0x6f, 0x72, 0x65, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x1f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, + 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x03, 0x28, 0x04, 0x52, 0x1c, 0x62, + 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x66, 0x74, 0x65, 0x72, 0x45, 0x70, 0x6f, 0x63, + 0x68, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x11, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x47, 0x0a, 0x20, 0x61, 0x76, + 0x65, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x02, 0x52, 0x1d, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x41, 0x63, 0x74, + 0x69, 0x76, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, + 0x79, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, + 0x34, 0x38, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x2b, + 0x0a, 0x11, 0x69, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x63, 0x6f, + 0x72, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x04, 0x52, 0x10, 0x69, 0x6e, 0x61, 0x63, 0x74, + 0x69, 0x76, 0x69, 0x74, 0x79, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x22, 0x91, 0x03, 0x0a, 0x0e, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x63, 0x68, 0x75, 0x72, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x63, 0x68, 0x75, 0x72, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, + 0x40, 0x0a, 0x16, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, + 0x0a, 0x18, 0x01, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x52, 0x14, 0x61, 0x63, 0x74, + 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x73, 0x12, 0x34, 0x0a, 0x10, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x0a, 0x18, 0x01, 0x8a, + 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x52, 0x0e, 0x65, 0x78, 0x69, 0x74, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x78, 0x0a, 0x1c, 0x61, 0x63, 0x74, 0x69, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, + 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x42, 0x36, 0x82, + 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, + 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, + 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x1a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x12, 0x6c, 0x0a, 0x16, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x04, 0x42, 0x36, 0x82, 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, + 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x14, 0x65, 0x78, 0x69, 0x74, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, + 0xcd, 0x02, 0x0a, 0x1f, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x45, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x42, 0x2d, 0x82, 0xb5, 0x18, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, + 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, + 0x68, 0x48, 0x00, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x1a, 0x0a, 0x07, 0x67, 0x65, + 0x6e, 0x65, 0x73, 0x69, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x67, + 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x12, 0x29, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, 0x18, + 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x73, 0x12, 0x50, 0x0a, 0x07, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x04, 0x42, 0x36, 0x82, 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, + 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x07, 0x69, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, + 0x0e, 0x0a, 0x0c, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, + 0x95, 0x06, 0x0a, 0x14, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x73, 0x73, + 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2d, 0x82, 0xb5, 0x18, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, - 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x63, 0x6f, 0x72, - 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x5f, 0x76, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x08, 0x52, 0x14, 0x63, 0x6f, 0x72, 0x72, 0x65, - 0x63, 0x74, 0x6c, 0x79, 0x56, 0x6f, 0x74, 0x65, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, - 0x34, 0x0a, 0x16, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x5f, 0x76, 0x6f, 0x74, - 0x65, 0x64, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x08, 0x52, - 0x14, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x56, 0x6f, 0x74, 0x65, 0x64, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, - 0x6c, 0x79, 0x5f, 0x76, 0x6f, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x06, 0x20, - 0x03, 0x28, 0x08, 0x52, 0x12, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x56, 0x6f, - 0x74, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x12, 0x47, 0x0a, 0x20, 0x62, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x73, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, - 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x03, 0x28, - 0x04, 0x52, 0x1d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x42, 0x65, 0x66, 0x6f, 0x72, - 0x65, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x45, 0x0a, 0x1f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x5f, 0x61, 0x66, 0x74, - 0x65, 0x72, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x03, 0x28, 0x04, 0x52, 0x1c, 0x62, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x73, 0x41, 0x66, 0x74, 0x65, 0x72, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x09, 0x20, - 0x03, 0x28, 0x0c, 0x52, 0x11, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x47, 0x0a, 0x20, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, - 0x65, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x02, - 0x52, 0x1d, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, - 0x29, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x0b, - 0x20, 0x03, 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x52, 0x0a, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x91, 0x03, 0x0a, 0x0e, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x1f, 0x0a, - 0x0b, 0x63, 0x68, 0x75, 0x72, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0a, 0x63, 0x68, 0x75, 0x72, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x40, - 0x0a, 0x16, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x0a, - 0x18, 0x01, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x52, 0x14, 0x61, 0x63, 0x74, 0x69, - 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, - 0x12, 0x34, 0x0a, 0x10, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, - 0x6b, 0x65, 0x79, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x0a, 0x18, 0x01, 0x8a, 0xb5, - 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x52, 0x0e, 0x65, 0x78, 0x69, 0x74, 0x50, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x78, 0x0a, 0x1c, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, - 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x42, 0x36, 0x82, 0xb5, - 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, + 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x61, 0x0a, + 0x0b, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, + 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, + 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, + 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x1a, 0x8d, 0x04, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x74, 0x65, 0x65, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, + 0x63, 0x0a, 0x11, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x42, 0x36, 0x82, 0xb5, 0x18, 0x32, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x52, 0x10, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x65, 0x73, 0x12, 0x5f, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, + 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x36, 0x82, + 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, + 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, + 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x51, 0x0a, 0x0d, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2c, 0x82, 0xb5, + 0x18, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x52, 0x1a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, - 0x12, 0x6c, 0x0a, 0x16, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x04, - 0x42, 0x36, 0x82, 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x53, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, + 0x42, 0x2c, 0x82, 0xb5, 0x18, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, - 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x14, 0x65, 0x78, 0x69, 0x74, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0xcd, - 0x02, 0x0a, 0x1f, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x45, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x0d, + 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x6f, 0x74, 0x73, 0x12, 0x27, 0x0a, + 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x08, 0x18, 0x01, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x09, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x5f, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x42, + 0x36, 0x82, 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, + 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x95, 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x45, 0x0a, 0x05, + 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2d, 0x82, 0xb5, 0x18, + 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, + 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x00, 0x52, 0x05, 0x65, 0x70, + 0x6f, 0x63, 0x68, 0x12, 0x1a, 0x0a, 0x07, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x42, + 0x0e, 0x0a, 0x0c, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, + 0xd8, 0x01, 0x0a, 0x1e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x61, 0x72, + 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x43, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2d, 0x82, 0xb5, 0x18, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, - 0x48, 0x00, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x1a, 0x0a, 0x07, 0x67, 0x65, 0x6e, - 0x65, 0x73, 0x69, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x67, 0x65, - 0x6e, 0x65, 0x73, 0x69, 0x73, 0x12, 0x29, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, - 0x6b, 0x65, 0x79, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, 0x18, 0x04, - 0x3f, 0x2c, 0x34, 0x38, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, - 0x12, 0x50, 0x0a, 0x07, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x04, 0x42, 0x36, 0x82, 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, - 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x07, 0x69, 0x6e, 0x64, 0x69, 0x63, - 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, - 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x0e, - 0x0a, 0x0c, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x95, - 0x06, 0x0a, 0x14, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x73, 0x73, 0x69, - 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2d, 0x82, 0xb5, 0x18, 0x29, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, - 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, - 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x61, 0x0a, 0x0b, - 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x3f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, - 0x6e, 0x74, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, - 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, - 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x1a, 0x8d, 0x04, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x74, 0x65, 0x65, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x63, - 0x0a, 0x11, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, - 0x65, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x42, 0x36, 0x82, 0xb5, 0x18, 0x32, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, - 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x52, 0x10, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, - 0x65, 0x65, 0x73, 0x12, 0x5f, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, - 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x36, 0x82, 0xb5, - 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, - 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x12, 0x51, 0x0a, 0x0d, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, - 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2c, 0x82, 0xb5, 0x18, - 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, - 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, - 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, - 0x74, 0x65, 0x72, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x53, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x42, - 0x2c, 0x82, 0xb5, 0x18, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, - 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x0d, 0x70, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x6f, 0x74, 0x73, 0x12, 0x27, 0x0a, 0x0a, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, - 0x42, 0x08, 0x18, 0x01, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x5f, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x42, 0x36, - 0x82, 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, - 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, - 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x95, 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x45, 0x0a, 0x05, 0x65, - 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2d, 0x82, 0xb5, 0x18, 0x29, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, - 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x00, 0x52, 0x05, 0x65, 0x70, 0x6f, - 0x63, 0x68, 0x12, 0x1a, 0x0a, 0x07, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x42, 0x0e, - 0x0a, 0x0c, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0xd8, - 0x01, 0x0a, 0x1e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x61, 0x72, 0x74, - 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x43, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x42, 0x2d, 0x82, 0xb5, 0x18, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, - 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, - 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, - 0x7a, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x69, 0x6e, 0x61, 0x6c, - 0x69, 0x7a, 0x65, 0x64, 0x12, 0x53, 0x0a, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x61, 0x72, - 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x70, 0x61, 0x72, 0x74, - 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x54, 0x0a, 0x16, 0x41, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, - 0xa8, 0x01, 0x0a, 0x17, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, - 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0c, 0x61, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, - 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, - 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x74, - 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x0c, 0x42, - 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x47, 0x0a, 0x06, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x39, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0x79, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0f, 0x73, 0x6c, 0x61, - 0x73, 0x68, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x04, 0x42, 0x36, 0x82, 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, - 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x73, 0x6c, 0x61, 0x73, - 0x68, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0xd0, 0x01, 0x0a, 0x16, 0x49, - 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x56, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, + 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x7a, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x69, 0x6e, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x53, 0x0a, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, + 0x70, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x61, + 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x70, 0x61, 0x72, + 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x54, 0x0a, 0x16, 0x41, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, + 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x22, 0xa8, 0x01, 0x0a, 0x17, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0c, + 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, + 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, + 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x0c, + 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x47, 0x0a, 0x06, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x39, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x79, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0f, 0x73, 0x6c, + 0x61, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x04, 0x42, 0x36, 0x82, 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, + 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x73, 0x6c, 0x61, + 0x73, 0x68, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0xd0, 0x01, 0x0a, 0x16, + 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x56, 0x6f, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2d, 0x82, 0xb5, 0x18, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, + 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45, + 0x70, 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, + 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x50, 0x0a, 0x07, + 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x42, 0x36, 0x82, + 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, + 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, + 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x07, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0xbb, + 0x09, 0x0a, 0x16, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x56, 0x6f, 0x74, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x12, 0x67, 0x0a, 0x10, 0x69, 0x6e, 0x64, + 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x5f, 0x76, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x64, 0x69, + 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x56, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x64, 0x2e, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x56, 0x6f, 0x74, + 0x65, 0x52, 0x0f, 0x69, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x56, 0x6f, 0x74, + 0x65, 0x73, 0x1a, 0xb7, 0x08, 0x0a, 0x0e, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, + 0x6c, 0x56, 0x6f, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2d, 0x82, 0xb5, 0x18, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45, 0x70, - 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, - 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x50, 0x0a, 0x07, 0x69, - 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x42, 0x36, 0x82, 0xb5, - 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, - 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x52, 0x07, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x8c, 0x09, - 0x0a, 0x16, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x56, 0x6f, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x12, 0x67, 0x0a, 0x10, 0x69, 0x6e, 0x64, 0x69, - 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x5f, 0x76, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x64, 0x69, 0x76, - 0x69, 0x64, 0x75, 0x61, 0x6c, 0x56, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x64, 0x2e, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x56, 0x6f, 0x74, 0x65, - 0x52, 0x0f, 0x69, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x56, 0x6f, 0x74, 0x65, - 0x73, 0x1a, 0x88, 0x08, 0x0a, 0x0e, 0x49, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, - 0x56, 0x6f, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x42, 0x2d, 0x82, 0xb5, 0x18, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, - 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, - 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x5f, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x04, 0x42, 0x36, 0x82, 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, - 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, - 0x73, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, - 0x73, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x20, 0x69, 0x73, 0x5f, 0x77, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x1c, 0x69, 0x73, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x62, - 0x6c, 0x65, 0x49, 0x6e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, - 0x12, 0x3a, 0x0a, 0x1a, 0x69, 0x73, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, - 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x3c, 0x0a, 0x1b, - 0x69, 0x73, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x65, - 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x17, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x50, 0x72, 0x65, - 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x39, 0x0a, 0x19, 0x69, 0x73, - 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x61, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, - 0x73, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x20, 0x69, 0x73, 0x5f, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x1c, 0x69, 0x73, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x3b, 0x0a, - 0x1a, 0x69, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, 0x6f, - 0x63, 0x68, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x17, 0x69, 0x73, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, - 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x21, 0x69, 0x73, - 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x69, 0x73, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, - 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x1f, 0x69, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, - 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x61, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x69, - 0x73, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, - 0x61, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x24, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x65, 0x66, 0x66, 0x65, - 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x67, 0x77, - 0x65, 0x69, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x45, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x42, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x47, 0x77, 0x65, 0x69, 0x12, 0x53, 0x0a, 0x0e, 0x69, 0x6e, - 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x0e, 0x20, 0x01, - 0x28, 0x04, 0x42, 0x2c, 0x82, 0xb5, 0x18, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x5f, 0x0a, 0x0f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x42, 0x36, 0x82, 0xb5, 0x18, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, - 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, - 0x52, 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x6f, 0x74, 0x12, - 0x5b, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x69, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2c, 0x82, 0xb5, 0x18, - 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, - 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, - 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, - 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x9f, 0x01, 0x0a, + 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, + 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x69, 0x73, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x20, 0x69, 0x73, 0x5f, + 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x5f, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x1c, 0x69, 0x73, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, + 0x62, 0x6c, 0x65, 0x49, 0x6e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, + 0x68, 0x12, 0x3a, 0x0a, 0x1a, 0x69, 0x73, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, + 0x6e, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x49, + 0x6e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x3c, 0x0a, + 0x1b, 0x69, 0x73, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x70, 0x72, + 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x17, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x50, 0x72, + 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x39, 0x0a, 0x19, 0x69, + 0x73, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, + 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, + 0x69, 0x73, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x20, 0x69, 0x73, 0x5f, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x1c, 0x69, 0x73, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x3b, + 0x0a, 0x1a, 0x69, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, + 0x6f, 0x63, 0x68, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x17, 0x69, 0x73, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, + 0x6f, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x21, 0x69, + 0x73, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, + 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x69, 0x73, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x1f, 0x69, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, + 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, + 0x69, 0x73, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, + 0x65, 0x61, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x24, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x65, 0x66, 0x66, + 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x67, + 0x77, 0x65, 0x69, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x45, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, + 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x47, 0x77, 0x65, 0x69, 0x12, 0x55, 0x0a, 0x0e, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x04, 0x42, 0x2e, 0x18, 0x01, 0x82, 0xb5, 0x18, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, + 0x61, 0x62, 0x73, 0x2f, 0x65, 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, + 0x6c, 0x6f, 0x74, 0x52, 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x6c, + 0x6f, 0x74, 0x12, 0x5d, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2e, + 0x18, 0x01, 0x82, 0xb5, 0x18, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x65, + 0x74, 0x68, 0x32, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x11, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, + 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x69, 0x6e, 0x61, + 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x9f, 0x01, 0x0a, 0x1a, 0x57, 0x65, 0x61, 0x6b, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, diff --git a/proto/prysm/v1alpha1/beacon_chain.proto b/proto/prysm/v1alpha1/beacon_chain.proto index 6121183a38d3..d50f8498e2a3 100644 --- a/proto/prysm/v1alpha1/beacon_chain.proto +++ b/proto/prysm/v1alpha1/beacon_chain.proto @@ -680,14 +680,16 @@ message ValidatorPerformanceRequest { message ValidatorPerformanceResponse { // A list of validator effective balances mapped 1-to-1 with the request's - // public keys. + // public keys. repeated uint64 current_effective_balances = 1; // The slot of when validator's attestation got included in the chain at previous epoch, the slot // is mapped 1-to-1 with the request's public keys. - repeated uint64 inclusion_slots = 2 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Slot"]; + // Deprecated: This field can no longer be fetched from the beacon state after the Altair hard fork. + repeated uint64 inclusion_slots = 2 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Slot", deprecated = true]; // The distance of when validator submitted and got included in the chain, the distance // is mapped 1-to-1 with the request's public keys. - repeated uint64 inclusion_distances = 3 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Slot"]; + // Deprecated: This field can no longer be fetched from the beacon state after the Altair hard fork. + repeated uint64 inclusion_distances = 3 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Slot", deprecated = true]; // Whether the list of validator recently correctly voted for source at previous epoch, the result // is mapped 1-to-1 with the request's public keys. repeated bool correctly_voted_source = 4; @@ -697,19 +699,21 @@ message ValidatorPerformanceResponse { // Whether the list of validator recently correctly voted for head at previous epoch, the result // is mapped 1-to-1 with the request's public keys. repeated bool correctly_voted_head = 6; - // The balance of validators before epoch transition, the balance is mapped 1-to-1 with the requests's + // The balance of validators before epoch transition, the balance is mapped 1-to-1 with the requests' // public keys. repeated uint64 balances_before_epoch_transition = 7; - // The balance of validators after epoch transition, the balance is mapped 1-to-1 with the requests's + // The balance of validators after epoch transition, the balance is mapped 1-to-1 with the requests' // public keys. repeated uint64 balances_after_epoch_transition = 8; // The total number of validators from the request not found in // in the beacon chain. repeated bytes missing_validators = 9; - // The average active validator balance in the beacon chain. + // The average active validator balance in the beacon chain. float average_active_validator_balance = 10; // The public keys in the order they are in of the response. repeated bytes public_keys = 11 [(ethereum.eth.ext.ssz_size) = "?,48"]; + // The inactivity score of the validator tracks validator participation. [New in Altair] + repeated uint64 inactivity_scores = 12; } message ValidatorQueue { @@ -891,10 +895,12 @@ message IndividualVotesRespond { bool is_previous_epoch_head_attester = 12; // The current effective balance of the validator. uint64 current_epoch_effective_balance_gwei = 13; - // The slots of when the validator's attestation got included in the block. - uint64 inclusion_slot = 14 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Slot"]; - // How many slots have passed until the validator's attestation got included in the block. - uint64 inclusion_distance = 15 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Slot"]; + // The slots of when the validator's attestation got included in the block. Only available in phase0. + uint64 inclusion_slot = 14 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Slot", deprecated = true]; + // How many slots have passed until the validator's attestation got included in the block. Only available in phase0. + uint64 inclusion_distance = 15 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/eth2-types.Slot", deprecated = true]; + // The inactivity score of the validator tracks validator participation. [New in Altair] + uint64 inactivity_score = 16; } repeated IndividualVote individual_votes = 1; diff --git a/testing/endtoend/BUILD.bazel b/testing/endtoend/BUILD.bazel index 3598ace8daf3..e40d030c46b4 100644 --- a/testing/endtoend/BUILD.bazel +++ b/testing/endtoend/BUILD.bazel @@ -38,6 +38,7 @@ go_test( "//testing/endtoend/params:go_default_library", "//testing/endtoend/types:go_default_library", "//testing/require:go_default_library", + "@com_github_pkg_errors//:go_default_library", "@com_github_prysmaticlabs_eth2_types//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@org_golang_google_grpc//:go_default_library", diff --git a/testing/endtoend/endtoend_test.go b/testing/endtoend/endtoend_test.go index 382155e2d20a..a7ff5dd7f918 100644 --- a/testing/endtoend/endtoend_test.go +++ b/testing/endtoend/endtoend_test.go @@ -6,7 +6,6 @@ package endtoend import ( "context" - "errors" "fmt" "os" "path" @@ -15,6 +14,7 @@ import ( "testing" "time" + "github.com/pkg/errors" types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/beacon-chain/core/transition" "github.com/prysmaticlabs/prysm/config/params" @@ -79,38 +79,53 @@ func (r *testRunner) run() { // ETH1 node. eth1Node := components.NewEth1Node() g.Go(func() error { - return eth1Node.Start(ctx) + if err := eth1Node.Start(ctx); err != nil { + return errors.Wrap(err, "failed to start eth1node") + } + return nil }) g.Go(func() error { if err := helpers.ComponentsStarted(ctx, []e2etypes.ComponentRunner{eth1Node}); err != nil { - return fmt.Errorf("sending and mining deposits require ETH1 node to run: %w", err) + return errors.Wrap(err, "sending and mining deposits require ETH1 node to run") + } + if err := components.SendAndMineDeposits(eth1Node.KeystorePath(), minGenesisActiveCount, 0, true /* partial */); err != nil { + return errors.Wrap(err, "failed to send and mine deposits") } - return components.SendAndMineDeposits(eth1Node.KeystorePath(), minGenesisActiveCount, 0, true /* partial */) + return nil }) // Boot node. bootNode := components.NewBootNode() g.Go(func() error { - return bootNode.Start(ctx) + if err := bootNode.Start(ctx); err != nil { + return errors.Wrap(err, "failed to start bootnode") + } + return nil }) // Beacon nodes. beaconNodes := components.NewBeaconNodes(config) g.Go(func() error { if err := helpers.ComponentsStarted(ctx, []e2etypes.ComponentRunner{eth1Node, bootNode}); err != nil { - return fmt.Errorf("beacon nodes require ETH1 and boot node to run: %w", err) + return errors.Wrap(err, "beacon nodes require ETH1 and boot node to run") } beaconNodes.SetENR(bootNode.ENR()) - return beaconNodes.Start(ctx) + if err := beaconNodes.Start(ctx); err != nil { + return errors.Wrap(err, "failed to start beacon nodes") + } + return nil }) // Validator nodes. validatorNodes := components.NewValidatorNodeSet(config) g.Go(func() error { if err := helpers.ComponentsStarted(ctx, []e2etypes.ComponentRunner{beaconNodes}); err != nil { - return fmt.Errorf("validator nodes require beacon nodes to run: %w", err) + return errors.Wrap(err, "validator nodes require beacon nodes to run") + } + if err := validatorNodes.Start(ctx); err != nil { + return errors.Wrap(err, "failed to start validator nodes") } - return validatorNodes.Start(ctx) + return nil }) // Run E2E evaluators and tests. @@ -129,7 +144,7 @@ func (r *testRunner) run() { ctxAllNodesReady, cancel := context.WithTimeout(ctx, allNodesStartTimeout) defer cancel() if err := helpers.ComponentsStarted(ctxAllNodesReady, requiredComponents); err != nil { - return fmt.Errorf("components take too long to start: %w", err) + return errors.Wrap(err, "components take too long to start") } // Since defer unwraps in LIFO order, parent context will be closed only after logs are written. @@ -169,7 +184,7 @@ func (r *testRunner) run() { // Run assigned evaluators. if err := r.runEvaluators(conns, tickingStartTime); err != nil { - return err + return errors.Wrap(err, "one or more evaluators failed") } // If requested, run sync test. @@ -177,9 +192,12 @@ func (r *testRunner) run() { return nil } if err := r.testBeaconChainSync(ctx, g, conns, tickingStartTime, bootNode.ENR()); err != nil { - return err + return errors.Wrap(err, "beacon chain sync test failed") + } + if err := r.testDoppelGangerProtection(ctx); err != nil { + return errors.Wrap(err, "doppel ganger protection check failed") } - return r.testDoppelGangerProtection(ctx) + return nil }) if err := g.Wait(); err != nil && !errors.Is(err, context.Canceled) { diff --git a/testing/endtoend/minimal_e2e_test.go b/testing/endtoend/minimal_e2e_test.go index 20025656d695..a37e49c61711 100644 --- a/testing/endtoend/minimal_e2e_test.go +++ b/testing/endtoend/minimal_e2e_test.go @@ -8,6 +8,7 @@ import ( "github.com/prysmaticlabs/prysm/config/params" ev "github.com/prysmaticlabs/prysm/testing/endtoend/evaluators" + "github.com/prysmaticlabs/prysm/testing/endtoend/helpers" e2eParams "github.com/prysmaticlabs/prysm/testing/endtoend/params" "github.com/prysmaticlabs/prysm/testing/endtoend/types" "github.com/prysmaticlabs/prysm/testing/require" @@ -37,7 +38,7 @@ func e2eMinimal(t *testing.T, usePrysmSh bool) { if usePrysmSh { // If using prysm.sh, run for only 6 epochs. // TODO(#9166): remove this block once v2 changes are live. - epochsToRun = 6 + epochsToRun = helpers.AltairE2EForkEpoch - 1 } const tracingEndpoint = "127.0.0.1:9411" evals := []types.Evaluator{ diff --git a/validator/client/metrics.go b/validator/client/metrics.go index 686674ddb17e..f30cd20ffa0a 100644 --- a/validator/client/metrics.go +++ b/validator/client/metrics.go @@ -92,7 +92,7 @@ var ( prometheus.GaugeOpts{ Namespace: "validator", Name: "inclusion_distance", - Help: "Inclusion distance of last attestation.", + Help: "Inclusion distance of last attestation. Deprecated after Altair hard fork.", }, []string{ "pubkey", @@ -114,7 +114,10 @@ var ( prometheus.GaugeOpts{ Namespace: "validator", Name: "correctly_voted_source", - Help: "True if correctly voted source in last attestation.", + Help: "True if correctly voted source in last attestation. In Altair, this " + + "value will be false if the attestation was not included within " + + "integer_squareroot(SLOTS_PER_EPOCH) slots, even if it was a vote for the " + + "correct source.", }, []string{ "pubkey", @@ -125,7 +128,9 @@ var ( prometheus.GaugeOpts{ Namespace: "validator", Name: "correctly_voted_target", - Help: "True if correctly voted target in last attestation.", + Help: "True if correctly voted target in last attestation. In Altair, this " + + "value will be false if the attestation was not included within " + + "SLOTS_PER_EPOCH slots, even if it was a vote for the correct target.", }, []string{ "pubkey", @@ -136,7 +141,8 @@ var ( prometheus.GaugeOpts{ Namespace: "validator", Name: "correctly_voted_head", - Help: "True if correctly voted head in last attestation.", + Help: "True if correctly voted head in last attestation. In Altair, this value " + + "will be false if the attestation was not included in the next slot.", }, []string{ "pubkey", @@ -194,6 +200,17 @@ var ( "pubkey", }, ) + // ValidatorInactivityScoreGaugeVec used to track validator inactivity scores. + ValidatorInactivityScoreGaugeVec = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: "validator", + Name: "inactivity_score", + Help: "Validator inactivity score. 0 is optimum number. New in Altair hardfork", + }, + []string{ + "pubkey", + }, + ) ) // LogValidatorGainsAndLosses logs important metrics related to this validator client's @@ -242,58 +259,125 @@ func (v *validator) LogValidatorGainsAndLosses(ctx context.Context, slot types.S gweiPerEth := float64(params.BeaconConfig().GweiPerEth) v.prevBalanceLock.Lock() for i, pubKey := range resp.PublicKeys { + truncatedKey := fmt.Sprintf("%#x", bytesutil.Trunc(pubKey)) pubKeyBytes := bytesutil.ToBytes48(pubKey) if slot < params.BeaconConfig().SlotsPerEpoch { v.prevBalance[pubKeyBytes] = params.BeaconConfig().MaxEffectiveBalance } + + // Safely load data from response with slice out of bounds checks. The server should return + // the response with all slices of equal length, but the validator could panic if the server + // did not do so for whatever reason. + var balBeforeEpoch uint64 + var balAfterEpoch uint64 + var correctlyVotedSource bool + var correctlyVotedTarget bool + var correctlyVotedHead bool + if i < len(resp.BalancesBeforeEpochTransition) { + balBeforeEpoch = resp.BalancesBeforeEpochTransition[i] + } else { + log.WithField("pubKey", truncatedKey).Warn("Missing balance before epoch transition") + } + if i < len(resp.BalancesAfterEpochTransition) { + balAfterEpoch = resp.BalancesAfterEpochTransition[i] + } else { + } + if i < len(resp.CorrectlyVotedSource) { + correctlyVotedSource = resp.CorrectlyVotedSource[i] + } else { + log.WithField("pubKey", truncatedKey).Warn("Missing correctly voted source") + } + if i < len(resp.CorrectlyVotedTarget) { + correctlyVotedTarget = resp.CorrectlyVotedTarget[i] + } else { + log.WithField("pubKey", truncatedKey).Warn("Missing correctly voted target") + } + if i < len(resp.CorrectlyVotedHead) { + correctlyVotedHead = resp.CorrectlyVotedHead[i] + } else { + log.WithField("pubKey", truncatedKey).Warn("Missing correctly voted head") + } + if _, ok := v.startBalances[pubKeyBytes]; !ok { - v.startBalances[pubKeyBytes] = resp.BalancesBeforeEpochTransition[i] + v.startBalances[pubKeyBytes] = balBeforeEpoch } fmtKey := fmt.Sprintf("%#x", pubKey) - truncatedKey := fmt.Sprintf("%#x", bytesutil.Trunc(pubKey)) if v.prevBalance[pubKeyBytes] > 0 { - newBalance := float64(resp.BalancesAfterEpochTransition[i]) / gweiPerEth - prevBalance := float64(resp.BalancesBeforeEpochTransition[i]) / gweiPerEth + newBalance := float64(balAfterEpoch) / gweiPerEth + prevBalance := float64(balBeforeEpoch) / gweiPerEth startBalance := float64(v.startBalances[pubKeyBytes]) / gweiPerEth percentNet := (newBalance - prevBalance) / prevBalance percentSinceStart := (newBalance - startBalance) / startBalance - log.WithFields(logrus.Fields{ + + previousEpochSummaryFields := logrus.Fields{ "pubKey": truncatedKey, "epoch": prevEpoch, - "correctlyVotedSource": resp.CorrectlyVotedSource[i], - "correctlyVotedTarget": resp.CorrectlyVotedTarget[i], - "correctlyVotedHead": resp.CorrectlyVotedHead[i], - "inclusionSlot": resp.InclusionSlots[i], - "inclusionDistance": resp.InclusionDistances[i], + "correctlyVotedSource": correctlyVotedSource, + "correctlyVotedTarget": correctlyVotedTarget, + "correctlyVotedHead": correctlyVotedHead, "startBalance": startBalance, "oldBalance": prevBalance, "newBalance": newBalance, "percentChange": fmt.Sprintf("%.5f%%", percentNet*100), "percentChangeSinceStart": fmt.Sprintf("%.5f%%", percentSinceStart*100), - }).Info("Previous epoch voting summary") + } + + // These fields are deprecated after Altair. + if core.SlotToEpoch(slot) < params.BeaconConfig().AltairForkEpoch { + if i < len(resp.InclusionSlots) { + previousEpochSummaryFields["inclusionSlot"] = resp.InclusionSlots[i] + } else { + log.WithField("pubKey", truncatedKey).Warn("Missing inclusion slot") + } + if i < len(resp.InclusionDistances) { + previousEpochSummaryFields["inclusionDistance"] = resp.InclusionDistances[i] + } else { + log.WithField("pubKey", truncatedKey).Warn("Missing inclusion distance") + } + } + if core.SlotToEpoch(slot) >= params.BeaconConfig().AltairForkEpoch { + if i < len(resp.InactivityScores) { + previousEpochSummaryFields["inactivityScore"] = resp.InactivityScores[i] + } else { + log.WithField("pubKey", truncatedKey).Warn("Missing inactivity score") + } + } + + log.WithFields(previousEpochSummaryFields).Info("Previous epoch voting summary") if v.emitAccountMetrics { ValidatorBalancesGaugeVec.WithLabelValues(fmtKey).Set(newBalance) - ValidatorInclusionDistancesGaugeVec.WithLabelValues(fmtKey).Set(float64(resp.InclusionDistances[i])) - if resp.CorrectlyVotedSource[i] { + if correctlyVotedSource { ValidatorCorrectlyVotedSourceGaugeVec.WithLabelValues(fmtKey).Set(1) } else { ValidatorCorrectlyVotedSourceGaugeVec.WithLabelValues(fmtKey).Set(0) } - if resp.CorrectlyVotedTarget[i] { + if correctlyVotedTarget { ValidatorCorrectlyVotedTargetGaugeVec.WithLabelValues(fmtKey).Set(1) } else { ValidatorCorrectlyVotedTargetGaugeVec.WithLabelValues(fmtKey).Set(0) } - if resp.CorrectlyVotedHead[i] { + if correctlyVotedHead { ValidatorCorrectlyVotedHeadGaugeVec.WithLabelValues(fmtKey).Set(1) } else { ValidatorCorrectlyVotedHeadGaugeVec.WithLabelValues(fmtKey).Set(0) } + // Phase0 specific metrics + if core.SlotToEpoch(slot) < params.BeaconConfig().AltairForkEpoch { + if i < len(resp.InclusionDistances) { + ValidatorInclusionDistancesGaugeVec.WithLabelValues(fmtKey).Set(float64(resp.InclusionDistances[i])) + } + } else { // Altair specific metrics. + // Reset phase0 fields that no longer apply + ValidatorInclusionDistancesGaugeVec.DeleteLabelValues(fmtKey) + if i < len(resp.InactivityScores) { + ValidatorInactivityScoreGaugeVec.WithLabelValues(fmtKey).Set(float64(resp.InactivityScores[i])) + } + } } } - v.prevBalance[pubKeyBytes] = resp.BalancesBeforeEpochTransition[i] + v.prevBalance[pubKeyBytes] = balBeforeEpoch } v.prevBalanceLock.Unlock() @@ -306,26 +390,39 @@ func (v *validator) UpdateLogAggregateStats(resp *ethpb.ValidatorPerformanceResp summary := &v.voteStats currentEpoch := types.Epoch(slot / params.BeaconConfig().SlotsPerEpoch) var included uint64 - var correctSource, correctTarget, correctHead int + var correctSource, correctTarget, correctHead, inactivityScore int for i := range resp.PublicKeys { - if uint64(resp.InclusionSlots[i]) != ^uint64(0) { + // In phase0, we consider attestations included if the inclusion slot is not max uint64. + // In altair, we consider attestations included if correctlyVotedTarget is true. + if core.SlotToEpoch(slot) < params.BeaconConfig().AltairForkEpoch && i < len(resp.InclusionDistances) { + if uint64(resp.InclusionSlots[i]) != ^uint64(0) { + included++ + summary.includedAttestedCount++ + summary.totalDistance += resp.InclusionDistances[i] + } + } else if resp.CorrectlyVotedTarget[i] { included++ summary.includedAttestedCount++ - summary.totalDistance += resp.InclusionDistances[i] } - if resp.CorrectlyVotedSource[i] { + + if i < len(resp.CorrectlyVotedSource) && resp.CorrectlyVotedSource[i] { correctSource++ summary.correctSources++ } - if resp.CorrectlyVotedTarget[i] { + if i < len(resp.CorrectlyVotedTarget) && resp.CorrectlyVotedTarget[i] { correctTarget++ summary.correctTargets++ } - if resp.CorrectlyVotedHead[i] { + if i < len(resp.CorrectlyVotedHead) && resp.CorrectlyVotedHead[i] { correctHead++ summary.correctHeads++ } + + // Altair metrics + if core.SlotToEpoch(slot) > params.BeaconConfig().AltairForkEpoch && i < len(resp.InactivityScores) { + inactivityScore += int(resp.InactivityScores[i]) + } } // Return early if no attestation got included from previous epoch. @@ -334,18 +431,25 @@ func (v *validator) UpdateLogAggregateStats(resp *ethpb.ValidatorPerformanceResp return } - summary.totalAttestedCount += uint64(len(resp.InclusionSlots)) + summary.totalAttestedCount += uint64(len(resp.CorrectlyVotedTarget)) summary.totalSources += included summary.totalTargets += included summary.totalHeads += included - log.WithFields(logrus.Fields{ + epochSummaryFields := logrus.Fields{ "epoch": currentEpoch - 1, - "attestationInclusionPct": fmt.Sprintf("%.0f%%", (float64(included)/float64(len(resp.InclusionSlots)))*100), + "attestationInclusionPct": fmt.Sprintf("%.0f%%", (float64(included)/float64(len(resp.CorrectlyVotedTarget)))*100), "correctlyVotedSourcePct": fmt.Sprintf("%.0f%%", (float64(correctSource)/float64(included))*100), "correctlyVotedTargetPct": fmt.Sprintf("%.0f%%", (float64(correctTarget)/float64(included))*100), "correctlyVotedHeadPct": fmt.Sprintf("%.0f%%", (float64(correctHead)/float64(included))*100), - }).Info("Previous epoch aggregated voting summary") + } + + // Altair summary fields. + if core.SlotToEpoch(slot) > params.BeaconConfig().AltairForkEpoch && len(resp.CorrectlyVotedTarget) > 0 { + epochSummaryFields["averageInactivityScore"] = fmt.Sprintf("%.0f", float64(inactivityScore)/float64(len(resp.CorrectlyVotedTarget))) + } + + log.WithFields(epochSummaryFields).Info("Previous epoch aggregated voting summary") var totalStartBal, totalPrevBal uint64 for i, val := range v.startBalances { @@ -353,13 +457,28 @@ func (v *validator) UpdateLogAggregateStats(resp *ethpb.ValidatorPerformanceResp totalPrevBal += v.prevBalance[i] } - log.WithFields(logrus.Fields{ + if totalStartBal == 0 || + summary.includedAttestedCount == 0 || + summary.totalSources == 0 || + summary.totalTargets == 0 || + summary.totalHeads == 0 { + log.Error("Failed to print launch summary: one or more divisors is 0") + return + } + + launchSummaryFields := logrus.Fields{ "numberOfEpochs": fmt.Sprintf("%d", currentEpoch-summary.startEpoch), "attestationsInclusionPct": fmt.Sprintf("%.0f%%", (float64(summary.includedAttestedCount)/float64(summary.totalAttestedCount))*100), - "averageInclusionDistance": fmt.Sprintf("%.2f slots", float64(summary.totalDistance)/float64(summary.includedAttestedCount)), "correctlyVotedSourcePct": fmt.Sprintf("%.0f%%", (float64(summary.correctSources)/float64(summary.totalSources))*100), "correctlyVotedTargetPct": fmt.Sprintf("%.0f%%", (float64(summary.correctTargets)/float64(summary.totalTargets))*100), "correctlyVotedHeadPct": fmt.Sprintf("%.0f%%", (float64(summary.correctHeads)/float64(summary.totalHeads))*100), "pctChangeCombinedBalance": fmt.Sprintf("%.5f%%", (float64(totalPrevBal)-float64(totalStartBal))/float64(totalStartBal)*100), - }).Info("Vote summary since launch") + } + + // Add phase0 specific fields + if core.SlotToEpoch(slot) < params.BeaconConfig().AltairForkEpoch { + launchSummaryFields["averageInclusionDistance"] = fmt.Sprintf("%.2f slots", float64(summary.totalDistance)/float64(summary.includedAttestedCount)) + } + + log.WithFields(launchSummaryFields).Info("Vote summary since launch") } diff --git a/validator/client/runner.go b/validator/client/runner.go index 6944cbcbd197..13f6a1add581 100644 --- a/validator/client/runner.go +++ b/validator/client/runner.go @@ -212,10 +212,18 @@ func run(ctx context.Context, v iface.Validator) { }(role, pubKey) } } - // Wait for all processes to complete, then report span complete. + // Wait for all processes to complete, then report span complete. go func() { wg.Wait() + defer span.End() + defer func() { + if err := recover(); err != nil { // catch any panic in logging + log.WithField("err", err). + Error("Panic occurred when logging validator report. This" + + " should never happen! Please file a report at github.com/prysmaticlabs/prysm/issues/new") + } + }() // Log this client performance in the previous epoch v.LogAttestationsSubmitted() if err := v.LogValidatorGainsAndLosses(slotCtx, slot); err != nil { @@ -224,7 +232,6 @@ func run(ctx context.Context, v iface.Validator) { if err := v.LogNextDutyTimeLeft(slot); err != nil { log.WithError(err).Error("Could not report next count down") } - span.End() }() } } From 13cdb835910bf763f6e2d2d75767842c2e6ada7f Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Wed, 29 Sep 2021 16:25:45 -0500 Subject: [PATCH 21/25] Validator Changes for Optimized Slasher (#9705) * val changes * mock * mocks * gomock any * gaz Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- beacon-chain/core/blocks/BUILD.bazel | 2 +- hack/update-mockgen.sh | 5 +- testing/mock/BUILD.bazel | 1 + testing/mock/slasher_client_mock.go | 97 ++++++++++++ validator/client/BUILD.bazel | 2 +- validator/client/attest_protect.go | 8 +- validator/client/attest_protect_test.go | 52 +++++-- validator/client/iface/validator.go | 1 - validator/client/propose.go | 24 +-- validator/client/propose_protect.go | 35 ++--- validator/client/propose_protect_test.go | 147 ++++++++++-------- validator/client/propose_test.go | 37 +++-- validator/client/runner.go | 6 - validator/client/runner_test.go | 12 -- validator/client/service.go | 5 - .../slashing_protection_interchange_test.go | 19 +-- validator/client/validator.go | 42 +---- validator/node/BUILD.bazel | 2 - validator/node/node.go | 41 ----- 19 files changed, 278 insertions(+), 260 deletions(-) create mode 100644 testing/mock/slasher_client_mock.go diff --git a/beacon-chain/core/blocks/BUILD.bazel b/beacon-chain/core/blocks/BUILD.bazel index 8e8547306782..c921a24a3d13 100644 --- a/beacon-chain/core/blocks/BUILD.bazel +++ b/beacon-chain/core/blocks/BUILD.bazel @@ -21,7 +21,7 @@ go_library( "//testing/fuzz:__pkg__", "//testing/spectest:__subpackages__", "//testing/util:__pkg__", - "//validator/accounts:__pkg__", + "//validator:__subpackages__", ], deps = [ "//beacon-chain/core:go_default_library", diff --git a/hack/update-mockgen.sh b/hack/update-mockgen.sh index c84fbad04c9d..8d7e5b3fcb92 100755 --- a/hack/update-mockgen.sh +++ b/hack/update-mockgen.sh @@ -3,12 +3,13 @@ # Script to update mock files after proto/prysm/v1alpha1/services.proto changes. # Use a space to separate mock destination from its interfaces. -mock_path="shared/mock" +mock_path="testing/mock" mocks=( "$mock_path/beacon_service_mock.go BeaconChainClient,BeaconChain_StreamChainHeadClient,BeaconChain_StreamAttestationsClient,BeaconChain_StreamBlocksClient,BeaconChain_StreamValidatorsInfoClient,BeaconChain_StreamIndexedAttestationsClient" "$mock_path/beacon_chain_service_mock.go BeaconChain_StreamChainHeadServer,BeaconChain_StreamAttestationsServer,BeaconChain_StreamBlocksServer,BeaconChain_StreamValidatorsInfoServer,BeaconChain_StreamIndexedAttestationsServer" "$mock_path/beacon_validator_server_mock.go BeaconNodeValidatorServer,BeaconNodeValidator_WaitForActivationServer,BeaconNodeValidator_WaitForChainStartServer,BeaconNodeValidator_StreamDutiesServer" "$mock_path/beacon_validator_client_mock.go BeaconNodeValidatorClient,BeaconNodeValidator_WaitForChainStartClient,BeaconNodeValidator_WaitForActivationClient,BeaconNodeValidator_StreamDutiesClient" + "$mock_path/slasher_client_mock.go SlasherClient" "$mock_path/event_service_mock.go EventsClient,Events_StreamEventsClient,Events_StreamEventsServer" "$mock_path/node_service_mock.go NodeClient" "$mock_path/keymanager_mock.go RemoteSignerClient" @@ -19,8 +20,8 @@ for ((i = 0; i < ${#mocks[@]}; i++)); do interfaces=${mocks[i]#* }; echo "generating $file for interfaces: $interfaces"; GO11MODULE=on mockgen -package=mock -destination="$file" github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1 "$interfaces" - GO11MODULE=on mockgen -package=mock -destination="$file" github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1 "$interfaces" done goimports -w "$mock_path/." gofmt -s -w "$mock_path/." + diff --git a/testing/mock/BUILD.bazel b/testing/mock/BUILD.bazel index 224159376038..ed0cd34a6a42 100644 --- a/testing/mock/BUILD.bazel +++ b/testing/mock/BUILD.bazel @@ -16,6 +16,7 @@ go_library( "event_service_mock.go", "keymanager_mock.go", "node_service_mock.go", + "slasher_client_mock.go", ], importpath = "github.com/prysmaticlabs/prysm/testing/mock", visibility = ["//visibility:public"], diff --git a/testing/mock/slasher_client_mock.go b/testing/mock/slasher_client_mock.go new file mode 100644 index 000000000000..01af5c4c0487 --- /dev/null +++ b/testing/mock/slasher_client_mock.go @@ -0,0 +1,97 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1 (interfaces: SlasherClient) + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + grpc "google.golang.org/grpc" +) + +// MockSlasherClient is a mock of SlasherClient interface. +type MockSlasherClient struct { + ctrl *gomock.Controller + recorder *MockSlasherClientMockRecorder +} + +// MockSlasherClientMockRecorder is the mock recorder for MockSlasherClient. +type MockSlasherClientMockRecorder struct { + mock *MockSlasherClient +} + +// NewMockSlasherClient creates a new mock instance. +func NewMockSlasherClient(ctrl *gomock.Controller) *MockSlasherClient { + mock := &MockSlasherClient{ctrl: ctrl} + mock.recorder = &MockSlasherClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSlasherClient) EXPECT() *MockSlasherClientMockRecorder { + return m.recorder +} + +// HighestAttestations mocks base method. +func (m *MockSlasherClient) HighestAttestations(arg0 context.Context, arg1 *eth.HighestAttestationRequest, arg2 ...grpc.CallOption) (*eth.HighestAttestationResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "HighestAttestations", varargs...) + ret0, _ := ret[0].(*eth.HighestAttestationResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HighestAttestations indicates an expected call of HighestAttestations. +func (mr *MockSlasherClientMockRecorder) HighestAttestations(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HighestAttestations", reflect.TypeOf((*MockSlasherClient)(nil).HighestAttestations), varargs...) +} + +// IsSlashableAttestation mocks base method. +func (m *MockSlasherClient) IsSlashableAttestation(arg0 context.Context, arg1 *eth.IndexedAttestation, arg2 ...grpc.CallOption) (*eth.AttesterSlashingResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "IsSlashableAttestation", varargs...) + ret0, _ := ret[0].(*eth.AttesterSlashingResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsSlashableAttestation indicates an expected call of IsSlashableAttestation. +func (mr *MockSlasherClientMockRecorder) IsSlashableAttestation(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSlashableAttestation", reflect.TypeOf((*MockSlasherClient)(nil).IsSlashableAttestation), varargs...) +} + +// IsSlashableBlock mocks base method. +func (m *MockSlasherClient) IsSlashableBlock(arg0 context.Context, arg1 *eth.SignedBeaconBlockHeader, arg2 ...grpc.CallOption) (*eth.ProposerSlashingResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "IsSlashableBlock", varargs...) + ret0, _ := ret[0].(*eth.ProposerSlashingResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsSlashableBlock indicates an expected call of IsSlashableBlock. +func (mr *MockSlasherClientMockRecorder) IsSlashableBlock(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSlashableBlock", reflect.TypeOf((*MockSlasherClient)(nil).IsSlashableBlock), varargs...) +} diff --git a/validator/client/BUILD.bazel b/validator/client/BUILD.bazel index d845993727a6..ed96b55a6769 100644 --- a/validator/client/BUILD.bazel +++ b/validator/client/BUILD.bazel @@ -53,7 +53,6 @@ go_library( "//validator/keymanager:go_default_library", "//validator/keymanager/imported:go_default_library", "//validator/keymanager/remote:go_default_library", - "//validator/slashing-protection/iface:go_default_library", "@com_github_dgraph_io_ristretto//:go_default_library", "@com_github_ferranbt_fastssz//:go_default_library", "@com_github_grpc_ecosystem_go_grpc_middleware//:go_default_library", @@ -113,6 +112,7 @@ go_test( "//encoding/bytesutil:go_default_library", "//io/file:go_default_library", "//proto/prysm/v1alpha1:go_default_library", + "//proto/prysm/v1alpha1/block:go_default_library", "//proto/prysm/v1alpha1/validator-client:go_default_library", "//proto/prysm/v1alpha1/wrapper:go_default_library", "//runtime:go_default_library", diff --git a/validator/client/attest_protect.go b/validator/client/attest_protect.go index 7763c706dfcc..5c7a2bf8a03f 100644 --- a/validator/client/attest_protect.go +++ b/validator/client/attest_protect.go @@ -81,8 +81,12 @@ func (v *validator) slashableAttestationCheck( return errors.Wrap(err, "could not save attestation history for validator public key") } - if features.Get().RemoteSlasherProtection && v.protector != nil { - if !v.protector.CheckAttestationSafety(ctx, indexedAtt) { + if features.Get().RemoteSlasherProtection { + slashing, err := v.slashingProtectionClient.IsSlashableAttestation(ctx, indexedAtt) + if err != nil { + return errors.Wrap(err, "could not check if attestation is slashable") + } + if slashing != nil && len(slashing.AttesterSlashings) > 0 { if v.emitAccountMetrics { ValidatorAttestFailVecSlasher.WithLabelValues(fmtKey).Inc() } diff --git a/validator/client/attest_protect_test.go b/validator/client/attest_protect_test.go index d5d6b07b5aa5..724bd19a8298 100644 --- a/validator/client/attest_protect_test.go +++ b/validator/client/attest_protect_test.go @@ -10,7 +10,6 @@ import ( "github.com/prysmaticlabs/prysm/encoding/bytesutil" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/testing/require" - mockSlasher "github.com/prysmaticlabs/prysm/validator/testing" ) func Test_slashableAttestationCheck(t *testing.T) { @@ -19,7 +18,7 @@ func Test_slashableAttestationCheck(t *testing.T) { } reset := features.InitWithReset(config) defer reset() - validator, _, validatorKey, finish := setup(t) + validator, m, validatorKey, finish := setup(t) defer finish() pubKey := [48]byte{} copy(pubKey[:], validatorKey.PublicKey().Marshal()) @@ -39,11 +38,23 @@ func Test_slashableAttestationCheck(t *testing.T) { }, }, } - mockProtector := &mockSlasher.MockProtector{AllowAttestation: false} - validator.protector = mockProtector + + m.slasherClient.EXPECT().IsSlashableAttestation( + gomock.Any(), // ctx + att, + ).Return(ðpb.AttesterSlashingResponse{AttesterSlashings: []*ethpb.AttesterSlashing{{ + Attestation_1: ðpb.IndexedAttestation{}, + Attestation_2: ðpb.IndexedAttestation{}, + }}}, nil /*err*/) + err := validator.slashableAttestationCheck(context.Background(), att, pubKey, [32]byte{1}) require.ErrorContains(t, failedPostAttSignExternalErr, err) - mockProtector.AllowAttestation = true + + m.slasherClient.EXPECT().IsSlashableAttestation( + gomock.Any(), // ctx + att, + ).Return(ðpb.AttesterSlashingResponse{}, nil /*err*/) + err = validator.slashableAttestationCheck(context.Background(), att, pubKey, [32]byte{1}) require.NoError(t, err, "Expected allowed attestation not to throw error") } @@ -75,18 +86,23 @@ func Test_slashableAttestationCheck_UpdatesLowestSignedEpochs(t *testing.T) { }, }, } - mockProtector := &mockSlasher.MockProtector{AllowAttestation: false} - validator.protector = mockProtector + + m.slasherClient.EXPECT().IsSlashableAttestation( + gomock.Any(), // ctx + att, + ).Return(ðpb.AttesterSlashingResponse{}, nil /*err*/) + m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx ðpb.DomainRequest{Epoch: 10, Domain: []byte{1, 0, 0, 0}}, ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) _, sr, err := validator.getDomainAndSigningRoot(ctx, att.Data) require.NoError(t, err) - mockProtector.AllowAttestation = true + err = validator.slashableAttestationCheck(context.Background(), att, pubKey, sr) require.NoError(t, err) differentSigningRoot := [32]byte{2} + err = validator.slashableAttestationCheck(context.Background(), att, pubKey, differentSigningRoot) require.ErrorContains(t, "could not sign attestation", err) @@ -102,12 +118,12 @@ func Test_slashableAttestationCheck_UpdatesLowestSignedEpochs(t *testing.T) { func Test_slashableAttestationCheck_OK(t *testing.T) { config := &features.Flags{ - RemoteSlasherProtection: false, + RemoteSlasherProtection: true, } reset := features.InitWithReset(config) defer reset() ctx := context.Background() - validator, _, _, finish := setup(t) + validator, mocks, _, finish := setup(t) defer finish() att := ðpb.IndexedAttestation{ AttestingIndices: []uint64{1, 2}, @@ -127,18 +143,24 @@ func Test_slashableAttestationCheck_OK(t *testing.T) { } sr := [32]byte{1} fakePubkey := bytesutil.ToBytes48([]byte("test")) + + mocks.slasherClient.EXPECT().IsSlashableAttestation( + gomock.Any(), // ctx + att, + ).Return(ðpb.AttesterSlashingResponse{}, nil /*err*/) + err := validator.slashableAttestationCheck(ctx, att, fakePubkey, sr) require.NoError(t, err, "Expected allowed attestation not to throw error") } func Test_slashableAttestationCheck_GenesisEpoch(t *testing.T) { config := &features.Flags{ - RemoteSlasherProtection: false, + RemoteSlasherProtection: true, } reset := features.InitWithReset(config) defer reset() ctx := context.Background() - validator, _, _, finish := setup(t) + validator, mocks, _, finish := setup(t) defer finish() att := ðpb.IndexedAttestation{ AttestingIndices: []uint64{1, 2}, @@ -156,6 +178,12 @@ func Test_slashableAttestationCheck_GenesisEpoch(t *testing.T) { }, }, } + + mocks.slasherClient.EXPECT().IsSlashableAttestation( + gomock.Any(), // ctx + att, + ).Return(ðpb.AttesterSlashingResponse{}, nil /*err*/) + fakePubkey := bytesutil.ToBytes48([]byte("test")) err := validator.slashableAttestationCheck(ctx, att, fakePubkey, [32]byte{}) require.NoError(t, err, "Expected allowed attestation not to throw error") diff --git a/validator/client/iface/validator.go b/validator/client/iface/validator.go index 198999e87e97..1f46b0e456a1 100644 --- a/validator/client/iface/validator.go +++ b/validator/client/iface/validator.go @@ -36,7 +36,6 @@ type Validator interface { WaitForChainStart(ctx context.Context) error WaitForSync(ctx context.Context) error WaitForActivation(ctx context.Context, accountsChangedChan chan [][48]byte) error - SlasherReady(ctx context.Context) error CanonicalHeadSlot(ctx context.Context) (types.Slot, error) NextSlot() <-chan types.Slot SlotDeadline(slot types.Slot) time.Time diff --git a/validator/client/propose.go b/validator/client/propose.go index ac67486c672b..d9c37b1eb4f8 100644 --- a/validator/client/propose.go +++ b/validator/client/propose.go @@ -119,20 +119,13 @@ func (v *validator) proposeBlockPhase0(ctx context.Context, slot types.Slot, pub return } - if err := v.preBlockSignValidations(ctx, pubKey, wrapper.WrappedPhase0BeaconBlock(b), signingRoot); err != nil { + if err := v.slashableProposalCheck(ctx, pubKey, wrapper.WrappedPhase0SignedBeaconBlock(blk), signingRoot); err != nil { log.WithFields( blockLogFields(pubKey, wrapper.WrappedPhase0BeaconBlock(b), nil), ).WithError(err).Error("Failed block slashing protection check") return } - if err := v.postBlockSignUpdate(ctx, pubKey, wrapper.WrappedPhase0SignedBeaconBlock(blk), signingRoot); err != nil { - log.WithFields( - blockLogFields(pubKey, wrapper.WrappedPhase0BeaconBlock(b), sig), - ).WithError(err).Error("Failed block slashing protection check") - return - } - // Propose and broadcast block via beacon node blkResp, err := v.validatorClient.ProposeBlock(ctx, blk) if err != nil { @@ -252,16 +245,6 @@ func (v *validator) proposeBlockAltair(ctx context.Context, slot types.Slot, pub return } - if err := v.preBlockSignValidations(ctx, pubKey, wb, signingRoot); err != nil { - log.WithFields( - blockLogFields(pubKey, wb, nil), - ).WithError(err).Error("Failed block slashing protection check") - if v.emitAccountMetrics { - ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() - } - return - } - wsb, err := wrapper.WrappedAltairSignedBeaconBlock(blk) if err != nil { log.WithError(err).Error("Failed to wrap signed block") @@ -270,9 +253,10 @@ func (v *validator) proposeBlockAltair(ctx context.Context, slot types.Slot, pub } return } - if err := v.postBlockSignUpdate(ctx, pubKey, wsb, signingRoot); err != nil { + + if err := v.slashableProposalCheck(ctx, pubKey, wsb, signingRoot); err != nil { log.WithFields( - blockLogFields(pubKey, wb, sig), + blockLogFields(pubKey, wb, nil), ).WithError(err).Error("Failed block slashing protection check") if v.emitAccountMetrics { ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() diff --git a/validator/client/propose_protect.go b/validator/client/propose_protect.go index 1c742285fbd1..662f617feff7 100644 --- a/validator/client/propose_protect.go +++ b/validator/client/propose_protect.go @@ -11,15 +11,15 @@ import ( "github.com/sirupsen/logrus" ) -var failedPreBlockSignLocalErr = "attempted to sign a double proposal, block rejected by local protection" -var failedPreBlockSignExternalErr = "attempted a double proposal, block rejected by remote slashing protection" -var failedPostBlockSignErr = "made a double proposal, considered slashable by remote slashing protection" +var failedBlockSignLocalErr = "attempted to sign a double proposal, block rejected by local protection" +var failedBlockSignExternalErr = "attempted a double proposal, block rejected by remote slashing protection" -func (v *validator) preBlockSignValidations( - ctx context.Context, pubKey [48]byte, blk block.BeaconBlock, signingRoot [32]byte, +func (v *validator) slashableProposalCheck( + ctx context.Context, pubKey [48]byte, signedBlock block.SignedBeaconBlock, signingRoot [32]byte, ) error { fmtKey := fmt.Sprintf("%#x", pubKey[:]) + blk := signedBlock.Block() prevSigningRoot, proposalAtSlotExists, err := v.db.ProposalHistoryForSlot(ctx, pubKey, blk.Slot()) if err != nil { if v.emitAccountMetrics { @@ -42,7 +42,7 @@ func (v *validator) preBlockSignValidations( if v.emitAccountMetrics { ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() } - return errors.New(failedPreBlockSignLocalErr) + return errors.New(failedBlockSignLocalErr) } // Based on EIP3076, validator should refuse to sign any proposal with slot less @@ -56,29 +56,24 @@ func (v *validator) preBlockSignValidations( blk.Slot(), ) } - return nil -} -func (v *validator) postBlockSignUpdate( - ctx context.Context, - pubKey [48]byte, - blk block.SignedBeaconBlock, - signingRoot [32]byte, -) error { - fmtKey := fmt.Sprintf("%#x", pubKey[:]) - if features.Get().RemoteSlasherProtection && v.protector != nil { - blockHdr, err := block.SignedBeaconBlockHeaderFromBlockInterface(blk) + if features.Get().RemoteSlasherProtection { + blockHdr, err := block.SignedBeaconBlockHeaderFromBlockInterface(signedBlock) if err != nil { return errors.Wrap(err, "failed to get block header from block") } - if !v.protector.CheckBlockSafety(ctx, blockHdr) { + slashing, err := v.slashingProtectionClient.IsSlashableBlock(ctx, blockHdr) + if err != nil { + return errors.Wrap(err, "could not check if block is slashable") + } + if slashing != nil && len(slashing.ProposerSlashings) > 0 { if v.emitAccountMetrics { ValidatorProposeFailVecSlasher.WithLabelValues(fmtKey).Inc() } - return errors.New(failedPostBlockSignErr) + return errors.New(failedBlockSignExternalErr) } } - if err := v.db.SaveProposalHistoryForSlot(ctx, pubKey, blk.Block().Slot(), signingRoot[:]); err != nil { + if err := v.db.SaveProposalHistoryForSlot(ctx, pubKey, blk.Slot(), signingRoot[:]); err != nil { if v.emitAccountMetrics { ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc() } diff --git a/validator/client/propose_protect_test.go b/validator/client/propose_protect_test.go index 05bb0aa28a5c..e2cd1ac96d50 100644 --- a/validator/client/propose_protect_test.go +++ b/validator/client/propose_protect_test.go @@ -4,16 +4,18 @@ import ( "context" "testing" + "github.com/golang/mock/gomock" types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/config/features" + "github.com/prysmaticlabs/prysm/config/params" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block" "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper" "github.com/prysmaticlabs/prysm/testing/require" "github.com/prysmaticlabs/prysm/testing/util" - mockSlasher "github.com/prysmaticlabs/prysm/validator/testing" ) -func TestPreBlockSignLocalValidation_PreventsLowerThanMinProposal(t *testing.T) { +func Test_slashableProposalCheck_PreventsLowerThanMinProposal(t *testing.T) { ctx := context.Background() validator, _, validatorKey, finish := setup(t) defer finish() @@ -28,55 +30,64 @@ func TestPreBlockSignLocalValidation_PreventsLowerThanMinProposal(t *testing.T) // We expect the same block with a slot lower than the lowest // signed slot to fail validation. - block := ðpb.BeaconBlock{ - Slot: lowestSignedSlot - 1, - ProposerIndex: 0, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: lowestSignedSlot - 1, + ProposerIndex: 0, + }, + Signature: params.BeaconConfig().EmptySignature[:], } - err = validator.preBlockSignValidations(context.Background(), pubKeyBytes, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{4}) + err = validator.slashableProposalCheck(context.Background(), pubKeyBytes, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{4}) require.ErrorContains(t, "could not sign block with slot <= lowest signed", err) // We expect the same block with a slot equal to the lowest // signed slot to pass validation if signing roots are equal. - block = ðpb.BeaconBlock{ - Slot: lowestSignedSlot, - ProposerIndex: 0, + block = ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: lowestSignedSlot, + ProposerIndex: 0, + }, + Signature: params.BeaconConfig().EmptySignature[:], } - err = validator.preBlockSignValidations(context.Background(), pubKeyBytes, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{1}) + err = validator.slashableProposalCheck(context.Background(), pubKeyBytes, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{1}) require.NoError(t, err) // We expect the same block with a slot equal to the lowest // signed slot to fail validation if signing roots are different. - block = ðpb.BeaconBlock{ - Slot: lowestSignedSlot, - ProposerIndex: 0, - } - err = validator.preBlockSignValidations(context.Background(), pubKeyBytes, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{4}) - require.ErrorContains(t, failedPreBlockSignLocalErr, err) + err = validator.slashableProposalCheck(context.Background(), pubKeyBytes, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{4}) + require.ErrorContains(t, failedBlockSignLocalErr, err) // We expect the same block with a slot > than the lowest // signed slot to pass validation. - block = ðpb.BeaconBlock{ - Slot: lowestSignedSlot + 1, - ProposerIndex: 0, + block = ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: lowestSignedSlot + 1, + ProposerIndex: 0, + }, + Signature: params.BeaconConfig().EmptySignature[:], } - err = validator.preBlockSignValidations(context.Background(), pubKeyBytes, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{3}) + err = validator.slashableProposalCheck(context.Background(), pubKeyBytes, wrapper.WrappedPhase0SignedBeaconBlock(block), [32]byte{3}) require.NoError(t, err) } -func TestPreBlockSignLocalValidation(t *testing.T) { +func Test_slashableProposalCheck(t *testing.T) { ctx := context.Background() config := &features.Flags{ - RemoteSlasherProtection: false, + RemoteSlasherProtection: true, } reset := features.InitWithReset(config) defer reset() - validator, _, validatorKey, finish := setup(t) + validator, mocks, validatorKey, finish := setup(t) defer finish() - block := ðpb.BeaconBlock{ - Slot: 10, - ProposerIndex: 0, - } + blk := util.HydrateSignedBeaconBlock(ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 10, + ProposerIndex: 0, + }, + Signature: params.BeaconConfig().EmptySignature[:], + }) + pubKeyBytes := [48]byte{} copy(pubKeyBytes[:], validatorKey.PublicKey().Marshal()) @@ -90,65 +101,77 @@ func TestPreBlockSignLocalValidation(t *testing.T) { require.NoError(t, err) pubKey := [48]byte{} copy(pubKey[:], validatorKey.PublicKey().Marshal()) + sBlock := wrapper.WrappedPhase0SignedBeaconBlock(blk) + blockHdr, err := block.SignedBeaconBlockHeaderFromBlockInterface(sBlock) + require.NoError(t, err) + + mocks.slasherClient.EXPECT().IsSlashableBlock( + gomock.Any(), // ctx + blockHdr, + ).Return(ðpb.ProposerSlashingResponse{}, nil /*err*/) // We expect the same block sent out with the same root should not be slasahble. - err = validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block), dummySigningRoot) + err = validator.slashableProposalCheck(context.Background(), pubKey, sBlock, dummySigningRoot) require.NoError(t, err) // We expect the same block sent out with a different signing root should be slasahble. - err = validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{2}) - require.ErrorContains(t, failedPreBlockSignLocalErr, err) + err = validator.slashableProposalCheck(context.Background(), pubKey, sBlock, [32]byte{2}) + require.ErrorContains(t, failedBlockSignLocalErr, err) // We save a proposal at slot 11 with a nil signing root. - block.Slot = 11 - err = validator.db.SaveProposalHistoryForSlot(ctx, pubKeyBytes, block.Slot, nil) + blk.Block.Slot = 11 + sBlock = wrapper.WrappedPhase0SignedBeaconBlock(blk) + err = validator.db.SaveProposalHistoryForSlot(ctx, pubKeyBytes, blk.Block.Slot, nil) require.NoError(t, err) // We expect the same block sent out should return slashable error even // if we had a nil signing root stored in the database. - err = validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{2}) - require.ErrorContains(t, failedPreBlockSignLocalErr, err) + err = validator.slashableProposalCheck(context.Background(), pubKey, sBlock, [32]byte{2}) + require.ErrorContains(t, failedBlockSignLocalErr, err) // A block with a different slot for which we do not have a proposing history // should not be failing validation. - block.Slot = 9 - err = validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block), [32]byte{3}) - require.NoError(t, err, "Expected allowed block not to throw error") -} - -func TestPreBlockSignValidation(t *testing.T) { - validator, _, validatorKey, finish := setup(t) - defer finish() - pubKey := [48]byte{} - copy(pubKey[:], validatorKey.PublicKey().Marshal()) - - block := util.NewBeaconBlock() - block.Block.Slot = 10 - mockProtector := &mockSlasher.MockProtector{AllowBlock: false} - validator.protector = mockProtector - mockProtector.AllowBlock = true - err := validator.preBlockSignValidations(context.Background(), pubKey, wrapper.WrappedPhase0BeaconBlock(block.Block), [32]byte{2}) + blk.Block.Slot = 9 + sBlock = wrapper.WrappedPhase0SignedBeaconBlock(blk) + blockHdr, err = block.SignedBeaconBlockHeaderFromBlockInterface(sBlock) + require.NoError(t, err) + mocks.slasherClient.EXPECT().IsSlashableBlock( + gomock.Any(), // ctx + blockHdr, + ).Return(ðpb.ProposerSlashingResponse{}, nil /*err*/) + err = validator.slashableProposalCheck(context.Background(), pubKey, sBlock, [32]byte{3}) require.NoError(t, err, "Expected allowed block not to throw error") } -func TestPostBlockSignUpdate(t *testing.T) { +func Test_slashableProposalCheck_RemoteProtection(t *testing.T) { config := &features.Flags{ RemoteSlasherProtection: true, } reset := features.InitWithReset(config) defer reset() - validator, _, validatorKey, finish := setup(t) + validator, m, validatorKey, finish := setup(t) defer finish() pubKey := [48]byte{} copy(pubKey[:], validatorKey.PublicKey().Marshal()) - emptyBlock := util.NewBeaconBlock() - emptyBlock.Block.Slot = 10 - emptyBlock.Block.ProposerIndex = 0 - mockProtector := &mockSlasher.MockProtector{AllowBlock: false} - validator.protector = mockProtector - err := validator.postBlockSignUpdate(context.Background(), pubKey, wrapper.WrappedPhase0SignedBeaconBlock(emptyBlock), [32]byte{}) - require.ErrorContains(t, failedPostBlockSignErr, err, "Expected error when post signature update is detected as slashable") - mockProtector.AllowBlock = true - err = validator.postBlockSignUpdate(context.Background(), pubKey, wrapper.WrappedPhase0SignedBeaconBlock(emptyBlock), [32]byte{}) + + blk := util.NewBeaconBlock() + blk.Block.Slot = 10 + sBlock := wrapper.WrappedPhase0SignedBeaconBlock(blk) + blockHdr, err := block.SignedBeaconBlockHeaderFromBlockInterface(sBlock) + require.NoError(t, err) + m.slasherClient.EXPECT().IsSlashableBlock( + gomock.Any(), // ctx + blockHdr, + ).Return(ðpb.ProposerSlashingResponse{ProposerSlashings: []*ethpb.ProposerSlashing{{}}}, nil /*err*/) + + err = validator.slashableProposalCheck(context.Background(), pubKey, sBlock, [32]byte{2}) + require.ErrorContains(t, failedBlockSignExternalErr, err) + + m.slasherClient.EXPECT().IsSlashableBlock( + gomock.Any(), // ctx + blockHdr, + ).Return(ðpb.ProposerSlashingResponse{}, nil /*err*/) + + err = validator.slashableProposalCheck(context.Background(), pubKey, sBlock, [32]byte{2}) require.NoError(t, err, "Expected allowed block not to throw error") } diff --git a/validator/client/propose_test.go b/validator/client/propose_test.go index 1fb18c5e56f2..b3902476d782 100644 --- a/validator/client/propose_test.go +++ b/validator/client/propose_test.go @@ -18,7 +18,7 @@ import ( validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client" "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper" "github.com/prysmaticlabs/prysm/testing/assert" - mock2 "github.com/prysmaticlabs/prysm/testing/mock" + mock "github.com/prysmaticlabs/prysm/testing/mock" "github.com/prysmaticlabs/prysm/testing/require" "github.com/prysmaticlabs/prysm/testing/util" testing2 "github.com/prysmaticlabs/prysm/validator/db/testing" @@ -29,8 +29,9 @@ import ( ) type mocks struct { - validatorClient *mock2.MockBeaconNodeValidatorClient - nodeClient *mock2.MockNodeClient + validatorClient *mock.MockBeaconNodeValidatorClient + nodeClient *mock.MockNodeClient + slasherClient *mock.MockSlasherClient signExitFunc func(context.Context, *validatorpb.SignRequest) (bls.Signature, error) } @@ -67,8 +68,9 @@ func setupWithKey(t *testing.T, validatorKey bls.SecretKey) (*validator, *mocks, valDB := testing2.SetupDB(t, [][48]byte{pubKey}) ctrl := gomock.NewController(t) m := &mocks{ - validatorClient: mock2.NewMockBeaconNodeValidatorClient(ctrl), - nodeClient: mock2.NewMockNodeClient(ctrl), + validatorClient: mock.NewMockBeaconNodeValidatorClient(ctrl), + nodeClient: mock.NewMockNodeClient(ctrl), + slasherClient: mock.NewMockSlasherClient(ctrl), signExitFunc: func(ctx context.Context, req *validatorpb.SignRequest) (bls.Signature, error) { return mockSignature{}, nil }, @@ -85,6 +87,7 @@ func setupWithKey(t *testing.T, validatorKey bls.SecretKey) (*validator, *mocks, db: valDB, keyManager: km, validatorClient: m.validatorClient, + slashingProtectionClient: m.slasherClient, graffiti: []byte{}, attLogs: make(map[[32]byte]*attSubmitted), aggregatedSlotCommitteeIDCache: aggregatedSlotCommitteeIDCache, @@ -296,10 +299,10 @@ func TestProposeBlock_BlocksDoubleProposal(t *testing.T) { ).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/) validator.ProposeBlock(context.Background(), slot, pubKey) - require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) + require.LogsDoNotContain(t, hook, failedBlockSignLocalErr) validator.ProposeBlock(context.Background(), slot, pubKey) - require.LogsContain(t, hook, failedPreBlockSignLocalErr) + require.LogsContain(t, hook, failedBlockSignLocalErr) } func TestProposeBlockAltair_BlocksDoubleProposal(t *testing.T) { @@ -356,10 +359,10 @@ func TestProposeBlockAltair_BlocksDoubleProposal(t *testing.T) { ).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/) validator.ProposeBlock(context.Background(), slot, pubKey) - require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) + require.LogsDoNotContain(t, hook, failedBlockSignLocalErr) validator.ProposeBlock(context.Background(), slot, pubKey) - require.LogsContain(t, hook, failedPreBlockSignLocalErr) + require.LogsContain(t, hook, failedBlockSignLocalErr) } func TestProposeBlock_BlocksDoubleProposal_After54KEpochs(t *testing.T) { @@ -408,10 +411,10 @@ func TestProposeBlock_BlocksDoubleProposal_After54KEpochs(t *testing.T) { ).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/) validator.ProposeBlock(context.Background(), farFuture, pubKey) - require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) + require.LogsDoNotContain(t, hook, failedBlockSignLocalErr) validator.ProposeBlock(context.Background(), farFuture, pubKey) - require.LogsContain(t, hook, failedPreBlockSignLocalErr) + require.LogsContain(t, hook, failedBlockSignLocalErr) } func TestProposeBlock_AllowsPastProposals(t *testing.T) { @@ -449,7 +452,7 @@ func TestProposeBlock_AllowsPastProposals(t *testing.T) { ).Times(2).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/) validator.ProposeBlock(context.Background(), farAhead, pubKey) - require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) + require.LogsDoNotContain(t, hook, failedBlockSignLocalErr) past := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().WeakSubjectivityPeriod - 400)) blk2 := util.NewBeaconBlock() @@ -459,7 +462,7 @@ func TestProposeBlock_AllowsPastProposals(t *testing.T) { gomock.Any(), ).Return(blk2.Block, nil /*err*/) validator.ProposeBlock(context.Background(), past, pubKey) - require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) + require.LogsDoNotContain(t, hook, failedBlockSignLocalErr) } func TestProposeBlock_AllowsSameEpoch(t *testing.T) { @@ -497,7 +500,7 @@ func TestProposeBlock_AllowsSameEpoch(t *testing.T) { ).Times(2).Return(ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil /*error*/) validator.ProposeBlock(context.Background(), farAhead, pubKey) - require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) + require.LogsDoNotContain(t, hook, failedBlockSignLocalErr) blk2 := util.NewBeaconBlock() blk2.Block.Slot = farAhead - 4 @@ -507,7 +510,7 @@ func TestProposeBlock_AllowsSameEpoch(t *testing.T) { ).Return(blk2.Block, nil /*err*/) validator.ProposeBlock(context.Background(), farAhead-4, pubKey) - require.LogsDoNotContain(t, hook, failedPreBlockSignLocalErr) + require.LogsDoNotContain(t, hook, failedBlockSignLocalErr) } func TestProposeBlock_BroadcastsBlock(t *testing.T) { @@ -875,7 +878,7 @@ func TestSignAltairBlock(t *testing.T) { func TestGetGraffiti_Ok(t *testing.T) { ctrl := gomock.NewController(t) m := &mocks{ - validatorClient: mock2.NewMockBeaconNodeValidatorClient(ctrl), + validatorClient: mock.NewMockBeaconNodeValidatorClient(ctrl), } pubKey := [48]byte{'a'} tests := []struct { @@ -958,7 +961,7 @@ func TestGetGraffitiOrdered_Ok(t *testing.T) { valDB := testing2.SetupDB(t, [][48]byte{pubKey}) ctrl := gomock.NewController(t) m := &mocks{ - validatorClient: mock2.NewMockBeaconNodeValidatorClient(ctrl), + validatorClient: mock.NewMockBeaconNodeValidatorClient(ctrl), } m.validatorClient.EXPECT(). ValidatorIndex(gomock.Any(), ðpb.ValidatorIndexRequest{PublicKey: pubKey[:]}). diff --git a/validator/client/runner.go b/validator/client/runner.go index 13f6a1add581..03f6fd502acd 100644 --- a/validator/client/runner.go +++ b/validator/client/runner.go @@ -9,7 +9,6 @@ import ( "github.com/pkg/errors" types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/beacon-chain/core" - "github.com/prysmaticlabs/prysm/config/features" "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/validator/client/iface" @@ -40,11 +39,6 @@ func run(ctx context.Context, v iface.Validator) { cleanup() log.Fatalf("Wallet is not ready: %v", err) } - if features.Get().RemoteSlasherProtection { - if err := v.SlasherReady(ctx); err != nil { - log.Fatalf("Slasher is not ready: %v", err) - } - } ticker := time.NewTicker(backOffPeriod) defer ticker.Stop() diff --git a/validator/client/runner_test.go b/validator/client/runner_test.go index 6abe12d9860f..5775093f7ec8 100644 --- a/validator/client/runner_test.go +++ b/validator/client/runner_test.go @@ -8,7 +8,6 @@ import ( types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/async/event" - "github.com/prysmaticlabs/prysm/config/features" "github.com/prysmaticlabs/prysm/testing/assert" "github.com/prysmaticlabs/prysm/testing/require" "github.com/prysmaticlabs/prysm/validator/client/iface" @@ -62,17 +61,6 @@ func TestCancelledContext_WaitsForActivation(t *testing.T) { assert.Equal(t, 1, v.WaitForActivationCalled, "Expected WaitForActivation() to be called") } -func TestCancelledContext_ChecksSlasherReady(t *testing.T) { - v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} - cfg := &features.Flags{ - RemoteSlasherProtection: true, - } - reset := features.InitWithReset(cfg) - defer reset() - run(cancelledContext(), v) - assert.Equal(t, true, v.SlasherReadyCalled, "Expected SlasherReady() to be called") -} - func TestUpdateDuties_NextSlot(t *testing.T) { v := &testutil.FakeValidator{Keymanager: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} ctx, cancel := context.WithCancel(context.Background()) diff --git a/validator/client/service.go b/validator/client/service.go index e7083c438a87..15778b751988 100644 --- a/validator/client/service.go +++ b/validator/client/service.go @@ -27,7 +27,6 @@ import ( "github.com/prysmaticlabs/prysm/validator/graffiti" "github.com/prysmaticlabs/prysm/validator/keymanager" "github.com/prysmaticlabs/prysm/validator/keymanager/imported" - slashingiface "github.com/prysmaticlabs/prysm/validator/slashing-protection/iface" "go.opencensus.io/plugin/ocgrpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -64,7 +63,6 @@ type ValidatorService struct { withCert string endpoint string validator iface.Validator - protector slashingiface.Protector ctx context.Context keyManager keymanager.IKeymanager grpcHeaders []string @@ -82,7 +80,6 @@ type Config struct { GrpcRetriesFlag uint GrpcRetryDelay time.Duration GrpcMaxCallRecvMsgSizeFlag int - Protector slashingiface.Protector Endpoint string Validator iface.Validator ValDB db.Database @@ -112,7 +109,6 @@ func NewValidatorService(ctx context.Context, cfg *Config) (*ValidatorService, e grpcRetries: cfg.GrpcRetriesFlag, grpcRetryDelay: cfg.GrpcRetryDelay, grpcHeaders: strings.Split(cfg.GrpcHeadersFlag, ","), - protector: cfg.Protector, validator: cfg.Validator, db: cfg.ValDB, walletInitializedFeed: cfg.WalletInitializedFeed, @@ -188,7 +184,6 @@ func (v *ValidatorService) Start() { attLogs: make(map[[32]byte]*attSubmitted), domainDataCache: cache, aggregatedSlotCommitteeIDCache: aggregatedSlotCommitteeIDCache, - protector: v.protector, voteStats: voteStats{startEpoch: types.Epoch(^uint64(0))}, useWeb: v.useWeb, walletInitializedFeed: v.walletInitializedFeed, diff --git a/validator/client/slashing_protection_interchange_test.go b/validator/client/slashing_protection_interchange_test.go index a5ae48b78955..7df157bb9760 100644 --- a/validator/client/slashing_protection_interchange_test.go +++ b/validator/client/slashing_protection_interchange_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/bazelbuild/rules_go/go/tools/bazel" - "github.com/prysmaticlabs/prysm/config/features" "github.com/prysmaticlabs/prysm/io/file" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper" @@ -76,12 +75,6 @@ func setupEIP3076SpecTests(t *testing.T) []*eip3076TestCase { } func TestEIP3076SpecTests(t *testing.T) { - config := &features.Flags{ - RemoteSlasherProtection: true, - } - reset := features.InitWithReset(config) - defer reset() - testCases := setupEIP3076SpecTests(t) for _, tt := range testCases { t.Run(tt.Name, func(t *testing.T) { @@ -130,22 +123,12 @@ func TestEIP3076SpecTests(t *testing.T) { copy(signingRoot[:], signingRootBytes) } - err = validator.preBlockSignValidations(context.Background(), pk, wrapper.WrappedPhase0BeaconBlock(b.Block), signingRoot) + err = validator.slashableProposalCheck(context.Background(), pk, wrapper.WrappedPhase0SignedBeaconBlock(b), signingRoot) if sb.ShouldSucceed { require.NoError(t, err) } else { require.NotEqual(t, nil, err, "pre validation should have failed for block") } - - // Only proceed post update if pre validation did not error. - if err == nil { - err = validator.postBlockSignUpdate(context.Background(), pk, wrapper.WrappedPhase0SignedBeaconBlock(b), signingRoot) - if sb.ShouldSucceed { - require.NoError(t, err) - } else { - require.NotEqual(t, nil, err, "post validation should have failed for block") - } - } } // This loops through a list of attestation signings to attempt after importing the interchange data above. diff --git a/validator/client/validator.go b/validator/client/validator.go index 275370ebd3dd..fe5004d54c9b 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -36,20 +36,17 @@ import ( "github.com/prysmaticlabs/prysm/validator/db/kv" "github.com/prysmaticlabs/prysm/validator/graffiti" "github.com/prysmaticlabs/prysm/validator/keymanager" - slashingiface "github.com/prysmaticlabs/prysm/validator/slashing-protection/iface" "github.com/sirupsen/logrus" "go.opencensus.io/trace" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/emptypb" ) -// reconnectPeriod is the frequency that we try to restart our -// slasher connection when the slasher client connection is not ready. -var reconnectPeriod = 5 * time.Second - // keyFetchPeriod is the frequency that we try to refetch validating keys // in case no keys were fetched previously. -var keyRefetchPeriod = 30 * time.Second +var ( + keyRefetchPeriod = 30 * time.Second +) var ( msgCouldNotFetchKeys = "could not fetch validating keys" @@ -81,7 +78,7 @@ type validator struct { keyManager keymanager.IKeymanager beaconClient ethpb.BeaconChainClient validatorClient ethpb.BeaconNodeValidatorClient - protector slashingiface.Protector + slashingProtectionClient ethpb.SlasherClient db vdb.Database graffiti []byte voteStats voteStats @@ -224,37 +221,6 @@ func (v *validator) WaitForSync(ctx context.Context) error { } } -// SlasherReady checks if slasher that was configured as external protection -// is reachable. -func (v *validator) SlasherReady(ctx context.Context) error { - ctx, span := trace.StartSpan(ctx, "validator.SlasherReady") - defer span.End() - if features.Get().RemoteSlasherProtection { - err := v.protector.Status() - if err == nil { - return nil - } - ticker := time.NewTicker(reconnectPeriod) - defer ticker.Stop() - for { - select { - case <-ticker.C: - log.WithError(err).Info("Slasher connection wasn't ready. Trying again") - err = v.protector.Status() - if err != nil { - continue - } - log.Info("Slasher connection is ready") - return nil - case <-ctx.Done(): - log.Debug("Context closed, exiting reconnect external protection") - return ctx.Err() - } - } - } - return nil -} - // ReceiveBlocks starts a gRPC client stream listener to obtain // blocks from the beacon node. Upon receiving a block, the service // broadcasts it to a feed for other usages to subscribe to. diff --git a/validator/node/BUILD.bazel b/validator/node/BUILD.bazel index 7a14077621f8..002ee914d5c0 100644 --- a/validator/node/BUILD.bazel +++ b/validator/node/BUILD.bazel @@ -52,8 +52,6 @@ go_library( "//validator/keymanager:go_default_library", "//validator/keymanager/imported:go_default_library", "//validator/rpc:go_default_library", - "//validator/slashing-protection:go_default_library", - "//validator/slashing-protection/iface:go_default_library", "//validator/web:go_default_library", "@com_github_grpc_ecosystem_grpc_gateway_v2//runtime:go_default_library", "@com_github_pkg_errors//:go_default_library", diff --git a/validator/node/node.go b/validator/node/node.go index 5377904f0a92..6596725adef8 100644 --- a/validator/node/node.go +++ b/validator/node/node.go @@ -40,8 +40,6 @@ import ( "github.com/prysmaticlabs/prysm/validator/keymanager" "github.com/prysmaticlabs/prysm/validator/keymanager/imported" "github.com/prysmaticlabs/prysm/validator/rpc" - slashingprotection "github.com/prysmaticlabs/prysm/validator/slashing-protection" - "github.com/prysmaticlabs/prysm/validator/slashing-protection/iface" "github.com/prysmaticlabs/prysm/validator/web" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" @@ -251,9 +249,6 @@ func (c *ValidatorClient) initializeFromCLI(cliCtx *cli.Context) error { return err } } - if err := c.registerSlasherService(); err != nil { - return err - } if err := c.registerValidatorService(keyManager); err != nil { return err } @@ -338,9 +333,6 @@ func (c *ValidatorClient) initializeForWeb(cliCtx *cli.Context) error { return err } } - if err := c.registerSlasherService(); err != nil { - return err - } if err := c.registerValidatorService(keyManager); err != nil { return err } @@ -391,12 +383,6 @@ func (c *ValidatorClient) registerValidatorService( maxCallRecvMsgSize := c.cliCtx.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name) grpcRetries := c.cliCtx.Uint(flags.GrpcRetriesFlag.Name) grpcRetryDelay := c.cliCtx.Duration(flags.GrpcRetryDelayFlag.Name) - var sp *slashingprotection.Service - var protector iface.Protector - if err := c.services.FetchService(&sp); err == nil { - protector = sp - } - gStruct := &g.Graffiti{} var err error if c.cliCtx.IsSet(flags.GraffitiFileFlag.Name) { @@ -419,7 +405,6 @@ func (c *ValidatorClient) registerValidatorService( GrpcRetriesFlag: grpcRetries, GrpcRetryDelay: grpcRetryDelay, GrpcHeadersFlag: c.cliCtx.String(flags.GrpcHeadersFlag.Name), - Protector: protector, ValDB: c.db, UseWeb: c.cliCtx.Bool(flags.EnableWebFlag.Name), WalletInitializedFeed: c.walletInitialized, @@ -432,32 +417,6 @@ func (c *ValidatorClient) registerValidatorService( return c.services.RegisterService(v) } -func (c *ValidatorClient) registerSlasherService() error { - if !features.Get().RemoteSlasherProtection { - return nil - } - endpoint := c.cliCtx.String(flags.SlasherRPCProviderFlag.Name) - if endpoint == "" { - return errors.New("external slasher feature flag is set but no slasher endpoint is configured") - - } - cert := c.cliCtx.String(flags.SlasherCertFlag.Name) - maxCallRecvMsgSize := c.cliCtx.Int(cmd.GrpcMaxCallRecvMsgSizeFlag.Name) - grpcRetries := c.cliCtx.Uint(flags.GrpcRetriesFlag.Name) - grpcRetryDelay := c.cliCtx.Duration(flags.GrpcRetryDelayFlag.Name) - sp, err := slashingprotection.NewService(c.cliCtx.Context, &slashingprotection.Config{ - Endpoint: endpoint, - CertFlag: cert, - GrpcMaxCallRecvMsgSizeFlag: maxCallRecvMsgSize, - GrpcRetriesFlag: grpcRetries, - GrpcRetryDelay: grpcRetryDelay, - GrpcHeadersFlag: c.cliCtx.String(flags.GrpcHeadersFlag.Name), - }) - if err != nil { - return errors.Wrap(err, "could not initialize slasher service") - } - return c.services.RegisterService(sp) -} func (c *ValidatorClient) registerRPCService(cliCtx *cli.Context, km keymanager.IKeymanager) error { var vs *client.ValidatorService From 62dc74af2f3ccc1c13fb0f568df0fcf73eb73a8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kapka?= Date: Thu, 30 Sep 2021 00:18:10 +0200 Subject: [PATCH 22/25] Move head event's epoch transition to epoch start (#9704) Co-authored-by: Preston Van Loon --- beacon-chain/blockchain/head.go | 2 +- beacon-chain/blockchain/head_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/beacon-chain/blockchain/head.go b/beacon-chain/blockchain/head.go index 3fc59f3c183d..927ecdf20720 100644 --- a/beacon-chain/blockchain/head.go +++ b/beacon-chain/blockchain/head.go @@ -366,7 +366,7 @@ func (s *Service) notifyNewHeadEvent( Slot: newHeadSlot, Block: newHeadRoot, State: newHeadStateRoot, - EpochTransition: core.IsEpochEnd(newHeadSlot), + EpochTransition: core.IsEpochStart(newHeadSlot), PreviousDutyDependentRoot: previousDutyDependentRoot, CurrentDutyDependentRoot: currentDutyDependentRoot, }, diff --git a/beacon-chain/blockchain/head_test.go b/beacon-chain/blockchain/head_test.go index 7e268109f3a8..1d3e82661665 100644 --- a/beacon-chain/blockchain/head_test.go +++ b/beacon-chain/blockchain/head_test.go @@ -206,7 +206,7 @@ func Test_notifyNewHeadEvent(t *testing.T) { Slot: epoch2Start, Block: newHeadRoot[:], State: newHeadStateRoot[:], - EpochTransition: false, + EpochTransition: true, PreviousDutyDependentRoot: genesisRoot[:], CurrentDutyDependentRoot: make([]byte, 32), } From 7dae66afc9f188104d8e9a21ef781596abe89dde Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Wed, 29 Sep 2021 21:33:28 -0500 Subject: [PATCH 23/25] Highest Attestations Endpoint for Optimized Slasher (#9706) * begin highest atts * highest atts * deep source --- beacon-chain/db/iface/interface.go | 4 ++ .../rpc/prysm/v1alpha1/slasher/BUILD.bazel | 7 ++- .../prysm/v1alpha1/slasher/attestations.go | 13 +++- .../v1alpha1/slasher/attestations_test.go | 63 +++++++++++++++++++ beacon-chain/slasher/mock_slashing_checker.go | 16 +++++ beacon-chain/slasher/rpc.go | 13 ++++ beacon-chain/slasher/rpc_test.go | 62 ++++++++++++++++++ beacon-chain/slasher/service.go | 4 ++ 8 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 beacon-chain/rpc/prysm/v1alpha1/slasher/attestations_test.go diff --git a/beacon-chain/db/iface/interface.go b/beacon-chain/db/iface/interface.go index f409b8517d1e..61706a868aaf 100644 --- a/beacon-chain/db/iface/interface.go +++ b/beacon-chain/db/iface/interface.go @@ -147,6 +147,10 @@ type SlasherDatabase interface { PruneProposalsAtEpoch( ctx context.Context, maxEpoch types.Epoch, ) (numPruned uint, err error) + HighestAttestations( + ctx context.Context, + indices []types.ValidatorIndex, + ) ([]*ethpb.HighestAttestation, error) DatabasePath() string ClearDB() error } diff --git a/beacon-chain/rpc/prysm/v1alpha1/slasher/BUILD.bazel b/beacon-chain/rpc/prysm/v1alpha1/slasher/BUILD.bazel index 38e9907d4e38..7bcc109a99fd 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/slasher/BUILD.bazel +++ b/beacon-chain/rpc/prysm/v1alpha1/slasher/BUILD.bazel @@ -12,6 +12,7 @@ go_library( deps = [ "//beacon-chain/slasher:go_default_library", "//proto/prysm/v1alpha1:go_default_library", + "@com_github_prysmaticlabs_eth2_types//:go_default_library", "@org_golang_google_grpc//codes:go_default_library", "@org_golang_google_grpc//status:go_default_library", ], @@ -19,11 +20,15 @@ go_library( go_test( name = "go_default_test", - srcs = ["server_test.go"], + srcs = [ + "attestations_test.go", + "server_test.go", + ], embed = [":go_default_library"], deps = [ "//beacon-chain/slasher:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//testing/require:go_default_library", + "@com_github_prysmaticlabs_eth2_types//:go_default_library", ], ) diff --git a/beacon-chain/rpc/prysm/v1alpha1/slasher/attestations.go b/beacon-chain/rpc/prysm/v1alpha1/slasher/attestations.go index d34e823aa383..ba882785f639 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/slasher/attestations.go +++ b/beacon-chain/rpc/prysm/v1alpha1/slasher/attestations.go @@ -3,6 +3,7 @@ package slasher import ( "context" + types "github.com/prysmaticlabs/eth2-types" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -27,8 +28,16 @@ func (s *Server) IsSlashableAttestation( // HighestAttestations returns the highest source and target epochs attested for // validator indices that have been observed by slasher. -func (_ *Server) HighestAttestations( +func (s *Server) HighestAttestations( ctx context.Context, req *ethpb.HighestAttestationRequest, ) (*ethpb.HighestAttestationResponse, error) { - return nil, status.Error(codes.Unimplemented, "Unimplemented") + valIndices := make([]types.ValidatorIndex, len(req.ValidatorIndices)) + for i, valIdx := range req.ValidatorIndices { + valIndices[i] = types.ValidatorIndex(valIdx) + } + atts, err := s.SlashingChecker.HighestAttestations(ctx, valIndices) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get highest attestations: %v", err) + } + return ðpb.HighestAttestationResponse{Attestations: atts}, nil } diff --git a/beacon-chain/rpc/prysm/v1alpha1/slasher/attestations_test.go b/beacon-chain/rpc/prysm/v1alpha1/slasher/attestations_test.go new file mode 100644 index 000000000000..c0d42dd7301c --- /dev/null +++ b/beacon-chain/rpc/prysm/v1alpha1/slasher/attestations_test.go @@ -0,0 +1,63 @@ +package slasher + +import ( + "context" + "testing" + + types "github.com/prysmaticlabs/eth2-types" + "github.com/prysmaticlabs/prysm/beacon-chain/slasher" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/testing/require" +) + +func TestServer_HighestAttestations(t *testing.T) { + highestAtts := map[types.ValidatorIndex]*ethpb.HighestAttestation{ + 0: { + ValidatorIndex: 0, + HighestSourceEpoch: 1, + HighestTargetEpoch: 2, + }, + 1: { + ValidatorIndex: 1, + HighestSourceEpoch: 2, + HighestTargetEpoch: 3, + }, + } + mockSlasher := &slasher.MockSlashingChecker{ + HighestAtts: highestAtts, + } + s := Server{SlashingChecker: mockSlasher} + ctx := context.Background() + t.Run("single index found", func(t *testing.T) { + resp, err := s.HighestAttestations(ctx, ðpb.HighestAttestationRequest{ + ValidatorIndices: []uint64{0}, + }) + require.NoError(t, err) + require.Equal(t, 1, len(resp.Attestations)) + require.DeepEqual(t, highestAtts[0], resp.Attestations[0]) + }) + t.Run("single index not found", func(t *testing.T) { + resp, err := s.HighestAttestations(ctx, ðpb.HighestAttestationRequest{ + ValidatorIndices: []uint64{3}, + }) + require.NoError(t, err) + require.Equal(t, 0, len(resp.Attestations)) + }) + t.Run("multiple indices all found", func(t *testing.T) { + resp, err := s.HighestAttestations(ctx, ðpb.HighestAttestationRequest{ + ValidatorIndices: []uint64{0, 1}, + }) + require.NoError(t, err) + require.Equal(t, 2, len(resp.Attestations)) + require.DeepEqual(t, highestAtts[0], resp.Attestations[0]) + require.DeepEqual(t, highestAtts[1], resp.Attestations[1]) + }) + t.Run("multiple indices some not found", func(t *testing.T) { + resp, err := s.HighestAttestations(ctx, ðpb.HighestAttestationRequest{ + ValidatorIndices: []uint64{0, 3}, + }) + require.NoError(t, err) + require.Equal(t, 1, len(resp.Attestations)) + require.DeepEqual(t, highestAtts[0], resp.Attestations[0]) + }) +} diff --git a/beacon-chain/slasher/mock_slashing_checker.go b/beacon-chain/slasher/mock_slashing_checker.go index a1f260aba624..43efdcab3519 100644 --- a/beacon-chain/slasher/mock_slashing_checker.go +++ b/beacon-chain/slasher/mock_slashing_checker.go @@ -3,6 +3,7 @@ package slasher import ( "context" + types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/config/params" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" ) @@ -10,6 +11,21 @@ import ( type MockSlashingChecker struct { AttesterSlashingFound bool ProposerSlashingFound bool + HighestAtts map[types.ValidatorIndex]*ethpb.HighestAttestation +} + +func (s *MockSlashingChecker) HighestAttestations( + _ context.Context, indices []types.ValidatorIndex, +) ([]*ethpb.HighestAttestation, error) { + atts := make([]*ethpb.HighestAttestation, 0, len(indices)) + for _, valIdx := range indices { + att, ok := s.HighestAtts[valIdx] + if !ok { + continue + } + atts = append(atts, att) + } + return atts, nil } func (s *MockSlashingChecker) IsSlashableBlock(ctx context.Context, proposal *ethpb.SignedBeaconBlockHeader) (*ethpb.ProposerSlashing, error) { diff --git a/beacon-chain/slasher/rpc.go b/beacon-chain/slasher/rpc.go index 80ac93a10009..ee5a1b0a32cb 100644 --- a/beacon-chain/slasher/rpc.go +++ b/beacon-chain/slasher/rpc.go @@ -3,12 +3,25 @@ package slasher import ( "context" + "github.com/pkg/errors" + types "github.com/prysmaticlabs/eth2-types" slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) +// HighestAttestations committed for an input list of validator indices. +func (s *Service) HighestAttestations( + ctx context.Context, validatorIndices []types.ValidatorIndex, +) ([]*ethpb.HighestAttestation, error) { + atts, err := s.serviceCfg.Database.HighestAttestations(ctx, validatorIndices) + if err != nil { + return nil, errors.Wrap(err, "could not get highest attestations from database") + } + return atts, nil +} + // IsSlashableBlock checks if an input block header is slashable // with respect to historical block proposal data. func (s *Service) IsSlashableBlock( diff --git a/beacon-chain/slasher/rpc_test.go b/beacon-chain/slasher/rpc_test.go index 872a17baad7b..03ece4459df0 100644 --- a/beacon-chain/slasher/rpc_test.go +++ b/beacon-chain/slasher/rpc_test.go @@ -9,6 +9,7 @@ import ( dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types" "github.com/prysmaticlabs/prysm/config/params" + ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/testing/assert" "github.com/prysmaticlabs/prysm/testing/require" ) @@ -135,3 +136,64 @@ func TestIsSlashableAttestation(t *testing.T) { }) } } + +func TestService_HighestAttestations(t *testing.T) { + ctx := context.Background() + slasherDB := dbtest.SetupSlasherDB(t) + + currentEpoch := types.Epoch(3) + currentTime := time.Now() + totalSlots := uint64(currentEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch) + secondsSinceGenesis := time.Duration(totalSlots * params.BeaconConfig().SecondsPerSlot) + genesisTime := currentTime.Add(-secondsSinceGenesis * time.Second) + + s := &Service{ + serviceCfg: &ServiceConfig{ + Database: slasherDB, + }, + params: DefaultParams(), + blksQueue: newBlocksQueue(), + genesisTime: genesisTime, + } + prevAtts := []*slashertypes.IndexedAttestationWrapper{ + createAttestationWrapper(t, 0, 1, []uint64{1}, []byte{1}), + createAttestationWrapper(t, 2, 3, []uint64{2}, []byte{1}), + } + err := slasherDB.SaveAttestationRecordsForValidators(ctx, prevAtts) + require.NoError(t, err) + t.Run("single index not found", func(t *testing.T) { + atts, err := s.HighestAttestations(ctx, []types.ValidatorIndex{0}) + require.NoError(t, err) + require.Equal(t, 0, len(atts)) + }) + t.Run("single index case 1", func(t *testing.T) { + atts, err := s.HighestAttestations(ctx, []types.ValidatorIndex{1}) + require.NoError(t, err) + require.Equal(t, 1, len(atts)) + require.DeepEqual(t, ðpb.HighestAttestation{ValidatorIndex: 1, HighestSourceEpoch: 0, HighestTargetEpoch: 1}, atts[0]) + }) + t.Run("single index case 2", func(t *testing.T) { + atts, err := s.HighestAttestations(ctx, []types.ValidatorIndex{2}) + require.NoError(t, err) + require.Equal(t, 1, len(atts)) + require.DeepEqual(t, ðpb.HighestAttestation{ValidatorIndex: 2, HighestSourceEpoch: 2, HighestTargetEpoch: 3}, atts[0]) + }) + t.Run("multiple indices all found", func(t *testing.T) { + atts, err := s.HighestAttestations(ctx, []types.ValidatorIndex{1, 2}) + require.NoError(t, err) + require.Equal(t, 2, len(atts)) + require.DeepEqual(t, ðpb.HighestAttestation{ValidatorIndex: 1, HighestSourceEpoch: 0, HighestTargetEpoch: 1}, atts[0]) + require.DeepEqual(t, ðpb.HighestAttestation{ValidatorIndex: 2, HighestSourceEpoch: 2, HighestTargetEpoch: 3}, atts[1]) + }) + t.Run("multiple indices all not found", func(t *testing.T) { + atts, err := s.HighestAttestations(ctx, []types.ValidatorIndex{3, 4}) + require.NoError(t, err) + require.Equal(t, 0, len(atts)) + }) + t.Run("multiple indices some not found", func(t *testing.T) { + atts, err := s.HighestAttestations(ctx, []types.ValidatorIndex{1, 4}) + require.NoError(t, err) + require.Equal(t, 1, len(atts)) + require.DeepEqual(t, ðpb.HighestAttestation{ValidatorIndex: 1, HighestSourceEpoch: 0, HighestTargetEpoch: 1}, atts[0]) + }) +} diff --git a/beacon-chain/slasher/service.go b/beacon-chain/slasher/service.go index 66f252c81dc1..cefd79cef87f 100644 --- a/beacon-chain/slasher/service.go +++ b/beacon-chain/slasher/service.go @@ -8,6 +8,7 @@ import ( "context" "time" + types "github.com/prysmaticlabs/eth2-types" "github.com/prysmaticlabs/prysm/async/event" "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" @@ -40,6 +41,9 @@ type ServiceConfig struct { type SlashingChecker interface { IsSlashableBlock(ctx context.Context, proposal *ethpb.SignedBeaconBlockHeader) (*ethpb.ProposerSlashing, error) IsSlashableAttestation(ctx context.Context, attestation *ethpb.IndexedAttestation) ([]*ethpb.AttesterSlashing, error) + HighestAttestations( + ctx context.Context, indices []types.ValidatorIndex, + ) ([]*ethpb.HighestAttestation, error) } // Service defining a slasher implementation as part of From 3c052d917f36567ed89aea1ba85cfb6cb8fbe63e Mon Sep 17 00:00:00 2001 From: Nishant Das Date: Thu, 30 Sep 2021 11:39:53 +0800 Subject: [PATCH 24/25] Use Scoring Parameters To Enable Global Score (#8794) * fix score parameters * add check * fix deadlock issues * fix * fix again * gaz * comment * fix tests and victor's review * gaz * clean up * fix tests * fix tests * gate behind flag * add scorer to dev Co-authored-by: prestonvanloon Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> --- beacon-chain/p2p/peers/BUILD.bazel | 1 + .../p2p/peers/scorers/bad_responses.go | 8 +- .../p2p/peers/scorers/bad_responses_test.go | 6 +- .../p2p/peers/scorers/gossip_scorer.go | 7 +- .../p2p/peers/scorers/gossip_scorer_test.go | 4 +- beacon-chain/p2p/peers/scorers/peer_status.go | 2 + beacon-chain/p2p/peers/scorers/service.go | 32 +++-- .../p2p/peers/scorers/service_test.go | 41 +++--- beacon-chain/p2p/peers/status.go | 134 +++++++++++++++++- beacon-chain/p2p/peers/status_test.go | 9 ++ beacon-chain/sync/rpc_goodbye.go | 6 +- beacon-chain/sync/validate_aggregate_proof.go | 4 + .../sync/validate_aggregate_proof_test.go | 6 +- .../sync/validate_beacon_attestation.go | 5 +- config/features/flags.go | 1 + 15 files changed, 218 insertions(+), 48 deletions(-) diff --git a/beacon-chain/p2p/peers/BUILD.bazel b/beacon-chain/p2p/peers/BUILD.bazel index 7d5f899d3b4a..df1a77fbb22d 100644 --- a/beacon-chain/p2p/peers/BUILD.bazel +++ b/beacon-chain/p2p/peers/BUILD.bazel @@ -9,6 +9,7 @@ go_library( "//beacon-chain/core:go_default_library", "//beacon-chain/p2p/peers/peerdata:go_default_library", "//beacon-chain/p2p/peers/scorers:go_default_library", + "//config/features:go_default_library", "//config/params:go_default_library", "//crypto/rand:go_default_library", "//proto/prysm/v1alpha1:go_default_library", diff --git a/beacon-chain/p2p/peers/scorers/bad_responses.go b/beacon-chain/p2p/peers/scorers/bad_responses.go index f9375630b430..3219d0faee89 100644 --- a/beacon-chain/p2p/peers/scorers/bad_responses.go +++ b/beacon-chain/p2p/peers/scorers/bad_responses.go @@ -15,6 +15,9 @@ const ( // DefaultBadResponsesDecayInterval defines how often to decay previous statistics. // Every interval bad responses counter will be decremented by 1. DefaultBadResponsesDecayInterval = time.Hour + // DefaultBadResponsesPenaltyFactor defines the penalty factor applied to a peer based on their bad + // response count. + DefaultBadResponsesPenaltyFactor = 10 ) // BadResponsesScorer represents bad responses scoring service. @@ -68,8 +71,9 @@ func (s *BadResponsesScorer) score(pid peer.ID) float64 { } if peerData.BadResponses > 0 { score = float64(peerData.BadResponses) / float64(s.config.Threshold) - // Since score represents a penalty, negate it. - score *= -1 + // Since score represents a penalty, negate it and multiply + // it by a factor. + score *= -DefaultBadResponsesPenaltyFactor } return score } diff --git a/beacon-chain/p2p/peers/scorers/bad_responses_test.go b/beacon-chain/p2p/peers/scorers/bad_responses_test.go index a8a413ae5e80..41652144baf9 100644 --- a/beacon-chain/p2p/peers/scorers/bad_responses_test.go +++ b/beacon-chain/p2p/peers/scorers/bad_responses_test.go @@ -30,12 +30,12 @@ func TestScorers_BadResponses_Score(t *testing.T) { assert.Equal(t, 0.0, scorer.Score("peer1"), "Unexpected score for unregistered peer") scorer.Increment("peer1") - assert.Equal(t, -0.25, scorer.Score("peer1")) + assert.Equal(t, -2.5, scorer.Score("peer1")) scorer.Increment("peer1") - assert.Equal(t, -0.5, scorer.Score("peer1")) + assert.Equal(t, float64(-5), scorer.Score("peer1")) scorer.Increment("peer1") scorer.Increment("peer1") - assert.Equal(t, -1.0, scorer.Score("peer1")) + assert.Equal(t, -100.0, scorer.Score("peer1")) assert.Equal(t, true, scorer.IsBadPeer("peer1")) } diff --git a/beacon-chain/p2p/peers/scorers/gossip_scorer.go b/beacon-chain/p2p/peers/scorers/gossip_scorer.go index 37c75c867ace..2e7145653550 100644 --- a/beacon-chain/p2p/peers/scorers/gossip_scorer.go +++ b/beacon-chain/p2p/peers/scorers/gossip_scorer.go @@ -8,6 +8,11 @@ import ( var _ Scorer = (*GossipScorer)(nil) +const ( + // The boundary till which a peer's gossip score is acceptable. + gossipThreshold = -100.0 +) + // GossipScorer represents scorer that evaluates peers based on their gossip performance. // Gossip scoring metrics are periodically calculated in libp2p's internal pubsub module. type GossipScorer struct { @@ -58,7 +63,7 @@ func (s *GossipScorer) isBadPeer(pid peer.ID) bool { if !ok { return false } - return peerData.GossipScore < 0 + return peerData.GossipScore < gossipThreshold } // BadPeers returns the peers that are considered bad. diff --git a/beacon-chain/p2p/peers/scorers/gossip_scorer_test.go b/beacon-chain/p2p/peers/scorers/gossip_scorer_test.go index c908b8bf0830..7960c0247106 100644 --- a/beacon-chain/p2p/peers/scorers/gossip_scorer_test.go +++ b/beacon-chain/p2p/peers/scorers/gossip_scorer_test.go @@ -30,10 +30,10 @@ func TestScorers_Gossip_Score(t *testing.T) { { name: "existent bad peer", update: func(scorer *scorers.GossipScorer) { - scorer.SetGossipData("peer1", -10.0, 1, nil) + scorer.SetGossipData("peer1", -101.0, 1, nil) }, check: func(scorer *scorers.GossipScorer) { - assert.Equal(t, -10.0, scorer.Score("peer1"), "Unexpected score") + assert.Equal(t, -101.0, scorer.Score("peer1"), "Unexpected score") assert.Equal(t, true, scorer.IsBadPeer("peer1"), "Unexpected good peer") }, }, diff --git a/beacon-chain/p2p/peers/scorers/peer_status.go b/beacon-chain/p2p/peers/scorers/peer_status.go index dbb02c7cb859..78d8b6b77782 100644 --- a/beacon-chain/p2p/peers/scorers/peer_status.go +++ b/beacon-chain/p2p/peers/scorers/peer_status.go @@ -82,6 +82,8 @@ func (s *PeerStatusScorer) isBadPeer(pid peer.ID) bool { // Mark peer as bad, if the latest error is one of the terminal ones. terminalErrs := []error{ p2ptypes.ErrWrongForkDigestVersion, + p2ptypes.ErrInvalidFinalizedRoot, + p2ptypes.ErrInvalidRequest, } for _, err := range terminalErrs { if errors.Is(peerData.ChainStateValidationError, err) { diff --git a/beacon-chain/p2p/peers/scorers/service.go b/beacon-chain/p2p/peers/scorers/service.go index bd7b5676275a..c6e38300cb08 100644 --- a/beacon-chain/p2p/peers/scorers/service.go +++ b/beacon-chain/p2p/peers/scorers/service.go @@ -7,6 +7,7 @@ import ( "github.com/libp2p/go-libp2p-core/peer" "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/peerdata" + "github.com/prysmaticlabs/prysm/config/features" ) var _ Scorer = (*Service)(nil) @@ -16,7 +17,9 @@ var _ Scorer = (*Service)(nil) const ScoreRoundingFactor = 10000 // BadPeerScore defines score that is returned for a bad peer (all other metrics are ignored). -const BadPeerScore = -1.00 +// The bad peer score was decided to be based on our determined gossip threshold, so that +// all the other scoring services have their relevant penalties on similar scales. +const BadPeerScore = gossipThreshold // Scorer defines minimum set of methods every peer scorer must expose. type Scorer interface { @@ -55,13 +58,13 @@ func NewService(ctx context.Context, store *peerdata.Store, config *Config) *Ser // Register scorers. s.scorers.badResponsesScorer = newBadResponsesScorer(store, config.BadResponsesScorerConfig) - s.setScorerWeight(s.scorers.badResponsesScorer, 1.0) + s.setScorerWeight(s.scorers.badResponsesScorer, 0.3) s.scorers.blockProviderScorer = newBlockProviderScorer(store, config.BlockProviderScorerConfig) - s.setScorerWeight(s.scorers.blockProviderScorer, 1.0) + s.setScorerWeight(s.scorers.blockProviderScorer, 0.0) s.scorers.peerStatusScorer = newPeerStatusScorer(store, config.PeerStatusScorerConfig) - s.setScorerWeight(s.scorers.peerStatusScorer, 0.0) + s.setScorerWeight(s.scorers.peerStatusScorer, 0.3) s.scorers.gossipScorer = newGossipScorer(store, config.GossipScorerConfig) - s.setScorerWeight(s.scorers.gossipScorer, 0.0) + s.setScorerWeight(s.scorers.gossipScorer, 0.4) // Start background tasks. go s.loop(ctx) @@ -104,7 +107,11 @@ func (s *Service) ActiveScorersCount() int { func (s *Service) Score(pid peer.ID) float64 { s.store.RLock() defer s.store.RUnlock() + return s.ScoreNoLock(pid) +} +// ScoreNoLock is a lock-free version of Score. +func (s *Service) ScoreNoLock(pid peer.ID) float64 { score := float64(0) if _, ok := s.store.PeerData(pid); !ok { return 0 @@ -120,19 +127,22 @@ func (s *Service) Score(pid peer.ID) float64 { func (s *Service) IsBadPeer(pid peer.ID) bool { s.store.RLock() defer s.store.RUnlock() - return s.isBadPeer(pid) + return s.IsBadPeerNoLock(pid) } -// isBadPeer is a lock-free version of isBadPeer. -func (s *Service) isBadPeer(pid peer.ID) bool { +// IsBadPeerNoLock is a lock-free version of IsBadPeer. +func (s *Service) IsBadPeerNoLock(pid peer.ID) bool { if s.scorers.badResponsesScorer.isBadPeer(pid) { return true } if s.scorers.peerStatusScorer.isBadPeer(pid) { return true } - // TODO(#6043): Hook in gossip scorer's relevant - // method to check if peer has a bad gossip score. + if features.Get().EnablePeerScorer { + if s.scorers.gossipScorer.isBadPeer(pid) { + return true + } + } return false } @@ -143,7 +153,7 @@ func (s *Service) BadPeers() []peer.ID { badPeers := make([]peer.ID, 0) for pid := range s.store.Peers() { - if s.isBadPeer(pid) { + if s.IsBadPeerNoLock(pid) { badPeers = append(badPeers, pid) } } diff --git a/beacon-chain/p2p/peers/scorers/service_test.go b/beacon-chain/p2p/peers/scorers/service_test.go index 9504a93ad3e8..d4afe1e4aa5e 100644 --- a/beacon-chain/p2p/peers/scorers/service_test.go +++ b/beacon-chain/p2p/peers/scorers/service_test.go @@ -92,6 +92,14 @@ func TestScorers_Service_Score(t *testing.T) { return scores } + blkProviderScorers := func(s *scorers.Service, pids []peer.ID) map[string]float64 { + scores := make(map[string]float64, len(pids)) + for _, pid := range pids { + scores[string(pid)] = s.BlockProviderScorer().Score(pid) + } + return scores + } + pack := func(scorer *scorers.Service, s1, s2, s3 float64) map[string]float64 { return map[string]float64{ "peer1": roundScore(s1), @@ -116,9 +124,7 @@ func TestScorers_Service_Score(t *testing.T) { pids := []peer.ID{"peer1", "peer2", "peer3"} for _, pid := range pids { peerStatuses.Add(nil, pid, nil, network.DirUnknown) - // Not yet used peer gets boosted score. - startScore := s.BlockProviderScorer().MaxScore() - assert.Equal(t, startScore/float64(s.ActiveScorersCount()), s.Score(pid), "Unexpected score for not yet used peer") + assert.Equal(t, float64(0), s.Score(pid), "Unexpected score for not yet used peer") } return s, pids } @@ -136,8 +142,8 @@ func TestScorers_Service_Score(t *testing.T) { t.Run("bad responses score", func(t *testing.T) { s, pids := setupScorer() // Peers start with boosted start score (new peers are boosted by block provider). - startScore := s.BlockProviderScorer().MaxScore() / float64(s.ActiveScorersCount()) - penalty := (-1 / float64(s.BadResponsesScorer().Params().Threshold)) / float64(s.ActiveScorersCount()) + startScore := float64(0) + penalty := (-10 / float64(s.BadResponsesScorer().Params().Threshold)) * float64(0.3) // Update peers' stats and test the effect on peer order. s.BadResponsesScorer().Increment("peer2") @@ -156,54 +162,53 @@ func TestScorers_Service_Score(t *testing.T) { t.Run("block providers score", func(t *testing.T) { s, pids := setupScorer() s1 := s.BlockProviderScorer() - startScore := s.BlockProviderScorer().MaxScore() / 2 - batchWeight := s1.Params().ProcessedBatchWeight / 2 + startScore := s.BlockProviderScorer().MaxScore() + batchWeight := s1.Params().ProcessedBatchWeight // Partial batch. s1.IncrementProcessedBlocks("peer1", batchSize/4) - assert.Equal(t, 0.0, s.Score("peer1"), "Unexpected %q score", "peer1") + assert.Equal(t, 0.0, s.BlockProviderScorer().Score("peer1"), "Unexpected %q score", "peer1") // Single batch. s1.IncrementProcessedBlocks("peer1", batchSize) - assert.DeepEqual(t, pack(s, batchWeight, startScore, startScore), peerScores(s, pids), "Unexpected scores") + assert.DeepEqual(t, pack(s, batchWeight, startScore, startScore), blkProviderScorers(s, pids), "Unexpected scores") // Multiple batches. s1.IncrementProcessedBlocks("peer2", batchSize*4) - assert.DeepEqual(t, pack(s, batchWeight, batchWeight*4, startScore), peerScores(s, pids), "Unexpected scores") + assert.DeepEqual(t, pack(s, batchWeight, batchWeight*4, startScore), blkProviderScorers(s, pids), "Unexpected scores") // Partial batch. s1.IncrementProcessedBlocks("peer3", batchSize/2) - assert.DeepEqual(t, pack(s, batchWeight, batchWeight*4, 0), peerScores(s, pids), "Unexpected scores") + assert.DeepEqual(t, pack(s, batchWeight, batchWeight*4, 0), blkProviderScorers(s, pids), "Unexpected scores") // See effect of decaying. assert.Equal(t, batchSize+batchSize/4, s1.ProcessedBlocks("peer1")) assert.Equal(t, batchSize*4, s1.ProcessedBlocks("peer2")) assert.Equal(t, batchSize/2, s1.ProcessedBlocks("peer3")) - assert.DeepEqual(t, pack(s, batchWeight, batchWeight*4, 0), peerScores(s, pids), "Unexpected scores") + assert.DeepEqual(t, pack(s, batchWeight, batchWeight*4, 0), blkProviderScorers(s, pids), "Unexpected scores") s1.Decay() assert.Equal(t, batchSize/4, s1.ProcessedBlocks("peer1")) assert.Equal(t, batchSize*3, s1.ProcessedBlocks("peer2")) assert.Equal(t, uint64(0), s1.ProcessedBlocks("peer3")) - assert.DeepEqual(t, pack(s, 0, batchWeight*3, 0), peerScores(s, pids), "Unexpected scores") + assert.DeepEqual(t, pack(s, 0, batchWeight*3, 0), blkProviderScorers(s, pids), "Unexpected scores") }) t.Run("overall score", func(t *testing.T) { s, _ := setupScorer() s1 := s.BlockProviderScorer() s2 := s.BadResponsesScorer() - batchWeight := s1.Params().ProcessedBatchWeight / float64(s.ActiveScorersCount()) - penalty := (-1 / float64(s.BadResponsesScorer().Params().Threshold)) / float64(s.ActiveScorersCount()) + penalty := (-10 / float64(s.BadResponsesScorer().Params().Threshold)) * float64(0.3) // Full score, no penalty. s1.IncrementProcessedBlocks("peer1", batchSize*5) - assert.Equal(t, roundScore(batchWeight*5), s.Score("peer1")) + assert.Equal(t, float64(0), s.Score("peer1")) // Now, adjust score by introducing penalty for bad responses. s2.Increment("peer1") s2.Increment("peer1") - assert.Equal(t, roundScore(batchWeight*5+2*penalty), s.Score("peer1"), "Unexpected overall score") + assert.Equal(t, roundScore(2*penalty), s.Score("peer1"), "Unexpected overall score") // If peer continues to misbehave, score becomes negative. s2.Increment("peer1") - assert.Equal(t, roundScore(batchWeight*5+3*penalty), s.Score("peer1"), "Unexpected overall score") + assert.Equal(t, roundScore(3*penalty), s.Score("peer1"), "Unexpected overall score") }) } diff --git a/beacon-chain/p2p/peers/status.go b/beacon-chain/p2p/peers/status.go index 102514f93f75..d521f874cc70 100644 --- a/beacon-chain/p2p/peers/status.go +++ b/beacon-chain/p2p/peers/status.go @@ -38,6 +38,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core" "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/peerdata" "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/scorers" + "github.com/prysmaticlabs/prysm/config/features" "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/crypto/rand" pb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" @@ -326,7 +327,14 @@ func (p *Status) ChainStateLastUpdated(pid peer.ID) (time.Time, error) { // IsBad states if the peer is to be considered bad (by *any* of the registered scorers). // If the peer is unknown this will return `false`, which makes using this function easier than returning an error. func (p *Status) IsBad(pid peer.ID) bool { - return p.isfromBadIP(pid) || p.scorers.IsBadPeer(pid) + p.store.RLock() + defer p.store.RUnlock() + return p.isBad(pid) +} + +// isBad is the lock-free version of IsBad. +func (p *Status) isBad(pid peer.ID) bool { + return p.isfromBadIP(pid) || p.scorers.IsBadPeerNoLock(pid) } // NextValidTime gets the earliest possible time it is to contact/dial @@ -535,6 +543,58 @@ func (p *Status) Prune() { p.store.Lock() defer p.store.Unlock() + // Default to old method if flag isnt enabled. + if !features.Get().EnablePeerScorer { + p.deprecatedPrune() + return + } + // Exit early if there is nothing to prune. + if len(p.store.Peers()) <= p.store.Config().MaxPeers { + return + } + notBadPeer := func(pid peer.ID) bool { + return !p.isBad(pid) + } + type peerResp struct { + pid peer.ID + score float64 + } + peersToPrune := make([]*peerResp, 0) + // Select disconnected peers with a smaller bad response count. + for pid, peerData := range p.store.Peers() { + if peerData.ConnState == PeerDisconnected && notBadPeer(pid) { + peersToPrune = append(peersToPrune, &peerResp{ + pid: pid, + score: p.Scorers().ScoreNoLock(pid), + }) + } + } + + // Sort peers in descending order, so the peers with the + // highest score are pruned first. This + // is to protect the node from malicious/lousy peers so + // that their memory is still kept. + sort.Slice(peersToPrune, func(i, j int) bool { + return peersToPrune[i].score > peersToPrune[j].score + }) + + limitDiff := len(p.store.Peers()) - p.store.Config().MaxPeers + if limitDiff > len(peersToPrune) { + limitDiff = len(peersToPrune) + } + + peersToPrune = peersToPrune[:limitDiff] + + // Delete peers from map. + for _, peerData := range peersToPrune { + p.store.DeletePeerData(peerData.pid) + } + p.tallyIPTracker() +} + +// Deprecated: This is the old peer pruning method based on +// bad response counts. +func (p *Status) deprecatedPrune() { // Exit early if there is nothing to prune. if len(p.store.Peers()) <= p.store.Config().MaxPeers { return @@ -570,9 +630,7 @@ func (p *Status) Prune() { if limitDiff > len(peersToPrune) { limitDiff = len(peersToPrune) } - peersToPrune = peersToPrune[:limitDiff] - // Delete peers from map. for _, peerData := range peersToPrune { p.store.DeletePeerData(peerData.pid) @@ -687,6 +745,70 @@ func (p *Status) BestNonFinalized(minPeers int, ourHeadEpoch types.Epoch) (types // bad response count. In the future scoring will be used // to determine the most suitable peers to take out. func (p *Status) PeersToPrune() []peer.ID { + if !features.Get().EnablePeerScorer { + return p.deprecatedPeersToPrune() + } + connLimit := p.ConnectedPeerLimit() + inBoundLimit := p.InboundLimit() + activePeers := p.Active() + numInboundPeers := len(p.InboundConnected()) + // Exit early if we are still below our max + // limit. + if len(activePeers) <= int(connLimit) { + return []peer.ID{} + } + p.store.Lock() + defer p.store.Unlock() + + type peerResp struct { + pid peer.ID + score float64 + } + peersToPrune := make([]*peerResp, 0) + // Select connected and inbound peers to prune. + for pid, peerData := range p.store.Peers() { + if peerData.ConnState == PeerConnected && + peerData.Direction == network.DirInbound { + peersToPrune = append(peersToPrune, &peerResp{ + pid: pid, + score: p.scorers.ScoreNoLock(pid), + }) + } + } + + // Sort in ascending order to favour pruning peers with a + // lower score. + sort.Slice(peersToPrune, func(i, j int) bool { + return peersToPrune[i].score < peersToPrune[j].score + }) + + // Determine amount of peers to prune using our + // max connection limit. + amountToPrune := len(activePeers) - int(connLimit) + + // Also check for inbound peers above our limit. + excessInbound := 0 + if numInboundPeers > inBoundLimit { + excessInbound = numInboundPeers - inBoundLimit + } + // Prune the largest amount between excess peers and + // excess inbound peers. + if excessInbound > amountToPrune { + amountToPrune = excessInbound + } + if amountToPrune < len(peersToPrune) { + peersToPrune = peersToPrune[:amountToPrune] + } + ids := make([]peer.ID, 0, len(peersToPrune)) + for _, pr := range peersToPrune { + ids = append(ids, pr.pid) + } + return ids +} + +// Deprecated: Is used to represent the older method +// of pruning which utilized bad response counts. +func (p *Status) deprecatedPeersToPrune() []peer.ID { connLimit := p.ConnectedPeerLimit() inBoundLimit := p.InboundLimit() activePeers := p.Active() @@ -724,7 +846,6 @@ func (p *Status) PeersToPrune() []peer.ID { // Determine amount of peers to prune using our // max connection limit. amountToPrune := len(activePeers) - int(connLimit) - // Also check for inbound peers above our limit. excessInbound := 0 if numInboundPeers > inBoundLimit { @@ -768,10 +889,9 @@ func (p *Status) ConnectedPeerLimit() uint64 { return uint64(maxLim) - maxLimitBuffer } +// this method assumes the store lock is acquired before +// executing the method. func (p *Status) isfromBadIP(pid peer.ID) bool { - p.store.RLock() - defer p.store.RUnlock() - peerData, ok := p.store.PeerData(pid) if !ok { return false diff --git a/beacon-chain/p2p/peers/status_test.go b/beacon-chain/p2p/peers/status_test.go index 2a7d206dee36..398aebe36945 100644 --- a/beacon-chain/p2p/peers/status_test.go +++ b/beacon-chain/p2p/peers/status_test.go @@ -16,6 +16,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers" "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/peerdata" "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/scorers" + "github.com/prysmaticlabs/prysm/config/features" "github.com/prysmaticlabs/prysm/config/params" ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1" pb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" @@ -548,6 +549,10 @@ func TestPrune(t *testing.T) { } func TestPeerIPTracker(t *testing.T) { + resetCfg := features.InitWithReset(&features.Flags{ + EnablePeerScorer: false, + }) + defer resetCfg() maxBadResponses := 2 p := peers.NewStatus(context.Background(), &peers.StatusConfig{ PeerLimit: 30, @@ -686,6 +691,10 @@ func TestAtInboundPeerLimit(t *testing.T) { } func TestPrunePeers(t *testing.T) { + resetCfg := features.InitWithReset(&features.Flags{ + EnablePeerScorer: false, + }) + defer resetCfg() p := peers.NewStatus(context.Background(), &peers.StatusConfig{ PeerLimit: 30, ScorerParams: &scorers.Config{ diff --git a/beacon-chain/sync/rpc_goodbye.go b/beacon-chain/sync/rpc_goodbye.go index 5daedd83f302..263738ef6ba8 100644 --- a/beacon-chain/sync/rpc_goodbye.go +++ b/beacon-chain/sync/rpc_goodbye.go @@ -58,7 +58,11 @@ func (s *Service) disconnectBadPeer(ctx context.Context, id peer.ID) { if !s.cfg.P2P.Peers().IsBad(id) { return } - goodbyeCode := p2ptypes.ErrToGoodbyeCode(s.cfg.P2P.Peers().Scorers().ValidationError(id)) + err := s.cfg.P2P.Peers().Scorers().ValidationError(id) + goodbyeCode := p2ptypes.ErrToGoodbyeCode(err) + if err == nil { + goodbyeCode = p2ptypes.GoodbyeCodeBanned + } if err := s.sendGoodByeAndDisconnect(ctx, goodbyeCode, id); err != nil { log.Debugf("Error when disconnecting with bad peer: %v", err) } diff --git a/beacon-chain/sync/validate_aggregate_proof.go b/beacon-chain/sync/validate_aggregate_proof.go index a2d5a5d22114..b4e60a00c4cb 100644 --- a/beacon-chain/sync/validate_aggregate_proof.go +++ b/beacon-chain/sync/validate_aggregate_proof.go @@ -56,6 +56,10 @@ func (s *Service) validateAggregateAndProof(ctx context.Context, pid peer.ID, ms if err := helpers.ValidateNilAttestation(m.Message.Aggregate); err != nil { return pubsub.ValidationReject, err } + // Do not process slot 0 aggregates. + if m.Message.Aggregate.Data.Slot == 0 { + return pubsub.ValidationIgnore, nil + } // Broadcast the aggregated attestation on a feed to notify other services in the beacon node // of a received aggregated attestation. diff --git a/beacon-chain/sync/validate_aggregate_proof_test.go b/beacon-chain/sync/validate_aggregate_proof_test.go index 74174c508d83..4480ae300d16 100644 --- a/beacon-chain/sync/validate_aggregate_proof_test.go +++ b/beacon-chain/sync/validate_aggregate_proof_test.go @@ -316,6 +316,7 @@ func TestValidateAggregateAndProof_CanValidate(t *testing.T) { aggBits.SetBitAt(0, true) att := ðpb.Attestation{ Data: ðpb.AttestationData{ + Slot: 1, BeaconBlockRoot: root[:], Source: ðpb.Checkpoint{Epoch: 0, Root: bytesutil.PadTo([]byte("hello-world"), 32)}, Target: ðpb.Checkpoint{Epoch: 0, Root: root[:]}, @@ -357,7 +358,7 @@ func TestValidateAggregateAndProof_CanValidate(t *testing.T) { P2P: p, DB: db, InitialSync: &mockSync.Sync{IsSyncing: false}, - Chain: &mock.ChainService{Genesis: time.Now(), + Chain: &mock.ChainService{Genesis: time.Now().Add(-oneEpoch()), State: beaconState, ValidAttestation: true, FinalizedCheckPoint: ðpb.Checkpoint{ @@ -410,6 +411,7 @@ func TestVerifyIndexInCommittee_SeenAggregatorEpoch(t *testing.T) { aggBits.SetBitAt(0, true) att := ðpb.Attestation{ Data: ðpb.AttestationData{ + Slot: 1, BeaconBlockRoot: root[:], Source: ðpb.Checkpoint{Epoch: 0, Root: bytesutil.PadTo([]byte("hello-world"), 32)}, Target: ðpb.Checkpoint{Epoch: 0, Root: root[:]}, @@ -450,7 +452,7 @@ func TestVerifyIndexInCommittee_SeenAggregatorEpoch(t *testing.T) { P2P: p, DB: db, InitialSync: &mockSync.Sync{IsSyncing: false}, - Chain: &mock.ChainService{Genesis: time.Now(), + Chain: &mock.ChainService{Genesis: time.Now().Add(-oneEpoch()), ValidatorsRoot: [32]byte{'A'}, State: beaconState, ValidAttestation: true, diff --git a/beacon-chain/sync/validate_beacon_attestation.go b/beacon-chain/sync/validate_beacon_attestation.go index e46cdb667554..925ef42b2f39 100644 --- a/beacon-chain/sync/validate_beacon_attestation.go +++ b/beacon-chain/sync/validate_beacon_attestation.go @@ -61,7 +61,10 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p if err := helpers.ValidateNilAttestation(att); err != nil { return pubsub.ValidationReject, err } - + // Do not process slot 0 attestations. + if att.Data.Slot == 0 { + return pubsub.ValidationIgnore, nil + } // Broadcast the unaggregated attestation on a feed to notify other services in the beacon node // of a received unaggregated attestation. s.cfg.AttestationNotifier.OperationFeed().Send(&feed.Event{ diff --git a/config/features/flags.go b/config/features/flags.go index 283f335b831e..ed67a4a51271 100644 --- a/config/features/flags.go +++ b/config/features/flags.go @@ -144,6 +144,7 @@ var ( // devModeFlags holds list of flags that are set when development mode is on. var devModeFlags = []cli.Flag{ enableLargerGossipHistory, + enablePeerScorer, forceOptMaxCoverAggregationStategy, enableBatchGossipVerification, } From a196c78bed77c0d2aaa8488e89de00f7c0e53f37 Mon Sep 17 00:00:00 2001 From: Nishant Das Date: Thu, 30 Sep 2021 23:10:49 +0800 Subject: [PATCH 25/25] clean up (#9709) --- beacon-chain/core/helpers/beacon_committee.go | 9 +++------ beacon-chain/core/helpers/validators.go | 3 --- beacon-chain/sync/validate_beacon_blocks.go | 16 ++++------------ 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/beacon-chain/core/helpers/beacon_committee.go b/beacon-chain/core/helpers/beacon_committee.go index 47de38eab206..29386fc62698 100644 --- a/beacon-chain/core/helpers/beacon_committee.go +++ b/beacon-chain/core/helpers/beacon_committee.go @@ -318,17 +318,14 @@ func UpdateCommitteeCache(state state.ReadOnlyBeaconState, epoch types.Epoch) er // UpdateProposerIndicesInCache updates proposer indices entry of the committee cache. func UpdateProposerIndicesInCache(ctx context.Context, state state.ReadOnlyBeaconState) error { - // The cache uses the state root at the (current epoch - 2)'s slot as key. (e.g. for epoch 2, the key is root at slot 31) + // The cache uses the state root at the (current epoch - 1)'s slot as key. (e.g. for epoch 2, the key is root at slot 63) // Which is the reason why we skip genesis epoch. if core.CurrentEpoch(state) <= params.BeaconConfig().GenesisEpoch+params.BeaconConfig().MinSeedLookahead { return nil } - // Use state root from (current_epoch - 1 - lookahead)) - wantedEpoch := core.CurrentEpoch(state) - 1 - if wantedEpoch >= params.BeaconConfig().MinSeedLookahead { - wantedEpoch -= params.BeaconConfig().MinSeedLookahead - } + // Use state root from (current_epoch - 1)) + wantedEpoch := core.PrevEpoch(state) s, err := core.EndSlot(wantedEpoch) if err != nil { return err diff --git a/beacon-chain/core/helpers/validators.go b/beacon-chain/core/helpers/validators.go index dae525f4f70c..ac208c2a56ab 100644 --- a/beacon-chain/core/helpers/validators.go +++ b/beacon-chain/core/helpers/validators.go @@ -229,9 +229,6 @@ func BeaconProposerIndex(ctx context.Context, state state.ReadOnlyBeaconState) ( // For simplicity, the node will skip caching of genesis epoch. if e > params.BeaconConfig().GenesisEpoch+params.BeaconConfig().MinSeedLookahead { wantedEpoch := core.PrevEpoch(state) - if wantedEpoch >= params.BeaconConfig().MinSeedLookahead { - wantedEpoch -= params.BeaconConfig().MinSeedLookahead - } s, err := core.EndSlot(wantedEpoch) if err != nil { return 0, err diff --git a/beacon-chain/sync/validate_beacon_blocks.go b/beacon-chain/sync/validate_beacon_blocks.go index ff25db245d19..95072b5492a6 100644 --- a/beacon-chain/sync/validate_beacon_blocks.go +++ b/beacon-chain/sync/validate_beacon_blocks.go @@ -204,22 +204,14 @@ func (s *Service) validateBeaconBlock(ctx context.Context, blk block.SignedBeaco s.setBadBlock(ctx, blockRoot) 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 := core.NextEpoch(parentState) - expectedEpoch := core.SlotToEpoch(blk.Block().Slot()) - if expectedEpoch <= nextEpoch { - err = parentState.SetSlot(blk.Block().Slot()) + // In the event the block is more than an epoch ahead from its + // parent state, we have to advance the state forward. + if features.Get().EnableNextSlotStateCache { + parentState, err = transition.ProcessSlotsUsingNextSlotCache(ctx, parentState, blk.Block().ParentRoot(), 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 = transition.ProcessSlots(ctx, parentState, blk.Block().Slot()) if err != nil { return err