From dfc83e399575ab960b43b4b860d12ca0a5050a34 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:12:10 +0100 Subject: [PATCH 01/22] feat: update proto messages to hold stake --- proto/pocket/gateway/gateway.proto | 6 +++++- proto/pocket/gateway/tx.proto | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/proto/pocket/gateway/gateway.proto b/proto/pocket/gateway/gateway.proto index 168f5e909..f2a450cb3 100644 --- a/proto/pocket/gateway/gateway.proto +++ b/proto/pocket/gateway/gateway.proto @@ -3,7 +3,11 @@ package pocket.gateway; option go_package = "pocket/x/gateway/types"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; + message Gateway { - string address = 1; + string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // The Bech32 address of the gateway + cosmos.base.v1beta1.Coin stake = 2; // The total amount of uPOKT the gateway has staked } diff --git a/proto/pocket/gateway/tx.proto b/proto/pocket/gateway/tx.proto index b6ba4616f..faacafe3a 100644 --- a/proto/pocket/gateway/tx.proto +++ b/proto/pocket/gateway/tx.proto @@ -4,13 +4,17 @@ package pocket.gateway; option go_package = "pocket/x/gateway/types"; +import "cosmos_proto/cosmos.proto"; +import "cosmos/base/v1beta1/coin.proto"; + // Msg defines the Msg service. service Msg { rpc StakeGateway (MsgStakeGateway ) returns (MsgStakeGatewayResponse ); rpc UnstakeGateway (MsgUnstakeGateway) returns (MsgUnstakeGatewayResponse); } message MsgStakeGateway { - string address = 1; + string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // The Bech32 address of the gateway + cosmos.base.v1beta1.Coin stake = 2; // The total amount of uPOKT the gateway is staking. Must be ≥ to the current amount that the gateway has staked (if any) } message MsgStakeGatewayResponse {} From a15e1f78bcd198603a4748d07fa43fc31bc51fa5 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:14:01 +0100 Subject: [PATCH 02/22] feat: update stake message creation --- x/gateway/client/cli/tx_stake_gateway.go | 16 +++++++++----- x/gateway/types/message_stake_gateway.go | 27 +++++++++++++++++++++--- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/x/gateway/client/cli/tx_stake_gateway.go b/x/gateway/client/cli/tx_stake_gateway.go index 8a21c5129..739491d32 100644 --- a/x/gateway/client/cli/tx_stake_gateway.go +++ b/x/gateway/client/cli/tx_stake_gateway.go @@ -3,29 +3,35 @@ package cli import ( "strconv" + "pocket/x/gateway/types" + "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" - "pocket/x/gateway/types" ) var _ = strconv.Itoa(0) func CmdStakeGateway() *cobra.Command { cmd := &cobra.Command{ - Use: "stake-gateway", + Use: "stake-gateway [amount]", Short: "Broadcast message stake-gateway", - Args: cobra.ExactArgs(0), + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) (err error) { - clientCtx, err := client.GetClientTxContext(cmd) if err != nil { return err } - + stakeAmountString := args[0] + stakeAmount, err := sdk.ParseCoinNormalized(stakeAmountString) + if err != nil { + return err + } msg := types.NewMsgStakeGateway( clientCtx.GetFromAddress().String(), + stakeAmount, ) if err := msg.ValidateBasic(); err != nil { return err diff --git a/x/gateway/types/message_stake_gateway.go b/x/gateway/types/message_stake_gateway.go index bb64360f9..9fca33a90 100644 --- a/x/gateway/types/message_stake_gateway.go +++ b/x/gateway/types/message_stake_gateway.go @@ -1,17 +1,19 @@ package types import ( + "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + types "github.com/cosmos/cosmos-sdk/types" ) const TypeMsgStakeGateway = "stake_gateway" var _ sdk.Msg = &MsgStakeGateway{} -func NewMsgStakeGateway(address string) *MsgStakeGateway { +func NewMsgStakeGateway(address string, stakeAmount types.Coin) *MsgStakeGateway { return &MsgStakeGateway{ Address: address, + Stake: &stakeAmount, } } @@ -37,9 +39,28 @@ func (msg *MsgStakeGateway) GetSignBytes() []byte { } func (msg *MsgStakeGateway) ValidateBasic() error { + // Validate the address _, err := sdk.AccAddressFromBech32(msg.Address) if err != nil { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid address address (%s)", err) + return errors.Wrapf(ErrGatewayInvalidAddress, "invalid gateway address %s; (%v)", msg.Address, err) + } + + // Validate the stake amount + if msg.Stake == nil { + return errors.Wrapf(ErrGatewayInvalidStake, "nil gateway stake; (%v)", err) + } + stakeAmount, err := sdk.ParseCoinNormalized(msg.Stake.String()) + if !stakeAmount.IsValid() { + return errors.Wrapf(ErrGatewayInvalidStake, "invalid gateway stake %v; (%v)", msg.Stake, stakeAmount.Validate()) + } + if err != nil { + return errors.Wrapf(ErrGatewayInvalidStake, "cannot parse gateway stake %v; (%v)", msg.Stake, err) + } + if stakeAmount.IsZero() || stakeAmount.IsNegative() { + return errors.Wrapf(ErrGatewayInvalidStake, "invalid stake amount for gateway: %v <= 0", msg.Stake) + } + if stakeAmount.Denom != "upokt" { + return errors.Wrapf(ErrGatewayInvalidStake, "invalid stake amount denom for gateway %v", msg.Stake) } return nil } From 507e8b6982a314c2d2a40a8b5ebe920b1569b9e1 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:39:31 +0100 Subject: [PATCH 03/22] chore: add mockgen and test helper targets --- Makefile | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f170d1802..1555e52ee 100644 --- a/Makefile +++ b/Makefile @@ -102,6 +102,13 @@ localnet_regenesis: ## Regenerate the localnet genesis file go_test: go_version_check ## Run all go tests go test -v ./... +.PHONY: go_mockgen +go_mockgen: ## Use `mockgen` to generate mocks used for testing purposes of all the modules. + go generate ./x/gateway/types/ + +.PHONY: go_develop +go_develop: proto_regen go_mockgen go_test ## Generate protos, mocks and run all tests + ############# ### TODOS ### ############# @@ -157,4 +164,4 @@ todo_count: ## Print a count of all the TODOs in the project .PHONY: todo_this_commit todo_this_commit: ## List all the TODOs needed to be done in this commit - grep --exclude-dir={.git,vendor,prototype,.vscode} --exclude=Makefile -r -e "TODO_IN_THIS_COMMIT" -e "DISCUSS_IN_THIS_COMMIT" \ No newline at end of file + grep --exclude-dir={.git,vendor,prototype,.vscode} --exclude=Makefile -r -e "TODO_IN_THIS_COMMIT" -e "DISCUSS_IN_THIS_COMMIT" From c53f4a531ec2cfd2672e178d9708d89eacec4357 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:40:40 +0100 Subject: [PATCH 04/22] feat: add genesis state validation --- x/gateway/types/genesis.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/x/gateway/types/genesis.go b/x/gateway/types/genesis.go index e06a6c5cb..7594f0c08 100644 --- a/x/gateway/types/genesis.go +++ b/x/gateway/types/genesis.go @@ -2,6 +2,8 @@ package types import ( "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" ) // DefaultIndex is the default global index @@ -19,15 +21,32 @@ func DefaultGenesis() *GenesisState { // Validate performs basic genesis state validation returning an error upon any // failure. func (gs GenesisState) Validate() error { - // Check for duplicated index in gateway gatewayIndexMap := make(map[string]struct{}) for _, elem := range gs.GatewayList { + // Check for duplicated index in gateway index := string(GatewayKey(elem.Address)) if _, ok := gatewayIndexMap[index]; ok { return fmt.Errorf("duplicated index for gateway") } gatewayIndexMap[index] = struct{}{} + // Validate the stake of each gateway + if elem.Stake == nil { + return fmt.Errorf("nil stake amount for gateway") + } + stakeAmount, err := sdk.ParseCoinNormalized(elem.Stake.String()) + if !stakeAmount.IsValid() { + return fmt.Errorf("invalid stake amount for gateway %v; (%v)", elem.Stake, stakeAmount.Validate()) + } + if err != nil { + return fmt.Errorf("cannot parse stake amount for gateway %v; (%v)", elem.Stake, err) + } + if stakeAmount.IsZero() || stakeAmount.IsNegative() { + return fmt.Errorf("invalid stake amount for gateway: %v <= 0", elem.Stake) + } + if stakeAmount.Denom != "upokt" { + return fmt.Errorf("invalid stake amount denom for gateway %v", elem.Stake) + } } // this line is used by starport scaffolding # genesis/types/validate From f1aa6ef187d6b2bcd336b033cc8bb38f4968c656 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:41:01 +0100 Subject: [PATCH 05/22] feat: add custom error types --- x/gateway/types/errors.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x/gateway/types/errors.go b/x/gateway/types/errors.go index 32dfef208..4d9a312d1 100644 --- a/x/gateway/types/errors.go +++ b/x/gateway/types/errors.go @@ -8,5 +8,7 @@ import ( // x/gateway module sentinel errors var ( - ErrSample = sdkerrors.Register(ModuleName, 1100, "sample error") + ErrSample = sdkerrors.Register(ModuleName, 1100, "sample error") + ErrGatewayInvalidAddress = sdkerrors.Register(ModuleName, 1101, "invalid gateway address") + ErrGatewayInvalidStake = sdkerrors.Register(ModuleName, 1102, "invalid gateway stake") ) From 12137df091b7ed4f5c69e76c6db022ced592c18d Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:41:18 +0100 Subject: [PATCH 06/22] feat: add expected keepers for the bank module --- x/gateway/types/expected_keepers.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x/gateway/types/expected_keepers.go b/x/gateway/types/expected_keepers.go index 6aa6e9778..257c7db5c 100644 --- a/x/gateway/types/expected_keepers.go +++ b/x/gateway/types/expected_keepers.go @@ -1,5 +1,7 @@ package types +//go:generate mockgen -destination ../../../testutil/gateway/mocks/expected_keepers_mock.go -package mocks . AccountKeeper,BankKeeper + import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -13,6 +15,5 @@ type AccountKeeper interface { // BankKeeper defines the expected interface needed to retrieve account balances. type BankKeeper interface { - SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - // Methods imported from bank should be defined here + DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error } From 85f14dbd54dbc116433093b19150fad9e3d94c61 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:41:33 +0100 Subject: [PATCH 07/22] feat: update tests --- go.mod | 6 +- .../gateway/mocks/expected_keepers_mock.go | 87 +++++++++++++ testutil/gateway/mocks/mocks.go | 3 + testutil/keeper/gateway.go | 14 ++- x/gateway/client/cli/helpers_test.go | 42 +++++++ x/gateway/client/cli/query_gateway_test.go | 18 --- x/gateway/client/cli/tx_stake_gateway_test.go | 115 ++++++++++++++++++ x/gateway/types/genesis_test.go | 74 +++++++++-- x/gateway/types/message_stake_gateway_test.go | 43 ++++++- 9 files changed, 366 insertions(+), 36 deletions(-) create mode 100644 testutil/gateway/mocks/expected_keepers_mock.go create mode 100644 testutil/gateway/mocks/mocks.go create mode 100644 x/gateway/client/cli/helpers_test.go create mode 100644 x/gateway/client/cli/tx_stake_gateway_test.go diff --git a/go.mod b/go.mod index 6867f8051..f5715875f 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ go 1.19 require ( cosmossdk.io/api v0.3.1 + cosmossdk.io/errors v1.0.0-beta.7 github.com/cometbft/cometbft v0.37.2 github.com/cometbft/cometbft-db v0.8.0 - github.com/cosmos/cosmos-proto v1.0.0-beta.2 github.com/cosmos/cosmos-sdk v0.47.3 github.com/cosmos/gogoproto v1.4.10 github.com/cosmos/ibc-go/v7 v7.1.0 @@ -19,7 +19,6 @@ require ( github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.4 - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 google.golang.org/grpc v1.56.1 gopkg.in/yaml.v2 v2.4.0 ) @@ -32,7 +31,6 @@ require ( cloud.google.com/go/storage v1.29.0 // indirect cosmossdk.io/core v0.5.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.3 // indirect - cosmossdk.io/errors v1.0.0-beta.7 // indirect cosmossdk.io/log v1.1.1-0.20230704160919-88f2c830b0ca // indirect cosmossdk.io/math v1.0.1 // indirect cosmossdk.io/tools/rosetta v0.2.1 // indirect @@ -64,6 +62,7 @@ require ( github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v0.20.0 // indirect @@ -259,6 +258,7 @@ require ( gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/api v0.122.0 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/testutil/gateway/mocks/expected_keepers_mock.go b/testutil/gateway/mocks/expected_keepers_mock.go new file mode 100644 index 000000000..2e5887ea0 --- /dev/null +++ b/testutil/gateway/mocks/expected_keepers_mock.go @@ -0,0 +1,87 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: pocket/x/gateway/types (interfaces: AccountKeeper,BankKeeper) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + types "github.com/cosmos/cosmos-sdk/types" + types0 "github.com/cosmos/cosmos-sdk/x/auth/types" + gomock "github.com/golang/mock/gomock" +) + +// MockAccountKeeper is a mock of AccountKeeper interface. +type MockAccountKeeper struct { + ctrl *gomock.Controller + recorder *MockAccountKeeperMockRecorder +} + +// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper. +type MockAccountKeeperMockRecorder struct { + mock *MockAccountKeeper +} + +// NewMockAccountKeeper creates a new mock instance. +func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper { + mock := &MockAccountKeeper{ctrl: ctrl} + mock.recorder = &MockAccountKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder { + return m.recorder +} + +// GetAccount mocks base method. +func (m *MockAccountKeeper) GetAccount(arg0 types.Context, arg1 types.AccAddress) types0.AccountI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccount", arg0, arg1) + ret0, _ := ret[0].(types0.AccountI) + return ret0 +} + +// GetAccount indicates an expected call of GetAccount. +func (mr *MockAccountKeeperMockRecorder) GetAccount(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), arg0, arg1) +} + +// MockBankKeeper is a mock of BankKeeper interface. +type MockBankKeeper struct { + ctrl *gomock.Controller + recorder *MockBankKeeperMockRecorder +} + +// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper. +type MockBankKeeperMockRecorder struct { + mock *MockBankKeeper +} + +// NewMockBankKeeper creates a new mock instance. +func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper { + mock := &MockBankKeeper{ctrl: ctrl} + mock.recorder = &MockBankKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { + return m.recorder +} + +// DelegateCoinsFromAccountToModule mocks base method. +func (m *MockBankKeeper) DelegateCoinsFromAccountToModule(arg0 types.Context, arg1 types.AccAddress, arg2 string, arg3 types.Coins) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DelegateCoinsFromAccountToModule", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(error) + return ret0 +} + +// DelegateCoinsFromAccountToModule indicates an expected call of DelegateCoinsFromAccountToModule. +func (mr *MockBankKeeperMockRecorder) DelegateCoinsFromAccountToModule(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DelegateCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).DelegateCoinsFromAccountToModule), arg0, arg1, arg2, arg3) +} diff --git a/testutil/gateway/mocks/mocks.go b/testutil/gateway/mocks/mocks.go new file mode 100644 index 000000000..16355b5a1 --- /dev/null +++ b/testutil/gateway/mocks/mocks.go @@ -0,0 +1,3 @@ +package mocks + +// This file is in place to declare the package for dynamically generated mocks diff --git a/testutil/keeper/gateway.go b/testutil/keeper/gateway.go index 8447a7bf2..65f3da440 100644 --- a/testutil/keeper/gateway.go +++ b/testutil/keeper/gateway.go @@ -3,6 +3,11 @@ package keeper import ( "testing" + "pocket/x/gateway/keeper" + "pocket/x/gateway/types" + + mocks "pocket/testutil/gateway/mocks" + tmdb "github.com/cometbft/cometbft-db" "github.com/cometbft/cometbft/libs/log" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" @@ -12,9 +17,8 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" typesparams "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" - "pocket/x/gateway/keeper" - "pocket/x/gateway/types" ) func GatewayKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { @@ -30,6 +34,10 @@ func GatewayKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { registry := codectypes.NewInterfaceRegistry() cdc := codec.NewProtoCodec(registry) + ctrl := gomock.NewController(t) + mockBankKeeper := mocks.NewMockBankKeeper(ctrl) + mockBankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), gomock.Any(), types.ModuleName, gomock.Any()).AnyTimes() + paramsSubspace := typesparams.NewSubspace(cdc, types.Amino, storeKey, @@ -41,7 +49,7 @@ func GatewayKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { storeKey, memStoreKey, paramsSubspace, - nil, + mockBankKeeper, nil, ) diff --git a/x/gateway/client/cli/helpers_test.go b/x/gateway/client/cli/helpers_test.go new file mode 100644 index 000000000..ab6543370 --- /dev/null +++ b/x/gateway/client/cli/helpers_test.go @@ -0,0 +1,42 @@ +package cli_test + +import ( + "strconv" + "testing" + + "pocket/testutil/network" + "pocket/testutil/nullify" + "pocket/x/gateway/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/stretchr/testify/require" +) + +// networkWithGatewayObjects creates a network with a populated gateway state of n gateway objects +func networkWithGatewayObjects(t *testing.T, n int) (*network.Network, []types.Gateway) { + t.Helper() + cfg := network.DefaultConfig() + state := gatewayModuleGenesis(t, n) + buf, err := cfg.Codec.MarshalJSON(state) + require.NoError(t, err) + cfg.GenesisState[types.ModuleName] = buf + return network.New(t, cfg), state.GatewayList +} + +// gatewayModuleGenesis generates a default genesis state for the gateway module and then +// populates it with n gateway objects +func gatewayModuleGenesis(t *testing.T, n int) *types.GenesisState { + t.Helper() + state := types.DefaultGenesis() + for i := 0; i < n; i++ { + stake := sdk.NewCoin("upokt", sdk.NewInt(int64(i))) + gateway := types.Gateway{ + Address: strconv.Itoa(i), + Stake: &stake, + } + nullify.Fill(&gateway) + state.GatewayList = append(state.GatewayList, gateway) + } + return state +} diff --git a/x/gateway/client/cli/query_gateway_test.go b/x/gateway/client/cli/query_gateway_test.go index db9a9691c..37e0ec3b3 100644 --- a/x/gateway/client/cli/query_gateway_test.go +++ b/x/gateway/client/cli/query_gateway_test.go @@ -12,7 +12,6 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "pocket/testutil/network" "pocket/testutil/nullify" "pocket/x/gateway/client/cli" "pocket/x/gateway/types" @@ -21,23 +20,6 @@ import ( // Prevent strconv unused error var _ = strconv.IntSize -func networkWithGatewayObjects(t *testing.T, n int) (*network.Network, []types.Gateway) { - t.Helper() - cfg := network.DefaultConfig() - state := types.GenesisState{} - for i := 0; i < n; i++ { - gateway := types.Gateway{ - Address: strconv.Itoa(i), - } - nullify.Fill(&gateway) - state.GatewayList = append(state.GatewayList, gateway) - } - buf, err := cfg.Codec.MarshalJSON(&state) - require.NoError(t, err) - cfg.GenesisState[types.ModuleName] = buf - return network.New(t, cfg), state.GatewayList -} - func TestShowGateway(t *testing.T) { net, objs := networkWithGatewayObjects(t, 2) diff --git a/x/gateway/client/cli/tx_stake_gateway_test.go b/x/gateway/client/cli/tx_stake_gateway_test.go new file mode 100644 index 000000000..3575649ff --- /dev/null +++ b/x/gateway/client/cli/tx_stake_gateway_test.go @@ -0,0 +1,115 @@ +package cli_test + +import ( + "fmt" + "testing" + + "pocket/x/gateway/client/cli" + "pocket/x/gateway/types" + + errorsmod "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" +) + +func TestCLI_StakeGateway(t *testing.T) { + net, _ := networkWithGatewayObjects(t, 2) + val := net.Validators[0] + ctx := val.ClientCtx + + // Create a keyring and add an account for the gateway to be staked + kr := ctx.Keyring + accounts := testutil.CreateKeyringAccounts(t, kr, 1) + gatewayAccount := 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()), + } + + tests := []struct { + desc string + address string + stakeAmount string + err *errorsmod.Error + }{ + { + desc: "stake gateway: invalid address", + address: "invalid", + stakeAmount: "1000upokt", + err: types.ErrGatewayInvalidAddress, + }, + { + desc: "stake gateway: invalid stake amount (zero)", + address: gatewayAccount.Address.String(), + stakeAmount: "0upokt", + err: types.ErrGatewayInvalidStake, + }, + { + desc: "stake gateway: invalid stake amount (negative)", + address: gatewayAccount.Address.String(), + stakeAmount: "-1000upokt", + err: types.ErrGatewayInvalidStake, + }, + { + desc: "stake gateway: invalid stake denom", + address: gatewayAccount.Address.String(), + stakeAmount: "1000invalid", + err: types.ErrGatewayInvalidStake, + }, + { + desc: "stake gateway: valid", + address: gatewayAccount.Address.String(), + stakeAmount: "1000upokt", + }, + } + + // Initialize the Gateway Account by sending it some funds from the validator account that is part of genesis + sendArgs := []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + } + sendArgs = append(sendArgs, commonArgs...) + amount := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(200))) + _, err := clitestutil.MsgSendExec(ctx, net.Validators[0].Address, gatewayAccount.Address, amount, sendArgs...) + require.NoError(t, err) + + // Stake 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()) + + // Prepare the arguments for the CLI command + args := []string{ + tt.stakeAmount, + 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) + require.True(t, ok) + require.Contains(t, stat.Message(), tt.err.Error()) + return + } + + require.NoError(t, err) + var resp sdk.TxResponse + require.NoError(t, net.Config.Codec.UnmarshalJSON(outStake.Bytes(), &resp)) + require.NotNil(t, resp) + fmt.Println(resp) + }) + } +} diff --git a/x/gateway/types/genesis_test.go b/x/gateway/types/genesis_test.go index 3227a7cb0..b620904ef 100644 --- a/x/gateway/types/genesis_test.go +++ b/x/gateway/types/genesis_test.go @@ -3,11 +3,20 @@ package types_test import ( "testing" - "github.com/stretchr/testify/require" + "pocket/testutil/sample" "pocket/x/gateway/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" ) func TestGenesisState_Validate(t *testing.T) { + addr1 := sample.AccAddress() + stake1 := sdk.NewCoin("upokt", sdk.NewInt(100)) + + addr2 := sample.AccAddress() + stake2 := sdk.NewCoin("upokt", sdk.NewInt(100)) + tests := []struct { desc string genState *types.GenesisState @@ -21,13 +30,14 @@ func TestGenesisState_Validate(t *testing.T) { { desc: "valid genesis state", genState: &types.GenesisState{ - GatewayList: []types.Gateway{ { - Address: "0", + Address: addr1, + Stake: &stake1, }, { - Address: "1", + Address: addr2, + Stake: &stake2, }, }, // this line is used by starport scaffolding # types/genesis/validField @@ -35,14 +45,64 @@ func TestGenesisState_Validate(t *testing.T) { valid: true, }, { - desc: "duplicated gateway", + desc: "invalid - duplicated gateway address", + genState: &types.GenesisState{ + GatewayList: []types.Gateway{ + { + Address: addr1, + Stake: &stake1, + }, + { + Address: addr1, + Stake: &stake2, + }, + }, + }, + valid: false, + }, + { + desc: "invalid - nil gateway stake", + genState: &types.GenesisState{ + GatewayList: []types.Gateway{ + { + Address: addr1, + Stake: &stake1, + }, + { + Address: addr2, + Stake: nil, + }, + }, + }, + valid: false, + }, + { + desc: "invalid - missing gateway stake", + genState: &types.GenesisState{ + GatewayList: []types.Gateway{ + { + Address: addr1, + Stake: &stake1, + }, + { + Address: addr2, + // Stake: stake2, + }, + }, + }, + valid: false, + }, + { + desc: "invalid - negative gateway stake", genState: &types.GenesisState{ GatewayList: []types.Gateway{ { - Address: "0", + Address: addr1, + Stake: &stake1, }, { - Address: "0", + Address: addr2, + Stake: &sdk.Coin{Denom: "upokt", Amount: sdk.NewInt(-100)}, }, }, }, diff --git a/x/gateway/types/message_stake_gateway_test.go b/x/gateway/types/message_stake_gateway_test.go index a3852f041..3e3a4b927 100644 --- a/x/gateway/types/message_stake_gateway_test.go +++ b/x/gateway/types/message_stake_gateway_test.go @@ -3,30 +3,63 @@ package types import ( "testing" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/stretchr/testify/require" "pocket/testutil/sample" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" ) func TestMsgStakeGateway_ValidateBasic(t *testing.T) { + coins := sdk.NewCoin("upokt", sdk.NewInt(100)) tests := []struct { name string msg MsgStakeGateway err error }{ { - name: "invalid address", + name: "invalid address - no stake", msg: MsgStakeGateway{ Address: "invalid_address", + // Stake explicitly nil + }, + err: ErrGatewayInvalidAddress, + }, { + name: "valid address - nil stake", + msg: MsgStakeGateway{ + Address: sample.AccAddress(), + // Stake explicitly nil + }, + err: ErrGatewayInvalidStake, + }, { + name: "valid address - zero stake", + msg: MsgStakeGateway{ + Address: sample.AccAddress(), + Stake: &sdk.Coin{Denom: "upokt", Amount: sdk.NewInt(0)}, + }, + err: ErrGatewayInvalidStake, + }, { + name: "valid address - negative stake", + msg: MsgStakeGateway{ + Address: sample.AccAddress(), + Stake: &sdk.Coin{Denom: "upokt", Amount: sdk.NewInt(-100)}, }, - err: sdkerrors.ErrInvalidAddress, + err: ErrGatewayInvalidStake, }, { - name: "valid address", + name: "valid address - invalid stake denom", msg: MsgStakeGateway{ Address: sample.AccAddress(), + Stake: &sdk.Coin{Denom: "invalid", Amount: sdk.NewInt(100)}, + }, + err: ErrGatewayInvalidStake, + }, { + name: "valid address - valid stake", + msg: MsgStakeGateway{ + Address: sample.AccAddress(), + Stake: &coins, }, }, } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.msg.ValidateBasic() From 5bc75b65d50f7c9a13662f41a95b4d4ab923e660 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:54:36 +0100 Subject: [PATCH 08/22] chore: update test cases for more coverage --- go.mod | 2 +- x/gateway/client/cli/tx_stake_gateway_test.go | 16 ++++++- x/gateway/types/genesis_test.go | 48 +++++++++++++++++++ x/gateway/types/message_stake_gateway_test.go | 7 +++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f5715875f..207e6c312 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.19 require ( cosmossdk.io/api v0.3.1 cosmossdk.io/errors v1.0.0-beta.7 + cosmossdk.io/math v1.0.1 github.com/cometbft/cometbft v0.37.2 github.com/cometbft/cometbft-db v0.8.0 github.com/cosmos/cosmos-sdk v0.47.3 @@ -32,7 +33,6 @@ require ( cosmossdk.io/core v0.5.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.3 // indirect cosmossdk.io/log v1.1.1-0.20230704160919-88f2c830b0ca // indirect - cosmossdk.io/math v1.0.1 // indirect cosmossdk.io/tools/rosetta v0.2.1 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect diff --git a/x/gateway/client/cli/tx_stake_gateway_test.go b/x/gateway/client/cli/tx_stake_gateway_test.go index 3575649ff..63173e60c 100644 --- a/x/gateway/client/cli/tx_stake_gateway_test.go +++ b/x/gateway/client/cli/tx_stake_gateway_test.go @@ -7,7 +7,7 @@ import ( "pocket/x/gateway/client/cli" "pocket/x/gateway/types" - errorsmod "cosmossdk.io/errors" + "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/testutil" @@ -41,7 +41,7 @@ func TestCLI_StakeGateway(t *testing.T) { desc string address string stakeAmount string - err *errorsmod.Error + err *errors.Error }{ { desc: "stake gateway: invalid address", @@ -67,6 +67,18 @@ func TestCLI_StakeGateway(t *testing.T) { stakeAmount: "1000invalid", err: types.ErrGatewayInvalidStake, }, + { + desc: "stake gateway: invalid stake missing denom", + address: gatewayAccount.Address.String(), + stakeAmount: "1000", + err: types.ErrGatewayInvalidStake, + }, + { + desc: "stake gateway: invalid stake missing stake", + address: gatewayAccount.Address.String(), + // stakeAmount: "1000upokt", + err: types.ErrGatewayInvalidStake, + }, { desc: "stake gateway: valid", address: gatewayAccount.Address.String(), diff --git a/x/gateway/types/genesis_test.go b/x/gateway/types/genesis_test.go index b620904ef..e08ce6f13 100644 --- a/x/gateway/types/genesis_test.go +++ b/x/gateway/types/genesis_test.go @@ -92,6 +92,22 @@ func TestGenesisState_Validate(t *testing.T) { }, valid: false, }, + { + desc: "invalid - zero gateway stake", + genState: &types.GenesisState{ + GatewayList: []types.Gateway{ + { + Address: addr1, + Stake: &stake1, + }, + { + Address: addr2, + Stake: &sdk.Coin{Denom: "upokt", Amount: sdk.NewInt(0)}, + }, + }, + }, + valid: false, + }, { desc: "invalid - negative gateway stake", genState: &types.GenesisState{ @@ -108,6 +124,38 @@ func TestGenesisState_Validate(t *testing.T) { }, valid: false, }, + { + desc: "invalid - wrong stake denom", + genState: &types.GenesisState{ + GatewayList: []types.Gateway{ + { + Address: addr1, + Stake: &stake1, + }, + { + Address: addr2, + Stake: &sdk.Coin{Denom: "invalid", Amount: sdk.NewInt(100)}, + }, + }, + }, + valid: false, + }, + { + desc: "invalid - missing denom", + genState: &types.GenesisState{ + GatewayList: []types.Gateway{ + { + Address: addr1, + Stake: &stake1, + }, + { + Address: addr2, + Stake: &sdk.Coin{Denom: "", Amount: sdk.NewInt(100)}, + }, + }, + }, + valid: false, + }, // this line is used by starport scaffolding # types/genesis/testcase } for _, tc := range tests { diff --git a/x/gateway/types/message_stake_gateway_test.go b/x/gateway/types/message_stake_gateway_test.go index 3e3a4b927..ae3659ade 100644 --- a/x/gateway/types/message_stake_gateway_test.go +++ b/x/gateway/types/message_stake_gateway_test.go @@ -51,6 +51,13 @@ func TestMsgStakeGateway_ValidateBasic(t *testing.T) { Stake: &sdk.Coin{Denom: "invalid", Amount: sdk.NewInt(100)}, }, err: ErrGatewayInvalidStake, + }, { + name: "valid address - invalid stake missing denom", + msg: MsgStakeGateway{ + Address: sample.AccAddress(), + Stake: &sdk.Coin{Denom: "", Amount: sdk.NewInt(100)}, + }, + err: ErrGatewayInvalidStake, }, { name: "valid address - valid stake", msg: MsgStakeGateway{ From 80186a2cd58f68a741ec693b97a4ac0489247031 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 14:10:43 +0100 Subject: [PATCH 09/22] feat: test gateway module address is deterministic --- cmd/pocketd/cmd/config.go | 2 +- cmd/pocketd/cmd/root.go | 3 ++- x/gateway/keeper/gateway_test.go | 17 +++++++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/cmd/pocketd/cmd/config.go b/cmd/pocketd/cmd/config.go index 858b65931..b79a38da4 100644 --- a/cmd/pocketd/cmd/config.go +++ b/cmd/pocketd/cmd/config.go @@ -6,7 +6,7 @@ import ( "pocket/app" ) -func initSDKConfig() { +func InitSDKConfig() { // Set prefixes accountPubKeyPrefix := app.AccountAddressPrefix + "pub" validatorAddressPrefix := app.AccountAddressPrefix + "valoper" diff --git a/cmd/pocketd/cmd/root.go b/cmd/pocketd/cmd/root.go index db4d6a0ab..b2e3dbff8 100644 --- a/cmd/pocketd/cmd/root.go +++ b/cmd/pocketd/cmd/root.go @@ -36,6 +36,7 @@ import ( "github.com/spf13/cast" "github.com/spf13/cobra" "github.com/spf13/pflag" + // this line is used by starport scaffolding # root/moduleImport "pocket/app" @@ -104,7 +105,7 @@ func initRootCmd( encodingConfig appparams.EncodingConfig, ) { // Set config - initSDKConfig() + InitSDKConfig() gentxModule := app.ModuleBasics[genutiltypes.ModuleName].(genutil.AppModuleBasic) rootCmd.AddCommand( diff --git a/x/gateway/keeper/gateway_test.go b/x/gateway/keeper/gateway_test.go index ff8c43a28..b61928b70 100644 --- a/x/gateway/keeper/gateway_test.go +++ b/x/gateway/keeper/gateway_test.go @@ -4,17 +4,24 @@ import ( "strconv" "testing" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" + "pocket/cmd/pocketd/cmd" keepertest "pocket/testutil/keeper" "pocket/testutil/nullify" "pocket/x/gateway/keeper" "pocket/x/gateway/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/stretchr/testify/require" ) // Prevent strconv unused error var _ = strconv.IntSize +func init() { + cmd.InitSDKConfig() +} + func createNGateway(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.Gateway { items := make([]types.Gateway, n) for i := range items { @@ -25,6 +32,11 @@ func createNGateway(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.Gatew return items } +func TestGatewayModuleAddress(t *testing.T) { + moduleAddress := authtypes.NewModuleAddress(types.ModuleName) + require.Equal(t, "pokt1f6j7u6875p2cvyrgjr0d2uecyzah0kget9vlpl", moduleAddress.String()) +} + func TestGatewayGet(t *testing.T) { keeper, ctx := keepertest.GatewayKeeper(t) items := createNGateway(keeper, ctx, 10) @@ -39,6 +51,7 @@ func TestGatewayGet(t *testing.T) { ) } } + func TestGatewayRemove(t *testing.T) { keeper, ctx := keepertest.GatewayKeeper(t) items := createNGateway(keeper, ctx, 10) From d3b81f77d586e477ce53028dd60b377b1caf6b3b Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 15:02:25 +0100 Subject: [PATCH 10/22] feat: hydrate keeper stake method --- x/gateway/keeper/msg_server_stake_gateway.go | 74 ++++++++++++++++++-- x/gateway/types/errors.go | 13 ++-- 2 files changed, 76 insertions(+), 11 deletions(-) diff --git a/x/gateway/keeper/msg_server_stake_gateway.go b/x/gateway/keeper/msg_server_stake_gateway.go index 33682cba1..6244cb5bb 100644 --- a/x/gateway/keeper/msg_server_stake_gateway.go +++ b/x/gateway/keeper/msg_server_stake_gateway.go @@ -3,15 +3,81 @@ package keeper import ( "context" - sdk "github.com/cosmos/cosmos-sdk/types" "pocket/x/gateway/types" + + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" ) -func (k msgServer) StakeGateway(goCtx context.Context, msg *types.MsgStakeGateway) (*types.MsgStakeGatewayResponse, error) { +func (k msgServer) StakeGateway( + goCtx context.Context, + msg *types.MsgStakeGateway, +) (*types.MsgStakeGatewayResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - // TODO: Handling the message - _ = ctx + logger := k.Logger(ctx).With("method", "StakeGateway") + logger.Info("About to stake gateway with msg: %v", msg) + + // Check if the gateway already exists or not + var err error + var coinsToDelegate sdk.Coin + gateway, isGatewayFound := k.GetGateway(ctx, msg.Address) + if !isGatewayFound { + logger.Info("Gateway not found. Creating new gateway for address %s", msg.Address) + gateway = k.createGateway(ctx, msg) + coinsToDelegate = *msg.Stake + } else { + logger.Info("Gateway found. Updating gateway stake for address %s", msg.Address) + currGatewayStake := *gateway.Stake + if err = k.updateGateway(ctx, &gateway, msg); err != nil { + return nil, err + } + coinsToDelegate = (*msg.Stake).Sub(currGatewayStake) + } + + // Retrieve the address of the gateway + gatewayAddress, err := sdk.AccAddressFromBech32(msg.Address) + if err != nil { + logger.Error("could not parse address %s", msg.Address) + return nil, err + } + + // Send the coins from the gateway to the staked gateway pool + err = k.bankKeeper.DelegateCoinsFromAccountToModule(ctx, gatewayAddress, types.ModuleName, []sdk.Coin{coinsToDelegate}) + if err != nil { + logger.Error("could not send %v coins from %s to %s module account due to %v", coinsToDelegate, gatewayAddress, types.ModuleName, err) + return nil, err + } + + // Update the Gateway in the store + k.SetGateway(ctx, gateway) + logger.Info("Successfully updated stake for gateway: %+v", gateway) return &types.MsgStakeGatewayResponse{}, nil } + +func (k msgServer) createGateway(ctx sdk.Context, msg *types.MsgStakeGateway) types.Gateway { + return types.Gateway{ + Address: msg.Address, + Stake: msg.Stake, + } +} + +func (k msgServer) updateGateway( + ctx sdk.Context, + gateway *types.Gateway, + msg *types.MsgStakeGateway, +) error { + // Checks if the the msg address is the same as the current owner + if msg.Address != gateway.Address { + return errors.Wrapf(types.ErrGatewayUnauthorized, "msg Address (%s) != gateway address (%s)", msg.Address, gateway.Address) + } + if msg.Stake == nil { + return errors.Wrapf(types.ErrGatewayInvalidStake, "stake amount cannot be nil") + } + if gateway.Stake.IsGTE(*msg.Stake) { + return errors.Wrapf(types.ErrGatewayInvalidStake, "stake amount %v must be higher than previous stake amount %v", msg.Stake, gateway.Stake) + } + gateway.Stake = msg.Stake + return nil +} diff --git a/x/gateway/types/errors.go b/x/gateway/types/errors.go index 4d9a312d1..91839e42d 100644 --- a/x/gateway/types/errors.go +++ b/x/gateway/types/errors.go @@ -1,14 +1,13 @@ package types -// DONTCOVER +import "cosmossdk.io/errors" -import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) +// DONTCOVER // x/gateway module sentinel errors var ( - ErrSample = sdkerrors.Register(ModuleName, 1100, "sample error") - ErrGatewayInvalidAddress = sdkerrors.Register(ModuleName, 1101, "invalid gateway address") - ErrGatewayInvalidStake = sdkerrors.Register(ModuleName, 1102, "invalid gateway stake") + ErrSample = errors.Register(ModuleName, 1100, "sample error") + ErrGatewayInvalidAddress = errors.Register(ModuleName, 1101, "invalid gateway address") + ErrGatewayInvalidStake = errors.Register(ModuleName, 1102, "invalid gateway stake") + ErrGatewayUnauthorized = errors.Register(ModuleName, 1103, "unauthorized signer") ) From d137e24c269e31982f03298f40e1efd3acedd861 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 15:02:41 +0100 Subject: [PATCH 11/22] feat: add unit tests for keeper stake methods --- .../keeper/msg_server_stake_gateway_test.go | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 x/gateway/keeper/msg_server_stake_gateway_test.go diff --git a/x/gateway/keeper/msg_server_stake_gateway_test.go b/x/gateway/keeper/msg_server_stake_gateway_test.go new file mode 100644 index 000000000..dfcb98e4a --- /dev/null +++ b/x/gateway/keeper/msg_server_stake_gateway_test.go @@ -0,0 +1,94 @@ +package keeper_test + +import ( + "testing" + + keepertest "pocket/testutil/keeper" + + "pocket/testutil/sample" + "pocket/x/gateway/keeper" + "pocket/x/gateway/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func TestMsgServer_StakeGateway_SuccessfulCreateAndUpdate(t *testing.T) { + k, ctx := keepertest.GatewayKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + wctx := sdk.WrapSDKContext(ctx) + + // Generate an address for the gateway + addr := sample.AccAddress() + + // Verify that the gateway does not exist yet + _, isGatewayFound := k.GetGateway(ctx, addr) + require.False(t, isGatewayFound) + + // Prepare the gateway + initialStake := sdk.NewCoin("upokt", sdk.NewInt(100)) + gateway := &types.MsgStakeGateway{ + Address: addr, + Stake: &initialStake, + } + + // Stake the gateway + _, err := srv.StakeGateway(wctx, gateway) + require.NoError(t, err) + + // Verify that the gateway exists + foundGateway, isGatewayFound := k.GetGateway(ctx, addr) + require.True(t, isGatewayFound) + require.Equal(t, addr, foundGateway.Address) + require.Equal(t, initialStake.Amount, foundGateway.Stake.Amount) + + // Prepare an updated gateway with a higher stake + updatedStake := sdk.NewCoin("upokt", sdk.NewInt(200)) + updatedGateway := &types.MsgStakeGateway{ + Address: addr, + Stake: &updatedStake, + } + + // Update the staked gateway + _, err = srv.StakeGateway(wctx, updatedGateway) + require.NoError(t, err) + foundGateway, isGatewayFound = k.GetGateway(ctx, addr) + require.True(t, isGatewayFound) + require.Equal(t, updatedStake.Amount, foundGateway.Stake.Amount) +} + +func TestMsgServer_StakeGateway_FailLoweringStake(t *testing.T) { + k, ctx := keepertest.GatewayKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + wctx := sdk.WrapSDKContext(ctx) + + // Prepare the gateway + addr := sample.AccAddress() + initialStake := sdk.NewCoin("upokt", sdk.NewInt(100)) + gateway := &types.MsgStakeGateway{ + Address: addr, + Stake: &initialStake, + } + + // Stake the gateway & verify that the gateway exists + _, err := srv.StakeGateway(wctx, gateway) + require.NoError(t, err) + _, isGatewayFound := k.GetGateway(ctx, addr) + require.True(t, isGatewayFound) + + // Prepare an updated gateway with a lower stake + updatedStake := sdk.NewCoin("upokt", sdk.NewInt(50)) + updatedGateway := &types.MsgStakeGateway{ + Address: addr, + Stake: &updatedStake, + } + + // Verify that it fails + _, err = srv.StakeGateway(wctx, updatedGateway) + require.Error(t, err) + + // Verify that the gateway stake is unchanged + gatewayFound, isGatewayFound := k.GetGateway(ctx, addr) + require.True(t, isGatewayFound) + require.Equal(t, initialStake.Amount, gatewayFound.Stake.Amount) +} From b627bec6cbb054f784b8a203b72fcb47d749371c Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 15:14:19 +0100 Subject: [PATCH 12/22] chore: update gitignore --- .gitignore | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index d2c22748a..fe560aac0 100644 --- a/.gitignore +++ b/.gitignore @@ -50,5 +50,8 @@ ts-client/ .tool-versions # Proto artifacts -*/*.pb.go -*/*.pb.gw.go +**/*.pb.go +**/*.pb.gw.go + +# Mock +**/*_mock.go From 503b8e9679d49f274e3c176f99744c6f08da9d23 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 15:14:33 +0100 Subject: [PATCH 13/22] feat: add gateway stake helpers --- Makefile | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1555e52ee..a21c2a5c2 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .SILENT: POCKETD_HOME := ./localnet/pocketd -POCKET_NODE = 127.0.0.1:36657 # The pocket rollup node (full node and sequencer in the localnet context) +POCKET_NODE := tcp://127.0.0.1:36657 # The pocket rollup node (full node and sequencer in the localnet context) ######################## ### Makefile Helpers ### @@ -165,3 +165,27 @@ todo_count: ## Print a count of all the TODOs in the project .PHONY: todo_this_commit todo_this_commit: ## List all the TODOs needed to be done in this commit grep --exclude-dir={.git,vendor,prototype,.vscode} --exclude=Makefile -r -e "TODO_IN_THIS_COMMIT" -e "DISCUSS_IN_THIS_COMMIT" + +#################### +### Gateways ### +#################### + +.PHONY: gateway_list +gateway_list: ## List all the staked gateways + pocketd --home=$(POCKETD_HOME) q gateway list-gateway --node $(POCKET_NODE) + +.PHONY: gateway_stake +gateway_stake: ## Stake tokens for the gateway specified (must specify the gateway env var) + pocketd --home=$(POCKETD_HOME) tx gateway stake-gateway 1000upokt --keyring-backend test --from $(gateway) --node $(POCKET_NODE) + +.PHONY: gateway1_stake +gateway1_stake: ## Stake gateway1 + gateway=gateway1 make gateway_stake + +.PHONY: gateway2_stake +gateway2_stake: ## Stake gateway2 + gateway=gateway2 make gateway_stake + +.PHONY: gateway3_stake +gateway3_stake: ## Stake gateway3 + gateway=gateway3 make gateway_stake From 35e740e2940117afc8f7761fdd933f498e78c908 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 15:14:47 +0100 Subject: [PATCH 14/22] feat: s/portal/gateway/ge --- config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.yml b/config.yml index a366b262d..153e5fff8 100644 --- a/config.yml +++ b/config.yml @@ -32,15 +32,15 @@ accounts: mnemonic: "client city senior tenant source soda spread buffalo shaft amused bar carbon keen off feel coral easily announce metal orphan sustain maple expand loop" coins: - 330000000upokt - - name: portal1 + - name: gateway1 mnemonic: "salt iron goat also absorb depend involve agent apology between lift shy door left bulb arrange industry father jelly olive rifle return predict into" coins: - 100000000upokt - - name: portal2 + - name: gateway2 mnemonic: "suffer wet jelly furnace cousin flip layer render finish frequent pledge feature economy wink like water disease final erase goat include apple state furnace" coins: - 200000000upokt - - name: portal3 + - name: gateway3 mnemonic: "elder spatial erosion soap athlete tide subject recipe also awkward head pattern cart version beach usual oxygen confirm erupt diamond maze smooth census garment" coins: - 300000000upokt From 8c69f7de697190279bdf0726ca3d20aa25bc6bb2 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 15:17:26 +0100 Subject: [PATCH 15/22] chore: ignite chain build --- docs/static/openapi.yml | 82 +++++++++++++++++++++++++++++++++++++++++ go.mod | 4 +- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/docs/static/openapi.yml b/docs/static/openapi.yml index 3668091ac..c5f803391 100644 --- a/docs/static/openapi.yml +++ b/docs/static/openapi.yml @@ -46650,6 +46650,23 @@ paths: properties: address: type: string + title: The Bech32 address of the gateway + stake: + title: The total amount of uPOKT the gateway has staked + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. pagination: type: object properties: @@ -46770,6 +46787,23 @@ paths: properties: address: type: string + title: The Bech32 address of the gateway + stake: + title: The total amount of uPOKT the gateway has staked + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the + custom method + + signatures required by gogoproto. default: description: An unexpected error response. schema: @@ -75943,6 +75977,20 @@ definitions: properties: address: type: string + title: The Bech32 address of the gateway + stake: + title: The total amount of uPOKT the gateway has staked + type: object + properties: + denom: + type: string + amount: + type: string + description: |- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. pocket.gateway.MsgStakeGatewayResponse: type: object pocket.gateway.MsgUnstakeGatewayResponse: @@ -75960,6 +76008,23 @@ definitions: properties: address: type: string + title: The Bech32 address of the gateway + stake: + title: The total amount of uPOKT the gateway has staked + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. pagination: type: object properties: @@ -75994,6 +76059,23 @@ definitions: properties: address: type: string + title: The Bech32 address of the gateway + stake: + title: The total amount of uPOKT the gateway has staked + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + + NOTE: The amount field is an Int which implements the custom + method + + signatures required by gogoproto. pocket.gateway.QueryParamsResponse: type: object properties: diff --git a/go.mod b/go.mod index 207e6c312..d514b191a 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( cosmossdk.io/math v1.0.1 github.com/cometbft/cometbft v0.37.2 github.com/cometbft/cometbft-db v0.8.0 + github.com/cosmos/cosmos-proto v1.0.0-beta.2 github.com/cosmos/cosmos-sdk v0.47.3 github.com/cosmos/gogoproto v1.4.10 github.com/cosmos/ibc-go/v7 v7.1.0 @@ -20,6 +21,7 @@ require ( github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.4 + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 google.golang.org/grpc v1.56.1 gopkg.in/yaml.v2 v2.4.0 ) @@ -62,7 +64,6 @@ require ( github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect - github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v0.20.0 // indirect @@ -258,7 +259,6 @@ require ( gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/api v0.122.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect From 6264d8f2859be2455785375ad1bb53cb1e3002b6 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 15:18:47 +0100 Subject: [PATCH 16/22] chore: remove mockgen file --- .../gateway/mocks/expected_keepers_mock.go | 87 ------------------- 1 file changed, 87 deletions(-) delete mode 100644 testutil/gateway/mocks/expected_keepers_mock.go diff --git a/testutil/gateway/mocks/expected_keepers_mock.go b/testutil/gateway/mocks/expected_keepers_mock.go deleted file mode 100644 index 2e5887ea0..000000000 --- a/testutil/gateway/mocks/expected_keepers_mock.go +++ /dev/null @@ -1,87 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: pocket/x/gateway/types (interfaces: AccountKeeper,BankKeeper) - -// Package mocks is a generated GoMock package. -package mocks - -import ( - reflect "reflect" - - types "github.com/cosmos/cosmos-sdk/types" - types0 "github.com/cosmos/cosmos-sdk/x/auth/types" - gomock "github.com/golang/mock/gomock" -) - -// MockAccountKeeper is a mock of AccountKeeper interface. -type MockAccountKeeper struct { - ctrl *gomock.Controller - recorder *MockAccountKeeperMockRecorder -} - -// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper. -type MockAccountKeeperMockRecorder struct { - mock *MockAccountKeeper -} - -// NewMockAccountKeeper creates a new mock instance. -func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper { - mock := &MockAccountKeeper{ctrl: ctrl} - mock.recorder = &MockAccountKeeperMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder { - return m.recorder -} - -// GetAccount mocks base method. -func (m *MockAccountKeeper) GetAccount(arg0 types.Context, arg1 types.AccAddress) types0.AccountI { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAccount", arg0, arg1) - ret0, _ := ret[0].(types0.AccountI) - return ret0 -} - -// GetAccount indicates an expected call of GetAccount. -func (mr *MockAccountKeeperMockRecorder) GetAccount(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).GetAccount), arg0, arg1) -} - -// MockBankKeeper is a mock of BankKeeper interface. -type MockBankKeeper struct { - ctrl *gomock.Controller - recorder *MockBankKeeperMockRecorder -} - -// MockBankKeeperMockRecorder is the mock recorder for MockBankKeeper. -type MockBankKeeperMockRecorder struct { - mock *MockBankKeeper -} - -// NewMockBankKeeper creates a new mock instance. -func NewMockBankKeeper(ctrl *gomock.Controller) *MockBankKeeper { - mock := &MockBankKeeper{ctrl: ctrl} - mock.recorder = &MockBankKeeperMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBankKeeper) EXPECT() *MockBankKeeperMockRecorder { - return m.recorder -} - -// DelegateCoinsFromAccountToModule mocks base method. -func (m *MockBankKeeper) DelegateCoinsFromAccountToModule(arg0 types.Context, arg1 types.AccAddress, arg2 string, arg3 types.Coins) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DelegateCoinsFromAccountToModule", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// DelegateCoinsFromAccountToModule indicates an expected call of DelegateCoinsFromAccountToModule. -func (mr *MockBankKeeperMockRecorder) DelegateCoinsFromAccountToModule(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DelegateCoinsFromAccountToModule", reflect.TypeOf((*MockBankKeeper)(nil).DelegateCoinsFromAccountToModule), arg0, arg1, arg2, arg3) -} From 0a642ff1c771c809635a15c738048af1f1d87aee Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Sun, 15 Oct 2023 15:37:15 +0100 Subject: [PATCH 17/22] chore: add mockgen step to CI --- .github/workflows/go.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 38f4f6283..8ea6cf87f 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -30,5 +30,8 @@ jobs: - name: Build run: ignite chain build --debug + - name: Mockgen + run: make go_mockgen + - name: Test run: go test -v ./... From c91d50af888714f895a4a18e3e15d04d85494903 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Mon, 16 Oct 2023 19:43:13 +0100 Subject: [PATCH 18/22] chore: go.mod --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index d514b191a..207e6c312 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( cosmossdk.io/math v1.0.1 github.com/cometbft/cometbft v0.37.2 github.com/cometbft/cometbft-db v0.8.0 - github.com/cosmos/cosmos-proto v1.0.0-beta.2 github.com/cosmos/cosmos-sdk v0.47.3 github.com/cosmos/gogoproto v1.4.10 github.com/cosmos/ibc-go/v7 v7.1.0 @@ -21,7 +20,6 @@ require ( github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.4 - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 google.golang.org/grpc v1.56.1 gopkg.in/yaml.v2 v2.4.0 ) @@ -64,6 +62,7 @@ require ( github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v0.20.0 // indirect @@ -259,6 +258,7 @@ require ( gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/api v0.122.0 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect From 0ad93f8844bb124fe9cb584b9a8cb87239346792 Mon Sep 17 00:00:00 2001 From: DK Date: Mon, 16 Oct 2023 17:49:16 -0700 Subject: [PATCH 19/22] install mockgen in CI --- .github/workflows/go.yml | 4 +++- Makefile | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 8ea6cf87f..28dc689f0 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -7,7 +7,6 @@ on: push: branches: ["main"] pull_request: - branches: ["main"] jobs: build: @@ -27,6 +26,9 @@ jobs: with: go-version: "1.20" + - name: Install CI dependencies + run: make install_ci_deps + - name: Build run: ignite chain build --debug diff --git a/Makefile b/Makefile index a21c2a5c2..f2d371be0 100644 --- a/Makefile +++ b/Makefile @@ -109,6 +109,10 @@ go_mockgen: ## Use `mockgen` to generate mocks used for testing purposes of all .PHONY: go_develop go_develop: proto_regen go_mockgen go_test ## Generate protos, mocks and run all tests +.PHONY: install_ci_deps +install_ci_deps: ## Installs the tools necessary for CI + go install "github.com/golang/mock/mockgen@v1.6.0" && mockgen --version + ############# ### TODOS ### ############# From c5788e3b7ab7e4485c26b3cbacd870e4d5bf64d9 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Wed, 18 Oct 2023 17:02:43 +0100 Subject: [PATCH 20/22] fixup: makefile --- Makefile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Makefile b/Makefile index 696ba5e98..cd03769a6 100644 --- a/Makefile +++ b/Makefile @@ -123,10 +123,6 @@ go_mockgen: ## Use `mockgen` to generate mocks used for testing purposes of all .PHONY: go_develop go_develop: proto_regen go_mockgen go_test ## Generate protos, mocks and run all tests -.PHONY: install_ci_deps -install_ci_deps: ## Installs the tools necessary for CI - go install "github.com/golang/mock/mockgen@v1.6.0" && mockgen --version - ############# ### TODOS ### ############# From db765fff1ed4f348abad122cda65fa047b9e05f9 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Wed, 18 Oct 2023 17:31:41 +0100 Subject: [PATCH 21/22] chore: address comments --- proto/pocket/gateway/tx.proto | 3 ++ testutil/network/network.go | 34 ++++++++++++------- x/application/client/cli/helpers_test.go | 6 +++- x/gateway/client/cli/helpers_test.go | 27 ++------------- x/gateway/client/cli/tx_stake_gateway.go | 14 +++++--- x/gateway/client/cli/tx_stake_gateway_test.go | 19 ++++++----- x/gateway/keeper/msg_server_stake_gateway.go | 2 +- x/gateway/types/errors.go | 7 ++-- x/gateway/types/genesis.go | 15 ++++---- x/gateway/types/message_stake_gateway.go | 4 +-- 10 files changed, 66 insertions(+), 65 deletions(-) diff --git a/proto/pocket/gateway/tx.proto b/proto/pocket/gateway/tx.proto index faacafe3a..0c568080c 100644 --- a/proto/pocket/gateway/tx.proto +++ b/proto/pocket/gateway/tx.proto @@ -4,6 +4,7 @@ package pocket.gateway; option go_package = "pocket/x/gateway/types"; +import "cosmos/msg/v1/msg.proto"; import "cosmos_proto/cosmos.proto"; import "cosmos/base/v1beta1/coin.proto"; @@ -13,6 +14,8 @@ service Msg { rpc UnstakeGateway (MsgUnstakeGateway) returns (MsgUnstakeGatewayResponse); } message MsgStakeGateway { + option (cosmos.msg.v1.signer) = "address"; // https://docs.cosmos.network/main/build/building-modules/messages-and-queries + string address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // The Bech32 address of the gateway cosmos.base.v1beta1.Coin stake = 2; // The total amount of uPOKT the gateway is staking. Must be ≥ to the current amount that the gateway has staked (if any) } diff --git a/testutil/network/network.go b/testutil/network/network.go index 8f9b66b73..1d8226ff7 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -2,6 +2,7 @@ package network import ( "fmt" + "strconv" "testing" "time" @@ -17,7 +18,6 @@ import ( clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" "github.com/cosmos/cosmos-sdk/testutil/network" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - cosmostypes "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/stretchr/testify/require" @@ -25,7 +25,8 @@ import ( "pocket/app" "pocket/testutil/nullify" "pocket/testutil/sample" - "pocket/x/application/types" + app_types "pocket/x/application/types" + gateway_types "pocket/x/gateway/types" ) type ( @@ -98,14 +99,14 @@ func DefaultConfig() network.Config { } } -// applicationModuleGenesis generates a GenesisState object with a given number of applications. +// DefaultApplicationModuleGenesisState generates a GenesisState object with a given number of applications. // It returns the populated GenesisState object. -func DefaultApplicationModuleGenesisState(t *testing.T, n int) *types.GenesisState { +func DefaultApplicationModuleGenesisState(t *testing.T, n int) *app_types.GenesisState { t.Helper() - state := types.DefaultGenesis() + state := app_types.DefaultGenesis() for i := 0; i < n; i++ { stake := sdk.NewCoin("upokt", sdk.NewInt(int64(i+1))) - application := types.Application{ + application := app_types.Application{ Address: sample.AccAddress(), Stake: &stake, } @@ -115,16 +116,25 @@ func DefaultApplicationModuleGenesisState(t *testing.T, n int) *types.GenesisSta return state } -// HydrateGenesisState adds a given module's GenesisState to the network's genesis state. -func HydrateGenesisState(t *testing.T, cfg *network.Config, moduleGenesisState *types.GenesisState, moduleName string) { +// DefaultGatewayModuleGenesisState generates a GenesisState object with a given number of gateways. +// It returns the populated GenesisState object. +func DefaultGatewayModuleGenesisState(t *testing.T, n int) *gateway_types.GenesisState { t.Helper() - buf, err := cfg.Codec.MarshalJSON(moduleGenesisState) - require.NoError(t, err) - cfg.GenesisState[moduleName] = buf + state := gateway_types.DefaultGenesis() + for i := 0; i < n; i++ { + stake := sdk.NewCoin("upokt", sdk.NewInt(int64(i))) + gateway := gateway_types.Gateway{ + Address: strconv.Itoa(i), + Stake: &stake, + } + nullify.Fill(&gateway) + state.GatewayList = append(state.GatewayList, gateway) + } + return state } // Initialize an Account by sending it some funds from the validator in the network to the address provided -func InitAccount(t *testing.T, net *Network, addr cosmostypes.AccAddress) { +func InitAccount(t *testing.T, net *Network, addr sdk.AccAddress) { t.Helper() val := net.Validators[0] ctx := val.ClientCtx diff --git a/x/application/client/cli/helpers_test.go b/x/application/client/cli/helpers_test.go index 8d2d90bb4..aa60d57ed 100644 --- a/x/application/client/cli/helpers_test.go +++ b/x/application/client/cli/helpers_test.go @@ -8,6 +8,8 @@ import ( "pocket/cmd/pocketd/cmd" "pocket/testutil/network" "pocket/x/application/types" + + "github.com/stretchr/testify/require" ) // Dummy variable to avoid unused import error. @@ -24,6 +26,8 @@ func networkWithApplicationObjects(t *testing.T, n int) (*network.Network, []typ t.Helper() cfg := network.DefaultConfig() appGenesisState := network.DefaultApplicationModuleGenesisState(t, n) - network.HydrateGenesisState(t, &cfg, appGenesisState, types.ModuleName) + buf, err := cfg.Codec.MarshalJSON(appGenesisState) + require.NoError(t, err) + cfg.GenesisState[types.ModuleName] = buf return network.New(t, cfg), appGenesisState.ApplicationList } diff --git a/x/gateway/client/cli/helpers_test.go b/x/gateway/client/cli/helpers_test.go index ab6543370..32f159e55 100644 --- a/x/gateway/client/cli/helpers_test.go +++ b/x/gateway/client/cli/helpers_test.go @@ -1,15 +1,11 @@ package cli_test import ( - "strconv" "testing" "pocket/testutil/network" - "pocket/testutil/nullify" "pocket/x/gateway/types" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/stretchr/testify/require" ) @@ -17,26 +13,9 @@ import ( func networkWithGatewayObjects(t *testing.T, n int) (*network.Network, []types.Gateway) { t.Helper() cfg := network.DefaultConfig() - state := gatewayModuleGenesis(t, n) - buf, err := cfg.Codec.MarshalJSON(state) + gatewayGenesisState := network.DefaultGatewayModuleGenesisState(t, n) + buf, err := cfg.Codec.MarshalJSON(gatewayGenesisState) require.NoError(t, err) cfg.GenesisState[types.ModuleName] = buf - return network.New(t, cfg), state.GatewayList -} - -// gatewayModuleGenesis generates a default genesis state for the gateway module and then -// populates it with n gateway objects -func gatewayModuleGenesis(t *testing.T, n int) *types.GenesisState { - t.Helper() - state := types.DefaultGenesis() - for i := 0; i < n; i++ { - stake := sdk.NewCoin("upokt", sdk.NewInt(int64(i))) - gateway := types.Gateway{ - Address: strconv.Itoa(i), - Stake: &stake, - } - nullify.Fill(&gateway) - state.GatewayList = append(state.GatewayList, gateway) - } - return state + return network.New(t, cfg), gatewayGenesisState.GatewayList } diff --git a/x/gateway/client/cli/tx_stake_gateway.go b/x/gateway/client/cli/tx_stake_gateway.go index 739491d32..29ee3873b 100644 --- a/x/gateway/client/cli/tx_stake_gateway.go +++ b/x/gateway/client/cli/tx_stake_gateway.go @@ -17,21 +17,25 @@ var _ = strconv.Itoa(0) func CmdStakeGateway() *cobra.Command { cmd := &cobra.Command{ Use: "stake-gateway [amount]", - Short: "Broadcast message stake-gateway", - Args: cobra.ExactArgs(1), + Short: "Stake an gateway", + Long: `Stake an 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: +$ pocketd --home=$(POCKETD_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) if err != nil { return err } - stakeAmountString := args[0] - stakeAmount, err := sdk.ParseCoinNormalized(stakeAmountString) + stakeString := args[0] + stake, err := sdk.ParseCoinNormalized(stakeString) if err != nil { return err } msg := types.NewMsgStakeGateway( clientCtx.GetFromAddress().String(), - stakeAmount, + stake, ) if err := msg.ValidateBasic(); err != nil { return err diff --git a/x/gateway/client/cli/tx_stake_gateway_test.go b/x/gateway/client/cli/tx_stake_gateway_test.go index 63173e60c..110e7f661 100644 --- a/x/gateway/client/cli/tx_stake_gateway_test.go +++ b/x/gateway/client/cli/tx_stake_gateway_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "pocket/testutil/network" "pocket/x/gateway/client/cli" "pocket/x/gateway/types" @@ -49,6 +50,12 @@ func TestCLI_StakeGateway(t *testing.T) { stakeAmount: "1000upokt", err: types.ErrGatewayInvalidAddress, }, + { + desc: "stake gateway: missing address", + // address: gatewayAccount.Address.String(), + stakeAmount: "1000upokt", + err: types.ErrGatewayInvalidAddress, + }, { desc: "stake gateway: invalid stake amount (zero)", address: gatewayAccount.Address.String(), @@ -87,13 +94,7 @@ func TestCLI_StakeGateway(t *testing.T) { } // Initialize the Gateway Account by sending it some funds from the validator account that is part of genesis - sendArgs := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), - } - sendArgs = append(sendArgs, commonArgs...) - amount := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(200))) - _, err := clitestutil.MsgSendExec(ctx, net.Validators[0].Address, gatewayAccount.Address, amount, sendArgs...) - require.NoError(t, err) + network.InitAccount(t, net, gatewayAccount.Address) // Stake the tests for _, tt := range tests { @@ -116,12 +117,14 @@ func TestCLI_StakeGateway(t *testing.T) { require.Contains(t, stat.Message(), tt.err.Error()) return } + require.NoError(t, err) require.NoError(t, err) var resp sdk.TxResponse require.NoError(t, net.Config.Codec.UnmarshalJSON(outStake.Bytes(), &resp)) require.NotNil(t, resp) - fmt.Println(resp) + require.NotNil(t, resp.TxHash) + require.Equal(t, uint32(0), resp.Code) }) } } diff --git a/x/gateway/keeper/msg_server_stake_gateway.go b/x/gateway/keeper/msg_server_stake_gateway.go index 6244cb5bb..fc5e6f639 100644 --- a/x/gateway/keeper/msg_server_stake_gateway.go +++ b/x/gateway/keeper/msg_server_stake_gateway.go @@ -75,7 +75,7 @@ func (k msgServer) updateGateway( if msg.Stake == nil { return errors.Wrapf(types.ErrGatewayInvalidStake, "stake amount cannot be nil") } - if gateway.Stake.IsGTE(*msg.Stake) { + if msg.Stake.IsLTE(*gateway.Stake) { return errors.Wrapf(types.ErrGatewayInvalidStake, "stake amount %v must be higher than previous stake amount %v", msg.Stake, gateway.Stake) } gateway.Stake = msg.Stake diff --git a/x/gateway/types/errors.go b/x/gateway/types/errors.go index 91839e42d..a52003b29 100644 --- a/x/gateway/types/errors.go +++ b/x/gateway/types/errors.go @@ -6,8 +6,7 @@ import "cosmossdk.io/errors" // x/gateway module sentinel errors var ( - ErrSample = errors.Register(ModuleName, 1100, "sample error") - ErrGatewayInvalidAddress = errors.Register(ModuleName, 1101, "invalid gateway address") - ErrGatewayInvalidStake = errors.Register(ModuleName, 1102, "invalid gateway stake") - ErrGatewayUnauthorized = errors.Register(ModuleName, 1103, "unauthorized signer") + ErrGatewayInvalidAddress = errors.Register(ModuleName, 1, "invalid gateway address") + ErrGatewayInvalidStake = errors.Register(ModuleName, 2, "invalid gateway stake") + ErrGatewayUnauthorized = errors.Register(ModuleName, 3, "unauthorized signer") ) diff --git a/x/gateway/types/genesis.go b/x/gateway/types/genesis.go index 7594f0c08..e4595b667 100644 --- a/x/gateway/types/genesis.go +++ b/x/gateway/types/genesis.go @@ -1,8 +1,7 @@ package types import ( - "fmt" - + "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -27,25 +26,25 @@ func (gs GenesisState) Validate() error { // Check for duplicated index in gateway index := string(GatewayKey(elem.Address)) if _, ok := gatewayIndexMap[index]; ok { - return fmt.Errorf("duplicated index for gateway") + return errors.Wrap(ErrGatewayInvalidAddress, "duplicated index for gateway") } gatewayIndexMap[index] = struct{}{} // Validate the stake of each gateway if elem.Stake == nil { - return fmt.Errorf("nil stake amount for gateway") + return errors.Wrap(ErrGatewayInvalidStake, "nil stake amount for gateway") } stakeAmount, err := sdk.ParseCoinNormalized(elem.Stake.String()) if !stakeAmount.IsValid() { - return fmt.Errorf("invalid stake amount for gateway %v; (%v)", elem.Stake, stakeAmount.Validate()) + return errors.Wrapf(ErrGatewayInvalidStake, "invalid stake amount for gateway %v; (%v)", elem.Stake, stakeAmount.Validate()) } if err != nil { - return fmt.Errorf("cannot parse stake amount for gateway %v; (%v)", elem.Stake, err) + return errors.Wrapf(ErrGatewayInvalidStake, "cannot parse stake amount for gateway %v; (%v)", elem.Stake, err) } if stakeAmount.IsZero() || stakeAmount.IsNegative() { - return fmt.Errorf("invalid stake amount for gateway: %v <= 0", elem.Stake) + return errors.Wrapf(ErrGatewayInvalidStake, "invalid stake amount for gateway: %v <= 0", elem.Stake) } if stakeAmount.Denom != "upokt" { - return fmt.Errorf("invalid stake amount denom for gateway %v", elem.Stake) + return errors.Wrapf(ErrGatewayInvalidStake, "invalid stake amount denom for gateway %v", elem.Stake) } } // this line is used by starport scaffolding # genesis/types/validate diff --git a/x/gateway/types/message_stake_gateway.go b/x/gateway/types/message_stake_gateway.go index 9fca33a90..4e0ef5d3c 100644 --- a/x/gateway/types/message_stake_gateway.go +++ b/x/gateway/types/message_stake_gateway.go @@ -10,10 +10,10 @@ const TypeMsgStakeGateway = "stake_gateway" var _ sdk.Msg = &MsgStakeGateway{} -func NewMsgStakeGateway(address string, stakeAmount types.Coin) *MsgStakeGateway { +func NewMsgStakeGateway(address string, stake types.Coin) *MsgStakeGateway { return &MsgStakeGateway{ Address: address, - Stake: &stakeAmount, + Stake: &stake, } } From 6c7caa893a406b531e317480a53deaaae50782e0 Mon Sep 17 00:00:00 2001 From: harry <53987565+h5law@users.noreply.github.com> Date: Wed, 18 Oct 2023 17:56:28 +0100 Subject: [PATCH 22/22] chore: add back ignite scaffold line --- cmd/pocketd/cmd/root.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/pocketd/cmd/root.go b/cmd/pocketd/cmd/root.go index fc84c4f81..59c45dcce 100644 --- a/cmd/pocketd/cmd/root.go +++ b/cmd/pocketd/cmd/root.go @@ -39,6 +39,8 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" + // this line is used by starport scaffolding # root/moduleImport + "pocket/app" appparams "pocket/app/params" )