github.com/prysmaticlabs/prysm@v1.4.4/tools/forkchecker/forkchecker.go (about) 1 /** 2 * Fork choice checker 3 * 4 * A gRPC client that polls beacon node at every slot to log or compare nodes current head. 5 * 6 * Example: 2 beacon nodes with 2 gRPC end points, 127.0.0.1:4000 and 127.0.0.1:4001 7 * For logging heads: forkchecker --endpoint 127.0.0.1:4000 --endpoint 127.0.0.1:4001 8 * For comparing heads: forkchecker --endpoint 127.0.0.1:4000 --endpoint 127.0.0.1:4001 --compare 9 */ 10 package main 11 12 import ( 13 "context" 14 "encoding/hex" 15 "flag" 16 "reflect" 17 "time" 18 19 types "github.com/prysmaticlabs/eth2-types" 20 pb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 21 "github.com/prysmaticlabs/prysm/shared/params" 22 "github.com/sirupsen/logrus" 23 "google.golang.org/grpc" 24 "google.golang.org/protobuf/types/known/emptypb" 25 ) 26 27 var log = logrus.WithField("prefix", "forkchoice_checker") 28 29 type endpoint []string 30 31 func (e *endpoint) String() string { 32 return "gRPC endpoints" 33 } 34 35 // Set adds endpoint value to list. 36 func (e *endpoint) Set(value string) error { 37 *e = append(*e, value) 38 return nil 39 } 40 41 func main() { 42 var endpts endpoint 43 clients := make(map[string]pb.BeaconChainClient) 44 45 flag.Var(&endpts, "endpoint", "Specify gRPC end points for beacon node") 46 compare := flag.Bool("compare", false, "Enable head comparisons between all end points") 47 flag.Parse() 48 49 for _, endpt := range endpts { 50 conn, err := grpc.Dial(endpt, grpc.WithInsecure()) 51 if err != nil { 52 log.Fatalf("fail to dial: %v", err) 53 } 54 clients[endpt] = pb.NewBeaconChainClient(conn) 55 } 56 57 ticker := time.NewTicker(time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second) 58 go func() { 59 for range ticker.C { 60 if *compare { 61 compareHeads(clients) 62 } else { 63 displayHeads(clients) 64 } 65 } 66 }() 67 select {} 68 } 69 70 // log heads for all RPC end points 71 func displayHeads(clients map[string]pb.BeaconChainClient) { 72 for endpt, client := range clients { 73 head, err := client.GetChainHead(context.Background(), &emptypb.Empty{}) 74 if err != nil { 75 log.Fatal(err) 76 } 77 logHead(endpt, head) 78 } 79 } 80 81 // compare heads between all RPC end points, log the missmatch if there's one. 82 func compareHeads(clients map[string]pb.BeaconChainClient) { 83 endpt1 := randomEndpt(clients) 84 head1, err := clients[endpt1].GetChainHead(context.Background(), &emptypb.Empty{}) 85 if err != nil { 86 log.Fatal(err) 87 } 88 89 log.Infof("Comparing all heads for head slot :%d", head1.HeadSlot) 90 if (head1.HeadSlot+1)%params.BeaconConfig().SlotsPerEpoch == 0 { 91 p, err := clients[endpt1].GetValidatorParticipation(context.Background(), &pb.GetValidatorParticipationRequest{}) 92 if err != nil { 93 log.Fatal(err) 94 } 95 logParticipation(endpt1, p.Participation) 96 } 97 98 for endpt2, client := range clients { 99 head2, err := client.GetChainHead(context.Background(), &emptypb.Empty{}) 100 if err != nil { 101 log.Fatal(err) 102 } 103 if !reflect.DeepEqual(head1, head2) { 104 log.Error("Uh oh! Heads missmatched!") 105 logHead(endpt1, head1) 106 logHead(endpt2, head2) 107 108 if (head1.HeadSlot+1)%params.BeaconConfig().SlotsPerEpoch == 0 { 109 p, err := clients[endpt2].GetValidatorParticipation(context.Background(), &pb.GetValidatorParticipationRequest{ 110 QueryFilter: &pb.GetValidatorParticipationRequest_Epoch{ 111 Epoch: types.Epoch(head2.HeadSlot / params.BeaconConfig().SlotsPerEpoch), 112 }, 113 }) 114 if err != nil { 115 log.Fatal(err) 116 } 117 logParticipation(endpt2, p.Participation) 118 } 119 } 120 } 121 } 122 123 func logHead(endpt string, head *pb.ChainHead) { 124 log.WithFields( 125 logrus.Fields{ 126 "HeadSlot": head.HeadSlot, 127 "HeadRoot": hex.EncodeToString(head.HeadBlockRoot), 128 "JustifiedEpoch": head.JustifiedEpoch, 129 "JustifiedRoot": hex.EncodeToString(head.JustifiedBlockRoot), 130 "FinalizedEpoch": head.FinalizedEpoch, 131 "FinalizedRoot": hex.EncodeToString(head.FinalizedBlockRoot), 132 }).Info("Head from beacon node ", endpt) 133 } 134 135 func logParticipation(endpt string, p *pb.ValidatorParticipation) { 136 log.WithFields( 137 logrus.Fields{ 138 "VotedEther": p.VotedEther, 139 "TotalEther": p.EligibleEther, 140 "ParticipationRate": p.GlobalParticipationRate, 141 }).Info("Participation rate from beacon node ", endpt) 142 } 143 144 func randomEndpt(clients map[string]pb.BeaconChainClient) string { 145 for endpt := range clients { 146 return endpt 147 } 148 return "" 149 }