Files
prysm/beacon-chain/core/helpers/weak_subjectivity_test.go
Preston Van Loon 2fd6bd8150 Add golang.org/x/tools modernize static analyzer and fix violations (#15946)
* Ran gopls modernize to fix everything

go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...

* Override rules_go provided dependency for golang.org/x/tools to v0.38.0.

To update this, checked out rules_go, then ran `bazel run //go/tools/releaser -- upgrade-dep -mirror=false org_golang_x_tools` and copied the patches.

* Fix buildtag violations and ignore buildtag violations in external

* Introduce modernize analyzer package.

* Add modernize "any" analyzer.

* Fix violations of any analyzer

* Add modernize "appendclipped" analyzer.

* Fix violations of appendclipped

* Add modernize "bloop" analyzer.

* Add modernize "fmtappendf" analyzer.

* Add modernize "forvar" analyzer.

* Add modernize "mapsloop" analyzer.

* Add modernize "minmax" analyzer.

* Fix violations of minmax analyzer

* Add modernize "omitzero" analyzer.

* Add modernize "rangeint" analyzer.

* Fix violations of rangeint.

* Add modernize "reflecttypefor" analyzer.

* Fix violations of reflecttypefor analyzer.

* Add modernize "slicescontains" analyzer.

* Add modernize "slicessort" analyzer.

* Add modernize "slicesdelete" analyzer. This is disabled by default for now. See https://go.dev/issue/73686.

* Add modernize "stringscutprefix" analyzer.

* Add modernize "stringsbuilder" analyzer.

* Fix violations of stringsbuilder analyzer.

* Add modernize "stringsseq" analyzer.

* Add modernize "testingcontext" analyzer.

* Add modernize "waitgroup" analyzer.

* Changelog fragment

* gofmt

* gazelle

* Add modernize "newexpr" analyzer.

* Disable newexpr until go1.26

* Add more details in WORKSPACE on how to update the override

* @nalepae feedback on min()

* gofmt

* Fix violations of forvar
2025-11-14 01:27:22 +00:00

288 lines
10 KiB
Go

package helpers_test
import (
"fmt"
"testing"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/testing/assert"
"github.com/OffchainLabs/prysm/v7/testing/require"
"github.com/OffchainLabs/prysm/v7/testing/util"
)
func TestWeakSubjectivity_ComputeWeakSubjectivityPeriod(t *testing.T) {
tests := []struct {
valCount uint64
avgBalance uint64
want primitives.Epoch
}{
// Asserting that we get the same numbers as defined in the reference table:
// https://github.com/ethereum/consensus-specs/blob/master/specs/phase0/weak-subjectivity.md#calculating-the-weak-subjectivity-period
{valCount: 32768, avgBalance: 28, want: 504},
{valCount: 65536, avgBalance: 28, want: 752},
{valCount: 131072, avgBalance: 28, want: 1248},
{valCount: 262144, avgBalance: 28, want: 2241},
{valCount: 524288, avgBalance: 28, want: 2241},
{valCount: 1048576, avgBalance: 28, want: 2241},
{valCount: 32768, avgBalance: 32, want: 665},
{valCount: 65536, avgBalance: 32, want: 1075},
{valCount: 131072, avgBalance: 32, want: 1894},
{valCount: 262144, avgBalance: 32, want: 3532},
{valCount: 524288, avgBalance: 32, want: 3532},
{valCount: 1048576, avgBalance: 32, want: 3532},
// Additional test vectors, to check case when T*(200+3*D) >= t*(200+12*D)
{valCount: 32768, avgBalance: 22, want: 277},
{valCount: 65536, avgBalance: 22, want: 298},
{valCount: 131072, avgBalance: 22, want: 340},
{valCount: 262144, avgBalance: 22, want: 424},
{valCount: 524288, avgBalance: 22, want: 593},
{valCount: 1048576, avgBalance: 22, want: 931},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("valCount: %d, avgBalance: %d", tt.valCount, tt.avgBalance), func(t *testing.T) {
// Reset committee cache - as we need to recalculate active validator set for each test.
helpers.ClearCache()
got, err := helpers.ComputeWeakSubjectivityPeriod(t.Context(), genState(t, tt.valCount, tt.avgBalance), params.BeaconConfig())
require.NoError(t, err)
assert.Equal(t, tt.want, got, "valCount: %v, avgBalance: %v", tt.valCount, tt.avgBalance)
})
}
}
type mockWsCheckpoint func() (stateRoot [32]byte, blockRoot [32]byte, e primitives.Epoch)
func TestWeakSubjectivity_IsWithinWeakSubjectivityPeriod(t *testing.T) {
tests := []struct {
name string
epoch primitives.Epoch
genWsState func() state.ReadOnlyBeaconState
genWsCheckpoint mockWsCheckpoint
want bool
wantedErr string
}{
{
name: "nil weak subjectivity state",
genWsState: func() state.ReadOnlyBeaconState {
return nil
},
genWsCheckpoint: func() ([32]byte, [32]byte, primitives.Epoch) {
return [32]byte{}, [32]byte{}, 42
},
wantedErr: "invalid weak subjectivity state or checkpoint",
},
{
name: "state and checkpoint roots do not match",
genWsState: func() state.ReadOnlyBeaconState {
beaconState := genState(t, 128, 32)
require.NoError(t, beaconState.SetSlot(42*params.BeaconConfig().SlotsPerEpoch))
err := beaconState.SetLatestBlockHeader(&ethpb.BeaconBlockHeader{
Slot: 42 * params.BeaconConfig().SlotsPerEpoch,
StateRoot: bytesutil.PadTo([]byte("stateroot1"), 32),
})
require.NoError(t, err)
return beaconState
},
genWsCheckpoint: func() ([32]byte, [32]byte, primitives.Epoch) {
var sr [32]byte
copy(sr[:], bytesutil.PadTo([]byte("stateroot2"), 32))
return sr, [32]byte{}, 42
},
wantedErr: fmt.Sprintf("state (%#x) and checkpoint (%#x) roots do not match",
bytesutil.PadTo([]byte("stateroot1"), 32), bytesutil.PadTo([]byte("stateroot2"), 32)),
},
{
name: "state and checkpoint epochs do not match",
genWsState: func() state.ReadOnlyBeaconState {
beaconState := genState(t, 128, 32)
require.NoError(t, beaconState.SetSlot(42*params.BeaconConfig().SlotsPerEpoch))
err := beaconState.SetLatestBlockHeader(&ethpb.BeaconBlockHeader{
Slot: 42 * params.BeaconConfig().SlotsPerEpoch,
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
})
require.NoError(t, err)
return beaconState
},
genWsCheckpoint: func() ([32]byte, [32]byte, primitives.Epoch) {
var sr [32]byte
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
return sr, [32]byte{}, 43
},
wantedErr: "state (42) and checkpoint (43) epochs do not match",
},
{
name: "no active validators",
genWsState: func() state.ReadOnlyBeaconState {
beaconState := genState(t, 0, 32)
require.NoError(t, beaconState.SetSlot(42*params.BeaconConfig().SlotsPerEpoch))
err := beaconState.SetLatestBlockHeader(&ethpb.BeaconBlockHeader{
Slot: 42 * params.BeaconConfig().SlotsPerEpoch,
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
})
require.NoError(t, err)
return beaconState
},
genWsCheckpoint: func() ([32]byte, [32]byte, primitives.Epoch) {
var sr [32]byte
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
return sr, [32]byte{}, 42
},
wantedErr: "cannot compute weak subjectivity period: no active validators found",
},
{
name: "outside weak subjectivity period",
epoch: 300,
genWsState: func() state.ReadOnlyBeaconState {
beaconState := genState(t, 128, 32)
require.NoError(t, beaconState.SetSlot(42*params.BeaconConfig().SlotsPerEpoch))
err := beaconState.SetLatestBlockHeader(&ethpb.BeaconBlockHeader{
Slot: 42 * params.BeaconConfig().SlotsPerEpoch,
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
})
require.NoError(t, err)
return beaconState
},
genWsCheckpoint: func() ([32]byte, [32]byte, primitives.Epoch) {
var sr [32]byte
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
return sr, [32]byte{}, 42
},
want: false,
},
{
name: "within weak subjectivity period",
epoch: 299,
genWsState: func() state.ReadOnlyBeaconState {
beaconState := genState(t, 128, 32)
require.NoError(t, beaconState.SetSlot(42*params.BeaconConfig().SlotsPerEpoch))
err := beaconState.SetLatestBlockHeader(&ethpb.BeaconBlockHeader{
Slot: 42 * params.BeaconConfig().SlotsPerEpoch,
StateRoot: bytesutil.PadTo([]byte("stateroot"), 32),
})
require.NoError(t, err)
return beaconState
},
genWsCheckpoint: func() ([32]byte, [32]byte, primitives.Epoch) {
var sr [32]byte
copy(sr[:], bytesutil.PadTo([]byte("stateroot"), 32))
return sr, [32]byte{}, 42
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
helpers.ClearCache()
sr, _, e := tt.genWsCheckpoint()
got, err := helpers.IsWithinWeakSubjectivityPeriod(t.Context(), tt.epoch, tt.genWsState(), sr, e, params.BeaconConfig())
if tt.wantedErr != "" {
assert.Equal(t, false, got)
assert.ErrorContains(t, tt.wantedErr, err)
return
}
require.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}
func TestWeakSubjectivity_ParseWeakSubjectivityInputString(t *testing.T) {
tests := []struct {
name string
input string
checkpt *ethpb.Checkpoint
wantedErr string
}{
{
name: "No column in string",
input: "0x111111;123",
wantedErr: "did not contain column",
},
{
name: "Too many columns in string",
input: "0x010203:123:456",
wantedErr: "weak subjectivity checkpoint input should be in `block_root:epoch_number` format",
},
{
name: "Incorrect block root length",
input: "0x010203:987",
wantedErr: "block root is not length of 32",
},
{
name: "Correct input",
input: "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:123456789",
checkpt: &ethpb.Checkpoint{
Root: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
Epoch: primitives.Epoch(123456789),
},
},
{
name: "Correct input without 0x",
input: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:123456789",
checkpt: &ethpb.Checkpoint{
Root: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
Epoch: primitives.Epoch(123456789),
},
},
{
name: "Correct input",
input: "0xF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:123456789",
checkpt: &ethpb.Checkpoint{
Root: []byte{0xf0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
Epoch: primitives.Epoch(123456789),
},
},
{
name: "Correct input without 0x",
input: "F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:123456789",
checkpt: &ethpb.Checkpoint{
Root: []byte{0xf0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
Epoch: primitives.Epoch(123456789),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
helpers.ClearCache()
wsCheckpt, err := helpers.ParseWeakSubjectivityInputString(tt.input)
if tt.wantedErr != "" {
require.ErrorContains(t, tt.wantedErr, err)
return
}
require.NoError(t, err)
require.NotNil(t, wsCheckpt)
require.DeepEqual(t, tt.checkpt.Root, wsCheckpt.Root, "Roots do not match")
require.Equal(t, tt.checkpt.Epoch, wsCheckpt.Epoch, "Epochs do not match")
})
}
}
func genState(t *testing.T, valCount, avgBalance uint64) state.BeaconState {
beaconState, err := util.NewBeaconState()
require.NoError(t, err)
validators := make([]*ethpb.Validator, valCount)
balances := make([]uint64, len(validators))
for i := range valCount {
validators[i] = &ethpb.Validator{
PublicKey: make([]byte, params.BeaconConfig().BLSPubkeyLength),
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: avgBalance * 1e9,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
balances[i] = validators[i].EffectiveBalance
}
require.NoError(t, beaconState.SetValidators(validators))
require.NoError(t, beaconState.SetBalances(balances))
return beaconState
}