You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

241 lines
5.5 KiB

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "math"
  6. "time"
  7. em "github.com/tendermint/go-event-meter"
  8. events "github.com/tendermint/go-events"
  9. rpc_client "github.com/tendermint/go-rpc/client"
  10. tmtypes "github.com/tendermint/tendermint/types"
  11. crypto "github.com/tendermint/go-crypto"
  12. wire "github.com/tendermint/go-wire"
  13. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  14. )
  15. const maxRestarts = 25
  16. type Node struct {
  17. rpcAddr string
  18. IsValidator bool `json:"is_validator"` // validator or non-validator?
  19. pubKey crypto.PubKey `json:"pub_key"`
  20. Name string `json:"name"`
  21. Online bool `json:"online"`
  22. Height uint64 `json:"height"`
  23. BlockLatency float64 `json:"block_latency" wire:"unsafe"` // ms, interval between block commits
  24. // em holds the ws connection. Each eventMeter callback is called in a separate go-routine.
  25. em eventMeter
  26. // rpcClient is an http client for making RPC calls to TM
  27. rpcClient *rpc_client.ClientURI
  28. blockCh chan<- tmtypes.Header
  29. blockLatencyCh chan<- float64
  30. disconnectCh chan<- bool
  31. quit chan struct{}
  32. }
  33. func NewNode(rpcAddr string) *Node {
  34. em := em.NewEventMeter(rpcAddr, UnmarshalEvent)
  35. return NewNodeWithEventMeter(rpcAddr, em)
  36. }
  37. func NewNodeWithEventMeter(rpcAddr string, em eventMeter) *Node {
  38. return &Node{
  39. rpcAddr: rpcAddr,
  40. em: em,
  41. rpcClient: rpc_client.NewClientURI(rpcAddr),
  42. Name: rpcAddr,
  43. quit: make(chan struct{}),
  44. }
  45. }
  46. func (n *Node) SendBlocksTo(ch chan<- tmtypes.Header) {
  47. n.blockCh = ch
  48. }
  49. func (n *Node) SendBlockLatenciesTo(ch chan<- float64) {
  50. n.blockLatencyCh = ch
  51. }
  52. func (n *Node) NotifyAboutDisconnects(ch chan<- bool) {
  53. n.disconnectCh = ch
  54. }
  55. func (n *Node) Start() error {
  56. if _, err := n.em.Start(); err != nil {
  57. return err
  58. }
  59. n.em.RegisterLatencyCallback(latencyCallback(n))
  60. n.em.Subscribe(tmtypes.EventStringNewBlockHeader(), newBlockCallback(n))
  61. n.em.RegisterDisconnectCallback(disconnectCallback(n))
  62. n.Online = true
  63. go n.checkIsValidator()
  64. return nil
  65. }
  66. func (n *Node) Stop() {
  67. n.Online = false
  68. n.em.RegisterLatencyCallback(nil)
  69. n.em.Unsubscribe(tmtypes.EventStringNewBlockHeader())
  70. n.em.RegisterDisconnectCallback(nil)
  71. // FIXME stop blocks at event_meter.go:140
  72. // n.em.Stop()
  73. close(n.quit)
  74. }
  75. // implements eventmeter.EventCallbackFunc
  76. func newBlockCallback(n *Node) em.EventCallbackFunc {
  77. return func(metric *em.EventMetric, data events.EventData) {
  78. block := data.(tmtypes.EventDataNewBlockHeader).Header
  79. n.Height = uint64(block.Height)
  80. if n.blockCh != nil {
  81. n.blockCh <- *block
  82. }
  83. }
  84. }
  85. // implements eventmeter.EventLatencyFunc
  86. func latencyCallback(n *Node) em.LatencyCallbackFunc {
  87. return func(latency float64) {
  88. n.BlockLatency = latency / 1000000.0 // ns to ms
  89. if n.blockLatencyCh != nil {
  90. n.blockLatencyCh <- latency
  91. }
  92. }
  93. }
  94. // implements eventmeter.DisconnectCallbackFunc
  95. func disconnectCallback(n *Node) em.DisconnectCallbackFunc {
  96. return func() {
  97. n.Online = false
  98. if n.disconnectCh != nil {
  99. n.disconnectCh <- true
  100. }
  101. if err := n.RestartBackOff(); err != nil {
  102. log.Error(err.Error())
  103. } else {
  104. n.Online = true
  105. if n.disconnectCh != nil {
  106. n.disconnectCh <- false
  107. }
  108. }
  109. }
  110. }
  111. func (n *Node) RestartBackOff() error {
  112. attempt := 0
  113. for {
  114. d := time.Duration(math.Exp2(float64(attempt)))
  115. time.Sleep(d * time.Second)
  116. if err := n.Start(); err != nil {
  117. log.Debug("Can't connect to node %v due to %v", n, err)
  118. } else {
  119. // TODO: authenticate pubkey
  120. return nil
  121. }
  122. attempt++
  123. if attempt > maxRestarts {
  124. return fmt.Errorf("Reached max restarts for node %v", n)
  125. }
  126. }
  127. }
  128. func (n *Node) NumValidators() (height uint64, num int, err error) {
  129. height, vals, err := n.validators()
  130. if err != nil {
  131. return 0, 0, err
  132. }
  133. return height, len(vals), nil
  134. }
  135. func (n *Node) validators() (height uint64, validators []*tmtypes.Validator, err error) {
  136. var result ctypes.TMResult
  137. if _, err = n.rpcClient.Call("validators", nil, &result); err != nil {
  138. return 0, make([]*tmtypes.Validator, 0), err
  139. }
  140. vals := result.(*ctypes.ResultValidators)
  141. return uint64(vals.BlockHeight), vals.Validators, nil
  142. }
  143. func (n *Node) checkIsValidator() {
  144. for {
  145. select {
  146. case <-n.quit:
  147. return
  148. case <-time.After(5 * time.Second):
  149. _, validators, err := n.validators()
  150. if err == nil {
  151. for _, v := range validators {
  152. key, err := n.getPubKey()
  153. if err == nil && v.PubKey == key {
  154. n.IsValidator = true
  155. }
  156. }
  157. } else {
  158. log.Debug(err.Error())
  159. }
  160. }
  161. }
  162. }
  163. func (n *Node) getPubKey() (crypto.PubKey, error) {
  164. if n.pubKey != nil {
  165. return n.pubKey, nil
  166. }
  167. var result ctypes.TMResult
  168. _, err := n.rpcClient.Call("status", nil, &result)
  169. if err != nil {
  170. return nil, err
  171. }
  172. status := result.(*ctypes.ResultStatus)
  173. n.pubKey = status.PubKey
  174. return n.pubKey, nil
  175. }
  176. type eventMeter interface {
  177. Start() (bool, error)
  178. Stop() bool
  179. RegisterLatencyCallback(em.LatencyCallbackFunc)
  180. RegisterDisconnectCallback(em.DisconnectCallbackFunc)
  181. Subscribe(string, em.EventCallbackFunc) error
  182. Unsubscribe(string) error
  183. }
  184. // UnmarshalEvent unmarshals a json event
  185. func UnmarshalEvent(b json.RawMessage) (string, events.EventData, error) {
  186. var err error
  187. result := new(ctypes.TMResult)
  188. wire.ReadJSONPtr(result, b, &err)
  189. if err != nil {
  190. return "", nil, err
  191. }
  192. event, ok := (*result).(*ctypes.ResultEvent)
  193. if !ok {
  194. return "", nil, nil // TODO: handle non-event messages (ie. return from subscribe/unsubscribe)
  195. // fmt.Errorf("Result is not type *ctypes.ResultEvent. Got %v", reflect.TypeOf(*result))
  196. }
  197. return event.Name, event.Data, nil
  198. }