From ed3ab828a1a26cc04d0d839c6aefe44c48d6ce14 Mon Sep 17 00:00:00 2001 From: Ivan Martinez Date: Thu, 23 Jan 2020 00:52:01 -0500 Subject: [PATCH] Implement attester protection into validator client (#4598) * Add flag for attester protection * Remove flags * Add attestation history DB functions to validator client * Fix comments * Update interface to new funcs * Fix test * Add flags * Implement most of attester protection * Fix tests * Add test for pruning * Add more test cases for prunes * Remove todo comment * Fix comments * Rename functions * Fix logs Co-authored-by: Raul Jordan --- proto/slashing/slashing.pb.go | 7 +- shared/featureconfig/config.go | 13 +- shared/featureconfig/flags.go | 12 +- validator/client/validator_attest.go | 96 ++++++++ validator/client/validator_attest_test.go | 272 +++++++++++++++++++++ validator/client/validator_propose.go | 4 +- validator/client/validator_propose_test.go | 6 +- 7 files changed, 395 insertions(+), 15 deletions(-) diff --git a/proto/slashing/slashing.pb.go b/proto/slashing/slashing.pb.go index 119f9050046..aba1ed8a505 100644 --- a/proto/slashing/slashing.pb.go +++ b/proto/slashing/slashing.pb.go @@ -6,6 +6,10 @@ package ethereum_slashing import ( context "context" fmt "fmt" + io "io" + math "math" + math_bits "math/bits" + _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" types "github.com/gogo/protobuf/types" @@ -14,9 +18,6 @@ import ( grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" - io "io" - math "math" - math_bits "math/bits" ) // Reference imports to suppress errors if they are not otherwise used. diff --git a/shared/featureconfig/config.go b/shared/featureconfig/config.go index db929a3751c..4b957e7baf6 100644 --- a/shared/featureconfig/config.go +++ b/shared/featureconfig/config.go @@ -35,7 +35,8 @@ type Flags struct { EnableSnappyDBCompression bool // EnableSnappyDBCompression in the database. InitSyncCacheState bool // InitSyncCacheState caches state during initial sync. KafkaBootstrapServers string // KafkaBootstrapServers to find kafka servers to stream blocks, attestations, etc. - BlockDoubleProposals bool // BlockDoubleProposals prevents the validator client from signing any proposals that would be considered a slashable offense. + ProtectProposer bool // ProtectProposer prevents the validator client from signing any proposals that would be considered a slashable offense. + ProtectAttester bool // ProtectAttester prevents the validator client from signing any attestations that would be considered a slashable offense. // DisableForkChoice disables using LMD-GHOST fork choice to update // the head of the chain based on attestations and instead accepts any valid received block @@ -150,9 +151,13 @@ func ConfigureValidator(ctx *cli.Context) { log.Warn("Using minimal config") cfg.MinimalConfig = true } - if ctx.GlobalBool(blockDoubleProposals.Name) { - log.Warn("Enabled validator double proposal slashing protection.") - cfg.BlockDoubleProposals = true + if ctx.GlobalBool(protectProposerFlag.Name) { + log.Warn("Enabled validator proposal slashing protection.") + cfg.ProtectProposer = true + } + if ctx.GlobalBool(protectAttesterFlag.Name) { + log.Warn("Enabled validator attestation slashing protection.") + cfg.ProtectAttester = true } Init(cfg) } diff --git a/shared/featureconfig/flags.go b/shared/featureconfig/flags.go index 608f283bf33..abf097e95c5 100644 --- a/shared/featureconfig/flags.go +++ b/shared/featureconfig/flags.go @@ -83,11 +83,16 @@ var ( Name: "cache-proposer-indices", Usage: "Cache proposer indices on per epoch basis.", } - blockDoubleProposals = cli.BoolFlag{ - Name: "block-double-proposals", + protectProposerFlag = cli.BoolFlag{ + Name: "protect-proposer", Usage: "Prevent the validator client from signing and broadcasting 2 different block " + "proposals in the same epoch. Protects from slashing.", } + protectAttesterFlag = cli.BoolFlag{ + Name: "protect-attester", + Usage: "Prevent the validator client from signing and broadcasting 2 any slashable attestations. " + + "Protects from slashing.", + } ) // Deprecated flags list. @@ -199,7 +204,8 @@ var deprecatedFlags = []cli.Flag{ // ValidatorFlags contains a list of all the feature flags that apply to the validator client. var ValidatorFlags = append(deprecatedFlags, []cli.Flag{ minimalConfigFlag, - blockDoubleProposals, + protectAttesterFlag, + protectProposerFlag, }...) // BeaconChainFlags contains a list of all the feature flags that apply to the beacon-chain client. diff --git a/validator/client/validator_attest.go b/validator/client/validator_attest.go index afd9f05304a..85f7db49607 100644 --- a/validator/client/validator_attest.go +++ b/validator/client/validator_attest.go @@ -10,11 +10,14 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/go-ssz" + slashpb "github.com/prysmaticlabs/prysm/proto/slashing" "github.com/prysmaticlabs/prysm/shared/bytesutil" + "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/roughtime" "github.com/prysmaticlabs/prysm/shared/slotutil" + "github.com/sirupsen/logrus" "go.opencensus.io/trace" ) @@ -55,6 +58,21 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot uint64, pubKey [ return } + if featureconfig.Get().ProtectAttester { + history, err := v.db.AttestationHistory(ctx, pubKey[:]) + if err != nil { + log.Errorf("Could not get attestation history from DB: %v", err) + return + } + if isNewAttSlashable(history, data.Source.Epoch, data.Target.Epoch) { + log.WithFields(logrus.Fields{ + "sourceEpoch": data.Source.Epoch, + "targetEpoch": data.Target.Epoch, + }).Error("Attempted to make a slashable attestation, rejected") + return + } + } + sig, err := v.signAtt(ctx, pubKey, data) if err != nil { log.WithError(err).Error("Could not sign attestation") @@ -75,6 +93,19 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot uint64, pubKey [ return } + if featureconfig.Get().ProtectAttester { + history, err := v.db.AttestationHistory(ctx, pubKey[:]) + if err != nil { + log.Errorf("Could not get attestation history from DB: %v", err) + return + } + history = markAttestationForTargetEpoch(history, data.Source.Epoch, data.Target.Epoch) + if err := v.db.SaveAttestationHistory(ctx, pubKey[:], history); err != nil { + log.Errorf("Could not save attestation history to DB: %v", err) + return + } + } + if err := v.saveAttesterIndexToData(data, validatorIndex); err != nil { log.WithError(err).Error("Could not save validator index for logging") return @@ -180,3 +211,68 @@ func (v *validator) saveAttesterIndexToData(data *ethpb.AttestationData, index u return nil } + +// isNewAttSlashable uses the attestation history to determine if an attestation of sourceEpoch +// and targetEpoch would be slashable. It can detect double, surrounding, and surrounded votes. +func isNewAttSlashable(history *slashpb.AttestationHistory, sourceEpoch uint64, targetEpoch uint64) bool { + farFuture := params.BeaconConfig().FarFutureEpoch + wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod + + // Previously pruned, we should return false. + if int(targetEpoch) <= int(history.LatestEpochWritten)-int(wsPeriod) { + return false + } + + // Check if there has already been a vote for this target epoch. + if safeTargetToSource(history, targetEpoch) != farFuture { + return true + } + + // Check if the new attestation would be surrounding another attestation. + for i := sourceEpoch; i <= targetEpoch; i++ { + // Unattested for epochs are marked as FAR_FUTURE_EPOCH. + if safeTargetToSource(history, i) == farFuture { + continue + } + if history.TargetToSource[i%wsPeriod] > sourceEpoch { + return true + } + } + + // Check if the new attestation is being surrounded. + for i := targetEpoch; i <= history.LatestEpochWritten; i++ { + if safeTargetToSource(history, i) < sourceEpoch { + return true + } + } + + return false +} + +// markAttestationForTargetEpoch returns the modified attestation history with the passed-in epochs marked +// as attested for. This is done to prevent the validator client from signing any slashable attestations. +func markAttestationForTargetEpoch(history *slashpb.AttestationHistory, sourceEpoch uint64, targetEpoch uint64) *slashpb.AttestationHistory { + wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod + + if targetEpoch > history.LatestEpochWritten { + // If the target epoch to mark is ahead of latest written epoch, override the old targets and mark the requested epoch. + // Limit the overwriting to one weak subjectivity period as further is not needed. + maxToWrite := history.LatestEpochWritten + wsPeriod + for i := history.LatestEpochWritten + 1; i < targetEpoch && i <= maxToWrite; i++ { + history.TargetToSource[i%wsPeriod] = params.BeaconConfig().FarFutureEpoch + } + history.LatestEpochWritten = targetEpoch + } + history.TargetToSource[targetEpoch%wsPeriod] = sourceEpoch + return history +} + +// safeTargetToSource makes sure the epoch accessed is within bounds, and if it's not it at +// returns the "default" FAR_FUTURE_EPOCH value. +func safeTargetToSource(history *slashpb.AttestationHistory, targetEpoch uint64) uint64 { + wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod + if targetEpoch > history.LatestEpochWritten || int(targetEpoch) < int(history.LatestEpochWritten)-int(wsPeriod) { + return params.BeaconConfig().FarFutureEpoch + } + return history.TargetToSource[targetEpoch%wsPeriod] +} diff --git a/validator/client/validator_attest_test.go b/validator/client/validator_attest_test.go index 231059a34bc..921a5c7ca5c 100644 --- a/validator/client/validator_attest_test.go +++ b/validator/client/validator_attest_test.go @@ -12,6 +12,8 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/go-ssz" + slashpb "github.com/prysmaticlabs/prysm/proto/slashing" + "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/roughtime" "github.com/prysmaticlabs/prysm/shared/testutil" @@ -137,6 +139,146 @@ func TestAttestToBlockHead_AttestsCorrectly(t *testing.T) { } } +func TestAttestToBlockHead_BlocksDoubleAtt(t *testing.T) { + config := &featureconfig.Flags{ + ProtectAttester: true, + } + featureconfig.Init(config) + hook := logTest.NewGlobal() + validator, m, finish := setup(t) + defer finish() + validatorIndex := uint64(7) + committee := []uint64{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10} + validator.duties = ðpb.DutiesResponse{Duties: []*ethpb.DutiesResponse_Duty{ + { + PublicKey: validatorKey.PublicKey.Marshal(), + CommitteeIndex: 5, + Committee: committee, + }}} + m.validatorClient.EXPECT().GetAttestationData( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}), + ).Times(2).Return(ðpb.AttestationData{ + BeaconBlockRoot: []byte("A"), + Target: ðpb.Checkpoint{Root: []byte("B"), Epoch: 4}, + Source: ðpb.Checkpoint{Root: []byte("C"), Epoch: 3}, + }, nil) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + gomock.Any(), // epoch + ).Return(ðpb.DomainResponse{}, nil /*err*/) + + m.validatorClient.EXPECT().ProposeAttestation( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.Attestation{}), + ).Return(ðpb.AttestResponse{}, nil /* error */) + + validator.SubmitAttestation(context.Background(), 30, validatorPubKey) + validator.SubmitAttestation(context.Background(), 30, validatorPubKey) + testutil.AssertLogsContain(t, hook, "Attempted to make a slashable attestation, rejected") +} + +func TestAttestToBlockHead_BlocksSurroundAtt(t *testing.T) { + config := &featureconfig.Flags{ + ProtectAttester: true, + } + featureconfig.Init(config) + hook := logTest.NewGlobal() + validator, m, finish := setup(t) + defer finish() + validatorIndex := uint64(7) + committee := []uint64{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10} + validator.duties = ðpb.DutiesResponse{Duties: []*ethpb.DutiesResponse_Duty{ + { + PublicKey: validatorKey.PublicKey.Marshal(), + CommitteeIndex: 5, + Committee: committee, + }}} + m.validatorClient.EXPECT().GetAttestationData( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}), + ).Return(ðpb.AttestationData{ + BeaconBlockRoot: []byte("A"), + Target: ðpb.Checkpoint{Root: []byte("B"), Epoch: 2}, + Source: ðpb.Checkpoint{Root: []byte("C"), Epoch: 1}, + }, nil) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + gomock.Any(), // epoch + ).Return(ðpb.DomainResponse{}, nil /*err*/) + + m.validatorClient.EXPECT().ProposeAttestation( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.Attestation{}), + ).Return(ðpb.AttestResponse{}, nil /* error */) + + validator.SubmitAttestation(context.Background(), 30, validatorPubKey) + + m.validatorClient.EXPECT().GetAttestationData( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}), + ).Return(ðpb.AttestationData{ + BeaconBlockRoot: []byte("A"), + Target: ðpb.Checkpoint{Root: []byte("B"), Epoch: 4}, + Source: ðpb.Checkpoint{Root: []byte("C"), Epoch: 0}, + }, nil) + + validator.SubmitAttestation(context.Background(), 30, validatorPubKey) + testutil.AssertLogsContain(t, hook, "Attempted to make a slashable attestation, rejected") +} + +func TestAttestToBlockHead_BlocksSurroundedAtt(t *testing.T) { + config := &featureconfig.Flags{ + ProtectAttester: true, + } + featureconfig.Init(config) + hook := logTest.NewGlobal() + validator, m, finish := setup(t) + defer finish() + validatorIndex := uint64(7) + committee := []uint64{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10} + validator.duties = ðpb.DutiesResponse{Duties: []*ethpb.DutiesResponse_Duty{ + { + PublicKey: validatorKey.PublicKey.Marshal(), + CommitteeIndex: 5, + Committee: committee, + }}} + m.validatorClient.EXPECT().GetAttestationData( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}), + ).Return(ðpb.AttestationData{ + BeaconBlockRoot: []byte("A"), + Target: ðpb.Checkpoint{Root: []byte("B"), Epoch: 3}, + Source: ðpb.Checkpoint{Root: []byte("C"), Epoch: 0}, + }, nil) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + gomock.Any(), // epoch + ).Return(ðpb.DomainResponse{}, nil /*err*/) + + m.validatorClient.EXPECT().ProposeAttestation( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.Attestation{}), + ).Return(ðpb.AttestResponse{}, nil /* error */) + + validator.SubmitAttestation(context.Background(), 30, validatorPubKey) + + m.validatorClient.EXPECT().GetAttestationData( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}), + ).Return(ðpb.AttestationData{ + BeaconBlockRoot: []byte("A"), + Target: ðpb.Checkpoint{Root: []byte("B"), Epoch: 2}, + Source: ðpb.Checkpoint{Root: []byte("C"), Epoch: 1}, + }, nil) + + validator.SubmitAttestation(context.Background(), 30, validatorPubKey) + testutil.AssertLogsContain(t, hook, "Attempted to make a slashable attestation, rejected") +} + func TestAttestToBlockHead_DoesNotAttestBeforeDelay(t *testing.T) { validator, m, finish := setup(t) defer finish() @@ -259,3 +401,133 @@ func TestWaitForSlotOneThird_WaitCorrectly(t *testing.T) { t.Errorf("Wanted %d time for slot one-third but got %d", oneThird, currentTime) } } + +func TestAttestationHistory_BlocksDoubleAttestation(t *testing.T) { + newMap := make(map[uint64]uint64) + newMap[0] = params.BeaconConfig().FarFutureEpoch + attestations := &slashpb.AttestationHistory{ + TargetToSource: newMap, + LatestEpochWritten: 0, + } + + // Mark an attestation spanning epochs 0 to 3. + newAttSource := uint64(0) + newAttTarget := uint64(3) + attestations = markAttestationForTargetEpoch(attestations, newAttSource, newAttTarget) + if attestations.LatestEpochWritten != newAttTarget { + t.Fatalf("Expected latest epoch written to be %d, received %d", newAttTarget, attestations.LatestEpochWritten) + } + + // Try an attestation that should be slashable (double att) spanning epochs 1 to 3. + newAttSource = uint64(1) + newAttTarget = uint64(3) + if !isNewAttSlashable(attestations, newAttSource, newAttTarget) { + t.Fatalf("Expected attestation of source %d and target %d to be considered slashable", newAttSource, newAttTarget) + } +} + +func TestAttestationHistory_Prunes(t *testing.T) { + wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod + newMap := make(map[uint64]uint64) + newMap[0] = params.BeaconConfig().FarFutureEpoch + attestations := &slashpb.AttestationHistory{ + TargetToSource: newMap, + LatestEpochWritten: 0, + } + + // Try an attestation on totally unmarked history, should not be slashable. + if isNewAttSlashable(attestations, 0, wsPeriod+5) { + t.Fatalf("Expected attestation of source 0, target %d to be considered slashable", wsPeriod+5) + } + + // Mark attestations spanning epochs 0 to 3 and 6 to 9. + prunedNewAttSource := uint64(0) + prunedNewAttTarget := uint64(3) + attestations = markAttestationForTargetEpoch(attestations, prunedNewAttSource, prunedNewAttTarget) + newAttSource := prunedNewAttSource + 6 + newAttTarget := prunedNewAttTarget + 6 + attestations = markAttestationForTargetEpoch(attestations, newAttSource, newAttTarget) + if attestations.LatestEpochWritten != newAttTarget { + t.Fatalf("Expected latest epoch written to be %d, received %d", newAttTarget, attestations.LatestEpochWritten) + } + + // Mark an attestation spanning epochs 54000 to 54003. + farNewAttSource := newAttSource + wsPeriod + farNewAttTarget := newAttTarget + wsPeriod + attestations = markAttestationForTargetEpoch(attestations, farNewAttSource, farNewAttTarget) + if attestations.LatestEpochWritten != farNewAttTarget { + t.Fatalf("Expected latest epoch written to be %d, received %d", newAttTarget, attestations.LatestEpochWritten) + } + + if safeTargetToSource(attestations, prunedNewAttTarget) != params.BeaconConfig().FarFutureEpoch { + t.Fatalf("Expected attestation at target epoch %d to not be marked", prunedNewAttTarget) + } + + if safeTargetToSource(attestations, farNewAttTarget) != farNewAttSource { + t.Fatalf("Expected attestation at target epoch %d to not be marked", farNewAttSource) + } + + // Try an attestation from existing source to outside prune, should slash. + if !isNewAttSlashable(attestations, newAttSource, farNewAttTarget) { + t.Fatalf("Expected attestation of source %d, target %d to be considered slashable", newAttSource, farNewAttTarget) + } + // Try an attestation from before existing target to outside prune, should slash. + if !isNewAttSlashable(attestations, newAttTarget-1, farNewAttTarget) { + t.Fatalf("Expected attestation of source %d, target %d to be considered slashable", newAttTarget-1, farNewAttTarget) + } + // Try an attestation larger than pruning amount, should slash. + if !isNewAttSlashable(attestations, 0, farNewAttTarget+5) { + t.Fatalf("Expected attestation of source 0, target %d to be considered slashable", farNewAttTarget+5) + } +} + +func TestAttestationHistory_BlocksSurroundedAttestation(t *testing.T) { + newMap := make(map[uint64]uint64) + newMap[0] = params.BeaconConfig().FarFutureEpoch + attestations := &slashpb.AttestationHistory{ + TargetToSource: newMap, + LatestEpochWritten: 0, + } + + // Mark an attestation spanning epochs 0 to 3. + newAttSource := uint64(0) + newAttTarget := uint64(3) + attestations = markAttestationForTargetEpoch(attestations, newAttSource, newAttTarget) + if attestations.LatestEpochWritten != newAttTarget { + t.Fatalf("Expected latest epoch written to be %d, received %d", newAttTarget, attestations.LatestEpochWritten) + } + + // Try an attestation that should be slashable (being surrounded) spanning epochs 1 to 2. + newAttSource = uint64(1) + newAttTarget = uint64(2) + if !isNewAttSlashable(attestations, newAttSource, newAttTarget) { + t.Fatalf("Expected attestation of source %d and target %d to be considered slashable", newAttSource, newAttTarget) + } +} + +func TestAttestationHistory_BlocksSurroundingAttestation(t *testing.T) { + newMap := make(map[uint64]uint64) + newMap[0] = params.BeaconConfig().FarFutureEpoch + attestations := &slashpb.AttestationHistory{ + TargetToSource: newMap, + LatestEpochWritten: 0, + } + + // Mark an attestation spanning epochs 1 to 2. + newAttSource := uint64(1) + newAttTarget := uint64(2) + attestations = markAttestationForTargetEpoch(attestations, newAttSource, newAttTarget) + if attestations.LatestEpochWritten != newAttTarget { + t.Fatalf("Expected latest epoch written to be %d, received %d", newAttTarget, attestations.LatestEpochWritten) + } + if attestations.TargetToSource[newAttTarget] != newAttSource { + t.Fatalf("Expected source epoch to be %d, received %d", newAttSource, attestations.TargetToSource[newAttTarget]) + } + + // Try an attestation that should be slashable (surrounding) spanning epochs 0 to 3. + newAttSource = uint64(0) + newAttTarget = uint64(3) + if !isNewAttSlashable(attestations, newAttSource, newAttTarget) { + t.Fatalf("Expected attestation of source %d and target %d to be considered slashable", newAttSource, newAttTarget) + } +} diff --git a/validator/client/validator_propose.go b/validator/client/validator_propose.go index 65195c71e9c..b42e6e3dc27 100644 --- a/validator/client/validator_propose.go +++ b/validator/client/validator_propose.go @@ -52,7 +52,7 @@ func (v *validator) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]by return } - if featureconfig.Get().BlockDoubleProposals { + if featureconfig.Get().ProtectProposer { history, err := v.db.ProposalHistory(ctx, pubKey[:]) if err != nil { log.WithError(err).Error("Failed to get proposal history") @@ -83,7 +83,7 @@ func (v *validator) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]by return } - if featureconfig.Get().BlockDoubleProposals { + if featureconfig.Get().ProtectProposer { history, err := v.db.ProposalHistory(ctx, pubKey[:]) if err != nil { log.WithError(err).Error("Failed to get proposal history") diff --git a/validator/client/validator_propose_test.go b/validator/client/validator_propose_test.go index cf70af10be9..d5663755521 100644 --- a/validator/client/validator_propose_test.go +++ b/validator/client/validator_propose_test.go @@ -114,7 +114,7 @@ func TestProposeBlock_ProposeBlockFailed(t *testing.T) { func TestProposeBlock_BlocksDoubleProposal(t *testing.T) { cfg := &featureconfig.Flags{ - BlockDoubleProposals: true, + ProtectProposer: true, } featureconfig.Init(cfg) hook := logTest.NewGlobal() @@ -151,7 +151,7 @@ func TestProposeBlock_BlocksDoubleProposal(t *testing.T) { func TestProposeBlock_BlocksDoubleProposal_After54KEpochs(t *testing.T) { cfg := &featureconfig.Flags{ - BlockDoubleProposals: true, + ProtectProposer: true, } featureconfig.Init(cfg) hook := logTest.NewGlobal() @@ -189,7 +189,7 @@ func TestProposeBlock_BlocksDoubleProposal_After54KEpochs(t *testing.T) { func TestProposeBlock_AllowsPastProposals(t *testing.T) { cfg := &featureconfig.Flags{ - BlockDoubleProposals: true, + ProtectProposer: true, } featureconfig.Init(cfg) hook := logTest.NewGlobal()