Skip to content

Commit

Permalink
Add fast function for comparing attestation data (#5834)
Browse files Browse the repository at this point in the history
* Add function for comparing attestation datas

* Fix imports

* Apply beneficial spots

* Gaz
  • Loading branch information
0xKiwi committed May 12, 2020
1 parent cd1a4f1 commit 6488ab4
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 5 deletions.
2 changes: 1 addition & 1 deletion beacon-chain/core/blocks/block_operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ func IsSlashableAttestationData(data1 *ethpb.AttestationData, data2 *ethpb.Attes
if data1 == nil || data2 == nil || data1.Target == nil || data2.Target == nil || data1.Source == nil || data2.Source == nil {
return false
}
isDoubleVote := !proto.Equal(data1, data2) && data1.Target.Epoch == data2.Target.Epoch
isDoubleVote := !attestationutil.AttDataIsEqual(data1, data2) && data1.Target.Epoch == data2.Target.Epoch
isSurroundVote := data1.Source.Epoch < data2.Source.Epoch && data2.Target.Epoch < data1.Target.Epoch
return isDoubleVote || isSurroundVote
}
Expand Down
6 changes: 5 additions & 1 deletion shared/attestationutil/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,9 @@ go_test(
name = "go_default_test",
srcs = ["attestation_utils_test.go"],
embed = [":go_default_library"],
deps = ["@com_github_prysmaticlabs_go_bitfield//:go_default_library"],
deps = [
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
],
)
27 changes: 27 additions & 0 deletions shared/attestationutil/attestation_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package attestationutil

import (
"bytes"
"context"
"fmt"
"reflect"
Expand Down Expand Up @@ -167,3 +168,29 @@ func IsValidAttestationIndices(ctx context.Context, indexedAttestation *ethpb.In
}
return nil
}

// AttDataIsEqual this function performs an equality check between 2 attestation data, if they're unequal, it will return false.
func AttDataIsEqual(attData1 *ethpb.AttestationData, attData2 *ethpb.AttestationData) bool {
if attData1.Slot != attData2.Slot {
return false
}
if attData1.CommitteeIndex != attData2.CommitteeIndex {
return false
}
if !bytes.Equal(attData1.BeaconBlockRoot, attData2.BeaconBlockRoot) {
return false
}
if attData1.Source.Epoch != attData2.Source.Epoch {
return false
}
if !bytes.Equal(attData1.Source.Root, attData2.Source.Root) {
return false
}
if attData1.Target.Epoch != attData2.Target.Epoch {
return false
}
if !bytes.Equal(attData1.Target.Root, attData2.Target.Root) {
return false
}
return true
}
187 changes: 187 additions & 0 deletions shared/attestationutil/attestation_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"reflect"
"testing"

"github.com/gogo/protobuf/proto"
eth "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/shared/attestationutil"
)
Expand Down Expand Up @@ -53,3 +55,188 @@ func BenchmarkAttestingIndices_PartialCommittee(b *testing.B) {
_ = attestationutil.AttestingIndices(bf, committee)
}
}

func TestAttDataIsEqual(t *testing.T) {
type test struct {
name string
attData1 *eth.AttestationData
attData2 *eth.AttestationData
equal bool
}
tests := []test{
{
name: "same",
attData1: &eth.AttestationData{
Slot: 5,
CommitteeIndex: 2,
BeaconBlockRoot: []byte("great block"),
Source: &eth.Checkpoint{
Epoch: 4,
Root: []byte("good source"),
},
Target: &eth.Checkpoint{
Epoch: 10,
Root: []byte("good target"),
},
},
attData2: &eth.AttestationData{
Slot: 5,
CommitteeIndex: 2,
BeaconBlockRoot: []byte("great block"),
Source: &eth.Checkpoint{
Epoch: 4,
Root: []byte("good source"),
},
Target: &eth.Checkpoint{
Epoch: 10,
Root: []byte("good target"),
},
},
equal: true,
},
{
name: "diff slot",
attData1: &eth.AttestationData{
Slot: 5,
CommitteeIndex: 2,
BeaconBlockRoot: []byte("great block"),
Source: &eth.Checkpoint{
Epoch: 4,
Root: []byte("good source"),
},
Target: &eth.Checkpoint{
Epoch: 10,
Root: []byte("good target"),
},
},
attData2: &eth.AttestationData{
Slot: 4,
CommitteeIndex: 2,
BeaconBlockRoot: []byte("great block"),
Source: &eth.Checkpoint{
Epoch: 4,
Root: []byte("good source"),
},
Target: &eth.Checkpoint{
Epoch: 10,
Root: []byte("good target"),
},
},
},
{
name: "diff block",
attData1: &eth.AttestationData{
Slot: 5,
CommitteeIndex: 2,
BeaconBlockRoot: []byte("good block"),
Source: &eth.Checkpoint{
Epoch: 4,
Root: []byte("good source"),
},
Target: &eth.Checkpoint{
Epoch: 10,
Root: []byte("good target"),
},
},
attData2: &eth.AttestationData{
Slot: 5,
CommitteeIndex: 2,
BeaconBlockRoot: []byte("great block"),
Source: &eth.Checkpoint{
Epoch: 4,
Root: []byte("good source"),
},
Target: &eth.Checkpoint{
Epoch: 10,
Root: []byte("good target"),
},
},
},
{
name: "diff source root",
attData1: &eth.AttestationData{
Slot: 5,
CommitteeIndex: 2,
BeaconBlockRoot: []byte("great block"),
Source: &eth.Checkpoint{
Epoch: 4,
Root: []byte("good source"),
},
Target: &eth.Checkpoint{
Epoch: 10,
Root: []byte("good target"),
},
},
attData2: &eth.AttestationData{
Slot: 5,
CommitteeIndex: 2,
BeaconBlockRoot: []byte("great block"),
Source: &eth.Checkpoint{
Epoch: 4,
Root: []byte("bad source"),
},
Target: &eth.Checkpoint{
Epoch: 10,
Root: []byte("good target"),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
areEqual := attestationutil.AttDataIsEqual(tt.attData1, tt.attData2)
if areEqual != tt.equal {
t.Errorf("Expected %t, received %t", tt.equal, areEqual)
}
})
}
}

func BenchmarkAttDataIsEqual(b *testing.B) {
attData1 := &eth.AttestationData{
Slot: 5,
CommitteeIndex: 2,
BeaconBlockRoot: []byte("great block"),
Source: &eth.Checkpoint{
Epoch: 4,
Root: []byte("good source"),
},
Target: &eth.Checkpoint{
Epoch: 10,
Root: []byte("good target"),
},
}
attData2 := &eth.AttestationData{
Slot: 5,
CommitteeIndex: 2,
BeaconBlockRoot: []byte("great block"),
Source: &eth.Checkpoint{
Epoch: 4,
Root: []byte("good source"),
},
Target: &eth.Checkpoint{
Epoch: 10,
Root: []byte("good target"),
},
}

b.Run("fast", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
areEqual := attestationutil.AttDataIsEqual(attData1, attData2)
if !areEqual {
b.Error(areEqual)
}
}
})

b.Run("proto.Equal", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
areEqual := proto.Equal(attData1, attData2)
if !areEqual {
b.Error(areEqual)
}
}
})
}
2 changes: 1 addition & 1 deletion slasher/detection/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ go_library(
visibility = ["//slasher:__subpackages__"],
deps = [
"//beacon-chain/state/stateutil:go_default_library",
"//shared/attestationutil:go_default_library",
"//shared/event:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/hashutil:go_default_library",
Expand All @@ -25,7 +26,6 @@ go_library(
"//slasher/detection/attestations/types:go_default_library",
"//slasher/detection/proposals:go_default_library",
"//slasher/detection/proposals/iface:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
Expand Down
4 changes: 2 additions & 2 deletions slasher/detection/detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import (
"bytes"
"context"

"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/attestationutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/sliceutil"
status "github.com/prysmaticlabs/prysm/slasher/db/types"
Expand Down Expand Up @@ -174,7 +174,7 @@ func isDoublePropose(
}

func isDoubleVote(incomingAtt *ethpb.IndexedAttestation, prevAtt *ethpb.IndexedAttestation) bool {
return !proto.Equal(incomingAtt.Data, prevAtt.Data) && incomingAtt.Data.Target.Epoch == prevAtt.Data.Target.Epoch
return !attestationutil.AttDataIsEqual(incomingAtt.Data, prevAtt.Data) && incomingAtt.Data.Target.Epoch == prevAtt.Data.Target.Epoch
}

func isSurrounding(incomingAtt *ethpb.IndexedAttestation, prevAtt *ethpb.IndexedAttestation) bool {
Expand Down

0 comments on commit 6488ab4

Please sign in to comment.