mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-23 03:51:29 +00:00
856102cba8
* Implement bound on expected withdrawals This PR implements https://github.com/ethereum/consensus-specs/pull/3095 * Terence's suggestion Co-authored-by: terencechain <terence@prysmaticlabs.com> * Update spectests * fix minimal preset * Clear committee cache during header tests to ensure active validators ain't leaking * Rm debug log * Rm bad init * Update consensus spec sha * Fix bad tests and remove redundant casting * Fix build * MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP typo Co-authored-by: terencechain <terence@prysmaticlabs.com> Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
366 lines
13 KiB
Go
366 lines
13 KiB
Go
package state_native
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/prysmaticlabs/prysm/v3/config/params"
|
|
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
|
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
|
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v3/runtime/version"
|
|
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
|
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
|
)
|
|
|
|
func TestNextWithdrawalIndex(t *testing.T) {
|
|
t.Run("ok", func(t *testing.T) {
|
|
s := BeaconState{version: version.Capella, nextWithdrawalIndex: 123}
|
|
i, err := s.NextWithdrawalIndex()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, uint64(123), i)
|
|
})
|
|
t.Run("version before Capella not supported", func(t *testing.T) {
|
|
s := BeaconState{version: version.Bellatrix}
|
|
_, err := s.NextWithdrawalIndex()
|
|
assert.ErrorContains(t, "NextWithdrawalIndex is not supported", err)
|
|
})
|
|
}
|
|
|
|
func TestLastWithdrawalValidatorIndex(t *testing.T) {
|
|
t.Run("ok", func(t *testing.T) {
|
|
s := BeaconState{version: version.Capella, nextWithdrawalValidatorIndex: 123}
|
|
i, err := s.NextWithdrawalValidatorIndex()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, types.ValidatorIndex(123), i)
|
|
})
|
|
t.Run("version before Capella not supported", func(t *testing.T) {
|
|
s := BeaconState{version: version.Bellatrix}
|
|
_, err := s.NextWithdrawalValidatorIndex()
|
|
assert.ErrorContains(t, "NextWithdrawalValidatorIndex is not supported", err)
|
|
})
|
|
}
|
|
|
|
func TestHasETH1WithdrawalCredentials(t *testing.T) {
|
|
creds := []byte{0xFA, 0xCC}
|
|
v := ðpb.Validator{WithdrawalCredentials: creds}
|
|
require.Equal(t, false, hasETH1WithdrawalCredential(v))
|
|
creds = []byte{params.BeaconConfig().ETH1AddressWithdrawalPrefixByte, 0xCC}
|
|
v = ðpb.Validator{WithdrawalCredentials: creds}
|
|
require.Equal(t, true, hasETH1WithdrawalCredential(v))
|
|
// No Withdrawal cred
|
|
v = ðpb.Validator{}
|
|
require.Equal(t, false, hasETH1WithdrawalCredential(v))
|
|
}
|
|
|
|
func TestIsFullyWithdrawableValidator(t *testing.T) {
|
|
// No ETH1 prefix
|
|
creds := []byte{0xFA, 0xCC}
|
|
v := ðpb.Validator{
|
|
WithdrawalCredentials: creds,
|
|
WithdrawableEpoch: 2,
|
|
}
|
|
require.Equal(t, false, isFullyWithdrawableValidator(v, 3))
|
|
// Wrong withdrawable epoch
|
|
creds = []byte{params.BeaconConfig().ETH1AddressWithdrawalPrefixByte, 0xCC}
|
|
v = ðpb.Validator{
|
|
WithdrawalCredentials: creds,
|
|
WithdrawableEpoch: 2,
|
|
}
|
|
require.Equal(t, false, isFullyWithdrawableValidator(v, 1))
|
|
// Fully withdrawable
|
|
creds = []byte{params.BeaconConfig().ETH1AddressWithdrawalPrefixByte, 0xCC}
|
|
v = ðpb.Validator{
|
|
WithdrawalCredentials: creds,
|
|
WithdrawableEpoch: 2,
|
|
}
|
|
require.Equal(t, true, isFullyWithdrawableValidator(v, 3))
|
|
}
|
|
|
|
func TestIsPartiallyWithdrawableValidator(t *testing.T) {
|
|
// No ETH1 prefix
|
|
creds := []byte{0xFA, 0xCC}
|
|
v, err := NewValidator(ðpb.Validator{
|
|
WithdrawalCredentials: creds,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, false, v.IsPartiallyWithdrawable(params.BeaconConfig().MaxEffectiveBalance+1))
|
|
// Not the right effective balance
|
|
creds = []byte{params.BeaconConfig().ETH1AddressWithdrawalPrefixByte, 0xCC}
|
|
v, err = NewValidator(ðpb.Validator{
|
|
WithdrawalCredentials: creds,
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance - 1,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, false, v.IsPartiallyWithdrawable(params.BeaconConfig().MaxEffectiveBalance+1))
|
|
// Not enough balance
|
|
creds = []byte{params.BeaconConfig().ETH1AddressWithdrawalPrefixByte, 0xCC}
|
|
v, err = NewValidator(ðpb.Validator{
|
|
WithdrawalCredentials: creds,
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, false, v.IsPartiallyWithdrawable(params.BeaconConfig().MaxEffectiveBalance))
|
|
// Partially Withdrawable
|
|
creds = []byte{params.BeaconConfig().ETH1AddressWithdrawalPrefixByte, 0xCC}
|
|
v, err = NewValidator(ðpb.Validator{
|
|
WithdrawalCredentials: creds,
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, v.IsPartiallyWithdrawable(params.BeaconConfig().MaxEffectiveBalance+1))
|
|
}
|
|
|
|
func TestExpectedWithdrawals(t *testing.T) {
|
|
t.Run("no withdrawals", func(t *testing.T) {
|
|
s := BeaconState{
|
|
version: version.Capella,
|
|
validators: make([]*ethpb.Validator, 100),
|
|
balances: make([]uint64, 100),
|
|
}
|
|
for i := range s.validators {
|
|
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
|
val := ðpb.Validator{
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
WithdrawableEpoch: types.Epoch(1),
|
|
}
|
|
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
|
s.validators[i] = val
|
|
}
|
|
expected, err := s.ExpectedWithdrawals()
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(expected))
|
|
})
|
|
t.Run("one fully withdrawable", func(t *testing.T) {
|
|
s := BeaconState{
|
|
version: version.Capella,
|
|
validators: make([]*ethpb.Validator, 100),
|
|
balances: make([]uint64, 100),
|
|
nextWithdrawalValidatorIndex: 20,
|
|
}
|
|
for i := range s.validators {
|
|
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
|
val := ðpb.Validator{
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
WithdrawableEpoch: types.Epoch(1),
|
|
}
|
|
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
|
s.validators[i] = val
|
|
}
|
|
s.validators[3].WithdrawableEpoch = types.Epoch(0)
|
|
expected, err := s.ExpectedWithdrawals()
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(expected))
|
|
withdrawal := &enginev1.Withdrawal{
|
|
Index: 0,
|
|
ValidatorIndex: 3,
|
|
Address: s.validators[3].WithdrawalCredentials[12:],
|
|
Amount: s.balances[3],
|
|
}
|
|
require.DeepEqual(t, withdrawal, expected[0])
|
|
})
|
|
t.Run("one partially withdrawable", func(t *testing.T) {
|
|
s := BeaconState{
|
|
version: version.Capella,
|
|
validators: make([]*ethpb.Validator, 100),
|
|
balances: make([]uint64, 100),
|
|
}
|
|
for i := range s.validators {
|
|
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
|
val := ðpb.Validator{
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
WithdrawableEpoch: types.Epoch(1),
|
|
}
|
|
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
|
s.validators[i] = val
|
|
}
|
|
s.balances[3] += params.BeaconConfig().MinDepositAmount
|
|
expected, err := s.ExpectedWithdrawals()
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(expected))
|
|
withdrawal := &enginev1.Withdrawal{
|
|
Index: 0,
|
|
ValidatorIndex: 3,
|
|
Address: s.validators[3].WithdrawalCredentials[12:],
|
|
Amount: params.BeaconConfig().MinDepositAmount,
|
|
}
|
|
require.DeepEqual(t, withdrawal, expected[0])
|
|
})
|
|
t.Run("one partially and one fully withdrawable", func(t *testing.T) {
|
|
s := BeaconState{
|
|
version: version.Capella,
|
|
validators: make([]*ethpb.Validator, 100),
|
|
balances: make([]uint64, 100),
|
|
}
|
|
for i := range s.validators {
|
|
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
|
val := ðpb.Validator{
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
WithdrawableEpoch: types.Epoch(1),
|
|
}
|
|
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
|
val.WithdrawalCredentials[31] = byte(i)
|
|
s.validators[i] = val
|
|
}
|
|
s.balances[3] += params.BeaconConfig().MinDepositAmount
|
|
s.validators[7].WithdrawableEpoch = types.Epoch(0)
|
|
expected, err := s.ExpectedWithdrawals()
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(expected))
|
|
|
|
withdrawalFull := &enginev1.Withdrawal{
|
|
Index: 1,
|
|
ValidatorIndex: 7,
|
|
Address: s.validators[7].WithdrawalCredentials[12:],
|
|
Amount: s.balances[7],
|
|
}
|
|
withdrawalPartial := &enginev1.Withdrawal{
|
|
Index: 0,
|
|
ValidatorIndex: 3,
|
|
Address: s.validators[3].WithdrawalCredentials[12:],
|
|
Amount: params.BeaconConfig().MinDepositAmount,
|
|
}
|
|
require.DeepEqual(t, withdrawalPartial, expected[0])
|
|
require.DeepEqual(t, withdrawalFull, expected[1])
|
|
})
|
|
t.Run("all partially withdrawable", func(t *testing.T) {
|
|
s := BeaconState{
|
|
version: version.Capella,
|
|
validators: make([]*ethpb.Validator, 100),
|
|
balances: make([]uint64, 100),
|
|
}
|
|
for i := range s.validators {
|
|
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance + 1
|
|
val := ðpb.Validator{
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
WithdrawableEpoch: types.Epoch(1),
|
|
}
|
|
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
|
s.validators[i] = val
|
|
}
|
|
expected, err := s.ExpectedWithdrawals()
|
|
require.NoError(t, err)
|
|
require.Equal(t, params.BeaconConfig().MaxWithdrawalsPerPayload, uint64(len(expected)))
|
|
withdrawal := &enginev1.Withdrawal{
|
|
Index: 0,
|
|
ValidatorIndex: 0,
|
|
Address: s.validators[0].WithdrawalCredentials[12:],
|
|
Amount: 1,
|
|
}
|
|
require.DeepEqual(t, withdrawal, expected[0])
|
|
})
|
|
t.Run("all fully withdrawable", func(t *testing.T) {
|
|
s := BeaconState{
|
|
version: version.Capella,
|
|
validators: make([]*ethpb.Validator, 100),
|
|
balances: make([]uint64, 100),
|
|
}
|
|
for i := range s.validators {
|
|
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
|
val := ðpb.Validator{
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
WithdrawableEpoch: types.Epoch(0),
|
|
}
|
|
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
|
s.validators[i] = val
|
|
}
|
|
expected, err := s.ExpectedWithdrawals()
|
|
require.NoError(t, err)
|
|
require.Equal(t, params.BeaconConfig().MaxWithdrawalsPerPayload, uint64(len(expected)))
|
|
withdrawal := &enginev1.Withdrawal{
|
|
Index: 0,
|
|
ValidatorIndex: 0,
|
|
Address: s.validators[0].WithdrawalCredentials[12:],
|
|
Amount: params.BeaconConfig().MaxEffectiveBalance,
|
|
}
|
|
require.DeepEqual(t, withdrawal, expected[0])
|
|
})
|
|
t.Run("all fully and partially withdrawable", func(t *testing.T) {
|
|
s := BeaconState{
|
|
version: version.Capella,
|
|
validators: make([]*ethpb.Validator, 100),
|
|
balances: make([]uint64, 100),
|
|
}
|
|
for i := range s.validators {
|
|
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance + 1
|
|
val := ðpb.Validator{
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
WithdrawableEpoch: types.Epoch(0),
|
|
}
|
|
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
|
s.validators[i] = val
|
|
}
|
|
expected, err := s.ExpectedWithdrawals()
|
|
require.NoError(t, err)
|
|
require.Equal(t, params.BeaconConfig().MaxWithdrawalsPerPayload, uint64(len(expected)))
|
|
withdrawal := &enginev1.Withdrawal{
|
|
Index: 0,
|
|
ValidatorIndex: 0,
|
|
Address: s.validators[0].WithdrawalCredentials[12:],
|
|
Amount: params.BeaconConfig().MaxEffectiveBalance + 1,
|
|
}
|
|
require.DeepEqual(t, withdrawal, expected[0])
|
|
})
|
|
t.Run("one fully withdrawable but zero balance", func(t *testing.T) {
|
|
s := BeaconState{
|
|
version: version.Capella,
|
|
validators: make([]*ethpb.Validator, 100),
|
|
balances: make([]uint64, 100),
|
|
nextWithdrawalValidatorIndex: 20,
|
|
}
|
|
for i := range s.validators {
|
|
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
|
val := ðpb.Validator{
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
WithdrawableEpoch: types.Epoch(1),
|
|
}
|
|
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
|
s.validators[i] = val
|
|
}
|
|
s.validators[3].WithdrawableEpoch = types.Epoch(0)
|
|
s.balances[3] = 0
|
|
expected, err := s.ExpectedWithdrawals()
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(expected))
|
|
})
|
|
t.Run("one partially withdrawable, one above sweep bound", func(t *testing.T) {
|
|
s := BeaconState{
|
|
version: version.Capella,
|
|
validators: make([]*ethpb.Validator, 100),
|
|
balances: make([]uint64, 100),
|
|
}
|
|
for i := range s.validators {
|
|
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
|
val := ðpb.Validator{
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
WithdrawableEpoch: types.Epoch(1),
|
|
}
|
|
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
|
s.validators[i] = val
|
|
}
|
|
s.balances[3] += params.BeaconConfig().MinDepositAmount
|
|
s.balances[10] += params.BeaconConfig().MinDepositAmount
|
|
saved := params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep
|
|
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = 10
|
|
expected, err := s.ExpectedWithdrawals()
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(expected))
|
|
withdrawal := &enginev1.Withdrawal{
|
|
Index: 0,
|
|
ValidatorIndex: 3,
|
|
Address: s.validators[3].WithdrawalCredentials[12:],
|
|
Amount: params.BeaconConfig().MinDepositAmount,
|
|
}
|
|
require.DeepEqual(t, withdrawal, expected[0])
|
|
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = saved
|
|
})
|
|
}
|