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