Skip to content
Open
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
13 changes: 12 additions & 1 deletion oracle/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"sort"
"strconv"

"fmt"
Expand Down Expand Up @@ -300,7 +301,16 @@ func (or *Oracle) ValidatorCleanup(slot uint64) error {
// Iterate over all validators. If two or more validators exit or get slashed in the same slot,
// this cleanup will eventually set both of their pending rewards to 0 and share them among the pool
rewardsToDistribute := big.NewInt(0)
for _, validator := range validatorInfo {

keys := make([]int, 0, len(validatorInfo))
for k := range validatorInfo {
keys = append(keys, int(k))
}
sort.Ints(keys)

for _, k := range keys {
validator := validatorInfo[phase0.ValidatorIndex(k)]

// If a validator is subscribed but not active onchain, we have to unsubscribe it and treat it as a ban:
// this means setting the validator rewards to 0 and sharing them among the pool
idx := uint64(validator.Index)
Expand All @@ -316,6 +326,7 @@ func (or *Oracle) ValidatorCleanup(slot uint64) error {
}).Info("Cleaning up validator")

or.advanceStateMachine(idx, Unsubscribe)

// sourceToTarget map will only be populated if the oracle is past the Electra fork and there are pending consolidations
if targetIdx, ok := sourceToTarget[idx]; ok {
log.WithFields(log.Fields{
Expand Down
101 changes: 101 additions & 0 deletions oracle/oracle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3330,6 +3330,107 @@ func Test_ValidatorCleanup_Consolidations(t *testing.T) {
require.Equal(t, NotSubscribed, oracle.state.Validators[80].ValidatorStatus)
})

t.Run("Test8: Consolidation from not subscribed validators", func(t *testing.T) {
oracle := NewOracle(&Config{Network: "mainnet"})
oracle.state.Validators[80] = &ValidatorInfo{PendingRewardsWei: big.NewInt(999), ValidatorStatus: NotSubscribed}
oracle.state.Validators[81] = &ValidatorInfo{PendingRewardsWei: big.NewInt(0), ValidatorStatus: NotSubscribed}

oracle.SetGetSetOfValidatorsFunc(func(_ []phase0.ValidatorIndex, _ string, _ ...retry.Option) (map[phase0.ValidatorIndex]*v1.Validator, error) {
return map[phase0.ValidatorIndex]*v1.Validator{
50: {Index: 80, Status: v1.ValidatorStateExitedUnslashed},
60: {Index: 81, Status: v1.ValidatorStateExitedUnslashed},
}, nil
})
oracle.GetPendingConsolidationsFunc(func(stateID string, opts ...retry.Option) (*PendingConsolidationsResponse, error) {
return &PendingConsolidationsResponse{Data: []PendingConsolidation{
{SourceIndex: 80, TargetIndex: 81},
}}, nil
})

err := oracle.ValidatorCleanup(mainnetElectra + 1)
require.NoError(t, err)
require.Equal(t, big.NewInt(0), oracle.state.Validators[80].PendingRewardsWei)
require.Equal(t, big.NewInt(0), oracle.state.Validators[81].PendingRewardsWei)
require.Equal(t, big.NewInt(999), oracle.state.PoolAccumulatedFees)
require.Equal(t, NotSubscribed, oracle.state.Validators[80].ValidatorStatus)
})

t.Run("Test8: Consolidation from banned to not subscribed", func(t *testing.T) {
oracle := NewOracle(&Config{Network: "mainnet"})
oracle.state.Validators[80] = &ValidatorInfo{PendingRewardsWei: big.NewInt(999), ValidatorStatus: Banned}
oracle.state.Validators[81] = &ValidatorInfo{PendingRewardsWei: big.NewInt(0), ValidatorStatus: NotSubscribed}

oracle.SetGetSetOfValidatorsFunc(func(_ []phase0.ValidatorIndex, _ string, _ ...retry.Option) (map[phase0.ValidatorIndex]*v1.Validator, error) {
return map[phase0.ValidatorIndex]*v1.Validator{
50: {Index: 80, Status: v1.ValidatorStateExitedUnslashed},
60: {Index: 81, Status: v1.ValidatorStateExitedUnslashed},
}, nil
})
oracle.GetPendingConsolidationsFunc(func(stateID string, opts ...retry.Option) (*PendingConsolidationsResponse, error) {
return &PendingConsolidationsResponse{Data: []PendingConsolidation{
{SourceIndex: 80, TargetIndex: 81},
}}, nil
})

err := oracle.ValidatorCleanup(mainnetElectra + 1)
require.NoError(t, err)
require.Equal(t, big.NewInt(0), oracle.state.Validators[80].PendingRewardsWei)
require.Equal(t, big.NewInt(999), oracle.state.PoolAccumulatedFees)
require.Equal(t, Banned, oracle.state.Validators[80].ValidatorStatus)
})

t.Run("Test9: Consolidation from notsubscribed to banned", func(t *testing.T) {
oracle := NewOracle(&Config{Network: "mainnet"})
oracle.state.Validators[80] = &ValidatorInfo{PendingRewardsWei: big.NewInt(999), ValidatorStatus: Banned}
oracle.state.Validators[85] = &ValidatorInfo{PendingRewardsWei: big.NewInt(0), ValidatorStatus: NotSubscribed}

oracle.SetGetSetOfValidatorsFunc(func(_ []phase0.ValidatorIndex, _ string, _ ...retry.Option) (map[phase0.ValidatorIndex]*v1.Validator, error) {
return map[phase0.ValidatorIndex]*v1.Validator{
50: {Index: 80, Status: v1.ValidatorStateExitedUnslashed},
60: {Index: 85, Status: v1.ValidatorStateExitedUnslashed},
}, nil
})
oracle.GetPendingConsolidationsFunc(func(stateID string, opts ...retry.Option) (*PendingConsolidationsResponse, error) {
return &PendingConsolidationsResponse{Data: []PendingConsolidation{
{SourceIndex: 80, TargetIndex: 85},
}}, nil
})

err := oracle.ValidatorCleanup(mainnetElectra + 1)
require.NoError(t, err)
require.Equal(t, big.NewInt(0), oracle.state.Validators[80].PendingRewardsWei)
require.Equal(t, big.NewInt(0), oracle.state.Validators[85].PendingRewardsWei)
require.Equal(t, big.NewInt(999), oracle.state.PoolAccumulatedFees)
require.Equal(t, Banned, oracle.state.Validators[80].ValidatorStatus)
require.Equal(t, NotSubscribed, oracle.state.Validators[85].ValidatorStatus)
})

t.Run("Test10: Consolidation from Banned to Banned", func(t *testing.T) {
oracle := NewOracle(&Config{Network: "mainnet"})
oracle.state.Validators[80] = &ValidatorInfo{PendingRewardsWei: big.NewInt(999), ValidatorStatus: Banned}
oracle.state.Validators[85] = &ValidatorInfo{PendingRewardsWei: big.NewInt(0), ValidatorStatus: Banned}

oracle.SetGetSetOfValidatorsFunc(func(_ []phase0.ValidatorIndex, _ string, _ ...retry.Option) (map[phase0.ValidatorIndex]*v1.Validator, error) {
return map[phase0.ValidatorIndex]*v1.Validator{
50: {Index: 80, Status: v1.ValidatorStateExitedUnslashed},
60: {Index: 85, Status: v1.ValidatorStateActiveExiting},
}, nil
})
oracle.GetPendingConsolidationsFunc(func(stateID string, opts ...retry.Option) (*PendingConsolidationsResponse, error) {
return &PendingConsolidationsResponse{Data: []PendingConsolidation{
{SourceIndex: 80, TargetIndex: 85},
}}, nil
})

err := oracle.ValidatorCleanup(mainnetElectra + 1)
require.NoError(t, err)
require.Equal(t, big.NewInt(0), oracle.state.Validators[80].PendingRewardsWei)
require.Equal(t, big.NewInt(0), oracle.state.Validators[85].PendingRewardsWei)
require.Equal(t, big.NewInt(999), oracle.state.PoolAccumulatedFees)
require.Equal(t, Banned, oracle.state.Validators[80].ValidatorStatus)
require.Equal(t, Banned, oracle.state.Validators[85].ValidatorStatus)
})

}

func Test_increaseValidatorPendingRewards(t *testing.T) {
Expand Down