You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: adminnodes.go
    path of file: ./repos/wormhole/node/cmd/guardiand
    
    the code of the file until where you have to start completion: package guardiand

import (
	"context"
	"fmt"
	"log"
	"os"
	"sort"
	"strings"
	"text/tabwriter"
	"time"

	publicrpcv1 "github.com/certusone/wormhole/node/pkg/proto/publicrpc/v1"
	"github.com/spf13/cobra"
	"github.com/wormhole-foundation/wormhole/sdk/vaa"
)

// How to test in container:
//    kubectl exec guardian-0 -- /guardiand admin list-nodes --socket /tmp/admin.sock

var (
	showDetails bool
	only        []string
)

func init() {
	AdminClientListNodes.Flags().BoolVar(&showDetails, "showDetails", false, "Show error counter and contract addresses")
	AdminClientListNodes.Flags().StringSliceVar(&only, "only", nil, "Show only networks with the given name")
}

var AdminClientListNodes = &cobra.Command{
	Use:   "list-nodes",
	Short: "Fetches an aggregated list of guardian nodes",
	Run:   runListNodes,
}

func runListNodes(cmd *cobra.Command, args []string) {
	ctx := context.Background()
	conn, c, err := getPublicRPCServiceClient(ctx, *clientSocketPath)
	if err != nil {
		log.Fatalf("failed to get publicrpc client: %v", err)
	}
	defer conn.Close()

	lastHeartbeats, err := c.GetLastHeartbeats(ctx, &publicrpcv1.GetLastHeartbeatsReq
    
func runListNodes(cmd *cobra.Command, args []string) {
	ctx := context.Background()
	conn, c, err := getPublicRPCServiceClient(ctx, *clientSocketPath)
	if err != nil {
		log.Fatalf("failed to get publicrpc client: %v", err)
	}
	defer conn.Close()

	lastHeartbeats, err := c.GetLastHeartbeats(ctx, &publicrpcv1.GetLastHeartbeatsRequest{})
	if err != nil {
		log.Fatalf("failed to list nodes: %v", err)
	}

	gs, err := c.GetCurrentGuardianSet(ctx, &publicrpcv1.GetCurrentGuardianSetRequest{})
	if err != nil {
		log.Fatalf("failed to list current guardian get: %v", err)
	}

	log.Printf("current guardian set index: %d (%d guardians)",
		gs.GuardianSet.Index, len(gs.GuardianSet.Addresses))

	nodes := lastHeartbeats.Entries

	sort.Slice(nodes, func(i, j int) bool {
		if nodes[i].RawHeartbeat == nil || nodes[j].RawHeartbeat == nil {
			return false
		}
		return nodes[i].RawHeartbeat.NodeName < nodes[j].RawHeartbeat.NodeName
	})

	log.Printf("%d nodes in guardian state set", len(nodes))

	w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)

	headers := []string{
		"Node key",
		"Guardian key",
		"Node name",
		"Version",
		"Last seen",
	}

	if showDetails {
		headers = append(headers, "Uptime")
	}

	type network struct {
		string
		vaa.ChainID
	}

	networks := []network{
		{"Solana", vaa.ChainIDSolana},
		{"Ethereum", vaa.ChainIDEthereum},
		{"Terra", vaa.ChainIDTerra},
		{"BSC", vaa.ChainIDBSC},
		{"Polygon", vaa.ChainIDPolygon},
		{"Avalanche", vaa.ChainIDAvalanche},
		{"Algorand", vaa.ChainIDAlgorand},
		{"Aptos", vaa.ChainIDAptos},
		{"Sui", vaa.ChainIDSui},
		{"Oasis", vaa.ChainIDOasis},
		{"Aurora", vaa.ChainIDAurora},
		{"Fantom", vaa.ChainIDFantom},
		{"Karura", vaa.ChainIDKarura},
		{"Acala", vaa.ChainIDAcala},
		{"Klaytn", vaa.ChainIDKlaytn},
		{"Celo", vaa.ChainIDCelo},
		{"Near", vaa.ChainIDNear},
		{"Terra2", vaa.ChainIDTerra2},
		{"Pythnet", vaa.ChainIDPythNet},
		{"Moonbeam", vaa.ChainIDMoonbeam},
		{"Arbitrum", vaa.ChainIDArbitrum},
		{"Optimism", vaa.ChainIDOptimism},
		{"Xpla", vaa.ChainIDXpla},
		{"Btc", vaa.ChainIDBtc},
		{"Injective", vaa.ChainIDInjective},
		{"Base", vaa.ChainIDBase},
		{"Sei", vaa.ChainIDSei},
		{"Scroll", vaa.ChainIDScroll},
		{"Mantle", vaa.ChainIDMantle},
		{"Wormchain", vaa.ChainIDWormchain},
		{"Sepolia", vaa.ChainIDSepolia},
		{"Holesky", vaa.ChainIDHolesky},
		{"ArbitrumSepolia", vaa.ChainIDArbitrumSepolia},
		{"BaseSepolia", vaa.ChainIDBaseSepolia},
		{"OptimismSepolia", vaa.ChainIDOptimismSepolia},
	}

	if len(only) > 0 {
		var filtered []network
		for _, network := range networks {
			for _, name := range only {
				if strings.EqualFold(network.string, name) {
					filtered = append(filtered, network)
				}
			}
		}
		networks = filtered
	}

	for _, k := range networks {
		headers = append(headers, k.string)
	}

	for _, header := range headers {
		_, _ = fmt.Fprintf(w, "%s\t", header)
	}
	_, _ = fmt.Fprintln(w)

	for _, h := range nodes {
		if h.RawHeartbeat == nil {
			continue
		}

		last := time.Unix(0, h.RawHeartbeat.Timestamp)
		boot := time.Unix(0, h.RawHeartbeat.BootTimestamp)

		heights := map[vaa.ChainID]int64{}
		truncAddrs := make(map[vaa.ChainID]string)
		errors := map[vaa.ChainID]uint64{}
		for _, n := range h.RawHeartbeat.Networks {
			heights[vaa.ChainID(n.Id)] = n.Height
			errors[vaa.ChainID(n.Id)] = n.ErrorCount
			if len(n.ContractAddress) >= 16 {
				truncAddrs[vaa.ChainID(n.Id)] = n.ContractAddress[:16]
			} else {
				truncAddrs[vaa.ChainID(n.Id)] = "INVALID"
			}
		}

		fields := []string{
			h.P2PNodeAddr,
			h.RawHeartbeat.GuardianAddr,
			h.RawHeartbeat.NodeName,
			h.RawHeartbeat.Version,
			time.Since(last).String(),
		}

		if showDetails {
			fields = append(fields, time.Since(boot).String())
		}

		for _, n := range networks {
			if showDetails {
				fields = append(fields, fmt.Sprintf("%s %d (%d)",
					truncAddrs[n.ChainID], heights[n.ChainID], errors[n.ChainID]))
			} else {
				fields = append(fields, fmt.Sprintf("%d", heights[n.ChainID]))
			}
		}

		for _, field := range fields {
			_, _ = fmt.Fprintf(w, "%s\t", field)
		}

		_, _ = fmt.Fprintln(w)
	}

	w.Flush()
	fmt.Print("\n")

	for _, addr := range gs.GuardianSet.Addresses {
		var found bool
		for _, h := range nodes {
			if h.VerifiedGuardianAddr == addr {
				found = true
			}
		}

		if !found {
			fmt.Printf("Missing guardian: %s\n", addr)
		}
	}

	fmt.Println("\n[do not parse - use the gRPC or REST API for scripting]")
}