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  }