From 412c8bd72480c411c4f8988e0192f9320dabc2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0lker=20G=2E=20=C3=96zt=C3=BCrk?= Date: Mon, 29 Nov 2021 15:54:06 +0300 Subject: [PATCH] fix(pkg/cosmosclient): ensure account has funds after faucet transfer this is useful if tx for faucet transfer not sent in the `sync` mode. * pkg/cosmosclient: improve faucet defaults. * pkg/cosmosfaucet: ensure that transfer tx uses `syncA mode.` --- go.mod | 1 + go.sum | 2 + starport/pkg/chaincmd/chaincmd.go | 4 + starport/pkg/cosmosclient/cosmosclient.go | 100 +++++++++++----------- 4 files changed, 55 insertions(+), 52 deletions(-) diff --git a/go.mod b/go.mod index 44d544ce66..cb22684365 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/blang/semver v3.5.1+incompatible github.com/briandowns/spinner v1.11.1 github.com/cenkalti/backoff v2.2.1+incompatible + github.com/cenkalti/backoff/v4 v4.1.2 // indirect github.com/charmbracelet/glow v1.4.0 github.com/containerd/containerd v1.5.8 // indirect github.com/cosmos/cosmos-sdk v0.44.3 diff --git a/go.sum b/go.sum index de5ef48506..b017290d2f 100644 --- a/go.sum +++ b/go.sum @@ -247,6 +247,8 @@ github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo= +github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= diff --git a/starport/pkg/chaincmd/chaincmd.go b/starport/pkg/chaincmd/chaincmd.go index 53855add2a..3af50ea000 100644 --- a/starport/pkg/chaincmd/chaincmd.go +++ b/starport/pkg/chaincmd/chaincmd.go @@ -38,9 +38,11 @@ const ( optionValidatorGasPrices = "--gas-prices" optionYes = "--yes" optionHomeClient = "--home-client" + optionBroadcastMode = "--broadcast-mode" constTendermint = "tendermint" constJSON = "json" + constSync = "sync" ) type KeyringBackend string @@ -435,6 +437,8 @@ func (c ChainCmd) BankSendCommand(fromAddress, toAddress, amount string) step.Op fromAddress, toAddress, amount, + optionBroadcastMode, + constSync, optionYes, ) diff --git a/starport/pkg/cosmosclient/cosmosclient.go b/starport/pkg/cosmosclient/cosmosclient.go index 645df3e318..1a538f05b5 100644 --- a/starport/pkg/cosmosclient/cosmosclient.go +++ b/starport/pkg/cosmosclient/cosmosclient.go @@ -4,16 +4,15 @@ package cosmosclient import ( "context" "encoding/hex" - "encoding/json" "fmt" "io" - "net/http" "os" "path/filepath" - "strconv" "strings" "sync" + "time" + "github.com/cenkalti/backoff/v4" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" @@ -24,6 +23,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx/signing" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" staking "github.com/cosmos/cosmos-sdk/x/staking/types" proto "github.com/gogo/protobuf/proto" prototypes "github.com/gogo/protobuf/types" @@ -33,6 +33,10 @@ import ( rpchttp "github.com/tendermint/tendermint/rpc/client/http" ) +// FaucetTransferEnsureDuration is the duration that BroadcastTx will wait when a faucet transfer +// is triggered prior to broadcasting but transfer's tx is not committed in the state yet. +var FaucetTransferEnsureDuration = time.Minute * 2 + const ( defaultNodeAddress = "http://localhost:26657" defaultGasAdjustment = 1.0 @@ -40,8 +44,9 @@ const ( ) const ( - faucetDenom = "token" - faucetMinAmount = 100 + defaultFaucetAddress = "http://localhost:4500" + defaultFaucetDenom = "token" + defaultFaucetMinAmount = 100 ) // Client is a client to access your chain by querying and broadcasting transactions. @@ -61,14 +66,13 @@ type Client struct { addressPrefix string nodeAddress string - apiAddress string out io.Writer chainID string useFaucet bool faucetAddress string faucetDenom string - faucetMinAmount int64 + faucetMinAmount uint64 homePath string keyringServiceName string @@ -110,19 +114,13 @@ func WithNodeAddress(addr string) Option { } } -func WithAPIAddress(addr string) Option { - return func(c *Client) { - c.apiAddress = addr - } -} - func WithAddressPrefix(prefix string) Option { return func(c *Client) { c.addressPrefix = prefix } } -func WithUseFaucet(faucetAddress, denom string, minAmount int64) Option { +func WithUseFaucet(faucetAddress, denom string, minAmount uint64) Option { return func(c *Client) { c.useFaucet = true c.faucetAddress = faucetAddress @@ -138,10 +136,13 @@ func WithUseFaucet(faucetAddress, denom string, minAmount int64) Option { // New creates a new client with given options. func New(ctx context.Context, options ...Option) (Client, error) { c := Client{ - nodeAddress: defaultNodeAddress, - keyringBackend: cosmosaccount.KeyringTest, - addressPrefix: "cosmos", - out: io.Discard, + nodeAddress: defaultNodeAddress, + keyringBackend: cosmosaccount.KeyringTest, + addressPrefix: "cosmos", + faucetAddress: defaultFaucetAddress, + faucetDenom: defaultFaucetDenom, + faucetMinAmount: defaultFaucetMinAmount, + out: io.Discard, } var err error @@ -334,39 +335,8 @@ func (c *Client) prepareBroadcast(ctx context.Context, accountName string, _ []s // makeSureAccountHasTokens makes sure the address has a positive balance // it requests funds from the faucet if the address has an empty balance func (c *Client) makeSureAccountHasTokens(ctx context.Context, address string) error { - // check the balance. - balancesEndpoint := fmt.Sprintf("%s/bank/balances/%s", c.apiAddress, address) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, balancesEndpoint, nil) - if err != nil { - return err - } - resp, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - var balances struct { - Result []struct { - Denom string `json:"denom"` - Amount string `json:"amount"` - } `json:"result"` - } - if err := json.NewDecoder(resp.Body).Decode(&balances); err != nil { - return err - } - - // if the balance is enough do nothing. - if len(balances.Result) > 0 { - for _, c := range balances.Result { - amount, err := strconv.ParseInt(c.Amount, 10, 32) - if err != nil { - return err - } - if c.Denom == faucetDenom && amount >= faucetMinAmount { - return nil - } - } + if err := c.checkAccountBalance(ctx, address); err == nil { + return nil } // request coins from the faucet. @@ -384,7 +354,33 @@ func (c *Client) makeSureAccountHasTokens(ctx context.Context, address string) e } } - return nil + // make sure funds are retrieved. + ctx, cancel := context.WithTimeout(ctx, FaucetTransferEnsureDuration) + defer cancel() + + return backoff.Retry(func() error { + return c.checkAccountBalance(ctx, address) + }, backoff.WithContext(backoff.NewConstantBackOff(time.Second), ctx)) +} + +func (c *Client) checkAccountBalance(ctx context.Context, address string) (err error) { + balancesResp, err := banktypes.NewQueryClient(c.Context).AllBalances(ctx, &banktypes.QueryAllBalancesRequest{ + Address: address, + }) + if err != nil { + return err + } + + // if the balance is enough do nothing. + if len(balancesResp.Balances) > 0 { + for _, coin := range balancesResp.Balances { + if coin.Denom == c.faucetDenom && coin.Amount.Uint64() >= c.faucetMinAmount { + return nil + } + } + } + + return errors.New("account has not enough balance") } // handleBroadcastResult handles the result of broadcast messages result and checks if an error occurred