package service import ( "context" "errors" "github.com/ethereum/go-ethereum/ethclient" "github.com/fbsobreira/gotron-sdk/pkg/client" logging "github.com/ipfs/go-log/v2" "google.golang.org/grpc" "regexp" "strconv" "time" ) var log = logging.Logger("server") type Node struct { ethRpcAddrs []string ethBlockNumber uint64 ethClient *ethclient.Client tronRpcAddrs []string tronBlockNumber int64 tronClient *client.GrpcClient } func NewNode(ethRpcAddrs []string, tronRpcAddrs []string) (*Node, error) { ethC, ethBlockNumber, err := getBestEthNode(ethRpcAddrs) if err != nil { return nil, err } tronC, tronBlockNumber, err := getBestTronNode(tronRpcAddrs) if err != nil { return nil, err } n := &Node{ ethRpcAddrs: ethRpcAddrs, ethBlockNumber: ethBlockNumber, ethClient: ethC, tronRpcAddrs: tronRpcAddrs, tronBlockNumber: tronBlockNumber, tronClient: tronC, } go n.monitor() return n, nil } func (n *Node) monitor() { ticker := time.NewTicker(time.Second * 60) for { select { case <-ticker.C: ethBlockNumber := n.keepEthClientConnect() tronBlockNumber := n.keepTronClientConnect() log.Infow("node monitor", "ethBlockNumber", ethBlockNumber, "tronBlockNumber", tronBlockNumber) } } } func (n *Node) keepEthClientConnect() uint64 { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() number, err := n.ethClient.BlockNumber(ctx) if err != nil || n.ethBlockNumber > number { ethC, ethBlockNumber, err := getBestEthNode(n.ethRpcAddrs) if err != nil { log.Errorw("keepEthClientConnect: getBestEthNode", "err", err) return 0 } n.ethClient.Close() n.ethBlockNumber = ethBlockNumber n.ethClient = ethC } return n.ethBlockNumber } func (n *Node) keepTronClientConnect() int64 { num, err := getTronBlockNumber(n.tronClient) if err != nil || n.tronBlockNumber > num { tronC, tronBlockNumber, err := getBestTronNode(n.tronRpcAddrs) if err != nil { log.Errorw("keepTronClientConnect: getBestTronNode", "err", err) return 0 } n.tronClient.Stop() n.tronBlockNumber = tronBlockNumber n.tronClient = tronC } return n.tronBlockNumber } func getBestEthNode(ethRpcAddrs []string) (*ethclient.Client, uint64, error) { var cs = make([]*ethclient.Client, 0, len(ethRpcAddrs)) for _, addr := range ethRpcAddrs { c, err := ethclient.Dial(addr) if err != nil { log.Warnw("getBestEthNode: bad rpc", "rpc", addr) continue } tem := *c cs = append(cs, &tem) } if len(cs) == 0 { return nil, 0, errors.New("no active eth node") } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() blockNumber := uint64(0) bestIndex := -1 for i, c := range cs { b, err := c.BlockNumber(ctx) if err != nil { log.Warnw("getBestEthNode: bad client", "err", err) continue } if b > blockNumber { bestIndex = i blockNumber = b } } if bestIndex == -1 { return nil, 0, errors.New("no active eth node") } for i, c := range cs { if i == bestIndex { continue } c.Close() } return cs[bestIndex], blockNumber, nil } func getBestTronNode(tronRpcAddrs []string) (*client.GrpcClient, int64, error) { var cs = make([]*client.GrpcClient, 0, len(tronRpcAddrs)) for _, addr := range tronRpcAddrs { c := client.NewGrpcClient(addr) err := c.Start(grpc.WithInsecure()) if err != nil { log.Warnw("getBestTronNode: bad rpc", "rpc", addr) continue } tem := *c cs = append(cs, &tem) } if len(cs) == 0 { return nil, 0, errors.New("no active tron node") } blockNumber := int64(0) bestIndex := -1 for i, c := range cs { num, err := getTronBlockNumber(c) if err != nil { log.Warnw("getBestTronNode: bad client", "err", err) continue } if num > blockNumber { bestIndex = i blockNumber = num } } if bestIndex == -1 { return nil, 0, errors.New("no active tron node") } for i, c := range cs { if i == bestIndex { continue } c.Stop() } return cs[bestIndex], blockNumber, nil } func getTronBlockNumber(c *client.GrpcClient) (int64, error) { b, err := c.GetNodeInfo() if err != nil { return 0, err } re := regexp.MustCompile(`Num:(\d+)`) match := re.FindStringSubmatch(b.Block) if len(match) < 2 { return 0, err } num, err := strconv.ParseInt(match[1], 10, 64) if err != nil { return 0, err } return num, nil }