adminnodes.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package guardiand
  2. import (
  3. "context"
  4. "fmt"
  5. publicrpcv1 "github.com/certusone/wormhole/node/pkg/proto/publicrpc/v1"
  6. "github.com/certusone/wormhole/node/pkg/vaa"
  7. "github.com/spf13/cobra"
  8. "log"
  9. "os"
  10. "sort"
  11. "text/tabwriter"
  12. "time"
  13. )
  14. // How to test in container:
  15. // kubectl exec guardian-0 -- /guardiand admin list-nodes --socket /tmp/admin.sock
  16. var (
  17. showDetails bool
  18. )
  19. func init() {
  20. AdminClientListNodes.Flags().BoolVar(&showDetails, "showDetails", false, "Show error counter and contract addresses")
  21. }
  22. var AdminClientListNodes = &cobra.Command{
  23. Use: "list-nodes",
  24. Short: "Fetches an aggregated list of guardian nodes",
  25. Run: runListNodes,
  26. }
  27. func runListNodes(cmd *cobra.Command, args []string) {
  28. ctx := context.Background()
  29. conn, err, c := getPublicRPCServiceClient(ctx, *clientSocketPath)
  30. defer conn.Close()
  31. if err != nil {
  32. log.Fatalf("failed to get publicrpc client: %v", err)
  33. }
  34. lastHeartbeats, err := c.GetLastHeartbeats(ctx, &publicrpcv1.GetLastHeartbeatsRequest{})
  35. if err != nil {
  36. log.Fatalf("failed to list nodes: %v", err)
  37. }
  38. gs, err := c.GetCurrentGuardianSet(ctx, &publicrpcv1.GetCurrentGuardianSetRequest{})
  39. if err != nil {
  40. log.Fatalf("failed to list current guardian get: %v", err)
  41. }
  42. log.Printf("current guardian set index: %d (%d guardians)",
  43. gs.GuardianSet.Index, len(gs.GuardianSet.Addresses))
  44. nodes := lastHeartbeats.Entries
  45. sort.Slice(nodes, func(i, j int) bool {
  46. if nodes[i].RawHeartbeat == nil || nodes[j].RawHeartbeat == nil {
  47. return false
  48. }
  49. return nodes[i].RawHeartbeat.NodeName < nodes[j].RawHeartbeat.NodeName
  50. })
  51. log.Printf("%d nodes in guardian state set", len(nodes))
  52. w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)
  53. if showDetails {
  54. w.Write([]byte("Node key\tGuardian key\tNode name\tVersion\tLast seen\tUptime\tSolana\tEthereum\tTerra\tBSC\n"))
  55. } else {
  56. w.Write([]byte("Node key\tGuardian key\tNode name\tVersion\tLast seen\tSolana\tEthereum\tTerra\tBSC\n"))
  57. }
  58. for _, h := range nodes {
  59. if h.RawHeartbeat == nil {
  60. continue
  61. }
  62. last := time.Unix(0, h.RawHeartbeat.Timestamp)
  63. boot := time.Unix(0, h.RawHeartbeat.BootTimestamp)
  64. heights := map[vaa.ChainID]int64{}
  65. truncAddrs := make(map[vaa.ChainID]string)
  66. errors := map[vaa.ChainID]uint64{}
  67. for _, n := range h.RawHeartbeat.Networks {
  68. heights[vaa.ChainID(n.Id)] = n.Height
  69. errors[vaa.ChainID(n.Id)] = n.ErrorCount
  70. if len(n.BridgeAddress) >= 16 {
  71. truncAddrs[vaa.ChainID(n.Id)] = n.BridgeAddress[:16]
  72. } else {
  73. truncAddrs[vaa.ChainID(n.Id)] = "INVALID"
  74. }
  75. }
  76. if showDetails {
  77. fmt.Fprintf(w,
  78. "%s\t%s\t%s\t%s\t%s\t%s\t%s %d (%d)\t%s %d (%d)\t%s %d (%d)\t%s %d (%d)\n",
  79. h.P2PNodeAddr,
  80. h.RawHeartbeat.GuardianAddr,
  81. h.RawHeartbeat.NodeName,
  82. h.RawHeartbeat.Version,
  83. time.Since(last),
  84. time.Since(boot),
  85. truncAddrs[vaa.ChainIDSolana],
  86. heights[vaa.ChainIDSolana],
  87. errors[vaa.ChainIDSolana],
  88. truncAddrs[vaa.ChainIDEthereum],
  89. heights[vaa.ChainIDEthereum],
  90. errors[vaa.ChainIDEthereum],
  91. truncAddrs[vaa.ChainIDTerra],
  92. heights[vaa.ChainIDTerra],
  93. errors[vaa.ChainIDTerra],
  94. truncAddrs[vaa.ChainIDBSC],
  95. heights[vaa.ChainIDBSC],
  96. errors[vaa.ChainIDBSC],
  97. )
  98. } else {
  99. fmt.Fprintf(w,
  100. "%s\t%s\t%s\t%s\t%s\t%d\t%d\t%d\t%d\n",
  101. h.P2PNodeAddr,
  102. h.RawHeartbeat.GuardianAddr,
  103. h.RawHeartbeat.NodeName,
  104. h.RawHeartbeat.Version,
  105. time.Since(last),
  106. heights[vaa.ChainIDSolana],
  107. heights[vaa.ChainIDEthereum],
  108. heights[vaa.ChainIDTerra],
  109. heights[vaa.ChainIDBSC],
  110. )
  111. }
  112. }
  113. w.Flush()
  114. fmt.Print("\n")
  115. for _, addr := range gs.GuardianSet.Addresses {
  116. var found bool
  117. for _, h := range nodes {
  118. if h.VerifiedGuardianAddr == addr {
  119. found = true
  120. }
  121. }
  122. if !found {
  123. fmt.Printf("Missing guardian: %s\n", addr)
  124. }
  125. }
  126. fmt.Println("\n[do not parse - use the gRPC or REST API for scripting]")
  127. }