Skip to content

Commit

Permalink
[Configs] feat: Add staking config parser of gateway staking (#302)
Browse files Browse the repository at this point in the history
* feat: Add staking config parser of gateway staking

* fix: Pass the config path param

* fix: Adapt gateway staking e2e tests

* fix: Remove trailing comma from stake amount

* fix: Create gateway staking file from test step
  • Loading branch information
red-0ne authored Jan 10, 2024
1 parent f08c32a commit 4dcb43f
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 40 deletions.
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -274,19 +274,19 @@ gateway_list: ## List all the staked gateways

.PHONY: gateway_stake
gateway_stake: ## Stake tokens for the gateway specified (must specify the gateway env var)
poktrolld --home=$(POKTROLLD_HOME) tx gateway stake-gateway 1000upokt --keyring-backend test --from $(GATEWAY) --node $(POCKET_NODE)
poktrolld --home=$(POKTROLLD_HOME) tx gateway stake-gateway --config $(POKTROLLD_HOME)/config/$(STAKE) --keyring-backend test --from $(GATEWAY) --node $(POCKET_NODE)

.PHONY: gateway1_stake
gateway1_stake: ## Stake gateway1
GATEWAY=gateway1 make gateway_stake
GATEWAY=gateway1 STAKE=gateway1_stake_config.yaml make gateway_stake

.PHONY: gateway2_stake
gateway2_stake: ## Stake gateway2
GATEWAY=gateway2 make gateway_stake
GATEWAY=gateway2 STAKE=gateway2_stake_config.yaml make gateway_stake

.PHONY: gateway3_stake
gateway3_stake: ## Stake gateway3
GATEWAY=gateway3 make gateway_stake
GATEWAY=gateway3 STAKE=gateway3_stake_config.yaml make gateway_stake

.PHONY: gateway_unstake
gateway_unstake: ## Unstake an gateway (must specify the GATEWAY env var)
Expand Down
22 changes: 21 additions & 1 deletion e2e/tests/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package e2e
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"regexp"
Expand Down Expand Up @@ -166,17 +167,36 @@ func (s *suite) TheUserShouldWaitForSeconds(dur int64) {
}

func (s *suite) TheUserStakesAWithUpoktFromTheAccount(actorType string, amount int64, accName string) {
// Create a temporary config file
configPathPattern := fmt.Sprintf("%s_stake_config_*.yaml", accName)
configContent := fmt.Sprintf(`stake_amount: %d upokt`, amount)
configFile, err := ioutil.TempFile("", configPathPattern)
if err != nil {
s.Fatalf("error creating config file: %q", err)
}
if _, err = configFile.Write([]byte(configContent)); err != nil {
s.Fatalf("error writing config file: %q", err)
}

args := []string{
"tx",
actorType,
fmt.Sprintf("stake-%s", actorType),
fmt.Sprintf("%dupokt", amount),
"--config",
configFile.Name(),
"--from",
accName,
keyRingFlag,
"-y",
}
res, err := s.pocketd.RunCommandOnHost("", args...)

// Remove the temporary config file
err = os.Remove(configFile.Name())
if err != nil {
s.Fatalf("error removing config file: %q", err)
}

if err != nil {
s.Fatalf("error staking %s: %s", actorType, err)
}
Expand Down
1 change: 1 addition & 0 deletions localnet/poktrolld/config/gateway1_stake_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
stake_amount: 1000upokt
1 change: 1 addition & 0 deletions localnet/poktrolld/config/gateway2_stake_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
stake_amount: 1000upokt
1 change: 1 addition & 0 deletions localnet/poktrolld/config/gateway3_stake_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
stake_amount: 1000upokt
31 changes: 21 additions & 10 deletions x/gateway/client/cli/tx_stake_gateway.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,51 @@
package cli

import (
"os"
"strconv"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"

"github.com/pokt-network/poktroll/x/gateway/client/config"
"github.com/pokt-network/poktroll/x/gateway/types"
)

var _ = strconv.Itoa(0)
var (
flagStakeConfig string
_ = strconv.Itoa(0)
)

func CmdStakeGateway() *cobra.Command {
cmd := &cobra.Command{
Use: "stake-gateway <upokt_amount>",
Use: "stake-gateway --config <config_file.yaml>",
Short: "Stake a gateway",
Long: `Stake a gateway with the provided parameters. This is a broadcast operation that
will stake the tokens and associate them with the gateway specified by the 'from' address.
Example:
$ poktrolld --home=$(POKTROLLD_HOME) tx gateway stake-gateway 1000upokt --keyring-backend test --from $(GATEWAY) --node $(POCKET_NODE)`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientTxContext(cmd)
$ poktrolld --home=$(POKTROLLD_HOME) tx gateway stake-gateway --config stake_config.yaml --keyring-backend test --from $(GATEWAY) --node $(POCKET_NODE)`,
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, _ []string) (err error) {
configContent, err := os.ReadFile(flagStakeConfig)
if err != nil {
return err
}
stakeString := args[0]
stake, err := sdk.ParseCoinNormalized(stakeString)

gatewayStakeConfig, err := config.ParseGatewayConfig(configContent)
if err != nil {
return err
}

clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

msg := types.NewMsgStakeGateway(
clientCtx.GetFromAddress().String(),
stake,
gatewayStakeConfig.StakeAmount,
)
if err := msg.ValidateBasic(); err != nil {
return err
Expand All @@ -44,6 +54,7 @@ $ poktrolld --home=$(POKTROLLD_HOME) tx gateway stake-gateway 1000upokt --keyrin
},
}

cmd.Flags().StringVar(&flagStakeConfig, "config", "", "Path to the stake config file")
flags.AddTxFlagsToCmd(cmd)

return cmd
Expand Down
70 changes: 45 additions & 25 deletions x/gateway/client/cli/tx_stake_gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cli_test

import (
"fmt"
"os"
"testing"

sdkerrors "cosmossdk.io/errors"
Expand All @@ -14,6 +15,7 @@ import (
"google.golang.org/grpc/status"

"github.com/pokt-network/poktroll/testutil/network"
"github.com/pokt-network/poktroll/testutil/yaml"
"github.com/pokt-network/poktroll/x/gateway/client/cli"
"github.com/pokt-network/poktroll/x/gateway/types"
)
Expand All @@ -39,57 +41,71 @@ func TestCLI_StakeGateway(t *testing.T) {
}

tests := []struct {
desc string
address string
stake string
err *sdkerrors.Error
desc string
address string
inputConfig string
expectedError *sdkerrors.Error
}{
{
desc: "stake gateway: invalid address",
address: "invalid",
stake: "1000upokt",
err: types.ErrGatewayInvalidAddress,
inputConfig: `
stake_amount: 1000upokt
`,
expectedError: types.ErrGatewayInvalidAddress,
},
{
desc: "stake gateway: missing address",
// address: gatewayAccount.Address.String(),
stake: "1000upokt",
err: types.ErrGatewayInvalidAddress,
inputConfig: `
stake_amount: 1000upokt
`,
expectedError: types.ErrGatewayInvalidAddress,
},
{
desc: "stake gateway: invalid stake amount (zero)",
address: gatewayAccount.Address.String(),
stake: "0upokt",
err: types.ErrGatewayInvalidStake,
inputConfig: `
stake_amount: 0upokt
`,
expectedError: types.ErrGatewayInvalidStake,
},
{
desc: "stake gateway: invalid stake amount (negative)",
address: gatewayAccount.Address.String(),
stake: "-1000upokt",
err: types.ErrGatewayInvalidStake,
inputConfig: `
stake_amount: -1000upokt
`,
expectedError: types.ErrGatewayInvalidStake,
},
{
desc: "stake gateway: invalid stake denom",
address: gatewayAccount.Address.String(),
stake: "1000invalid",
err: types.ErrGatewayInvalidStake,
inputConfig: `
stake_amount: 1000invalid
`,
expectedError: types.ErrGatewayInvalidStake,
},
{
desc: "stake gateway: invalid stake missing denom",
address: gatewayAccount.Address.String(),
stake: "1000",
err: types.ErrGatewayInvalidStake,
inputConfig: `
stake_amount: 1000
`,
expectedError: types.ErrGatewayInvalidStake,
},
{
desc: "stake gateway: invalid stake missing stake",
address: gatewayAccount.Address.String(),
// stake: "1000upokt",
err: types.ErrGatewayInvalidStake,
desc: "stake gateway: invalid stake missing stake",
address: gatewayAccount.Address.String(),
inputConfig: ``,
expectedError: types.ErrGatewayInvalidStake,
},
{
desc: "stake gateway: valid",
address: gatewayAccount.Address.String(),
stake: "1000upokt",
inputConfig: `
stake_amount: 1000upokt
`,
},
}

Expand All @@ -102,19 +118,23 @@ func TestCLI_StakeGateway(t *testing.T) {
// Wait for a new block to be committed
require.NoError(t, net.WaitForNextBlock())

// write the stake config to a file
configPath := testutil.WriteToNewTempFile(t, yaml.NormalizeYAMLIndentation(tt.inputConfig)).Name()
t.Cleanup(func() { os.Remove(configPath) })

// Prepare the arguments for the CLI command
args := []string{
tt.stake,
fmt.Sprintf("--config=%s", configPath),
fmt.Sprintf("--%s=%s", flags.FlagFrom, tt.address),
}
args = append(args, commonArgs...)

// Execute the command
outStake, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdStakeGateway(), args)
if tt.err != nil {
stat, ok := status.FromError(tt.err)
if tt.expectedError != nil {
stat, ok := status.FromError(tt.expectedError)
require.True(t, ok)
require.Contains(t, stat.Message(), tt.err.Error())
require.Contains(t, stat.Message(), tt.expectedError.Error())
return
}
require.NoError(t, err)
Expand Down
10 changes: 10 additions & 0 deletions x/gateway/client/config/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package config

import sdkerrors "cosmossdk.io/errors"

var (
codespace = "gatewayconfig"
ErrGatewayConfigEmptyContent = sdkerrors.Register(codespace, 1, "empty gateway staking config content")
ErrGatewayConfigUnmarshalYAML = sdkerrors.Register(codespace, 2, "config reader cannot unmarshal yaml content")
ErrGatewayConfigInvalidStake = sdkerrors.Register(codespace, 3, "invalid stake in gateway stake config")
)
62 changes: 62 additions & 0 deletions x/gateway/client/config/gateway_config_reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package config

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"gopkg.in/yaml.v2"
)

// YAMLStakeGateway is the structure describing the gateway stake config file
type YAMLStakeGateway struct {
StakeAmount string `yaml:"stake_amount"`
}

// GatewayStakeConfig is the structure describing the gateway stake config
type GatewayStakeConfig struct {
StakeAmount sdk.Coin
}

// ParseGatewayConfig parses the gateway stake yaml config file into a StakeGatewayConfig struct
func ParseGatewayConfig(configContent []byte) (*GatewayStakeConfig, error) {
var stakeConfig *YAMLStakeGateway

if len(configContent) == 0 {
return nil, ErrGatewayConfigEmptyContent
}

// Unmarshal the stake config file into a stakeConfig
if err := yaml.Unmarshal(configContent, &stakeConfig); err != nil {
return nil, ErrGatewayConfigUnmarshalYAML.Wrap(err.Error())
}

// Validate the stake config
if len(stakeConfig.StakeAmount) == 0 {
return nil, ErrGatewayConfigInvalidStake
}

// Parse the stake amount to a coin struct
stakeAmount, err := sdk.ParseCoinNormalized(stakeConfig.StakeAmount)
if err != nil {
return nil, ErrGatewayConfigInvalidStake.Wrap(err.Error())
}

// Basic validation of the stake amount
if err := stakeAmount.Validate(); err != nil {
return nil, ErrGatewayConfigInvalidStake.Wrap(err.Error())
}

if stakeAmount.IsZero() {
return nil, ErrGatewayConfigInvalidStake.Wrap("stake amount cannot be zero")
}

// Only allow upokt coins staking
if stakeAmount.Denom != "upokt" {
return nil, ErrGatewayConfigInvalidStake.Wrapf(
"invalid stake denom, expecting: upokt, got: %s",
stakeAmount.Denom,
)
}

return &GatewayStakeConfig{
StakeAmount: stakeAmount,
}, nil
}
Loading

0 comments on commit 4dcb43f

Please sign in to comment.