Skip to content

Commit c563a89

Browse files
committed
fixes
1 parent 7e842d5 commit c563a89

3 files changed

Lines changed: 110 additions & 34 deletions

File tree

cmd/push-validator/deps.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"io"
88
"os"
99
"os/exec"
10+
"path/filepath"
1011
"strings"
1112
"time"
1213

@@ -57,7 +58,41 @@ type Deps struct {
5758
type execRunner struct{}
5859

5960
func (r *execRunner) Run(ctx context.Context, name string, args ...string) ([]byte, error) {
60-
return exec.CommandContext(ctx, name, args...).Output()
61+
cmd := exec.CommandContext(ctx, name, args...)
62+
63+
// Set DYLD_LIBRARY_PATH for macOS to find libwasmvm.dylib
64+
// Check multiple potential locations for the dylib
65+
dylibPaths := []string{}
66+
67+
// 1. Same directory as binary
68+
binDir := filepath.Dir(name)
69+
if binDir != "" && binDir != "." {
70+
dylibPaths = append(dylibPaths, binDir)
71+
}
72+
73+
// 2. Common cosmovisor locations
74+
homeDir, err := os.UserHomeDir()
75+
if err == nil {
76+
cosmovisorDirs := []string{
77+
filepath.Join(homeDir, ".pchain/cosmovisor/genesis/bin"),
78+
filepath.Join(homeDir, ".pchain/cosmovisor/current/bin"),
79+
}
80+
dylibPaths = append(dylibPaths, cosmovisorDirs...)
81+
}
82+
83+
// Build DYLD_LIBRARY_PATH
84+
if len(dylibPaths) > 0 {
85+
env := os.Environ()
86+
existingPath := os.Getenv("DYLD_LIBRARY_PATH")
87+
newPath := strings.Join(dylibPaths, ":")
88+
if existingPath != "" {
89+
newPath = newPath + ":" + existingPath
90+
}
91+
env = append(env, "DYLD_LIBRARY_PATH="+newPath)
92+
cmd.Env = env
93+
}
94+
95+
return cmd.Output()
6196
}
6297

6398
// prodFetcher is the production implementation of ValidatorFetcher.

internal/validator/fetcher.go

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,50 @@ import (
1515
"github.com/pushchain/push-validator-cli/internal/config"
1616
)
1717

18+
// commandContext creates an exec.CommandContext with DYLD_LIBRARY_PATH set for macOS
19+
// to find libwasmvm.dylib in the same directory as the binary
20+
func commandContext(ctx context.Context, name string, args ...string) *exec.Cmd {
21+
cmd := exec.CommandContext(ctx, name, args...)
22+
23+
// Set DYLD_LIBRARY_PATH for macOS to find libwasmvm.dylib
24+
// Check multiple potential locations for the dylib
25+
dylibPaths := []string{}
26+
27+
// 1. Same directory as binary
28+
binDir := filepath.Dir(name)
29+
if binDir != "" && binDir != "." {
30+
dylibPaths = append(dylibPaths, binDir)
31+
}
32+
33+
// 2. Common cosmovisor locations
34+
homeDir, err := os.UserHomeDir()
35+
if err == nil {
36+
cosmovisorDirs := []string{
37+
filepath.Join(homeDir, ".pchain/cosmovisor/genesis/bin"),
38+
filepath.Join(homeDir, ".pchain/cosmovisor/current/bin"),
39+
}
40+
dylibPaths = append(dylibPaths, cosmovisorDirs...)
41+
}
42+
43+
// Build DYLD_LIBRARY_PATH
44+
if len(dylibPaths) > 0 {
45+
env := os.Environ()
46+
existingPath := os.Getenv("DYLD_LIBRARY_PATH")
47+
newPath := strings.Join(dylibPaths, ":")
48+
if existingPath != "" {
49+
newPath = newPath + ":" + existingPath
50+
}
51+
env = append(env, "DYLD_LIBRARY_PATH="+newPath)
52+
cmd.Env = env
53+
}
54+
55+
return cmd
56+
}
57+
1858
// resolvePchaindBin finds pchaind binary in PATH or cosmovisor directory.
59+
// Prefers cosmovisor binaries to ensure libwasmvm.dylib compatibility.
1960
func resolvePchaindBin(homeDir string) (string, error) {
20-
if bin, err := exec.LookPath("pchaind"); err == nil {
21-
return bin, nil
22-
}
23-
// Check cosmovisor genesis directory
61+
// Check cosmovisor genesis directory first (has matching libwasmvm.dylib)
2462
cosmovisorPath := filepath.Join(homeDir, "cosmovisor", "genesis", "bin", "pchaind")
2563
if _, err := os.Stat(cosmovisorPath); err == nil {
2664
return cosmovisorPath, nil
@@ -30,6 +68,10 @@ func resolvePchaindBin(homeDir string) (string, error) {
3068
if _, err := os.Stat(currentPath); err == nil {
3169
return currentPath, nil
3270
}
71+
// Fallback to PATH (may have dylib compatibility issues)
72+
if bin, err := exec.LookPath("pchaind"); err == nil {
73+
return bin, nil
74+
}
3375
return "", fmt.Errorf("pchaind not found in PATH or %s", filepath.Join(homeDir, "cosmovisor"))
3476
}
3577

@@ -156,7 +198,7 @@ func (f *Fetcher) fetchAllValidators(ctx context.Context, cfg config.Config) (Va
156198
}
157199

158200
remote := fmt.Sprintf("https://%s", cfg.GenesisDomain)
159-
cmd := exec.CommandContext(ctx, bin, "query", "staking", "validators", "--node", remote, "-o", "json")
201+
cmd := commandContext(ctx, bin, "query", "staking", "validators", "--node", remote, "-o", "json")
160202
output, err := cmd.Output()
161203
if err != nil {
162204
return ValidatorList{}, fmt.Errorf("query validators failed: %w", err)
@@ -234,7 +276,7 @@ func (f *Fetcher) fetchMyValidator(ctx context.Context, cfg config.Config) (MyVa
234276
}
235277

236278
// Get local consensus pubkey using 'tendermint show-validator'
237-
showValCmd := exec.CommandContext(ctx, bin, "tendermint", "show-validator", "--home", cfg.HomeDir)
279+
showValCmd := commandContext(ctx, bin, "tendermint", "show-validator", "--home", cfg.HomeDir)
238280
pubkeyBytes, err := showValCmd.Output()
239281
if err != nil {
240282
// No validator key file exists
@@ -258,7 +300,7 @@ func (f *Fetcher) fetchMyValidator(ctx context.Context, cfg config.Config) (MyVa
258300

259301
// Get local node moniker from status (for conflict detection)
260302
var localMoniker string
261-
statusCmd := exec.CommandContext(ctx, bin, "status", "--node", cfg.RPCLocal)
303+
statusCmd := commandContext(ctx, bin, "status", "--node", cfg.RPCLocal)
262304
if statusOutput, err := statusCmd.Output(); err == nil {
263305
var statusData struct {
264306
NodeInfo struct {
@@ -272,7 +314,7 @@ func (f *Fetcher) fetchMyValidator(ctx context.Context, cfg config.Config) (MyVa
272314

273315
// Fetch all validators to match by consensus pubkey
274316
remote := fmt.Sprintf("https://%s", cfg.GenesisDomain)
275-
queryCmd := exec.CommandContext(ctx, bin, "query", "staking", "validators", "--node", remote, "-o", "json")
317+
queryCmd := commandContext(ctx, bin, "query", "staking", "validators", "--node", remote, "-o", "json")
276318
valsOutput, err := queryCmd.Output()
277319
if err != nil {
278320
return MyValidatorInfo{IsValidator: false}, err
@@ -528,7 +570,7 @@ func GetValidatorRewards(ctx context.Context, cfg config.Config, validatorAddr s
528570

529571
// Fetch commission rewards
530572
commissionRewards := "—"
531-
commCmd := exec.CommandContext(queryCtx, bin, "query", "distribution", "commission", validatorAddr, "--node", remote, "-o", "json")
573+
commCmd := commandContext(queryCtx, bin, "query", "distribution", "commission", validatorAddr, "--node", remote, "-o", "json")
532574
if commOutput, err := commCmd.Output(); err == nil {
533575
var commResult struct {
534576
Commission struct {
@@ -554,7 +596,7 @@ func GetValidatorRewards(ctx context.Context, cfg config.Config, validatorAddr s
554596
// Retry up to 2 times with 2s delay on failure
555597
maxRetries := 2
556598
for attempt := 0; attempt <= maxRetries; attempt++ {
557-
outCmd := exec.CommandContext(queryCtx, bin, "query", "distribution", "validator-outstanding-rewards", validatorAddr, "--node", remote, "-o", "json")
599+
outCmd := commandContext(queryCtx, bin, "query", "distribution", "validator-outstanding-rewards", validatorAddr, "--node", remote, "-o", "json")
558600
outOutput, outErr = outCmd.Output()
559601

560602
if outErr == nil {
@@ -644,7 +686,7 @@ func GetEVMAddress(ctx context.Context, validatorAddr string) string {
644686
return "—"
645687
}
646688

647-
cmd := exec.CommandContext(ctx, bin, "debug", "addr", validatorAddr)
689+
cmd := commandContext(ctx, bin, "debug", "addr", validatorAddr)
648690
output, err := cmd.Output()
649691
if err != nil {
650692
return "—"
@@ -676,7 +718,7 @@ func GetSlashingInfo(ctx context.Context, cfg config.Config, consensusPubkey str
676718

677719
// Query signing info to get jail details
678720
// consensusPubkey should be a JSON string like: {"@type":"/cosmos.crypto.ed25519.PubKey","key":"..."}
679-
cmd := exec.CommandContext(ctx, bin, "query", "slashing", "signing-info", consensusPubkey, "--node", remote, "-o", "json")
721+
cmd := commandContext(ctx, bin, "query", "slashing", "signing-info", consensusPubkey, "--node", remote, "-o", "json")
680722
output, err := cmd.CombinedOutput()
681723
if err != nil {
682724
return SlashingInfo{}, fmt.Errorf("failed to query slashing info: %w", err)
@@ -730,7 +772,7 @@ func getKeyringAddresses(bin string, cfg config.Config) []string {
730772
var addresses []string
731773

732774
// List all keys in the keyring
733-
cmd := exec.Command(bin, "keys", "list", "--keyring-backend", cfg.KeyringBackend, "--home", cfg.HomeDir, "--output", "json")
775+
cmd := commandContext(context.Background(), bin, "keys", "list", "--keyring-backend", cfg.KeyringBackend, "--home", cfg.HomeDir, "--output", "json")
734776
output, err := cmd.Output()
735777
if err != nil {
736778
return addresses

internal/validator/service_impl.go

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"errors"
77
"fmt"
88
"os"
9-
"os/exec"
109
"strings"
1110
"time"
1211
)
@@ -33,15 +32,15 @@ func (s *svc) EnsureKey(ctx context.Context, name string) (KeyInfo, error) {
3332
}
3433

3534
// Check if key already exists
36-
show := exec.CommandContext(ctx, s.opts.BinPath, "keys", "show", name, "-a", "--keyring-backend", s.opts.Keyring, "--home", s.opts.HomeDir)
35+
show := commandContext(ctx, s.opts.BinPath, "keys", "show", name, "-a", "--keyring-backend", s.opts.Keyring, "--home", s.opts.HomeDir)
3736
out, err := show.Output()
3837
if err == nil {
3938
// Key exists - fetch details
4039
return s.getKeyInfo(ctx, name, strings.TrimSpace(string(out)), "")
4140
}
4241

4342
// Key doesn't exist - create it and capture output
44-
add := exec.CommandContext(ctx, s.opts.BinPath, "keys", "add", name, "--keyring-backend", s.opts.Keyring, "--algo", "eth_secp256k1", "--home", s.opts.HomeDir)
43+
add := commandContext(ctx, s.opts.BinPath, "keys", "add", name, "--keyring-backend", s.opts.Keyring, "--algo", "eth_secp256k1", "--home", s.opts.HomeDir)
4544

4645
// Capture output to parse mnemonic
4746
output, err := add.CombinedOutput()
@@ -53,7 +52,7 @@ func (s *svc) EnsureKey(ctx context.Context, name string) (KeyInfo, error) {
5352
mnemonic := extractMnemonic(string(output))
5453

5554
// Get the address
56-
out2, err := exec.CommandContext(ctx, s.opts.BinPath, "keys", "show", name, "-a", "--keyring-backend", s.opts.Keyring, "--home", s.opts.HomeDir).Output()
55+
out2, err := commandContext(ctx, s.opts.BinPath, "keys", "show", name, "-a", "--keyring-backend", s.opts.Keyring, "--home", s.opts.HomeDir).Output()
5756
if err != nil {
5857
return KeyInfo{}, fmt.Errorf("keys show: %w", err)
5958
}
@@ -65,7 +64,7 @@ func (s *svc) EnsureKey(ctx context.Context, name string) (KeyInfo, error) {
6564
// getKeyInfo fetches full key details
6665
func (s *svc) getKeyInfo(ctx context.Context, name, addr, mnemonic string) (KeyInfo, error) {
6766
// Get key details in JSON format
68-
cmd := exec.CommandContext(ctx, s.opts.BinPath, "keys", "show", name, "--keyring-backend", s.opts.Keyring, "--home", s.opts.HomeDir, "--output", "json")
67+
cmd := commandContext(ctx, s.opts.BinPath, "keys", "show", name, "--keyring-backend", s.opts.Keyring, "--home", s.opts.HomeDir, "--output", "json")
6968
out, err := cmd.Output()
7069
if err != nil {
7170
return KeyInfo{Address: addr, Name: name, Mnemonic: mnemonic}, nil
@@ -176,13 +175,13 @@ func (s *svc) ImportKey(ctx context.Context, name string, mnemonic string) (KeyI
176175
}
177176

178177
// Check if key already exists
179-
show := exec.CommandContext(ctx, s.opts.BinPath, "keys", "show", name, "-a", "--keyring-backend", s.opts.Keyring, "--home", s.opts.HomeDir)
178+
show := commandContext(ctx, s.opts.BinPath, "keys", "show", name, "-a", "--keyring-backend", s.opts.Keyring, "--home", s.opts.HomeDir)
180179
if out, err := show.Output(); err == nil {
181180
return KeyInfo{}, fmt.Errorf("key '%s' already exists with address %s", name, strings.TrimSpace(string(out)))
182181
}
183182

184183
// Import key using --recover flag with mnemonic piped via stdin
185-
add := exec.CommandContext(ctx, s.opts.BinPath, "keys", "add", name,
184+
add := commandContext(ctx, s.opts.BinPath, "keys", "add", name,
186185
"--recover",
187186
"--keyring-backend", s.opts.Keyring,
188187
"--algo", "eth_secp256k1",
@@ -206,7 +205,7 @@ func (s *svc) ImportKey(ctx context.Context, name string, mnemonic string) (KeyI
206205
}
207206

208207
// Get the address of the imported key
209-
out2, err := exec.CommandContext(ctx, s.opts.BinPath, "keys", "show", name, "-a", "--keyring-backend", s.opts.Keyring, "--home", s.opts.HomeDir).Output()
208+
out2, err := commandContext(ctx, s.opts.BinPath, "keys", "show", name, "-a", "--keyring-backend", s.opts.Keyring, "--home", s.opts.HomeDir).Output()
210209
if err != nil {
211210
return KeyInfo{}, fmt.Errorf("failed to get imported key address: %w", err)
212211
}
@@ -226,7 +225,7 @@ func (s *svc) findExistingKeyByMnemonic(ctx context.Context, name, mnemonic stri
226225
}
227226
defer os.RemoveAll(tmpDir)
228227

229-
dryRun := exec.CommandContext(ctx, s.opts.BinPath, "keys", "add", "temp",
228+
dryRun := commandContext(ctx, s.opts.BinPath, "keys", "add", "temp",
230229
"--recover", "--dry-run",
231230
"--keyring-backend", "test",
232231
"--algo", "eth_secp256k1",
@@ -247,7 +246,7 @@ func (s *svc) findExistingKeyByMnemonic(ctx context.Context, name, mnemonic stri
247246
}
248247

249248
// List all keys and find the one with matching address
250-
listCmd := exec.CommandContext(ctx, s.opts.BinPath, "keys", "list",
249+
listCmd := commandContext(ctx, s.opts.BinPath, "keys", "list",
251250
"--keyring-backend", s.opts.Keyring,
252251
"--home", s.opts.HomeDir,
253252
"--output", "json")
@@ -280,7 +279,7 @@ func (s *svc) GetEVMAddress(ctx context.Context, addr string) (string, error) {
280279
if s.opts.BinPath == "" {
281280
s.opts.BinPath = "pchaind"
282281
}
283-
cmd := exec.CommandContext(ctx, s.opts.BinPath, "debug", "addr", addr)
282+
cmd := commandContext(ctx, s.opts.BinPath, "debug", "addr", addr)
284283
out, err := cmd.Output()
285284
if err != nil {
286285
return "", fmt.Errorf("debug addr: %w", err)
@@ -304,7 +303,7 @@ func (s *svc) IsValidator(ctx context.Context, addr string) (bool, error) {
304303
s.opts.BinPath = "pchaind"
305304
}
306305
// Compare local consensus pubkey with remote validators
307-
showVal := exec.CommandContext(ctx, s.opts.BinPath, "tendermint", "show-validator", "--home", s.opts.HomeDir)
306+
showVal := commandContext(ctx, s.opts.BinPath, "tendermint", "show-validator", "--home", s.opts.HomeDir)
308307
b, err := showVal.Output()
309308
if err != nil {
310309
return false, fmt.Errorf("show-validator: %w", err)
@@ -320,7 +319,7 @@ func (s *svc) IsValidator(ctx context.Context, addr string) (bool, error) {
320319
}
321320
// Query validators from remote
322321
remote := fmt.Sprintf("https://%s", s.opts.GenesisDomain)
323-
q := exec.CommandContext(ctx, s.opts.BinPath, "query", "staking", "validators", "--node", remote, "-o", "json")
322+
q := commandContext(ctx, s.opts.BinPath, "query", "staking", "validators", "--node", remote, "-o", "json")
324323
vb, err := q.Output()
325324
if err != nil {
326325
return false, fmt.Errorf("query validators: %w", err)
@@ -354,7 +353,7 @@ func (s *svc) IsAddressValidator(ctx context.Context, cosmosAddr string) (bool,
354353

355354
// Query validators from remote
356355
remote := fmt.Sprintf("https://%s", s.opts.GenesisDomain)
357-
q := exec.CommandContext(ctx, s.opts.BinPath, "query", "staking", "validators", "--node", remote, "-o", "json")
356+
q := commandContext(ctx, s.opts.BinPath, "query", "staking", "validators", "--node", remote, "-o", "json")
358357
vb, err := q.Output()
359358
if err != nil {
360359
return false, fmt.Errorf("query validators: %w", err)
@@ -401,7 +400,7 @@ func (s *svc) Balance(ctx context.Context, addr string) (string, error) {
401400
}
402401
// Always query remote genesis node for canonical state during validator registration
403402
remote := fmt.Sprintf("https://%s", s.opts.GenesisDomain)
404-
q := exec.CommandContext(ctx, s.opts.BinPath, "query", "bank", "balances", addr, "--node", remote, "-o", "json")
403+
q := commandContext(ctx, s.opts.BinPath, "query", "bank", "balances", addr, "--node", remote, "-o", "json")
405404
out, err := q.Output()
406405
if err != nil {
407406
return "0", fmt.Errorf("query balance: %w", err)
@@ -427,7 +426,7 @@ func (s *svc) Register(ctx context.Context, args RegisterArgs) (string, error) {
427426
// Prepare validator JSON - use a separate timeout for this command
428427
showCtx, showCancel := context.WithTimeout(context.Background(), 30*time.Second)
429428
defer showCancel()
430-
pubJSON, err := exec.CommandContext(showCtx, s.opts.BinPath, "tendermint", "show-validator", "--home", s.opts.HomeDir).Output()
429+
pubJSON, err := commandContext(showCtx, s.opts.BinPath, "tendermint", "show-validator", "--home", s.opts.HomeDir).Output()
431430
if err != nil {
432431
return "", fmt.Errorf("show-validator: %w", err)
433432
}
@@ -460,7 +459,7 @@ func (s *svc) Register(ctx context.Context, args RegisterArgs) (string, error) {
460459
remote := fmt.Sprintf("https://%s", s.opts.GenesisDomain)
461460
ctxTimeout, cancel := context.WithTimeout(ctx, 60*time.Second)
462461
defer cancel()
463-
cmd := exec.CommandContext(ctxTimeout, s.opts.BinPath, "tx", "staking", "create-validator", tmp.Name(),
462+
cmd := commandContext(ctxTimeout, s.opts.BinPath, "tx", "staking", "create-validator", tmp.Name(),
464463
"--from", args.KeyName,
465464
"--chain-id", s.opts.ChainID,
466465
"--keyring-backend", s.opts.Keyring,
@@ -541,7 +540,7 @@ func (s *svc) Unjail(ctx context.Context, keyName string) (string, error) {
541540
ctxTimeout, cancel := context.WithTimeout(ctx, 60*time.Second)
542541
defer cancel()
543542

544-
cmd := exec.CommandContext(ctxTimeout, s.opts.BinPath, "tx", "slashing", "unjail",
543+
cmd := commandContext(ctxTimeout, s.opts.BinPath, "tx", "slashing", "unjail",
545544
"--from", keyName,
546545
"--chain-id", s.opts.ChainID,
547546
"--keyring-backend", s.opts.Keyring,
@@ -608,7 +607,7 @@ func (s *svc) WithdrawRewards(ctx context.Context, validatorAddr string, keyName
608607
ctxTimeout, cancel := context.WithTimeout(ctx, 60*time.Second)
609608
defer cancel()
610609

611-
cmd := exec.CommandContext(ctxTimeout, s.opts.BinPath, args...)
610+
cmd := commandContext(ctxTimeout, s.opts.BinPath, args...)
612611
out, err := cmd.CombinedOutput()
613612
if err != nil {
614613
// Extract and enhance error message
@@ -672,7 +671,7 @@ func (s *svc) Delegate(ctx context.Context, args DelegateArgs) (string, error) {
672671
ctxTimeout, cancel := context.WithTimeout(ctx, 60*time.Second)
673672
defer cancel()
674673

675-
cmd := exec.CommandContext(ctxTimeout, s.opts.BinPath, "tx", "staking", "delegate",
674+
cmd := commandContext(ctxTimeout, s.opts.BinPath, "tx", "staking", "delegate",
676675
args.ValidatorAddress,
677676
fmt.Sprintf("%s%s", args.Amount, s.opts.Denom),
678677
"--from", args.KeyName,

0 commit comments

Comments
 (0)