github.com/prysmaticlabs/prysm@v1.4.4/endtoend/evaluators/node.go (about)

     1  // Package evaluators defines functions which can peer into end to end
     2  // tests to determine if a chain is running as required.
     3  package evaluators
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"time"
    12  
    13  	"github.com/pkg/errors"
    14  	types "github.com/prysmaticlabs/eth2-types"
    15  	e2e "github.com/prysmaticlabs/prysm/endtoend/params"
    16  	"github.com/prysmaticlabs/prysm/endtoend/policies"
    17  	e2etypes "github.com/prysmaticlabs/prysm/endtoend/types"
    18  	eth "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    19  	"google.golang.org/grpc"
    20  	"google.golang.org/protobuf/types/known/emptypb"
    21  )
    22  
    23  // Allow a very short delay after disconnecting to prevent connection refused issues.
    24  var connTimeDelay = 50 * time.Millisecond
    25  
    26  // PeersConnect checks all beacon nodes and returns whether they are connected to each other as peers.
    27  var PeersConnect = e2etypes.Evaluator{
    28  	Name:       "peers_connect_epoch_%d",
    29  	Policy:     policies.OnEpoch(0),
    30  	Evaluation: peersConnect,
    31  }
    32  
    33  // HealthzCheck pings healthz and errors if it doesn't have the expected OK status.
    34  var HealthzCheck = e2etypes.Evaluator{
    35  	Name:       "healthz_check_epoch_%d",
    36  	Policy:     policies.AfterNthEpoch(0),
    37  	Evaluation: healthzCheck,
    38  }
    39  
    40  // FinishedSyncing returns whether the beacon node with the given rpc port has finished syncing.
    41  var FinishedSyncing = e2etypes.Evaluator{
    42  	Name:       "finished_syncing",
    43  	Policy:     policies.AllEpochs,
    44  	Evaluation: finishedSyncing,
    45  }
    46  
    47  // AllNodesHaveSameHead ensures all nodes have the same head epoch. Checks finality and justification as well.
    48  // Not checking head block root as it may change irregularly for the validator connected nodes.
    49  var AllNodesHaveSameHead = e2etypes.Evaluator{
    50  	Name:       "all_nodes_have_same_head",
    51  	Policy:     policies.AllEpochs,
    52  	Evaluation: allNodesHaveSameHead,
    53  }
    54  
    55  func healthzCheck(conns ...*grpc.ClientConn) error {
    56  	count := len(conns)
    57  	for i := 0; i < count; i++ {
    58  		resp, err := http.Get(fmt.Sprintf("http://localhost:%d/healthz", e2e.TestParams.BeaconNodeMetricsPort+i))
    59  		if err != nil {
    60  			// Continue if the connection fails, regular flake.
    61  			continue
    62  		}
    63  		if resp.StatusCode != http.StatusOK {
    64  			body, err := ioutil.ReadAll(resp.Body)
    65  			if err != nil {
    66  				return err
    67  			}
    68  			return fmt.Errorf("expected status code OK for beacon node %d, received %v with body %s", i, resp.StatusCode, body)
    69  		}
    70  		if err = resp.Body.Close(); err != nil {
    71  			return err
    72  		}
    73  		time.Sleep(connTimeDelay)
    74  	}
    75  
    76  	for i := 0; i < count; i++ {
    77  		resp, err := http.Get(fmt.Sprintf("http://localhost:%d/healthz", e2e.TestParams.ValidatorMetricsPort+i))
    78  		if err != nil {
    79  			// Continue if the connection fails, regular flake.
    80  			continue
    81  		}
    82  		if resp.StatusCode != http.StatusOK {
    83  			body, err := ioutil.ReadAll(resp.Body)
    84  			if err != nil {
    85  				return err
    86  			}
    87  			return fmt.Errorf("expected status code OK for validator client %d, received %v with body %s", i, resp.StatusCode, body)
    88  		}
    89  		if err = resp.Body.Close(); err != nil {
    90  			return err
    91  		}
    92  		time.Sleep(connTimeDelay)
    93  	}
    94  	return nil
    95  }
    96  
    97  func peersConnect(conns ...*grpc.ClientConn) error {
    98  	if len(conns) == 1 {
    99  		return nil
   100  	}
   101  	ctx := context.Background()
   102  	for _, conn := range conns {
   103  		nodeClient := eth.NewNodeClient(conn)
   104  		peersResp, err := nodeClient.ListPeers(ctx, &emptypb.Empty{})
   105  		if err != nil {
   106  			return err
   107  		}
   108  		expectedPeers := len(conns) - 1
   109  		if expectedPeers != len(peersResp.Peers) {
   110  			return fmt.Errorf("unexpected amount of peers, expected %d, received %d", expectedPeers, len(peersResp.Peers))
   111  		}
   112  		time.Sleep(connTimeDelay)
   113  	}
   114  	return nil
   115  }
   116  
   117  func finishedSyncing(conns ...*grpc.ClientConn) error {
   118  	conn := conns[0]
   119  	syncNodeClient := eth.NewNodeClient(conn)
   120  	syncStatus, err := syncNodeClient.GetSyncStatus(context.Background(), &emptypb.Empty{})
   121  	if err != nil {
   122  		return err
   123  	}
   124  	if syncStatus.Syncing {
   125  		return errors.New("expected node to have completed sync")
   126  	}
   127  	return nil
   128  }
   129  
   130  func allNodesHaveSameHead(conns ...*grpc.ClientConn) error {
   131  	headEpochs := make([]types.Epoch, len(conns))
   132  	justifiedRoots := make([][]byte, len(conns))
   133  	prevJustifiedRoots := make([][]byte, len(conns))
   134  	finalizedRoots := make([][]byte, len(conns))
   135  	for i, conn := range conns {
   136  		beaconClient := eth.NewBeaconChainClient(conn)
   137  		chainHead, err := beaconClient.GetChainHead(context.Background(), &emptypb.Empty{})
   138  		if err != nil {
   139  			return err
   140  		}
   141  		headEpochs[i] = chainHead.HeadEpoch
   142  		justifiedRoots[i] = chainHead.JustifiedBlockRoot
   143  		prevJustifiedRoots[i] = chainHead.PreviousJustifiedBlockRoot
   144  		finalizedRoots[i] = chainHead.FinalizedBlockRoot
   145  		time.Sleep(connTimeDelay)
   146  	}
   147  
   148  	for i := 0; i < len(conns); i++ {
   149  		if headEpochs[0] != headEpochs[i] {
   150  			return fmt.Errorf(
   151  				"received conflicting head epochs on node %d, expected %d, received %d",
   152  				i,
   153  				headEpochs[0],
   154  				headEpochs[i],
   155  			)
   156  		}
   157  		if !bytes.Equal(justifiedRoots[0], justifiedRoots[i]) {
   158  			return fmt.Errorf(
   159  				"received conflicting justified block roots on node %d, expected %#x, received %#x",
   160  				i,
   161  				justifiedRoots[0],
   162  				justifiedRoots[i],
   163  			)
   164  		}
   165  		if !bytes.Equal(prevJustifiedRoots[0], prevJustifiedRoots[i]) {
   166  			return fmt.Errorf(
   167  				"received conflicting previous justified block roots on node %d, expected %#x, received %#x",
   168  				i,
   169  				prevJustifiedRoots[0],
   170  				prevJustifiedRoots[i],
   171  			)
   172  		}
   173  		if !bytes.Equal(finalizedRoots[0], finalizedRoots[i]) {
   174  			return fmt.Errorf(
   175  				"received conflicting finalized epoch roots on node %d, expected %#x, received %#x",
   176  				i,
   177  				finalizedRoots[0],
   178  				finalizedRoots[i],
   179  			)
   180  		}
   181  	}
   182  
   183  	return nil
   184  }