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

     1  package voluntaryexits
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  	"testing"
     7  
     8  	types "github.com/prysmaticlabs/eth2-types"
     9  	"github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
    10  	p2ppb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    11  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    12  	"github.com/prysmaticlabs/prysm/shared/params"
    13  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    14  	"google.golang.org/protobuf/proto"
    15  )
    16  
    17  func TestPool_InsertVoluntaryExit(t *testing.T) {
    18  	type fields struct {
    19  		pending []*ethpb.SignedVoluntaryExit
    20  	}
    21  	type args struct {
    22  		exit *ethpb.SignedVoluntaryExit
    23  	}
    24  	tests := []struct {
    25  		name   string
    26  		fields fields
    27  		args   args
    28  		want   []*ethpb.SignedVoluntaryExit
    29  	}{
    30  		{
    31  			name: "Prevent inserting nil exit",
    32  			fields: fields{
    33  				pending: make([]*ethpb.SignedVoluntaryExit, 0),
    34  			},
    35  			args: args{
    36  				exit: nil,
    37  			},
    38  			want: []*ethpb.SignedVoluntaryExit{},
    39  		},
    40  		{
    41  			name: "Prevent inserting malformed exit",
    42  			fields: fields{
    43  				pending: make([]*ethpb.SignedVoluntaryExit, 0),
    44  			},
    45  			args: args{
    46  				exit: &ethpb.SignedVoluntaryExit{
    47  					Exit: nil,
    48  				},
    49  			},
    50  			want: []*ethpb.SignedVoluntaryExit{},
    51  		},
    52  		{
    53  			name: "Empty list",
    54  			fields: fields{
    55  				pending: make([]*ethpb.SignedVoluntaryExit, 0),
    56  			},
    57  			args: args{
    58  				exit: &ethpb.SignedVoluntaryExit{
    59  					Exit: &ethpb.VoluntaryExit{
    60  						Epoch:          12,
    61  						ValidatorIndex: 1,
    62  					},
    63  				},
    64  			},
    65  			want: []*ethpb.SignedVoluntaryExit{
    66  				{
    67  					Exit: &ethpb.VoluntaryExit{
    68  						Epoch:          12,
    69  						ValidatorIndex: 1,
    70  					},
    71  				},
    72  			},
    73  		},
    74  		{
    75  			name: "Duplicate identical exit",
    76  			fields: fields{
    77  				pending: []*ethpb.SignedVoluntaryExit{
    78  					{
    79  						Exit: &ethpb.VoluntaryExit{
    80  							Epoch:          12,
    81  							ValidatorIndex: 1,
    82  						},
    83  					},
    84  				},
    85  			},
    86  			args: args{
    87  				exit: &ethpb.SignedVoluntaryExit{
    88  					Exit: &ethpb.VoluntaryExit{
    89  						Epoch:          12,
    90  						ValidatorIndex: 1,
    91  					},
    92  				},
    93  			},
    94  			want: []*ethpb.SignedVoluntaryExit{
    95  				{
    96  					Exit: &ethpb.VoluntaryExit{
    97  						Epoch:          12,
    98  						ValidatorIndex: 1,
    99  					},
   100  				},
   101  			},
   102  		},
   103  		{
   104  			name: "Duplicate exit in pending list",
   105  			fields: fields{
   106  				pending: []*ethpb.SignedVoluntaryExit{
   107  					{
   108  						Exit: &ethpb.VoluntaryExit{
   109  							Epoch:          12,
   110  							ValidatorIndex: 1,
   111  						},
   112  					},
   113  				},
   114  			},
   115  			args: args{
   116  				exit: &ethpb.SignedVoluntaryExit{
   117  					Exit: &ethpb.VoluntaryExit{
   118  						Epoch:          12,
   119  						ValidatorIndex: 1,
   120  					},
   121  				},
   122  			},
   123  			want: []*ethpb.SignedVoluntaryExit{
   124  				{
   125  					Exit: &ethpb.VoluntaryExit{
   126  						Epoch:          12,
   127  						ValidatorIndex: 1,
   128  					},
   129  				},
   130  			},
   131  		},
   132  		{
   133  			name: "Duplicate validator index",
   134  			fields: fields{
   135  				pending: []*ethpb.SignedVoluntaryExit{
   136  					{
   137  						Exit: &ethpb.VoluntaryExit{
   138  							Epoch:          12,
   139  							ValidatorIndex: 1,
   140  						},
   141  					},
   142  				},
   143  			},
   144  			args: args{
   145  				exit: &ethpb.SignedVoluntaryExit{
   146  					Exit: &ethpb.VoluntaryExit{
   147  						Epoch:          20,
   148  						ValidatorIndex: 1,
   149  					},
   150  				},
   151  			},
   152  			want: []*ethpb.SignedVoluntaryExit{
   153  				{
   154  					Exit: &ethpb.VoluntaryExit{
   155  						Epoch:          12,
   156  						ValidatorIndex: 1,
   157  					},
   158  				},
   159  			},
   160  		},
   161  		{
   162  			name: "Duplicate received with more favorable exit epoch",
   163  			fields: fields{
   164  				pending: []*ethpb.SignedVoluntaryExit{
   165  					{
   166  						Exit: &ethpb.VoluntaryExit{
   167  							Epoch:          12,
   168  							ValidatorIndex: 1,
   169  						},
   170  					},
   171  				},
   172  			},
   173  			args: args{
   174  				exit: &ethpb.SignedVoluntaryExit{
   175  					Exit: &ethpb.VoluntaryExit{
   176  						Epoch:          4,
   177  						ValidatorIndex: 1,
   178  					},
   179  				},
   180  			},
   181  			want: []*ethpb.SignedVoluntaryExit{
   182  				{
   183  					Exit: &ethpb.VoluntaryExit{
   184  						Epoch:          4,
   185  						ValidatorIndex: 1,
   186  					},
   187  				},
   188  			},
   189  		},
   190  		{
   191  			name: "Exit for already exited validator",
   192  			fields: fields{
   193  				pending: []*ethpb.SignedVoluntaryExit{},
   194  			},
   195  			args: args{
   196  				exit: &ethpb.SignedVoluntaryExit{
   197  					Exit: &ethpb.VoluntaryExit{
   198  						Epoch:          12,
   199  						ValidatorIndex: 2,
   200  					},
   201  				},
   202  			},
   203  			want: []*ethpb.SignedVoluntaryExit{},
   204  		},
   205  		{
   206  			name: "Maintains sorted order",
   207  			fields: fields{
   208  				pending: []*ethpb.SignedVoluntaryExit{
   209  					{
   210  						Exit: &ethpb.VoluntaryExit{
   211  							Epoch:          12,
   212  							ValidatorIndex: 0,
   213  						},
   214  					},
   215  					{
   216  						Exit: &ethpb.VoluntaryExit{
   217  							Epoch:          12,
   218  							ValidatorIndex: 2,
   219  						},
   220  					},
   221  				},
   222  			},
   223  			args: args{
   224  				exit: &ethpb.SignedVoluntaryExit{
   225  					Exit: &ethpb.VoluntaryExit{
   226  						Epoch:          10,
   227  						ValidatorIndex: 1,
   228  					},
   229  				},
   230  			},
   231  			want: []*ethpb.SignedVoluntaryExit{
   232  				{
   233  					Exit: &ethpb.VoluntaryExit{
   234  						Epoch:          12,
   235  						ValidatorIndex: 0,
   236  					},
   237  				},
   238  				{
   239  					Exit: &ethpb.VoluntaryExit{
   240  						Epoch:          10,
   241  						ValidatorIndex: 1,
   242  					},
   243  				},
   244  				{
   245  					Exit: &ethpb.VoluntaryExit{
   246  						Epoch:          12,
   247  						ValidatorIndex: 2,
   248  					},
   249  				},
   250  			},
   251  		},
   252  	}
   253  	ctx := context.Background()
   254  	validators := []*ethpb.Validator{
   255  		{ // 0
   256  			ExitEpoch: params.BeaconConfig().FarFutureEpoch,
   257  		},
   258  		{ // 1
   259  			ExitEpoch: params.BeaconConfig().FarFutureEpoch,
   260  		},
   261  		{ // 2 - Already exited.
   262  			ExitEpoch: 15,
   263  		},
   264  		{ // 3
   265  			ExitEpoch: params.BeaconConfig().FarFutureEpoch,
   266  		},
   267  	}
   268  	for _, tt := range tests {
   269  		t.Run(tt.name, func(t *testing.T) {
   270  			p := &Pool{
   271  				pending: tt.fields.pending,
   272  			}
   273  			s, err := v1.InitializeFromProtoUnsafe(&p2ppb.BeaconState{Validators: validators})
   274  			require.NoError(t, err)
   275  			p.InsertVoluntaryExit(ctx, s, tt.args.exit)
   276  			if len(p.pending) != len(tt.want) {
   277  				t.Fatalf("Mismatched lengths of pending list. Got %d, wanted %d.", len(p.pending), len(tt.want))
   278  			}
   279  			for i := range p.pending {
   280  				if !proto.Equal(p.pending[i], tt.want[i]) {
   281  					t.Errorf("Pending exit at index %d does not match expected. Got=%v wanted=%v", i, p.pending[i], tt.want[i])
   282  				}
   283  			}
   284  		})
   285  	}
   286  }
   287  
   288  func TestPool_MarkIncluded(t *testing.T) {
   289  	type fields struct {
   290  		pending []*ethpb.SignedVoluntaryExit
   291  	}
   292  	type args struct {
   293  		exit *ethpb.SignedVoluntaryExit
   294  	}
   295  	tests := []struct {
   296  		name   string
   297  		fields fields
   298  		args   args
   299  		want   fields
   300  	}{
   301  		{
   302  			name: "Removes from pending list",
   303  			fields: fields{
   304  				pending: []*ethpb.SignedVoluntaryExit{
   305  					{
   306  						Exit: &ethpb.VoluntaryExit{ValidatorIndex: 1},
   307  					},
   308  					{
   309  						Exit: &ethpb.VoluntaryExit{ValidatorIndex: 2},
   310  					},
   311  					{
   312  						Exit: &ethpb.VoluntaryExit{ValidatorIndex: 3},
   313  					},
   314  				},
   315  			},
   316  			args: args{
   317  				exit: &ethpb.SignedVoluntaryExit{
   318  					Exit: &ethpb.VoluntaryExit{ValidatorIndex: 2},
   319  				},
   320  			},
   321  			want: fields{
   322  				pending: []*ethpb.SignedVoluntaryExit{
   323  					{
   324  						Exit: &ethpb.VoluntaryExit{ValidatorIndex: 1},
   325  					},
   326  					{
   327  						Exit: &ethpb.VoluntaryExit{ValidatorIndex: 3},
   328  					},
   329  				},
   330  			},
   331  		},
   332  	}
   333  	for _, tt := range tests {
   334  		t.Run(tt.name, func(t *testing.T) {
   335  			p := &Pool{
   336  				pending: tt.fields.pending,
   337  			}
   338  			p.MarkIncluded(tt.args.exit)
   339  			if len(p.pending) != len(tt.want.pending) {
   340  				t.Fatalf("Mismatched lengths of pending list. Got %d, wanted %d.", len(p.pending), len(tt.want.pending))
   341  			}
   342  			for i := range p.pending {
   343  				if !proto.Equal(p.pending[i], tt.want.pending[i]) {
   344  					t.Errorf("Pending exit at index %d does not match expected. Got=%v wanted=%v", i, p.pending[i], tt.want.pending[i])
   345  				}
   346  			}
   347  		})
   348  	}
   349  }
   350  
   351  func TestPool_PendingExits(t *testing.T) {
   352  	type fields struct {
   353  		pending []*ethpb.SignedVoluntaryExit
   354  		noLimit bool
   355  	}
   356  	type args struct {
   357  		slot types.Slot
   358  	}
   359  	tests := []struct {
   360  		name   string
   361  		fields fields
   362  		args   args
   363  		want   []*ethpb.SignedVoluntaryExit
   364  	}{
   365  		{
   366  			name: "Empty list",
   367  			fields: fields{
   368  				pending: []*ethpb.SignedVoluntaryExit{},
   369  			},
   370  			args: args{
   371  				slot: 100000,
   372  			},
   373  			want: []*ethpb.SignedVoluntaryExit{},
   374  		},
   375  		{
   376  			name: "All eligible",
   377  			fields: fields{
   378  				pending: []*ethpb.SignedVoluntaryExit{
   379  					{Exit: &ethpb.VoluntaryExit{Epoch: 0}},
   380  					{Exit: &ethpb.VoluntaryExit{Epoch: 1}},
   381  					{Exit: &ethpb.VoluntaryExit{Epoch: 2}},
   382  					{Exit: &ethpb.VoluntaryExit{Epoch: 3}},
   383  					{Exit: &ethpb.VoluntaryExit{Epoch: 4}},
   384  				},
   385  			},
   386  			args: args{
   387  				slot: 1000000,
   388  			},
   389  			want: []*ethpb.SignedVoluntaryExit{
   390  				{Exit: &ethpb.VoluntaryExit{Epoch: 0}},
   391  				{Exit: &ethpb.VoluntaryExit{Epoch: 1}},
   392  				{Exit: &ethpb.VoluntaryExit{Epoch: 2}},
   393  				{Exit: &ethpb.VoluntaryExit{Epoch: 3}},
   394  				{Exit: &ethpb.VoluntaryExit{Epoch: 4}},
   395  			},
   396  		},
   397  		{
   398  			name: "All eligible, above max",
   399  			fields: fields{
   400  				noLimit: true,
   401  				pending: []*ethpb.SignedVoluntaryExit{
   402  					{Exit: &ethpb.VoluntaryExit{Epoch: 0}},
   403  					{Exit: &ethpb.VoluntaryExit{Epoch: 1}},
   404  					{Exit: &ethpb.VoluntaryExit{Epoch: 2}},
   405  					{Exit: &ethpb.VoluntaryExit{Epoch: 3}},
   406  					{Exit: &ethpb.VoluntaryExit{Epoch: 4}},
   407  					{Exit: &ethpb.VoluntaryExit{Epoch: 5}},
   408  					{Exit: &ethpb.VoluntaryExit{Epoch: 6}},
   409  					{Exit: &ethpb.VoluntaryExit{Epoch: 7}},
   410  					{Exit: &ethpb.VoluntaryExit{Epoch: 8}},
   411  					{Exit: &ethpb.VoluntaryExit{Epoch: 9}},
   412  					{Exit: &ethpb.VoluntaryExit{Epoch: 10}},
   413  					{Exit: &ethpb.VoluntaryExit{Epoch: 11}},
   414  					{Exit: &ethpb.VoluntaryExit{Epoch: 12}},
   415  					{Exit: &ethpb.VoluntaryExit{Epoch: 13}},
   416  					{Exit: &ethpb.VoluntaryExit{Epoch: 14}},
   417  					{Exit: &ethpb.VoluntaryExit{Epoch: 15}},
   418  					{Exit: &ethpb.VoluntaryExit{Epoch: 16}},
   419  					{Exit: &ethpb.VoluntaryExit{Epoch: 17}},
   420  					{Exit: &ethpb.VoluntaryExit{Epoch: 18}},
   421  					{Exit: &ethpb.VoluntaryExit{Epoch: 19}},
   422  				},
   423  			},
   424  			args: args{
   425  				slot: 1000000,
   426  			},
   427  			want: []*ethpb.SignedVoluntaryExit{
   428  				{Exit: &ethpb.VoluntaryExit{Epoch: 0}},
   429  				{Exit: &ethpb.VoluntaryExit{Epoch: 1}},
   430  				{Exit: &ethpb.VoluntaryExit{Epoch: 2}},
   431  				{Exit: &ethpb.VoluntaryExit{Epoch: 3}},
   432  				{Exit: &ethpb.VoluntaryExit{Epoch: 4}},
   433  				{Exit: &ethpb.VoluntaryExit{Epoch: 5}},
   434  				{Exit: &ethpb.VoluntaryExit{Epoch: 6}},
   435  				{Exit: &ethpb.VoluntaryExit{Epoch: 7}},
   436  				{Exit: &ethpb.VoluntaryExit{Epoch: 8}},
   437  				{Exit: &ethpb.VoluntaryExit{Epoch: 9}},
   438  				{Exit: &ethpb.VoluntaryExit{Epoch: 10}},
   439  				{Exit: &ethpb.VoluntaryExit{Epoch: 11}},
   440  				{Exit: &ethpb.VoluntaryExit{Epoch: 12}},
   441  				{Exit: &ethpb.VoluntaryExit{Epoch: 13}},
   442  				{Exit: &ethpb.VoluntaryExit{Epoch: 14}},
   443  				{Exit: &ethpb.VoluntaryExit{Epoch: 15}},
   444  				{Exit: &ethpb.VoluntaryExit{Epoch: 16}},
   445  				{Exit: &ethpb.VoluntaryExit{Epoch: 17}},
   446  				{Exit: &ethpb.VoluntaryExit{Epoch: 18}},
   447  				{Exit: &ethpb.VoluntaryExit{Epoch: 19}},
   448  			},
   449  		},
   450  		{
   451  			name: "All eligible, block max",
   452  			fields: fields{
   453  				pending: []*ethpb.SignedVoluntaryExit{
   454  					{Exit: &ethpb.VoluntaryExit{Epoch: 0}},
   455  					{Exit: &ethpb.VoluntaryExit{Epoch: 1}},
   456  					{Exit: &ethpb.VoluntaryExit{Epoch: 2}},
   457  					{Exit: &ethpb.VoluntaryExit{Epoch: 3}},
   458  					{Exit: &ethpb.VoluntaryExit{Epoch: 4}},
   459  					{Exit: &ethpb.VoluntaryExit{Epoch: 5}},
   460  					{Exit: &ethpb.VoluntaryExit{Epoch: 6}},
   461  					{Exit: &ethpb.VoluntaryExit{Epoch: 7}},
   462  					{Exit: &ethpb.VoluntaryExit{Epoch: 8}},
   463  					{Exit: &ethpb.VoluntaryExit{Epoch: 9}},
   464  					{Exit: &ethpb.VoluntaryExit{Epoch: 10}},
   465  					{Exit: &ethpb.VoluntaryExit{Epoch: 11}},
   466  					{Exit: &ethpb.VoluntaryExit{Epoch: 12}},
   467  					{Exit: &ethpb.VoluntaryExit{Epoch: 13}},
   468  					{Exit: &ethpb.VoluntaryExit{Epoch: 14}},
   469  					{Exit: &ethpb.VoluntaryExit{Epoch: 15}},
   470  					{Exit: &ethpb.VoluntaryExit{Epoch: 16}},
   471  					{Exit: &ethpb.VoluntaryExit{Epoch: 17}},
   472  					{Exit: &ethpb.VoluntaryExit{Epoch: 18}},
   473  					{Exit: &ethpb.VoluntaryExit{Epoch: 19}},
   474  				},
   475  			},
   476  			args: args{
   477  				slot: 1000000,
   478  			},
   479  			want: []*ethpb.SignedVoluntaryExit{
   480  				{Exit: &ethpb.VoluntaryExit{Epoch: 0}},
   481  				{Exit: &ethpb.VoluntaryExit{Epoch: 1}},
   482  				{Exit: &ethpb.VoluntaryExit{Epoch: 2}},
   483  				{Exit: &ethpb.VoluntaryExit{Epoch: 3}},
   484  				{Exit: &ethpb.VoluntaryExit{Epoch: 4}},
   485  				{Exit: &ethpb.VoluntaryExit{Epoch: 5}},
   486  				{Exit: &ethpb.VoluntaryExit{Epoch: 6}},
   487  				{Exit: &ethpb.VoluntaryExit{Epoch: 7}},
   488  				{Exit: &ethpb.VoluntaryExit{Epoch: 8}},
   489  				{Exit: &ethpb.VoluntaryExit{Epoch: 9}},
   490  				{Exit: &ethpb.VoluntaryExit{Epoch: 10}},
   491  				{Exit: &ethpb.VoluntaryExit{Epoch: 11}},
   492  				{Exit: &ethpb.VoluntaryExit{Epoch: 12}},
   493  				{Exit: &ethpb.VoluntaryExit{Epoch: 13}},
   494  				{Exit: &ethpb.VoluntaryExit{Epoch: 14}},
   495  				{Exit: &ethpb.VoluntaryExit{Epoch: 15}},
   496  			},
   497  		},
   498  		{
   499  			name: "Some eligible",
   500  			fields: fields{
   501  				pending: []*ethpb.SignedVoluntaryExit{
   502  					{Exit: &ethpb.VoluntaryExit{Epoch: 0}},
   503  					{Exit: &ethpb.VoluntaryExit{Epoch: 3}},
   504  					{Exit: &ethpb.VoluntaryExit{Epoch: 4}},
   505  					{Exit: &ethpb.VoluntaryExit{Epoch: 2}},
   506  					{Exit: &ethpb.VoluntaryExit{Epoch: 1}},
   507  				},
   508  			},
   509  			args: args{
   510  				slot: 2 * params.BeaconConfig().SlotsPerEpoch,
   511  			},
   512  			want: []*ethpb.SignedVoluntaryExit{
   513  				{Exit: &ethpb.VoluntaryExit{Epoch: 0}},
   514  				{Exit: &ethpb.VoluntaryExit{Epoch: 2}},
   515  				{Exit: &ethpb.VoluntaryExit{Epoch: 1}},
   516  			},
   517  		},
   518  	}
   519  	for _, tt := range tests {
   520  		t.Run(tt.name, func(t *testing.T) {
   521  			p := &Pool{
   522  				pending: tt.fields.pending,
   523  			}
   524  			s, err := v1.InitializeFromProtoUnsafe(&p2ppb.BeaconState{Validators: []*ethpb.Validator{{ExitEpoch: params.BeaconConfig().FarFutureEpoch}}})
   525  			require.NoError(t, err)
   526  			if got := p.PendingExits(s, tt.args.slot, tt.fields.noLimit); !reflect.DeepEqual(got, tt.want) {
   527  				t.Errorf("PendingExits() = %v, want %v", got, tt.want)
   528  			}
   529  		})
   530  	}
   531  }