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

fix: improve faucet reliability #1974

Merged
merged 51 commits into from
Jan 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
6e55daf
chore: sync master (#1945)
ilgooz Dec 17, 2021
3aa7d47
feat(changelog): reset `Unreleased` section (#1946)
ilgooz Dec 17, 2021
73a8ec0
chore: sync master
ilgooz Dec 18, 2021
e826818
fix(docs): change lazy ledger to celestia (#1952)
0xkrane Dec 23, 2021
78e098a
(docs): fix typo in hello.md (#1951)
0xkrane Dec 23, 2021
3577095
Minor grammar fix (#1950)
git-sgmoore Dec 23, 2021
56a27d0
Minor grammar fix (#1949)
git-sgmoore Dec 23, 2021
9176081
docs: add v0.19 to the readme (#1956)
fadeev Dec 26, 2021
9fe6d4a
Single transfer
lumtis Jan 5, 2022
956ae82
initialize faucet tests
lumtis Jan 6, 2022
49b9927
transfer mutex
lumtis Jan 6, 2022
297ccd7
batch request test
lumtis Jan 6, 2022
fbbd0f5
Fix total transfer
lumtis Jan 6, 2022
f51fb6d
Add wait tx
lumtis Jan 6, 2022
fe4fe14
lint
lumtis Jan 6, 2022
6b3712a
use http client
lumtis Jan 7, 2022
20afede
move test
lumtis Jan 7, 2022
72e6efa
Refacto faucet served
lumtis Jan 7, 2022
5763afe
use cosmosclient
lumtis Jan 7, 2022
57542df
remove coins
lumtis Jan 7, 2022
e70a39f
Merge branch 'release/v0.19.2' of https://github.com/tendermint/starp…
lumtis Jan 7, 2022
e4650a0
Merge branch 'release/v0.19.2' into fix/faucet-account-sequence-2
lumtis Jan 7, 2022
eac8ad4
Single transfer
lumtis Jan 5, 2022
b151775
initialize faucet tests
lumtis Jan 6, 2022
9384815
transfer mutex
lumtis Jan 6, 2022
51e3ede
batch request test
lumtis Jan 6, 2022
58ab4e5
Fix total transfer
lumtis Jan 6, 2022
b347935
Add wait tx
lumtis Jan 6, 2022
a5b2124
lint
lumtis Jan 6, 2022
1d6cf83
use http client
lumtis Jan 7, 2022
d3869f6
move test
lumtis Jan 7, 2022
7af590c
Refacto faucet served
lumtis Jan 7, 2022
a4de11b
use cosmosclient
lumtis Jan 7, 2022
42e662f
remove coins
lumtis Jan 7, 2022
ad30f75
Merge branch 'fix/faucet-account-sequence-2' of https://github.com/te…
lumtis Jan 7, 2022
68e30e1
Update integration/faucet/faucet_test.go
lumtis Jan 11, 2022
86db09c
Update integration/faucet/faucet_test.go
lumtis Jan 11, 2022
ee956e5
remove must
lumtis Jan 11, 2022
5d4828d
Merge branch 'fix/faucet-account-sequence-2' of https://github.com/te…
lumtis Jan 11, 2022
c12762b
Revert "feat(changelog): reset `Unreleased` section (#1946)"
lumtis Jan 11, 2022
34a0ee4
Revert "fix(docs): change lazy ledger to celestia (#1952)"
lumtis Jan 11, 2022
c975af9
Revert "(docs): fix typo in hello.md (#1951)"
lumtis Jan 11, 2022
dbc3402
Revert "Minor grammar fix (#1950)"
lumtis Jan 11, 2022
790f081
Revert "Minor grammar fix (#1949)"
lumtis Jan 11, 2022
dde15f2
Revert "docs: add v0.19 to the readme (#1956)"
lumtis Jan 11, 2022
6c797dd
suggestion
lumtis Jan 12, 2022
8b39e2c
update openapi
lumtis Jan 12, 2022
08190a4
add tx code check
lumtis Jan 12, 2022
f251864
update test
lumtis Jan 12, 2022
086e925
lint
lumtis Jan 12, 2022
ef5125c
Ensure JSON encoding on tx parsing
ilgooz Jan 13, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ require (
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf
google.golang.org/grpc v1.42.0
gopkg.in/yaml.v2 v2.4.0 // indirect
)

replace (
Expand Down
45 changes: 42 additions & 3 deletions integration/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/tendermint/starport/starport/pkg/availableport"
"github.com/tendermint/starport/starport/pkg/cmdrunner"
"github.com/tendermint/starport/starport/pkg/cmdrunner/step"
"github.com/tendermint/starport/starport/pkg/cosmosfaucet"
"github.com/tendermint/starport/starport/pkg/gocmd"
"github.com/tendermint/starport/starport/pkg/httpstatuschecker"
"github.com/tendermint/starport/starport/pkg/xexec"
Expand All @@ -29,6 +30,7 @@ import (
const (
ServeTimeout = time.Minute * 15
StarportApp = "starport"
ConfigYML = "config.yml"
)

var isCI, _ = strconv.ParseBool(os.Getenv("CI"))
Expand Down Expand Up @@ -233,7 +235,7 @@ func (e Env) Simulate(appPath string, numBlocks, blockSize int) {
// EnsureAppIsSteady ensures that app living at the path can compile and its tests
// are passing.
func (e Env) EnsureAppIsSteady(appPath string) {
_, statErr := os.Stat(filepath.Join(appPath, "config.yml"))
_, statErr := os.Stat(filepath.Join(appPath, ConfigYML))
require.False(e.t, os.IsNotExist(statErr), "config.yml cannot be found")

e.Exec("make sure app is steady",
Expand All @@ -257,6 +259,15 @@ func (e Env) IsAppServed(ctx context.Context, host chainconfig.Host) error {
return backoff.Retry(checkAlive, backoff.WithContext(backoff.NewConstantBackOff(time.Second), ctx))
}

// IsFaucetServed checks that faucet of the app is served properly
func (e Env) IsFaucetServed(ctx context.Context, faucetClient cosmosfaucet.HTTPClient) error {
checkAlive := func() error {
_, err := faucetClient.FaucetInfo(ctx)
return err
}
return backoff.Retry(checkAlive, backoff.WithContext(backoff.NewConstantBackOff(time.Second), ctx))
}

// TmpDir creates a new temporary directory.
func (e Env) TmpDir() (path string) {
path, err := os.MkdirTemp("", "integration")
Expand All @@ -269,7 +280,7 @@ func (e Env) TmpDir() (path string) {
// its config.yml and returns new values.
func (e Env) RandomizeServerPorts(path string, configFile string) chainconfig.Host {
if configFile == "" {
configFile = "config.yml"
configFile = ConfigYML
}

// generate random server ports and servers list.
Expand Down Expand Up @@ -306,10 +317,38 @@ func (e Env) RandomizeServerPorts(path string, configFile string) chainconfig.Ho
return servers
}

// ConfigureFaucet finds a random port for the app faucet and updates config.yml with this port and provided coins options
func (e Env) ConfigureFaucet(path string, configFile string, coins, coinsMax []string) string {
if configFile == "" {
configFile = ConfigYML
}

// find a random available port
port, err := availableport.Find(1)
require.NoError(e.t, err)

configyml, err := os.OpenFile(filepath.Join(path, configFile), os.O_RDWR|os.O_CREATE, 0755)
require.NoError(e.t, err)
defer configyml.Close()

var conf chainconfig.Config
require.NoError(e.t, yaml.NewDecoder(configyml).Decode(&conf))

conf.Faucet.Port = port[0]
conf.Faucet.Coins = coins
conf.Faucet.CoinsMax = coinsMax
require.NoError(e.t, configyml.Truncate(0))
_, err = configyml.Seek(0, 0)
require.NoError(e.t, err)
require.NoError(e.t, yaml.NewEncoder(configyml).Encode(conf))

return xurl.HTTP(fmt.Sprintf("0.0.0.0:%d", port[0]))
}

// SetRandomHomeConfig sets in the blockchain config files generated temporary directories for home directories
func (e Env) SetRandomHomeConfig(path string, configFile string) {
if configFile == "" {
configFile = "config.yml"
configFile = ConfigYML
}

// update config.yml with the generated temporary directories
Expand Down
114 changes: 114 additions & 0 deletions integration/faucet/faucet_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package faucet_test

import (
"context"
"errors"
"fmt"
"net/http"
"strings"
"testing"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/stretchr/testify/require"
envtest "github.com/tendermint/starport/integration"
"github.com/tendermint/starport/starport/pkg/cosmosclient"
"github.com/tendermint/starport/starport/pkg/cosmosfaucet"
"github.com/tendermint/starport/starport/pkg/xurl"
"golang.org/x/sync/errgroup"
)

const (
addr = "cosmos1zqr2gd7hwkyw55knad0l6ml6ngutd70878evqj"
)

var (
defaultCoins = []string{"10token", "1stake"}
maxCoins = []string{"102token", "100000000stake"}
)

func TestRequestCoinsFromFaucet(t *testing.T) {
var (
env = envtest.New(t)
apath = env.Scaffold("faucet")
servers = env.RandomizeServerPorts(apath, "")
faucetURL = env.ConfigureFaucet(apath, "", defaultCoins, maxCoins)
ctx, cancel = context.WithTimeout(env.Ctx(), envtest.ServeTimeout)
faucetClient = cosmosfaucet.NewClient(faucetURL)
)
isErrTransferRequest := func(err error, expectedCode int) {
require.ErrorAs(t, err, &cosmosfaucet.ErrTransferRequest{})
errTransfer := err.(cosmosfaucet.ErrTransferRequest)
require.EqualValues(t, expectedCode, errTransfer.StatusCode)
}

// serve the app
go func() {
env.Serve("should serve app", apath, "", "", envtest.ExecCtx(ctx))
}()

// wait servers to be online
defer cancel()
err := env.IsAppServed(ctx, servers)
require.NoError(t, err)

err = env.IsFaucetServed(ctx, faucetClient)
require.NoError(t, err)

// error "account doesn't have any balances" occurs if a sleep is not included
time.Sleep(time.Second * 1)
ilgooz marked this conversation as resolved.
Show resolved Hide resolved

cosmosClient, err := cosmosclient.New(ctx, cosmosclient.WithNodeAddress(xurl.HTTP(servers.RPC)))
require.NoError(t, err)

// the faucet sends the default faucet coins value when not specified
_, err = faucetClient.Transfer(ctx, cosmosfaucet.NewTransferRequest(addr, nil))
require.NoError(t, err)
checkAccountBalance(t, ctx, cosmosClient, addr, defaultCoins)

// the faucet can send a specified amount of coins
_, err = faucetClient.Transfer(ctx, cosmosfaucet.NewTransferRequest(addr, []string{"20token", "2stake"}))
require.NoError(t, err)
checkAccountBalance(t, ctx, cosmosClient, addr, []string{"30token", "3stake"})

// faucet request fails on malformed coins
_, err = faucetClient.Transfer(ctx, cosmosfaucet.NewTransferRequest(addr, []string{"no-token"}))
isErrTransferRequest(err, http.StatusBadRequest)

// faucet request fails when requesting more than max coins
_, err = faucetClient.Transfer(ctx, cosmosfaucet.NewTransferRequest(addr, []string{"500token"}))
isErrTransferRequest(err, http.StatusInternalServerError)

// faucet request fails when transfer should fail
_, err = faucetClient.Transfer(ctx, cosmosfaucet.NewTransferRequest(addr, []string{"500nonexistent"}))
isErrTransferRequest(err, http.StatusInternalServerError)

// send several request in parallel and check max coins is not overflown
g, ctx := errgroup.WithContext(ctx)
for i := 0; i < 10; i++ {
g.Go(func() error {
_, err = faucetClient.Transfer(ctx, cosmosfaucet.NewTransferRequest(addr, nil))
if err != nil && !errors.As(err, &cosmosfaucet.ErrTransferRequest{}) {
return err
}
return nil
})
}
require.NoError(t, g.Wait())
checkAccountBalance(t, ctx, cosmosClient, addr, []string{"100token", "10stake"})
}

func checkAccountBalance(t *testing.T, ctx context.Context, c cosmosclient.Client, accAddr string, coins []string) {
resp, err := banktypes.NewQueryClient(c.Context).AllBalances(ctx, &banktypes.QueryAllBalancesRequest{
Address: accAddr,
})
require.NoError(t, err)

require.Len(t, resp.Balances, len(coins))
expectedCoins, err := sdk.ParseCoinsNormalized(strings.Join(coins, ","))
require.NoError(t, err)
require.True(t, resp.Balances.IsEqual(expectedCoins),
fmt.Sprintf("%s should be equals to %s", resp.Balances.String(), expectedCoins.String()),
)
}
20 changes: 10 additions & 10 deletions starport/cmd/chain_faucet.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ package starportcmd

import (
"fmt"
"strings"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"
"github.com/tendermint/starport/starport/pkg/chaincmd"
"github.com/tendermint/starport/starport/pkg/cosmoscoin"
"github.com/tendermint/starport/starport/services/chain"
)

Expand Down Expand Up @@ -47,14 +46,15 @@ func chainFaucetHandler(cmd *cobra.Command, args []string) error {
return err
}

for _, coin := range strings.Split(coins, ",") {
amount, denom, err := cosmoscoin.Parse(coin)
if err != nil {
return fmt.Errorf("%s: %s", err, coin)
}
if err := faucet.Transfer(cmd.Context(), toAddress, amount, denom); err != nil {
return err
}
// parse provided coins
parsedCoins, err := sdk.ParseCoinsNormalized(coins)
if err != nil {
return err
}

// perform transfer from faucet
if err := faucet.Transfer(cmd.Context(), toAddress, parsedCoins); err != nil {
return err
}

fmt.Println("📨 Coins sent.")
Expand Down
12 changes: 12 additions & 0 deletions starport/pkg/chaincmd/chaincmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,18 @@ func (c ChainCmd) BankSendCommand(fromAddress, toAddress, amount string) step.Op
return c.cliCommand(command)
}

// QueryTxCommand returns the command to query tx
func (c ChainCmd) QueryTxCommand(txHash string) step.Option {
command := []string{
commandQuery,
"tx",
txHash,
}

command = c.attachNode(command)
return c.cliCommand(command)
}

// QueryTxEventsCommand returns the command to query events.
func (c ChainCmd) QueryTxEventsCommand(query string) step.Option {
command := []string{
Expand Down
60 changes: 43 additions & 17 deletions starport/pkg/chaincmd/runner/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"os"
"regexp"
"strings"
"time"

"github.com/cenkalti/backoff"
"github.com/pkg/errors"
"github.com/tendermint/starport/starport/pkg/chaincmd"
"github.com/tendermint/starport/starport/pkg/cmdrunner/step"
"github.com/tendermint/starport/starport/pkg/cosmosver"
Expand Down Expand Up @@ -163,7 +164,7 @@ func (r Runner) Status(ctx context.Context) (NodeStatus, error) {
}

// BankSend sends amount from fromAccount to toAccount.
func (r Runner) BankSend(ctx context.Context, fromAccount, toAccount, amount string) error {
func (r Runner) BankSend(ctx context.Context, fromAccount, toAccount, amount string) (string, error) {
b := newBuffer()
opt := []step.Option{
r.chainCmd.BankSendCommand(fromAccount, toAccount, amount),
Expand All @@ -181,30 +182,55 @@ func (r Runner) BankSend(ctx context.Context, fromAccount, toAccount, amount str
if strings.Contains(err.Error(), "key not found") || // stargate
strings.Contains(err.Error(), "unknown address") || // launchpad
strings.Contains(b.String(), "item could not be found") { // launchpad
return errors.New("account doesn't have any balances")
return "", errors.New("account doesn't have any balances")
}

return err
return "", err
}

out := struct {
Code int `json:"code"`
Error string `json:"raw_log"`
}{}

data, err := b.JSONEnsuredBytes()
txResult, err := decodeTxResult(b)
if err != nil {
return err
}
if err := json.Unmarshal(data, &out); err != nil {
return err
return "", err
}

if out.Code > 0 {
return fmt.Errorf("cannot send tokens (SDK code %d): %s", out.Code, out.Error)
if txResult.Code > 0 {
return "", fmt.Errorf("cannot send tokens (SDK code %d): %s", txResult.Code, txResult.RawLog)
}

return nil
return txResult.TxHash, nil
}

// WaitTx waits until a tx is successfully added to a block and can be queried
func (r Runner) WaitTx(ctx context.Context, txHash string, retryDelay time.Duration, maxRetry int) error {
retry := 0

// retry querying the request
checkTx := func() error {
b := newBuffer()
if err := r.run(ctx, runOptions{stdout: b}, r.chainCmd.QueryTxCommand(txHash)); err != nil {
// filter not found error and check for max retry
if !strings.Contains(err.Error(), "not found") {
return backoff.Permanent(err)
}
retry++
if retry == maxRetry {
return backoff.Permanent(fmt.Errorf("can't retrieve tx %s", txHash))
}
return err
}

// parse tx and check code
txResult, err := decodeTxResult(b)
if err != nil {
return backoff.Permanent(err)
}
if txResult.Code != 0 {
return backoff.Permanent(fmt.Errorf("tx %s failed: %s", txHash, txResult.RawLog))
}

return nil
}
return backoff.Retry(checkTx, backoff.WithContext(backoff.NewConstantBackOff(retryDelay), ctx))
}

// Export exports the state of the chain into the specified file
Expand Down
18 changes: 18 additions & 0 deletions starport/pkg/chaincmd/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package chaincmdrunner
import (
"bytes"
"context"
"encoding/json"
"io"

"github.com/ghodss/yaml"
Expand Down Expand Up @@ -176,3 +177,20 @@ func (b *buffer) JSONEnsuredBytes() ([]byte, error) {

return bytes, nil
}

type txResult struct {
Code int `json:"code"`
RawLog string `json:"raw_log"`
TxHash string `json:"txhash"`
}

func decodeTxResult(b *buffer) (txResult, error) {
var r txResult

data, err := b.JSONEnsuredBytes()
if err != nil {
return r, err
}

return r, json.Unmarshal(data, &r)
}
Loading