-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
reconcile: stake supplier CLI commands
- Loading branch information
1 parent
8ca9b82
commit 2168765
Showing
2 changed files
with
364 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package supplier | ||
|
||
import ( | ||
"os" | ||
"strconv" | ||
|
||
"github.com/cosmos/cosmos-sdk/client" | ||
"github.com/cosmos/cosmos-sdk/client/flags" | ||
"github.com/cosmos/cosmos-sdk/client/tx" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/pokt-network/poktroll/x/supplier/config" | ||
"github.com/pokt-network/poktroll/x/supplier/types" | ||
) | ||
|
||
var ( | ||
flagStakeConfig string | ||
_ = strconv.Itoa(0) // Part of the default ignite imports | ||
) | ||
|
||
func CmdStakeSupplier() *cobra.Command { | ||
// fromAddress & signature is retrieved via `flags.FlagFrom` in the `clientCtx` | ||
cmd := &cobra.Command{ | ||
Use: "stake-supplier --config <config_file.yaml>", | ||
Short: "Stake a supplier", | ||
Long: `Stake an supplier with the provided parameters. This is a broadcast operation that | ||
will stake the tokens and associate them with the supplier specified by the 'from' address. | ||
Example: | ||
$ poktrolld --home=$(POKTROLLD_HOME) tx supplier stake-supplier --config stake_config.yaml --keyring-backend test --from $(APP) --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 | ||
} | ||
|
||
supplierStakeConfigs, err := config.ParseSupplierConfigs(configContent) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
clientCtx, err := client.GetClientTxContext(cmd) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
msg := types.NewMsgStakeSupplier( | ||
clientCtx.GetFromAddress().String(), | ||
supplierStakeConfigs.StakeAmount, | ||
supplierStakeConfigs.Services, | ||
) | ||
|
||
if err := msg.ValidateBasic(); err != nil { | ||
return err | ||
} | ||
|
||
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) | ||
}, | ||
} | ||
|
||
cmd.Flags().StringVar(&flagStakeConfig, "config", "", "Path to the stake config file") | ||
flags.AddTxFlagsToCmd(cmd) | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,297 @@ | ||
package supplier_test | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
sdkerrors "cosmossdk.io/errors" | ||
sdkmath "cosmossdk.io/math" | ||
"github.com/cosmos/cosmos-sdk/client/flags" | ||
"github.com/cosmos/cosmos-sdk/testutil" | ||
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/stretchr/testify/require" | ||
"google.golang.org/grpc/status" | ||
|
||
"github.com/pokt-network/poktroll/testutil/network" | ||
"github.com/pokt-network/poktroll/testutil/yaml" | ||
supplier "github.com/pokt-network/poktroll/x/supplier/module" | ||
"github.com/pokt-network/poktroll/x/supplier/types" | ||
) | ||
|
||
func TestCLI_StakeSupplier(t *testing.T) { | ||
net, _ := networkWithSupplierObjects(t, 2) | ||
val := net.Validators[0] | ||
ctx := val.ClientCtx | ||
|
||
// Create a keyring and add an account for the supplier to be staked | ||
kr := ctx.Keyring | ||
accounts := testutil.CreateKeyringAccounts(t, kr, 1) | ||
supplierAccount := accounts[0] | ||
|
||
// Update the context with the new keyring | ||
ctx = ctx.WithKeyring(kr) | ||
|
||
// Common args used for all requests | ||
commonArgs := []string{ | ||
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), | ||
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync), | ||
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(net.Config.BondDenom, sdkmath.NewInt(10))).String()), | ||
} | ||
|
||
defaultConfig := ` | ||
stake_amount: 1000upokt | ||
services: | ||
- service_id: svc1 | ||
endpoints: | ||
- url: http://pokt.network:8081 | ||
rpc_type: json_rpc | ||
` | ||
|
||
tests := []struct { | ||
desc string | ||
address string | ||
config string | ||
err *sdkerrors.Error | ||
}{ | ||
// Happy Paths | ||
{ | ||
desc: "stake supplier: valid", | ||
address: supplierAccount.Address.String(), | ||
config: defaultConfig, | ||
}, | ||
|
||
// Error Paths - Address Related | ||
{ | ||
desc: "stake supplier: missing address", | ||
// address: "explicitly missing", | ||
err: types.ErrSupplierInvalidAddress, | ||
config: defaultConfig, | ||
}, | ||
{ | ||
desc: "stake supplier: invalid address", | ||
address: "invalid", | ||
err: types.ErrSupplierInvalidAddress, | ||
config: defaultConfig, | ||
}, | ||
|
||
// Error Paths - Stake Related | ||
{ | ||
desc: "stake supplier: missing stake", | ||
address: supplierAccount.Address.String(), | ||
err: types.ErrSupplierInvalidStake, | ||
config: ` | ||
# explicitly omitted stake | ||
services: | ||
- service_id: svc1 | ||
endpoints: | ||
- url: http://pokt.network:8081 | ||
rpc_type: json_rpc | ||
`, | ||
}, | ||
{ | ||
desc: "stake supplier: invalid stake denom", | ||
address: supplierAccount.Address.String(), | ||
err: types.ErrSupplierInvalidStake, | ||
config: ` | ||
stake_amount: 1000invalid | ||
services: | ||
- service_id: svc1 | ||
endpoints: | ||
- url: http://pokt.network:8081 | ||
rpc_type: json_rpc | ||
`, | ||
}, | ||
{ | ||
desc: "stake supplier: invalid stake amount (zero)", | ||
address: supplierAccount.Address.String(), | ||
err: types.ErrSupplierInvalidStake, | ||
config: ` | ||
stake_amount: 0upokt | ||
services: | ||
- service_id: svc1 | ||
endpoints: | ||
- url: http://pokt.network:8081 | ||
rpc_type: json_rpc | ||
`, | ||
}, | ||
{ | ||
desc: "stake supplier: invalid stake amount (negative)", | ||
address: supplierAccount.Address.String(), | ||
err: types.ErrSupplierInvalidStake, | ||
config: ` | ||
stake_amount: -1000upokt | ||
services: | ||
- service_id: svc1 | ||
endpoints: | ||
- url: http://pokt.network:8081 | ||
rpc_type: json_rpc | ||
`, | ||
}, | ||
|
||
// Happy Paths - Service Related | ||
{ | ||
desc: "services_test: valid multiple services", | ||
address: supplierAccount.Address.String(), | ||
config: ` | ||
stake_amount: 1000upokt | ||
services: | ||
- service_id: svc1 | ||
endpoints: | ||
- url: http://pokt.network:8081 | ||
rpc_type: json_rpc | ||
- service_id: svc2 | ||
endpoints: | ||
- url: http://pokt.network:8082 | ||
rpc_type: json_rpc | ||
`, | ||
}, | ||
{ | ||
desc: "services_test: valid localhost", | ||
address: supplierAccount.Address.String(), | ||
config: ` | ||
stake_amount: 1000upokt | ||
services: | ||
- service_id: svc1 | ||
endpoints: | ||
- url: http://127.0.0.1:8082 | ||
rpc_type: json_rpc | ||
`, | ||
}, | ||
{ | ||
desc: "services_test: valid loopback", | ||
address: supplierAccount.Address.String(), | ||
config: ` | ||
stake_amount: 1000upokt | ||
services: | ||
- service_id: svc1 | ||
endpoints: | ||
- url: http://localhost:8082 | ||
rpc_type: json_rpc | ||
`, | ||
}, | ||
{ | ||
desc: "services_test: valid without a pork", | ||
address: supplierAccount.Address.String(), | ||
config: ` | ||
stake_amount: 1000upokt | ||
services: | ||
- service_id: svc1 | ||
endpoints: | ||
- url: http://pokt.network | ||
rpc_type: json_rpc | ||
`, | ||
}, | ||
|
||
// Error Paths - Service Related | ||
{ | ||
desc: "services_test: invalid services (missing argument)", | ||
address: supplierAccount.Address.String(), | ||
err: types.ErrSupplierInvalidServiceConfig, | ||
// servicesString: "explicitly omitted", | ||
config: ` | ||
stake_amount: 1000upokt | ||
`, | ||
}, | ||
{ | ||
desc: "services_test: invalid services (empty string)", | ||
address: supplierAccount.Address.String(), | ||
err: types.ErrSupplierInvalidServiceConfig, | ||
config: ` | ||
stake_amount: 1000upokt | ||
services: | ||
`, | ||
}, | ||
{ | ||
desc: "services_test: invalid URL", | ||
address: supplierAccount.Address.String(), | ||
err: types.ErrSupplierInvalidServiceConfig, | ||
config: ` | ||
stake_amount: 1000upokt | ||
services: | ||
- service_id: svc1 | ||
endpoints: | ||
- url: bad_url | ||
rpc_type: json_rpc | ||
`, | ||
}, | ||
{ | ||
desc: "services_test: missing URLs", | ||
address: supplierAccount.Address.String(), | ||
err: types.ErrSupplierInvalidServiceConfig, | ||
config: ` | ||
stake_amount: 1000upokt | ||
services: | ||
- service_id: svc1 | ||
- service_id: svc2 | ||
`, | ||
}, | ||
{ | ||
desc: "services_test: missing service IDs", | ||
address: supplierAccount.Address.String(), | ||
err: types.ErrSupplierInvalidServiceConfig, | ||
config: ` | ||
stake_amount: 1000upokt | ||
services: | ||
- endpoints: | ||
- url: localhost:8081 | ||
rpc_type: json_rpc | ||
- endpoints: | ||
- url: localhost:8082 | ||
rpc_type: json_rpc | ||
`, | ||
}, | ||
{ | ||
desc: "services_test: missing rpc type", | ||
address: supplierAccount.Address.String(), | ||
err: types.ErrSupplierInvalidServiceConfig, | ||
config: ` | ||
stake_amount: 1000upokt | ||
services: | ||
- service_id: svc1 | ||
endpoints: | ||
- url: localhost:8082 | ||
`, | ||
}, | ||
} | ||
|
||
// Initialize the Supplier Account by sending it some funds from the validator account that is part of genesis | ||
network.InitAccount(t, net, supplierAccount.Address) | ||
|
||
// Run the tests | ||
for _, tt := range tests { | ||
t.Run(tt.desc, func(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.config)).Name() | ||
|
||
// Prepare the arguments for the CLI command | ||
args := []string{ | ||
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, supplier.CmdStakeSupplier(), args) | ||
|
||
// Validate the error if one is expected | ||
if tt.err != nil { | ||
stat, ok := status.FromError(tt.err) | ||
require.True(t, ok) | ||
require.Contains(t, stat.Message(), tt.err.Error()) | ||
return | ||
} | ||
require.NoError(t, err) | ||
|
||
// Check the response | ||
var resp sdk.TxResponse | ||
require.NoError(t, net.Config.Codec.UnmarshalJSON(outStake.Bytes(), &resp)) | ||
require.NotNil(t, resp) | ||
require.NotNil(t, resp.TxHash) | ||
require.Equal(t, uint32(0), resp.Code) | ||
}) | ||
} | ||
} |