github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/operations/attestations/kv/unaggregated_test.go (about) 1 package kv 2 3 import ( 4 "bytes" 5 "context" 6 "sort" 7 "testing" 8 9 fssz "github.com/ferranbt/fastssz" 10 c "github.com/patrickmn/go-cache" 11 "github.com/prysmaticlabs/go-bitfield" 12 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 13 "github.com/prysmaticlabs/prysm/shared/testutil" 14 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 15 "github.com/prysmaticlabs/prysm/shared/testutil/require" 16 ) 17 18 func TestKV_Unaggregated_SaveUnaggregatedAttestation(t *testing.T) { 19 tests := []struct { 20 name string 21 att *ethpb.Attestation 22 count int 23 wantErrString string 24 }{ 25 { 26 name: "nil attestation", 27 att: nil, 28 }, 29 { 30 name: "already aggregated", 31 att: ðpb.Attestation{AggregationBits: bitfield.Bitlist{0b10101}, Data: ðpb.AttestationData{Slot: 2}}, 32 wantErrString: "attestation is aggregated", 33 }, 34 { 35 name: "invalid hash", 36 att: ðpb.Attestation{ 37 Data: ðpb.AttestationData{ 38 BeaconBlockRoot: []byte{0b0}, 39 }, 40 }, 41 wantErrString: fssz.ErrBytesLength.Error(), 42 }, 43 { 44 name: "normal save", 45 att: testutil.HydrateAttestation(ðpb.Attestation{AggregationBits: bitfield.Bitlist{0b0001}}), 46 count: 1, 47 }, 48 { 49 name: "already seen", 50 att: testutil.HydrateAttestation(ðpb.Attestation{ 51 Data: ðpb.AttestationData{ 52 Slot: 100, 53 }, 54 AggregationBits: bitfield.Bitlist{0b10000001}, 55 }), 56 count: 0, 57 }, 58 } 59 r, err := hashFn(testutil.HydrateAttestationData(ðpb.AttestationData{Slot: 100})) 60 require.NoError(t, err) 61 62 for _, tt := range tests { 63 t.Run(tt.name, func(t *testing.T) { 64 cache := NewAttCaches() 65 cache.seenAtt.Set(string(r[:]), []bitfield.Bitlist{{0xff}}, c.DefaultExpiration) 66 assert.Equal(t, 0, len(cache.unAggregatedAtt), "Invalid start pool, atts: %d", len(cache.unAggregatedAtt)) 67 68 if tt.att != nil && tt.att.Signature == nil { 69 tt.att.Signature = make([]byte, 96) 70 } 71 72 err := cache.SaveUnaggregatedAttestation(tt.att) 73 if tt.wantErrString != "" { 74 assert.ErrorContains(t, tt.wantErrString, err) 75 } else { 76 assert.NoError(t, err) 77 } 78 assert.Equal(t, tt.count, len(cache.unAggregatedAtt), "Wrong attestation count") 79 assert.Equal(t, tt.count, cache.UnaggregatedAttestationCount(), "Wrong attestation count") 80 }) 81 } 82 } 83 84 func TestKV_Unaggregated_SaveUnaggregatedAttestations(t *testing.T) { 85 tests := []struct { 86 name string 87 atts []*ethpb.Attestation 88 count int 89 wantErrString string 90 }{ 91 { 92 name: "unaggregated only", 93 atts: []*ethpb.Attestation{ 94 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1}}), 95 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 2}}), 96 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 3}}), 97 }, 98 count: 3, 99 }, 100 { 101 name: "has aggregated", 102 atts: []*ethpb.Attestation{ 103 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1}}), 104 {AggregationBits: bitfield.Bitlist{0b1111}, Data: ðpb.AttestationData{Slot: 2}}, 105 testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 3}}), 106 }, 107 wantErrString: "attestation is aggregated", 108 count: 1, 109 }, 110 } 111 112 for _, tt := range tests { 113 t.Run(tt.name, func(t *testing.T) { 114 cache := NewAttCaches() 115 assert.Equal(t, 0, len(cache.unAggregatedAtt), "Invalid start pool, atts: %d", len(cache.unAggregatedAtt)) 116 117 err := cache.SaveUnaggregatedAttestations(tt.atts) 118 if tt.wantErrString != "" { 119 assert.ErrorContains(t, tt.wantErrString, err) 120 } else { 121 assert.NoError(t, err) 122 } 123 assert.Equal(t, tt.count, len(cache.unAggregatedAtt), "Wrong attestation count") 124 assert.Equal(t, tt.count, cache.UnaggregatedAttestationCount(), "Wrong attestation count") 125 }) 126 } 127 } 128 129 func TestKV_Unaggregated_DeleteUnaggregatedAttestation(t *testing.T) { 130 t.Run("nil attestation", func(t *testing.T) { 131 cache := NewAttCaches() 132 assert.NoError(t, cache.DeleteUnaggregatedAttestation(nil)) 133 }) 134 135 t.Run("aggregated attestation", func(t *testing.T) { 136 cache := NewAttCaches() 137 att := ðpb.Attestation{AggregationBits: bitfield.Bitlist{0b1111}, Data: ðpb.AttestationData{Slot: 2}} 138 err := cache.DeleteUnaggregatedAttestation(att) 139 assert.ErrorContains(t, "attestation is aggregated", err) 140 }) 141 142 t.Run("successful deletion", func(t *testing.T) { 143 cache := NewAttCaches() 144 att1 := testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b101}}) 145 att2 := testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b110}}) 146 att3 := testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b110}}) 147 atts := []*ethpb.Attestation{att1, att2, att3} 148 require.NoError(t, cache.SaveUnaggregatedAttestations(atts)) 149 for _, att := range atts { 150 assert.NoError(t, cache.DeleteUnaggregatedAttestation(att)) 151 } 152 returned, err := cache.UnaggregatedAttestations() 153 require.NoError(t, err) 154 assert.DeepEqual(t, []*ethpb.Attestation{}, returned) 155 }) 156 } 157 158 func TestKV_Unaggregated_DeleteSeenUnaggregatedAttestations(t *testing.T) { 159 d := testutil.HydrateAttestationData(ðpb.AttestationData{}) 160 161 t.Run("no attestations", func(t *testing.T) { 162 cache := NewAttCaches() 163 count, err := cache.DeleteSeenUnaggregatedAttestations() 164 assert.NoError(t, err) 165 assert.Equal(t, 0, count) 166 }) 167 168 t.Run("none seen", func(t *testing.T) { 169 cache := NewAttCaches() 170 atts := []*ethpb.Attestation{ 171 testutil.HydrateAttestation(ðpb.Attestation{Data: d, AggregationBits: bitfield.Bitlist{0b1001}}), 172 testutil.HydrateAttestation(ðpb.Attestation{Data: d, AggregationBits: bitfield.Bitlist{0b1010}}), 173 testutil.HydrateAttestation(ðpb.Attestation{Data: d, AggregationBits: bitfield.Bitlist{0b1100}}), 174 } 175 require.NoError(t, cache.SaveUnaggregatedAttestations(atts)) 176 assert.Equal(t, 3, cache.UnaggregatedAttestationCount()) 177 178 // As none of attestations have been marked seen, nothing should be deleted. 179 count, err := cache.DeleteSeenUnaggregatedAttestations() 180 assert.NoError(t, err) 181 assert.Equal(t, 0, count) 182 assert.Equal(t, 3, cache.UnaggregatedAttestationCount()) 183 }) 184 185 t.Run("some seen", func(t *testing.T) { 186 cache := NewAttCaches() 187 atts := []*ethpb.Attestation{ 188 testutil.HydrateAttestation(ðpb.Attestation{Data: d, AggregationBits: bitfield.Bitlist{0b1001}}), 189 testutil.HydrateAttestation(ðpb.Attestation{Data: d, AggregationBits: bitfield.Bitlist{0b1010}}), 190 testutil.HydrateAttestation(ðpb.Attestation{Data: d, AggregationBits: bitfield.Bitlist{0b1100}}), 191 } 192 require.NoError(t, cache.SaveUnaggregatedAttestations(atts)) 193 assert.Equal(t, 3, cache.UnaggregatedAttestationCount()) 194 195 require.NoError(t, cache.insertSeenBit(atts[1])) 196 197 // Only seen attestations must be deleted. 198 count, err := cache.DeleteSeenUnaggregatedAttestations() 199 assert.NoError(t, err) 200 assert.Equal(t, 1, count) 201 assert.Equal(t, 2, cache.UnaggregatedAttestationCount()) 202 returned, err := cache.UnaggregatedAttestations() 203 sort.Slice(returned, func(i, j int) bool { 204 return bytes.Compare(returned[i].AggregationBits, returned[j].AggregationBits) < 0 205 }) 206 require.NoError(t, err) 207 assert.DeepEqual(t, []*ethpb.Attestation{atts[0], atts[2]}, returned) 208 }) 209 210 t.Run("all seen", func(t *testing.T) { 211 cache := NewAttCaches() 212 atts := []*ethpb.Attestation{ 213 testutil.HydrateAttestation(ðpb.Attestation{Data: d, AggregationBits: bitfield.Bitlist{0b1001}}), 214 testutil.HydrateAttestation(ðpb.Attestation{Data: d, AggregationBits: bitfield.Bitlist{0b1010}}), 215 testutil.HydrateAttestation(ðpb.Attestation{Data: d, AggregationBits: bitfield.Bitlist{0b1100}}), 216 } 217 require.NoError(t, cache.SaveUnaggregatedAttestations(atts)) 218 assert.Equal(t, 3, cache.UnaggregatedAttestationCount()) 219 220 require.NoError(t, cache.insertSeenBit(atts[0])) 221 require.NoError(t, cache.insertSeenBit(atts[1])) 222 require.NoError(t, cache.insertSeenBit(atts[2])) 223 224 // All attestations have been processed -- all should be removed. 225 count, err := cache.DeleteSeenUnaggregatedAttestations() 226 assert.NoError(t, err) 227 assert.Equal(t, 3, count) 228 assert.Equal(t, 0, cache.UnaggregatedAttestationCount()) 229 returned, err := cache.UnaggregatedAttestations() 230 require.NoError(t, err) 231 assert.DeepEqual(t, []*ethpb.Attestation{}, returned) 232 }) 233 } 234 235 func TestKV_Unaggregated_UnaggregatedAttestationsBySlotIndex(t *testing.T) { 236 cache := NewAttCaches() 237 238 att1 := testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1, CommitteeIndex: 1}, AggregationBits: bitfield.Bitlist{0b101}}) 239 att2 := testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1, CommitteeIndex: 2}, AggregationBits: bitfield.Bitlist{0b110}}) 240 att3 := testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 2, CommitteeIndex: 1}, AggregationBits: bitfield.Bitlist{0b110}}) 241 atts := []*ethpb.Attestation{att1, att2, att3} 242 243 for _, att := range atts { 244 require.NoError(t, cache.SaveUnaggregatedAttestation(att)) 245 } 246 ctx := context.Background() 247 returned := cache.UnaggregatedAttestationsBySlotIndex(ctx, 1, 1) 248 assert.DeepEqual(t, []*ethpb.Attestation{att1}, returned) 249 returned = cache.UnaggregatedAttestationsBySlotIndex(ctx, 1, 2) 250 assert.DeepEqual(t, []*ethpb.Attestation{att2}, returned) 251 returned = cache.UnaggregatedAttestationsBySlotIndex(ctx, 2, 1) 252 assert.DeepEqual(t, []*ethpb.Attestation{att3}, returned) 253 }