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: &ethpb.AttestationData{
    28  				Target: &ethpb.Checkpoint{Root: make([]byte, 32)},
    29  				Source: &ethpb.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: &ethpb.AttestationData{
    68  				Target: &ethpb.Checkpoint{
    69  					Root: emptyRoot[:],
    70  				},
    71  				Source: &ethpb.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: &ethpb.AttestationData{
   152  				Target: &ethpb.Checkpoint{
   153  					Root: emptyRoot[:],
   154  				},
   155  				Source: &ethpb.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: &ethpb.AttestationData{
   188  				Target: &ethpb.Checkpoint{
   189  					Root: emptyRoot[:],
   190  				},
   191  				Source: &ethpb.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: &ethpb.AttestationData{
   220  				Target: &ethpb.Checkpoint{Root: make([]byte, 32)},
   221  				Source: &ethpb.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] = &ethpb.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:         &ethpb.Checkpoint{Root: make([]byte, 32)},
   294  		PreviousJustifiedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, 32)},
   295  		CurrentJustifiedCheckpoint:  &ethpb.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  }