Add surround check to endpoint (#4065)

* first version of the watchtower api

* service files

* Begin work on grpc server

* More changes to server

* REnames and mock setup

* working test

* merge

* double propose detection test

* nishant review

* todo change

* gaz

* fix service

* gaz

* remove unused import

* gaz

* resolve circular dependency

* resolve circular dependency 2nd try

* remove package

* fix package

* fix test

* added tests

* gaz

* remove status check

* gaz

* remove context

* remove context

* change var name

* moved to rpc dir

* gaz

* remove server code

* gaz

* slasher server

* visibility change

* pb

* service update

* gaz

* slasher grpc server

* making it work

* setup db and start

* gaz

* service flags fixes

* grpc service running

* go imports

* remove new initializer

* gaz

* remove feature flags

* change back SetupSlasherDB

* fix SetupSlasherDB calls

* define err

* fix bad merge

* fix test

* fix imports

* fix imports

* fix imports

* add cancel

* comment stop

* fix cancel issue

* remove unneeded code

* bring back bad merge that removed TODO

* remove use of epoch as am input

* fixed slasher to be runable again

* wait for channel close

* gaz

* small test

* flags fix

* fix flag order

* double vote detection

* remove source epoch from indexed attestation indices

* change server method to receive indexed attestation

* start implementation

* double vote detection

* proto

* pb

* fir comment

* add surround detection and retrieval to endpoint

* nishant review

* import fix

* fix miss order

* fix detection 0 case
added tests

* terence review
This commit is contained in:
shayzluf 2019-11-21 12:41:23 +05:30 committed by GitHub
parent 835418d1e3
commit 4330839bc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 218 additions and 5 deletions

View File

@ -5,7 +5,6 @@ package ethereum.beacon.rpc.v1;
import "google/protobuf/empty.proto";
import "proto/eth/v1alpha1/beacon_block.proto";
import "proto/eth/v1alpha1/attestation.proto";
import "google/api/annotations.proto";
service AttesterService {
rpc RequestAttestation(AttestationRequest) returns (ethereum.eth.v1alpha1.AttestationData);

View File

@ -32,7 +32,8 @@ func detectMin(attestationEpochSpan uint64,
attestationSourceEpoch uint64) uint64 {
minSpan := uint64(recorderEpochSpan.MinEpochSpan)
if minSpan < attestationEpochSpan {
if minSpan > 0 && minSpan < attestationEpochSpan {
return minSpan + attestationSourceEpoch
}
return 0
@ -45,6 +46,13 @@ func detectMin(attestationEpochSpan uint64,
// Logic for this detection method was designed by https://github.com/protolambda
// Detailed here: https://github.com/protolambda/eth2-surround/blob/master/README.md#min-max-surround
func (ss *Server) DetectAndUpdateMaxEpochSpan(ctx context.Context, source uint64, target uint64, validatorIdx uint64) (uint64, error) {
if target < source {
return 0, fmt.Errorf(
"target: %d < source: %d ",
target,
source,
)
}
targetEpoch, span, spanMap, err := ss.detectSlashingByEpochSpan(source, target, validatorIdx, detectMax)
if err != nil {
return 0, err
@ -53,7 +61,7 @@ func (ss *Server) DetectAndUpdateMaxEpochSpan(ctx context.Context, source uint64
return targetEpoch, nil
}
for i := uint64(1); i < target-source; i++ {
val := uint32(span - i - 1)
val := uint32(span - i)
if _, ok := spanMap.EpochSpanMap[source+i]; !ok {
spanMap.EpochSpanMap[source+i] = &ethpb.MinMaxEpochSpan{}
}
@ -77,6 +85,13 @@ func (ss *Server) DetectAndUpdateMaxEpochSpan(ctx context.Context, source uint64
// Logic is following the detection method designed by https://github.com/protolambda
// Detailed here: https://github.com/protolambda/eth2-surround/blob/master/README.md#min-max-surround
func (ss *Server) DetectAndUpdateMinEpochSpan(ctx context.Context, source uint64, target uint64, validatorIdx uint64) (uint64, error) {
if target < source {
return 0, fmt.Errorf(
"target: %d < source: %d ",
target,
source,
)
}
targetEpoch, _, spanMap, err := ss.detectSlashingByEpochSpan(source, target, validatorIdx, detectMin)
if err != nil {
return 0, err
@ -109,7 +124,7 @@ func (ss *Server) DetectAndUpdateMinEpochSpan(ctx context.Context, source uint64
// attestation target. This method receives a detector function in order to be used
// for both surrounding and surrounded vote cases.
func (ss *Server) detectSlashingByEpochSpan(source, target, validatorIdx uint64, detector detectFn) (uint64, uint64, *ethpb.EpochSpanMap, error) {
span := target - source + 1
span := target - source
if span > params.BeaconConfig().WeakSubjectivityPeriod {
return 0, span, nil, fmt.Errorf("target: %d - source: %d > weakSubjectivityPeriod",
params.BeaconConfig().WeakSubjectivityPeriod,

View File

@ -45,7 +45,18 @@ func (ss *Server) IsSlashableAttestation(ctx context.Context, req *ethpb.Indexed
}
}
//TODO(#3133): add surround detection
for _, idx := range indices {
atts, err := ss.DetectSurroundVotes(ctx, req.Data.Source.Epoch, req.Data.Target.Epoch, idx)
if err != nil {
return nil, err
}
for _, ia := range atts {
atsSlashinngRes.AttesterSlashing = append(atsSlashinngRes.AttesterSlashing, &ethpb.AttesterSlashing{
Attestation_1: req,
Attestation_2: ia,
})
}
}
return atsSlashinngRes, nil
}
@ -87,3 +98,40 @@ func (ss *Server) SlashableAttestations(req *types.Empty, server ethpb.Slasher_S
//TODO(3133): implement stream provider for newly discovered listening to slashable attestation.
return status.Error(codes.Unimplemented, "not implemented")
}
// DetectSurroundVotes is a method used to return the attestation that were detected
// by min max surround detection method.
func (ss *Server) DetectSurroundVotes(ctx context.Context, source uint64, target uint64, validatorIdx uint64) ([]*ethpb.IndexedAttestation, error) {
minTargetEpoch, err := ss.DetectAndUpdateMinEpochSpan(ctx, source, target, validatorIdx)
if err != nil {
return nil, err
}
maxTargetEpoch, err := ss.DetectAndUpdateMaxEpochSpan(ctx, source, target, validatorIdx)
if err != nil {
return nil, err
}
var idxAtts []*ethpb.IndexedAttestation
if minTargetEpoch > 0 {
attestations, err := ss.SlasherDB.IndexedAttestation(minTargetEpoch, validatorIdx)
if err != nil {
return nil, err
}
for _, ia := range attestations {
if ia.Data.Source.Epoch > source && ia.Data.Target.Epoch < target {
idxAtts = append(idxAtts, ia)
}
}
}
if maxTargetEpoch > 0 {
attestations, err := ss.SlasherDB.IndexedAttestation(maxTargetEpoch, validatorIdx)
if err != nil {
return nil, err
}
for _, ia := range attestations {
if ia.Data.Source.Epoch < source && ia.Data.Target.Epoch > target {
idxAtts = append(idxAtts, ia)
}
}
}
return idxAtts, nil
}

View File

@ -418,3 +418,154 @@ func TestServer_DontSlashSameAttestationData(t *testing.T) {
t.Errorf("Should not return slashaing proof for same data: %v", sr)
}
}
func TestServer_SlashSurroundedAttestation(t *testing.T) {
dbs := db.SetupSlasherDB(t)
defer db.TeardownSlasherDB(t, dbs)
ctx := context.Background()
slasherServer := &Server{
ctx: ctx,
SlasherDB: dbs,
}
ia1 := &ethpb.IndexedAttestation{
CustodyBit_0Indices: []uint64{0},
CustodyBit_1Indices: []uint64{},
Signature: []byte("sig2"),
Data: &ethpb.AttestationData{
Slot: 4*params.BeaconConfig().SlotsPerEpoch + 1,
Index: 0,
BeaconBlockRoot: []byte("block1"),
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 4},
},
}
ia2 := &ethpb.IndexedAttestation{
CustodyBit_0Indices: []uint64{0},
CustodyBit_1Indices: []uint64{},
Signature: []byte("sig1"),
Data: &ethpb.AttestationData{
Slot: 4*params.BeaconConfig().SlotsPerEpoch + 1,
Index: 0,
BeaconBlockRoot: []byte("block2"),
Source: &ethpb.Checkpoint{Epoch: 2},
Target: &ethpb.Checkpoint{Epoch: 3},
},
}
want := &ethpb.AttesterSlashing{
Attestation_1: ia2,
Attestation_2: ia1,
}
if _, err := slasherServer.IsSlashableAttestation(ctx, ia1); err != nil {
t.Errorf("Could not call RPC method: %v", err)
}
sr, err := slasherServer.IsSlashableAttestation(ctx, ia2)
if err != nil {
t.Errorf("Could not call RPC method: %v", err)
}
if len(sr.AttesterSlashing) != 1 {
t.Fatalf("Should return 1 slashaing proof: %v", sr.AttesterSlashing)
}
if !proto.Equal(sr.AttesterSlashing[0], want) {
t.Errorf("Wanted slashing proof: %v got: %v", want, sr.AttesterSlashing[0])
}
}
func TestServer_SlashSurroundAttestation(t *testing.T) {
dbs := db.SetupSlasherDB(t)
defer db.TeardownSlasherDB(t, dbs)
ctx := context.Background()
slasherServer := &Server{
ctx: ctx,
SlasherDB: dbs,
}
ia1 := &ethpb.IndexedAttestation{
CustodyBit_0Indices: []uint64{0},
CustodyBit_1Indices: []uint64{},
Signature: []byte("sig2"),
Data: &ethpb.AttestationData{
Slot: 4*params.BeaconConfig().SlotsPerEpoch + 1,
Index: 0,
BeaconBlockRoot: []byte("block1"),
Source: &ethpb.Checkpoint{Epoch: 2},
Target: &ethpb.Checkpoint{Epoch: 3},
},
}
ia2 := &ethpb.IndexedAttestation{
CustodyBit_0Indices: []uint64{0},
CustodyBit_1Indices: []uint64{},
Signature: []byte("sig1"),
Data: &ethpb.AttestationData{
Slot: 4*params.BeaconConfig().SlotsPerEpoch + 1,
Index: 0,
BeaconBlockRoot: []byte("block2"),
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 4},
},
}
want := &ethpb.AttesterSlashing{
Attestation_1: ia2,
Attestation_2: ia1,
}
if _, err := slasherServer.IsSlashableAttestation(ctx, ia1); err != nil {
t.Errorf("Could not call RPC method: %v", err)
}
sr, err := slasherServer.IsSlashableAttestation(ctx, ia2)
if err != nil {
t.Errorf("Could not call RPC method: %v", err)
}
if len(sr.AttesterSlashing) != 1 {
t.Fatalf("Should return 1 slashaing proof: %v", sr.AttesterSlashing)
}
if !proto.Equal(sr.AttesterSlashing[0], want) {
t.Errorf("Wanted slashing proof: %v got: %v", want, sr.AttesterSlashing[0])
}
}
func TestServer_DontSlashValidAttestations(t *testing.T) {
dbs := db.SetupSlasherDB(t)
defer db.TeardownSlasherDB(t, dbs)
ctx := context.Background()
slasherServer := &Server{
ctx: ctx,
SlasherDB: dbs,
}
ia1 := &ethpb.IndexedAttestation{
CustodyBit_0Indices: []uint64{0},
CustodyBit_1Indices: []uint64{},
Signature: []byte("sig2"),
Data: &ethpb.AttestationData{
Slot: 5*params.BeaconConfig().SlotsPerEpoch + 1,
Index: 0,
BeaconBlockRoot: []byte("block1"),
Source: &ethpb.Checkpoint{Epoch: 2},
Target: &ethpb.Checkpoint{Epoch: 4},
},
}
ia2 := &ethpb.IndexedAttestation{
CustodyBit_0Indices: []uint64{0},
CustodyBit_1Indices: []uint64{},
Signature: []byte("sig1"),
Data: &ethpb.AttestationData{
Slot: 5*params.BeaconConfig().SlotsPerEpoch + 1,
Index: 0,
BeaconBlockRoot: []byte("block2"),
Source: &ethpb.Checkpoint{Epoch: 3},
Target: &ethpb.Checkpoint{Epoch: 5},
},
}
if _, err := slasherServer.IsSlashableAttestation(ctx, ia1); err != nil {
t.Errorf("Could not call RPC method: %v", err)
}
sr, err := slasherServer.IsSlashableAttestation(ctx, ia2)
if err != nil {
t.Errorf("Could not call RPC method: %v", err)
}
if len(sr.AttesterSlashing) != 0 {
t.Errorf("Should not return slashaing proof for same data: %v", sr)
}
}