github.com/evdatsion/aphelion-dpos-bft@v0.32.1/tools/tm-monitor/monitor/node.go (about) 1 package monitor 2 3 import ( 4 "encoding/json" 5 "math" 6 "time" 7 8 "github.com/pkg/errors" 9 10 "github.com/evdatsion/aphelion-dpos-bft/crypto" 11 "github.com/evdatsion/aphelion-dpos-bft/libs/events" 12 "github.com/evdatsion/aphelion-dpos-bft/libs/log" 13 ctypes "github.com/evdatsion/aphelion-dpos-bft/rpc/core/types" 14 rpc_client "github.com/evdatsion/aphelion-dpos-bft/rpc/lib/client" 15 em "github.com/evdatsion/aphelion-dpos-bft/tools/tm-monitor/eventmeter" 16 tmtypes "github.com/evdatsion/aphelion-dpos-bft/types" 17 ) 18 19 const maxRestarts = 25 20 21 type Node struct { 22 rpcAddr string 23 24 IsValidator bool `json:"is_validator"` // validator or non-validator? 25 pubKey crypto.PubKey 26 27 Name string `json:"name"` 28 Online bool `json:"online"` 29 Height int64 `json:"height"` 30 BlockLatency float64 `json:"block_latency" amino:"unsafe"` // ms, interval between block commits 31 32 // em holds the ws connection. Each eventMeter callback is called in a separate go-routine. 33 em eventMeter 34 35 // rpcClient is an client for making RPC calls to TM 36 rpcClient rpc_client.HTTPClient 37 38 blockCh chan<- tmtypes.Header 39 blockLatencyCh chan<- float64 40 disconnectCh chan<- bool 41 42 checkIsValidatorInterval time.Duration 43 44 quit chan struct{} 45 46 logger log.Logger 47 } 48 49 func NewNode(rpcAddr string, options ...func(*Node)) *Node { 50 em := em.NewEventMeter(rpcAddr, UnmarshalEvent) 51 rpcClient := rpc_client.NewURIClient(rpcAddr) // HTTP client by default 52 rpcClient.SetCodec(cdc) 53 return NewNodeWithEventMeterAndRpcClient(rpcAddr, em, rpcClient, options...) 54 } 55 56 func NewNodeWithEventMeterAndRpcClient(rpcAddr string, em eventMeter, rpcClient rpc_client.HTTPClient, options ...func(*Node)) *Node { 57 n := &Node{ 58 rpcAddr: rpcAddr, 59 em: em, 60 rpcClient: rpcClient, 61 Name: rpcAddr, 62 quit: make(chan struct{}), 63 checkIsValidatorInterval: 5 * time.Second, 64 logger: log.NewNopLogger(), 65 } 66 67 for _, option := range options { 68 option(n) 69 } 70 71 return n 72 } 73 74 // SetCheckIsValidatorInterval lets you change interval for checking whenever 75 // node is still a validator or not. 76 func SetCheckIsValidatorInterval(d time.Duration) func(n *Node) { 77 return func(n *Node) { 78 n.checkIsValidatorInterval = d 79 } 80 } 81 82 func (n *Node) SendBlocksTo(ch chan<- tmtypes.Header) { 83 n.blockCh = ch 84 } 85 86 func (n *Node) SendBlockLatenciesTo(ch chan<- float64) { 87 n.blockLatencyCh = ch 88 } 89 90 func (n *Node) NotifyAboutDisconnects(ch chan<- bool) { 91 n.disconnectCh = ch 92 } 93 94 // SetLogger lets you set your own logger 95 func (n *Node) SetLogger(l log.Logger) { 96 n.logger = l 97 n.em.SetLogger(l) 98 } 99 100 func (n *Node) Start() error { 101 if err := n.em.Start(); err != nil { 102 return err 103 } 104 105 n.em.RegisterLatencyCallback(latencyCallback(n)) 106 err := n.em.Subscribe(tmtypes.EventQueryNewBlockHeader.String(), newBlockCallback(n)) 107 if err != nil { 108 return err 109 } 110 n.em.RegisterDisconnectCallback(disconnectCallback(n)) 111 112 n.Online = true 113 114 n.checkIsValidator() 115 go n.checkIsValidatorLoop() 116 117 return nil 118 } 119 120 func (n *Node) Stop() { 121 n.Online = false 122 123 n.em.Stop() 124 125 close(n.quit) 126 } 127 128 // implements eventmeter.EventCallbackFunc 129 func newBlockCallback(n *Node) em.EventCallbackFunc { 130 return func(metric *em.EventMetric, data interface{}) { 131 block := data.(tmtypes.TMEventData).(tmtypes.EventDataNewBlockHeader).Header 132 133 n.Height = block.Height 134 n.logger.Info("new block", "height", block.Height, "numTxs", block.NumTxs) 135 136 if n.blockCh != nil { 137 n.blockCh <- block 138 } 139 } 140 } 141 142 // implements eventmeter.EventLatencyFunc 143 func latencyCallback(n *Node) em.LatencyCallbackFunc { 144 return func(latency float64) { 145 n.BlockLatency = latency / 1000000.0 // ns to ms 146 n.logger.Info("new block latency", "latency", n.BlockLatency) 147 148 if n.blockLatencyCh != nil { 149 n.blockLatencyCh <- latency 150 } 151 } 152 } 153 154 // implements eventmeter.DisconnectCallbackFunc 155 func disconnectCallback(n *Node) em.DisconnectCallbackFunc { 156 return func() { 157 n.Online = false 158 n.logger.Info("status", "down") 159 160 if n.disconnectCh != nil { 161 n.disconnectCh <- true 162 } 163 } 164 } 165 166 func (n *Node) RestartEventMeterBackoff() error { 167 attempt := 0 168 169 for { 170 d := time.Duration(math.Exp2(float64(attempt))) 171 time.Sleep(d * time.Second) 172 173 if err := n.em.Start(); err != nil { 174 n.logger.Info("restart failed", "err", err) 175 } else { 176 // TODO: authenticate pubkey 177 return nil 178 } 179 180 attempt++ 181 182 if attempt > maxRestarts { 183 return errors.New("Reached max restarts") 184 } 185 } 186 } 187 188 func (n *Node) NumValidators() (height int64, num int, err error) { 189 height, vals, err := n.validators() 190 if err != nil { 191 return 0, 0, err 192 } 193 return height, len(vals), nil 194 } 195 196 func (n *Node) validators() (height int64, validators []*tmtypes.Validator, err error) { 197 vals := new(ctypes.ResultValidators) 198 if _, err = n.rpcClient.Call("validators", nil, vals); err != nil { 199 return 0, make([]*tmtypes.Validator, 0), err 200 } 201 return vals.BlockHeight, vals.Validators, nil 202 } 203 204 func (n *Node) checkIsValidatorLoop() { 205 for { 206 select { 207 case <-n.quit: 208 return 209 case <-time.After(n.checkIsValidatorInterval): 210 n.checkIsValidator() 211 } 212 } 213 } 214 215 func (n *Node) checkIsValidator() { 216 _, validators, err := n.validators() 217 if err == nil { 218 for _, v := range validators { 219 key, err1 := n.getPubKey() 220 if err1 == nil && v.PubKey.Equals(key) { 221 n.IsValidator = true 222 } 223 } 224 } else { 225 n.logger.Info("check is validator failed", "err", err) 226 } 227 } 228 229 func (n *Node) getPubKey() (crypto.PubKey, error) { 230 if n.pubKey != nil { 231 return n.pubKey, nil 232 } 233 234 status := new(ctypes.ResultStatus) 235 _, err := n.rpcClient.Call("status", nil, status) 236 if err != nil { 237 return nil, err 238 } 239 n.pubKey = status.ValidatorInfo.PubKey 240 return n.pubKey, nil 241 } 242 243 type eventMeter interface { 244 Start() error 245 Stop() 246 RegisterLatencyCallback(em.LatencyCallbackFunc) 247 RegisterDisconnectCallback(em.DisconnectCallbackFunc) 248 Subscribe(string, em.EventCallbackFunc) error 249 Unsubscribe(string) error 250 SetLogger(l log.Logger) 251 } 252 253 // UnmarshalEvent unmarshals a json event 254 func UnmarshalEvent(b json.RawMessage) (string, events.EventData, error) { 255 event := new(ctypes.ResultEvent) 256 if err := cdc.UnmarshalJSON(b, event); err != nil { 257 return "", nil, err 258 } 259 return event.Query, event.Data, nil 260 }