diff --git a/beacon-chain/core/epoch/epoch_processing.go b/beacon-chain/core/epoch/epoch_processing.go index bddf0d163..1317a6009 100644 --- a/beacon-chain/core/epoch/epoch_processing.go +++ b/beacon-chain/core/epoch/epoch_processing.go @@ -111,145 +111,6 @@ func AttestingBalance(state *pb.BeaconState, atts []*pb.PendingAttestation) (uin return helpers.TotalBalance(state, indices), nil } -// ProcessJustificationAndFinalization processes justification and finalization during -// epoch processing. This is where a beacon node can justify and finalize a new epoch. -// -// Spec pseudocode definition: -// def process_justification_and_finalization(state: BeaconState) -> None: -// if get_current_epoch(state) <= GENESIS_EPOCH + 1: -// return -// -// previous_epoch = get_previous_epoch(state) -// current_epoch = get_current_epoch(state) -// old_previous_justified_checkpoint = state.previous_justified_checkpoint -// old_current_justified_checkpoint = state.current_justified_checkpoint -// -// # Process justifications -// state.previous_justified_checkpoint = state.current_justified_checkpoint -// state.justification_bits[1:] = state.justification_bits[:-1] -// state.justification_bits[0] = 0b0 -// matching_target_attestations = get_matching_target_attestations(state, previous_epoch) # Previous epoch -// if get_attesting_balance(state, matching_target_attestations) * 3 >= get_total_active_balance(state) * 2: -// state.current_justified_checkpoint = Checkpoint(epoch=previous_epoch, -// root=get_block_root(state, previous_epoch)) -// state.justification_bits[1] = 0b1 -// matching_target_attestations = get_matching_target_attestations(state, current_epoch) # Current epoch -// if get_attesting_balance(state, matching_target_attestations) * 3 >= get_total_active_balance(state) * 2: -// state.current_justified_checkpoint = Checkpoint(epoch=current_epoch, -// root=get_block_root(state, current_epoch)) -// state.justification_bits[0] = 0b1 -// -// # Process finalizations -// bits = state.justification_bits -// # The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source -// if all(bits[1:4]) and old_previous_justified_checkpoint.epoch + 3 == current_epoch: -// state.finalized_checkpoint = old_previous_justified_checkpoint -// # The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source -// if all(bits[1:3]) and old_previous_justified_checkpoint.epoch + 2 == current_epoch: -// state.finalized_checkpoint = old_previous_justified_checkpoint -// # The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source -// if all(bits[0:3]) and old_current_justified_checkpoint.epoch + 2 == current_epoch: -// state.finalized_checkpoint = old_current_justified_checkpoint -// # The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source -// if all(bits[0:2]) and old_current_justified_checkpoint.epoch + 1 == current_epoch: -// state.finalized_checkpoint = old_current_justified_checkpoint -func ProcessJustificationAndFinalization(state *pb.BeaconState, prevAttestedBal uint64, currAttestedBal uint64) (*pb.BeaconState, error) { - if state.Slot <= helpers.StartSlot(2) { - return state, nil - } - - prevEpoch := helpers.PrevEpoch(state) - currentEpoch := helpers.CurrentEpoch(state) - oldPrevJustifiedCheckpoint := state.PreviousJustifiedCheckpoint - oldCurrJustifiedCheckpoint := state.CurrentJustifiedCheckpoint - - totalBal, err := helpers.TotalActiveBalance(state) - if err != nil { - return nil, errors.Wrap(err, "could not get total balance") - } - - // Process justifications - state.PreviousJustifiedCheckpoint = state.CurrentJustifiedCheckpoint - state.JustificationBits.Shift(1) - - // Note: the spec refers to the bit index position starting at 1 instead of starting at zero. - // We will use that paradigm here for consistency with the godoc spec definition. - - // If 2/3 or more of total balance attested in the previous epoch. - if 3*prevAttestedBal >= 2*totalBal { - blockRoot, err := helpers.BlockRoot(state, prevEpoch) - if err != nil { - return nil, errors.Wrapf(err, "could not get block root for previous epoch %d", prevEpoch) - } - state.CurrentJustifiedCheckpoint = ðpb.Checkpoint{Epoch: prevEpoch, Root: blockRoot} - state.JustificationBits.SetBitAt(1, true) - } - - // If 2/3 or more of the total balance attested in the current epoch. - if 3*currAttestedBal >= 2*totalBal { - blockRoot, err := helpers.BlockRoot(state, currentEpoch) - if err != nil { - return nil, errors.Wrapf(err, "could not get block root for current epoch %d", prevEpoch) - } - state.CurrentJustifiedCheckpoint = ðpb.Checkpoint{Epoch: currentEpoch, Root: blockRoot} - state.JustificationBits.SetBitAt(0, true) - } - - // Process finalization according to ETH2.0 specifications. - justification := state.JustificationBits.Bytes()[0] - - // 2nd/3rd/4th (0b1110) most recent epochs are justified, the 2nd using the 4th as source. - if justification&0x0E == 0x0E && (oldPrevJustifiedCheckpoint.Epoch+3) == currentEpoch { - state.FinalizedCheckpoint = oldPrevJustifiedCheckpoint - } - - // 2nd/3rd (0b0110) most recent epochs are justified, the 2nd using the 3rd as source. - if justification&0x06 == 0x06 && (oldPrevJustifiedCheckpoint.Epoch+2) == currentEpoch { - state.FinalizedCheckpoint = oldPrevJustifiedCheckpoint - } - - // 1st/2nd/3rd (0b0111) most recent epochs are justified, the 1st using the 3rd as source. - if justification&0x07 == 0x07 && (oldCurrJustifiedCheckpoint.Epoch+2) == currentEpoch { - state.FinalizedCheckpoint = oldCurrJustifiedCheckpoint - } - - // The 1st/2nd (0b0011) most recent epochs are justified, the 1st using the 2nd as source - if justification&0x03 == 0x03 && (oldCurrJustifiedCheckpoint.Epoch+1) == currentEpoch { - state.FinalizedCheckpoint = oldCurrJustifiedCheckpoint - } - - return state, nil -} - -// ProcessRewardsAndPenalties processes the rewards and penalties of individual validator. -// -// Spec pseudocode definition: -// def process_rewards_and_penalties(state: BeaconState) -> None: -// if get_current_epoch(state) == GENESIS_EPOCH: -// return -// -// rewards1, penalties1 = get_attestation_deltas(state) -// rewards2, penalties2 = get_crosslink_deltas(state) -// for i in range(len(state.validator_registry)): -// increase_balance(state, i, rewards1[i] + rewards2[i]) -// decrease_balance(state, i, penalties1[i] + penalties2[i]) -func ProcessRewardsAndPenalties(state *pb.BeaconState) (*pb.BeaconState, error) { - // Can't process rewards and penalties in genesis epoch. - if helpers.CurrentEpoch(state) == 0 { - return state, nil - } - attsRewards, attsPenalties, err := attestationDelta(state) - if err != nil { - return nil, errors.Wrap(err, "could not get attestation delta") - } - - for i := 0; i < len(state.Validators); i++ { - state = helpers.IncreaseBalance(state, uint64(i), attsRewards[i]) - state = helpers.DecreaseBalance(state, uint64(i), attsPenalties[i]) - } - return state, nil -} - // ProcessRegistryUpdates rotates validators in and out of active pool. // the amount to rotate is determined churn limit. // @@ -526,183 +387,3 @@ func BaseReward(state *pb.BeaconState, index uint64) (uint64, error) { mathutil.IntegerSquareRoot(totalBalance) / params.BeaconConfig().BaseRewardsPerEpoch return baseReward, nil } - -// attestationDelta calculates the rewards and penalties of individual -// validator for voting the correct FFG source, FFG target, and head. It -// also calculates proposer delay inclusion and inactivity rewards -// and penalties. Individual rewards and penalties are returned in list. -// -// Note: we calculated adjusted quotient outside of base reward because it's too inefficient -// to repeat the same calculation for every validator versus just doing it once. -// -// Spec pseudocode definition: -// def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]: -// previous_epoch = get_previous_epoch(state) -// total_balance = get_total_active_balance(state) -// rewards = [Gwei(0) for _ in range(len(state.validators))] -// penalties = [Gwei(0) for _ in range(len(state.validators))] -// eligible_validator_indices = [ -// ValidatorIndex(index) for index, v in enumerate(state.validators) -// if is_active_validator(v, previous_epoch) or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch) -// ] -// -// # Micro-incentives for matching FFG source, FFG target, and head -// matching_source_attestations = get_matching_source_attestations(state, previous_epoch) -// matching_target_attestations = get_matching_target_attestations(state, previous_epoch) -// matching_head_attestations = get_matching_head_attestations(state, previous_epoch) -// for attestations in (matching_source_attestations, matching_target_attestations, matching_head_attestations): -// unslashed_attesting_indices = get_unslashed_attesting_indices(state, attestations) -// attesting_balance = get_total_balance(state, unslashed_attesting_indices) -// for index in eligible_validator_indices: -// if index in unslashed_attesting_indices: -// rewards[index] += get_base_reward(state, index) * attesting_balance // total_balance -// else: -// penalties[index] += get_base_reward(state, index) -// -// # Proposer and inclusion delay micro-rewards -// for index in get_unslashed_attesting_indices(state, matching_source_attestations): -// index = ValidatorIndex(index) -// attestation = min([ -// a for a in matching_source_attestations -// if index in get_attesting_indices(state, a.data, a.aggregation_bits) -// ], key=lambda a: a.inclusion_delay) -// proposer_reward = Gwei(get_base_reward(state, index) // PROPOSER_REWARD_QUOTIENT) -// rewards[attestation.proposer_index] += proposer_reward -// max_attester_reward = get_base_reward(state, index) - proposer_reward -// rewards[index] += Gwei(max_attester_reward // attestation.inclusion_delay) -// -// # Inactivity penalty -// finality_delay = previous_epoch - state.finalized_checkpoint.epoch -// if finality_delay > MIN_EPOCHS_TO_INACTIVITY_PENALTY: -// matching_target_attesting_indices = get_unslashed_attesting_indices(state, matching_target_attestations) -// for index in eligible_validator_indices: -// index = ValidatorIndex(index) -// penalties[index] += Gwei(BASE_REWARDS_PER_EPOCH * get_base_reward(state, index)) -// if index not in matching_target_attesting_indices: -// penalties[index] += Gwei( -// state.validators[index].effective_balance * finality_delay // INACTIVITY_PENALTY_QUOTIENT -// ) -// -// return rewards, penalties -func attestationDelta(state *pb.BeaconState) ([]uint64, []uint64, error) { - prevEpoch := helpers.PrevEpoch(state) - totalBalance, err := helpers.TotalActiveBalance(state) - if err != nil { - return nil, nil, errors.Wrap(err, "could not get total active balance") - } - - rewards := make([]uint64, len(state.Validators)) - penalties := make([]uint64, len(state.Validators)) - - // Filter out the list of eligible validator indices. The eligible validator - // has to be active or slashed but before withdrawn. - var eligible []uint64 - for i, v := range state.Validators { - isActive := helpers.IsActiveValidator(v, prevEpoch) - isSlashed := v.Slashed && (prevEpoch+1 < v.WithdrawableEpoch) - if isActive || isSlashed { - eligible = append(eligible, uint64(i)) - } - } - - // Apply rewards and penalties for voting correct source target and head. - // Construct a attestations list contains source, target and head attestations. - atts, err := MatchAttestations(state, prevEpoch) - if err != nil { - return nil, nil, errors.Wrap(err, "could not get source, target and head attestations") - } - var attsPackage [][]*pb.PendingAttestation - attsPackage = append(attsPackage, atts.source) - attsPackage = append(attsPackage, atts.Target) - attsPackage = append(attsPackage, atts.head) - - // Cache the validators who voted correctly for source in a map - // to calculate earliest attestation rewards later. - attestersVotedSource := make(map[uint64]*pb.PendingAttestation) - // Compute rewards / penalties for each attestation in the list and update - // the rewards and penalties lists. - for i, matchAtt := range attsPackage { - indices, err := unslashedAttestingIndices(state, matchAtt) - if err != nil { - return nil, nil, errors.Wrap(err, "could not get attestation indices") - } - - attested := make(map[uint64]bool) - // Construct a map to look up validators that voted for source, target or head. - for _, index := range indices { - if i == 0 { - attestersVotedSource[index] = &pb.PendingAttestation{InclusionDelay: params.BeaconConfig().FarFutureEpoch} - } - attested[index] = true - } - attestedBalance := helpers.TotalBalance(state, indices) - - // Update rewards and penalties to each eligible validator index. - for _, index := range eligible { - base, err := BaseReward(state, index) - if err != nil { - return nil, nil, errors.Wrap(err, "could not get base reward") - } - if _, ok := attested[index]; ok { - rewards[index] += base * attestedBalance / totalBalance - } else { - penalties[index] += base - } - } - } - - // For every index, filter the matching source attestation that correspond to the index, - // sort by inclusion delay and get the one that was included on chain first. - for _, att := range atts.source { - indices, err := helpers.AttestingIndices(state, att.Data, att.AggregationBits) - if err != nil { - return nil, nil, errors.Wrap(err, "could not get attester indices") - } - for _, i := range indices { - if _, ok := attestersVotedSource[i]; ok { - if attestersVotedSource[i].InclusionDelay > att.InclusionDelay { - attestersVotedSource[i] = att - } - } - } - } - - for i, a := range attestersVotedSource { - baseReward, err := BaseReward(state, i) - if err != nil { - return nil, nil, errors.Wrap(err, "could not get proposer reward") - } - proposerReward := baseReward / params.BeaconConfig().ProposerRewardQuotient - rewards[a.ProposerIndex] += proposerReward - attesterReward := baseReward - proposerReward - rewards[i] += attesterReward / a.InclusionDelay - } - - // Apply penalties for quadratic leaks. - // When epoch since finality exceeds inactivity penalty constant, the penalty gets increased - // based on the finality delay. - finalityDelay := prevEpoch - state.FinalizedCheckpoint.Epoch - if finalityDelay > params.BeaconConfig().MinEpochsToInactivityPenalty { - targetIndices, err := unslashedAttestingIndices(state, atts.Target) - if err != nil { - return nil, nil, errors.Wrap(err, "could not get attestation indices") - } - attestedTarget := make(map[uint64]bool) - for _, index := range targetIndices { - attestedTarget[index] = true - } - for _, index := range eligible { - base, err := BaseReward(state, index) - if err != nil { - return nil, nil, errors.Wrap(err, "could not get base reward") - } - penalties[index] += params.BeaconConfig().BaseRewardsPerEpoch * base - if _, ok := attestedTarget[index]; !ok { - penalties[index] += state.Validators[index].EffectiveBalance * finalityDelay / - params.BeaconConfig().InactivityPenaltyQuotient - } - } - } - - return rewards, penalties, nil -} diff --git a/beacon-chain/core/epoch/epoch_processing_test.go b/beacon-chain/core/epoch/epoch_processing_test.go index afe9bb37b..c6dd064aa 100644 --- a/beacon-chain/core/epoch/epoch_processing_test.go +++ b/beacon-chain/core/epoch/epoch_processing_test.go @@ -332,211 +332,6 @@ func TestBaseReward_AccurateRewards(t *testing.T) { } } -func TestProcessJustificationAndFinalization_CantJustifyFinalize(t *testing.T) { - e := params.BeaconConfig().FarFutureEpoch - a := params.BeaconConfig().MaxEffectiveBalance - state := &pb.BeaconState{ - JustificationBits: []byte{0x00}, - Slot: params.BeaconConfig().SlotsPerEpoch * 2, - PreviousJustifiedCheckpoint: ðpb.Checkpoint{ - Epoch: 0, - Root: params.BeaconConfig().ZeroHash[:], - }, - CurrentJustifiedCheckpoint: ðpb.Checkpoint{ - Epoch: 0, - Root: params.BeaconConfig().ZeroHash[:], - }, - Validators: []*ethpb.Validator{{ExitEpoch: e, EffectiveBalance: a}, {ExitEpoch: e, EffectiveBalance: a}, - {ExitEpoch: e, EffectiveBalance: a}, {ExitEpoch: e, EffectiveBalance: a}}, - } - // Since Attested balances are less than total balances, nothing happened. - newState, err := ProcessJustificationAndFinalization(state, 0, 0) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(state, newState) { - t.Error("Did not get the original state") - } -} - -func TestProcessJustificationAndFinalization_NoBlockRootCurrentEpoch(t *testing.T) { - e := params.BeaconConfig().FarFutureEpoch - a := params.BeaconConfig().MaxEffectiveBalance - blockRoots := make([][]byte, params.BeaconConfig().SlotsPerEpoch*2+1) - for i := 0; i < len(blockRoots); i++ { - blockRoots[i] = []byte{byte(i)} - } - state := &pb.BeaconState{ - Slot: params.BeaconConfig().SlotsPerEpoch * 3, - PreviousJustifiedCheckpoint: ðpb.Checkpoint{ - Epoch: 0, - Root: params.BeaconConfig().ZeroHash[:], - }, - CurrentJustifiedCheckpoint: ðpb.Checkpoint{ - Epoch: 0, - Root: params.BeaconConfig().ZeroHash[:], - }, - FinalizedCheckpoint: ðpb.Checkpoint{}, - JustificationBits: []byte{0x03}, // 0b0011 - Validators: []*ethpb.Validator{{ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}}, - Balances: []uint64{a, a, a, a}, // validator total balance should be 128000000000 - BlockRoots: blockRoots, - } - attestedBalance := 4 * e * 3 / 2 - _, err := ProcessJustificationAndFinalization(state, 0, attestedBalance) - want := "could not get block root for current epoch" - if err == nil || !strings.Contains(err.Error(), want) { - t.Fatal("Did not receive correct error") - } -} - -func TestProcessJustificationAndFinalization_ConsecutiveEpochs(t *testing.T) { - e := params.BeaconConfig().FarFutureEpoch - a := params.BeaconConfig().MaxEffectiveBalance - blockRoots := make([][]byte, params.BeaconConfig().SlotsPerEpoch*2+1) - for i := 0; i < len(blockRoots); i++ { - blockRoots[i] = []byte{byte(i)} - } - state := &pb.BeaconState{ - Slot: params.BeaconConfig().SlotsPerEpoch*2 + 1, - PreviousJustifiedCheckpoint: ðpb.Checkpoint{ - Epoch: 0, - Root: params.BeaconConfig().ZeroHash[:], - }, - CurrentJustifiedCheckpoint: ðpb.Checkpoint{ - Epoch: 0, - Root: params.BeaconConfig().ZeroHash[:], - }, - FinalizedCheckpoint: ðpb.Checkpoint{}, - JustificationBits: bitfield.Bitvector4{0x0F}, // 0b1111 - Validators: []*ethpb.Validator{{ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}}, - Balances: []uint64{a, a, a, a}, // validator total balance should be 128000000000 - BlockRoots: blockRoots, - } - attestedBalance := 4 * e * 3 / 2 - newState, err := ProcessJustificationAndFinalization(state, 0, attestedBalance) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) { - t.Errorf("Wanted current justified root: %v, got: %v", - []byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root) - } - if newState.CurrentJustifiedCheckpoint.Epoch != 2 { - t.Errorf("Wanted justified epoch: %d, got: %d", - 2, newState.CurrentJustifiedCheckpoint.Epoch) - } - if newState.PreviousJustifiedCheckpoint.Epoch != 0 { - t.Errorf("Wanted previous justified epoch: %d, got: %d", - 0, newState.PreviousJustifiedCheckpoint.Epoch) - } - if !bytes.Equal(newState.FinalizedCheckpoint.Root, params.BeaconConfig().ZeroHash[:]) { - t.Errorf("Wanted current finalized root: %v, got: %v", - params.BeaconConfig().ZeroHash, newState.FinalizedCheckpoint.Root) - } - if newState.FinalizedCheckpoint.Epoch != 0 { - t.Errorf("Wanted finalized epoch: 0, got: %d", newState.FinalizedCheckpoint.Epoch) - } -} - -func TestProcessJustificationAndFinalization_JustifyCurrentEpoch(t *testing.T) { - e := params.BeaconConfig().FarFutureEpoch - a := params.BeaconConfig().MaxEffectiveBalance - blockRoots := make([][]byte, params.BeaconConfig().SlotsPerEpoch*2+1) - for i := 0; i < len(blockRoots); i++ { - blockRoots[i] = []byte{byte(i)} - } - state := &pb.BeaconState{ - Slot: params.BeaconConfig().SlotsPerEpoch*2 + 1, - PreviousJustifiedCheckpoint: ðpb.Checkpoint{ - Epoch: 0, - Root: params.BeaconConfig().ZeroHash[:], - }, - CurrentJustifiedCheckpoint: ðpb.Checkpoint{ - Epoch: 0, - Root: params.BeaconConfig().ZeroHash[:], - }, - FinalizedCheckpoint: ðpb.Checkpoint{}, - JustificationBits: bitfield.Bitvector4{0x03}, // 0b0011 - Validators: []*ethpb.Validator{{ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}}, - Balances: []uint64{a, a, a, a}, // validator total balance should be 128000000000 - BlockRoots: blockRoots, - } - attestedBalance := 4 * e * 3 / 2 - newState, err := ProcessJustificationAndFinalization(state, 0, attestedBalance) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) { - t.Errorf("Wanted current justified root: %v, got: %v", - []byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root) - } - if newState.CurrentJustifiedCheckpoint.Epoch != 2 { - t.Errorf("Wanted justified epoch: %d, got: %d", - 2, newState.CurrentJustifiedCheckpoint.Epoch) - } - if newState.PreviousJustifiedCheckpoint.Epoch != 0 { - t.Errorf("Wanted previous justified epoch: %d, got: %d", - 0, newState.PreviousJustifiedCheckpoint.Epoch) - } - if !bytes.Equal(newState.FinalizedCheckpoint.Root, params.BeaconConfig().ZeroHash[:]) { - t.Errorf("Wanted current finalized root: %v, got: %v", - params.BeaconConfig().ZeroHash, newState.FinalizedCheckpoint.Root) - } - if newState.FinalizedCheckpoint.Epoch != 0 { - t.Errorf("Wanted finalized epoch: 0, got: %d", newState.FinalizedCheckpoint.Epoch) - } -} - -func TestProcessJustificationAndFinalization_JustifyPrevEpoch(t *testing.T) { - helpers.ClearAllCaches() - e := params.BeaconConfig().FarFutureEpoch - a := params.BeaconConfig().MaxEffectiveBalance - blockRoots := make([][]byte, params.BeaconConfig().SlotsPerEpoch*2+1) - for i := 0; i < len(blockRoots); i++ { - blockRoots[i] = []byte{byte(i)} - } - state := &pb.BeaconState{ - Slot: params.BeaconConfig().SlotsPerEpoch*2 + 1, - PreviousJustifiedCheckpoint: ðpb.Checkpoint{ - Epoch: 0, - Root: params.BeaconConfig().ZeroHash[:], - }, - CurrentJustifiedCheckpoint: ðpb.Checkpoint{ - Epoch: 0, - Root: params.BeaconConfig().ZeroHash[:], - }, - JustificationBits: bitfield.Bitvector4{0x03}, // 0b0011 - Validators: []*ethpb.Validator{{ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}, {ExitEpoch: e}}, - Balances: []uint64{a, a, a, a}, // validator total balance should be 128000000000 - BlockRoots: blockRoots, FinalizedCheckpoint: ðpb.Checkpoint{}, - } - attestedBalance := 4 * e * 3 / 2 - newState, err := ProcessJustificationAndFinalization(state, attestedBalance, 0) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(newState.CurrentJustifiedCheckpoint.Root, []byte{byte(64)}) { - t.Errorf("Wanted current justified root: %v, got: %v", - []byte{byte(64)}, newState.CurrentJustifiedCheckpoint.Root) - } - if newState.PreviousJustifiedCheckpoint.Epoch != 0 { - t.Errorf("Wanted previous justified epoch: %d, got: %d", - 0, newState.PreviousJustifiedCheckpoint.Epoch) - } - if newState.CurrentJustifiedCheckpoint.Epoch != 2 { - t.Errorf("Wanted justified epoch: %d, got: %d", - 2, newState.CurrentJustifiedCheckpoint.Epoch) - } - if !bytes.Equal(newState.FinalizedCheckpoint.Root, params.BeaconConfig().ZeroHash[:]) { - t.Errorf("Wanted current finalized root: %v, got: %v", - params.BeaconConfig().ZeroHash, newState.FinalizedCheckpoint.Root) - } - if newState.FinalizedCheckpoint.Epoch != 0 { - t.Errorf("Wanted finalized epoch: 0, got: %d", newState.FinalizedCheckpoint.Epoch) - } -} - func TestProcessSlashings_NotSlashed(t *testing.T) { s := &pb.BeaconState{ Slot: 0, @@ -709,212 +504,6 @@ func TestProcessRegistryUpdates_NoRotation(t *testing.T) { } } -func TestAttestationDelta_CantGetBlockRoot(t *testing.T) { - e := params.BeaconConfig().SlotsPerEpoch - - state := buildState(2*e, 1) - state.Slot = 0 - - _, _, err := attestationDelta(state) - wanted := "could not get block root for epoch" - if !strings.Contains(err.Error(), wanted) { - t.Fatalf("Got: %v, want: %v", err.Error(), wanted) - } -} - -func TestAttestationDelta_CantGetAttestation(t *testing.T) { - state := buildState(0, 1) - - _, _, err := attestationDelta(state) - wanted := "could not get source, target and head attestations" - if !strings.Contains(err.Error(), wanted) { - t.Fatalf("Got: %v, want: %v", err.Error(), wanted) - } -} - -func TestAttestationDelta_NoOneAttested(t *testing.T) { - e := params.BeaconConfig().SlotsPerEpoch - validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount / 32 - state := buildState(e+2, validatorCount) - //startShard := uint64(960) - atts := make([]*pb.PendingAttestation, 2) - for i := 0; i < len(atts); i++ { - atts[i] = &pb.PendingAttestation{ - Data: ðpb.AttestationData{ - Target: ðpb.Checkpoint{}, - Source: ðpb.Checkpoint{}, - }, - InclusionDelay: uint64(i + 100), - AggregationBits: bitfield.Bitlist{0xC0, 0x01}, - } - } - - rewards, penalties, err := attestationDelta(state) - if err != nil { - t.Fatal(err) - } - for i := uint64(0); i < validatorCount; i++ { - // Since no one attested, all the validators should gain 0 reward - if rewards[i] != 0 { - t.Errorf("Wanted reward balance 0, got %d", rewards[i]) - } - // Since no one attested, all the validators should get penalized the same - // it's 3 times the penalized amount because source, target and head. - base, err := BaseReward(state, i) - if err != nil { - t.Errorf("Could not get base reward: %v", err) - } - wanted := 3 * base - if penalties[i] != wanted { - t.Errorf("Wanted penalty balance %d, got %d", - wanted, penalties[i]) - } - } -} - -func TestAttestationDelta_SomeAttested(t *testing.T) { - helpers.ClearAllCaches() - e := params.BeaconConfig().SlotsPerEpoch - validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount / 8 - state := buildState(e+2, validatorCount) - atts := make([]*pb.PendingAttestation, 3) - for i := 0; i < len(atts); i++ { - atts[i] = &pb.PendingAttestation{ - Data: ðpb.AttestationData{ - Target: ðpb.Checkpoint{}, - Source: ðpb.Checkpoint{}, - }, - AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01}, - InclusionDelay: 1, - } - } - state.PreviousEpochAttestations = atts - - rewards, penalties, err := attestationDelta(state) - if err != nil { - t.Fatal(err) - } - attestedBalance, err := AttestingBalance(state, atts) - if err != nil { - t.Error(err) - } - totalBalance, err := helpers.TotalActiveBalance(state) - if err != nil { - t.Fatal(err) - } - - attestedIndices := []uint64{100, 106, 196, 641, 654, 1606} - for _, i := range attestedIndices { - base, err := BaseReward(state, i) - if err != nil { - t.Errorf("Could not get base reward: %v", err) - } - - // Base rewards for getting source right - wanted := 3 * (base * attestedBalance / totalBalance) - // Base rewards for proposer and attesters working together getting attestation - // on chain in the fastest manner - proposerReward := base / params.BeaconConfig().ProposerRewardQuotient - wanted += (base - proposerReward) / params.BeaconConfig().MinAttestationInclusionDelay - - if rewards[i] != wanted { - t.Errorf("Wanted reward balance %d, got %d", wanted, rewards[i]) - } - // Since all these validators attested, they shouldn't get penalized. - if penalties[i] != 0 { - t.Errorf("Wanted penalty balance 0, got %d", penalties[i]) - } - } - - nonAttestedIndices := []uint64{12, 23, 45, 79} - for _, i := range nonAttestedIndices { - base, err := BaseReward(state, i) - if err != nil { - t.Errorf("Could not get base reward: %v", err) - } - wanted := 3 * base - // Since all these validators did not attest, they shouldn't get rewarded. - if rewards[i] != 0 { - t.Errorf("Wanted reward balance 0, got %d", rewards[i]) - } - // Base penalties for not attesting. - if penalties[i] != wanted { - t.Errorf("Wanted penalty balance %d, got %d", wanted, penalties[i]) - } - } -} - -func TestAttestationDelta_SomeAttestedFinalityDelay(t *testing.T) { - helpers.ClearAllCaches() - e := params.BeaconConfig().SlotsPerEpoch - validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount / 8 - state := buildState(e+4, validatorCount) - atts := make([]*pb.PendingAttestation, 3) - for i := 0; i < len(atts); i++ { - atts[i] = &pb.PendingAttestation{ - Data: ðpb.AttestationData{ - Target: ðpb.Checkpoint{}, - Source: ðpb.Checkpoint{}, - }, - AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01}, - InclusionDelay: 1, - } - } - state.PreviousEpochAttestations = atts - state.FinalizedCheckpoint.Epoch = 0 - - rewards, penalties, err := attestationDelta(state) - if err != nil { - t.Fatal(err) - } - attestedBalance, err := AttestingBalance(state, atts) - if err != nil { - t.Error(err) - } - totalBalance, err := helpers.TotalActiveBalance(state) - if err != nil { - t.Fatal(err) - } - - attestedIndices := []uint64{100, 106, 196, 641, 654, 1606} - for _, i := range attestedIndices { - base, err := BaseReward(state, i) - if err != nil { - t.Errorf("Could not get base reward: %v", err) - } - // Base rewards for getting source right - wanted := 3 * (base * attestedBalance / totalBalance) - // Base rewards for proposer and attesters working together getting attestation - // on chain in the fatest manner - proposerReward := base / params.BeaconConfig().ProposerRewardQuotient - wanted += (base - proposerReward) * params.BeaconConfig().MinAttestationInclusionDelay - if rewards[i] != wanted { - t.Errorf("Wanted reward balance %d, got %d", wanted, rewards[i]) - } - // Since all these validators attested, they shouldn't get penalized. - if penalties[i] != 0 { - t.Errorf("Wanted penalty balance 0, got %d", penalties[i]) - } - } - - nonAttestedIndices := []uint64{12, 23, 45, 79} - for _, i := range nonAttestedIndices { - base, err := BaseReward(state, i) - if err != nil { - t.Errorf("Could not get base reward: %v", err) - } - wanted := 3 * base - // Since all these validators did not attest, they shouldn't get rewarded. - if rewards[i] != 0 { - t.Errorf("Wanted reward balance 0, got %d", rewards[i]) - } - // Base penalties for not attesting. - if penalties[i] != wanted { - t.Errorf("Wanted penalty balance %d, got %d", wanted, penalties[i]) - } - } -} - func TestProcessRegistryUpdates_EligibleToActivate(t *testing.T) { state := &pb.BeaconState{ Slot: 5 * params.BeaconConfig().SlotsPerEpoch, @@ -1033,51 +622,6 @@ func TestProcessRegistryUpdates_CanExits(t *testing.T) { } } -func TestProcessRewardsAndPenalties_GenesisEpoch(t *testing.T) { - state := &pb.BeaconState{Slot: params.BeaconConfig().SlotsPerEpoch - 1} - newState, err := ProcessRewardsAndPenalties(state) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(state, newState) { - t.Error("genesis state mutated") - } -} - -func TestProcessRewardsAndPenalties_SomeAttested(t *testing.T) { - helpers.ClearAllCaches() - e := params.BeaconConfig().SlotsPerEpoch - validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount / 8 - state := buildState(e+2, validatorCount) - atts := make([]*pb.PendingAttestation, 3) - for i := 0; i < len(atts); i++ { - atts[i] = &pb.PendingAttestation{ - Data: ðpb.AttestationData{ - Target: ðpb.Checkpoint{}, - Source: ðpb.Checkpoint{}, - }, - AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01}, - InclusionDelay: 1, - } - } - state.PreviousEpochAttestations = atts - - state, err := ProcessRewardsAndPenalties(state) - if err != nil { - t.Fatal(err) - } - wanted := uint64(31999873505) - if state.Balances[0] != wanted { - t.Errorf("wanted balance: %d, got: %d", - wanted, state.Balances[0]) - } - wanted = uint64(31999810265) - if state.Balances[4] != wanted { - t.Errorf("wanted balance: %d, got: %d", - wanted, state.Balances[1]) - } -} - func buildState(slot uint64, validatorCount uint64) *pb.BeaconState { validators := make([]*ethpb.Validator, validatorCount) for i := 0; i < len(validators); i++ { diff --git a/beacon-chain/core/epoch/spectest/justification_and_finalization_test.go b/beacon-chain/core/epoch/spectest/justification_and_finalization_test.go index d09f080ae..caa6a26bc 100644 --- a/beacon-chain/core/epoch/spectest/justification_and_finalization_test.go +++ b/beacon-chain/core/epoch/spectest/justification_and_finalization_test.go @@ -1,15 +1,11 @@ package spectest import ( - "bytes" "context" - "fmt" "path" "testing" - "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch" "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute" - "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/params/spectest" "github.com/prysmaticlabs/prysm/shared/testutil" @@ -25,40 +21,11 @@ func runJustificationAndFinalizationTests(t *testing.T, config string) { for _, folder := range testFolders { t.Run(folder.Name(), func(t *testing.T) { folderPath := path.Join(testsFolderPath, folder.Name()) - testutil.RunEpochOperationTest(t, folderPath, processJustificationAndFinalizationWrapper) testutil.RunEpochOperationTest(t, folderPath, processJustificationAndFinalizationPrecomputeWrapper) }) } } -// This is a subset of state.ProcessEpoch. The spec test defines input data for -// `justification_and_finalization` only. -func processJustificationAndFinalizationWrapper(t *testing.T, state *pb.BeaconState) (*pb.BeaconState, error) { - prevEpochAtts, err := targetAtts(state, helpers.PrevEpoch(state)) - if err != nil { - t.Fatalf("could not get target atts prev epoch %d: %v", helpers.PrevEpoch(state), err) - } - currentEpochAtts, err := targetAtts(state, helpers.CurrentEpoch(state)) - if err != nil { - t.Fatalf("could not get target atts current epoch %d: %v", helpers.CurrentEpoch(state), err) - } - prevEpochAttestedBalance, err := epoch.AttestingBalance(state, prevEpochAtts) - if err != nil { - t.Fatalf("could not get attesting balance prev epoch: %v", err) - } - currentEpochAttestedBalance, err := epoch.AttestingBalance(state, currentEpochAtts) - if err != nil { - t.Fatalf("could not get attesting balance current epoch: %v", err) - } - - state, err = epoch.ProcessJustificationAndFinalization(state, prevEpochAttestedBalance, currentEpochAttestedBalance) - if err != nil { - t.Fatalf("could not process justification: %v", err) - } - - return state, nil -} - func processJustificationAndFinalizationPrecomputeWrapper(t *testing.T, state *pb.BeaconState) (*pb.BeaconState, error) { ctx := context.Background() vp, bp := precompute.New(ctx, state) @@ -74,36 +41,3 @@ func processJustificationAndFinalizationPrecomputeWrapper(t *testing.T, state *p return state, nil } - -func targetAtts(state *pb.BeaconState, epoch uint64) ([]*pb.PendingAttestation, error) { - currentEpoch := helpers.CurrentEpoch(state) - previousEpoch := helpers.PrevEpoch(state) - - // Input epoch for matching the source attestations has to be within range - // of current epoch & previous epoch. - if epoch != currentEpoch && epoch != previousEpoch { - return nil, fmt.Errorf("input epoch: %d != current epoch: %d or previous epoch: %d", - epoch, currentEpoch, previousEpoch) - } - - // Decide if the source attestations are coming from current or previous epoch. - var srcAtts []*pb.PendingAttestation - if epoch == currentEpoch { - srcAtts = state.CurrentEpochAttestations - } else { - srcAtts = state.PreviousEpochAttestations - } - targetRoot, err := helpers.BlockRoot(state, epoch) - if err != nil { - return nil, err - } - - tgtAtts := make([]*pb.PendingAttestation, 0, len(srcAtts)) - for _, srcAtt := range srcAtts { - if bytes.Equal(srcAtt.Data.Target.Root, targetRoot) { - tgtAtts = append(tgtAtts, srcAtt) - } - } - - return tgtAtts, nil -} diff --git a/beacon-chain/core/state/transition.go b/beacon-chain/core/state/transition.go index 9bdd59410..13cee9856 100644 --- a/beacon-chain/core/state/transition.go +++ b/beacon-chain/core/state/transition.go @@ -599,72 +599,6 @@ func CanProcessEpoch(state *pb.BeaconState) bool { return (state.Slot+1)%params.BeaconConfig().SlotsPerEpoch == 0 } -// ProcessEpoch describes the per epoch operations that are performed on the -// beacon state. It focuses on the validator registry, adjusting balances, and finalizing slots. -// -// Spec pseudocode definition: -// -// def process_epoch(state: BeaconState) -> None: -// process_justification_and_finalization(state) -// process_crosslinks(state) -// process_rewards_and_penalties(state) -// process_registry_updates(state) -// # @process_reveal_deadlines -// # @process_challenge_deadlines -// process_slashings(state) -// process_final_updates(state) -// # @after_process_final_updates -func ProcessEpoch(ctx context.Context, state *pb.BeaconState) (*pb.BeaconState, error) { - ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessEpoch") - defer span.End() - span.AddAttributes(trace.Int64Attribute("epoch", int64(helpers.SlotToEpoch(state.Slot)))) - - prevEpochAtts, err := e.MatchAttestations(state, helpers.PrevEpoch(state)) - if err != nil { - return nil, fmt.Errorf("could not get target atts prev epoch %d: %v", - helpers.PrevEpoch(state), err) - } - currentEpochAtts, err := e.MatchAttestations(state, helpers.CurrentEpoch(state)) - if err != nil { - return nil, fmt.Errorf("could not get target atts current epoch %d: %v", - helpers.CurrentEpoch(state), err) - } - prevEpochAttestedBalance, err := e.AttestingBalance(state, prevEpochAtts.Target) - if err != nil { - return nil, errors.Wrap(err, "could not get attesting balance prev epoch") - } - currentEpochAttestedBalance, err := e.AttestingBalance(state, currentEpochAtts.Target) - if err != nil { - return nil, errors.Wrap(err, "could not get attesting balance current epoch") - } - - state, err = e.ProcessJustificationAndFinalization(state, prevEpochAttestedBalance, currentEpochAttestedBalance) - if err != nil { - return nil, errors.Wrap(err, "could not process justification") - } - - state, err = e.ProcessRewardsAndPenalties(state) - if err != nil { - return nil, errors.Wrap(err, "could not process rewards and penalties") - } - - state, err = e.ProcessRegistryUpdates(state) - if err != nil { - return nil, errors.Wrap(err, "could not process registry updates") - } - - state, err = e.ProcessSlashings(state) - if err != nil { - return nil, errors.Wrap(err, "could not process slashings") - } - - state, err = e.ProcessFinalUpdates(state) - if err != nil { - return nil, errors.Wrap(err, "could not process final updates") - } - return state, nil -} - // ProcessEpochPrecompute describes the per epoch operations that are performed on the beacon state. // It's optimized by pre computing validator attested info and epoch total/attested balances upfront. func ProcessEpochPrecompute(ctx context.Context, state *pb.BeaconState) (*pb.BeaconState, error) { diff --git a/beacon-chain/core/state/transition_test.go b/beacon-chain/core/state/transition_test.go index c2fde12f6..79dd727c8 100644 --- a/beacon-chain/core/state/transition_test.go +++ b/beacon-chain/core/state/transition_test.go @@ -474,53 +474,6 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) { } } -func TestProcessEpoch_CantGetTgtAttsPrevEpoch(t *testing.T) { - atts := []*pb.PendingAttestation{{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{Epoch: 1}}}} - _, err := state.ProcessEpoch(context.Background(), &pb.BeaconState{CurrentEpochAttestations: atts}) - if !strings.Contains(err.Error(), "could not get target atts prev epoch") { - t.Fatal("Did not receive wanted error") - } -} - -func TestProcessEpoch_CantGetTgtAttsCurrEpoch(t *testing.T) { - epoch := uint64(1) - - atts := []*pb.PendingAttestation{{Data: ðpb.AttestationData{}}} - _, err := state.ProcessEpoch(context.Background(), &pb.BeaconState{ - Slot: epoch * params.BeaconConfig().SlotsPerEpoch, - BlockRoots: make([][]byte, 128), - RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), - CurrentEpochAttestations: atts}) - if !strings.Contains(err.Error(), "could not get target atts current epoch") { - t.Fatal("Did not receive wanted error") - } -} - -func TestProcessEpoch_CanProcess(t *testing.T) { - helpers.ClearAllCaches() - epoch := uint64(1) - - atts := []*pb.PendingAttestation{{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{}}}} - newState, err := state.ProcessEpoch(context.Background(), &pb.BeaconState{ - Slot: epoch*params.BeaconConfig().SlotsPerEpoch + 1, - BlockRoots: make([][]byte, 128), - Slashings: []uint64{0, 1e9, 1e9}, - RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), - CurrentEpochAttestations: atts, - FinalizedCheckpoint: ðpb.Checkpoint{}, - JustificationBits: bitfield.Bitvector4{0x00}, - CurrentJustifiedCheckpoint: ðpb.Checkpoint{}, - }) - if err != nil { - t.Fatal(err) - } - - wanted := uint64(0) - if newState.Slashings[2] != wanted { - t.Errorf("Wanted slashed balance: %d, got: %d", wanted, newState.Slashings[2]) - } -} - func TestProcessEpochPrecompute_CanProcess(t *testing.T) { helpers.ClearAllCaches() epoch := uint64(1) @@ -546,72 +499,6 @@ func TestProcessEpochPrecompute_CanProcess(t *testing.T) { t.Errorf("Wanted slashed balance: %d, got: %d", wanted, newState.Slashings[2]) } } - -func TestProcessEpoch_NotPanicOnEmptyActiveValidatorIndices(t *testing.T) { - newState := &pb.BeaconState{ - Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector), - RandaoMixes: make([][]byte, params.BeaconConfig().SlotsPerEpoch), - } - - state.ProcessEpoch(context.Background(), newState) -} - -func BenchmarkProcessEpoch65536Validators(b *testing.B) { - logrus.SetLevel(logrus.PanicLevel) - - helpers.ClearAllCaches() - epoch := uint64(1) - - validatorCount := params.BeaconConfig().MinGenesisActiveValidatorCount * 4 - comitteeCount := validatorCount / params.BeaconConfig().TargetCommitteeSize - validators := make([]*ethpb.Validator, validatorCount) - balances := make([]uint64, validatorCount) - - for i := 0; i < len(validators); i++ { - validators[i] = ðpb.Validator{ - ExitEpoch: params.BeaconConfig().FarFutureEpoch, - EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, - } - balances[i] = params.BeaconConfig().MaxEffectiveBalance - } - - var atts []*pb.PendingAttestation - for i := uint64(0); i < comitteeCount; i++ { - atts = append(atts, &pb.PendingAttestation{ - Data: ðpb.AttestationData{}, - AggregationBits: []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - InclusionDelay: 1, - }) - } - - s := &pb.BeaconState{ - Slot: epoch*params.BeaconConfig().SlotsPerEpoch + 1, - Validators: validators, - Balances: balances, - FinalizedCheckpoint: ðpb.Checkpoint{}, - BlockRoots: make([][]byte, 254), - Slashings: []uint64{0, 1e9, 0}, - RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), - PreviousEpochAttestations: atts, - } - - // Precache the shuffled indices - for i := uint64(0); i < comitteeCount; i++ { - if _, err := helpers.BeaconCommittee(s, 0, i); err != nil { - b.Fatal(err) - } - } - - b.ResetTimer() - for n := 0; n < b.N; n++ { - _, err := state.ProcessEpoch(context.Background(), s) - if err != nil { - b.Fatal(err) - } - } -} - func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) { logrus.SetLevel(logrus.PanicLevel) helpers.ClearAllCaches()