prysm-pulse/beacon-chain/core/helpers/attestation_test.go
Radosław Kapka 73443208a1
Remove proto state (#11445)
* Remove native state flag and use native state in spectests

* remove feature from tests

* use e2e config in slasher simulator

* use params.BeaconConfig in testutil

* use correct function

* use minimal config in go_test

* fix TestListValidators

* parameterize sync committee bits and aggregation bits

* Fix TestServer_ListIndexedAttestations_GenesisEpoch

(cherry picked from commit 254ab623dde08ae8886b152facdbbd8889ed79db)

* fix more tests

* fix even more

* moreeee

* aaaand more

* one more fix

* one more

* simplify TestGetAltairDuties_UnknownPubkey

* comment out problematic test

* one more fix

* one more

* aaaand one more

* another

* use fieldparams in HydrateBlindedBeaconBlockBodyBellatrix

* create new package for mainnet tests

* TestServer_GetBellatrixBeaconBlock

* change slashed validator index

* clear cache in reward_test.go

* deprecate flag

* create bazel mainnet target

* move attester mainnet test to mainnet target

* "fix" proposer tests

* use minimal config in TestServer_circuitBreakBuilder

* fix TestProposer_ProposeBlock_OK

* more fixes in validator package

* more fixes

* more fixes

* test code

* move TestProposer_GetBeaconBlock_BellatrixEpoch to minimal

* finally

* remove proposer_bellatrix_mainnet_test.go

* fix TestServer_GetBellatrixBeaconBlock_HappyCase

* fix TestServer_GetBellatrixBeaconBlock_BuilderCase

* Preston needs to fix this!

* Revert "Preston needs to fix this!"

This reverts commit b03d97a16e3080e254c7b19d7f193d3c600ca869.

* remove proto state tests

* fix migration tests

* static analysis fix

* review

* remove proto state

* swap state in tests

* fix BUILD file in /proto/testing

* remove metrics test with nil state
2022-09-16 18:17:46 -04:00

339 lines
11 KiB
Go

package helpers_test
import (
"context"
"strconv"
"testing"
"time"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v3/config/params"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/crypto/bls"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/testing/assert"
"github.com/prysmaticlabs/prysm/v3/testing/require"
"github.com/prysmaticlabs/prysm/v3/testing/util"
prysmTime "github.com/prysmaticlabs/prysm/v3/time"
"github.com/prysmaticlabs/prysm/v3/time/slots"
)
func TestAttestation_IsAggregator(t *testing.T) {
t.Run("aggregator", func(t *testing.T) {
beaconState, privKeys := util.DeterministicGenesisState(t, 100)
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, 0, 0)
require.NoError(t, err)
sig := privKeys[0].Sign([]byte{'A'})
agg, err := helpers.IsAggregator(uint64(len(committee)), sig.Marshal())
require.NoError(t, err)
assert.Equal(t, true, agg, "Wanted aggregator true")
})
t.Run("not aggregator", func(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.MinimalSpecConfig())
beaconState, privKeys := util.DeterministicGenesisState(t, 2048)
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, 0, 0)
require.NoError(t, err)
sig := privKeys[0].Sign([]byte{'A'})
agg, err := helpers.IsAggregator(uint64(len(committee)), sig.Marshal())
require.NoError(t, err)
assert.Equal(t, false, agg, "Wanted aggregator false")
})
}
func TestAttestation_AggregateSignature(t *testing.T) {
t.Run("verified", func(t *testing.T) {
pubkeys := make([]bls.PublicKey, 0, 100)
atts := make([]*ethpb.Attestation, 0, 100)
msg := bytesutil.ToBytes32([]byte("hello"))
for i := 0; i < 100; i++ {
priv, err := bls.RandKey()
require.NoError(t, err)
pub := priv.PublicKey()
sig := priv.Sign(msg[:])
pubkeys = append(pubkeys, pub)
att := &ethpb.Attestation{Signature: sig.Marshal()}
atts = append(atts, att)
}
aggSig, err := helpers.AggregateSignature(atts)
require.NoError(t, err)
assert.Equal(t, true, aggSig.FastAggregateVerify(pubkeys, msg), "Signature did not verify")
})
t.Run("not verified", func(t *testing.T) {
pubkeys := make([]bls.PublicKey, 0, 100)
atts := make([]*ethpb.Attestation, 0, 100)
msg := []byte("hello")
for i := 0; i < 100; i++ {
priv, err := bls.RandKey()
require.NoError(t, err)
pub := priv.PublicKey()
sig := priv.Sign(msg)
pubkeys = append(pubkeys, pub)
att := &ethpb.Attestation{Signature: sig.Marshal()}
atts = append(atts, att)
}
aggSig, err := helpers.AggregateSignature(atts[0 : len(atts)-2])
require.NoError(t, err)
assert.Equal(t, false, aggSig.FastAggregateVerify(pubkeys, bytesutil.ToBytes32(msg)), "Signature not suppose to verify")
})
}
func TestAttestation_ComputeSubnetForAttestation(t *testing.T) {
// Create 10 committees
committeeCount := uint64(10)
validatorCount := committeeCount * params.BeaconConfig().TargetCommitteeSize
validators := make([]*ethpb.Validator, validatorCount)
for i := 0; i < len(validators); i++ {
k := make([]byte, 48)
copy(k, strconv.Itoa(i))
validators[i] = &ethpb.Validator{
PublicKey: k,
WithdrawalCredentials: make([]byte, 32),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
state, err := state_native.InitializeFromProtoPhase0(&ethpb.BeaconState{
Validators: validators,
Slot: 200,
BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot),
StateRoots: make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
})
require.NoError(t, err)
att := &ethpb.Attestation{
AggregationBits: []byte{'A'},
Data: &ethpb.AttestationData{
Slot: 34,
CommitteeIndex: 4,
BeaconBlockRoot: []byte{'C'},
Source: nil,
Target: nil,
},
Signature: []byte{'B'},
}
valCount, err := helpers.ActiveValidatorCount(context.Background(), state, slots.ToEpoch(att.Data.Slot))
require.NoError(t, err)
sub := helpers.ComputeSubnetForAttestation(valCount, att)
assert.Equal(t, uint64(6), sub, "Did not get correct subnet for attestation")
}
func Test_ValidateAttestationTime(t *testing.T) {
if params.BeaconNetworkConfig().MaximumGossipClockDisparity < 200*time.Millisecond {
t.Fatal("This test expects the maximum clock disparity to be at least 200ms")
}
type args struct {
attSlot types.Slot
genesisTime time.Time
}
tests := []struct {
name string
args args
wantedErr string
}{
{
name: "attestation.slot == current_slot",
args: args{
attSlot: 15,
genesisTime: prysmTime.Now().Add(-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
},
},
{
name: "attestation.slot == current_slot, received in middle of slot",
args: args{
attSlot: 15,
genesisTime: prysmTime.Now().Add(
-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second,
).Add(-(time.Duration(params.BeaconConfig().SecondsPerSlot/2) * time.Second)),
},
},
{
name: "attestation.slot == current_slot, received 200ms early",
args: args{
attSlot: 16,
genesisTime: prysmTime.Now().Add(
-16 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second,
).Add(-200 * time.Millisecond),
},
},
{
name: "attestation.slot > current_slot",
args: args{
attSlot: 16,
genesisTime: prysmTime.Now().Add(-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
},
wantedErr: "not within attestation propagation range",
},
{
name: "attestation.slot < current_slot-ATTESTATION_PROPAGATION_SLOT_RANGE",
args: args{
attSlot: 100 - params.BeaconNetworkConfig().AttestationPropagationSlotRange - 1,
genesisTime: prysmTime.Now().Add(-100 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
},
wantedErr: "not within attestation propagation range",
},
{
name: "attestation.slot = current_slot-ATTESTATION_PROPAGATION_SLOT_RANGE",
args: args{
attSlot: 100 - params.BeaconNetworkConfig().AttestationPropagationSlotRange,
genesisTime: prysmTime.Now().Add(-100 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
},
},
{
name: "attestation.slot = current_slot-ATTESTATION_PROPAGATION_SLOT_RANGE, received 200ms late",
args: args{
attSlot: 100 - params.BeaconNetworkConfig().AttestationPropagationSlotRange,
genesisTime: prysmTime.Now().Add(
-100 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second,
).Add(200 * time.Millisecond),
},
},
{
name: "attestation.slot is well beyond current slot",
args: args{
attSlot: 1 << 32,
genesisTime: prysmTime.Now().Add(-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
},
wantedErr: "which exceeds max allowed value relative to the local clock",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := helpers.ValidateAttestationTime(tt.args.attSlot, tt.args.genesisTime,
params.BeaconNetworkConfig().MaximumGossipClockDisparity)
if tt.wantedErr != "" {
assert.ErrorContains(t, tt.wantedErr, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestVerifyCheckpointEpoch_Ok(t *testing.T) {
// Genesis was 6 epochs ago exactly.
offset := params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot * 6)
genesis := time.Now().Add(-1 * time.Second * time.Duration(offset))
assert.Equal(t, true, helpers.VerifyCheckpointEpoch(&ethpb.Checkpoint{Epoch: 6}, genesis))
assert.Equal(t, true, helpers.VerifyCheckpointEpoch(&ethpb.Checkpoint{Epoch: 5}, genesis))
assert.Equal(t, false, helpers.VerifyCheckpointEpoch(&ethpb.Checkpoint{Epoch: 4}, genesis))
assert.Equal(t, false, helpers.VerifyCheckpointEpoch(&ethpb.Checkpoint{Epoch: 2}, genesis))
}
func TestValidateNilAttestation(t *testing.T) {
tests := []struct {
name string
attestation *ethpb.Attestation
errString string
}{
{
name: "nil attestation",
attestation: nil,
errString: "attestation can't be nil",
},
{
name: "nil attestation data",
attestation: &ethpb.Attestation{},
errString: "attestation's data can't be nil",
},
{
name: "nil attestation source",
attestation: &ethpb.Attestation{
Data: &ethpb.AttestationData{
Source: nil,
Target: &ethpb.Checkpoint{},
},
},
errString: "attestation's source can't be nil",
},
{
name: "nil attestation target",
attestation: &ethpb.Attestation{
Data: &ethpb.AttestationData{
Target: nil,
Source: &ethpb.Checkpoint{},
},
},
errString: "attestation's target can't be nil",
},
{
name: "nil attestation bitfield",
attestation: &ethpb.Attestation{
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
},
errString: "attestation's bitfield can't be nil",
},
{
name: "good attestation",
attestation: &ethpb.Attestation{
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{},
Source: &ethpb.Checkpoint{},
},
AggregationBits: []byte{},
},
errString: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.errString != "" {
require.ErrorContains(t, tt.errString, helpers.ValidateNilAttestation(tt.attestation))
} else {
require.NoError(t, helpers.ValidateNilAttestation(tt.attestation))
}
})
}
}
func TestValidateSlotTargetEpoch(t *testing.T) {
tests := []struct {
name string
attestation *ethpb.Attestation
errString string
}{
{
name: "incorrect slot",
attestation: &ethpb.Attestation{
Data: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{Epoch: 1},
Source: &ethpb.Checkpoint{},
},
AggregationBits: []byte{},
},
errString: "slot 0 does not match target epoch 1",
},
{
name: "good attestation",
attestation: &ethpb.Attestation{
Data: &ethpb.AttestationData{
Slot: 2 * params.BeaconConfig().SlotsPerEpoch,
Target: &ethpb.Checkpoint{Epoch: 2},
Source: &ethpb.Checkpoint{},
},
AggregationBits: []byte{},
},
errString: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.errString != "" {
require.ErrorContains(t, tt.errString, helpers.ValidateSlotTargetEpoch(tt.attestation.Data))
} else {
require.NoError(t, helpers.ValidateSlotTargetEpoch(tt.attestation.Data))
}
})
}
}