github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_attestations_test.go (about) 1 package validator 2 3 import ( 4 "bytes" 5 "sort" 6 "testing" 7 8 types "github.com/prysmaticlabs/eth2-types" 9 "github.com/prysmaticlabs/go-bitfield" 10 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 11 "github.com/prysmaticlabs/prysm/shared/featureconfig" 12 "github.com/prysmaticlabs/prysm/shared/testutil" 13 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 14 "github.com/prysmaticlabs/prysm/shared/testutil/require" 15 ) 16 17 func TestProposer_ProposerAtts_sortByProfitability(t *testing.T) { 18 atts := proposerAtts([]*ethpb.Attestation{ 19 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 4}, AggregationBits: bitfield.Bitlist{0b11100000}}), 20 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b11000000}}), 21 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b11100000}}), 22 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 4}, AggregationBits: bitfield.Bitlist{0b11110000}}), 23 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b11100000}}), 24 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b11000000}}), 25 }) 26 want := proposerAtts([]*ethpb.Attestation{ 27 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 4}, AggregationBits: bitfield.Bitlist{0b11110000}}), 28 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 4}, AggregationBits: bitfield.Bitlist{0b11100000}}), 29 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b11000000}}), 30 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b11100000}}), 31 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b11100000}}), 32 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b11000000}}), 33 }) 34 atts, err := atts.sortByProfitability() 35 if err != nil { 36 t.Error(err) 37 } 38 require.DeepEqual(t, want, atts) 39 } 40 41 func TestProposer_ProposerAtts_sortByProfitabilityUsingMaxCover(t *testing.T) { 42 resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{ 43 ProposerAttsSelectionUsingMaxCover: true, 44 }) 45 defer resetCfg() 46 47 type testData struct { 48 slot types.Slot 49 bits bitfield.Bitlist 50 } 51 getAtts := func(data []testData) proposerAtts { 52 var atts proposerAtts 53 for _, att := range data { 54 atts = append(atts, testutil.HydrateAttestation(ðpb.Attestation{ 55 Data: ðpb.AttestationData{Slot: att.slot}, AggregationBits: att.bits})) 56 } 57 return atts 58 } 59 60 t.Run("no atts", func(t *testing.T) { 61 atts := getAtts([]testData{}) 62 want := getAtts([]testData{}) 63 atts, err := atts.sortByProfitability() 64 if err != nil { 65 t.Error(err) 66 } 67 require.DeepEqual(t, want, atts) 68 }) 69 70 t.Run("single att", func(t *testing.T) { 71 atts := getAtts([]testData{ 72 {4, bitfield.Bitlist{0b11100000, 0b1}}, 73 }) 74 want := getAtts([]testData{ 75 {4, bitfield.Bitlist{0b11100000, 0b1}}, 76 }) 77 atts, err := atts.sortByProfitability() 78 if err != nil { 79 t.Error(err) 80 } 81 require.DeepEqual(t, want, atts) 82 }) 83 84 t.Run("single att per slot", func(t *testing.T) { 85 atts := getAtts([]testData{ 86 {1, bitfield.Bitlist{0b11000000, 0b1}}, 87 {4, bitfield.Bitlist{0b11100000, 0b1}}, 88 }) 89 want := getAtts([]testData{ 90 {4, bitfield.Bitlist{0b11100000, 0b1}}, 91 {1, bitfield.Bitlist{0b11000000, 0b1}}, 92 }) 93 atts, err := atts.sortByProfitability() 94 if err != nil { 95 t.Error(err) 96 } 97 require.DeepEqual(t, want, atts) 98 }) 99 100 t.Run("two atts on one of the slots", func(t *testing.T) { 101 atts := getAtts([]testData{ 102 {1, bitfield.Bitlist{0b11000000, 0b1}}, 103 {4, bitfield.Bitlist{0b11100000, 0b1}}, 104 {4, bitfield.Bitlist{0b11110000, 0b1}}, 105 }) 106 want := getAtts([]testData{ 107 {4, bitfield.Bitlist{0b11110000, 0b1}}, 108 {4, bitfield.Bitlist{0b11100000, 0b1}}, 109 {1, bitfield.Bitlist{0b11000000, 0b1}}, 110 }) 111 atts, err := atts.sortByProfitability() 112 if err != nil { 113 t.Error(err) 114 } 115 require.DeepEqual(t, want, atts) 116 }) 117 118 t.Run("compare to native sort", func(t *testing.T) { 119 // The naive sort will end up with 0b11001000 being selected second (which is not optimal 120 // as it only contains a single unknown bit). 121 // The max-cover based approach will select 0b00001100 instead, despite lower bit count 122 // (since it has two new/unknown bits). 123 t.Run("naive", func(t *testing.T) { 124 resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{ 125 ProposerAttsSelectionUsingMaxCover: false, 126 }) 127 defer resetCfg() 128 129 atts := getAtts([]testData{ 130 {1, bitfield.Bitlist{0b11000011, 0b1}}, 131 {1, bitfield.Bitlist{0b11001000, 0b1}}, 132 {1, bitfield.Bitlist{0b00001100, 0b1}}, 133 }) 134 want := getAtts([]testData{ 135 {1, bitfield.Bitlist{0b11000011, 0b1}}, 136 {1, bitfield.Bitlist{0b11001000, 0b1}}, 137 {1, bitfield.Bitlist{0b00001100, 0b1}}, 138 }) 139 atts, err := atts.sortByProfitability() 140 if err != nil { 141 t.Error(err) 142 } 143 require.DeepEqual(t, want, atts) 144 }) 145 t.Run("max-cover", func(t *testing.T) { 146 resetCfg := featureconfig.InitWithReset(&featureconfig.Flags{ 147 ProposerAttsSelectionUsingMaxCover: true, 148 }) 149 defer resetCfg() 150 151 atts := getAtts([]testData{ 152 {1, bitfield.Bitlist{0b11000011, 0b1}}, 153 {1, bitfield.Bitlist{0b11001000, 0b1}}, 154 {1, bitfield.Bitlist{0b00001100, 0b1}}, 155 }) 156 want := getAtts([]testData{ 157 {1, bitfield.Bitlist{0b11000011, 0b1}}, 158 {1, bitfield.Bitlist{0b00001100, 0b1}}, 159 {1, bitfield.Bitlist{0b11001000, 0b1}}, 160 }) 161 atts, err := atts.sortByProfitability() 162 if err != nil { 163 t.Error(err) 164 } 165 require.DeepEqual(t, want, atts) 166 }) 167 }) 168 169 t.Run("multiple slots", func(t *testing.T) { 170 atts := getAtts([]testData{ 171 {2, bitfield.Bitlist{0b11100000, 0b1}}, 172 {4, bitfield.Bitlist{0b11100000, 0b1}}, 173 {1, bitfield.Bitlist{0b11000000, 0b1}}, 174 {4, bitfield.Bitlist{0b11110000, 0b1}}, 175 {1, bitfield.Bitlist{0b11100000, 0b1}}, 176 {3, bitfield.Bitlist{0b11000000, 0b1}}, 177 }) 178 want := getAtts([]testData{ 179 {4, bitfield.Bitlist{0b11110000, 0b1}}, 180 {4, bitfield.Bitlist{0b11100000, 0b1}}, 181 {3, bitfield.Bitlist{0b11000000, 0b1}}, 182 {2, bitfield.Bitlist{0b11100000, 0b1}}, 183 {1, bitfield.Bitlist{0b11100000, 0b1}}, 184 {1, bitfield.Bitlist{0b11000000, 0b1}}, 185 }) 186 atts, err := atts.sortByProfitability() 187 if err != nil { 188 t.Error(err) 189 } 190 require.DeepEqual(t, want, atts) 191 }) 192 193 t.Run("selected and non selected atts sorted by bit count", func(t *testing.T) { 194 // Items at slot 4, must be first split into two lists by max-cover, with 195 // 0b10000011 scoring higher (as it provides more info in addition to already selected 196 // attestations) than 0b11100001 (despite naive bit count suggesting otherwise). Then, 197 // both selected and non-selected attestations must be additionally sorted by bit count. 198 atts := getAtts([]testData{ 199 {4, bitfield.Bitlist{0b00000001, 0b1}}, 200 {4, bitfield.Bitlist{0b11100001, 0b1}}, 201 {1, bitfield.Bitlist{0b11000000, 0b1}}, 202 {2, bitfield.Bitlist{0b11100000, 0b1}}, 203 {4, bitfield.Bitlist{0b10000011, 0b1}}, 204 {4, bitfield.Bitlist{0b11111000, 0b1}}, 205 {1, bitfield.Bitlist{0b11100000, 0b1}}, 206 {3, bitfield.Bitlist{0b11000000, 0b1}}, 207 }) 208 want := getAtts([]testData{ 209 {4, bitfield.Bitlist{0b11111000, 0b1}}, 210 {4, bitfield.Bitlist{0b10000011, 0b1}}, 211 {4, bitfield.Bitlist{0b11100001, 0b1}}, 212 {4, bitfield.Bitlist{0b00000001, 0b1}}, 213 {3, bitfield.Bitlist{0b11000000, 0b1}}, 214 {2, bitfield.Bitlist{0b11100000, 0b1}}, 215 {1, bitfield.Bitlist{0b11100000, 0b1}}, 216 {1, bitfield.Bitlist{0b11000000, 0b1}}, 217 }) 218 atts, err := atts.sortByProfitability() 219 if err != nil { 220 t.Error(err) 221 } 222 require.DeepEqual(t, want, atts) 223 }) 224 } 225 226 func TestProposer_ProposerAtts_dedup(t *testing.T) { 227 data1 := testutil.HydrateAttestationData(ðpb.AttestationData{ 228 Slot: 4, 229 }) 230 data2 := testutil.HydrateAttestationData(ðpb.AttestationData{ 231 Slot: 5, 232 }) 233 tests := []struct { 234 name string 235 atts proposerAtts 236 want proposerAtts 237 }{ 238 { 239 name: "nil list", 240 atts: nil, 241 want: proposerAtts(nil), 242 }, 243 { 244 name: "empty list", 245 atts: proposerAtts{}, 246 want: proposerAtts{}, 247 }, 248 { 249 name: "single item", 250 atts: proposerAtts{ 251 ðpb.Attestation{AggregationBits: bitfield.Bitlist{}}, 252 }, 253 want: proposerAtts{ 254 ðpb.Attestation{AggregationBits: bitfield.Bitlist{}}, 255 }, 256 }, 257 { 258 name: "two items no duplicates", 259 atts: proposerAtts{ 260 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b10111110, 0x01}}, 261 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01111111, 0x01}}, 262 }, 263 want: proposerAtts{ 264 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01111111, 0x01}}, 265 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b10111110, 0x01}}, 266 }, 267 }, 268 { 269 name: "two items with duplicates", 270 atts: proposerAtts{ 271 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0xba, 0x01}}, 272 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0xba, 0x01}}, 273 }, 274 want: proposerAtts{ 275 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0xba, 0x01}}, 276 }, 277 }, 278 { 279 name: "sorted no duplicates", 280 atts: proposerAtts{ 281 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 282 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 283 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00101011, 0b1}}, 284 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b10100000, 0b1}}, 285 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00010000, 0b1}}, 286 }, 287 want: proposerAtts{ 288 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 289 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 290 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00101011, 0b1}}, 291 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b10100000, 0b1}}, 292 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00010000, 0b1}}, 293 }, 294 }, 295 { 296 name: "sorted with duplicates", 297 atts: proposerAtts{ 298 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 299 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 300 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 301 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 302 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00001111, 0b1}}, 303 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 304 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 305 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000001, 0b1}}, 306 }, 307 want: proposerAtts{ 308 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 309 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 310 }, 311 }, 312 { 313 name: "all equal", 314 atts: proposerAtts{ 315 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 316 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 317 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 318 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 319 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 320 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 321 }, 322 want: proposerAtts{ 323 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 324 }, 325 }, 326 { 327 name: "unsorted no duplicates", 328 atts: proposerAtts{ 329 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 330 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00100010, 0b1}}, 331 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b10100101, 0b1}}, 332 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00010000, 0b1}}, 333 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 334 }, 335 want: proposerAtts{ 336 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 337 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 338 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b10100101, 0b1}}, 339 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00100010, 0b1}}, 340 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00010000, 0b1}}, 341 }, 342 }, 343 { 344 name: "unsorted with duplicates", 345 atts: proposerAtts{ 346 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00001111, 0b1}}, 347 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 348 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b10100101, 0b1}}, 349 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b10100101, 0b1}}, 350 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000001, 0b1}}, 351 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 352 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 353 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 354 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000001, 0b1}}, 355 }, 356 want: proposerAtts{ 357 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 358 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 359 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b10100101, 0b1}}, 360 }, 361 }, 362 { 363 name: "no proper subset (same root)", 364 atts: proposerAtts{ 365 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000101, 0b1}}, 366 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 367 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b10000001, 0b1}}, 368 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00011001, 0b1}}, 369 }, 370 want: proposerAtts{ 371 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00011001, 0b1}}, 372 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 373 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000101, 0b1}}, 374 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b10000001, 0b1}}, 375 }, 376 }, 377 { 378 name: "proper subset (same root)", 379 atts: proposerAtts{ 380 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00001111, 0b1}}, 381 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 382 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00001111, 0b1}}, 383 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00001111, 0b1}}, 384 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000001, 0b1}}, 385 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 386 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 387 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000001, 0b1}}, 388 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 389 }, 390 want: proposerAtts{ 391 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 392 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 393 }, 394 }, 395 { 396 name: "no proper subset (different root)", 397 atts: proposerAtts{ 398 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000101, 0b1}}, 399 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 400 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b10000001, 0b1}}, 401 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b00011001, 0b1}}, 402 }, 403 want: proposerAtts{ 404 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b00011001, 0b1}}, 405 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b10000001, 0b1}}, 406 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 407 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000101, 0b1}}, 408 }, 409 }, 410 { 411 name: "proper subset (different root 1)", 412 atts: proposerAtts{ 413 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b00001111, 0b1}}, 414 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 415 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b00001111, 0b1}}, 416 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b00001111, 0b1}}, 417 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000001, 0b1}}, 418 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b00000011, 0b1}}, 419 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 420 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00000001, 0b1}}, 421 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 422 }, 423 want: proposerAtts{ 424 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 425 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b01101101, 0b1}}, 426 }, 427 }, 428 { 429 name: "proper subset (different root 2)", 430 atts: proposerAtts{ 431 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b00001111, 0b1}}, 432 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 433 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b00001111, 0b1}}, 434 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 435 }, 436 want: proposerAtts{ 437 ðpb.Attestation{Data: data2, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 438 ðpb.Attestation{Data: data1, AggregationBits: bitfield.Bitlist{0b11001111, 0b1}}, 439 }, 440 }, 441 } 442 for _, tt := range tests { 443 t.Run(tt.name, func(t *testing.T) { 444 atts, err := tt.atts.dedup() 445 if err != nil { 446 t.Error(err) 447 } 448 sort.Slice(atts, func(i, j int) bool { 449 if atts[i].AggregationBits.Count() == atts[j].AggregationBits.Count() { 450 if atts[i].Data.Slot == atts[j].Data.Slot { 451 return bytes.Compare(atts[i].AggregationBits, atts[j].AggregationBits) <= 0 452 } 453 return atts[i].Data.Slot > atts[j].Data.Slot 454 } 455 return atts[i].AggregationBits.Count() > atts[j].AggregationBits.Count() 456 }) 457 assert.DeepEqual(t, tt.want, atts) 458 }) 459 } 460 }