Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block rewards - separate code version #12009

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions beacon-chain/core/rewards/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
load("@prysm//tools/go:def.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = [
"block_attestations.go",
"block_slashings.go",
"rewards.go",
],
importpath = "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/rewards",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/state:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/attestation:go_default_library",
"//runtime/version:go_default_library",
"//time/slots:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
],
)
130 changes: 130 additions & 0 deletions beacon-chain/core/rewards/block_attestations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package rewards

import (
"context"
"fmt"

"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v3/config/params"
consensusblocks "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1/attestation"
"go.opencensus.io/trace"
)

func attestationsReward(
ctx context.Context,
beaconState state.BeaconState,
b interfaces.ReadOnlySignedBeaconBlock,
r *BlockRewardsInfo,
) (state.BeaconState, error) {
if err := consensusblocks.BeaconBlockIsNil(b); err != nil {
return nil, err
}
body := b.Block().Body()
totalBalance, err := helpers.TotalActiveBalance(beaconState)
if err != nil {
return nil, err
}
var totalReward uint64
for idx, att := range body.Attestations() {
var reward uint64
beaconState, reward, err = attestationReward(ctx, beaconState, att, totalBalance)
if err != nil {
return nil, errors.Wrapf(err, "could not get reward for attestation at index %d in block", idx)
}
totalReward += reward
}

r.Attestations = totalReward
return beaconState, nil
}

func attestationReward(
ctx context.Context,
beaconState state.BeaconState,
att *ethpb.Attestation,
totalBalance uint64,
) (state.BeaconState, uint64, error) {
ctx, span := trace.StartSpan(ctx, "altair.ProcessAttestationNoVerifySignature")
defer span.End()

delay, err := beaconState.Slot().SafeSubSlot(att.Data.Slot)
if err != nil {
return nil, 0, fmt.Errorf("att slot %d can't be greater than state slot %d", att.Data.Slot, beaconState.Slot())
}
participatedFlags, err := altair.AttestationParticipationFlagIndices(beaconState, att.Data, delay)
if err != nil {
return nil, 0, err
}
committee, err := helpers.BeaconCommitteeFromState(ctx, beaconState, att.Data.Slot, att.Data.CommitteeIndex)
if err != nil {
return nil, 0, err
}
indices, err := attestation.AttestingIndices(att.AggregationBits, committee)
if err != nil {
return nil, 0, err
}

return setParticipationAndRewardProposer(ctx, beaconState, att.Data.Target.Epoch, indices, participatedFlags, totalBalance)
}

func setParticipationAndRewardProposer(
ctx context.Context,
beaconState state.BeaconState,
targetEpoch primitives.Epoch,
indices []uint64,
participatedFlags map[uint8]bool, totalBalance uint64) (state.BeaconState, uint64, error) {
var proposerRewardNumerator uint64
currentEpoch := time.CurrentEpoch(beaconState)
var stateErr error
if targetEpoch == currentEpoch {
stateErr = beaconState.ModifyCurrentParticipationBits(func(val []byte) ([]byte, error) {
propRewardNum, epochParticipation, err := altair.EpochParticipation(beaconState, indices, val, participatedFlags, totalBalance)
if err != nil {
return nil, err
}
proposerRewardNumerator = propRewardNum
return epochParticipation, nil
})
} else {
stateErr = beaconState.ModifyPreviousParticipationBits(func(val []byte) ([]byte, error) {
propRewardNum, epochParticipation, err := altair.EpochParticipation(beaconState, indices, val, participatedFlags, totalBalance)
if err != nil {
return nil, err
}
proposerRewardNumerator = propRewardNum
return epochParticipation, nil
})
}
if stateErr != nil {
return nil, 0, stateErr
}

reward, err := rewardProposer(ctx, beaconState, proposerRewardNumerator)
if err != nil {
return nil, 0, err
}

return beaconState, reward, nil
}

func rewardProposer(ctx context.Context, beaconState state.BeaconState, proposerRewardNumerator uint64) (uint64, error) {
cfg := params.BeaconConfig()
d := (cfg.WeightDenominator - cfg.ProposerWeight) * cfg.WeightDenominator / cfg.ProposerWeight
proposerReward := proposerRewardNumerator / d
i, err := helpers.BeaconProposerIndex(ctx, beaconState)
if err != nil {
return 0, err
}
if err := helpers.IncreaseBalance(beaconState, i, proposerReward); err != nil {
return 0, err
}
return proposerReward, nil
}
197 changes: 197 additions & 0 deletions beacon-chain/core/rewards/block_slashings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package rewards

import (
"context"
"sort"

"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v3/config/params"
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/runtime/version"
"github.com/prysmaticlabs/prysm/v3/time/slots"
)

type slashValidatorFunc func(
ctx context.Context,
st state.BeaconState,
vid primitives.ValidatorIndex,
penaltyQuotient,
proposerRewardQuotient uint64,
) (state.BeaconState, uint64, error)

func processProposerSlashings(
ctx context.Context,
beaconState state.BeaconState,
slashings []*ethpb.ProposerSlashing,
slashFunc slashValidatorFunc,
r *BlockRewardsInfo,
) (state.BeaconState, error) {
var err error
var totalReward uint64
for _, slashing := range slashings {
var reward uint64
beaconState, reward, err = processProposerSlashing(ctx, beaconState, slashing, slashFunc)
if err != nil {
return nil, err
}
totalReward += reward
}

r.ProposerSlashings = totalReward
return beaconState, nil
}

func processProposerSlashing(
ctx context.Context,
beaconState state.BeaconState,
slashing *ethpb.ProposerSlashing,
slashFunc slashValidatorFunc,
) (state.BeaconState, uint64, error) {
var err error
if slashing == nil {
return nil, 0, errors.New("nil proposer slashings in block body")
}
cfg := params.BeaconConfig()
var slashingQuotient uint64
switch {
case beaconState.Version() == version.Phase0:
slashingQuotient = cfg.MinSlashingPenaltyQuotient
case beaconState.Version() == version.Altair:
slashingQuotient = cfg.MinSlashingPenaltyQuotientAltair
case beaconState.Version() >= version.Bellatrix:
slashingQuotient = cfg.MinSlashingPenaltyQuotientBellatrix
default:
return nil, 0, errors.New("unknown state version")
}
var reward uint64
beaconState, reward, err = slashFunc(ctx, beaconState, slashing.Header_1.Header.ProposerIndex, slashingQuotient, cfg.ProposerRewardQuotient)
if err != nil {
return nil, 0, errors.Wrapf(err, "could not slash proposer index %d", slashing.Header_1.Header.ProposerIndex)
}
return beaconState, reward, nil
}

func processAttesterSlashings(
ctx context.Context,
beaconState state.BeaconState,
slashings []*ethpb.AttesterSlashing,
slashFunc slashValidatorFunc,
r *BlockRewardsInfo,
) (state.BeaconState, error) {
var err error
var totalReward uint64
for _, slashing := range slashings {
var reward uint64
beaconState, reward, err = processAttesterSlashing(ctx, beaconState, slashing, slashFunc)
if err != nil {
return nil, err
}
totalReward += reward
}

r.AttesterSlashings = totalReward
return beaconState, nil
}

func processAttesterSlashing(
ctx context.Context,
beaconState state.BeaconState,
slashing *ethpb.AttesterSlashing,
slashFunc slashValidatorFunc,
) (state.BeaconState, uint64, error) {
slashableIndices := blocks.SlashableAttesterIndices(slashing)
sort.SliceStable(slashableIndices, func(i, j int) bool {
return slashableIndices[i] < slashableIndices[j]
})
currentEpoch := slots.ToEpoch(beaconState.Slot())
var err error
var slashedAny bool
var val state.ReadOnlyValidator
var totalReward uint64
for _, validatorIndex := range slashableIndices {
val, err = beaconState.ValidatorAtIndexReadOnly(primitives.ValidatorIndex(validatorIndex))
if err != nil {
return nil, 0, err
}
if helpers.IsSlashableValidator(val.ActivationEpoch(), val.WithdrawableEpoch(), val.Slashed(), currentEpoch) {
cfg := params.BeaconConfig()
var slashingQuotient uint64
switch {
case beaconState.Version() == version.Phase0:
slashingQuotient = cfg.MinSlashingPenaltyQuotient
case beaconState.Version() == version.Altair:
slashingQuotient = cfg.MinSlashingPenaltyQuotientAltair
case beaconState.Version() >= version.Bellatrix:
slashingQuotient = cfg.MinSlashingPenaltyQuotientBellatrix
default:
return nil, 0, errors.New("unknown state version")
}
var reward uint64
beaconState, reward, err = slashFunc(ctx, beaconState, primitives.ValidatorIndex(validatorIndex), slashingQuotient, cfg.ProposerRewardQuotient)
if err != nil {
return nil, 0, errors.Wrapf(err, "could not slash validator index %d",
validatorIndex)
}
totalReward += reward
slashedAny = true
}
}
if !slashedAny {
return nil, 0, errors.New("unable to slash any validator despite confirmed attester slashing")
}
return beaconState, totalReward, nil
}

func slashValidator(
ctx context.Context,
s state.BeaconState,
slashedIdx primitives.ValidatorIndex,
penaltyQuotient uint64,
proposerRewardQuotient uint64) (state.BeaconState, uint64, error) {
currentEpoch := slots.ToEpoch(s.Slot())
validator, err := s.ValidatorAtIndex(slashedIdx)
if err != nil {
return nil, 0, err
}
validator.Slashed = true
maxWithdrawableEpoch := primitives.MaxEpoch(validator.WithdrawableEpoch, currentEpoch+params.BeaconConfig().EpochsPerSlashingsVector)
validator.WithdrawableEpoch = maxWithdrawableEpoch

if err := s.UpdateValidatorAtIndex(slashedIdx, validator); err != nil {
return nil, 0, err
}

// The slashing amount is represented by epochs per slashing vector. The validator's effective balance is then applied to that amount.
slashings := s.Slashings()
currentSlashing := slashings[currentEpoch%params.BeaconConfig().EpochsPerSlashingsVector]
if err := s.UpdateSlashingsAtIndex(
uint64(currentEpoch%params.BeaconConfig().EpochsPerSlashingsVector),
currentSlashing+validator.EffectiveBalance,
); err != nil {
return nil, 0, err
}
if err := helpers.DecreaseBalance(s, slashedIdx, validator.EffectiveBalance/penaltyQuotient); err != nil {
return nil, 0, err
}

proposerIdx, err := helpers.BeaconProposerIndex(ctx, s)
if err != nil {
return nil, 0, errors.Wrap(err, "could not get proposer idx")
}
whistleBlowerIdx := proposerIdx
whistleblowerReward := validator.EffectiveBalance / params.BeaconConfig().WhistleBlowerRewardQuotient
proposerReward := whistleblowerReward / proposerRewardQuotient
err = helpers.IncreaseBalance(s, proposerIdx, proposerReward)
if err != nil {
return nil, 0, err
}
err = helpers.IncreaseBalance(s, whistleBlowerIdx, whistleblowerReward-proposerReward)
if err != nil {
return nil, 0, err
}
return s, proposerReward, nil
}
Loading