2020-11-19 03:53:19 -08:00
|
|
|
package guardiand
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-11-01 09:17:02 -07:00
|
|
|
"encoding/hex"
|
2020-11-19 03:53:19 -08:00
|
|
|
"fmt"
|
2022-02-14 05:30:13 -08:00
|
|
|
"github.com/certusone/wormhole/node/pkg/common"
|
2021-12-20 13:23:45 -08:00
|
|
|
gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
|
2021-08-26 01:35:09 -07:00
|
|
|
publicrpcv1 "github.com/certusone/wormhole/node/pkg/proto/publicrpc/v1"
|
2021-10-29 05:17:13 -07:00
|
|
|
"github.com/certusone/wormhole/node/pkg/vaa"
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
2021-08-07 12:12:08 -07:00
|
|
|
"github.com/spf13/pflag"
|
2020-11-19 03:53:19 -08:00
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
2021-10-04 04:42:56 -07:00
|
|
|
"strconv"
|
2021-10-29 05:17:13 -07:00
|
|
|
"strings"
|
2020-11-19 03:53:19 -08:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/status-im/keycard-go/hexutils"
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
"google.golang.org/protobuf/encoding/prototext"
|
|
|
|
|
2021-08-26 01:35:09 -07:00
|
|
|
nodev1 "github.com/certusone/wormhole/node/pkg/proto/node/v1"
|
2020-11-19 03:53:19 -08:00
|
|
|
)
|
|
|
|
|
2021-08-07 12:12:08 -07:00
|
|
|
var (
|
|
|
|
clientSocketPath *string
|
2021-12-18 14:39:30 -08:00
|
|
|
shouldBackfill *bool
|
2021-08-07 12:12:08 -07:00
|
|
|
)
|
2020-11-19 03:53:19 -08:00
|
|
|
|
|
|
|
func init() {
|
2021-08-07 12:12:08 -07:00
|
|
|
// Shared flags for all admin commands
|
|
|
|
pf := pflag.NewFlagSet("commonAdminFlags", pflag.ContinueOnError)
|
2020-11-19 03:53:19 -08:00
|
|
|
clientSocketPath = pf.String("socket", "", "gRPC admin server socket to connect to")
|
|
|
|
err := cobra.MarkFlagRequired(pf, "socket")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2021-08-07 12:12:08 -07:00
|
|
|
|
2021-12-18 14:39:30 -08:00
|
|
|
shouldBackfill = AdminClientFindMissingMessagesCmd.Flags().Bool(
|
|
|
|
"backfill", false, "backfill missing VAAs from public RPC")
|
|
|
|
|
2021-08-07 12:12:08 -07:00
|
|
|
AdminClientInjectGuardianSetUpdateCmd.Flags().AddFlagSet(pf)
|
2021-10-04 04:42:56 -07:00
|
|
|
AdminClientFindMissingMessagesCmd.Flags().AddFlagSet(pf)
|
2021-08-07 12:12:08 -07:00
|
|
|
AdminClientListNodes.Flags().AddFlagSet(pf)
|
2021-10-29 05:17:13 -07:00
|
|
|
DumpVAAByMessageID.Flags().AddFlagSet(pf)
|
2021-12-20 13:23:45 -08:00
|
|
|
SendObservationRequest.Flags().AddFlagSet(pf)
|
2020-11-19 03:53:19 -08:00
|
|
|
|
|
|
|
AdminCmd.AddCommand(AdminClientInjectGuardianSetUpdateCmd)
|
2021-10-04 04:42:56 -07:00
|
|
|
AdminCmd.AddCommand(AdminClientFindMissingMessagesCmd)
|
2021-01-19 04:01:45 -08:00
|
|
|
AdminCmd.AddCommand(AdminClientGovernanceVAAVerifyCmd)
|
2021-08-07 12:12:08 -07:00
|
|
|
AdminCmd.AddCommand(AdminClientListNodes)
|
2021-10-29 05:17:13 -07:00
|
|
|
AdminCmd.AddCommand(DumpVAAByMessageID)
|
2021-12-20 13:23:45 -08:00
|
|
|
AdminCmd.AddCommand(SendObservationRequest)
|
2020-11-19 03:53:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
var AdminCmd = &cobra.Command{
|
|
|
|
Use: "admin",
|
|
|
|
Short: "Guardian node admin commands",
|
|
|
|
}
|
|
|
|
|
|
|
|
var AdminClientInjectGuardianSetUpdateCmd = &cobra.Command{
|
2021-01-20 15:01:34 -08:00
|
|
|
Use: "governance-vaa-inject [FILENAME]",
|
2021-01-19 04:01:45 -08:00
|
|
|
Short: "Inject and sign a governance VAA from a prototxt file (see docs!)",
|
|
|
|
Run: runInjectGovernanceVAA,
|
2020-11-19 03:53:19 -08:00
|
|
|
Args: cobra.ExactArgs(1),
|
|
|
|
}
|
|
|
|
|
2021-10-04 04:42:56 -07:00
|
|
|
var AdminClientFindMissingMessagesCmd = &cobra.Command{
|
|
|
|
Use: "find-missing-messages [CHAIN_ID] [EMITTER_ADDRESS_HEX]",
|
|
|
|
Short: "Find sequence number gaps for the given chain ID and emitter address",
|
|
|
|
Run: runFindMissingMessages,
|
|
|
|
Args: cobra.ExactArgs(2),
|
|
|
|
}
|
|
|
|
|
2021-10-29 05:17:13 -07:00
|
|
|
var DumpVAAByMessageID = &cobra.Command{
|
|
|
|
Use: "dump-vaa-by-message-id [MESSAGE_ID]",
|
|
|
|
Short: "Retrieve a VAA by message ID (chain/emitter/seq) and decode and dump the VAA",
|
|
|
|
Run: runDumpVAAByMessageID,
|
|
|
|
Args: cobra.ExactArgs(1),
|
|
|
|
}
|
|
|
|
|
2021-12-20 13:23:45 -08:00
|
|
|
var SendObservationRequest = &cobra.Command{
|
|
|
|
Use: "send-observation-request [CHAIN_ID] [TX_HASH_HEX]",
|
|
|
|
Short: "Broadcast an observation request for the given chain ID and chain-specific tx_hash",
|
|
|
|
Run: runSendObservationRequest,
|
|
|
|
Args: cobra.ExactArgs(2),
|
|
|
|
}
|
|
|
|
|
2021-08-21 15:34:58 -07:00
|
|
|
func getAdminClient(ctx context.Context, addr string) (*grpc.ClientConn, error, nodev1.NodePrivilegedServiceClient) {
|
2020-11-19 03:53:19 -08:00
|
|
|
conn, err := grpc.DialContext(ctx, fmt.Sprintf("unix:///%s", addr), grpc.WithInsecure())
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to connect to %s: %v", addr, err)
|
|
|
|
}
|
|
|
|
|
2021-08-21 15:34:58 -07:00
|
|
|
c := nodev1.NewNodePrivilegedServiceClient(conn)
|
2020-11-19 03:53:19 -08:00
|
|
|
return conn, err, c
|
|
|
|
}
|
|
|
|
|
2021-08-21 15:34:58 -07:00
|
|
|
func getPublicRPCServiceClient(ctx context.Context, addr string) (*grpc.ClientConn, error, publicrpcv1.PublicRPCServiceClient) {
|
2021-07-22 04:48:53 -07:00
|
|
|
conn, err := grpc.DialContext(ctx, fmt.Sprintf("unix:///%s", addr), grpc.WithInsecure())
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to connect to %s: %v", addr, err)
|
|
|
|
}
|
|
|
|
|
2021-08-21 15:34:58 -07:00
|
|
|
c := publicrpcv1.NewPublicRPCServiceClient(conn)
|
2021-07-22 04:48:53 -07:00
|
|
|
return conn, err, c
|
|
|
|
}
|
|
|
|
|
2021-01-19 04:01:45 -08:00
|
|
|
func runInjectGovernanceVAA(cmd *cobra.Command, args []string) {
|
2020-11-19 03:53:19 -08:00
|
|
|
path := args[0]
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
conn, err, c := getAdminClient(ctx, *clientSocketPath)
|
|
|
|
defer conn.Close()
|
2021-07-22 04:48:53 -07:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to get admin client: %v", err)
|
|
|
|
}
|
2020-11-19 03:53:19 -08:00
|
|
|
|
|
|
|
b, err := ioutil.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to read file: %v", err)
|
|
|
|
}
|
|
|
|
|
2021-01-19 04:01:45 -08:00
|
|
|
var msg nodev1.InjectGovernanceVAARequest
|
2020-11-19 03:53:19 -08:00
|
|
|
err = prototext.Unmarshal(b, &msg)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to deserialize: %v", err)
|
|
|
|
}
|
|
|
|
|
2021-01-19 04:01:45 -08:00
|
|
|
resp, err := c.InjectGovernanceVAA(ctx, &msg)
|
2020-11-19 03:53:19 -08:00
|
|
|
if err != nil {
|
2021-01-19 04:01:45 -08:00
|
|
|
log.Fatalf("failed to submit governance VAA: %v", err)
|
2020-11-19 03:53:19 -08:00
|
|
|
}
|
|
|
|
|
2021-10-29 08:50:46 -07:00
|
|
|
for _, digest := range resp.Digests {
|
|
|
|
log.Printf("VAA successfully injected with digest %s", hexutils.BytesToHex(digest))
|
|
|
|
}
|
2020-11-19 03:53:19 -08:00
|
|
|
}
|
2021-10-04 04:42:56 -07:00
|
|
|
|
|
|
|
func runFindMissingMessages(cmd *cobra.Command, args []string) {
|
|
|
|
chainID, err := strconv.Atoi(args[0])
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("invalid chain ID: %v", err)
|
|
|
|
}
|
|
|
|
emitterAddress := args[1]
|
|
|
|
|
2021-12-18 14:39:30 -08:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
2021-10-04 04:42:56 -07:00
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
conn, err, c := getAdminClient(ctx, *clientSocketPath)
|
|
|
|
defer conn.Close()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to get admin client: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
msg := nodev1.FindMissingMessagesRequest{
|
|
|
|
EmitterChain: uint32(chainID),
|
|
|
|
EmitterAddress: emitterAddress,
|
2021-12-18 14:39:30 -08:00
|
|
|
RpcBackfill: *shouldBackfill,
|
2022-02-14 05:30:13 -08:00
|
|
|
BackfillNodes: common.PublicRPCEndpoints,
|
2021-10-04 04:42:56 -07:00
|
|
|
}
|
|
|
|
resp, err := c.FindMissingMessages(ctx, &msg)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to run find FindMissingMessages RPC: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, id := range resp.MissingMessages {
|
|
|
|
fmt.Println(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("processed %s sequences %d to %d (%d gaps)",
|
|
|
|
emitterAddress, resp.FirstSequence, resp.LastSequence, len(resp.MissingMessages))
|
|
|
|
}
|
2021-10-29 05:17:13 -07:00
|
|
|
|
|
|
|
// runDumpVAAByMessageID uses GetSignedVAA to request the given message,
|
|
|
|
// then decode and dump the VAA.
|
|
|
|
func runDumpVAAByMessageID(cmd *cobra.Command, args []string) {
|
|
|
|
// Parse the {chain,emitter,seq} string.
|
|
|
|
parts := strings.Split(args[0], "/")
|
|
|
|
if len(parts) != 3 {
|
|
|
|
log.Fatalf("invalid message ID: %s", args[0])
|
|
|
|
}
|
|
|
|
chainID, err := strconv.ParseUint(parts[0], 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("invalid chain ID: %v", err)
|
|
|
|
}
|
|
|
|
emitterAddress := parts[1]
|
|
|
|
seq, err := strconv.ParseUint(parts[2], 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("invalid sequence number: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
conn, err, c := getPublicRPCServiceClient(ctx, *clientSocketPath)
|
|
|
|
defer conn.Close()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to get public RPC service client: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
msg := publicrpcv1.GetSignedVAARequest{
|
|
|
|
MessageId: &publicrpcv1.MessageID{
|
|
|
|
EmitterChain: publicrpcv1.ChainID(chainID),
|
|
|
|
EmitterAddress: emitterAddress,
|
|
|
|
Sequence: seq,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
resp, err := c.GetSignedVAA(ctx, &msg)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to run GetSignedVAA RPC: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
v, err := vaa.Unmarshal(resp.VaaBytes)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to decode VAA: %v", err)
|
|
|
|
}
|
|
|
|
|
2021-11-01 09:17:02 -07:00
|
|
|
log.Printf("VAA with digest %s: %+v\n", v.HexDigest(), spew.Sdump(v))
|
|
|
|
fmt.Printf("Bytes:\n%s\n", hex.EncodeToString(resp.VaaBytes))
|
2021-10-29 05:17:13 -07:00
|
|
|
}
|
2021-12-20 13:23:45 -08:00
|
|
|
|
|
|
|
func runSendObservationRequest(cmd *cobra.Command, args []string) {
|
|
|
|
chainID, err := strconv.Atoi(args[0])
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("invalid chain ID: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
txHash, err := hex.DecodeString(args[1])
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("invalid transaction hash: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
conn, err, c := getAdminClient(ctx, *clientSocketPath)
|
|
|
|
defer conn.Close()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to get admin client: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = c.SendObservationRequest(ctx, &nodev1.SendObservationRequestRequest{
|
|
|
|
ObservationRequest: &gossipv1.ObservationRequest{
|
|
|
|
ChainId: uint32(chainID),
|
|
|
|
TxHash: txHash,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to send observation request: %v", err)
|
|
|
|
}
|
|
|
|
}
|