diff --git a/x/supplier/keeper/msg_server_stake_supplier.go b/x/supplier/keeper/msg_server_stake_supplier.go index f750b99ff..3ed4add08 100644 --- a/x/supplier/keeper/msg_server_stake_supplier.go +++ b/x/supplier/keeper/msg_server_stake_supplier.go @@ -96,7 +96,8 @@ func (k msgServer) StakeSupplier(ctx context.Context, msg *types.MsgStakeSupplie } coinsToEscrow, err = (*msg.Stake).SafeSub(currSupplierStake) if err != nil { - return nil, err + logger.Info(fmt.Sprintf("ERROR: %s", err)) + return nil, status.Error(codes.Internal, err.Error()) } logger.Info(fmt.Sprintf("Supplier is going to escrow an additional %+v coins", coinsToEscrow)) @@ -104,13 +105,24 @@ func (k msgServer) StakeSupplier(ctx context.Context, msg *types.MsgStakeSupplie supplier.UnstakeSessionEndHeight = sharedtypes.SupplierNotUnstaking } - // Must always stake or upstake (> 0 delta) + // MUST ALWAYS stake or upstake (> 0 delta) if coinsToEscrow.IsZero() { err = types.ErrSupplierInvalidStake.Wrapf("Signer %q must escrow more than 0 additional coins", msg.Signer) logger.Info(fmt.Sprintf("WARN: %s", err)) return nil, status.Error(codes.InvalidArgument, err.Error()) } + // MUST ALWAYS have at least minimum stake. + minStake := k.GetParams(ctx).MinStake + if msg.Stake.Amount.LT(minStake.Amount) { + err = types.ErrSupplierInvalidStake.Wrapf( + "supplier with owner %q must stake at least %s", + msg.GetOwnerAddress(), minStake, + ) + logger.Info(fmt.Sprintf("ERROR: %s", err)) + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + // Retrieve the account address of the message signer msgSignerAddress, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { diff --git a/x/supplier/keeper/msg_server_stake_supplier_test.go b/x/supplier/keeper/msg_server_stake_supplier_test.go index a4c1b5815..5682425fe 100644 --- a/x/supplier/keeper/msg_server_stake_supplier_test.go +++ b/x/supplier/keeper/msg_server_stake_supplier_test.go @@ -4,15 +4,17 @@ import ( "testing" "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" + cosmostypes "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/pokt-network/poktroll/app/volatile" keepertest "github.com/pokt-network/poktroll/testutil/keeper" "github.com/pokt-network/poktroll/testutil/sample" sharedtypes "github.com/pokt-network/poktroll/x/shared/types" "github.com/pokt-network/poktroll/x/supplier/keeper" - "github.com/pokt-network/poktroll/x/supplier/types" + suppliertypes "github.com/pokt-network/poktroll/x/supplier/types" ) func TestMsgServer_StakeSupplier_SuccessfulCreateAndUpdate(t *testing.T) { @@ -28,7 +30,7 @@ func TestMsgServer_StakeSupplier_SuccessfulCreateAndUpdate(t *testing.T) { require.False(t, isSupplierFound) // Prepare the stakeMsg - stakeMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 100, "svcId") + stakeMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 1000000, "svcId") // Stake the supplier _, err := srv.StakeSupplier(ctx, stakeMsg) @@ -38,14 +40,14 @@ func TestMsgServer_StakeSupplier_SuccessfulCreateAndUpdate(t *testing.T) { foundSupplier, isSupplierFound := supplierModuleKeepers.GetSupplier(ctx, operatorAddr) require.True(t, isSupplierFound) require.Equal(t, operatorAddr, foundSupplier.OperatorAddress) - require.Equal(t, int64(100), foundSupplier.Stake.Amount.Int64()) + require.Equal(t, int64(1000000), foundSupplier.Stake.Amount.Int64()) require.Len(t, foundSupplier.Services, 1) require.Equal(t, "svcId", foundSupplier.Services[0].ServiceId) require.Len(t, foundSupplier.Services[0].Endpoints, 1) require.Equal(t, "http://localhost:8080", foundSupplier.Services[0].Endpoints[0].Url) // Prepare an updated supplier with a higher stake and a different URL for the service - updateMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 200, "svcId2") + updateMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 2000000, "svcId2") updateMsg.Services[0].Endpoints[0].Url = "http://localhost:8082" // Update the staked supplier @@ -54,7 +56,7 @@ func TestMsgServer_StakeSupplier_SuccessfulCreateAndUpdate(t *testing.T) { foundSupplier, isSupplierFound = supplierModuleKeepers.GetSupplier(ctx, operatorAddr) require.True(t, isSupplierFound) - require.Equal(t, int64(200), foundSupplier.Stake.Amount.Int64()) + require.Equal(t, int64(2000000), foundSupplier.Stake.Amount.Int64()) require.Len(t, foundSupplier.Services, 1) require.Equal(t, "svcId2", foundSupplier.Services[0].ServiceId) require.Len(t, foundSupplier.Services[0].Endpoints, 1) @@ -70,7 +72,7 @@ func TestMsgServer_StakeSupplier_FailRestakingDueToInvalidServices(t *testing.T) operatorAddr := sample.AccAddress() // Prepare the supplier stake message - stakeMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 100, "svcId") + stakeMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 1000000, "svcId") // Stake the supplier _, err := srv.StakeSupplier(ctx, stakeMsg) @@ -120,7 +122,7 @@ func TestMsgServer_StakeSupplier_FailLoweringStake(t *testing.T) { operatorAddr := sample.AccAddress() // Prepare the supplier stake message - stakeMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 100, "svcId") + stakeMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 1000000, "svcId") // Stake the supplier & verify that the supplier exists _, err := srv.StakeSupplier(ctx, stakeMsg) @@ -140,7 +142,7 @@ func TestMsgServer_StakeSupplier_FailLoweringStake(t *testing.T) { // Verify that the supplier stake is unchanged supplierFound, isSupplierFound := supplierModuleKeepers.GetSupplier(ctx, operatorAddr) require.True(t, isSupplierFound) - require.Equal(t, int64(100), supplierFound.Stake.Amount.Int64()) + require.Equal(t, int64(1000000), supplierFound.Stake.Amount.Int64()) require.Len(t, supplierFound.Services, 1) } @@ -153,11 +155,12 @@ func TestMsgServer_StakeSupplier_FailWithNonExistingService(t *testing.T) { operatorAddr := sample.AccAddress() // Prepare the supplier stake message with a non-existing service ID - stakeMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 100, "newService") + stakeMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 1000000, "newService") // Stake the supplier & verify that it fails because the service does not exist. _, err := srv.StakeSupplier(ctx, stakeMsg) - require.ErrorContains(t, err, types.ErrSupplierServiceNotFound.Wrapf( + require.Equal(t, codes.InvalidArgument, status.Code(err)) + require.ErrorContains(t, err, suppliertypes.ErrSupplierServiceNotFound.Wrapf( "service %q does not exist", "newService", ).Error()) } @@ -171,7 +174,7 @@ func TestMsgServer_StakeSupplier_OperatorAuthorizations(t *testing.T) { operatorAddr := sample.AccAddress() // Stake using the operator address as the signer and verify that it succeeds. - stakeMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 100, "svcId") + stakeMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 1000000, "svcId") setStakeMsgSigner(stakeMsg, operatorAddr) _, err := srv.StakeSupplier(ctx, stakeMsg) require.NoError(t, err) @@ -181,7 +184,7 @@ func TestMsgServer_StakeSupplier_OperatorAuthorizations(t *testing.T) { require.Equal(t, ownerAddr, supplier.OwnerAddress) // Update the supplier using the operator address as the signer and verify that it succeeds. - stakeMsgUpdateUrl := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 200, "svcId") + stakeMsgUpdateUrl := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 2000000, "svcId") operatorUpdatedServiceUrl := "http://localhost:8081" stakeMsgUpdateUrl.Services[0].Endpoints[0].Url = operatorUpdatedServiceUrl setStakeMsgSigner(stakeMsgUpdateUrl, operatorAddr) @@ -196,7 +199,7 @@ func TestMsgServer_StakeSupplier_OperatorAuthorizations(t *testing.T) { // Update the supplier URL by using the owner address as the singer and verify that it succeeds. ownerUpdaterServiceUrl := "http://localhost:8082" stakeMsgUpdateUrl.Services[0].Endpoints[0].Url = ownerUpdaterServiceUrl - stakeMsgUpdateUrl.Stake.Amount = math.NewInt(300) + stakeMsgUpdateUrl.Stake.Amount = math.NewInt(3000000) setStakeMsgSigner(stakeMsgUpdateUrl, ownerAddr) _, err = srv.StakeSupplier(ctx, stakeMsgUpdateUrl) require.NoError(t, err) @@ -209,7 +212,7 @@ func TestMsgServer_StakeSupplier_OperatorAuthorizations(t *testing.T) { // Try updating the supplier's operator address using the old operator as a signer // will create a new supplier. - stakeMsgUpdateOperator := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 300, "svcId") + stakeMsgUpdateOperator := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 3000000, "svcId") newOperatorAddress := sample.AccAddress() stakeMsgUpdateOperator.OperatorAddress = newOperatorAddress setStakeMsgSigner(stakeMsgUpdateOperator, operatorAddr) @@ -229,7 +232,7 @@ func TestMsgServer_StakeSupplier_OperatorAuthorizations(t *testing.T) { // will create a new supplier. newOperatorAddress = sample.AccAddress() stakeMsgUpdateOperator.OperatorAddress = newOperatorAddress - stakeMsgUpdateOperator.Stake.Amount = math.NewInt(400) + stakeMsgUpdateOperator.Stake.Amount = math.NewInt(4000000) setStakeMsgSigner(stakeMsgUpdateOperator, ownerAddr) _, err = srv.StakeSupplier(ctx, stakeMsgUpdateOperator) require.NoError(t, err) @@ -246,10 +249,11 @@ func TestMsgServer_StakeSupplier_OperatorAuthorizations(t *testing.T) { // Try updating the supplier's owner address using the operator as a signer // and verify that it fails. newOwnerAddress := sample.AccAddress() - stakeMsgUpdateOwner := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 500, "svcId") + stakeMsgUpdateOwner := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 5000000, "svcId") stakeMsgUpdateOwner.OwnerAddress = newOwnerAddress setStakeMsgSigner(stakeMsgUpdateOwner, operatorAddr) _, err = srv.StakeSupplier(ctx, stakeMsgUpdateOwner) + require.Equal(t, codes.InvalidArgument, status.Code(err)) require.ErrorContains(t, err, sharedtypes.ErrSharedUnauthorizedSupplierUpdate.Wrapf( "signer %q is not allowed to update the owner address %q", operatorAddr, ownerAddr, @@ -275,13 +279,13 @@ func TestMsgServer_StakeSupplier_ActiveSupplier(t *testing.T) { operatorAddr := sample.AccAddress() // Prepare the supplier - stakeMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 100, "svcId") + stakeMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 1000000, "svcId") // Stake the supplier & verify that the supplier exists. _, err := srv.StakeSupplier(ctx, stakeMsg) require.NoError(t, err) - sdkCtx := sdk.UnwrapSDKContext(ctx) + sdkCtx := cosmostypes.UnwrapSDKContext(ctx) currentHeight := sdkCtx.BlockHeight() sessionEndHeight := supplierModuleKeepers.SharedKeeper.GetSessionEndHeight(ctx, currentHeight) @@ -304,7 +308,7 @@ func TestMsgServer_StakeSupplier_ActiveSupplier(t *testing.T) { ctx = keepertest.SetBlockHeight(ctx, sessionEndHeight+1) // Prepare the supplier stake message with a different service - updateMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 200, "svcId", "svcId2") + updateMsg := stakeSupplierForServicesMsg(ownerAddr, operatorAddr, 2000000, "svcId", "svcId2") updateMsg.Signer = operatorAddr // Update the staked supplier @@ -339,7 +343,7 @@ func stakeSupplierForServicesMsg( ownerAddr, operatorAddr string, amount int64, serviceIds ...string, -) *types.MsgStakeSupplier { +) *suppliertypes.MsgStakeSupplier { services := make([]*sharedtypes.SupplierServiceConfig, 0, len(serviceIds)) for _, serviceId := range serviceIds { services = append(services, &sharedtypes.SupplierServiceConfig{ @@ -348,7 +352,7 @@ func stakeSupplierForServicesMsg( { Url: "http://localhost:8080", RpcType: sharedtypes.RPCType_JSON_RPC, - Configs: make([]*sharedtypes.ConfigOption, 0), + Configs: nil, }, }, RevShare: []*sharedtypes.ServiceRevenueShare{ @@ -360,20 +364,82 @@ func stakeSupplierForServicesMsg( }) } - return &types.MsgStakeSupplier{ + return &suppliertypes.MsgStakeSupplier{ Signer: ownerAddr, OwnerAddress: ownerAddr, OperatorAddress: operatorAddr, - Stake: &sdk.Coin{Denom: volatile.DenomuPOKT, Amount: math.NewInt(amount)}, + Stake: &cosmostypes.Coin{Denom: volatile.DenomuPOKT, Amount: math.NewInt(amount)}, Services: services, } } // setStakeMsgSigner sets the signer of the given MsgStakeSupplier to the given address func setStakeMsgSigner( - msg *types.MsgStakeSupplier, + msg *suppliertypes.MsgStakeSupplier, signer string, -) *types.MsgStakeSupplier { +) *suppliertypes.MsgStakeSupplier { msg.Signer = signer return msg } + +func TestMsgServer_StakeSupplier_FailBelowMinStake(t *testing.T) { + k, ctx := keepertest.SupplierKeeper(t) + srv := keeper.NewMsgServerImpl(*k.Keeper) + + addr := sample.AccAddress() + supplierStake := cosmostypes.NewInt64Coin(volatile.DenomuPOKT, 100) + minStake := supplierStake.AddAmount(math.NewInt(1)) + expectedErr := suppliertypes.ErrSupplierInvalidStake.Wrapf("supplier with owner %q must stake at least %s", addr, minStake) + + // Set the minimum stake to be greater than the supplier stake. + params := k.Keeper.GetParams(ctx) + params.MinStake = &minStake + err := k.SetParams(ctx, params) + require.NoError(t, err) + + // Prepare the supplier stake message. + stakeMsg := stakeSupplierForServicesMsg(addr, addr, 100, "svcId") + + // Attempt to stake the supplier & verify that the supplier does NOT exist. + _, err = srv.StakeSupplier(ctx, stakeMsg) + require.ErrorContains(t, err, expectedErr.Error()) + _, isSupplierFound := k.GetSupplier(ctx, addr) + require.False(t, isSupplierFound) +} + +func TestMsgServer_StakeSupplier_UpStakeFromBelowMinStake(t *testing.T) { + k, ctx := keepertest.SupplierKeeper(t) + srv := keeper.NewMsgServerImpl(*k.Keeper) + + addr := sample.AccAddress() + supplierParams := k.Keeper.GetParams(ctx) + minStake := supplierParams.GetMinStake() + belowMinStake := minStake.AddAmount(math.NewInt(-1)) + aboveMinStake := minStake.AddAmount(math.NewInt(1)) + + stakeMsg := stakeSupplierForServicesMsg(addr, addr, aboveMinStake.Amount.Int64(), "svcId") + + // Stake (via keeper methods) a supplier with stake below min. stake. + initialSupplier := sharedtypes.Supplier{ + OwnerAddress: addr, + OperatorAddress: addr, + Stake: &belowMinStake, + Services: stakeMsg.GetServices(), + ServicesActivationHeightsMap: map[string]uint64{ + "svcId": 0, + }, + } + + k.SetSupplier(ctx, initialSupplier) + + // Attempt to upstake the supplier with stake above min. stake. + _, err := srv.StakeSupplier(ctx, stakeMsg) + require.NoError(t, err) + + // Assert supplier is staked for above min. stake. + expectedSupplier := initialSupplier + expectedSupplier.Stake = &aboveMinStake + supplier, isSupplierFound := k.GetSupplier(ctx, addr) + require.True(t, isSupplierFound) + require.EqualValues(t, expectedSupplier, supplier) +} diff --git a/x/supplier/keeper/msg_server_unstake_supplier_test.go b/x/supplier/keeper/msg_server_unstake_supplier_test.go index 91d49ed7f..262d1caa9 100644 --- a/x/supplier/keeper/msg_server_unstake_supplier_test.go +++ b/x/supplier/keeper/msg_server_unstake_supplier_test.go @@ -12,7 +12,7 @@ import ( "github.com/pokt-network/poktroll/x/shared" sharedtypes "github.com/pokt-network/poktroll/x/shared/types" "github.com/pokt-network/poktroll/x/supplier/keeper" - "github.com/pokt-network/poktroll/x/supplier/types" + suppliertypes "github.com/pokt-network/poktroll/x/supplier/types" ) func TestMsgServer_UnstakeSupplier_Success(t *testing.T) { @@ -28,7 +28,7 @@ func TestMsgServer_UnstakeSupplier_Success(t *testing.T) { _, isSupplierFound := supplierModuleKeepers.GetSupplier(ctx, unstakingSupplierOperatorAddr) require.False(t, isSupplierFound) - initialStake := int64(100) + initialStake := suppliertypes.DefaultMinStake.Amount.Int64() stakeMsg := createStakeMsg(unstakingSupplierOperatorAddr, initialStake) // Stake the supplier @@ -54,7 +54,7 @@ func TestMsgServer_UnstakeSupplier_Success(t *testing.T) { require.True(t, isSupplierFound) // Initiate the supplier unstaking - unstakeMsg := &types.MsgUnstakeSupplier{ + unstakeMsg := &suppliertypes.MsgUnstakeSupplier{ Signer: unstakingSupplierOperatorAddr, OperatorAddress: unstakingSupplierOperatorAddr, } @@ -95,7 +95,7 @@ func TestMsgServer_UnstakeSupplier_CancelUnbondingIfRestaked(t *testing.T) { supplierOperatorAddr := sample.AccAddress() // Stake the supplier - initialStake := int64(100) + initialStake := suppliertypes.DefaultMinStake.Amount.Int64() stakeMsg := createStakeMsg(supplierOperatorAddr, initialStake) _, err := srv.StakeSupplier(ctx, stakeMsg) require.NoError(t, err) @@ -106,7 +106,7 @@ func TestMsgServer_UnstakeSupplier_CancelUnbondingIfRestaked(t *testing.T) { require.False(t, foundSupplier.IsUnbonding()) // Initiate the supplier unstaking - unstakeMsg := &types.MsgUnstakeSupplier{ + unstakeMsg := &suppliertypes.MsgUnstakeSupplier{ Signer: supplierOperatorAddr, OperatorAddress: supplierOperatorAddr, } @@ -154,13 +154,13 @@ func TestMsgServer_UnstakeSupplier_FailIfNotStaked(t *testing.T) { require.False(t, isSupplierFound) // Initiate the supplier unstaking - unstakeMsg := &types.MsgUnstakeSupplier{ + unstakeMsg := &suppliertypes.MsgUnstakeSupplier{ Signer: supplierOperatorAddr, OperatorAddress: supplierOperatorAddr, } _, err := srv.UnstakeSupplier(ctx, unstakeMsg) require.Error(t, err) - require.ErrorIs(t, err, types.ErrSupplierNotFound) + require.ErrorIs(t, err, suppliertypes.ErrSupplierNotFound) _, isSupplierFound = supplierModuleKeepers.GetSupplier(ctx, supplierOperatorAddr) require.False(t, isSupplierFound) @@ -174,13 +174,13 @@ func TestMsgServer_UnstakeSupplier_FailIfCurrentlyUnstaking(t *testing.T) { supplierOperatorAddr := sample.AccAddress() // Stake the supplier - initialStake := int64(100) + initialStake := suppliertypes.DefaultMinStake.Amount.Int64() stakeMsg := createStakeMsg(supplierOperatorAddr, initialStake) _, err := srv.StakeSupplier(ctx, stakeMsg) require.NoError(t, err) // Initiate the supplier unstaking - unstakeMsg := &types.MsgUnstakeSupplier{ + unstakeMsg := &suppliertypes.MsgUnstakeSupplier{ Signer: supplierOperatorAddr, OperatorAddress: supplierOperatorAddr, } @@ -191,7 +191,7 @@ func TestMsgServer_UnstakeSupplier_FailIfCurrentlyUnstaking(t *testing.T) { ctx = keepertest.SetBlockHeight(ctx, int64(sdkCtx.BlockHeight()+1)) _, err = srv.UnstakeSupplier(ctx, unstakeMsg) - require.ErrorIs(t, err, types.ErrSupplierIsUnstaking) + require.ErrorIs(t, err, suppliertypes.ErrSupplierIsUnstaking) } func TestMsgServer_UnstakeSupplier_OperatorCanUnstake(t *testing.T) { @@ -203,14 +203,14 @@ func TestMsgServer_UnstakeSupplier_OperatorCanUnstake(t *testing.T) { supplierOperatorAddr := sample.AccAddress() // Stake the supplier - initialStake := int64(100) + initialStake := suppliertypes.DefaultMinStake.Amount.Int64() stakeMsg := createStakeMsg(ownerAddr, initialStake) stakeMsg.OperatorAddress = supplierOperatorAddr _, err := srv.StakeSupplier(ctx, stakeMsg) require.NoError(t, err) // Initiate the supplier unstaking - unstakeMsg := &types.MsgUnstakeSupplier{ + unstakeMsg := &suppliertypes.MsgUnstakeSupplier{ Signer: supplierOperatorAddr, OperatorAddress: supplierOperatorAddr, } @@ -238,9 +238,9 @@ func TestMsgServer_UnstakeSupplier_OperatorCanUnstake(t *testing.T) { require.Equal(t, initialStake, supplierModuleKeepers.SupplierUnstakedFundsMap[ownerAddr]) } -func createStakeMsg(supplierOwnerAddr string, stakeAmount int64) *types.MsgStakeSupplier { +func createStakeMsg(supplierOwnerAddr string, stakeAmount int64) *suppliertypes.MsgStakeSupplier { initialStake := sdk.NewCoin("upokt", math.NewInt(stakeAmount)) - return &types.MsgStakeSupplier{ + return &suppliertypes.MsgStakeSupplier{ Signer: supplierOwnerAddr, OwnerAddress: supplierOwnerAddr, OperatorAddress: supplierOwnerAddr,