From 2243c0877557a867bf0c56ee02b3364a3b6fc18d Mon Sep 17 00:00:00 2001 From: Javad Date: Wed, 3 Jul 2024 11:51:46 +0330 Subject: [PATCH 1/8] feat: add switch timeout to change client connection timeout --- cmd/wallet/main.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd/wallet/main.go b/cmd/wallet/main.go index 1282cc6ef..f25ff0b41 100644 --- a/cmd/wallet/main.go +++ b/cmd/wallet/main.go @@ -1,6 +1,8 @@ package main import ( + "time" + "github.com/pactus-project/pactus/cmd" "github.com/pactus-project/pactus/wallet" "github.com/spf13/cobra" @@ -10,6 +12,7 @@ var ( pathOpt *string offlineOpt *bool serverAddrOpt *string + timeoutOpt *int ) func addPasswordOption(c *cobra.Command) *string { @@ -27,6 +30,8 @@ func openWallet() (*wallet.Wallet, error) { wlt.SetServerAddr(*serverAddrOpt) } + wlt.SetClientTimeout(time.Duration(*timeoutOpt) * time.Second) + return wlt, err } @@ -44,6 +49,11 @@ func main() { cmd.PactusDefaultWalletPath(cmd.PactusDefaultHomeDir()), "the path to the wallet file") offlineOpt = rootCmd.PersistentFlags().Bool("offline", false, "offline mode") serverAddrOpt = rootCmd.PersistentFlags().String("server", "", "server gRPC address") + timeoutOpt = rootCmd.PersistentFlags().Int("timeout", 1, + "specifies the timeout duration for the client connection in seconds. "+ + "this timeout determines how long the client will attempt to establish a connection with the server "+ + "before failing with an error. Adjust this value based on network conditions and server response times "+ + "to ensure a balance between responsiveness and reliability.") buildCreateCmd(rootCmd) buildRecoverCmd(rootCmd) From 96bb8cd308ec8c932b2881f55c5be99c7f2024b2 Mon Sep 17 00:00:00 2001 From: Javad Date: Wed, 3 Jul 2024 11:52:23 +0330 Subject: [PATCH 2/8] feat: add custom dial context to set custom timeout when dialing --- wallet/client.go | 30 ++++++++++++++++++++++++++---- wallet/wallet.go | 4 ++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/wallet/client.go b/wallet/client.go index 6864a35a8..2b804c0ea 100644 --- a/wallet/client.go +++ b/wallet/client.go @@ -4,6 +4,8 @@ import ( "context" "encoding/hex" "errors" + "net" + "time" "github.com/pactus-project/pactus/crypto/hash" "github.com/pactus-project/pactus/types/amount" @@ -20,15 +22,17 @@ type grpcClient struct { ctx context.Context servers []string conn *grpc.ClientConn + timeout time.Duration blockchainClient pactus.BlockchainClient transactionClient pactus.TransactionClient } func newGrpcClient() *grpcClient { - ctx := context.WithoutCancel(context.Background()) + ctx := context.Background() return &grpcClient{ ctx: ctx, + timeout: time.Second * 1, conn: nil, blockchainClient: nil, transactionClient: nil, @@ -39,16 +43,30 @@ func (c *grpcClient) SetServerAddrs(servers []string) { c.servers = servers } +func (c *grpcClient) SetTimeoutConnection(timeout time.Duration) { + if timeout > 0 { + c.timeout = timeout + } +} + func (c *grpcClient) connect() error { if c.conn != nil { return nil } for _, server := range c.servers { - conn, err := grpc.NewClient(server, - grpc.WithTransportCredentials(insecure.NewCredentials())) + opts := make([]grpc.DialOption, 0) + opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) + + if len(c.servers) > 0 { + opts = append(opts, grpc.WithContextDialer(func(_ context.Context, s string) (net.Conn, error) { + return net.DialTimeout("tcp", s, c.timeout) + })) + } + + conn, err := grpc.NewClient(server, opts...) if err != nil { - continue + return err } blockchainClient := pactus.NewBlockchainClient(conn) @@ -58,6 +76,10 @@ func (c *grpcClient) connect() error { _, err = blockchainClient.GetBlockchainInfo(c.ctx, &pactus.GetBlockchainInfoRequest{}) if err != nil { + if err := conn.Close(); err != nil { + return err + } + continue } diff --git a/wallet/wallet.go b/wallet/wallet.go index 3f0bf528a..cf88ca5b9 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -156,6 +156,10 @@ func (w *Wallet) SetServerAddr(addr string) { w.grpcClient.SetServerAddrs([]string{addr}) } +func (w *Wallet) SetClientTimeout(timeout time.Duration) { + w.grpcClient.SetTimeoutConnection(timeout) +} + func (w *Wallet) Name() string { return path.Base(w.path) } From dace722dd8a2feca746ac368cf6b5a5afa8140bd Mon Sep 17 00:00:00 2001 From: Javad Date: Wed, 3 Jul 2024 11:59:28 +0330 Subject: [PATCH 3/8] fix: change len servers bigger than 1 --- wallet/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/client.go b/wallet/client.go index 2b804c0ea..034cafc53 100644 --- a/wallet/client.go +++ b/wallet/client.go @@ -58,7 +58,7 @@ func (c *grpcClient) connect() error { opts := make([]grpc.DialOption, 0) opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) - if len(c.servers) > 0 { + if len(c.servers) > 1 { opts = append(opts, grpc.WithContextDialer(func(_ context.Context, s string) (net.Conn, error) { return net.DialTimeout("tcp", s, c.timeout) })) From 0d7a4a78b4db66c77d8aaa74f2b67c2ffc05e256 Mon Sep 17 00:00:00 2001 From: Javad Date: Wed, 3 Jul 2024 13:51:29 +0330 Subject: [PATCH 4/8] fix: continue for other server when client error happen --- wallet/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/client.go b/wallet/client.go index 034cafc53..68b3d4d51 100644 --- a/wallet/client.go +++ b/wallet/client.go @@ -66,7 +66,7 @@ func (c *grpcClient) connect() error { conn, err := grpc.NewClient(server, opts...) if err != nil { - return err + continue } blockchainClient := pactus.NewBlockchainClient(conn) From cb985cf6f95489ff549a0deefa5b6dfdb5a224d5 Mon Sep 17 00:00:00 2001 From: Javad Date: Sun, 7 Jul 2024 09:06:56 +0330 Subject: [PATCH 5/8] fix: servers support multiple value and wallet opts --- cmd/wallet/main.go | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/cmd/wallet/main.go b/cmd/wallet/main.go index f25ff0b41..6da00a9fe 100644 --- a/cmd/wallet/main.go +++ b/cmd/wallet/main.go @@ -9,10 +9,10 @@ import ( ) var ( - pathOpt *string - offlineOpt *bool - serverAddrOpt *string - timeoutOpt *int + pathOpt *string + offlineOpt *bool + serverAddrsOpt *[]string + timeoutOpt *int ) func addPasswordOption(c *cobra.Command) *string { @@ -21,16 +21,20 @@ func addPasswordOption(c *cobra.Command) *string { } func openWallet() (*wallet.Wallet, error) { - wlt, err := wallet.Open(*pathOpt, *offlineOpt) - if err != nil { - return nil, err + opts := make([]wallet.Option, 0) + + if *serverAddrsOpt != nil { + opts = append(opts, wallet.WithCustomServers(*serverAddrsOpt)) } - if *serverAddrOpt != "" { - wlt.SetServerAddr(*serverAddrOpt) + if *timeoutOpt > 0 { + opts = append(opts, wallet.WithTimeout(time.Duration(*timeoutOpt)*time.Second)) } - wlt.SetClientTimeout(time.Duration(*timeoutOpt) * time.Second) + wlt, err := wallet.Open(*pathOpt, *offlineOpt, opts...) + if err != nil { + return nil, err + } return wlt, err } @@ -48,12 +52,9 @@ func main() { pathOpt = rootCmd.PersistentFlags().String("path", cmd.PactusDefaultWalletPath(cmd.PactusDefaultHomeDir()), "the path to the wallet file") offlineOpt = rootCmd.PersistentFlags().Bool("offline", false, "offline mode") - serverAddrOpt = rootCmd.PersistentFlags().String("server", "", "server gRPC address") + serverAddrsOpt = rootCmd.PersistentFlags().StringSlice("servers", []string{}, "servers gRPC address") timeoutOpt = rootCmd.PersistentFlags().Int("timeout", 1, - "specifies the timeout duration for the client connection in seconds. "+ - "this timeout determines how long the client will attempt to establish a connection with the server "+ - "before failing with an error. Adjust this value based on network conditions and server response times "+ - "to ensure a balance between responsiveness and reliability.") + "specifies the timeout duration for the client connection in seconds") buildCreateCmd(rootCmd) buildRecoverCmd(rootCmd) From e30a843d035437400bba27aeb090ff06ebe2e8ba Mon Sep 17 00:00:00 2001 From: Javad Date: Sun, 7 Jul 2024 09:07:18 +0330 Subject: [PATCH 6/8] feat: add global wallet options --- wallet/client.go | 29 ++++++++++------------------- wallet/manager.go | 3 +-- wallet/options.go | 27 +++++++++++++++++++++++++++ wallet/wallet.go | 37 ++++++++++++++++++++++--------------- wallet/wallet_test.go | 12 ++++++------ 5 files changed, 66 insertions(+), 42 deletions(-) create mode 100644 wallet/options.go diff --git a/wallet/client.go b/wallet/client.go index 68b3d4d51..276b96162 100644 --- a/wallet/client.go +++ b/wallet/client.go @@ -27,26 +27,22 @@ type grpcClient struct { transactionClient pactus.TransactionClient } -func newGrpcClient() *grpcClient { +func newGrpcClient(timeout time.Duration, servers []string) *grpcClient { ctx := context.Background() - return &grpcClient{ + cli := &grpcClient{ ctx: ctx, - timeout: time.Second * 1, + timeout: timeout, conn: nil, blockchainClient: nil, transactionClient: nil, } -} - -func (c *grpcClient) SetServerAddrs(servers []string) { - c.servers = servers -} -func (c *grpcClient) SetTimeoutConnection(timeout time.Duration) { - if timeout > 0 { - c.timeout = timeout + if len(servers) > 0 { + cli.servers = servers } + + return cli } func (c *grpcClient) connect() error { @@ -55,16 +51,11 @@ func (c *grpcClient) connect() error { } for _, server := range c.servers { - opts := make([]grpc.DialOption, 0) - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) - - if len(c.servers) > 1 { - opts = append(opts, grpc.WithContextDialer(func(_ context.Context, s string) (net.Conn, error) { + conn, err := grpc.NewClient(server, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithContextDialer(func(_ context.Context, s string) (net.Conn, error) { return net.DialTimeout("tcp", s, c.timeout) })) - } - - conn, err := grpc.NewClient(server, opts...) if err != nil { continue } diff --git a/wallet/manager.go b/wallet/manager.go index 5bdcebb39..a96f5f8ab 100644 --- a/wallet/manager.go +++ b/wallet/manager.go @@ -84,11 +84,10 @@ func (wm *Manager) LoadWallet(walletName, serverAddr string) error { } walletPath := util.MakeAbs(filepath.Join(wm.walletDirectory, walletName)) - wlt, err := Open(walletPath, true) + wlt, err := Open(walletPath, true, WithCustomServers([]string{serverAddr})) if err != nil { return err } - wlt.SetServerAddr(serverAddr) wm.wallets[walletName] = wlt diff --git a/wallet/options.go b/wallet/options.go new file mode 100644 index 000000000..87e3614f9 --- /dev/null +++ b/wallet/options.go @@ -0,0 +1,27 @@ +package wallet + +import "time" + +type walletOpt struct { + timeout time.Duration + servers []string +} + +type Option func(*walletOpt) + +var defaultWalletOpt = &walletOpt{ + timeout: 1 * time.Second, + servers: make([]string, 0), +} + +func WithTimeout(timeout time.Duration) Option { + return func(opt *walletOpt) { + opt.timeout = timeout + } +} + +func WithCustomServers(servers []string) Option { + return func(opt *walletOpt) { + opt.servers = servers + } +} diff --git a/wallet/wallet.go b/wallet/wallet.go index cf88ca5b9..0ae20e2a0 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -47,7 +47,7 @@ func CheckMnemonic(mnemonic string) error { // A wallet can be opened in offline or online modes. // Offline wallet doesn’t have any connection to any node. // Online wallet has a connection to one of the pre-defined servers. -func Open(walletPath string, offline bool) (*Wallet, error) { +func Open(walletPath string, offline bool, options ...Option) (*Wallet, error) { data, err := util.ReadFile(walletPath) if err != nil { return nil, err @@ -59,12 +59,24 @@ func Open(walletPath string, offline bool) (*Wallet, error) { return nil, err } - return newWallet(walletPath, store, offline) + opts := defaultWalletOpt + + for _, opt := range options { + opt(opts) + } + + return newWallet(walletPath, store, offline, opts) } // Create creates a wallet from mnemonic (seed phrase) and save it at the // given path. -func Create(walletPath, mnemonic, password string, chain genesis.ChainType) (*Wallet, error) { +func Create(walletPath, mnemonic, password string, chain genesis.ChainType, options ...Option) (*Wallet, error) { + opts := defaultWalletOpt + + for _, opt := range options { + opt(opts) + } + walletPath = util.MakeAbs(walletPath) if util.PathExists(walletPath) { return nil, ExitsError{ @@ -89,7 +101,7 @@ func Create(walletPath, mnemonic, password string, chain genesis.ChainType) (*Wa Network: chain, Vault: nil, } - wallet, err := newWallet(walletPath, store, true) + wallet, err := newWallet(walletPath, store, true, opts) if err != nil { return nil, err } @@ -106,7 +118,7 @@ func Create(walletPath, mnemonic, password string, chain genesis.ChainType) (*Wa return wallet, nil } -func newWallet(walletPath string, store *store, offline bool) (*Wallet, error) { +func newWallet(walletPath string, store *store, offline bool, option *walletOpt) (*Wallet, error) { if !store.Network.IsMainnet() { crypto.AddressHRP = "tpc" crypto.PublicKeyHRP = "tpublic" @@ -115,7 +127,7 @@ func newWallet(walletPath string, store *store, offline bool) (*Wallet, error) { crypto.XPrivateKeyHRP = "txsecret" } - client := newGrpcClient() + client := newGrpcClient(option.timeout, option.servers) w := &Wallet{ store: store, @@ -146,20 +158,15 @@ func newWallet(walletPath string, store *store, offline bool) (*Wallet, error) { } util.Shuffle(netServers) - w.grpcClient.SetServerAddrs(netServers) + + if client.servers == nil { + client.servers = netServers + } } return w, nil } -func (w *Wallet) SetServerAddr(addr string) { - w.grpcClient.SetServerAddrs([]string{addr}) -} - -func (w *Wallet) SetClientTimeout(timeout time.Duration) { - w.grpcClient.SetTimeoutConnection(timeout) -} - func (w *Wallet) Name() string { return path.Base(w.path) } diff --git a/wallet/wallet_test.go b/wallet/wallet_test.go index ce3715fef..7cb629a54 100644 --- a/wallet/wallet_test.go +++ b/wallet/wallet_test.go @@ -34,11 +34,6 @@ func setup(t *testing.T) *testData { password := "" walletPath := util.TempFilePath() mnemonic, _ := wallet.GenerateMnemonic(128) - wlt, err := wallet.Create(walletPath, mnemonic, password, genesis.Mainnet) - assert.NoError(t, err) - assert.False(t, wlt.IsEncrypted()) - assert.Equal(t, wlt.Path(), walletPath) - assert.Equal(t, wlt.Name(), path.Base(walletPath)) grpcConf := &grpc.Config{ Enable: true, @@ -57,7 +52,12 @@ func setup(t *testing.T) *testData { assert.NoError(t, gRPCServer.StartServer()) - wlt.SetServerAddr(gRPCServer.Address()) + wlt, err := wallet.Create(walletPath, mnemonic, password, genesis.Mainnet, + wallet.WithCustomServers([]string{gRPCServer.Address()})) + assert.NoError(t, err) + assert.False(t, wlt.IsEncrypted()) + assert.Equal(t, wlt.Path(), walletPath) + assert.Equal(t, wlt.Name(), path.Base(walletPath)) return &testData{ TestSuite: ts, From 0edc1bdb173ab9d8a4771a734b97b61f34c8a422 Mon Sep 17 00:00:00 2001 From: Javad Date: Sun, 7 Jul 2024 09:09:54 +0330 Subject: [PATCH 7/8] fix: ignored connection close error --- wallet/client.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/wallet/client.go b/wallet/client.go index 276b96162..9ec400414 100644 --- a/wallet/client.go +++ b/wallet/client.go @@ -67,9 +67,7 @@ func (c *grpcClient) connect() error { _, err = blockchainClient.GetBlockchainInfo(c.ctx, &pactus.GetBlockchainInfoRequest{}) if err != nil { - if err := conn.Close(); err != nil { - return err - } + _ = conn.Close() continue } From ada1aff50726e2c993e1c95ce4c5a6b2e0e9d78d Mon Sep 17 00:00:00 2001 From: Javad Date: Sun, 7 Jul 2024 10:48:17 +0330 Subject: [PATCH 8/8] fix: gui wallet set grpc addr --- cmd/cmd.go | 3 ++- cmd/gtk/main.go | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index 25123cce1..82124b5cf 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -388,7 +388,8 @@ func StartNode(workingDir string, passwordFetcher func(*wallet.Wallet) (string, } defaultWalletPath := PactusDefaultWalletPath(workingDir) - walletInstance, err := wallet.Open(defaultWalletPath, true) + walletInstance, err := wallet.Open(defaultWalletPath, true, + wallet.WithCustomServers([]string{conf.GRPC.Listen})) if err != nil { return nil, nil, err } diff --git a/cmd/gtk/main.go b/cmd/gtk/main.go index 216ab874f..0f522c207 100644 --- a/cmd/gtk/main.go +++ b/cmd/gtk/main.go @@ -170,8 +170,6 @@ func run(n *node.Node, wlt *wallet.Wallet, app *gtk.Application) { grpcAddr := n.GRPC().Address() cmd.PrintInfoMsgf("connect wallet to grpc server: %s\n", grpcAddr) - wlt.SetServerAddr(grpcAddr) - nodeModel := newNodeModel(n) walletModel := newWalletModel(wlt, n)