mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-25 04:47:18 +00:00
73443208a1
* Remove native state flag and use native state in spectests * remove feature from tests * use e2e config in slasher simulator * use params.BeaconConfig in testutil * use correct function * use minimal config in go_test * fix TestListValidators * parameterize sync committee bits and aggregation bits * Fix TestServer_ListIndexedAttestations_GenesisEpoch (cherry picked from commit 254ab623dde08ae8886b152facdbbd8889ed79db) * fix more tests * fix even more * moreeee * aaaand more * one more fix * one more * simplify TestGetAltairDuties_UnknownPubkey * comment out problematic test * one more fix * one more * aaaand one more * another * use fieldparams in HydrateBlindedBeaconBlockBodyBellatrix * create new package for mainnet tests * TestServer_GetBellatrixBeaconBlock * change slashed validator index * clear cache in reward_test.go * deprecate flag * create bazel mainnet target * move attester mainnet test to mainnet target * "fix" proposer tests * use minimal config in TestServer_circuitBreakBuilder * fix TestProposer_ProposeBlock_OK * more fixes in validator package * more fixes * more fixes * test code * move TestProposer_GetBeaconBlock_BellatrixEpoch to minimal * finally * remove proposer_bellatrix_mainnet_test.go * fix TestServer_GetBellatrixBeaconBlock_HappyCase * fix TestServer_GetBellatrixBeaconBlock_BuilderCase * Preston needs to fix this! * Revert "Preston needs to fix this!" This reverts commit b03d97a16e3080e254c7b19d7f193d3c600ca869. * remove proto state tests * fix migration tests * static analysis fix * review * remove proto state * swap state in tests * fix BUILD file in /proto/testing * remove metrics test with nil state
1250 lines
42 KiB
Go
1250 lines
42 KiB
Go
package validator
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/d4l3k/messagediff"
|
|
mockChain "github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain/testing"
|
|
"github.com/prysmaticlabs/prysm/v3/beacon-chain/cache/depositcache"
|
|
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
|
|
mockExecution "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/testing"
|
|
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
|
state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native"
|
|
mockstategen "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/stategen/mock"
|
|
mockSync "github.com/prysmaticlabs/prysm/v3/beacon-chain/sync/initial-sync/testing"
|
|
"github.com/prysmaticlabs/prysm/v3/config/params"
|
|
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
|
"github.com/prysmaticlabs/prysm/v3/container/trie"
|
|
"github.com/prysmaticlabs/prysm/v3/crypto/bls"
|
|
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
|
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
|
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
|
"github.com/prysmaticlabs/prysm/v3/testing/util"
|
|
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
func TestValidatorStatus_DepositedEth1(t *testing.T) {
|
|
ctx := context.Background()
|
|
deposits, _, err := util.DeterministicDepositsAndKeys(1)
|
|
require.NoError(t, err, "Could not generate deposits and keys")
|
|
deposit := deposits[0]
|
|
pubKey1 := deposit.Data.PublicKey
|
|
depositTrie, err := trie.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err, "Could not setup deposit trie")
|
|
depositCache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
root, err := depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, root))
|
|
height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix()
|
|
p := &mockExecution.Chain{
|
|
TimesByHeight: map[int]uint64{
|
|
0: uint64(height),
|
|
},
|
|
}
|
|
stateObj, err := state_native.InitializeFromProtoUnsafePhase0(ðpb.BeaconState{})
|
|
require.NoError(t, err)
|
|
vs := &Server{
|
|
DepositFetcher: depositCache,
|
|
BlockFetcher: p,
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: stateObj,
|
|
},
|
|
Eth1InfoFetcher: p,
|
|
}
|
|
req := ðpb.ValidatorStatusRequest{
|
|
PublicKey: pubKey1,
|
|
}
|
|
resp, err := vs.ValidatorStatus(context.Background(), req)
|
|
require.NoError(t, err, "Could not get validator status")
|
|
assert.Equal(t, ethpb.ValidatorStatus_DEPOSITED, resp.Status)
|
|
}
|
|
|
|
func TestValidatorStatus_Deposited(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
pubKey1 := pubKey(1)
|
|
depData := ðpb.Deposit_Data{
|
|
Amount: params.BeaconConfig().MaxEffectiveBalance,
|
|
PublicKey: pubKey1,
|
|
Signature: bytesutil.PadTo([]byte("hi"), 96),
|
|
WithdrawalCredentials: bytesutil.PadTo([]byte("hey"), 32),
|
|
}
|
|
deposit := ðpb.Deposit{
|
|
Data: depData,
|
|
}
|
|
depositTrie, err := trie.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err, "Could not setup deposit trie")
|
|
depositCache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
root, err := depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, root))
|
|
height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix()
|
|
p := &mockExecution.Chain{
|
|
TimesByHeight: map[int]uint64{
|
|
0: uint64(height),
|
|
},
|
|
}
|
|
stateObj, err := state_native.InitializeFromProtoUnsafePhase0(ðpb.BeaconState{
|
|
Validators: []*ethpb.Validator{
|
|
{
|
|
PublicKey: pubKey1,
|
|
ActivationEligibilityEpoch: 1,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
vs := &Server{
|
|
DepositFetcher: depositCache,
|
|
BlockFetcher: p,
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: stateObj,
|
|
},
|
|
Eth1InfoFetcher: p,
|
|
}
|
|
req := ðpb.ValidatorStatusRequest{
|
|
PublicKey: pubKey1,
|
|
}
|
|
resp, err := vs.ValidatorStatus(context.Background(), req)
|
|
require.NoError(t, err, "Could not get validator status")
|
|
assert.Equal(t, ethpb.ValidatorStatus_DEPOSITED, resp.Status)
|
|
}
|
|
|
|
func TestValidatorStatus_PartiallyDeposited(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
pubKey1 := pubKey(1)
|
|
depData := ðpb.Deposit_Data{
|
|
Amount: params.BeaconConfig().MinDepositAmount,
|
|
PublicKey: pubKey1,
|
|
Signature: []byte("hi"),
|
|
WithdrawalCredentials: []byte("hey"),
|
|
}
|
|
deposit := ðpb.Deposit{
|
|
Data: depData,
|
|
}
|
|
depositTrie, err := trie.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err, "Could not setup deposit trie")
|
|
depositCache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
root, err := depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, root))
|
|
height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix()
|
|
p := &mockExecution.Chain{
|
|
TimesByHeight: map[int]uint64{
|
|
0: uint64(height),
|
|
},
|
|
}
|
|
stateObj, err := state_native.InitializeFromProtoUnsafePhase0(ðpb.BeaconState{
|
|
Validators: []*ethpb.Validator{
|
|
{
|
|
PublicKey: pubKey1,
|
|
ActivationEligibilityEpoch: 1,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
vs := &Server{
|
|
DepositFetcher: depositCache,
|
|
BlockFetcher: p,
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: stateObj,
|
|
},
|
|
Eth1InfoFetcher: p,
|
|
}
|
|
req := ðpb.ValidatorStatusRequest{
|
|
PublicKey: pubKey1,
|
|
}
|
|
resp, err := vs.ValidatorStatus(context.Background(), req)
|
|
require.NoError(t, err, "Could not get validator status")
|
|
assert.Equal(t, ethpb.ValidatorStatus_PARTIALLY_DEPOSITED, resp.Status)
|
|
}
|
|
|
|
func TestValidatorStatus_Pending(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
pubKey := pubKey(1)
|
|
block := util.NewBeaconBlock()
|
|
genesisRoot, err := block.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
// Pending active because activation epoch is still defaulted at far future slot.
|
|
st, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
require.NoError(t, st.SetSlot(5000))
|
|
err = st.SetValidators([]*ethpb.Validator{
|
|
{
|
|
ActivationEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKey,
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
depData := ðpb.Deposit_Data{
|
|
PublicKey: pubKey,
|
|
Signature: bytesutil.PadTo([]byte("hi"), 96),
|
|
WithdrawalCredentials: bytesutil.PadTo([]byte("hey"), 32),
|
|
}
|
|
|
|
deposit := ðpb.Deposit{
|
|
Data: depData,
|
|
}
|
|
depositTrie, err := trie.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err, "Could not setup deposit trie")
|
|
depositCache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
root, err := depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, root))
|
|
|
|
height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix()
|
|
p := &mockExecution.Chain{
|
|
TimesByHeight: map[int]uint64{
|
|
0: uint64(height),
|
|
},
|
|
}
|
|
vs := &Server{
|
|
ChainStartFetcher: p,
|
|
BlockFetcher: p,
|
|
Eth1InfoFetcher: p,
|
|
DepositFetcher: depositCache,
|
|
HeadFetcher: &mockChain.ChainService{State: st, Root: genesisRoot[:]},
|
|
}
|
|
req := ðpb.ValidatorStatusRequest{
|
|
PublicKey: pubKey,
|
|
}
|
|
resp, err := vs.ValidatorStatus(context.Background(), req)
|
|
require.NoError(t, err, "Could not get validator status")
|
|
assert.Equal(t, ethpb.ValidatorStatus_PENDING, resp.Status)
|
|
}
|
|
|
|
func TestValidatorStatus_Exiting(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
pubKey := pubKey(1)
|
|
|
|
// Initiated exit because validator exit epoch and withdrawable epoch are not FAR_FUTURE_EPOCH
|
|
slot := types.Slot(10000)
|
|
epoch := slots.ToEpoch(slot)
|
|
exitEpoch := helpers.ActivationExitEpoch(epoch)
|
|
withdrawableEpoch := exitEpoch + params.BeaconConfig().MinValidatorWithdrawabilityDelay
|
|
block := util.NewBeaconBlock()
|
|
genesisRoot, err := block.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
|
|
st := ðpb.BeaconState{
|
|
Slot: slot,
|
|
Validators: []*ethpb.Validator{{
|
|
PublicKey: pubKey,
|
|
ActivationEpoch: 0,
|
|
ExitEpoch: exitEpoch,
|
|
WithdrawableEpoch: withdrawableEpoch},
|
|
}}
|
|
stateObj, err := state_native.InitializeFromProtoUnsafePhase0(st)
|
|
require.NoError(t, err)
|
|
depData := ðpb.Deposit_Data{
|
|
PublicKey: pubKey,
|
|
Signature: bytesutil.PadTo([]byte("hi"), 96),
|
|
WithdrawalCredentials: bytesutil.PadTo([]byte("hey"), 32),
|
|
}
|
|
|
|
deposit := ðpb.Deposit{
|
|
Data: depData,
|
|
}
|
|
depositTrie, err := trie.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err, "Could not setup deposit trie")
|
|
depositCache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
root, err := depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, root))
|
|
height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix()
|
|
p := &mockExecution.Chain{
|
|
TimesByHeight: map[int]uint64{
|
|
0: uint64(height),
|
|
},
|
|
}
|
|
vs := &Server{
|
|
ChainStartFetcher: p,
|
|
BlockFetcher: p,
|
|
Eth1InfoFetcher: p,
|
|
DepositFetcher: depositCache,
|
|
HeadFetcher: &mockChain.ChainService{State: stateObj, Root: genesisRoot[:]},
|
|
}
|
|
req := ðpb.ValidatorStatusRequest{
|
|
PublicKey: pubKey,
|
|
}
|
|
resp, err := vs.ValidatorStatus(context.Background(), req)
|
|
require.NoError(t, err, "Could not get validator status")
|
|
assert.Equal(t, ethpb.ValidatorStatus_EXITING, resp.Status)
|
|
}
|
|
|
|
func TestValidatorStatus_Slashing(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
pubKey := pubKey(1)
|
|
|
|
// Exit slashed because slashed is true, exit epoch is =< current epoch and withdrawable epoch > epoch .
|
|
slot := types.Slot(10000)
|
|
epoch := slots.ToEpoch(slot)
|
|
block := util.NewBeaconBlock()
|
|
genesisRoot, err := block.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
|
|
st := ðpb.BeaconState{
|
|
Slot: slot,
|
|
Validators: []*ethpb.Validator{{
|
|
Slashed: true,
|
|
PublicKey: pubKey,
|
|
WithdrawableEpoch: epoch + 1},
|
|
}}
|
|
stateObj, err := state_native.InitializeFromProtoUnsafePhase0(st)
|
|
require.NoError(t, err)
|
|
depData := ðpb.Deposit_Data{
|
|
PublicKey: pubKey,
|
|
Signature: bytesutil.PadTo([]byte("hi"), 96),
|
|
WithdrawalCredentials: bytesutil.PadTo([]byte("hey"), 32),
|
|
}
|
|
|
|
deposit := ðpb.Deposit{
|
|
Data: depData,
|
|
}
|
|
depositTrie, err := trie.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err, "Could not setup deposit trie")
|
|
depositCache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
root, err := depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, root))
|
|
height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix()
|
|
p := &mockExecution.Chain{
|
|
TimesByHeight: map[int]uint64{
|
|
0: uint64(height),
|
|
},
|
|
}
|
|
vs := &Server{
|
|
ChainStartFetcher: p,
|
|
Eth1InfoFetcher: p,
|
|
DepositFetcher: depositCache,
|
|
BlockFetcher: p,
|
|
HeadFetcher: &mockChain.ChainService{State: stateObj, Root: genesisRoot[:]},
|
|
}
|
|
req := ðpb.ValidatorStatusRequest{
|
|
PublicKey: pubKey,
|
|
}
|
|
resp, err := vs.ValidatorStatus(context.Background(), req)
|
|
require.NoError(t, err, "Could not get validator status")
|
|
assert.Equal(t, ethpb.ValidatorStatus_EXITED, resp.Status)
|
|
}
|
|
|
|
func TestValidatorStatus_Exited(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
pubKey := pubKey(1)
|
|
|
|
// Exit because only exit epoch is =< current epoch.
|
|
slot := types.Slot(10000)
|
|
epoch := slots.ToEpoch(slot)
|
|
block := util.NewBeaconBlock()
|
|
genesisRoot, err := block.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
st, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
require.NoError(t, st.SetSlot(slot))
|
|
err = st.SetValidators([]*ethpb.Validator{{
|
|
PublicKey: pubKey,
|
|
WithdrawableEpoch: epoch + 1,
|
|
WithdrawalCredentials: make([]byte, 32)},
|
|
})
|
|
require.NoError(t, err)
|
|
depData := ðpb.Deposit_Data{
|
|
PublicKey: pubKey,
|
|
Signature: bytesutil.PadTo([]byte("hi"), 96),
|
|
WithdrawalCredentials: bytesutil.PadTo([]byte("hey"), 32),
|
|
}
|
|
|
|
deposit := ðpb.Deposit{
|
|
Data: depData,
|
|
}
|
|
depositTrie, err := trie.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err, "Could not setup deposit trie")
|
|
depositCache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
root, err := depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, root))
|
|
height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix()
|
|
p := &mockExecution.Chain{
|
|
TimesByHeight: map[int]uint64{
|
|
0: uint64(height),
|
|
},
|
|
}
|
|
vs := &Server{
|
|
ChainStartFetcher: p,
|
|
Eth1InfoFetcher: p,
|
|
BlockFetcher: p,
|
|
DepositFetcher: depositCache,
|
|
HeadFetcher: &mockChain.ChainService{State: st, Root: genesisRoot[:]},
|
|
}
|
|
req := ðpb.ValidatorStatusRequest{
|
|
PublicKey: pubKey,
|
|
}
|
|
resp, err := vs.ValidatorStatus(context.Background(), req)
|
|
require.NoError(t, err, "Could not get validator status")
|
|
assert.Equal(t, ethpb.ValidatorStatus_EXITED, resp.Status)
|
|
}
|
|
|
|
func TestValidatorStatus_UnknownStatus(t *testing.T) {
|
|
pubKey := pubKey(1)
|
|
depositCache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
stateObj, err := state_native.InitializeFromProtoUnsafePhase0(ðpb.BeaconState{
|
|
Slot: 0,
|
|
})
|
|
require.NoError(t, err)
|
|
vs := &Server{
|
|
DepositFetcher: depositCache,
|
|
Eth1InfoFetcher: &mockExecution.Chain{},
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: stateObj,
|
|
},
|
|
}
|
|
req := ðpb.ValidatorStatusRequest{
|
|
PublicKey: pubKey,
|
|
}
|
|
resp, err := vs.ValidatorStatus(context.Background(), req)
|
|
require.NoError(t, err, "Could not get validator status")
|
|
assert.Equal(t, ethpb.ValidatorStatus_UNKNOWN_STATUS, resp.Status)
|
|
}
|
|
|
|
func TestActivationStatus_OK(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
deposits, _, err := util.DeterministicDepositsAndKeys(4)
|
|
require.NoError(t, err)
|
|
pubKeys := [][]byte{deposits[0].Data.PublicKey, deposits[1].Data.PublicKey, deposits[2].Data.PublicKey, deposits[3].Data.PublicKey}
|
|
stateObj, err := state_native.InitializeFromProtoUnsafePhase0(ðpb.BeaconState{
|
|
Slot: 4000,
|
|
Validators: []*ethpb.Validator{
|
|
{
|
|
ActivationEpoch: 0,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[0],
|
|
},
|
|
{
|
|
ActivationEpoch: 0,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[1],
|
|
},
|
|
{
|
|
ActivationEligibilityEpoch: 700,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[3],
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
block := util.NewBeaconBlock()
|
|
genesisRoot, err := block.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
dep := deposits[0]
|
|
depositTrie, err := trie.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err, "Could not setup deposit trie")
|
|
depositCache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
root, err := depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(ctx, dep, 10 /*blockNum*/, 0, root))
|
|
|
|
dep = deposits[2]
|
|
assert.NoError(t, depositTrie.Insert(dep.Data.Signature, 15))
|
|
root, err = depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(context.Background(), dep, 0, 1, root))
|
|
|
|
vs := &Server{
|
|
Ctx: context.Background(),
|
|
ChainStartFetcher: &mockExecution.Chain{},
|
|
BlockFetcher: &mockExecution.Chain{},
|
|
Eth1InfoFetcher: &mockExecution.Chain{},
|
|
DepositFetcher: depositCache,
|
|
HeadFetcher: &mockChain.ChainService{State: stateObj, Root: genesisRoot[:]},
|
|
}
|
|
activeExists, response, err := vs.activationStatus(context.Background(), pubKeys)
|
|
require.NoError(t, err)
|
|
require.Equal(t, true, activeExists, "No activated validator exists when there was supposed to be 2")
|
|
if response[0].Status.Status != ethpb.ValidatorStatus_ACTIVE {
|
|
t.Errorf("Validator with pubkey %#x is not activated and instead has this status: %s",
|
|
response[0].PublicKey, response[0].Status.Status.String())
|
|
}
|
|
if response[0].Index != 0 {
|
|
t.Errorf("Validator with pubkey %#x is expected to have index %d, received %d", response[0].PublicKey, 0, response[0].Index)
|
|
}
|
|
|
|
if response[1].Status.Status != ethpb.ValidatorStatus_ACTIVE {
|
|
t.Errorf("Validator with pubkey %#x was activated when not supposed to",
|
|
response[1].PublicKey)
|
|
}
|
|
if response[1].Index != 1 {
|
|
t.Errorf("Validator with pubkey %#x is expected to have index %d, received %d", response[1].PublicKey, 1, response[1].Index)
|
|
}
|
|
|
|
if response[2].Status.Status != ethpb.ValidatorStatus_DEPOSITED {
|
|
t.Errorf("Validator with pubkey %#x is not unknown and instead has this status: %s",
|
|
response[2].PublicKey, response[2].Status.Status.String())
|
|
}
|
|
if uint64(response[2].Index) != uint64(params.BeaconConfig().FarFutureEpoch) {
|
|
t.Errorf("Validator with pubkey %#x is expected to have index %d, received %d", response[2].PublicKey, params.BeaconConfig().FarFutureEpoch, response[2].Index)
|
|
}
|
|
|
|
if response[3].Status.Status != ethpb.ValidatorStatus_DEPOSITED {
|
|
t.Errorf("Validator with pubkey %#x was not deposited and has this status: %s",
|
|
response[3].PublicKey, response[3].Status.Status.String())
|
|
}
|
|
if response[3].Index != 2 {
|
|
t.Errorf("Validator with pubkey %#x is expected to have index %d, received %d", response[3].PublicKey, 2, response[3].Index)
|
|
}
|
|
}
|
|
|
|
func TestOptimisticStatus(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
server := &Server{OptimisticModeFetcher: &mockChain.ChainService{}, TimeFetcher: &mockChain.ChainService{}}
|
|
err := server.optimisticStatus(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
cfg := params.BeaconConfig().Copy()
|
|
cfg.BellatrixForkEpoch = 2
|
|
params.OverrideBeaconConfig(cfg)
|
|
|
|
server = &Server{OptimisticModeFetcher: &mockChain.ChainService{Optimistic: true}, TimeFetcher: &mockChain.ChainService{}}
|
|
err = server.optimisticStatus(context.Background())
|
|
s, ok := status.FromError(err)
|
|
require.Equal(t, true, ok)
|
|
require.DeepEqual(t, codes.Unavailable, s.Code())
|
|
require.ErrorContains(t, errOptimisticMode.Error(), err)
|
|
|
|
server = &Server{OptimisticModeFetcher: &mockChain.ChainService{Optimistic: false}, TimeFetcher: &mockChain.ChainService{}}
|
|
err = server.optimisticStatus(context.Background())
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestValidatorStatus_CorrectActivationQueue(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
pbKey := pubKey(5)
|
|
block := util.NewBeaconBlock()
|
|
genesisRoot, err := block.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
currentSlot := types.Slot(5000)
|
|
// Pending active because activation epoch is still defaulted at far future slot.
|
|
validators := []*ethpb.Validator{
|
|
{
|
|
ActivationEpoch: 0,
|
|
PublicKey: pubKey(0),
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
},
|
|
{
|
|
ActivationEpoch: 0,
|
|
PublicKey: pubKey(1),
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
},
|
|
{
|
|
ActivationEpoch: 0,
|
|
PublicKey: pubKey(2),
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
},
|
|
{
|
|
ActivationEpoch: 0,
|
|
PublicKey: pubKey(3),
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
},
|
|
{
|
|
ActivationEpoch: types.Epoch(currentSlot/params.BeaconConfig().SlotsPerEpoch + 1),
|
|
PublicKey: pbKey,
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
},
|
|
{
|
|
ActivationEpoch: types.Epoch(currentSlot/params.BeaconConfig().SlotsPerEpoch + 4),
|
|
PublicKey: pubKey(5),
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
},
|
|
}
|
|
st, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
require.NoError(t, st.SetValidators(validators))
|
|
require.NoError(t, st.SetSlot(currentSlot))
|
|
|
|
depositTrie, err := trie.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err, "Could not setup deposit trie")
|
|
depositCache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
for i := 0; i < 6; i++ {
|
|
depData := ðpb.Deposit_Data{
|
|
PublicKey: pubKey(uint64(i)),
|
|
Signature: bytesutil.PadTo([]byte("hi"), 96),
|
|
WithdrawalCredentials: bytesutil.PadTo([]byte("hey"), 32),
|
|
}
|
|
|
|
deposit := ðpb.Deposit{
|
|
Data: depData,
|
|
}
|
|
root, err := depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, int64(i), root))
|
|
|
|
}
|
|
|
|
height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix()
|
|
p := &mockExecution.Chain{
|
|
TimesByHeight: map[int]uint64{
|
|
0: uint64(height),
|
|
},
|
|
}
|
|
vs := &Server{
|
|
ChainStartFetcher: p,
|
|
BlockFetcher: p,
|
|
Eth1InfoFetcher: p,
|
|
DepositFetcher: depositCache,
|
|
HeadFetcher: &mockChain.ChainService{State: st, Root: genesisRoot[:]},
|
|
}
|
|
req := ðpb.ValidatorStatusRequest{
|
|
PublicKey: pbKey,
|
|
}
|
|
resp, err := vs.ValidatorStatus(context.Background(), req)
|
|
require.NoError(t, err, "Could not get validator status")
|
|
assert.Equal(t, ethpb.ValidatorStatus_PENDING, resp.Status)
|
|
assert.Equal(t, uint64(2), resp.PositionInActivationQueue, "Unexpected position in activation queue")
|
|
}
|
|
|
|
func TestMultipleValidatorStatus_Pubkeys(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
deposits, _, err := util.DeterministicDepositsAndKeys(6)
|
|
require.NoError(t, err)
|
|
pubKeys := [][]byte{
|
|
deposits[0].Data.PublicKey,
|
|
deposits[1].Data.PublicKey,
|
|
deposits[2].Data.PublicKey,
|
|
deposits[3].Data.PublicKey,
|
|
deposits[4].Data.PublicKey,
|
|
deposits[5].Data.PublicKey,
|
|
}
|
|
stateObj, err := state_native.InitializeFromProtoUnsafePhase0(ðpb.BeaconState{
|
|
Slot: 4000,
|
|
Validators: []*ethpb.Validator{
|
|
{
|
|
ActivationEpoch: 0,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[0],
|
|
},
|
|
{
|
|
ActivationEpoch: 0,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[1],
|
|
},
|
|
{
|
|
ActivationEligibilityEpoch: 700,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[3],
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
ActivationEligibilityEpoch: 700,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[4],
|
|
EffectiveBalance: params.BeaconConfig().MinDepositAmount,
|
|
},
|
|
{
|
|
ActivationEligibilityEpoch: 700,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[5],
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
block := util.NewBeaconBlock()
|
|
genesisRoot, err := block.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
depositTrie, err := trie.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err, "Could not setup deposit trie")
|
|
depositCache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
dep := deposits[0]
|
|
root, err := depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(ctx, dep, 10 /*blockNum*/, 0, root))
|
|
dep = deposits[2]
|
|
assert.NoError(t, depositTrie.Insert(dep.Data.Signature, 15))
|
|
root, err = depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(context.Background(), dep, 0, 1, root))
|
|
|
|
vs := &Server{
|
|
Ctx: context.Background(),
|
|
ChainStartFetcher: &mockExecution.Chain{},
|
|
BlockFetcher: &mockExecution.Chain{},
|
|
Eth1InfoFetcher: &mockExecution.Chain{},
|
|
DepositFetcher: depositCache,
|
|
HeadFetcher: &mockChain.ChainService{State: stateObj, Root: genesisRoot[:]},
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
}
|
|
|
|
want := []*ethpb.ValidatorStatusResponse{
|
|
{
|
|
Status: ethpb.ValidatorStatus_ACTIVE,
|
|
},
|
|
{
|
|
Status: ethpb.ValidatorStatus_ACTIVE,
|
|
},
|
|
{
|
|
Status: ethpb.ValidatorStatus_DEPOSITED,
|
|
ActivationEpoch: 18446744073709551615,
|
|
},
|
|
{
|
|
Status: ethpb.ValidatorStatus_DEPOSITED,
|
|
},
|
|
{
|
|
Status: ethpb.ValidatorStatus_PARTIALLY_DEPOSITED,
|
|
},
|
|
{
|
|
Status: ethpb.ValidatorStatus_PENDING,
|
|
},
|
|
}
|
|
|
|
req := ðpb.MultipleValidatorStatusRequest{PublicKeys: pubKeys}
|
|
response, err := vs.MultipleValidatorStatus(context.Background(), req)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, len(response.PublicKeys), len(pubKeys))
|
|
for i, resp := range response.PublicKeys {
|
|
require.DeepEqual(t, pubKeys[i], resp)
|
|
}
|
|
assert.Equal(t, len(pubKeys), len(response.Statuses))
|
|
for i, resp := range response.Statuses {
|
|
if !proto.Equal(want[i], resp) {
|
|
t.Fatalf("Wanted %v\n Recieved: %v\n", want[i], resp)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMultipleValidatorStatus_Indices(t *testing.T) {
|
|
slot := types.Slot(10000)
|
|
epoch := slots.ToEpoch(slot)
|
|
pubKeys := [][]byte{pubKey(1), pubKey(2), pubKey(3), pubKey(4), pubKey(5), pubKey(6), pubKey(7)}
|
|
beaconState := ðpb.BeaconState{
|
|
Slot: 4000,
|
|
Validators: []*ethpb.Validator{
|
|
{
|
|
ActivationEpoch: 0,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[0],
|
|
},
|
|
{
|
|
ActivationEpoch: 0,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[1],
|
|
},
|
|
{
|
|
ActivationEligibilityEpoch: 700,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[2],
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Slashed: true,
|
|
ExitEpoch: epoch + 1,
|
|
PublicKey: pubKeys[3],
|
|
},
|
|
{
|
|
ActivationEligibilityEpoch: 700,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[4],
|
|
EffectiveBalance: params.BeaconConfig().MinDepositAmount,
|
|
},
|
|
{
|
|
ActivationEligibilityEpoch: 700,
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
PublicKey: pubKeys[5],
|
|
},
|
|
},
|
|
}
|
|
stateObj, err := state_native.InitializeFromProtoUnsafePhase0(beaconState)
|
|
require.NoError(t, err)
|
|
block := util.NewBeaconBlock()
|
|
genesisRoot, err := block.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
|
|
vs := &Server{
|
|
Ctx: context.Background(),
|
|
ChainStartFetcher: &mockExecution.Chain{},
|
|
BlockFetcher: &mockExecution.Chain{},
|
|
Eth1InfoFetcher: &mockExecution.Chain{},
|
|
HeadFetcher: &mockChain.ChainService{State: stateObj, Root: genesisRoot[:]},
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
}
|
|
|
|
want := []*ethpb.ValidatorStatusResponse{
|
|
{
|
|
Status: ethpb.ValidatorStatus_ACTIVE,
|
|
},
|
|
{
|
|
Status: ethpb.ValidatorStatus_ACTIVE,
|
|
},
|
|
{
|
|
Status: ethpb.ValidatorStatus_DEPOSITED,
|
|
},
|
|
{
|
|
Status: ethpb.ValidatorStatus_SLASHING,
|
|
},
|
|
{
|
|
Status: ethpb.ValidatorStatus_PARTIALLY_DEPOSITED,
|
|
},
|
|
{
|
|
Status: ethpb.ValidatorStatus_PENDING,
|
|
},
|
|
}
|
|
|
|
// Note: Index 6 should be skipped.
|
|
req := ðpb.MultipleValidatorStatusRequest{Indices: []int64{0, 1, 2, 3, 4, 5, 6}}
|
|
response, err := vs.MultipleValidatorStatus(context.Background(), req)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, len(beaconState.Validators), len(response.PublicKeys))
|
|
for i, resp := range response.PublicKeys {
|
|
expected := beaconState.Validators[i].PublicKey
|
|
require.DeepEqual(t, expected, resp)
|
|
}
|
|
assert.Equal(t, len(beaconState.Validators), len(response.Statuses))
|
|
for i, resp := range response.Statuses {
|
|
if !proto.Equal(want[i], resp) {
|
|
t.Fatalf("Wanted %v\n Recieved: %v\n", want[i], resp)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidatorStatus_Invalid(t *testing.T) {
|
|
ctx := context.Background()
|
|
deposits, _, err := util.DeterministicDepositsAndKeys(1)
|
|
require.NoError(t, err, "Could not generate deposits and keys")
|
|
deposit := deposits[0]
|
|
pubKey1 := deposit.Data.PublicKey
|
|
deposit.Data.Signature = deposit.Data.Signature[1:]
|
|
depositTrie, err := trie.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
|
|
require.NoError(t, err, "Could not setup deposit trie")
|
|
depositCache, err := depositcache.New()
|
|
require.NoError(t, err)
|
|
|
|
root, err := depositTrie.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
assert.NoError(t, depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, root))
|
|
height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix()
|
|
p := &mockExecution.Chain{
|
|
TimesByHeight: map[int]uint64{
|
|
0: uint64(height),
|
|
},
|
|
}
|
|
stateObj, err := state_native.InitializeFromProtoUnsafePhase0(ðpb.BeaconState{})
|
|
require.NoError(t, err)
|
|
vs := &Server{
|
|
DepositFetcher: depositCache,
|
|
BlockFetcher: p,
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: stateObj,
|
|
},
|
|
Eth1InfoFetcher: p,
|
|
}
|
|
req := ðpb.ValidatorStatusRequest{
|
|
PublicKey: pubKey1,
|
|
}
|
|
resp, err := vs.ValidatorStatus(context.Background(), req)
|
|
require.NoError(t, err, "Could not get validator status")
|
|
assert.Equal(t, ethpb.ValidatorStatus_INVALID, resp.Status)
|
|
}
|
|
|
|
func Test_DepositStatus(t *testing.T) {
|
|
assert.Equal(t, depositStatus(0), ethpb.ValidatorStatus_PENDING)
|
|
assert.Equal(t, depositStatus(params.BeaconConfig().MinDepositAmount), ethpb.ValidatorStatus_PARTIALLY_DEPOSITED)
|
|
assert.Equal(t, depositStatus(params.BeaconConfig().MaxEffectiveBalance), ethpb.ValidatorStatus_DEPOSITED)
|
|
}
|
|
|
|
func TestServer_CheckDoppelGanger(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
wantErr bool
|
|
svSetup func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse)
|
|
}{
|
|
{
|
|
name: "normal doppelganger request",
|
|
wantErr: false,
|
|
svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) {
|
|
hs, ps, keys := createStateSetupAltair(t, 3)
|
|
rb := mockstategen.NewMockReplayerBuilder()
|
|
rb.SetMockStateForSlot(ps, 23)
|
|
vs := &Server{
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: hs,
|
|
},
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
ReplayerBuilder: rb,
|
|
}
|
|
request := ðpb.DoppelGangerRequest{
|
|
ValidatorRequests: make([]*ethpb.DoppelGangerRequest_ValidatorRequest, 0),
|
|
}
|
|
response := ðpb.DoppelGangerResponse{Responses: make([]*ethpb.DoppelGangerResponse_ValidatorResponse, 0)}
|
|
for i := 0; i < 3; i++ {
|
|
request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
Epoch: 1,
|
|
SignedRoot: []byte{'A'},
|
|
})
|
|
response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
DuplicateExists: false,
|
|
})
|
|
}
|
|
return vs, request, response
|
|
},
|
|
},
|
|
{
|
|
name: "doppelganger exists current epoch",
|
|
wantErr: false,
|
|
svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) {
|
|
hs, ps, keys := createStateSetupAltair(t, 3)
|
|
rb := mockstategen.NewMockReplayerBuilder()
|
|
rb.SetMockStateForSlot(ps, 23)
|
|
currentIndices := make([]byte, 64)
|
|
currentIndices[2] = 1
|
|
require.NoError(t, hs.SetCurrentParticipationBits(currentIndices))
|
|
|
|
vs := &Server{
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: hs,
|
|
},
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
ReplayerBuilder: rb,
|
|
}
|
|
request := ðpb.DoppelGangerRequest{
|
|
ValidatorRequests: make([]*ethpb.DoppelGangerRequest_ValidatorRequest, 0),
|
|
}
|
|
response := ðpb.DoppelGangerResponse{Responses: make([]*ethpb.DoppelGangerResponse_ValidatorResponse, 0)}
|
|
for i := 0; i < 2; i++ {
|
|
request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
Epoch: 1,
|
|
SignedRoot: []byte{'A'},
|
|
})
|
|
response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
DuplicateExists: false,
|
|
})
|
|
}
|
|
|
|
// Add in for duplicate validator
|
|
request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{
|
|
PublicKey: keys[2].PublicKey().Marshal(),
|
|
Epoch: 0,
|
|
SignedRoot: []byte{'A'},
|
|
})
|
|
response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{
|
|
PublicKey: keys[2].PublicKey().Marshal(),
|
|
DuplicateExists: true,
|
|
})
|
|
return vs, request, response
|
|
},
|
|
},
|
|
{
|
|
name: "doppelganger exists previous epoch",
|
|
wantErr: false,
|
|
svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) {
|
|
hs, ps, keys := createStateSetupAltair(t, 3)
|
|
prevIndices := make([]byte, 64)
|
|
prevIndices[2] = 1
|
|
require.NoError(t, ps.SetPreviousParticipationBits(prevIndices))
|
|
rb := mockstategen.NewMockReplayerBuilder()
|
|
rb.SetMockStateForSlot(ps, 23)
|
|
|
|
vs := &Server{
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: hs,
|
|
},
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
ReplayerBuilder: rb,
|
|
}
|
|
request := ðpb.DoppelGangerRequest{
|
|
ValidatorRequests: make([]*ethpb.DoppelGangerRequest_ValidatorRequest, 0),
|
|
}
|
|
response := ðpb.DoppelGangerResponse{Responses: make([]*ethpb.DoppelGangerResponse_ValidatorResponse, 0)}
|
|
for i := 0; i < 2; i++ {
|
|
request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
Epoch: 1,
|
|
SignedRoot: []byte{'A'},
|
|
})
|
|
response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
DuplicateExists: false,
|
|
})
|
|
}
|
|
|
|
// Add in for duplicate validator
|
|
request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{
|
|
PublicKey: keys[2].PublicKey().Marshal(),
|
|
Epoch: 0,
|
|
SignedRoot: []byte{'A'},
|
|
})
|
|
response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{
|
|
PublicKey: keys[2].PublicKey().Marshal(),
|
|
DuplicateExists: true,
|
|
})
|
|
return vs, request, response
|
|
},
|
|
},
|
|
{
|
|
name: "multiple doppelganger exists",
|
|
wantErr: false,
|
|
svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) {
|
|
hs, ps, keys := createStateSetupAltair(t, 3)
|
|
currentIndices := make([]byte, 64)
|
|
currentIndices[10] = 1
|
|
currentIndices[11] = 2
|
|
require.NoError(t, hs.SetPreviousParticipationBits(currentIndices))
|
|
rb := mockstategen.NewMockReplayerBuilder()
|
|
rb.SetMockStateForSlot(ps, 23)
|
|
|
|
prevIndices := make([]byte, 64)
|
|
for i := 12; i < 20; i++ {
|
|
prevIndices[i] = 1
|
|
}
|
|
require.NoError(t, ps.SetCurrentParticipationBits(prevIndices))
|
|
|
|
vs := &Server{
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: hs,
|
|
},
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
ReplayerBuilder: rb,
|
|
}
|
|
request := ðpb.DoppelGangerRequest{
|
|
ValidatorRequests: make([]*ethpb.DoppelGangerRequest_ValidatorRequest, 0),
|
|
}
|
|
response := ðpb.DoppelGangerResponse{Responses: make([]*ethpb.DoppelGangerResponse_ValidatorResponse, 0)}
|
|
for i := 10; i < 15; i++ {
|
|
// Add in for duplicate validator
|
|
request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
Epoch: 0,
|
|
SignedRoot: []byte{'A'},
|
|
})
|
|
response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
DuplicateExists: true,
|
|
})
|
|
}
|
|
for i := 15; i < 20; i++ {
|
|
request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
Epoch: 3,
|
|
SignedRoot: []byte{'A'},
|
|
})
|
|
response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
DuplicateExists: false,
|
|
})
|
|
}
|
|
|
|
return vs, request, response
|
|
},
|
|
},
|
|
{
|
|
name: "attesters are too recent",
|
|
wantErr: false,
|
|
svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) {
|
|
hs, ps, keys := createStateSetupAltair(t, 3)
|
|
rb := mockstategen.NewMockReplayerBuilder()
|
|
rb.SetMockStateForSlot(ps, 23)
|
|
currentIndices := make([]byte, 64)
|
|
currentIndices[0] = 1
|
|
currentIndices[1] = 2
|
|
require.NoError(t, hs.SetPreviousParticipationBits(currentIndices))
|
|
vs := &Server{
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: hs,
|
|
},
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
ReplayerBuilder: rb,
|
|
}
|
|
request := ðpb.DoppelGangerRequest{
|
|
ValidatorRequests: make([]*ethpb.DoppelGangerRequest_ValidatorRequest, 0),
|
|
}
|
|
response := ðpb.DoppelGangerResponse{Responses: make([]*ethpb.DoppelGangerResponse_ValidatorResponse, 0)}
|
|
for i := 0; i < 15; i++ {
|
|
request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
Epoch: 2,
|
|
SignedRoot: []byte{'A'},
|
|
})
|
|
response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
DuplicateExists: false,
|
|
})
|
|
}
|
|
|
|
return vs, request, response
|
|
},
|
|
},
|
|
{
|
|
name: "attesters are too recent(previous state)",
|
|
wantErr: false,
|
|
svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) {
|
|
hs, ps, keys := createStateSetupAltair(t, 3)
|
|
rb := mockstategen.NewMockReplayerBuilder()
|
|
rb.SetMockStateForSlot(ps, 23)
|
|
currentIndices := make([]byte, 64)
|
|
currentIndices[0] = 1
|
|
currentIndices[1] = 2
|
|
require.NoError(t, ps.SetPreviousParticipationBits(currentIndices))
|
|
vs := &Server{
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: hs,
|
|
},
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
ReplayerBuilder: rb,
|
|
}
|
|
request := ðpb.DoppelGangerRequest{
|
|
ValidatorRequests: make([]*ethpb.DoppelGangerRequest_ValidatorRequest, 0),
|
|
}
|
|
response := ðpb.DoppelGangerResponse{Responses: make([]*ethpb.DoppelGangerResponse_ValidatorResponse, 0)}
|
|
for i := 0; i < 15; i++ {
|
|
request.ValidatorRequests = append(request.ValidatorRequests, ðpb.DoppelGangerRequest_ValidatorRequest{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
Epoch: 1,
|
|
SignedRoot: []byte{'A'},
|
|
})
|
|
response.Responses = append(response.Responses, ðpb.DoppelGangerResponse_ValidatorResponse{
|
|
PublicKey: keys[i].PublicKey().Marshal(),
|
|
DuplicateExists: false,
|
|
})
|
|
}
|
|
|
|
return vs, request, response
|
|
},
|
|
},
|
|
{
|
|
name: "exit early for Phase 0",
|
|
wantErr: false,
|
|
svSetup: func(t *testing.T) (*Server, *ethpb.DoppelGangerRequest, *ethpb.DoppelGangerResponse) {
|
|
hs, _, keys := createStateSetupPhase0(t, 3)
|
|
|
|
vs := &Server{
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: hs,
|
|
},
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
}
|
|
request := ðpb.DoppelGangerRequest{
|
|
ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{
|
|
{
|
|
PublicKey: keys[0].PublicKey().Marshal(),
|
|
Epoch: 1,
|
|
SignedRoot: []byte{'A'},
|
|
},
|
|
},
|
|
}
|
|
response := ðpb.DoppelGangerResponse{
|
|
Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{
|
|
{
|
|
PublicKey: keys[0].PublicKey().Marshal(),
|
|
DuplicateExists: false,
|
|
},
|
|
},
|
|
}
|
|
|
|
return vs, request, response
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
vs, req, resp := tt.svSetup(t)
|
|
got, err := vs.CheckDoppelGanger(context.Background(), req)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("CheckDoppelGanger() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, resp) {
|
|
diff, _ := messagediff.PrettyDiff(resp, got)
|
|
t.Errorf("CheckDoppelGanger() difference = %v", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func createStateSetupPhase0(t *testing.T, head types.Epoch) (state.BeaconState,
|
|
state.BeaconState, []bls.SecretKey) {
|
|
gs, keys := util.DeterministicGenesisState(t, 64)
|
|
hs := gs.Copy()
|
|
|
|
// Head State
|
|
headSlot := types.Slot(head)*params.BeaconConfig().SlotsPerEpoch + params.BeaconConfig().SlotsPerEpoch/2
|
|
assert.NoError(t, hs.SetSlot(headSlot))
|
|
|
|
// Previous Epoch State
|
|
prevSlot := headSlot - params.BeaconConfig().SlotsPerEpoch
|
|
ps := gs.Copy()
|
|
assert.NoError(t, ps.SetSlot(prevSlot))
|
|
|
|
return hs, ps, keys
|
|
}
|
|
|
|
func createStateSetupAltair(t *testing.T, head types.Epoch) (state.BeaconState,
|
|
state.BeaconState, []bls.SecretKey) {
|
|
gs, keys := util.DeterministicGenesisStateAltair(t, 64)
|
|
hs := gs.Copy()
|
|
|
|
// Head State
|
|
headSlot := types.Slot(head)*params.BeaconConfig().SlotsPerEpoch + params.BeaconConfig().SlotsPerEpoch/2
|
|
assert.NoError(t, hs.SetSlot(headSlot))
|
|
|
|
// Previous Epoch State
|
|
prevSlot := headSlot - params.BeaconConfig().SlotsPerEpoch
|
|
ps := gs.Copy()
|
|
assert.NoError(t, ps.SetSlot(prevSlot))
|
|
|
|
return hs, ps, keys
|
|
}
|