github.com/prysmaticlabs/prysm@v1.4.4/shared/aggregation/attestations/maxcover_test.go (about)

     1  package attestations
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/prysmaticlabs/go-bitfield"
     7  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
     8  	"github.com/prysmaticlabs/prysm/shared/aggregation"
     9  	"github.com/prysmaticlabs/prysm/shared/bls"
    10  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    11  )
    12  
    13  func TestAggregateAttestations_MaxCover_NewMaxCover(t *testing.T) {
    14  	type args struct {
    15  		atts []*ethpb.Attestation
    16  	}
    17  	tests := []struct {
    18  		name string
    19  		args args
    20  		want *aggregation.MaxCoverProblem
    21  	}{
    22  		{
    23  			name: "nil attestations",
    24  			args: args{
    25  				atts: nil,
    26  			},
    27  			want: &aggregation.MaxCoverProblem{Candidates: []*aggregation.MaxCoverCandidate{}},
    28  		},
    29  		{
    30  			name: "no attestations",
    31  			args: args{
    32  				atts: []*ethpb.Attestation{},
    33  			},
    34  			want: &aggregation.MaxCoverProblem{Candidates: []*aggregation.MaxCoverCandidate{}},
    35  		},
    36  		{
    37  			name: "single attestation",
    38  			args: args{
    39  				atts: []*ethpb.Attestation{
    40  					{AggregationBits: bitfield.Bitlist{0b00001010, 0b1}},
    41  				},
    42  			},
    43  			want: &aggregation.MaxCoverProblem{
    44  				Candidates: aggregation.MaxCoverCandidates{
    45  					aggregation.NewMaxCoverCandidate(0, &bitfield.Bitlist{0b00001010, 0b1}),
    46  				},
    47  			},
    48  		},
    49  		{
    50  			name: "multiple attestations",
    51  			args: args{
    52  				atts: []*ethpb.Attestation{
    53  					{AggregationBits: bitfield.Bitlist{0b00001010, 0b1}},
    54  					{AggregationBits: bitfield.Bitlist{0b00101010, 0b1}},
    55  					{AggregationBits: bitfield.Bitlist{0b11111010, 0b1}},
    56  					{AggregationBits: bitfield.Bitlist{0b00000010, 0b1}},
    57  					{AggregationBits: bitfield.Bitlist{0b00000001, 0b1}},
    58  				},
    59  			},
    60  			want: &aggregation.MaxCoverProblem{
    61  				Candidates: aggregation.MaxCoverCandidates{
    62  					aggregation.NewMaxCoverCandidate(0, &bitfield.Bitlist{0b00001010, 0b1}),
    63  					aggregation.NewMaxCoverCandidate(1, &bitfield.Bitlist{0b00101010, 0b1}),
    64  					aggregation.NewMaxCoverCandidate(2, &bitfield.Bitlist{0b11111010, 0b1}),
    65  					aggregation.NewMaxCoverCandidate(3, &bitfield.Bitlist{0b00000010, 0b1}),
    66  					aggregation.NewMaxCoverCandidate(4, &bitfield.Bitlist{0b00000001, 0b1}),
    67  				},
    68  			},
    69  		},
    70  	}
    71  	for _, tt := range tests {
    72  		t.Run(tt.name, func(t *testing.T) {
    73  			assert.DeepEqual(t, tt.want, NewMaxCover(tt.args.atts))
    74  		})
    75  	}
    76  }
    77  
    78  func TestAggregateAttestations_MaxCover_AttList_validate(t *testing.T) {
    79  	tests := []struct {
    80  		name      string
    81  		atts      attList
    82  		wantedErr string
    83  	}{
    84  		{
    85  			name:      "nil list",
    86  			atts:      nil,
    87  			wantedErr: "nil list",
    88  		},
    89  		{
    90  			name:      "empty list",
    91  			atts:      attList{},
    92  			wantedErr: "empty list",
    93  		},
    94  		{
    95  			name:      "first bitlist is nil",
    96  			atts:      attList{&ethpb.Attestation{}},
    97  			wantedErr: "bitlist cannot be nil or empty",
    98  		},
    99  		{
   100  			name: "non first bitlist is nil",
   101  			atts: attList{
   102  				&ethpb.Attestation{AggregationBits: bitfield.NewBitlist(64)},
   103  				&ethpb.Attestation{},
   104  			},
   105  			wantedErr: "bitlist cannot be nil or empty",
   106  		},
   107  		{
   108  			name: "first bitlist is empty",
   109  			atts: attList{
   110  				&ethpb.Attestation{AggregationBits: bitfield.Bitlist{}},
   111  			},
   112  			wantedErr: "bitlist cannot be nil or empty",
   113  		},
   114  		{
   115  			name: "non first bitlist is empty",
   116  			atts: attList{
   117  				&ethpb.Attestation{AggregationBits: bitfield.NewBitlist(64)},
   118  				&ethpb.Attestation{AggregationBits: bitfield.Bitlist{}},
   119  			},
   120  			wantedErr: "bitlist cannot be nil or empty",
   121  		},
   122  		{
   123  			name: "valid bitlists",
   124  			atts: attList{
   125  				&ethpb.Attestation{AggregationBits: bitfield.NewBitlist(64)},
   126  				&ethpb.Attestation{AggregationBits: bitfield.NewBitlist(64)},
   127  				&ethpb.Attestation{AggregationBits: bitfield.NewBitlist(64)},
   128  				&ethpb.Attestation{AggregationBits: bitfield.NewBitlist(64)},
   129  			},
   130  		},
   131  	}
   132  	for _, tt := range tests {
   133  		t.Run(tt.name, func(t *testing.T) {
   134  			err := tt.atts.validate()
   135  			if tt.wantedErr != "" {
   136  				assert.ErrorContains(t, tt.wantedErr, err)
   137  			} else {
   138  				assert.NoError(t, err)
   139  			}
   140  		})
   141  	}
   142  }
   143  
   144  func TestAggregateAttestations_rearrangeProcessedAttestations(t *testing.T) {
   145  	tests := []struct {
   146  		name     string
   147  		atts     []*ethpb.Attestation
   148  		keys     []int
   149  		wantAtts []*ethpb.Attestation
   150  	}{
   151  		{
   152  			name: "nil attestations",
   153  		},
   154  		{
   155  			name: "single attestation no processed keys",
   156  			atts: []*ethpb.Attestation{
   157  				{},
   158  			},
   159  			wantAtts: []*ethpb.Attestation{
   160  				{},
   161  			},
   162  		},
   163  		{
   164  			name: "single attestation processed",
   165  			atts: []*ethpb.Attestation{
   166  				{},
   167  			},
   168  			keys: []int{0},
   169  			wantAtts: []*ethpb.Attestation{
   170  				nil,
   171  			},
   172  		},
   173  		{
   174  			name: "multiple processed, last attestation marked",
   175  			atts: []*ethpb.Attestation{
   176  				{AggregationBits: bitfield.Bitlist{0x00}},
   177  				{AggregationBits: bitfield.Bitlist{0x01}},
   178  				{AggregationBits: bitfield.Bitlist{0x02}},
   179  				{AggregationBits: bitfield.Bitlist{0x03}},
   180  				{AggregationBits: bitfield.Bitlist{0x04}},
   181  			},
   182  			keys: []int{1, 4}, // Only attestation at index 1, should be moved, att at 4 is already at the end.
   183  			wantAtts: []*ethpb.Attestation{
   184  				{AggregationBits: bitfield.Bitlist{0x00}},
   185  				{AggregationBits: bitfield.Bitlist{0x03}},
   186  				{AggregationBits: bitfield.Bitlist{0x02}},
   187  				nil, nil,
   188  			},
   189  		},
   190  		{
   191  			name: "all processed",
   192  			atts: []*ethpb.Attestation{
   193  				{AggregationBits: bitfield.Bitlist{0x00}},
   194  				{AggregationBits: bitfield.Bitlist{0x01}},
   195  				{AggregationBits: bitfield.Bitlist{0x02}},
   196  				{AggregationBits: bitfield.Bitlist{0x03}},
   197  				{AggregationBits: bitfield.Bitlist{0x04}},
   198  			},
   199  			keys: []int{0, 1, 2, 3, 4},
   200  			wantAtts: []*ethpb.Attestation{
   201  				nil, nil, nil, nil, nil,
   202  			},
   203  		},
   204  		{
   205  			name: "operate on slice, single attestation marked",
   206  			atts: []*ethpb.Attestation{
   207  				{AggregationBits: bitfield.Bitlist{0x00}},
   208  				{AggregationBits: bitfield.Bitlist{0x01}},
   209  				{AggregationBits: bitfield.Bitlist{0x02}},
   210  				{AggregationBits: bitfield.Bitlist{0x03}},
   211  				{AggregationBits: bitfield.Bitlist{0x04}},
   212  				// Assuming some attestations have been already marked as nil, during previous rounds:
   213  				nil, nil, nil,
   214  			},
   215  			keys: []int{2},
   216  			wantAtts: []*ethpb.Attestation{
   217  				{AggregationBits: bitfield.Bitlist{0x00}},
   218  				{AggregationBits: bitfield.Bitlist{0x01}},
   219  				{AggregationBits: bitfield.Bitlist{0x04}},
   220  				{AggregationBits: bitfield.Bitlist{0x03}},
   221  				nil, nil, nil, nil,
   222  			},
   223  		},
   224  		{
   225  			name: "operate on slice, non-last attestation marked",
   226  			atts: []*ethpb.Attestation{
   227  				{AggregationBits: bitfield.Bitlist{0x00}},
   228  				{AggregationBits: bitfield.Bitlist{0x01}},
   229  				{AggregationBits: bitfield.Bitlist{0x02}},
   230  				{AggregationBits: bitfield.Bitlist{0x03}},
   231  				{AggregationBits: bitfield.Bitlist{0x04}},
   232  				{AggregationBits: bitfield.Bitlist{0x05}},
   233  				// Assuming some attestations have been already marked as nil, during previous rounds:
   234  				nil, nil, nil,
   235  			},
   236  			keys: []int{2, 3},
   237  			wantAtts: []*ethpb.Attestation{
   238  				{AggregationBits: bitfield.Bitlist{0x00}},
   239  				{AggregationBits: bitfield.Bitlist{0x01}},
   240  				{AggregationBits: bitfield.Bitlist{0x05}},
   241  				{AggregationBits: bitfield.Bitlist{0x04}},
   242  				nil, nil, nil, nil, nil,
   243  			},
   244  		},
   245  		{
   246  			name: "operate on slice, last attestation marked",
   247  			atts: []*ethpb.Attestation{
   248  				{AggregationBits: bitfield.Bitlist{0x00}},
   249  				{AggregationBits: bitfield.Bitlist{0x01}},
   250  				{AggregationBits: bitfield.Bitlist{0x02}},
   251  				{AggregationBits: bitfield.Bitlist{0x03}},
   252  				{AggregationBits: bitfield.Bitlist{0x04}},
   253  				// Assuming some attestations have been already marked as nil, during previous rounds:
   254  				nil, nil, nil,
   255  			},
   256  			keys: []int{2, 4},
   257  			wantAtts: []*ethpb.Attestation{
   258  				{AggregationBits: bitfield.Bitlist{0x00}},
   259  				{AggregationBits: bitfield.Bitlist{0x01}},
   260  				{AggregationBits: bitfield.Bitlist{0x03}},
   261  				nil, nil, nil, nil, nil,
   262  			},
   263  		},
   264  		{
   265  			name: "many items, many selected, keys unsorted",
   266  			atts: []*ethpb.Attestation{
   267  				{AggregationBits: bitfield.Bitlist{0x00}},
   268  				{AggregationBits: bitfield.Bitlist{0x01}},
   269  				{AggregationBits: bitfield.Bitlist{0x02}},
   270  				{AggregationBits: bitfield.Bitlist{0x03}},
   271  				{AggregationBits: bitfield.Bitlist{0x04}},
   272  				{AggregationBits: bitfield.Bitlist{0x05}},
   273  				{AggregationBits: bitfield.Bitlist{0x06}},
   274  			},
   275  			keys: []int{4, 1, 2, 5, 6},
   276  			wantAtts: []*ethpb.Attestation{
   277  				{AggregationBits: bitfield.Bitlist{0x00}},
   278  				{AggregationBits: bitfield.Bitlist{0x03}},
   279  				nil, nil, nil, nil, nil,
   280  			},
   281  		},
   282  	}
   283  	for _, tt := range tests {
   284  		t.Run(tt.name, func(t *testing.T) {
   285  			candidates := make([]*bitfield.Bitlist64, len(tt.atts))
   286  			for i := 0; i < len(tt.atts); i++ {
   287  				if tt.atts[i] != nil {
   288  					var err error
   289  					candidates[i], err = tt.atts[i].AggregationBits.ToBitlist64()
   290  					if err != nil {
   291  						t.Error(err)
   292  					}
   293  				}
   294  			}
   295  			rearrangeProcessedAttestations(tt.atts, candidates, tt.keys)
   296  			assert.DeepEqual(t, tt.atts, tt.wantAtts)
   297  		})
   298  	}
   299  }
   300  
   301  func TestAggregateAttestations_aggregateAttestations(t *testing.T) {
   302  	sign := bls.NewAggregateSignature().Marshal()
   303  	tests := []struct {
   304  		name          string
   305  		atts          []*ethpb.Attestation
   306  		wantAtts      []*ethpb.Attestation
   307  		keys          []int
   308  		coverage      *bitfield.Bitlist64
   309  		wantTargetIdx int
   310  		wantErr       string
   311  	}{
   312  		{
   313  			name:          "nil attestation",
   314  			wantTargetIdx: 0,
   315  			wantErr:       ErrInvalidAttestationCount.Error(),
   316  			keys:          []int{0, 1, 2},
   317  		},
   318  		{
   319  			name: "single attestation",
   320  			atts: []*ethpb.Attestation{
   321  				{},
   322  			},
   323  			wantTargetIdx: 0,
   324  			wantErr:       ErrInvalidAttestationCount.Error(),
   325  			keys:          []int{0, 1, 2},
   326  		},
   327  		{
   328  			name:          "no keys",
   329  			wantTargetIdx: 0,
   330  			wantErr:       ErrInvalidAttestationCount.Error(),
   331  		},
   332  		{
   333  			name: "two attestations, none selected",
   334  			atts: []*ethpb.Attestation{
   335  				{AggregationBits: bitfield.Bitlist{0x00}},
   336  				{AggregationBits: bitfield.Bitlist{0x01}},
   337  			},
   338  			wantTargetIdx: 0,
   339  			wantErr:       ErrInvalidAttestationCount.Error(),
   340  			keys:          []int{},
   341  		},
   342  		{
   343  			name: "two attestations, one selected",
   344  			atts: []*ethpb.Attestation{
   345  				{AggregationBits: bitfield.Bitlist{0x00}},
   346  				{AggregationBits: bitfield.Bitlist{0x01}},
   347  			},
   348  			wantTargetIdx: 0,
   349  			wantErr:       ErrInvalidAttestationCount.Error(),
   350  			keys:          []int{0},
   351  		},
   352  		{
   353  			name: "two attestations, both selected, empty coverage",
   354  			atts: []*ethpb.Attestation{
   355  				{AggregationBits: bitfield.Bitlist{0b00000001, 0b1}, Signature: sign},
   356  				{AggregationBits: bitfield.Bitlist{0b00000110, 0b1}, Signature: sign},
   357  			},
   358  			wantAtts: []*ethpb.Attestation{
   359  				{AggregationBits: bitfield.Bitlist{0b00000111, 0b1}, Signature: sign},
   360  				{AggregationBits: bitfield.Bitlist{0b00000110, 0b1}, Signature: sign},
   361  			},
   362  			wantTargetIdx: 0,
   363  			wantErr:       "invalid or empty coverage",
   364  			keys:          []int{0, 1},
   365  		},
   366  		{
   367  			name: "two attestations, both selected",
   368  			atts: []*ethpb.Attestation{
   369  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00000001, 0b1}, Signature: sign},
   370  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00000010, 0b1}, Signature: sign},
   371  			},
   372  			wantAtts: []*ethpb.Attestation{
   373  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00000011, 0b1}, Signature: sign},
   374  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00000010, 0b1}, Signature: sign},
   375  			},
   376  			wantTargetIdx: 0,
   377  			keys:          []int{0, 1},
   378  			coverage: func() *bitfield.Bitlist64 {
   379  				b, err := bitfield.NewBitlist64FromBytes(64, []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00000011})
   380  				if err != nil {
   381  					t.Fatal(err)
   382  				}
   383  				return b
   384  			}(),
   385  		},
   386  		{
   387  			name: "many attestations, several selected",
   388  			atts: []*ethpb.Attestation{
   389  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00000001, 0b1}, Signature: sign},
   390  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00000010, 0b1}, Signature: sign},
   391  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00000100, 0b1}, Signature: sign},
   392  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00001000, 0b1}, Signature: sign},
   393  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00010000, 0b1}, Signature: sign},
   394  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00100000, 0b1}, Signature: sign},
   395  			},
   396  			wantAtts: []*ethpb.Attestation{
   397  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00000001, 0b1}, Signature: sign},
   398  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00010110, 0b1}, Signature: sign},
   399  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00000100, 0b1}, Signature: sign},
   400  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00001000, 0b1}, Signature: sign},
   401  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00010000, 0b1}, Signature: sign},
   402  				{AggregationBits: bitfield.Bitlist{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00100000, 0b1}, Signature: sign},
   403  			},
   404  			wantTargetIdx: 1,
   405  			keys:          []int{1, 2, 4},
   406  			coverage: func() *bitfield.Bitlist64 {
   407  				b, err := bitfield.NewBitlist64FromBytes(64, []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0b00010110})
   408  				if err != nil {
   409  					t.Fatal(err)
   410  				}
   411  				return b
   412  			}(),
   413  		},
   414  	}
   415  	for _, tt := range tests {
   416  		t.Run(tt.name, func(t *testing.T) {
   417  			gotTargetIdx, err := aggregateAttestations(tt.atts, tt.keys, tt.coverage)
   418  			if tt.wantErr != "" {
   419  				assert.ErrorContains(t, tt.wantErr, err)
   420  				return
   421  			} else {
   422  				assert.NoError(t, err)
   423  			}
   424  			assert.Equal(t, tt.wantTargetIdx, gotTargetIdx)
   425  			extractBitlists := func(atts []*ethpb.Attestation) []bitfield.Bitlist {
   426  				bl := make([]bitfield.Bitlist, len(atts))
   427  				for i, att := range atts {
   428  					bl[i] = att.AggregationBits
   429  				}
   430  				return bl
   431  			}
   432  			assert.DeepEqual(t, extractBitlists(tt.atts), extractBitlists(tt.wantAtts))
   433  		})
   434  	}
   435  }