Removed process epoch (#4251)

This commit is contained in:
terence tsao 2019-12-10 20:40:14 -08:00 committed by Preston Van Loon
parent 812311f6f7
commit 6d2c37caf1
5 changed files with 0 additions and 1020 deletions

View File

@ -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 = &ethpb.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 = &ethpb.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
}

View File

@ -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: &ethpb.Checkpoint{
Epoch: 0,
Root: params.BeaconConfig().ZeroHash[:],
},
CurrentJustifiedCheckpoint: &ethpb.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: &ethpb.Checkpoint{
Epoch: 0,
Root: params.BeaconConfig().ZeroHash[:],
},
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{
Epoch: 0,
Root: params.BeaconConfig().ZeroHash[:],
},
FinalizedCheckpoint: &ethpb.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: &ethpb.Checkpoint{
Epoch: 0,
Root: params.BeaconConfig().ZeroHash[:],
},
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{
Epoch: 0,
Root: params.BeaconConfig().ZeroHash[:],
},
FinalizedCheckpoint: &ethpb.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: &ethpb.Checkpoint{
Epoch: 0,
Root: params.BeaconConfig().ZeroHash[:],
},
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{
Epoch: 0,
Root: params.BeaconConfig().ZeroHash[:],
},
FinalizedCheckpoint: &ethpb.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: &ethpb.Checkpoint{
Epoch: 0,
Root: params.BeaconConfig().ZeroHash[:],
},
CurrentJustifiedCheckpoint: &ethpb.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: &ethpb.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: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{},
Source: &ethpb.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: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{},
Source: &ethpb.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: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{},
Source: &ethpb.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: &ethpb.AttestationData{
Target: &ethpb.Checkpoint{},
Source: &ethpb.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++ {

View File

@ -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
}

View File

@ -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) {

View File

@ -474,53 +474,6 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
}
}
func TestProcessEpoch_CantGetTgtAttsPrevEpoch(t *testing.T) {
atts := []*pb.PendingAttestation{{Data: &ethpb.AttestationData{Target: &ethpb.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: &ethpb.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: &ethpb.AttestationData{Target: &ethpb.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: &ethpb.Checkpoint{},
JustificationBits: bitfield.Bitvector4{0x00},
CurrentJustifiedCheckpoint: &ethpb.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] = &ethpb.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: &ethpb.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: &ethpb.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()