github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/operations/attestations/prepare_forkchoice_test.go (about) 1 package attestations 2 3 import ( 4 "context" 5 "fmt" 6 "sort" 7 "testing" 8 9 "github.com/prysmaticlabs/go-bitfield" 10 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 11 attaggregation "github.com/prysmaticlabs/prysm/shared/aggregation/attestations" 12 "github.com/prysmaticlabs/prysm/shared/bls" 13 "github.com/prysmaticlabs/prysm/shared/testutil" 14 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 15 "github.com/prysmaticlabs/prysm/shared/testutil/require" 16 "google.golang.org/protobuf/proto" 17 ) 18 19 func TestBatchAttestations_Multiple(t *testing.T) { 20 s, err := NewService(context.Background(), &Config{Pool: NewPool()}) 21 require.NoError(t, err) 22 23 priv, err := bls.RandKey() 24 require.NoError(t, err) 25 sig := priv.Sign([]byte("dummy_test_data")) 26 var mockRoot [32]byte 27 28 unaggregatedAtts := []*ethpb.Attestation{ 29 {Data: ðpb.AttestationData{ 30 Slot: 2, 31 BeaconBlockRoot: mockRoot[:], 32 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 33 Target: ðpb.Checkpoint{Root: mockRoot[:]}}, AggregationBits: bitfield.Bitlist{0b100100}, Signature: sig.Marshal()}, 34 {Data: ðpb.AttestationData{ 35 Slot: 1, 36 BeaconBlockRoot: mockRoot[:], 37 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 38 Target: ðpb.Checkpoint{Root: mockRoot[:]}}, AggregationBits: bitfield.Bitlist{0b101000}, Signature: sig.Marshal()}, 39 {Data: ðpb.AttestationData{ 40 Slot: 0, 41 BeaconBlockRoot: mockRoot[:], 42 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 43 Target: ðpb.Checkpoint{Root: mockRoot[:]}}, AggregationBits: bitfield.Bitlist{0b100010}, Signature: sig.Marshal()}, 44 } 45 aggregatedAtts := []*ethpb.Attestation{ 46 {Data: ðpb.AttestationData{ 47 Slot: 2, 48 BeaconBlockRoot: mockRoot[:], 49 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 50 Target: ðpb.Checkpoint{Root: mockRoot[:]}}, AggregationBits: bitfield.Bitlist{0b111000}, Signature: sig.Marshal()}, 51 {Data: ðpb.AttestationData{ 52 Slot: 1, 53 BeaconBlockRoot: mockRoot[:], 54 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 55 Target: ðpb.Checkpoint{Root: mockRoot[:]}}, AggregationBits: bitfield.Bitlist{0b100011}, Signature: sig.Marshal()}, 56 {Data: ðpb.AttestationData{ 57 Slot: 0, 58 BeaconBlockRoot: mockRoot[:], 59 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 60 Target: ðpb.Checkpoint{Root: mockRoot[:]}}, AggregationBits: bitfield.Bitlist{0b110001}, Signature: sig.Marshal()}, 61 } 62 blockAtts := []*ethpb.Attestation{ 63 {Data: ðpb.AttestationData{ 64 Slot: 2, 65 BeaconBlockRoot: mockRoot[:], 66 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 67 Target: ðpb.Checkpoint{Root: mockRoot[:]}}, AggregationBits: bitfield.Bitlist{0b100001}, Signature: sig.Marshal()}, 68 {Data: ðpb.AttestationData{ 69 Slot: 1, 70 BeaconBlockRoot: mockRoot[:], 71 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 72 Target: ðpb.Checkpoint{Root: mockRoot[:]}}, AggregationBits: bitfield.Bitlist{0b100100}, Signature: sig.Marshal()}, 73 {Data: ðpb.AttestationData{ 74 Slot: 0, 75 BeaconBlockRoot: mockRoot[:], 76 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 77 Target: ðpb.Checkpoint{Root: mockRoot[:]}}, AggregationBits: bitfield.Bitlist{0b100100}, Signature: sig.Marshal()}, 78 {Data: ðpb.AttestationData{ 79 Slot: 2, 80 BeaconBlockRoot: mockRoot[:], 81 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 82 Target: ðpb.Checkpoint{Root: mockRoot[:]}}, AggregationBits: bitfield.Bitlist{0b111000}, Signature: sig.Marshal()}, // Duplicated 83 {Data: ðpb.AttestationData{ 84 Slot: 1, 85 BeaconBlockRoot: mockRoot[:], 86 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 87 Target: ðpb.Checkpoint{Root: mockRoot[:]}}, AggregationBits: bitfield.Bitlist{0b100011}, Signature: sig.Marshal()}, // Duplicated 88 } 89 require.NoError(t, s.cfg.Pool.SaveUnaggregatedAttestations(unaggregatedAtts)) 90 require.NoError(t, s.cfg.Pool.SaveAggregatedAttestations(aggregatedAtts)) 91 require.NoError(t, s.cfg.Pool.SaveBlockAttestations(blockAtts)) 92 require.NoError(t, s.batchForkChoiceAtts(context.Background())) 93 94 wanted, err := attaggregation.Aggregate([]*ethpb.Attestation{aggregatedAtts[0], blockAtts[0]}) 95 require.NoError(t, err) 96 aggregated, err := attaggregation.Aggregate([]*ethpb.Attestation{aggregatedAtts[1], blockAtts[1]}) 97 require.NoError(t, err) 98 wanted = append(wanted, aggregated...) 99 aggregated, err = attaggregation.Aggregate([]*ethpb.Attestation{aggregatedAtts[2], blockAtts[2]}) 100 require.NoError(t, err) 101 102 wanted = append(wanted, aggregated...) 103 require.NoError(t, s.cfg.Pool.AggregateUnaggregatedAttestations(context.Background())) 104 received := s.cfg.Pool.ForkchoiceAttestations() 105 106 sort.Slice(received, func(i, j int) bool { 107 return received[i].Data.Slot < received[j].Data.Slot 108 }) 109 sort.Slice(wanted, func(i, j int) bool { 110 return wanted[i].Data.Slot < wanted[j].Data.Slot 111 }) 112 113 assert.DeepSSZEqual(t, wanted, received) 114 } 115 116 func TestBatchAttestations_Single(t *testing.T) { 117 s, err := NewService(context.Background(), &Config{Pool: NewPool()}) 118 require.NoError(t, err) 119 120 priv, err := bls.RandKey() 121 require.NoError(t, err) 122 sig := priv.Sign([]byte("dummy_test_data")) 123 mockRoot := [32]byte{} 124 d := ðpb.AttestationData{ 125 BeaconBlockRoot: mockRoot[:], 126 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 127 Target: ðpb.Checkpoint{Root: mockRoot[:]}, 128 } 129 130 unaggregatedAtts := []*ethpb.Attestation{ 131 {Data: d, AggregationBits: bitfield.Bitlist{0b101000}, Signature: sig.Marshal()}, 132 {Data: d, AggregationBits: bitfield.Bitlist{0b100100}, Signature: sig.Marshal()}, 133 } 134 aggregatedAtts := []*ethpb.Attestation{ 135 {Data: d, AggregationBits: bitfield.Bitlist{0b101100}, Signature: sig.Marshal()}, 136 {Data: d, AggregationBits: bitfield.Bitlist{0b110010}, Signature: sig.Marshal()}, 137 } 138 blockAtts := []*ethpb.Attestation{ 139 {Data: d, AggregationBits: bitfield.Bitlist{0b110010}, Signature: sig.Marshal()}, 140 {Data: d, AggregationBits: bitfield.Bitlist{0b100010}, Signature: sig.Marshal()}, 141 {Data: d, AggregationBits: bitfield.Bitlist{0b110010}, Signature: sig.Marshal()}, // Duplicated 142 } 143 require.NoError(t, s.cfg.Pool.SaveUnaggregatedAttestations(unaggregatedAtts)) 144 require.NoError(t, s.cfg.Pool.SaveAggregatedAttestations(aggregatedAtts)) 145 require.NoError(t, s.cfg.Pool.SaveBlockAttestations(blockAtts)) 146 require.NoError(t, s.batchForkChoiceAtts(context.Background())) 147 148 wanted, err := attaggregation.Aggregate(append(aggregatedAtts, unaggregatedAtts...)) 149 require.NoError(t, err) 150 151 wanted, err = attaggregation.Aggregate(append(wanted, blockAtts...)) 152 require.NoError(t, err) 153 154 got := s.cfg.Pool.ForkchoiceAttestations() 155 assert.DeepEqual(t, wanted, got) 156 } 157 158 func TestAggregateAndSaveForkChoiceAtts_Single(t *testing.T) { 159 s, err := NewService(context.Background(), &Config{Pool: NewPool()}) 160 require.NoError(t, err) 161 162 priv, err := bls.RandKey() 163 require.NoError(t, err) 164 sig := priv.Sign([]byte("dummy_test_data")) 165 mockRoot := [32]byte{} 166 d := ðpb.AttestationData{ 167 BeaconBlockRoot: mockRoot[:], 168 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 169 Target: ðpb.Checkpoint{Root: mockRoot[:]}, 170 } 171 172 atts := []*ethpb.Attestation{ 173 {Data: d, AggregationBits: bitfield.Bitlist{0b101}, Signature: sig.Marshal()}, 174 {Data: d, AggregationBits: bitfield.Bitlist{0b110}, Signature: sig.Marshal()}} 175 require.NoError(t, s.aggregateAndSaveForkChoiceAtts(atts)) 176 177 wanted, err := attaggregation.Aggregate(atts) 178 require.NoError(t, err) 179 assert.DeepEqual(t, wanted, s.cfg.Pool.ForkchoiceAttestations()) 180 } 181 182 func TestAggregateAndSaveForkChoiceAtts_Multiple(t *testing.T) { 183 s, err := NewService(context.Background(), &Config{Pool: NewPool()}) 184 require.NoError(t, err) 185 186 priv, err := bls.RandKey() 187 require.NoError(t, err) 188 sig := priv.Sign([]byte("dummy_test_data")) 189 mockRoot := [32]byte{} 190 d := ðpb.AttestationData{ 191 BeaconBlockRoot: mockRoot[:], 192 Source: ðpb.Checkpoint{Root: mockRoot[:]}, 193 Target: ðpb.Checkpoint{Root: mockRoot[:]}, 194 } 195 d1, ok := proto.Clone(d).(*ethpb.AttestationData) 196 require.Equal(t, true, ok, "Entity is not of type *ethpb.AttestationData") 197 d1.Slot = 1 198 d2, ok := proto.Clone(d).(*ethpb.AttestationData) 199 require.Equal(t, true, ok, "Entity is not of type *ethpb.AttestationData") 200 d2.Slot = 2 201 202 atts1 := []*ethpb.Attestation{ 203 {Data: d, AggregationBits: bitfield.Bitlist{0b101}, Signature: sig.Marshal()}, 204 {Data: d, AggregationBits: bitfield.Bitlist{0b110}, Signature: sig.Marshal()}, 205 } 206 require.NoError(t, s.aggregateAndSaveForkChoiceAtts(atts1)) 207 atts2 := []*ethpb.Attestation{ 208 {Data: d1, AggregationBits: bitfield.Bitlist{0b10110}, Signature: sig.Marshal()}, 209 {Data: d1, AggregationBits: bitfield.Bitlist{0b11100}, Signature: sig.Marshal()}, 210 {Data: d1, AggregationBits: bitfield.Bitlist{0b11000}, Signature: sig.Marshal()}, 211 } 212 require.NoError(t, s.aggregateAndSaveForkChoiceAtts(atts2)) 213 att3 := []*ethpb.Attestation{ 214 {Data: d2, AggregationBits: bitfield.Bitlist{0b1100}, Signature: sig.Marshal()}, 215 } 216 require.NoError(t, s.aggregateAndSaveForkChoiceAtts(att3)) 217 218 wanted, err := attaggregation.Aggregate(atts1) 219 require.NoError(t, err) 220 aggregated, err := attaggregation.Aggregate(atts2) 221 require.NoError(t, err) 222 223 wanted = append(wanted, aggregated...) 224 wanted = append(wanted, att3...) 225 226 received := s.cfg.Pool.ForkchoiceAttestations() 227 sort.Slice(received, func(i, j int) bool { 228 return received[i].Data.Slot < received[j].Data.Slot 229 }) 230 assert.DeepEqual(t, wanted, received) 231 } 232 233 func TestSeenAttestations_PresentInCache(t *testing.T) { 234 s, err := NewService(context.Background(), &Config{Pool: NewPool()}) 235 require.NoError(t, err) 236 237 ad1 := testutil.HydrateAttestationData(ðpb.AttestationData{}) 238 att1 := ðpb.Attestation{Data: ad1, Signature: []byte{'A'}, AggregationBits: bitfield.Bitlist{0x13} /* 0b00010011 */} 239 got, err := s.seen(att1) 240 require.NoError(t, err) 241 assert.Equal(t, false, got) 242 243 att2 := ðpb.Attestation{Data: ad1, Signature: []byte{'A'}, AggregationBits: bitfield.Bitlist{0x17} /* 0b00010111 */} 244 got, err = s.seen(att2) 245 require.NoError(t, err) 246 assert.Equal(t, false, got) 247 248 att3 := ðpb.Attestation{Data: ad1, Signature: []byte{'A'}, AggregationBits: bitfield.Bitlist{0x17} /* 0b00010111 */} 249 got, err = s.seen(att3) 250 require.NoError(t, err) 251 assert.Equal(t, true, got) 252 } 253 254 func TestService_seen(t *testing.T) { 255 ad1 := testutil.HydrateAttestationData(ðpb.AttestationData{Slot: 1}) 256 257 ad2 := testutil.HydrateAttestationData(ðpb.AttestationData{Slot: 2}) 258 259 // Attestation are checked in order of this list. 260 tests := []struct { 261 att *ethpb.Attestation 262 want bool 263 }{ 264 { 265 att: ðpb.Attestation{ 266 AggregationBits: bitfield.Bitlist{0b11011}, 267 Data: ad1, 268 }, 269 want: false, 270 }, 271 { 272 att: ðpb.Attestation{ 273 AggregationBits: bitfield.Bitlist{0b11011}, 274 Data: ad1, 275 }, 276 want: true, // Exact same attestation should return true 277 }, 278 { 279 att: ðpb.Attestation{ 280 AggregationBits: bitfield.Bitlist{0b10101}, 281 Data: ad1, 282 }, 283 want: false, // Haven't seen the bit at index 2 yet. 284 }, 285 { 286 att: ðpb.Attestation{ 287 AggregationBits: bitfield.Bitlist{0b11111}, 288 Data: ad1, 289 }, 290 want: true, // We've full committee at this point. 291 }, 292 { 293 att: ðpb.Attestation{ 294 AggregationBits: bitfield.Bitlist{0b11111}, 295 Data: ad2, 296 }, 297 want: false, // Different root is different bitlist. 298 }, 299 { 300 att: ðpb.Attestation{ 301 AggregationBits: bitfield.Bitlist{0b11111001}, 302 Data: ad1, 303 }, 304 want: false, // Sanity test that an attestation of different lengths does not panic. 305 }, 306 } 307 308 s, err := NewService(context.Background(), &Config{Pool: NewPool()}) 309 require.NoError(t, err) 310 311 for i, tt := range tests { 312 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 313 got, err := s.seen(tt.att) 314 require.NoError(t, err) 315 assert.Equal(t, tt.want, got) 316 }) 317 } 318 }