Skip to content

Commit

Permalink
Param proposals, balance inquiries, and IBC transfer error handling (#…
Browse files Browse the repository at this point in the history
…393)

* Extract file writing/copying logic to helpers

* Add ParamChangeProposal

* Update file naming for param change proposal

* Use Code to detect errors in IBC transfers

* Add AllBalances and ParamChangeProposal
  • Loading branch information
bigs authored Feb 8, 2023
1 parent 0bdc194 commit 0b3a8a0
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 17 deletions.
70 changes: 53 additions & 17 deletions chain/cosmos/chain_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/types"
paramsutils "github.com/cosmos/cosmos-sdk/x/params/client/utils"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
dockertypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
Expand Down Expand Up @@ -145,8 +146,8 @@ func (tn *ChainNode) genesisFileContent(ctx context.Context) ([]byte, error) {
}

func (tn *ChainNode) overwriteGenesisFile(ctx context.Context, content []byte) error {
fw := dockerutil.NewFileWriter(tn.logger(), tn.DockerClient, tn.TestName)
if err := fw.WriteFile(ctx, tn.VolumeName, "config/genesis.json", content); err != nil {
err := tn.WriteFile(ctx, content, "config/genesis.json")
if err != nil {
return fmt.Errorf("overwriting genesis.json: %w", err)
}

Expand All @@ -167,8 +168,8 @@ func (tn *ChainNode) copyGentx(ctx context.Context, destVal *ChainNode) error {
return fmt.Errorf("getting gentx content: %w", err)
}

fw := dockerutil.NewFileWriter(destVal.logger(), destVal.DockerClient, destVal.TestName)
if err := fw.WriteFile(ctx, destVal.VolumeName, relPath, gentx); err != nil {
err = destVal.WriteFile(ctx, gentx, relPath)
if err != nil {
return fmt.Errorf("overwriting gentx: %w", err)
}

Expand Down Expand Up @@ -502,6 +503,25 @@ func (tn *ChainNode) InitHomeFolder(ctx context.Context) error {
return err
}

// WriteFile accepts file contents in a byte slice and writes the contents to
// the docker filesystem. relPath describes the location of the file in the
// docker volume relative to the home directory
func (tn *ChainNode) WriteFile(ctx context.Context, content []byte, relPath string) error {
fw := dockerutil.NewFileWriter(tn.logger(), tn.DockerClient, tn.TestName)
return fw.WriteFile(ctx, tn.VolumeName, relPath, content)
}

// CopyFile adds a file from the host filesystem to the docker filesystem
// relPath describes the location of the file in the docker volume relative to
// the home directory
func (tn *ChainNode) CopyFile(ctx context.Context, srcPath, dstPath string) error {
content, err := os.ReadFile(srcPath)
if err != nil {
return err
}
return tn.WriteFile(ctx, content, dstPath)
}

// CreateKey creates a key in the keyring backend test for the given node
func (tn *ChainNode) CreateKey(ctx context.Context, name string) error {
tn.lock.Lock()
Expand Down Expand Up @@ -645,14 +665,9 @@ type CodeInfosResponse struct {

// StoreContract takes a file path to smart contract and stores it on-chain. Returns the contracts code id.
func (tn *ChainNode) StoreContract(ctx context.Context, keyName string, fileName string) (string, error) {
content, err := os.ReadFile(fileName)
if err != nil {
return "", err
}

_, file := filepath.Split(fileName)
fw := dockerutil.NewFileWriter(tn.logger(), tn.DockerClient, tn.TestName)
if err := fw.WriteFile(ctx, tn.VolumeName, file, content); err != nil {
err := tn.CopyFile(ctx, fileName, file)
if err != nil {
return "", fmt.Errorf("writing contract file to docker volume: %w", err)
}

Expand Down Expand Up @@ -728,13 +743,9 @@ func (tn *ChainNode) QueryContract(ctx context.Context, contractAddress string,
// StoreClientContract takes a file path to a client smart contract and stores it on-chain. Returns the contracts code id.
func (tn *ChainNode) StoreClientContract(ctx context.Context, keyName string, fileName string) (string, error) {
content, err := os.ReadFile(fileName)
if err != nil {
return "", err
}

_, file := filepath.Split(fileName)
fw := dockerutil.NewFileWriter(tn.logger(), tn.DockerClient, tn.TestName)
if err := fw.WriteFile(ctx, tn.VolumeName, file, content); err != nil {
err = tn.WriteFile(ctx, content, file)
if err != nil {
return "", fmt.Errorf("writing contract file to docker volume: %w", err)
}

Expand Down Expand Up @@ -816,6 +827,31 @@ func (tn *ChainNode) TextProposal(ctx context.Context, keyName string, prop Text
return tn.ExecTx(ctx, keyName, command...)
}

// ParamChangeProposal submits a param change proposal to the chain, signed by keyName.
func (tn *ChainNode) ParamChangeProposal(ctx context.Context, keyName string, prop *paramsutils.ParamChangeProposalJSON) (string, error) {
content, err := json.Marshal(prop)
if err != nil {
return "", err
}

hash := sha256.Sum256(content)
proposalFilename := fmt.Sprintf("%x.json", hash)
err = tn.WriteFile(ctx, content, proposalFilename)
if err != nil {
return "", fmt.Errorf("writing param change proposal: %w", err)
}

proposalPath := filepath.Join(tn.HomeDir(), proposalFilename)

command := []string{
"gov", "submit-proposal",
"param-change",
proposalPath,
}

return tn.ExecTx(ctx, keyName, command...)
}

// DumpContractState dumps the state of a contract at a block height.
func (tn *ChainNode) DumpContractState(ctx context.Context, contractAddress string, height int64) (*DumpContractStateResponse, error) {
stdout, _, err := tn.ExecQuery(ctx,
Expand Down
34 changes: 34 additions & 0 deletions chain/cosmos/cosmos_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/cosmos/cosmos-sdk/types"
authTx "github.com/cosmos/cosmos-sdk/x/auth/tx"
bankTypes "github.com/cosmos/cosmos-sdk/x/bank/types"
paramsutils "github.com/cosmos/cosmos-sdk/x/params/client/utils"
chanTypes "github.com/cosmos/ibc-go/v6/modules/core/04-channel/types"
dockertypes "github.com/docker/docker/api/types"
volumetypes "github.com/docker/docker/api/types/volume"
Expand Down Expand Up @@ -304,6 +305,9 @@ func (c *CosmosChain) SendIBCTransfer(
if err != nil {
return tx, fmt.Errorf("failed to get transaction %s: %w", txHash, err)
}
if txResp.Code != 0 {
return tx, fmt.Errorf("error in transaction (code: %d): %s", txResp.Code, txResp.RawLog)
}
tx.Height = uint64(txResp.Height)
tx.TxHash = txHash
// In cosmos, user is charged for entire gas requested, not the actual gas used.
Expand Down Expand Up @@ -367,6 +371,16 @@ func (c *CosmosChain) TextProposal(ctx context.Context, keyName string, prop Tex
return c.txProposal(txHash)
}

// ParamChangeProposal submits a param change proposal to the chain, signed by keyName.
func (c *CosmosChain) ParamChangeProposal(ctx context.Context, keyName string, prop *paramsutils.ParamChangeProposalJSON) (tx TxProposal, _ error) {
txHash, err := c.getFullNode().ParamChangeProposal(ctx, keyName, prop)
if err != nil {
return tx, fmt.Errorf("failed to submit param change proposal: %w", err)
}

return c.txProposal(txHash)
}

func (c *CosmosChain) txProposal(txHash string) (tx TxProposal, _ error) {
txResp, err := c.getTransaction(txHash)
if err != nil {
Expand Down Expand Up @@ -449,6 +463,26 @@ func (c *CosmosChain) GetBalance(ctx context.Context, address string, denom stri
return res.Balance.Amount.Int64(), nil
}

// AllBalances fetches an account address's balance for all denoms it holds
func (c *CosmosChain) AllBalances(ctx context.Context, address string) (types.Coins, error) {
params := bankTypes.QueryAllBalancesRequest{Address: address}
grpcAddress := c.getFullNode().hostGRPCPort
conn, err := grpc.Dial(grpcAddress, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, err
}
defer conn.Close()

queryClient := bankTypes.NewQueryClient(conn)
res, err := queryClient.AllBalances(ctx, &params)

if err != nil {
return nil, err
}

return res.GetBalances(), nil
}

func (c *CosmosChain) getTransaction(txHash string) (*types.TxResponse, error) {
// Retry because sometimes the tx is not committed to state yet.
var txResp *types.TxResponse
Expand Down

0 comments on commit 0b3a8a0

Please sign in to comment.