@@ -2,6 +2,7 @@ package validator
22
33import (
44 "context"
5+ "encoding/hex"
56 "encoding/json"
67 "fmt"
78 "os"
@@ -12,9 +13,32 @@ import (
1213 "sync"
1314 "time"
1415
16+ "github.com/btcsuite/btcutil/bech32"
1517 "github.com/pushchain/push-validator-cli/internal/config"
1618)
1719
20+ // Bech32ToHex converts a bech32 address (push1..., pushvaloper1...) to EVM hex format (0x...)
21+ // This is a pure Go implementation that doesn't require subprocess calls.
22+ func Bech32ToHex (addr string ) string {
23+ if addr == "" {
24+ return "—"
25+ }
26+
27+ // Decode bech32 address
28+ _ , data , err := bech32 .Decode (addr )
29+ if err != nil {
30+ return "—"
31+ }
32+
33+ // Convert 5-bit groups to 8-bit bytes
34+ converted , err := bech32 .ConvertBits (data , 5 , 8 , false )
35+ if err != nil {
36+ return "—"
37+ }
38+
39+ return "0x" + strings .ToUpper (hex .EncodeToString (converted ))
40+ }
41+
1842// commandContext creates an exec.CommandContext with DYLD_LIBRARY_PATH set for macOS
1943// to find libwasmvm.dylib in the same directory as the binary
2044func commandContext (ctx context.Context , name string , args ... string ) * exec.Cmd {
@@ -550,6 +574,7 @@ func parseStatus(status string) string {
550574}
551575
552576// GetValidatorRewards fetches commission and outstanding rewards for a validator
577+ // Both queries are executed in parallel for better performance.
553578func GetValidatorRewards (ctx context.Context , cfg config.Config , validatorAddr string ) (commission string , outstanding string , err error ) {
554579 if validatorAddr == "" {
555580 return "—" , "—" , fmt .Errorf ("validator address required" )
@@ -560,73 +585,64 @@ func GetValidatorRewards(ctx context.Context, cfg config.Config, validatorAddr s
560585 return "—" , "—" , fmt .Errorf ("pchaind not found: %w" , err )
561586 }
562587
563- // Create child context with 15s timeout per validator to avoid deadline issues
564- // when fetching rewards for multiple validators in parallel
565- // Increased from 5s to handle network latency and slower nodes
566- queryCtx , cancel := context .WithTimeout (context .Background (), 15 * time .Second )
567- defer cancel ()
588+ // Use the provided context or create one with timeout
589+ queryCtx := ctx
590+ if queryCtx == nil {
591+ var cancel context.CancelFunc
592+ queryCtx , cancel = context .WithTimeout (context .Background (), 15 * time .Second )
593+ defer cancel ()
594+ }
568595
569596 remote := fmt .Sprintf ("https://%s" , cfg .GenesisDomain )
570597
571- // Fetch commission rewards
598+ // Fetch commission and outstanding rewards in parallel
599+ var wg sync.WaitGroup
572600 commissionRewards := "—"
573- commCmd := commandContext (queryCtx , bin , "query" , "distribution" , "commission" , validatorAddr , "--node" , remote , "-o" , "json" )
574- if commOutput , err := commCmd .Output (); err == nil {
575- var commResult struct {
576- Commission struct {
577- Commission []string `json:"commission"`
578- } `json:"commission"`
579- }
580- if err := json .Unmarshal (commOutput , & commResult ); err == nil && len (commResult .Commission .Commission ) > 0 {
581- // Extract numeric part from amount string (format: "123.45upc")
582- amountStr := commResult .Commission .Commission [0 ]
583- // Remove denom suffix
584- amountStr = strings .TrimSuffix (amountStr , "upc" )
585- if amount , err := strconv .ParseFloat (amountStr , 64 ); err == nil {
586- commissionRewards = fmt .Sprintf ("%.2f" , amount / 1e18 )
587- }
588- }
589- }
590-
591- // Fetch outstanding rewards with retry logic
592601 outstandingRewards := "—"
593- var outOutput []byte
594- var outErr error
595602
596- // Retry up to 2 times with 2s delay on failure
597- maxRetries := 2
598- for attempt := 0 ; attempt <= maxRetries ; attempt ++ {
599- outCmd := commandContext (queryCtx , bin , "query" , "distribution" , "validator-outstanding-rewards" , validatorAddr , "--node" , remote , "-o" , "json" )
600- outOutput , outErr = outCmd .Output ()
603+ wg .Add (2 )
601604
602- if outErr == nil {
603- break // Success, exit retry loop
604- }
605-
606- // Wait before retry (except on last attempt)
607- if attempt < maxRetries {
608- time .Sleep (2 * time .Second )
605+ // Fetch commission rewards
606+ go func () {
607+ defer wg .Done ()
608+ commCmd := commandContext (queryCtx , bin , "query" , "distribution" , "commission" , validatorAddr , "--node" , remote , "-o" , "json" )
609+ if commOutput , err := commCmd .Output (); err == nil {
610+ var commResult struct {
611+ Commission struct {
612+ Commission []string `json:"commission"`
613+ } `json:"commission"`
614+ }
615+ if err := json .Unmarshal (commOutput , & commResult ); err == nil && len (commResult .Commission .Commission ) > 0 {
616+ amountStr := commResult .Commission .Commission [0 ]
617+ amountStr = strings .TrimSuffix (amountStr , "upc" )
618+ if amount , err := strconv .ParseFloat (amountStr , 64 ); err == nil {
619+ commissionRewards = fmt .Sprintf ("%.2f" , amount / 1e18 )
620+ }
621+ }
609622 }
610- }
623+ }()
611624
612- // Parse outstanding rewards if fetch succeeded
613- if outErr == nil {
614- var outResult struct {
615- Rewards struct {
616- Rewards []string `json:"rewards"`
617- } `json:"rewards"`
618- }
619- if err := json .Unmarshal (outOutput , & outResult ); err == nil && len (outResult .Rewards .Rewards ) > 0 {
620- // Extract numeric part from amount string (format: "123.45upc")
621- amountStr := outResult .Rewards .Rewards [0 ]
622- // Remove denom suffix
623- amountStr = strings .TrimSuffix (amountStr , "upc" )
624- if amount , err := strconv .ParseFloat (amountStr , 64 ); err == nil {
625- outstandingRewards = fmt .Sprintf ("%.2f" , amount / 1e18 )
625+ // Fetch outstanding rewards (no retry for speed)
626+ go func () {
627+ defer wg .Done ()
628+ outCmd := commandContext (queryCtx , bin , "query" , "distribution" , "validator-outstanding-rewards" , validatorAddr , "--node" , remote , "-o" , "json" )
629+ if outOutput , err := outCmd .Output (); err == nil {
630+ var outResult struct {
631+ Rewards struct {
632+ Rewards []string `json:"rewards"`
633+ } `json:"rewards"`
634+ }
635+ if err := json .Unmarshal (outOutput , & outResult ); err == nil && len (outResult .Rewards .Rewards ) > 0 {
636+ amountStr := outResult .Rewards .Rewards [0 ]
637+ amountStr = strings .TrimSuffix (amountStr , "upc" )
638+ if amount , err := strconv .ParseFloat (amountStr , 64 ); err == nil {
639+ outstandingRewards = fmt .Sprintf ("%.2f" , amount / 1e18 )
640+ }
626641 }
627642 }
628- }
643+ }()
629644
645+ wg .Wait ()
630646 return commissionRewards , outstandingRewards , nil
631647}
632648
0 commit comments