adminclient.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. package guardiand
  2. import (
  3. "context"
  4. "encoding/hex"
  5. "fmt"
  6. publicrpcv1 "github.com/certusone/wormhole/node/pkg/proto/publicrpc/v1"
  7. "github.com/certusone/wormhole/node/pkg/vaa"
  8. "github.com/davecgh/go-spew/spew"
  9. "github.com/spf13/pflag"
  10. "io/ioutil"
  11. "log"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "github.com/spf13/cobra"
  16. "github.com/status-im/keycard-go/hexutils"
  17. "google.golang.org/grpc"
  18. "google.golang.org/protobuf/encoding/prototext"
  19. nodev1 "github.com/certusone/wormhole/node/pkg/proto/node/v1"
  20. )
  21. var (
  22. clientSocketPath *string
  23. )
  24. func init() {
  25. // Shared flags for all admin commands
  26. pf := pflag.NewFlagSet("commonAdminFlags", pflag.ContinueOnError)
  27. clientSocketPath = pf.String("socket", "", "gRPC admin server socket to connect to")
  28. err := cobra.MarkFlagRequired(pf, "socket")
  29. if err != nil {
  30. panic(err)
  31. }
  32. AdminClientInjectGuardianSetUpdateCmd.Flags().AddFlagSet(pf)
  33. AdminClientFindMissingMessagesCmd.Flags().AddFlagSet(pf)
  34. AdminClientListNodes.Flags().AddFlagSet(pf)
  35. DumpVAAByMessageID.Flags().AddFlagSet(pf)
  36. AdminCmd.AddCommand(AdminClientInjectGuardianSetUpdateCmd)
  37. AdminCmd.AddCommand(AdminClientFindMissingMessagesCmd)
  38. AdminCmd.AddCommand(AdminClientGovernanceVAAVerifyCmd)
  39. AdminCmd.AddCommand(AdminClientListNodes)
  40. AdminCmd.AddCommand(DumpVAAByMessageID)
  41. }
  42. var AdminCmd = &cobra.Command{
  43. Use: "admin",
  44. Short: "Guardian node admin commands",
  45. }
  46. var AdminClientInjectGuardianSetUpdateCmd = &cobra.Command{
  47. Use: "governance-vaa-inject [FILENAME]",
  48. Short: "Inject and sign a governance VAA from a prototxt file (see docs!)",
  49. Run: runInjectGovernanceVAA,
  50. Args: cobra.ExactArgs(1),
  51. }
  52. var AdminClientFindMissingMessagesCmd = &cobra.Command{
  53. Use: "find-missing-messages [CHAIN_ID] [EMITTER_ADDRESS_HEX]",
  54. Short: "Find sequence number gaps for the given chain ID and emitter address",
  55. Run: runFindMissingMessages,
  56. Args: cobra.ExactArgs(2),
  57. }
  58. var DumpVAAByMessageID = &cobra.Command{
  59. Use: "dump-vaa-by-message-id [MESSAGE_ID]",
  60. Short: "Retrieve a VAA by message ID (chain/emitter/seq) and decode and dump the VAA",
  61. Run: runDumpVAAByMessageID,
  62. Args: cobra.ExactArgs(1),
  63. }
  64. func getAdminClient(ctx context.Context, addr string) (*grpc.ClientConn, error, nodev1.NodePrivilegedServiceClient) {
  65. conn, err := grpc.DialContext(ctx, fmt.Sprintf("unix:///%s", addr), grpc.WithInsecure())
  66. if err != nil {
  67. log.Fatalf("failed to connect to %s: %v", addr, err)
  68. }
  69. c := nodev1.NewNodePrivilegedServiceClient(conn)
  70. return conn, err, c
  71. }
  72. func getPublicRPCServiceClient(ctx context.Context, addr string) (*grpc.ClientConn, error, publicrpcv1.PublicRPCServiceClient) {
  73. conn, err := grpc.DialContext(ctx, fmt.Sprintf("unix:///%s", addr), grpc.WithInsecure())
  74. if err != nil {
  75. log.Fatalf("failed to connect to %s: %v", addr, err)
  76. }
  77. c := publicrpcv1.NewPublicRPCServiceClient(conn)
  78. return conn, err, c
  79. }
  80. func runInjectGovernanceVAA(cmd *cobra.Command, args []string) {
  81. path := args[0]
  82. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  83. defer cancel()
  84. conn, err, c := getAdminClient(ctx, *clientSocketPath)
  85. defer conn.Close()
  86. if err != nil {
  87. log.Fatalf("failed to get admin client: %v", err)
  88. }
  89. b, err := ioutil.ReadFile(path)
  90. if err != nil {
  91. log.Fatalf("failed to read file: %v", err)
  92. }
  93. var msg nodev1.InjectGovernanceVAARequest
  94. err = prototext.Unmarshal(b, &msg)
  95. if err != nil {
  96. log.Fatalf("failed to deserialize: %v", err)
  97. }
  98. resp, err := c.InjectGovernanceVAA(ctx, &msg)
  99. if err != nil {
  100. log.Fatalf("failed to submit governance VAA: %v", err)
  101. }
  102. for _, digest := range resp.Digests {
  103. log.Printf("VAA successfully injected with digest %s", hexutils.BytesToHex(digest))
  104. }
  105. }
  106. func runFindMissingMessages(cmd *cobra.Command, args []string) {
  107. chainID, err := strconv.Atoi(args[0])
  108. if err != nil {
  109. log.Fatalf("invalid chain ID: %v", err)
  110. }
  111. emitterAddress := args[1]
  112. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  113. defer cancel()
  114. conn, err, c := getAdminClient(ctx, *clientSocketPath)
  115. defer conn.Close()
  116. if err != nil {
  117. log.Fatalf("failed to get admin client: %v", err)
  118. }
  119. msg := nodev1.FindMissingMessagesRequest{
  120. EmitterChain: uint32(chainID),
  121. EmitterAddress: emitterAddress,
  122. }
  123. resp, err := c.FindMissingMessages(ctx, &msg)
  124. if err != nil {
  125. log.Fatalf("failed to run find FindMissingMessages RPC: %v", err)
  126. }
  127. for _, id := range resp.MissingMessages {
  128. fmt.Println(id)
  129. }
  130. log.Printf("processed %s sequences %d to %d (%d gaps)",
  131. emitterAddress, resp.FirstSequence, resp.LastSequence, len(resp.MissingMessages))
  132. }
  133. // runDumpVAAByMessageID uses GetSignedVAA to request the given message,
  134. // then decode and dump the VAA.
  135. func runDumpVAAByMessageID(cmd *cobra.Command, args []string) {
  136. // Parse the {chain,emitter,seq} string.
  137. parts := strings.Split(args[0], "/")
  138. if len(parts) != 3 {
  139. log.Fatalf("invalid message ID: %s", args[0])
  140. }
  141. chainID, err := strconv.ParseUint(parts[0], 10, 32)
  142. if err != nil {
  143. log.Fatalf("invalid chain ID: %v", err)
  144. }
  145. emitterAddress := parts[1]
  146. seq, err := strconv.ParseUint(parts[2], 10, 64)
  147. if err != nil {
  148. log.Fatalf("invalid sequence number: %v", err)
  149. }
  150. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  151. defer cancel()
  152. conn, err, c := getPublicRPCServiceClient(ctx, *clientSocketPath)
  153. defer conn.Close()
  154. if err != nil {
  155. log.Fatalf("failed to get public RPC service client: %v", err)
  156. }
  157. msg := publicrpcv1.GetSignedVAARequest{
  158. MessageId: &publicrpcv1.MessageID{
  159. EmitterChain: publicrpcv1.ChainID(chainID),
  160. EmitterAddress: emitterAddress,
  161. Sequence: seq,
  162. },
  163. }
  164. resp, err := c.GetSignedVAA(ctx, &msg)
  165. if err != nil {
  166. log.Fatalf("failed to run GetSignedVAA RPC: %v", err)
  167. }
  168. v, err := vaa.Unmarshal(resp.VaaBytes)
  169. if err != nil {
  170. log.Fatalf("failed to decode VAA: %v", err)
  171. }
  172. log.Printf("VAA with digest %s: %+v\n", v.HexDigest(), spew.Sdump(v))
  173. fmt.Printf("Bytes:\n%s\n", hex.EncodeToString(resp.VaaBytes))
  174. }