github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/operations/slashings/service_proposer_test.go (about)

     1  package slashings
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	types "github.com/prysmaticlabs/eth2-types"
     8  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
     9  	"github.com/prysmaticlabs/prysm/shared/params"
    10  	"github.com/prysmaticlabs/prysm/shared/testutil"
    11  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    12  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    13  )
    14  
    15  func proposerSlashingForValIdx(valIdx types.ValidatorIndex) *ethpb.ProposerSlashing {
    16  	return &ethpb.ProposerSlashing{
    17  		Header_1: &ethpb.SignedBeaconBlockHeader{
    18  			Header: &ethpb.BeaconBlockHeader{ProposerIndex: valIdx},
    19  		},
    20  		Header_2: &ethpb.SignedBeaconBlockHeader{
    21  			Header: &ethpb.BeaconBlockHeader{ProposerIndex: valIdx},
    22  		},
    23  	}
    24  }
    25  
    26  func TestPool_InsertProposerSlashing(t *testing.T) {
    27  	type fields struct {
    28  		wantedErr string
    29  		pending   []*ethpb.ProposerSlashing
    30  		included  map[types.ValidatorIndex]bool
    31  	}
    32  	type args struct {
    33  		slashings []*ethpb.ProposerSlashing
    34  	}
    35  
    36  	beaconState, privKeys := testutil.DeterministicGenesisState(t, 64)
    37  	slashings := make([]*ethpb.ProposerSlashing, 20)
    38  	for i := 0; i < len(slashings); i++ {
    39  		sl, err := testutil.GenerateProposerSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i))
    40  		require.NoError(t, err)
    41  		slashings[i] = sl
    42  	}
    43  
    44  	require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch))
    45  
    46  	// We mark the following validators with some preconditions.
    47  	exitedVal, err := beaconState.ValidatorAtIndex(types.ValidatorIndex(2))
    48  	require.NoError(t, err)
    49  	exitedVal.WithdrawableEpoch = 0
    50  	futureExitedVal, err := beaconState.ValidatorAtIndex(types.ValidatorIndex(4))
    51  	require.NoError(t, err)
    52  	futureExitedVal.WithdrawableEpoch = 17
    53  	slashedVal, err := beaconState.ValidatorAtIndex(types.ValidatorIndex(5))
    54  	require.NoError(t, err)
    55  	slashedVal.Slashed = true
    56  	require.NoError(t, beaconState.UpdateValidatorAtIndex(types.ValidatorIndex(2), exitedVal))
    57  	require.NoError(t, beaconState.UpdateValidatorAtIndex(types.ValidatorIndex(4), futureExitedVal))
    58  	require.NoError(t, beaconState.UpdateValidatorAtIndex(types.ValidatorIndex(5), slashedVal))
    59  
    60  	tests := []struct {
    61  		name   string
    62  		fields fields
    63  		args   args
    64  		want   []*ethpb.ProposerSlashing
    65  	}{
    66  		{
    67  			name: "Empty list",
    68  			fields: fields{
    69  				pending:  make([]*ethpb.ProposerSlashing, 0),
    70  				included: make(map[types.ValidatorIndex]bool),
    71  			},
    72  			args: args{
    73  				slashings: slashings[0:1],
    74  			},
    75  			want: slashings[0:1],
    76  		},
    77  		{
    78  			name: "Duplicate identical slashing",
    79  			fields: fields{
    80  				pending:   slashings[0:1],
    81  				included:  make(map[types.ValidatorIndex]bool),
    82  				wantedErr: "slashing object already exists in pending proposer slashings",
    83  			},
    84  			args: args{
    85  				slashings: slashings[0:1],
    86  			},
    87  			want: slashings[0:1],
    88  		},
    89  		{
    90  			name: "Slashing for exited validator",
    91  			fields: fields{
    92  				pending:   []*ethpb.ProposerSlashing{},
    93  				included:  make(map[types.ValidatorIndex]bool),
    94  				wantedErr: "is not slashable",
    95  			},
    96  			args: args{
    97  				slashings: slashings[2:3],
    98  			},
    99  			want: []*ethpb.ProposerSlashing{},
   100  		},
   101  		{
   102  			name: "Slashing for exiting validator",
   103  			fields: fields{
   104  				pending:  []*ethpb.ProposerSlashing{},
   105  				included: make(map[types.ValidatorIndex]bool),
   106  			},
   107  			args: args{
   108  				slashings: slashings[4:5],
   109  			},
   110  			want: slashings[4:5],
   111  		},
   112  		{
   113  			name: "Slashing for slashed validator",
   114  			fields: fields{
   115  				pending:   []*ethpb.ProposerSlashing{},
   116  				included:  make(map[types.ValidatorIndex]bool),
   117  				wantedErr: "not slashable",
   118  			},
   119  			args: args{
   120  				slashings: slashings[5:6],
   121  			},
   122  			want: []*ethpb.ProposerSlashing{},
   123  		},
   124  		{
   125  			name: "Already included",
   126  			fields: fields{
   127  				pending: []*ethpb.ProposerSlashing{},
   128  				included: map[types.ValidatorIndex]bool{
   129  					1: true,
   130  				},
   131  				wantedErr: "cannot be slashed",
   132  			},
   133  			args: args{
   134  				slashings: slashings[1:2],
   135  			},
   136  			want: []*ethpb.ProposerSlashing{},
   137  		},
   138  		{
   139  			name: "Maintains sorted order",
   140  			fields: fields{
   141  				pending: []*ethpb.ProposerSlashing{
   142  					slashings[0],
   143  					slashings[2],
   144  				},
   145  				included: make(map[types.ValidatorIndex]bool),
   146  			},
   147  			args: args{
   148  				slashings: slashings[1:2],
   149  			},
   150  			want: []*ethpb.ProposerSlashing{
   151  				slashings[0],
   152  				slashings[1],
   153  				slashings[2],
   154  			},
   155  		},
   156  	}
   157  	for _, tt := range tests {
   158  		t.Run(tt.name, func(t *testing.T) {
   159  			p := &Pool{
   160  				pendingProposerSlashing: tt.fields.pending,
   161  				included:                tt.fields.included,
   162  			}
   163  			var err error
   164  			for i := 0; i < len(tt.args.slashings); i++ {
   165  				err = p.InsertProposerSlashing(context.Background(), beaconState, tt.args.slashings[i])
   166  			}
   167  			if tt.fields.wantedErr != "" {
   168  				require.ErrorContains(t, tt.fields.wantedErr, err)
   169  			} else {
   170  				require.NoError(t, err)
   171  			}
   172  			assert.Equal(t, len(tt.want), len(p.pendingProposerSlashing))
   173  			for i := range p.pendingAttesterSlashing {
   174  				assert.Equal(t, p.pendingProposerSlashing[i].Header_1.Header.ProposerIndex, tt.want[i].Header_1.Header.ProposerIndex)
   175  				assert.DeepEqual(t, tt.want[i], p.pendingProposerSlashing[i], "Proposer slashing at index %d does not match expected", i)
   176  			}
   177  		})
   178  	}
   179  }
   180  
   181  func TestPool_InsertProposerSlashing_SigFailsVerify_ClearPool(t *testing.T) {
   182  	params.SetupTestConfigCleanup(t)
   183  	conf := params.BeaconConfig()
   184  	conf.MaxAttesterSlashings = 2
   185  	params.OverrideBeaconConfig(conf)
   186  	beaconState, privKeys := testutil.DeterministicGenesisState(t, 64)
   187  	slashings := make([]*ethpb.ProposerSlashing, 2)
   188  	for i := 0; i < 2; i++ {
   189  		sl, err := testutil.GenerateProposerSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i))
   190  		require.NoError(t, err)
   191  		slashings[i] = sl
   192  	}
   193  	// We mess up the signature of the second slashing.
   194  	badSig := make([]byte, 96)
   195  	copy(badSig, "muahaha")
   196  	slashings[1].Header_1.Signature = badSig
   197  	p := &Pool{
   198  		pendingProposerSlashing: make([]*ethpb.ProposerSlashing, 0),
   199  	}
   200  	// We only want a single slashing to remain.
   201  	require.NoError(t, p.InsertProposerSlashing(context.Background(), beaconState, slashings[0]))
   202  	err := p.InsertProposerSlashing(context.Background(), beaconState, slashings[1])
   203  	require.ErrorContains(t, "could not verify proposer slashing", err, "Expected slashing with bad signature to fail")
   204  	assert.Equal(t, 1, len(p.pendingProposerSlashing))
   205  }
   206  
   207  func TestPool_MarkIncludedProposerSlashing(t *testing.T) {
   208  	type fields struct {
   209  		pending  []*ethpb.ProposerSlashing
   210  		included map[types.ValidatorIndex]bool
   211  	}
   212  	type args struct {
   213  		slashing *ethpb.ProposerSlashing
   214  	}
   215  	tests := []struct {
   216  		name   string
   217  		fields fields
   218  		args   args
   219  		want   fields
   220  	}{
   221  		{
   222  			name: "Included, does not exist in pending",
   223  			fields: fields{
   224  				pending: []*ethpb.ProposerSlashing{
   225  					proposerSlashingForValIdx(1),
   226  				},
   227  				included: make(map[types.ValidatorIndex]bool),
   228  			},
   229  			args: args{
   230  				slashing: proposerSlashingForValIdx(3),
   231  			},
   232  			want: fields{
   233  				pending: []*ethpb.ProposerSlashing{
   234  					proposerSlashingForValIdx(1),
   235  				},
   236  				included: map[types.ValidatorIndex]bool{
   237  					3: true,
   238  				},
   239  			},
   240  		},
   241  		{
   242  			name: "Removes from pending list",
   243  			fields: fields{
   244  				pending: []*ethpb.ProposerSlashing{
   245  					proposerSlashingForValIdx(1),
   246  					proposerSlashingForValIdx(2),
   247  					proposerSlashingForValIdx(3),
   248  				},
   249  				included: map[types.ValidatorIndex]bool{
   250  					0: true,
   251  				},
   252  			},
   253  			args: args{
   254  				slashing: proposerSlashingForValIdx(2),
   255  			},
   256  			want: fields{
   257  				pending: []*ethpb.ProposerSlashing{
   258  					proposerSlashingForValIdx(1),
   259  					proposerSlashingForValIdx(3),
   260  				},
   261  				included: map[types.ValidatorIndex]bool{
   262  					0: true,
   263  					2: true,
   264  				},
   265  			},
   266  		},
   267  		{
   268  			name: "Removes from pending long list",
   269  			fields: fields{
   270  				pending: []*ethpb.ProposerSlashing{
   271  					proposerSlashingForValIdx(1),
   272  					proposerSlashingForValIdx(2),
   273  					proposerSlashingForValIdx(3),
   274  					proposerSlashingForValIdx(4),
   275  					proposerSlashingForValIdx(5),
   276  					proposerSlashingForValIdx(6),
   277  					proposerSlashingForValIdx(7),
   278  					proposerSlashingForValIdx(8),
   279  					proposerSlashingForValIdx(9),
   280  					proposerSlashingForValIdx(10),
   281  				},
   282  				included: map[types.ValidatorIndex]bool{
   283  					0: true,
   284  				},
   285  			},
   286  			args: args{
   287  				slashing: proposerSlashingForValIdx(7),
   288  			},
   289  			want: fields{
   290  				pending: []*ethpb.ProposerSlashing{
   291  					proposerSlashingForValIdx(1),
   292  					proposerSlashingForValIdx(2),
   293  					proposerSlashingForValIdx(3),
   294  					proposerSlashingForValIdx(4),
   295  					proposerSlashingForValIdx(5),
   296  					proposerSlashingForValIdx(6),
   297  					proposerSlashingForValIdx(8),
   298  					proposerSlashingForValIdx(9),
   299  					proposerSlashingForValIdx(10),
   300  				},
   301  				included: map[types.ValidatorIndex]bool{
   302  					0: true,
   303  					7: true,
   304  				},
   305  			},
   306  		},
   307  	}
   308  	for _, tt := range tests {
   309  		t.Run(tt.name, func(t *testing.T) {
   310  			p := &Pool{
   311  				pendingProposerSlashing: tt.fields.pending,
   312  				included:                tt.fields.included,
   313  			}
   314  			p.MarkIncludedProposerSlashing(tt.args.slashing)
   315  			assert.Equal(t, len(tt.want.pending), len(p.pendingProposerSlashing))
   316  			for i := range p.pendingProposerSlashing {
   317  				assert.DeepSSZEqual(t, tt.want.pending[i], p.pendingProposerSlashing[i], "Unexpected pending proposer slashing at index %d", i)
   318  			}
   319  			assert.DeepEqual(t, tt.want.included, p.included)
   320  		})
   321  	}
   322  }
   323  
   324  func TestPool_PendingProposerSlashings(t *testing.T) {
   325  	type fields struct {
   326  		pending []*ethpb.ProposerSlashing
   327  		noLimit bool
   328  	}
   329  	beaconState, privKeys := testutil.DeterministicGenesisState(t, 64)
   330  	slashings := make([]*ethpb.ProposerSlashing, 20)
   331  	for i := 0; i < len(slashings); i++ {
   332  		sl, err := testutil.GenerateProposerSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i))
   333  		require.NoError(t, err)
   334  		slashings[i] = sl
   335  	}
   336  	tests := []struct {
   337  		name   string
   338  		fields fields
   339  		want   []*ethpb.ProposerSlashing
   340  	}{
   341  		{
   342  			name: "Empty list",
   343  			fields: fields{
   344  				pending: []*ethpb.ProposerSlashing{},
   345  			},
   346  			want: []*ethpb.ProposerSlashing{},
   347  		},
   348  		{
   349  			name: "All",
   350  			fields: fields{
   351  				pending: slashings,
   352  				noLimit: true,
   353  			},
   354  			want: slashings,
   355  		},
   356  		{
   357  			name: "All block eligible",
   358  			fields: fields{
   359  				pending: slashings[:params.BeaconConfig().MaxProposerSlashings],
   360  			},
   361  			want: slashings[:params.BeaconConfig().MaxProposerSlashings],
   362  		},
   363  		{
   364  			name: "Multiple indices",
   365  			fields: fields{
   366  				pending: slashings[3:6],
   367  			},
   368  			want: slashings[3:6],
   369  		},
   370  	}
   371  	for _, tt := range tests {
   372  		t.Run(tt.name, func(t *testing.T) {
   373  			p := &Pool{
   374  				pendingProposerSlashing: tt.fields.pending,
   375  			}
   376  			assert.DeepEqual(t, tt.want, p.PendingProposerSlashings(context.Background(), beaconState, tt.fields.noLimit))
   377  		})
   378  	}
   379  }
   380  
   381  func TestPool_PendingProposerSlashings_Slashed(t *testing.T) {
   382  	type fields struct {
   383  		all     bool
   384  		pending []*ethpb.ProposerSlashing
   385  	}
   386  	beaconState, privKeys := testutil.DeterministicGenesisState(t, 64)
   387  	val, err := beaconState.ValidatorAtIndex(0)
   388  	require.NoError(t, err)
   389  	val.Slashed = true
   390  	require.NoError(t, beaconState.UpdateValidatorAtIndex(0, val))
   391  	val, err = beaconState.ValidatorAtIndex(5)
   392  	require.NoError(t, err)
   393  	val.Slashed = true
   394  	require.NoError(t, beaconState.UpdateValidatorAtIndex(5, val))
   395  	slashings := make([]*ethpb.ProposerSlashing, 32)
   396  	slashings2 := make([]*ethpb.ProposerSlashing, 32)
   397  	result := make([]*ethpb.ProposerSlashing, 32)
   398  	for i := 0; i < len(slashings); i++ {
   399  		sl, err := testutil.GenerateProposerSlashingForValidator(beaconState, privKeys[i], types.ValidatorIndex(i))
   400  		require.NoError(t, err)
   401  		slashings[i] = sl
   402  		slashings2[i] = sl
   403  		result[i] = sl
   404  	}
   405  	result = append(result[1:5], result[6:]...)
   406  	tests := []struct {
   407  		name   string
   408  		fields fields
   409  		want   []*ethpb.ProposerSlashing
   410  	}{
   411  		{
   412  			name: "removes slashed",
   413  			fields: fields{
   414  				pending: slashings,
   415  			},
   416  			want: result[:16],
   417  		},
   418  		{
   419  			name: "gets noLimit and no slashed",
   420  			fields: fields{
   421  				all:     true,
   422  				pending: slashings2,
   423  			},
   424  			want: result,
   425  		},
   426  	}
   427  	for _, tt := range tests {
   428  		t.Run(tt.name, func(t *testing.T) {
   429  			p := &Pool{
   430  				pendingProposerSlashing: tt.fields.pending,
   431  			}
   432  			result := p.PendingProposerSlashings(context.Background(), beaconState, tt.fields.all /*noLimit*/)
   433  			t.Log(tt.want[0].Header_1.Header.ProposerIndex)
   434  			t.Log(result[0].Header_1.Header.ProposerIndex)
   435  			assert.DeepEqual(t, tt.want, result)
   436  		})
   437  	}
   438  }