github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/helpers/attestation_test.go (about) 1 package helpers_test 2 3 import ( 4 "strconv" 5 "testing" 6 "time" 7 8 types "github.com/prysmaticlabs/eth2-types" 9 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 10 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1" 11 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 12 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 13 "github.com/prysmaticlabs/prysm/shared/bls" 14 "github.com/prysmaticlabs/prysm/shared/bytesutil" 15 "github.com/prysmaticlabs/prysm/shared/params" 16 "github.com/prysmaticlabs/prysm/shared/testutil" 17 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 18 "github.com/prysmaticlabs/prysm/shared/testutil/require" 19 "github.com/prysmaticlabs/prysm/shared/timeutils" 20 ) 21 22 func TestAttestation_IsAggregator(t *testing.T) { 23 t.Run("aggregator", func(t *testing.T) { 24 beaconState, privKeys := testutil.DeterministicGenesisState(t, 100) 25 committee, err := helpers.BeaconCommitteeFromState(beaconState, 0, 0) 26 require.NoError(t, err) 27 sig := privKeys[0].Sign([]byte{'A'}) 28 agg, err := helpers.IsAggregator(uint64(len(committee)), sig.Marshal()) 29 require.NoError(t, err) 30 assert.Equal(t, true, agg, "Wanted aggregator true") 31 }) 32 33 t.Run("not aggregator", func(t *testing.T) { 34 params.UseMinimalConfig() 35 defer params.UseMainnetConfig() 36 beaconState, privKeys := testutil.DeterministicGenesisState(t, 2048) 37 38 committee, err := helpers.BeaconCommitteeFromState(beaconState, 0, 0) 39 require.NoError(t, err) 40 sig := privKeys[0].Sign([]byte{'A'}) 41 agg, err := helpers.IsAggregator(uint64(len(committee)), sig.Marshal()) 42 require.NoError(t, err) 43 assert.Equal(t, false, agg, "Wanted aggregator false") 44 }) 45 } 46 47 func TestAttestation_AggregateSignature(t *testing.T) { 48 t.Run("verified", func(t *testing.T) { 49 pubkeys := make([]bls.PublicKey, 0, 100) 50 atts := make([]*ethpb.Attestation, 0, 100) 51 msg := bytesutil.ToBytes32([]byte("hello")) 52 for i := 0; i < 100; i++ { 53 priv, err := bls.RandKey() 54 require.NoError(t, err) 55 pub := priv.PublicKey() 56 sig := priv.Sign(msg[:]) 57 pubkeys = append(pubkeys, pub) 58 att := ðpb.Attestation{Signature: sig.Marshal()} 59 atts = append(atts, att) 60 } 61 aggSig, err := helpers.AggregateSignature(atts) 62 require.NoError(t, err) 63 assert.Equal(t, true, aggSig.FastAggregateVerify(pubkeys, msg), "Signature did not verify") 64 }) 65 66 t.Run("not verified", func(t *testing.T) { 67 pubkeys := make([]bls.PublicKey, 0, 100) 68 atts := make([]*ethpb.Attestation, 0, 100) 69 msg := []byte("hello") 70 for i := 0; i < 100; i++ { 71 priv, err := bls.RandKey() 72 require.NoError(t, err) 73 pub := priv.PublicKey() 74 sig := priv.Sign(msg) 75 pubkeys = append(pubkeys, pub) 76 att := ðpb.Attestation{Signature: sig.Marshal()} 77 atts = append(atts, att) 78 } 79 aggSig, err := helpers.AggregateSignature(atts[0 : len(atts)-2]) 80 require.NoError(t, err) 81 assert.Equal(t, false, aggSig.FastAggregateVerify(pubkeys, bytesutil.ToBytes32(msg)), "Signature not suppose to verify") 82 }) 83 } 84 85 func TestAttestation_ComputeSubnetForAttestation(t *testing.T) { 86 // Create 10 committees 87 committeeCount := uint64(10) 88 validatorCount := committeeCount * params.BeaconConfig().TargetCommitteeSize 89 validators := make([]*ethpb.Validator, validatorCount) 90 91 for i := 0; i < len(validators); i++ { 92 k := make([]byte, 48) 93 copy(k, strconv.Itoa(i)) 94 validators[i] = ðpb.Validator{ 95 PublicKey: k, 96 WithdrawalCredentials: make([]byte, 32), 97 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 98 } 99 } 100 101 state, err := v1.InitializeFromProto(&pb.BeaconState{ 102 Validators: validators, 103 Slot: 200, 104 BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot), 105 StateRoots: make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot), 106 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), 107 }) 108 require.NoError(t, err) 109 att := ðpb.Attestation{ 110 AggregationBits: []byte{'A'}, 111 Data: ðpb.AttestationData{ 112 Slot: 34, 113 CommitteeIndex: 4, 114 BeaconBlockRoot: []byte{'C'}, 115 Source: nil, 116 Target: nil, 117 }, 118 Signature: []byte{'B'}, 119 } 120 valCount, err := helpers.ActiveValidatorCount(state, helpers.SlotToEpoch(att.Data.Slot)) 121 require.NoError(t, err) 122 sub := helpers.ComputeSubnetForAttestation(valCount, att) 123 assert.Equal(t, uint64(6), sub, "Did not get correct subnet for attestation") 124 } 125 126 func Test_ValidateAttestationTime(t *testing.T) { 127 if params.BeaconNetworkConfig().MaximumGossipClockDisparity < 200*time.Millisecond { 128 t.Fatal("This test expects the maximum clock disparity to be at least 200ms") 129 } 130 131 type args struct { 132 attSlot types.Slot 133 genesisTime time.Time 134 } 135 tests := []struct { 136 name string 137 args args 138 wantedErr string 139 }{ 140 { 141 name: "attestation.slot == current_slot", 142 args: args{ 143 attSlot: 15, 144 genesisTime: timeutils.Now().Add(-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second), 145 }, 146 }, 147 { 148 name: "attestation.slot == current_slot, received in middle of slot", 149 args: args{ 150 attSlot: 15, 151 genesisTime: timeutils.Now().Add( 152 -15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second, 153 ).Add(-(time.Duration(params.BeaconConfig().SecondsPerSlot/2) * time.Second)), 154 }, 155 }, 156 { 157 name: "attestation.slot == current_slot, received 200ms early", 158 args: args{ 159 attSlot: 16, 160 genesisTime: timeutils.Now().Add( 161 -16 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second, 162 ).Add(-200 * time.Millisecond), 163 }, 164 }, 165 { 166 name: "attestation.slot > current_slot", 167 args: args{ 168 attSlot: 16, 169 genesisTime: timeutils.Now().Add(-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second), 170 }, 171 wantedErr: "not within attestation propagation range", 172 }, 173 { 174 name: "attestation.slot < current_slot-ATTESTATION_PROPAGATION_SLOT_RANGE", 175 args: args{ 176 attSlot: 100 - params.BeaconNetworkConfig().AttestationPropagationSlotRange - 1, 177 genesisTime: timeutils.Now().Add(-100 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second), 178 }, 179 wantedErr: "not within attestation propagation range", 180 }, 181 { 182 name: "attestation.slot = current_slot-ATTESTATION_PROPAGATION_SLOT_RANGE", 183 args: args{ 184 attSlot: 100 - params.BeaconNetworkConfig().AttestationPropagationSlotRange, 185 genesisTime: timeutils.Now().Add(-100 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second), 186 }, 187 }, 188 { 189 name: "attestation.slot = current_slot-ATTESTATION_PROPAGATION_SLOT_RANGE, received 200ms late", 190 args: args{ 191 attSlot: 100 - params.BeaconNetworkConfig().AttestationPropagationSlotRange, 192 genesisTime: timeutils.Now().Add( 193 -100 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second, 194 ).Add(200 * time.Millisecond), 195 }, 196 }, 197 { 198 name: "attestation.slot is well beyond current slot", 199 args: args{ 200 attSlot: 1 << 32, 201 genesisTime: timeutils.Now().Add(-15 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second), 202 }, 203 wantedErr: "which exceeds max allowed value relative to the local clock", 204 }, 205 } 206 for _, tt := range tests { 207 t.Run(tt.name, func(t *testing.T) { 208 err := helpers.ValidateAttestationTime(tt.args.attSlot, tt.args.genesisTime, 209 params.BeaconNetworkConfig().MaximumGossipClockDisparity) 210 if tt.wantedErr != "" { 211 assert.ErrorContains(t, tt.wantedErr, err) 212 } else { 213 assert.NoError(t, err) 214 } 215 }) 216 } 217 } 218 219 func TestVerifyCheckpointEpoch_Ok(t *testing.T) { 220 // Genesis was 6 epochs ago exactly. 221 offset := params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot * 6) 222 genesis := time.Now().Add(-1 * time.Second * time.Duration(offset)) 223 assert.Equal(t, true, helpers.VerifyCheckpointEpoch(ðpb.Checkpoint{Epoch: 6}, genesis)) 224 assert.Equal(t, true, helpers.VerifyCheckpointEpoch(ðpb.Checkpoint{Epoch: 5}, genesis)) 225 assert.Equal(t, false, helpers.VerifyCheckpointEpoch(ðpb.Checkpoint{Epoch: 4}, genesis)) 226 assert.Equal(t, false, helpers.VerifyCheckpointEpoch(ðpb.Checkpoint{Epoch: 2}, genesis)) 227 } 228 229 func TestValidateNilAttestation(t *testing.T) { 230 tests := []struct { 231 name string 232 attestation *ethpb.Attestation 233 errString string 234 }{ 235 { 236 name: "nil attestation", 237 attestation: nil, 238 errString: "attestation can't be nil", 239 }, 240 { 241 name: "nil attestation data", 242 attestation: ðpb.Attestation{}, 243 errString: "attestation's data can't be nil", 244 }, 245 { 246 name: "nil attestation source", 247 attestation: ðpb.Attestation{ 248 Data: ðpb.AttestationData{ 249 Source: nil, 250 Target: ðpb.Checkpoint{}, 251 }, 252 }, 253 errString: "attestation's source can't be nil", 254 }, 255 { 256 name: "nil attestation target", 257 attestation: ðpb.Attestation{ 258 Data: ðpb.AttestationData{ 259 Target: nil, 260 Source: ðpb.Checkpoint{}, 261 }, 262 }, 263 errString: "attestation's target can't be nil", 264 }, 265 { 266 name: "nil attestation bitfield", 267 attestation: ðpb.Attestation{ 268 Data: ðpb.AttestationData{ 269 Target: ðpb.Checkpoint{}, 270 Source: ðpb.Checkpoint{}, 271 }, 272 }, 273 errString: "attestation's bitfield can't be nil", 274 }, 275 { 276 name: "good attestation", 277 attestation: ðpb.Attestation{ 278 Data: ðpb.AttestationData{ 279 Target: ðpb.Checkpoint{}, 280 Source: ðpb.Checkpoint{}, 281 }, 282 AggregationBits: []byte{}, 283 }, 284 errString: "", 285 }, 286 } 287 for _, tt := range tests { 288 t.Run(tt.name, func(t *testing.T) { 289 if tt.errString != "" { 290 require.ErrorContains(t, tt.errString, helpers.ValidateNilAttestation(tt.attestation)) 291 } else { 292 require.NoError(t, helpers.ValidateNilAttestation(tt.attestation)) 293 } 294 }) 295 } 296 } 297 298 func TestValidateSlotTargetEpoch(t *testing.T) { 299 tests := []struct { 300 name string 301 attestation *ethpb.Attestation 302 errString string 303 }{ 304 { 305 name: "incorrect slot", 306 attestation: ðpb.Attestation{ 307 Data: ðpb.AttestationData{ 308 Target: ðpb.Checkpoint{Epoch: 1}, 309 Source: ðpb.Checkpoint{}, 310 }, 311 AggregationBits: []byte{}, 312 }, 313 errString: "slot 0 does not match target epoch 1", 314 }, 315 { 316 name: "good attestation", 317 attestation: ðpb.Attestation{ 318 Data: ðpb.AttestationData{ 319 Slot: 2 * params.BeaconConfig().SlotsPerEpoch, 320 Target: ðpb.Checkpoint{Epoch: 2}, 321 Source: ðpb.Checkpoint{}, 322 }, 323 AggregationBits: []byte{}, 324 }, 325 errString: "", 326 }, 327 } 328 for _, tt := range tests { 329 t.Run(tt.name, func(t *testing.T) { 330 if tt.errString != "" { 331 require.ErrorContains(t, tt.errString, helpers.ValidateSlotTargetEpoch(tt.attestation.Data)) 332 } else { 333 require.NoError(t, helpers.ValidateSlotTargetEpoch(tt.attestation.Data)) 334 } 335 }) 336 } 337 }