Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Source lrg target #7839

Merged
merged 8 commits into from
Nov 17, 2020
37 changes: 34 additions & 3 deletions slasher/detection/attestations/spanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ var (
Name: "latest_max_span_distance_observed",
Help: "The latest distance between target - source observed for max spans",
})
sourceLargerThenTargetObserved = promauto.NewCounter(prometheus.CounterOpts{
Name: "attestation_source_larger_then_target",
Help: "The number of attestation data source epoch that aren larger then target epoch.",
})
)

// We look back 128 epochs when updating min/max spans
Expand Down Expand Up @@ -64,11 +68,21 @@ func (s *SpanDetector) DetectSlashingsForAttestation(
defer traceSpan.End()
sourceEpoch := att.Data.Source.Epoch
targetEpoch := att.Data.Target.Epoch
if (targetEpoch - sourceEpoch) > params.BeaconConfig().WeakSubjectivityPeriod {
dis := targetEpoch - sourceEpoch

if sourceEpoch > targetEpoch { //Prevent underflow and handle source > target slashable cases.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why flip source and target epoch here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spans are recorded at source epoch. we need to get the right spans for detection to work right

dis = sourceEpoch - targetEpoch
tmp := sourceEpoch
sourceEpoch = targetEpoch
targetEpoch = tmp
sourceLargerThenTargetObserved.Inc()
}

if dis > params.BeaconConfig().WeakSubjectivityPeriod {
return nil, fmt.Errorf(
"attestation span was greater than weak subjectivity period %d, received: %d",
params.BeaconConfig().WeakSubjectivityPeriod,
targetEpoch-sourceEpoch,
dis,
)
}

Expand All @@ -82,7 +96,7 @@ func (s *SpanDetector) DetectSlashingsForAttestation(
}

var detections []*types.DetectionResult
distance := uint16(targetEpoch - sourceEpoch)
distance := uint16(dis)
for _, idx := range att.AttestingIndices {
if ctx.Err() != nil {
return nil, errors.Wrap(ctx.Err(), "could not detect slashings")
Expand Down Expand Up @@ -171,6 +185,11 @@ func (s *SpanDetector) saveSigBytes(ctx context.Context, att *ethpb.IndexedAttes
ctx, traceSpan := trace.StartSpan(ctx, "spanner.saveSigBytes")
defer traceSpan.End()
target := att.Data.Target.Epoch
source := att.Data.Source.Epoch
// handle source > target well
if source > target {
target = source
}
spanMap, err := s.slasherDB.EpochSpans(ctx, target, dbTypes.UseCache)
if err != nil {
return err
Expand Down Expand Up @@ -220,6 +239,12 @@ func (s *SpanDetector) updateMinSpan(ctx context.Context, att *ethpb.IndexedAtte
if source < 1 {
return nil
}
// handle source > target well
if source > target {
tmp := source
source = target
target = tmp
}
valIndices := make([]uint64, len(att.AttestingIndices))
copy(valIndices, att.AttestingIndices)
latestMinSpanDistanceObserved.Set(float64(att.Data.Target.Epoch - att.Data.Source.Epoch))
Expand Down Expand Up @@ -292,6 +317,12 @@ func (s *SpanDetector) updateMaxSpan(ctx context.Context, att *ethpb.IndexedAtte
defer traceSpan.End()
source := att.Data.Source.Epoch
target := att.Data.Target.Epoch
// handle source > target well
if source > target {
tmp := source
source = target
target = tmp
}
latestMaxSpanDistanceObserved.Set(float64(target - source))
valIndices := make([]uint64, len(att.AttestingIndices))
copy(valIndices, att.AttestingIndices)
Expand Down
104 changes: 104 additions & 0 deletions slasher/detection/attestations/spanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,29 @@ func TestSpanDetector_DetectSlashingsForAttestation_Surround(t *testing.T) {
17: {0, 0},
},
},
{
name: "Should slash if max span > distance && source > target",
sourceEpoch: 6,
targetEpoch: 3,
slashableEpoch: 7,
shouldSlash: true,
// Given a distance of (6 - 3) = 3, we want the validator at epoch 3 to have
// committed a slashable offense by having a max span of 4 > distance.
spansByEpochForValidator: map[uint64][3]uint16{
3: {0, 4},
},
},
{
name: "Should not slash if max span = distance && source > target",
sourceEpoch: 6,
targetEpoch: 3,
shouldSlash: false,
// Given a distance of (6 - 3) = 3, we want the validator at epoch 3 to NOT
// have committed slashable offense by having a max span of 1 < distance.
spansByEpochForValidator: map[uint64][3]uint16{
3: {0, 1},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -623,6 +646,87 @@ func TestSpanDetector_DetectSlashingsForAttestation_MultipleValidators(t *testin
indexedAttestation(1, 5, []uint64{3}),
},
},
{
name: "3 of 4 validators slashed, differing surrounds source > target",
incomingAtt: &ethpb.IndexedAttestation{
AttestingIndices: []uint64{0, 1, 2, 3},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{
Epoch: 7,
Root: []byte("good source"),
},
Target: &ethpb.Checkpoint{
Epoch: 5,
Root: []byte("good target"),
},
},
Signature: []byte{1, 2},
},
slashableEpochs: []uint64{8, 9, 10, 0},
// Detections - surround, surround, surround, none.
shouldSlash: []bool{true, true, true, false},
// Atts in map: (src, epoch) - 0: (1, 8), 1: (3, 9), 2: (2, 10), 3: (4, 6)
atts: []*ethpb.IndexedAttestation{
indexedAttestation(1, 8, []uint64{0}),
indexedAttestation(3, 9, []uint64{1}),
indexedAttestation(2, 10, []uint64{2}),
indexedAttestation(4, 6, []uint64{3}),
},
},
{
name: "3 of 4 validators slashed, differing surrounded source > target",
incomingAtt: &ethpb.IndexedAttestation{
AttestingIndices: []uint64{0, 1, 2, 3},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{
Epoch: 9,
Root: []byte("good source"),
},
Target: &ethpb.Checkpoint{
Epoch: 2,
Root: []byte("good target"),
},
},
Signature: []byte{1, 2},
},
slashableEpochs: []uint64{8, 8, 7, 0},
// Detections - surround, surround, surround, none.
shouldSlash: []bool{true, true, true, false},
// Atts in map: (src, epoch) - 0: (5, 8), 1: (3, 8), 2: (4, 7), 3: (1, 5)
atts: []*ethpb.IndexedAttestation{
indexedAttestation(5, 8, []uint64{0}),
indexedAttestation(3, 8, []uint64{1}),
indexedAttestation(4, 7, []uint64{2}),
indexedAttestation(1, 5, []uint64{3}),
},
},
{
name: "3 of 4 validators slashed, differing surrounded source > target in update",
incomingAtt: &ethpb.IndexedAttestation{
AttestingIndices: []uint64{0, 1, 2, 3},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{
Epoch: 2,
Root: []byte("good source"),
},
Target: &ethpb.Checkpoint{
Epoch: 9,
Root: []byte("good target"),
},
},
Signature: []byte{1, 2},
},
slashableEpochs: []uint64{8, 8, 7, 0},
// Detections - surround, surround, surround, none.
shouldSlash: []bool{true, true, true, false},
// Atts in map: (src, epoch) - 0: (5, 8), 1: (3, 8), 2: (4, 7), 3: (1, 5)
atts: []*ethpb.IndexedAttestation{
indexedAttestation(8, 5, []uint64{0}),
indexedAttestation(8, 3, []uint64{1}),
indexedAttestation(7, 4, []uint64{2}),
indexedAttestation(5, 1, []uint64{3}),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down