Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@
* [4548](https://github.com/zeta-chain/node/pull/4548) - check grantee for IsSystemTx
* [4550](https://github.com/zeta-chain/node/pull/4550) - add nil guards for Solana tx metadata and use safe signature parsing in inbound tracker
* [4557](https://github.com/zeta-chain/node/pull/4557) - reject Solana transactions with nil metadata instead of treating as successful
* [4560](https://github.com/zeta-chain/node/pull/4560) - update get tss address query

### Tests

* [4539](https://github.com/zeta-chain/node/pull/4539) - add support for `signet` name in the e2e config

### Features

* [4559](https://github.com/zeta-chain/node/pull/4559) - add `tss-number` flag to `tss-balances` command and add a new commands `list-observers`,`track-tss-migration` to zetatools

## Release ReForge
- zetacored: v37.0.0
- zetaclientd: v38.0.0
Expand Down
3 changes: 1 addition & 2 deletions cmd/zetatool/cli/db_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,7 @@ func displayStats(stats *databaseStats, format OutputFormat) error {

// displayTable renders the statistics in a formatted table
func displayTable(stats *databaseStats) {
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t := newTableWriter()
t.AppendHeader(
table.Row{"Module", "Avg Key Size", "Avg Value Size", "Total Key Size", "Total Value Size", "Total Key Pairs"},
)
Expand Down
4 changes: 1 addition & 3 deletions cmd/zetatool/cli/list_chains.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cli
import (
"context"
"fmt"
"os"

"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -84,8 +83,7 @@ func listChains(cmd *cobra.Command, args []string) error {
func printChainList(chainList []chains.Chain) {
fmt.Println()

t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t := newTableWriter()
t.AppendHeader(table.Row{"Chain ID", "Name", "Network", "VM", "External"})

for _, c := range chainList {
Expand Down
161 changes: 161 additions & 0 deletions cmd/zetatool/cli/list_observers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package cli

import (
"context"
"fmt"
"sync"

sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"

zetatoolcommon "github.com/zeta-chain/node/cmd/zetatool/common"
"github.com/zeta-chain/node/cmd/zetatool/config"
"github.com/zeta-chain/node/pkg/rpc"
observertypes "github.com/zeta-chain/node/x/observer/types"
)

// observerInfo holds observer address and resolved validator moniker
type observerInfo struct {
ObserverAddress string
OperatorAddress string
Moniker string
Error string
}

// NewListObserversCMD creates a command to list all observers with their validator monikers
func NewListObserversCMD() *cobra.Command {
return &cobra.Command{
Use: "list-observers <chain>",
Short: "List observers with their validator monikers",
Long: `List all active observers and resolve their validator monikers from the staking module.

The chain argument can be:
- A chain ID (e.g., 7000, 7001)
- A chain name (e.g., zeta_mainnet, zeta_testnet)

The network type (mainnet/testnet/etc) is inferred from the chain.

Examples:
zetatool list-observers 7000
zetatool list-observers zeta_mainnet
zetatool list-observers zeta_testnet --config custom_config.json`,
Args: cobra.ExactArgs(1),
RunE: listObservers,
}
}

func listObservers(cmd *cobra.Command, args []string) error {
chain, err := zetatoolcommon.ResolveChain(args[0])
if err != nil {
return fmt.Errorf("failed to resolve chain %q: %w", args[0], err)
}

network := zetatoolcommon.NetworkTypeFromChain(chain)

configFile, err := cmd.Flags().GetString(config.FlagConfig)
if err != nil {
return fmt.Errorf("failed to read value for flag %s: %w", config.FlagConfig, err)
}

cfg, err := config.GetConfigByNetwork(network, configFile)
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}

if cfg.ZetaChainRPC == "" {
return fmt.Errorf("ZetaChainRPC is not configured for network %s", network)
}

zetacoreClient, err := rpc.NewCometBFTClients(cfg.ZetaChainRPC)
if err != nil {
return fmt.Errorf("failed to create zetacore client: %w", err)
}

ctx := context.Background()

// Fetch the observer set
observerSetRes, err := zetacoreClient.Observer.ObserverSet(ctx, &observertypes.QueryObserverSet{})
if err != nil {
return fmt.Errorf("failed to fetch observer set: %w", err)
}

if len(observerSetRes.Observers) == 0 {
fmt.Println("No observers found")
return nil
}

// Resolve monikers concurrently
var wg sync.WaitGroup
results := make(chan observerInfo, len(observerSetRes.Observers))

for _, obs := range observerSetRes.Observers {
wg.Add(1)
go func(observerAddr string) {
defer wg.Done()
results <- resolveObserverMoniker(ctx, zetacoreClient.Staking, observerAddr)
}(obs)
}

go func() {
wg.Wait()
close(results)
}()

infos := make([]observerInfo, 0, len(observerSetRes.Observers))
for info := range results {
infos = append(infos, info)
}

printObserverTable(infos)
return nil
}

// resolveObserverMoniker converts an observer address to a validator operator address
// and queries the staking module for the validator's moniker.
func resolveObserverMoniker(
ctx context.Context,
stakingClient stakingtypes.QueryClient,
observerAddr string,
) observerInfo {
info := observerInfo{ObserverAddress: observerAddr}

// Convert observer address (zeta1xxx) to validator operator address (zetavaloper1xxx)
accAddr, err := sdk.AccAddressFromBech32(observerAddr)
if err != nil {
info.Error = fmt.Sprintf("invalid address: %v", err)
return info
}
valAddr := sdk.ValAddress(accAddr.Bytes())
info.OperatorAddress = valAddr.String()

// Query the staking module for this validator
valRes, err := stakingClient.Validator(ctx, &stakingtypes.QueryValidatorRequest{
ValidatorAddr: info.OperatorAddress,
})
if err != nil {
info.Error = fmt.Sprintf("validator query failed: %v", err)
return info
}

info.Moniker = valRes.Validator.Description.Moniker
return info
}

func printObserverTable(infos []observerInfo) {
t := newTableWriter()
t.AppendHeader(table.Row{"#", "Observer Address", "Moniker"})

for i, info := range infos {
moniker := info.Moniker
if info.Error != "" {
moniker = info.Error
}

t.AppendRow(table.Row{i + 1, info.ObserverAddress, moniker})
}

fmt.Println()
t.Render()
}
21 changes: 21 additions & 0 deletions cmd/zetatool/cli/table.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cli

import (
"os"

"github.com/jedib0t/go-pretty/v6/table"
"golang.org/x/term"
)

// newTableWriter creates a table.Writer configured to fit the terminal width.
// If the terminal width cannot be detected, no width limit is applied.
func newTableWriter() table.Writer {
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)

if width, _, err := term.GetSize(int(os.Stdout.Fd())); err == nil && width > 0 {
t.Style().Size.WidthMax = width
}

return t
}
Loading
Loading