mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-22 03:30:35 +00:00
Add static analysis to enforce usage of InitWithReset (#5654)
* Refactor attestation packing slightly to reduce the skip slot / HTR of process slots * Merge branch 'master' into refactor-attestation-packing * gofmt * Merge branch 'refactor-attestation-packing' of github.com:prysmaticlabs/prysm into refactor-attestation-packing * Merge branch 'master' of github.com:prysmaticlabs/prysm into refactor-attestation-packing * Add static analysis to enforce usage of InitWithReset * Add comment / lint * fix a few usages * more fixes for featureconfig.Init * Fix analyzer * Merge branch 'sa-fc-init' of github.com:prysmaticlabs/prysm into sa-fc-init * Merge refs/heads/master into sa-fc-init * Merge refs/heads/master into sa-fc-init
This commit is contained in:
parent
6700383863
commit
e30349d410
@ -106,6 +106,7 @@ nogo(
|
|||||||
"//tools/analyzers/maligned:go_tool_library",
|
"//tools/analyzers/maligned:go_tool_library",
|
||||||
"//tools/analyzers/roughtime:go_tool_library",
|
"//tools/analyzers/roughtime:go_tool_library",
|
||||||
"//tools/analyzers/errcheck:go_tool_library",
|
"//tools/analyzers/errcheck:go_tool_library",
|
||||||
|
"//tools/analyzers/featureconfig:go_tool_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,12 +13,12 @@ go_library(
|
|||||||
"alias.go",
|
"alias.go",
|
||||||
"http_backup_handler.go",
|
"http_backup_handler.go",
|
||||||
] + select({
|
] + select({
|
||||||
"//conditions:default": [
|
|
||||||
"db_kafka_wrapped.go",
|
|
||||||
],
|
|
||||||
":kafka_disabled": [
|
":kafka_disabled": [
|
||||||
"db.go",
|
"db.go",
|
||||||
],
|
],
|
||||||
|
"//conditions:default": [
|
||||||
|
"db_kafka_wrapped.go",
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db",
|
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db",
|
||||||
visibility = [
|
visibility = [
|
||||||
|
@ -7,6 +7,7 @@ go_library(
|
|||||||
"passthrough.go",
|
"passthrough.go",
|
||||||
],
|
],
|
||||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db/kafka",
|
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/db/kafka",
|
||||||
|
tags = ["manual"],
|
||||||
visibility = ["//beacon-chain/db:__pkg__"],
|
visibility = ["//beacon-chain/db:__pkg__"],
|
||||||
deps = [
|
deps = [
|
||||||
"//beacon-chain/db/filters:go_default_library",
|
"//beacon-chain/db/filters:go_default_library",
|
||||||
|
@ -84,5 +84,10 @@
|
|||||||
".*/.*mock\\.go": "Mocks are OK",
|
".*/.*mock\\.go": "Mocks are OK",
|
||||||
".*/testmain\\.go": "Test runner generated code"
|
".*/testmain\\.go": "Test runner generated code"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"featureconfig": {
|
||||||
|
"only_files": {
|
||||||
|
".*_test\\.go": "Only tests"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,8 @@ The process for implementing new features using this package is as follows:
|
|||||||
cfg := &featureconfig.Flags{
|
cfg := &featureconfig.Flags{
|
||||||
VerifyAttestationSigs: true,
|
VerifyAttestationSigs: true,
|
||||||
}
|
}
|
||||||
featureconfig.Init(cfg)
|
resetCfg := featureconfig.InitWithReset(cfg)
|
||||||
|
defer resetCfg()
|
||||||
6. Add the string for the flags that should be running within E2E to E2EValidatorFlags
|
6. Add the string for the flags that should be running within E2E to E2EValidatorFlags
|
||||||
and E2EBeaconChainFlags.
|
and E2EBeaconChainFlags.
|
||||||
*/
|
*/
|
||||||
|
25
tools/analyzers/featureconfig/BUILD.bazel
Normal file
25
tools/analyzers/featureconfig/BUILD.bazel
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_tool_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["analyzer.go"],
|
||||||
|
importpath = "github.com/prysmaticlabs/prysm/tools/analyzers/featureconfig",
|
||||||
|
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 = "featureconfig",
|
||||||
|
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",
|
||||||
|
],
|
||||||
|
)
|
91
tools/analyzers/featureconfig/analyzer.go
Normal file
91
tools/analyzers/featureconfig/analyzer.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package featureconfig
|
||||||
|
|
||||||
|
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 = "Enforce usage of featureconfig.InitWithReset to prevent leaking globals in tests."
|
||||||
|
|
||||||
|
// Analyzer runs static analysis.
|
||||||
|
var Analyzer = &analysis.Analyzer{
|
||||||
|
Name: "featureconfig",
|
||||||
|
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.CallExpr)(nil),
|
||||||
|
(*ast.ExprStmt)(nil),
|
||||||
|
(*ast.GoStmt)(nil),
|
||||||
|
(*ast.DeferStmt)(nil),
|
||||||
|
(*ast.AssignStmt)(nil),
|
||||||
|
}
|
||||||
|
|
||||||
|
inspect.Preorder(nodeFilter, func(node ast.Node) {
|
||||||
|
if ce, ok := node.(*ast.CallExpr); ok && isPkgDot(ce.Fun, "featureconfig", "Init") {
|
||||||
|
reportForbiddenUsage(pass, ce.Pos())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch stmt := node.(type) {
|
||||||
|
case *ast.ExprStmt:
|
||||||
|
if call, ok := stmt.X.(*ast.CallExpr); ok&& isPkgDot(call.Fun, "featureconfig", "InitWithReset"){
|
||||||
|
reportUnhandledReset(pass, call.Lparen)
|
||||||
|
}
|
||||||
|
case *ast.GoStmt:
|
||||||
|
if isPkgDot(stmt.Call, "featureconfig", "InitWithReset") {
|
||||||
|
reportUnhandledReset(pass, stmt.Call.Lparen)
|
||||||
|
}
|
||||||
|
case *ast.DeferStmt:
|
||||||
|
if isPkgDot(stmt.Call, "featureconfig", "InitWithReset") {
|
||||||
|
reportUnhandledReset(pass, stmt.Call.Lparen)
|
||||||
|
}
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
if ce, ok := stmt.Rhs[0].(*ast.CallExpr); ok && isPkgDot(ce.Fun, "featureconfig", "InitWithReset") {
|
||||||
|
for i := 0; i < len(stmt.Lhs); i++ {
|
||||||
|
if id, ok := stmt.Lhs[i].(*ast.Ident); ok {
|
||||||
|
if id.Name == "_" {
|
||||||
|
reportUnhandledReset(pass, id.NamePos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func reportForbiddenUsage(pass *analysis.Pass, pos token.Pos) {
|
||||||
|
pass.Reportf(pos, "Use of featureconfig.Init is forbidden in test code. Please use " +
|
||||||
|
"featureconfig.InitWithReset and call reset in the same test function.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func reportUnhandledReset(pass *analysis.Pass, pos token.Pos) {
|
||||||
|
pass.Reportf(pos, "Unhandled reset featureconfig not found in test " +
|
||||||
|
"method. Be sure to call the returned reset function from featureconfig.InitWithReset " +
|
||||||
|
"within this test method.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPkgDot(expr ast.Expr, pkg, name string) bool {
|
||||||
|
sel, ok := expr.(*ast.SelectorExpr)
|
||||||
|
return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIdent(expr ast.Expr, ident string) bool {
|
||||||
|
id, ok := expr.(*ast.Ident)
|
||||||
|
return ok && id.Name == ident
|
||||||
|
}
|
@ -19,13 +19,9 @@ var (
|
|||||||
state = flag.Uint("state", 0, "Extract state at this slot.")
|
state = flag.Uint("state", 0, "Extract state at this slot.")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
fc := featureconfig.Get()
|
|
||||||
fc.WriteSSZStateTransitions = true
|
|
||||||
featureconfig.Init(fc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{WriteSSZStateTransitions: true})
|
||||||
|
defer resetCfg()
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
fmt.Println("Starting process...")
|
fmt.Println("Starting process...")
|
||||||
d, err := db.NewDB(*datadir, cache.NewStateSummaryCache())
|
d, err := db.NewDB(*datadir, cache.NewStateSummaryCache())
|
||||||
|
@ -37,7 +37,8 @@ func TestCancelledContext_WaitsForSynced(t *testing.T) {
|
|||||||
cfg := &featureconfig.Flags{
|
cfg := &featureconfig.Flags{
|
||||||
WaitForSynced: true,
|
WaitForSynced: true,
|
||||||
}
|
}
|
||||||
featureconfig.Init(cfg)
|
reset := featureconfig.InitWithReset(cfg)
|
||||||
|
defer reset()
|
||||||
v := &fakeValidator{}
|
v := &fakeValidator{}
|
||||||
run(cancelledContext(), v)
|
run(cancelledContext(), v)
|
||||||
if !v.WaitForSyncedCalled {
|
if !v.WaitForSyncedCalled {
|
||||||
|
@ -132,7 +132,8 @@ func TestAttestToBlockHead_BlocksDoubleAtt(t *testing.T) {
|
|||||||
config := &featureconfig.Flags{
|
config := &featureconfig.Flags{
|
||||||
ProtectAttester: true,
|
ProtectAttester: true,
|
||||||
}
|
}
|
||||||
featureconfig.Init(config)
|
reset := featureconfig.InitWithReset(config)
|
||||||
|
defer reset()
|
||||||
hook := logTest.NewGlobal()
|
hook := logTest.NewGlobal()
|
||||||
validator, m, finish := setup(t)
|
validator, m, finish := setup(t)
|
||||||
defer finish()
|
defer finish()
|
||||||
@ -173,7 +174,8 @@ func TestAttestToBlockHead_BlocksSurroundAtt(t *testing.T) {
|
|||||||
config := &featureconfig.Flags{
|
config := &featureconfig.Flags{
|
||||||
ProtectAttester: true,
|
ProtectAttester: true,
|
||||||
}
|
}
|
||||||
featureconfig.Init(config)
|
reset := featureconfig.InitWithReset(config)
|
||||||
|
defer reset()
|
||||||
hook := logTest.NewGlobal()
|
hook := logTest.NewGlobal()
|
||||||
validator, m, finish := setup(t)
|
validator, m, finish := setup(t)
|
||||||
defer finish()
|
defer finish()
|
||||||
@ -224,7 +226,8 @@ func TestAttestToBlockHead_BlocksSurroundedAtt(t *testing.T) {
|
|||||||
config := &featureconfig.Flags{
|
config := &featureconfig.Flags{
|
||||||
ProtectAttester: true,
|
ProtectAttester: true,
|
||||||
}
|
}
|
||||||
featureconfig.Init(config)
|
reset := featureconfig.InitWithReset(config)
|
||||||
|
defer reset()
|
||||||
hook := logTest.NewGlobal()
|
hook := logTest.NewGlobal()
|
||||||
validator, m, finish := setup(t)
|
validator, m, finish := setup(t)
|
||||||
defer finish()
|
defer finish()
|
||||||
|
@ -33,11 +33,11 @@ func setup(t *testing.T) (*validator, *mocks, func()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validator := &validator{
|
validator := &validator{
|
||||||
db: valDB,
|
db: valDB,
|
||||||
validatorClient: m.validatorClient,
|
validatorClient: m.validatorClient,
|
||||||
keyManager: testKeyManager,
|
keyManager: testKeyManager,
|
||||||
graffiti: []byte{},
|
graffiti: []byte{},
|
||||||
attLogs: make(map[[32]byte]*attSubmitted),
|
attLogs: make(map[[32]byte]*attSubmitted),
|
||||||
aggregatedSlotCommitteeIDCache: aggregatedSlotCommitteeIDCache,
|
aggregatedSlotCommitteeIDCache: aggregatedSlotCommitteeIDCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +119,8 @@ func TestProposeBlock_BlocksDoubleProposal(t *testing.T) {
|
|||||||
cfg := &featureconfig.Flags{
|
cfg := &featureconfig.Flags{
|
||||||
ProtectProposer: true,
|
ProtectProposer: true,
|
||||||
}
|
}
|
||||||
featureconfig.Init(cfg)
|
reset := featureconfig.InitWithReset(cfg)
|
||||||
|
defer reset()
|
||||||
hook := logTest.NewGlobal()
|
hook := logTest.NewGlobal()
|
||||||
validator, m, finish := setup(t)
|
validator, m, finish := setup(t)
|
||||||
defer finish()
|
defer finish()
|
||||||
@ -156,7 +157,8 @@ func TestProposeBlock_BlocksDoubleProposal_After54KEpochs(t *testing.T) {
|
|||||||
cfg := &featureconfig.Flags{
|
cfg := &featureconfig.Flags{
|
||||||
ProtectProposer: true,
|
ProtectProposer: true,
|
||||||
}
|
}
|
||||||
featureconfig.Init(cfg)
|
reset := featureconfig.InitWithReset(cfg)
|
||||||
|
defer reset()
|
||||||
hook := logTest.NewGlobal()
|
hook := logTest.NewGlobal()
|
||||||
validator, m, finish := setup(t)
|
validator, m, finish := setup(t)
|
||||||
defer finish()
|
defer finish()
|
||||||
@ -194,7 +196,8 @@ func TestProposeBlock_AllowsPastProposals(t *testing.T) {
|
|||||||
cfg := &featureconfig.Flags{
|
cfg := &featureconfig.Flags{
|
||||||
ProtectProposer: true,
|
ProtectProposer: true,
|
||||||
}
|
}
|
||||||
featureconfig.Init(cfg)
|
reset := featureconfig.InitWithReset(cfg)
|
||||||
|
defer reset()
|
||||||
hook := logTest.NewGlobal()
|
hook := logTest.NewGlobal()
|
||||||
validator, m, finish := setup(t)
|
validator, m, finish := setup(t)
|
||||||
defer finish()
|
defer finish()
|
||||||
@ -233,7 +236,8 @@ func TestProposeBlock_AllowsSameEpoch(t *testing.T) {
|
|||||||
cfg := &featureconfig.Flags{
|
cfg := &featureconfig.Flags{
|
||||||
ProtectProposer: true,
|
ProtectProposer: true,
|
||||||
}
|
}
|
||||||
featureconfig.Init(cfg)
|
reset := featureconfig.InitWithReset(cfg)
|
||||||
|
defer reset()
|
||||||
hook := logTest.NewGlobal()
|
hook := logTest.NewGlobal()
|
||||||
validator, m, finish := setup(t)
|
validator, m, finish := setup(t)
|
||||||
defer finish()
|
defer finish()
|
||||||
|
Loading…
Reference in New Issue
Block a user