mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 11:41:19 +00:00
Adding InititateValidatorExit (#6478)
Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#initiate_validator_exit Part of https://github.com/ledgerwatch/erigon/issues/5965 Co-authored-by: Giulio rebuffo <giulio.rebuffo@gmail.com> Co-authored-by: ledgerwatch <akhounov@gmail.com> Co-authored-by: Alex Sharp <alexsharp@Alexs-MacBook-Pro-2.local>
This commit is contained in:
parent
1a09dcbdb3
commit
d595accc63
71
cmd/erigon-cl/core/transition/mutators.go
Normal file
71
cmd/erigon-cl/core/transition/mutators.go
Normal file
@ -0,0 +1,71 @@
|
||||
package transition
|
||||
|
||||
import (
|
||||
"github.com/ledgerwatch/erigon/cmd/erigon-cl/core/state"
|
||||
)
|
||||
|
||||
const (
|
||||
FAR_FUTURE_EPOCH = (1<<64 - 1)
|
||||
MAX_SEED_LOOKAHEAD = 4
|
||||
MIN_PER_EPOCH_CHURN_LIMIT = 4
|
||||
CHURN_LIMIT_QUOTIENT = (1 << 16)
|
||||
MIN_VALIDATOR_WITHDRAWABILITY_DELAY = (1 << 8)
|
||||
)
|
||||
|
||||
func IncreaseBalance(state *state.BeaconState, index, delta uint64) {
|
||||
state.Balances()[index] += delta
|
||||
}
|
||||
|
||||
func DecreaseBalance(state *state.BeaconState, index, delta uint64) {
|
||||
curAmount := state.Balances()[index]
|
||||
if curAmount < delta {
|
||||
state.Balances()[index] = 0
|
||||
return
|
||||
}
|
||||
state.Balances()[index] -= delta
|
||||
}
|
||||
|
||||
func ComputeActivationExitEpoch(epoch uint64) uint64 {
|
||||
return epoch + 1 + MAX_SEED_LOOKAHEAD
|
||||
}
|
||||
|
||||
func GetValidtorChurnLimit(state *state.BeaconState) uint64 {
|
||||
inds := GetActiveValidatorIndices(state, GetEpochAtSlot(state.Slot()))
|
||||
churnLimit := len(inds) / CHURN_LIMIT_QUOTIENT
|
||||
if churnLimit > MIN_PER_EPOCH_CHURN_LIMIT {
|
||||
return uint64(churnLimit)
|
||||
}
|
||||
return MIN_PER_EPOCH_CHURN_LIMIT
|
||||
}
|
||||
|
||||
func InitiateValidatorExit(state *state.BeaconState, index uint64) {
|
||||
validator := state.ValidatorAt(int(index))
|
||||
if validator.ExitEpoch != FAR_FUTURE_EPOCH {
|
||||
return
|
||||
}
|
||||
|
||||
currentEpoch := GetEpochAtSlot(state.Slot())
|
||||
exitQueueEpoch := currentEpoch
|
||||
activationExitEpoch := ComputeActivationExitEpoch(currentEpoch)
|
||||
for _, v := range state.Validators() {
|
||||
if v.ExitEpoch != FAR_FUTURE_EPOCH {
|
||||
potentialExit := v.ExitEpoch + activationExitEpoch
|
||||
if potentialExit > exitQueueEpoch {
|
||||
exitQueueEpoch = potentialExit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exitQueueChurn := 0
|
||||
for _, v := range state.Validators() {
|
||||
if v.ExitEpoch == exitQueueEpoch {
|
||||
exitQueueChurn += 1
|
||||
}
|
||||
}
|
||||
if exitQueueChurn >= int(GetValidtorChurnLimit(state)) {
|
||||
exitQueueEpoch += 1
|
||||
}
|
||||
|
||||
validator.ExitEpoch = exitQueueEpoch
|
||||
validator.WithdrawableEpoch = exitQueueEpoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||
}
|
136
cmd/erigon-cl/core/transition/mutators_test.go
Normal file
136
cmd/erigon-cl/core/transition/mutators_test.go
Normal file
@ -0,0 +1,136 @@
|
||||
package transition
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ledgerwatch/erigon/cl/cltypes"
|
||||
"github.com/ledgerwatch/erigon/cmd/erigon-cl/core/state"
|
||||
)
|
||||
|
||||
const (
|
||||
testExitEpoch = 53
|
||||
)
|
||||
|
||||
func getTestStateBalances(t *testing.T) *state.BeaconState {
|
||||
numVals := uint64(2048)
|
||||
balances := make([]uint64, numVals)
|
||||
for i := uint64(0); i < numVals; i++ {
|
||||
balances[i] = i
|
||||
}
|
||||
return state.FromBellatrixState(&cltypes.BeaconStateBellatrix{
|
||||
Balances: balances,
|
||||
})
|
||||
}
|
||||
|
||||
func getTestStateValidators(t *testing.T, numVals int) *state.BeaconState {
|
||||
validators := make([]*cltypes.Validator, numVals)
|
||||
for i := 0; i < numVals; i++ {
|
||||
validators[i] = &cltypes.Validator{
|
||||
ActivationEpoch: 0,
|
||||
ExitEpoch: testExitEpoch,
|
||||
}
|
||||
}
|
||||
return state.FromBellatrixState(&cltypes.BeaconStateBellatrix{
|
||||
Slot: testExitEpoch * SLOTS_PER_EPOCH,
|
||||
Validators: validators,
|
||||
})
|
||||
}
|
||||
|
||||
func TestIncreaseBalance(t *testing.T) {
|
||||
state := getTestStateBalances(t)
|
||||
testInd := uint64(42)
|
||||
amount := uint64(100)
|
||||
beforeBalance := state.Balances()[testInd]
|
||||
IncreaseBalance(state, testInd, amount)
|
||||
afterBalance := state.Balances()[testInd]
|
||||
if afterBalance != beforeBalance+amount {
|
||||
t.Errorf("unepected after balance: %d, before balance: %d, increase: %d", afterBalance, beforeBalance, amount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecreaseBalance(t *testing.T) {
|
||||
sampleState := getTestStateBalances(t)
|
||||
testInd := uint64(42)
|
||||
beforeBalance := sampleState.Balances()[testInd]
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
delta uint64
|
||||
expectedBalance uint64
|
||||
}{
|
||||
{
|
||||
description: "zero_remaining",
|
||||
delta: beforeBalance,
|
||||
expectedBalance: 0,
|
||||
},
|
||||
{
|
||||
description: "non_zero_remaining",
|
||||
delta: 1,
|
||||
expectedBalance: beforeBalance - 1,
|
||||
},
|
||||
{
|
||||
description: "underflow",
|
||||
delta: beforeBalance + 1,
|
||||
expectedBalance: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
state := getTestStateBalances(t)
|
||||
DecreaseBalance(state, testInd, tc.delta)
|
||||
afterBalance := state.Balances()[testInd]
|
||||
if afterBalance != tc.expectedBalance {
|
||||
t.Errorf("unexpected resulting balance: got %d, want %d", afterBalance, tc.expectedBalance)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitiatieValidatorExit(t *testing.T) {
|
||||
exitDelay := uint64(testExitEpoch + MAX_SEED_LOOKAHEAD + 1)
|
||||
testCases := []struct {
|
||||
description string
|
||||
numValidators uint64
|
||||
expectedExitEpoch uint64
|
||||
expectedWithdrawlableEpoch uint64
|
||||
validator *cltypes.Validator
|
||||
}{
|
||||
{
|
||||
description: "success",
|
||||
numValidators: 3,
|
||||
expectedExitEpoch: testExitEpoch + exitDelay,
|
||||
expectedWithdrawlableEpoch: testExitEpoch + exitDelay + MIN_VALIDATOR_WITHDRAWABILITY_DELAY,
|
||||
validator: &cltypes.Validator{
|
||||
ExitEpoch: FAR_FUTURE_EPOCH,
|
||||
ActivationEpoch: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "exit_epoch_set",
|
||||
numValidators: 3,
|
||||
expectedExitEpoch: testExitEpoch,
|
||||
expectedWithdrawlableEpoch: testExitEpoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY,
|
||||
validator: &cltypes.Validator{
|
||||
ExitEpoch: testExitEpoch,
|
||||
WithdrawableEpoch: testExitEpoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY,
|
||||
ActivationEpoch: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
state := getTestStateValidators(t, int(tc.numValidators))
|
||||
state.SetValidators(append(state.Validators(), tc.validator))
|
||||
testInd := uint64(len(state.Validators()) - 1)
|
||||
InitiateValidatorExit(state, testInd)
|
||||
val := state.ValidatorAt(int(testInd))
|
||||
if val.ExitEpoch != tc.expectedExitEpoch {
|
||||
t.Errorf("unexpected exit epoch: got %d, want %d", val.ExitEpoch, tc.expectedExitEpoch)
|
||||
}
|
||||
if val.WithdrawableEpoch != tc.expectedWithdrawlableEpoch {
|
||||
t.Errorf("unexpected withdrawable epoch: got %d, want %d", val.WithdrawableEpoch, tc.expectedWithdrawlableEpoch)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user