2020-05-29 02:51:00 +00:00
package evaluators
import (
2020-10-19 19:35:34 +00:00
"bytes"
2020-05-29 02:51:00 +00:00
"context"
"fmt"
2020-06-11 06:38:15 +00:00
"math"
2022-11-19 03:40:32 +00:00
"strings"
2023-10-11 14:08:06 +00:00
"github.com/ethereum/go-ethereum/common/hexutil"
2020-05-29 02:51:00 +00:00
"github.com/pkg/errors"
2023-10-11 14:08:06 +00:00
"github.com/prysmaticlabs/prysm/v4/api/client/beacon"
2023-03-17 18:52:56 +00:00
corehelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/signing"
2023-10-11 14:08:06 +00:00
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
2023-03-17 18:52:56 +00:00
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/encoding/ssz/detect"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/endtoend/helpers"
e2e "github.com/prysmaticlabs/prysm/v4/testing/endtoend/params"
"github.com/prysmaticlabs/prysm/v4/testing/endtoend/policies"
e2etypes "github.com/prysmaticlabs/prysm/v4/testing/endtoend/types"
"github.com/prysmaticlabs/prysm/v4/testing/util"
2020-05-29 02:51:00 +00:00
"golang.org/x/exp/rand"
"google.golang.org/grpc"
2021-05-17 18:32:04 +00:00
"google.golang.org/protobuf/types/known/emptypb"
2020-05-29 02:51:00 +00:00
)
2020-08-01 17:22:53 +00:00
var depositValCount = e2e . DepositCount
2023-02-10 06:19:15 +00:00
var numOfExits = 2
2020-08-01 17:22:53 +00:00
// Deposits should be processed in twice the length of the epochs per eth1 voting period.
2022-12-13 23:13:49 +00:00
var depositsInBlockStart = params . E2ETestConfig ( ) . EpochsPerEth1VotingPeriod * 2
2020-08-02 21:30:59 +00:00
// deposits included + finalization + MaxSeedLookahead for activation.
var depositActivationStartEpoch = depositsInBlockStart + 2 + params . E2ETestConfig ( ) . MaxSeedLookahead
2023-10-05 14:37:11 +00:00
var depositEndEpoch = depositActivationStartEpoch + primitives . Epoch ( math . Ceil ( float64 ( depositValCount ) / float64 ( params . E2ETestConfig ( ) . MinPerEpochChurnLimit ) ) )
2023-02-23 11:04:53 +00:00
var exitSubmissionEpoch = primitives . Epoch ( 7 )
2020-08-01 17:22:53 +00:00
// ProcessesDepositsInBlocks ensures the expected amount of deposits are accepted into blocks.
2021-02-09 10:05:22 +00:00
var ProcessesDepositsInBlocks = e2etypes . Evaluator {
2020-08-01 17:22:53 +00:00
Name : "processes_deposits_in_blocks_epoch_%d" ,
2020-10-19 19:35:34 +00:00
Policy : policies . OnEpoch ( depositsInBlockStart ) , // We expect all deposits to enter in one epoch.
2020-08-01 17:22:53 +00:00
Evaluation : processesDepositsInBlocks ,
}
2020-06-11 06:38:15 +00:00
2020-12-04 23:15:12 +00:00
// VerifyBlockGraffiti ensures the block graffiti is one of the random list.
2021-02-09 10:05:22 +00:00
var VerifyBlockGraffiti = e2etypes . Evaluator {
2020-12-04 23:15:12 +00:00
Name : "verify_graffiti_in_blocks_epoch_%d" ,
Policy : policies . AfterNthEpoch ( 0 ) ,
Evaluation : verifyGraffitiInBlocks ,
}
2020-08-01 17:22:53 +00:00
// ActivatesDepositedValidators ensures the expected amount of validator deposits are activated into the state.
2021-02-09 10:05:22 +00:00
var ActivatesDepositedValidators = e2etypes . Evaluator {
2020-05-29 02:51:00 +00:00
Name : "processes_deposit_validators_epoch_%d" ,
2020-10-19 19:35:34 +00:00
Policy : policies . BetweenEpochs ( depositActivationStartEpoch , depositEndEpoch ) ,
2020-08-01 17:22:53 +00:00
Evaluation : activatesDepositedValidators ,
2020-05-29 02:51:00 +00:00
}
// DepositedValidatorsAreActive ensures the expected amount of validators are active after their deposits are processed.
2021-02-09 10:05:22 +00:00
var DepositedValidatorsAreActive = e2etypes . Evaluator {
2020-05-29 02:51:00 +00:00
Name : "deposited_validators_are_active_epoch_%d" ,
2020-10-19 19:35:34 +00:00
Policy : policies . AfterNthEpoch ( depositEndEpoch ) ,
2020-05-29 02:51:00 +00:00
Evaluation : depositedValidatorsAreActive ,
}
// ProposeVoluntaryExit sends a voluntary exit from randomly selected validator in the genesis set.
2021-02-09 10:05:22 +00:00
var ProposeVoluntaryExit = e2etypes . Evaluator {
2020-05-29 02:51:00 +00:00
Name : "propose_voluntary_exit_epoch_%d" ,
2023-02-23 11:04:53 +00:00
Policy : policies . OnEpoch ( exitSubmissionEpoch ) ,
2020-05-29 02:51:00 +00:00
Evaluation : proposeVoluntaryExit ,
}
2022-11-19 03:40:32 +00:00
// ValidatorsHaveExited checks the beacon state for the exited validator and ensures its marked as exited.
var ValidatorsHaveExited = e2etypes . Evaluator {
2020-05-29 02:51:00 +00:00
Name : "voluntary_has_exited_%d" ,
2020-10-19 19:35:34 +00:00
Policy : policies . OnEpoch ( 8 ) ,
2022-11-19 03:40:32 +00:00
Evaluation : validatorsHaveExited ,
2020-05-29 02:51:00 +00:00
}
2023-02-10 06:19:15 +00:00
// SubmitWithdrawal sends a withdrawal from a previously exited validator.
var SubmitWithdrawal = e2etypes . Evaluator {
Name : "submit_withdrawal_epoch_%d" ,
Policy : policies . BetweenEpochs ( helpers . CapellaE2EForkEpoch - 2 , helpers . CapellaE2EForkEpoch + 1 ) ,
Evaluation : submitWithdrawal ,
}
// ValidatorsHaveWithdrawn checks the beacon state for the withdrawn validator and ensures it has been withdrawn.
var ValidatorsHaveWithdrawn = e2etypes . Evaluator {
2023-02-23 11:04:53 +00:00
Name : "validator_has_withdrawn_%d" ,
Policy : func ( currentEpoch primitives . Epoch ) bool {
// Determine the withdrawal epoch by using the max seed lookahead. This value
// differs for our minimal and mainnet config which is why we calculate it
// each time the policy is executed.
validWithdrawnEpoch := exitSubmissionEpoch + 1 + params . BeaconConfig ( ) . MaxSeedLookahead
// Only run this for minimal setups after capella
if params . BeaconConfig ( ) . ConfigName == params . EndToEndName {
validWithdrawnEpoch = helpers . CapellaE2EForkEpoch + 1
}
requiredPolicy := policies . OnEpoch ( validWithdrawnEpoch )
return requiredPolicy ( currentEpoch )
} ,
2023-02-10 06:19:15 +00:00
Evaluation : validatorsAreWithdrawn ,
}
2020-10-19 19:35:34 +00:00
// ValidatorsVoteWithTheMajority verifies whether validator vote for eth1data using the majority algorithm.
2021-02-09 10:05:22 +00:00
var ValidatorsVoteWithTheMajority = e2etypes . Evaluator {
2020-10-19 19:35:34 +00:00
Name : "validators_vote_with_the_majority_%d" ,
Policy : policies . AfterNthEpoch ( 0 ) ,
Evaluation : validatorsVoteWithTheMajority ,
2020-05-29 02:51:00 +00:00
}
2022-11-19 03:40:32 +00:00
type mismatch struct {
k [ 48 ] byte
e uint64
o uint64
}
func ( m mismatch ) String ( ) string {
return fmt . Sprintf ( "(%#x:%d:%d)" , m . k , m . e , m . o )
}
2023-01-06 07:49:42 +00:00
func processesDepositsInBlocks ( ec * e2etypes . EvaluationContext , conns ... * grpc . ClientConn ) error {
2022-11-19 03:40:32 +00:00
expected := ec . Balances ( e2etypes . PostGenesisDepositBatch )
2020-08-01 17:22:53 +00:00
conn := conns [ 0 ]
2021-09-10 19:59:43 +00:00
client := ethpb . NewBeaconChainClient ( conn )
2021-05-17 18:32:04 +00:00
chainHead , err := client . GetChainHead ( context . Background ( ) , & emptypb . Empty { } )
2020-08-01 17:22:53 +00:00
if err != nil {
return errors . Wrap ( err , "failed to get chain head" )
}
2021-09-10 19:59:43 +00:00
req := & ethpb . ListBlocksRequest { QueryFilter : & ethpb . ListBlocksRequest_Epoch { Epoch : chainHead . HeadEpoch - 1 } }
2021-09-07 15:17:50 +00:00
blks , err := client . ListBeaconBlocks ( context . Background ( ) , req )
2020-08-01 17:22:53 +00:00
if err != nil {
return errors . Wrap ( err , "failed to get blocks from beacon-chain" )
}
2022-11-19 03:40:32 +00:00
observed := make ( map [ [ 48 ] byte ] uint64 )
2020-08-01 17:22:53 +00:00
for _ , blk := range blks . BlockContainers {
2022-11-19 03:40:32 +00:00
sb , err := blocks . BeaconBlockContainerToSignedBeaconBlock ( blk )
if err != nil {
return errors . Wrap ( err , "failed to convert api response type to SignedBeaconBlock interface" )
2021-09-07 15:17:50 +00:00
}
2022-11-19 03:40:32 +00:00
b := sb . Block ( )
deposits := b . Body ( ) . Deposits ( )
for _ , d := range deposits {
k := bytesutil . ToBytes48 ( d . Data . PublicKey )
v := observed [ k ]
observed [ k ] = v + d . Data . Amount
}
}
mismatches := [ ] string { }
for k , ev := range expected {
ov := observed [ k ]
if ev != ov {
mismatches = append ( mismatches , mismatch { k : k , e : ev , o : ov } . String ( ) )
}
2020-08-01 17:22:53 +00:00
}
2022-11-19 03:40:32 +00:00
if len ( mismatches ) != 0 {
return fmt . Errorf ( "not all expected deposits observed on chain, len(expected)=%d, len(observed)=%d, mismatches=%d; details(key:expected:observed): %s" , len ( expected ) , len ( observed ) , len ( mismatches ) , strings . Join ( mismatches , "," ) )
2020-08-01 17:22:53 +00:00
}
return nil
}
2023-01-06 07:49:42 +00:00
func verifyGraffitiInBlocks ( _ * e2etypes . EvaluationContext , conns ... * grpc . ClientConn ) error {
2020-12-04 23:15:12 +00:00
conn := conns [ 0 ]
2021-09-10 19:59:43 +00:00
client := ethpb . NewBeaconChainClient ( conn )
2021-05-17 18:32:04 +00:00
chainHead , err := client . GetChainHead ( context . Background ( ) , & emptypb . Empty { } )
2020-12-04 23:15:12 +00:00
if err != nil {
return errors . Wrap ( err , "failed to get chain head" )
}
2022-12-13 23:13:49 +00:00
begin := chainHead . HeadEpoch
// Prevent underflow when this runs at epoch 0.
if begin > 0 {
begin = begin . Sub ( 1 )
}
req := & ethpb . ListBlocksRequest { QueryFilter : & ethpb . ListBlocksRequest_Epoch { Epoch : begin } }
2021-09-07 15:17:50 +00:00
blks , err := client . ListBeaconBlocks ( context . Background ( ) , req )
2020-12-04 23:15:12 +00:00
if err != nil {
return errors . Wrap ( err , "failed to get blocks from beacon-chain" )
}
2021-09-10 19:59:43 +00:00
for _ , ctr := range blks . BlockContainers {
2022-11-19 03:40:32 +00:00
blk , err := blocks . BeaconBlockContainerToSignedBeaconBlock ( ctr )
2021-09-10 19:59:43 +00:00
if err != nil {
return err
2021-09-07 15:17:50 +00:00
}
2021-09-10 19:59:43 +00:00
var e bool
slot := blk . Block ( ) . Slot ( )
graffitiInBlock := blk . Block ( ) . Body ( ) . Graffiti ( )
2020-12-04 23:15:12 +00:00
for _ , graffiti := range helpers . Graffiti {
2022-09-06 14:30:16 +00:00
if bytes . Equal ( bytesutil . PadTo ( [ ] byte ( graffiti ) , 32 ) , graffitiInBlock [ : ] ) {
2020-12-04 23:15:12 +00:00
e = true
break
}
}
2021-09-07 15:17:50 +00:00
if ! e && slot != 0 {
2020-12-04 23:15:12 +00:00
return errors . New ( "could not get graffiti from the list" )
}
}
return nil
}
2023-01-06 07:49:42 +00:00
func activatesDepositedValidators ( ec * e2etypes . EvaluationContext , conns ... * grpc . ClientConn ) error {
2020-05-29 02:51:00 +00:00
conn := conns [ 0 ]
2021-09-10 19:59:43 +00:00
client := ethpb . NewBeaconChainClient ( conn )
2020-06-11 06:38:15 +00:00
2021-05-17 18:32:04 +00:00
chainHead , err := client . GetChainHead ( context . Background ( ) , & emptypb . Empty { } )
2020-06-11 06:38:15 +00:00
if err != nil {
return errors . Wrap ( err , "failed to get chain head" )
}
2022-11-19 03:40:32 +00:00
epoch := chainHead . HeadEpoch
2020-06-11 06:38:15 +00:00
2022-11-19 03:40:32 +00:00
validators , err := getAllValidators ( client )
2020-05-29 02:51:00 +00:00
if err != nil {
return errors . Wrap ( err , "failed to get validators" )
}
2022-11-19 03:40:32 +00:00
expected := ec . Balances ( e2etypes . PostGenesisDepositBatch )
2020-05-29 02:51:00 +00:00
2022-11-19 03:40:32 +00:00
var deposits , lowBalance , wrongExit , wrongWithdraw int
for _ , v := range validators {
key := bytesutil . ToBytes48 ( v . PublicKey )
if _ , ok := expected [ key ] ; ! ok {
continue
}
delete ( expected , key )
if v . ActivationEpoch != epoch {
continue
}
deposits ++
if v . EffectiveBalance < params . BeaconConfig ( ) . MaxEffectiveBalance {
lowBalance ++
}
if v . ExitEpoch != params . BeaconConfig ( ) . FarFutureEpoch {
wrongExit ++
}
if v . WithdrawableEpoch != params . BeaconConfig ( ) . FarFutureEpoch {
wrongWithdraw ++
}
2020-05-29 02:51:00 +00:00
}
2022-11-19 03:40:32 +00:00
// Make sure every post-genesis deposit has been proecssed, resulting in a validator.
if len ( expected ) > 0 {
return fmt . Errorf ( "missing %d validators for post-genesis deposits" , len ( expected ) )
2020-05-29 02:51:00 +00:00
}
2022-11-19 03:40:32 +00:00
2023-09-28 21:05:23 +00:00
if uint64 ( deposits ) != params . BeaconConfig ( ) . MinPerEpochChurnLimit {
return fmt . Errorf ( "expected %d deposits to be processed in epoch %d, received %d" , params . BeaconConfig ( ) . MinPerEpochChurnLimit , epoch , deposits )
2020-06-11 06:38:15 +00:00
}
2020-05-29 02:51:00 +00:00
2022-11-19 03:40:32 +00:00
if lowBalance > 0 {
2020-05-29 02:51:00 +00:00
return fmt . Errorf (
"%d validators did not have genesis validator effective balance of %d" ,
2022-11-19 03:40:32 +00:00
lowBalance ,
2020-05-29 02:51:00 +00:00
params . BeaconConfig ( ) . MaxEffectiveBalance ,
)
2022-11-19 03:40:32 +00:00
} else if wrongExit > 0 {
return fmt . Errorf ( "%d validators did not have an exit epoch of far future epoch" , wrongExit )
} else if wrongWithdraw > 0 {
return fmt . Errorf ( "%d validators did not have a withdrawable epoch of far future epoch" , wrongWithdraw )
2020-05-29 02:51:00 +00:00
}
return nil
}
2022-11-19 03:40:32 +00:00
func getAllValidators ( c ethpb . BeaconChainClient ) ( [ ] * ethpb . Validator , error ) {
vals := make ( [ ] * ethpb . Validator , 0 )
pageToken := "0"
for pageToken != "" {
validatorRequest := & ethpb . ListValidatorsRequest {
PageSize : 100 ,
PageToken : pageToken ,
}
validators , err := c . ListValidators ( context . Background ( ) , validatorRequest )
if err != nil {
return nil , errors . Wrap ( err , "failed to get validators" )
}
for _ , v := range validators . ValidatorList {
vals = append ( vals , v . Validator )
}
pageToken = validators . NextPageToken
2020-05-29 02:51:00 +00:00
}
2022-11-19 03:40:32 +00:00
return vals , nil
}
2020-05-29 02:51:00 +00:00
2023-01-06 07:49:42 +00:00
func depositedValidatorsAreActive ( ec * e2etypes . EvaluationContext , conns ... * grpc . ClientConn ) error {
2022-11-19 03:40:32 +00:00
conn := conns [ 0 ]
client := ethpb . NewBeaconChainClient ( conn )
2020-05-29 02:51:00 +00:00
2021-05-17 18:32:04 +00:00
chainHead , err := client . GetChainHead ( context . Background ( ) , & emptypb . Empty { } )
2020-05-29 02:51:00 +00:00
if err != nil {
return errors . Wrap ( err , "failed to get chain head" )
}
2022-11-19 03:40:32 +00:00
vals , err := getAllValidators ( client )
if err != nil {
return errors . Wrap ( err , "error retrieving validator list from API" )
}
inactive := 0
lowBalance := 0
nexits := 0
expected := ec . Balances ( e2etypes . PostGenesisDepositBatch )
nexpected := len ( expected )
for _ , v := range vals {
key := bytesutil . ToBytes48 ( v . PublicKey )
if _ , ok := expected [ key ] ; ! ok {
continue // we aren't checking for this validator
}
// ignore voluntary exits when checking balance and active status
2023-01-06 07:49:42 +00:00
exited := ec . ExitedVals [ key ]
2022-11-19 03:40:32 +00:00
if exited {
nexits ++
delete ( expected , key )
continue
}
if ! corehelpers . IsActiveValidator ( v , chainHead . HeadEpoch ) {
inactive ++
2020-05-29 02:51:00 +00:00
}
2022-11-19 03:40:32 +00:00
if v . EffectiveBalance < params . BeaconConfig ( ) . MaxEffectiveBalance {
lowBalance ++
2020-08-01 17:22:53 +00:00
}
2022-11-19 03:40:32 +00:00
delete ( expected , key )
2020-05-29 02:51:00 +00:00
}
2022-11-19 03:40:32 +00:00
if len ( expected ) > 0 {
mk := make ( [ ] string , 0 )
for k := range expected {
mk = append ( mk , fmt . Sprintf ( "%#x" , k ) )
}
return fmt . Errorf ( "API response missing %d validators, based on deposits; keys=%s" , len ( expected ) , strings . Join ( mk , "," ) )
2020-05-29 02:51:00 +00:00
}
2022-11-19 03:40:32 +00:00
if inactive != 0 || lowBalance != 0 {
return fmt . Errorf ( "active validator set does not match %d total deposited. %d exited, %d inactive, %d low balance" , nexpected , nexits , inactive , lowBalance )
2020-08-01 17:22:53 +00:00
}
2022-11-19 03:40:32 +00:00
2020-05-29 02:51:00 +00:00
return nil
}
2023-01-06 07:49:42 +00:00
func proposeVoluntaryExit ( ec * e2etypes . EvaluationContext , conns ... * grpc . ClientConn ) error {
2020-05-29 02:51:00 +00:00
conn := conns [ 0 ]
2022-11-11 17:33:48 +00:00
valClient := ethpb . NewBeaconNodeValidatorClient ( conn )
2021-09-10 19:59:43 +00:00
beaconClient := ethpb . NewBeaconChainClient ( conn )
2023-02-10 06:19:15 +00:00
debugClient := ethpb . NewDebugClient ( conn )
2020-05-29 02:51:00 +00:00
ctx := context . Background ( )
2021-05-17 18:32:04 +00:00
chainHead , err := beaconClient . GetChainHead ( ctx , & emptypb . Empty { } )
2020-05-29 02:51:00 +00:00
if err != nil {
return errors . Wrap ( err , "could not get chain head" )
}
2023-02-10 06:19:15 +00:00
stObj , err := debugClient . GetBeaconState ( ctx , & ethpb . BeaconStateRequest { QueryFilter : & ethpb . BeaconStateRequest_Slot { Slot : chainHead . HeadSlot } } )
2020-05-29 02:51:00 +00:00
if err != nil {
2023-02-10 06:19:15 +00:00
return errors . Wrap ( err , "could not get state object" )
2020-05-29 02:51:00 +00:00
}
2023-02-10 06:19:15 +00:00
versionedMarshaler , err := detect . FromState ( stObj . Encoded )
if err != nil {
return errors . Wrap ( err , "could not get state marshaler" )
2020-05-29 02:51:00 +00:00
}
2023-02-10 06:19:15 +00:00
st , err := versionedMarshaler . UnmarshalBeaconState ( stObj . Encoded )
if err != nil {
return errors . Wrap ( err , "could not get state" )
2020-05-29 02:51:00 +00:00
}
2023-02-10 06:19:15 +00:00
execIndices := [ ] int { }
err = st . ReadFromEveryValidator ( func ( idx int , val state . ReadOnlyValidator ) error {
if val . WithdrawalCredentials ( ) [ 0 ] == params . BeaconConfig ( ) . ETH1AddressWithdrawalPrefixByte {
execIndices = append ( execIndices , idx )
}
return nil
} )
2020-05-29 02:51:00 +00:00
if err != nil {
return err
}
2023-02-10 06:19:15 +00:00
if len ( execIndices ) > numOfExits {
execIndices = execIndices [ : numOfExits ]
}
deposits , privKeys , err := util . DeterministicDepositsAndKeys ( params . BeaconConfig ( ) . MinGenesisActiveValidatorCount )
2020-05-29 02:51:00 +00:00
if err != nil {
return err
}
2023-02-10 06:19:15 +00:00
var sendExit = func ( exitedIndex primitives . ValidatorIndex ) error {
voluntaryExit := & ethpb . VoluntaryExit {
Epoch : chainHead . HeadEpoch ,
ValidatorIndex : exitedIndex ,
}
req := & ethpb . DomainRequest {
Epoch : chainHead . HeadEpoch ,
Domain : params . BeaconConfig ( ) . DomainVoluntaryExit [ : ] ,
}
domain , err := valClient . DomainData ( ctx , req )
if err != nil {
return err
}
signingData , err := signing . ComputeSigningRoot ( voluntaryExit , domain . SignatureDomain )
if err != nil {
return err
}
signature := privKeys [ exitedIndex ] . Sign ( signingData [ : ] )
signedExit := & ethpb . SignedVoluntaryExit {
Exit : voluntaryExit ,
Signature : signature . Marshal ( ) ,
}
if _ , err = valClient . ProposeExit ( ctx , signedExit ) ; err != nil {
return errors . Wrap ( err , "could not propose exit" )
}
pubk := bytesutil . ToBytes48 ( deposits [ exitedIndex ] . Data . PublicKey )
ec . ExitedVals [ pubk ] = true
return nil
}
// Send exits for keys which already contain execution credentials.
for _ , idx := range execIndices {
if err := sendExit ( primitives . ValidatorIndex ( idx ) ) ; err != nil {
return err
}
2020-05-29 02:51:00 +00:00
}
2023-02-10 06:19:15 +00:00
// Send an exit for a non-exited validator.
for i := 0 ; i < numOfExits ; {
randIndex := primitives . ValidatorIndex ( rand . Uint64 ( ) % params . BeaconConfig ( ) . MinGenesisActiveValidatorCount )
if ec . ExitedVals [ bytesutil . ToBytes48 ( privKeys [ randIndex ] . PublicKey ( ) . Marshal ( ) ) ] {
continue
}
if err := sendExit ( randIndex ) ; err != nil {
return err
}
i ++
2020-05-29 02:51:00 +00:00
}
2022-11-19 03:40:32 +00:00
2020-05-29 02:51:00 +00:00
return nil
}
2023-01-06 07:49:42 +00:00
func validatorsHaveExited ( ec * e2etypes . EvaluationContext , conns ... * grpc . ClientConn ) error {
2020-05-29 02:51:00 +00:00
conn := conns [ 0 ]
2021-09-10 19:59:43 +00:00
client := ethpb . NewBeaconChainClient ( conn )
2023-01-06 07:49:42 +00:00
for k := range ec . ExitedVals {
2022-11-19 03:40:32 +00:00
validatorRequest := & ethpb . GetValidatorRequest {
QueryFilter : & ethpb . GetValidatorRequest_PublicKey {
PublicKey : k [ : ] ,
} ,
}
validator , err := client . GetValidator ( context . Background ( ) , validatorRequest )
if err != nil {
return errors . Wrap ( err , "failed to get validators" )
}
if validator . ExitEpoch == params . BeaconConfig ( ) . FarFutureEpoch {
return fmt . Errorf ( "expected validator %#x to be submitted for exit" , k )
}
2020-05-29 02:51:00 +00:00
}
return nil
}
2020-10-19 19:35:34 +00:00
2023-01-06 07:49:42 +00:00
func validatorsVoteWithTheMajority ( ec * e2etypes . EvaluationContext , conns ... * grpc . ClientConn ) error {
2020-10-19 19:35:34 +00:00
conn := conns [ 0 ]
2021-09-10 19:59:43 +00:00
client := ethpb . NewBeaconChainClient ( conn )
2021-05-17 18:32:04 +00:00
chainHead , err := client . GetChainHead ( context . Background ( ) , & emptypb . Empty { } )
2020-10-19 19:35:34 +00:00
if err != nil {
return errors . Wrap ( err , "failed to get chain head" )
}
2022-12-13 23:13:49 +00:00
begin := chainHead . HeadEpoch
// Prevent underflow when this runs at epoch 0.
if begin > 0 {
begin = begin . Sub ( 1 )
}
req := & ethpb . ListBlocksRequest { QueryFilter : & ethpb . ListBlocksRequest_Epoch { Epoch : begin } }
2021-09-10 19:59:43 +00:00
blks , err := client . ListBeaconBlocks ( context . Background ( ) , req )
2020-10-19 19:35:34 +00:00
if err != nil {
return errors . Wrap ( err , "failed to get blocks from beacon-chain" )
}
2022-12-13 23:13:49 +00:00
slotsPerVotingPeriod := params . E2ETestConfig ( ) . SlotsPerEpoch . Mul ( uint64 ( params . E2ETestConfig ( ) . EpochsPerEth1VotingPeriod ) )
2020-10-19 19:35:34 +00:00
for _ , blk := range blks . BlockContainers {
2023-01-26 14:40:12 +00:00
var slot primitives . Slot
2021-09-07 15:17:50 +00:00
var vote [ ] byte
switch blk . Block . ( type ) {
2021-09-10 19:59:43 +00:00
case * ethpb . BeaconBlockContainer_Phase0Block :
2021-09-07 15:17:50 +00:00
b := blk . GetPhase0Block ( ) . Block
slot = b . Slot
vote = b . Body . Eth1Data . BlockHash
2021-09-10 19:59:43 +00:00
case * ethpb . BeaconBlockContainer_AltairBlock :
2021-09-07 15:17:50 +00:00
b := blk . GetAltairBlock ( ) . Block
slot = b . Slot
vote = b . Body . Eth1Data . BlockHash
2022-04-05 14:02:46 +00:00
case * ethpb . BeaconBlockContainer_BellatrixBlock :
b := blk . GetBellatrixBlock ( ) . Block
slot = b . Slot
vote = b . Body . Eth1Data . BlockHash
2022-08-15 14:16:20 +00:00
case * ethpb . BeaconBlockContainer_BlindedBellatrixBlock :
b := blk . GetBlindedBellatrixBlock ( ) . Block
slot = b . Slot
vote = b . Body . Eth1Data . BlockHash
2023-02-10 06:19:15 +00:00
case * ethpb . BeaconBlockContainer_CapellaBlock :
b := blk . GetCapellaBlock ( ) . Block
slot = b . Slot
vote = b . Body . Eth1Data . BlockHash
case * ethpb . BeaconBlockContainer_BlindedCapellaBlock :
b := blk . GetBlindedCapellaBlock ( ) . Block
slot = b . Slot
vote = b . Body . Eth1Data . BlockHash
2023-07-10 19:02:44 +00:00
case * ethpb . BeaconBlockContainer_DenebBlock :
b := blk . GetDenebBlock ( ) . Block
slot = b . Slot
vote = b . Body . Eth1Data . BlockHash
case * ethpb . BeaconBlockContainer_BlindedDenebBlock :
2023-09-18 15:32:10 +00:00
b := blk . GetBlindedDenebBlock ( ) . Message
2023-07-10 19:02:44 +00:00
slot = b . Slot
vote = b . Body . Eth1Data . BlockHash
2021-09-07 15:17:50 +00:00
default :
2022-08-15 14:16:20 +00:00
return errors . New ( "block neither phase0,altair or bellatrix" )
2021-09-07 15:17:50 +00:00
}
2023-01-06 07:49:42 +00:00
ec . SeenVotes [ slot ] = vote
2020-10-19 19:35:34 +00:00
// We treat epoch 1 differently from other epoch for two reasons:
// - this evaluator is not executed for epoch 0 so we have to calculate the first slot differently
// - for some reason the vote for the first slot in epoch 1 is 0x000... so we skip this slot
var isFirstSlotInVotingPeriod bool
2022-02-02 19:13:52 +00:00
if chainHead . HeadEpoch == 1 && slot % params . BeaconConfig ( ) . SlotsPerEpoch == 0 {
2020-10-19 19:35:34 +00:00
continue
}
// We skipped the first slot so we treat the second slot as the starting slot of epoch 1.
if chainHead . HeadEpoch == 1 {
2022-02-02 19:13:52 +00:00
isFirstSlotInVotingPeriod = slot % params . BeaconConfig ( ) . SlotsPerEpoch == 1
2020-10-19 19:35:34 +00:00
} else {
isFirstSlotInVotingPeriod = slot % slotsPerVotingPeriod == 0
}
if isFirstSlotInVotingPeriod {
2023-01-06 07:49:42 +00:00
ec . ExpectedEth1DataVote = vote
2020-10-19 19:35:34 +00:00
return nil
}
2023-01-06 07:49:42 +00:00
if ! bytes . Equal ( vote , ec . ExpectedEth1DataVote ) {
2023-01-26 14:40:12 +00:00
for i := primitives . Slot ( 0 ) ; i < slot ; i ++ {
2023-01-06 07:49:42 +00:00
v , ok := ec . SeenVotes [ i ]
2022-12-21 04:43:29 +00:00
if ok {
fmt . Printf ( "vote at slot=%d = %#x\n" , i , v )
} else {
fmt . Printf ( "did not see slot=%d\n" , i )
}
}
2020-10-19 19:35:34 +00:00
return fmt . Errorf ( "incorrect eth1data vote for slot %d; expected: %#x vs voted: %#x" ,
2023-01-06 07:49:42 +00:00
slot , ec . ExpectedEth1DataVote , vote )
2020-10-19 19:35:34 +00:00
}
}
return nil
}
2023-02-10 06:19:15 +00:00
func submitWithdrawal ( ec * e2etypes . EvaluationContext , conns ... * grpc . ClientConn ) error {
conn := conns [ 0 ]
beaconClient := ethpb . NewBeaconChainClient ( conn )
debugClient := ethpb . NewDebugClient ( conn )
ctx := context . Background ( )
chainHead , err := beaconClient . GetChainHead ( ctx , & emptypb . Empty { } )
if err != nil {
return errors . Wrap ( err , "could not get chain head" )
}
stObj , err := debugClient . GetBeaconState ( ctx , & ethpb . BeaconStateRequest { QueryFilter : & ethpb . BeaconStateRequest_Slot { Slot : chainHead . HeadSlot } } )
if err != nil {
return errors . Wrap ( err , "could not get state object" )
}
versionedMarshaler , err := detect . FromState ( stObj . Encoded )
if err != nil {
return errors . Wrap ( err , "could not get state marshaler" )
}
st , err := versionedMarshaler . UnmarshalBeaconState ( stObj . Encoded )
if err != nil {
return errors . Wrap ( err , "could not get state" )
}
exitedIndices := make ( [ ] primitives . ValidatorIndex , 0 )
for key := range ec . ExitedVals {
valIdx , ok := st . ValidatorIndexByPubkey ( key )
if ! ok {
return errors . Errorf ( "pubkey %#x does not exist in our state" , key )
}
exitedIndices = append ( exitedIndices , valIdx )
}
_ , privKeys , err := util . DeterministicDepositsAndKeys ( params . BeaconConfig ( ) . MinGenesisActiveValidatorCount )
if err != nil {
return err
}
2023-10-11 14:08:06 +00:00
changes := make ( [ ] * shared . SignedBLSToExecutionChange , 0 )
2023-02-10 06:19:15 +00:00
// Only send half the number of changes each time, to allow us to test
// at the fork boundary.
wantedChanges := numOfExits / 2
for _ , idx := range exitedIndices {
// Exit sending more change messages.
if len ( changes ) >= wantedChanges {
break
}
val , err := st . ValidatorAtIndex ( idx )
if err != nil {
return err
}
if val . WithdrawalCredentials [ 0 ] == params . BeaconConfig ( ) . ETH1AddressWithdrawalPrefixByte {
continue
}
if ! bytes . Equal ( val . PublicKey , privKeys [ idx ] . PublicKey ( ) . Marshal ( ) ) {
return errors . Errorf ( "pubkey is not equal, wanted %#x but received %#x" , val . PublicKey , privKeys [ idx ] . PublicKey ( ) . Marshal ( ) )
}
2023-10-11 14:08:06 +00:00
message := & ethpb . BLSToExecutionChange {
2023-02-10 06:19:15 +00:00
ValidatorIndex : idx ,
FromBlsPubkey : privKeys [ idx ] . PublicKey ( ) . Marshal ( ) ,
ToExecutionAddress : bytesutil . ToBytes ( uint64 ( idx ) , 20 ) ,
}
domain , err := signing . ComputeDomain ( params . BeaconConfig ( ) . DomainBLSToExecutionChange , params . BeaconConfig ( ) . GenesisForkVersion , st . GenesisValidatorsRoot ( ) )
if err != nil {
return err
}
sigRoot , err := signing . ComputeSigningRoot ( message , domain )
if err != nil {
return err
}
signature := privKeys [ idx ] . Sign ( sigRoot [ : ] ) . Marshal ( )
2023-10-11 14:08:06 +00:00
change , err := shared . BlsToExecutionChangeFromConsensus ( message )
if err != nil {
return err
2023-02-10 06:19:15 +00:00
}
2023-10-11 14:08:06 +00:00
changes = append ( changes , & shared . SignedBLSToExecutionChange {
Message : change ,
Signature : hexutil . Encode ( signature ) ,
} )
}
beaconAPIClient , err := beacon . NewClient ( fmt . Sprintf ( "http://localhost:%d/eth/v1" , e2e . TestParams . Ports . PrysmBeaconNodeGatewayPort ) ) // only uses the first node so no updates to port
if err != nil {
return err
2023-02-10 06:19:15 +00:00
}
2023-10-11 14:08:06 +00:00
return beaconAPIClient . SubmitChangeBLStoExecution ( ctx , changes )
2023-02-10 06:19:15 +00:00
}
func validatorsAreWithdrawn ( ec * e2etypes . EvaluationContext , conns ... * grpc . ClientConn ) error {
conn := conns [ 0 ]
beaconClient := ethpb . NewBeaconChainClient ( conn )
debugClient := ethpb . NewDebugClient ( conn )
ctx := context . Background ( )
chainHead , err := beaconClient . GetChainHead ( ctx , & emptypb . Empty { } )
if err != nil {
return errors . Wrap ( err , "could not get chain head" )
}
stObj , err := debugClient . GetBeaconState ( ctx , & ethpb . BeaconStateRequest { QueryFilter : & ethpb . BeaconStateRequest_Slot { Slot : chainHead . HeadSlot } } )
if err != nil {
return errors . Wrap ( err , "could not get state object" )
}
versionedMarshaler , err := detect . FromState ( stObj . Encoded )
if err != nil {
return errors . Wrap ( err , "could not get state marshaler" )
}
st , err := versionedMarshaler . UnmarshalBeaconState ( stObj . Encoded )
if err != nil {
return errors . Wrap ( err , "could not get state" )
}
for key := range ec . ExitedVals {
valIdx , ok := st . ValidatorIndexByPubkey ( key )
if ! ok {
return errors . Errorf ( "pubkey %#x does not exist in our state" , key )
}
bal , err := st . BalanceAtIndex ( valIdx )
if err != nil {
return err
}
2023-02-17 07:49:30 +00:00
// Only return an error if the validator has more than 1 eth
// in its balance.
if bal > 1 * params . BeaconConfig ( ) . GweiPerEth {
2023-02-10 06:19:15 +00:00
return errors . Errorf ( "Validator index %d with key %#x hasn't withdrawn. Their balance is %d." , valIdx , key , bal )
}
}
return nil
}