github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/epoch/precompute/reward_penalty_test.go (about) 1 package precompute 2 3 import ( 4 "context" 5 "testing" 6 7 types "github.com/prysmaticlabs/eth2-types" 8 "github.com/prysmaticlabs/go-bitfield" 9 "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch" 10 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 11 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1" 12 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 13 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 14 "github.com/prysmaticlabs/prysm/shared/mathutil" 15 "github.com/prysmaticlabs/prysm/shared/params" 16 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 17 "github.com/prysmaticlabs/prysm/shared/testutil/require" 18 ) 19 20 func TestProcessRewardsAndPenaltiesPrecompute(t *testing.T) { 21 e := params.BeaconConfig().SlotsPerEpoch 22 validatorCount := uint64(2048) 23 base := buildState(e+3, validatorCount) 24 atts := make([]*pb.PendingAttestation, 3) 25 for i := 0; i < len(atts); i++ { 26 atts[i] = &pb.PendingAttestation{ 27 Data: ðpb.AttestationData{ 28 Target: ðpb.Checkpoint{Root: make([]byte, 32)}, 29 Source: ðpb.Checkpoint{Root: make([]byte, 32)}, 30 }, 31 AggregationBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x01}, 32 InclusionDelay: 1, 33 } 34 } 35 base.PreviousEpochAttestations = atts 36 37 beaconState, err := v1.InitializeFromProto(base) 38 require.NoError(t, err) 39 40 vp, bp, err := New(context.Background(), beaconState) 41 require.NoError(t, err) 42 vp, bp, err = ProcessAttestations(context.Background(), beaconState, vp, bp) 43 require.NoError(t, err) 44 45 processedState, err := ProcessRewardsAndPenaltiesPrecompute(beaconState, bp, vp, AttestationsDelta, ProposersDelta) 46 require.NoError(t, err) 47 beaconState, ok := processedState.(*v1.BeaconState) 48 require.Equal(t, true, ok) 49 50 // Indices that voted everything except for head, lost a bit money 51 wanted := uint64(31999810265) 52 assert.Equal(t, wanted, beaconState.Balances()[4], "Unexpected balance") 53 54 // Indices that did not vote, lost more money 55 wanted = uint64(31999873505) 56 assert.Equal(t, wanted, beaconState.Balances()[0], "Unexpected balance") 57 } 58 59 func TestAttestationDeltaPrecompute(t *testing.T) { 60 e := params.BeaconConfig().SlotsPerEpoch 61 validatorCount := uint64(2048) 62 base := buildState(e+2, validatorCount) 63 atts := make([]*pb.PendingAttestation, 3) 64 var emptyRoot [32]byte 65 for i := 0; i < len(atts); i++ { 66 atts[i] = &pb.PendingAttestation{ 67 Data: ðpb.AttestationData{ 68 Target: ðpb.Checkpoint{ 69 Root: emptyRoot[:], 70 }, 71 Source: ðpb.Checkpoint{ 72 Root: emptyRoot[:], 73 }, 74 BeaconBlockRoot: emptyRoot[:], 75 }, 76 AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x01}, 77 InclusionDelay: 1, 78 } 79 } 80 base.PreviousEpochAttestations = atts 81 beaconState, err := v1.InitializeFromProto(base) 82 require.NoError(t, err) 83 slashedAttestedIndices := []types.ValidatorIndex{1413} 84 for _, i := range slashedAttestedIndices { 85 vs := beaconState.Validators() 86 vs[i].Slashed = true 87 require.Equal(t, nil, beaconState.SetValidators(vs)) 88 } 89 90 vp, bp, err := New(context.Background(), beaconState) 91 require.NoError(t, err) 92 vp, bp, err = ProcessAttestations(context.Background(), beaconState, vp, bp) 93 require.NoError(t, err) 94 95 // Add some variances to target and head balances. 96 // See: https://github.com/prysmaticlabs/prysm/issues/5593 97 bp.PrevEpochTargetAttested /= 2 98 bp.PrevEpochHeadAttested = bp.PrevEpochHeadAttested * 2 / 3 99 rewards, penalties, err := AttestationsDelta(beaconState, bp, vp) 100 require.NoError(t, err) 101 attestedBalance, err := epoch.AttestingBalance(beaconState, atts) 102 require.NoError(t, err) 103 totalBalance, err := helpers.TotalActiveBalance(beaconState) 104 require.NoError(t, err) 105 106 attestedIndices := []types.ValidatorIndex{55, 1339, 1746, 1811, 1569} 107 for _, i := range attestedIndices { 108 base, err := epoch.BaseReward(beaconState, i) 109 require.NoError(t, err, "Could not get base reward") 110 111 // Base rewards for getting source right 112 wanted := attestedBalance*base/totalBalance + 113 bp.PrevEpochTargetAttested*base/totalBalance + 114 bp.PrevEpochHeadAttested*base/totalBalance 115 // Base rewards for proposer and attesters working together getting attestation 116 // on chain in the fatest manner 117 proposerReward := base / params.BeaconConfig().ProposerRewardQuotient 118 wanted += (base-proposerReward)*uint64(params.BeaconConfig().MinAttestationInclusionDelay) - 1 119 assert.Equal(t, wanted, rewards[i], "Unexpected reward balance for validator with index %d", i) 120 // Since all these validators attested, they shouldn't get penalized. 121 assert.Equal(t, uint64(0), penalties[i], "Unexpected penalty balance") 122 } 123 124 for _, i := range slashedAttestedIndices { 125 base, err := epoch.BaseReward(beaconState, i) 126 assert.NoError(t, err, "Could not get base reward") 127 assert.Equal(t, uint64(0), rewards[i], "Unexpected slashed indices reward balance") 128 assert.Equal(t, 3*base, penalties[i], "Unexpected slashed indices penalty balance") 129 } 130 131 nonAttestedIndices := []types.ValidatorIndex{434, 677, 872, 791} 132 for _, i := range nonAttestedIndices { 133 base, err := epoch.BaseReward(beaconState, i) 134 assert.NoError(t, err, "Could not get base reward") 135 wanted := 3 * base 136 // Since all these validators did not attest, they shouldn't get rewarded. 137 assert.Equal(t, uint64(0), rewards[i], "Unexpected reward balance") 138 // Base penalties for not attesting. 139 assert.Equal(t, wanted, penalties[i], "Unexpected penalty balance") 140 } 141 } 142 143 func TestAttestationDeltas_ZeroEpoch(t *testing.T) { 144 e := params.BeaconConfig().SlotsPerEpoch 145 validatorCount := uint64(2048) 146 base := buildState(e+2, validatorCount) 147 atts := make([]*pb.PendingAttestation, 3) 148 var emptyRoot [32]byte 149 for i := 0; i < len(atts); i++ { 150 atts[i] = &pb.PendingAttestation{ 151 Data: ðpb.AttestationData{ 152 Target: ðpb.Checkpoint{ 153 Root: emptyRoot[:], 154 }, 155 Source: ðpb.Checkpoint{ 156 Root: emptyRoot[:], 157 }, 158 BeaconBlockRoot: emptyRoot[:], 159 }, 160 AggregationBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x01}, 161 InclusionDelay: 1, 162 } 163 } 164 base.PreviousEpochAttestations = atts 165 beaconState, err := v1.InitializeFromProto(base) 166 require.NoError(t, err) 167 168 pVals, pBal, err := New(context.Background(), beaconState) 169 assert.NoError(t, err) 170 pVals, pBal, err = ProcessAttestations(context.Background(), beaconState, pVals, pBal) 171 require.NoError(t, err) 172 173 pBal.ActiveCurrentEpoch = 0 // Could cause a divide by zero panic. 174 175 _, _, err = AttestationsDelta(beaconState, pBal, pVals) 176 require.NoError(t, err) 177 } 178 179 func TestAttestationDeltas_ZeroInclusionDelay(t *testing.T) { 180 e := params.BeaconConfig().SlotsPerEpoch 181 validatorCount := uint64(2048) 182 base := buildState(e+2, validatorCount) 183 atts := make([]*pb.PendingAttestation, 3) 184 var emptyRoot [32]byte 185 for i := 0; i < len(atts); i++ { 186 atts[i] = &pb.PendingAttestation{ 187 Data: ðpb.AttestationData{ 188 Target: ðpb.Checkpoint{ 189 Root: emptyRoot[:], 190 }, 191 Source: ðpb.Checkpoint{ 192 Root: emptyRoot[:], 193 }, 194 BeaconBlockRoot: emptyRoot[:], 195 }, 196 AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01}, 197 // Inclusion delay of 0 is not possible in a valid state and could cause a divide by 198 // zero panic. 199 InclusionDelay: 0, 200 } 201 } 202 base.PreviousEpochAttestations = atts 203 beaconState, err := v1.InitializeFromProto(base) 204 require.NoError(t, err) 205 206 pVals, pBal, err := New(context.Background(), beaconState) 207 require.NoError(t, err) 208 _, _, err = ProcessAttestations(context.Background(), beaconState, pVals, pBal) 209 require.ErrorContains(t, "attestation with inclusion delay of 0", err) 210 } 211 212 func TestProcessRewardsAndPenaltiesPrecompute_SlashedInactivePenalty(t *testing.T) { 213 e := params.BeaconConfig().SlotsPerEpoch 214 validatorCount := uint64(2048) 215 base := buildState(e+3, validatorCount) 216 atts := make([]*pb.PendingAttestation, 3) 217 for i := 0; i < len(atts); i++ { 218 atts[i] = &pb.PendingAttestation{ 219 Data: ðpb.AttestationData{ 220 Target: ðpb.Checkpoint{Root: make([]byte, 32)}, 221 Source: ðpb.Checkpoint{Root: make([]byte, 32)}, 222 }, 223 AggregationBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x01}, 224 InclusionDelay: 1, 225 } 226 } 227 base.PreviousEpochAttestations = atts 228 229 beaconState, err := v1.InitializeFromProto(base) 230 require.NoError(t, err) 231 require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch*10)) 232 233 slashedAttestedIndices := []types.ValidatorIndex{14, 37, 68, 77, 139} 234 for _, i := range slashedAttestedIndices { 235 vs := beaconState.Validators() 236 vs[i].Slashed = true 237 require.NoError(t, beaconState.SetValidators(vs)) 238 } 239 240 vp, bp, err := New(context.Background(), beaconState) 241 require.NoError(t, err) 242 vp, bp, err = ProcessAttestations(context.Background(), beaconState, vp, bp) 243 require.NoError(t, err) 244 rewards, penalties, err := AttestationsDelta(beaconState, bp, vp) 245 require.NoError(t, err) 246 247 finalityDelay := helpers.PrevEpoch(beaconState) - beaconState.FinalizedCheckpointEpoch() 248 for _, i := range slashedAttestedIndices { 249 base, err := epoch.BaseReward(beaconState, i) 250 require.NoError(t, err, "Could not get base reward") 251 penalty := 3 * base 252 proposerReward := base / params.BeaconConfig().ProposerRewardQuotient 253 penalty += params.BeaconConfig().BaseRewardsPerEpoch*base - proposerReward 254 penalty += vp[i].CurrentEpochEffectiveBalance * uint64(finalityDelay) / params.BeaconConfig().InactivityPenaltyQuotient 255 assert.Equal(t, penalty, penalties[i], "Unexpected slashed indices penalty balance") 256 assert.Equal(t, uint64(0), rewards[i], "Unexpected slashed indices reward balance") 257 } 258 } 259 260 func buildState(slot types.Slot, validatorCount uint64) *pb.BeaconState { 261 validators := make([]*ethpb.Validator, validatorCount) 262 for i := 0; i < len(validators); i++ { 263 validators[i] = ðpb.Validator{ 264 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 265 EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, 266 } 267 } 268 validatorBalances := make([]uint64, len(validators)) 269 for i := 0; i < len(validatorBalances); i++ { 270 validatorBalances[i] = params.BeaconConfig().MaxEffectiveBalance 271 } 272 latestActiveIndexRoots := make( 273 [][]byte, 274 params.BeaconConfig().EpochsPerHistoricalVector, 275 ) 276 for i := 0; i < len(latestActiveIndexRoots); i++ { 277 latestActiveIndexRoots[i] = params.BeaconConfig().ZeroHash[:] 278 } 279 latestRandaoMixes := make( 280 [][]byte, 281 params.BeaconConfig().EpochsPerHistoricalVector, 282 ) 283 for i := 0; i < len(latestRandaoMixes); i++ { 284 latestRandaoMixes[i] = params.BeaconConfig().ZeroHash[:] 285 } 286 return &pb.BeaconState{ 287 Slot: slot, 288 Balances: validatorBalances, 289 Validators: validators, 290 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), 291 Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector), 292 BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerEpoch*10), 293 FinalizedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}, 294 PreviousJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}, 295 CurrentJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}, 296 } 297 } 298 299 func TestProposerDeltaPrecompute_HappyCase(t *testing.T) { 300 e := params.BeaconConfig().SlotsPerEpoch 301 validatorCount := uint64(10) 302 base := buildState(e, validatorCount) 303 beaconState, err := v1.InitializeFromProto(base) 304 require.NoError(t, err) 305 306 proposerIndex := types.ValidatorIndex(1) 307 b := &Balance{ActiveCurrentEpoch: 1000} 308 v := []*Validator{ 309 {IsPrevEpochAttester: true, CurrentEpochEffectiveBalance: 32, ProposerIndex: proposerIndex}, 310 } 311 r, err := ProposersDelta(beaconState, b, v) 312 require.NoError(t, err) 313 314 baseReward := v[0].CurrentEpochEffectiveBalance * params.BeaconConfig().BaseRewardFactor / 315 mathutil.IntegerSquareRoot(b.ActiveCurrentEpoch) / params.BeaconConfig().BaseRewardsPerEpoch 316 proposerReward := baseReward / params.BeaconConfig().ProposerRewardQuotient 317 318 assert.Equal(t, proposerReward, r[proposerIndex], "Unexpected proposer reward") 319 } 320 321 func TestProposerDeltaPrecompute_ValidatorIndexOutOfRange(t *testing.T) { 322 e := params.BeaconConfig().SlotsPerEpoch 323 validatorCount := uint64(10) 324 base := buildState(e, validatorCount) 325 beaconState, err := v1.InitializeFromProto(base) 326 require.NoError(t, err) 327 328 proposerIndex := types.ValidatorIndex(validatorCount) 329 b := &Balance{ActiveCurrentEpoch: 1000} 330 v := []*Validator{ 331 {IsPrevEpochAttester: true, CurrentEpochEffectiveBalance: 32, ProposerIndex: proposerIndex}, 332 } 333 _, err = ProposersDelta(beaconState, b, v) 334 assert.ErrorContains(t, "proposer index out of range", err) 335 } 336 337 func TestProposerDeltaPrecompute_SlashedCase(t *testing.T) { 338 e := params.BeaconConfig().SlotsPerEpoch 339 validatorCount := uint64(10) 340 base := buildState(e, validatorCount) 341 beaconState, err := v1.InitializeFromProto(base) 342 require.NoError(t, err) 343 344 proposerIndex := types.ValidatorIndex(1) 345 b := &Balance{ActiveCurrentEpoch: 1000} 346 v := []*Validator{ 347 {IsPrevEpochAttester: true, CurrentEpochEffectiveBalance: 32, ProposerIndex: proposerIndex, IsSlashed: true}, 348 } 349 r, err := ProposersDelta(beaconState, b, v) 350 require.NoError(t, err) 351 assert.Equal(t, uint64(0), r[proposerIndex], "Unexpected proposer reward for slashed") 352 }