adminclient.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. package guardiand
  2. import (
  3. "bufio"
  4. "context"
  5. "encoding/csv"
  6. "encoding/hex"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "log"
  11. "os"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "github.com/cosmos/cosmos-sdk/types"
  16. "github.com/davecgh/go-spew/spew"
  17. ethcommon "github.com/ethereum/go-ethereum/common"
  18. "github.com/ethereum/go-ethereum/crypto"
  19. "github.com/mr-tron/base58"
  20. "github.com/spf13/pflag"
  21. "golang.org/x/crypto/sha3"
  22. "github.com/certusone/wormhole/node/pkg/common"
  23. gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
  24. publicrpcv1 "github.com/certusone/wormhole/node/pkg/proto/publicrpc/v1"
  25. "github.com/wormhole-foundation/wormhole/sdk"
  26. "github.com/wormhole-foundation/wormhole/sdk/vaa"
  27. "github.com/spf13/cobra"
  28. "github.com/status-im/keycard-go/hexutils"
  29. "google.golang.org/grpc"
  30. "google.golang.org/grpc/credentials/insecure"
  31. "google.golang.org/protobuf/encoding/prototext"
  32. nodev1 "github.com/certusone/wormhole/node/pkg/proto/node/v1"
  33. )
  34. var (
  35. clientSocketPath *string
  36. shouldBackfill *bool
  37. unsafeDevnetMode *bool
  38. )
  39. func init() {
  40. // Shared flags for all admin commands
  41. pf := pflag.NewFlagSet("commonAdminFlags", pflag.ContinueOnError)
  42. clientSocketPath = pf.String("socket", "", "gRPC admin server socket to connect to")
  43. err := cobra.MarkFlagRequired(pf, "socket")
  44. if err != nil {
  45. panic(err)
  46. }
  47. shouldBackfill = AdminClientFindMissingMessagesCmd.Flags().Bool(
  48. "backfill", false, "backfill missing VAAs from public RPC")
  49. AdminClientInjectGuardianSetUpdateCmd.Flags().AddFlagSet(pf)
  50. AdminClientFindMissingMessagesCmd.Flags().AddFlagSet(pf)
  51. AdminClientListNodes.Flags().AddFlagSet(pf)
  52. DumpVAAByMessageID.Flags().AddFlagSet(pf)
  53. DumpRPCs.Flags().AddFlagSet(pf)
  54. SendObservationRequest.Flags().AddFlagSet(pf)
  55. ClientChainGovernorStatusCmd.Flags().AddFlagSet(pf)
  56. ClientChainGovernorReloadCmd.Flags().AddFlagSet(pf)
  57. ClientChainGovernorDropPendingVAACmd.Flags().AddFlagSet(pf)
  58. ClientChainGovernorReleasePendingVAACmd.Flags().AddFlagSet(pf)
  59. ClientChainGovernorResetReleaseTimerCmd.Flags().AddFlagSet(pf)
  60. PurgePythNetVaasCmd.Flags().AddFlagSet(pf)
  61. SignExistingVaaCmd.Flags().AddFlagSet(pf)
  62. SignExistingVaasFromCSVCmd.Flags().AddFlagSet(pf)
  63. GetAndObserveMissingVAAs.Flags().AddFlagSet(pf)
  64. adminClientSignWormchainAddressFlags := pflag.NewFlagSet("adminClientSignWormchainAddressFlags", pflag.ContinueOnError)
  65. unsafeDevnetMode = adminClientSignWormchainAddressFlags.Bool("unsafeDevMode", false, "Run in unsafe devnet mode")
  66. AdminClientSignWormchainAddress.Flags().AddFlagSet(adminClientSignWormchainAddressFlags)
  67. AdminCmd.AddCommand(AdminClientInjectGuardianSetUpdateCmd)
  68. AdminCmd.AddCommand(AdminClientFindMissingMessagesCmd)
  69. AdminCmd.AddCommand(AdminClientGovernanceVAAVerifyCmd)
  70. AdminCmd.AddCommand(AdminClientListNodes)
  71. AdminCmd.AddCommand(AdminClientSignWormchainAddress)
  72. AdminCmd.AddCommand(DumpVAAByMessageID)
  73. AdminCmd.AddCommand(DumpRPCs)
  74. AdminCmd.AddCommand(SendObservationRequest)
  75. AdminCmd.AddCommand(ClientChainGovernorStatusCmd)
  76. AdminCmd.AddCommand(ClientChainGovernorReloadCmd)
  77. AdminCmd.AddCommand(ClientChainGovernorDropPendingVAACmd)
  78. AdminCmd.AddCommand(ClientChainGovernorReleasePendingVAACmd)
  79. AdminCmd.AddCommand(ClientChainGovernorResetReleaseTimerCmd)
  80. AdminCmd.AddCommand(PurgePythNetVaasCmd)
  81. AdminCmd.AddCommand(SignExistingVaaCmd)
  82. AdminCmd.AddCommand(SignExistingVaasFromCSVCmd)
  83. AdminCmd.AddCommand(Keccak256Hash)
  84. AdminCmd.AddCommand(GetAndObserveMissingVAAs)
  85. }
  86. var AdminCmd = &cobra.Command{
  87. Use: "admin",
  88. Short: "Guardian node admin commands",
  89. }
  90. var AdminClientSignWormchainAddress = &cobra.Command{
  91. Use: "sign-wormchain-address [/path/to/guardianKey] [wormchain-validator-address]",
  92. Short: "Sign a wormchain validator address. Only sign the address that you control the key for and will be for your validator.",
  93. RunE: runSignWormchainValidatorAddress,
  94. Args: cobra.ExactArgs(2),
  95. }
  96. var AdminClientInjectGuardianSetUpdateCmd = &cobra.Command{
  97. Use: "governance-vaa-inject [FILENAME]",
  98. Short: "Inject and sign a governance VAA from a prototxt file (see docs!)",
  99. Run: runInjectGovernanceVAA,
  100. Args: cobra.ExactArgs(1),
  101. }
  102. var AdminClientFindMissingMessagesCmd = &cobra.Command{
  103. Use: "find-missing-messages [CHAIN_ID] [EMITTER_ADDRESS_HEX]",
  104. Short: "Find sequence number gaps for the given chain ID and emitter address",
  105. Run: runFindMissingMessages,
  106. Args: cobra.ExactArgs(2),
  107. }
  108. var DumpVAAByMessageID = &cobra.Command{
  109. Use: "dump-vaa-by-message-id [MESSAGE_ID]",
  110. Short: "Retrieve a VAA by message ID (chain/emitter/seq) and decode and dump the VAA",
  111. Run: runDumpVAAByMessageID,
  112. Args: cobra.ExactArgs(1),
  113. }
  114. var SendObservationRequest = &cobra.Command{
  115. Use: "send-observation-request [CHAIN_ID|CHAIN_NAME] [TX_HASH_HEX]",
  116. Short: "Broadcast an observation request for the given chain ID and chain-specific tx_hash",
  117. Run: runSendObservationRequest,
  118. Args: cobra.ExactArgs(2),
  119. }
  120. var ClientChainGovernorStatusCmd = &cobra.Command{
  121. Use: "governor-status",
  122. Short: "Displays the status of the chain governor",
  123. Run: runChainGovernorStatus,
  124. Args: cobra.ExactArgs(0),
  125. }
  126. var ClientChainGovernorReloadCmd = &cobra.Command{
  127. Use: "governor-reload",
  128. Short: "Clears the chain governor history and reloads it from the database",
  129. Run: runChainGovernorReload,
  130. Args: cobra.ExactArgs(0),
  131. }
  132. var ClientChainGovernorDropPendingVAACmd = &cobra.Command{
  133. Use: "governor-drop-pending-vaa [VAA_ID]",
  134. Short: "Removes the specified VAA (chain/emitter/seq) from the chain governor pending list",
  135. Run: runChainGovernorDropPendingVAA,
  136. Args: cobra.ExactArgs(1),
  137. }
  138. var ClientChainGovernorReleasePendingVAACmd = &cobra.Command{
  139. Use: "governor-release-pending-vaa [VAA_ID]",
  140. Short: "Releases the specified VAA (chain/emitter/seq) from the chain governor pending list, publishing it immediately",
  141. Run: runChainGovernorReleasePendingVAA,
  142. Args: cobra.ExactArgs(1),
  143. }
  144. var ClientChainGovernorResetReleaseTimerCmd = &cobra.Command{
  145. Use: "governor-reset-release-timer [VAA_ID] <num_days>",
  146. Short: "Resets the release timer for a chain governor pending VAA, extending it to num_days (up to a maximum of 7), defaulting to one day if num_days is omitted",
  147. Run: runChainGovernorResetReleaseTimer,
  148. Args: cobra.RangeArgs(1, 2),
  149. }
  150. var PurgePythNetVaasCmd = &cobra.Command{
  151. Use: "purge-pythnet-vaas [DAYS_OLD] <logonly>",
  152. Short: "Deletes PythNet VAAs from the database that are more than [DAYS_OLD] days only (if logonly is specified, doesn't delete anything)",
  153. Run: runPurgePythNetVaas,
  154. Args: cobra.RangeArgs(1, 2),
  155. }
  156. var SignExistingVaaCmd = &cobra.Command{
  157. Use: "sign-existing-vaa [VAA] [NEW_GUARDIANS] [NEW_GUARDIAN_SET_INDEX]",
  158. Short: "Signs an existing VAA for a new guardian set using the local guardian key. This only works if the new VAA would have quorum.",
  159. Run: runSignExistingVaa,
  160. Args: cobra.ExactArgs(3),
  161. }
  162. var SignExistingVaasFromCSVCmd = &cobra.Command{
  163. Use: "sign-existing-vaas-csv [IN_FILE] [OUT_FILE] [NEW_GUARDIANS] [NEW_GUARDIAN_SET_INDEX]",
  164. Short: "Signs a CSV [VAA_ID,VAA_HEX] of existing VAAs for a new guardian set using the local guardian key and writes it to a new CSV. VAAs that don't have quorum on the new set will be dropped.",
  165. Run: runSignExistingVaasFromCSV,
  166. Args: cobra.ExactArgs(4),
  167. }
  168. var DumpRPCs = &cobra.Command{
  169. Use: "dump-rpcs",
  170. Short: "Displays the RPCs in use by the guardian",
  171. Run: runDumpRPCs,
  172. Args: cobra.ExactArgs(0),
  173. }
  174. var GetAndObserveMissingVAAs = &cobra.Command{
  175. Use: "get-and-observe-missing-vaas [URL] [API_KEY]",
  176. Short: "Get the list of missing VAAs from a cloud function and try to reobserve them.",
  177. Run: runGetAndObserveMissingVAAs,
  178. Args: cobra.ExactArgs(2),
  179. }
  180. var Keccak256Hash = &cobra.Command{
  181. Use: "keccak256",
  182. Short: "Compute legacy keccak256 hash",
  183. Run: runKeccak256Hash,
  184. Args: cobra.ExactArgs(0),
  185. }
  186. func getAdminClient(ctx context.Context, addr string) (*grpc.ClientConn, nodev1.NodePrivilegedServiceClient, error) {
  187. conn, err := grpc.DialContext(ctx, fmt.Sprintf("unix:///%s", addr), grpc.WithTransportCredentials(insecure.NewCredentials()))
  188. if err != nil {
  189. log.Fatalf("failed to connect to %s: %v", addr, err)
  190. }
  191. c := nodev1.NewNodePrivilegedServiceClient(conn)
  192. return conn, c, err
  193. }
  194. func getPublicRPCServiceClient(ctx context.Context, addr string) (*grpc.ClientConn, publicrpcv1.PublicRPCServiceClient, error) {
  195. conn, err := grpc.DialContext(ctx, fmt.Sprintf("unix:///%s", addr), grpc.WithTransportCredentials(insecure.NewCredentials()))
  196. if err != nil {
  197. log.Fatalf("failed to connect to %s: %v", addr, err)
  198. }
  199. c := publicrpcv1.NewPublicRPCServiceClient(conn)
  200. return conn, c, err
  201. }
  202. func runSignWormchainValidatorAddress(cmd *cobra.Command, args []string) error {
  203. guardianKeyPath := args[0]
  204. wormchainAddress := args[1]
  205. if !strings.HasPrefix(wormchainAddress, "wormhole") || strings.HasPrefix(wormchainAddress, "wormholeval") {
  206. return errors.New("must provide a bech32 address that has 'wormhole' prefix")
  207. }
  208. gk, err := common.LoadGuardianKey(guardianKeyPath, *unsafeDevnetMode)
  209. if err != nil {
  210. return fmt.Errorf("failed to load guardian key: %w", err)
  211. }
  212. addr, err := types.GetFromBech32(wormchainAddress, "wormhole")
  213. if err != nil {
  214. return fmt.Errorf("failed to decode wormchain address: %w", err)
  215. }
  216. // Hash and sign address
  217. addrHash := crypto.Keccak256Hash(sdk.SignedWormchainAddressPrefix, addr)
  218. sig, err := crypto.Sign(addrHash[:], gk)
  219. if err != nil {
  220. return fmt.Errorf("failed to sign wormchain address: %w", err)
  221. }
  222. fmt.Println(hex.EncodeToString(sig))
  223. return nil
  224. }
  225. func runInjectGovernanceVAA(cmd *cobra.Command, args []string) {
  226. path := args[0]
  227. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  228. defer cancel()
  229. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  230. if err != nil {
  231. log.Fatalf("failed to get admin client: %v", err)
  232. }
  233. defer conn.Close()
  234. b, err := os.ReadFile(path)
  235. if err != nil {
  236. log.Fatalf("failed to read file: %v", err)
  237. }
  238. var msg nodev1.InjectGovernanceVAARequest
  239. err = prototext.Unmarshal(b, &msg)
  240. if err != nil {
  241. log.Fatalf("failed to deserialize: %v", err)
  242. }
  243. resp, err := c.InjectGovernanceVAA(ctx, &msg)
  244. if err != nil {
  245. log.Fatalf("failed to submit governance VAA: %v", err)
  246. }
  247. for _, digest := range resp.Digests {
  248. log.Printf("VAA successfully injected with digest %s", hexutils.BytesToHex(digest))
  249. }
  250. }
  251. func runFindMissingMessages(cmd *cobra.Command, args []string) {
  252. chainID, err := strconv.Atoi(args[0])
  253. if err != nil {
  254. log.Fatalf("invalid chain ID: %v", err)
  255. }
  256. emitterAddress := args[1]
  257. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
  258. defer cancel()
  259. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  260. if err != nil {
  261. log.Fatalf("failed to get admin client: %v", err)
  262. }
  263. defer conn.Close()
  264. msg := nodev1.FindMissingMessagesRequest{
  265. EmitterChain: uint32(chainID),
  266. EmitterAddress: emitterAddress,
  267. RpcBackfill: *shouldBackfill,
  268. BackfillNodes: sdk.PublicRPCEndpoints,
  269. }
  270. resp, err := c.FindMissingMessages(ctx, &msg)
  271. if err != nil {
  272. log.Fatalf("failed to run find FindMissingMessages RPC: %v", err)
  273. }
  274. for _, id := range resp.MissingMessages {
  275. fmt.Println(id)
  276. }
  277. log.Printf("processed %s sequences %d to %d (%d gaps)",
  278. emitterAddress, resp.FirstSequence, resp.LastSequence, len(resp.MissingMessages))
  279. }
  280. // runDumpVAAByMessageID uses GetSignedVAA to request the given message,
  281. // then decode and dump the VAA.
  282. func runDumpVAAByMessageID(cmd *cobra.Command, args []string) {
  283. // Parse the {chain,emitter,seq} string.
  284. parts := strings.Split(args[0], "/")
  285. if len(parts) != 3 {
  286. log.Fatalf("invalid message ID: %s", args[0])
  287. }
  288. chainID, err := strconv.ParseUint(parts[0], 10, 32)
  289. if err != nil {
  290. log.Fatalf("invalid chain ID: %v", err)
  291. }
  292. emitterAddress := parts[1]
  293. seq, err := strconv.ParseUint(parts[2], 10, 64)
  294. if err != nil {
  295. log.Fatalf("invalid sequence number: %v", err)
  296. }
  297. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  298. defer cancel()
  299. conn, c, err := getPublicRPCServiceClient(ctx, *clientSocketPath)
  300. if err != nil {
  301. log.Fatalf("failed to get public RPC service client: %v", err)
  302. }
  303. defer conn.Close()
  304. msg := publicrpcv1.GetSignedVAARequest{
  305. MessageId: &publicrpcv1.MessageID{
  306. EmitterChain: publicrpcv1.ChainID(chainID),
  307. EmitterAddress: emitterAddress,
  308. Sequence: seq,
  309. },
  310. }
  311. resp, err := c.GetSignedVAA(ctx, &msg)
  312. if err != nil {
  313. log.Fatalf("failed to run GetSignedVAA RPC: %v", err)
  314. }
  315. v, err := vaa.Unmarshal(resp.VaaBytes)
  316. if err != nil {
  317. log.Fatalf("failed to decode VAA: %v", err)
  318. }
  319. log.Printf("VAA with digest %s: %+v\n", v.HexDigest(), spew.Sdump(v))
  320. fmt.Printf("Bytes:\n%s\n", hex.EncodeToString(resp.VaaBytes))
  321. }
  322. func runSendObservationRequest(cmd *cobra.Command, args []string) {
  323. chainID, err := parseChainID(args[0])
  324. if err != nil {
  325. log.Fatalf("invalid chain ID: %v", err)
  326. }
  327. // Support tx with or without leading 0x so copy / pasta
  328. // from monitoring tools is easier.
  329. txHash, err := hex.DecodeString(strings.TrimPrefix(args[1], "0x"))
  330. if err != nil {
  331. txHash, err = base58.Decode(args[1])
  332. if err != nil {
  333. log.Fatalf("invalid transaction hash (neither hex nor base58): %v", err)
  334. }
  335. }
  336. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  337. defer cancel()
  338. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  339. if err != nil {
  340. log.Fatalf("failed to get admin client: %v", err)
  341. }
  342. defer conn.Close()
  343. _, err = c.SendObservationRequest(ctx, &nodev1.SendObservationRequestRequest{
  344. ObservationRequest: &gossipv1.ObservationRequest{
  345. ChainId: uint32(chainID),
  346. TxHash: txHash,
  347. },
  348. })
  349. if err != nil {
  350. log.Fatalf("failed to send observation request: %v", err)
  351. }
  352. }
  353. func runDumpRPCs(cmd *cobra.Command, args []string) {
  354. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  355. defer cancel()
  356. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  357. if err != nil {
  358. log.Fatalf("failed to get admin client: %v", err)
  359. }
  360. defer conn.Close()
  361. resp, err := c.DumpRPCs(ctx, &nodev1.DumpRPCsRequest{})
  362. if err != nil {
  363. log.Fatalf("failed to run dump-rpcs: %s", err)
  364. }
  365. for parm, rpc := range resp.Response {
  366. fmt.Println(parm, " = [", rpc, "]")
  367. }
  368. }
  369. func runGetAndObserveMissingVAAs(cmd *cobra.Command, args []string) {
  370. url := args[0]
  371. if !strings.HasPrefix(url, "https://") {
  372. log.Fatalf("invalid url: %s", url)
  373. }
  374. apiKey := args[1]
  375. if len(apiKey) == 0 {
  376. log.Fatalf("missing api key")
  377. }
  378. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  379. defer cancel()
  380. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  381. if err != nil {
  382. log.Fatalf("failed to get admin client: %v", err)
  383. }
  384. defer conn.Close()
  385. cmdInfo := nodev1.GetAndObserveMissingVAAsRequest{
  386. Url: url,
  387. ApiKey: apiKey,
  388. }
  389. resp, err := c.GetAndObserveMissingVAAs(ctx, &cmdInfo)
  390. if err != nil {
  391. log.Fatalf("failed to run get-missing-vaas: %s", err)
  392. }
  393. fmt.Println(resp.GetResponse())
  394. }
  395. func runChainGovernorStatus(cmd *cobra.Command, args []string) {
  396. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  397. defer cancel()
  398. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  399. if err != nil {
  400. log.Fatalf("failed to get admin client: %v", err)
  401. }
  402. defer conn.Close()
  403. msg := nodev1.ChainGovernorStatusRequest{}
  404. resp, err := c.ChainGovernorStatus(ctx, &msg)
  405. if err != nil {
  406. log.Fatalf("failed to run ChainGovernorStatus RPC: %s", err)
  407. }
  408. fmt.Println(resp.Response)
  409. }
  410. func runChainGovernorReload(cmd *cobra.Command, args []string) {
  411. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  412. defer cancel()
  413. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  414. if err != nil {
  415. log.Fatalf("failed to get admin client: %v", err)
  416. }
  417. defer conn.Close()
  418. msg := nodev1.ChainGovernorReloadRequest{}
  419. resp, err := c.ChainGovernorReload(ctx, &msg)
  420. if err != nil {
  421. log.Fatalf("failed to run ChainGovernorReload RPC: %s", err)
  422. }
  423. fmt.Println(resp.Response)
  424. }
  425. func runChainGovernorDropPendingVAA(cmd *cobra.Command, args []string) {
  426. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  427. defer cancel()
  428. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  429. if err != nil {
  430. log.Fatalf("failed to get admin client: %v", err)
  431. }
  432. defer conn.Close()
  433. msg := nodev1.ChainGovernorDropPendingVAARequest{
  434. VaaId: args[0],
  435. }
  436. resp, err := c.ChainGovernorDropPendingVAA(ctx, &msg)
  437. if err != nil {
  438. log.Fatalf("failed to run ChainGovernorDropPendingVAA RPC: %s", err)
  439. }
  440. fmt.Println(resp.Response)
  441. }
  442. func runChainGovernorReleasePendingVAA(cmd *cobra.Command, args []string) {
  443. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  444. defer cancel()
  445. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  446. if err != nil {
  447. log.Fatalf("failed to get admin client: %v", err)
  448. }
  449. defer conn.Close()
  450. msg := nodev1.ChainGovernorReleasePendingVAARequest{
  451. VaaId: args[0],
  452. }
  453. resp, err := c.ChainGovernorReleasePendingVAA(ctx, &msg)
  454. if err != nil {
  455. log.Fatalf("failed to run ChainGovernorReleasePendingVAA RPC: %s", err)
  456. }
  457. fmt.Println(resp.Response)
  458. }
  459. func runChainGovernorResetReleaseTimer(cmd *cobra.Command, args []string) {
  460. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  461. defer cancel()
  462. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  463. if err != nil {
  464. log.Fatalf("failed to get admin client: %v", err)
  465. }
  466. defer conn.Close()
  467. // defaults to 1 day if num_days isn't specified
  468. numDays := uint32(1)
  469. if len(args) > 1 {
  470. numDaysArg, err := strconv.Atoi(args[1])
  471. if err != nil {
  472. log.Fatalf("invalid num_days: %v", err)
  473. }
  474. numDays = uint32(numDaysArg)
  475. }
  476. msg := nodev1.ChainGovernorResetReleaseTimerRequest{
  477. VaaId: args[0],
  478. NumDays: numDays,
  479. }
  480. resp, err := c.ChainGovernorResetReleaseTimer(ctx, &msg)
  481. if err != nil {
  482. log.Fatalf("failed to run ChainGovernorResetReleaseTimer RPC: %s", err)
  483. }
  484. fmt.Println(resp.Response)
  485. }
  486. func runPurgePythNetVaas(cmd *cobra.Command, args []string) {
  487. daysOld, err := strconv.Atoi(args[0])
  488. if err != nil {
  489. log.Fatalf("invalid DAYS_OLD: %v", err)
  490. }
  491. if daysOld < 0 {
  492. log.Fatalf("DAYS_OLD may not be negative")
  493. }
  494. logOnly := false
  495. if len(args) > 1 {
  496. if args[1] != "logonly" {
  497. log.Fatalf("invalid option, only \"logonly\" is supported")
  498. }
  499. logOnly = true
  500. }
  501. ctx, cancel := context.WithCancel(context.Background())
  502. defer cancel()
  503. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  504. if err != nil {
  505. log.Fatalf("failed to get admin client: %v", err)
  506. }
  507. defer conn.Close()
  508. msg := nodev1.PurgePythNetVaasRequest{
  509. DaysOld: uint64(daysOld),
  510. LogOnly: logOnly,
  511. }
  512. resp, err := c.PurgePythNetVaas(ctx, &msg)
  513. if err != nil {
  514. log.Fatalf("failed to run PurgePythNetVaas RPC: %s", err)
  515. }
  516. fmt.Println(resp.Response)
  517. }
  518. func runSignExistingVaa(cmd *cobra.Command, args []string) {
  519. existingVAA := ethcommon.Hex2Bytes(args[0])
  520. if len(existingVAA) == 0 {
  521. log.Fatalf("vaa hex invalid")
  522. }
  523. newGsStrings := strings.Split(args[1], ",")
  524. newGsIndex, err := strconv.ParseUint(args[2], 10, 32)
  525. if err != nil {
  526. log.Fatalf("invalid new guardian set index")
  527. }
  528. ctx, cancel := context.WithCancel(context.Background())
  529. defer cancel()
  530. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  531. if err != nil {
  532. log.Fatalf("failed to get admin client: %v", err)
  533. }
  534. defer conn.Close()
  535. msg := nodev1.SignExistingVAARequest{
  536. Vaa: existingVAA,
  537. NewGuardianAddrs: newGsStrings,
  538. NewGuardianSetIndex: uint32(newGsIndex),
  539. }
  540. resp, err := c.SignExistingVAA(ctx, &msg)
  541. if err != nil {
  542. log.Fatalf("failed to run SignExistingVAA RPC: %s", err)
  543. }
  544. fmt.Println(hex.EncodeToString(resp.Vaa))
  545. }
  546. func runSignExistingVaasFromCSV(cmd *cobra.Command, args []string) {
  547. oldVAAFile, err := os.Open(args[0])
  548. if err != nil {
  549. log.Fatalf("failed to read old VAA db: %v", err)
  550. }
  551. defer oldVAAFile.Close()
  552. newVAAFile, err := os.OpenFile(args[1], os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644)
  553. if err != nil {
  554. log.Fatalf("failed to create new VAA db: %v", err)
  555. }
  556. defer newVAAFile.Close()
  557. newVAAWriter := csv.NewWriter(newVAAFile)
  558. newGsStrings := strings.Split(args[2], ",")
  559. newGsIndex, err := strconv.ParseUint(args[3], 10, 32)
  560. if err != nil {
  561. log.Fatalf("invalid new guardian set index")
  562. }
  563. ctx, cancel := context.WithCancel(context.Background())
  564. defer cancel()
  565. conn, c, err := getAdminClient(ctx, *clientSocketPath)
  566. if err != nil {
  567. log.Fatalf("failed to get admin client: %v", err)
  568. }
  569. defer conn.Close()
  570. // Scan the CSV once to make sure it won't fail while reading unless raced
  571. oldVAAReader := csv.NewReader(oldVAAFile)
  572. numOldVAAs := 0
  573. for {
  574. row, err := oldVAAReader.Read()
  575. if err != nil {
  576. if err == io.EOF {
  577. break
  578. }
  579. log.Fatalf("failed to parse VAA CSV: %v", err)
  580. }
  581. if len(row) != 2 {
  582. log.Fatalf("row [%d] does not have 2 elements", numOldVAAs)
  583. }
  584. numOldVAAs++
  585. }
  586. // Reset reader
  587. _, err = oldVAAFile.Seek(0, io.SeekStart)
  588. if err != nil {
  589. log.Fatalf("failed to seek back in CSV file: %v", err)
  590. }
  591. oldVAAReader = csv.NewReader(oldVAAFile)
  592. counter, i := 0, 0
  593. for {
  594. row, err := oldVAAReader.Read()
  595. if err != nil {
  596. if err == io.EOF {
  597. break
  598. }
  599. log.Fatalf("failed to parse VAA CSV: %v", err)
  600. }
  601. if len(row) != 2 {
  602. log.Fatalf("row [%d] does not have 2 elements", i)
  603. }
  604. i++
  605. if i%10 == 0 {
  606. log.Printf("Processing VAA %d/%d", i, numOldVAAs)
  607. }
  608. vaaBytes := ethcommon.Hex2Bytes(row[1])
  609. msg := nodev1.SignExistingVAARequest{
  610. Vaa: vaaBytes,
  611. NewGuardianAddrs: newGsStrings,
  612. NewGuardianSetIndex: uint32(newGsIndex),
  613. }
  614. resp, err := c.SignExistingVAA(ctx, &msg)
  615. if err != nil {
  616. log.Printf("signing VAA (%s)[%d] failed - skipping: %v", row[0], i, err)
  617. continue
  618. }
  619. err = newVAAWriter.Write([]string{row[0], hex.EncodeToString(resp.Vaa)})
  620. if err != nil {
  621. log.Fatalf("failed to write new VAA to out db: %v", err)
  622. }
  623. counter++
  624. }
  625. log.Printf("Successfully signed %d out of %d VAAs", counter, numOldVAAs)
  626. newVAAWriter.Flush()
  627. }
  628. // This exposes keccak256 as a command line utility, mostly for validating goverance messages
  629. // that use this hash. There isn't any common utility that computes this since this is nonstandard outside of evm.
  630. // It is used similar to other hashing utilities, e.g. `cat <file> | guardiand admin keccak256`.
  631. func runKeccak256Hash(cmd *cobra.Command, args []string) {
  632. reader := bufio.NewReader(os.Stdin)
  633. hash := sha3.NewLegacyKeccak256()
  634. // ~10 MB chunks
  635. buf := make([]byte, 10*1024*1024)
  636. for {
  637. count, err := reader.Read(buf)
  638. if err != nil && err != io.EOF {
  639. log.Fatalf("could not read: %v", err)
  640. }
  641. _, errHash := hash.Write(buf[:count])
  642. if errHash != nil {
  643. log.Fatalf("could not hash: %v", errHash)
  644. }
  645. if err == io.EOF {
  646. break
  647. }
  648. }
  649. digest := hash.Sum([]byte{})
  650. fmt.Printf("%s", hex.EncodeToString(digest))
  651. }