github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/blockchain/process_attestation_test.go (about) 1 package blockchain 2 3 import ( 4 "context" 5 "testing" 6 7 types "github.com/prysmaticlabs/eth2-types" 8 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 9 "github.com/prysmaticlabs/prysm/beacon-chain/core/state" 10 testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" 11 "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray" 12 "github.com/prysmaticlabs/prysm/beacon-chain/state/stategen" 13 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 14 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 15 "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper" 16 "github.com/prysmaticlabs/prysm/shared/bytesutil" 17 "github.com/prysmaticlabs/prysm/shared/params" 18 "github.com/prysmaticlabs/prysm/shared/testutil" 19 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 20 "github.com/prysmaticlabs/prysm/shared/testutil/require" 21 "github.com/prysmaticlabs/prysm/shared/timeutils" 22 ) 23 24 func TestStore_OnAttestation_ErrorConditions(t *testing.T) { 25 ctx := context.Background() 26 beaconDB := testDB.SetupDB(t) 27 28 cfg := &Config{ 29 BeaconDB: beaconDB, 30 ForkChoiceStore: protoarray.New(0, 0, [32]byte{}), 31 StateGen: stategen.New(beaconDB), 32 } 33 service, err := NewService(ctx, cfg) 34 require.NoError(t, err) 35 36 _, err = blockTree1(t, beaconDB, []byte{'g'}) 37 require.NoError(t, err) 38 39 BlkWithOutState := testutil.NewBeaconBlock() 40 BlkWithOutState.Block.Slot = 0 41 require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(BlkWithOutState))) 42 BlkWithOutStateRoot, err := BlkWithOutState.Block.HashTreeRoot() 43 require.NoError(t, err) 44 45 BlkWithStateBadAtt := testutil.NewBeaconBlock() 46 BlkWithStateBadAtt.Block.Slot = 1 47 require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(BlkWithStateBadAtt))) 48 BlkWithStateBadAttRoot, err := BlkWithStateBadAtt.Block.HashTreeRoot() 49 require.NoError(t, err) 50 51 s, err := testutil.NewBeaconState() 52 require.NoError(t, err) 53 require.NoError(t, s.SetSlot(100*params.BeaconConfig().SlotsPerEpoch)) 54 require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, s, BlkWithStateBadAttRoot)) 55 56 BlkWithValidState := testutil.NewBeaconBlock() 57 BlkWithValidState.Block.Slot = 2 58 require.NoError(t, beaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(BlkWithValidState))) 59 60 BlkWithValidStateRoot, err := BlkWithValidState.Block.HashTreeRoot() 61 require.NoError(t, err) 62 s, err = testutil.NewBeaconState() 63 require.NoError(t, err) 64 err = s.SetFork(&pb.Fork{ 65 Epoch: 0, 66 CurrentVersion: params.BeaconConfig().GenesisForkVersion, 67 PreviousVersion: params.BeaconConfig().GenesisForkVersion, 68 }) 69 require.NoError(t, err) 70 require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, s, BlkWithValidStateRoot)) 71 72 tests := []struct { 73 name string 74 a *ethpb.Attestation 75 wantedErr string 76 }{ 77 { 78 name: "attestation's data slot not aligned with target vote", 79 a: testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: params.BeaconConfig().SlotsPerEpoch, Target: ðpb.Checkpoint{Root: make([]byte, 32)}}}), 80 wantedErr: "slot 32 does not match target epoch 0", 81 }, 82 { 83 name: "no pre state for attestations's target block", 84 a: testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{Root: BlkWithOutStateRoot[:]}}}), 85 wantedErr: "could not get pre state for epoch 0", 86 }, 87 { 88 name: "process attestation doesn't match current epoch", 89 a: testutil.HydrateAttestation(ðpb.Attestation{Data: ðpb.AttestationData{Slot: 100 * params.BeaconConfig().SlotsPerEpoch, Target: ðpb.Checkpoint{Epoch: 100, 90 Root: BlkWithStateBadAttRoot[:]}}}), 91 wantedErr: "target epoch 100 does not match current epoch", 92 }, 93 { 94 name: "process nil attestation", 95 a: nil, 96 wantedErr: "attestation can't be nil", 97 }, 98 { 99 name: "process nil field (a.Data) in attestation", 100 a: ðpb.Attestation{}, 101 wantedErr: "attestation's data can't be nil", 102 }, 103 { 104 name: "process nil field (a.Target) in attestation", 105 a: ðpb.Attestation{ 106 Data: ðpb.AttestationData{ 107 BeaconBlockRoot: make([]byte, 32), 108 Target: nil, 109 Source: ðpb.Checkpoint{Root: make([]byte, 32)}, 110 }, 111 AggregationBits: make([]byte, 1), 112 Signature: make([]byte, 96), 113 }, 114 wantedErr: "attestation's target can't be nil", 115 }, 116 } 117 118 for _, tt := range tests { 119 t.Run(tt.name, func(t *testing.T) { 120 err := service.onAttestation(ctx, tt.a) 121 if tt.wantedErr != "" { 122 assert.ErrorContains(t, tt.wantedErr, err) 123 } else { 124 assert.NoError(t, err) 125 } 126 }) 127 } 128 } 129 130 func TestStore_OnAttestation_Ok(t *testing.T) { 131 ctx := context.Background() 132 beaconDB := testDB.SetupDB(t) 133 134 cfg := &Config{ 135 BeaconDB: beaconDB, 136 ForkChoiceStore: protoarray.New(0, 0, [32]byte{}), 137 StateGen: stategen.New(beaconDB), 138 } 139 service, err := NewService(ctx, cfg) 140 require.NoError(t, err) 141 genesisState, pks := testutil.DeterministicGenesisState(t, 64) 142 require.NoError(t, genesisState.SetGenesisTime(uint64(timeutils.Now().Unix())-params.BeaconConfig().SecondsPerSlot)) 143 require.NoError(t, service.saveGenesisData(ctx, genesisState)) 144 att, err := testutil.GenerateAttestations(genesisState, pks, 1, 0, false) 145 require.NoError(t, err) 146 tRoot := bytesutil.ToBytes32(att[0].Data.Target.Root) 147 copied := genesisState.Copy() 148 copied, err = state.ProcessSlots(ctx, copied, 1) 149 require.NoError(t, err) 150 require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, copied, tRoot)) 151 require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(ctx, 0, tRoot, tRoot, tRoot, 1, 1)) 152 require.NoError(t, service.onAttestation(ctx, att[0])) 153 } 154 155 func TestStore_SaveCheckpointState(t *testing.T) { 156 ctx := context.Background() 157 beaconDB := testDB.SetupDB(t) 158 159 cfg := &Config{ 160 BeaconDB: beaconDB, 161 StateGen: stategen.New(beaconDB), 162 } 163 service, err := NewService(ctx, cfg) 164 require.NoError(t, err) 165 166 s, err := testutil.NewBeaconState() 167 require.NoError(t, err) 168 err = s.SetFinalizedCheckpoint(ðpb.Checkpoint{Root: bytesutil.PadTo([]byte{'A'}, 32)}) 169 require.NoError(t, err) 170 val := ðpb.Validator{ 171 PublicKey: bytesutil.PadTo([]byte("foo"), 48), 172 WithdrawalCredentials: bytesutil.PadTo([]byte("bar"), 32), 173 } 174 err = s.SetValidators([]*ethpb.Validator{val}) 175 require.NoError(t, err) 176 err = s.SetBalances([]uint64{0}) 177 require.NoError(t, err) 178 r := [32]byte{'g'} 179 require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, s, r)) 180 181 service.justifiedCheckpt = ðpb.Checkpoint{Root: r[:]} 182 service.bestJustifiedCheckpt = ðpb.Checkpoint{Root: r[:]} 183 service.finalizedCheckpt = ðpb.Checkpoint{Root: r[:]} 184 service.prevFinalizedCheckpt = ðpb.Checkpoint{Root: r[:]} 185 186 r = bytesutil.ToBytes32([]byte{'A'}) 187 cp1 := ðpb.Checkpoint{Epoch: 1, Root: bytesutil.PadTo([]byte{'A'}, 32)} 188 require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, s, bytesutil.ToBytes32([]byte{'A'}))) 189 require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, &pb.StateSummary{Root: bytesutil.PadTo([]byte{'A'}, 32)})) 190 191 s1, err := service.getAttPreState(ctx, cp1) 192 require.NoError(t, err) 193 assert.Equal(t, 1*params.BeaconConfig().SlotsPerEpoch, s1.Slot(), "Unexpected state slot") 194 195 cp2 := ðpb.Checkpoint{Epoch: 2, Root: bytesutil.PadTo([]byte{'B'}, 32)} 196 require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, s, bytesutil.ToBytes32([]byte{'B'}))) 197 require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, &pb.StateSummary{Root: bytesutil.PadTo([]byte{'B'}, 32)})) 198 s2, err := service.getAttPreState(ctx, cp2) 199 require.NoError(t, err) 200 assert.Equal(t, 2*params.BeaconConfig().SlotsPerEpoch, s2.Slot(), "Unexpected state slot") 201 202 s1, err = service.getAttPreState(ctx, cp1) 203 require.NoError(t, err) 204 assert.Equal(t, 1*params.BeaconConfig().SlotsPerEpoch, s1.Slot(), "Unexpected state slot") 205 206 s1, err = service.checkpointStateCache.StateByCheckpoint(cp1) 207 require.NoError(t, err) 208 assert.Equal(t, 1*params.BeaconConfig().SlotsPerEpoch, s1.Slot(), "Unexpected state slot") 209 210 s2, err = service.checkpointStateCache.StateByCheckpoint(cp2) 211 require.NoError(t, err) 212 assert.Equal(t, 2*params.BeaconConfig().SlotsPerEpoch, s2.Slot(), "Unexpected state slot") 213 214 require.NoError(t, s.SetSlot(params.BeaconConfig().SlotsPerEpoch+1)) 215 service.justifiedCheckpt = ðpb.Checkpoint{Root: r[:]} 216 service.bestJustifiedCheckpt = ðpb.Checkpoint{Root: r[:]} 217 service.finalizedCheckpt = ðpb.Checkpoint{Root: r[:]} 218 service.prevFinalizedCheckpt = ðpb.Checkpoint{Root: r[:]} 219 cp3 := ðpb.Checkpoint{Epoch: 1, Root: bytesutil.PadTo([]byte{'C'}, 32)} 220 require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, s, bytesutil.ToBytes32([]byte{'C'}))) 221 require.NoError(t, service.cfg.BeaconDB.SaveStateSummary(ctx, &pb.StateSummary{Root: bytesutil.PadTo([]byte{'C'}, 32)})) 222 s3, err := service.getAttPreState(ctx, cp3) 223 require.NoError(t, err) 224 assert.Equal(t, s.Slot(), s3.Slot(), "Unexpected state slot") 225 } 226 227 func TestStore_UpdateCheckpointState(t *testing.T) { 228 ctx := context.Background() 229 beaconDB := testDB.SetupDB(t) 230 231 cfg := &Config{ 232 BeaconDB: beaconDB, 233 StateGen: stategen.New(beaconDB), 234 } 235 service, err := NewService(ctx, cfg) 236 require.NoError(t, err) 237 238 epoch := types.Epoch(1) 239 baseState, _ := testutil.DeterministicGenesisState(t, 1) 240 checkpoint := ðpb.Checkpoint{Epoch: epoch, Root: bytesutil.PadTo([]byte("hi"), 32)} 241 require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, baseState, bytesutil.ToBytes32(checkpoint.Root))) 242 returned, err := service.getAttPreState(ctx, checkpoint) 243 require.NoError(t, err) 244 assert.Equal(t, params.BeaconConfig().SlotsPerEpoch.Mul(uint64(checkpoint.Epoch)), returned.Slot(), "Incorrectly returned base state") 245 246 cached, err := service.checkpointStateCache.StateByCheckpoint(checkpoint) 247 require.NoError(t, err) 248 assert.Equal(t, returned.Slot(), cached.Slot(), "State should have been cached") 249 250 epoch = 2 251 newCheckpoint := ðpb.Checkpoint{Epoch: epoch, Root: bytesutil.PadTo([]byte("bye"), 32)} 252 require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, baseState, bytesutil.ToBytes32(newCheckpoint.Root))) 253 returned, err = service.getAttPreState(ctx, newCheckpoint) 254 require.NoError(t, err) 255 s, err := helpers.StartSlot(newCheckpoint.Epoch) 256 require.NoError(t, err) 257 baseState, err = state.ProcessSlots(ctx, baseState, s) 258 require.NoError(t, err) 259 assert.Equal(t, returned.Slot(), baseState.Slot(), "Incorrectly returned base state") 260 261 cached, err = service.checkpointStateCache.StateByCheckpoint(newCheckpoint) 262 require.NoError(t, err) 263 require.DeepSSZEqual(t, returned.InnerStateUnsafe(), cached.InnerStateUnsafe()) 264 } 265 266 func TestAttEpoch_MatchPrevEpoch(t *testing.T) { 267 ctx := context.Background() 268 beaconDB := testDB.SetupDB(t) 269 270 cfg := &Config{BeaconDB: beaconDB} 271 service, err := NewService(ctx, cfg) 272 require.NoError(t, err) 273 274 nowTime := uint64(params.BeaconConfig().SlotsPerEpoch) * params.BeaconConfig().SecondsPerSlot 275 require.NoError(t, service.verifyAttTargetEpoch(ctx, 0, nowTime, ðpb.Checkpoint{Root: make([]byte, 32)})) 276 } 277 278 func TestAttEpoch_MatchCurrentEpoch(t *testing.T) { 279 ctx := context.Background() 280 beaconDB := testDB.SetupDB(t) 281 282 cfg := &Config{BeaconDB: beaconDB} 283 service, err := NewService(ctx, cfg) 284 require.NoError(t, err) 285 286 nowTime := uint64(params.BeaconConfig().SlotsPerEpoch) * params.BeaconConfig().SecondsPerSlot 287 require.NoError(t, service.verifyAttTargetEpoch(ctx, 0, nowTime, ðpb.Checkpoint{Epoch: 1})) 288 } 289 290 func TestAttEpoch_NotMatch(t *testing.T) { 291 ctx := context.Background() 292 beaconDB := testDB.SetupDB(t) 293 294 cfg := &Config{BeaconDB: beaconDB} 295 service, err := NewService(ctx, cfg) 296 require.NoError(t, err) 297 298 nowTime := 2 * uint64(params.BeaconConfig().SlotsPerEpoch) * params.BeaconConfig().SecondsPerSlot 299 err = service.verifyAttTargetEpoch(ctx, 0, nowTime, ðpb.Checkpoint{Root: make([]byte, 32)}) 300 assert.ErrorContains(t, "target epoch 0 does not match current epoch 2 or prev epoch 1", err) 301 } 302 303 func TestVerifyBeaconBlock_NoBlock(t *testing.T) { 304 ctx := context.Background() 305 beaconDB := testDB.SetupDB(t) 306 307 cfg := &Config{BeaconDB: beaconDB} 308 service, err := NewService(ctx, cfg) 309 require.NoError(t, err) 310 311 d := testutil.HydrateAttestationData(ðpb.AttestationData{}) 312 assert.ErrorContains(t, "signed beacon block can't be nil", service.verifyBeaconBlock(ctx, d)) 313 } 314 315 func TestVerifyBeaconBlock_futureBlock(t *testing.T) { 316 ctx := context.Background() 317 beaconDB := testDB.SetupDB(t) 318 319 cfg := &Config{BeaconDB: beaconDB} 320 service, err := NewService(ctx, cfg) 321 require.NoError(t, err) 322 323 b := testutil.NewBeaconBlock() 324 b.Block.Slot = 2 325 require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b))) 326 r, err := b.Block.HashTreeRoot() 327 require.NoError(t, err) 328 d := ðpb.AttestationData{Slot: 1, BeaconBlockRoot: r[:]} 329 330 assert.ErrorContains(t, "could not process attestation for future block", service.verifyBeaconBlock(ctx, d)) 331 } 332 333 func TestVerifyBeaconBlock_OK(t *testing.T) { 334 ctx := context.Background() 335 beaconDB := testDB.SetupDB(t) 336 337 cfg := &Config{BeaconDB: beaconDB} 338 service, err := NewService(ctx, cfg) 339 require.NoError(t, err) 340 341 b := testutil.NewBeaconBlock() 342 b.Block.Slot = 2 343 require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b))) 344 r, err := b.Block.HashTreeRoot() 345 require.NoError(t, err) 346 d := ðpb.AttestationData{Slot: 2, BeaconBlockRoot: r[:]} 347 348 assert.NoError(t, service.verifyBeaconBlock(ctx, d), "Did not receive the wanted error") 349 } 350 351 func TestVerifyFinalizedConsistency_InconsistentRoot(t *testing.T) { 352 ctx := context.Background() 353 beaconDB := testDB.SetupDB(t) 354 355 cfg := &Config{BeaconDB: beaconDB, ForkChoiceStore: protoarray.New(0, 0, [32]byte{})} 356 service, err := NewService(ctx, cfg) 357 require.NoError(t, err) 358 359 b32 := testutil.NewBeaconBlock() 360 b32.Block.Slot = 32 361 require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b32))) 362 r32, err := b32.Block.HashTreeRoot() 363 require.NoError(t, err) 364 365 service.finalizedCheckpt = ðpb.Checkpoint{Epoch: 1} 366 367 b33 := testutil.NewBeaconBlock() 368 b33.Block.Slot = 33 369 b33.Block.ParentRoot = r32[:] 370 require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b33))) 371 r33, err := b33.Block.HashTreeRoot() 372 require.NoError(t, err) 373 374 err = service.VerifyFinalizedConsistency(context.Background(), r33[:]) 375 require.ErrorContains(t, "Root and finalized store are not consistent", err) 376 } 377 378 func TestVerifyFinalizedConsistency_OK(t *testing.T) { 379 ctx := context.Background() 380 beaconDB := testDB.SetupDB(t) 381 382 cfg := &Config{BeaconDB: beaconDB, ForkChoiceStore: protoarray.New(0, 0, [32]byte{})} 383 service, err := NewService(ctx, cfg) 384 require.NoError(t, err) 385 386 b32 := testutil.NewBeaconBlock() 387 b32.Block.Slot = 32 388 require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b32))) 389 r32, err := b32.Block.HashTreeRoot() 390 require.NoError(t, err) 391 392 service.finalizedCheckpt = ðpb.Checkpoint{Epoch: 1, Root: r32[:]} 393 394 b33 := testutil.NewBeaconBlock() 395 b33.Block.Slot = 33 396 b33.Block.ParentRoot = r32[:] 397 require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(b33))) 398 r33, err := b33.Block.HashTreeRoot() 399 require.NoError(t, err) 400 401 err = service.VerifyFinalizedConsistency(context.Background(), r33[:]) 402 require.NoError(t, err) 403 } 404 405 func TestVerifyFinalizedConsistency_IsCanonical(t *testing.T) { 406 ctx := context.Background() 407 beaconDB := testDB.SetupDB(t) 408 409 cfg := &Config{BeaconDB: beaconDB, ForkChoiceStore: protoarray.New(0, 0, [32]byte{})} 410 service, err := NewService(ctx, cfg) 411 require.NoError(t, err) 412 413 b32 := testutil.NewBeaconBlock() 414 b32.Block.Slot = 32 415 r32, err := b32.Block.HashTreeRoot() 416 require.NoError(t, err) 417 418 service.finalizedCheckpt = ðpb.Checkpoint{Epoch: 1, Root: r32[:]} 419 420 b33 := testutil.NewBeaconBlock() 421 b33.Block.Slot = 33 422 b33.Block.ParentRoot = r32[:] 423 r33, err := b33.Block.HashTreeRoot() 424 require.NoError(t, err) 425 426 require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(ctx, b32.Block.Slot, r32, [32]byte{}, [32]byte{}, 0, 0)) 427 require.NoError(t, service.cfg.ForkChoiceStore.ProcessBlock(ctx, b33.Block.Slot, r33, r32, [32]byte{}, 0, 0)) 428 429 _, err = service.cfg.ForkChoiceStore.Head(ctx, 0, r32, []uint64{}, 0) 430 require.NoError(t, err) 431 err = service.VerifyFinalizedConsistency(context.Background(), r33[:]) 432 require.NoError(t, err) 433 }