github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/helpers/attestation_test.go (about)

     1  package helpers_test
     2  
     3  import (
     4  	"strconv"
     5  	"testing"
     6  	"time"
     7  
     8  	types "github.com/prysmaticlabs/eth2-types"
     9  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    10  	"github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
    11  	pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    12  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    13  	"github.com/prysmaticlabs/prysm/shared/bls"
    14  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    15  	"github.com/prysmaticlabs/prysm/shared/params"
    16  	"github.com/prysmaticlabs/prysm/shared/testutil"
    17  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    18  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    19  	"github.com/prysmaticlabs/prysm/shared/timeutils"
    20  )
    21  
    22  func TestAttestation_IsAggregator(t *testing.T) {
    23  	t.Run("aggregator", func(t *testing.T) {
    24  		beaconState, privKeys := testutil.DeterministicGenesisState(t, 100)
    25  		committee, err := helpers.BeaconCommitteeFromState(beaconState, 0, 0)
    26  		require.NoError(t, err)
    27  		sig := privKeys[0].Sign([]byte{'A'})
    28  		agg, err := helpers.IsAggregator(uint64(len(committee)), sig.Marshal())
    29  		require.NoError(t, err)
    30  		assert.Equal(t, true, agg, "Wanted aggregator true")
    31  	})
    32  
    33  	t.Run("not aggregator", func(t *testing.T) {
    34  		params.UseMinimalConfig()
    35  		defer params.UseMainnetConfig()
    36  		beaconState, privKeys := testutil.DeterministicGenesisState(t, 2048)
    37  
    38  		committee, err := helpers.BeaconCommitteeFromState(beaconState, 0, 0)
    39  		require.NoError(t, err)
    40  		sig := privKeys[0].Sign([]byte{'A'})
    41  		agg, err := helpers.IsAggregator(uint64(len(committee)), sig.Marshal())
    42  		require.NoError(t, err)
    43  		assert.Equal(t, false, agg, "Wanted aggregator false")
    44  	})
    45  }
    46  
    47  func TestAttestation_AggregateSignature(t *testing.T) {
    48  	t.Run("verified", func(t *testing.T) {
    49  		pubkeys := make([]bls.PublicKey, 0, 100)
    50  		atts := make([]*ethpb.Attestation, 0, 100)
    51  		msg := bytesutil.ToBytes32([]byte("hello"))
    52  		for i := 0; i < 100; i++ {
    53  			priv, err := bls.RandKey()
    54  			require.NoError(t, err)
    55  			pub := priv.PublicKey()
    56  			sig := priv.Sign(msg[:])
    57  			pubkeys = append(pubkeys, pub)
    58  			att := &ethpb.Attestation{Signature: sig.Marshal()}
    59  			atts = append(atts, att)
    60  		}
    61  		aggSig, err := helpers.AggregateSignature(atts)
    62  		require.NoError(t, err)
    63  		assert.Equal(t, true, aggSig.FastAggregateVerify(pubkeys, msg), "Signature did not verify")
    64  	})
    65  
    66  	t.Run("not verified", func(t *testing.T) {
    67  		pubkeys := make([]bls.PublicKey, 0, 100)
    68  		atts := make([]*ethpb.Attestation, 0, 100)
    69  		msg := []byte("hello")
    70  		for i := 0; i < 100; i++ {
    71  			priv, err := bls.RandKey()
    72  			require.NoError(t, err)
    73  			pub := priv.PublicKey()
    74  			sig := priv.Sign(msg)
    75  			pubkeys = append(pubkeys, pub)
    76  			att := &ethpb.Attestation{Signature: sig.Marshal()}
    77  			atts = append(atts, att)
    78  		}
    79  		aggSig, err := helpers.AggregateSignature(atts[0 : len(atts)-2])
    80  		require.NoError(t, err)
    81  		assert.Equal(t, false, aggSig.FastAggregateVerify(pubkeys, bytesutil.ToBytes32(msg)), "Signature not suppose to verify")
    82  	})
    83  }
    84  
    85  func TestAttestation_ComputeSubnetForAttestation(t *testing.T) {
    86  	// Create 10 committees
    87  	committeeCount := uint64(10)
    88  	validatorCount := committeeCount * params.BeaconConfig().TargetCommitteeSize
    89  	validators := make([]*ethpb.Validator, validatorCount)
    90  
    91  	for i := 0; i < len(validators); i++ {
    92  		k := make([]byte, 48)
    93  		copy(k, strconv.Itoa(i))
    94  		validators[i] = &ethpb.Validator{
    95  			PublicKey:             k,
    96  			WithdrawalCredentials: make([]byte, 32),
    97  			ExitEpoch:             params.BeaconConfig().FarFutureEpoch,
    98  		}
    99  	}
   100  
   101  	state, err := v1.InitializeFromProto(&pb.BeaconState{
   102  		Validators:  validators,
   103  		Slot:        200,
   104  		BlockRoots:  make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot),
   105  		StateRoots:  make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot),
   106  		RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
   107  	})
   108  	require.NoError(t, err)
   109  	att := &ethpb.Attestation{
   110  		AggregationBits: []byte{'A'},
   111  		Data: &ethpb.AttestationData{
   112  			Slot:            34,
   113  			CommitteeIndex:  4,
   114  			BeaconBlockRoot: []byte{'C'},
   115  			Source:          nil,
   116  			Target:          nil,
   117  		},
   118  		Signature: []byte{'B'},
   119  	}
   120  	valCount, err := helpers.ActiveValidatorCount(state, helpers.SlotToEpoch(att.Data.Slot))
   121  	require.NoError(t, err)
   122  	sub := helpers.ComputeSubnetForAttestation(valCount, att)
   123  	assert.Equal(t, uint64(6), sub, "Did not get correct subnet for attestation")
   124  }
   125  
   126  func Test_ValidateAttestationTime(t *testing.T) {
   127  	if params.BeaconNetworkConfig().MaximumGossipClockDisparity < 200*time.Millisecond {
   128  		t.Fatal("This test expects the maximum clock disparity to be at least 200ms")
   129  	}
   130  
   131  	type args struct {
   132  		attSlot     types.Slot
   133  		genesisTime time.Time
   134  	}
   135  	tests := []struct {
   136  		name      string
   137  		args      args
   138  		wantedErr string
   139  	}{
   140  		{
   141  			name: "attestation.slot == current_slot",
   142  			args: args{
   143  				attSlot:     15,
   144  				genesisTime: timeutils.Now().Add(-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
   145  			},
   146  		},
   147  		{
   148  			name: "attestation.slot == current_slot, received in middle of slot",
   149  			args: args{
   150  				attSlot: 15,
   151  				genesisTime: timeutils.Now().Add(
   152  					-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second,
   153  				).Add(-(time.Duration(params.BeaconConfig().SecondsPerSlot/2) * time.Second)),
   154  			},
   155  		},
   156  		{
   157  			name: "attestation.slot == current_slot, received 200ms early",
   158  			args: args{
   159  				attSlot: 16,
   160  				genesisTime: timeutils.Now().Add(
   161  					-16 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second,
   162  				).Add(-200 * time.Millisecond),
   163  			},
   164  		},
   165  		{
   166  			name: "attestation.slot > current_slot",
   167  			args: args{
   168  				attSlot:     16,
   169  				genesisTime: timeutils.Now().Add(-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
   170  			},
   171  			wantedErr: "not within attestation propagation range",
   172  		},
   173  		{
   174  			name: "attestation.slot < current_slot-ATTESTATION_PROPAGATION_SLOT_RANGE",
   175  			args: args{
   176  				attSlot:     100 - params.BeaconNetworkConfig().AttestationPropagationSlotRange - 1,
   177  				genesisTime: timeutils.Now().Add(-100 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
   178  			},
   179  			wantedErr: "not within attestation propagation range",
   180  		},
   181  		{
   182  			name: "attestation.slot = current_slot-ATTESTATION_PROPAGATION_SLOT_RANGE",
   183  			args: args{
   184  				attSlot:     100 - params.BeaconNetworkConfig().AttestationPropagationSlotRange,
   185  				genesisTime: timeutils.Now().Add(-100 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
   186  			},
   187  		},
   188  		{
   189  			name: "attestation.slot = current_slot-ATTESTATION_PROPAGATION_SLOT_RANGE, received 200ms late",
   190  			args: args{
   191  				attSlot: 100 - params.BeaconNetworkConfig().AttestationPropagationSlotRange,
   192  				genesisTime: timeutils.Now().Add(
   193  					-100 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second,
   194  				).Add(200 * time.Millisecond),
   195  			},
   196  		},
   197  		{
   198  			name: "attestation.slot is well beyond current slot",
   199  			args: args{
   200  				attSlot:     1 << 32,
   201  				genesisTime: timeutils.Now().Add(-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second),
   202  			},
   203  			wantedErr: "which exceeds max allowed value relative to the local clock",
   204  		},
   205  	}
   206  	for _, tt := range tests {
   207  		t.Run(tt.name, func(t *testing.T) {
   208  			err := helpers.ValidateAttestationTime(tt.args.attSlot, tt.args.genesisTime,
   209  				params.BeaconNetworkConfig().MaximumGossipClockDisparity)
   210  			if tt.wantedErr != "" {
   211  				assert.ErrorContains(t, tt.wantedErr, err)
   212  			} else {
   213  				assert.NoError(t, err)
   214  			}
   215  		})
   216  	}
   217  }
   218  
   219  func TestVerifyCheckpointEpoch_Ok(t *testing.T) {
   220  	// Genesis was 6 epochs ago exactly.
   221  	offset := params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot * 6)
   222  	genesis := time.Now().Add(-1 * time.Second * time.Duration(offset))
   223  	assert.Equal(t, true, helpers.VerifyCheckpointEpoch(&ethpb.Checkpoint{Epoch: 6}, genesis))
   224  	assert.Equal(t, true, helpers.VerifyCheckpointEpoch(&ethpb.Checkpoint{Epoch: 5}, genesis))
   225  	assert.Equal(t, false, helpers.VerifyCheckpointEpoch(&ethpb.Checkpoint{Epoch: 4}, genesis))
   226  	assert.Equal(t, false, helpers.VerifyCheckpointEpoch(&ethpb.Checkpoint{Epoch: 2}, genesis))
   227  }
   228  
   229  func TestValidateNilAttestation(t *testing.T) {
   230  	tests := []struct {
   231  		name        string
   232  		attestation *ethpb.Attestation
   233  		errString   string
   234  	}{
   235  		{
   236  			name:        "nil attestation",
   237  			attestation: nil,
   238  			errString:   "attestation can't be nil",
   239  		},
   240  		{
   241  			name:        "nil attestation data",
   242  			attestation: &ethpb.Attestation{},
   243  			errString:   "attestation's data can't be nil",
   244  		},
   245  		{
   246  			name: "nil attestation source",
   247  			attestation: &ethpb.Attestation{
   248  				Data: &ethpb.AttestationData{
   249  					Source: nil,
   250  					Target: &ethpb.Checkpoint{},
   251  				},
   252  			},
   253  			errString: "attestation's source can't be nil",
   254  		},
   255  		{
   256  			name: "nil attestation target",
   257  			attestation: &ethpb.Attestation{
   258  				Data: &ethpb.AttestationData{
   259  					Target: nil,
   260  					Source: &ethpb.Checkpoint{},
   261  				},
   262  			},
   263  			errString: "attestation's target can't be nil",
   264  		},
   265  		{
   266  			name: "nil attestation bitfield",
   267  			attestation: &ethpb.Attestation{
   268  				Data: &ethpb.AttestationData{
   269  					Target: &ethpb.Checkpoint{},
   270  					Source: &ethpb.Checkpoint{},
   271  				},
   272  			},
   273  			errString: "attestation's bitfield can't be nil",
   274  		},
   275  		{
   276  			name: "good attestation",
   277  			attestation: &ethpb.Attestation{
   278  				Data: &ethpb.AttestationData{
   279  					Target: &ethpb.Checkpoint{},
   280  					Source: &ethpb.Checkpoint{},
   281  				},
   282  				AggregationBits: []byte{},
   283  			},
   284  			errString: "",
   285  		},
   286  	}
   287  	for _, tt := range tests {
   288  		t.Run(tt.name, func(t *testing.T) {
   289  			if tt.errString != "" {
   290  				require.ErrorContains(t, tt.errString, helpers.ValidateNilAttestation(tt.attestation))
   291  			} else {
   292  				require.NoError(t, helpers.ValidateNilAttestation(tt.attestation))
   293  			}
   294  		})
   295  	}
   296  }
   297  
   298  func TestValidateSlotTargetEpoch(t *testing.T) {
   299  	tests := []struct {
   300  		name        string
   301  		attestation *ethpb.Attestation
   302  		errString   string
   303  	}{
   304  		{
   305  			name: "incorrect slot",
   306  			attestation: &ethpb.Attestation{
   307  				Data: &ethpb.AttestationData{
   308  					Target: &ethpb.Checkpoint{Epoch: 1},
   309  					Source: &ethpb.Checkpoint{},
   310  				},
   311  				AggregationBits: []byte{},
   312  			},
   313  			errString: "slot 0 does not match target epoch 1",
   314  		},
   315  		{
   316  			name: "good attestation",
   317  			attestation: &ethpb.Attestation{
   318  				Data: &ethpb.AttestationData{
   319  					Slot:   2 * params.BeaconConfig().SlotsPerEpoch,
   320  					Target: &ethpb.Checkpoint{Epoch: 2},
   321  					Source: &ethpb.Checkpoint{},
   322  				},
   323  				AggregationBits: []byte{},
   324  			},
   325  			errString: "",
   326  		},
   327  	}
   328  	for _, tt := range tests {
   329  		t.Run(tt.name, func(t *testing.T) {
   330  			if tt.errString != "" {
   331  				require.ErrorContains(t, tt.errString, helpers.ValidateSlotTargetEpoch(tt.attestation.Data))
   332  			} else {
   333  				require.NoError(t, helpers.ValidateSlotTargetEpoch(tt.attestation.Data))
   334  			}
   335  		})
   336  	}
   337  }