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

Added process slashings #6728

Merged
merged 3 commits into from
Jan 27, 2023
Merged
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
8 changes: 8 additions & 0 deletions cmd/erigon-cl/core/state/accessors.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ func (b *BeaconState) GetTotalActiveBalance() (uint64, error) {
return b.GetTotalBalance(b.GetActiveValidatorsIndices(b.Epoch()))
}

// GetTotalSlashingAmount return the sum of all slashings.
func (b *BeaconState) GetTotalSlashingAmount() (t uint64) {
for _, slash := range &b.slashings {
t += slash
}
return
}

// GetBlockRoot returns blook root at start of a given epoch
func (b *BeaconState) GetBlockRoot(epoch uint64) (libcommon.Hash, error) {
return b.GetBlockRootAtSlot(epoch * b.beaconConfig.SlotsPerEpoch)
Expand Down
10 changes: 10 additions & 0 deletions cmd/erigon-cl/core/state/setters.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ func (b *BeaconState) AddEth1DataVote(vote *cltypes.Eth1Data) {
b.eth1DataVotes = append(b.eth1DataVotes, vote)
}

func (b *BeaconState) ResetEth1DataVotes() {
b.touchedLeaves[Eth1DataVotesLeafIndex] = true
b.eth1DataVotes = b.eth1DataVotes[:0]
}

func (b *BeaconState) SetEth1DepositIndex(eth1DepositIndex uint64) {
b.touchedLeaves[Eth1DepositIndexLeafIndex] = true
b.eth1DepositIndex = eth1DepositIndex
Expand All @@ -90,6 +95,11 @@ func (b *BeaconState) SetBalances(balances []uint64) {
b.balances = balances
}

func (b *BeaconState) AddBalance(balance uint64) {
b.touchedLeaves[BalancesLeafIndex] = true
b.balances = append(b.balances, balance)
}

func (b *BeaconState) SetValidatorBalance(index int, balance uint64) {
b.touchedLeaves[BalancesLeafIndex] = true
b.balances[index] = balance
Expand Down
6 changes: 6 additions & 0 deletions cmd/erigon-cl/core/state/test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,9 @@ func GetEmptyBeaconState() *BeaconState {
b.initBeaconState()
return b
}

func GetEmptyBeaconStateWithVersion(v clparams.StateVersion) *BeaconState {
b := GetEmptyBeaconState()
b.version = v
return b
}
47 changes: 47 additions & 0 deletions cmd/erigon-cl/core/transition/process_slashings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package transition

import "github.com/ledgerwatch/erigon/cl/clparams"

func (s *StateTransistor) processSlashings(slashingMultiplier uint64) error {
// Get the current epoch
epoch := s.state.Epoch()
// Get the total active balance
totalBalance, err := s.state.GetTotalActiveBalance()
if err != nil {
return err
}
// Calculate the total slashing amount
// by summing all slashings and multiplying by the provided multiplier
slashing := s.state.GetTotalSlashingAmount() * slashingMultiplier
// Adjust the total slashing amount to be no greater than the total active balance
if totalBalance < slashing {
slashing = totalBalance
}
// Apply penalties to validators who have been slashed and reached the withdrawable epoch
for i, validator := range s.state.Validators() {
if !validator.Slashed || epoch+s.beaconConfig.EpochsPerSlashingsVector/2 != validator.WithdrawableEpoch {
continue
}
// Get the effective balance increment
increment := s.beaconConfig.EffectiveBalanceIncrement
// Calculate the penalty numerator by multiplying the validator's effective balance by the total slashing amount
penaltyNumerator := validator.EffectiveBalance / increment * slashing
// Calculate the penalty by dividing the penalty numerator by the total balance and multiplying by the increment
penalty := penaltyNumerator / totalBalance * increment
// Decrease the validator's balance by the calculated penalty
s.state.DecreaseBalance(uint64(i), penalty)
}
return nil
}

func (s *StateTransistor) ProcessSlashings() error {
// Depending on the version of the state, use different multipliers
switch s.state.Version() {
case clparams.Phase0Version:
return s.processSlashings(s.beaconConfig.ProportionalSlashingMultiplier)
case clparams.AltairVersion:
return s.processSlashings(s.beaconConfig.ProportionalSlashingMultiplierAltair)
default:
return s.processSlashings(s.beaconConfig.ProportionalSlashingMultiplierBellatrix)
}
}
97 changes: 97 additions & 0 deletions cmd/erigon-cl/core/transition/process_slashings_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package transition_test

import (
"fmt"
"testing"

"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/cltypes"
"github.com/ledgerwatch/erigon/cmd/erigon-cl/core/state"
"github.com/ledgerwatch/erigon/cmd/erigon-cl/core/transition"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestProcessSlashingsNoSlash(t *testing.T) {
base := state.GetEmptyBeaconStateWithVersion(clparams.AltairVersion)
base.AddValidator(&cltypes.Validator{
Slashed: true,
})
base.AddBalance(clparams.MainnetBeaconConfig.MaxEffectiveBalance)
base.SetSlashingSegmentAt(0, 0)
base.SetSlashingSegmentAt(1, 1e9)
s := transition.New(base, &clparams.MainnetBeaconConfig, nil)
require.NoError(t, s.ProcessSlashings())
wanted := clparams.MainnetBeaconConfig.MaxEffectiveBalance
require.Equal(t, wanted, base.Balances()[0], "Unexpected slashed balance")
}

func getTestStateSlashings1() *state.BeaconState {
state := state.GetEmptyBeaconStateWithVersion(clparams.AltairVersion)
state.AddValidator(&cltypes.Validator{Slashed: true,
WithdrawableEpoch: clparams.MainnetBeaconConfig.EpochsPerSlashingsVector / 2,
EffectiveBalance: clparams.MainnetBeaconConfig.MaxEffectiveBalance})
state.AddValidator(&cltypes.Validator{ExitEpoch: clparams.MainnetBeaconConfig.FarFutureEpoch, EffectiveBalance: clparams.MainnetBeaconConfig.MaxEffectiveBalance})
state.SetBalances([]uint64{clparams.MainnetBeaconConfig.MaxEffectiveBalance, clparams.MainnetBeaconConfig.MaxEffectiveBalance})
state.SetSlashingSegmentAt(0, 0)
state.SetSlashingSegmentAt(1, 1e9)
return state
}

func getTestStateSlashings2() *state.BeaconState {
state := getTestStateSlashings1()
state.AddValidator(&cltypes.Validator{ExitEpoch: clparams.MainnetBeaconConfig.FarFutureEpoch, EffectiveBalance: clparams.MainnetBeaconConfig.MaxEffectiveBalance})
return state
}

func getTestStateSlashings3() *state.BeaconState {
state := getTestStateSlashings2()
state.SetSlashingSegmentAt(1, 2*1e9)
return state
}

func getTestStateSlashings4() *state.BeaconState {
state := getTestStateSlashings1()
state.SetValidatorAt(1, &cltypes.Validator{ExitEpoch: clparams.MainnetBeaconConfig.FarFutureEpoch, EffectiveBalance: clparams.MainnetBeaconConfig.MaxEffectiveBalance - clparams.MainnetBeaconConfig.EffectiveBalanceIncrement})
state.SetBalances([]uint64{clparams.MainnetBeaconConfig.MaxEffectiveBalance - clparams.MainnetBeaconConfig.EffectiveBalanceIncrement, clparams.MainnetBeaconConfig.MaxEffectiveBalance - clparams.MainnetBeaconConfig.EffectiveBalanceIncrement})
return state
}

func TestProcessSlashingsSlash(t *testing.T) {
tests := []struct {
state *state.BeaconState
want uint64
}{
{
state: getTestStateSlashings1(),
want: uint64(30000000000),
},
{
state: getTestStateSlashings2(),
want: uint64(31000000000),
},
{
state: getTestStateSlashings3(),
want: uint64(30000000000),
},
{
state: getTestStateSlashings4(),
want: uint64(29000000000),
},
}

for i, tt := range tests {
t.Run(fmt.Sprint(i), func(t *testing.T) {
transitistor := transition.New(tt.state, &clparams.MainnetBeaconConfig, nil)
require.NoError(t, transitistor.ProcessSlashings())
assert.Equal(t, tt.want, tt.state.Balances()[0])
})
}
}

/*

func TestProcessSlashings_SlashedLess(t *testing.T) {

}
*/
19 changes: 19 additions & 0 deletions cmd/erigon-cl/core/transition/resets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package transition

func (s *StateTransistor) ProcessEth1DataReset() {
nextEpoch := s.state.Epoch() + 1
if nextEpoch%s.beaconConfig.EpochsPerEth1VotingPeriod == 0 {
s.state.ResetEth1DataVotes()
}
}

func (s *StateTransistor) ProcessSlashingsReset() {
s.state.SetSlashingSegmentAt(int(s.state.Epoch()+1)%int(s.beaconConfig.EpochsPerSlashingsVector), 0)

}

func (s *StateTransistor) ProcessRandaoMixesReset() {
currentEpoch := s.state.Epoch()
nextEpoch := s.state.Epoch() + 1
s.state.SetRandaoMixAt(int(nextEpoch%s.beaconConfig.EpochsPerHistoricalVector), s.state.GetRandaoMixes(currentEpoch))
}
39 changes: 39 additions & 0 deletions cmd/erigon-cl/core/transition/resets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package transition_test

import (
"testing"

"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/cltypes"
"github.com/ledgerwatch/erigon/cmd/erigon-cl/core/state"
"github.com/ledgerwatch/erigon/cmd/erigon-cl/core/transition"
"github.com/stretchr/testify/require"
)

func TestProcessEth1DataReset(t *testing.T) {
testState := state.GetEmptyBeaconState()
testState.SetSlot(63 * clparams.MainnetBeaconConfig.SlotsPerEpoch)
testState.AddEth1DataVote(&cltypes.Eth1Data{})
transitioner := transition.New(testState, &clparams.MainnetBeaconConfig, nil)
transitioner.ProcessEth1DataReset()
require.Zero(t, len(testState.Eth1DataVotes()))
}

func TestProcessSlashingsReset(t *testing.T) {
testState := state.GetEmptyBeaconState()
testState.SetSlashingSegmentAt(1, 9)
testState.SetSlot(0) // Epoch 0
transitioner := transition.New(testState, &clparams.MainnetBeaconConfig, nil)
transitioner.ProcessSlashingsReset()
require.Zero(t, testState.SlashingSegmentAt(1))
}

func TestProcessRandaoMixesReset(t *testing.T) {
testState := state.GetEmptyBeaconState()
testState.SetRandaoMixAt(1, common.HexToHash("a"))
testState.SetSlot(0) // Epoch 0
transitioner := transition.New(testState, &clparams.MainnetBeaconConfig, nil)
transitioner.ProcessRandaoMixesReset()
require.Equal(t, testState.GetRandaoMixes(1), [32]byte{})
}