No-op Instruction analyzer (#7249)

* analyzer, tests and fixes
* error check
* gazelle
* add more asserts
* Merge branch 'master' into nop-analyzer
* Merge refs/heads/master into nop-analyzer
* fix no-op in blst package
* Merge refs/heads/master into nop-analyzer
* add assert after mutation
* mark test as TODO
* add issue number to TODO
This commit is contained in:
Radosław Kapka 2020-09-17 18:18:19 +02:00 committed by GitHub
parent e1e233a6d0
commit a2cf235687
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 175 additions and 18 deletions

View File

@ -110,6 +110,7 @@ nogo(
"//tools/analyzers/featureconfig:go_tool_library",
"//tools/analyzers/comparesame:go_tool_library",
"//tools/analyzers/shadowpredecl:go_tool_library",
"//tools/analyzers/nop:go_tool_library",
] + select({
# nogo checks that fail with coverage enabled.
":coverage_enabled": [],

View File

@ -123,5 +123,12 @@
"rules_go_work-.*": "Third party code",
"tools/analyzers/shadowpredecl/testdata/shadow.go": "Analyzer testdata has to break rules"
}
},
"nop": {
"exclude_files": {
"external/.*": "Third party code",
"rules_go_work-.*": "Third party code",
"tools/analyzers/nop/testdata/no_op.go": "Analyzer testdata has to break rules"
}
}
}

View File

@ -205,7 +205,8 @@ func (s *Signature) Marshal() []byte {
// Copy returns a full deep copy of a signature.
func (s *Signature) Copy() iface.Signature {
return &Signature{s: &*s.s}
sign := *s.s
return &Signature{s: &sign}
}
// VerifyCompressed verifies that the compressed signature and pubkey

View File

@ -137,3 +137,18 @@ func TestSignatureFromBytes(t *testing.T) {
})
}
}
// TODO(7249): Make this test work
/*func TestCopy(t *testing.T) {
key := RandKey().(*bls12SecretKey)
signatureA := &Signature{s: new(blstSignature).Sign(key.p, []byte("foo"), dst)}
signatureB, ok := signatureA.Copy().(*Signature)
require.Equal(t, true, ok)
assert.NotEqual(t, signatureA, signatureB)
assert.NotEqual(t, signatureA.s, signatureB.s)
assert.DeepEqual(t, signatureA, signatureB)
signatureA.s = new(blstSignature).Sign(key.p, []byte("bar"), dst)
assert.DeepNotEqual(t, signatureA, signatureB)
}*/

View File

@ -210,5 +210,6 @@ func (s *Signature) Marshal() []byte {
// Copy returns a full deep copy of a signature.
func (s *Signature) Copy() iface.Signature {
return &Signature{s: &*s.s}
sign := *s.s
return &Signature{s: &sign}
}

View File

@ -1,18 +1,17 @@
package herumi_test
package herumi
import (
"errors"
"testing"
bls12 "github.com/herumi/bls-eth-go-binary/bls"
"github.com/prysmaticlabs/prysm/shared/bls/herumi"
"github.com/prysmaticlabs/prysm/shared/bls/iface"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
)
func TestSignVerify(t *testing.T) {
priv := herumi.RandKey()
priv := RandKey()
pub := priv.PublicKey()
msg := []byte("hello")
sig := priv.Sign(msg)
@ -25,14 +24,14 @@ func TestAggregateVerify(t *testing.T) {
var msgs [][32]byte
for i := 0; i < 100; i++ {
msg := [32]byte{'h', 'e', 'l', 'l', 'o', byte(i)}
priv := herumi.RandKey()
priv := RandKey()
pub := priv.PublicKey()
sig := priv.Sign(msg[:])
pubkeys = append(pubkeys, pub)
sigs = append(sigs, sig)
msgs = append(msgs, msg)
}
aggSig := herumi.Aggregate(sigs)
aggSig := Aggregate(sigs)
assert.DeepEqual(t, true, aggSig.AggregateVerify(pubkeys, msgs))
}
@ -41,13 +40,13 @@ func TestFastAggregateVerify(t *testing.T) {
sigs := make([]iface.Signature, 0, 100)
msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
for i := 0; i < 100; i++ {
priv := herumi.RandKey()
priv := RandKey()
pub := priv.PublicKey()
sig := priv.Sign(msg[:])
pubkeys = append(pubkeys, pub)
sigs = append(sigs, sig)
}
aggSig := herumi.AggregateSignatures(sigs)
aggSig := AggregateSignatures(sigs)
assert.DeepEqual(t, true, aggSig.FastAggregateVerify(pubkeys, msg))
}
@ -57,14 +56,14 @@ func TestMultipleSignatureVerification(t *testing.T) {
var msgs [][32]byte
for i := 0; i < 100; i++ {
msg := [32]byte{'h', 'e', 'l', 'l', 'o', byte(i)}
priv := herumi.RandKey()
priv := RandKey()
pub := priv.PublicKey()
sig := priv.Sign(msg[:])
pubkeys = append(pubkeys, pub)
sigs = append(sigs, sig)
msgs = append(msgs, msg)
}
verify, err := herumi.VerifyMultipleSignatures(sigs, msgs, pubkeys)
verify, err := VerifyMultipleSignatures(sigs, msgs, pubkeys)
assert.NoError(t, err)
assert.Equal(t, true, verify, "Signature did not verify")
}
@ -75,7 +74,7 @@ func TestMultipleSignatureVerification_FailsCorrectly(t *testing.T) {
var msgs [][32]byte
for i := 0; i < 100; i++ {
msg := [32]byte{'h', 'e', 'l', 'l', 'o', byte(i)}
priv := herumi.RandKey()
priv := RandKey()
pub := priv.PublicKey()
sig := priv.Sign(msg[:])
pubkeys = append(pubkeys, pub)
@ -113,21 +112,21 @@ func TestMultipleSignatureVerification_FailsCorrectly(t *testing.T) {
bls12.G2Add(firstG2, firstG2, g2Point)
bls12.G2Sub(secondG2, secondG2, g2Point)
lastSig, err := herumi.SignatureFromBytes(rawSig.Serialize())
lastSig, err := SignatureFromBytes(rawSig.Serialize())
require.NoError(t, err)
secondLastSig, err = herumi.SignatureFromBytes(rawSig2.Serialize())
secondLastSig, err = SignatureFromBytes(rawSig2.Serialize())
require.NoError(t, err)
sigs[len(sigs)-1] = lastSig
sigs[len(sigs)-2] = secondLastSig
// This method is expected to pass, as it would not
// be able to detect bad signatures
aggSig := herumi.AggregateSignatures(sigs)
aggSig := AggregateSignatures(sigs)
if !aggSig.AggregateVerify(pubkeys, msgs) {
t.Error("Signature did not verify")
}
// This method would be expected to fail.
verify, err := herumi.VerifyMultipleSignatures(sigs, msgs, pubkeys)
verify, err := VerifyMultipleSignatures(sigs, msgs, pubkeys)
assert.NoError(t, err)
assert.Equal(t, false, verify, "Signature verified when it was not supposed to")
}
@ -136,7 +135,7 @@ func TestFastAggregateVerify_ReturnsFalseOnEmptyPubKeyList(t *testing.T) {
var pubkeys []iface.PublicKey
msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
aggSig := herumi.NewAggregateSignature()
aggSig := NewAggregateSignature()
if aggSig.FastAggregateVerify(pubkeys, msg) != false {
t.Error("Expected FastAggregateVerify to return false with empty input " +
"of public keys.")
@ -181,7 +180,7 @@ func TestSignatureFromBytes(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
res, err := herumi.SignatureFromBytes(test.input)
res, err := SignatureFromBytes(test.input)
if test.err != nil {
assert.ErrorContains(t, test.err.Error(), err)
} else {
@ -191,3 +190,16 @@ func TestSignatureFromBytes(t *testing.T) {
})
}
}
func TestCopy(t *testing.T) {
signatureA := &Signature{s: bls12.HashAndMapToSignature([]byte("foo"))}
signatureB, ok := signatureA.Copy().(*Signature)
require.Equal(t, true, ok)
assert.NotEqual(t, signatureA, signatureB)
assert.NotEqual(t, signatureA.s, signatureB.s)
assert.DeepEqual(t, signatureA, signatureB)
signatureA.s.Add(bls12.HashAndMapToSignature([]byte("bar")))
assert.DeepNotEqual(t, signatureA, signatureB)
}

View File

@ -0,0 +1,28 @@
load("@prysm//tools/go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_tool_library")
go_library(
name = "go_default_library",
srcs = ["analyzer.go"],
importpath = "github.com/prysmaticlabs/prysm/tools/analyzers/nop",
visibility = ["//visibility:public"],
deps = [
"@org_golang_x_tools//go/analysis:go_default_library",
"@org_golang_x_tools//go/analysis/passes/inspect:go_default_library",
"@org_golang_x_tools//go/ast/inspector:go_default_library",
],
)
go_tool_library(
name = "go_tool_library",
srcs = ["analyzer.go"],
importpath = "github.com/prysmaticlabs/prysm/tools/analyzers/nop",
visibility = ["//visibility:public"],
deps = [
"@org_golang_x_tools//go/analysis:go_tool_library",
"@org_golang_x_tools//go/analysis/passes/inspect:go_tool_library",
"@org_golang_x_tools//go/ast/inspector:go_tool_library",
],
)
# gazelle:exclude analyzer_test.go

View File

@ -0,0 +1,59 @@
package nop
import (
"errors"
"go/ast"
"go/token"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
)
// Doc explaining the tool.
const Doc = "Tool to detect no-op instructions."
const message = "Found a no-op instruction that can be safely removed. " +
"It might be a result of writing code that does not do what was intended."
// Analyzer runs static analysis.
var Analyzer = &analysis.Analyzer{
Name: "nop",
Doc: Doc,
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run,
}
func run(pass *analysis.Pass) (interface{}, error) {
inspect, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
if !ok {
return nil, errors.New("analyzer is not type *inspector.Inspector")
}
nodeFilter := []ast.Node{
(*ast.StarExpr)(nil),
(*ast.UnaryExpr)(nil),
}
inspect.Preorder(nodeFilter, func(node ast.Node) {
switch expr := node.(type) {
case *ast.StarExpr:
unaryExpr, ok := expr.X.(*ast.UnaryExpr)
if !ok {
return
}
if unaryExpr.Op == token.AND {
pass.Reportf(expr.Star, message)
}
case *ast.UnaryExpr:
if expr.Op == token.AND {
if _, ok := expr.X.(*ast.StarExpr); ok {
pass.Reportf(expr.OpPos, message)
}
}
}
})
return nil, nil
}

View File

@ -0,0 +1,11 @@
package nop
import (
"testing"
"golang.org/x/tools/go/analysis/analysistest"
)
func TestAnalyzer(t *testing.T) {
analysistest.Run(t, analysistest.TestData(), Analyzer)
}

View File

@ -0,0 +1,8 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["no_op.go"],
importpath = "github.com/prysmaticlabs/prysm/tools/analyzers/nop/testdata",
visibility = ["//visibility:public"],
)

14
tools/analyzers/nop/testdata/no_op.go vendored Normal file
View File

@ -0,0 +1,14 @@
package testdata
type foo struct {
}
func AddressOfDereferencedValue() {
x := &foo{}
_ = &*x // want "Found a no-op instruction that can be safely removed. It might be a result of writing code that does not work as expected."
}
func DereferencedAddressOfValue() {
x := foo{}
_ = *&x // want "Found a no-op instruction that can be safely removed. It might be a result of writing code that does not work as expected."
}