Skip to content

Commit

Permalink
[incentivization] fix: genesis bug + add more test coverage (#487)
Browse files Browse the repository at this point in the history
* fix: genesis bug and add tests

* chore: cover msg server with tests

* add: msg create incentivization program tests

* add: msg fund incentivization program tests

* chore: lint

* Update x/incentivization/keeper/keeper_test.go

Co-authored-by: Walter White <[email protected]>

* fix: merge

* chore: lint

Co-authored-by: Walter White <[email protected]>
  • Loading branch information
testinginprod and NibiruHeisenberg authored May 26, 2022
1 parent 804a7b9 commit ad82aeb
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 15 deletions.
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ func NewNibiruApp(
appCodec, app.VpoolKeeper, app.PricefeedKeeper,
)

incentivizationModule := incentivization.NewAppModule(appCodec, app.IncentivizationKeeper)
incentivizationModule := incentivization.NewAppModule(appCodec, app.IncentivizationKeeper, app.AccountKeeper)

// NOTE: Any module instantiated in the module manager that is later modified
// must be passed by reference here.
Expand Down
8 changes: 4 additions & 4 deletions x/incentivization/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type Keeper struct {

func (k Keeper) CreateIncentivizationProgram(
ctx sdk.Context,
lpDenom string, minLockupDuration time.Duration, starTime time.Time, epochs int64) (*types.IncentivizationProgram, error) {
lpDenom string, minLockupDuration time.Duration, startTime time.Time, epochs int64) (*types.IncentivizationProgram, error) {
// TODO(mercilex): assert lp denom from dex keeper

if epochs < MinEpochs {
Expand All @@ -65,8 +65,8 @@ func (k Keeper) CreateIncentivizationProgram(
return nil, types.ErrMinLockupDurationTooLow.Wrapf("%s is lower than minimum allowed %s", minLockupDuration, MinLockupDuration)
}

if ctx.BlockTime().After(starTime) {
return nil, types.ErrStartTimeInPast.Wrapf("current time %s, got: %s", ctx.BlockTime(), starTime)
if ctx.BlockTime().After(startTime) {
return nil, types.ErrStartTimeInPast.Wrapf("current time %s, got: %s", ctx.BlockTime(), startTime)
}

// we create a new instance of an incentivization program
Expand All @@ -80,7 +80,7 @@ func (k Keeper) CreateIncentivizationProgram(
RemainingEpochs: epochs,
LpDenom: lpDenom,
MinLockupDuration: minLockupDuration,
StartTime: starTime,
StartTime: startTime,
}

k.IncentivizationProgramsState(ctx).Create(program)
Expand Down
27 changes: 27 additions & 0 deletions x/incentivization/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import (
"testing"
"time"

tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

"github.com/NibiruChain/nibiru/x/incentivization/types"

"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
Expand All @@ -28,6 +32,29 @@ func TestKeeper_CreateIncentivizationProgram(t *testing.T) {
require.Equal(t, uint64(0), createdProgram.Id)
require.Equal(t, authtypes.NewModuleAddress(keeper.NewEscrowAccountName(0)).String(), createdProgram.EscrowAddress)
})
t.Run("min lockup duration too low", func(t *testing.T) {
app, ctx := testutil.NewNibiruApp(false)

_, err := app.IncentivizationKeeper.CreateIncentivizationProgram(ctx, "denom", 1*time.Second, ctx.BlockTime(), keeper.MinEpochs)

require.ErrorIs(t, err, types.ErrMinLockupDurationTooLow)
})

t.Run("epochs lower than minimum", func(t *testing.T) {
app, ctx := testutil.NewNibiruApp(false)

_, err := app.IncentivizationKeeper.CreateIncentivizationProgram(ctx, "denom", 48*time.Hour, ctx.BlockTime(), keeper.MinEpochs-1)

require.ErrorIs(t, err, types.ErrEpochsTooLow)
})

t.Run("start time before block time", func(t *testing.T) {
app := testutil.NewTestApp(false)
ctx := app.NewContext(false, tmproto.Header{Time: time.Now()})
_, err := app.IncentivizationKeeper.CreateIncentivizationProgram(ctx, "denom", 48*time.Hour, ctx.BlockTime().Add(-1*time.Second), keeper.MinEpochs+1)

require.ErrorIs(t, err, types.ErrStartTimeInPast)
})
}

func TestKeeper_FundIncentivizationProgram(t *testing.T) {
Expand Down
79 changes: 79 additions & 0 deletions x/incentivization/keeper/servers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import (
"testing"
"time"

"github.com/cosmos/cosmos-sdk/simapp"

"github.com/NibiruChain/nibiru/x/testutil/sample"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/stretchr/testify/require"
Expand All @@ -14,6 +18,81 @@ import (
"github.com/NibiruChain/nibiru/x/testutil"
)

func TestMsgServer_CreateIncentivizationProgram(t *testing.T) {
t.Run("success", func(t *testing.T) {
app := testutil.NewTestApp(false)
s := keeper.NewMsgServer(app.IncentivizationKeeper)
ctx := app.NewContext(false, tmproto.Header{Time: time.Now()})

addr := sample.AccAddress()
funds := sdk.NewCoins(sdk.NewInt64Coin("test", 1000))
require.NoError(t, simapp.FundAccount(app.BankKeeper, ctx, addr, funds))

lockDuration := keeper.MinLockupDuration
startTime := ctx.BlockTime().Add(10 * time.Second)

// here we test the whole flow, custom start time + initial funds
resp, err := s.CreateIncentivizationProgram(sdk.WrapSDKContext(ctx), &types.MsgCreateIncentivizationProgram{
Sender: addr.String(),
LpDenom: "denom",
MinLockupDuration: &lockDuration,
StartTime: &startTime,
Epochs: keeper.MinEpochs,
InitialFunds: funds,
})
require.NoError(t, err)

program, err := app.IncentivizationKeeper.IncentivizationProgramsState(ctx).Get(resp.ProgramId)
require.NoError(t, err)

escrowAddr, err := sdk.AccAddressFromBech32(program.EscrowAddress)
require.NoError(t, err)

escrowBalance := app.BankKeeper.GetAllBalances(ctx, escrowAddr)
require.Equal(t, funds, escrowBalance)
})
}

func TestMsgServer_FundIncentivizationProgram(t *testing.T) {
t.Run("success", func(t *testing.T) {
app := testutil.NewTestApp(false)
s := keeper.NewMsgServer(app.IncentivizationKeeper)
ctx := app.NewContext(false, tmproto.Header{Time: time.Now()})

addr := sample.AccAddress()
// create program
lockDuration := keeper.MinLockupDuration
creationResp, err := s.CreateIncentivizationProgram(sdk.WrapSDKContext(ctx), &types.MsgCreateIncentivizationProgram{
Sender: addr.String(),
LpDenom: "denom",
MinLockupDuration: &lockDuration,
Epochs: keeper.MinEpochs,
})
require.NoError(t, err)

// fund account
funds := sdk.NewCoins(sdk.NewInt64Coin("test", 1000))
require.NoError(t, simapp.FundAccount(app.BankKeeper, ctx, addr, funds))
// fund incentivization program
_, err = s.FundIncentivizationProgram(sdk.WrapSDKContext(ctx), &types.MsgFundIncentivizationProgram{
Sender: addr.String(),
Id: creationResp.ProgramId,
Funds: funds,
})
require.NoError(t, err)

// assert the program was funded
program, err := app.IncentivizationKeeper.IncentivizationProgramsState(ctx).Get(creationResp.ProgramId)
require.NoError(t, err)

escrowAddr, err := sdk.AccAddressFromBech32(program.EscrowAddress)
require.NoError(t, err)

escrowBalance := app.BankKeeper.GetAllBalances(ctx, escrowAddr)
require.Equal(t, funds, escrowBalance)
})
}

func TestQueryServer_IncentivizationProgram(t *testing.T) {
app := testutil.NewTestApp(false)
q := keeper.NewQueryServer(app.IncentivizationKeeper)
Expand Down
22 changes: 15 additions & 7 deletions x/incentivization/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"math/rand"

authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"

"github.com/NibiruChain/nibiru/x/incentivization/client/cli"

"github.com/cosmos/cosmos-sdk/client"
Expand Down Expand Up @@ -102,12 +104,14 @@ type AppModule struct {
AppModuleBasic

keeper keeper.Keeper
ak authkeeper.AccountKeeperI
}

func NewAppModule(cdc codec.Codec, keeper keeper.Keeper) AppModule {
func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, ak authkeeper.AccountKeeperI) AppModule {
return AppModule{
AppModuleBasic: NewAppModuleBasic(cdc),
keeper: keeper,
ak: ak,
}
}

Expand Down Expand Up @@ -157,16 +161,20 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.Ra
// NOTE(mercilex): since incentivization programs are exported
// in an ordered way, if the order of re-creation is the same
// then we will generate the same IDs and same escrow addresses.
// We also use the raw state create function because we don't want to go
// through block time and epoch checks.
state := am.keeper.IncentivizationProgramsState(ctx)
for _, program := range genState.IncentivizationPrograms {
_, err := am.keeper.CreateIncentivizationProgram(ctx,
program.LpDenom,
program.MinLockupDuration,
program.StartTime,
program.RemainingEpochs,
)
// we check if escrow account exists
escrowAddr, err := sdk.AccAddressFromBech32(program.EscrowAddress)
if err != nil {
panic(err)
}
if !am.ak.HasAccount(ctx, escrowAddr) {
panic(fmt.Errorf("incentivization program %d has escrow account: %s which holds funds but it does not exist in the accounts list", program.Id, program.EscrowAddress))
}
program.Id = 0 // reset program ID to avoid panics
state.Create(program)
}

return []abci.ValidatorUpdate{}
Expand Down
16 changes: 15 additions & 1 deletion x/incentivization/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"testing"
"time"

authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

"github.com/stretchr/testify/require"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

Expand All @@ -16,7 +18,7 @@ import (
func TestAppModule_InitGenesis_ExportGenesis(t *testing.T) {
app := testutil.NewTestApp(false)

am := incentivization.NewAppModule(app.AppCodec(), app.IncentivizationKeeper)
am := incentivization.NewAppModule(app.AppCodec(), app.IncentivizationKeeper, app.AccountKeeper)
ctxUncached := app.NewContext(false, tmproto.Header{Time: time.Now()})
ctx, _ := ctxUncached.CacheContext()
// create some programs
Expand All @@ -37,6 +39,18 @@ func TestAppModule_InitGenesis_ExportGenesis(t *testing.T) {
require.Equal(t, programs, genesis.IncentivizationPrograms) // must be equal to creation
// init genesis
ctx, _ = ctxUncached.CacheContext()
require.Panics(t, func() {
// tests the lack of existence of escrow accounts in auth
am.InitGenesis(ctx, app.AppCodec(), genesisRaw)
})
ctx, _ = ctxUncached.CacheContext()
ctx = ctx.WithBlockTime(ctxUncached.BlockTime().Add(10 * time.Second)) // set in the future
// init escrow accounts
for _, p := range programs {
escrowAccount := app.AccountKeeper.NewAccount(ctx, authtypes.NewEmptyModuleAccount(keeper.NewEscrowAccountName(p.Id))) // module account that holds the escrowed funds.
app.AccountKeeper.SetAccount(ctx, escrowAccount)
}
// init working genesis
am.InitGenesis(ctx, app.AppCodec(), genesisRaw)
// export again
genesisRaw = am.ExportGenesis(ctx, app.AppCodec())
Expand Down
4 changes: 2 additions & 2 deletions x/incentivization/types/incentivization.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (m *MsgCreateIncentivizationProgram) ValidateBasic() error {
return fmt.Errorf("invalid denom")
}
if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil {
return err
return fmt.Errorf("invalid address")
}

if m.Epochs == 0 {
Expand Down Expand Up @@ -78,7 +78,7 @@ func (m *MsgCreateIncentivizationProgram) GetSigners() []sdk.AccAddress {

func (m *MsgFundIncentivizationProgram) ValidateBasic() error {
if err := m.Funds.Validate(); err != nil {
return err
return fmt.Errorf("invalid funds")
}
if m.Funds.IsZero() {
return fmt.Errorf("no funding provided")
Expand Down
Loading

0 comments on commit ad82aeb

Please sign in to comment.