Add PulseChain burn

This commit is contained in:
Shane Bammel 2023-01-03 20:53:00 -06:00
parent 20c8b73730
commit 298143bca4
14 changed files with 129 additions and 13 deletions

View File

@ -234,7 +234,7 @@ func RewardProposer(ctx context.Context, beaconState state.BeaconState, proposer
return err
}
return helpers.IncreaseBalance(beaconState, i, proposerReward)
return helpers.IncreaseBalance(beaconState, i, proposerReward, cfg.IsPulseChain())
}
// AttestationParticipationFlagIndices retrieves a map of attestation scoring based on Altair's participation flag indices.

View File

@ -66,6 +66,7 @@ func processSyncAggregate(ctx context.Context, s state.BeaconState, sync *ethpb.
[]bls.PublicKey,
uint64,
error) {
cfg := params.BeaconConfig()
currentSyncCommittee, err := s.CurrentSyncCommittee()
if err != nil {
return nil, nil, 0, err
@ -106,7 +107,7 @@ func processSyncAggregate(ctx context.Context, s state.BeaconState, sync *ethpb.
return nil, nil, 0, err
}
votedKeys = append(votedKeys, pubKey)
if err := helpers.IncreaseBalance(s, vIdx, participantReward); err != nil {
if err := helpers.IncreaseBalance(s, vIdx, participantReward, cfg.IsPulseChain()); err != nil {
return nil, nil, 0, err
}
earnedProposerReward += proposerReward
@ -116,7 +117,7 @@ func processSyncAggregate(ctx context.Context, s state.BeaconState, sync *ethpb.
}
}
}
if err := helpers.IncreaseBalance(s, proposerIndex, earnedProposerReward); err != nil {
if err := helpers.IncreaseBalance(s, proposerIndex, earnedProposerReward, cfg.IsPulseChain()); err != nil {
return nil, nil, 0, err
}
return s, votedKeys, earnedProposerReward, err

View File

@ -244,7 +244,7 @@ func ProcessRewardsAndPenaltiesPrecompute(
// Compute the post balance of the validator after accounting for the
// attester and proposer rewards and penalties.
delta := attDeltas[i]
balances[i], err = helpers.IncreaseBalanceWithVal(balances[i], delta.HeadReward+delta.SourceReward+delta.TargetReward)
balances[i], err = helpers.IncreaseBalanceWithVal(balances[i], delta.HeadReward+delta.SourceReward+delta.TargetReward, cfg.IsPulseChain())
if err != nil {
return nil, err
}

View File

@ -202,7 +202,8 @@ func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, verif
if err := beaconState.AppendBalance(amount); err != nil {
return nil, newValidator, err
}
} else if err := helpers.IncreaseBalance(beaconState, index, amount); err != nil {
// Do not apply burn to deposits.
} else if err := helpers.IncreaseBalance(beaconState, index, amount, false); err != nil {
return nil, newValidator, err
}

View File

@ -23,6 +23,7 @@ func ProcessRewardsAndPenaltiesPrecompute(
attRewardsFunc attesterRewardsFunc,
proRewardsFunc proposerRewardsFunc,
) (state.BeaconState, error) {
cfg := params.BeaconConfig()
// Can't process rewards and penalties in genesis epoch.
if time.CurrentEpoch(state) == 0 {
return state, nil
@ -48,7 +49,7 @@ func ProcessRewardsAndPenaltiesPrecompute(
// Compute the post balance of the validator after accounting for the
// attester and proposer rewards and penalties.
validatorBals[i], err = helpers.IncreaseBalanceWithVal(validatorBals[i], attsRewards[i]+proposerRewards[i])
validatorBals[i], err = helpers.IncreaseBalanceWithVal(validatorBals[i], attsRewards[i]+proposerRewards[i], cfg.IsPulseChain())
if err != nil {
return nil, err
}

View File

@ -19,6 +19,7 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/cache:go_default_library",
"//beacon-chain/core/pulse:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/forkchoice/types:go_default_library",
"//beacon-chain/state:go_default_library",

View File

@ -6,6 +6,7 @@ import (
"math/big"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/pulse"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
@ -99,6 +100,8 @@ func TotalActiveBalance(s state.ReadOnlyBeaconState) (*big.Int, error) {
// IncreaseBalance increases validator with the given 'index' balance by 'delta' in Gwei.
//
// Optional `applyBurn` applies the PulseChain burn to the delta.
//
// Spec pseudocode definition:
//
// def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
@ -106,12 +109,12 @@ func TotalActiveBalance(s state.ReadOnlyBeaconState) (*big.Int, error) {
// Increase the validator balance at index ``index`` by ``delta``.
// """
// state.balances[index] += delta
func IncreaseBalance(state state.BeaconState, idx primitives.ValidatorIndex, delta uint64) error {
func IncreaseBalance(state state.BeaconState, idx primitives.ValidatorIndex, delta uint64, applyBurn bool) error {
balAtIdx, err := state.BalanceAtIndex(idx)
if err != nil {
return err
}
newBal, err := IncreaseBalanceWithVal(balAtIdx, delta)
newBal, err := IncreaseBalanceWithVal(balAtIdx, delta, applyBurn)
if err != nil {
return err
}
@ -122,6 +125,8 @@ func IncreaseBalance(state state.BeaconState, idx primitives.ValidatorIndex, del
// This method is flattened version of the spec method, taking in the raw balance and returning
// the post balance.
//
// Optional `applyBurn` applies the PulseChain burn to the delta.
//
// Spec pseudocode definition:
//
// def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None:
@ -129,7 +134,10 @@ func IncreaseBalance(state state.BeaconState, idx primitives.ValidatorIndex, del
// Increase the validator balance at index ``index`` by ``delta``.
// """
// state.balances[index] += delta
func IncreaseBalanceWithVal(currBalance, delta uint64) (uint64, error) {
func IncreaseBalanceWithVal(currBalance, delta uint64, applyBurn bool) (uint64, error) {
if applyBurn {
delta = pulse.ApplyBurn(delta)
}
res, err := mathutil.Add64(currBalance, delta)
if err != nil {
logrus.Warn("validator balance overflow detected")

View File

@ -149,7 +149,30 @@ func TestIncreaseBalance_OK(t *testing.T) {
Balances: test.b,
})
require.NoError(t, err)
require.NoError(t, IncreaseBalance(state, test.i, test.nb))
require.NoError(t, IncreaseBalance(state, test.i, test.nb, false))
assert.Equal(t, test.eb, state.Balances()[test.i], "Incorrect Validator balance")
}
}
func TestIncreaseBalanceWithBurn_OK(t *testing.T) {
tests := []struct {
i primitives.ValidatorIndex
b []uint64
nb uint64
eb uint64
}{
{i: 0, b: []uint64{27 * 1e9, 28 * 1e9, 32 * 1e9}, nb: 100, eb: 27*1e9 + 75},
{i: 1, b: []uint64{27 * 1e9, 28 * 1e9, 32 * 1e9}, nb: 0, eb: 28 * 1e9},
{i: 2, b: []uint64{27 * 1e9, 28 * 1e9, 32 * 1e9}, nb: 33 * 1e9, eb: 32*1e9 + (33 * 1e9 * 3 / 4)},
}
for _, test := range tests {
state, err := state_native.InitializeFromProtoPhase0(&ethpb.BeaconState{
Validators: []*ethpb.Validator{
{EffectiveBalance: 4}, {EffectiveBalance: 4}, {EffectiveBalance: 4}},
Balances: test.b,
})
require.NoError(t, err)
require.NoError(t, IncreaseBalance(state, test.i, test.nb, true))
assert.Equal(t, test.eb, state.Balances()[test.i], "Incorrect Validator balance")
}
}
@ -293,7 +316,7 @@ func TestIncreaseBalance_OverflowCapped(t *testing.T) {
Balances: test.b,
})
require.NoError(t, err)
require.NoError(t, IncreaseBalance(state, test.i, test.nb))
require.NoError(t, IncreaseBalance(state, test.i, test.nb, false))
for _, bal := range state.Balances() {
require.Equal(t, bal, uint64(math.MaxUint64))
}

View File

@ -0,0 +1,22 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["reward_burn.go"],
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/pulse",
visibility = ["//visibility:public"],
deps = [
"//config/params:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["reward_burn_test.go"],
embed = [":go_default_library"],
deps = [
"//testing/require:go_default_library",
],
size = "small"
)

View File

@ -0,0 +1,24 @@
// Package pulse implements the PulseChain fork
package pulse
import (
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/sirupsen/logrus"
)
// Applies the PulseChain burn to a pending validator reward.
func ApplyBurn(baseReward uint64) uint64 {
secondsPerSlot := params.BeaconConfig().SecondsPerSlot
// First we compensate for the increased block frequency.
afterBurn := baseReward * secondsPerSlot / 12
// Then we burn an additional 25%.
afterBurn = afterBurn * 3 / 4
logrus.WithFields(logrus.Fields{
"baseReward": baseReward,
"afterBurn": afterBurn,
}).Debug("Applied PulseChain Burn 🔥")
return afterBurn
}

View File

@ -0,0 +1,28 @@
package pulse
import (
"testing"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/testing/require"
)
func TestApplyBurn(t *testing.T) {
t.Run("Test burn with various slot times", func(t *testing.T) {
beforeBurn := uint64(1000000)
// Default 12 second slots => 25% general burn.
afterBurn := ApplyBurn(beforeBurn)
require.Equal(t, uint64(750000), afterBurn)
// 6 second slots => 50% burn then 25% general burn.
params.BeaconConfig().SecondsPerSlot = 6
afterBurn = ApplyBurn(beforeBurn)
require.Equal(t, uint64(375000), afterBurn)
// 3 second slots => 75% burn then 25% general burn.
params.BeaconConfig().SecondsPerSlot = 3
afterBurn = ApplyBurn(beforeBurn)
require.Equal(t, uint64(187500), afterBurn)
})
}

View File

@ -172,11 +172,12 @@ func SlashValidator(
whistleBlowerIdx := proposerIdx
whistleblowerReward := validator.EffectiveBalance / params.BeaconConfig().WhistleBlowerRewardQuotient
proposerReward := whistleblowerReward / proposerRewardQuotient
err = helpers.IncreaseBalance(s, proposerIdx, proposerReward)
// Do not apply burn to slashing rewards.
err = helpers.IncreaseBalance(s, proposerIdx, proposerReward, false)
if err != nil {
return nil, err
}
err = helpers.IncreaseBalance(s, whistleBlowerIdx, whistleblowerReward-proposerReward)
err = helpers.IncreaseBalance(s, whistleBlowerIdx, whistleblowerReward-proposerReward, false)
if err != nil {
return nil, err
}

View File

@ -318,6 +318,11 @@ func (b *BeaconChainConfig) MaximumGossipClockDisparityDuration() time.Duration
return time.Duration(b.MaximumGossipClockDisparity) * time.Millisecond
}
// IsPulseChain returns true if the current chain config is running the pulsechain preset.
func (b *BeaconChainConfig) IsPulseChain() bool {
return b.PresetBase == "pulsechain"
}
// DenebEnabled centralizes the check to determine if code paths
// that are specific to deneb should be allowed to execute. This will make it easier to find call sites that do this
// kind of check and remove them post-deneb.

View File

@ -14,6 +14,7 @@ func UsePulseChainTestnetNetworkConfig() {
func PulseChainTestnetConfig() *BeaconChainConfig {
cfg := MainnetConfig().Copy()
cfg.ConfigName = PulseChainTestnetName
cfg.PresetBase = "pulsechain"
cfg.TerminalTotalDifficulty = "58750003716598352947541"
cfg.MinGenesisActiveValidatorCount = 5000
cfg.MinGenesisTime = 1674864000