Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wallet): add timeout client connection #1396

Merged
merged 9 commits into from
Jul 8, 2024
3 changes: 2 additions & 1 deletion cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,8 @@
}

defaultWalletPath := PactusDefaultWalletPath(workingDir)
walletInstance, err := wallet.Open(defaultWalletPath, true)
walletInstance, err := wallet.Open(defaultWalletPath, true,
wallet.WithCustomServers([]string{conf.GRPC.Listen}))

Check warning on line 392 in cmd/cmd.go

View check run for this annotation

Codecov / codecov/patch

cmd/cmd.go#L391-L392

Added lines #L391 - L392 were not covered by tests
if err != nil {
return nil, nil, err
}
Expand Down
2 changes: 0 additions & 2 deletions cmd/gtk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
29 changes: 20 additions & 9 deletions cmd/wallet/main.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package main

import (
"time"

"github.com/pactus-project/pactus/cmd"
"github.com/pactus-project/pactus/wallet"
"github.com/spf13/cobra"
)

var (
pathOpt *string
offlineOpt *bool
serverAddrOpt *string
pathOpt *string
offlineOpt *bool
serverAddrsOpt *[]string
timeoutOpt *int
)

func addPasswordOption(c *cobra.Command) *string {
Expand All @@ -18,13 +21,19 @@
}

func openWallet() (*wallet.Wallet, error) {
wlt, err := wallet.Open(*pathOpt, *offlineOpt)
if err != nil {
return nil, err
opts := make([]wallet.Option, 0)

Check warning on line 24 in cmd/wallet/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/wallet/main.go#L24

Added line #L24 was not covered by tests

if *serverAddrsOpt != nil {
opts = append(opts, wallet.WithCustomServers(*serverAddrsOpt))

Check warning on line 27 in cmd/wallet/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/wallet/main.go#L26-L27

Added lines #L26 - L27 were not covered by tests
}

if *serverAddrOpt != "" {
wlt.SetServerAddr(*serverAddrOpt)
if *timeoutOpt > 0 {
opts = append(opts, wallet.WithTimeout(time.Duration(*timeoutOpt)*time.Second))

Check warning on line 31 in cmd/wallet/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/wallet/main.go#L30-L31

Added lines #L30 - L31 were not covered by tests
}

wlt, err := wallet.Open(*pathOpt, *offlineOpt, opts...)
if err != nil {
return nil, err

Check warning on line 36 in cmd/wallet/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/wallet/main.go#L34-L36

Added lines #L34 - L36 were not covered by tests
}

return wlt, err
Expand All @@ -43,7 +52,9 @@
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")

Check warning on line 57 in cmd/wallet/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/wallet/main.go#L55-L57

Added lines #L55 - L57 were not covered by tests

buildCreateCmd(rootCmd)
buildRecoverCmd(rootCmd)
Expand Down
25 changes: 18 additions & 7 deletions wallet/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"context"
"encoding/hex"
"errors"
"net"
"time"

"github.com/pactus-project/pactus/crypto/hash"
"github.com/pactus-project/pactus/types/amount"
Expand All @@ -20,23 +22,27 @@
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())
func newGrpcClient(timeout time.Duration, servers []string) *grpcClient {
ctx := context.Background()

return &grpcClient{
cli := &grpcClient{
ctx: ctx,
timeout: timeout,
conn: nil,
blockchainClient: nil,
transactionClient: nil,
}
}

func (c *grpcClient) SetServerAddrs(servers []string) {
c.servers = servers
if len(servers) > 0 {
cli.servers = servers
}

return cli
}

func (c *grpcClient) connect() error {
Expand All @@ -46,7 +52,10 @@

for _, server := range c.servers {
conn, err := grpc.NewClient(server,
grpc.WithTransportCredentials(insecure.NewCredentials()))
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithContextDialer(func(_ context.Context, s string) (net.Conn, error) {
return net.DialTimeout("tcp", s, c.timeout)
}))
if err != nil {
continue
}
Expand All @@ -58,6 +67,8 @@
_, err = blockchainClient.GetBlockchainInfo(c.ctx,
&pactus.GetBlockchainInfoRequest{})
if err != nil {
_ = conn.Close()

Check warning on line 70 in wallet/client.go

View check run for this annotation

Codecov / codecov/patch

wallet/client.go#L70

Added line #L70 was not covered by tests

continue
}

Expand Down
3 changes: 1 addition & 2 deletions wallet/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,10 @@
}

walletPath := util.MakeAbs(filepath.Join(wm.walletDirectory, walletName))
wlt, err := Open(walletPath, true)
wlt, err := Open(walletPath, true, WithCustomServers([]string{serverAddr}))

Check warning on line 87 in wallet/manager.go

View check run for this annotation

Codecov / codecov/patch

wallet/manager.go#L87

Added line #L87 was not covered by tests
if err != nil {
return err
}
wlt.SetServerAddr(serverAddr)

wm.wallets[walletName] = wlt

Expand Down
27 changes: 27 additions & 0 deletions wallet/options.go
Original file line number Diff line number Diff line change
@@ -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

Check warning on line 19 in wallet/options.go

View check run for this annotation

Codecov / codecov/patch

wallet/options.go#L18-L19

Added lines #L18 - L19 were not covered by tests
}
}

func WithCustomServers(servers []string) Option {
return func(opt *walletOpt) {
opt.servers = servers
}
}
33 changes: 22 additions & 11 deletions wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
// 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
Expand All @@ -59,12 +59,24 @@
return nil, err
}

return newWallet(walletPath, store, offline)
opts := defaultWalletOpt

for _, opt := range options {
opt(opts)

Check warning on line 65 in wallet/wallet.go

View check run for this annotation

Codecov / codecov/patch

wallet/wallet.go#L65

Added line #L65 was not covered by tests
}

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{
Expand All @@ -89,7 +101,7 @@
Network: chain,
Vault: nil,
}
wallet, err := newWallet(walletPath, store, true)
wallet, err := newWallet(walletPath, store, true, opts)
if err != nil {
return nil, err
}
Expand All @@ -106,7 +118,7 @@
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"
Expand All @@ -115,7 +127,7 @@
crypto.XPrivateKeyHRP = "txsecret"
}

client := newGrpcClient()
client := newGrpcClient(option.timeout, option.servers)

w := &Wallet{
store: store,
Expand Down Expand Up @@ -146,16 +158,15 @@
}

util.Shuffle(netServers)
w.grpcClient.SetServerAddrs(netServers)

if client.servers == nil {
client.servers = netServers

Check warning on line 163 in wallet/wallet.go

View check run for this annotation

Codecov / codecov/patch

wallet/wallet.go#L162-L163

Added lines #L162 - L163 were not covered by tests
}
}

return w, nil
}

func (w *Wallet) SetServerAddr(addr string) {
w.grpcClient.SetServerAddrs([]string{addr})
}

func (w *Wallet) Name() string {
return path.Base(w.path)
}
Expand Down
12 changes: 6 additions & 6 deletions wallet/wallet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Loading